Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added apps/farminglog/src/assets/Icons/pagenation_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/farminglog/src/assets/Icons/pagenation_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 91 additions & 1 deletion apps/farminglog/src/pages/farminglog/view/index.styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,94 @@ export const FarmingLogWriteButtonImage = styled.img<ResponsiveProps>`
flex-shrink: 0;
aspect-ratio: 1/1;
cursor: pointer;
`;
`;

/** 페이지네이션 컨테이너 */
export const PaginationContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
margin-top: 40px;
margin-bottom: 40px;
`;

/** 페이지네이션 버튼 컨테이너 */
export const PaginationButton = styled.div`
display: flex;
gap: 10px;
align-items: center;
`;

/** 페이지네이션 버튼 텍스트 */
export const PaginationButtonText = styled.span<{
$active?: boolean;
$disabled?: boolean;
$isMobile?: boolean;
$isTablet?: boolean;
}>`
border-radius: 6px;
cursor: ${(props) => (props.$disabled ? 'not-allowed' : 'pointer')};
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;

/* 사이즈 조절 */
img[alt="nextArrow"]{
width: ${(props) => (props.$isMobile ? '6px' : props.$isTablet ? '12px' : '15px')};
height: ${(props) => (props.$isMobile ? '12px' : props.$isTablet ? '24px' : '30px')};
margin-right: 10px;
}

img[alt="jumpArrow"]{
width: ${(props) => (props.$isMobile ? '24px' : props.$isTablet ? '48px' : '60px')};
height: ${(props) => (props.$isMobile ? '24px' : props.$isTablet ? '48px' : '60px')};
}

/* nextArrow 이미지 회전 */
img[alt="nextArrow_right"] {
width: ${(props) => (props.$isMobile ? '6px' : props.$isTablet ? '12px' : '15px')};
height: ${(props) => (props.$isMobile ? '12px' : props.$isTablet ? '24px' : '30px')};
transform: rotate(180deg);
margin-left: 10px;
}

img[alt="jumpArrow_right"] {
width: ${(props) => (props.$isMobile ? '24px' : props.$isTablet ? '48px' : '60px')};
height: ${(props) => (props.$isMobile ? '24px' : props.$isTablet ? '48px' : '60px')};
transform: rotate(180deg);
}

&:hover {
${(props) => !props.$disabled && `
background-color: ${props.$active ? 'var(--FarmSystem_Green06)' : '#f0f0f0'};
transform: translateY(-1px);
`}
}

&:active {
${(props) => !props.$disabled && `
transform: translateY(0);
`}
}
`;

export const PaginationPageButton = styled.span<{
$active?: boolean;
$disabled?: boolean;
$isMobile?: boolean;
$isTablet?: boolean;
}>`
width: ${(props) => (props.$isMobile ? '20px' : props.$isTablet ? '26px' : '40px')};
height: ${(props) => (props.$isMobile ? '20px' : props.$isTablet ? '26px' : '40px')};
display: flex;
justify-content: center;
align-items: center;
border-radius: ${(props) => (props.$isMobile ? '10px' : props.$isTablet ? '13px' : '20px')};
cursor: ${(props) => (props.$disabled ? 'not-allowed' : 'pointer')};

background-color: ${(props) => (props.$active ? 'var(--FarmSystem_Green06)' : 'var(--FarmSystem_DarkGrey)')};
color: white;
font-size: ${(props) => (props.$isMobile ? '8px' : props.$isTablet ? '12px' : '14px')};
font-weight: 500;
transition: all 0.2s ease;
`;
160 changes: 108 additions & 52 deletions apps/farminglog/src/pages/farminglog/view/index.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
import React, { useCallback, useEffect, useRef } from 'react';
import {useEffect, useState } from 'react';
import * as S from './index.styled';
import Card from './Card';
import { useNavigate } from 'react-router';
import useMediaQueries from '@/hooks/useMediaQueries';
import WhiteContentContainer from '@/layouts/WhiteContentContainer';
import { useFarmingLogsInfiniteQuery } from '@/services/query/useFarmingLogInfiniteQuery';
import { useFarmingLogQuery } from '@/services/query/useFarmingLogInQuery';
import useFarmingLogStore from '@/stores/farminglogStore';
import CardSkeleton from './CardSkeleton';


