Skip to content

[FEAT] 오늘 페이지 뷰 구현 - #144

Merged
ehye1 merged 16 commits into
developfrom
feat/web/141-today-todocard-interaction
Jul 13, 2026
Merged

[FEAT] 오늘 페이지 뷰 구현#144
ehye1 merged 16 commits into
developfrom
feat/web/141-today-todocard-interaction

Conversation

@ehye1

@ehye1 ehye1 commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

ISSUE 🔗

close #141



What is this PR? 🔍

Today 페이지에 날짜 헤더 컨테이너를 추가하고, 투두 목록이 지정 영역 안에서 스크롤되는 레이아웃으로 변경했습니다.
아울러 TodayTodoCardContainerisPlaying 로컬 상태를 제거하고 prop 기반 파생값으로 리팩터링했습니다.

배경

  • 기존 구조: 투두 리스트가 고정 높이 없이 쌓이는 구조여서, 항목이 많아지면 페이지 전체가 길어지는 구조였습니다.
  • 발생 문제: 콘텐츠 영역 내 스크롤이 동작하지 않아 카드가 많아질수록 레이아웃이 밀려 내려갔습니다.
  • 해결 방향: h-full + min-h-0 flex-1 overflow-y-auto 조합으로 컨테이너 구조를 재편해, 헤더는 고정하고 카드 목록만 스크롤되도록 했습니다.

TodayDayHeaderContainer

  • 변경 요약: 날짜 헤더 컨테이너를 신규 추가했습니다.
  • 이유: 오늘 날짜와 완료/전체 투두 개수를 한눈에 보여주는 영역이 필요했습니다.
  • 구현 방식: completedCounttotalCount를 props로 받아 표시합니다. 현재는 TodayTodoListContainer의 로컬 상태에서 계산한 값을 내려주며, API 연동 후 서버 응답값으로 교체할 예정입니다.

Today 페이지 스크롤 레이아웃

  • 변경 요약: page.tsxTodayTodoListContainer의 레이아웃 구조를 h-full 기반으로 변경했습니다.
  • 이유: 기존 <section> + <div class="p-4"> 구조는 높이 제약이 없어 스크롤 컨테이너를 만들 수 없었습니다.
  • 구현 방식: page.tsxflex h-full flex-col로 변경하고, 그 안의 <section>flex min-h-0 flex-1 flex-col로 구성했습니다. TodayTodoListContainer 내부에서 헤더는 상단 고정, 카드 목록 영역에 min-h-0 flex-1 overflow-y-auto를 적용해 스크롤이 해당 영역 안에서만 동작하도록 했습니다.

TodayTodoCardContainer 리팩터링

  • 변경 요약: isPlaying 로컬 state와 useEffect를 제거하고, timerStatus prop에서 직접 파생하도록 변경했습니다.
  • 이유: isPlaying은 부모에서 내려오는 timerStatus를 그대로 복사한 값이었습니다. prop을 state로 미러링하면 prop 변경 → effect 실행 사이에 값이 일치하지 않는 타이밍 문제가 생길 수 있습니다.
  • 구현 방식: const isPlaying = timerStatus === "RUNNING"으로 단순화했습니다. 타이머 상태는 부모(TodayTodoListContainer)가 단일 출처로 관리합니다.



To Reviewers

모달 연결이 아직 되어 있지 않습니다. trash 아이콘 클릭 시 현재는 확인 없이 바로 삭제됩니다. 삭제 확인 모달 및 타이머 전환 모달 연결은 별도 작업으로 진행될 예정이니, 해당 흐름은 이번 PR에서 확인하지 않으셔도 됩니다.

TodayDayHeaderContainer의 완료/전체 카운트는 로컬 state 기반이며 API 연동 후 교체가 필요합니다.

완료된 투두를 하단으로 자동 정렬하는 기능도 API 연동 후 구현할 예정입니다.

today-todo-mock.ts에 추가한 todoId 4~8 항목은 스크롤 동작 확인용 임시 데이터입니다. API 연동 시 제거됩니다.



Screenshot 📷

Timo.-.Chrome.2026-07-12.10-12-24.mp4



Test Checklist ✔

  • lint-staged 통과 (커밋 시 자동 실행)
  • 투두 카드 hover / check / play / delete 동작 확인
  • 카드 목록 영역 스크롤 동작 확인
  • 완료된 투두 dimmed 처리 확인

@vercel

vercel Bot commented Jul 11, 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 13, 2026 2:30pm

Request Review

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

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

오늘 페이지에 날짜 헤더와 스크롤 가능한 투두 목록을 구성하고, 투두 상태·카드 상호작용·생성 모달을 훅 기반으로 연결했습니다. 공용 날짜·모달 컴포넌트와 생성 폼 필드를 추가했으며, 드롭다운 패널 정렬 옵션을 확장했습니다.

Changes

오늘 페이지 기능

