Skip to content

[FEAT] 기존 카드 투두 클릭 모달 구현 - #164

Merged
yumin-kim2 merged 29 commits into
developfrom
feat/web/151-detail-todo-modal
Jul 14, 2026
Merged

[FEAT] 기존 카드 투두 클릭 모달 구현#164
yumin-kim2 merged 29 commits into
developfrom
feat/web/151-detail-todo-modal

Conversation

@yumin-kim2

@yumin-kim2 yumin-kim2 commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

ISSUE 🔗

close #151



What is this PR? 🔍

기존 투두 카드를 클릭했을 때 확인할 수 있는 상세 조회 모달을 구현했습니다.
상세 모달에서는 기존 투두의 제목, 하위 태스크, 아이콘, 날짜/시간, 우선순위, 태그, 반복, 메모 정보를 한 화면에서 확인할 수 있으며, 일부 값은 모달 내부에서 바로 수정할 수 있도록 연결했습니다.

주요 작업

1. 기존 투두 카드 클릭 시 상세 모달 진입

기존 홈 화면의 투두 카드를 클릭하면 상세 투두 모달이 열리도록 연결했습니다.

처음에는 기존 홈 컨테이너 안에 모달 상태와 UI를 바로 추가하는 방식도 가능했지만, 그렇게 하면 홈 화면의 책임이 너무 커질 수 있다고 판단했습니다.
그래서 상세 모달은 별도 컨테이너와 콘텐츠 컴포넌트로 분리해, 홈 화면은 “어떤 투두를 열지”만 관리하고 모달 내부 UI와 상태는 모달 쪽에서 담당하도록 구성했습니다.

2. 상세 투두 모달 레이아웃 구성

화면 설계서를 기준으로 상세 모달의 큰 구조를 먼저 잡았습니다.

  • 날짜 / 요일 영역
  • 아이콘 영역
  • 제목 및 하위 태스크 영역
  • 툴바 영역
  • 메모 영역

각 영역은 이후 수정 기능이 붙을 수 있도록 단순 마크업이 아니라 상태와 이벤트를 받을 수 있는 구조로 만들었습니다.

특히 제목과 하위 태스크는 단순 텍스트가 아니라 입력 가능한 필드로 구성했습니다.
하위 태스크가 여러 개로 늘어나는 케이스도 고려해 리스트 형태로 렌더링하고, 각 항목은 subtaskId를 key로 사용하도록 했습니다.

3. 기존 생성 모달 컴포넌트 재사용

상세 모달에서도 생성 모달과 동일하게 사용하는 영역이 있어, 중복 구현 대신 기존 컴포넌트를 재사용할 수 있도록 정리했습니다.

대표적으로 아이콘 추가 영역은 생성 모달과 상세 모달에서 동일한 UI와 동작을 가지기 때문에 TodoIconField 형태로 공통화했습니다.

이 방식으로 선택한 이유는 다음과 같습니다.

  • 동일한 UI를 두 번 구현하지 않아도 됨
  • 추후 아이콘 선택 UI가 바뀌어도 한 곳만 수정하면 됨
  • 생성 모달과 상세 모달의 사용 경험을 자연스럽게 맞출 수 있음

4. 상세 투두 전용 form 상태 훅 분리

상세 모달 내부 상태가 많아지면서 컴포넌트 안에서 모든 상태를 직접 관리하면 가독성이 떨어질 수 있다고 판단했습니다.

그래서 상세 투두의 로컬 상태는 useDetailTodoForm 훅으로 분리했습니다.

이 훅에서는 다음 값을 관리합니다.

  • 제목
  • 하위 태스크 목록
  • 완료 여부
  • 아이콘
  • 날짜
  • 시간
  • 우선순위
  • 태그
  • 반복 설정
  • 메모

컴포넌트는 UI를 그리는 역할에 집중하고, 상태 변경 로직은 훅에서 관리하도록 나누었습니다.

<DetailTodoTaskFields
  titleValue={detailTodoForm.title}
  subtaskInputs={detailTodoForm.subtaskInputs}
  onTitleChange={detailTodoForm.changeTitle}
  onSubtaskInputChange={detailTodoForm.changeSubtaskInput}
/>

이렇게 구성하면 DetailTodoTaskFields는 화면을 그리는 역할에 집중하고, 실제 상태 변경 방식은 useDetailTodoForm에서 관리할 수 있습니다.

5. 제목 / 하위 태스크 수정 연결

상세 모달에서 투두 제목과 하위 태스크를 수정할 수 있도록 입력 필드로 연결했습니다.

이때 생성 모달에서 이미 사용 중인 입력 유틸을 재사용했습니다.

  • 글자 수 제한
  • 엔터 입력 시 하위 태스크 추가
  • 백스페이스 입력 시 빈 하위 태스크 제거
  • 입력 후 포커스 이동

이미 검증된 로직을 다시 사용하는 방식이므로, 새로 구현하면서 생길 수 있는 동작 차이를 줄일 수 있었습니다.

6. 실행 버튼 동작 연결

상세 모달의 실행 버튼을 클릭하면 현재 투두의 타이머 / 타임박스 실행이 시작되도록 연결했습니다.

동작 규칙에 따라 실행 버튼 클릭 후에는 상세 모달이 닫히도록 처리했습니다.

7. 투두 삭제 동작 연결

툴바 옆 삭제 버튼을 클릭하면 현재 상세 모달에서 보고 있는 투두가 삭제되도록 연결했습니다.

삭제 후에는 모달이 닫히며, 홈 화면의 투두 목록에서도 해당 투두가 제거됩니다.

8. 다국어 지원 적용

상세 모달에 남아 있던 하드코딩 문구를 next-intl 기반 번역 문구로 교체했습니다.

적용한 영역은 다음과 같습니다.

  • 모달 aria label
  • 닫기 버튼 label
  • 요일
  • 아이콘 추가 문구
  • 메모 placeholder
  • 반복 설정 문구
  • 태그 추가 문구
  • 우선순위 드롭다운 문구
  • 삭제 버튼 label

특히 태그 / 반복 / 우선순위 드롭다운은 디자인시스템 컴포넌트 내부에 한국어가 고정되어 있던 부분이 있었습니다.

이 부분은 디자인시스템 컴포넌트에 번역 함수를 직접 넣는 대신, 외부에서 라벨을 props로 주입받도록 확장했습니다.

이 부분은 디자인시스템 컴포넌트에 번역 함수를 직접 넣는 대신, 외부에서 라벨을 props로 주입받도록 확장했습니다.

<PrioritySelector
  trigger={<PriorityIcon priority={priority ?? "Disable"} />}
  selected={priority}
  labels={priorityLabels}
  onSelect={onSelectPriority}
/>

앱 레이어에서는 locale에 맞는 번역 값을 만들어 TodoToolbar에 전달합니다.

priorityLabels={{
  VERY_HIGH: tCommon("priority.urgent"),
  HIGH: tCommon("priority.high"),
  MEDIUM: tCommon("priority.medium"),
  LOW: tCommon("priority.low"),
}}

이 방식을 선택한 이유는 다음과 같습니다.

  • 디자인시스템이 특정 i18n 라이브러리에 의존하지 않음
  • 기존 사용처는 기본 한국어 라벨을 그대로 사용할 수 있음
  • 필요한 화면에서만 번역된 라벨을 주입할 수 있음
  • 공통 컴포넌트의 재사용성을 유지할 수 있음

고민한 지점

모달 내부를 어디까지 분리할 것인가

처음에는 기존 홈 화면 파일에 모달 코드를 바로 추가할 수도 있었습니다.
하지만 상세 모달은 단순 조회 UI가 아니라 제목 수정, 하위 태스크 수정, 툴바 상태 변경, 삭제, 실행, 메모 입력까지 포함하게 되므로 하나의 파일에 모두 넣으면 책임이 너무 커질 수 있다고 판단했습니다.

그래서 다음 기준으로 분리했습니다.

  • 홈 컨테이너: 어떤 투두를 열고 닫을지 관리
  • 상세 모달 컨테이너: 상세 모달에 필요한 이벤트 연결
  • 상세 모달 콘텐츠: 실제 UI 렌더링
  • 상세 form 훅: 모달 내부 상태 관리
  • 하위 UI 컴포넌트: 제목/하위 태스크, 메모, 아이콘 등 영역별 UI

px 고정 대신 내용에 따라 자연스럽게 늘어나는 구조

하위 태스크나 메모는 개수가 늘어나거나 입력 내용이 길어질 수 있습니다.
그래서 모달 크기를 무조건 고정하기보다는, 내부 콘텐츠가 늘어날 수 있는 구조로 잡았습니다.

다만 화면 설계서에서 명확한 간격이 있는 부분은 mt, gap, py 등을 사용해 의도된 spacing을 유지했습니다.

공통 컴포넌트 수정 범위

디자인시스템 컴포넌트를 수정할 때 기존 사용처에 영향을 줄 수 있기 때문에, 기존 동작을 바꾸는 방식은 피했습니다.

대신 optional prop을 추가하고 기본값은 기존 한국어 문구로 유지했습니다.

예를 들어:

  • PrioritySelectorlabels prop을 받을 수 있음
  • TagSelectoraddLabel prop을 받을 수 있음
  • RepeatSelectorfrequencyHeading prop을 받을 수 있음
  • TodoToolbar는 해당 라벨들을 하위 컴포넌트로 전달함

이렇게 하면 기존 화면은 그대로 동작하고, 상세/생성 모달처럼 다국어가 필요한 곳에서만 번역 문구를 넘길 수 있습니다.



To Reviewers

리뷰 반영으로 바뀐 점: 폴더 구조

생성 TODO 모달과 상세 TODO 모달에서 함께 사용하는 로직/컴포넌트가 home 내부에만 묶여 있으면, 다른 화면에서 재사용하기 어려울 수 있다고 판단했습니다.

