Skip to content

[✨ Feature/278] 할 일 생성 수정 모달 구현 api 연결#311

Merged
aahreum merged 25 commits intomainfrom
feature/278/card-update
Dec 2, 2025
Merged

[✨ Feature/278] 할 일 생성 수정 모달 구현 api 연결#311
aahreum merged 25 commits intomainfrom
feature/278/card-update

Conversation

@aahreum
Copy link
Member

@aahreum aahreum commented Dec 2, 2025

✅ PR 체크리스트

1. 코드 & 기능

  • 변수/함수 이름 이해 가능한지
  • 기능 정상 동작 & 콘솔 에러 없음

2. UI

  • UI 동작 및 레이아웃 확인

3. 컨벤션

  • 팀 컨벤션에 맞게 함수 이름을 정의했는지

🔗 이슈 번호

✨ 작업한 내용

  • 수정 api 연동했습니다..
  • 일단 동작은 하는데... 잔에러가 많습니다 (타입에러, 콤보박스에서 선택된 유저 프로필 폴백 컬러 변경 등)
  • 코드래빗 리뷰 받으면서 수정해보겠습니다...!!

💁 리뷰 요청 / 코멘트

💡 참고사항

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 카드 편집 모달 추가: 상태, 담당자, 제목, 설명, 마감일, 태그, 이미지 편집 및 제출 지원
  • 개선 사항

    • 카드 수정 시 목록에 즉시 반영되는 인플레이스 업데이트 흐름
    • 콤보박스 표시·검색·선택 확장(닉네임·상태명·제목 등) 및 관련 값 타입 확장
    • 카드 상세→편집 이중 모달 및 이미지 업로드/제출 처리 개선
    • 컬럼 데이터 전달로 렌더링 정보 확대
  • 버그 수정

    • 빈/공백 태그가 화면에 표시되지 않도록 필터링 개선
    • 아바타 레이아웃(축소 동작) 조정으로 UI 안정성 향상

✏️ Tip: You can customize this high-level summary in your review settings.

@aahreum aahreum requested review from jung518 and yujin-fe December 2, 2025 02:21
@aahreum aahreum self-assigned this Dec 2, 2025
@aahreum aahreum added the ✨ Feature 새로운 기능 구현 label Dec 2, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 2, 2025

Walkthrough

카드 편집 모달(ChangeCardModal) 추가 및 CardDetailModal 편집·업데이트 흐름 통합, Combobox 타입·컨텍스트 확장, 카드 관련 타입·API·유틸(updateCardRequestBody) 추가, 여러 컴포넌트의 prop 시그니처와 렌더링 로직이 변경되었습니다. (50단어 이내)

Changes

Cohort / File(s) Change Summary
새 컴포넌트: 카드 수정 모달
src/components/dashboard-detail/modal/ChangeCardModal.tsx
카드 편집 모달 추가: 폼 상태·이미지 업로드·변경 감지·유효성 검사 및 제출(onSubmit(formValue, imageFile)) 구현.
카드 상세 모달 및 편집 통합
src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx, src/components/dashboard-detail/modal/card-detail-modal/CardDetailModalDesktop.tsx
상세/편집 모달 분리 제어, update/delete mutation 추가 및 onUpdateCard 호출 로직, 이미지 업로드 연동, 데스크탑 높이 조정.
카드 리스트·카드 컴포넌트의 prop 확장
src/components/dashboard-detail/card/ColumnCardList.tsx, src/components/dashboard-detail/card/DashboardCard.tsx, src/pages/DashboardDetail.tsx
columnListData, memberData, onUpdateCard 등 prop 추가 및 상위→하위 전달로 시그니처·호출부 변경.
Combobox 타입·구현 변경
src/components/dashboard/combobox/Combobox.tsx, src/components/dashboard/combobox/ComboboxItem.tsx, src/components/dashboard/combobox/ComboboxList.tsx, src/components/dashboard/combobox/ComboboxTrigger.tsx, src/context/comboboxContext.ts
UserComboboxValue/StatusComboboxValue 타입 추가, ComboboxProps.value/setValue 타입 확장, context에서 selectedNode 제거, Item/Trigger/List의 선택 비교·렌더링·필터링 로직 조정.
카드 API 타입·유틸 변경
src/lib/apis/cards.ts, src/utils/card/updateCardRequestBody.ts, src/types/card.ts
ChangeCardTypeUpdateCardType 명칭·내보내기 변경, 카드 타입 조정, updateCardRequestBody 유틸 추가(폼→UpdateCardType 변환).
폼 보조 컴포넌트 소소 변경
src/components/dashboard-detail/modal/CreateCardModal.tsx, src/components/dashboard-detail/modal/TagInput.tsx
Combobox 관련 값 타입 허용 범위 확장, TagInput에서 빈/공백 태그 필터링 적용.
UI 미세 조정
src/components/common/avatar/Avatar.tsx
Avatar 컨테이너 클래스에 shrink-0 추가(레이아웃 관련 CSS 변경).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