import jumpArrow_left from '@/assets/Icons/pagenation_1.png';
import jumpArrow_right from '@/assets/Icons/pagenation_1.png';
import nextArrow_left from '@/assets/Icons/pagenation_2.png';
import nextArrow_right from '@/assets/Icons/pagenation_2.png';

import EditImage from '@/assets/Icons/edit-3.png';

export default function View() {
const navigate = useNavigate();
const { isApp, isMobile, isDesktop } = useMediaQueries();
const { isApp, isMobile, isDesktop, isTablet } = useMediaQueries();
const [currentPage, setCurrentPage] = useState<number>(0);

const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isFetching,
isLoading,
error,
refetch
} = useFarmingLogsInfiniteQuery();
} = useFarmingLogQuery(currentPage, 10); // 10개씩 페이지네이션
const { isNeedRefresh, setIsNeedRefresh } = useFarmingLogStore();

useEffect(() => {
Expand All @@ -33,22 +36,48 @@ export default function View() {
}
}, [isNeedRefresh, refetch, setIsNeedRefresh]);

// 마지막 카드 요소를 관찰하여 다음 페이지를 불러오기 위한 IntersectionObserver
const observerRef = useRef<IntersectionObserver | null>(null);
const lastLogRef = useCallback(
(node: HTMLDivElement) => {
if (isFetchingNextPage) return;
if (observerRef.current) observerRef.current.disconnect();

observerRef.current = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && hasNextPage) {
fetchNextPage();
}
});
if (node) observerRef.current.observe(node);
},
[isFetchingNextPage, fetchNextPage, hasNextPage]
);
// 페이지 번호 배열 생성
const generatePageNumbers = () => {
if (!data) return [];

const totalPages = data.totalPages;
const current = data.number; // 현재 페이지 번호 (0시작)
const pages: number[] = [];

// 최대 5개의 페이지 번호만 표시
const maxVisiblePages = 5;
let startPage = Math.max(0, current - Math.floor(maxVisiblePages / 2));
const endPage = Math.min(totalPages - 1, startPage + maxVisiblePages - 1);

// 시작 페이지 조정
if (endPage - startPage < maxVisiblePages - 1) {
startPage = Math.max(0, endPage - maxVisiblePages + 1);
}

for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}

return pages;
};

// 페이지네이션 핸들러
const handlePageChange = (page: number) => {
setCurrentPage(page);
};

const handlePreviousPage = () => {
if (data && !data.first) {
setCurrentPage(currentPage - 1);
}
};

const handleNextPage = () => {
if (data && !data.last) {
setCurrentPage(currentPage + 1);
}
};

