feat: 회의록페이지/ 위젯 db연동 - #61
Conversation
|
Warning Review limit reached
Next review available in: 35 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthrough회의록 기능이 mock 데이터와 로컬 스토어에서 Supabase 기반 조회·생성·수정·삭제 흐름으로 전환되었습니다. 입력 검증, 권한 정책, 편집 페이지, React Query 캐시와 최근 회의록 위젯이 추가되었습니다. Changes회의록 DB 계약과 서버 처리
회의록 관리 UI
공유 멤버 데이터와 위젯
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant MeetingNotesPage
participant getMeetingNotes
participant Supabase
participant MeetingNotesList
MeetingNotesPage->>getMeetingNotes: workspaceId로 목록 조회
getMeetingNotes->>Supabase: meeting_notes, workspace_members, profiles 조회
Supabase-->>getMeetingNotes: 회의록과 viewer 데이터
getMeetingNotes-->>MeetingNotesPage: MeetingNoteBoardData
MeetingNotesPage->>MeetingNotesList: meetingNotes와 viewer 전달
sequenceDiagram
participant MeetingNotesList
participant deleteMeetingNote
participant Supabase
MeetingNotesList->>deleteMeetingNote: 삭제 요청
deleteMeetingNote->>Supabase: 권한 확인 및 회의록 삭제
Supabase-->>deleteMeetingNote: 삭제 결과
deleteMeetingNote-->>MeetingNotesList: 성공 또는 실패 응답
🚥 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 |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 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 `@src/entities/meeting-note/api/get-meeting-notes.ts`:
- Around line 24-28: Update the meeting-notes query in the getMeetingNotes flow
to avoid fetching the entire workspace history on every list request. Add an
initial result limit using the existing query builder, while preserving the
workspace filter, selected fields, and descending meeting_at order so the newest
notes are returned first.
- Line 15: Update the workspaceIdSchema definition to use Zod’s strict UUID
validator z.uuid() instead of the more permissive z.guid(), ensuring workspace
IDs are validated as RFC-compliant UUIDs before database queries.
In `@src/entities/meeting-note/api/update-meeting-note.ts`:
- Around line 36-53: Update src/entities/meeting-note/api/update-meeting-note.ts
lines 36-53 in updateMeetingNote to select the affected id with maybeSingle()
after the update and treat missing data as failure; apply the same change to
src/entities/meeting-note/api/delete-meeting-note.ts lines 31-40 in
deleteMeetingNote after the delete. Preserve the existing error response for
database errors and return failure whenever no row was actually affected.
In `@src/entities/meeting-note/ui/MeetingNoteCard.tsx`:
- Around line 78-124: Update the card’s handleKeyDown handler to return when
event.target differs from event.currentTarget, so keyboard events from the menu,
edit, and delete controls do not toggle the card. Keep the existing onClick
guard and Enter/Space activation behavior unchanged.
In `@src/widgets/side-project/dashboard-recent-notes/ui/RecentNotes.tsx`:
- Around line 7-22: Move the static header definition into the RecentNotes
component so it can access workspaceId, import and use useRouter, and connect
the WidgetCardAction in the header to navigate to the workspace’s meeting-notes
list page when clicked. Preserve the existing title and header layout.
🪄 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: 8269e46a-ed03-4aaa-a96a-d83d4da92fa6
📒 Files selected for processing (32)
src/app/workspaces/[workspaceId]/meeting-notes/[noteId]/edit/page.tsxsrc/app/workspaces/[workspaceId]/sprint-board/page.tsxsrc/app/workspaces/[workspaceId]/work-schedule/page.tsxsrc/entities/meeting-note/api/create-meeting-note.tssrc/entities/meeting-note/api/delete-meeting-note.tssrc/entities/meeting-note/api/get-meeting-note.tssrc/entities/meeting-note/api/get-meeting-notes.tssrc/entities/meeting-note/api/shared.tssrc/entities/meeting-note/api/update-meeting-note.tssrc/entities/meeting-note/index.tssrc/entities/meeting-note/model/meeting-note-query.tssrc/entities/meeting-note/model/meeting-note.db.types.tssrc/entities/meeting-note/model/meeting-note.mapper.tssrc/entities/meeting-note/model/meeting-note.schema.tssrc/entities/meeting-note/model/meeting-note.types.tssrc/entities/meeting-note/model/mock-meeting-notes-by-workspace.tssrc/entities/meeting-note/ui/MeetingNoteCard.tsxsrc/entities/workspace-member/api/use-workspace-members-by-id.tssrc/features/manage-meeting-notes/index.tssrc/features/manage-meeting-notes/model/use-meeting-notes-store.tssrc/features/manage-meeting-notes/ui/MeetingNoteForm.tsxsrc/features/manage-meeting-notes/ui/MeetingNotesList.tsxsrc/features/manage-member-profile/ui/MemberProfileForm.tsxsrc/features/manage-workspace-members/model/use-member-management.tssrc/views/dashboard/config/widget-catalog.tsxsrc/views/meeting-notes/index.tssrc/views/meeting-notes/ui/EditMeetingNotePage.tsxsrc/views/meeting-notes/ui/MeetingNotesPage.tsxsrc/views/side-project/sprint-board/ui/SprintBoardView.tsxsrc/views/store-operation/work-schedule/ui/WorkScheduleView.tsxsrc/widgets/side-project/dashboard-recent-notes/ui/RecentNotes.tsxsupabase/migrations/20260715000000_restrict_meeting_note_mutations.sql
💤 Files with no reviewable changes (3)
- src/entities/meeting-note/model/mock-meeting-notes-by-workspace.ts
- src/features/manage-meeting-notes/index.ts
- src/features/manage-meeting-notes/model/use-meeting-notes-store.ts
| } from '../model/meeting-note.mapper'; | ||
| import type { MeetingNoteBoardData } from '../model/meeting-note.types'; | ||
|
|
||
| const workspaceIdSchema = z.guid(); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win
엄격한 UUID 검증이 필요하면 z.uuid()를 사용하세요.
z.guid()는 Zod 4에서 "UUID-like"한 값을 허용하는 더 관대한 검증기입니다. workspaceId가 Postgres UUID 기본키라면 z.uuid()(RFC 표준 준수)를 사용하는 것이 더 명확한 입력 오류를 제공합니다. 현재는 형식이 다소 어긋난 값도 통과해 Supabase 쿼리 단계에서 원시 Postgrest 오류로 이어질 수 있습니다.
♻️ 제안
-const workspaceIdSchema = z.guid();
+const workspaceIdSchema = z.uuid();Also applies to: 18-18
🤖 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 `@src/entities/meeting-note/api/get-meeting-notes.ts` at line 15, Update the
workspaceIdSchema definition to use Zod’s strict UUID validator z.uuid() instead
of the more permissive z.guid(), ensuring workspace IDs are validated as
RFC-compliant UUIDs before database queries.
| supabase | ||
| .from('meeting_notes') | ||
| .select(MEETING_NOTE_SELECT_QUERY) | ||
| .eq('workspace_id', parsedWorkspaceId) | ||
| .order('meeting_at', { ascending: false }), |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🔵 Trivial
페이지네이션 없이 워크스페이스 전체 회의록을 매번 조회합니다.
현재는 meeting_at 내림차순으로 전체 행을 가져옵니다. 워크스페이스가 오래될수록 목록 페이지 방문마다 전체 회의록 이력을 로드하게 되어 I/O 비용이 커질 수 있습니다. 초기 규모에서는 문제가 없지만, 추후 커서 기반 페이지네이션이나 limit을 고려해두면 좋습니다.
🤖 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 `@src/entities/meeting-note/api/get-meeting-notes.ts` around lines 24 - 28,
Update the meeting-notes query in the getMeetingNotes flow to avoid fetching the
entire workspace history on every list request. Add an initial result limit
using the existing query builder, while preserving the workspace filter,
selected fields, and descending meeting_at order so the newest notes are
returned first.
| const { error } = await authorized.context.supabase | ||
| .from('meeting_notes') | ||
| .update( | ||
| toMeetingNoteUpdate({ | ||
| title: value.title, | ||
| meetingDate: value.meetingDate, | ||
| participantIds: value.participantIds, | ||
| decisions: value.decisions, | ||
| followUpActions: value.followUpActions, | ||
| }), | ||
| ) | ||
| .eq('id', value.meetingNoteId) | ||
| .eq('workspace_id', value.workspaceId); | ||
|
|
||
| if (error) { | ||
| console.error('[meeting-note/updateMeetingNote] 수정 실패:', error); | ||
| return { ok: false, message: '회의록 수정에 실패했습니다. 잠시 후 다시 시도해주세요.' }; | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
update/delete 서버 액션이 실제 반영 행 수를 확인하지 않아 거짓 성공을 반환할 수 있습니다.
두 파일 모두 authorizeMeetingNoteMutation으로 권한을 먼저 확인한 뒤 .update()/.delete()를 호출하지만 .select()가 없어, 매칭되는 행이 0개여도(권한 확인과 쓰기 사이의 레이스, 동시 삭제 등으로 RLS가 조용히 막는 경우) error가 발생하지 않고 { ok: true }가 반환됩니다. 근본 원인은 두 곳 모두 동일하게 결과 확인 없이 성공을 가정하는 것입니다.
src/entities/meeting-note/api/update-meeting-note.ts#L36-L53:.update(...).eq(...).eq(...)에.select('id').maybeSingle()을 추가하고,data가 없으면 실패로 처리하세요.src/entities/meeting-note/api/delete-meeting-note.ts#L31-L40:.delete().eq(...).eq(...)에.select('id').maybeSingle()을 추가하고,data가 없으면 실패로 처리하세요.
📍 Affects 2 files
src/entities/meeting-note/api/update-meeting-note.ts#L36-L53(this comment)src/entities/meeting-note/api/delete-meeting-note.ts#L31-L40
🤖 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 `@src/entities/meeting-note/api/update-meeting-note.ts` around lines 36 - 53,
Update src/entities/meeting-note/api/update-meeting-note.ts lines 36-53 in
updateMeetingNote to select the affected id with maybeSingle() after the update
and treat missing data as failure; apply the same change to
src/entities/meeting-note/api/delete-meeting-note.ts lines 31-40 in
deleteMeetingNote after the delete. Preserve the existing error response for
database errors and return failure whenever no row was actually affected.
seongjinss555
left a comment
There was a problem hiding this comment.
리뷰 반영해주세요~ 고생하셨습니다
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/entities/meeting-note/ui/MeetingNoteCard.tsx (1)
52-60: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
role="button"컨테이너 안에 native<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 `@src/entities/meeting-note/ui/MeetingNoteCard.tsx` around lines 52 - 60, MeetingNoteCard의 role="button" 카드 토글 영역 안에 관리용 native button이 중첩되지 않도록 구조를 변경하세요. 카드 토글 컨테이너와 메뉴·수정·삭제 버튼 영역을 형제 요소로 분리하고, 카드의 onClick·onKeyDown·aria-expanded 동작은 토글 영역에만 유지하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/entities/meeting-note/ui/MeetingNoteCard.tsx`:
- Around line 52-60: MeetingNoteCard의 role="button" 카드 토글 영역 안에 관리용 native
button이 중첩되지 않도록 구조를 변경하세요. 카드 토글 컨테이너와 메뉴·수정·삭제 버튼 영역을 형제 요소로 분리하고, 카드의
onClick·onKeyDown·aria-expanded 동작은 토글 영역에만 유지하세요.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: ff54a0b7-e59b-41e7-b9e6-688307844076
📒 Files selected for processing (2)
src/entities/meeting-note/ui/MeetingNoteCard.tsxsrc/widgets/side-project/dashboard-recent-notes/ui/RecentNotes.tsx
Pull Request
작업 내용
작업 결과
tsc --noEmit) / 린트(eslint) 통과.변경 사항
Added
model:db.types/mapper(row↔UI, 참석자·날짜 변환, 색상 파생) /schema(zod) /query-keyapi: 목록 조회getMeetingNotes, 단건 조회getMeetingNote,createMeetingNote,updateMeetingNote,deleteMeetingNote, 권한 검증 공용 헬퍼shared.ts/workspaces/[workspaceId]/meeting-notes/[noteId]/edit20260715000000_restrict_meeting_note_mutations.sqlauthor_id = auth.uid()강제, 수정/삭제는 작성자·소유자만, 감사 필드(workspace_id/author_id/created_at) 불변 트리거useWorkspaceMembersByWorkspaceId훅에 선택적initialData지원 (하이브리드 전환 기반)Changed
getMeetingNotes로 DB 조회MeetingNoteForm): 생성/수정 겸용화, 서버 액션 +useMutation연동, 참석자는userId만 저장MeetingNoteCard): 권한이 있을 때만 노출되는 케밥(수정/삭제) 메뉴 추가RecentNotes): 클라이언트 React Query로 전환 (자료실 위젯과 동일 패턴)workspaceMembersByWorkspaceQueryKey공유 캐시로 통합MemberProfileForm: 닉네임 저장을useMutation으로 전환하고onSuccess에서 멤버 캐시 무효화mock-meeting-notes-by-workspace.ts,use-meeting-notes-store.tsFixed
RecentNotes위젯이 회의록이 없을 때 발생하던 크래시(빈 배열에서latest.title접근) 수정실행화면
테스트
supabase db push) 후 RLS·권한 동작 확인리뷰 체크리스트
feature/*->develop, 배포 시develop또는release/*->main)Type/#issue-number/description형식을 따릅니다.console.log, 주석, 임시 코드를 제거했습니다. (서버 액션의console.error는 오류 로깅 목적)리뷰 요청사항
initialData시드 + 클라이언트useQuery공유 캐시)이 적절한지 봐주세요.profiles.real_name(실명)으로 해석되는 반면, 회의록 폼의 참석자 드롭다운은workspace_nickname(닉네임)을 사용합니다. 실명/닉네임 혼용을 통일할지 논의가 필요합니다. (이번 PR 범위에서는 제외)restrict_meeting_note_mutations) 검토 부탁드립니다.관련 이슈
Closes #58
Summary by CodeRabbit