Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ 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 |
Summary of ChangesHello @hongik-luke, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 이 PR은 조 편성 및 채팅 기능의 사용자 인터페이스와 경험을 크게 개선하는 데 중점을 둡니다. 사용자는 이제 직관적인 드래그 앤 드롭 방식으로 모임 조를 구성할 수 있으며, 다양한 기기에서 최적화된 채팅 환경을 경험할 수 있습니다. 더불어, 코드베이스의 불필요한 부분을 정리하고 구조를 개선하여 유지보수성을 높였습니다. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
이번 PR은 채팅 및 조 편성 UI를 개선하고 관련 코드를 리팩토링하는 내용을 담고 있습니다. 전반적으로 dnd-kit을 도입하여 드래그 앤 드롭 기능을 구현하고, 컴포넌트 분리와 타입 정의를 통해 코드 구조를 개선한 점이 인상적입니다. 몇 가지 코드 안정성 및 유지보수성 향상을 위한 제안 사항을 남깁니다. 하드코딩된 URL 수정, 중복되는 드래그 앤 드롭 로직 제거, 사용되지 않는 prop 정리 등을 검토해 주시면 좋겠습니다.
| <ButtonWithoutImg | ||
| text="이번 모임 바로가기" | ||
| onClick={() => router.push(joinUrl)} | ||
| onClick={() => router.push(`${Number(groupId)}/notice/4`)} |
There was a problem hiding this comment.
| iconSrc="/icons_chat.svg" | ||
| iconAlt="채팅" | ||
| onClick={() => setIsChatTeamModalOpen(true)} | ||
| className={`${isChatTeamModalOpen ? "mb-15 t:mb-0" : ""}`} |
| <div | ||
| draggable={draggable} | ||
| onDragStart={(e) => { | ||
| if (!draggable) return; | ||
| e.dataTransfer.setData( | ||
| "application/x-checkmo-member", | ||
| JSON.stringify({ | ||
| clubMemberId: member.clubMemberId, | ||
| fromTeamNumber: member.teamNumber, | ||
| }) | ||
| ); | ||
| e.dataTransfer.effectAllowed = "move"; | ||
| }} | ||
| className={[ | ||
| "flex items-center gap-2.5", | ||
| "w-full self-stretch", | ||
| "px-5 py-4", | ||
| "rounded-[8px]", | ||
| "bg-White", | ||
| draggable ? "cursor-grab active:cursor-grabbing" : "", | ||
| ].join(" ")} | ||
| > |
There was a problem hiding this comment.
이 컴포넌트는 상위 컴포넌트에서 dnd-kit을 통해 드래그 기능이 제어되고 있습니다. MemberItem 자체에 네이티브 HTML 드래그 속성(draggable, onDragStart)을 두는 것은 중복이며 dnd-kit의 동작과 충돌할 수 있습니다. draggable prop과 관련 로직을 모두 제거하고, 드래그 관련 책임(커서 스타일 포함)은 dnd-kit을 사용하는 상위 컴포넌트에 위임하는 것이 좋습니다.
| <div | |
| draggable={draggable} | |
| onDragStart={(e) => { | |
| if (!draggable) return; | |
| e.dataTransfer.setData( | |
| "application/x-checkmo-member", | |
| JSON.stringify({ | |
| clubMemberId: member.clubMemberId, | |
| fromTeamNumber: member.teamNumber, | |
| }) | |
| ); | |
| e.dataTransfer.effectAllowed = "move"; | |
| }} | |
| className={[ | |
| "flex items-center gap-2.5", | |
| "w-full self-stretch", | |
| "px-5 py-4", | |
| "rounded-[8px]", | |
| "bg-White", | |
| draggable ? "cursor-grab active:cursor-grabbing" : "", | |
| ].join(" ")} | |
| > | |
| <div | |
| className={[ | |
| "flex items-center gap-2.5", | |
| "w-full self-stretch", | |
| "px-5 py-4", | |
| "rounded-[8px]", | |
| "bg-White", | |
| ].join(" ")} | |
| > |
| import Image from "next/image"; | ||
|
|
||
| type Props = { | ||
| meetingId?: number; |
There was a problem hiding this comment.
Pull request overview
Adds new UI flows for group bookcase/meeting pages, including an admin team-grouping (drag & drop) interface and a chat team selection modal, while aligning bookcase screens to API-shaped types/models.
Changes:
- Introduced API-aligned types + mappers for bookcase home/detail data and updated bookcase list/detail pages to use
meetingId+tabquery. - Added admin “조 편성” management page using dnd-kit (with supporting UI components) and updated layout routing exclusions.
- Added a movable/responsive chat team selection modal and integrated it into the meeting page floating button.
Reviewed changes
Copilot reviewed 20 out of 25 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/types/groups/bookcasehome.ts | Adds API response/view-model types and mapping/grouping utilities for bookcase home. |
| src/types/groups/bookcasedetail.ts | Adds team/member types and helpers for team labeling/normalization. |
| src/components/base-ui/Bookcase/MeetingInfo.tsx | Updates meeting info card UI and gates “조 관리하기” by admin flag. |
| src/components/base-ui/Bookcase/ChatTeamSelectModal.tsx | Adds responsive/movable chat team selection + placeholder chat UI modal. |
| src/components/base-ui/Bookcase/BookcaseCard.tsx | Tightens imageUrl prop typing (now required). |
| src/components/base-ui/Bookcase/BookDetailNav.tsx | Refactors tabs to stable keys (topic/review/meeting) and maps to labels. |
| src/components/base-ui/Bookcase/Admin/bookdetailgrouping/TeamBoard.tsx | Adds droppable team board and draggable member items using dnd-kit. |
| src/components/base-ui/Bookcase/Admin/bookdetailgrouping/MemberPool.tsx | Adds droppable “unassigned members” pool + save confirmation UI. |
| src/components/base-ui/Bookcase/Admin/bookdetailgrouping/MemberItem.tsx | Adds reusable member row UI component. |
| src/components/base-ui/Bookcase/Admin/.gitkeep | Keeps Admin directory tracked. |
| src/app/groups/[id]/page.tsx | Updates “이번 모임 바로가기” navigation behavior on desktop. |
| src/app/groups/[id]/layout.tsx | Expands layout-exclusion condition to cover all /admin/bookcase routes. |
| src/app/groups/[id]/bookcase/page.tsx | Replaces dummy grouping with API-shaped mock + groupByGeneration mapping; routes by meetingId + tab. |
| src/app/groups/[id]/bookcase/[bookId]/page.tsx | Syncs active tab with ?tab= query and adds link to admin team management page. |
| src/app/groups/[id]/bookcase/[bookId]/meeting/page.tsx | Integrates chat team modal with floating button and refines UI interactions. |
| src/app/groups/[id]/bookcase/[bookId]/MeetingTabSection.tsx | Adds meeting-tab container that renders MeetingInfo + team filter/section from meeting detail payload. |
| src/app/groups/[id]/admin/bookcase/page.tsx | Removes the old admin bookcase list page (dummy-based). |
| src/app/groups/[id]/admin/bookcase/[meetingId]/page.tsx | Adds admin team management page with dnd-kit DnDContext and submit payload building. |
| src/app/groups/[id]/admin/bookcase/[meetingId]/layout.tsx | Adds a route-level layout wrapper for the admin team management page. |
| src/app/groups/[id]/admin/bookcase/[meetingId]/dummy.ts | Adds dummy fetch + response for meeting team management. |
| public/icon_plus_2.svg | Adds plus icon asset for “팀 추가”. |
| public/Polygon7.svg | Adds polygon icon asset used for mobile back button. |
| public/ArrowLeft3.svg | Adds left arrow icon asset. |
| package.json | Adds @dnd-kit/core and @dnd-kit/utilities dependencies. |
| package-lock.json | Locks new dnd-kit dependency tree. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| export type MeetingInfo = { | ||
| meetingId: number; | ||
| generation: number; // 1,2,3... | ||
| tag: "MEETING" | string; // 서버가 더 늘리면 string으로 대응 |
There was a problem hiding this comment.
tag: "MEETING" | string는 TypeScript에서 결국 string과 동일한 타입이라(리터럴이 string에 흡수됨) 의미가 없습니다. 서버 확장을 염두에 둔 거라면 tag: string으로 단순화하거나, 실제로 제한하고 싶다면 tag: "MEETING" | "..."처럼 구체적인 리터럴 유니온으로 관리하는 편이 유지보수에 유리합니다.
| tag: "MEETING" | string; // 서버가 더 늘리면 string으로 대응 | |
| tag: string; // 서버가 더 늘리면 string으로 대응 |
|
|
||
| type Props = { | ||
| imageUrl?: string; | ||
| imageUrl: string; |
There was a problem hiding this comment.
imageUrl prop을 필수로 바꾸면서(Props에서 imageUrl: string) 기존 호출부 중 imageUrl을 전달하지 않는 곳(예: src/components/base-ui/Group/BookshelfModal.tsx)에서 타입 에러가 발생합니다. 컴포넌트 내부는 여전히 기본값(imageUrl = "/dummy_book_cover.png")을 두고 있어 API가 불일치하니, (1) imageUrl을 다시 optional로 돌리거나 (2) 모든 호출부에 imageUrl을 넘기고 기본값을 제거하는 쪽으로 정리하는 게 필요합니다.
| imageUrl: string; | |
| imageUrl?: string; |
| </div> | ||
|
|
||
| {/* 입력창 */} | ||
| <div className="w-full mb-15 t:mb-0"> |
There was a problem hiding this comment.
mb-15는 기본 Tailwind spacing 스케일에 없는 클래스라서(현재 tailwind.config.js에서도 spacing 확장이 없음) 실제로 스타일이 적용되지 않습니다. 떠있는 입력창/Float 간격을 확보하려는 의도라면 mb-16 같은 유효한 값으로 바꾸거나, bottom-*/translate-y로 위치를 조정하는 방식으로 수정이 필요합니다.
| <div className="w-full mb-15 t:mb-0"> | |
| <div className="w-full mb-16 t:mb-0"> |
| <ButtonWithoutImg | ||
| text="이번 모임 바로가기" | ||
| onClick={() => router.push(joinUrl)} | ||
| onClick={() => router.push(`${Number(groupId)}/notice/4`)} |
There was a problem hiding this comment.
데스크톱 버튼의 router.push(${Number(groupId)}/notice/4)는 앞에 /groups/가 없어 현재 경로 기준 상대경로로 이동할 가능성이 큽니다(예: /groups/123에서 123/notice/4로 push하면 /groups/123/123/notice/4가 됨). 모바일 구간은 여전히 joinUrl을 사용하고 있어 동작이 불일치하니, 데스크톱도 joinUrl을 사용하거나 /groups/${groupId}/notice/4처럼 절대경로로 맞춰주세요.
| onClick={() => router.push(`${Number(groupId)}/notice/4`)} | |
| onClick={() => router.push(`/groups/${Number(groupId)}/notice/4`)} |
| {/* ✅ UI는 기존 MemberItem 그대로 재사용 */} | ||
| <MemberItem member={member} /> |
There was a problem hiding this comment.
dnd-kit의 useDraggable로 드래그를 처리하는데, 내부에서 렌더링하는 MemberItem이 기본값으로 draggable={true} + onDragStart(native HTML5 drag&drop)를 활성화하고 있어 두 드래그 시스템이 충돌할 수 있습니다(브라우저 기본 drag ghost 이미지/이벤트가 같이 뜨는 케이스). MemberItem을 여기서 사용할 때는 draggable={false}로 끄거나, MemberItem에서 native drag 로직을 제거/옵트인으로 바꿔주세요.
| {/* ✅ UI는 기존 MemberItem 그대로 재사용 */} | |
| <MemberItem member={member} /> | |
| {/* ✅ UI는 기존 MemberItem 그대로 재사용하되, dnd-kit와 충돌하지 않도록 native drag 비활성화 */} | |
| <MemberItem member={member} draggable={false} /> |
| iconSrc="/icons_chat.svg" | ||
| iconAlt="채팅" | ||
| onClick={() => setIsChatTeamModalOpen(true)} | ||
| className={`${isChatTeamModalOpen ? "mb-15 t:mb-0" : ""}`} |
There was a problem hiding this comment.
FloatingFab에 적용한 mb-15는 기본 Tailwind spacing 스케일에 없는 클래스라서(현재 tailwind.config.js에서 spacing 확장 없음) 실제로 버튼 위치가 올라가지 않습니다. 모달이 열렸을 때 버튼을 위로 올려 입력/전송을 막지 않게 하려는 목적이라면 mb-16 등 유효한 클래스나 bottom-* 조정으로 변경해 주세요.
| className={`${isChatTeamModalOpen ? "mb-15 t:mb-0" : ""}`} | |
| className={`${isChatTeamModalOpen ? "mb-16 t:mb-0" : ""}`} |
| /** | ||
| * 조 선택하면 "채팅 페이지"로 이동 (입장만) | ||
| * - chat은 "현재 경로 하위의 chat"으로 이동시키는 상대 경로 | ||
| * - teamId/teamName을 쿼리로 넘겨서 다음 페이지에서 어떤 방인지 알게 함 | ||
| */ | ||
| const handleEnterChat = (team: ChatTeam) => { | ||
| setIsChatTeamModalOpen(false); | ||
|
|
||
| // 상대경로: 현재 페이지가 .../meeting 이면 .../meeting/chat 로 감 | ||
| router.push( | ||
| `chat?teamId=${team.teamId}&teamName=${encodeURIComponent(team.teamName)}` | ||
| ); | ||
|
|
||
| // ❗️만약 상대경로가 안 맞으면 절대경로로 바꿔야 함: | ||
| // router.push(`/groups/${params.groupId}/meeting/${bookId}/chat?teamId=${team.teamId}`); | ||
| }; | ||
|
|
There was a problem hiding this comment.
handleEnterChat가 선언되어 있지만 현재 어떤 컴포넌트에도 전달/호출되지 않아(dead code) 유지보수에 혼란을 줄 수 있습니다. 조 선택 후 채팅 진입을 의도한 거라면 ChatTeamSelectModal에 onSelectTeam(또는 onEnterChat) 같은 콜백 prop을 추가해 연결하고, 모달 내부의 view === "chat" 전환 로직과 역할을 정리해 주세요. 의도가 모달 내 채팅 UI라면 이 핸들러/주석은 제거하는 게 안전합니다.
| /** | |
| * 조 선택하면 "채팅 페이지"로 이동 (입장만) | |
| * - chat은 "현재 경로 하위의 chat"으로 이동시키는 상대 경로 | |
| * - teamId/teamName을 쿼리로 넘겨서 다음 페이지에서 어떤 방인지 알게 함 | |
| */ | |
| const handleEnterChat = (team: ChatTeam) => { | |
| setIsChatTeamModalOpen(false); | |
| // 상대경로: 현재 페이지가 .../meeting 이면 .../meeting/chat 로 감 | |
| router.push( | |
| `chat?teamId=${team.teamId}&teamName=${encodeURIComponent(team.teamName)}` | |
| ); | |
| // ❗️만약 상대경로가 안 맞으면 절대경로로 바꿔야 함: | |
| // router.push(`/groups/${params.groupId}/meeting/${bookId}/chat?teamId=${team.teamId}`); | |
| }; |
| // Next가 params를 Record<string, string | string[]>로 주는 케이스가 있어서 안전빵 | ||
| const groupId = Array.isArray(params?.id) ? params?.id[0] : (params?.id as string | undefined); | ||
| const meetingId = Array.isArray(params?.meetingId) | ||
| ? params?.meetingId[0] | ||
| : (params?.meetingId as string | undefined); | ||
|
|
There was a problem hiding this comment.
groupId/meetingId를 params에서 파싱해두었지만 현재 로직에서 실제로 사용되지 않습니다(주석에만 등장). Next/TS eslint 설정에서는 unused 변수가 경고/에러가 될 수 있고, 읽는 사람 입장에서도 “여기서 API 호출을 하는가?” 혼동이 생깁니다. 아직 미사용이라면 제거하고, 곧 API 연동 예정이라면 handleSubmit/fetch 쪽에서 실제로 사용하도록 연결해 주세요.
| const handleAddTeam = () => { | ||
| setTeams((prev) => { | ||
| if (prev.length >= 7) return prev; | ||
| return [...prev, prev.length + 1]; | ||
| }); |
There was a problem hiding this comment.
팀 추가 로직에서 최대 팀 수를 7로 하드코딩하고 있는데, 같은 PR에서 MAX_TEAMS 상수를 이미 정의/사용하고 있습니다(TeamBoard는 MAX_TEAMS 기반). 한쪽만 변경되어 불일치가 생기지 않도록 여기서도 MAX_TEAMS를 사용해 주세요.
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
📌 개요 (Summary)
책장, 책장상세 페이지 API에 맞춰서 수정 -> dnd-kit 추가(스크롤 기능) -> 이후 조 편성 페이지 UI 제작
드래그로써 스크롤이 되는 기능을 추가하여 모바일 환경에서도 손 쉽게 조를 편성할 수 있게 만들었으며 조를 편성한 뒤, 확정을 하는 UI를 만듦으로써 오류와 무분별한 API 난사를 막았다.
테블릿, 데스크톱 크기일 때 채팅창을 자유롭게 배치할 수 있도록 움직일 수 있게 설계하였고 뒤에 있는 발제에 대한 것들이나 정렬하기 등의 기능도 함께 사용가능하도록 설계하였다. 또한 채팅창의 크기를 고려하여 모바일 환경에서는 전체화면이 되게끔 만들었으며 이때 Float가 채팅 전송을 막는 것을 방지하기 위해 Float의 위 올라간다.
없어져야할 페이지들 제거, 약간의 버그 수정, 폴더링 수정
-> 불필요한(중복/미사용) 페이지 제거, 폴더 구조 정리, 자잘한 UI/동작 버그 수정
🛠️ 변경 사항 (Changes)
📸 스크린샷 (Screenshots)
2026-02-20.014018.mp4
2026-02-20.014234.mp4
2026-02-20.015719.mp4