그래서 TODO 모달 관련 파일을 홈 전용 경로에서 공용 경로로 이동했습니다.

  • components/todo-modal/create

    • TODO 생성 모달 전용 컴포넌트
    • CreateTodoModalContent
    • CreateTodoModalContainer
    • CreateTodoTaskFields
    • CreateTodoMemoField
  • components/todo-modal/detail

    • TODO 상세 모달 전용 컴포넌트
    • DetailTodoModalContent
    • DetailTodoModalContainer
    • DetailTodoTaskFields
    • DetailTodoMemoField
  • components/todo-modal/common

    • 생성/상세 모달에서 함께 사용하는 공용 컴포넌트
    • TodoIconField
  • hooks/todo-modal

    • TODO 모달에서 사용하는 입력/상태 관리 훅
    • 생성 모달 훅과 상세 모달 훅을 함께 배치
  • utils/todo

    • TODO 도메인에서 공통으로 사용하는 유틸
    • 텍스트 길이 제한, 시간 변환, 태그 라벨 판별 등

이렇게 나눈 이유는 “홈 화면에서만 쓰는 코드”와 “TODO 모달 도메인에서 재사용 가능한 코드”를 분리하기 위해서입니다.

특히 TodoIconField는 생성 모달과 상세 모달에서 동일하게 사용되므로 common 폴더로 분리했고, 생성/상세 각각에만 필요한 컴포넌트는 create, detail 폴더에 따로 두었습니다.



Screenshot 📷

2026-07-13.4.15.41.mov
2026-07-13.4.17.03.mov



Test Checklist ✔

  • 기존 투두 카드 클릭 시 상세 모달이 열리도록 연결
  • 상세 투두 모달 기본 레이아웃 구성
  • 제목 수정 입력 연결
  • 하위 태스크 수정 입력 연결
  • 실행 버튼 클릭 시 모달 닫힘 및 실행 동작 연결
  • 투두 삭제 버튼 추가 및 삭제 동작 연결
  • 메모 입력 최대 300자 제한 적용
  • 메모 입력 시 내부 스크롤 대신 영역이 자연스럽게 늘어나도록 처리
  • 다국어 처리
  • API 연결

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

vercel Bot commented Jul 12, 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 14, 2026 6:23am

Request Review

@github-actions github-actions Bot added ⏰ Timo-web Timo 웹 서비스 ⌚ Timo-Design-system Timo 디자인 시스템 ✨ Feature 새로운 기능(기능성) 구현 ♣️ 유민 유민양 labels Jul 12, 2026
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e4a7ed1e-367a-4683-949e-08bb92868f0b

📥 Commits

Reviewing files that changed from the base of the PR and between 14343d0 and 9470836.

📒 Files selected for processing (9)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayDateHeaderContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/components/todo-modal/create/CreateTodoModalContainer.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoTaskFields.tsx
  • apps/timo-web/hooks/.gitkeep
  • apps/timo-web/hooks/todo-modal/use-detail-subtask-field.ts
  • apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts
  • apps/timo-web/utils/date/date.ts

Walkthrough

투두 생성·상세 모달용 입력 컴포넌트와 폼 훅을 추가하고, 공통 선택 컴포넌트에 번역 라벨 주입을 지원합니다. 홈 투두 카드의 클릭·키보드 이벤트를 상세 모달 및 날짜별 삭제 상태 갱신과 연결했습니다.

Changes

공통 입력 계약과 생성 모달

Layer / File(s) Summary
공통 선택 컴포넌트와 생성 입력 계약
packages/timo-design-system/src/components/{priority,repeat,tag}/..., packages/timo-design-system/src/components/todo/todo-toolbar/TodoToolbar.tsx, apps/timo-web/components/todo-modal/common/*, apps/timo-web/components/todo-modal/create/*
우선순위·반복·태그 라벨을 외부에서 주입할 수 있도록 확장하고, 생성 모달의 제목·서브태스크·메모 입력과 제출 payload 변환을 추가했습니다.
공용 유틸리티와 번역 연결
apps/timo-web/utils/todo/*, apps/timo-web/messages/*.json, apps/timo-web/.../CreateTodoModalContainer.tsx
태그 키 검증과 가중 길이 제한 유틸리티, 모달 번역 키를 추가하고 관련 import 경로를 정리했습니다.

상세 폼 상태 훅

Layer / File(s) Summary
상세 투두 폼과 서브태스크 상태
apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts, apps/timo-web/hooks/todo-modal/use-subtask-field.ts, apps/timo-web/hooks/todo-modal/use-detail-subtask-field.ts
투두 데이터로 폼을 초기화하고 아이콘·반복·시간·요일·완료 상태와 서브태스크 입력, 키보드 기반 추가·삭제·포커스를 관리합니다.

상세 모달 구성과 입력 UI

Layer / File(s) Summary
상세 모달 컨테이너와 콘텐츠
apps/timo-web/components/todo-modal/detail/*
오버레이 기반 상세 모달을 추가하고 작업·서브태스크·메모·툴바·삭제 UI를 폼 훅과 연결했습니다.

홈 카드와 삭제 상태 연결

Layer / File(s) Summary
카드 이벤트와 모달·삭제 연동
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/*, apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/*, apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/*
카드의 클릭·Enter·Space 동작을 상세 모달 열기로 연결하고, 삭제 시 날짜별 상태에서 해당 투두를 제거하도록 확장했습니다.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant HomeTodoCard
  participant HomeTodoContainer
  participant DetailTodoModalContainer
  participant DetailTodoModalContent
  participant useHomeTodosByDate
  User->>HomeTodoCard: 카드 클릭 또는 키보드 입력
  HomeTodoCard->>HomeTodoContainer: onClickTodo 호출
  HomeTodoContainer->>DetailTodoModalContainer: 상세 모달 열기
  DetailTodoModalContainer->>DetailTodoModalContent: todo와 삭제 콜백 전달
  DetailTodoModalContent->>HomeTodoContainer: onDelete 호출
  HomeTodoContainer->>useHomeTodosByDate: handleDeleteTodo 호출
Loading

Possibly related PRs

Suggested labels: ♦️ 민아

Suggested reviewers: ehye1, jjangminii

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning 상세 모달 열기와 편집 UI는 구현됐지만, 수정 내용을 저장·반영하는 업데이트 흐름은 보이지 않습니다. 투두 상세 수정값을 저장하는 업데이트 mutation과 제출 경로를 추가하고, 성공 시 홈 목록에 반영되도록 연결하세요.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 기존 투두 카드를 클릭해 상세 모달을 여는 변경을 핵심적으로 잘 요약합니다.
Description check ✅ Passed 상세 모달 구현과 수정 연결을 설명해 실제 변경 사항과 잘 맞습니다.
Out of Scope Changes check ✅ Passed 공용 컴포넌트·유틸 이동과 라벨 props 확장은 상세 모달 구현을 지원하는 범위로 보이며, 뚜렷한 무관 변경은 없습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/web/151-detail-todo-modal

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

❤️ Share

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

@github-actions

github-actions Bot commented Jul 12, 2026

Copy link
Copy Markdown

Storybook Preview

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

마지막 업데이트: 2026-07-14 06:22 UTC

@github-actions

github-actions Bot commented Jul 12, 2026

Copy link
Copy Markdown

Timo Performance Report

Bundle Size — timo-web
라우트 크기 First Load JS
/[locale]/home 206.08 kB 🔴 411.96 kB
/[locale]/today 188.31 kB 🔴 394.19 kB
/[locale]/focus 159.42 kB 🔴 365.30 kB
/[locale]/settings/account 0 B 🟡 205.88 kB
/[locale]/settings 168.51 kB 🔴 374.38 kB
/[locale]/statistics 155.99 kB 🔴 361.87 kB
/[locale]/[...rest] 0 B 🟡 205.88 kB
/[locale]/login 215.53 kB 🔴 421.41 kB
/[locale]/oauth/callback 122.17 kB 🟡 328.05 kB
/[locale]/onboarding 235.01 kB 🔴 440.89 kB
/[locale] 121.55 kB 🟡 327.43 kB
/[locale]/policy 127.66 kB 🟡 333.54 kB

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

Lighthouse — timo-web
URL Perf A11y LCP CLS TBT
/en/home 🔴 69 🟢 95 🔴 15.5s 🟢 0.000 🟡 291ms
/en/today 🟡 70 🟢 95 🔴 15.3s 🟢 0.000 🟡 246ms
/en/focus 🔴 62 🟢 95 🔴 15.0s 🟢 0.000 🟡 498ms
/en/statistics 🟡 70 🟢 95 🔴 15.0s 🟢 0.000 🟡 234ms

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 변환 권장

측정 커밋: ddddd40

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

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

⚠️ Outside diff range comments (2)
packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx (1)

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

CreateTodoModalContent.tsxfrequencyHeading을 전달하세요
RepeatSelector의 기본값이 "반복 일정"이라 이 호출부는 로케일과 무관하게 한글이 노출됩니다. 다른 호출부처럼 t("repeatFrequencyHeading")을 넘기면 됩니다.
TypeScript optional properties: https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#optional-properties

🤖 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/repeat/repeat-selector/RepeatSelector.tsx`
around lines 42 - 50, CreateTodoModalContent의 RepeatSelector 호출부에 로컬라이즈된
frequencyHeading을 전달하세요. 기존 번역 함수 t를 사용해 repeatFrequencyHeading 키의 값을 넘기고,
RepeatSelector의 한글 기본값에 의존하지 않도록 수정하세요.
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContent.tsx (1)

190-214: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

frequencyHeading을 함께 넘겨주세요
RepeatSelector 기본값이 "반복 일정"이라, 이 prop이 빠지면 영어 로케일에서도 한글이 그대로 보여요. 이미 있는 Home.detailModal.repeatFrequencyHeading을 재사용하면 됩니다. next-intl 네임스페이스 참고: https://next-intl.dev/docs/usage/translations

🤖 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/CreateTodoModalContent.tsx
around lines 190 - 214, Update the repeat configuration passed from
CreateTodoModalContent to include frequencyHeading, using the existing
Home.detailModal.repeatFrequencyHeading translation so RepeatSelector does not
fall back to its Korean default in English locales.
🤖 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/_components/todo-card/HomeTodoCard.tsx:
- Around line 87-101: Update handleCardKeyDown to ignore keyboard events
originating from nested controls, matching handleCardClick’s
target.closest("button, input, label") filtering. Perform this check before
preventDefault and onClickTodo so focused checkboxes and playback controls
retain their native Enter/Space behavior while the card still responds to
keyboard activation elsewhere.
- Around line 134-148: Remove the card-level button semantics and keyboard/click
handling from the sortable container in HomeTodoCard, including role, tabIndex,
onClick, and onKeyDown tied to onClickTodo. Keep the card as a drag/display
container and add or use a separate accessible detail button for onClickTodo,
ensuring Checkbox and PlayButton remain independent controls with distinct roles
and tab order.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/home/_components/todo-modal/DetailTodoMemoField.tsx:
- Line 33: DetailTodoMemoField의 textarea에서 outline-none을 제거하고, 키보드 사용자가 포커스 상태를
확인할 수 있도록 프로젝트의 기존 포커스 스타일을 적용하세요.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/home/_components/todo-modal/DetailTodoTaskFields.tsx:
- Line 63: Update the textarea class definitions in DetailTodoTaskFields,
including both affected fields, so removing the default outline is accompanied
by a clear keyboard-only focus indicator such as a focus-visible ring or
equivalent accessible focus style; preserve the existing layout and typography
classes.
- Around line 93-103: Update the subtask textarea ref in DetailTodoTaskFields so
it continues registering the element through registerSubtaskInputRef(index) and
also calls resizeTextarea on mount. Preserve the existing change and keydown
handlers while ensuring existing multi-line subtask content is sized correctly
on initial render.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/DetailTodoModalContent.tsx:
- Around line 66-72: Update the date header in DetailTodoModalContent to derive
the displayed day and weekday from detailTodoForm.date instead of the hardcoded
“22” and Monday label. Reuse the existing weekday mapping, such as
DETAIL_TODO_WEEKDAY_IDS, so the selected todo’s actual date and weekday are
shown.

In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-detail-todo-form.ts:
- Around line 82-83: Initialize selectedTime in useDetailTodoForm to the
DETAIL_TODO_TIME_OPTIONS entry matching todo.durationSeconds converted to
minutes, instead of leaving it undefined. Keep the existing durationText
initialization and ensure the matching option is highlighted when the modal
opens, while preserving undefined only when no option matches.
- Around line 57-65: Replace the hardcoded labels in DETAIL_TODO_WEEKDAYS with
an exported ID-only collection, such as DETAIL_TODO_WEEKDAY_IDS. Update the
consuming weekday mapping in DetailTodoModalContent to call tCommon with each
ID’s Common namespace key (weekday.${id}), preserving the existing weekday order
and IDs.
- Around line 81-117: 연결된 실제 데이터로 상세 폼 초기값을 구성하세요. useDetailTodoForm의 date 상태는
하드코딩된 날짜 대신 상위 컨테이너의 선택 날짜를 인자로 받아 초기화하고, memo는 Todo API 계약의 메모 값으로 설정하세요. 반복 관련
상태인 isRepeatActive, repeatFrequency, selectedWeekdayIds, repeatDay도 todo의 API
필드에서 초기화하도록 Todo 입력 타입과 호출부를 함께 갱신하세요.

---

Outside diff comments:
In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContent.tsx:
- Around line 190-214: Update the repeat configuration passed from
CreateTodoModalContent to include frequencyHeading, using the existing
Home.detailModal.repeatFrequencyHeading translation so RepeatSelector does not
fall back to its Korean default in English locales.

In
`@packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx`:
- Around line 42-50: CreateTodoModalContent의 RepeatSelector 호출부에 로컬라이즈된
frequencyHeading을 전달하세요. 기존 번역 함수 t를 사용해 repeatFrequencyHeading 키의 값을 넘기고,
RepeatSelector의 한글 기본값에 의존하지 않도록 수정하세요.
🪄 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: 23235f6c-d483-4cdd-8ddb-1dd70fcd12e0

📥 Commits

Reviewing files that changed from the base of the PR and between 8b39b01 and b128a11.

📒 Files selected for processing (16)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-card/HomeTodoCard.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-modal/DetailTodoMemoField.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-modal/DetailTodoTaskFields.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-modal/TodoIconField.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/CreateTodoModalContent.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/DetailTodoModalContainer.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/DetailTodoModalContent.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-detail-todo-form.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts
  • apps/timo-web/messages/en.json
  • apps/timo-web/messages/ko.json
  • packages/timo-design-system/src/components/priority/priority-selector/PrioritySelector.tsx
  • packages/timo-design-system/src/components/repeat/repeat-selector/RepeatSelector.tsx
  • packages/timo-design-system/src/components/tag/tag-selector/TagSelector.tsx
  • packages/timo-design-system/src/components/todo/todo-toolbar/TodoToolbar.tsx

Comment on lines +134 to 148
<div
ref={setNodeRef}
style={sortableStyle}
{...attributes}
{...listeners}
role={onClickTodo ? "button" : attributes.role}
tabIndex={onClickTodo ? 0 : attributes.tabIndex}
onClick={onClickTodo ? handleCardClick : undefined}
onKeyDown={onClickTodo ? handleCardKeyDown : undefined}
className={cn(
"border-timo-gray-500 flex w-full shrink-0 flex-col items-start gap-2 overflow-hidden rounded-[4px] border border-solid px-3.5 py-3",
isCompleted ? "bg-timo-gray-200" : "bg-white",
onClickTodo && "cursor-pointer",
)}
>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the target file and nearby related components
git ls-files 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-card/*' \
  'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/**/*todo-card*' \
  'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/**/*TodoCard*'

