Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
"use client";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

_components/ 디렉터리의 'use client' 사용을 검토해주세요.

경로 규칙에 따르면 _components/'use client' 없이 동작 가능한 순수 UI여야 합니다. 이 컴포넌트는 onClick 핸들러가 추가되어 클라이언트 렌더링이 필수가 되었습니다. 인터랙티브 로직을 _containers/로 분리하거나, 이 컴포넌트가 여전히 props 기반 UI로서 _components/에 머무는 것이 타당한지 팀과 논의해보시길 권장합니다.

As per path instructions, _components/는 props만 받는 순수 UI — 'use client' 없이 동작 가능해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
at line 1, StatisticsCalendar의 onClick 등 인터랙티브 로직을 _containers/로 이동해
_components/가 순수 props 기반 UI로 동작하도록 분리하거나, 구조상 해당 컴포넌트를 _containers/로 옮기고 'use
client'를 유지하세요.

Source: Path instructions


import {
StatisticsClockEmptyIcon,
StatisticsClockFilledIcon,
StatisticsClockLightIcon,
StatisticsClockOutlineIcon,
} from "@repo/timo-design-system/icons";
import { cn } from "@repo/timo-design-system/utils";
import { useLocale } from "next-intl";

import type { StatisticsCalendarResponse } from "@/app/[locale]/(main)/statistics/_types/statistics";

