From e25bad583f8c3e4d98b959305665861b71a1f253 Mon Sep 17 00:00:00 2001 From: evaain706 Date: Tue, 5 Aug 2025 05:24:11 +0900 Subject: [PATCH] =?UTF-8?q?[#156]=20fix:=20=EC=9D=BC=EC=A0=95=EA=B2=80?= =?UTF-8?q?=EC=A6=9D=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../[id]/hooks/useEditActivityForm.ts | 6 +++ .../myactivity/components/ScheduleSelect.tsx | 17 ++++--- .../components/ScheduleSelectForm.tsx | 48 +++++++++++++++---- .../myactivity/utils/dateValidatoin.ts | 31 ++++++++++++ 4 files changed, 83 insertions(+), 19 deletions(-) create mode 100644 src/app/(with-header)/myactivity/utils/dateValidatoin.ts diff --git a/src/app/(with-header)/myactivity/[id]/hooks/useEditActivityForm.ts b/src/app/(with-header)/myactivity/[id]/hooks/useEditActivityForm.ts index 7ed9ba5f..6290a048 100644 --- a/src/app/(with-header)/myactivity/[id]/hooks/useEditActivityForm.ts +++ b/src/app/(with-header)/myactivity/[id]/hooks/useEditActivityForm.ts @@ -9,6 +9,7 @@ import { ActivityDetailEdit, Schedule } from '@/types/activityDetailType'; import { AxiosError } from 'axios'; import { toast } from 'sonner'; import { notFound } from 'next/navigation'; +import { validateSchedules } from '../../utils/dateValidatoin'; interface SubImageType { id?: number; @@ -208,6 +209,11 @@ export const useEditActivityForm = () => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + const validationMessage = validateSchedules(dates); + if (validationMessage) { + toast.error(validationMessage); + return; + } try { await mutation.mutateAsync(); } catch (error) { diff --git a/src/app/(with-header)/myactivity/components/ScheduleSelect.tsx b/src/app/(with-header)/myactivity/components/ScheduleSelect.tsx index c00f2cdd..c64fd3f5 100644 --- a/src/app/(with-header)/myactivity/components/ScheduleSelect.tsx +++ b/src/app/(with-header)/myactivity/components/ScheduleSelect.tsx @@ -19,7 +19,6 @@ interface ScheduleSelectProps { export function ScheduleSelect({ index, isRemovable, - onRemove, onDateChange, onStartTimeChange, @@ -29,9 +28,9 @@ export function ScheduleSelect({ endTime, }: ScheduleSelectProps) { return ( -
-
-
+
+
+
-
+
-
+
{isRemovable && ( -
+
diff --git a/src/app/(with-header)/myactivity/components/ScheduleSelectForm.tsx b/src/app/(with-header)/myactivity/components/ScheduleSelectForm.tsx index 8115b04e..4e63ec11 100644 --- a/src/app/(with-header)/myactivity/components/ScheduleSelectForm.tsx +++ b/src/app/(with-header)/myactivity/components/ScheduleSelectForm.tsx @@ -1,5 +1,6 @@ 'use client'; +import { toast } from 'sonner'; import { ScheduleSelect } from './ScheduleSelect'; import { Schedule } from '@/types/activityDetailType'; @@ -14,6 +15,17 @@ interface ScheduleSelectFormProps { ) => void; } +function isPastDate(dateStr: string) { + const selected = new Date(dateStr); + const today = new Date(); + today.setHours(0, 0, 0, 0); + return selected < today; +} + +function isInvalidTimeRange(start: string, end: string) { + return start >= end; +} + export function ScheduleSelectForm({ dates, onAddDate, @@ -23,17 +35,18 @@ export function ScheduleSelectForm({ return (
-

예약 가능한 시간대

+

예약 가능한 시간대

+ {dates.map((dateSlot, idx) => (
1} onAddDate={onAddDate} onRemove={onRemoveDate} - onDateChange={(index, value) => onDateChange(index, 'date', value)} - onStartTimeChange={(index, value) => - onDateChange(index, 'startTime', value) - } - onEndTimeChange={(index, value) => - onDateChange(index, 'endTime', value) - } + onDateChange={(index, value) => { + if (isPastDate(value)) { + toast.error('오늘 이전 날짜는 선택할 수 없습니다.'); + return; + } + onDateChange(index, 'date', value); + }} + onStartTimeChange={(index, value) => { + const end = dates[index].endTime; + if (end && isInvalidTimeRange(value, end)) { + toast.error('시작 시간은 종료 시간보다 빨라야 합니다.'); + return; + } + onDateChange(index, 'startTime', value); + }} + onEndTimeChange={(index, value) => { + const start = dates[index].startTime; + if (start && isInvalidTimeRange(start, value)) { + toast.error('종료 시간은 시작 시간보다 늦어야 합니다.'); + return; + } + onDateChange(index, 'endTime', value); + }} date={dateSlot.date} startTime={dateSlot.startTime} endTime={dateSlot.endTime} />
))} -
); } diff --git a/src/app/(with-header)/myactivity/utils/dateValidatoin.ts b/src/app/(with-header)/myactivity/utils/dateValidatoin.ts new file mode 100644 index 00000000..6aa91ce6 --- /dev/null +++ b/src/app/(with-header)/myactivity/utils/dateValidatoin.ts @@ -0,0 +1,31 @@ +import { Schedule } from '@/types/activityDetailType'; + +export function validateSchedules(schedules: Schedule[]) { + const today = new Date(); + today.setHours(0, 0, 0, 0); + + for (let i = 0; i < schedules.length; i++) { + const { date, startTime, endTime } = schedules[i]; + + if (!date || !startTime || !endTime) { + return `날짜와 시간이 모두 입력되어야 합니다!`; + } + + const selectedDate = new Date(date); + if (selectedDate < today) { + return `오늘보다 이전 날짜는 선택할 수 없습니다!`; + } + + const [startHour, startMinute] = startTime.split(':').map(Number); + const [endHour, endMinute] = endTime.split(':').map(Number); + + const startMinutes = startHour * 60 + startMinute; + const endMinutes = endHour * 60 + endMinute; + + if (startMinutes >= endMinutes) { + return `종료 시간은 시작 시간보다 늦어야 합니다!`; + } + } + + return null; +}