# Show the target file with line numbers if present
target='apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-card/HomeTodoCard.tsx'
if [ -f "$target" ]; then
  sed -n '1,260p' "$target" | cat -n
fi

# Find keyboard / click handling and interactive descendants in the home todo card area
rg -n --context 3 'role="button"|tabIndex=0|onKeyDown|onClickTodo|checkbox|button|play|aria-label' \
  'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home' \
  'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components' \
  'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers' \
  'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks' \
  || true

Repository: Team-Timo/Timo-client

Length of output: 50377


카드 전체를 버튼으로 만들기보다 내부 컨트롤과 분리해 주세요

role="button"/tabIndex=0를 카드 전체에 주면 CheckboxPlayButton이 같은 인터랙션 트리 안에 들어가 탭 순서와 역할이 겹칩니다. WAI-ARIA APG도 이런 중첩 패턴은 피하라고 권장합니다: https://www.w3.org/WAI/ARIA/apg/

상세 진입이 필요하면 카드 자체는 드래그/표시용으로 두고, 별도의 상세보기 버튼으로 분리하는 쪽이 안전합니다. 버튼이 많은 카드일수록 역할 구분이 또렷해야 합니다. 😄

🤖 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/_components/todo-card/HomeTodoCard.tsx
around lines 134 - 148, Remove the card-level button semantics and
keyboard/click handling from the sortable container in HomeTodoCard, including
role, tabIndex, onClick, and onKeyDown tied to onClickTodo. Keep the card as a
drag/display container and add or use a separate accessible detail button for
onClickTodo, ensuring Checkbox and PlayButton remain independent controls with
distinct roles and tab order.

placeholder={placeholder}
maxLength={maxLength}
onChange={(event) => onChange(event.target.value)}
className="typo-body-r-14 text-timo-black placeholder:text-timo-gray-700 min-h-20 w-full resize-none overflow-hidden p-1 outline-none"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

outline-none으로 포커스 테두리가 제거되었습니다.

메모 textarea도 대체 포커스 스타일 없이 outline-none만 적용되어 있어 키보드 사용자가 포커스 위치를 알기 어렵습니다.

