[FEAT] 오늘 화면 TodayTodoCard 컴포넌트 구현#106
Conversation
- isDone + isDraggable 조합으로 4가지 variant 처리 (Record 패턴) - Checkbox, PlayButton 디자인 시스템 컴포넌트 재사용 - icon/onIconClick prop으로 아이콘 피커 연동 구조 준비 - HamburgerGray 아이콘 추가 (done 상태 핸들) - PriorityIcon Disable 색상 gray-500 -> gray-700 수정 - Calendar SVG 계열 clipPath 오프셋 0.5px 수정 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- toolbar 관련 props를 TodayTodoCardToolbar 객체로 묶음 - isDimmed = isDone && !isHovered 로 hover시 흐려짐 해제 구현 - 체크박스 disabled 제거 (항상 클릭 가능) - icon 조건 수정: icon && (onIconClick만 있을 때 빈 버튼 렌더 방지) - page.tsx 테스트 코드 제거 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthrough오늘 화면에 투두 카드와 툴바가 연결되었고, 카드 상태 관리용 컨테이너와 목데이터가 추가되었습니다. 태그/우선순위 아이콘의 disable variant도 디자인시스템에 반영되었습니다. ChangesToday Todo UI
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant TodayPage
participant TodayTodoCardContainer
participant TodayTodoCard
participant CreateTodoToolbar
User->>TodayPage: 오늘 화면 진입
TodayPage->>TodayTodoCardContainer: todayTodoMock 전달
TodayTodoCardContainer->>TodayTodoCard: 상태와 이벤트 핸들러 전달
TodayTodoCard->>CreateTodoToolbar: date/time/priority/tag/memo/repeat/delete 전달
User->>TodayTodoCardContainer: 체크/재생/서브투두 조작
TodayTodoCardContainer->>TodayTodoCardContainer: 내부 상태 갱신
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Timo Performance ReportBundle Size — timo-web
Lighthouse — timo-web
Image Optimization — timo-web
측정 커밋: |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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/today/_components/TodayTodoCard.tsx`:
- Line 30: The type alias for the priority union in TodayTodoCard should follow
the team convention by using a Types suffix. Rename the `Priority` alias defined
from `ComponentProps<typeof PriorityIcon>["priority"]` to a `Types`-suffixed
name, and update all references in the same component such as the `priority?:
...` prop and `toolbar.priority` usage to match the new alias name.
- Around line 143-157: 서브투두 렌더링 블록에서 `div`로 감싼 목록을 시맨틱 리스트로 바꿔 접근성을 개선하세요.
`TodayTodoCard`의 `subTodos.map` 구간과 각 항목의 컨테이너를 `ul`/`li` 구조로 변경하고, 기존
`flex`/`gap` 스타일은 유지하면 됩니다. `Checkbox`, `sub.id`, `sub.text`, `onSubTodoCheck`
동작은 그대로 두고, 목록 의미가 전달되도록 리스트 마크업만 정리하세요.
- Around line 161-184: The date/time UI in TodayTodoCard is rendered as button
elements without any actual action, so replace the toolbar date/time buttons
with non-interactive text elements unless there is a real click behavior. Update
the toolbar rendering in TodayTodoCard to use span/div for the date and time
display, or, if interaction is intended, add and wire up onDateClick/onTimeClick
props so the button elements in the toolbar have a concrete handler.
- Line 1: TodayTodoCard still has client-only hover state in a _components_ UI
file, which breaks the pure presentational layer rule. Move the isDimmed
behavior into the _containers_ layer or replace the stateful hover logic with
CSS-based group-hover/group-focus-within handling, then remove the use client
directive from TodayTodoCard so it remains a pure props-only component.
🪄 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: d69c418b-cbfb-4fcd-b021-8af8eb219b4c
⛔ Files ignored due to path filters (5)
packages/timo-design-system/src/icons/source/calendar-blue.svgis excluded by!**/*.svgpackages/timo-design-system/src/icons/source/calendar-disable.svgis excluded by!**/*.svgpackages/timo-design-system/src/icons/source/calendar-on.svgis excluded by!**/*.svgpackages/timo-design-system/src/icons/source/hamburger-gray.svgis excluded by!**/*.svgpackages/timo-design-system/src/icons/source/hamburger.svgis excluded by!**/*.svg
📒 Files selected for processing (3)
apps/timo-web/app/today/_components/.gitkeepapps/timo-web/app/today/_components/TodayTodoCard.tsxpackages/timo-design-system/src/components/priority/priority-icon/PriorityIcon.tsx
yumin-kim2
left a comment
There was a problem hiding this comment.
아이콘/텍스트 반복되는 UI 요소(날짜, 시간, 태그 등)를 비슷한 패턴으로 통일감 있게 작성하셔서, 코드를 읽을 때 패턴만 한 번 이해하면 나머지는 쉽게 따라 읽을 수 있었습니다 👍
코멘트 달았는데 확인해주세요 수고하셨써요~~~~!!!!✨
- CreateTodoToolbar 컴포넌트를 구현했습니다 - TodayTodoCard 인라인 toolbar를 CreateTodoToolbar로 교체했습니다 - isDimmed 상태에서 모든 toolbar 아이템을 disable 처리했습니다 - 한글 20자·영어 30자 기준 truncateTitle 함수를 적용했습니다 - 미사용 CARD_STYLE meta 필드를 제거했습니다 - TodayPage에 테스트 데이터를 추가했습니다
- 좌우 padding을 6.5px에서 10px로 수정했습니다
- w-[36px]을 Tailwind 표준 클래스 w-9으로 변경했습니다
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 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/today/_components/TodayTodoCard.tsx`:
- Around line 175-186: CreateTodoToolbar is rendered with only the delete action
wired, so the other toolbar controls are visually clickable but do nothing. In
TodayTodoCard, pass through the missing handlers from the parent state/props for
onDateClick, onTimeClick, onPriorityClick, onTagClick, onMemoClick, and
onRepeatClick alongside the existing toolbar values, using the CreateTodoToolbar
component as the reference point.
- Line 18: The import in TodayTodoCard should use the existing `@/` alias instead
of a brittle relative path. Update the CreateTodoToolbar import in TodayTodoCard
to reference the alias form so it stays stable if the component moves, and keep
the rest of the component unchanged.
- Around line 24-39: The truncateTitle function is trimming one character too
early at the exact display limit because the boundary check uses a non-strict
comparison. Update the truncation condition inside truncateTitle so titles at
the exact Korean/other character thresholds are preserved, and only truncate
when the limit is actually exceeded; keep the fix localized to the loop logic
and the text.slice call in TodayTodoCard.tsx.
In `@apps/timo-web/app/today/page.tsx`:
- Around line 1-44: `TodayPage` is carrying client state and handlers, so move
the `useState` logic and `handleSubTodoCheck` out of `page.tsx` into a
client-only container such as `TodayContainer` under `_containers/`. Keep
`page.tsx` as a Server Component that only composes and renders the container,
and let `TodayContainer` own the `TodayTodoCard` wiring, `isDone`, `subTodos`,
and toggle handlers. Preserve the existing props and behavior by referencing
`TodayTodoCard`, `INITIAL_SUB_TODOS`, and `handleSubTodoCheck` in the new
container.
In `@apps/timo-web/components/CreateTodoToolbar.tsx`:
- Around line 36-52: The boolean props in CreateTodoToolbarProps are missing the
required is/has-style prefix, and delete is also a reserved word that forces
awkward renaming. Rename these flags in CreateTodoToolbarProps to prefixed
names, update the CreateTodoToolbar component’s prop destructuring to match, and
change the TodayTodoCard call site to pass the new prop names consistently. Keep
the API aligned with the boolean naming convention so the component interface is
clearer and avoids reserved-word handling.
- Around line 140-145: The tag control in CreateTodoToolbar currently renders an
empty TagIcon when tag is missing, leaving a blank chip visible. Update the tag
rendering logic around onTagClick and TagIcon so that when tag is absent it
either shows the same disabled state used by the other fields or is
conditionally not rendered at all, instead of passing an empty string as text.
Keep the behavior consistent with the existing activeItem-based rendering.
🪄 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: d5709388-ffe9-4dc6-8864-4d0cc1f2e8c4
⛔ Files ignored due to path filters (1)
packages/timo-design-system/src/icons/source/memo-blue.svgis excluded by!**/*.svg
📒 Files selected for processing (5)
apps/timo-web/app/today/_components/TodayTodoCard.tsxapps/timo-web/app/today/page.tsxapps/timo-web/components/.gitkeepapps/timo-web/components/CreateTodoToolbar.tsxpackages/timo-design-system/src/components/tag/tag-icon/TagIcon.tsx
| import { cn } from "@repo/timo-design-system/utils"; | ||
| import { useEffect, useState } from "react"; | ||
|
|
||
| import { CreateTodoToolbar } from "../../../components/CreateTodoToolbar"; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
fd -e json 'tsconfig' apps/timo-web -x cat {}Repository: Team-Timo/Timo-client
Length of output: 528
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== TodayTodoCard.tsx ==\n'
cat -n apps/timo-web/app/today/_components/TodayTodoCard.tsx | sed -n '1,80p'
printf '\n== CreateTodoToolbar files ==\n'
fd -a 'CreateTodoToolbar' apps/timo-web | sed -n '1,20p'
printf '\n== tsconfig paths ==\n'
cat -n apps/timo-web/tsconfig.json | sed -n '1,80p'Repository: Team-Timo/Timo-client
Length of output: 3048
상대 경로 import를 별칭으로 바꿔주세요.
apps/timo-web/tsconfig.json에 @/*가 이미 잡혀 있어서 ../../../components/CreateTodoToolbar 대신 @/components/CreateTodoToolbar로 쓰면 경로 이동에 더 강해집니다.
관련 문서: Next.js Absolute Imports and Module Path Aliases
🤖 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/today/_components/TodayTodoCard.tsx` at line 18, The import
in TodayTodoCard should use the existing `@/` alias instead of a brittle relative
path. Update the CreateTodoToolbar import in TodayTodoCard to reference the
alias form so it stays stable if the component moves, and keep the rest of the
component unchanged.
Source: Path instructions
| <div className="flex justify-end"> | ||
| <CreateTodoToolbar | ||
| date={isDimmed ? undefined : toolbar?.date} | ||
| time={isDimmed ? undefined : toolbar?.time} | ||
| priority={isDimmed ? undefined : toolbar?.priority} | ||
| tag={isDimmed ? undefined : toolbar?.tag} | ||
| memo={!isDimmed && toolbar?.memo} | ||
| repeat={!isDimmed && toolbar?.repeat} | ||
| delete={!isDimmed} | ||
| onDeleteClick={onDelete} | ||
| /> | ||
| </div> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔵 Trivial
CreateTodoToolbar에 클릭 핸들러가 하나도 안 물려있어요 (delete 제외).
onDateClick/onTimeClick/onPriorityClick/onTagClick/onMemoClick/onRepeatClick이 전달되지 않아서, 버튼들은 시각적으로만 클릭 가능한 상태로 남아요(포커스는 되지만 아무 동작도 없음). 아직 상위 상태 연결 전 단계라면 의도된 것일 수 있지만, PR 목표에 명시되지 않은 부분이라 확인이 필요해 보여요.
향후 PR에서 처리될 예정이라면 무시하셔도 좋아요. 필요하시면 page.tsx(혹은 향후 _containers/)에서 해당 핸들러들을 연결하는 코드를 같이 만들어드릴까요?
🤖 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/today/_components/TodayTodoCard.tsx` around lines 175 -
186, CreateTodoToolbar is rendered with only the delete action wired, so the
other toolbar controls are visually clickable but do nothing. In TodayTodoCard,
pass through the missing handlers from the parent state/props for onDateClick,
onTimeClick, onPriorityClick, onTagClick, onMemoClick, and onRepeatClick
alongside the existing toolbar values, using the CreateTodoToolbar component as
the reference point.
- app/today/_components에서 app/[locale]/(main)/today/_components로 이동했습니다 - CreateTodoToolbar import 경로를 새 위치에 맞게 수정했습니다
- truncateTitle 비교 연산자를 >= 에서 > 로 수정해 정확히 한국어 20자/영어 30자를 표시했습니다 - isDone 자동 정지 시 onPlay?.()를 호출해 부모 컴포넌트에 상태 변화를 알렸습니다 - CreateTodoToolbar 상대 경로 import를 @/components alias로 교체했습니다 - tag 값이 없을 때 빈 TagIcon 칩이 렌더링되던 버그를 수정했습니다 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
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/TodayTodoCardContainer.tsx:
- Around line 49-57: Rename the callback in TodayTodoCardContainer’s handleCheck
flow so the prop name reflects the neutral timer state change rather than
playback only; update the local usage of onPlay and its prop definition to
something like onTimerToggle, or split out a dedicated onStop if you need
separate stop behavior. Make sure all references in TodayTodoCardContainer stay
consistent so the check-driven auto-stop and any button-triggered action use the
updated, clearer callback name.
🪄 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: b946eac1-bf97-42bf-bcfb-dd162469b9b1
📒 Files selected for processing (7)
apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsxapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock.tsapps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/page.tsxapps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsxapps/timo-web/components/CreateTodoToolbar.tsxpackages/timo-design-system/src/components/tag/tag-icon/TagIcon.stories.tsxpackages/timo-design-system/src/components/tag/tag-icon/TagIcon.tsx
| const handleCheck = () => { | ||
| const next = !isDone; | ||
| setIsDone(next); | ||
| if (next && isPlaying) { | ||
| setIsPlaying(false); | ||
| onPlay?.(); | ||
| } | ||
| onCheck?.(); | ||
| }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔵 Trivial | 💤 Low value
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file outline =="
ast-grep outline "apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx" --view expanded || true
echo "== relevant file lines =="
sed -n '1,180p' "apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_containers/TodayTodoCardContainer.tsx"
echo "== search usages of TodayTodoCardContainer and onPlay =="
rg -n "TodayTodoCardContainer|onPlay|onStop|onCheck" "apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today" -SRepository: Team-Timo/Timo-client
Length of output: 5339
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== TodayTodoCard component outline =="
ast-grep outline "apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx" --view expanded || true
echo "== TodayTodoCard component lines =="
sed -n '1,220p' "apps/timo-web/app/[locale]/(main)/today/_components/TodayTodoCard.tsx"
echo "== today-todo-mock lines =="
sed -n '1,220p' "apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/_mocks/today-todo-mock.ts"
echo "== page lines =="
sed -n '1,120p' "apps/timo-web/app/[locale]/(main)/(with-time-sidebar)/today/page.tsx"Repository: Team-Timo/Timo-client
Length of output: 7540
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "\bonPlay\b|\bonStop\b|\bonTimerToggle\b|\bPlayButton\b" apps/timo-web -SRepository: Team-Timo/Timo-client
Length of output: 1645
onPlay 이름을 더 중립적으로 바꾸세요.
구조는 깔끔한데, 이름만 살짝 더 맞추면 읽기 쉬워져요. 지금은 재생 버튼 클릭과 체크로 인한 자동 정지를 같은 콜백으로 전달해서, onPlay보다 onTimerToggle 같은 이름이 의도를 더 잘 드러냅니다. 정지 동작을 따로 다뤄야 하면 onStop을 분리하는 쪽도 좋습니다.
React 이벤트 패턴: https://react.dev/learn/responding-to-events
🤖 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 49 - 57, Rename the callback in TodayTodoCardContainer’s
handleCheck flow so the prop name reflects the neutral timer state change rather
than playback only; update the local usage of onPlay and its prop definition to
something like onTimerToggle, or split out a dedicated onStop if you need
separate stop behavior. Make sure all references in TodayTodoCardContainer stay
consistent so the check-driven auto-stop and any button-triggered action use the
updated, clearer callback name.
- timerStatus prop이 바뀌어도 isPlaying 상태가 갱신되지 않는 버그를 수정했습니다
- 긴 제목이 레이아웃을 깨는 문제를 방지하기 위해 truncate 클래스를 추가했습니다
- TodayTodoListContainer를 신규 생성하여 todo 목록 상태를 관리했습니다 - runningTodoId로 동시에 하나의 타이머만 실행되도록 제약을 구현했습니다 - page.tsx를 TodayTodoListContainer 단일 렌더링으로 단순화했습니다 - mock 데이터를 API 응답 형태(TodoMock)로 재구성했습니다
- CreateTodoToolbar에 isDimmed prop을 추가하여 dimmed 상태 아이콘을 Disable 변형으로 전환했습니다 - dimmed 상태에서 날짜·시간 텍스트 색상을 gray-700으로 변경했습니다 - toolbar 항목을 dimmed 상태에서 항상 표시하도록 변경했습니다 - isDraggable prop을 TodayTodoCard 및 TodayTodoCardContainer에서 제거했습니다 - handleCheck 시 서브투두를 일괄 완료 처리하도록 수정했습니다
|
제가 또 수정했습니다.
|
date, time, priority, memo, onDateClick, onTimeClick, onPriorityClick, onTagClick, onMemoClick에서 불필요한 ? 를 제거했습니다. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
type Priority를 type PriorityTypes로 변경했습니다. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
memo → hasMemo, repeat → hasRepeat, delete → hasDelete로 이름을 변경했습니다. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tag는 미설정 상태가 가능해 string | undefined이므로 ? 를 복구했습니다. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
TodayTodoCard에서 제공하지 않는 핸들러들을 다시 옵셔널로 되돌렸습니다. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| const formatDuration = (seconds: number): string => { | ||
| const h = Math.floor(seconds / 3600); | ||
| const m = Math.floor((seconds % 3600) / 60); | ||
| return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`; | ||
| }; | ||
|
|
||
| const formatDate = (isoDate: string): string => { | ||
| const date = new Date(isoDate); | ||
| return `${date.getMonth() + 1}/${date.getDate()}`; | ||
| }; | ||
|
|
There was a problem hiding this comment.
이거 전체적으로 재사용될 가능성이 있어 보여서 공통 유틸로 빼줍시당
There was a problem hiding this comment.
TodayTodoListContainer의 formatDuration·formatDate 인라인 함수를 apps/timo-web/utils/로 분리했고, formatDuration은 focus/_utils/duration.ts와 동일해 convert-duration-to-time-text.ts로 통합했습니다!
| @@ -0,0 +1,159 @@ | |||
| "use client"; | |||
- TodayTodoListContainer의 formatDate·formatDuration 인라인 함수를 utils 폴더로 분리했습니다 - formatDuration은 기존 focus/_utils/duration.ts와 동일한 로직으로 convert-duration-to-time-text.ts로 통합했습니다 - FocusSessionContainer의 import 경로를 공유 유틸로 교체했습니다
- 서버 컴포넌트로 전환하여 불필요한 use client 디렉티브를 제거했습니다
jjangminii
left a comment
There was a problem hiding this comment.
변경사항 많았을텐데 고생하셨습니다~
크리티컬은 아니지만 로직에 대해 간단한 코멘트 남겨뒀어요 확인해주세요-!
| const handleSubTodoCheck = (todoId: number, subtaskId: string) => { | ||
| // TODO: API | ||
| setTodos((prev) => | ||
| prev.map((t) => | ||
| t.todoId === todoId | ||
| ? { | ||
| ...t, | ||
| subtasks: t.subtasks.map((s) => | ||
| s.subtaskId === Number(subtaskId) | ||
| ? { ...s, completed: !s.completed } | ||
| : s, | ||
| ), | ||
| } | ||
| : t, | ||
| ), | ||
| ); | ||
| }; | ||
|
|
There was a problem hiding this comment.
subtaskId가 number → string → number로 왕복 변환되고 있는데, SubTodo.id를 number로 맞추면 변환 로직 자체를 없앨 수 있을 것 같아요-! 지금은 안전하지만 나중에 id 형식이 바뀌면 Number() 변환에서 조용히 실패할 수 있어서, 미리 타입을 통일해두면 어떨까요?
| const [todos, setTodos] = useState<TodoMock[]>(todayTodoMocks); | ||
| const [runningTodoId, setRunningTodoId] = useState<number | null>( | ||
| todayTodoMocks.find((t) => t.timerStatus === "RUNNING")?.todoId ?? null, | ||
| ); |
There was a problem hiding this comment.
runningTodoId가 todos의 timerStatus와 별도 state로 관리되고 있어서, todo가 삭제/완료될 때마다 두 state를 수동으로 동기화해주고 계신 것 같아요! todos.find(t => t.timerStatus === "RUNNING")?.todoId로 파생시키면 동기화 코드 자체가 필요 없어질 것 같은데, 혹시 Zustand 연결 시점에 구조가 또 바뀔 예정이라 지금은 편의상 분리해두신 걸까요?
| interface TodoTag { | ||
| tagId: number; | ||
| name: string; | ||
| } | ||
|
|
||
| interface TodoSubtask { | ||
| subtaskId: number; | ||
| content: string; | ||
| completed: boolean; | ||
| } | ||
|
|
||
| export interface TodoMock { | ||
| todoId: number; | ||
| icon: string; | ||
| title: string; | ||
| completed: boolean; | ||
| date: string; | ||
| durationSeconds: number; | ||
| priority: "URGENT" | "HIGH" | "MEDIUM" | "LOW"; | ||
| tag: TodoTag | null; | ||
| hasMemo: boolean; | ||
| isRepeated: boolean; | ||
| timerStatus: "RUNNING" | "PAUSED" | "STOPPED"; | ||
| sortOrder: number; | ||
| subtasks: TodoSubtask[]; | ||
| } |
There was a problem hiding this comment.
인터페이스들 나중에 API 연동 시에는 zod 도입해 봅시다!
ISSUE 🔗
close #101
What is this PR? 🔍
TodayTodoCard 컴포넌트 구현
4가지 variant를 2개의 boolean prop으로 처리
isDone×isDimmed조합으로 카드 상태를 커버하며, 스타일은 Record 패턴으로 관리합니다.hover 시 흐려짐 해제
isDone=true이면 카드가 흐려지지만, 마우스를 올리면 즉시 원래 상태로 복귀합니다. 체크박스는 항상 클릭 가능합니다.toolbar props 그룹화
날짜, 시간, 우선순위 등 메타 정보를 flat props 대신
toolbar객체로 묶어 호출부를 간결하게 했습니다.isDraggable 제거
드래그 핸들은 별도 prop 없이 항상 렌더링하는 방향으로 결정하여
isDraggableprop을 제거했습니다.dimmed 상태 아이콘 처리
isDimmed=true일 때 toolbar 아이콘 전체가 disable 버전으로 전환됩니다.CreateTodoToolbar에isDimmedprop을 그대로 전달해 아이콘 상태를 결정합니다.아이콘 피커 연동 구조 준비
icon?: ReactNode+onIconClick?: () => void로 추후 피커 구현 시 연동 가능하도록 열어뒀습니다.CreateTodoToolbar 컴포넌트 구현
피그마 스펙에 맞춰
CreateTodoToolbar를 별도 컴포넌트로 분리하고,TodayTodoCard의 인라인 toolbar를 교체했습니다.isDimmed상태에서 모든 toolbar 아이콘을 disable 버전으로 전환PriorityIcon버튼을 피그마 스펙(22×22)에 맞게size-[22px] flex items-center justify-center적용w-9(36px) 고정 — 시간 변경 시 toolbar 너비 흔들림 방지date,time,priority,memo,onDateClick~onMemoClick에서 불필요한?제거 — 항상 제공되는 props를 타입 수준에서 명시적으로 표현했습니다Design System 수정
HamburgerGray아이콘 추가 — done 상태 드래그 핸들용PriorityIconDisable 색상gray-500→gray-700TagIcon좌우 padding6.5px→10px(피그마 스펙)translate(2, 2.5)→translate(2, 2)) — Figma export 버그로 달력 아이콘이 0.5px 아래로 내려가 있던 문제 수정체크박스 상태 관리 — uncontrolled 패턴 채택
TodayTodoCard는 모달에서 생성 버튼을 눌렀을 때 추가되는 구조입니다. 실제 목록 관리는 추후 Zustand store에서 담당할 예정이어서, 지금 단계에서는
isDone과subTodos를 컴포넌트 내부 state로 관리하는 uncontrolled 패턴을 택했습니다.onCheck/onSubTodoCheck콜백은 옵셔널 — Zustand 연결 시 해당 콜백을 꽂으면 부모에게도 알림이 전달되는 구조isDone 자동 정지 시 부모 알림
타이머가 돌아가는 중(
isPlaying=true)에 할 일을 완료 처리하면 타이머가 자동으로 멈추고,onPlay?.()를 호출해 부모(추후 전역 store)에게 정지 사실을 알립니다.deps를
[internalIsDone]하나만 두는 이유:internalIsDone이 바뀌는 시점의isPlaying값을 참조하는 게 의도된 동작입니다. Zustand + 실제 타이머 연결 시 이 effect는 전면 교체될 예정입니다.개발 확인용 mock 데이터
page.tsx는 Server Component라 직접 state를 가질 수 없고, 별도 client wrapper를 만드는 것보다 uncontrolled 패턴으로 카드 자체가 상태를 갖는 게 현 단계에서 더 자연스럽다고 판단했습니다. mock 데이터는_mocks/today-todo-mock.ts로 분리해두었으며, API 연결 후 제거할 예정입니다.To Reviewers
isDimmed = isDone && !isHovered패턴으로 hover 시 흐려짐 해제를 구현했습니다. 클릭 시 hover 상태이므로 마우스가 카드를 벗어나야 흐려지는 건 의도된 동작입니다.icon/onIconClickprop만 열어뒀습니다.CreateTodoToolbar는 현재 각 버튼 클릭 시 모달 연결이 남아 있습니다. 이슈를 새로 파서 후속 PR에서 처리할 예정입니다.onCheck/onSubTodoCheck콜백을 통해 외부 store와 연결됩니다.Screenshot 📷
Timo.-.Chrome.2026-07-07.03-07-16.mp4
Test Checklist ✔