Layer / File(s) Summary
오늘 헤더와 목록 상태
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/_components/DateInformation.tsx, .../today/_containers/*, .../today/_hooks/useTodayTodoList.ts, .../today/_mocks/*, .../today/page.tsx
공용 DateInformation을 연결하고 오늘 날짜·완료 개수·투두 목록 상태와 스크롤 레이아웃을 구성했습니다.
카드 타이머와 삭제 상호작용
apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx, .../today/_containers/TodayTodoCardContainer.tsx, .../messages/*
카드 재생 상태를 timerStatus에서 파생하고 카드 클릭 계약, 이벤트 전파 차단, 툴바·삭제 버튼과 번역 키를 변경했습니다.
오늘 투두 생성 진입과 변환
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/*, .../_hooks/todo-modal/use-create-todo-submit.ts, .../_utils/todo-time.ts
추가 버튼으로 모달을 열고 제출 데이터를 TodoMock으로 변환해 목록에 추가하는 흐름을 연결했습니다.
투두 생성 필드와 폼 상태
apps/timo-web/components/todo-modal/*, apps/timo-web/hooks/todo-modal/use-icon-field.ts, use-repeat-field.ts
아이콘·메모·제목·서브태스크·반복 입력 컴포넌트와 폼 상태 훅을 추가했습니다.
시간 추천과 날짜 필드
apps/timo-web/hooks/todo-modal/use-time-field.ts, apps/timo-web/components/todo-modal/CreateTodoModalContent.tsx
시간 입력 및 AI 추천 상태를 추가하고 날짜 키와 기본 duration 처리를 변경했습니다.
공용 모달 연결과 입력 검증
apps/timo-web/components/tag-modal/*, apps/timo-web/hooks/todo-modal/use-tag-field.tsx, .../home/_containers/todo-modal/*, .../oauth/callback/page.tsx
태그 생성 유효성 검증과 공용 모달 경로 연결을 반영했습니다.

드롭다운 배치 옵션

Layer / File(s) Summary
드롭다운 정렬 계약과 패널 위치
packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx
align 타입과 prop을 추가하고 드롭다운 패널을 좌우로 배치하도록 변경했습니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant TodayTodoListContainer
  participant useTodayTodoList
  participant TodayDateHeaderContainer
  participant CreateTodoModalContainer
  participant CreateTodoModalContent
  participant useCreateTodoSubmit

  TodayTodoListContainer->>useTodayTodoList: todos와 핸들러 요청
  useTodayTodoList-->>TodayTodoListContainer: 목록 상태 반환
  TodayTodoListContainer->>TodayDateHeaderContainer: 완료 개수와 전체 개수 전달
  TodayDateHeaderContainer->>CreateTodoModalContainer: 오늘 날짜와 생성 콜백 전달
  CreateTodoModalContainer->>CreateTodoModalContent: 모달과 제출 핸들러 연결
  CreateTodoModalContent->>useCreateTodoSubmit: 생성 요청과 태그 전달
  useCreateTodoSubmit->>useTodayTodoList: TodoMock 추가 콜백 호출
Loading

Possibly related PRs

Suggested labels: ♦️ 민아

Suggested reviewers: yumin-kim2

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning [#141] 스크롤과 헤더는 반영됐지만 play 클릭의 홈 탭 동일 타이머 사이드바 연동이 구현되지 않았습니다. play 클릭 시 홈 탭과 같은 타이머 사이드바 오픈 흐름을 연결하고, 현재 로컬 상태만 갱신하는 경로를 보완하세요.
Out of Scope Changes check ⚠️ Warning 이슈 #141 범위를 넘어 할일 추가 모달과 공용 컴포넌트·디자인시스템 리팩터링이 함께 들어왔습니다. 오늘 페이지 스크롤·카드 UI와 play 연동만 남기고, 추가 모달과 공용 리팩터링은 별도 PR로 분리하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 오늘 페이지 구현이라는 핵심 변경을 직접적으로 요약해 PR 내용과 잘 맞습니다.
Description check ✅ Passed 설명이 날짜 헤더, 스크롤 레이아웃, 카드 리팩터링 등 실제 변경 사항과 일치합니다.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/web/141-today-todocard-interaction

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.

@ehye1 ehye1 changed the title [FEAT] Today 투두 리스트 스크롤 레이아웃 및 날짜 헤더 추가 [FEAT] 오늘 페이지 뷰 구현 Jul 11, 2026
@github-actions

github-actions Bot commented Jul 11, 2026

Copy link
Copy Markdown

Timo Performance Report

Bundle Size — timo-web
라우트 크기 First Load JS
/[locale]/home 202.44 kB 🔴 408.27 kB
/[locale]/today 183.09 kB 🔴 388.93 kB
/[locale]/focus 158.61 kB 🔴 364.44 kB
/[locale]/settings/account 0 B 🟡 205.83 kB
/[locale]/settings 163.23 kB 🔴 369.07 kB
/[locale]/statistics 151.75 kB 🔴 357.58 kB
/[locale]/[...rest] 0 B 🟡 205.83 kB
/[locale]/login 116.91 kB 🟡 322.75 kB
/[locale]/oauth/callback 118.65 kB 🟡 324.48 kB
/[locale]/onboarding 230.59 kB 🔴 436.43 kB
/[locale] 0 B 🟡 205.83 kB

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

Lighthouse — timo-web
URL Perf A11y LCP CLS TBT
/en/home 🔴 59 🟢 95 🔴 15.8s 🟢 0.000 🔴 623ms
/en/today 🔴 58 🟢 95 🔴 15.7s 🟢 0.000 🔴 651ms
/en/focus 🔴 61 🟢 95 🔴 15.3s 🟢 0.000 🟡 545ms
/en/statistics 🔴 60 🟢 95 🔴 15.4s 🟢 0.000 🟡 578ms

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

Image Optimization — timo-web
파일 크기 포맷 상태
images/google-calendar.png 36.20 kB PNG ⚠️ 🟢
images/google-logo.png 26.79 kB PNG ⚠️ 🟢

총 2개 · 63.00 kB  |  🟢 < 200KB  |  🟡 < 500KB  |  🔴 ≥ 500KB
⚠️ 2개 파일 WebP/AVIF 변환 권장

측정 커밋: 3c6ce6d

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

🤖 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)/(with-time-sidebar)/today/_containers/TodayDayHeaderContainer.tsx:
- Around line 6-10: Remove the direct today-to-home imports in
TodayDayHeaderContainer, relocating convertDateToDateText and getToday to the
shared utils/lib area and updating their consumers. Extract HomeDateInformation
into the project’s shared component location, then update
TodayDayHeaderContainer and the original home consumers to import the shared
symbols instead of home-owned paths.
🪄 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: 967b7c28-5043-4909-b917-ef5644048e28

📥 Commits

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

📒 Files selected for processing (5)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDayHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/page.tsx

Comment on lines +6 to +10
import { HomeDateInformation } from "@/app/[locale]/(main)/(with-time-sidebar)/home/_components/HomeDateInformation";
import {
convertDateToDateText,
getToday,
} from "@/app/[locale]/(main)/(with-time-sidebar)/home/_utils/date";

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

home 도메인에서 직접 import하는 것이 path instruction을 위반합니다.

today 도메인에서 home/_components/HomeDateInformationhome/_utils/dateconvertDateToDateText, getToday를 직접 import하고 있습니다. 이는 hometoday 도메인 간 결합을 만들어, home 도메인 변경 시 today 페이지가 영향을 받습니다.

getDayOfWeekKey가 이미 @/utils/에 위치한 것처럼, date 유틸리티들도 공유 위치(utils/ 또는 lib/)로 이동해야 합니다. HomeDateInformation은 props만 받는 presentational 컴포넌트이므로 공유 컴포넌트로 추출(예: packages/ 또는 공유 _components/)을 권장합니다.

As per path instructions, "도메인 간 직접 import 금지 — 공유 로직은 반드시 lib/ 또는 packages/로 추출"해야 합니다. Based on learnings, *HeaderContainer.tsx 파일 내 중복 wiring은 의도적이나, 공유 컴포넌트/유틸리티의 도메인 간 직접 import는 별개의 문제입니다.

🤖 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)/(with-time-sidebar)/today/_containers/TodayDayHeaderContainer.tsx
around lines 6 - 10, Remove the direct today-to-home imports in
TodayDayHeaderContainer, relocating convertDateToDateText and getToday to the
shared utils/lib area and updating their consumers. Extract HomeDateInformation
into the project’s shared component location, then update
TodayDayHeaderContainer and the original home consumers to import the shared
symbols instead of home-owned paths.

Sources: Path instructions, Learnings

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx (1)

22-23: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

runningTodoId 중간 변수 대신 todo.timerStatus를 직접 전달하는 것을 고려해보세요.

runningTodoIdtodos.find(...)로 파생한 뒤, 렌더 시 다시 runningTodoId === todo.todoId 비교로 "RUNNING" | "STOPPED"를 재구성하고 있습니다. handlePlay가 이미 각 todo의 timerStatus 필드를 올바르게 유지하므로, timerStatus={todo.timerStatus}로 직접 전달해도 동일한 결과를 얻을 수 있습니다.

현재는 문제가 없지만, 향후 "PAUSED" 상태를 지원할 때 runningTodoId 기반 분기는 "PAUSED""STOPPED"로 렌더링해버리는 잠재적 불일치가 발생할 수 있습니다.

♻️ 제안: timerStatus 직접 전달
   const runningTodoId =
     todos.find((todo) => todo.timerStatus === "RUNNING")?.todoId ?? null;
   const completedCount = todos.filter((todo) => todo.completed).length;
-            timerStatus={runningTodoId === todo.todoId ? "RUNNING" : "STOPPED"}
+            timerStatus={todo.timerStatus}

Also applies to: 85-85

🤖 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)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx
around lines 22 - 23, Remove the derived runningTodoId logic in
TodayTodoListContainer and pass each todo’s existing timerStatus directly to the
rendered todo component. Replace the runningTodoId comparison used to
reconstruct "RUNNING" or "STOPPED", preserving all timerStatus values for future
states such as "PAUSED".
🤖 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.

Outside diff comments:
In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx:
- Around line 22-23: Remove the derived runningTodoId logic in
TodayTodoListContainer and pass each todo’s existing timerStatus directly to the
rendered todo component. Replace the runningTodoId comparison used to
reconstruct "RUNNING" or "STOPPED", preserving all timerStatus values for future
states such as "PAUSED".

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8ad9f426-76da-4be5-a202-16509d528133

📥 Commits

Reviewing files that changed from the base of the PR and between 3694c7c and 29b5072.

📒 Files selected for processing (1)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx

Comment on lines +7 to +10
import {
convertDateToDateText,
getToday,
} from "@/app/[locale]/(main)/(with-time-sidebar)/home/_utils/date";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

공통으로 쓰는 유틸을 제가 이전 PR에서 공통 유틸로 빼 뒀어요! 머지되면 해당 임포트로 교체하면 될 것 같아요.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx (1)

35-36: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

좋아요, isHoliday만 오늘 기준으로 살리면 됩니다 apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx에서 isHoliday={false}가 고정이라 일요일도 빨간색 표시가 나오지 않습니다. 이미 만든 dayKeydayKey === "SUN"을 넘기면 되고, 공휴일은 별도 데이터가 없으니 후속 연동으로 분리하세요. 참고: MDN Date.getDay()https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getDay

🤖 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)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx
around lines 35 - 36, Update TodayDateHeaderContainer’s DateHeader props to
derive isHoliday from the existing dayKey, passing true only when dayKey ===
"SUN" instead of hardcoding false. Leave public-holiday handling out until
separate holiday data is integrated.
🤖 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.

Outside diff comments:
In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx:
- Around line 35-36: Update TodayDateHeaderContainer’s DateHeader props to
derive isHoliday from the existing dayKey, passing true only when dayKey ===
"SUN" instead of hardcoding false. Leave public-holiday handling out until
separate holiday data is integrated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 943c673a-99d2-4e0d-9ed2-983779865549

📥 Commits

Reviewing files that changed from the base of the PR and between 29b5072 and 6dfb5f6.

📒 Files selected for processing (4)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/_components/DateInformation.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeDayHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx

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

🤖 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)/(with-time-sidebar)/home/_hooks/todo-modal/use-create-todo-submit.ts:
- Around line 8-28: Update buildTodoFromRequest to generate collision-resistant
unique todoId and subtaskId values instead of relying directly on Date.now().
Preserve the existing numeric ID contract if Todo identifiers are number-typed,
using a module-scoped incrementing counter or another safe numeric strategy for
both the todo and its subtasks.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts:
- Line 19: Replace the Date.now-based todoId generation in the todo creation
flow with a module-level incrementing counter or dedicated ID generator that
guarantees uniqueness across consecutive todo and subtask creations, including
when created within the same millisecond. Use the generated IDs for stable
dynamic-list keys without changing the surrounding submission behavior.

In `@apps/timo-web/app/`[locale]/(main)/today/_components/TodayTodoCard.tsx:
- Around line 164-166: Update the delete button in TodayTodoCard to set its
disabled state from isDimmed, preventing onDelete from running when the button
appears disabled while preserving the existing icon selection.

In `@apps/timo-web/components/todo-modal/CreateTodoIconField.tsx`:
- Around line 28-36: Update the selected-icon button in CreateTodoIconField so
its aria-label describes reopening the panel to change the existing icon, using
a dedicated change-icon label instead of addIconLabel. Keep addIconLabel for the
no-icon state and preserve the current onOpenPanel behavior.

In `@apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx`:
- Around line 13-19: Update the textarea in CreateTodoMemoField to provide an
accessible name, adding an appropriate aria-label while preserving the existing
value, change handler, placeholder, and styling behavior.

In `@apps/timo-web/components/todo-modal/CreateTodoModalContent.tsx`:
- Around line 30-42: Update createDefaultValues so tagId defaults to
DEFAULT_TAG.id instead of null, keeping the TodoToolbar initial state and
handleFormSubmit persistence behavior consistent. Reuse the existing DEFAULT_TAG
symbol and leave unrelated default fields unchanged.
- Around line 182-207: 반복 설정을 다시 “NONE”으로 선택할 수 있도록 CreateTodoModalContent의
repeat 옵션과 RepeatSelectorProps 흐름을 확장하세요. useRepeatField의 repeatType 상태가 NONE으로
전환되도록 onFrequencyChange 또는 별도 활성화 핸들러를 연결하고, 선택 시 기존 반복 설정이 비활성화되는 UI 동작을 유지하세요.

In `@apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx`:
- Around line 25-29: Update both title and subtask inputs in
CreateTodoTaskFields to include unique id values and associate each with a
corresponding label via htmlFor; if visible labels are unavailable, provide an
appropriate aria-label instead.

In `@apps/timo-web/hooks/todo-modal/use-repeat-field.ts`:
- Around line 62-67: Update handleRepeatDayChange to accept repeat-day values
only when they are finite and within the createTodoRequestSchema range of 1
through 31; otherwise pass null to repeatDayField.field.onChange. Preserve the
existing trimmed-empty input behavior.

In `@packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx`:
- Around line 17-24: Rename the public union type alias DropdownAlign to
DropdownAlignTypes to follow the repository naming convention, and update every
reference to it, including the align property in DropdownContextValue and any
related component/API usage. Preserve the union members and existing behavior.
🪄 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: d105e7a1-90a5-4630-ae89-667b6a2cfd43

📥 Commits

Reviewing files that changed from the base of the PR and between 6dfb5f6 and d1241f5.

📒 Files selected for processing (21)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-card/HomeDayHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_utils/todo-time.ts
  • apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx
  • apps/timo-web/app/[locale]/oauth/callback/page.tsx
  • apps/timo-web/components/CreateTodoToolbar.tsx
  • apps/timo-web/components/tag-modal/CreateTagModalContainer.tsx
  • apps/timo-web/components/todo-modal/CreateTodoIconField.tsx
  • apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx
  • apps/timo-web/components/todo-modal/CreateTodoModalContent.tsx
  • apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx
  • apps/timo-web/hooks/todo-modal/use-icon-field.ts
  • apps/timo-web/hooks/todo-modal/use-repeat-field.ts
  • apps/timo-web/hooks/todo-modal/use-tag-field.tsx
  • apps/timo-web/hooks/todo-modal/use-time-field.ts
  • packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx
💤 Files with no reviewable changes (1)
  • apps/timo-web/components/CreateTodoToolbar.tsx

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 10

🤖 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)/(with-time-sidebar)/home/_hooks/todo-modal/use-create-todo-submit.ts:
- Around line 8-28: Update buildTodoFromRequest to generate collision-resistant
unique todoId and subtaskId values instead of relying directly on Date.now().
Preserve the existing numeric ID contract if Todo identifiers are number-typed,
using a module-scoped incrementing counter or another safe numeric strategy for
both the todo and its subtasks.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts:
- Line 19: Replace the Date.now-based todoId generation in the todo creation
flow with a module-level incrementing counter or dedicated ID generator that
guarantees uniqueness across consecutive todo and subtask creations, including
when created within the same millisecond. Use the generated IDs for stable
dynamic-list keys without changing the surrounding submission behavior.

In `@apps/timo-web/app/`[locale]/(main)/today/_components/TodayTodoCard.tsx:
- Around line 164-166: Update the delete button in TodayTodoCard to set its
disabled state from isDimmed, preventing onDelete from running when the button
appears disabled while preserving the existing icon selection.

In `@apps/timo-web/components/todo-modal/CreateTodoIconField.tsx`:
- Around line 28-36: Update the selected-icon button in CreateTodoIconField so
its aria-label describes reopening the panel to change the existing icon, using
a dedicated change-icon label instead of addIconLabel. Keep addIconLabel for the
no-icon state and preserve the current onOpenPanel behavior.

In `@apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx`:
- Around line 13-19: Update the textarea in CreateTodoMemoField to provide an
accessible name, adding an appropriate aria-label while preserving the existing
value, change handler, placeholder, and styling behavior.

In `@apps/timo-web/components/todo-modal/CreateTodoModalContent.tsx`:
- Around line 30-42: Update createDefaultValues so tagId defaults to
DEFAULT_TAG.id instead of null, keeping the TodoToolbar initial state and
handleFormSubmit persistence behavior consistent. Reuse the existing DEFAULT_TAG
symbol and leave unrelated default fields unchanged.
- Around line 182-207: 반복 설정을 다시 “NONE”으로 선택할 수 있도록 CreateTodoModalContent의
repeat 옵션과 RepeatSelectorProps 흐름을 확장하세요. useRepeatField의 repeatType 상태가 NONE으로
전환되도록 onFrequencyChange 또는 별도 활성화 핸들러를 연결하고, 선택 시 기존 반복 설정이 비활성화되는 UI 동작을 유지하세요.

In `@apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx`:
- Around line 25-29: Update both title and subtask inputs in
CreateTodoTaskFields to include unique id values and associate each with a
corresponding label via htmlFor; if visible labels are unavailable, provide an
appropriate aria-label instead.

In `@apps/timo-web/hooks/todo-modal/use-repeat-field.ts`:
- Around line 62-67: Update handleRepeatDayChange to accept repeat-day values
only when they are finite and within the createTodoRequestSchema range of 1
through 31; otherwise pass null to repeatDayField.field.onChange. Preserve the
existing trimmed-empty input behavior.

In `@packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx`:
- Around line 17-24: Rename the public union type alias DropdownAlign to
DropdownAlignTypes to follow the repository naming convention, and update every
reference to it, including the align property in DropdownContextValue and any
related component/API usage. Preserve the union members and existing behavior.
🪄 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: d105e7a1-90a5-4630-ae89-667b6a2cfd43

📥 Commits

Reviewing files that changed from the base of the PR and between 6dfb5f6 and d1241f5.

📒 Files selected for processing (21)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-card/HomeDayHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_utils/todo-time.ts
  • apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx
  • apps/timo-web/app/[locale]/oauth/callback/page.tsx
  • apps/timo-web/components/CreateTodoToolbar.tsx
  • apps/timo-web/components/tag-modal/CreateTagModalContainer.tsx
  • apps/timo-web/components/todo-modal/CreateTodoIconField.tsx
  • apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx
  • apps/timo-web/components/todo-modal/CreateTodoModalContent.tsx
  • apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx
  • apps/timo-web/hooks/todo-modal/use-icon-field.ts
  • apps/timo-web/hooks/todo-modal/use-repeat-field.ts
  • apps/timo-web/hooks/todo-modal/use-tag-field.tsx
  • apps/timo-web/hooks/todo-modal/use-time-field.ts
  • packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx
💤 Files with no reviewable changes (1)
  • apps/timo-web/components/CreateTodoToolbar.tsx
🛑 Comments failed to post (10)
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-create-todo-submit.ts (1)

8-28: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Date.now()로 만든 id는 충돌 가능성이 있어요.

todoId: Date.now()는 같은 밀리초 안에 두 번 호출되면(더블 클릭, 연속 제출 등) 동일한 값이 나올 수 있습니다. 목데이터 단계라 임팩트는 제한적이지만, 이 값이 그대로 React 리스트의 key로 쓰일 가능성이 높아서 중복되면 렌더링 오류나 잘못된 카드 업데이트로 이어질 수 있어요. crypto.randomUUID()나 간단한 증가 카운터로 바꿔두면 더 안전합니다.

참고: crypto.randomUUID() – MDN

♻️ 제안
 const buildTodoFromRequest = (
   data: CreateTodoRequest,
   tag: CreateTodoTag,
 ): Todo => ({
-  todoId: Date.now(),
+  todoId: crypto.randomUUID() as unknown as number, // 또는 Todo.todoId 타입을 string으로 정리
   ...
   subtasks: (data.subtasks ?? []).map((content, index) => ({
-    subtaskId: Date.now() + index,
+    subtaskId: crypto.randomUUID() as unknown as number,
     content,
     completed: false,
   })),
 });

(만약 Todo.todoIdnumber로 고정돼 있다면 Date.now() * 1000 + index 같은 조합이나 모듈 스코프 카운터가 더 자연스러울 수 있습니다.)

📝 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.

const buildTodoFromRequest = (
  data: CreateTodoRequest,
  tag: CreateTodoTag,
): Todo => ({
  todoId: crypto.randomUUID() as unknown as number, // 또는 Todo.todoId 타입을 string으로 정리
  icon: data.icon,
  title: data.title,
  completed: false,
  durationSeconds: convertApiDurationToSeconds(data.duration),
  priority: data.priority ?? "MEDIUM",
  tag: { tagId: tag.id, name: tag.name },
  hasMemo: Boolean(data.memo?.trim()),
  isRepeated: data.repeatType !== "NONE",
  timerStatus: "STOPPED",
  sortOrder: 0,
  subtasks: (data.subtasks ?? []).map((content, index) => ({
    subtaskId: crypto.randomUUID() as unknown as number,
    content,
    completed: false,
  })),
});
🤖 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)/(with-time-sidebar)/home/_hooks/todo-modal/use-create-todo-submit.ts
around lines 8 - 28, Update buildTodoFromRequest to generate collision-resistant
unique todoId and subtaskId values instead of relying directly on Date.now().
Preserve the existing numeric ID contract if Todo identifiers are number-typed,
using a module-scoped incrementing counter or another safe numeric strategy for
both the todo and its subtasks.
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts (1)

19-19: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## file outline\n'
ast-grep outline apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts --view expanded || true

printf '\n## relevant file excerpt\n'
cat -n apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts | sed -n '1,220p'

printf '\n## search for todoId / subtaskId usages\n'
rg -n "todoId|subtaskId" apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today -g '!**/node_modules/**' || true

Repository: Team-Timo/Timo-client

Length of output: 229


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE='apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts'

printf '\n## file outline\n'
ast-grep outline "$FILE" --view expanded || true

printf '\n## relevant file excerpt\n'
cat -n "$FILE" | sed -n '1,220p'

printf '\n## search for todoId / subtaskId usages\n'
rg -n "todoId|subtaskId" 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today' || true

Repository: Team-Timo/Timo-client

Length of output: 7959


중복되지 않는 로컬 ID 생성으로 바꾸자
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts:19-32
Date.now()를 두 번 쓰면 같은 밀리초에 todoId와 첫 subtaskId가 겹칠 수 있고, todoId 자체도 연속 생성 시 충돌할 수 있습니다. 동적 리스트의 key는 안정적인 고유값이 필요하니 모듈 레벨 증가 카운터(또는 별도 ID 생성기)로 바꾸는 편이 안전합니다. React의 key 가이드도 함께 보면 좋습니다: https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key

🤖 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)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
at line 19, Replace the Date.now-based todoId generation in the todo creation
flow with a module-level incrementing counter or dedicated ID generator that
guarantees uniqueness across consecutive todo and subtask creations, including
when created within the same millisecond. Use the generated IDs for stable
dynamic-list keys without changing the surrounding submission behavior.
apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx (1)

164-166: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

삭제 버튼에 disabled 속성이 빠졌어요.

isDimmed일 때 TrashDisableIcon으로 바뀌어 시각적으로는 비활성 상태처럼 보이지만, 실제로는 disabled 속성이 없어 클릭하면 그대로 onDelete가 실행됩니다. 바로 위 PlayButtondisabled={isDone}을 명시했는데 삭제 버튼만 누락된 것으로 보여요. 아이콘과 실제 동작이 일치하도록 맞춰주시면 좋을 것 같습니다.

참고: MDN - button disabled 속성

🐛 제안 수정
-        <button type="button" onClick={onDelete} aria-label="삭제">
+        <button type="button" onClick={onDelete} disabled={isDimmed} aria-label="삭제">
           {isDimmed ? <TrashDisableIcon /> : <TrashOnIcon />}
         </button>
📝 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.

        <button type="button" onClick={onDelete} disabled={isDimmed} aria-label="삭제">
          {isDimmed ? <TrashDisableIcon /> : <TrashOnIcon />}
        </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)/today/_components/TodayTodoCard.tsx around
lines 164 - 166, Update the delete button in TodayTodoCard to set its disabled
state from isDimmed, preventing onDelete from running when the button appears
disabled while preserving the existing icon selection.
apps/timo-web/components/todo-modal/CreateTodoIconField.tsx (1)

28-36: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

아이콘 변경 버튼의 aria-label이 실제 동작과 살짝 안 맞아요.

이미 아이콘이 선택된 상태에서 이 버튼은 "패널을 다시 열어 아이콘을 바꾸는" 역할인데, aria-labeladdIconLabel("아이콘 추가" 류의 문구)로 되어 있어서 스크린리더 사용자에게는 마치 새로 추가하는 버튼처럼 들릴 수 있습니다. "아이콘 변경" 같은 별도 라벨을 넘겨주면 의미가 더 명확해질 것 같아요.

참고: MDN aria-label

♻️ 제안
 export interface CreateTodoIconFieldProps {
   icon: TodoIconValue | null;
   isIconPanelOpen: boolean;
   addIconLabel: string;
+  changeIconLabel?: string;
   onOpenPanel: () => void;
   ...
 }
...
         <button
           type="button"
           onClick={onOpenPanel}
-          aria-label={addIconLabel}
+          aria-label={changeIconLabel ?? addIconLabel}
           className="flex size-6 shrink-0 items-center justify-center rounded-[4px]"
         >
📝 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.

      {!isIconPanelOpen && icon ? (
        <button
          type="button"
          onClick={onOpenPanel}
          aria-label={changeIconLabel ?? addIconLabel}
          className="flex size-6 shrink-0 items-center justify-center rounded-[4px]"
        >
          <IconGraphic icon={icon} />
        </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/components/todo-modal/CreateTodoIconField.tsx` around lines 28
- 36, Update the selected-icon button in CreateTodoIconField so its aria-label
describes reopening the panel to change the existing icon, using a dedicated
change-icon label instead of addIconLabel. Keep addIconLabel for the no-icon
state and preserve the current onOpenPanel behavior.
apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx (1)

13-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

메모 입력창에 접근 가능한 이름이 없어요.

textareaid/aria-label 없이 placeholder만 있어서 스크린 리더 사용자는 이 입력창의 용도를 알기 어렵습니다. 최소한 aria-label이라도 붙여주시면 좋겠습니다 (MDN: ARIA: textbox role 참고).

+export interface CreateTodoMemoFieldProps {
+  value: string;
+  placeholder: string;
+  onChange: (value: string) => void;
+  ariaLabel: string;
+}
...
   <textarea
     value={value}
     onChange={(event) => onChange(event.target.value)}
     placeholder={placeholder}
+    aria-label={ariaLabel}
     rows={1}
🤖 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/components/todo-modal/CreateTodoMemoField.tsx` around lines 13
- 19, Update the textarea in CreateTodoMemoField to provide an accessible name,
adding an appropriate aria-label while preserving the existing value, change
handler, placeholder, and styling behavior.
apps/timo-web/components/todo-modal/CreateTodoModalContent.tsx (2)

30-42: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

태그를 고르지 않아도 조용히 "dailyLife" 태그가 붙어요.

createDefaultValues에서 tagId: null로 시작하기 때문에 TodoToolbar에는 태그 미선택 placeholder(createModal.tagLabel)가 보입니다. 하지만 handleFormSubmit(95~104번 줄)에서 selectedTagOption이 없으면 DEFAULT_TAG로 대체해버려서, 사용자는 "태그 없음"으로 봤는데 실제로는 "dailyLife" 태그가 저장되는 불일치가 생겨요. 기본값을 DEFAULT_TAG.id로 맞춰서 UI와 실제 저장값을 일치시키는 게 좋을 것 같습니다.

♻️ 제안
+import { DEFAULT_TAG } from "`@/hooks/todo-modal/use-tag-field`";
+
 const createDefaultValues = (defaultDate?: Date): CreateTodoRequest => ({
   icon: null,
   title: "",
   subtasks: [],
   date: formatDateKey(defaultDate ?? new Date()),
   duration: "0:00",
   priority: null,
-  tagId: null,
+  tagId: DEFAULT_TAG.id,
   repeatType: "NONE",
📝 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.

import { DEFAULT_TAG } from "`@/hooks/todo-modal/use-tag-field`";

const createDefaultValues = (defaultDate?: Date): CreateTodoRequest => ({
  icon: null,
  title: "",
  subtasks: [],
  date: formatDateKey(defaultDate ?? new Date()),
  duration: "0:00",
  priority: null,
  tagId: DEFAULT_TAG.id,
  repeatType: "NONE",
  repeatWeekdays: [],
  repeatDayOfMonth: null,
  memo: null,
});
🤖 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/components/todo-modal/CreateTodoModalContent.tsx` around lines
30 - 42, Update createDefaultValues so tagId defaults to DEFAULT_TAG.id instead
of null, keeping the TodoToolbar initial state and handleFormSubmit persistence
behavior consistent. Reuse the existing DEFAULT_TAG symbol and leave unrelated
default fields unchanged.

182-207: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# TodoToolbar가 반복 해제(NONE) 관련 prop을 요구하는지 확인
fd -e ts -e tsx TodoToolbar packages/timo-design-system | xargs -I{} sed -n '1,80p' {}

Repository: Team-Timo/Timo-client

Length of output: 1964


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## TodoToolbar / RepeatSelector / useRepeatField references"
rg -n --hidden --glob '!node_modules' --glob '!dist' --glob '!build' \
  'interface RepeatSelectorProps|type RepeatSelectorProps|onRepeatActiveChange|isRepeatActive|handleFrequencyChange|RepeatFrequency|NONE|repeatType' \
  apps packages

echo
echo "## Locate RepeatSelector source"
fd -e ts -e tsx RepeatSelector apps packages

echo
echo "## Locate useRepeatField source"
fd -e ts -e tsx useRepeatField apps packages

Repository: Team-Timo/Timo-client

Length of output: 5870


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## RepeatSelector.tsx"
sed -n '1,260p' packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx

echo
echo "## use-repeat-field.ts"
sed -n '1,220p' apps/timo-web/hooks/todo-modal/use-repeat-field.ts

echo
echo "## CreateTodoModalContent.tsx relevant section"
sed -n '1,240p' apps/timo-web/components/todo-modal/CreateTodoModalContent.tsx

Repository: Team-Timo/Timo-client

Length of output: 16646


반복을 “안 함(NONE)”으로 되돌리는 경로를 추가하세요.
지금 RepeatSelectorProps에는 onFrequencyChange만 있어서 DAILY/WEEKLY/MONTHLY로만 이동하고, useRepeatFieldrepeatType === "NONE"을 UI에서 다시 선택할 수 있게 열어두지 않습니다. 사용자가 반복을 한 번 켠 뒤에는 해제할 방법이 없으니, NONE 옵션을 selector에 추가하거나 onRepeatActiveChange 같은 별도 핸들러를 넘겨서 토글 가능하게 바꾸는 게 좋습니다.
참고: React Hook Form Controller 문서, 컴포넌트 상태 설계 가이드.

🤖 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/components/todo-modal/CreateTodoModalContent.tsx` around lines
182 - 207, 반복 설정을 다시 “NONE”으로 선택할 수 있도록 CreateTodoModalContent의 repeat 옵션과
RepeatSelectorProps 흐름을 확장하세요. useRepeatField의 repeatType 상태가 NONE으로 전환되도록
onFrequencyChange 또는 별도 활성화 핸들러를 연결하고, 선택 시 기존 반복 설정이 비활성화되는 UI 동작을 유지하세요.
apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx (1)

25-29: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

<input>에 라벨 연결이 빠졌어요.

제목/서브태스크 <input> 둘 다 id<label htmlFor> 연결이 없어 스크린 리더 사용자에게 필드 목적이 전달되지 않습니다. 경로 지침상 <input>에는 id 부여 후 <label htmlFor> 연결이 필요합니다. 시각적으로 라벨을 노출하기 어렵다면 aria-label로라도 대체해 주세요 (WAI-ARIA Authoring Practices의 텍스트 필드 라벨링 가이드 참고).

 <input
   {...register("title")}
   placeholder={titlePlaceholder}
+  aria-label={titlePlaceholder}
   className="typo-headline-b-14 text-timo-black min-w-0 flex-1 outline-none"
 />
...
 <input
   value={subtaskInput}
   onChange={(event) => onSubtaskInputChange(event.target.value)}
   placeholder={subtaskPlaceholder}
+  aria-label={subtaskPlaceholder}
   className="typo-body-r-12 text-timo-gray-800 min-w-0 flex-1 outline-none"
 />

<input>id 부여 후 <label htmlFor> 연결이라는 접근성 컨벤션에 근거한 코멘트입니다.

Also applies to: 33-38

🤖 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/components/todo-modal/CreateTodoTaskFields.tsx` around lines 25
- 29, Update both title and subtask inputs in CreateTodoTaskFields to include
unique id values and associate each with a corresponding label via htmlFor; if
visible labels are unavailable, provide an appropriate aria-label instead.

Source: Path instructions

apps/timo-web/hooks/todo-modal/use-repeat-field.ts (1)

62-67: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

repeatDayOfMonth 범위 검증이 빠져 있어요.

createTodoRequestSchemarepeatDayOfMonthmin(1).max(31)로 제한하는데, 이 핸들러는 Number.isFinite만 확인하고 바로 폼에 반영합니다. 사용자가 "0"이나 "45" 같은 값을 입력하면 즉시 저장되고, 제출 시점에서야 zod 에러로 튕겨나가게 됩니다. 입력 시점에 미리 걸러주면 UX가 더 매끄러울 거예요.

참고: Zod number validations

♻️ 제안
   const handleRepeatDayChange = (value: string) => {
     const parsed = Number(value);
-    repeatDayField.field.onChange(
-      value.trim() && Number.isFinite(parsed) ? parsed : null,
-    );
+    const isValidDay =
+      value.trim() && Number.isFinite(parsed) && parsed >= 1 && parsed <= 31;
+    repeatDayField.field.onChange(isValidDay ? parsed : null);
   };
📝 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.

  const handleRepeatDayChange = (value: string) => {
    const parsed = Number(value);
    const isValidDay =
      value.trim() && Number.isFinite(parsed) && parsed >= 1 && parsed <= 31;
    repeatDayField.field.onChange(isValidDay ? parsed : null);
  };
🤖 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/hooks/todo-modal/use-repeat-field.ts` around lines 62 - 67,
Update handleRepeatDayChange to accept repeat-day values only when they are
finite and within the createTodoRequestSchema range of 1 through 31; otherwise
pass null to repeatDayField.field.onChange. Preserve the existing trimmed-empty
input behavior.
packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx (1)

17-24: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

유니언 타입 별칭 이름을 컨벤션에 맞춰주세요.

DropdownAlign은 유니언 type alias이므로 저장소의 ...Types 접미사 규칙을 위반합니다. 공개 API로 추가된 타입인 만큼 DropdownAlignTypes로 이름을 바꾸고 모든 참조도 함께 갱신해 주세요.

공식 참고: TypeScript Handbook의 Type Aliases 설명. (typescriptlang.org)

제안 수정
-export type DropdownAlign = "left" | "right";
+export type DropdownAlignTypes = "left" | "right";

 interface DropdownContextValue {
-  align: DropdownAlign;
+  align: DropdownAlignTypes;
 }

 export interface DropdownProps {
-  align?: DropdownAlign;
+  align?: DropdownAlignTypes;
 }

Also applies to: 41-51

🤖 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 `@packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx`
around lines 17 - 24, Rename the public union type alias DropdownAlign to
DropdownAlignTypes to follow the repository naming convention, and update every
reference to it, including the align property in DropdownContextValue and any
related component/API usage. Preserve the union members and existing behavior.

Source: Path instructions

@jjangminii jjangminii left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

수고하셨습니다~ 간단한 코멘트 남겼으니 확인해주세요

Comment thread apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx Outdated
ehye1 and others added 8 commits July 13, 2026 22:48
- TodayDayHeaderContainer를 추가하고 완료/전체 투두 개수를 표시했습니다
- TodayTodoListContainer에 헤더 컨테이너를 연동하고 스크롤 레이아웃을 적용했습니다
- Today 페이지 레이아웃을 h-full 기반으로 변경해 스크롤 영역이 정상 동작하도록 했습니다
…141)

- 로컬 isPlaying state와 useEffect를 제거하고 timerStatus prop에서 직접 파생했습니다
- 부모가 timerStatus를 관리하므로 로컬 상태 이중 관리가 불필요했습니다
- 스크롤 동작 확인을 위해 todo 목데이터를 3개에서 8개로 늘렸습니다
투두 리스트 컨테이너에 pr-1 클래스를 추가해 오른쪽 패딩을 적용했습니다

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…분리 (#141)

TodayDayHeaderContainer를 TodayDateHeaderContainer로 리네임하고,
HomeDateInformation을 (with-time-sidebar)/_components/DateInformation으로 이동했습니다

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 투두 모달 컴포넌트를 home/_components에서 components/todo-modal로 이동했습니다
- 투두 모달 훅을 home/_hooks에서 hooks/todo-modal로 이동했습니다
- 태그 모달 컨테이너를 home/_containers에서 components/tag-modal로 이동했습니다
- CreateTodoToolbar 컴포넌트를 삭제했습니다
- 오늘 화면 투두카드 클릭 시 상세/수정 모달을 열도록 구현했습니다
- 투두 완료 체크박스 토글 인터랙션을 추가했습니다
- TodayTodoListContainer에서 투두 목록 인터랙션 핸들러를 연결했습니다
- TodayDateHeaderContainer에 날짜 표시 로직을 적용했습니다
- 오늘 화면 전용 투두 생성 모달 컨테이너를 추가했습니다
- 오늘 화면 전용 투두 관련 훅과 유틸리티를 추가했습니다
- CreateTodoModalContainer에서 공통 경로의 모달 컴포넌트를 사용하도록 수정했습니다
- use-create-todo-submit 훅을 공통 로직에 맞게 리팩토링했습니다
- CreateTodoModalContent를 공통 컴포넌트로 분리한 내용을 반영했습니다
- use-tag-field 훅을 공통 훅으로 분리한 내용을 반영했습니다
ehye1 added 4 commits July 13, 2026 22:56
- OAuth 콜백 처리 로직을 수정했습니다
- Dropdown 컴포넌트 동작을 수정했습니다
- 아이콘 버튼을 span으로 교체하여 클릭 불가 처리했습니다
- 툴바에 pointer-events-none을 적용하여 표시만 되도록 변경했습니다
- 카드 전체를 클릭하면 상세 모달이 열리는 구조로 변경했습니다 (onCardClick, 추후 연결 예정)
- 체크박스, 플레이버튼, 삭제버튼에 stopPropagation을 추가했습니다
- 삭제 버튼 aria-label을 Common.delete i18n 키로 교체했습니다
…141)

- 투두 목록 상태 및 핸들러를 useTodayTodoList 커스텀 훅으로 추출했습니다
- TodayTodoListContainer는 훅 호출과 렌더링만 담당하도록 변경했습니다
@ehye1
ehye1 force-pushed the feat/web/141-today-todocard-interaction branch from d1241f5 to 1e10160 Compare July 13, 2026 14:00
@github-actions github-actions Bot added the ⌚ Timo-Design-system Timo 디자인 시스템 label Jul 13, 2026
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

Storybook Preview

항목 링크
Storybook 열기
Chromatic 빌드 확인

마지막 업데이트: 2026-07-13 14:29 UTC

- getToday를 home/_utils/date가 아닌 @/utils/date에서 임포트하도록 수정했습니다
- parseDateKey를 home/_utils/date가 아닌 @/utils/date에서 임포트하도록 수정했습니다
- toolbar 인터페이스에 dateValue: Date를 추가하고 isDimmed 상태에 따라 date/time prop을 조건부 전달하여 아이콘 색상을 처리했습니다
- onDelete prop을 제거하고 trash 버튼을 span으로 교체하여 카드를 display-only로 전환했습니다

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx (1)

62-95: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

onOpenChangetoggle에서만 호출되고, 다른 닫기 경로에서는 누락됩니다.

toggle(Line 90-94)에서는 onOpenChange?.(next)를 정상적으로 호출하지만, 외부 클릭(Line 69), Escape 키(Line 75), 그리고 close 함수(Line 95)는 모두 setIsOpen(false)만 호출하고 onOpenChange?.(false)를 전달하지 않습니다. 이로 인해 onOpenChange로 열림/닫힘 상태를 추적하는 소비자는 드롭다운이 닫혔을 때 알림을 받지 못합니다.

모든 닫기 경로에서 onOpenChange가 일관되게 호출되도록 수정해 주세요.

🔧 제안하는 수정안
 const handleOutsideClick = (event: MouseEvent) => {
   if (!rootRef.current?.contains(event.target as Node)) {
-    setIsOpen(false);
+    close();
   }
 };

 const handleEscapeKeydown = (event: KeyboardEvent) => {
   if (event.key === "Escape") {
-    setIsOpen(false);
+    close();
     triggerRef.current?.focus();
   }
 };
-  const close = () => setIsOpen(false);
+  const close = () => {
+    setIsOpen(false);
+    onOpenChange?.(false);
+  };

참고: closeuseEffect 내부에서 사용할 경우, effect 의존성 배열에 close가 포함되지 않아 stale closure가 발생할 수 있습니다. effect 내부에서 직접 onOpenChange?.(false)를 호출하거나, onOpenChange를 ref로 감싸서 stale closure를 방지하는 패턴을 고려해 보세요.

🤖 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 `@packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx`
around lines 62 - 95, Ensure every dropdown closing path consistently invokes
onOpenChange with false: update the outside-click handler, Escape-key handler,
and close function alongside setIsOpen(false). Preserve the existing toggle
behavior, and avoid introducing a stale closure by handling the callback
directly within the effect or using an appropriate stable callback/ref pattern.
🤖 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)/(with-time-sidebar)/home/_containers/todo-card/HomeDayHeaderContainer.tsx:
- Around line 8-12: Move the shared date utilities used by
TodayDateHeaderContainer and HomeDayHeaderContainer, including
convertDateToDateText, getToday, and parseDateKey as applicable, from
home/_utils/date into the project’s shared lib or common package. Update both
containers to import them through the new shared entry point, preserving
existing behavior and removing direct cross-domain imports from home.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContainer.tsx:
- Around line 8-9: 정적 분석 규칙에 맞게 CreateTodoModalContainer의 import 순서를 조정하세요.
AnimatedToast import를 CreateTodoModalContent import보다 앞에 배치하고, 다른 import나 코드는
변경하지 마세요.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx:
- Line 8: Move or reuse convertDateToDateText from a shared date utility
location such as `@/utils/date` instead of importing it from the home domain.
Update TodayDateHeaderContainer to reference the shared export while preserving
the current date-text behavior, and remove the direct home-domain dependency.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx:
- Around line 40-44: Update TodayTodoCardContainer to derive isDone and subTodos
directly from the current initialIsDone and initialSubTodos props, removing
their local useState declarations and any dependent synchronization. Preserve
the existing isPlaying derivation pattern and ensure updated parent values are
reflected when the reused card receives new props.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx:
- Around line 36-62: Update the todo list rendering around the todos.map call to
use semantic ul/li elements instead of the outer div and direct card children.
Preserve the existing layout classes and TodayTodoCardContainer props, placing
each rendered todo card inside an li with a stable key.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts:
- Around line 15-47: Update useCreateTodoSubmit and buildTodoFromRequest so
handleSubmit accepts only CreateTodoRequest, deriving the tag from data.tagId or
assigning null as appropriate. In
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
lines 15-47, remove the required tag parameter and update TodoMock construction
accordingly;
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsx
lines 22-31 requires no direct change because onSubmit={handleSubmit} will then
match the corrected signature.
- Around line 15-36: Update handleSubmit to accept only the CreateTodoRequest
passed by CreateTodoModalContent, and remove its required tag argument. In
buildTodoFromRequest, derive the tag from data.tagId using the available tag
source or assign null when no tag exists, then guard tag.id and tag.name access
so submitting without a tag cannot crash.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts:
- Around line 28-37: Update handleCheck in useTodayTodoList so toggling a todo’s
completed state also updates its subtasks consistently in the parent todos
state, or make the card render subtasks exclusively from that parent state.
Ensure completion and uncompletion cannot leave local subtask state diverging
from todos[].subtasks.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock.ts:
- Around line 77-161: Extract the duplicated TodoMock interface into a shared
todo type module under the today feature, then update today-todo-mock.ts,
useTodayTodoList.ts, and use-create-todo-submit.ts to import and reuse that
definition. Remove the local duplicate declarations while preserving the
existing TodoMock shape and behavior.

In `@apps/timo-web/app/`[locale]/(main)/today/_components/TodayTodoCard.tsx:
- Around line 163-197: Replace the hardcoded Korean labels passed to TodoToolbar
with translations from the existing useTranslations("Common") instance,
including the tag fallback, repeat detail heading, daily/weekly/monthly option
labels, and repeat-day label. Add or reuse the corresponding translation keys in
en.json and preserve the current Korean strings through localized values.

In `@apps/timo-web/components/todo-modal/CreateTodoIconField.tsx`:
- Around line 29-36: Update the icon panel trigger button in
CreateTodoIconField, identified by its onClick={onOpenPanel} handler, to include
aria-expanded={false}. Leave the existing toggle button and other button
attributes unchanged.

In `@apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx`:
- Around line 3-7: Extract the duplicated resizeTextarea implementation into a
shared utility, preserving its null handling and textarea height behavior. In
apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx lines 3-7 and
apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx lines 6-10, remove
the local definitions and import the shared resizeTextarea utility instead.
- Line 29: Replace the outline-none-only focus treatment on the three textarea
classNames: CreateTodoMemoField.tsx lines 29-29, CreateTodoTaskFields.tsx lines
51-51, and CreateTodoTaskFields.tsx lines 71-71. Add a visible focus or
focus-visible style to each textarea, preserving the existing styling while
ensuring keyboard users can identify the focused field.

In `@apps/timo-web/components/todo-modal/CreateTodoModalContent.tsx`:
- Around line 13-14: Move useSubtaskField and useTitleField out of the
home-specific _hooks/todo-modal location into the shared hooks/todo-modal
directory, then update CreateTodoModalContent imports and any affected
consumers. Preserve both hooks’ behavior while removing the shared component’s
direct dependency on the home domain.
- Line 22: Remove the unused DEFAULT_TAG import from the import declaration in
CreateTodoModalContent.tsx, while retaining the useTagField import.

In `@apps/timo-web/messages/ko.json`:
- Line 206: Update the Focus.stopModal.switchButton Korean translation from
“완료하기” to a term matching the original “Switch,” such as “전환하기” or “바꾸기,” while
leaving Focus.completeModal.completeButton unchanged.

In `@packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx`:
- Line 21: Rename the DropdownAlign type alias to follow the convention by
adding the Types suffix, and update every reference to use the renamed type
while preserving the existing "left" | "right" union.

---

Outside diff comments:
In `@packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx`:
- Around line 62-95: Ensure every dropdown closing path consistently invokes
onOpenChange with false: update the outside-click handler, Escape-key handler,
and close function alongside setIsOpen(false). Preserve the existing toggle
behavior, and avoid introducing a stale closure by handling the callback
directly within the effect or using an appropriate stable callback/ref pattern.
🪄 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: 9cb66c6c-6c09-4747-b205-0d551fa385c8

📥 Commits

Reviewing files that changed from the base of the PR and between d1241f5 and 1e10160.

📒 Files selected for processing (27)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/_components/DateInformation.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-card/HomeDayHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_utils/todo-time.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/page.tsx
  • apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx
  • apps/timo-web/app/[locale]/oauth/callback/page.tsx
  • apps/timo-web/components/CreateTodoToolbar.tsx
  • apps/timo-web/components/tag-modal/CreateTagModalContainer.tsx
  • apps/timo-web/components/todo-modal/CreateTodoIconField.tsx
  • apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx
  • apps/timo-web/components/todo-modal/CreateTodoModalContent.tsx
  • apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx
  • apps/timo-web/hooks/todo-modal/use-icon-field.ts
  • apps/timo-web/hooks/todo-modal/use-repeat-field.ts
  • apps/timo-web/hooks/todo-modal/use-tag-field.tsx
  • apps/timo-web/hooks/todo-modal/use-time-field.ts
  • apps/timo-web/messages/en.json
  • apps/timo-web/messages/ko.json
  • packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx
💤 Files with no reviewable changes (2)
  • apps/timo-web/app/[locale]/oauth/callback/page.tsx
  • apps/timo-web/components/CreateTodoToolbar.tsx

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx (1)

62-95: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

onOpenChangetoggle에서만 호출되고, 다른 닫기 경로에서는 누락됩니다.

toggle(Line 90-94)에서는 onOpenChange?.(next)를 정상적으로 호출하지만, 외부 클릭(Line 69), Escape 키(Line 75), 그리고 close 함수(Line 95)는 모두 setIsOpen(false)만 호출하고 onOpenChange?.(false)를 전달하지 않습니다. 이로 인해 onOpenChange로 열림/닫힘 상태를 추적하는 소비자는 드롭다운이 닫혔을 때 알림을 받지 못합니다.

모든 닫기 경로에서 onOpenChange가 일관되게 호출되도록 수정해 주세요.

🔧 제안하는 수정안
 const handleOutsideClick = (event: MouseEvent) => {
   if (!rootRef.current?.contains(event.target as Node)) {
-    setIsOpen(false);
+    close();
   }
 };

 const handleEscapeKeydown = (event: KeyboardEvent) => {
   if (event.key === "Escape") {
-    setIsOpen(false);
+    close();
     triggerRef.current?.focus();
   }
 };
-  const close = () => setIsOpen(false);
+  const close = () => {
+    setIsOpen(false);
+    onOpenChange?.(false);
+  };

참고: closeuseEffect 내부에서 사용할 경우, effect 의존성 배열에 close가 포함되지 않아 stale closure가 발생할 수 있습니다. effect 내부에서 직접 onOpenChange?.(false)를 호출하거나, onOpenChange를 ref로 감싸서 stale closure를 방지하는 패턴을 고려해 보세요.

🤖 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 `@packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx`
around lines 62 - 95, Ensure every dropdown closing path consistently invokes
onOpenChange with false: update the outside-click handler, Escape-key handler,
and close function alongside setIsOpen(false). Preserve the existing toggle
behavior, and avoid introducing a stale closure by handling the callback
directly within the effect or using an appropriate stable callback/ref pattern.
🤖 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)/(with-time-sidebar)/home/_containers/todo-card/HomeDayHeaderContainer.tsx:
- Around line 8-12: Move the shared date utilities used by
TodayDateHeaderContainer and HomeDayHeaderContainer, including
convertDateToDateText, getToday, and parseDateKey as applicable, from
home/_utils/date into the project’s shared lib or common package. Update both
containers to import them through the new shared entry point, preserving
existing behavior and removing direct cross-domain imports from home.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContainer.tsx:
- Around line 8-9: 정적 분석 규칙에 맞게 CreateTodoModalContainer의 import 순서를 조정하세요.
AnimatedToast import를 CreateTodoModalContent import보다 앞에 배치하고, 다른 import나 코드는
변경하지 마세요.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx:
- Line 8: Move or reuse convertDateToDateText from a shared date utility
location such as `@/utils/date` instead of importing it from the home domain.
Update TodayDateHeaderContainer to reference the shared export while preserving
the current date-text behavior, and remove the direct home-domain dependency.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx:
- Around line 40-44: Update TodayTodoCardContainer to derive isDone and subTodos
directly from the current initialIsDone and initialSubTodos props, removing
their local useState declarations and any dependent synchronization. Preserve
the existing isPlaying derivation pattern and ensure updated parent values are
reflected when the reused card receives new props.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx:
- Around line 36-62: Update the todo list rendering around the todos.map call to
use semantic ul/li elements instead of the outer div and direct card children.
Preserve the existing layout classes and TodayTodoCardContainer props, placing
each rendered todo card inside an li with a stable key.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts:
- Around line 15-47: Update useCreateTodoSubmit and buildTodoFromRequest so
handleSubmit accepts only CreateTodoRequest, deriving the tag from data.tagId or
assigning null as appropriate. In
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
lines 15-47, remove the required tag parameter and update TodoMock construction
accordingly;
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsx
lines 22-31 requires no direct change because onSubmit={handleSubmit} will then
match the corrected signature.
- Around line 15-36: Update handleSubmit to accept only the CreateTodoRequest
passed by CreateTodoModalContent, and remove its required tag argument. In
buildTodoFromRequest, derive the tag from data.tagId using the available tag
source or assign null when no tag exists, then guard tag.id and tag.name access
so submitting without a tag cannot crash.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts:
- Around line 28-37: Update handleCheck in useTodayTodoList so toggling a todo’s
completed state also updates its subtasks consistently in the parent todos
state, or make the card render subtasks exclusively from that parent state.
Ensure completion and uncompletion cannot leave local subtask state diverging
from todos[].subtasks.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock.ts:
- Around line 77-161: Extract the duplicated TodoMock interface into a shared
todo type module under the today feature, then update today-todo-mock.ts,
useTodayTodoList.ts, and use-create-todo-submit.ts to import and reuse that
definition. Remove the local duplicate declarations while preserving the
existing TodoMock shape and behavior.

In `@apps/timo-web/app/`[locale]/(main)/today/_components/TodayTodoCard.tsx:
- Around line 163-197: Replace the hardcoded Korean labels passed to TodoToolbar
with translations from the existing useTranslations("Common") instance,
including the tag fallback, repeat detail heading, daily/weekly/monthly option
labels, and repeat-day label. Add or reuse the corresponding translation keys in
en.json and preserve the current Korean strings through localized values.

In `@apps/timo-web/components/todo-modal/CreateTodoIconField.tsx`:
- Around line 29-36: Update the icon panel trigger button in
CreateTodoIconField, identified by its onClick={onOpenPanel} handler, to include
aria-expanded={false}. Leave the existing toggle button and other button
attributes unchanged.

In `@apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx`:
- Around line 3-7: Extract the duplicated resizeTextarea implementation into a
shared utility, preserving its null handling and textarea height behavior. In
apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx lines 3-7 and
apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx lines 6-10, remove
the local definitions and import the shared resizeTextarea utility instead.
- Line 29: Replace the outline-none-only focus treatment on the three textarea
classNames: CreateTodoMemoField.tsx lines 29-29, CreateTodoTaskFields.tsx lines
51-51, and CreateTodoTaskFields.tsx lines 71-71. Add a visible focus or
focus-visible style to each textarea, preserving the existing styling while
ensuring keyboard users can identify the focused field.

In `@apps/timo-web/components/todo-modal/CreateTodoModalContent.tsx`:
- Around line 13-14: Move useSubtaskField and useTitleField out of the
home-specific _hooks/todo-modal location into the shared hooks/todo-modal
directory, then update CreateTodoModalContent imports and any affected
consumers. Preserve both hooks’ behavior while removing the shared component’s
direct dependency on the home domain.
- Line 22: Remove the unused DEFAULT_TAG import from the import declaration in
CreateTodoModalContent.tsx, while retaining the useTagField import.

In `@apps/timo-web/messages/ko.json`:
- Line 206: Update the Focus.stopModal.switchButton Korean translation from
“완료하기” to a term matching the original “Switch,” such as “전환하기” or “바꾸기,” while
leaving Focus.completeModal.completeButton unchanged.

In `@packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx`:
- Line 21: Rename the DropdownAlign type alias to follow the convention by
adding the Types suffix, and update every reference to use the renamed type
while preserving the existing "left" | "right" union.

---

Outside diff comments:
In `@packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx`:
- Around line 62-95: Ensure every dropdown closing path consistently invokes
onOpenChange with false: update the outside-click handler, Escape-key handler,
and close function alongside setIsOpen(false). Preserve the existing toggle
behavior, and avoid introducing a stale closure by handling the callback
directly within the effect or using an appropriate stable callback/ref pattern.
🪄 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: 9cb66c6c-6c09-4747-b205-0d551fa385c8

📥 Commits

Reviewing files that changed from the base of the PR and between d1241f5 and 1e10160.

📒 Files selected for processing (27)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/_components/DateInformation.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-card/HomeDayHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_utils/todo-time.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/page.tsx
  • apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx
  • apps/timo-web/app/[locale]/oauth/callback/page.tsx
  • apps/timo-web/components/CreateTodoToolbar.tsx
  • apps/timo-web/components/tag-modal/CreateTagModalContainer.tsx
  • apps/timo-web/components/todo-modal/CreateTodoIconField.tsx
  • apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx
  • apps/timo-web/components/todo-modal/CreateTodoModalContent.tsx
  • apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx
  • apps/timo-web/hooks/todo-modal/use-icon-field.ts
  • apps/timo-web/hooks/todo-modal/use-repeat-field.ts
  • apps/timo-web/hooks/todo-modal/use-tag-field.tsx
  • apps/timo-web/hooks/todo-modal/use-time-field.ts
  • apps/timo-web/messages/en.json
  • apps/timo-web/messages/ko.json
  • packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx
💤 Files with no reviewable changes (2)
  • apps/timo-web/app/[locale]/oauth/callback/page.tsx
  • apps/timo-web/components/CreateTodoToolbar.tsx
🛑 Comments failed to post (17)
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-card/HomeDayHeaderContainer.tsx (1)

8-12: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# TodayDateHeaderContainer가 home/_utils/date에서 import하는지 확인
rg -n "home/_utils" apps/timo-web/app/\[locale\]/\(main\)/\(with-time-sidebar\)/today/ --type ts --type tsx

Repository: Team-Timo/Timo-client

Length of output: 187


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## related files\n'
git ls-files 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/**' | sed -n '1,200p'

printf '\n## direct imports of home/_utils/date\n'
rg -n 'home/_utils/date' apps/timo-web/app/[locale]/\(main\)/\(with-time-sidebar\) -g '*.ts' -g '*.tsx'

printf '\n## imports of _utils/date and nearby context\n'
rg -n '_utils/date' apps/timo-web/app/[locale]/\(main\)/\(with-time-sidebar\) -g '*.ts' -g '*.tsx'

Repository: Team-Timo/Timo-client

Length of output: 1088


공통 날짜 유틸은 home 밖으로 빼주세요
좋은 분리입니다. 다만 today/_containers/TodayDateHeaderContainer.tsxhome/_containers/todo-card/HomeDayHeaderContainer.tsx가 모두 home/_utils/date를 직접 가져가고 있어 도메인 간 직접 import 규칙을 다시 밟고 있습니다. convertDateToDateText 같은 공통 로직은 lib/(또는 공용 packages/)로 옮겨 두 도메인이 같은 진입점을 쓰게 해주세요.
참고: docs/architecture/components.md

🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] 8-8: Turbopack build failed: Export getToday doesn't exist in target module. (Import at line 8 reported as missing export)


[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🪛 GitHub Actions: ✅ CI / Build

[error] 8-8: Turbopack build failed: Export getToday doesn't exist in target module (import at line 8).

🪛 GitHub Actions: ✨ Performance Report / 0_성능 분석.txt

[error] 8-12: Turbopack build failed: Export getToday doesn't exist in target module "apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_utils/date.ts". Did you mean to import formatDateToIsoDate?


[error] 8-12: Turbopack build failed: Export parseDateKey doesn't exist in target module "apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_utils/date.ts". Did you mean to import convertDateToDateText?

🪛 GitHub Actions: ✨ Performance Report / 성능 분석

[error] 8-12: Turbopack build failed: Export "getToday" was not found in module "apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_utils/date.ts". Did you mean to import "formatDateToIsoDate"?


[error] 8-12: Turbopack build failed: Export "parseDateKey" was not found in module "apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_utils/date.ts". Did you mean to import "convertDateToDateText"?

🤖 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)/(with-time-sidebar)/home/_containers/todo-card/HomeDayHeaderContainer.tsx
around lines 8 - 12, Move the shared date utilities used by
TodayDateHeaderContainer and HomeDayHeaderContainer, including
convertDateToDateText, getToday, and parseDateKey as applicable, from
home/_utils/date into the project’s shared lib or common package. Update both
containers to import them through the new shared entry point, preserving
existing behavior and removing direct cross-domain imports from home.

Source: Path instructions

apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContainer.tsx (1)

8-9: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

import 순서를 정렬해주세요.

정적 분석에서 @/components/toast/AnimatedToast@/components/todo-modal/CreateTodoModalContent보다 먼저 와야 한다고 지적했습니다. 두 줄을 바꾸면 됩니다.

🧹 제안 수정
-import { CreateTodoModalContent } from "`@/components/todo-modal/CreateTodoModalContent`";
-import { AnimatedToast } from "`@/components/toast/AnimatedToast`";
+import { AnimatedToast } from "`@/components/toast/AnimatedToast`";
+import { CreateTodoModalContent } from "`@/components/todo-modal/CreateTodoModalContent`";
📝 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.

import { AnimatedToast } from "`@/components/toast/AnimatedToast`";
import { CreateTodoModalContent } from "`@/components/todo-modal/CreateTodoModalContent`";
🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🪛 GitHub Check: Lint

[warning] 9-9:
@/components/toast/AnimatedToast import should occur before import of @/components/todo-modal/CreateTodoModalContent

🤖 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)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContainer.tsx
around lines 8 - 9, 정적 분석 규칙에 맞게 CreateTodoModalContainer의 import 순서를 조정하세요.
AnimatedToast import를 CreateTodoModalContent import보다 앞에 배치하고, 다른 import나 코드는
변경하지 마세요.

Source: Linters/SAST tools

apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx (1)

8-8: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

도메인 간 직접 import — home 유틸을 today에서 바로 가져오고 있어요.

convertDateToDateTexthome/_utils/date에서 직접 import하고 있는데, 바로 아래 줄(10번)에서는 이미 공유 유틸(@/utils/date)의 getToday를 쓰고 있는 것과 대비됩니다. 도메인 경계를 지키려면 이 유틸도 home이 아니라 공유 위치(utils/date.ts 등)로 옮겨서 참조해야 합니다.

과거 리뷰에서도 동일한 패턴(kimminna)에 대해 "공통 유틸로 빼뒀으니 머지되면 교체하자"는 코멘트가 있었던 걸로 봐서, 이미 준비된 공용 유틸이 있다면 지금 바로 교체하는 게 좋아보여요.

♻️ 제안 (공용 유틸이 존재한다는 가정)
-import { convertDateToDateText } from "`@/app/`[locale]/(main)/(with-time-sidebar)/home/_utils/date";
+import { convertDateToDateText } from "`@/utils/date`";

As per path instructions, "도메인 간 직접 import 금지 — 공유 로직은 반드시 lib/ 또는 packages/로 추출" 규정을 참고했습니다. Based on learnings, 이전 PR(#111)에서 동일한 유틸 공통화 이슈가 제기된 적이 있습니다.

🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🤖 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)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx
at line 8, Move or reuse convertDateToDateText from a shared date utility
location such as `@/utils/date` instead of importing it from the home domain.
Update TodayDateHeaderContainer to reference the shared export while preserving
the current date-text behavior, and remove the direct home-domain dependency.

Sources: Path instructions, Learnings

apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx (1)

40-44: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

isPlaying처럼 isDone/subTodos도 prop에서 직접 파생하는 게 어떨까요?

이번 diff에서 isPlayinguseState+useEffect 동기화 대신 timerStatus prop에서 바로 파생하도록 정리됐는데, isDone/subTodos는 여전히 마운트 시점 값만 캡처하는 로컬 useState로 남아있어요. 이 카드는 key={todo.todoId}로 언마운트 없이 재사용되므로, 부모(useTodayTodoList)가 이후에 isDone/subTodos를 갱신해도 이 로컬 state는 절대 따라가지 않습니다. isPlaying에 적용한 패턴을 그대로 확장하면 좋아보입니다.

React 공식 문서 - state를 prop과 동기화하기 위해 key 사용하기도 참고해보세요.

🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🤖 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)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx
around lines 40 - 44, Update TodayTodoCardContainer to derive isDone and
subTodos directly from the current initialIsDone and initialSubTodos props,
removing their local useState declarations and any dependent synchronization.
Preserve the existing isPlaying derivation pattern and ensure updated parent
values are reflected when the reused card receives new props.
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx (1)

36-62: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

투두 목록을 <ul>/<li>로 감싸는 걸 고려해보세요.

서브태스크 리스트(TodayTodoCard.tsx)는 이미 <ul>/<li>로 구성돼 있는데, 상위 투두 리스트는 여전히 일반 div로 렌더링되고 있어요. 스크린리더 사용자가 "몇 개 항목의 목록"이라는 걸 인지할 수 있도록 시맨틱 리스트 마크업으로 통일하면 좋을 것 같습니다.

♻️ 제안
-      <div className="flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto pr-1 pb-4">
+      <ul className="flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto pr-1 pb-4">
         {todos.map((todo) => (
-          <TodayTodoCardContainer
-            key={todo.todoId}
-            ...
-          />
+          <li key={todo.todoId}>
+            <TodayTodoCardContainer ... />
+          </li>
         ))}
-      </div>
+      </ul>

As per path instructions, **/*.{ts,tsx} 규칙 중 "div 남발 금지 → ... 시맨틱 태그 사용"을 참고했습니다.

