diff --git a/src/App.tsx b/src/App.tsx index ca77be0..0364d7e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from 'react'; import { Routes, Route } from 'react-router-dom'; -import MainPage from './pages/MainPage'; +// import MainPage from './pages/MainPage'; import LoginPage from './pages/LoginPage'; import MyPage from './pages/MyPage'; import CreateBoard from './pages/CreateBoardPage'; @@ -31,12 +31,11 @@ import { useMediaQuery } from 'react-responsive'; import Flex from './components/Flex'; import { MobileDisplay } from './styles/ErrorPageStyled'; import RouteChangeTracker from './components/RouteChangeTracker'; -import PersonalDashBoard from './components/PersonalDashboard'; +import PersonalDashboard from './components/PersonalDashboard'; import TeamDashBoard from './components/TeamDashboard'; const queryClient = new QueryClient(); -// 로그인 상태를 체크하는 함수 const useAuth = () => { const [isLoggedIn, setIsLoggedIn] = useState(false); const [loading, setLoading] = useState(true); @@ -58,8 +57,6 @@ const App = () => { const isMobile = useMediaQuery({ query: '(max-width: 1000px)' }); const { isLoggedIn, loading } = useAuth(); - RouteChangeTracker(); - if (loading) { return
Loading...
; } @@ -90,37 +87,48 @@ const App = () => { theme="light" style={{ width: '21.875rem', lineHeight: '1.5rem' }} /> + } /> } /> } /> } /> + - + } - /> - + + + + } + /> + + {/* - + } - /> + /> */} - + } > @@ -128,6 +136,14 @@ const App = () => { } /> + {/* + + + } + /> */} { - // navigate(`personalBlock/${blockId}`); - navigate(`personalBlock/${blockId}`, { + navigate(`${blockId}`, { state: { highlightColor, progress }, }); }; diff --git a/src/components/CompletedDashboard.tsx b/src/components/CompletedDashboard.tsx index b50f028..23bfcdb 100644 --- a/src/components/CompletedDashboard.tsx +++ b/src/components/CompletedDashboard.tsx @@ -46,7 +46,9 @@ const CompletedDashboard = ({ list, id, dashboardId, onLoadMore }: Props) => { // console.log(blockId); const { highlightColor, progress } = settings; - navigate(`personalBlock/${blockId}`, { state: { highlightColor, progress, blockId } }); + navigate(`${blockId}`, { + state: { highlightColor, progress, blockId }, + }); }; // 세로 무한 스크롤 @@ -77,20 +79,6 @@ const CompletedDashboard = ({ list, id, dashboardId, onLoadMore }: Props) => { className="container" {...provided.droppableProps} > - {/* {list?.map((block, index) => ( - - ))} */} {list?.map((block, index) => { const isLastBlock = index === list.length - 1; return ( @@ -115,7 +103,6 @@ const CompletedDashboard = ({ list, id, dashboardId, onLoadMore }: Props) => { )} - ); }; diff --git a/src/components/DeleteButton.tsx b/src/components/DeleteButton.tsx index 7d15c05..c11f97e 100644 --- a/src/components/DeleteButton.tsx +++ b/src/components/DeleteButton.tsx @@ -7,7 +7,7 @@ import { BlockListResDto, DeletedBlockList } from '../types/PersonalBlock'; interface Props { id: string; - list: DeletedBlockList; + list: BlockListResDto[]; removeValue: boolean; } const DeleteButton = ({ id, list, removeValue }: Props) => { @@ -30,7 +30,7 @@ const DeleteButton = ({ id, list, removeValue }: Props) => {

휴지통

- {list.blockListResDto.map( + {list.map( ( { title, blockId, contents, dDay, dashboardId, dType, nickname, picture }, index diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 97e1f85..f5a7d23 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -14,7 +14,7 @@ type Props = { const Header = ({ mainTitle, subTitle, blockProgress, dashboardType }: Props) => { const navigate = useNavigate(); const location = useLocation(); - const dashboardId = location.pathname.split('/')[1]; + const dashboardId = location.pathname.split('/')[2]; // URL에 "teamdocument"가 포함되어 있는지 확인하는 함수 // => 전역 변수로 개인 대시보드인지 팀 대시보드인지 확인할 예정이라 주석 처리 diff --git a/src/components/InProgressDashboard.tsx b/src/components/InProgressDashboard.tsx index c982289..19961a6 100644 --- a/src/components/InProgressDashboard.tsx +++ b/src/components/InProgressDashboard.tsx @@ -49,7 +49,7 @@ const InProgressDashboard = ({ list, id, dashboardId, onLoadMore }: Props) => { // console.log(blockId); const { highlightColor, progress } = settings; - navigate(`personalBlock/${blockId}`, { + navigate(`${blockId}`, { state: { highlightColor, progress, blockId }, }); }; @@ -82,20 +82,6 @@ const InProgressDashboard = ({ list, id, dashboardId, onLoadMore }: Props) => { className="container" {...provided.droppableProps} > - {/* {list?.map((block, index) => ( - - ))} */} {list?.map((block, index) => { const isLastBlock = index === list.length - 1; return ( @@ -120,7 +106,6 @@ const InProgressDashboard = ({ list, id, dashboardId, onLoadMore }: Props) => { )} - ); }; diff --git a/src/components/NotStartedDashboard.tsx b/src/components/NotStartedDashboard.tsx index 1770ace..800f90f 100644 --- a/src/components/NotStartedDashboard.tsx +++ b/src/components/NotStartedDashboard.tsx @@ -8,7 +8,6 @@ import main3 from '../img/main3.png'; import { BlockListResDto } from '../types/PersonalBlock'; import { useInView } from 'react-intersection-observer'; import { useEffect } from 'react'; -import { progress } from 'framer-motion'; type Props = { list: BlockListResDto[]; @@ -45,7 +44,7 @@ const NotStartedDashboard = ({ list, id, dashboardId, onLoadMore }: Props) => { const blockId = await createPersonalBlock(data); const { highlightColor, progress } = settings; - navigate(`personalBlock/${blockId}`, { + navigate(`${blockId}`, { state: { highlightColor, progress, blockId }, }); }; @@ -104,7 +103,6 @@ const NotStartedDashboard = ({ list, id, dashboardId, onLoadMore }: Props) => { )} - ); }; diff --git a/src/components/PersonalDashboard.tsx b/src/components/PersonalDashboard.tsx index 649a39f..d851e99 100644 --- a/src/components/PersonalDashboard.tsx +++ b/src/components/PersonalDashboard.tsx @@ -1,50 +1,143 @@ import { useQuery, useQueryClient } from '@tanstack/react-query'; import { getPersonalBlock, getPersonalDashboard } from '../api/BoardApi'; -import { getDeleteBlock } from '../api/PersonalBlockApi'; +import { + deleteBlock, + getDeleteBlock, + updateOrderBlock, + updatePersonalBlock, +} from '../api/PersonalBlockApi'; import DashBoardLayout from './DashBoardLayout'; import Header from './Header'; -import { useLocation } from 'react-router-dom'; +import { Outlet, useLocation } from 'react-router-dom'; import NotStartedDashboard from './NotStartedDashboard'; import InProgressDashboard from './InProgressDashboard'; import CompletedDashboard from './CompletedDashboard'; import { useState } from 'react'; -import { DragDropContext } from 'react-beautiful-dnd'; +import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd'; import * as S from '../styles/MainPageStyled'; import DeleteButton from './DeleteButton'; +import { TItems, TItemStatus } from '../utils/columnsConfig'; +import useItems from '../hooks/useItems'; +import { BlockListResDto } from '../types/PersonalBlock'; +import { useDebounce } from '../hooks/useDebounce'; + +type PageState = { + todo: number; // 할 일 페이지 번호 + doing: number; // 진행 중인 일 페이지 번호 + completed: number; // 완료된 일 페이지 번호 +}; const PersonalDashBoard = () => { const location = useLocation(); - const dashboardId = location.pathname.split('/')[2]; - - const [page, setPage] = useState(0); - - const { data: NotStarted } = useQuery({ - queryKey: ['NOT_STARTED', dashboardId], - queryFn: () => getPersonalBlock(dashboardId, 0, 10, 'NOT_STARTED'), - }); - const { data: InProgress } = useQuery({ - queryKey: ['IN_PROGRESS', dashboardId], - queryFn: () => getPersonalBlock(dashboardId, 0, 10, 'IN_PROGRESS'), - }); - const { data: COMPLETED } = useQuery({ - queryKey: ['COMPLETED', dashboardId], - queryFn: () => getPersonalBlock(dashboardId, 0, 10, 'COMPLETED'), + // const [page, setPage] = useState(0); + const [todoPage, setTodoPage] = useState(0); + const [doingPage, setDoingPage] = useState(0); + const [page, setPage] = useState({ + todo: 0, + doing: 0, + completed: 0, }); - const { data: DeletedBlock } = useQuery({ - queryKey: ['DeletedBlock', dashboardId], - queryFn: () => getDeleteBlock(dashboardId), - }); - console.log('지운거 조회 :', DeletedBlock); + const { + items, + setItems, + fetchNextNotStarted, + hasMoreNotStarted, + fetchNextInProgress, + hasMoreInProgress, + fetchNextCompleted, + hasMoreCompleted, + } = useItems(dashboardId, page, location.pathname); + // console.log(items); + const { data: PersonalDashboardInfo } = useQuery({ queryKey: ['PersonalDashboardInfo', dashboardId], queryFn: () => getPersonalDashboard(dashboardId), }); - // 세로 무한 스크롤 감지 이벤트 - const handleLoadMore = async () => { - setPage(prevPage => prevPage + 1); + // * 세로 무한 스크롤 감지 이벤트 + const handleLoadMore = async (status: 'todo' | 'doing' | 'completed') => { + // 다음 페이지 요청 + if (status === 'todo') { + await fetchNextNotStarted(); // todo 상태에 대한 다음 페이지 요청 + } else if (status === 'doing') { + // 비슷한 방식으로 진행 중인 상태에 대한 요청 추가 + await fetchNextInProgress(); // fetchNextDoing는 해당 상태에 맞는 함수를 정의해야 합니다. + } else if (status === 'completed') { + // 완료된 상태에 대한 요청 추가 + await fetchNextCompleted(); // fetchNextCompleted는 해당 상태에 맞는 함수를 정의해야 합니다. + } + + // 페이지 상태 업데이트 + if (status === 'todo' && hasMoreNotStarted) { + setPage(prevPage => ({ + ...prevPage, + todo: prevPage.todo + 1, + })); + } else if (status === 'doing' && hasMoreInProgress) { + // hasMoreDoing은 해당 상태에 대한 변수입니다. + setPage(prevPage => ({ + ...prevPage, + doing: prevPage.doing + 1, + })); + } else if (status === 'completed' && hasMoreCompleted) { + // hasMoreCompleted은 해당 상태에 대한 변수입니다. + setPage(prevPage => ({ + ...prevPage, + completed: prevPage.completed + 1, + })); + } + }; + + // * 상태 변경 함수 + const updateState = (destinationKey: string, targetItem: BlockListResDto) => { + const blockId = targetItem.blockId; + + if (blockId) { + if (destinationKey !== 'delete') { + updatePersonalBlock(blockId, status(destinationKey)); // 블록 상태 업데이트 + } else { + console.log('블록 삭제할게요'); + deleteBlock(blockId); // 블록 삭제 + } + } }; + + // * 순서 변경 함수 + const updateOrder = (_items: TItems) => { + const orderArray = { + dashboardId: dashboardId, + notStartedList: _items.todo.map(item => item.blockId), + inProgressList: _items.doing.map(item => item.blockId), + completedList: _items.completed.map(item => item.blockId), + }; + updateOrderBlock(orderArray); + }; + + // * 드래그앤드롭 함수 + const onDragEnd = ({ source, destination }: DropResult) => { + if (!destination) return; + + const sourceKey = source.droppableId as TItemStatus; + const destinationKey = destination.droppableId as TItemStatus; + + const _items = JSON.parse(JSON.stringify(items)) as typeof items; + + if (!_items[sourceKey] || !_items[destinationKey]) { + console.error('Invalid droppableId:', sourceKey, destinationKey); + return; + } + const [targetItem] = _items[sourceKey].splice(source.index, 1); + _items[destinationKey].splice(destination.index, 0, targetItem); + setItems(_items); + + if (sourceKey !== destinationKey) { + updateState(destinationKey, targetItem); + } + + updateOrder(_items); + }; + return ( <> @@ -54,31 +147,47 @@ const PersonalDashBoard = () => { blockProgress={(Math.floor(PersonalDashboardInfo?.blockProgress ?? 0) * 10) / 10} dashboardType={true} /> - {}}> + handleLoadMore('todo')} > handleLoadMore('doing')} > handleLoadMore('completed')} > - + + ); }; export default PersonalDashBoard; + +const status = (status: string) => { + switch (status) { + case 'todo': + return 'NOT_STARTED'; + case 'doing': + return 'IN_PROGRESS'; + case 'completed': + return 'COMPLETED'; + case 'delete': + return 'DELETED'; + default: + return 'UNKNOWN'; + } +}; diff --git a/src/components/SidebarScreen.tsx b/src/components/SidebarScreen.tsx index 561bf83..b51d233 100644 --- a/src/components/SidebarScreen.tsx +++ b/src/components/SidebarScreen.tsx @@ -7,6 +7,7 @@ import { useAtom } from 'jotai'; import closebutton from '../img/closebutton.png'; import { useRef, useState } from 'react'; import * as S from '../styles/SideScreenStyled'; +import { useNavigate } from 'react-router-dom'; const SidebarScreen = () => { const [inputText, setInputText] = useState(''); diff --git a/src/components/TeamDashboard.tsx b/src/components/TeamDashboard.tsx index 8b43c2c..91df5a6 100644 --- a/src/components/TeamDashboard.tsx +++ b/src/components/TeamDashboard.tsx @@ -1,48 +1,140 @@ import { useState } from 'react'; import { useQuery } from '@tanstack/react-query'; +import { Outlet, useLocation } from 'react-router-dom'; import { getPersonalBlock } from '../api/BoardApi'; -import { getDeleteBlock } from '../api/PersonalBlockApi'; +import { + deleteBlock, + getDeleteBlock, + updateOrderBlock, + updatePersonalBlock, +} from '../api/PersonalBlockApi'; import { getTeamDashboard } from '../api/TeamDashBoardApi'; import DashBoardLayout from './DashBoardLayout'; import Header from './Header'; -import { DragDropContext } from 'react-beautiful-dnd'; +import { DragDropContext, DropResult } from 'react-beautiful-dnd'; import NotStartedDashboard from './NotStartedDashboard'; import InProgressDashboard from './InProgressDashboard'; import CompletedDashboard from './CompletedDashboard'; import DeleteButton from './DeleteButton'; import * as S from '../styles/MainPageStyled'; +import useItems from '../hooks/useItems'; +import { BlockListResDto } from '../types/PersonalBlock'; +import { TItems, TItemStatus } from '../utils/columnsConfig'; + +type PageState = { + todo: number; // 할 일 페이지 번호 + doing: number; // 진행 중인 일 페이지 번호 + completed: number; // 완료된 일 페이지 번호 +}; const TeamDashBoard = () => { + const location = useLocation(); const dashboardId = location.pathname.split('/')[2]; - - const [page, setPage] = useState(0); - - const { data: NotStarted } = useQuery({ - queryKey: ['NOT_STARTED'], - queryFn: () => getPersonalBlock(dashboardId, 0, 10, 'NOT_STARTED'), + // const [page, setPage] = useState(0); + const [todoPage, setTodoPage] = useState(0); + const [page, setPage] = useState({ + todo: 0, + doing: 0, + completed: 0, }); + // const { items, setItems } = useItems(dashboardId, page); + const { + items, + setItems, + fetchNextNotStarted, + hasMoreNotStarted, + fetchNextInProgress, + hasMoreInProgress, + fetchNextCompleted, + hasMoreCompleted, + } = useItems(dashboardId, page, location.pathname); - const { data: InProgress } = useQuery({ - queryKey: ['IN_PROGRESS'], - queryFn: () => getPersonalBlock(dashboardId, 0, 10, 'IN_PROGRESS'), - }); - - const { data: COMPLETED } = useQuery({ - queryKey: ['COMPLETED'], - queryFn: () => getPersonalBlock(dashboardId, 0, 10, 'COMPLETED'), - }); - - const { data: DeletedBlock } = useQuery({ - queryKey: ['DeletedBlock'], - queryFn: () => getDeleteBlock(dashboardId), - }); const { data: TeamDashboardInfo } = useQuery({ - queryKey: ['TeamDashboardInfo'], + queryKey: ['TeamDashboardInfo', dashboardId], queryFn: () => getTeamDashboard(dashboardId), }); - // 세로 무한 스크롤 감지 이벤트 - const handleLoadMore = async () => { - setPage(prevPage => prevPage + 1); + + // * 세로 무한 스크롤 감지 이벤트 + const handleLoadMore = async (status: 'todo' | 'doing' | 'completed') => { + // 다음 페이지 요청 + if (status === 'todo') { + await fetchNextNotStarted(); // todo 상태에 대한 다음 페이지 요청 + } else if (status === 'doing') { + // 비슷한 방식으로 진행 중인 상태에 대한 요청 추가 + await fetchNextInProgress(); // fetchNextDoing는 해당 상태에 맞는 함수를 정의해야 합니다. + } else if (status === 'completed') { + // 완료된 상태에 대한 요청 추가 + await fetchNextCompleted(); // fetchNextCompleted는 해당 상태에 맞는 함수를 정의해야 합니다. + } + + // 페이지 상태 업데이트 + if (status === 'todo' && hasMoreNotStarted) { + setPage(prevPage => ({ + ...prevPage, + todo: prevPage.todo + 1, + })); + } else if (status === 'doing' && hasMoreInProgress) { + // hasMoreDoing은 해당 상태에 대한 변수입니다. + setPage(prevPage => ({ + ...prevPage, + doing: prevPage.doing + 1, + })); + } else if (status === 'completed' && hasMoreCompleted) { + // hasMoreCompleted은 해당 상태에 대한 변수입니다. + setPage(prevPage => ({ + ...prevPage, + completed: prevPage.completed + 1, + })); + } + }; + + // * 상태 변경 함수 + const updateState = (destinationKey: string, targetItem: BlockListResDto) => { + const blockId = targetItem.blockId; + + if (blockId) { + if (destinationKey !== 'delete') { + updatePersonalBlock(blockId, status(destinationKey)); // 블록 상태 업데이트 + } else { + console.log('블록 삭제할게요'); + deleteBlock(blockId); // 블록 삭제 + } + } + }; + + // * 순서 변경 함수 + const updateOrder = (_items: TItems) => { + const orderArray = { + dashboardId: dashboardId, + notStartedList: _items.todo.map(item => item.blockId), + inProgressList: _items.doing.map(item => item.blockId), + completedList: _items.completed.map(item => item.blockId), + }; + updateOrderBlock(orderArray); + }; + + // * 드래그앤드롭 함수 + const onDragEnd = ({ source, destination }: DropResult) => { + if (!destination) return; + + const sourceKey = source.droppableId as TItemStatus; + const destinationKey = destination.droppableId as TItemStatus; + + const _items = JSON.parse(JSON.stringify(items)) as typeof items; + + if (!_items[sourceKey] || !_items[destinationKey]) { + console.error('Invalid droppableId:', sourceKey, destinationKey); + return; + } + const [targetItem] = _items[sourceKey].splice(source.index, 1); + _items[destinationKey].splice(destination.index, 0, targetItem); + setItems(_items); + + if (sourceKey !== destinationKey) { + updateState(destinationKey, targetItem); + } + + updateOrder(_items); }; return ( <> @@ -52,31 +144,47 @@ const TeamDashBoard = () => { subTitle={TeamDashboardInfo?.description || ''} blockProgress={(Math.floor(TeamDashboardInfo?.blockProgress ?? 0) * 10) / 10} /> - {}}> + handleLoadMore('todo')} > handleLoadMore('doing')} > handleLoadMore('completed')} > - + + ); }; export default TeamDashBoard; + +const status = (status: string) => { + switch (status) { + case 'todo': + return 'NOT_STARTED'; + case 'doing': + return 'IN_PROGRESS'; + case 'completed': + return 'COMPLETED'; + case 'delete': + return 'DELETED'; + default: + return 'UNKNOWN'; + } +}; diff --git a/src/hooks/useItems.ts b/src/hooks/useItems.ts new file mode 100644 index 0000000..25032c0 --- /dev/null +++ b/src/hooks/useItems.ts @@ -0,0 +1,135 @@ +import { TItems } from '../utils/columnsConfig'; +import { useState, useEffect } from 'react'; +import { useInfiniteQuery, useQuery } from '@tanstack/react-query'; +import { getPersonalBlock } from '../api/BoardApi'; +import { getDeleteBlock } from '../api/PersonalBlockApi'; +import { useAtom } from 'jotai'; +import { fetchTriggerAtom } from '../contexts/atoms'; + +type PageState = { + todo: number; // 할 일 페이지 번호 + doing: number; // 진행 중인 일 페이지 번호 + completed: number; // 완료된 일 페이지 번호 +}; + +export default function useItems(dashboardId: string, pageParam: PageState, pathname: string) { + const [fetchTrigger] = useAtom(fetchTriggerAtom); + + const { + data: NotStarted, + fetchNextPage: fetchNextNotStarted, + hasNextPage: hasMoreNotStarted, + } = useInfiniteQuery({ + queryKey: ['NOT_STARTED', dashboardId, fetchTrigger, pathname], + queryFn: ({ pageParam }) => { + const todo = pageParam; // 객체에서 todo 페이지 번호를 가져옴 + return getPersonalBlock(dashboardId, todo, 10, 'NOT_STARTED'); + }, + initialPageParam: 0, + getNextPageParam: lastPage => { + const currentPage = lastPage?.pageInfoResDto?.currentPage ?? 0; + const totalPages = lastPage?.pageInfoResDto?.totalPages ?? 1; + + return currentPage < totalPages ? currentPage + 1 : undefined; + }, + }); + console.log(NotStarted); + + const { + data: InProgress, + fetchNextPage: fetchNextInProgress, + hasNextPage: hasMoreInProgress, + } = useInfiniteQuery({ + queryKey: ['IN_PROGRESS', dashboardId, fetchTrigger, pathname], + queryFn: ({ pageParam }) => { + const doing = pageParam; // 객체에서 todo 페이지 번호를 가져옴 + return getPersonalBlock(dashboardId, doing, 10, 'IN_PROGRESS'); + }, + initialPageParam: 0, + getNextPageParam: lastPage => { + const currentPage = lastPage?.pageInfoResDto?.currentPage ?? 0; + const totalPages = lastPage?.pageInfoResDto?.totalPages ?? 1; + + return currentPage < totalPages ? currentPage + 1 : undefined; + }, + }); + + const { + data: Completed, + fetchNextPage: fetchNextCompleted, + hasNextPage: hasMoreCompleted, + } = useInfiniteQuery({ + queryKey: ['COMPLETED', dashboardId, fetchTrigger, pathname], + queryFn: ({ pageParam }) => { + const completed = pageParam; // 객체에서 todo 페이지 번호를 가져옴 + return getPersonalBlock(dashboardId, completed, 10, 'COMPLETED'); + }, + initialPageParam: 0, + getNextPageParam: lastPage => { + const currentPage = lastPage?.pageInfoResDto?.currentPage ?? 0; + const totalPages = lastPage?.pageInfoResDto?.totalPages ?? 1; + + return currentPage < totalPages ? currentPage + 1 : undefined; + }, + }); + + const { data: DeletedBlock } = useQuery({ + queryKey: ['DeletedBlock', dashboardId, fetchTrigger, pathname], + queryFn: () => getDeleteBlock(dashboardId), + }); + // const [items, setItems] = useState(); + const [items, setItems] = useState({ + todo: NotStarted?.pages[0]?.blockListResDto || [], + doing: InProgress?.pages[0]?.blockListResDto || [], + completed: Completed?.pages[0]?.blockListResDto || [], + delete: DeletedBlock?.blockListResDto || [], + }); + + useEffect(() => { + if (NotStarted) { + const allNotStartedItems = NotStarted.pages.flatMap(page => page?.blockListResDto || []); + setItems(prevItems => ({ + ...prevItems, + todo: allNotStartedItems, + })); + } + }, [NotStarted]); + + useEffect(() => { + if (InProgress) { + const allInProgressItems = InProgress.pages.flatMap(page => page?.blockListResDto || []); + setItems(prevItems => ({ + ...prevItems, + doing: allInProgressItems, + })); + } + }, [InProgress]); + + useEffect(() => { + if (Completed) { + const allCompletedItems = Completed.pages.flatMap(page => page?.blockListResDto || []); + setItems(prevItems => ({ + ...prevItems, + completed: allCompletedItems, + })); + } + }, [Completed]); + + useEffect(() => { + setItems(prevItems => ({ + ...prevItems, + delete: DeletedBlock?.blockListResDto || [], + })); + }, [DeletedBlock]); + + return { + items, + setItems, + fetchNextNotStarted, + hasMoreNotStarted, + fetchNextInProgress, + hasMoreInProgress, + fetchNextCompleted, + hasMoreCompleted, + }; +} diff --git a/src/hooks/usePersonalDashBoard.tsx b/src/hooks/usePersonalDashBoard.tsx index 1e5598b..0edd9ba 100644 --- a/src/hooks/usePersonalDashBoard.tsx +++ b/src/hooks/usePersonalDashBoard.tsx @@ -84,7 +84,7 @@ const usePersonalDashBoard = (dashboardId: string | null) => { const responseDashboardId = dashboardId ? await patchDashBoard(dashboardId, formData) // 기존 대시보드 수정 : await createDashBoard(formData); // 새 대시보드 생성 - navigate(`/${responseDashboardId}`); // 해당 대시보드 페이지로 이동 + navigate(`/personal/${responseDashboardId}`); // 해당 대시보드 페이지로 이동 } catch (error) { console.error('개인 대시보드 생성 및 수정시 오류 발생!', error); } diff --git a/src/hooks/useTeamItems.ts b/src/hooks/useTeamItems.ts new file mode 100644 index 0000000..f770954 --- /dev/null +++ b/src/hooks/useTeamItems.ts @@ -0,0 +1,46 @@ +import { TItems } from '../utils/columnsConfig'; +import { useState, useEffect } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { getPersonalBlock } from '../api/BoardApi'; +import { getDeleteBlock } from '../api/PersonalBlockApi'; + +export default function useItems(dashboardId: string) { + const { data: NotStarted } = useQuery({ + queryKey: ['NOT_STARTED', dashboardId], + queryFn: () => getPersonalBlock(dashboardId, 0, 10, 'NOT_STARTED'), + }); + + const { data: InProgress } = useQuery({ + queryKey: ['IN_PROGRESS', dashboardId], + queryFn: () => getPersonalBlock(dashboardId, 0, 10, 'IN_PROGRESS'), + }); + + const { data: COMPLETED } = useQuery({ + queryKey: ['COMPLETED', dashboardId], + queryFn: () => getPersonalBlock(dashboardId, 0, 10, 'COMPLETED'), + }); + + const { data: DeletedBlock } = useQuery({ + queryKey: ['DeletedBlock', dashboardId], + queryFn: () => getDeleteBlock(dashboardId), + }); + // Set items with fetched data + const [items, setItems] = useState({ + todo: NotStarted?.blockListResDto || [], + doing: InProgress?.blockListResDto || [], + completed: COMPLETED?.blockListResDto || [], + delete: DeletedBlock?.blockListResDto || [], + }); + + // Update items whenever the fetched data changes + useEffect(() => { + setItems({ + todo: NotStarted?.blockListResDto || [], + doing: InProgress?.blockListResDto || [], + completed: COMPLETED?.blockListResDto || [], + delete: DeletedBlock?.blockListResDto || [], + }); + }, [NotStarted, InProgress, COMPLETED, DeletedBlock]); + + return { items, setItems }; +} diff --git a/src/pages/CreateTeamBoardPage.tsx b/src/pages/CreateTeamBoardPage.tsx index 4e0ddaf..8d60426 100644 --- a/src/pages/CreateTeamBoardPage.tsx +++ b/src/pages/CreateTeamBoardPage.tsx @@ -132,7 +132,7 @@ const CreateTeamBoard = () => { const responseDashboardId = dashboardId ? await patchTeamDashBoard(dashboardId, updatedFormData) // 기존 대시보드 수정 : await createTeamDashBoard(updatedFormData); // 새 대시보드 생성 - navigate(`/${responseDashboardId}`); // 해당 대시보드 페이지로 이동 + navigate(`/team/${responseDashboardId}`); // 해당 대시보드 페이지로 이동 } catch (error) { console.error('팀 대시보드 생성 및 수정시 오류 발생!', error); } diff --git a/src/pages/MainPage.tsx b/src/pages/MainPage.tsx deleted file mode 100644 index 0318538..0000000 --- a/src/pages/MainPage.tsx +++ /dev/null @@ -1,387 +0,0 @@ -import { useEffect, useState, useCallback } from 'react'; -import Navbar from '../components/Navbar'; -import Header from '../components/Header'; -import * as S from '../styles/MainPageStyled'; -import { DragDropContext, DropResult } from 'react-beautiful-dnd'; -import { handleAutoScroll } from '../utils/handleAutoScroll'; -import { useLocation } from 'react-router-dom'; -import { BlockListResDto, StatusPersonalBlock } from '../types/PersonalBlock'; -import { - deleteBlock, - getDeleteBlock, - updateOrderBlock, - updatePersonalBlock, -} from '../api/PersonalBlockApi'; -import { useDebounce } from '../hooks/useDebounce'; -import { initialColumns } from '../utils/columnsConfig'; -import { DashboardItem } from '../types/PersonalDashBoard'; -import { getPersonalBlock, getPersonalDashboard } from '../api/BoardApi'; -import DeleteButton from '../components/DeleteButton'; -import { useAtom } from 'jotai'; -import { fetchTriggerAtom } from '../contexts/atoms'; -import { getTeamDashboard } from '../api/TeamDashBoardApi'; -import { TeamDashboardInfoResDto } from '../types/TeamDashBoard'; -import 'react-toastify/dist/ReactToastify.css'; -import { flushSync } from 'react-dom'; -import { Helmet } from 'react-helmet-async'; - -export type TItemStatus = 'todo' | 'doing' | 'done' | 'delete'; - -type Props = { - list: BlockListResDto[]; - id: string; - dashboardId: string; - onLoadMore: () => void; -}; - -const MainPage = () => { - const location = useLocation(); - const dashboardId = location.pathname.split('/')[1]; - const [page, setPage] = useState(0); - const [fetchTrigger] = useAtom(fetchTriggerAtom); // 상태 트리거 가져오기 - const [dashboardDetail, setDashboardDetail] = useState(null); - const [teamDashboardDetail, setTeamDashboardDetail] = useState( - null - ); - - const [columns, setColumns] = useState<{ - [key in TItemStatus]: { - id: string; - list: StatusPersonalBlock['blockListResDto']; - pageInfo?: StatusPersonalBlock['pageInfoResDto']; - component?: React.ComponentType; - }; - }>(initialColumns); - - // 블록 순서 변경 디바운스 처리 - const debouncedData = useDebounce(columns, 10); - - // 개인 및 팀 대시보드 데이터를 가져오는 useCallback 함수 - const fetchData = useCallback( - async (page: number = 0) => { - try { - // 개인 및 팀 데이터를 병렬로 가져옴 - const [todo, doing, done, remove, personalDashboardData, teamDashboardData] = - await Promise.all([ - getPersonalBlock(dashboardId, page, 10, 'NOT_STARTED'), - getPersonalBlock(dashboardId, page, 10, 'IN_PROGRESS'), - getPersonalBlock(dashboardId, page, 10, 'COMPLETED'), - getDeleteBlock(dashboardId), - getPersonalDashboard(dashboardId), - getTeamDashboard(dashboardId), - ]); - - // 개인 블록 데이터를 업데이트 - if (todo && doing && done) { - setColumns(prevColumns => ({ - ...prevColumns, - todo: { - ...prevColumns.todo, - list: - page === 0 - ? todo.blockListResDto - : [...prevColumns.todo.list, ...todo.blockListResDto], - pageInfo: todo.pageInfoResDto, - }, - doing: { - ...prevColumns.doing, - list: - page === 0 - ? doing.blockListResDto - : [...prevColumns.doing.list, ...doing.blockListResDto], - pageInfo: doing.pageInfoResDto, - }, - done: { - ...prevColumns.done, - list: - page === 0 - ? done.blockListResDto - : [...prevColumns.done.list, ...done.blockListResDto], - pageInfo: done.pageInfoResDto, - }, - delete: { - ...prevColumns.delete, - list: page === 0 ? remove.blockListResDto : [...remove.blockListResDto], - pageInfo: remove.pageInfoResDto, - }, - })); - } - - // 개인 대시보드 데이터 업데이트 - if (personalDashboardData) { - setDashboardDetail(personalDashboardData); - setTeamDashboardDetail(null); - } - - //팀 대시보드 데이터 업데이트 - if (teamDashboardData) { - setDashboardDetail(null); - setTeamDashboardDetail(teamDashboardData); - } - } catch (error) { - console.error('Error fetching data', error); - } - }, - [dashboardId] - ); - - // 데이터 fetch 후 팀 또는 개인 대시보드 데이터를 설정 - useEffect(() => { - setPage(0); - setColumns(prevColumns => ({ - ...prevColumns, - todo: { - ...prevColumns.todo, - list: [], - pageInfo: { currentPage: 0, totalPages: 1, totalItems: 1 }, - }, - doing: { - ...prevColumns.doing, - list: [], - pageInfo: { currentPage: 0, totalPages: 1, totalItems: 1 }, - }, - done: { - ...prevColumns.done, - list: [], - pageInfo: { currentPage: 0, totalPages: 1, totalItems: 1 }, - }, - delete: { - ...prevColumns.delete, - list: prevColumns.delete.list, // 휴지통 리스트 유지 - pageInfo: prevColumns.delete.pageInfo, // 휴지통 페이지 정보 유지 - }, - })); - - fetchData(0); - fetchDashboardData(); - }, [location.pathname, fetchData]); - - // 페이지가 변경될 때 데이터를 다시 가져옴 - useEffect(() => { - if (page > 0) { - fetchData(page); - } - }, [page, fetchData]); - - useEffect(() => { - fetchBlockData(0); // 페이지가 로드될 때 처음으로 데이터를 불러옵니다. - }, [dashboardId]); - - // fetchTrigger 상태가 변경되면 데이터를 다시 불러옴 - useEffect(() => { - fetchBlockData(0); // 트리거가 변경되면 다시 데이터 호출 - }, [fetchTrigger]); - - useEffect(() => { - const orderArray = { - notStartedList: columns.todo.list.map(item => item.blockId), - inProgressList: columns.doing.list.map(item => item.blockId), - completedList: columns.done.list.map(item => item.blockId), - }; - updateOrderBlock(orderArray); - }, [debouncedData]); - - // * get 대시보드 블록 - const fetchBlockData = async (page: number = 0) => { - try { - // 완료된 블록의 진행률을 다시 받아옴 - const updatedDashboardDetail = await getPersonalDashboard(dashboardId); - setDashboardDetail(updatedDashboardDetail); // 진행률 업데이트 - } catch (error) { - console.error('Error updating progress', error); - } - }; - - const updateProgress = async () => { - try { - const [updatedDashboardDetail, updatedTeamDashboardDetail] = await Promise.all([ - getPersonalDashboard(dashboardId), - getTeamDashboard(dashboardId), - ]); - setDashboardDetail(updatedDashboardDetail); // 진행률 업데이트 - setTeamDashboardDetail(updatedTeamDashboardDetail); - } catch (error) { - console.error('Error updating progress', error); - } - }; - // 세로 무한 스크롤 감지 이벤트 - const handleLoadMore = async () => { - setPage(prevPage => prevPage + 1); - }; - - // 드래그 앤 드롭 핸들러 - const onDragEnd = async ({ source, destination }: DropResult) => { - if (!destination) return; - - const sourceKey = source.droppableId as TItemStatus; - const destinationKey = destination.droppableId as TItemStatus; - - const sourceList = columns[sourceKey]?.list || []; - const destinationList = columns[destinationKey]?.list || []; - - if (!columns[sourceKey] || !columns[destinationKey]) { - console.error('Invalid source or destination key'); - return; - } - - const blockId = sourceList[source.index]?.blockId; - if (!blockId) return; - - if (sourceKey === destinationKey) { - const newList = Array.from(sourceList); - const [movedItem] = newList.splice(source.index, 1); - newList.splice(destination.index, 0, movedItem); - - // ! 강제 렌더링 - flushSync(() => { - setColumns({ - ...columns, - [sourceKey]: { - ...columns[sourceKey], - list: newList, - }, - }); - }); - } else { - const [movedItem] = sourceList.splice(source.index, 1); - destinationList.splice(destination.index, 0, movedItem); - - if (destinationKey !== 'delete') { - await updatePersonalBlock(blockId, status(destinationKey)); // 블록 상태 업데이트 - } else { - await deleteBlock(blockId); // 블록 삭제 - } - - if (sourceKey === 'delete' && destinationKey !== 'delete') { - await deleteBlock(blockId); // 블록 복구 - } - - // 상태 업데이트 - flushSync(() => { - setColumns({ - ...columns, - [sourceKey]: { - ...columns[sourceKey], - list: sourceList, - }, - [destinationKey]: { - ...columns[destinationKey], - list: destinationList, - }, - }); - }); - - if ( - destinationKey === 'done' || - destinationKey === 'doing' || - destinationKey === 'todo' || - destinationKey === 'delete' - ) { - await updateProgress(); // 최신 진행률을 다시 받아와서 업데이트 - } - } - }; - - // 대시보드 상세 정보 가져오기 - const fetchDashboardData = async () => { - const personalData = await getPersonalDashboard(dashboardId); - const teamData = await getTeamDashboard(dashboardId); - - if (personalData) setDashboardDetail(personalData); - if (teamData) setTeamDashboardDetail(teamData); - }; - - // 유효한 데이터에 따라 mainTitle과 subTitle을 설정 - const mainTitle = teamDashboardDetail?.title || dashboardDetail?.title || '대시보드 제목'; - const subTitle = - teamDashboardDetail?.description || dashboardDetail?.description || '대시보드 설명'; - const blockProgress = - Math.round((teamDashboardDetail?.blockProgress || dashboardDetail?.blockProgress || 0) * 10) / - 10; - - useEffect(() => { - const orderArray = { - notStartedList: columns.todo.list.map(item => item.blockId), - inProgressList: columns.doing.list.map(item => item.blockId), - completedList: columns.done.list.map(item => item.blockId), - }; - updateOrderBlock(orderArray); - }, [debouncedData]); - - useEffect(() => { - // fetchTrigger 변경될 때 데이터를 다시 가져옴 - fetchData(0); - }, [fetchTrigger]); - - // 데이터 fetch 후 팀 또는 개인 대시보드 데이터를 설정 - useEffect(() => { - setPage(0); - fetchData(0); - fetchDashboardData(); //대시보드 헤더 상세 정보 가져오기 - }, [location.pathname, fetchData, page]); - - useEffect(() => { - fetchDashboardData(); // 대시보드 헤더 정보를 가져옴 - }, [fetchData]); - - useEffect(() => { - fetchData(page); - }, [page]); - - return ( - <> - - 끄적끄적 | '{mainTitle}' 대시보드 - - - - -
- - - {Object.values(columns).map(column => { - const { id, component: DashboardComponent, ...props } = column; - if (!DashboardComponent) { - return null; - } - return ( - - ); - })} - - {/* */} - - - - - ); -}; - -export default MainPage; - -// Helper 함수: status 변환을 위한 함수 -const status = (status: string) => { - switch (status) { - case 'todo': - return 'NOT_STARTED'; - case 'doing': - return 'IN_PROGRESS'; - case 'done': - return 'COMPLETED'; - case 'delete': - return 'DELETED'; - default: - return 'UNKNOWN'; - } -}; diff --git a/src/pages/MyPage.tsx b/src/pages/MyPage.tsx index fac3d2f..ccb9c37 100644 --- a/src/pages/MyPage.tsx +++ b/src/pages/MyPage.tsx @@ -150,7 +150,7 @@ const MyPage = () => { { - navigate(`/${dashboardId}`); + navigate(`/personal/${dashboardId}`); }} > @@ -186,7 +186,7 @@ const MyPage = () => { { - navigate(`/${dashboardId}`); + navigate(`/team/${dashboardId}`); }} >