♿ 제안 diff
-      className="typo-body-r-14 text-timo-black placeholder:text-timo-gray-700 min-h-20 w-full resize-none overflow-hidden p-1 outline-none"
+      className="typo-body-r-14 text-timo-black placeholder:text-timo-gray-700 min-h-20 w-full resize-none overflow-hidden p-1 outline-none focus-visible:ring-1 focus-visible:ring-timo-gray-500"
As per path instructions, `outline: 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.

Suggested change
className="typo-body-r-14 text-timo-black placeholder:text-timo-gray-700 min-h-20 w-full resize-none overflow-hidden p-1 outline-none"
className="typo-body-r-14 text-timo-black placeholder:text-timo-gray-700 min-h-20 w-full resize-none overflow-hidden p-1 outline-none focus-visible:ring-1 focus-visible:ring-timo-gray-500"
🤖 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/_components/todo-modal/DetailTodoMemoField.tsx
at line 33, DetailTodoMemoField의 textarea에서 outline-none을 제거하고, 키보드 사용자가 포커스 상태를
확인할 수 있도록 프로젝트의 기존 포커스 스타일을 적용하세요.

Source: Path instructions

resizeTextarea(event.currentTarget);
}}
rows={1}
className="typo-headline-b-14 text-timo-black min-w-0 flex-1 resize-none overflow-hidden wrap-break-word outline-none"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

outline-none으로 포커스 테두리가 제거되어 키보드 접근성이 저하됩니다.

두 textarea 모두 outline-none만 적용되어 있고 대체 포커스 스타일이 없어서, 키보드로 탐색하는 사용자는 현재 포커스가 어디에 있는지 확인할 수 없습니다. outline-none을 쓸 경우엔 focus-visible:ring 등 대체 표시를 꼭 넣어주는 게 좋아요. 관련해서 Tailwind 공식 문서에서도 "본인만의 포커스 스타일을 적용하는 것을 강력히 권장"하고 있습니다.

♿ 제안 diff
-            className="typo-headline-b-14 text-timo-black min-w-0 flex-1 resize-none overflow-hidden wrap-break-word outline-none"
+            className="typo-headline-b-14 text-timo-black min-w-0 flex-1 resize-none overflow-hidden wrap-break-word outline-none focus-visible:ring-1 focus-visible:ring-timo-gray-500"
As per path instructions, `outline: none으로 포커스 테두리 제거 금지`.

Also applies to: 102-102

🤖 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/_components/todo-modal/DetailTodoTaskFields.tsx
at line 63, Update the textarea class definitions in DetailTodoTaskFields,
including both affected fields, so removing the default outline is accompanied
by a clear keyboard-only focus indicator such as a focus-visible ring or
equivalent accessible focus style; preserve the existing layout and typography
classes.

Source: Path instructions

Comment on lines +93 to +103
<textarea
value={subtask.value}
ref={registerSubtaskInputRef(index)}
onChange={(event) => {
onSubtaskInputChange(subtask.id, event.target.value);
resizeTextarea(event.currentTarget);
}}
onKeyDown={(event) => onSubtaskInputKeyDown(index, event)}
rows={1}
className="typo-body-r-12 text-timo-gray-700 min-w-0 flex-1 resize-none overflow-hidden wrap-break-word outline-none"
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

서브태스크 textarea는 최초 렌더링 시 높이가 자동으로 조절되지 않습니다.

제목 textarea(Line 57)는 ref={resizeTextarea}라서 마운트 시 한 번 리사이즈되지만, 서브태스크는 registerSubtaskInputRef(index)만 연결되어 있어 resizeTextarea가 호출되지 않습니다. 기존 투두를 열었을 때 서브태스크 내용이 길어서 여러 줄이면, 사용자가 타이핑하기 전까지 1행 높이로 잘려 보이는 문제가 생겨요.

ref 콜백에서 resizeTextarea도 함께 호출해주면 해결됩니다.

📏 제안 diff
               <textarea
                 value={subtask.value}
-                ref={registerSubtaskInputRef(index)}
+                ref={(element) => {
+                  registerSubtaskInputRef(index)(element);
+                  resizeTextarea(element);
+                }}
                 onChange={(event) => {
📝 Committable suggestion

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

Suggested change
<textarea
value={subtask.value}
ref={registerSubtaskInputRef(index)}
onChange={(event) => {
onSubtaskInputChange(subtask.id, event.target.value);
resizeTextarea(event.currentTarget);
}}
onKeyDown={(event) => onSubtaskInputKeyDown(index, event)}
rows={1}
className="typo-body-r-12 text-timo-gray-700 min-w-0 flex-1 resize-none overflow-hidden wrap-break-word outline-none"
/>
<textarea
value={subtask.value}
ref={(element) => {
registerSubtaskInputRef(index)(element);
resizeTextarea(element);
}}
onChange={(event) => {
onSubtaskInputChange(subtask.id, event.target.value);
resizeTextarea(event.currentTarget);
}}
onKeyDown={(event) => onSubtaskInputKeyDown(index, event)}
rows={1}
className="typo-body-r-12 text-timo-gray-700 min-w-0 flex-1 resize-none overflow-hidden wrap-break-word outline-none"
/>
🤖 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/_components/todo-modal/DetailTodoTaskFields.tsx
around lines 93 - 103, Update the subtask textarea ref in DetailTodoTaskFields
so it continues registering the element through registerSubtaskInputRef(index)
and also calls resizeTextarea on mount. Preserve the existing change and keydown
handlers while ensuring existing multi-line subtask content is sized correctly
on initial render.

Comment on lines +66 to +72
<div className="flex w-full flex-col gap-2">
<div className="flex flex-col">
<p className="typo-headline-b-30 text-timo-black">22</p>
<p className="typo-body-m-12 text-timo-gray-700">
{tCommon("weekday.MON")}
</p>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

날짜 표시가 실제 todo 정보와 무관하게 항상 "22일 월요일"로 고정되어 있습니다.

detailTodoForm.date가 훅에 이미 존재하는데도, 상단 날짜 영역은 22tCommon("weekday.MON")을 그대로 하드코딩해서 보여주고 있어요. 어떤 투두를 열어도 같은 날짜/요일이 뜨는 셈이라, "선택한 투두의 제목, 날짜, 시간 등 기존 정보 표시"라는 PR 목표와 바로 충돌하는 부분입니다.

detailTodoForm.date에서 일(day)과 요일을 계산해 넣어주면 좋겠습니다(요일 매핑은 아래 use-detail-todo-form.ts 리뷰에서 제안한 DETAIL_TODO_WEEKDAY_IDS와 연계 가능).

📅 제안 diff
+        const weekdayIds = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
...
         <div className="flex flex-col">
-          <p className="typo-headline-b-30 text-timo-black">22</p>
+          <p className="typo-headline-b-30 text-timo-black">
+            {detailTodoForm.date.getDate()}
+          </p>
           <p className="typo-body-m-12 text-timo-gray-700">
-            {tCommon("weekday.MON")}
+            {tCommon(`weekday.${weekdayIds[detailTodoForm.date.getDay()]}`)}
           </p>
         </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.

Suggested change
<div className="flex w-full flex-col gap-2">
<div className="flex flex-col">
<p className="typo-headline-b-30 text-timo-black">22</p>
<p className="typo-body-m-12 text-timo-gray-700">
{tCommon("weekday.MON")}
</p>
</div>
const weekdayIds = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
...
<div className="flex w-full flex-col gap-2">
<div className="flex flex-col">
<p className="typo-headline-b-30 text-timo-black">
{detailTodoForm.date.getDate()}
</p>
<p className="typo-body-m-12 text-timo-gray-700">
{tCommon(`weekday.${weekdayIds[detailTodoForm.date.getDay()]}`)}
</p>
</div>
🤖 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/DetailTodoModalContent.tsx
around lines 66 - 72, Update the date header in DetailTodoModalContent to derive
the displayed day and weekday from detailTodoForm.date instead of the hardcoded
“22” and Monday label. Reuse the existing weekday mapping, such as
DETAIL_TODO_WEEKDAY_IDS, so the selected todo’s actual date and weekday are
shown.

Comment on lines +57 to +65
export const DETAIL_TODO_WEEKDAYS = [
{ id: "MON", label: "월" },
{ id: "TUE", label: "화" },
{ id: "WED", label: "수" },
{ id: "THU", label: "목" },
{ id: "FRI", label: "금" },
{ id: "SAT", label: "토" },
{ id: "SUN", label: "일" },
];

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

요일 라벨이 한글로 하드코딩되어 있어 다국어 지원이 깨집니다.

DETAIL_TODO_WEEKDAYSlabel이 "월", "화"... 로 고정되어 있어, 영어 로케일에서도 한글 요일이 노출됩니다. 반면 같은 파일 세트의 DetailTodoModalContent.tsx에서는 이미 tCommon("weekday.MON")으로 Common 네임스페이스의 다국어 키를 쓰고 있으니(apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/DetailTodoModalContent.tsx Line 70), 동일한 키를 재사용하는 편이 일관성 있습니다.

id 배열만 export하고, 라벨은 소비하는 쪽에서 tCommon으로 매핑하는 방식을 제안합니다.

🌐 제안 diff
-export const DETAIL_TODO_WEEKDAYS = [
-  { id: "MON", label: "월" },
-  { id: "TUE", label: "화" },
-  { id: "WED", label: "수" },
-  { id: "THU", label: "목" },
-  { id: "FRI", label: "금" },
-  { id: "SAT", label: "토" },
-  { id: "SUN", label: "일" },
-];
+export const DETAIL_TODO_WEEKDAY_IDS = [
+  "MON",
+  "TUE",
+  "WED",
+  "THU",
+  "FRI",
+  "SAT",
+  "SUN",
+] as const;

소비 측(DetailTodoModalContent.tsx)에서는:

weekly: {
  weekdays: DETAIL_TODO_WEEKDAY_IDS.map((id) => ({
    id,
    label: tCommon(`weekday.${id}`),
  })),
  ...
}
📝 Committable suggestion

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

Suggested change
export const DETAIL_TODO_WEEKDAYS = [
{ id: "MON", label: "월" },
{ id: "TUE", label: "화" },
{ id: "WED", label: "수" },
{ id: "THU", label: "목" },
{ id: "FRI", label: "금" },
{ id: "SAT", label: "토" },
{ id: "SUN", label: "일" },
];
export const DETAIL_TODO_WEEKDAY_IDS = [
"MON",
"TUE",
"WED",
"THU",
"FRI",
"SAT",
"SUN",
] as const;
🤖 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-detail-todo-form.ts
around lines 57 - 65, Replace the hardcoded labels in DETAIL_TODO_WEEKDAYS with
an exported ID-only collection, such as DETAIL_TODO_WEEKDAY_IDS. Update the
consuming weekday mapping in DetailTodoModalContent to call tCommon with each
ID’s Common namespace key (weekday.${id}), preserving the existing weekday order
and IDs.

Comment on lines +81 to +117
const [date, setDate] = useState(new Date(2026, 6, 1));
const [time, setTime] = useState(durationText);
const [selectedTime, setSelectedTime] = useState<TimeSelection>();
const [priority, setPriority] = useState<PriorityLevel>(todo.priority);
const [selectedTag, setSelectedTag] = useState(tagLabel);
const [isRepeatActive, setIsRepeatActive] = useState(todo.isRepeated);
const [repeatFrequency, setRepeatFrequency] =
useState<RepeatFrequency>("DAILY");
const [selectedWeekdayIds, setSelectedWeekdayIds] = useState<string[]>([]);
const [repeatDay, setRepeatDay] = useState("");
const [isCompleted, setIsCompleted] = useState(todo.completed);
const [title, setTitle] = useState(todo.title);
const nextSubtaskInputId = useRef(0);
const createSubtaskInput = (value = ""): DetailTodoSubtaskInput => ({
id: nextSubtaskInputId.current++,
subtaskId: null,
completed: false,
value,
});
const [subtaskInputs, setSubtaskInputs] = useState<DetailTodoSubtaskInput[]>(
() =>
todo.subtasks.length > 0
? todo.subtasks.map((subtask) => ({
id: nextSubtaskInputId.current++,
subtaskId: subtask.subtaskId,
completed: subtask.completed,
value: subtask.content,
}))
: [createSubtaskInput()],
);
const subtaskInputRefs = useRef<Array<HTMLTextAreaElement | null>>([]);
const pendingSubtaskFocusIndex = useRef<number | null>(null);
const [memo, setMemo] = useState("");
const [icon, setIcon] = useState<TodoIconValue | null>(
isTodoIconValue(todo.icon) ? todo.icon : null,
);
const [isIconPanelOpen, setIsIconPanelOpen] = useState(false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Todo 타입 정의를 찾아 date/memo 관련 필드 존재 여부 확인
fd todo-type.ts --exec cat {}

Repository: Team-Timo/Timo-client

Length of output: 1241


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the hook and related modal usage around the referenced lines
printf '\n## use-detail-todo-form.ts\n'
sed -n '1,260p' apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-detail-todo-form.ts

printf '\n## DetailTodoModalContent.tsx\n'
sed -n '1,220p' apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-modal/DetailTodoModalContent.tsx

printf '\n## Search for hasMemo / memo usage\n'
rg -n "hasMemo|memo" apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home -g '!**/*.map'

Repository: Team-Timo/Timo-client

Length of output: 240


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the hook and related modal usage around the referenced lines
printf '\n## use-detail-todo-form.ts\n'
sed -n '1,260p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-detail-todo-form.ts'

printf '\n## DetailTodoModalContent.tsx\n'
sed -n '1,220p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-modal/DetailTodoModalContent.tsx'

printf '\n## Search for hasMemo / memo usage\n'
rg -n "hasMemo|memo" 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home' -g '!**/*.map'

Repository: Team-Timo/Timo-client

Length of output: 8148


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the modal component and any memo/date-related wiring
fd 'DetailTodoModalContent.tsx' apps/timo-web/app
printf '\n## search results\n'
rg -n "useDetailTodoForm|setMemo|memo|hasMemo|selectedTime|repeatFrequency|selectedWeekdayIds|repeatDay|new Date\\(2026, 6, 1\\)" 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home' -g '!**/*.map'

Repository: Team-Timo/Timo-client

Length of output: 7817


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the detail modal container and the related create-time hook for initialization patterns
printf '\n## DetailTodoModalContent.tsx\n'
sed -n '1,240p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/todo-modal/DetailTodoModalContent.tsx'

printf '\n## use-time-field.ts\n'
sed -n '1,220p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-time-field.ts'

printf '\n## use-repeat-field.ts\n'
sed -n '1,220p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-repeat-field.ts'

printf '\n## use-detail-todo-form.ts setDate usage\n'
rg -n "setDate|date," 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/todo-modal/use-detail-todo-form.ts'

Repository: Team-Timo/Timo-client

Length of output: 10102


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether the detail modal persists memo/date/repeat data anywhere else
rg -n "useDetailTodoForm|DetailTodoMemoField|onChange={detailTodoForm.setMemo}|selectedTime|repeatDayOfMonth|repeatWeekdays|repeatType|hasMemo" 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home' -g '!**/*.map'

printf '\n## todo schema\n'
sed -n '1,220p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type.ts'

Repository: Team-Timo/Timo-client

Length of output: 425


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for any repeat/date fields on the todo model or in detail modal save paths
printf '\n## todo / detail modal search\n'
rg -n "createdAt|date|memo|repeatType|repeatWeekdays|repeatDayOfMonth|durationSeconds|hasMemo" 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home' -g '!**/*.map'

printf '\n## todo type\n'
sed -n '1,220p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type.ts'

Repository: Team-Timo/Timo-client

Length of output: 23691


상세 모달 초기값을 실제 데이터 소스와 연결해 주세요.
Todo 타입에는 date/memo가 없어서 useDetailTodoFormnew Date(2026, 6, 1)"" 같은 placeholder로 시작합니다. 지금 상태로는 날짜가 항상 고정값이고, 메모/반복 값도 기존 정보를 보여주지 못합니다. 날짜는 상위 컨테이너의 선택 날짜를 넘기고, 메모와 반복 상태는 API 계약에 포함된 값으로 초기화해야 합니다.
React useState 참고: https://react.dev/reference/react/useState

🤖 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-detail-todo-form.ts
around lines 81 - 117, 연결된 실제 데이터로 상세 폼 초기값을 구성하세요. useDetailTodoForm의 date 상태는
하드코딩된 날짜 대신 상위 컨테이너의 선택 날짜를 인자로 받아 초기화하고, memo는 Todo API 계약의 메모 값으로 설정하세요. 반복 관련
상태인 isRepeatActive, repeatFrequency, selectedWeekdayIds, repeatDay도 todo의 API
필드에서 초기화하도록 Todo 입력 타입과 호출부를 함께 갱신하세요.

Comment on lines +82 to +83
const [time, setTime] = useState(durationText);
const [selectedTime, setSelectedTime] = useState<TimeSelection>();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

selectedTime 초기값이 기존 소요 시간과 매칭되지 않습니다.

timeconvertDurationToTimeText(todo.durationSeconds)로 텍스트만 채워지지만, selectedTime(Line 83)은 undefined로 시작해 DETAIL_TODO_TIME_OPTIONS 중 어떤 항목도 선택 표시되지 않습니다. 모달을 처음 열었을 때 시간 텍스트는 맞는데 선택된 옵션 하이라이트만 비어있는 시각적 불일치가 생겨요.

todo.durationSeconds(분 단위 환산)로 매칭되는 옵션을 찾아 초기값으로 넣어주면 좋겠습니다.

⏱️ 제안 diff
-  const [selectedTime, setSelectedTime] = useState<TimeSelection>();
+  const initialMinute = Math.round(todo.durationSeconds / 60);
+  const [selectedTime, setSelectedTime] = useState<TimeSelection | undefined>(
+    DETAIL_TODO_TIME_OPTIONS.find((option) => option.minute === initialMinute)
+      ?.minute,
+  );

Also applies to: 197-209

🤖 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-detail-todo-form.ts
around lines 82 - 83, Initialize selectedTime in useDetailTodoForm to the
DETAIL_TODO_TIME_OPTIONS entry matching todo.durationSeconds converted to
minutes, instead of leaving it undefined. Keep the existing durationText
initialization and ensure the matching option is highlighted when the modal
opens, while preserving undefined only when no option matches.

@kimminna kimminna left a comment

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.

고생했어요~ 코멘트 확인해서 수정해 주세용
전체적으로 모달과 관련된 컴포넌트 & 컨테이너들을 전체 공용 폴더로 분리하는 작업 진행하고, 공용 유틸 손보는 작업도 필요할 것 같습니다!

Comment thread apps/timo-web/messages/en.json
Comment thread apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts
Comment thread apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts
Comment thread apps/timo-web/components/todo-modal/common/TodoIconField.tsx
Comment thread apps/timo-web/components/todo-modal/detail/DetailTodoTaskFields.tsx
@yumin-kim2

Copy link
Copy Markdown
Contributor Author

폴더 구조에 관한건 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.

Actionable comments posted: 8

Caution

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

⚠️ Outside diff range comments (4)
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts (1)

109-115: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

handleDeleteTodo가 아직 서버에 반영되지 않아요 (TODO 인지 중).

로컬 상태만 갱신하고 있어서 새로고침하면 삭제된 투두가 다시 보일 거예요. 다른 핸들러들처럼 mutation + onSuccess: invalidateHomeView + onError 롤백 패턴을 적용하면 일관성 있게 마무리될 것 같습니다. 삭제 API가 준비되면 제가 mutation 배선 초안을 도와드려도 될까요, 아니면 별도 이슈로 추적해 둘까요?

🤖 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/use-home-todos-by-date.ts
around lines 109 - 115, Update handleDeleteTodo to persist deletion through the
available delete mutation rather than only modifying local state. Follow the
existing handlers’ mutation pattern, including invalidateHomeView in onSuccess
and restoring the previous todosByDate state in onError, while preserving the
current dateKey/todoId targeting.
apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx (1)

74-79: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

상세 모달 헤더 날짜를 선택된 TODO 값으로 바꿔 주세요
apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx:74-7922 / weekday.MON이 하드코딩되어 있어, 어떤 TODO를 열어도 상단 날짜가 같습니다. TodoToolbar처럼 detailTodoForm.date를 사용해 날짜와 요일을 렌더링하면 됩니다. Date/Intl.DateTimeFormat 문서도 같이 보면 좋아요. 😉

🤖 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/detail/DetailTodoModalContent.tsx` around
lines 74 - 79, Update the date header in DetailTodoModalContent to derive the
displayed day and weekday from detailTodoForm.date instead of the hardcoded “22”
and weekday.MON values. Follow the existing TodoToolbar date-rendering approach,
using Date/Intl.DateTimeFormat while preserving the current styling and
translation behavior.
apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts (2)