📝 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.

      <ul className="flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto pr-1 pb-4">
        {todos.map((todo) => (
          <li key={todo.todoId}>
            <TodayTodoCardContainer
              title={todo.title}
              isDone={todo.completed}
              timerStatus={runningTodoId === todo.todoId ? "RUNNING" : "STOPPED"}
              toolbar={{
                date: formatDate(todo.date),
                time: convertDurationToTimeText(todo.durationSeconds),
                priority: PRIORITY_MAP[todo.priority],
                tag: todo.tag?.name,
                hasMemo: todo.hasMemo,
                hasRepeat: todo.isRepeated,
              }}
              subTodos={todo.subtasks.map((s) => ({
                id: s.subtaskId,
                text: s.content,
                isDone: s.completed,
              }))}
              onPlay={() => handlePlay(todo.todoId)}
              onCheck={() => handleCheck(todo.todoId)}
              onDelete={() => handleDelete(todo.todoId)}
              onSubTodoCheck={(id) => handleSubTodoCheck(todo.todoId, id)}
            />
          </li>
        ))}
      </ul>
🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🤖 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)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx
around lines 36 - 62, Update the todo list rendering around the todos.map call
to use semantic ul/li elements instead of the outer div and direct card
children. Preserve the existing layout classes and TodayTodoCardContainer props,
placing each rendered todo card inside an li with a stable key.

