Skip to content

Commit

Permalink
Merge branch 'dev-#10' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Hailey0930 committed Mar 13, 2024
2 parents 229c695 + 140b149 commit 9994a77
Show file tree
Hide file tree
Showing 16 changed files with 684 additions and 2 deletions.
3 changes: 2 additions & 1 deletion public/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
Expand Down Expand Up @@ -29,6 +29,7 @@
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<div id="modal"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
Expand Down
Binary file added src/assets/icon_left-arrow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/icon_right-arrow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
188 changes: 188 additions & 0 deletions src/components/Calendar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import * as S from "../styles/Calendar.styles";
import LeftArrow from "../assets/icon_left-arrow.png";
import RightArrow from "../assets/icon_right-arrow.png";
import { DAYS_OF_WEEK, TODAY } from "../constants/Calendar.constants";
import { ICalendarProps } from "../types/Calendar.types";
import { useState } from "react";

export default function Calendar({
selectedDay,
handleClickDay,
}: ICalendarProps) {
const [currentMonth, setCurrentMonth] = useState(TODAY);

const isSameDay = (toDay: Date, compareDay?: Date | null) => {
if (
toDay.getFullYear() === compareDay?.getFullYear() &&
toDay.getMonth() === compareDay?.getMonth() &&
toDay.getDate() === compareDay?.getDate()
) {
return true;
}
return false;
};

// NOTE 전 달로 이동
const handleMoveToPrevCalendar = () => {
setCurrentMonth(
new Date(
currentMonth.getFullYear(),
currentMonth.getMonth() - 1,
currentMonth.getDate(),
),
);
};

// NOTE 다음 달로 이동
const handleMoveToNextCalendar = () => {
setCurrentMonth(
new Date(
currentMonth.getFullYear(),
currentMonth.getMonth() + 1,
currentMonth.getDate(),
),
);
};

// NOTE 캘린더 날짜 세팅
const buildCalendarDays = () => {
// 현재 월의 시작 요일
const currentMonthStartDate = new Date(
currentMonth.getFullYear(),
currentMonth.getMonth(),
1,
).getDay();

// 현재 월의 마지막 날짜
const currentMonthEndDate = new Date(
currentMonth.getFullYear(),
currentMonth.getMonth() + 1,
0,
);

// 이전 월의 마지막 날짜
const prevMonthEndDate = new Date(
currentMonth.getFullYear(),
currentMonth.getMonth(),
0,
);

// 다음 월의 첫 번째 날짜
const nextMonthStartDate = new Date(
currentMonth.getFullYear(),
currentMonth.getMonth() + 1,
1,
);

// 이전 월의 마지막 날부터 현재 월의 첫 번째 요일까지의 날짜들을 담음
const days = Array.from({ length: currentMonthStartDate }, (_, i) => {
return new Date(
currentMonth.getFullYear(),
currentMonth.getMonth() - 1,
prevMonthEndDate.getDate() - i,
);
}).reverse();

// 현재 월의 첫 번째 날부터 마지막 날까지의 날짜들을 추가
days.push(
...Array.from(
{ length: currentMonthEndDate.getDate() },
(_, i) =>
new Date(currentMonth.getFullYear(), currentMonth.getMonth(), i + 1),
),
);

// 남은 일수 계산
const remainingDays = 7 - (days.length % 7);
// 남은 일수가 7보다 작으면 다음 달의 시작 날짜부터 remainingDays개수 만큼 날짜 추가
if (remainingDays < 7) {
days.push(
...Array.from(
{ length: remainingDays },
(_, i) =>
new Date(
nextMonthStartDate.getFullYear(),
nextMonthStartDate.getMonth(),
i + 1,
),
),
);
}

return days;
};

// NOTE 날짜를 통해 el 생성
const buildCalendarTag = (calendarDays: Date[]) => {
return calendarDays.map((day: Date, index: number) => {
const isCurrentMonth = day.getMonth() === currentMonth.getMonth();

const dayClass = !isCurrentMonth
? "otherMonth"
: day < TODAY
? "prevDay"
: "futureDay";

const selectedDayClass = isSameDay(day, selectedDay) ? "choiceDay" : "";

return (
<S.CalendarDay key={index}>
<S.Day
className={`${dayClass} ${selectedDayClass}`}
onClick={() => {
if (isCurrentMonth) {
handleClickDay(day);
}
}}
>
{day.getDate()}
</S.Day>
</S.CalendarDay>
);
});
};

// NOTE 각 줄에 7일씩 나눠서 뿌리기
const divideWeek = (calendarTags: JSX.Element[]) => {
return calendarTags.reduce(
(acc: JSX.Element[][], day: JSX.Element, i: number) => {
if (i % 7 === 0) acc.push([day]);
else acc[acc.length - 1].push(day);
return acc;
},
[],
);
};

const calendarDays = buildCalendarDays();
const calendarTags = buildCalendarTag(calendarDays);
const calendarRows = divideWeek(calendarTags);

return (
<S.Container>
<S.TopContainer>
<S.ArrowIcon onClick={handleMoveToPrevCalendar}>
<img src={LeftArrow} alt="왼쪽 화살표" />
</S.ArrowIcon>
<p>
{currentMonth.getFullYear()}{currentMonth.getMonth() + 1}
</p>
<S.ArrowIcon onClick={handleMoveToNextCalendar}>
<img src={RightArrow} alt="오른쪽 화살표" />
</S.ArrowIcon>
</S.TopContainer>

<S.WeekContainer>
{DAYS_OF_WEEK.map((week, index) => (
<S.Week key={index}>{week}</S.Week>
))}
</S.WeekContainer>

<S.CalendarContainer>
{calendarRows.map((day, index) => (
<S.CalendarRows key={index}>{day}</S.CalendarRows>
))}
</S.CalendarContainer>
</S.Container>
);
}
23 changes: 23 additions & 0 deletions src/components/modal/ConfirmModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as S from "../../styles/ConfirmModal.styles";
import { IConfirmModalProps } from "../../types/ConfirmModal.types";