// 로딩 또는 에러 상태 처리
if (isLoading) return (
Expand Down Expand Up @@ -80,37 +109,64 @@ export default function View() {
$isMobile={isMobile}
$isDesktop={isDesktop}
>
{data.pages.map((page, pageIndex) => (
<React.Fragment key={pageIndex}>
{page.content.map((log, idx) => {
// 마지막 페이지의 마지막 요소에 ref 적용
const isLastItem =
pageIndex === data.pages.length - 1 &&
idx === page.content.length - 1;
return (
<div
key={log.farmingLogId}
ref={isLastItem ? lastLogRef : null}
>
<Card data={log} />
</div>
);
})}
</React.Fragment>
{data?.content.map((log) => (
<div key={log.farmingLogId}>
<Card data={log} />
</div>
))}
{/* 페이지네이션 */}
{data && data.content.length > 0 && (
<S.PaginationContainer>
<S.PaginationButton>
<S.PaginationButtonText
onClick={() => setCurrentPage(0)}
$disabled={data?.first}
$isMobile={isMobile}
$isTablet={isTablet}
>
<img src={jumpArrow_left} alt="jumpArrow" />
</S.PaginationButtonText>
<S.PaginationButtonText
onClick={handlePreviousPage}
$disabled={data?.first}
$isMobile={isMobile}
$isTablet={isTablet}
>
<img src={nextArrow_left} alt="nextArrow" />
</S.PaginationButtonText>

{generatePageNumbers().map((pageNum) => (
<S.PaginationPageButton
key={pageNum}
onClick={() => handlePageChange(pageNum)}
$active={pageNum === currentPage}
$isMobile={isMobile}
$isTablet={isTablet}
>
{pageNum + 1}
</S.PaginationPageButton>
))}

<S.PaginationButtonText
onClick={handleNextPage}
$disabled={data?.last}
$isMobile={isMobile}
$isTablet={isTablet}
>
<img src={nextArrow_right} alt="nextArrow_right" />
</S.PaginationButtonText>
<S.PaginationButtonText
onClick={() => setCurrentPage(data.totalPages - 1)}
$disabled={data?.last}
$isMobile={isMobile}
$isTablet={isTablet}
>
<img src={jumpArrow_right} alt="jumpArrow_right" />
</S.PaginationButtonText>
</S.PaginationButton>
</S.PaginationContainer>
)}
</S.FarmingLogCardContainer>
{isFetchingNextPage && <div>로딩중...</div>}
{!hasNextPage && !isFetching && (
<S.EndOfList>
<S.EndOfListText
$isApp={isApp}
$isMobile={isMobile}
$isDesktop={isDesktop}
>
더 이상 글이 없습니다.
</S.EndOfListText>
</S.EndOfList>
)}
<S.FarmingLogWriteButton
$isApp={isApp}
$isMobile={isMobile}
Expand Down
25 changes: 25 additions & 0 deletions apps/farminglog/src/services/query/useFarmingLogInQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 파밍로그 게시글 페이지네이션 쿼리
import { useQuery } from '@tanstack/react-query';
import { usePrivateApi } from '@repo/api/hooks/usePrivateApi';
import { queryKeys } from "../queryKeys";
import { FarmingLogsResponse } from '@/models/farminglog';

export const useFarmingLogQuery = (page: number, size: number) => {
const { getData } = usePrivateApi();

return useQuery<FarmingLogsResponse, Error>({
queryKey: [...queryKeys.farminglog, page, size], // 페이지별 캐싱
queryFn: async (): Promise<FarmingLogsResponse> => {
const response = await getData<FarmingLogsResponse>(
`/farming-logs?page=${page}&size=${size}`
);
if (!response) {
console.error("파밍로그 조회 실패");
throw new Error("파밍로그 조회 실패");
}
console.log("파밍로그 조회 성공");
return (response as FarmingLogsResponse);
},
staleTime: 1000 * 60 * 5, // 5분
});
};
30 changes: 0 additions & 30 deletions apps/farminglog/src/services/query/useFarmingLogInfiniteQuery.ts

This file was deleted.

4 changes: 2 additions & 2 deletions apps/website/src/pages/Blog/Blog/BlogList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ const BlogList: React.FC = () => {
const current = pageInfo.currentPage;
const pages: number[] = [];

// 최대 7개의 페이지 번호만 표시
const maxVisiblePages = 3;
// 최대 5개의 페이지 번호만 표시
const maxVisiblePages = 5;
let startPage = Math.max(0, current - Math.floor(maxVisiblePages / 2));
const endPage = Math.min(totalPages - 1, startPage + maxVisiblePages - 1);

Expand Down
5 changes: 2 additions & 3 deletions apps/website/src/pages/Blog/Project/ProjectList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ const ProjectList: React.FC = () => {
const current = pageInfo.currentPage;
const pages: number[] = [];

// 최대 7개의 페이지 번호만 표시
const maxVisiblePages = 3;
// 최대 5개의 페이지 번호만 표시
const maxVisiblePages = 5;
let startPage = Math.max(0, current - Math.floor(maxVisiblePages / 2));
const endPage = Math.min(totalPages - 1, startPage + maxVisiblePages - 1);

Expand All @@ -106,7 +106,6 @@ const ProjectList: React.FC = () => {

for (let i = startPage; i <= endPage; i++) {
pages.push(i);
console.log("pages", pages);
}

return pages;
Expand Down