Source: Path instructions

apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts (2)

15-36: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

buildTodoFromRequesttag 파라미터가 런타임에 undefined로 전달됩니다.

handleSubmit(data, tag) 두 개의 필수 인자를 요구하지만, CreateTodoModalContent에서 onSubmit(data)로 하나만 전달합니다. tagundefined가 되어 tag.id, tag.name 접근 시 크래시가 발생합니다. handleSubmit 시그니처를 (data: CreateTodoRequest) => void로 변경하고, buildTodoFromRequest에서 data.tagId로부터 tag를 파생하거나 null을 할당하도록 수정하세요.

🐛 제안 수정: handleSubmit 시그니처 및 buildTodoFromRequest 수정
-const buildTodoFromRequest = (
-  data: CreateTodoRequest,
-  tag: CreateTodoTag,
-): TodoMock => ({
+const buildTodoFromRequest = (data: CreateTodoRequest): TodoMock => ({
   todoId: Date.now(),
   icon: data.icon ?? "",
   title: data.title,
   completed: false,
   date: data.date,
   durationSeconds: convertApiDurationToSeconds(data.duration),
   priority: PRIORITY_TO_MOCK[data.priority ?? "MEDIUM"],
-  tag: { tagId: tag.id, name: tag.name },
+  tag: data.tagId ? { tagId: data.tagId, name: "" } : null,
   hasMemo: Boolean(data.memo?.trim()),
   isRepeated: data.repeatType !== "NONE",
   timerStatus: "STOPPED",
   sortOrder: 0,
   subtasks: (data.subtasks ?? []).map((content, index) => ({
     subtaskId: Date.now() + index,
     content,
     completed: false,
   })),
 });
-  const handleSubmit = (data: CreateTodoRequest, tag: CreateTodoTag) => {
-    onCreate(buildTodoFromRequest(data, tag));
+  const handleSubmit = (data: CreateTodoRequest) => {
+    onCreate(buildTodoFromRequest(data));
   };
🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🤖 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)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
around lines 15 - 36, Update handleSubmit to accept only the CreateTodoRequest
passed by CreateTodoModalContent, and remove its required tag argument. In
buildTodoFromRequest, derive the tag from data.tagId using the available tag
source or assign null when no tag exists, then guard tag.id and tag.name access
so submitting without a tag cannot crash.

15-47: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

handleSubmit 시그니처 불일치로 빌드 실패 및 런타임 크래시 발생. 두 파일 모두 동일한 근본 원인을 공유합니다: handleSubmit(data, tag) 두 개의 필수 인자를 요구하지만, CreateTodoModalContentonSubmit(data)로 하나만 전달합니다.

  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts#L15-L47: handleSubmit(data: CreateTodoRequest) => void로 변경하고, buildTodoFromRequest에서 data.tagId로부터 tag를 파생하거나 null을 할당하세요.
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsx#L22-L31: onSubmit={handleSubmit} 전달 시 타입이 일치하도록 위 수정을 적용하면 해결됩니다.
🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

📍 Affects 2 files
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts#L15-L47 (this comment)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsx#L22-L31
🤖 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)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
around lines 15 - 47, Update useCreateTodoSubmit and buildTodoFromRequest so
handleSubmit accepts only CreateTodoRequest, deriving the tag from data.tagId or
assigning null as appropriate. In
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/todo-modal/use-create-todo-submit.ts
lines 15-47, remove the required tag parameter and update TodoMock construction
accordingly;
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsx
lines 22-31 requires no direct change because onSubmit={handleSubmit} will then
match the corrected signature.
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts (1)

28-37: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate relevant files
fd -a "useTodayTodoList.ts|TodayTodoCardContainer.tsx|TodayTodoListContainer.tsx" apps/timo-web/app

# Show outline if available, then relevant file ranges
echo '--- useTodayTodoList.ts ---'
nl -ba apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts | sed -n '1,220p'

echo '--- TodayTodoCardContainer.tsx ---'
nl -ba apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx | sed -n '1,260p'

echo '--- TodayTodoListContainer.tsx ---'
nl -ba apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoListContainer.tsx | sed -n '1,260p'

# Search for usages of subtasks / completedCount / onSubTodoCheck
echo '--- usages ---'
rg -n "subtasks|completedCount|onSubTodoCheck|handleSubTodoCheck|isDone|completed:" apps/timo-web/app/[locale]/\(main\)/\(with-time-sidebar\)/today -g '!**/*.map'

Repository: Team-Timo/Timo-client

Length of output: 582


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base='apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today'

echo '--- useTodayTodoList.ts ---'
nl -ba "$base/_hooks/useTodayTodoList.ts" | sed -n '1,260p'

echo '--- TodayTodoCardContainer.tsx ---'
nl -ba "$base/_containers/TodayTodoCardContainer.tsx" | sed -n '1,320p'

echo '--- TodayTodoListContainer.tsx ---'
nl -ba "$base/_containers/TodayTodoListContainer.tsx" | sed -n '1,320p'

echo '--- usages ---'
rg -n "subtasks|completedCount|onSubTodoCheck|handleSubTodoCheck|isDone|completed:" "$base" -g '!**/*.map'

Repository: Team-Timo/Timo-client

Length of output: 227


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base='apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today'

python3 - <<'PY'
from pathlib import Path

base = Path('apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today')
for rel in [
    '_hooks/useTodayTodoList.ts',
    '_containers/TodayTodoCardContainer.tsx',
    '_containers/TodayTodoListContainer.tsx',
]:
    path = base / rel
    print(f'--- {path} ---')
    try:
        text = path.read_text()
    except Exception as e:
        print(f'ERROR: {e}')
        continue
    for i, line in enumerate(text.splitlines(), 1):
        if 1 <= i <= 260:
            print(f'{i:4d}: {line}')
    print()

print('--- usages ---')
for path in base.rglob('*'):
    if path.suffix in {'.ts', '.tsx'}:
        text = path.read_text()
        if any(k in text for k in ['subtasks', 'completedCount', 'onSubTodoCheck', 'handleSubTodoCheck', 'isDone', 'completed:']):
            print(path)
PY

Repository: Team-Timo/Timo-client

Length of output: 8529


투두 완료 시 서브태스크도 같은 원장에 맞춰 주세요.
useTodayTodoList.ts:28-37에서 todo의 completed만 바꾸면 subtasks는 그대로 남습니다. 지금은 카드가 서브태스크를 로컬 state로 따로 들고 있어서 화면상으론 맞아 보여도, 부모 todos[].subtasks와 쉽게 어긋납니다. 완료/해제 시 서브태스크까지 함께 갱신하거나, subTodos를 props 기반으로만 렌더링해 상태 출처를 하나로 두는 편이 안전합니다.
React 문서도 참고할 만합니다: https://react.dev/learn/sharing-state-between-components, https://react.dev/learn/preserving-and-resetting-state

🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🤖 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)/(with-time-sidebar)/today/_hooks/useTodayTodoList.ts
around lines 28 - 37, Update handleCheck in useTodayTodoList so toggling a
todo’s completed state also updates its subtasks consistently in the parent
todos state, or make the card render subtasks exclusively from that parent
state. Ensure completion and uncompletion cannot leave local subtask state
diverging from todos[].subtasks.
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock.ts (1)