주의가 필요한 영역:

  • src/components/dashboard-detail/modal/ChangeCardModal.tsx — 폼 동기화, 이미지 파일 처리, 변경 감지·검증 로직
  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx — update mutation 흐름(onUpdateCard 호출, 상세 재요청, 모달 전환) 및 이미지 업로드 연동
  • Combobox 스택 (src/components/dashboard/combobox/*, src/context/comboboxContext.ts) — 타입 확장으로 인한 소비지 일관성(Trigger/Item/List) 및 컨텍스트 변경 검증
  • src/utils/card/updateCardRequestBody.tssrc/lib/apis/cards.ts — assignee/duedate/tags 포맷·폴백 로직 확인

Possibly related PRs

Suggested reviewers

  • jung518
  • yujin-fe
  • hjiasdq

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Out of Scope Changes check ❓ Inconclusive 대부분의 변경 사항이 모달 구현 및 API 연결과 관련이 있으나, Avatar의 shrink-0 클래스 추가와 CardDetailModalDesktop의 높이 조정은 핵심 요구사항과 직접적인 관련이 다소 떨어집니다. Avatar와 CardDetailModalDesktop의 변경 사항이 이슈 #278의 범위에 포함되는지 명확히 해 주세요. 필요시 별도 이슈로 분리하는 것을 고려해 주세요.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경 사항을 명확하게 설명합니다. 할 일 수정 모달 구현과 API 연결이라는 핵심 내용을 잘 전달합니다.
Description check ✅ Passed PR 설명이 템플릿 구조를 대부분 따르며 체크리스트를 완료했고 이슈 번호와 작업 내용을 포함합니다.
Linked Issues check ✅ Passed PR의 변경 사항들이 #278 이슈의 요구사항인 '할 일 생성 수정 모달 구현 API 연결'을 충족합니다. ChangeCardModal 컴포넌트 추가, CardDetailModal 업데이트, updateCard API 통합이 구현되었습니다.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/278/card-update

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

@coderabbitai coderabbitai bot requested a review from hjiasdq December 2, 2025 02:22
Copy link

@coderabbitai coderabbitai bot left a comment

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 (1)
src/components/dashboard/combobox/Combobox.tsx (1)

47-48: 불필요한 상태 제거

selectedNodesetSelectedNodeuseComboboxState 훅에서 생성되고 Combobox 컴포넌트에서 context에 포함되지만, ComboboxContextType 인터페이스에 정의되지 않아 자식 컴포넌트에서 접근할 수 없습니다. 실제 사용되지 않는 dead code이므로 다음에서 제거해주세요:

  • useComboboxState.ts에서 상태 정의 및 반환값 제거
  • Combobox.tsx 47-48줄에서 구조 분해 제거
  • Combobox.tsx 59-60줄 및 70줄의 context 포함 제거
🧹 Nitpick comments (6)
src/types/card.ts (1)

1-2: 타입 파일에서 컴포넌트 파일 임포트는 순환 의존성 위험이 있습니다.

UserComboboxValue 타입을 컴포넌트 파일(@/components/...)에서 임포트하고 있습니다. 타입 정의는 별도의 types 파일로 분리하여 순환 의존성을 방지하는 것이 좋습니다.

UserComboboxValue 타입을 src/types/combobox.ts 같은 별도 파일로 이동하는 것을 권장합니다:

// src/types/combobox.ts
export type UserComboboxValue = {
  id: number;
  nickname: string;
  profileImageUrl: string | null;
};

export type StatusComboboxValue = {
  id: number;
  title: string;
};
src/components/dashboard/combobox/ComboboxTrigger.tsx (2)

54-82: 타입 가드 사용으로 타입 안전성 개선 권장

'nickname' in selectedValue'title' in selectedValue로 타입을 구분하고 있습니다. 현재는 동작하지만, 추후 타입이 확장되면 예상치 못한 동작이 발생할 수 있습니다.

타입 가드 함수를 사용하면 더 명확해집니다:

// utils 또는 상단에 정의
const isUserValue = (value: unknown): value is UserComboboxValue =>
  typeof value === 'object' && value !== null && 'nickname' in value;

const isStatusValue = (value: unknown): value is StatusComboboxValue =>
  typeof value === 'object' && value !== null && 'title' in value;

80-82: 예상치 못한 값에 대한 fallback 처리 확인 필요

String(selectedValue)는 객체일 경우 "[object Object]"를 렌더링할 수 있습니다. 현재 UserComboboxValueStatusComboboxValue만 사용된다면 이 분기에 도달하지 않겠지만, 방어적 로깅을 추가하면 디버깅에 도움이 됩니다.

 } else {
+  console.warn('ComboboxTrigger: 알 수 없는 selectedValue 타입', selectedValue);
   valueContent = <span className='font-medium'>{String(selectedValue)}</span>;
 }
src/components/dashboard-detail/modal/CreateCardModal.tsx (2)

9-12: 불필요한 타입 import

StatusComboboxValue는 이 컴포넌트에서 사용되지 않습니다. 담당자(assigneeUser)만 처리하므로 UserComboboxValue만 필요합니다.

 import Combobox, {
-  type StatusComboboxValue,
   type UserComboboxValue,
 } from '@/components/dashboard/combobox/Combobox';

45-49: handleChange 타입 간소화 고려

현재 StatusComboboxValue가 union에 포함되어 있지만, 이 컴포넌트에서는 assigneeUser 필드만 Combobox를 사용하므로 실제로 StatusComboboxValue가 전달될 일이 없습니다. 타입 정확성을 높이려면 UserComboboxValue | null로 좁힐 수 있습니다.

src/components/dashboard-detail/modal/ChangeCardModal.tsx (1)

12-12: 미사용 import 확인

DUE_DATEIMAGE_URL이 import되었지만, 조건 비교에만 사용됩니다. 실제로 필요한지 검토해주세요.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb2d23d and b28c59d.

📒 Files selected for processing (16)
  • src/components/common/avatar/Avatar.tsx (1 hunks)
  • src/components/dashboard-detail/card/ColumnCardList.tsx (4 hunks)
  • src/components/dashboard-detail/card/DashboardCard.tsx (2 hunks)
  • src/components/dashboard-detail/modal/ChangeCardModal.tsx (1 hunks)
  • src/components/dashboard-detail/modal/CreateCardModal.tsx (2 hunks)
  • src/components/dashboard-detail/modal/TagInput.tsx (1 hunks)
  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (4 hunks)
  • src/components/dashboard/combobox/Combobox.tsx (1 hunks)
  • src/components/dashboard/combobox/ComboboxItem.tsx (1 hunks)
  • src/components/dashboard/combobox/ComboboxList.tsx (1 hunks)
  • src/components/dashboard/combobox/ComboboxTrigger.tsx (4 hunks)
  • src/context/comboboxContext.ts (1 hunks)
  • src/lib/apis/cards.ts (3 hunks)
  • src/pages/DashboardDetail.tsx (1 hunks)
  • src/types/card.ts (3 hunks)
  • src/utils/card/updateCardRequestBody.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

⚙️ CodeRabbit configuration file

**/*.ts: language: "ko-KR"

reviews:
instructions: |
우리 팀은 다음의 코드 스타일 가이드를 따릅니다:

[기본 규칙]
- 변수는 항상 const 우선 사용
- 파일명은 camelCase로 작성
- 필요 없는 코드는 즉시 제거 (미사용 변수/함수 금지)
- 함수를 제외한 유틸 파일은 default export 대신 named export 권장

[함수 네이밍 규칙]
- 모든 함수는 **동사 + 목적어** 형태로 작성
- 데이터 조회 함수: get / fetch / load
- 데이터 수정 함수: set / update / save / remove
- 이벤트 함수: handle 사용
- 계산 / 변환 함수: calculate / convert / parse / format
- 조건 검사 함수: is / has / can

[디렉토리 & 파일 구조]
- 디렉토리명은 kebab-case 사용
- 에셋(icon, image, logos)은 index 파일을 통한 일괄 export
- import 시 `@/assets/icons` 형태로 그룹 단위 import

[상수 / 환경 변수]
- SNAKE_CASE 사용
- 예: PAGE_SIZE, API_URL

[주석 & 문서화]
- TODO 주석 적극 활용 (급한 PR / 미구현 영역)
- TSDoc 사용 가능 (컴포넌트 설명, 사용 예시 추가)
- @param은 TypeScript 타입으로 설명하기 때문에 비사용

Files:

  • src/utils/card/updateCardRequestBody.ts
  • src/types/card.ts
  • src/lib/apis/cards.ts
  • src/context/comboboxContext.ts
**/*.tsx

⚙️ CodeRabbit configuration file

**/*.tsx: reviews:
instructions: |
우리 팀은 다음의 코드 스타일 가이드를 따릅니다:

[기본 규칙]
- 변수는 const 우선 사용
- 파일명은 PascalCase로 작성
- 컴포넌트는 default export를 사용
- 파일명과 컴포넌트명은 반드시 일치 (PascalCase)
- 불필요한 props는 전달하지 않기
- UI 관련 상태는 명확한 이름 사용 (isOpen, isLoading 등)

[함수 네이밍 규칙]
- 모든 함수는 **동사 + 목적어**
- UI 이벤트 핸들러는 handle* 사용 (handleClick, handleToggle 등)
- 내부 로직은 calculate / convert / format 등 의미 기반 네이밍 사용

[디렉토리 & 파일 구조]
- 디렉토리는 kebab-case
- 컴포넌트 파일은 PascalCase
- assets는 index 기반 export (Icons, Images, Logos 등)
- import 시 그룹 단위 import 사용

[상수 / 환경 변수]
- SNAKE_CASE 사용
- 예: PAGE_SIZE, API_URL

[함수 선언 규칙]
- 컴포넌트는 선언식 function Component() 형태
- 컴포넌트 내부의 로직 함수는 화살표 함수 사용
- 유틸성 컴포넌트가 아니면 default export

[주석 & 문서화]
- TODO 주석 사용 (미완성 코드 표시)
- TSDoc을 컴포넌트 단위로 활용
- 예시를 포함한 간단한 설명 추가

Files:

  • src/components/dashboard/combobox/Combobox.tsx
  • src/components/dashboard-detail/modal/TagInput.tsx
  • src/components/common/avatar/Avatar.tsx
  • src/components/dashboard/combobox/ComboboxItem.tsx
  • src/pages/DashboardDetail.tsx
  • src/components/dashboard-detail/modal/ChangeCardModal.tsx
  • src/components/dashboard-detail/card/ColumnCardList.tsx
  • src/components/dashboard/combobox/ComboboxList.tsx
  • src/components/dashboard/combobox/ComboboxTrigger.tsx
  • src/components/dashboard-detail/modal/CreateCardModal.tsx
  • src/components/dashboard-detail/card/DashboardCard.tsx
  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
🧠 Learnings (7)
📚 Learning: 2025-11-30T15:48:53.701Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 283
File: src/utils/card/createCardReqBody.ts:20-30
Timestamp: 2025-11-30T15:48:53.701Z
Learning: In src/utils/card/createCardReqBody.ts, the backend requires a default IMAGE_URL fallback (`imageUrl ?? IMAGE_URL`) when no image is provided, rather than accepting null. This is part of the server API contract, similar to how it requires assigneeUserId: 0 and tags: [''] as default values.

Applied to files:

  • src/utils/card/updateCardRequestBody.ts
  • src/lib/apis/cards.ts
📚 Learning: 2025-11-30T15:48:27.926Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 283
File: src/utils/card/createCardReqBody.ts:17-18
Timestamp: 2025-11-30T15:48:27.926Z
Learning: In src/utils/card/createCardReqBody.ts, the backend requires tags to be `['']` (an array containing one empty string) when no tags are provided, not an empty array `[]`. This is a server-side requirement.

Applied to files:

  • src/utils/card/updateCardRequestBody.ts
  • src/components/dashboard-detail/modal/TagInput.tsx
📚 Learning: 2025-11-30T15:48:27.926Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 283
File: src/utils/card/createCardReqBody.ts:17-18
Timestamp: 2025-11-30T15:48:27.926Z
Learning: In src/utils/card/createCardReqBody.ts, the backend accepts `assigneeUserId: 0` as a valid value when no assignee is selected and no currentUserId is provided. This is part of the server API contract.

Applied to files:

  • src/utils/card/updateCardRequestBody.ts
  • src/types/card.ts
📚 Learning: 2025-11-29T18:56:30.435Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 265
File: src/pages/DashboardDetail.tsx:106-116
Timestamp: 2025-11-29T18:56:30.435Z
Learning: In src/pages/DashboardDetail.tsx, column names (title field) are allowed to contain leading or trailing whitespace. Trimming whitespace from column names is not required.

Applied to files:

  • src/components/dashboard-detail/modal/TagInput.tsx
  • src/pages/DashboardDetail.tsx
  • src/components/dashboard-detail/card/ColumnCardList.tsx
  • src/components/dashboard-detail/card/DashboardCard.tsx
📚 Learning: 2025-11-22T16:39:21.177Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 93
File: src/components/dashboard/dropdown/DropdownMenuSearchTrigger.tsx:32-63
Timestamp: 2025-11-22T16:39:21.177Z
Learning: In the DropdownMenuSearchTrigger component (src/components/dashboard/dropdown/DropdownMenuSearchTrigger.tsx), a div element is intentionally used instead of a button because the component conditionally renders an input element when open, and nesting input inside button is invalid HTML.

Applied to files:

  • src/components/dashboard/combobox/ComboboxTrigger.tsx
📚 Learning: 2025-11-23T09:21:58.302Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 93
File: src/context/dropdownContext.ts:6-21
Timestamp: 2025-11-23T09:21:58.302Z
Learning: src/context/dropdownContext.ts의 BasicDropdownContextType과 SearchableDropdownContextType 인터페이스는 파일 내부에서만 사용되며, useDropdownContext.ts의 훅들이 useContext()를 통해 자동으로 타입을 추론하므로 export가 필요하지 않습니다.

Applied to files:

  • src/context/comboboxContext.ts
📚 Learning: 2025-12-01T01:09:10.680Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 287
File: src/lib/apis/Invitations.ts:31-35
Timestamp: 2025-12-01T01:09:10.680Z
Learning: In this codebase, mutation functions (POST/PUT/DELETE) return the full response object (`res`) instead of `res.data`, because `useMutation` internally processes `res.data`. Only GET methods return `res.data` directly.

Applied to files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
🧬 Code graph analysis (10)
src/utils/card/updateCardRequestBody.ts (4)
src/types/card.ts (2)
  • CardEditFormValue (39-47)
  • CardDetailResponse (10-22)
src/lib/apis/cards.ts (1)
  • UpdateCardType (3-11)
src/utils/formatDateTime.ts (1)
  • formatDueDate (19-33)
src/constants/requestCardData.ts (2)
  • DUE_DATE (2-2)
  • IMAGE_URL (3-4)
src/components/dashboard-detail/modal/TagInput.tsx (2)
src/components/dashboard/Tag.tsx (1)
  • Tag (37-48)
src/utils/avatar.ts (1)
  • getProfileColorForId (19-22)
src/components/common/avatar/Avatar.tsx (1)
src/utils/cn.ts (1)
  • cn (5-7)
src/components/dashboard/combobox/ComboboxItem.tsx (1)
src/components/dashboard/combobox/Combobox.tsx (2)
  • UserComboboxValue (8-13)
  • StatusComboboxValue (15-18)
src/components/dashboard-detail/card/ColumnCardList.tsx (2)
src/types/column.ts (2)
  • ColumnsData (1-8)
  • ColumnsResponse (10-13)
src/types/card.ts (1)
  • CardDetailResponse (10-22)
src/components/dashboard/combobox/ComboboxList.tsx (2)
src/components/dashboard/combobox/ComboboxItem.tsx (1)
  • ComboboxItemProps (8-11)
src/utils/combobox.ts (1)
  • matchSearch (7-12)
src/components/dashboard/combobox/ComboboxTrigger.tsx (2)
src/components/common/avatar/Avatar.tsx (1)
  • Avatar (25-44)
src/components/dashboard-detail/card/CardStatusBadge.tsx (1)
  • CardStatusBadge (8-19)
src/components/dashboard-detail/modal/CreateCardModal.tsx (1)
src/components/dashboard/combobox/Combobox.tsx (2)
  • UserComboboxValue (8-13)
  • StatusComboboxValue (15-18)
src/context/comboboxContext.ts (1)
src/components/dashboard/combobox/Combobox.tsx (2)
  • UserComboboxValue (8-13)
  • StatusComboboxValue (15-18)
src/components/dashboard-detail/card/DashboardCard.tsx (3)
src/types/card.ts (1)
  • CardDetailResponse (10-22)
src/types/column.ts (1)
  • ColumnsResponse (10-13)
src/types/members.ts (1)
  • MembersResponse (12-15)
🪛 GitHub Actions: Preview
src/components/dashboard-detail/modal/ChangeCardModal.tsx

[error] 92-92: TS2322: Type 'ColumnsData | undefined' is not assignable to type 'UserComboboxValue | StatusComboboxValue | null'. Type 'undefined' is not assignable to type 'UserComboboxValue | StatusComboboxValue | null'.


[error] 93-93: TS18047: 'colObj' is possibly 'null'.


[error] 93-93: TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'string | Assignee | null'. Type 'undefined' is not assignable to type 'string | Assignee | null'.


[error] 112-112: TS2322: Type '(value: string | Assignee | null) => void' is not assignable to type '(value: UserComboboxValue | StatusComboboxValue | null) => void'. Types of parameters 'value' are incompatible. Type 'StatusComboboxValue' is not assignable to type 'string | Assignee | null'.

🔇 Additional comments (20)
src/components/common/avatar/Avatar.tsx (1)

27-30: LGTM!

shrink-0 추가는 flex 컨테이너 내에서 Avatar가 축소되지 않도록 보장합니다. Combobox나 카드 목록 등 공간이 제한된 레이아웃에서 Avatar 크기가 일관되게 유지됩니다.

src/components/dashboard-detail/card/DashboardCard.tsx (2)

13-21: LGTM!

새로운 props가 명확한 타입과 함께 잘 정의되었습니다. onUpdateCard 핸들러 네이밍이 코딩 가이드라인(동사 + 목적어)을 따르고 있습니다.


80-91: LGTM!

CardDetailModal에 필요한 props가 올바르게 전달되고 있습니다. 모달이 조건부 렌더링되어 불필요한 마운트를 방지합니다.

src/types/card.ts (1)

30-47: assigneeUser 타입 불일치 확인 필요

CardInitialValueType.assigneeUserUserComboboxValue | null을 사용하고, CardEditFormValue.assigneeUserAssignee | null을 사용합니다. 의도된 설계라면 괜찮지만, 두 인터페이스 간 변환 시 혼란을 야기할 수 있습니다.

두 타입의 용도가 다른지 확인해주세요:

  • CardInitialValueType: Combobox에서 사용하는 초기값
  • CardEditFormValue: API 요청에 사용하는 폼 값

의도적인 분리라면 주석으로 용도를 명시하면 좋겠습니다.

src/components/dashboard-detail/modal/TagInput.tsx (1)

51-58: LGTM!

빈 문자열 태그를 필터링하여 UI에 표시하지 않는 로직이 적절합니다. 백엔드에서 ['']를 요구하는 경우에도 UI에서는 깔끔하게 처리됩니다.

src/components/dashboard/combobox/ComboboxTrigger.tsx (1)

59-73: LGTM!

Avatar 컴포넌트에 전달되는 user 객체가 Assignee 타입 구조와 일치합니다. shrink-0 클래스가 적용된 Avatar와 함께 flex 레이아웃에서 안정적으로 렌더링됩니다.

src/components/dashboard/combobox/ComboboxList.tsx (1)

13-25: LGTM!

nicknametitle 속성을 체크하여 UserComboboxValueStatusComboboxValue를 적절히 처리하는 필터링 로직입니다. in 연산자를 통한 타입 좁히기가 잘 적용되었습니다.

src/pages/DashboardDetail.tsx (1)

182-192: LGTM!

카드 수정 모달에서 상태(컬럼) 선택을 지원하기 위해 columnListData prop을 추가하여 ColumnCardList로 전달하는 변경입니다. 데이터 흐름이 명확합니다.

src/lib/apis/cards.ts (1)

3-15: LGTM!

ChangeCardTypeUpdateCardType으로 명확하게 리네이밍하고 export했습니다. CreateCardTypeUpdateCardType을 확장하는 구조가 적절합니다.

src/context/comboboxContext.ts (2)

2-5: LGTM!

Combobox 컴포넌트에서 UserComboboxValueStatusComboboxValue 타입을 가져와 context 타입을 확장하는 방식이 적절합니다.


11-12: Union 타입 적용이 적절합니다.

selectedValueUserComboboxValue | StatusComboboxValue | null 타입을 사용하여 사용자 선택과 상태 선택 두 가지 Combobox 케이스를 모두 처리할 수 있게 되었습니다.

src/components/dashboard-detail/card/ColumnCardList.tsx (4)

11-14: LGTM!

타입 import가 적절하게 업데이트되었습니다. CardDetailResponseColumnsResponse 추가로 카드 수정 기능을 지원합니다.


17-23: Props 인터페이스 확장이 적절합니다.

columnListData prop 추가로 하위 컴포넌트에서 컬럼 정보를 활용할 수 있게 되었습니다.


111-121: LGTM!

handleUpdateCard 함수가 불변성을 유지하면서 카드를 업데이트합니다. map을 사용한 immutable update 패턴이 적절합니다.


153-161: LGTM!

DashboardCardonUpdateCard, memberData, columnListData props를 전달하여 카드 수정 플로우를 지원합니다.

src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (5)

6-19: LGTM!

카드 수정 기능을 위한 import가 적절하게 추가되었습니다. ChangeCardModal, updateCard API, 관련 타입 및 유틸리티 함수들이 포함되었습니다.


35-44: Props 인터페이스가 명확합니다.

CardDetailModalProps 인터페이스에 onUpdateCard, memberData, columnListData가 추가되어 카드 수정 플로우를 지원합니다.


59-60: 두 개의 모달 컨트롤러 패턴이 적절합니다.

editModaldetailModal을 분리하여 상세 보기와 수정 모드를 독립적으로 관리합니다. 고유한 modal name을 사용하여 충돌을 방지합니다.


70-85: Mutation 정의가 적절합니다.

deleteCardMutationupdateCardMutation이 적절하게 분리되어 있습니다. updateCardMutationonSuccess에서 refetch와 모달 전환 로직이 올바르게 구현되었습니다.


166-183: ChangeCardModal 렌더링이 적절합니다.

editModal.isOpencardDetailQuery.data 조건을 통해 데이터가 준비된 상태에서만 수정 모달을 렌더링합니다. initialValue로 기존 카드 데이터를 전달하는 패턴이 올바릅니다.

@github-actions
Copy link

github-actions bot commented Dec 2, 2025

@aahreum
Copy link
Member Author

aahreum commented Dec 2, 2025

테스트 진행중

  • 상태(컬럼) 바로 반영되는지

    → 컬럼 값이 안바뀜

  • 담당자 바로 바뀌는지

  • 제목 바로 바뀌는지

  • 설명 바로 바뀌는지

  • 마감일 바로 바뀌는지

  • 태그 바로 바뀌는지

  • 이미지 바로 바뀌는지

    → 이미지가 바로 반영 안됨

    → 이미지 삭제는 잘됨

    → 새로 추가했을때, 변경했을때 안보임

  • 기존 값이 한번 더 렌더링되었다가 모달이 닫힘 -> 해결

  • 담당자 폴백 아이콘 컬러가 바뀜

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/components/dashboard-detail/modal/ChangeCardModal.tsx (1)

97-99: 컴포넌트가 암시적으로 undefined 반환

if (!columnListData) return;undefined를 반환합니다. React 컴포넌트는 렌더링하지 않을 때 명시적으로 null을 반환해야 합니다.

다음과 같이 수정하세요:

 if (!columnListData) {
-  return;
+  return null;
 }
🧹 Nitpick comments (1)
src/components/dashboard-detail/modal/ChangeCardModal.tsx (1)

27-34: 컴포넌트 문서화 추가 권장

코딩 가이드라인에 따라 컴포넌트 단위로 TSDoc을 작성하여 props와 사용법을 설명하는 것이 좋습니다.

다음과 같은 TSDoc 추가를 고려해보세요:

+/**
+ * 카드(할 일) 편집 모달 컴포넌트
+ * 
+ * @example
+ * <ChangeCardModal
+ *   memberData={memberData}
+ *   modalName="editCard"
+ *   initialValue={cardData}
+ *   columnListData={columns}
+ *   serverErrorMessage={null}
+ *   onSubmit={handleCardUpdate}
+ * />
+ */
 export default function ChangeCardModal({
   memberData,
   modalName,

Based on coding guidelines requiring TSDoc for components.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b28c59d and 68eabef.

📒 Files selected for processing (2)
  • src/components/dashboard-detail/modal/ChangeCardModal.tsx (1 hunks)
  • src/utils/card/updateCardRequestBody.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/utils/card/updateCardRequestBody.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.tsx

⚙️ CodeRabbit configuration file

**/*.tsx: reviews:
instructions: |
우리 팀은 다음의 코드 스타일 가이드를 따릅니다:

[기본 규칙]
- 변수는 const 우선 사용
- 파일명은 PascalCase로 작성
- 컴포넌트는 default export를 사용
- 파일명과 컴포넌트명은 반드시 일치 (PascalCase)
- 불필요한 props는 전달하지 않기
- UI 관련 상태는 명확한 이름 사용 (isOpen, isLoading 등)

[함수 네이밍 규칙]
- 모든 함수는 **동사 + 목적어**
- UI 이벤트 핸들러는 handle* 사용 (handleClick, handleToggle 등)
- 내부 로직은 calculate / convert / format 등 의미 기반 네이밍 사용

[디렉토리 & 파일 구조]
- 디렉토리는 kebab-case
- 컴포넌트 파일은 PascalCase
- assets는 index 기반 export (Icons, Images, Logos 등)
- import 시 그룹 단위 import 사용

[상수 / 환경 변수]
- SNAKE_CASE 사용
- 예: PAGE_SIZE, API_URL

[함수 선언 규칙]
- 컴포넌트는 선언식 function Component() 형태
- 컴포넌트 내부의 로직 함수는 화살표 함수 사용
- 유틸성 컴포넌트가 아니면 default export

[주석 & 문서화]
- TODO 주석 사용 (미완성 코드 표시)
- TSDoc을 컴포넌트 단위로 활용
- 예시를 포함한 간단한 설명 추가

Files:

  • src/components/dashboard-detail/modal/ChangeCardModal.tsx
🧠 Learnings (1)
📚 Learning: 2025-11-29T18:56:30.435Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 265
File: src/pages/DashboardDetail.tsx:106-116
Timestamp: 2025-11-29T18:56:30.435Z
Learning: In src/pages/DashboardDetail.tsx, column names (title field) are allowed to contain leading or trailing whitespace. Trimming whitespace from column names is not required.

Applied to files:

  • src/components/dashboard-detail/modal/ChangeCardModal.tsx
🧬 Code graph analysis (1)
src/components/dashboard-detail/modal/ChangeCardModal.tsx (5)
src/types/members.ts (1)
  • MembersResponse (12-15)
src/types/card.ts (2)
  • CardEditFormValue (39-47)
  • Assignee (3-8)
src/types/column.ts (1)
  • ColumnsResponse (10-13)
src/components/dashboard/combobox/Combobox.tsx (2)
  • StatusComboboxValue (15-18)
  • Combobox (46-80)
src/constants/requestCardData.ts (2)
  • DUE_DATE (2-2)
  • IMAGE_URL (3-4)
🔇 Additional comments (6)
src/components/dashboard-detail/modal/ChangeCardModal.tsx (6)

45-47: LGTM!

단순 문자열 필드 업데이트를 위한 핸들러로, 깔끔하게 구현되었습니다.


49-70: LGTM!

Combobox 변경사항을 처리하는 로직이 타입 좁히기(type narrowing)를 통해 안전하게 구현되었습니다. 이전 리뷰에서 지적된 타입 호환성 문제를 적절하게 해결했습니다.


72-86: LGTM!

변경 감지 로직이 모든 편집 가능한 필드를 포괄적으로 검사하고 있으며, 필수 필드(제목, 설명)의 유효성 검증도 적절합니다.


88-95: LGTM!

비동기 제출 처리와 에러 핸들링이 적절하게 구현되었습니다.


177-178: LGTM!

DUE_DATE 상수를 사용한 기본값 처리 로직이 적절합니다. 기본값일 때 UI에 빈 문자열을 표시하여 사용자 경험을 개선했습니다.


209-213: LGTM!

서버 에러와 로컬 에러를 우선순위에 따라 표시하는 로직이 명확하게 구현되었습니다.

@github-actions
Copy link

github-actions bot commented Dec 2, 2025

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/components/dashboard-detail/modal/ChangeCardModal.tsx (2)

52-73: 타입 단언(type assertion) 사용

이전 리뷰에서 언급된 대로, handleComboboxChange 함수에서 타입 단언(as StatusComboboxValue | null, as UserComboboxValue | null)이 사용되고 있습니다. 기능적으로는 작동하지만, 타입 안전성을 위해 향후 리팩토링을 고려해 주세요.


144-152: Member 타입과 UserComboboxValue 타입 불일치

이전 리뷰에서 언급된 대로, Combobox.Itemvalue prop에 Member 객체를 직접 전달하고 있습니다. 타입 호환성을 위해 필요한 속성만 추출하여 전달하는 것이 권장됩니다.

- <Combobox.Item key={m.id} value={m}>
+ <Combobox.Item key={m.id} value={{ id: m.id, userId: m.userId, nickname: m.nickname, profileImageUrl: m.profileImageUrl }}>
🧹 Nitpick comments (2)
src/components/dashboard/combobox/ComboboxItem.tsx (1)

16-16: UserComboboxValueuserId 필드가 비교에서 누락됨현재 로직은 이전 리뷰에서 제안한 selectedValue?.id !== undefined 검증을 추가했지만, UserComboboxValue의 경우 id가 없고 userId만 있는 경우를 처리하지 못합니다.

ComboboxTrigger.tsx(Line 64)에서는 selectedValue.userId ?? selectedValue.id로 양쪽 필드를 모두 고려하는데, 이 비교 로직은 id만 확인합니다. 타입 일관성을 위해 고려해 주세요:

- const isSelected = selectedValue?.id !== undefined && selectedValue.id === value.id;
+ const isSelected = (() => {
+   if (!selectedValue) return false;
+   if ('nickname' in selectedValue && 'nickname' in value) {
+     const selId = selectedValue.userId ?? selectedValue.id;
+     const valId = (value as typeof selectedValue).userId ?? value.id;
+     return selId !== undefined && selId === valId;
+   }
+   return selectedValue.id !== undefined && selectedValue.id === value.id;
+ })();

또는 UserComboboxValueStatusComboboxValue에 discriminated union 패턴(kind 같은 공통 리터럴 프로퍼티)을 도입하면 타입 가드가 더 안전해집니다.

src/components/dashboard-detail/modal/ChangeCardModal.tsx (1)

91-98: 제출 전 에러 상태 초기화 고려

handleSubmit 호출 시 이전 에러 메시지가 남아있을 수 있습니다. 제출 시작 시 에러 상태를 초기화하면 UX가 개선됩니다.

  const handleSubmit = async () => {
+   setErrorMessage('');
    try {
      await onSubmit(formValue, imageFile);
    } catch (err) {
      const errorMsg = err instanceof Error ? err.message : '카드 수정 중 오류가 발생했습니다.';
      setErrorMessage(errorMsg);
    }
  };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68eabef and 2b01193.

📒 Files selected for processing (3)
  • src/components/dashboard-detail/modal/ChangeCardModal.tsx (1 hunks)
  • src/components/dashboard/combobox/ComboboxItem.tsx (1 hunks)
  • src/components/dashboard/combobox/ComboboxTrigger.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.tsx

⚙️ CodeRabbit configuration file

**/*.tsx: reviews:
instructions: |
우리 팀은 다음의 코드 스타일 가이드를 따릅니다:

[기본 규칙]
- 변수는 const 우선 사용
- 파일명은 PascalCase로 작성
- 컴포넌트는 default export를 사용
- 파일명과 컴포넌트명은 반드시 일치 (PascalCase)
- 불필요한 props는 전달하지 않기
- UI 관련 상태는 명확한 이름 사용 (isOpen, isLoading 등)

[함수 네이밍 규칙]
- 모든 함수는 **동사 + 목적어**
- UI 이벤트 핸들러는 handle* 사용 (handleClick, handleToggle 등)
- 내부 로직은 calculate / convert / format 등 의미 기반 네이밍 사용

[디렉토리 & 파일 구조]
- 디렉토리는 kebab-case
- 컴포넌트 파일은 PascalCase
- assets는 index 기반 export (Icons, Images, Logos 등)
- import 시 그룹 단위 import 사용

[상수 / 환경 변수]
- SNAKE_CASE 사용
- 예: PAGE_SIZE, API_URL

[함수 선언 규칙]
- 컴포넌트는 선언식 function Component() 형태
- 컴포넌트 내부의 로직 함수는 화살표 함수 사용
- 유틸성 컴포넌트가 아니면 default export

[주석 & 문서화]
- TODO 주석 사용 (미완성 코드 표시)
- TSDoc을 컴포넌트 단위로 활용
- 예시를 포함한 간단한 설명 추가

Files:

  • src/components/dashboard-detail/modal/ChangeCardModal.tsx
  • src/components/dashboard/combobox/ComboboxTrigger.tsx
  • src/components/dashboard/combobox/ComboboxItem.tsx
🧠 Learnings (2)
📚 Learning: 2025-11-29T18:56:30.435Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 265
File: src/pages/DashboardDetail.tsx:106-116
Timestamp: 2025-11-29T18:56:30.435Z
Learning: In src/pages/DashboardDetail.tsx, column names (title field) are allowed to contain leading or trailing whitespace. Trimming whitespace from column names is not required.

Applied to files:

  • src/components/dashboard-detail/modal/ChangeCardModal.tsx
📚 Learning: 2025-11-22T16:39:21.177Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 93
File: src/components/dashboard/dropdown/DropdownMenuSearchTrigger.tsx:32-63
Timestamp: 2025-11-22T16:39:21.177Z
Learning: In the DropdownMenuSearchTrigger component (src/components/dashboard/dropdown/DropdownMenuSearchTrigger.tsx), a div element is intentionally used instead of a button because the component conditionally renders an input element when open, and nesting input inside button is invalid HTML.

Applied to files:

  • src/components/dashboard/combobox/ComboboxTrigger.tsx
🧬 Code graph analysis (2)
src/components/dashboard/combobox/ComboboxTrigger.tsx (2)
src/components/common/avatar/Avatar.tsx (1)
  • Avatar (25-44)
src/components/dashboard-detail/card/CardStatusBadge.tsx (1)
  • CardStatusBadge (8-19)
src/components/dashboard/combobox/ComboboxItem.tsx (1)
src/components/dashboard/combobox/Combobox.tsx (2)
  • UserComboboxValue (8-13)
  • StatusComboboxValue (15-18)
🔇 Additional comments (7)
src/components/dashboard/combobox/ComboboxTrigger.tsx (2)

54-82: valueContent 조건부 렌더링 로직 검토

전반적인 구조는 잘 구성되어 있습니다. 다만 몇 가지 검토가 필요합니다:

  1. Line 64: UserComboboxValue에서 iduserId 모두 optional입니다. selectedValue.userId ?? selectedValue.idundefined를 반환할 수 있어 Avatar 컴포넌트에 전달되는 user.idundefined가 될 수 있습니다.

  2. Line 80-81: fallback 케이스에서 String(selectedValue)는 예상치 못한 객체 타입에 대해 "[object Object]"를 출력할 수 있습니다. 이 케이스가 실제로 발생할 수 있는지, 아니면 타입 가드로 방어할 수 있는지 확인이 필요합니다.

   } else if ('nickname' in selectedValue) {
     valueContent = (
       <div className='flex items-center gap-2'>
         <Avatar
           size='s'
           user={{
-            id: selectedValue.userId ?? selectedValue.id,
+            id: selectedValue.userId ?? selectedValue.id ?? 0,
             nickname: selectedValue.nickname,
             profileImageUrl: selectedValue.profileImageUrl,
           }}>

105-124: 닫힌 상태의 렌더링 구조 LGTM

isOpenfalse일 때 valueContent를 표시하고, 스크린 리더용 숨겨진 input을 유지하는 구조가 적절합니다. sr-only 클래스와 포커스 핸들링도 접근성 측면에서 잘 구현되어 있습니다.

src/components/dashboard/combobox/ComboboxItem.tsx (1)

18-26: handleListClick 토글 로직 LGTM

선택/해제 토글 후 setIsOpen(false)setSearchQuery('') 호출로 상태를 깔끔하게 정리합니다. 코딩 가이드라인의 handle* 네이밍 컨벤션도 준수했습니다.

src/components/dashboard-detail/modal/ChangeCardModal.tsx (4)

75-89: LGTM!

isDisabled 메모이제이션이 모든 필드 변경 감지와 유효성 검사를 포괄적으로 처리하고 있습니다. 태그 비교에 JSON.stringify를 사용하는 것은 작은 배열에서는 적절합니다.


100-102: LGTM!

이전 리뷰에서 지적된 암시적 undefined 반환 문제가 null 반환으로 올바르게 수정되었습니다.


219-226: LGTM!

모달 푸터 버튼 구현이 적절합니다. isDisabled 상태와 연동된 제출 버튼 비활성화 및 취소 버튼의 모달 닫기 처리가 올바르게 구현되었습니다.


202-208: The current code correctly handles the null case for defaultImageUrl. The ImageUpload component is explicitly typed to accept string | null and properly handles null values using a truthy check on line 53 (if (defaultImageUrl)). Passing null when defaultImageUrl is neither IMAGE_URL nor a valid string is the intended behavior, as the component will render without a preview image. No modification is needed.

Likely an incorrect or invalid review comment.

@github-actions
Copy link

github-actions bot commented Dec 2, 2025

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (3)
src/components/dashboard-detail/modal/ChangeCardModal.tsx (2)

140-148: Member 객체를 Combobox.Item에 직접 전달 시 타입 불일치 가능성

Combobox.Itemvalue prop은 UserComboboxValue | StatusComboboxValue를 기대합니다. Member 타입이 UserComboboxValue와 완전히 호환되는지 확인이 필요합니다. Member에 추가 속성이 있다면 명시적으로 필요한 속성만 전달하세요.

<Combobox.Item 
  key={m.id} 
  value={{ id: m.id, userId: m.userId, nickname: m.nickname, profileImageUrl: m.profileImageUrl }}>
#!/bin/bash
# Member와 UserComboboxValue 타입 비교
echo "=== Member type ==="
rg -nA 10 'export interface Member\b' src/types/members.ts

echo -e "\n=== UserComboboxValue type ==="
rg -nA 10 'export interface UserComboboxValue' src/components/dashboard/combobox/Combobox.tsx

39-42: initialValue 변경 시 formValuedefaultImageUrl이 동기화되지 않음

모달이 다른 카드로 재사용될 때 initialValue가 변경되어도 formValuedefaultImageUrl이 갱신되지 않습니다. PR 코멘트에서 언급된 "기존 값이 한 번 이중 렌더링 후 모달 닫힘" 문제와 관련될 수 있습니다.

+ import { useEffect, useMemo, useState } from 'react';
- import { useMemo, useState } from 'react';

  const [formValue, setFormValue] = useState(initialValue);
  const [imageFile, setImageFile] = useState<File | null>(null);
  const [defaultImageUrl, setDefaultImageUrl] = useState<string | null>(initialValue.imageUrl);
  const [errorMessage, setErrorMessage] = useState('');

+ useEffect(() => {
+   setFormValue(initialValue);
+   setDefaultImageUrl(initialValue.imageUrl);
+   setImageFile(null);
+   setErrorMessage('');
+ }, [initialValue]);
src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (1)

112-126: updateCardRequestBody 함수의 4번째 인자가 잘못되었습니다

updateCardRequestBody의 4번째 매개변수는 currentUserId로, 담당자(assignee) 폴백에 사용됩니다. 현재 formValue.columnId(컬럼 ID)를 전달하고 있어 담당자 할당 로직이 잘못될 수 있습니다.

src/utils/card/updateCardRequestBody.ts 시그니처:

updateCardRequestBody(formValue, cardDetailData, imageUrl, currentUserId?: number)
// assigneeUserId = formValue.assigneeUser?.userId ?? cardDetailData?.assignee.id ?? currentUserId ?? 0
  const handleSubmitUpdateCard = async (
    formValue: CardEditFormValue,
    imageFile: File | null
  ): Promise<void> => {
    const uploadedImageUrl = await uploadCardImage(formValue.columnId, imageFile);

    const body = updateCardRequestBody(
      formValue,
      cardDetailQuery.data,
      uploadedImageUrl,
-     formValue.columnId
+     undefined // 또는 현재 로그인한 사용자 ID를 전달
    );

    await updateCardMutation.mutate({ id: cardId, body });
  };

formValue.assigneeUser가 이미 선택된 담당자를 포함하고 있으므로, 폴백으로 현재 사용자 ID가 필요하다면 auth context에서 가져오세요. 폴백이 불필요하다면 undefined를 전달하세요.

🧹 Nitpick comments (2)
src/components/dashboard-detail/modal/ChangeCardModal.tsx (1)

48-69: handleComboboxChange 타입 단언 사용 최소화 권장

현재 as StatusComboboxValue | nullas UserComboboxValue | null 타입 단언이 사용되고 있습니다. 런타임에서 타입 안전성을 보장하려면 타입 가드를 사용하는 것이 좋습니다.

 const handleComboboxChange = (
   key: 'columnId' | 'assigneeUser',
   value: StatusComboboxValue | UserComboboxValue | null
 ) => {
   setFormValue((prev) => {
     if (key === 'columnId') {
-      const newValue = value as StatusComboboxValue | null;
+      const newValue = value && 'title' in value ? value : null;
       return {
         ...prev,
         columnId: newValue?.id ?? prev.columnId,
       };
     }
     if (key === 'assigneeUser') {
-      const newValue = value as UserComboboxValue | null;
+      const newValue = value && 'userId' in value ? value : null;
       return {
         ...prev,
         assigneeUser: newValue,
       };
     }
     return prev;
   });
 };
src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (1)

102-105: 모달 전환 시 handleModalCloseAll 사용 확인

handleCardEdit에서 detailModal.handleModalCloseAll()을 호출하면 URL 검색 파라미터의 모든 모달이 닫힙니다. 다른 모달이 동시에 열려 있을 경우 의도치 않게 닫힐 수 있습니다. detailModal.handleModalClose()로 현재 모달만 닫는 것이 더 안전할 수 있습니다.

  const handleCardEdit = () => {
-   detailModal.handleModalCloseAll();
+   detailModal.handleModalClose();
    editModal.handleModalOpen();
  };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b01193 and 2359d6e.

📒 Files selected for processing (2)
  • src/components/dashboard-detail/modal/ChangeCardModal.tsx (1 hunks)
  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.tsx

⚙️ CodeRabbit configuration file

**/*.tsx: reviews:
instructions: |
우리 팀은 다음의 코드 스타일 가이드를 따릅니다:

[기본 규칙]
- 변수는 const 우선 사용
- 파일명은 PascalCase로 작성
- 컴포넌트는 default export를 사용
- 파일명과 컴포넌트명은 반드시 일치 (PascalCase)
- 불필요한 props는 전달하지 않기
- UI 관련 상태는 명확한 이름 사용 (isOpen, isLoading 등)

[함수 네이밍 규칙]
- 모든 함수는 **동사 + 목적어**
- UI 이벤트 핸들러는 handle* 사용 (handleClick, handleToggle 등)
- 내부 로직은 calculate / convert / format 등 의미 기반 네이밍 사용

[디렉토리 & 파일 구조]
- 디렉토리는 kebab-case
- 컴포넌트 파일은 PascalCase
- assets는 index 기반 export (Icons, Images, Logos 등)
- import 시 그룹 단위 import 사용

[상수 / 환경 변수]
- SNAKE_CASE 사용
- 예: PAGE_SIZE, API_URL

[함수 선언 규칙]
- 컴포넌트는 선언식 function Component() 형태
- 컴포넌트 내부의 로직 함수는 화살표 함수 사용
- 유틸성 컴포넌트가 아니면 default export

[주석 & 문서화]
- TODO 주석 사용 (미완성 코드 표시)
- TSDoc을 컴포넌트 단위로 활용
- 예시를 포함한 간단한 설명 추가

Files:

  • src/components/dashboard-detail/modal/ChangeCardModal.tsx
  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
🧠 Learnings (4)
📚 Learning: 2025-11-29T18:56:30.435Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 265
File: src/pages/DashboardDetail.tsx:106-116
Timestamp: 2025-11-29T18:56:30.435Z
Learning: In src/pages/DashboardDetail.tsx, column names (title field) are allowed to contain leading or trailing whitespace. Trimming whitespace from column names is not required.

Applied to files:

  • src/components/dashboard-detail/modal/ChangeCardModal.tsx
📚 Learning: 2025-11-30T15:48:53.701Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 283
File: src/utils/card/createCardReqBody.ts:20-30
Timestamp: 2025-11-30T15:48:53.701Z
Learning: In src/utils/card/createCardReqBody.ts, the backend requires a default IMAGE_URL fallback (`imageUrl ?? IMAGE_URL`) when no image is provided, rather than accepting null. This is part of the server API contract, similar to how it requires assigneeUserId: 0 and tags: [''] as default values.

Applied to files:

  • src/components/dashboard-detail/modal/ChangeCardModal.tsx
  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
📚 Learning: 2025-11-30T15:48:27.926Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 283
File: src/utils/card/createCardReqBody.ts:17-18
Timestamp: 2025-11-30T15:48:27.926Z
Learning: In src/utils/card/createCardReqBody.ts, the backend accepts `assigneeUserId: 0` as a valid value when no assignee is selected and no currentUserId is provided. This is part of the server API contract.

Applied to files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
📚 Learning: 2025-12-01T01:09:10.680Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 287
File: src/lib/apis/Invitations.ts:31-35
Timestamp: 2025-12-01T01:09:10.680Z
Learning: In this codebase, mutation functions (POST/PUT/DELETE) return the full response object (`res`) instead of `res.data`, because `useMutation` internally processes `res.data`. Only GET methods return `res.data` directly.

Applied to files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
🧬 Code graph analysis (2)
src/components/dashboard-detail/modal/ChangeCardModal.tsx (4)
src/types/members.ts (1)
  • MembersResponse (12-15)
src/types/card.ts (1)
  • CardEditFormValue (39-47)
src/types/column.ts (1)
  • ColumnsResponse (10-13)
src/constants/requestCardData.ts (2)
  • DUE_DATE (2-2)
  • IMAGE_URL (3-4)
src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (9)
src/types/card.ts (2)
  • CardDetailResponse (10-22)
  • CardEditFormValue (39-47)
src/types/members.ts (1)
  • MembersResponse (12-15)
src/types/column.ts (1)
  • ColumnsResponse (10-13)
src/hooks/useModal.ts (1)
  • useModal (4-50)
src/lib/apis/cards.ts (3)
  • deleteCard (63-66)
  • UpdateCardType (3-11)
  • updateCard (51-54)
src/utils/card/uploadCardImage.ts (1)
  • uploadCardImage (4-21)
src/utils/card/updateCardRequestBody.ts (1)
  • updateCardRequestBody (6-28)
src/components/common/modal/ModalPortal.tsx (1)
  • ModalPortal (3-6)
src/components/dashboard-detail/modal/ChangeCardModal.tsx (1)
  • ChangeCardModal (30-226)
🔇 Additional comments (8)
src/components/dashboard-detail/modal/ChangeCardModal.tsx (5)

71-85: isDisabled 로직 구현 적절함

변경 감지와 필수 필드 유효성 검사가 잘 구현되어 있습니다. useMemo를 사용하여 불필요한 재계산을 방지하고, JSON.stringify로 태그 배열 비교도 적절합니다.


87-94: 에러 핸들링 구현 적절함

try-catch로 에러를 캡처하고 사용자에게 표시하는 방식이 적절합니다.


96-98: null 반환으로 수정됨 - 확인 완료

이전 리뷰에서 지적된 return; (undefined 반환) 문제가 return null;로 수정되었습니다.


111-127: 컬럼 Combobox 구현 개선됨

이전 리뷰에서 지적된 find()undefined 반환 문제가 ?? null로 해결되었고, map()으로 필요한 속성만 추출하여 StatusComboboxValue 타입에 맞게 전달하고 있습니다.


175-182: 마감일 입력 처리 확인 필요

DUE_DATE 상수('1000-10-10 10:00')와 비교하여 빈 문자열로 표시하는 로직이 있습니다. 사용자가 마감일을 지우고 저장할 때 DUE_DATE가 다시 설정되는지, 또는 실제 null/빈 값으로 저장되는지 확인이 필요합니다.

src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (3)

70-85: mutation 구현 적절함

deleteCardMutationupdateCardMutation이 잘 구현되어 있습니다. onSuccess 콜백에서 부모 컴포넌트 알림, 데이터 리페치, 모달 닫기가 적절히 처리됩니다.


166-183: ChangeCardModal 렌더링 및 initialValue 구성 적절함

cardDetailQuery.data에서 필요한 필드를 추출하여 initialValue를 구성하고 있습니다. serverErrorMessage로 mutation 에러도 적절히 전달됩니다.


128-164: 상세 모달 렌더링 로직 검토

detailModal.isOpen 조건으로 렌더링을 제어하고 있습니다. PR 코멘트에서 언급된 "기존 값 이중 렌더링" 문제가 모달 전환 타이밍과 관련될 수 있습니다. handleCardEdit에서 handleModalCloseAll 후 바로 editModal.handleModalOpen을 호출하므로, 비동기 상태 업데이트로 인해 일시적인 깜빡임이 발생할 수 있습니다.

@github-actions
Copy link

github-actions bot commented Dec 2, 2025

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (2)

60-63: 카드 편집 진입 시 handleModalCloseAll 사용으로 다른 모달까지 함께 닫힐 수 있습니다

useModal 구현을 보면 handleModalCloseAll은 URL query 전체를 비우기 때문에, handleCardEdit에서 이를 호출하면 동일한 URL 기반으로 열린 다른 모달들도 전부 닫히는 부작용이 있을 수 있습니다.

const handleCardEdit = () => {
  detailModal.handleModalCloseAll();
  editModal.handleModalOpen();
};

단순히 카드 상세 모달만 닫으면 되므로, 아래처럼 handleModalClose만 사용하는 쪽이 의도에 더 가깝고, 다른 모달과의 독립성도 보장됩니다.

-  const handleCardEdit = () => {
-    detailModal.handleModalCloseAll();
-    editModal.handleModalOpen();
-  };
+  const handleCardEdit = () => {
+    detailModal.handleModalClose();
+    editModal.handleModalOpen();
+  };

이렇게 바꾸면, 동일한 URL 쿼리 기반 모달 시스템을 쓰더라도 다른 모달 상태에 영향을 주지 않고 디테일 → 수정 모달로 자연스럽게 전환됩니다.

Also applies to: 104-107


168-181: 편집 모달 초기 column 값은 cardDetailQuery.data.columnId를 사용하는 편이 더 일관됩니다

현재 ChangeCardModalinitialValue.columnId는 부모에서 받은 columnId prop을 그대로 사용하고 있습니다.

initialValue={{
  columnId,
  assigneeUser: cardDetailQuery.data.assignee,
  // ...
}}

반면 나머지 필드들은 모두 cardDetailQuery.data를 사용하고 있고, 실제 최신 카드 상태의 단일 소스 오브 트루스도 cardDetailQuery.data이기 때문에:

-          initialValue={{
-            columnId,
+          initialValue={{
+            columnId: cardDetailQuery.data.columnId,
             assigneeUser: cardDetailQuery.data.assignee,
             title: cardDetailQuery.data.title,
             description: cardDetailQuery.data.description,
             dueDate: cardDetailQuery.data.dueDate,
             tags: cardDetailQuery.data.tags,
             imageUrl: cardDetailQuery.data.imageUrl,
           }}

처럼 맞춰두면,

  • 상위에서 columnId 상태 업데이트가 한 템포 늦더라도
  • 편집 모달을 다시 열었을 때 항상 서버 기준 최신 컬럼 값이 반영

되어, “컬럼 값이 안 바뀜”처럼 보이는 상황을 줄일 수 있습니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2359d6e and 157ab3e.

📒 Files selected for processing (1)
  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.tsx

⚙️ CodeRabbit configuration file

**/*.tsx: reviews:
instructions: |
우리 팀은 다음의 코드 스타일 가이드를 따릅니다:

[기본 규칙]
- 변수는 const 우선 사용
- 파일명은 PascalCase로 작성
- 컴포넌트는 default export를 사용
- 파일명과 컴포넌트명은 반드시 일치 (PascalCase)
- 불필요한 props는 전달하지 않기
- UI 관련 상태는 명확한 이름 사용 (isOpen, isLoading 등)

[함수 네이밍 규칙]
- 모든 함수는 **동사 + 목적어**
- UI 이벤트 핸들러는 handle* 사용 (handleClick, handleToggle 등)
- 내부 로직은 calculate / convert / format 등 의미 기반 네이밍 사용

[디렉토리 & 파일 구조]
- 디렉토리는 kebab-case
- 컴포넌트 파일은 PascalCase
- assets는 index 기반 export (Icons, Images, Logos 등)
- import 시 그룹 단위 import 사용

[상수 / 환경 변수]
- SNAKE_CASE 사용
- 예: PAGE_SIZE, API_URL

[함수 선언 규칙]
- 컴포넌트는 선언식 function Component() 형태
- 컴포넌트 내부의 로직 함수는 화살표 함수 사용
- 유틸성 컴포넌트가 아니면 default export

[주석 & 문서화]
- TODO 주석 사용 (미완성 코드 표시)
- TSDoc을 컴포넌트 단위로 활용
- 예시를 포함한 간단한 설명 추가

Files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-30T15:48:27.926Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 283
File: src/utils/card/createCardReqBody.ts:17-18
Timestamp: 2025-11-30T15:48:27.926Z
Learning: In src/utils/card/createCardReqBody.ts, the backend accepts `assigneeUserId: 0` as a valid value when no assignee is selected and no currentUserId is provided. This is part of the server API contract.

Applied to files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
📚 Learning: 2025-11-30T15:48:53.701Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 283
File: src/utils/card/createCardReqBody.ts:20-30
Timestamp: 2025-11-30T15:48:53.701Z
Learning: In src/utils/card/createCardReqBody.ts, the backend requires a default IMAGE_URL fallback (`imageUrl ?? IMAGE_URL`) when no image is provided, rather than accepting null. This is part of the server API contract, similar to how it requires assigneeUserId: 0 and tags: [''] as default values.

Applied to files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
📚 Learning: 2025-12-01T01:09:10.680Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 287
File: src/lib/apis/Invitations.ts:31-35
Timestamp: 2025-12-01T01:09:10.680Z
Learning: In this codebase, mutation functions (POST/PUT/DELETE) return the full response object (`res`) instead of `res.data`, because `useMutation` internally processes `res.data`. Only GET methods return `res.data` directly.

Applied to files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
🧬 Code graph analysis (1)
src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (7)
src/types/card.ts (2)
  • CardDetailResponse (10-22)
  • CardEditFormValue (39-47)
src/types/members.ts (1)
  • MembersResponse (12-15)
src/types/column.ts (1)
  • ColumnsResponse (10-13)
src/hooks/useModal.ts (1)
  • useModal (4-50)
src/lib/apis/cards.ts (3)
  • deleteCard (63-66)
  • UpdateCardType (3-11)
  • updateCard (51-54)
src/utils/card/uploadCardImage.ts (1)
  • uploadCardImage (4-21)
src/utils/card/updateCardRequestBody.ts (1)
  • updateCardRequestBody (6-28)

@github-actions
Copy link

github-actions bot commented Dec 2, 2025

@github-actions
Copy link

github-actions bot commented Dec 2, 2025

@coderabbitai coderabbitai bot requested a review from tkp12345 December 2, 2025 06:06
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 795b9dd and 49b4baa.

📒 Files selected for processing (1)
  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.tsx

⚙️ CodeRabbit configuration file

**/*.tsx: reviews:
instructions: |
우리 팀은 다음의 코드 스타일 가이드를 따릅니다:

[기본 규칙]
- 변수는 const 우선 사용
- 파일명은 PascalCase로 작성
- 컴포넌트는 default export를 사용
- 파일명과 컴포넌트명은 반드시 일치 (PascalCase)
- 불필요한 props는 전달하지 않기
- UI 관련 상태는 명확한 이름 사용 (isOpen, isLoading 등)

[함수 네이밍 규칙]
- 모든 함수는 **동사 + 목적어**
- UI 이벤트 핸들러는 handle* 사용 (handleClick, handleToggle 등)
- 내부 로직은 calculate / convert / format 등 의미 기반 네이밍 사용

[디렉토리 & 파일 구조]
- 디렉토리는 kebab-case
- 컴포넌트 파일은 PascalCase
- assets는 index 기반 export (Icons, Images, Logos 등)
- import 시 그룹 단위 import 사용

[상수 / 환경 변수]
- SNAKE_CASE 사용
- 예: PAGE_SIZE, API_URL

[함수 선언 규칙]
- 컴포넌트는 선언식 function Component() 형태
- 컴포넌트 내부의 로직 함수는 화살표 함수 사용
- 유틸성 컴포넌트가 아니면 default export

[주석 & 문서화]
- TODO 주석 사용 (미완성 코드 표시)
- TSDoc을 컴포넌트 단위로 활용
- 예시를 포함한 간단한 설명 추가

Files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
🧠 Learnings (2)
📚 Learning: 2025-11-30T15:48:53.701Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 283
File: src/utils/card/createCardReqBody.ts:20-30
Timestamp: 2025-11-30T15:48:53.701Z
Learning: In src/utils/card/createCardReqBody.ts, the backend requires a default IMAGE_URL fallback (`imageUrl ?? IMAGE_URL`) when no image is provided, rather than accepting null. This is part of the server API contract, similar to how it requires assigneeUserId: 0 and tags: [''] as default values.

Applied to files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
📚 Learning: 2025-12-01T01:09:10.680Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 287
File: src/lib/apis/Invitations.ts:31-35
Timestamp: 2025-12-01T01:09:10.680Z
Learning: In this codebase, mutation functions (POST/PUT/DELETE) return the full response object (`res`) instead of `res.data`, because `useMutation` internally processes `res.data`. Only GET methods return `res.data` directly.

Applied to files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
🔇 Additional comments (6)
src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (6)

1-20: 새로운 편집 기능을 위한 import가 적절하게 추가되었습니다

ChangeCardModal, useModal, useUserContext, 관련 타입 및 유틸리티 함수들이 모두 적절하게 import되어 있습니다.


36-45: 인터페이스 확장이 적절합니다

편집 흐름을 지원하기 위해 onUpdateCard, memberData, columnListData props가 명확한 타입과 함께 추가되었습니다.


57-62: 듀얼 모달 시스템이 잘 구성되었습니다

detailModaleditModal을 분리하여 관리하는 방식이 명확하며, 반응형 처리와 사용자 컨텍스트 접근도 적절합니다.


72-87: Mutation 설정이 올바릅니다

deleteCardMutationupdateCardMutation 모두 적절한 타입과 콜백으로 구성되어 있으며, 성공 시 상태 업데이트 및 refetch 로직이 정확합니다.


132-189: 렌더링 로직이 잘 구성되어 있습니다

듀얼 모달 시스템의 조건부 렌더링이 명확하며, props 전달과 초기값 설정도 적절합니다. ChangeCardModalcolumnListData={columnListData} (line 174)를 전달할 때 null 가능성이 있으므로, ChangeCardModal 내부에서 null 처리가 되어있는지 확인하세요.


77-87: The image update flow is correctly implemented through the established callback mechanism. When a card is updated:

  1. onUpdateCard(updated) immediately applies the updated card data (including new imageUrl) to the card list via handleUpdateCard in ColumnCardList (lines 111-121), which updates the infiniteData state
  2. cardDetailQuery.refetch() syncs the detail view without needing to be awaited
  3. The modal closes after initiating these updates

The DashboardCard component renders the image from cardData.imageUrl (line 46), which is already updated via the callback before the modal closes. The current implementation already provides optimistic updates through the onUpdateCard callback pattern.

No changes are necessary—the code correctly handles image reflection through its established state management pattern.

Likely an incorrect or invalid review comment.

@github-actions
Copy link

github-actions bot commented Dec 2, 2025

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (1)

114-130: 이미지 fallback 체인에 formValue.imageUrl을 추가하세요.

Line 120의 fallback 체인에 formValue.imageUrl이 누락되어 있습니다. 이전 리뷰에서 제안된 대로 이를 추가하면 이미지 변경 사항이 즉시 반영되지 않는 문제(PR 목표에 명시됨)를 해결하는 데 도움이 될 수 있습니다.

다음과 같이 수정하세요:

 const handleSubmitUpdateCard = async (
   formValue: CardEditFormValue,
   imageFile: File | null
 ): Promise<void> => {
   const uploadedImageUrl = await uploadCardImage(formValue.columnId, imageFile);

-  const nextImageUrl = uploadedImageUrl ?? cardDetailQuery.data?.imageUrl ?? null;
+  const nextImageUrl = 
+    uploadedImageUrl ?? 
+    formValue.imageUrl ?? 
+    cardDetailQuery.data?.imageUrl ?? 
+    null;

   const body = updateCardRequestBody(
     formValue,
     cardDetailQuery.data,
     nextImageUrl,
     userProfile?.id
   );

   await updateCardMutation.mutate({ id: cardId, body });
 };

이렇게 하면 폼에 이미 있던 이미지 URL도 fallback 체인에 포함되어 이미지 업데이트가 더 안정적으로 처리됩니다.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49b4baa and c55d0b6.

📒 Files selected for processing (1)
  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.tsx

⚙️ CodeRabbit configuration file

**/*.tsx: reviews:
instructions: |
우리 팀은 다음의 코드 스타일 가이드를 따릅니다:

[기본 규칙]
- 변수는 const 우선 사용
- 파일명은 PascalCase로 작성
- 컴포넌트는 default export를 사용
- 파일명과 컴포넌트명은 반드시 일치 (PascalCase)
- 불필요한 props는 전달하지 않기
- UI 관련 상태는 명확한 이름 사용 (isOpen, isLoading 등)

[함수 네이밍 규칙]
- 모든 함수는 **동사 + 목적어**
- UI 이벤트 핸들러는 handle* 사용 (handleClick, handleToggle 등)
- 내부 로직은 calculate / convert / format 등 의미 기반 네이밍 사용

[디렉토리 & 파일 구조]
- 디렉토리는 kebab-case
- 컴포넌트 파일은 PascalCase
- assets는 index 기반 export (Icons, Images, Logos 등)
- import 시 그룹 단위 import 사용

[상수 / 환경 변수]
- SNAKE_CASE 사용
- 예: PAGE_SIZE, API_URL

[함수 선언 규칙]
- 컴포넌트는 선언식 function Component() 형태
- 컴포넌트 내부의 로직 함수는 화살표 함수 사용
- 유틸성 컴포넌트가 아니면 default export

[주석 & 문서화]
- TODO 주석 사용 (미완성 코드 표시)
- TSDoc을 컴포넌트 단위로 활용
- 예시를 포함한 간단한 설명 추가

Files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
🧠 Learnings (3)
📚 Learning: 2025-11-30T15:48:53.701Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 283
File: src/utils/card/createCardReqBody.ts:20-30
Timestamp: 2025-11-30T15:48:53.701Z
Learning: In src/utils/card/createCardReqBody.ts, the backend requires a default IMAGE_URL fallback (`imageUrl ?? IMAGE_URL`) when no image is provided, rather than accepting null. This is part of the server API contract, similar to how it requires assigneeUserId: 0 and tags: [''] as default values.

Applied to files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
📚 Learning: 2025-11-30T15:48:27.926Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 283
File: src/utils/card/createCardReqBody.ts:17-18
Timestamp: 2025-11-30T15:48:27.926Z
Learning: In src/utils/card/createCardReqBody.ts, the backend accepts `assigneeUserId: 0` as a valid value when no assignee is selected and no currentUserId is provided. This is part of the server API contract.

Applied to files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
📚 Learning: 2025-12-01T01:09:10.680Z
Learnt from: aahreum
Repo: sprint-19-part3-7team/Taskify-Frontend PR: 287
File: src/lib/apis/Invitations.ts:31-35
Timestamp: 2025-12-01T01:09:10.680Z
Learning: In this codebase, mutation functions (POST/PUT/DELETE) return the full response object (`res`) instead of `res.data`, because `useMutation` internally processes `res.data`. Only GET methods return `res.data` directly.

Applied to files:

  • src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx
🔇 Additional comments (1)
src/components/dashboard-detail/modal/card-detail-modal/CardDetailModal.tsx (1)

77-87: mutation 타입 정의와 에러 처리가 올바르게 구현되었습니다.

updateCardMutation의 타입 정의와 onSuccess 핸들러가 적절하게 구현되었습니다. 업데이트된 데이터를 부모에게 전달하고, 카드 상세 정보를 다시 가져오며, 편집 모달을 닫는 흐름이 명확합니다.

@aahreum aahreum merged commit bd9503e into main Dec 2, 2025
2 checks passed
@aahreum aahreum deleted the feature/278/card-update branch December 2, 2025 06:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ Feature 새로운 기능 구현

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[✨ Feature] 할 일 생성 수정 모달 구현 api 연결

1 participant