Skip to content

Commit 503e0fe

Browse files
authored
Merge pull request #124 from WithTime12/feature/#123
[Feature] 반응형 수정
2 parents ec5afc6 + ffad20c commit 503e0fe

5 files changed

Lines changed: 59 additions & 80 deletions

File tree

src/components/dateCourse/dateCourseSearchFilterOption.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ export default function DateCourseSearchFilterOption({
179179
onChange={(e) => {
180180
const val = e.target.value;
181181
setDate(val);
182-
if (time) onChange(`${val}T${time}:00`);
182+
if (time) onChange(`${val}T${time}:00.000Z`);
183183
}}
184184
className="absolute top-0 left-0 w-full h-full opacity-0"
185185
/>

src/components/dateCourse/keywordButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export default function KeywordButton({ tag, selected = false, onClick, isButton
44
return (
55
<div
66
onClick={isButton ? onClick : undefined}
7-
className={`flex flex-nowrap rounding-32 px-[16px] py-[8px] font-body2 min-w-fit w-fit select-none
7+
className={`flex whitespace-nowrap rounding-32 px-[16px] py-[8px] font-body2 min-w-fit w-fit select-none
88
${selected ? 'bg-primary-700 text-white' : 'bg-default-gray-400 text-default-gray-700'}
99
${isButton && 'cursor-pointer'}
1010
`}

src/components/dateCourse/timeline.tsx

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,20 @@ function Timeline({ end = false, image, name, placeCategoryResponseList, roadNam
3737

3838
{open && (
3939
<div className="flex lg:items-start self-stretch w-full gap-[9px] flex-col lg:flex-row items-center">
40-
<div className="flex flex-col gap-[8px] lg:w-[50%] max-h-fit justify-around items-start">
41-
{signatureDish && (
42-
<div className="flex w-full flex-col ">
43-
<div className="flex gap-[8px] font-body1 select-none text-center items-center h-[24px] w-full">
44-
<div className="flex text-center h-full">WithTime Pick</div>
40+
{signatureDish && (
41+
<div className="flex flex-col gap-[8px] lg:w-[50%] w-full max-h-fit justify-around items-start">
42+
<div className="flex w-full flex-row self-start lg:flex-col h-fit lg:gap-0 gap-[8px]">
43+
<div className="flex gap-[8px] font-body1 select-none text-center items-center h-[35.6px] lg:w-full">
44+
<div className="flex text-center h-full items-center">WithTime Pick</div>
4545
<CheckSuccess stroke={'#000000'} />
4646
</div>
4747
<KeywordButton tag={signatureDish.name || ''} />
4848
</div>
49-
)}
50-
{image && <img src={image} className="w-[80%] self-start" />}
51-
</div>
52-
<div className="flex flex-col h-full lg:w-[50%] gap-[16px]">
49+
50+
{image && <img src={image} className="w-[80%] self-center lg:self-start" />}
51+
</div>
52+
)}
53+
<div className={`flex flex-col h-full gap-[16px] self-start ${signatureDish == null ? 'w-full' : 'lg:w-[50%]'}`}>
5354
<div className="flex gap-[16px] font-body2 text-default-gray-800 break-keep lg:items-start items-center">
5455
<Location stroke="#000000" className="min-w-[24px]" />
5556
{roadNameAddress}

src/components/modal/dateCourseSearchFilterModal.tsx

Lines changed: 45 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { useMemo } from 'react';
22
import { Navigate, useLocation } from 'react-router-dom';
33
import ClipLoader from 'react-spinners/ClipLoader';
44

5+
import type { IQuestion } from '@/types/dateCourse/dateCourse';
56
import { DateCourseQuestion } from '@/constants/dateCourseQuestion';
67

78
import {
@@ -64,6 +65,13 @@ function computeErrors(f: { budget: any; dateDurationTime: any; mealTypes?: any[
6465

6566
return e;
6667
}
68+
const TOTAL_QUESTIONS = 8;
69+
const Questions: IQuestion[] = Array.isArray(DateCourseQuestion)
70+
? DateCourseQuestion.slice(0, TOTAL_QUESTIONS - 1).map((q) => ({
71+
...q,
72+
type: q.type as IQuestion['type'],
73+
}))
74+
: [];
6775

6876
export default function DateCourseSearchFilterModal({ onClose }: TProps) {
6977
const location = useLocation();
@@ -89,36 +97,41 @@ export default function DateCourseSearchFilterModal({ onClose }: TProps) {
8997

9098
const data = isBookmarked ? bookmarkedData : courseData;
9199

92-
const Questions = useMemo(
93-
() =>
94-
(Array.isArray(DateCourseQuestion) ? DateCourseQuestion.slice(0, 7) : [])
95-
.map((q) => ({
96-
...q,
97-
type: q.type as 'choice' | 'search' | 'time' | 'choices' | 'keyword',
98-
}))
99-
.filter((q) => q.filterTitle !== ''),
100-
[],
101-
);
100+
const stepFieldMap = {
101+
0: 'budget',
102+
1: 'datePlaces',
103+
2: 'dateDurationTime',
104+
3: 'mealTypes',
105+
4: 'transportation',
106+
5: 'userPreferredKeywords',
107+
6: 'startTime',
108+
} as const;
109+
110+
const fieldValues = {
111+
budget,
112+
datePlaces,
113+
dateDurationTime,
114+
mealTypes,
115+
transportation,
116+
userPreferredKeywords,
117+
startTime,
118+
};
119+
120+
const valueByStep = (idx: number): string | string[] | null => {
121+
const step = idx; // ★ 중요
122+
const fieldName = stepFieldMap[step as keyof typeof stepFieldMap];
123+
return fieldName ? fieldValues[fieldName] : null;
124+
};
102125

103-
const valueByIndex = (idx: number) => {
104-
switch (idx) {
105-
case 0:
106-
return budget;
107-
case 1:
108-
return datePlaces;
109-
case 2:
110-
return dateDurationTime;
111-
case 3:
112-
return mealTypes;
113-
case 4:
114-
return transportation;
115-
case 5:
116-
return startTime;
117-
case 6:
118-
return userPreferredKeywords;
119-
default:
120-
return null;
126+
const updateByStep = (idx: number, v: any) => {
127+
const step = idx; // ★ 중요
128+
const fieldName = stepFieldMap[step as keyof typeof stepFieldMap];
129+
if (!fieldName) return;
130+
131+
if ([1, 3, 5].includes(step) && !Array.isArray(v)) {
132+
v = [];
121133
}
134+
setField(fieldName, v ?? null);
122135
};
123136

124137
const errorMessages = useMemo(
@@ -133,35 +146,6 @@ export default function DateCourseSearchFilterModal({ onClose }: TProps) {
133146
[budget, dateDurationTime, mealTypes, userPreferredKeywords, startTime],
134147
);
135148

136-
const updateByIndex = (idx: number, raw: any) => {
137-
let v = raw;
138-
if ([1, 3, 6].includes(idx) && !Array.isArray(v)) v = [];
139-
140-
switch (idx) {
141-
case 0:
142-
setField('budget', v ?? null);
143-
break;
144-
case 1:
145-
setField('datePlaces', v);
146-
break;
147-
case 2:
148-
setField('dateDurationTime', v ?? null);
149-
break;
150-
case 3:
151-
setField('mealTypes', v);
152-
break;
153-
case 4:
154-
setField('transportation', v ?? null);
155-
break;
156-
case 5:
157-
setField('startTime', v ?? null);
158-
break;
159-
case 6:
160-
setField('userPreferredKeywords', v);
161-
break;
162-
}
163-
};
164-
165149
if (bookmarkDataError || courseDataError) {
166150
return <Navigate to="/error" replace={true} />;
167151
}
@@ -172,14 +156,15 @@ export default function DateCourseSearchFilterModal({ onClose }: TProps) {
172156
{Questions.map((q, idx) => (
173157
<DateCourseSearchFilterOption
174158
key={q.id}
175-
title={q.filterTitle}
159+
title={q.title}
176160
subTitle={q.subTitle}
177161
options={q.options}
178-
value={valueByIndex(idx)}
179-
onChange={(v) => updateByIndex(idx, v)}
162+
value={valueByStep(idx)}
163+
onChange={(v) => updateByStep(idx, v)}
180164
type={q.type}
181165
apiRequestValue={q.apiRequestValue}
182166
errorMessage={errorMessages[idx] ?? ''}
167+
autoInit={q.type === 'time' && idx === 7}
183168
/>
184169
))}
185170

src/utils/dateCourseValidation.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,15 @@ const keywordGroups: Record<string, string[]> = {
2626
export const mealKeyword = ['양식', '한식', '중식', '이자카야/펍', '퓨전 음식점', '브런치 카페', '디저트 카페', '루프탑 카페'];
2727

2828
export function MealTimeValidation({ meal, time, totalTime }: { meal: string[]; time: string; totalTime: string }): string | null {
29-
if (!time || meal.length === 0 || !totalTime) return null;
29+
if (!time || meal.length === 0 || !totalTime || time == null) return null;
3030
const timeStr = time.split('T')[1]; // 'HH:mm'
3131
const toMinutes = (t: string) => {
3232
const [h, m] = t.split(':').map(Number);
3333
return h * 60 + m;
3434
};
35-
3635
const start = toMinutes(timeStr); // 데이트 시작 시간
3736
const duration = timeMap[totalTime]; // 소요 시간(시간 단위)
3837
const end = start + duration * 60; // 종료 시간 (분)
39-
4038
const isOverlapping = (start1: number, end1: number, start2: number, end2: number) => {
4139
if (start1 < start2) {
4240
// 데이트 시작시간이 식사 시작시간보다 빠를 경우
@@ -50,25 +48,20 @@ export function MealTimeValidation({ meal, time, totalTime }: { meal: string[];
5048
return false;
5149
}
5250
};
53-
5451
// 선택한 식사 중 하나라도 겹치면 통과
5552
for (const m of meal) {
5653
const mealRange = mealTimeRanges[m];
57-
5854
if (!mealRange) continue; // 존재하지 않으면 skip
59-
6055
const [mealStartStr, mealEndStr] = mealRange;
6156
const mealStart = toMinutes(mealStartStr);
6257
const mealEnd = toMinutes(mealEndStr);
6358
if (isOverlapping(start, end, mealStart, mealEnd)) {
6459
return null;
6560
}
6661
}
67-
6862
// 아무것도 안겹치면 첫 번째 식사를 기준으로 안내
6963
const first = meal[0];
7064
const [mealStartStr, mealEndStr] = mealTimeRanges[first];
71-
7265
return `선택하신 시간에는 ${mealTimeKorean[first]} 식사를 하기 어려워요. (가능 시간: ${mealStartStr}~${mealEndStr})`;
7366
}
7467

@@ -78,7 +71,7 @@ type TDateTimeStartValidationInput = {
7871
};
7972

8073
export function DateTimeStartValidation({ totalTime, time }: TDateTimeStartValidationInput): string | null {
81-
if (!totalTime || !time) return null;
74+
if (!totalTime || !time || time == null) return null;
8275
const duration = timeMap[totalTime];
8376
if (duration == null) return null;
8477
const timeStr = time.split('T')[1];

0 commit comments

Comments
 (0)