export default function ConfirmModal({
contents,
setIsModalOpen,
}: IConfirmModalProps) {
const handleCloseModal = () => {
setIsModalOpen(false);
};

return (
<S.Background>
<S.ModalContainer>
<S.ModalContents>{contents}</S.ModalContents>
<S.ButtonContainer>
<S.CancelButton onClick={handleCloseModal}>닫기</S.CancelButton>
<S.ConfirmButton>취소</S.ConfirmButton>
</S.ButtonContainer>
</S.ModalContainer>
</S.Background>
);
}
15 changes: 15 additions & 0 deletions src/components/modal/ModalPortal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ReactNode } from "react";
import ReactDom from "react-dom";

interface IModalPortalProps {
children: ReactNode;
}

export default function ModalPortal({ children }: IModalPortalProps) {
const el = document.getElementById("modal");

if (el) return ReactDom.createPortal(children, el);
else {
return <></>;
}
}
4 changes: 4 additions & 0 deletions src/constants/Calendar.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const TODAY = new Date();
TODAY.setHours(0, 0, 0, 0);

export const DAYS_OF_WEEK = ["일", "월", "화", "수", "목", "금", "토"];
3 changes: 3 additions & 0 deletions src/pages/trainer/Reservation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function TrainerReservation() {
return <div>트레이너 예약 관리 페이지</div>;
}
3 changes: 3 additions & 0 deletions src/pages/user/Reservation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function UserReservation() {
return <div>유저 예약하기 페이지</div>;
}
108 changes: 107 additions & 1 deletion src/pages/user/Schedule.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,109 @@
import { useState } from "react";
import Calendar from "../../components/Calendar";
import * as S from "../../styles/Schedule.styles";
import { TODAY } from "../../constants/Calendar.constants";
import { useNavigate } from "react-router";
import ModalPortal from "../../components/modal/ModalPortal";
import ConfirmModal from "../../components/modal/ConfirmModal";

