Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat[#18] 마이페이지 - 예약 현황 캘린더/리스트 구현 #205

Merged
merged 50 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
2e123e0
feat: 예약 현황 탭 컴포넌트 생성 및 mypage에서 불러오기
CheeseB Mar 24, 2024
8d60408
refactor: 스케줄 타입 변경사항 반영
CheeseB Mar 25, 2024
ddc264c
feat: 월별 예약 내역 데이터 임시 파일 생성
CheeseB Mar 24, 2024
0f6e6d8
feat: 예약 현황 탭 기본 구조 작성
CheeseB Mar 24, 2024
8ca4586
feat: 예약 현황 스케줄 관련 타입 정의
CheeseB Mar 24, 2024
b63c38b
refactor: 타입 변경사항 반영
CheeseB Mar 25, 2024
475f271
feat: 스케줄 관련 타입 재정의
CheeseB Mar 25, 2024
4ddd875
feat: 선택된 gameId를 캘린더 컴포넌트에 위임
CheeseB Mar 25, 2024
4d69d77
feat: arrow 버튼 캘린더에 맞게 수정
CheeseB Mar 25, 2024
c0a61f4
feat: 캘린더 스위치 버튼 컴포넌트 구현
CheeseB Mar 25, 2024
e25f7ba
feat: 날짜 관련 상수 추가
CheeseB Mar 25, 2024
eb8ca29
feat: 현재 날짜를 year, month, day로 나눠서 반환하는 유틸함수 추가
CheeseB Mar 25, 2024
fff0fee
feat: 달력 헤더부분 구현
CheeseB Mar 25, 2024
2740124
feat: 캘린터 틀 구성
CheeseB Mar 25, 2024
c7f1c79
feat: 캘린더 요일 추가
CheeseB Mar 26, 2024
894de78
feat: 연/월 변경 함수 수정
CheeseB Mar 26, 2024
f18f593
feat: rebase 이미지 아이콘 충돌 해결
CheeseB Mar 26, 2024
48145f3
feat: 달력 렌더링할 범위 가져오는 유틸함수 추가
CheeseB Mar 26, 2024
a9f3660
feat: 기본적인 월별 달력 렌더링 및 동작 구현
CheeseB Mar 26, 2024
aba35b7
feat: 가상 선택자로 캘린더 테두리 구현
CheeseB Mar 26, 2024
a09443d
feat: 가상 선택자로 캘린더 hover 테두리 효과 적용
CheeseB Mar 26, 2024
58c199c
feat: 캘린더에 오늘 날짜 표시
CheeseB Mar 26, 2024
64b8305
feat: 날짜 정보를 year, month, date 로 분리하고 합치는 유틸함수 추가
CheeseB Mar 26, 2024
d0e51cd
feat: 스케줄 관련 타입 추가
CheeseB Mar 26, 2024
dd2add7
feat: 월별 스케줄 모킹 데이터 추가
CheeseB Mar 26, 2024
a2dd72e
feat: 달력에 표시할 스케줄 Badge 컴포넌트 추가
CheeseB Mar 26, 2024
3af2641
feat: 모킹 데이터로 캘린더 스케줄 표시 구현
CheeseB Mar 26, 2024
f8285e5
fix: 유틸함수 index 파일 충돌 해결
CheeseB Mar 26, 2024
8377841
fix: 유틸함수 index 충돌 해결
CheeseB Mar 26, 2024
1f01eed
feat: 모킹 데이터 변경
CheeseB Mar 27, 2024
79c82a0
refactor: box-shadow 대신 outline 사용
CheeseB Mar 27, 2024
c91a1d1
feat: 요일 구하는 유틸함수 추가
CheeseB Mar 27, 2024
2b183d8
feat: 요일 상수 추가
CheeseB Mar 27, 2024
c738250
feat: 예약 현황 리스트 구현
CheeseB Mar 27, 2024
8f9395e
feat: 예약 내역이 있는 스케줄만 cursor pointer 적용
CheeseB Mar 27, 2024
6b1202b
feat: 날짜별 예약내역 모킹 데이터 추가
CheeseB Mar 27, 2024
55e4a4c
feat: 예약 내역 모킹데이터 추가
CheeseB Mar 27, 2024
8cd3900
feat: 캘린더/리스트 클릭 이벤트 추가 - 클릭한 날짜 반환
CheeseB Mar 27, 2024
bb4860d
refactor: 코드리뷰 반영
CheeseB Mar 27, 2024
c18ded0
fix: 캘린더 뱃지에 Pretendart 폰트 적용
CheeseB Mar 27, 2024
c7c0525
fix: 캘린더 요일 폰트 스타일 수정
CheeseB Mar 27, 2024
268be94
refactor: 리스트 카드 컴포넌트 파일명 index로 수정
CheeseB Mar 27, 2024
9d95083
feat: 리스트 카드 hover 했을때 글자 색도 변하도록 수정
CheeseB Mar 27, 2024
573d3b7
feat: 리스트 hover 할때 마지막 요소에만 border radius 적용
CheeseB Mar 27, 2024
d8d52ab
feat: 리스트 데이터 없을때 EmptyCard 표시
CheeseB Mar 27, 2024
1157aea
feat: MONTH, DAYS 상수 삭제하고 유틸함수에서 문자열 받아오도록 수정
CheeseB Mar 27, 2024
bab4e22
feat: 리스트 내부 empty card에 padding 추가
CheeseB Mar 27, 2024
6e97487
fix: 날짜 1일씩 밀리는 오류 해결
CheeseB Mar 27, 2024
f172c0e
feat: 캘린더 background 일요일만 검정색으로 수정
CheeseB Mar 27, 2024
e31ccda
feat: Operation Button 오류 수정
CheeseB Mar 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion public/svgs/ic-arrow-left-active.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions public/svgs/ic-arrow-left-default.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion public/svgs/ic-arrow-right-active.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions public/svgs/ic-arrow-right-default.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions src/components/calendar/ArrowButton/ArrowButton.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
%btn-base {
@include inline-flexbox;

@include responsive(M) {
width: 3.2rem;
height: 3.2rem;
}

width: 4.8rem;
height: 4.8rem;
background-color: $black70;
}

.btn-arrow {
&-left {
@extend %btn-base;

border-right: 0.1rem solid $black50;
border-radius: 0.6rem 0 0 0.6rem;
}

&-right {
@extend %btn-base;

border-radius: 0 0.6rem 0.6rem 0;
}

&-icon {
@include responsive(M) {
width: 1.6rem;
height: 1.6rem;
}

position: relative;
width: 2.4rem;
height: 2.4rem;
}
}
57 changes: 57 additions & 0 deletions src/components/calendar/ArrowButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Image from 'next/image';

import classNames from 'classnames/bind';

import { SVGS } from '@/constants';

import useToggleButton from '@/hooks/useToggleButton';

import styles from './ArrowButton.module.scss';

const cx = classNames.bind(styles);

const { default: leftDefault, active: leftActive } = SVGS.arrow.left;
const { default: rightDefault, active: rightActive } = SVGS.arrow.right;

type ArrowButtonProps = {
onClick: (addNumber: number) => void;
};

const ArrowButton = ({ onClick }: ArrowButtonProps) => {
const { isVisible: isLeftHovering, handleToggleClick: handleHoverLeft } = useToggleButton();
const { isVisible: isRightHovering, handleToggleClick: handleHoverRight } = useToggleButton();

const { url: leftUrl, alt: leftAlt } = isLeftHovering ? leftActive : leftDefault;
const { url: rightUrl, alt: rightAlt } = isRightHovering ? rightActive : rightDefault;

const handleLeftButtonClick = () => onClick(-1);

const handleRightButtonClick = () => onClick(1);

return (
<div>
<button
className={cx('btn-arrow-left')}
onMouseEnter={handleHoverLeft}
onMouseLeave={handleHoverLeft}
onClick={handleLeftButtonClick}
>
<div className={cx('btn-arrow-icon')}>
<Image src={leftUrl} alt={leftAlt} fill />
</div>
</button>
<button
className={cx('btn-arrow-right')}
onMouseEnter={handleHoverRight}
onMouseLeave={handleHoverRight}
onClick={handleRightButtonClick}
>
<div className={cx('btn-arrow-icon')}>
<Image src={rightUrl} alt={rightAlt} fill />
</div>
</button>
</div>
);
};

export default ArrowButton;
4 changes: 4 additions & 0 deletions src/components/calendar/Calendar.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.calendar-container {
border: 0.1rem solid $black50;
border-radius: 0.8rem;
}
79 changes: 79 additions & 0 deletions src/components/calendar/CalendarBody/CalendarBody.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
$calendar-item-column: 7;

@mixin border-horizontal($direction, $color) {
@include pos-center-x;

@if $direction == 'top' {
top: 0;
}

@if $direction == 'bottom' {
bottom: 0;
}

content: '';
width: 13.8rem;
height: 0.1rem;
background-color: $color;
}

.calendar-body {
&-container {
@include text-style-quantico(16, $white, regular, normal);

padding: 1.6rem;
}

&-week {
@include flexbox;
}

&-day {
@include flexbox;

width: 15.4rem;
height: 3.2rem;

&.weekends {
color: $gray20;
}
}

&-dates {
display: grid;
grid-auto-rows: auto;
grid-template-columns: repeat($calendar-item-column, 1fr);
}
}

.date-item {
position: relative;

&-button:not(.clickable) {
cursor: default;
}

&:nth-child(7n - 6) {
background-color: $black;
}

&:not(:nth-child(7n)) {
border-right: 0.1rem solid $black50;
}

&:not(:nth-child(n):nth-child(-n + 7)) {
&::after {
@include border-horizontal(top, $black50);
}
}

&.hover:hover {
&::before {
@include border-horizontal(bottom, $primary);
}

&::after {
@include border-horizontal(top, $primary);
}
}
}
85 changes: 85 additions & 0 deletions src/components/calendar/CalendarBody/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import classNames from 'classnames/bind';

import { getCalendarDates, getDateRange, getJoinedDateString } from '@/utils';

import CalendarItem from '@/components/calendar/CalendarItem';
import { CALENDAR_WEEKS } from '@/constants/date';

import { ReservationsByDate } from '@/types';

import styles from './CalendarBody.module.scss';

const cx = classNames.bind(styles);

const START_OF_MONTH = 1;

type CalendarBodyProps = {
today: {
year: number;
month: number;
date: number;
};
currentYear: number;
currentMonth: number;
schedules?: ReservationsByDate;
onClick: (date: string) => void;
};

const CalendarBody = ({ today, currentYear, currentMonth, schedules, onClick }: CalendarBodyProps) => {
const { startOfCalendar, endOfPrevMonth, endOfThisMonth, endOfCalendar } = getCalendarDates(
currentYear,
currentMonth,
);

const prevMonthDates = startOfCalendar !== START_OF_MONTH ? getDateRange(startOfCalendar, endOfPrevMonth) : [];
const thisMonthDates = getDateRange(START_OF_MONTH, endOfThisMonth);
const nextMonthDates = endOfCalendar !== endOfThisMonth ? getDateRange(START_OF_MONTH, endOfCalendar) : [];

const isToday = (date: number) => currentYear === today.year && currentMonth === today.month && date === today.date;

return (
<div className={cx('calendar-body-container')}>
<ul className={cx('calendar-body-week')}>
{CALENDAR_WEEKS.map((day) => {
const isWeekends = day === 'SUNDAY';

return (
<li className={cx('calendar-body-day', { weekends: isWeekends })} key={`day-${day}`}>
{day}
</li>
);
})}
</ul>
<ul className={cx('calendar-body-dates')}>
{prevMonthDates.map((date, index) => (
<li className={cx('date-item')} key={`date-prev-${index}`}>
<CalendarItem date={date} isDisabled />
</li>
))}
{thisMonthDates.map((date, index) => {
const formattedDate = getJoinedDateString(currentYear, currentMonth, date);
CheeseB marked this conversation as resolved.
Show resolved Hide resolved
const hasReservation = schedules?.[formattedDate] !== undefined;
const handleClick = () => {
if (!hasReservation) return;
onClick(formattedDate);
};

return (
<li className={cx('date-item', 'hover')} key={`date-${index}`}>
<button className={cx('date-item-button', { clickable: hasReservation })} onClick={handleClick}>
<CalendarItem date={date} isToday={isToday(date)} reservations={schedules?.[formattedDate]} />
</button>
</li>
);
})}
{nextMonthDates.map((date, index) => (
<li className={cx('date-item')} key={`date-next-${index}`}>
<CalendarItem date={date} isDisabled />
</li>
))}
</ul>
</div>
);
};

export default CalendarBody;
32 changes: 32 additions & 0 deletions src/components/calendar/CalendarHeader/CalendarHeader.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.calendar-header {
&-container {
@include responsive(M) {
height: 5.6rem;
padding: 1.2rem 1.6rem;
}

@include flexbox(between);

height: 8rem;
padding: 1.6rem;
border-bottom: 0.1rem solid $black50;
}

&-date {
@include text-style-quantico(24);

@include responsive(M) {
@include text-style-quantico(20);
}
}

&-year {
color: $primary;
}
}

.arrow-area {
@include responsive(T) {
order: 1;
}
}
46 changes: 46 additions & 0 deletions src/components/calendar/CalendarHeader/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Dispatch, SetStateAction } from 'react';

import classNames from 'classnames/bind';

import { getMonthString } from '@/utils';

import ArrowButton from '@/components/calendar/ArrowButton';
import SwitchButton from '@/components/calendar/SwitchButton';

import styles from './CalendarHeader.module.scss';

const cx = classNames.bind(styles);

type CalendarHeaderProps = {
currentYear: number;
currentMonth: number;
onChangeMonth: (addNumber: number) => void;
isCalendar: boolean;
setIsCalendar: Dispatch<SetStateAction<boolean>>;
};

const CalendarHeader = ({
currentYear,
currentMonth,
onChangeMonth,
isCalendar,
setIsCalendar,
}: CalendarHeaderProps) => {
const handleSwitchClick = (isCalendar: boolean) => setIsCalendar(isCalendar);

return (
<div className={cx('calendar-header-container')}>
<div className={cx('arrow-area')}>
<ArrowButton onClick={onChangeMonth} />
</div>
<div className={cx('calendar-header-date')}>
{getMonthString(currentMonth)} <span className={cx('calendar-header-year')}>{currentYear}</span>
</div>
<div className={cx('lg-only')}>
<SwitchButton isCalendar={isCalendar} onClick={handleSwitchClick} />
</div>
</div>
);
};

export default CalendarHeader;
Loading