Expand Down Expand Up @@ -52,27 +55,32 @@ const WEEKDAYS = [

interface StatisticsCalendarProps {
currentMonth: Date;
selectedDate: Date;
onSelectDate: (date: Date) => void;
calendarData: StatisticsCalendarResponse;
}

export const StatisticsCalendar = ({
currentMonth,
selectedDate,
onSelectDate,
calendarData,
}: StatisticsCalendarProps) => {
const locale = useLocale();
const today = new Date(calendarData.today);
const calendarDates = getCalendarDates(currentMonth);
const firstDayOffset = getFirstDayOffset(currentMonth);
const completionRateByDate = new Map(
calendarData.days.map(({ date, completionRate }) => [date, completionRate]),
);
const todayLabel = formatStatisticsCalendarDate(today);
const todayLabel = formatStatisticsCalendarDate(today, locale);

return (
<section className="w-full px-14.75 pt-10 pb-13">
<div className="w-199.5">
<div className="mb-17.25">
<h1 className="typo-headline-b-30 text-timo-gray-900">
{formatStatisticsMonth(currentMonth)}
{formatStatisticsMonth(currentMonth, locale)}
</h1>

<p className="typo-headline-m-14 text-timo-black mt-2">
Expand Down Expand Up @@ -100,17 +108,36 @@ export const StatisticsCalendar = ({

{calendarDates.map((calendarDate) => {
const dateKey = formatDateKey(calendarDate.date);
const isSelected = dateKey === formatDateKey(selectedDate);
const completionRate = completionRateByDate.get(dateKey) ?? null;
const status = getIconStatus(completionRate);
const Icon = STATUS_ICON[status];

return (
<div key={dateKey} className="flex flex-col items-center gap-2.5">
<Icon />
<span className="typo-body-sb-11 text-timo-gray-700">
<button
key={dateKey}
type="button"
className="flex flex-col items-center gap-2.5"
onClick={() => onSelectDate(calendarDate.date)}
>
<span
className={cn(
"rounded-full",
isSelected &&
"drop-shadow-[0_0_10px_var(--color-timo-blue-75)]",
)}
>
<Icon />
</span>
<span
className={cn(
"typo-body-sb-11 text-timo-gray-700",
isSelected && "text-timo-blue-300",
)}
>
{calendarDate.day}
</span>
</div>
</button>
Comment on lines +117 to +140

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

날짜 버튼에 aria-label 추가를 권장합니다.

버튼 내부에 날짜 숫자 텍스트가 있지만, 스크린 리더 사용자에게 "15"만 들리는 것보다 "2024년 1월 15일"과 같은 맥락 정보가 포함된 aria-label이 접근성에 도움이 됩니다. formatStatisticsCalendarDate 유틸을 활용하면 쉽게 구현할 수 있습니다.

🛡️ 제안하는 접근성 개선
              <button
                key={dateKey}
                type="button"
+               aria-label={formatStatisticsCalendarDate(calendarDate.date, locale)}
                className="flex flex-col items-center gap-2.5"
                onClick={() => onSelectDate(calendarDate.date)}
              >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
key={dateKey}
type="button"
className="flex flex-col items-center gap-2.5"
onClick={() => onSelectDate(calendarDate.date)}
>
<span
className={cn(
"rounded-full",
isSelected &&
"drop-shadow-[0_0_10px_var(--color-timo-blue-75)]",
)}
>
<Icon />
</span>
<span
className={cn(
"typo-body-sb-11 text-timo-gray-700",
isSelected && "text-timo-blue-300",
)}
>
{calendarDate.day}
</span>
</div>
</button>
<button
key={dateKey}
type="button"
aria-label={formatStatisticsCalendarDate(calendarDate.date, locale)}
className="flex flex-col items-center gap-2.5"
onClick={() => onSelectDate(calendarDate.date)}
>
<span
className={cn(
"rounded-full",
isSelected &&
"drop-shadow-[0_0_10px_var(--color-timo-blue-75)]",
)}
>
<Icon />
</span>
<span
className={cn(
"typo-body-sb-11 text-timo-gray-700",
isSelected && "text-timo-blue-300",
)}
>
{calendarDate.day}
</span>
</button>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
around lines 117 - 140, 날짜 선택 버튼에 전체 날짜 맥락이 포함된 접근성 라벨이 없습니다.
StatisticsCalendar의 날짜 버튼에 formatStatisticsCalendarDate(calendarDate.date)를 사용한
aria-label을 추가해 스크린 리더가 연도·월·일을 함께 읽도록 수정하세요.

);
})}
Comment on lines 109 to 142

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

formatDateKey(selectedDate)를 루프 밖으로 이동해주세요.

formatDateKey(selectedDate)calendarDates.map 콜백 내부(Line 111)에서 매 반복마다 호출됩니다. selectedDate는 루프 내에서 변하지 않으므로 컴포넌트 본문 상단에서 한 번만 계산하는 것이 효율적입니다.

♻️ 제안하는 리팩터링
  const todayLabel = formatStatisticsCalendarDate(today, locale);
+ const selectedDateKey = formatDateKey(selectedDate);

  return (
    <section className="w-full px-14.75 pt-10 pb-13">
      ...
          {calendarDates.map((calendarDate) => {
            const dateKey = formatDateKey(calendarDate.date);
-           const isSelected = dateKey === formatDateKey(selectedDate);
+           const isSelected = dateKey === selectedDateKey;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{calendarDates.map((calendarDate) => {
const dateKey = formatDateKey(calendarDate.date);
const isSelected = dateKey === formatDateKey(selectedDate);
const completionRate = completionRateByDate.get(dateKey) ?? null;
const status = getIconStatus(completionRate);
const Icon = STATUS_ICON[status];
return (
<div key={dateKey} className="flex flex-col items-center gap-2.5">
<Icon />
<span className="typo-body-sb-11 text-timo-gray-700">
<button
key={dateKey}
type="button"
className="flex flex-col items-center gap-2.5"
onClick={() => onSelectDate(calendarDate.date)}
>
<span
className={cn(
"rounded-full",
isSelected &&
"drop-shadow-[0_0_10px_var(--color-timo-blue-75)]",
)}
>
<Icon />
</span>
<span
className={cn(
"typo-body-sb-11 text-timo-gray-700",
isSelected && "text-timo-blue-300",
)}
>
{calendarDate.day}
</span>
</div>
</button>
);
})}
{calendarDates.map((calendarDate) => {
const dateKey = formatDateKey(calendarDate.date);
const isSelected = dateKey === selectedDateKey;
const completionRate = completionRateByDate.get(dateKey) ?? null;
const status = getIconStatus(completionRate);
const Icon = STATUS_ICON[status];
return (
<button
key={dateKey}
type="button"
className="flex flex-col items-center gap-2.5"
onClick={() => onSelectDate(calendarDate.date)}
>
<span
className={cn(
"rounded-full",
isSelected &&
"drop-shadow-[0_0_10px_var(--color-timo-blue-75)]",
)}
>
<Icon />
</span>
<span
className={cn(
"typo-body-sb-11 text-timo-gray-700",
isSelected && "text-timo-blue-300",
)}
>
{calendarDate.day}
</span>
</button>
);
})}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
around lines 109 - 142, Move the invariant formatDateKey(selectedDate)
calculation outside the calendarDates.map callback, store it in a clearly named
variable near the component’s other derived values, and compare each dateKey
against that variable when computing isSelected.

</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
"use client";

import { TagIcon } from "@repo/timo-design-system/ui";
import { cn } from "@repo/timo-design-system/utils";
import { useTranslations } from "next-intl";

import type {
StatisticsDayDetail,
Expand Down Expand Up @@ -29,9 +32,12 @@ type StatisticsSidePanelProps =
const SIDE_PANEL_CLASS_NAME =
"border-timo-gray-500 min-h-full w-76 border-l text-timo-black";

const getDiffLabel = (diffMinutes: number) => {
if (diffMinutes > 0) return "+초과";
if (diffMinutes < 0) return "-단축";
const getDiffLabel = (
diffMinutes: number,
t: ReturnType<typeof useTranslations<"Statistics.sidePanel">>,
) => {
if (diffMinutes > 0) return t("overtime");
if (diffMinutes < 0) return t("shortened");
return "";
};

Expand All @@ -42,34 +48,36 @@ const getDiffClassName = (diffMinutes: number) => {
};

export const StatisticsSidePanel = (props: StatisticsSidePanelProps) => {
const t = useTranslations("Statistics.sidePanel");

if (props.variant === "month") {
const { summary } = props;

return (
<aside className={cn(SIDE_PANEL_CLASS_NAME, "pt-20.75 pb-148")}>
<aside className={cn(SIDE_PANEL_CLASS_NAME, "w-76 pt-20.75 pb-148")}>
<div className="flex h-46.25 flex-col gap-5">
<div className="px-7.5 py-2.5">
<SummaryTimeBlock
label="이번 달의 총 기록 시간"
label={t("monthTotalRecordTime")}
minutes={summary.totalRecordMinutes}
/>
</div>

<dl className="typo-headline-r-14 flex flex-col gap-2 px-7.5">
<div className="flex items-center gap-3">
<dt className="text-timo-gray-900">활동일</dt>
<dd>{summary.activeDayCount}일</dd>
<dt className="text-timo-gray-900">{t("activeDays")}</dt>
<dd>{t("activeDayCount", { count: summary.activeDayCount })}</dd>
</div>

<div className="flex items-center gap-3">
<dt className="text-timo-gray-900">일평균</dt>
<dt className="text-timo-gray-900">{t("dailyAverage")}</dt>
<dd>
{formatStatisticsHourText(summary.averageRecordedMinutes)}
</dd>
</div>

<div className="flex items-center gap-3">
<dt className="text-timo-gray-900">누적 태스크</dt>
<dt className="text-timo-gray-900">{t("accumulatedTasks")}</dt>
<dd>
{summary.completedTodoCount}/{summary.totalTodoCount}
</dd>
Expand All @@ -88,7 +96,7 @@ export const StatisticsSidePanel = (props: StatisticsSidePanelProps) => {
<h2 className="typo-headline-b-24">{detail.date}</h2>

<SummaryTimeBlock
label="오늘의 총 기록 시간"
label={t("dayTotalRecordTime")}
minutes={detail.totalRecordMinutes}
/>
</div>
Expand Down Expand Up @@ -116,11 +124,12 @@ export const StatisticsSidePanel = (props: StatisticsSidePanelProps) => {
</strong>

<span>
계획: {formatStatisticsClockText(todo.estimatedTimeMinutes)}
{t("plan")}:{" "}
{formatStatisticsClockText(todo.estimatedTimeMinutes)}
</span>

<span className={getDiffClassName(diffMinutes)}>
{getDiffLabel(diffMinutes)}
{getDiffLabel(diffMinutes, t)}
</span>
</div>
</li>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,69 @@
"use client";

import { useLocale } from "next-intl";
import { useState } from "react";

import { StatisticsCalendar } from "@/app/[locale]/(main)/statistics/_components/StatisticsCalendar";
import { StatisticsSidePanel } from "@/app/[locale]/(main)/statistics/_components/StatisticsSidePanel";
import { StatisticsHeaderContainer } from "@/app/[locale]/(main)/statistics/_containers/StatisticsHeaderContainer";
import { MOCK_STATISTICS_CALENDAR } from "@/app/[locale]/(main)/statistics/_mocks/statistics-calendar";
import {
MOCK_STATISTICS_CALENDAR,
MOCK_STATISTICS_DAY_DETAILS,
MOCK_STATISTICS_MONTH_SUMMARY,
} from "@/app/[locale]/(main)/statistics/_mocks/statistics-calendar";
import { formatStatisticsSidePanelDate } from "@/app/[locale]/(main)/statistics/_utils/format-statistics-date";
import { formatDateKey } from "@/app/[locale]/(main)/statistics/_utils/statistics-calendar";

type StatisticsPanelMode = "month" | "day";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

타입 alias 명명 컨벤션을 준수해 주세요.

StatisticsPanelMode는 유니언 타입 alias이므로, 컨벤션에 따라 Types 접미사를 붙여 StatisticsPanelModeTypes로 명명해야 합니다. 이 파일 내에서만 사용되므로 변경 범위가 제한적입니다.

As per coding guidelines, **/*.{ts,tsx}: "type alias는 유니언·튜플·리터럴에만 사용, 접미사 Types"

♻️ 제안 수정안
-type StatisticsPanelMode = "month" | "day";
+type StatisticsPanelModeTypes = "month" | "day";

 export const StatisticsContainer = () => {
   const locale = useLocale();
   const [currentMonth, setCurrentMonth] = useState(() => new Date());
   const [selectedDate, setSelectedDate] = useState(
     () => new Date(MOCK_STATISTICS_CALENDAR.today),
   );
-  const [panelMode, setPanelMode] = useState<StatisticsPanelMode>("month");
+  const [panelMode, setPanelMode] = useState<StatisticsPanelModeTypes>("month");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
type StatisticsPanelMode = "month" | "day";
type StatisticsPanelModeTypes = "month" | "day";
export const StatisticsContainer = () => {
const locale = useLocale();
const [currentMonth, setCurrentMonth] = useState(() => new Date());
const [selectedDate, setSelectedDate] = useState(
() => new Date(MOCK_STATISTICS_CALENDAR.today),
);
const [panelMode, setPanelMode] = useState<StatisticsPanelModeTypes>("month");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/statistics/_containers/StatisticsContainer.tsx
at line 17, Rename the union type alias StatisticsPanelMode to
StatisticsPanelModeTypes and update all references within
StatisticsContainer.tsx accordingly.

Source: Coding guidelines


export const StatisticsContainer = () => {
const locale = useLocale();
const [currentMonth, setCurrentMonth] = useState(() => new Date());
const [selectedDate, setSelectedDate] = useState(
() => new Date(MOCK_STATISTICS_CALENDAR.today),
);
Comment on lines +22 to +24

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

fd -i 'StatisticsContainer.tsx|formatDateKey|MOCK_STATISTICS_CALENDAR' apps/timo-web app packages . && \
printf '\n--- outline ---\n' && \
ast-grep outline apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsContainer.tsx --view expanded && \
printf '\n--- search formatDateKey ---\n' && \
rg -n "formatDateKey|MOCK_STATISTICS_CALENDAR|selectedDateKey|today" apps/timo-web/app packages -g '!**/node_modules/**'

Repository: Team-Timo/Timo-client

Length of output: 347


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- files ---'
git ls-files 'apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsContainer.tsx' \
  'apps/timo-web/app' 'packages' | sed -n '1,120p'

printf '\n%s\n' '--- outline StatisticsContainer.tsx ---'
ast-grep outline 'apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsContainer.tsx' --view expanded

printf '\n%s\n' '--- search formatDateKey / mock data ---'
rg -n "formatDateKey|MOCK_STATISTICS_CALENDAR|selectedDateKey|today" apps/timo-web/app packages -g '!**/node_modules/**'

Repository: Team-Timo/Timo-client

Length of output: 15349


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- StatisticsContainer.tsx ---'
sed -n '1,140p' 'apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsContainer.tsx'

printf '\n%s\n' '--- statistics-calendar util ---'
sed -n '1,160p' 'apps/timo-web/app/[locale]/(main)/statistics/_utils/statistics-calendar.ts'

printf '\n%s\n' '--- statistics mocks ---'
sed -n '1,140p' 'apps/timo-web/app/[locale]/(main)/statistics/_mocks/statistics-calendar.ts'

printf '\n%s\n' '--- StatisticsCalendar.tsx date usage ---'
sed -n '1,180p' 'apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx'

printf '\n%s\n' '--- date parse probe ---'
python3 - <<'PY'
from datetime import datetime, timezone
from zoneinfo import ZoneInfo

s = "2026-07-10"
utc = datetime.fromisoformat(s).replace(tzinfo=timezone.utc)
for tz_name in ["UTC", "Asia/Seoul", "America/New_York", "America/Los_Angeles"]:
    dt = utc.astimezone(ZoneInfo(tz_name))
    print(tz_name, dt.date().isoformat())
PY

Repository: Team-Timo/Timo-client

Length of output: 12844


🏁 Script executed:

#!/bin/bash
set -euo pipefail

node - <<'JS'
const inputs = ["2026-07-10", "2026-07-10T00:00:00"];
const timezones = ["UTC", "Asia/Seoul", "America/New_York", "America/Los_Angeles"];

for (const tz of timezones) {
  process.env.TZ = tz;
  console.log(`TZ=${tz}`);
  for (const input of inputs) {
    const d = new Date(input);
    const y = d.getFullYear();
    const m = String(d.getMonth() + 1).padStart(2, "0");
    const day = String(d.getDate()).padStart(2, "0");
    console.log(`  ${input} -> ${y}-${m}-${day} | ${d.toISOString()}`);
  }
}
JS

Repository: Team-Timo/Timo-client

Length of output: 691


MOCK_STATISTICS_CALENDAR.today은 로컬 시간으로 생성해 주세요.
new Date("2026-07-10")은 UTC 자정으로 해석돼서, formatDateKey()의 로컬 getDate()와 어긋날 수 있습니다. 그래서 UTC-5/UTC-8 환경에서는 selectedDateKey가 전날로 밀리고, MOCK_STATISTICS_DAY_DETAILS를 못 찾아 빈 패널이 나옵니다. new Date(2026, 6, 10)처럼 로컬 기준으로 만들거나, mock 문자열에 시간 정보를 명시해 두면 됩니다.
MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/statistics/_containers/StatisticsContainer.tsx
around lines 22 - 24, MOCK_STATISTICS_CALENDAR.today is parsed as UTC and can
produce the previous local date. Update the mock date initialization used by
StatisticsContainer and the definition of MOCK_STATISTICS_CALENDAR to construct
July 10, 2026 in local time (for example, using numeric Date arguments) or
include an explicit local-time offset, while preserving formatDateKey
compatibility.

const [panelMode, setPanelMode] = useState<StatisticsPanelMode>("month");
const selectedDateKey = formatDateKey(selectedDate);
const selectedDetailBase = MOCK_STATISTICS_DAY_DETAILS[selectedDateKey] ?? {
date: selectedDateKey,
totalRecordMinutes: 0,
todos: [],
};
const selectedDetail = {
...selectedDetailBase,
date: formatStatisticsSidePanelDate(selectedDate, locale),
};
const handleChangeMonth: typeof setCurrentMonth = (value) => {
setCurrentMonth(value);
setPanelMode("month");
};
const handleSelectDate = (date: Date) => {
setSelectedDate(date);
setPanelMode("day");
};

return (
<>
<div className="flex h-full flex-col">
<StatisticsHeaderContainer
currentMonth={currentMonth}
onChangeMonth={setCurrentMonth}
/>
<StatisticsCalendar
currentMonth={currentMonth}
calendarData={MOCK_STATISTICS_CALENDAR}
onChangeMonth={handleChangeMonth}
/>
</>
<div className="flex min-h-0 flex-1">
<StatisticsCalendar
currentMonth={currentMonth}
selectedDate={selectedDate}
onSelectDate={handleSelectDate}
calendarData={MOCK_STATISTICS_CALENDAR}
/>
{panelMode === "month" ? (
<StatisticsSidePanel
variant="month"
summary={MOCK_STATISTICS_MONTH_SUMMARY}
/>
) : (
<StatisticsSidePanel variant="day" detail={selectedDetail} />
)}
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"use client";

import { useLocale } from "next-intl";

import type { Dispatch, SetStateAction } from "react";

import { Header } from "@/components/layout/header/Header";
Expand All @@ -10,10 +12,6 @@ interface StatisticsHeaderContainerProps {
onChangeMonth: Dispatch<SetStateAction<Date>>;
}

const MONTH_LABEL_FORMATTER = new Intl.DateTimeFormat("en-US", {
month: "long",
});

const addMonths = (date: Date, amount: number) =>
new Date(date.getFullYear(), date.getMonth() + amount, 1);

Expand All @@ -22,6 +20,10 @@ export const StatisticsHeaderContainer = ({
onChangeMonth,
}: StatisticsHeaderContainerProps) => {
const { isOpen, toggle } = useNavigationSidebar();
const locale = useLocale();
const monthLabel = new Intl.DateTimeFormat(locale, {
month: "long",
}).format(currentMonth);
Comment on lines +23 to +26

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

formatStatisticsMonth 유틸을 재사용해주세요.

StatisticsCalendar.tsx에서 이미 formatStatisticsMonth(currentMonth, locale)를 사용하고 있는데, 이 컨테이너에서는 동일한 로직을 new Intl.DateTimeFormat(locale, { month: "long" }).format(currentMonth)로 인라인하고 있습니다. _utils/format-statistics-date.tsformatStatisticsMonth를 import하여 중복을 제거해주세요.

♻️ 제안하는 리팩터링
+ import { formatStatisticsMonth } from "../_utils/format-statistics-date";
  ...
  const locale = useLocale();
- const monthLabel = new Intl.DateTimeFormat(locale, {
-   month: "long",
- }).format(currentMonth);
+ const monthLabel = formatStatisticsMonth(currentMonth, locale);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const locale = useLocale();
const monthLabel = new Intl.DateTimeFormat(locale, {
month: "long",
}).format(currentMonth);
import { formatStatisticsMonth } from "../_utils/format-statistics-date";
const locale = useLocale();
- const monthLabel = new Intl.DateTimeFormat(locale, {
- month: "long",
const monthLabel = formatStatisticsMonth(currentMonth, locale);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/timo-web/app/`[locale]/(main)/statistics/_containers/StatisticsHeaderContainer.tsx
around lines 23 - 26, StatisticsHeaderContainer의 인라인 월 포맷팅을 제거하고,
_utils/format-statistics-date.ts의 formatStatisticsMonth를 import하여
formatStatisticsMonth(currentMonth, locale)로 monthLabel을 생성하도록 변경하세요.


const handlePrev = () => onChangeMonth((prev) => addMonths(prev, -1));
const handleNext = () => onChangeMonth((prev) => addMonths(prev, 1));
Expand All @@ -34,7 +36,7 @@ export const StatisticsHeaderContainer = ({
<Header.WeeklyNav
onPrev={handlePrev}
onNext={handleNext}
label={MONTH_LABEL_FORMATTER.format(currentMonth)}
label={monthLabel}
/>
</>
}
Expand Down

This file was deleted.

Loading
Loading