77-161: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

TodoMock 인터페이스 중복 정의 제거 제안

추가된 모크 데이터는 TodoMock 인터페이스를 정확히 따르고 있으며, subtaskId·sortOrder 값도 기존 항목과 충돌 없이 고유하게 구성되어 있습니다. 다만 그래프 컨텍스트에 따르면 TodoMock 인터페이스가 today-todo-mock.ts, useTodayTodoList.ts, use-create-todo-submit.ts 3개 파일에 동일하게 정의되어 있습니다. 공유 타입 파일(예: today/_types/todo.ts)로 추출하여 DRY 원칙을 준수하는 것을 권장합니다.

#!/bin/bash
# TodoMock 인터페이스가 중복 정의되어 있는지 확인
rg -n "interface TodoMock" apps/timo-web/app/\[locale\]/\(main\)/\(with-time-sidebar\)/today/ --type ts
🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🤖 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)/(with-time-sidebar)/today/_mocks/today-todo-mock.ts
around lines 77 - 161, Extract the duplicated TodoMock interface into a shared
todo type module under the today feature, then update today-todo-mock.ts,
useTodayTodoList.ts, and use-create-todo-submit.ts to import and reuse that
definition. Remove the local duplicate declarations while preserving the
existing TodoMock shape and behavior.
apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx (1)

