Skip to content

[FEAT] 통계 페이지 뷰 구현 (PR수정중)#143

Open
yumin-kim2 wants to merge 4 commits into
developfrom
feat/web/142-statistics-calendar-view
Open

[FEAT] 통계 페이지 뷰 구현 (PR수정중)#143
yumin-kim2 wants to merge 4 commits into
developfrom
feat/web/142-statistics-calendar-view

Conversation

@yumin-kim2

Copy link
Copy Markdown
Contributor

ISSUE 🔗



What is this PR? 🔍



To Reviewers



Screenshot 📷



Test Checklist ✔

  • TODO
  • TODO
  • TODO

@yumin-kim2 yumin-kim2 linked an issue Jul 10, 2026 that may be closed by this pull request
4 tasks
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
timo Ready Ready Preview, Comment Jul 10, 2026 9:08pm

@github-actions github-actions Bot added the ⏰ Timo-web Timo 웹 서비스 label Jul 10, 2026
@github-actions github-actions Bot requested review from jjangminii and kimminna July 10, 2026 21:08
@github-actions github-actions Bot added ✨ Feature 새로운 기능(기능성) 구현 ♣️ 유민 유민양 labels Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

통계 페이지에 날짜 선택과 월·일 사이드 패널 전환이 추가되었습니다. 캘린더와 헤더는 현재 로케일을 사용하며, 사이드 패널 문구는 영어·한국어 번역 키로 표시됩니다.

Changes

통계 페이지 상호작용

Layer / File(s) Summary
통계 목 데이터와 날짜 포맷
apps/timo-web/app/[locale]/(main)/statistics/_mocks/statistics-calendar.ts, apps/timo-web/app/[locale]/(main)/statistics/_utils/format-statistics-date.ts
캘린더 목 데이터가 월별 요약과 일별 상세 데이터를 포함하도록 확장되고, 사이드 패널용 날짜 포맷 함수가 추가되었습니다.
월·일 상태와 패널 연결
apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsContainer.tsx
컨테이너가 현재 월, 선택 날짜, 패널 모드를 관리하며 월 요약 또는 선택 날짜 상세 정보를 사이드 패널에 전달합니다.
캘린더와 헤더 로케일 표시
apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx, apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsHeaderContainer.tsx
날짜 항목이 선택 가능한 버튼으로 변경되고 선택 상태가 표시되며, 월·오늘 라벨이 현재 로케일로 포맷됩니다.
사이드 패널 번역
apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsSidePanel.tsx, apps/timo-web/messages/en.json, apps/timo-web/messages/ko.json
월·일 통계 라벨과 계획, 초과·단축 문구가 번역 키를 통해 표시됩니다.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant 사용자
  participant StatisticsCalendar
  participant StatisticsContainer
  participant StatisticsSidePanel
  사용자->>StatisticsCalendar: 날짜 버튼 클릭
  StatisticsCalendar->>StatisticsContainer: 선택 날짜 전달
  StatisticsContainer->>StatisticsContainer: 일별 상세 데이터 조회
  StatisticsContainer->>StatisticsSidePanel: 일별 패널 데이터 전달
  StatisticsSidePanel-->>사용자: 선택 날짜 통계 표시
Loading

Possibly related issues

Possibly related PRs

Suggested labels: ♦️ 민아

Suggested reviewers: ehye1, jjangminii

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive 설명은 대부분 템플릿 자리표시자만 있어 변경 내용을 판단하기 어렵습니다. 실제 구현 내용(캘린더/사이드 패널 연결, 다국어, 선택 상태)을 간단히 적어 주세요.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 통계 페이지 뷰 구현이라는 실제 변경 내용을 잘 요약합니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/web/142-statistics-calendar-view

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

Timo Performance Report

Bundle Size — timo-web
라우트 크기 First Load JS
/[locale]/home 72.02 kB 🟡 277.83 kB
/[locale]/today 55.42 kB 🟡 261.24 kB
/[locale]/focus 52.27 kB 🟡 258.08 kB
/[locale]/settings/account 0 B 🟡 205.81 kB
/[locale]/settings 0 B 🟡 205.81 kB
/[locale]/settings/policy 0 B 🟡 205.81 kB
/[locale]/statistics 52.24 kB 🟡 258.06 kB
/[locale]/[...rest] 0 B 🟡 205.81 kB
/[locale]/onboarding 107.39 kB 🟡 313.21 kB
/[locale] 0 B 🟡 205.81 kB

공유 번들: 205.81 kB
🟢 < 200kB  |  🟡 < 350kB  |  🔴 ≥ 350kB (First Load JS · gzip)

