Skip to content

Commit

Permalink
feat: 캘린더 구현 (#173)
Browse files Browse the repository at this point in the history
* chore: 테스트를 위한 mock데이터 수정

* feat: 캘린더 현재 날짜 적용 및 날짜 선택 기능 추가

* feat: 날짜별 roomDetail 데이터 mock handler 추가

* feat: 날짜별 상세 데이터 options

* feat: getRoomDetail에 date 옵션 추가

* feat: 캘린더 UI 및 캘린더 선택시 데이터 업데이트 기능 추가

* fix: 변수명 수정 및 캘린더 로직 수정

* feat: serverTime props 추가 및 타입 정의

* feat: API 요청 경로 수정

* feat: detailByDate API 다시 수정

* style: roomDetail provider 분리

* fix: PR리뷰 적용

* fix: 테스트용 주석 제거
  • Loading branch information
nayeon-hub authored Nov 22, 2023
1 parent 1cb8d42 commit 9498858
Show file tree
Hide file tree
Showing 11 changed files with 267 additions and 79 deletions.
142 changes: 87 additions & 55 deletions src/RoomDetail/components/RoomCalendar.tsx
Original file line number Diff line number Diff line change
@@ -1,74 +1,106 @@
import { MouseEvent, useRef, useContext } from 'react';
import clsx from 'clsx';
import { twMerge } from 'tailwind-merge';
import { makeWeekCalendar } from '../utils/utils';
import { DAY_OF_THE_WEEK } from '../constants/constant';
import { DateRoomDetailContext } from './RoomDetailProvider';
import { Icon } from '@/shared/Icon';
import { Toast } from '@/shared/Toast';

const calendar = [
{
day: '월',
date: '8',
bug: true,
point: false
},
{
day: '화',
date: '9',
bug: true,
point: false
},
{
day: '수',
date: '10',
bug: false,
point: false
},
{
day: '목',
date: '11',
bug: true,
point: true
},
{
day: '금',
date: '12',
bug: true,
point: false
},
{
day: '토',
date: '13',
bug: false,
point: false
},
{
day: '일',
date: '14',
bug: true,
point: false
}
];
interface RoomCalendarProps {
certifiedDates: string[];
certifyTime: number;
serverTime: Date;
}

const RoomCalendar = ({
certifiedDates,
certifyTime,
serverTime
}: RoomCalendarProps) => {
const dateRef = useRef<HTMLDivElement>(null);
const { selectDate, date: chooseDate } = useContext(DateRoomDetailContext);

const { thisWeekTimestamp } = makeWeekCalendar(serverTime);
const chooseDateTimestamp = `${chooseDate.getFullYear()}-${
chooseDate.getMonth() + 1
}-${chooseDate.getDate()}`;

const handleDateClick = (e: MouseEvent<HTMLDivElement>) => {
if (e.target !== dateRef.current) {
return;
}

selectDate(new Date());
};

const RoomCalendar = () => {
return (
<div className="mb-[3.19rem] mt-[1.87rem]">
<h4 className="pb-2 text-base text-black dark:text-white">2023년 10월</h4>
<div className="flex justify-between">
{calendar.map(({ day, date, point, bug }) => {
<div
className="flex justify-between"
ref={dateRef}
onClick={handleDateClick}
>
{thisWeekTimestamp.map((thisDate) => {
const date = thisDate.getDate();
const day = thisDate.getDay();
const thisDateTimestamp = `${thisDate.getFullYear()}-${
thisDate.getMonth() + 1
}-${date}`;
const langKoDay = DAY_OF_THE_WEEK[day];
const bug = certifiedDates.find((el) => el === thisDateTimestamp);

const RoomCalendarStyle = {
calendarItem: clsx(
'flex h-[5.87rem] w-[3.12rem] flex-col items-center rounded-[0.62rem] pt-1 text-center ',
{
'border-light-point text-light-point dark:border-dark-point dark:text-dark-point border-[0.06rem]':
point === true,
'text-darkGray': point === false
}
calendarItem: twMerge(
clsx(
'relative flex h-[5.87rem] w-[3.12rem] cursor-default flex-col items-center pt-1 text-center',
{
'text-dark-gray': serverTime.getTime() < thisDate.getTime(),
'text-black': serverTime.getTime() >= thisDate.getTime(),
'rounded-[0.62rem] border-light-point text-light-point dark:border-dark-point dark:text-dark-point border-[0.06rem]':
thisDateTimestamp === chooseDateTimestamp
}
)
)
};

return (
<div
className={RoomCalendarStyle.calendarItem}
key={day}
onClick={() => {
const routineLimitTime = new Date(serverTime);
routineLimitTime.setHours(certifyTime);
routineLimitTime.setMinutes(50);

if (serverTime.getTime() >= thisDate.getTime()) {
selectDate(thisDate);

if (
thisDate.getDate() === routineLimitTime.getDate() &&
serverTime.getTime() < routineLimitTime.getTime()
) {
Toast.show(
{
message: '인증 시간 이후 확인 가능합니다',
status: 'info'
},
2000
);
}
} else {
Toast.show(
{
message: '조회가 가능하지 않은 날짜입니다',
status: 'info'
},
2000
);
}
}}
>
<div className="mb-1 text-sm">{day}</div>
<div className="mb-1 text-sm">{langKoDay}</div>
<div className="mb-2 text-2xl">{date}</div>
<div className="flex justify-center">
{bug && (
Expand Down
47 changes: 47 additions & 0 deletions src/RoomDetail/components/RoomDetailContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useContext } from 'react';
import { useQuery } from '@tanstack/react-query';
import { roomOptions } from '@/core/api/options';
import { DateRoomDetailContext } from './RoomDetailProvider';
import { RoomInfo, RoomWorkspace } from '@/RoomDetail';
import { RoomInfo as RoomInfoType } from '@/core/types/Room';
interface RoomDetailContainerProps {
roomDetailData: RoomInfoType;
serverTime: Date;
}

const RoomDetailContainer = ({
roomDetailData,
serverTime
}: RoomDetailContainerProps) => {
const { date } = useContext(DateRoomDetailContext);
const roomId = '1234';
const chooseDate = `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;

const { data: roomDetailDataByDate, status } = useQuery({
...roomOptions.detailByDate(roomId, chooseDate)
});

if (roomDetailDataByDate) {
roomDetailData = roomDetailDataByDate;
}

return (
<>
<div className="h-[20.56rem] bg-[url('/level1.png')] bg-cover bg-no-repeat text-white">
<RoomInfo
{...roomDetailData}
status={status}
/>
</div>
<div className="px-[1.81rem] pb-[1.62rem] pt-[1.88rem]">
<RoomWorkspace
{...roomDetailData}
status={status}
serverTime={serverTime}
/>
</div>
</>
);
};

export default RoomDetailContainer;
38 changes: 38 additions & 0 deletions src/RoomDetail/components/RoomDetailProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ReactNode, useState, createContext } from 'react';

export const DateRoomDetailContext = createContext<{
date: Date;
selectDate: (value: Date) => void;
}>({
date: new Date(),
selectDate: (value: Date) => {
return value;
}
});

interface RoomDetailProviderProps {
children: ReactNode;
serverTime: Date;
}

const RoomDetailProvider = ({
children,
serverTime
}: RoomDetailProviderProps) => {
const [changedDate, setChangeDate] = useState<Date>(serverTime);

return (
<DateRoomDetailContext.Provider
value={{
date: changedDate,
selectDate: (dateValue: Date) => {
setChangeDate(dateValue);
}
}}
>
{children}
</DateRoomDetailContext.Provider>
);
};

export default RoomDetailProvider;
23 changes: 16 additions & 7 deletions src/RoomDetail/components/RoomInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,28 @@ import RoomMemberRank from './RoomMemberRank';
import { ProgressBar } from '@/shared/ProgressBar';
import { RoomInfo as RoomInfoType } from '@/core/types/Room';

interface extendedProps {
status: 'pending' | 'error' | 'success';
}
type RoomInfoProps = extendedProps & RoomInfoType;
const RoomInfo = ({
level,
currentUserCount,
maxUserCount,
todayCertificateRank,
certifyTime
}: RoomInfoType) => {
certifyTime,
status
}: RoomInfoProps) => {
return (
<div className="relative flex h-[20.56rem] items-center justify-center">
<RoomMemberRank
todayCertificateRank={todayCertificateRank}
certifyTime={certifyTime}
/>
<div className="relative h-[20.56rem]">
{status !== 'success' ? (
<div>임시 Loading...</div>
) : (
<RoomMemberRank
todayCertificateRank={todayCertificateRank}
certifyTime={certifyTime}
/>
)}
<div className="absolute inset-x-0 bottom-0">
<div className="mb-2 flex items-end justify-between bg-inherit pl-3.5 pr-7">
<span className="block h-[1.93rem] w-[4.62rem] rounded-[6.25rem] bg-light-point py-[0.16rem] text-center font-IMHyemin-bold text-light-main dark:bg-dark-point">
Expand Down
35 changes: 28 additions & 7 deletions src/RoomDetail/components/RoomWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,22 @@ import { BottomSheet, useBottomSheet } from '@/shared/BottomSheet';
import { Tab, TabItem } from '@/shared/Tab';
import { RoomInfo } from '@/core/types/Room';

interface extendedProps {
status: 'pending' | 'error' | 'success';
serverTime: Date;
}

type RoomWorkspaceProps = extendedProps & RoomInfo;

const RoomWorkspace = ({
completePercentage,
routine,
todayCertificateRank
}: RoomInfo) => {
todayCertificateRank,
certifiedDates,
certifyTime,
status,
serverTime
}: RoomWorkspaceProps) => {
const { bottomSheetProps, toggle, close } = useBottomSheet();

const myCertificationImage = todayCertificateRank.find(
Expand Down Expand Up @@ -45,12 +56,22 @@ const RoomWorkspace = ({
defaultIndex={0}
>
<TabItem title="루틴">
<RoomCalendar />
<CertificationProgress percentage={completePercentage} />
<RoomRoutine
routines={routine}
myCertificationImage={myCertificationImage}
<RoomCalendar
certifiedDates={certifiedDates}
certifyTime={certifyTime}
serverTime={serverTime}
/>
{status !== 'success' ? (
<div>임시 Loading...</div>
) : (
<>
<CertificationProgress percentage={completePercentage} />
<RoomRoutine
routines={routine}
myCertificationImage={myCertificationImage}
/>
</>
)}
<button
className="mt-[1.19rem] text-sm text-black dark:text-white"
onClick={toggle}
Expand Down
1 change: 1 addition & 0 deletions src/RoomDetail/constants/constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const DAY_OF_THE_WEEK = ['일', '월', '화', '수', '목', '금', '토'];
19 changes: 19 additions & 0 deletions src/RoomDetail/utils/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const makeWeekCalendar = (serverTime: Date) => {
const weekArray: Date[] = Array.from({ length: 7 });

const todayDate = serverTime.getDate();
const todayDay = serverTime.getDay();

for (let i = 0; i < 7; i++) {
const mondayDate = todayDate - todayDay + 1;
const newDate = new Date(serverTime);

newDate.setFullYear(newDate.getFullYear());
newDate.setMonth(newDate.getMonth());
newDate.setDate(mondayDate + i);

weekArray[i] = newDate;
}

return { thisWeekTimestamp: weekArray };
};
1 change: 0 additions & 1 deletion src/core/api/functions/roomAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
TotalRooms
} from '@/core/types';
import { baseInstance, formDataInstance } from '../instance';

import { MyJoinRoom } from '@/core/types/MyJoinRoom';
import { RoomInfo, RoomInfoBeforeEditing } from '@/core/types/Room';

Expand Down
2 changes: 1 addition & 1 deletion src/core/mocks/datas/room.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const RoomInfo = {
maxUserCount: 9,
announcement: '2번 연속 인증 안하는 사람은 추방할 예정입니다 ㅅㄱ!',
completePercentage: 66.7,
certifiedDates: ['2023-03-02', '2023-03-03', '2023-03-02'],
certifiedDates: ['2023-11-13', '2023-11-14', '2023-11-16'],
routine: [
{
routineId: 5,
Expand Down
Loading

0 comments on commit 9498858

Please sign in to comment.