231-242: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Backspace 분기가 최신 상태 대신 클로저로 캡처된 subtaskInputs를 직접 참조합니다.

바로 위 Enter 분기(217-227)는 setSubtaskInputs((prev) => ...) 함수형 업데이트로 최신 상태를 보장하는데, Backspace 분기는 렌더 시점에 캡처된 subtaskInputs를 그대로 removeSubtaskInputOnBackspace에 넘기고 있어 비일관적입니다. 같은 이벤트 루프 내에서 상태 갱신이 겹치는 경우 stale 데이터로 잘못된 항목이 삭제될 위험이 있습니다.

하단 컨솔리데이션 코멘트에서 use-subtask-field.ts의 동일 패턴과 함께 다뤘습니다.

🤖 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-detail-todo-form.ts` around lines 231 -
242, Update the Backspace branch in the subtask input handler to use a
functional setSubtaskInputs update, passing the previous state to
removeSubtaskInputOnBackspace instead of the closure-captured subtaskInputs.
Preserve the existing focusIndex handling, preventDefault behavior, and
pendingSubtaskFocusIndex update.

139-176: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

서브태스크 입력 관리 로직이 use-subtask-field.ts와 거의 동일하게 중복 구현되어 있습니다.

ref 배열/포커스 이동 useEffect/Enter·Backspace 처리 흐름이 use-subtask-field.ts와 구조적으로 동일합니다. DetailTodoSubtaskInputsubtaskId/completed를 추가로 갖는다는 차이는 있지만, 제네릭 타입 파라미터로 처리 가능한 범위입니다. 두 파일을 하나의 공용 훅으로 통합하면 향후 버그 수정을 한 곳에서만 하면 되어 유지보수 부담이 줄어듭니다.

하단 컨솔리데이션 코멘트에서 use-subtask-field.ts와 함께 다뤘습니다.

Also applies to: 189-243

🤖 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-detail-todo-form.ts` around lines 139 -
176, Consolidate the duplicated subtask input management in the detail todo form
with the shared logic from use-subtask-field.ts, including ref tracking, pending
focus handling, and Enter/Backspace behavior. Extend the shared hook with a
generic input type or configuration so DetailTodoSubtaskInput retains its
subtaskId and completed fields while both consumers use the same implementation.
🤖 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/components/todo-modal/common/TodoIconField.tsx`:
- Around line 28-36: Update TodoIconField’s selected-icon button to use a
separate state-appropriate accessibility label instead of addIconLabel. Add or
reuse a prop for the existing-icon action (such as changing or removing the
icon), pass it to the button’s aria-label when icon is present, and preserve
addIconLabel for the no-icon add action.

In `@apps/timo-web/components/todo-modal/create/CreateTodoMemoField.tsx`:
- Around line 21-26: CreateTodoMemoField의 textarea 높이 조정 로직에 value 변경을 감시하는
useEffect를 추가하세요. 외부에서 value가 리셋되거나 프로그래밍 방식으로 변경될 때 resizeTextarea를 호출해 높이를 다시
계산하고, 기존 ref 및 onInput 동작은 유지하세요.
- Around line 3-7: Extract the duplicated resizeTextarea logic into a shared
helper under the todo-modal common utilities, export it, and update
CreateTodoMemoField, CreateTodoTaskFields, and DetailTodoTaskFields to import
and reuse that helper instead of defining local copies.

In `@apps/timo-web/components/todo-modal/create/CreateTodoTaskFields.tsx`:
- Line 41: Update the Checkbox usage in CreateTodoTaskFields so the fixed
unchecked state is explicitly non-interactive by adding its disabled handling.
Preserve the default unchecked appearance while preventing clicks, keyboard
interaction, and misleading hover/cursor behavior.
- Line 51: Update both textarea className declarations in CreateTodoTaskFields
to preserve a visible keyboard focus indicator: remove outline-none or replace
it with the design-system’s focus-visible ring styling, using the established
timo-blue-300 ring pattern. Apply the same treatment to both fields identified
by the textarea className usages.

In `@apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts`:
- Around line 88-103: Update the useDetailTodoForm default-value flow to source
date, repeat settings, and memo from TodoDetailResponse or explicitly injected
detail fields instead of fixed DAILY, empty, and current-date values. Preserve
existing todo-derived fields, and call react-hook-form reset with the newly
computed defaults when the modal receives a different todo/detail response.

In `@apps/timo-web/hooks/todo-modal/use-subtask-field.ts`:
- Around line 88-99: Update the Backspace handling in the subtask input handler
to use a functional setSubtaskInputs update, deriving entries and focusIndex
from the latest inputs state rather than the closure-captured subtaskInputs;
preserve the existing preventDefault and pendingFocusIndex behavior.
- Around line 1-114: Consolidate the duplicated subtask state and
keyboard-handling logic between useSubtaskField and use-detail-todo-form.ts.
Reuse a shared hook or extracted utility for createEntry, synchronization, input
changes, Enter/Backspace handling, focus management, and reset, while preserving
the existing subtask behavior and MAX_SUBTASK_COUNT constraint.

---

Outside diff comments:
In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts:
- Around line 109-115: Update handleDeleteTodo to persist deletion through the
available delete mutation rather than only modifying local state. Follow the
existing handlers’ mutation pattern, including invalidateHomeView in onSuccess
and restoring the previous todosByDate state in onError, while preserving the
current dateKey/todoId targeting.

In `@apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx`:
- Around line 74-79: Update the date header in DetailTodoModalContent to derive
the displayed day and weekday from detailTodoForm.date instead of the hardcoded
“22” and weekday.MON values. Follow the existing TodoToolbar date-rendering
approach, using Date/Intl.DateTimeFormat while preserving the current styling
and translation behavior.

In `@apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts`:
- Around line 231-242: Update the Backspace branch in the subtask input handler
to use a functional setSubtaskInputs update, passing the previous state to
removeSubtaskInputOnBackspace instead of the closure-captured subtaskInputs.
Preserve the existing focusIndex handling, preventDefault behavior, and
pendingSubtaskFocusIndex update.
- Around line 139-176: Consolidate the duplicated subtask input management in
the detail todo form with the shared logic from use-subtask-field.ts, including
ref tracking, pending focus handling, and Enter/Backspace behavior. Extend the
shared hook with a generic input type or configuration so DetailTodoSubtaskInput
retains its subtaskId and completed fields while both consumers use the same
implementation.
🪄 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: bb0acbd4-6a1c-40fb-aa2b-cc1a788fca91

📥 Commits

Reviewing files that changed from the base of the PR and between 83fe429 and 14343d0.

📒 Files selected for processing (24)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-card/HomeTodoCard.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.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/_hooks/use-home-todos-by-date.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/components/todo-modal/common/TodoIconField.tsx
  • apps/timo-web/components/todo-modal/create/CreateTodoMemoField.tsx
  • apps/timo-web/components/todo-modal/create/CreateTodoModalContainer.tsx
  • apps/timo-web/components/todo-modal/create/CreateTodoModalContent.tsx
  • apps/timo-web/components/todo-modal/create/CreateTodoTaskFields.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoMemoField.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoModalContainer.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoTaskFields.tsx
  • apps/timo-web/hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts
  • apps/timo-web/hooks/todo-modal/use-subtask-field.ts
  • apps/timo-web/hooks/todo-modal/use-title-field.ts
  • apps/timo-web/messages/en.json
  • apps/timo-web/messages/ko.json
  • apps/timo-web/utils/todo/tag-label.ts
  • apps/timo-web/utils/todo/text-length.ts
  • apps/timo-web/utils/todo/todo-time.ts
  • packages/timo-design-system/src/components/todo/todo-toolbar/TodoToolbar.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: 8

Caution

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

⚠️ Outside diff range comments (4)
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts (1)

109-115: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

handleDeleteTodo가 아직 서버에 반영되지 않아요 (TODO 인지 중).

로컬 상태만 갱신하고 있어서 새로고침하면 삭제된 투두가 다시 보일 거예요. 다른 핸들러들처럼 mutation + onSuccess: invalidateHomeView + onError 롤백 패턴을 적용하면 일관성 있게 마무리될 것 같습니다. 삭제 API가 준비되면 제가 mutation 배선 초안을 도와드려도 될까요, 아니면 별도 이슈로 추적해 둘까요?

🤖 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/use-home-todos-by-date.ts
around lines 109 - 115, Update handleDeleteTodo to persist deletion through the
available delete mutation rather than only modifying local state. Follow the
existing handlers’ mutation pattern, including invalidateHomeView in onSuccess
and restoring the previous todosByDate state in onError, while preserving the
current dateKey/todoId targeting.
apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx (1)

74-79: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

상세 모달 헤더 날짜를 선택된 TODO 값으로 바꿔 주세요
apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx:74-7922 / weekday.MON이 하드코딩되어 있어, 어떤 TODO를 열어도 상단 날짜가 같습니다. TodoToolbar처럼 detailTodoForm.date를 사용해 날짜와 요일을 렌더링하면 됩니다. Date/Intl.DateTimeFormat 문서도 같이 보면 좋아요. 😉

🤖 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/detail/DetailTodoModalContent.tsx` around
lines 74 - 79, Update the date header in DetailTodoModalContent to derive the
displayed day and weekday from detailTodoForm.date instead of the hardcoded “22”
and weekday.MON values. Follow the existing TodoToolbar date-rendering approach,
using Date/Intl.DateTimeFormat while preserving the current styling and
translation behavior.
apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts (2)