163-197: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

TodoToolbar에 넘기는 라벨들이 한국어로 하드코딩돼 있어요 — 영어 로케일에서 깨집니다.

tagLabel ?? "태그", detailHeading: "상세 설정", DAILY/WEEKLY/MONTHLY 옵션 라벨("매일"/"매주"/"매월"), repeatDayLabel: "일"이 전부 리터럴 한국어예요. 바로 위에서 t("delete")로 잘 번역해둔 것과 대비되네요. en.json을 쓰는 사용자에게는 이 영역이 항상 한국어로 보이게 됩니다.

useTranslations("Common")을 이미 쓰고 있으니 나머지도 같은 방식으로 키를 뽑아 쓰면 좋을 것 같아요.

🌐 제안
-            tagLabel={toolbar.tag ?? "태그"}
+            tagLabel={toolbar.tag ?? t("tag")}
             ...
             repeat={{
-              detailHeading: "상세 설정",
+              detailHeading: t("repeat.detailHeading"),
               options: [
-                { frequency: "DAILY", label: "매일" },
-                { frequency: "WEEKLY", label: "매주" },
-                { frequency: "MONTHLY", label: "매월" },
+                { frequency: "DAILY", label: t("repeat.daily") },
+                { frequency: "WEEKLY", label: t("repeat.weekly") },
+                { frequency: "MONTHLY", label: t("repeat.monthly") },
               ],
               frequency: "DAILY",
               weekly: { weekdays: [], selectedWeekdayIds: [] },
-              monthly: { repeatDayLabel: "일", repeatDay: "1" },
+              monthly: { repeatDayLabel: t("repeat.day"), repeatDay: "1" },
             }}
