From 6f4250df50b83e42e614a62c21773a8876ffd2db Mon Sep 17 00:00:00 2001 From: Suk Woo Date: Mon, 14 Oct 2024 23:25:52 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20#208=20PeriodDateInput=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=A2=85=EB=A3=8C=EC=9D=BC=20?= =?UTF-8?q?=ED=86=A0=EA=B8=80=20=EC=9E=90=EB=8F=99=20=ED=99=9C=EC=84=B1?= =?UTF-8?q?=ED=99=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(#209)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: #208 날짜 관련 Validation 로직 수정 * Feat: #208 프로젝트 목록 데이터 타입 변환 추가 * Feat: #208 PeriodDateInput 컴포넌트 종료일 토글 자동 활성화 기능 추가 * Chore: #208 PeriodDateInput Import 정리 * Chore: #208 오타 수정 (projctStartDate → projectStartDate) * Refactor: #208 시작일과 종료일 날짜 처리 과정 수정 --- src/components/common/PeriodDateInput.tsx | 48 +++++++++++++------ src/components/modal/task/ModalTaskForm.tsx | 10 ++-- src/components/modal/task/UpdateModalTask.tsx | 10 ++-- src/services/projectService.ts | 12 ++++- src/utils/Validator.ts | 11 +++-- 5 files changed, 60 insertions(+), 31 deletions(-) diff --git a/src/components/common/PeriodDateInput.tsx b/src/components/common/PeriodDateInput.tsx index 46dfdd5b..64daaba2 100644 --- a/src/components/common/PeriodDateInput.tsx +++ b/src/components/common/PeriodDateInput.tsx @@ -1,33 +1,36 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; +import { DateTime } from 'luxon'; import { useFormContext } from 'react-hook-form'; import { TASK_VALIDATION_RULES } from '@constants/formValidationRules'; import ToggleButton from '@components/common/ToggleButton'; +import useToast from '@hooks/useToast'; import type { FieldError } from 'react-hook-form'; import type { Project } from '@/types/ProjectType'; type PeriodDateInputProps = { - startDateLabel: string; - endDateLabel: string; startDateId: string; endDateId: string; - startDate: Project['startDate']; - endDate: Project['endDate']; + startDateLabel: string; + endDateLabel: string; startDateFieldName: string; endDateFieldName: string; + limitStartDate: Project['startDate']; + limitEndDate: Project['endDate']; }; export default function PeriodDateInput({ - startDateLabel, - endDateLabel, startDateId, endDateId, - startDate, - endDate, + startDateLabel, + endDateLabel, startDateFieldName, endDateFieldName, + limitStartDate, + limitEndDate, }: PeriodDateInputProps) { const [hasDeadline, setHasDeadline] = useState(false); + const { toastWarn } = useToast(); const { setValue, getValues, @@ -36,6 +39,14 @@ export default function PeriodDateInput({ register, formState: { errors }, } = useFormContext(); + const startDateStr = watch(startDateFieldName); + const endDateStr = watch(endDateFieldName); + + useEffect(() => { + const startDate = startDateStr ? DateTime.fromISO(startDateStr).startOf('day') : null; + const endDate = endDateStr ? DateTime.fromISO(endDateStr).startOf('day') : null; + if (startDate && endDate) setHasDeadline(startDate < endDate); + }, [startDateStr, endDateStr]); const handleDeadlineToggle = () => { setValue(endDateFieldName, getValues(startDateFieldName)); @@ -51,7 +62,7 @@ export default function PeriodDateInput({ id={startDateId} type="date" {...register(startDateFieldName, { - ...TASK_VALIDATION_RULES.START_DATE(startDate, endDate), + ...TASK_VALIDATION_RULES.START_DATE(limitStartDate, limitEndDate), onChange: (e) => { if (!hasDeadline) setValue(endDateFieldName, e.target.value); }, @@ -70,11 +81,18 @@ export default function PeriodDateInput({ id={endDateId} type="date" className={`${hasDeadline ? '' : '!bg-disable'}`} - disabled={!hasDeadline} - {...register( - endDateFieldName, - TASK_VALIDATION_RULES.END_DATE(hasDeadline, startDate, endDate, watch(startDateFieldName)), - )} + {...register(endDateFieldName, { + ...TASK_VALIDATION_RULES.END_DATE(hasDeadline, limitStartDate, limitEndDate, watch(startDateFieldName)), + onChange: (e) => { + const startDate = DateTime.fromISO(startDateStr).startOf('day'); + const endDate = DateTime.fromISO(e.target.value).startOf('day'); + if (startDate > endDate) { + toastWarn('종료일은 시작일과 같거나 이후로 설정해주세요.'); + setValue(endDateFieldName, startDateStr); + setHasDeadline(false); + } + }, + })} />
{(errors[endDateFieldName] as FieldError | undefined)?.message} diff --git a/src/components/modal/task/ModalTaskForm.tsx b/src/components/modal/task/ModalTaskForm.tsx index dfdb8404..9a56faa7 100644 --- a/src/components/modal/task/ModalTaskForm.tsx +++ b/src/components/modal/task/ModalTaskForm.tsx @@ -37,7 +37,7 @@ type ModalTaskFormProps = { // ToDo: React Query Error시 처리 추가할 것 export default function ModalTaskForm({ formId, project, taskId, onSubmit }: ModalTaskFormProps) { - const { projectId, startDate, endDate } = project; + const { projectId, startDate: projectStartDate, endDate: projectEndDate } = project; const [keyword, setKeyword] = useState(''); const [assignees, setAssignees] = useState([]); @@ -153,14 +153,14 @@ export default function ModalTaskForm({ formId, project, taskId, onSubmit }: Mod />
diff --git a/src/components/modal/task/UpdateModalTask.tsx b/src/components/modal/task/UpdateModalTask.tsx index d6bf6eac..7fea9e67 100644 --- a/src/components/modal/task/UpdateModalTask.tsx +++ b/src/components/modal/task/UpdateModalTask.tsx @@ -49,7 +49,7 @@ export default function UpdateModalTask({ onClose: handleClose, }: UpdateModalTaskProps) { const updateTaskFormId = 'updateTaskForm'; - const { projectId, startDate, endDate } = project; + const { projectId, startDate: projectStartDate, endDate: projectEndDate } = project; const [keyword, setKeyword] = useState(''); const { toastInfo, toastWarn } = useToast(); @@ -164,14 +164,14 @@ export default function UpdateModalTask({ /> diff --git a/src/services/projectService.ts b/src/services/projectService.ts index a0107e31..9d469043 100644 --- a/src/services/projectService.ts +++ b/src/services/projectService.ts @@ -33,7 +33,17 @@ export async function findUserByProject( * @returns {Promise>} */ export async function getProjectList(teamId: Team['teamId'], axiosConfig: AxiosRequestConfig = {}) { - return authAxios.get(`/team/${teamId}/project`, axiosConfig); + return authAxios.get(`/team/${teamId}/project`, { + transformResponse: (data) => { + const parsedData: Project[] = JSON.parse(data); + return parsedData.map((data) => { + data.startDate = data.startDate && new Date(data.startDate); + data.endDate = data.endDate && new Date(data.endDate); + return data; + }); + }, + ...axiosConfig, + }); } /** diff --git a/src/utils/Validator.ts b/src/utils/Validator.ts index 3f57999b..74534798 100644 --- a/src/utils/Validator.ts +++ b/src/utils/Validator.ts @@ -16,15 +16,16 @@ export default class Validator { } public static isWithinDateRange(start: Date | string, end: Date | string, target: Date | string) { - const startDate = DateTime.fromJSDate(new Date(start)); - const endDate = DateTime.fromJSDate(new Date(end)); - const targetDate = DateTime.fromJSDate(new Date(target)); + const startDate = DateTime.fromJSDate(new Date(start)).startOf('day'); + const endDate = DateTime.fromJSDate(new Date(end)).startOf('day'); + const targetDate = DateTime.fromJSDate(new Date(target)).startOf('day'); return targetDate >= startDate && targetDate < endDate; } public static isEarlierStartDate(start: Date | string, end: Date | string) { - const startDate = DateTime.fromJSDate(new Date(start)); - const endDate = DateTime.fromJSDate(new Date(end)); + const startDate = DateTime.fromJSDate(new Date(start)).startOf('day'); + const endDate = DateTime.fromJSDate(new Date(end)).startOf('day'); + console.log(startDate.toISODate(), endDate.toISODate()); return startDate <= endDate; }