From 54dd298117ebb37c649ccd40b59017736deba0ab Mon Sep 17 00:00:00 2001 From: Suk Woo Date: Thu, 26 Sep 2024 14:31:37 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20#156=20=EC=9D=BC=EC=A0=95=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=AA=A8=EB=8B=AC=20=EC=9D=BC=EC=A0=95=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EB=B3=80=EA=B2=BD=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: #156 일정 정보 수정 API를 위한 React Query 기능 추가 * Feat: #156 일정 정보 수정 API를 위한 MSW 기능 추가 * Feat: #156 일정 정보 수정 기능 구현 --- src/components/modal/task/UpdateModalTask.tsx | 8 ++--- src/hooks/query/useTaskQuery.ts | 23 +++++++++++++-- src/mocks/services/taskServiceHandler.ts | 29 ++++++++++++++++++- src/services/taskService.ts | 22 +++++++++++++- 4 files changed, 74 insertions(+), 8 deletions(-) diff --git a/src/components/modal/task/UpdateModalTask.tsx b/src/components/modal/task/UpdateModalTask.tsx index 3941972a..de40dde7 100644 --- a/src/components/modal/task/UpdateModalTask.tsx +++ b/src/components/modal/task/UpdateModalTask.tsx @@ -22,6 +22,7 @@ import { useReadAssignees, useReadStatusTasks, useReadTaskFiles, + useUpdateTaskInfo, } from '@hooks/query/useTaskQuery'; import { useReadProjectUserRoleList } from '@hooks/query/useProjectQuery'; import { findUserByProject } from '@services/projectService'; @@ -55,6 +56,7 @@ export default function UpdateModalTask({ project, taskId, onClose: handleClose const { assigneeList, isAssigneeLoading } = useReadAssignees(projectId, taskId); const { taskFileList, isTaskFileLoading } = useReadTaskFiles(projectId, taskId); + const { mutate: updateTaskInfoMutate } = useUpdateTaskInfo(projectId, taskId); const { mutate: addAssigneeMutate } = useAddAssignee(projectId, taskId); const { mutate: deleteAssigneeMutate } = useDeleteAssignee(projectId, taskId); @@ -128,10 +130,8 @@ export default function UpdateModalTask({ project, taskId, onClose: handleClose return ; } - // ToDo: 일정 수정 API 작업시 추가할 것 - const handleFormSubmit: SubmitHandler = async (data) => { - console.log('수정 폼 제출'); - console.log(data); + const handleFormSubmit: SubmitHandler = async (formData) => { + updateTaskInfoMutate(formData); handleClose(); }; return ( diff --git a/src/hooks/query/useTaskQuery.ts b/src/hooks/query/useTaskQuery.ts index a18fe70c..52c979a5 100644 --- a/src/hooks/query/useTaskQuery.ts +++ b/src/hooks/query/useTaskQuery.ts @@ -7,13 +7,14 @@ import { findAssignees, findTaskFiles, findTaskList, + updateTaskInfo, updateTaskOrder, } from '@services/taskService'; import useToast from '@hooks/useToast'; import type { User } from '@/types/UserType'; import type { Project } from '@/types/ProjectType'; -import type { Task, TaskCreationForm, TaskListWithStatus, TaskOrder } from '@/types/TaskType'; +import type { Task, TaskCreationForm, TaskInfoForm, TaskListWithStatus, TaskOrder } from '@/types/TaskType'; function getTaskNameList(taskList: TaskListWithStatus[], excludedTaskName?: Task['name']) { const taskNameList = taskList @@ -24,7 +25,7 @@ function getTaskNameList(taskList: TaskListWithStatus[], excludedTaskName?: Task return excludedTaskName ? taskNameList.filter((taskName) => taskName !== excludedTaskName) : taskNameList; } -// Todo: Task Query UD로직 작성하기 +// Todo: Task Query D로직 작성하기 // 일정 생성 export function useCreateStatusTask(projectId: Project['projectId']) { const { toastSuccess } = useToast(); @@ -138,6 +139,24 @@ export function useReadTaskFiles(projectId: Project['projectId'], taskId: Task[' return { taskFileList, isTaskFileLoading, taskFileError, isTaskFileError }; } +// 일정 정보 수정 +export function useUpdateTaskInfo(projectId: Project['projectId'], taskId: Task['taskId']) { + const { toastError, toastSuccess } = useToast(); + const queryClient = useQueryClient(); + const queryKey = ['projects', projectId, 'tasks']; + + const mutation = useMutation({ + mutationFn: (formData: TaskInfoForm) => updateTaskInfo(projectId, taskId, formData), + onError: () => toastError('일정 정보 수정에 실패 했습니다. 잠시후 다시 시도해주세요.'), + onSuccess: () => { + toastSuccess('일정 정보를 수정 했습니다.'); + queryClient.invalidateQueries({ queryKey }); + }, + }); + + return mutation; +} + // 일정 수행자 추가 export function useAddAssignee(projectId: Project['projectId'], taskId: Task['taskId']) { const { toastError, toastSuccess } = useToast(); diff --git a/src/mocks/services/taskServiceHandler.ts b/src/mocks/services/taskServiceHandler.ts index cb018ec6..bddc3afe 100644 --- a/src/mocks/services/taskServiceHandler.ts +++ b/src/mocks/services/taskServiceHandler.ts @@ -11,7 +11,7 @@ import { import { getRoleHash, getStatusHash, getTaskHash, getUserHash } from '@mocks/mockHash'; import type { UserWithRole } from '@/types/UserType'; -import type { TaskAssigneeForm, TaskCreationForm, TaskOrderForm } from '@/types/TaskType'; +import type { TaskAssigneeForm, TaskCreationForm, TaskInfoForm, TaskOrderForm } from '@/types/TaskType'; const BASE_URL = import.meta.env.VITE_BASE_URL; @@ -122,6 +122,33 @@ const taskServiceHandler = [ if (fileList.length === 0) return HttpResponse.json([]); return HttpResponse.json(fileList.length === 0 ? [] : fileList); }), + // 일정 정보 수정 API + http.patch(`${BASE_URL}/project/:projectId/task/:taskId`, async ({ request, params }) => { + const accessToken = request.headers.get('Authorization'); + const { projectId, taskId } = params; + const taskInfoData = (await request.json()) as TaskInfoForm; + taskInfoData.statusId = Number(taskInfoData.statusId); + + if (!accessToken) return new HttpResponse(null, { status: 401 }); + + // ToDo: JWT의 userId 정보를 가져와 프로젝트 권한 확인이 필요. + const project = PROJECT_DUMMY.find((project) => project.projectId === Number(projectId)); + if (!project) return new HttpResponse(null, { status: 404 }); + + const statuses = STATUS_DUMMY.filter((status) => status.projectId === project.projectId); + if (statuses.length === 0) return new HttpResponse(null, { status: 404 }); + + const task = TASK_DUMMY.find((task) => task.taskId === Number(taskId)); + if (!task) return new HttpResponse(null, { status: 404 }); + + const isIncludedTask = statuses.map((status) => status.statusId).includes(task.statusId); + if (!isIncludedTask) return new HttpResponse(null, { status: 404 }); + + const index = TASK_DUMMY.findIndex((task) => task.taskId === Number(taskId)); + if (index !== -1) TASK_DUMMY[index] = { ...TASK_DUMMY[index], ...taskInfoData }; + + return new HttpResponse(null, { status: 200 }); + }), // 일정 수행자 추가 API http.post(`${BASE_URL}/project/:projectId/task/:taskId/assignee`, async ({ request, params }) => { const accessToken = request.headers.get('Authorization'); diff --git a/src/services/taskService.ts b/src/services/taskService.ts index 76b21f0a..9399c8fd 100644 --- a/src/services/taskService.ts +++ b/src/services/taskService.ts @@ -4,7 +4,7 @@ import type { AxiosRequestConfig } from 'axios'; import type { TaskFile } from '@/types/FileType'; import type { Project } from '@/types/ProjectType'; import type { User, UserWithRole } from '@/types/UserType'; -import type { Task, TaskCreationForm, TaskListWithStatus, TaskOrderForm } from '@/types/TaskType'; +import type { Task, TaskCreationForm, TaskInfoForm, TaskListWithStatus, TaskOrderForm } from '@/types/TaskType'; /** * 프로젝트에 속한 모든 일정 목록 조회 API @@ -90,6 +90,26 @@ export async function findTaskFiles( return authAxios.get(`/project/${projectId}/task/${taskId}/attachment`, axiosConfig); } +/** + * 일정 정보 수정 API + * + * @export + * @async + * @param {Project['projectId']} projectId - 프로젝트 ID + * @param {Task['taskId']} taskId - 일정 ID + * @param {TaskInfoForm} formData - 일정 수정 정보 객체 + * @param {AxiosRequestConfig} [axiosConfig={}] - axios 요청 옵션 설정 객체 + * @returns {Promise>} + */ +export async function updateTaskInfo( + projectId: Project['projectId'], + taskId: Task['taskId'], + formData: TaskInfoForm, + axiosConfig: AxiosRequestConfig = {}, +) { + return authAxios.patch(`/project/${projectId}/task/${taskId}`, formData, axiosConfig); +} + /** * 일정 수행자 추가 API *