-
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.
Merge pull request #14 from PlanIt-Project/dev
Landing, Main, UserSchedule 페이지
- Loading branch information
Showing
21 changed files
with
824 additions
and
49 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
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,26 +1,79 @@ | ||
import * as S from "../styles/Main.styles"; | ||
import { useEffect } from "react"; | ||
import { MouseEvent, useEffect, useRef, useState } from "react"; | ||
import { setVH } from "../utils/setVH"; | ||
import { throttle } from "../utils/throttle"; | ||
|
||
export default function Main() { | ||
const trainerList = [ | ||
{ id: "1", imgUrl: "", info: "설명" }, | ||
{ id: "2", imgUrl: "", info: "설명" }, | ||
{ id: "3", imgUrl: "", info: "" }, | ||
{ id: "4", imgUrl: "", info: "" }, | ||
{ id: "5", imgUrl: "", info: "" }, | ||
{ id: "6", imgUrl: "", info: "" }, | ||
{ id: "7", imgUrl: "", info: "" }, | ||
]; | ||
|
||
const [isDrag, setIsDrag] = useState(false); | ||
const [startX, setStartX] = useState(0); | ||
|
||
const trainerScrollRef = useRef<HTMLDivElement>(null); | ||
|
||
useEffect(() => { | ||
window.addEventListener("resize", setVH); | ||
setVH(); | ||
}, []); | ||
|
||
const handleDragStart = (e: MouseEvent<HTMLDivElement>) => { | ||
e.preventDefault(); | ||
if (trainerScrollRef.current) { | ||
setIsDrag(true); | ||
setStartX(e.pageX + trainerScrollRef.current.scrollLeft); | ||
} | ||
}; | ||
|
||
const handleDragEnd = () => { | ||
setIsDrag(false); | ||
}; | ||
|
||
const handleDragMove = (e: MouseEvent<HTMLDivElement>) => { | ||
if (isDrag && trainerScrollRef.current) { | ||
const { scrollWidth, clientWidth, scrollLeft } = trainerScrollRef.current; | ||
|
||
trainerScrollRef.current.scrollLeft = startX - e.pageX; | ||
|
||
if (scrollLeft === 0) { | ||
setStartX(e.pageX); | ||
} else if (scrollWidth <= clientWidth + scrollLeft) { | ||
setStartX(e.pageX + scrollLeft); | ||
} | ||
} | ||
}; | ||
|
||
const throttleDragMove = throttle(handleDragMove, 50); | ||
|
||
return ( | ||
<S.Container> | ||
<S.Banner></S.Banner> | ||
<S.BottomContainer> | ||
<S.HalfContainer> | ||
<S.Title>이용 후기</S.Title> | ||
<S.ContentsContainer></S.ContentsContainer> | ||
</S.HalfContainer> | ||
<S.HalfContainer> | ||
<S.Title>이번주 예약 내역</S.Title> | ||
<S.ContentsContainer></S.ContentsContainer> | ||
</S.HalfContainer> | ||
</S.BottomContainer> | ||
<S.TrainerContainer> | ||
<S.Title>트레이너 소개</S.Title> | ||
<S.ContentsContainer | ||
ref={trainerScrollRef} | ||
onMouseDown={handleDragStart} | ||
onMouseMove={isDrag ? throttleDragMove : undefined} | ||
onMouseUp={handleDragEnd} | ||
onMouseLeave={handleDragEnd} | ||
> | ||
{trainerList.map((trainer) => ( | ||
<S.Grid key={trainer.id}> | ||
<S.ImageContainer> | ||
<img src={trainer.imgUrl} /> | ||
</S.ImageContainer> | ||
<S.InfoContainer>{trainer.info}</S.InfoContainer> | ||
</S.Grid> | ||
))} | ||
</S.ContentsContainer> | ||
</S.TrainerContainer> | ||
</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,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>; | ||
} |
Oops, something went wrong.