📝 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.

      <div className="flex items-center justify-end gap-2">
        <div className="pointer-events-none">
          <TodoToolbar
            dateLabel={toolbar.date}
            timeLabel={toolbar.time}
            timeOptions={[]}
            priority={toolbar.priority}
            tagLabel={toolbar.tag ?? t("tag")}
            tags={[]}
            selectedTag={toolbar.tag}
            hasMemo={toolbar.hasMemo}
            isRepeatActive={toolbar.hasRepeat}
            repeat={{
              detailHeading: t("repeat.detailHeading"),
              options: [
                { frequency: "DAILY", label: t("repeat.daily") },
                { frequency: "WEEKLY", label: t("repeat.weekly") },
                { frequency: "MONTHLY", label: t("repeat.monthly") },
              ],
              frequency: "DAILY",
              weekly: { weekdays: [], selectedWeekdayIds: [] },
              monthly: { repeatDayLabel: t("repeat.day"), repeatDay: "1" },
            }}
          />
        </div>
        <button
          type="button"
          onClick={(e) => {
            e.stopPropagation();
            onDelete();
          }}
          aria-label={t("delete")}
        >
          {isDimmed ? <TrashDisableIcon /> : <TrashOnIcon />}
        </button>
🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🤖 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)/today/_components/TodayTodoCard.tsx around
lines 163 - 197, Replace the hardcoded Korean labels passed to TodoToolbar with
translations from the existing useTranslations("Common") instance, including the
tag fallback, repeat detail heading, daily/weekly/monthly option labels, and
repeat-day label. Add or reuse the corresponding translation keys in en.json and
preserve the current Korean strings through localized values.
apps/timo-web/components/todo-modal/CreateTodoIconField.tsx (1)