Lighthouse — timo-web
URL Perf A11y LCP CLS TBT
/en/home 🔴 69 🟢 96 🔴 14.0s 🟢 0.000 🟡 299ms
/en/today 🟡 73 🟡 93 🔴 13.7s 🟢 0.000 🟢 171ms
/en/focus 🟡 71 🟡 91 🔴 13.7s 🟢 0.000 🟡 215ms
/en/statistics 🟡 71 🟢 96 🔴 13.6s 🟢 0.000 🟡 231ms

Perf ≥ 70 / A11y ≥ 85 목표
LCP 🟢 < 2.5s 🟡 < 4s 🔴 ≥ 4s  |  CLS 🟢 < 0.1 🟡 < 0.25 🔴 ≥ 0.25  |  TBT 🟢 < 200ms 🟡 < 600ms 🔴 ≥ 600ms

Image Optimization — timo-web

public/ 디렉토리에 이미지가 없습니다.

측정 커밋: 1149bf9

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🤖 Prompt for all review comments with 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.

Inline comments:
In
`@apps/timo-web/app/`[locale]/(main)/statistics/_components/StatisticsCalendar.tsx:
- Line 1: StatisticsCalendar의 onClick 등 인터랙티브 로직을 _containers/로 이동해
_components/가 순수 props 기반 UI로 동작하도록 분리하거나, 구조상 해당 컴포넌트를 _containers/로 옮기고 'use
client'를 유지하세요.
- Around line 117-140: 날짜 선택 버튼에 전체 날짜 맥락이 포함된 접근성 라벨이 없습니다. StatisticsCalendar의
날짜 버튼에 formatStatisticsCalendarDate(calendarDate.date)를 사용한 aria-label을 추가해 스크린
리더가 연도·월·일을 함께 읽도록 수정하세요.
- Around line 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.

In
`@apps/timo-web/app/`[locale]/(main)/statistics/_containers/StatisticsContainer.tsx:
- Line 17: Rename the union type alias StatisticsPanelMode to
StatisticsPanelModeTypes and update all references within
StatisticsContainer.tsx accordingly.
- Around line 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.

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

In `@apps/timo-web/app/`[locale]/(main)/statistics/_mocks/statistics-calendar.ts:
- Around line 55-61: MOCK_STATISTICS_MONTH_SUMMARY의 월간 요약 값이 일별 목 데이터와 일관되지
않습니다. MOCK_COMPLETION_RATES의 유효 일수와 일별 상세 데이터의 todo 개수를 기준으로 activeDayCount를 26,
totalTodoCount를 52로 맞추고, 일별 totalRecordMinutes 합계인 5340과 activeDayCount를 기준으로
totalRecordMinutes 및 averageRecordedMinutes를 조정하세요.

In `@apps/timo-web/messages/en.json`:
- Line 43: Update the activeDayCount message to use ICU pluralization, selecting
the singular “day” when count equals 1 and the plural “days” otherwise, while
preserving the count value in both variants.
- Line 48: Update the "overtime" translation value in the messages/en.json
localization resource from "+Overdue" to "+Overtime" (or "+Over") to accurately
represent exceeding the planned time.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 32ef4fa5-3816-4250-9998-0c81b8491eb0

📥 Commits

Reviewing files that changed from the base of the PR and between 9552e20 and dea7333.

📒 Files selected for processing (9)
  • apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsCalendar.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_components/StatisticsSidePanel.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsPageContainer.tsx
  • apps/timo-web/app/[locale]/(main)/statistics/_mocks/statistics-calendar.ts
  • apps/timo-web/app/[locale]/(main)/statistics/_utils/format-statistics-date.ts
  • apps/timo-web/messages/en.json
  • apps/timo-web/messages/ko.json
💤 Files with no reviewable changes (1)
  • apps/timo-web/app/[locale]/(main)/statistics/_containers/StatisticsPageContainer.tsx

@@ -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

Comment on lines 109 to 142
{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>
);
})}

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.

Comment on lines +117 to +140
<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>

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을 추가해 스크린 리더가 연도·월·일을 함께 읽도록 수정하세요.

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

Comment on lines +22 to +24
const [selectedDate, setSelectedDate] = useState(
() => new Date(MOCK_STATISTICS_CALENDAR.today),
);

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.

Comment on lines +23 to +26
const locale = useLocale();
const monthLabel = new Intl.DateTimeFormat(locale, {
month: "long",
}).format(currentMonth);

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을 생성하도록 변경하세요.