231-242: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Backspace 분기가 최신 상태 대신 클로저로 캡처된 subtaskInputs를 직접 참조합니다.

바로 위 Enter 분기(217-227)는 setSubtaskInputs((prev) => ...) 함수형 업데이트로 최신 상태를 보장하는데, Backspace 분기는 렌더 시점에 캡처된 subtaskInputs를 그대로 removeSubtaskInputOnBackspace에 넘기고 있어 비일관적입니다. 같은 이벤트 루프 내에서 상태 갱신이 겹치는 경우 stale 데이터로 잘못된 항목이 삭제될 위험이 있습니다.

하단 컨솔리데이션 코멘트에서 use-subtask-field.ts의 동일 패턴과 함께 다뤘습니다.

🤖 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-detail-todo-form.ts` around lines 231 -
242, Update the Backspace branch in the subtask input handler to use a
functional setSubtaskInputs update, passing the previous state to
removeSubtaskInputOnBackspace instead of the closure-captured subtaskInputs.
Preserve the existing focusIndex handling, preventDefault behavior, and
pendingSubtaskFocusIndex update.

139-176: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

서브태스크 입력 관리 로직이 use-subtask-field.ts와 거의 동일하게 중복 구현되어 있습니다.

ref 배열/포커스 이동 useEffect/Enter·Backspace 처리 흐름이 use-subtask-field.ts와 구조적으로 동일합니다. DetailTodoSubtaskInputsubtaskId/completed를 추가로 갖는다는 차이는 있지만, 제네릭 타입 파라미터로 처리 가능한 범위입니다. 두 파일을 하나의 공용 훅으로 통합하면 향후 버그 수정을 한 곳에서만 하면 되어 유지보수 부담이 줄어듭니다.

하단 컨솔리데이션 코멘트에서 use-subtask-field.ts와 함께 다뤘습니다.

Also applies to: 189-243

🤖 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-detail-todo-form.ts` around lines 139 -
176, Consolidate the duplicated subtask input management in the detail todo form
with the shared logic from use-subtask-field.ts, including ref tracking, pending
focus handling, and Enter/Backspace behavior. Extend the shared hook with a
generic input type or configuration so DetailTodoSubtaskInput retains its
subtaskId and completed fields while both consumers use the same implementation.
🤖 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/components/todo-modal/common/TodoIconField.tsx`:
- Around line 28-36: Update TodoIconField’s selected-icon button to use a
separate state-appropriate accessibility label instead of addIconLabel. Add or
reuse a prop for the existing-icon action (such as changing or removing the
icon), pass it to the button’s aria-label when icon is present, and preserve
addIconLabel for the no-icon add action.

In `@apps/timo-web/components/todo-modal/create/CreateTodoMemoField.tsx`:
- Around line 21-26: CreateTodoMemoField의 textarea 높이 조정 로직에 value 변경을 감시하는
useEffect를 추가하세요. 외부에서 value가 리셋되거나 프로그래밍 방식으로 변경될 때 resizeTextarea를 호출해 높이를 다시
계산하고, 기존 ref 및 onInput 동작은 유지하세요.
- Around line 3-7: Extract the duplicated resizeTextarea logic into a shared
helper under the todo-modal common utilities, export it, and update
CreateTodoMemoField, CreateTodoTaskFields, and DetailTodoTaskFields to import
and reuse that helper instead of defining local copies.

In `@apps/timo-web/components/todo-modal/create/CreateTodoTaskFields.tsx`:
- Line 41: Update the Checkbox usage in CreateTodoTaskFields so the fixed
unchecked state is explicitly non-interactive by adding its disabled handling.
Preserve the default unchecked appearance while preventing clicks, keyboard
interaction, and misleading hover/cursor behavior.
- Line 51: Update both textarea className declarations in CreateTodoTaskFields
to preserve a visible keyboard focus indicator: remove outline-none or replace
it with the design-system’s focus-visible ring styling, using the established
timo-blue-300 ring pattern. Apply the same treatment to both fields identified
by the textarea className usages.

In `@apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts`:
- Around line 88-103: Update the useDetailTodoForm default-value flow to source
date, repeat settings, and memo from TodoDetailResponse or explicitly injected
detail fields instead of fixed DAILY, empty, and current-date values. Preserve
existing todo-derived fields, and call react-hook-form reset with the newly
computed defaults when the modal receives a different todo/detail response.

In `@apps/timo-web/hooks/todo-modal/use-subtask-field.ts`:
- Around line 88-99: Update the Backspace handling in the subtask input handler
to use a functional setSubtaskInputs update, deriving entries and focusIndex
from the latest inputs state rather than the closure-captured subtaskInputs;
preserve the existing preventDefault and pendingFocusIndex behavior.
- Around line 1-114: Consolidate the duplicated subtask state and
keyboard-handling logic between useSubtaskField and use-detail-todo-form.ts.
Reuse a shared hook or extracted utility for createEntry, synchronization, input
changes, Enter/Backspace handling, focus management, and reset, while preserving
the existing subtask behavior and MAX_SUBTASK_COUNT constraint.

---

Outside diff comments:
In
`@apps/timo-web/app/`[locale]/(main)/(with-time-sidebar)/home/_hooks/use-home-todos-by-date.ts:
- Around line 109-115: Update handleDeleteTodo to persist deletion through the
available delete mutation rather than only modifying local state. Follow the
existing handlers’ mutation pattern, including invalidateHomeView in onSuccess
and restoring the previous todosByDate state in onError, while preserving the
current dateKey/todoId targeting.

In `@apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx`:
- Around line 74-79: Update the date header in DetailTodoModalContent to derive
the displayed day and weekday from detailTodoForm.date instead of the hardcoded
“22” and weekday.MON values. Follow the existing TodoToolbar date-rendering
approach, using Date/Intl.DateTimeFormat while preserving the current styling
and translation behavior.

In `@apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts`:
- Around line 231-242: Update the Backspace branch in the subtask input handler
to use a functional setSubtaskInputs update, passing the previous state to
removeSubtaskInputOnBackspace instead of the closure-captured subtaskInputs.
Preserve the existing focusIndex handling, preventDefault behavior, and
pendingSubtaskFocusIndex update.
- Around line 139-176: Consolidate the duplicated subtask input management in
the detail todo form with the shared logic from use-subtask-field.ts, including
ref tracking, pending focus handling, and Enter/Backspace behavior. Extend the
shared hook with a generic input type or configuration so DetailTodoSubtaskInput
retains its subtaskId and completed fields while both consumers use the same
implementation.
🪄 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: bb0acbd4-6a1c-40fb-aa2b-cc1a788fca91

📥 Commits

Reviewing files that changed from the base of the PR and between 83fe429 and 14343d0.

📒 Files selected for processing (24)
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_components/todo-card/HomeTodoCard.tsx
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_containers/HomeTodoContainer.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/_hooks/use-home-todos-by-date.ts
  • apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/todo-modal/CreateTodoModalContainer.tsx
  • apps/timo-web/components/todo-modal/common/TodoIconField.tsx
  • apps/timo-web/components/todo-modal/create/CreateTodoMemoField.tsx
  • apps/timo-web/components/todo-modal/create/CreateTodoModalContainer.tsx
  • apps/timo-web/components/todo-modal/create/CreateTodoModalContent.tsx
  • apps/timo-web/components/todo-modal/create/CreateTodoTaskFields.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoMemoField.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoModalContainer.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx
  • apps/timo-web/components/todo-modal/detail/DetailTodoTaskFields.tsx
  • apps/timo-web/hooks/todo-modal/use-create-todo-submit.ts
  • apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts
  • apps/timo-web/hooks/todo-modal/use-subtask-field.ts
  • apps/timo-web/hooks/todo-modal/use-title-field.ts
  • apps/timo-web/messages/en.json
  • apps/timo-web/messages/ko.json
  • apps/timo-web/utils/todo/tag-label.ts
  • apps/timo-web/utils/todo/text-length.ts
  • apps/timo-web/utils/todo/todo-time.ts
  • packages/timo-design-system/src/components/todo/todo-toolbar/TodoToolbar.tsx
🛑 Comments failed to post (8)
apps/timo-web/components/todo-modal/common/TodoIconField.tsx (1)

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

아이콘이 이미 있을 때도 "추가" 라벨이 쓰여요.

이미 아이콘이 선택된 상태에서 나오는 축약 버튼(라인 28-36)도 addIconLabel을 그대로 aria-label에 사용하고 있어요. 실제로는 패널을 열어 아이콘을 바꾸거나 지우는 동작이라, 스크린리더 사용자에게는 "아이콘 추가"라는 안내가 실제 동작과 어긋날 수 있습니다. 상태별로 다른 라벨(예: "아이콘 변경")을 주입할 수 있게 props를 분리하면 좋을 것 같아요. 참고: WAI-ARIA Authoring Practices - aria-label

♿ 제안 diff
 export interface TodoIconFieldProps {
   icon: TodoIconValue | null;
   isIconPanelOpen: boolean;
   addIconLabel: string;
+  editIconLabel?: string;
   onOpenPanel: () => void;
   ...
 }
...
-          aria-label={addIconLabel}
+          aria-label={editIconLabel ?? addIconLabel}
📝 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={editIconLabel ?? 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/common/TodoIconField.tsx` around lines 28
- 36, Update TodoIconField’s selected-icon button to use a separate
state-appropriate accessibility label instead of addIconLabel. Add or reuse a
prop for the existing-icon action (such as changing or removing the icon), pass
it to the button’s aria-label when icon is present, and preserve addIconLabel
for the no-icon add action.
apps/timo-web/components/todo-modal/create/CreateTodoMemoField.tsx (2)