29-36: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

아이콘 버튼에 aria-expanded 속성 추가를 권장합니다.

이 버튼은 아이콘 패널을 여는 컨트롤 역할을 하지만 aria-expanded가 없습니다. 토글 버튼(라인 38-55)에는 aria-expanded가 있으므로, 일관성과 스크린 리더 접근성을 위해 이 버튼에도 aria-expanded={false}를 추가하는 것이 좋습니다.

♿ 제안 수정: aria-expanded 추가
         <button
           type="button"
           onClick={onOpenPanel}
           aria-label={addIconLabel}
+          aria-expanded={false}
           className="flex size-6 shrink-0 items-center justify-center rounded-[4px]"
         >
📝 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.

        <button
          type="button"
          onClick={onOpenPanel}
          aria-label={addIconLabel}
          aria-expanded={false}
          className="flex size-6 shrink-0 items-center justify-center rounded-[4px]"
        >
          <IconGraphic icon={icon} />
        </button>
🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🤖 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/components/todo-modal/CreateTodoIconField.tsx` around lines 29
- 36, Update the icon panel trigger button in CreateTodoIconField, identified by
its onClick={onOpenPanel} handler, to include aria-expanded={false}. Leave the
existing toggle button and other button attributes unchanged.
apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx (2)

3-7: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

resizeTextarea 함수가 두 파일에 중복 정의되어 있습니다.

동일한 구현이 CreateTodoMemoField.tsxCreateTodoTaskFields.tsx에 각각 복사되어 있습니다. 공통 유틸로 추출하여 DRY 원칙을 지키고 향후 유지보수성을 높이세요.

  • apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx#L3-L7: resizeTextarea 함수를 공통 모듈에서 import하도록 변경.
  • apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx#L6-L10: 동일하게 공통 모듈에서 import하도록 변경.
♻️ 제안: 공통 유틸 추출
// utils/todo/textarea.ts
export const resizeTextarea = (element: HTMLTextAreaElement | null) => {
  if (!element) return;
  element.style.height = "auto";
  element.style.height = `${element.scrollHeight}px`;
};

각 파일에서는:

-import { resizeTextarea } from "`@/utils/todo/textarea`";
+// 기존 로컬 정의 제거
🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

📍 Affects 2 files
  • apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx#L3-L7 (this comment)
  • apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx#L6-L10
🤖 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/components/todo-modal/CreateTodoMemoField.tsx` around lines 3 -
7, Extract the duplicated resizeTextarea implementation into a shared utility,
preserving its null handling and textarea height behavior. In
apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx lines 3-7 and
apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx lines 6-10, remove
the local definitions and import the shared resizeTextarea utility instead.

29-29: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

outline-none 사용으로 포커스 테두리가 제거되어 있습니다.

세 곳의 textarea에 outline-none이 적용되어 있지만, 대체 포커스 스타일이 없어 키보드 사용자가 포커스 위치를 식별하기 어렵습니다. focus: 또는 focus-visible: 스타일을 추가해 접근성을 보장해주세요.

  • apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx#L29-L29: memo textarea className에 포커스 스타일 추가.
  • apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx#L51-L51: title textarea className에 포커스 스타일 추가.
  • apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx#L71-L71: subtask textarea className에 포커스 스타일 추가.

As per path instructions, "outline: none으로 포커스 테두리 제거 금지"해야 합니다.

♿ 제안: 포커스 스타일 추가
-className="... outline-none"
+className="... outline-none focus:outline-2 focus:outline-timo-blue-300"
📝 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.

      className="typo-body-r-12 text-timo-gray-800 max-h-[40vh] w-full resize-none overflow-y-auto wrap-break-word outline-none focus:outline-2 focus:outline-timo-blue-300"
🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

📍 Affects 2 files
  • apps/timo-web/components/todo-modal/CreateTodoMemoField.tsx#L29-L29 (this comment)
  • apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx#L51-L51
  • apps/timo-web/components/todo-modal/CreateTodoTaskFields.tsx#L71-L71
🤖 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/components/todo-modal/CreateTodoMemoField.tsx` at line 29,
Replace the outline-none-only focus treatment on the three textarea classNames:
CreateTodoMemoField.tsx lines 29-29, CreateTodoTaskFields.tsx lines 51-51, and
CreateTodoTaskFields.tsx lines 71-71. Add a visible focus or focus-visible style
to each textarea, preserving the existing styling while ensuring keyboard users
can identify the focused field.

Source: Path instructions

apps/timo-web/components/todo-modal/CreateTodoModalContent.tsx (2)

13-14: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

공유 컴포넌트가 home 도메인 훅을 직접 import하고 있습니다.

CreateTodoModalContent는 공용 components/todo-modal/에 위치하지만, useSubtaskFielduseTitleFieldapp/.../home/_hooks/에서 import합니다. 이는 today 도메인도 CreateTodoModalContent를 사용하므로, todayhome 도메인에 간접 의존하게 됩니다. 나머지 훅(useIconField, useRepeatField, useTimeField, useTagField)은 이미 hooks/todo-modal/에 있으므로, 두 훅도 같은 위치로 이동해 일관성을 맞추는 것을 권장합니다.

As per path instructions, "도메인 간 직접 import 금지 — 공유 로직은 반드시 lib/ 또는 packages/로 추출"해야 합니다.

🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🤖 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/components/todo-modal/CreateTodoModalContent.tsx` around lines
13 - 14, Move useSubtaskField and useTitleField out of the home-specific
_hooks/todo-modal location into the shared hooks/todo-modal directory, then
update CreateTodoModalContent imports and any affected consumers. Preserve both
hooks’ behavior while removing the shared component’s direct dependency on the
home domain.

Source: Path instructions


22-22: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

사용하지 않는 DEFAULT_TAG import를 제거해주세요.

정적 분석에서 DEFAULT_TAG가 정의되었지만 사용되지 않음이 확인되었습니다. import에서 제거하면 됩니다.

🧹 제안 수정
-import { DEFAULT_TAG, useTagField } from "`@/hooks/todo-modal/use-tag-field`";
+import { useTagField } from "`@/hooks/todo-modal/use-tag-field`";
📝 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.

import { useTagField } from "`@/hooks/todo-modal/use-tag-field`";
🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🪛 GitHub Check: Lint

[warning] 22-22:
'DEFAULT_TAG' is defined but never used

🤖 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/components/todo-modal/CreateTodoModalContent.tsx` at line 22,
Remove the unused DEFAULT_TAG import from the import declaration in
CreateTodoModalContent.tsx, while retaining the useTagField import.

Source: Linters/SAST tools

apps/timo-web/messages/ko.json (1)

206-206: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# switchButton 값의 변경 이력 확인
git log --oneline -5 -- apps/timo-web/messages/ko.json
git diff HEAD~1 -- apps/timo-web/messages/ko.json | grep -A 2 -B 2 "switchButton" || echo "No diff found in last commit, checking further..."

Repository: Team-Timo/Timo-client

Length of output: 572


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) 한국어 메시지 주변 맥락
sed -n '190,220p' apps/timo-web/messages/ko.json

# 2) 영문 메시지에서 대응 키 확인
if [ -f apps/timo-web/messages/en.json ]; then
  sed -n '190,220p' apps/timo-web/messages/en.json
fi

# 3) switchButton / completeButton 사용처 확인
rg -n '"switchButton"|"completeButton"|Focus\.stopModal|completeModal' apps/timo-web -g '!**/node_modules/**'

Repository: Team-Timo/Timo-client

Length of output: 2857


Focus.stopModal.switchButton 번역을 의미에 맞게 수정하세요
완료하기Focus.completeModal.completeButton에서도 이미 쓰이고 있어, stopModalswitchButton 의미가 묻힙니다. 전환하기/바꾸기처럼 원문(Switch)과 맞추면 사용자 혼동을 줄일 수 있어요.
관련 문서: Next.js i18n / ICU Message Format 문서

🧰 Tools
🪛 GitHub Actions: ✅ CI / 2_Build.txt

[error] Command failed with exit code 1: /home/runner/setup-pnpm/node_modules/.bin/pnpm run build (next build; Turbopack build failed with 4 errors)


[warning] [@sentry/nextjs] DEPRECATION WARNING: disableLogger is deprecated and will be removed in a future version. Use webpack.treeshake.removeDebugLogging instead. (Not supported with Turbopack.)


[warning] No build cache found. Please configure build caching for faster rebuilds.

🤖 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/ko.json` at line 206, Update the
Focus.stopModal.switchButton Korean translation from “완료하기” to a term matching
the original “Switch,” such as “전환하기” or “바꾸기,” while leaving
Focus.completeModal.completeButton unchanged.
packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx (1)

21-21: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

DropdownAlign 타입 명명 컨벤션 제안

"left" | "right" 유니언 타입에 type alias를 사용하고 있으므로, 컨벤션에 따라 Types 접미사를 붙이는 것을 권장합니다.

🤖 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 `@packages/timo-design-system/src/components/layout/dropdown/Dropdown.tsx` at
line 21, Rename the DropdownAlign type alias to follow the convention by adding
the Types suffix, and update every reference to use the renamed type while
preserving the existing "left" | "right" union.

Source: Path instructions

- CreateTodoModalContent에서 미사용 DEFAULT_TAG import를 제거했습니다
- home CreateTodoModalContainer의 import 순서를 수정했습니다
- today use-create-todo-submit의 CreateTodoTag 의존성을 제거하고 handleSubmit 시그니처를 단일 파라미터로 수정했습니다
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능(기능성) 구현 ⌚ Timo-Design-system Timo 디자인 시스템 ⏰ Timo-web Timo 웹 서비스 ♥️ 혜원 혜원양

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 오늘 페이지 뷰 구현

3 participants