Comment on lines +55 to +61
export const MOCK_STATISTICS_MONTH_SUMMARY: StatisticsMonthSummary = {
totalRecordMinutes: 1330,
activeDayCount: 28,
averageRecordedMinutes: 214,
completedTodoCount: 80,
totalTodoCount: 100,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

목 요약 목 데이터의 수치 일관성을 점검해 주세요.

MOCK_STATISTICS_MONTH_SUMMARY 내부 값들이 서로 맞지 않습니다:

  • averageRecordedMinutes(214) ≠ totalRecordMinutes / activeDayCount = 1330 / 28 ≈ 47.5
  • activeDayCount(28) ≠ MOCK_COMPLETION_RATES에서 null이 아닌 날 수(26개)
  • totalTodoCount(100) ≠ 일별 상세의 실제 todo 수(26일 × 2개 = 52)

StatisticsSidePanel 월 패널에서 totalRecordMinutesaverageRecordedMinutes가 함께 표시되므로, 개발 중 수치 불일치를 눈치챌 수 있습니다. 목 데이터라도 패널에 직접 노출되는 값들은 내부 연관성을 맞추는 것이 좋습니다.

🔧 제안 수정안
 export const MOCK_STATISTICS_MONTH_SUMMARY: StatisticsMonthSummary = {
-  totalRecordMinutes: 1330,
-  activeDayCount: 28,
-  averageRecordedMinutes: 214,
-  completedTodoCount: 80,
-  totalTodoCount: 100,
+  totalRecordMinutes: 5340,
+  activeDayCount: 26,
+  averageRecordedMinutes: 205,
+  completedTodoCount: 52,
+  totalTodoCount: 52,
 };

참고: totalRecordMinutes 5340는 일별 totalRecordMinutes 합계이고, averageRecordedMinutes는 5340 / 26 ≈ 205입니다.

📝 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
export const MOCK_STATISTICS_MONTH_SUMMARY: StatisticsMonthSummary = {
totalRecordMinutes: 1330,
activeDayCount: 28,
averageRecordedMinutes: 214,
completedTodoCount: 80,
totalTodoCount: 100,
};
export const MOCK_STATISTICS_MONTH_SUMMARY: StatisticsMonthSummary = {
totalRecordMinutes: 5340,
activeDayCount: 26,
averageRecordedMinutes: 205,
completedTodoCount: 52,
totalTodoCount: 52,
};
🤖 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/_mocks/statistics-calendar.ts
around lines 55 - 61, MOCK_STATISTICS_MONTH_SUMMARY의 월간 요약 값이 일별 목 데이터와 일관되지
않습니다. MOCK_COMPLETION_RATES의 유효 일수와 일별 상세 데이터의 todo 개수를 기준으로 activeDayCount를 26,
totalTodoCount를 52로 맞추고, 일별 totalRecordMinutes 합계인 5340과 activeDayCount를 기준으로
totalRecordMinutes 및 averageRecordedMinutes를 조정하세요.

"sidePanel": {
"monthTotalRecordTime": "Total record time this month",
"activeDays": "Active days",
"activeDayCount": "{count} days",

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

activeDayCount에 ICU 복수형을 적용해주세요.

현재 "{count} days"는 count가 1일 때 "1 days"가 되어 문법적으로 올바르지 않습니다. next-intl의 ICU MessageFormat 복수형 규칙을 사용해주세요.

💚 제안하는 수정
-      "activeDayCount": "{count} days",
+      "activeDayCount": "{count, plural, one {# day} other {# days}}",

참고: next-intl ICU MessageFormat 문서

📝 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
"activeDayCount": "{count} days",
"activeDayCount": "{count, plural, one {# day} other {# days}}",
🤖 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/messages/en.json` at line 43, Update the activeDayCount message
to use ICU pluralization, selecting the singular “day” when count equals 1 and
the plural “days” otherwise, while preserving the count value in both variants.

"accumulatedTasks": "Accumulated tasks",
"dayTotalRecordTime": "Total record time today",
"plan": "Plan",
"overtime": "+Overdue",

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

"Overdue"를 "Overtime"으로 수정해주세요.

한국어 "초과"는 계획 시간을 초과했음을 의미하지만, 영어 "Overdue"는 "기한이 지남"을 뜻합니다. 이 맥락에서는 "Overtime" 또는 "Over"가 올바른 번역입니다.

💚 제안하는 수정
-      "overtime": "+Overdue",
+      "overtime": "+Overtime",
📝 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
"overtime": "+Overdue",
"overtime": "+Overtime",
🤖 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/messages/en.json` at line 48, Update the "overtime" translation
value in the messages/en.json localization resource from "+Overdue" to
"+Overtime" (or "+Over") to accurately represent exceeding the planned time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능(기능성) 구현 ⏰ Timo-web Timo 웹 서비스 ♣️ 유민 유민양

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 통계 페이지 뷰 구현

1 participant