Skip to content

Commit 68a1302

Browse files
authored
feat(mobile): 날짜 선택 이후 렌더링되는 시간 선택 뷰를 구현합니다. (#122)
1 parent 0e62b41 commit 68a1302

File tree

9 files changed

+312
-90
lines changed

9 files changed

+312
-90
lines changed
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
1-
import SelectDateHeader from "../../components/select-date/SelectDateHeader";
2-
31
export default function layout({
42
children,
53
}: Readonly<{
64
children: React.ReactNode;
75
}>) {
86
return (
97
<section>
10-
<SelectDateHeader />
11-
<div className="px-[2rem] py-[3rem]">{children}</div>
8+
<div>{children}</div>
129
</section>
1310
);
1411
}
Lines changed: 73 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,92 @@
11
"use client";
22

33
import { Button, TextField } from "@setaday/ui";
4-
import { useState } from "react";
4+
import { useRouter } from "next/navigation";
5+
import { type SetStateAction, useRef, useState } from "react";
56
import SelectDateCalendar from "../../components/select-date/SelectDateCalendar";
7+
import SelectDateHeader from "../../components/select-date/SelectDateHeader";
8+
import SelectTimeRange from "../../components/select-date/SelectTimeRange";
9+
import { MAX_DATE, MAX_LENGTH, PLACE_HOLDER } from "../../constants/selectDateConst";
10+
import type { SelectedDateType, SelectedTimeType } from "../../type/selectedDateType";
611

712
export default function page() {
8-
const PLACE_HOLDER = "약속 이름을 작성해주세요";
9-
const MAX_LENGTH = 16;
13+
const router = useRouter();
1014

1115
const [planName, setPlanName] = useState("");
12-
const [isDateSelected, setIsDateSelected] = useState(false);
13-
const isRightName = planName.length > 0 && planName.length < 17;
14-
const isActiveBtn = isRightName && isDateSelected;
16+
const [selectedDate, setSelectedDate] = useState<Array<SelectedDateType>>([]);
17+
const [selectedTime, setSelectedTime] = useState<Array<SelectedTimeType>>([]);
18+
const [step, setStep] = useState("date");
19+
const selectedDateNum = useRef(0);
20+
21+
const isSelectTime = step === "time";
22+
const BTN_CONTENT = isSelectTime ? "약속 생성하기" : "다음으로";
23+
24+
const isValidName = planName.length && planName.length <= MAX_LENGTH;
25+
const isValidDate =
26+
selectedDate.length &&
27+
selectedDate.every(({ startYear, endYear }) => startYear && endYear) &&
28+
selectedDateNum.current <= MAX_DATE;
29+
const isValidTime = selectedTime.length && selectedTime.every(({ startTime, endTime }) => startTime && endTime);
30+
31+
const isActiveBtn = isSelectTime ? isValidTime : isValidName && isValidDate;
1532

1633
const handleChangeInput = (e: React.ChangeEvent<HTMLInputElement>) => {
1734
const { value } = e.currentTarget;
1835
setPlanName(value);
1936
};
2037

21-
const handleSelectDate = (isSelected: boolean) => {
22-
setIsDateSelected(isSelected);
38+
const handleSelectDate = (newDate: SetStateAction<Array<SelectedDateType>>) => {
39+
setSelectedDate(newDate);
40+
};
41+
42+
const handleSelectTime = (newTime: SetStateAction<Array<SelectedTimeType>>) => {
43+
setSelectedTime(newTime);
44+
};
45+
46+
const changeStep = (step: string) => {
47+
setStep(step);
48+
};
49+
50+
const handleClickSubmitBtn = () => {
51+
// isSelectTime의 경우, 추후 api 통신으로 바꿀 예정
52+
isSelectTime ? router.push("/") : changeStep("time");
2353
};
2454

2555
return (
26-
<div className="flex flex-col justify-between h-[calc(100dvh-11.2rem)]">
27-
<TextField
28-
inputSize="mobile"
29-
placeholder={PLACE_HOLDER}
30-
value={planName}
31-
maxLength={MAX_LENGTH}
32-
onChange={handleChangeInput}
33-
/>
34-
35-
<SelectDateCalendar handleSelectDate={handleSelectDate} />
36-
37-
<Button color={isActiveBtn ? "default" : "disabled"} font="default" size="mobile" disabled={!isActiveBtn}>
38-
다음으로
39-
</Button>
40-
</div>
56+
<>
57+
<SelectDateHeader step={step} changeStep={changeStep} />
58+
59+
<div className="flex flex-col justify-between h-[calc(100dvh-11.2rem)] mx-[2rem] my-[3rem]">
60+
{isSelectTime ? (
61+
<SelectTimeRange selectedTime={selectedTime} handleSelectTime={handleSelectTime} />
62+
) : (
63+
<>
64+
<TextField
65+
inputSize="mobile"
66+
placeholder={PLACE_HOLDER}
67+
value={planName}
68+
maxLength={MAX_LENGTH}
69+
onChange={handleChangeInput}
70+
/>
71+
72+
<SelectDateCalendar
73+
selectedDateNum={selectedDateNum}
74+
selectedDate={selectedDate}
75+
handleSelectDate={handleSelectDate}
76+
/>
77+
</>
78+
)}
79+
80+
<Button
81+
color={isActiveBtn ? "default" : "disabled"}
82+
font="default"
83+
size="mobile"
84+
disabled={!isActiveBtn}
85+
onClick={handleClickSubmitBtn}
86+
>
87+
{BTN_CONTENT}
88+
</Button>
89+
</div>
90+
</>
4191
);
4292
}

apps/mobile/components/select-date/SelectDateCalendar.tsx

Lines changed: 12 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,14 @@
11
"use client";
22

33
import { MobileIconArrowLeftGray, MobileIconArrowRightGray } from "@setaday/icon";
4-
import { useRef, useState } from "react";
5-
import { DAY, MONTH_NAMES } from "../../contants/calendarConst";
4+
import { useState } from "react";
5+
import { DAY, MAX_DATE, MONTH_NAMES } from "../../constants/selectDateConst";
6+
import type { ClickDateProps, SelctDateCalendarProps, SelectedDateType } from "../../type/selectedDateType";
67
import { getCalendarDate } from "../../utils/getCalendarDate";
78

8-
function SelectDateCalendar({
9-
handleSelectDate,
10-
}: {
11-
handleSelectDate: (isSelected: boolean) => void;
12-
}) {
9+
function SelectDateCalendar({ selectedDateNum, selectedDate, handleSelectDate }: SelctDateCalendarProps) {
1310
const [year, setYear] = useState(new Date().getFullYear());
1411
const [month, setMonth] = useState(new Date().getMonth() + 1);
15-
const [selectedDate, setSelectedDate] = useState<
16-
Array<{
17-
startYear: number;
18-
startMonth: number;
19-
startDate: number;
20-
endYear: number;
21-
endMonth: number;
22-
endDate: number;
23-
}>
24-
>([]);
25-
const selectedDateNum = useRef(0);
2612

2713
const ALL_DATE = getCalendarDate({ year, month });
2814

@@ -35,14 +21,7 @@ function SelectDateCalendar({
3521
endYear,
3622
endMonth,
3723
endDate,
38-
}: {
39-
startYear: number;
40-
startMonth: number;
41-
startDate: number;
42-
endYear: number;
43-
endMonth: number;
44-
endDate: number;
45-
}) => {
24+
}: SelectedDateType) => {
4625
const start = new Date(startYear, startMonth - 1, startDate);
4726
const end = new Date(endYear, endMonth - 1, endDate);
4827

@@ -80,7 +59,7 @@ function SelectDateCalendar({
8059
const endMonth = isSavedDateEarlier ? clickedMonth : month;
8160
const endDate = isSavedDateEarlier ? clickedDate : date;
8261

83-
setSelectedDate((prev) => [
62+
handleSelectDate((prev) => [
8463
// 이전 값을 유지한 채 마지막 요소만 변경
8564
...prev.slice(0, -1),
8665
{ startYear, startMonth, startDate, endYear, endMonth, endDate },
@@ -118,15 +97,7 @@ function SelectDateCalendar({
11897
}
11998
};
12099

121-
const handleClickDate = ({
122-
clickedYear,
123-
clickedMonth,
124-
clickedDate,
125-
}: {
126-
clickedYear: number;
127-
clickedMonth: number;
128-
clickedDate: number;
129-
}) => {
100+
const handleClickDate = ({ clickedYear, clickedMonth, clickedDate }: ClickDateProps) => {
130101
// 해당 날짜가 포함된 selectedDate 객체 찾기
131102
const idxOfDate = selectedDate.findIndex(
132103
({ startDate, startMonth, startYear, endDate, endMonth, endYear }) =>
@@ -172,16 +143,15 @@ function SelectDateCalendar({
172143
),
173144
];
174145

175-
setSelectedDate(updatedDate);
176-
handleSelectDate(false);
146+
handleSelectDate(updatedDate);
177147
} else {
178148
const lastDate = selectedDate[selectedDate.length - 1];
179149
const isStartDateNull = lastDate && lastDate.startDate === 0 && lastDate.endDate !== 0;
180150
const isEndDateNull = lastDate && lastDate.startDate !== 0 && lastDate.endDate === 0;
181151

182152
// 선택된 날짜가 없는 경우
183153
if (selectedDate.length === 0) {
184-
setSelectedDate([
154+
handleSelectDate([
185155
{
186156
startYear: clickedYear,
187157
startMonth: clickedMonth,
@@ -191,7 +161,6 @@ function SelectDateCalendar({
191161
endDate: 0,
192162
},
193163
]);
194-
handleSelectDate(false);
195164
}
196165

197166
// 시작 날짜와 끝나는 날짜 중 하나가 선택되어 있는 경우
@@ -212,11 +181,9 @@ function SelectDateCalendar({
212181
month: startMonth,
213182
date: startDate,
214183
});
215-
handleSelectDate(true);
216-
if (selectedDateNum.current >= 14) {
184+
if (selectedDateNum.current > MAX_DATE) {
217185
// 선택한 날짜가 14일을 넘었을 때 동작하는 플로우 추가 시 삭제 예정
218186
alert("14일 넘음");
219-
handleSelectDate(false);
220187
}
221188
}
222189

@@ -233,18 +200,16 @@ function SelectDateCalendar({
233200
month: endMonth,
234201
date: endDate,
235202
});
236-
handleSelectDate(true);
237-
if (selectedDateNum.current >= 14) {
203+
if (selectedDateNum.current > MAX_DATE) {
238204
// 선택한 날짜가 14일을 넘었을 때 동작하는 플로우 추가 시 삭제 예정
239205
alert("14일 넘음");
240-
handleSelectDate(false);
241206
}
242207
}
243208
}
244209

245210
// 선택된 날짜 묶음이 하나 이상이고, 시작/ 끝 날짜가 모두 선택되어 있는 경우
246211
else if (selectedDate.length > 0) {
247-
setSelectedDate((prev) => [
212+
handleSelectDate((prev) => [
248213
...prev,
249214
{
250215
startYear: clickedYear,
@@ -255,7 +220,6 @@ function SelectDateCalendar({
255220
endDate: 0,
256221
},
257222
]);
258-
handleSelectDate(false);
259223
}
260224
}
261225
};

apps/mobile/components/select-date/SelectDateHeader.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,25 @@
22

33
import { MobileIconArrowLeftBlack } from "@setaday/icon";
44
import { useRouter } from "next/navigation";
5+
import type { SelectDateHeaderProps } from "../../type/selectedDateType";
56

6-
function SelectDateHeader() {
7+
function SelectDateHeader({ step, changeStep }: SelectDateHeaderProps) {
78
const router = useRouter();
9+
10+
const isSelectTimeStep = step === "time";
11+
const HEADER_CONTENT = isSelectTimeStep ? "시간 선택" : "날짜 선택";
12+
813
const clickArrowBtn = () => {
9-
router.back();
14+
isSelectTimeStep ? changeStep("date") : router.back();
1015
};
16+
1117
return (
1218
<header className="border-gray-1 flex h-[5.2rem] w-[100dvw] items-center justify-between border-b-[1px] px-[0.9rem]">
1319
<button type="button" onClick={clickArrowBtn}>
1420
<MobileIconArrowLeftBlack />
1521
</button>
1622

17-
<h2 className="font-body3_m_16 text-gray-6">날짜 선택</h2>
23+
<h2 className="font-body3_m_16 text-gray-6">{HEADER_CONTENT}</h2>
1824

1925
<span className="h-[3.2rem] w-[3.2rem]" />
2026
</header>

0 commit comments

Comments
 (0)