3-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# resizeTextarea가 다른 파일에 중복 정의되어 있는지 확인
rg -n 'resizeTextarea' --type=tsx --type=ts -C2

Repository: Team-Timo/Timo-client

Length of output: 187


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Read the review skill instructions first
skill_file=".agents/skills/quality/timo-review/SKILL.md"
if [[ -f "$skill_file" ]]; then
  echo "=== $skill_file ==="
  cat -n "$skill_file"
else
  echo "Missing $skill_file"
fi

# 2) Locate resizeTextarea usages/definitions in the repo
echo "=== resizeTextarea matches ==="
rg -n "resizeTextarea" apps/timo-web -g '*.ts' -g '*.tsx' -C 2 || true

Repository: Team-Timo/Timo-client

Length of output: 7797


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the target file and related files if they exist to inspect context around resizeTextarea.
for f in \
  "apps/timo-web/components/todo-modal/create/CreateTodoMemoField.tsx" \
  "apps/timo-web/components/todo-modal/create/CreateTodoTaskFields.tsx" \
  "apps/timo-web/components/todo-modal/detail/DetailTodoTaskFields.tsx"
do
  if [[ -f "$f" ]]; then
    echo "=== $f (outline) ==="
    ast-grep outline "$f" --view expanded || true
    echo "=== $f (relevant lines) ==="
    nl -ba "$f" | sed -n '1,220p'
  else
    echo "Missing $f"
  fi
done

Repository: Team-Timo/Timo-client

Length of output: 710


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== locate resizeTextarea definitions/usages ==="
rg -n "resizeTextarea" apps/timo-web -g '*.ts' -g '*.tsx' -C 2 || true

echo
echo "=== file line counts ==="
for f in \
  "apps/timo-web/components/todo-modal/create/CreateTodoMemoField.tsx" \
  "apps/timo-web/components/todo-modal/create/CreateTodoTaskFields.tsx" \
  "apps/timo-web/components/todo-modal/detail/DetailTodoTaskFields.tsx"
do
  if [[ -f "$f" ]]; then
    wc -l "$f"
  fi
done

echo
echo "=== CreateTodoMemoField.tsx excerpt ==="
nl -ba "apps/timo-web/components/todo-modal/create/CreateTodoMemoField.tsx" | sed -n '1,220p'

Repository: Team-Timo/Timo-client

Length of output: 5684


resizeTextarea를 공용 헬퍼로 묶어주세요.
이 파일의 로직이 CreateTodoTaskFields.tsxDetailTodoTaskFields.tsx에도 그대로 반복됩니다. 지금처럼 각 컴포넌트에 흩어두면 높이 계산 규칙을 바꿀 때 3곳을 동시에 수정해야 해서 금방 어긋납니다. apps/timo-web/components/todo-modal/common/ 같은 곳으로 빼서 재사용하면 유지보수가 훨씬 편해집니다.
참고: MDN Modules https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules

🤖 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/create/CreateTodoMemoField.tsx` around
lines 3 - 7, Extract the duplicated resizeTextarea logic into a shared helper
under the todo-modal common utilities, export it, and update
CreateTodoMemoField, CreateTodoTaskFields, and DetailTodoTaskFields to import
and reuse that helper instead of defining local copies.

21-26: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

폼 리셋 등 외부 value 변경 시 textarea 높이가 줄어들지 않습니다.

ref={resizeTextarea}는 마운트 시 한 번만 호출되고, 이후 value가 부모에서 프로그래밍적으로 바뀌어도(예: 제출 후 메모 필드 초기화) React는 ref 콜백을 재호출하지 않습니다. onInput은 사용자가 직접 타이핑할 때만 발생하므로, 리셋 직후에는 이전에 늘어난 높이가 그대로 남아 다음 타이핑 전까지 보일 수 있습니다.

value가 바뀔 때도 높이를 재계산하도록 useEffect로 보강하는 걸 추천합니다. React의 ref 콜백 동작은 react.dev의 ref 문서를 참고해보세요.

💡 제안 diff
+import { useEffect, useRef } from "react";
+
 export const CreateTodoMemoField = ({
   value,
   placeholder,
   onChange,
 }: CreateTodoMemoFieldProps) => {
+  const textareaRef = useRef<HTMLTextAreaElement | null>(null);
+
+  useEffect(() => {
+    resizeTextarea(textareaRef.current);
+  }, [value]);
+
   return (
     <textarea
       value={value}
-      ref={resizeTextarea}
+      ref={textareaRef}
       onChange={(event) => onChange(event.target.value)}
       onInput={(event) => resizeTextarea(event.currentTarget)}
📝 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 { useEffect, useRef } from "react";

export const CreateTodoMemoField = ({
  value,
  placeholder,
  onChange,
}: CreateTodoMemoFieldProps) => {
  const textareaRef = useRef<HTMLTextAreaElement | null>(null);

  useEffect(() => {
    resizeTextarea(textareaRef.current);
  }, [value]);

  return (
    <textarea
      value={value}
      ref={textareaRef}
      onChange={(event) => onChange(event.target.value)}
      onInput={(event) => resizeTextarea(event.currentTarget)}
      placeholder={placeholder}
🤖 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/create/CreateTodoMemoField.tsx` around
lines 21 - 26, CreateTodoMemoField의 textarea 높이 조정 로직에 value 변경을 감시하는 useEffect를
추가하세요. 외부에서 value가 리셋되거나 프로그래밍 방식으로 변경될 때 resizeTextarea를 호출해 높이를 다시 계산하고, 기존
ref 및 onInput 동작은 유지하세요.
apps/timo-web/components/todo-modal/create/CreateTodoTaskFields.tsx (2)

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

체크박스가 클릭 가능해 보이지만 아무 동작도 하지 않아요 — disabled 처리가 필요해요

checked={false} + onChange={() => {}} 조합은 시각적으로는 클릭 가능한 체크박스지만, 사용자가 클릭해도 아무 일도 일어나지 않습니다. Checkbox 컴포넌트가 cursor-pointer와 hover 스타일을 제공하기 때문에 사용자는 앱이 고장난 것으로 인식할 수 있습니다.

생성 모달에서는 미완료 상태가 기본이므로 체크박스가 비활성 상태임이 명확해야 합니다. disabled prop을 추가하거나, 디자인상 활성 상태처럼 보여야 한다면 pointer-events: none으로 상호작용을 차단하세요.

참고: WAI-ARIA — Non-functional controls

🔧 제안 diff
-        <Checkbox checked={false} onChange={() => {}} />
+        <Checkbox checked={false} onChange={() => {}} disabled />
-          <Checkbox checked={false} onChange={() => {}} />
+          <Checkbox checked={false} onChange={() => {}} disabled />

Also applies to: 60-60

🤖 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/create/CreateTodoTaskFields.tsx` at line
41, Update the Checkbox usage in CreateTodoTaskFields so the fixed unchecked
state is explicitly non-interactive by adding its disabled handling. Preserve
the default unchecked appearance while preventing clicks, keyboard interaction,
and misleading hover/cursor behavior.

51-51: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

outline-none 사용 시 포커스 표시 대체 스타일이 빠졌어요 — 접근성 위반이에요

두 textarea 모두 outline-none 클래스로 기본 포커스 아웃라인을 제거했지만, focus: 또는 focus-visible: 커스텀 포커스 스타일이 없습니다. 키보드 사용자가 어느 필드에 포커스가 있는지 알 수 없어 입력 폼에서 작업 완료가 불가능한 접근성 차단 문제입니다.

디자인 시스템의 Checkbox 컴포넌트는 peer-focus-visible:ring-2 peer-focus-visible:ring-timo-blue-300 패턴을 사용하므로, 동일한 focus-visible:ring 스타일을 적용해 일관성을 맞추는 것을 권장합니다.

As per coding guidelines, outline: none으로 포커스 테두리 제거 금지 규칙을 따라야 합니다.

♿ 제안 diff — focus-visible 링 추가
-          className="typo-headline-b-14 text-timo-black min-w-0 flex-1 resize-none overflow-hidden wrap-break-word outline-none"
+          className="typo-headline-b-14 text-timo-black min-w-0 flex-1 resize-none overflow-hidden wrap-break-word outline-none focus-visible:ring-2 focus-visible:ring-timo-blue-300 focus-visible:ring-offset-1 rounded-[2px]"
-            className="typo-body-r-12 text-timo-gray-800 min-w-0 flex-1 resize-none overflow-hidden wrap-break-word outline-none"
+            className="typo-body-r-12 text-timo-gray-800 min-w-0 flex-1 resize-none overflow-hidden wrap-break-word outline-none focus-visible:ring-2 focus-visible:ring-timo-blue-300 focus-visible:ring-offset-1 rounded-[2px]"

Also applies to: 71-71

🤖 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/create/CreateTodoTaskFields.tsx` at line
51, Update both textarea className declarations in CreateTodoTaskFields to
preserve a visible keyboard focus indicator: remove outline-none or replace it
with the design-system’s focus-visible ring styling, using the established
timo-blue-300 ring pattern. Apply the same treatment to both fields identified
by the textarea className usages.

Source: Coding guidelines

apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts (1)

88-103: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Todo 타입에 date/repeatFrequency/memo 관련 필드가 있는지 확인
ast-grep run --pattern 'interface Todo { $$$ }' --lang typescript apps/timo-web
rg -n 'interface Todo\b' -A 30 apps/timo-web

Repository: Team-Timo/Timo-client

Length of output: 159


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files 'apps/timo-web/**' | rg 'use-detail-todo-form|todo|Todo|detail.*form|schema|types'

echo
echo "== hook outline =="
ast-grep outline apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts --view expanded || true

echo
echo "== hook excerpt =="
sed -n '1,240p' apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts | cat -n

echo
echo "== Todo identifiers search =="
rg -n '\b(Todo|DetailTodoFormValues|repeatFrequency|selectedWeekdayIds|repeatDay|memo)\b' apps/timo-web -g'*.ts' -g'*.tsx' -g'*.json' | sed -n '1,220p'

Repository: Team-Timo/Timo-client

Length of output: 21958


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== todo type =="
sed -n '1,220p' apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type.ts | cat -n

echo
echo "== detail modal content =="
sed -n '1,260p' apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx | cat -n

echo
echo "== create modal content (for comparison) =="
sed -n '1,260p' apps/timo-web/components/todo-modal/create/CreateTodoModalContent.tsx | cat -n

echo
echo "== generated detail response model =="
sed -n '1,220p' apps/timo-web/api/generated/models/todoDetailResponse.ts | cat -n

echo
echo "== todo schema =="
sed -n '1,220p' apps/timo-web/api/todo/todo-schema.ts | cat -n

Repository: Team-Timo/Timo-client

Length of output: 228


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== todo type =="
sed -n '1,220p' 'apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/home/_types/todo-type.ts' | cat -n

echo
echo "== detail modal content =="
sed -n '1,260p' 'apps/timo-web/components/todo-modal/detail/DetailTodoModalContent.tsx' | cat -n

echo
echo "== create modal content (for comparison) =="
sed -n '1,260p' 'apps/timo-web/components/todo-modal/create/CreateTodoModalContent.tsx' | cat -n

echo
echo "== generated detail response model =="
sed -n '1,220p' 'apps/timo-web/api/generated/models/todoDetailResponse.ts' | cat -n

echo
echo "== todo schema =="
sed -n '1,220p' 'apps/timo-web/api/todo/todo-schema.ts' | cat -n

Repository: Team-Timo/Timo-client

Length of output: 23773


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '240,360p' 'apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts' | cat -n

Repository: Team-Timo/Timo-client

Length of output: 2635


상세 투두 기본값을 상세 응답에서 채워주세요
훅 분리는 깔끔합니다. 다만 현재 Todo에는 date/repeat/memo가 없어서 defaultValues의 고정값으로는 기존 상세 정보를 복원할 수 없습니다. TodoDetailResponse처럼 상세 응답을 쓰거나 필요한 필드를 별도로 주입하고, 모달 재사용 시에는 reset(nextDefaults)로 다시 맞춰주세요. react-hook-form의 defaultValues/reset 문서도 같이 보면 좋습니다.

🤖 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-detail-todo-form.ts` around lines 88 -
103, Update the useDetailTodoForm default-value flow to source date, repeat
settings, and memo from TodoDetailResponse or explicitly injected detail fields
instead of fixed DAILY, empty, and current-date values. Preserve existing
todo-derived fields, and call react-hook-form reset with the newly computed
defaults when the modal receives a different todo/detail response.
apps/timo-web/hooks/todo-modal/use-subtask-field.ts (2)

1-114: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

전체 구조는 좋으나, use-detail-todo-form.ts의 서브태스크 로직과 중복됩니다.

하단 컨솔리데이션 코멘트에서 함께 다뤘습니다.

🤖 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-subtask-field.ts` around lines 1 - 114,
Consolidate the duplicated subtask state and keyboard-handling logic between
useSubtaskField and use-detail-todo-form.ts. Reuse a shared hook or extracted
utility for createEntry, synchronization, input changes, Enter/Backspace
handling, focus management, and reset, while preserving the existing subtask
behavior and MAX_SUBTASK_COUNT constraint.

88-99: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Backspace 분기가 클로저로 캡처된 subtaskInputs를 직접 참조합니다.

Enter 분기(74-84)는 함수형 업데이트를 쓰는데 Backspace 분기는 그렇지 않아 일관성이 떨어지고, stale 상태를 참조할 잠재적 위험이 있습니다. use-detail-todo-form.ts의 동일 패턴과 함께 하단 컨솔리데이션 코멘트에서 다뤘습니다.

🤖 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-subtask-field.ts` around lines 88 - 99,
Update the Backspace handling in the subtask input handler to use a functional
setSubtaskInputs update, deriving entries and focusIndex from the latest inputs
state rather than the closure-captured subtaskInputs; preserve the existing
preventDefault and pendingFocusIndex behavior.

Comment thread apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts Outdated
Comment thread apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts
Comment thread apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts Outdated
Comment thread apps/timo-web/hooks/todo-modal/use-detail-todo-form.ts Outdated
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] 기존 투두 확인 모달 구현

2 participants