diff --git a/src/components/dateCourse/dateCourseSearchFilterOption.tsx b/src/components/dateCourse/dateCourseSearchFilterOption.tsx index d8fd29f..98780a2 100644 --- a/src/components/dateCourse/dateCourseSearchFilterOption.tsx +++ b/src/components/dateCourse/dateCourseSearchFilterOption.tsx @@ -179,7 +179,7 @@ export default function DateCourseSearchFilterOption({ onChange={(e) => { const val = e.target.value; setDate(val); - if (time) onChange(`${val}T${time}:00`); + if (time) onChange(`${val}T${time}:00.000Z`); }} className="absolute top-0 left-0 w-full h-full opacity-0" /> diff --git a/src/components/dateCourse/keywordButton.tsx b/src/components/dateCourse/keywordButton.tsx index 47613ef..8dec41f 100644 --- a/src/components/dateCourse/keywordButton.tsx +++ b/src/components/dateCourse/keywordButton.tsx @@ -4,7 +4,7 @@ export default function KeywordButton({ tag, selected = false, onClick, isButton return (
-
- {signatureDish && ( -
-
-
WithTime Pick
+ {signatureDish && ( +
+
+
+
WithTime Pick
- )} - {image && } -
-
+ + {image && } +
+ )} +
{roadNameAddress} diff --git a/src/components/modal/dateCourseSearchFilterModal.tsx b/src/components/modal/dateCourseSearchFilterModal.tsx index c8b97ea..0af74ec 100644 --- a/src/components/modal/dateCourseSearchFilterModal.tsx +++ b/src/components/modal/dateCourseSearchFilterModal.tsx @@ -2,6 +2,7 @@ import { useMemo } from 'react'; import { Navigate, useLocation } from 'react-router-dom'; import ClipLoader from 'react-spinners/ClipLoader'; +import type { IQuestion } from '@/types/dateCourse/dateCourse'; import { DateCourseQuestion } from '@/constants/dateCourseQuestion'; import { @@ -64,6 +65,13 @@ function computeErrors(f: { budget: any; dateDurationTime: any; mealTypes?: any[ return e; } +const TOTAL_QUESTIONS = 8; +const Questions: IQuestion[] = Array.isArray(DateCourseQuestion) + ? DateCourseQuestion.slice(0, TOTAL_QUESTIONS - 1).map((q) => ({ + ...q, + type: q.type as IQuestion['type'], + })) + : []; export default function DateCourseSearchFilterModal({ onClose }: TProps) { const location = useLocation(); @@ -89,36 +97,41 @@ export default function DateCourseSearchFilterModal({ onClose }: TProps) { const data = isBookmarked ? bookmarkedData : courseData; - const Questions = useMemo( - () => - (Array.isArray(DateCourseQuestion) ? DateCourseQuestion.slice(0, 7) : []) - .map((q) => ({ - ...q, - type: q.type as 'choice' | 'search' | 'time' | 'choices' | 'keyword', - })) - .filter((q) => q.filterTitle !== ''), - [], - ); + const stepFieldMap = { + 0: 'budget', + 1: 'datePlaces', + 2: 'dateDurationTime', + 3: 'mealTypes', + 4: 'transportation', + 5: 'userPreferredKeywords', + 6: 'startTime', + } as const; + + const fieldValues = { + budget, + datePlaces, + dateDurationTime, + mealTypes, + transportation, + userPreferredKeywords, + startTime, + }; + + const valueByStep = (idx: number): string | string[] | null => { + const step = idx; // ★ 중요 + const fieldName = stepFieldMap[step as keyof typeof stepFieldMap]; + return fieldName ? fieldValues[fieldName] : null; + }; - const valueByIndex = (idx: number) => { - switch (idx) { - case 0: - return budget; - case 1: - return datePlaces; - case 2: - return dateDurationTime; - case 3: - return mealTypes; - case 4: - return transportation; - case 5: - return startTime; - case 6: - return userPreferredKeywords; - default: - return null; + const updateByStep = (idx: number, v: any) => { + const step = idx; // ★ 중요 + const fieldName = stepFieldMap[step as keyof typeof stepFieldMap]; + if (!fieldName) return; + + if ([1, 3, 5].includes(step) && !Array.isArray(v)) { + v = []; } + setField(fieldName, v ?? null); }; const errorMessages = useMemo( @@ -133,35 +146,6 @@ export default function DateCourseSearchFilterModal({ onClose }: TProps) { [budget, dateDurationTime, mealTypes, userPreferredKeywords, startTime], ); - const updateByIndex = (idx: number, raw: any) => { - let v = raw; - if ([1, 3, 6].includes(idx) && !Array.isArray(v)) v = []; - - switch (idx) { - case 0: - setField('budget', v ?? null); - break; - case 1: - setField('datePlaces', v); - break; - case 2: - setField('dateDurationTime', v ?? null); - break; - case 3: - setField('mealTypes', v); - break; - case 4: - setField('transportation', v ?? null); - break; - case 5: - setField('startTime', v ?? null); - break; - case 6: - setField('userPreferredKeywords', v); - break; - } - }; - if (bookmarkDataError || courseDataError) { return ; } @@ -172,14 +156,15 @@ export default function DateCourseSearchFilterModal({ onClose }: TProps) { {Questions.map((q, idx) => ( updateByIndex(idx, v)} + value={valueByStep(idx)} + onChange={(v) => updateByStep(idx, v)} type={q.type} apiRequestValue={q.apiRequestValue} errorMessage={errorMessages[idx] ?? ''} + autoInit={q.type === 'time' && idx === 7} /> ))} diff --git a/src/utils/dateCourseValidation.tsx b/src/utils/dateCourseValidation.tsx index e718fc1..27b3eef 100644 --- a/src/utils/dateCourseValidation.tsx +++ b/src/utils/dateCourseValidation.tsx @@ -26,17 +26,15 @@ const keywordGroups: Record = { export const mealKeyword = ['양식', '한식', '중식', '이자카야/펍', '퓨전 음식점', '브런치 카페', '디저트 카페', '루프탑 카페']; export function MealTimeValidation({ meal, time, totalTime }: { meal: string[]; time: string; totalTime: string }): string | null { - if (!time || meal.length === 0 || !totalTime) return null; + if (!time || meal.length === 0 || !totalTime || time == null) return null; const timeStr = time.split('T')[1]; // 'HH:mm' const toMinutes = (t: string) => { const [h, m] = t.split(':').map(Number); return h * 60 + m; }; - const start = toMinutes(timeStr); // 데이트 시작 시간 const duration = timeMap[totalTime]; // 소요 시간(시간 단위) const end = start + duration * 60; // 종료 시간 (분) - const isOverlapping = (start1: number, end1: number, start2: number, end2: number) => { if (start1 < start2) { // 데이트 시작시간이 식사 시작시간보다 빠를 경우 @@ -50,13 +48,10 @@ export function MealTimeValidation({ meal, time, totalTime }: { meal: string[]; return false; } }; - // 선택한 식사 중 하나라도 겹치면 통과 for (const m of meal) { const mealRange = mealTimeRanges[m]; - if (!mealRange) continue; // 존재하지 않으면 skip - const [mealStartStr, mealEndStr] = mealRange; const mealStart = toMinutes(mealStartStr); const mealEnd = toMinutes(mealEndStr); @@ -64,11 +59,9 @@ export function MealTimeValidation({ meal, time, totalTime }: { meal: string[]; return null; } } - // 아무것도 안겹치면 첫 번째 식사를 기준으로 안내 const first = meal[0]; const [mealStartStr, mealEndStr] = mealTimeRanges[first]; - return `선택하신 시간에는 ${mealTimeKorean[first]} 식사를 하기 어려워요. (가능 시간: ${mealStartStr}~${mealEndStr})`; } @@ -78,7 +71,7 @@ type TDateTimeStartValidationInput = { }; export function DateTimeStartValidation({ totalTime, time }: TDateTimeStartValidationInput): string | null { - if (!totalTime || !time) return null; + if (!totalTime || !time || time == null) return null; const duration = timeMap[totalTime]; if (duration == null) return null; const timeStr = time.split('T')[1];