export default function UserSchedule() {
return <div>유저 스케줄</div>;
const scheduleList = [
{
id: "1",
trainer: "홍길동",
time: "오전 11:00 ~ 11:50",
item: "개인 레슨 3개월",
},
{
id: "2",
trainer: "홍길동",
time: "오전 11:00 ~ 11:50",
item: "개인 레슨 3개월",
},
{
id: "3",
trainer: "홍길동",
time: "오전 11:00 ~ 11:50",
item: "개인 레슨 3개월",
},
];

const [selectedDay, setSelectedDay] = useState(TODAY);
const [isConfirmModalOpen, setIsConfirmModalOpen] = useState(false);

const navigate = useNavigate();

const handleMoveToReservation = () => {
navigate("/user/reservation");
};

// NOTE 날짜 선택
const handleClickDay = (day: Date) => {
setSelectedDay(day);
};

const handleMoveToEdit = (id: string) => {
navigate(`/user/reservation/${id}`);
};

const handleCancel = () => {
setIsConfirmModalOpen(true);
};

return (
<>
<S.Container>
<S.TopContainer>
<S.ReservationButton onClick={handleMoveToReservation}>
예약하기
</S.ReservationButton>
</S.TopContainer>
<S.CalendarContainer>
<Calendar selectedDay={selectedDay} handleClickDay={handleClickDay} />
</S.CalendarContainer>
<S.BottomContainer>
<S.Title>
{selectedDay.getFullYear()}{selectedDay.getMonth() + 1}{" "}
{selectedDay.getDate()}일 예약 내역
</S.Title>
{scheduleList ? (
scheduleList.map((schedule) => (
<S.ReservationContainer key={schedule.id}>
<S.LeftContainer>
<S.InfoContainer>
<p>{schedule.trainer}</p>
<p>{schedule.time}</p>
<p>이용권 : {schedule.item}</p>
</S.InfoContainer>
</S.LeftContainer>
<S.RightContainer>
<S.ChangeDeleteButton
onClick={() => {
handleMoveToEdit(schedule.id);
}}
>
변경하기
</S.ChangeDeleteButton>
<S.ChangeDeleteButton onClick={handleCancel}>
취소하기
</S.ChangeDeleteButton>
</S.RightContainer>
</S.ReservationContainer>
))
) : (
<S.NoContents>예약 내역이 없습니다.</S.NoContents>
)}
</S.BottomContainer>
</S.Container>

{isConfirmModalOpen && (
<ModalPortal>
<ConfirmModal
contents="취소하시겠습니까?"
setIsModalOpen={setIsConfirmModalOpen}
/>
</ModalPortal>
)}
</>
);
}
8 changes: 8 additions & 0 deletions src/routes/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import MyPage from "../pages/MyPage";
import Main from "../pages/Main";
import UserTicket from "../pages/user/Ticket";
import UserSchedule from "../pages/user/Schedule";
import UserReservation from "../pages/user/Reservation";
import TrainerReservation from "../pages/trainer/Reservation";
import TrainerSchedule from "../pages/trainer/Schedule";
import Account from "../pages/admin/Account";
import Trainer from "../pages/admin/Trainer";
Expand Down Expand Up @@ -37,12 +39,18 @@ export const router = createBrowserRouter([
children: [
{ path: "/user/ticket", element: <UserTicket /> },
{ path: "/user/schedule", element: <UserSchedule /> },
{ path: "/user/reservation", element: <UserReservation /> },
{
path: "/user/reservation/:reservationId",
element: <UserReservation />,
},
],
},
{
path: "/trainer",
element: <TrainerRoute />,
children: [
{ path: "/trainer/reservation", element: <TrainerReservation /> },
{ path: "/trainer/schedule", element: <TrainerSchedule /> },
],
},
Expand Down
Loading

0 comments on commit 9994a77

Please sign in to comment.