Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions src/features/study/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ export interface StudyCalendarDay {

export interface MonthlyCalendarResponse {
calendar: StudyCalendarDay[];
monthlyCompletedCount: number;
totalCompletedCount: number;
monthlyCompletedCount?: number;
totalCompletedCount?: number;
}

export interface PostDailyRetrospectRequest {
Expand Down
11 changes: 6 additions & 5 deletions src/features/study/ui/data-selector.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
'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;
onChange: (date: Date) => void;
}

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 (
Expand Down
24 changes: 15 additions & 9 deletions src/features/study/ui/study-card.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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 =
Expand Down Expand Up @@ -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 (
<>
Expand Down
16 changes: 15 additions & 1 deletion src/shared/lib/time.ts
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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); // 한국 시간 변환
Expand All @@ -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;
};
22 changes: 15 additions & 7 deletions src/widgets/home/calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ const Calendar = (props: React.ComponentProps<typeof ShadcnCalendar>) => {

if (isLoading) return <div>로딩 중...</div>;

const monthlyCount = data?.monthlyCompletedCount;
const totalCount = data?.totalCompletedCount;
const showFooter =
typeof monthlyCount === 'number' && typeof totalCount === 'number';

return (
<div
className={cn(
Expand Down Expand Up @@ -111,14 +116,17 @@ const Calendar = (props: React.ComponentProps<typeof ShadcnCalendar>) => {
day: 'text-center font-designer-14m rounded-full',
}}
footer={
<div className="flex w-full flex-col gap-75 pt-200">
<div className="rounded-100 bg-background-alternative font-designer-14m text-text-default px-150 py-100 text-ellipsis">
{month}월은 {data.monthlyCompletedCount}번의 스터디를 완료했어요.
</div>
<div className="rounded-100 bg-background-alternative font-designer-14m text-text-default px-150 py-100 text-ellipsis">
총 {data.totalCompletedCount}번의 스터디를 완료했어요.
showFooter ? (
<div className="flex w-full flex-col gap-75 pt-200">
<div className="rounded-100 bg-background-alternative font-designer-14m text-text-default px-150 py-100 text-ellipsis">
{month}월은 {data.monthlyCompletedCount}번의 스터디를
완료했어요.
</div>
<div className="rounded-100 bg-background-alternative font-designer-14m text-text-default px-150 py-100 text-ellipsis">
총 {data.totalCompletedCount}번의 스터디를 완료했어요.
</div>
</div>
</div>
) : undefined
}
{...props}
/>
Expand Down