-
Notifications
You must be signed in to change notification settings - Fork 0
[FEAT] 통계 페이지 뷰 구현 (PR수정중) #143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,10 +1,13 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "use client"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win 날짜 버튼에 버튼 내부에 날짜 숫자 텍스트가 있지만, 스크린 리더 사용자에게 "15"만 들리는 것보다 "2024년 1월 15일"과 같은 맥락 정보가 포함된 🛡️ 제안하는 접근성 개선 <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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
109
to
142
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win
♻️ 제안하는 리팩터링 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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"; | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win 타입 alias 명명 컨벤션을 준수해 주세요.
As per coding guidelines, ♻️ 제안 수정안-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
Suggested change
🤖 Prompt for AI AgentsSource: 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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())
PYRepository: 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()}`);
}
}
JSRepository: Team-Timo/Timo-client Length of output: 691
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| 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"; | ||||||||||||||||||||||
|
|
@@ -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); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
♻️ 제안하는 리팩터링+ 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
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const handlePrev = () => onChangeMonth((prev) => addMonths(prev, -1)); | ||||||||||||||||||||||
| const handleNext = () => onChangeMonth((prev) => addMonths(prev, 1)); | ||||||||||||||||||||||
|
|
@@ -34,7 +36,7 @@ export const StatisticsHeaderContainer = ({ | |||||||||||||||||||||
| <Header.WeeklyNav | ||||||||||||||||||||||
| onPrev={handlePrev} | ||||||||||||||||||||||
| onNext={handleNext} | ||||||||||||||||||||||
| label={MONTH_LABEL_FORMATTER.format(currentMonth)} | ||||||||||||||||||||||
| label={monthLabel} | ||||||||||||||||||||||
| /> | ||||||||||||||||||||||
| </> | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
This file was deleted.
There was a problem hiding this comment.
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
Source: Path instructions