From d5dba81d02f5ba8acd81a66abdc632297bafe7aa Mon Sep 17 00:00:00 2001 From: Suk Woo Date: Thu, 5 Sep 2024 16:57:27 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20#109=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84=20(#110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: #109 프로젝트 목록 조회 API를 위한 React Query 처리 추가 * Feat: #109 프로젝트 목록 조회 API MSW 처리 추가 * Feat: #109 프로젝트 목록 사이드바 노출 기능 구현 * Feat: #109 네트워크 통신 대기시 Spinner UI 노출 * Chore: #109 Spinner UI 변경으로 다른 설정 코드 수정 --- src/components/common/Spinner.tsx | 6 ++++- .../project-status/ModalProjectStatusForm.tsx | 8 +------ src/components/modal/task/ModalTaskForm.tsx | 8 +------ src/hooks/query/useProjectQuery.ts | 22 +++++++++++++++++++ src/layouts/page/ProjectLayout.tsx | 19 ++++++++++------ src/mocks/services/projectServiceHandler.ts | 13 ++++++++++- src/services/projectService.ts | 21 +++++++++++++++--- 7 files changed, 71 insertions(+), 26 deletions(-) create mode 100644 src/hooks/query/useProjectQuery.ts diff --git a/src/components/common/Spinner.tsx b/src/components/common/Spinner.tsx index 9770f344..818956dd 100644 --- a/src/components/common/Spinner.tsx +++ b/src/components/common/Spinner.tsx @@ -1,3 +1,7 @@ export default function Spinner() { - return
; + return ( +
+
+
+ ); } diff --git a/src/components/modal/project-status/ModalProjectStatusForm.tsx b/src/components/modal/project-status/ModalProjectStatusForm.tsx index f80c1d7f..7c2393a9 100644 --- a/src/components/modal/project-status/ModalProjectStatusForm.tsx +++ b/src/components/modal/project-status/ModalProjectStatusForm.tsx @@ -38,13 +38,7 @@ export default function ModalProjectStatusForm({ formId, project, statusId, onSu reset(initialValue); }, [initialValue, reset]); - if (isStatusLoading) { - return ( -
- -
- ); - } + if (isStatusLoading) return ; return (
diff --git a/src/components/modal/task/ModalTaskForm.tsx b/src/components/modal/task/ModalTaskForm.tsx index 3270885d..74d04d48 100644 --- a/src/components/modal/task/ModalTaskForm.tsx +++ b/src/components/modal/task/ModalTaskForm.tsx @@ -170,13 +170,7 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod updateFiles(files); }; - if (isStatusLoading) { - return ( -
- -
- ); - } + if (isStatusLoading) return ; return ( diff --git a/src/hooks/query/useProjectQuery.ts b/src/hooks/query/useProjectQuery.ts new file mode 100644 index 00000000..e2893ff2 --- /dev/null +++ b/src/hooks/query/useProjectQuery.ts @@ -0,0 +1,22 @@ +import { useQuery } from '@tanstack/react-query'; +import { getProjectList } from '@services/projectService'; + +import type { Team } from '@/types/TeamType'; + +// Todo: Project Query CUD로직 작성하기 +export default function useReadProjects(teamId: Team['teamId']) { + const { + data: projectList = [], + isLoading: isProjectLoading, + isError: isProjectError, + error: projectError, + } = useQuery({ + queryKey: ['teams', teamId, 'projects'], + queryFn: async () => { + const { data } = await getProjectList(teamId); + return data; + }, + }); + + return { projectList, isProjectLoading, isProjectError, projectError }; +} diff --git a/src/layouts/page/ProjectLayout.tsx b/src/layouts/page/ProjectLayout.tsx index 00e94da9..a23a19d1 100644 --- a/src/layouts/page/ProjectLayout.tsx +++ b/src/layouts/page/ProjectLayout.tsx @@ -1,30 +1,35 @@ import { useMemo } from 'react'; import { Navigate, NavLink, Outlet, useParams } from 'react-router-dom'; +import { RiSettings5Fill } from 'react-icons/ri'; +import { ProjectContext } from '@hooks/useProjectContext'; import useModal from '@hooks/useModal'; +import useReadProjects from '@hooks/query/useProjectQuery'; +import Spinner from '@components/common/Spinner'; import ListSidebar from '@components/sidebar/ListSidebar'; import ListProject from '@components/sidebar/ListProject'; import CreateModalTask from '@components/modal/task/CreateModalTask'; import CreateModalProjectStatus from '@components/modal/project-status/CreateModalProjectStatus'; -import { ProjectContext } from '@hooks/useProjectContext'; -import { PROJECT_DUMMY } from '@mocks/mockData'; -import { RiSettings5Fill } from 'react-icons/ri'; export default function ProjectLayout() { - const { projectId } = useParams(); + const { teamId, projectId } = useParams(); + const { projectList, isProjectLoading } = useReadProjects(Number(teamId)); const { showModal: showTaskModal, openModal: openTaskModal, closeModal: closeTaskModal } = useModal(); const { showModal: showStatusModal, openModal: openStatusModal, closeModal: closeStatusModal } = useModal(); + const project = useMemo( - () => PROJECT_DUMMY.find((project) => project.projectId.toString() === projectId), - [projectId], + () => projectList?.find((project) => project.projectId.toString() === projectId), + [projectList, projectId], ); + if (isProjectLoading) return ; + if (!project) return ; return ( <>
- +
diff --git a/src/mocks/services/projectServiceHandler.ts b/src/mocks/services/projectServiceHandler.ts index 333a6e0f..f8277183 100644 --- a/src/mocks/services/projectServiceHandler.ts +++ b/src/mocks/services/projectServiceHandler.ts @@ -1,5 +1,5 @@ import { http, HttpResponse } from 'msw'; -import { PROJECT_USER_DUMMY, ROLE_DUMMY, USER_DUMMY } from '@mocks/mockData'; +import { PROJECT_DUMMY, PROJECT_USER_DUMMY, ROLE_DUMMY, USER_DUMMY } from '@mocks/mockData'; import type { Role } from '@/types/RoleType'; import type { User } from '@/types/UserType'; @@ -35,6 +35,17 @@ const projectServiceHandler = [ return HttpResponse.json(matchedUserList); }), + // 프로젝트 목록 조회 API + http.get(`${BASE_URL}/team/:teamId/project`, ({ request, params }) => { + const accessToken = request.headers.get('Authorization'); + const { teamId } = params; + + if (!accessToken) return new HttpResponse(null, { status: 401 }); + + const projectList = PROJECT_DUMMY.filter((project) => project.teamId === Number(teamId)); + + return HttpResponse.json(projectList); + }), ]; export default projectServiceHandler; diff --git a/src/services/projectService.ts b/src/services/projectService.ts index b89aa788..402b99b6 100644 --- a/src/services/projectService.ts +++ b/src/services/projectService.ts @@ -1,15 +1,17 @@ import { authAxios } from '@services/axiosProvider'; + import type { AxiosRequestConfig } from 'axios'; -import type { User, UserWithRole } from '@/types/UserType'; +import type { Team } from '@/types/TeamType'; import type { Project } from '@/types/ProjectType'; +import type { User, UserWithRole } from '@/types/UserType'; /** * 프로젝트에 속한 유저 목록을 검색하는 API * * @export * @async - * @param {Project['projectId']} projectId - 프로젝트 아이디 - * @param {User['nickname']} nickname - 유저 닉네임 + * @param {Project['projectId']} projectId - 프로젝트 ID + * @param {User['nickname']} nickname - 유저 닉네임 * @param {AxiosRequestConfig} [axiosConfig={}] - axios 요청 옵션 설정 객체 * @returns {Promise>} */ @@ -20,3 +22,16 @@ export async function findUserByProject( ) { return authAxios.get(`project/${projectId}/user/search?nickname=${nickname}`, axiosConfig); } + +/** + * 프로젝트 목록 조회 API + * + * @export + * @async + * @param {Team['teamId']} teamId - 팀 ID + * @param {AxiosRequestConfig} axiosConfig - axios 요청 옵션 설정 객체 + * @returns {Promise>} + */ +export async function getProjectList(teamId: Team['teamId'], axiosConfig: AxiosRequestConfig = {}) { + return authAxios.get(`team/${teamId}/project`, axiosConfig); +}