diff --git a/src/features/study/api/types.ts b/src/features/study/api/types.ts index a77cbd89..70fd16c9 100644 --- a/src/features/study/api/types.ts +++ b/src/features/study/api/types.ts @@ -59,8 +59,8 @@ export interface StudyCalendarDay { export interface MonthlyCalendarResponse { calendar: StudyCalendarDay[]; - monthlyCompletedCount: number; - totalCompletedCount: number; + monthlyCompletedCount?: number; + totalCompletedCount?: number; } export interface PostDailyRetrospectRequest { diff --git a/src/features/study/ui/data-selector.tsx b/src/features/study/ui/data-selector.tsx index e1c94d2c..0f75625e 100644 --- a/src/features/study/ui/data-selector.tsx +++ b/src/features/study/ui/data-selector.tsx @@ -1,7 +1,8 @@ 'use client'; -import { startOfWeek, addDays, format, isSameDay } from 'date-fns'; +import { addDays, format, isSameDay } from 'date-fns'; import { useMemo } from 'react'; +import { getKoreaDisplayMonday } from '@/shared/lib/time'; interface Props { value: Date; @@ -9,12 +10,12 @@ interface Props { } export default function DateSelector({ value, onChange }: Props) { - const today = new Date(); - const monday = startOfWeek(today, { weekStartsOn: 1 }); const dayLabels = ['월', '화', '수', '목', '금']; + + const displayMonday = useMemo(() => getKoreaDisplayMonday(), []); const dates = useMemo( - () => Array.from({ length: 5 }, (_, i) => addDays(monday, i)), - [monday], + () => Array.from({ length: 5 }, (_, i) => addDays(displayMonday, i)), + [displayMonday], ); return ( diff --git a/src/features/study/ui/study-card.tsx b/src/features/study/ui/study-card.tsx index 0edb1e8d..04082f97 100644 --- a/src/features/study/ui/study-card.tsx +++ b/src/features/study/ui/study-card.tsx @@ -1,7 +1,12 @@ 'use client'; import { getMonth, getDay, startOfWeek, getDate } from 'date-fns'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; +import { + formatKoreaYMD, + getKoreaDate, + getKoreaDisplayMonday, +} from '@/shared/lib/time'; import DateSelector from './data-selector'; import TodayStudyCard from './today-study-card'; import StudyListSection from '../../../widgets/home/study-list-table'; @@ -12,13 +17,13 @@ import ReservationList from '../participation/ui/reservation-list'; // 스터디 주차 구하는 함수 function getWeekly(date: Date): { month: number; week: number } { const weekStartsOn = 0; - const target = new Date(date); - const currentWeekStart = startOfWeek(target, { weekStartsOn }); + const targetKST = getKoreaDate(date); + const currentWeekStart = startOfWeek(targetKST, { weekStartsOn }); const baseMonth = getMonth(currentWeekStart); // 목요일 기준 월의 첫 주 시작일 계산 - const firstOfMonth = new Date(target.getFullYear(), baseMonth, 1); + const firstOfMonth = new Date(targetKST.getFullYear(), baseMonth, 1); const firstWeekStart = startOfWeek(firstOfMonth, { weekStartsOn }); const firstDayOfWeek = getDay(firstOfMonth); const officialFirstWeekStart = @@ -56,17 +61,18 @@ function getWeekly(date: Date): { month: number; week: number } { export default function StudyCard() { const [selectedDate, setSelectedDate] = useState(new Date()); - const offset = selectedDate.getTimezoneOffset() * 60000; // ms단위라 60000곱해줌 - const dateOffset = new Date(selectedDate.getTime() - offset); - - const studyDate = dateOffset.toISOString().split('T')[0]; + const studyDate = formatKoreaYMD(selectedDate); const { data: status } = useStudyStatusQuery(); const { data: participationData } = useWeeklyParticipation(studyDate); const isParticipate = participationData?.isParticipate ?? false; - const { month, week } = getWeekly(selectedDate); + const displayMonday = useMemo( + () => getKoreaDisplayMonday(selectedDate), + [selectedDate], + ); + const { month, week } = getWeekly(displayMonday); return ( <> diff --git a/src/shared/lib/time.ts b/src/shared/lib/time.ts index 662bba84..9ef0d565 100644 --- a/src/shared/lib/time.ts +++ b/src/shared/lib/time.ts @@ -1,10 +1,13 @@ import { + addDays, differenceInDays, differenceInHours, differenceInMinutes, + format, + getDay, parseISO, + startOfWeek, } from 'date-fns'; -import { format } from 'path'; export const getKoreaDate = (targetDate?: Date) => { const date = targetDate || new Date(); @@ -17,6 +20,9 @@ export const getKoreaDate = (targetDate?: Date) => { return koreaNow; }; +export const formatKoreaYMD = (targetDate?: Date) => + format(getKoreaDate(targetDate), 'yyyy-MM-dd'); + export const formatKoreaRelativeTime = (targetDateStr: string): string => { const targetDate = parseISO(targetDateStr); const koreaTarget = getKoreaDate(targetDate); // 한국 시간 변환 @@ -35,3 +41,11 @@ export const formatKoreaRelativeTime = (targetDateStr: string): string => { return targetDateStr; }; + +export const getKoreaDisplayMonday = (base?: Date) => { + const todayKST = getKoreaDate(base); + const monday = startOfWeek(todayKST, { weekStartsOn: 1 }); // 월요일 시작 + const dow = getDay(todayKST); // 0=일, 6=토 + + return dow === 0 || dow === 6 ? addDays(monday, 7) : monday; +}; diff --git a/src/widgets/home/calendar.tsx b/src/widgets/home/calendar.tsx index 1d4769a0..402fd09d 100644 --- a/src/widgets/home/calendar.tsx +++ b/src/widgets/home/calendar.tsx @@ -79,6 +79,11 @@ const Calendar = (props: React.ComponentProps) => { if (isLoading) return
로딩 중...
; + const monthlyCount = data?.monthlyCompletedCount; + const totalCount = data?.totalCompletedCount; + const showFooter = + typeof monthlyCount === 'number' && typeof totalCount === 'number'; + return (
) => { day: 'text-center font-designer-14m rounded-full', }} footer={ -
-
- {month}월은 {data.monthlyCompletedCount}번의 스터디를 완료했어요. -
-
- 총 {data.totalCompletedCount}번의 스터디를 완료했어요. + showFooter ? ( +
+
+ {month}월은 {data.monthlyCompletedCount}번의 스터디를 + 완료했어요. +
+
+ 총 {data.totalCompletedCount}번의 스터디를 완료했어요. +
-
+ ) : undefined } {...props} />