Skip to content

Commit

Permalink
Feat: #109 프로젝트 목록 조회 기능 구현 (#110)
Browse files Browse the repository at this point in the history
* Feat: #109 프로젝트 목록 조회 API를 위한 React Query 처리 추가

* Feat: #109 프로젝트 목록 조회 API MSW 처리 추가

* Feat: #109 프로젝트 목록 사이드바 노출 기능 구현

* Feat: #109 네트워크 통신 대기시 Spinner UI 노출

* Chore: #109 Spinner UI 변경으로 다른 설정 코드 수정
  • Loading branch information
Seok93 authored Sep 5, 2024
1 parent bd4382a commit d5dba81
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 26 deletions.
6 changes: 5 additions & 1 deletion src/components/common/Spinner.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export default function Spinner() {
return <div className="box-border h-20 w-20 animate-spin rounded-full border-4 border-[#eee] border-t-main" />;
return (
<section className="flex h-full w-full items-center justify-center">
<div className="box-border h-20 w-20 animate-spin rounded-full border-4 border-[#eee] border-t-main" />
</section>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,7 @@ export default function ModalProjectStatusForm({ formId, project, statusId, onSu
reset(initialValue);
}, [initialValue, reset]);

if (isStatusLoading) {
return (
<section className="flex grow items-center justify-center">
<Spinner />
</section>
);
}
if (isStatusLoading) return <Spinner />;

return (
<form id={formId} className="mb-10 flex grow flex-col justify-center" onSubmit={handleSubmit(onSubmit)}>
Expand Down
8 changes: 1 addition & 7 deletions src/components/modal/task/ModalTaskForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,13 +170,7 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod
updateFiles(files);
};

if (isStatusLoading) {
return (
<section className="flex grow items-center justify-center">
<Spinner />
</section>
);
}
if (isStatusLoading) return <Spinner />;

return (
<form id={formId} className="mb-20 flex w-4/5 grow flex-col justify-center" onSubmit={handleSubmit(onSubmit)}>
Expand Down
22 changes: 22 additions & 0 deletions src/hooks/query/useProjectQuery.ts
Original file line number Diff line number Diff line change
@@ -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 };
}
19 changes: 12 additions & 7 deletions src/layouts/page/ProjectLayout.tsx
Original file line number Diff line number Diff line change
@@ -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 <Spinner />;

if (!project) return <Navigate to="/error" replace />;

return (
<>
<section className="flex h-full p-15">
<ListSidebar label="team" title="팀 이름...">
<ListProject data={PROJECT_DUMMY} targetId={projectId} />
<ListProject data={projectList} targetId={projectId} />
</ListSidebar>
<section className="flex grow flex-col border border-list bg-contents-box">
<header className="flex h-30 items-center justify-between border-b p-10">
Expand Down
13 changes: 12 additions & 1 deletion src/mocks/services/projectServiceHandler.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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;
21 changes: 18 additions & 3 deletions src/services/projectService.ts
Original file line number Diff line number Diff line change
@@ -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<AxiosResponse<UserWithRole[]>>}
*/
Expand All @@ -20,3 +22,16 @@ export async function findUserByProject(
) {
return authAxios.get<UserWithRole[]>(`project/${projectId}/user/search?nickname=${nickname}`, axiosConfig);
}

/**
* 프로젝트 목록 조회 API
*
* @export
* @async
* @param {Team['teamId']} teamId - 팀 ID
* @param {AxiosRequestConfig} axiosConfig - axios 요청 옵션 설정 객체
* @returns {Promise<AxiosResponse<Project[]>>}
*/
export async function getProjectList(teamId: Team['teamId'], axiosConfig: AxiosRequestConfig = {}) {
return authAxios.get<Project[]>(`team/${teamId}/project`, axiosConfig);
}

0 comments on commit d5dba81

Please sign in to comment.