-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
684 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 <></>; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = ["일", "월", "화", "수", "목", "금", "토"]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function TrainerReservation() { | ||
return <div>트레이너 예약 관리 페이지</div>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function UserReservation() { | ||
return <div>유저 예약하기 페이지</div>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
)} | ||
</> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.