Repository navigation
Conversation
- RESPONSIVE_AUDIT_REPORT.md - docs/hyunil/responsive-audit.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- MeetingPageClient: 본문 t:px-0 → t:px-6 (태블릿에서 가장자리 붙음 해소, 3곳) - TeamSection: t:px-0 → t:px-6 - NoticeDetailPageClient: 래퍼 px-[18px] → px-4 t:px-6, 댓글 px-[18px] → px-0 (공지 본문/댓글 이중패딩 어긋남 정렬) - notice_detail: 본문 min-w-[300px] → min-w-0 t:min-w-[300px] (모바일 압박 해소) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- find-account/result/find-password: 카드 w-[766px] px-[120px] py-[99px] → w-full max-w-[766px] mx-auto px-6 t:px-[120px] py-10 t:py-[99px] - 부모 래퍼에 px-4 추가(모바일 카드 좌우 여백) - PrimaryButton: w-[258px] → w-full t:w-[258px] Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- setting_report_list: 항목 행 w-[1000px] → w-full max-w-[1000px] (모바일/태블릿 오버플로 방지) - setting_news_list: 날짜 컬럼 min-w-[180px] → min-w-[80px] t:min-w-[180px] Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- StoryDetailClient: 본문/댓글 컨테이너 px-5 → px-5 t:px-6 (태블릿 여백 일관성) - BookStoryInfiniteList 그리드는 의도된 다단 설계(min-[540px]/md/lg)라 lg:→d: 변환 시 1024~1439px에서 2열로 회귀 위험 → 변경 보류 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- ReportModal: 모달 w-[734px] p-[40px] → w-full max-w-[734px] p-5 t:p-[40px], 제출 영역 w-[614px] → w-full, 종류 버튼 4개 w-[144px] → flex-1 min-w-[100px] t:w-[144px] t:flex-none + 컨테이너 flex-wrap (모바일 2x2 줄바꿈) - NotificationDropdown: w-[364px] → w-[364px] max-w-[calc(100vw-32px)] (364px 미만 소형 폰에서 우측 넘침 방지) - notification_element.tsx는 미사용(dead) 코드라 제외 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- GroupsPageClient: mx-auto px-6 → px-4 t:px-6 (모바일 24→16px, 표준 래퍼 패턴 통일) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- refactor-409 반응형 일괄 수정(Batch A~E + 모임목록)의 모바일/태블릿/데스크탑 before/after 캡처 59장 - docs/hyunil/responsive-shots/README.md: 명명 규칙 + 화면↔배치 매핑 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 커스텀 회원 카드(인라인 article/구독버튼) 제거 → 디자인시스템 FollowItem 재사용 - FollowItem에 선택적 prop 추가(badge/subLabel/hideFollow), 기존 사용처 영향 없음 - 무효 클래스 hover:brightness-98 제거 → 유효한 hover 적용 (구독 버튼 hover:bg-primary-1/Subbrown-3, 행 hover:bg-Subbrown-5, 닉네임 group-hover:underline) - "더보기" 버튼 제거 → useInView sentinel 무한 스크롤(FollowList 패턴) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- FindAccountPageClient: 입력/버튼 래퍼에 w-full 추가 (비밀번호 찾기와 동일) → 입력칸 카드 전체폭, "아이디 찾기" 버튼 한 줄로 표시 - find-account-m-after 스크린샷 갱신 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat: 모임 상세 회원 목록 조회 UI 추가
refactor: 반응형 깨짐 일괄 점검·수정 (Batch A~E)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds responsive audit documentation, updates Tailwind padding and width classes across public and main screens, and introduces cursor-based club participant pagination with new API, service, hook, and follow-card support. ChangesResponsive layout sweep
Group participants pagination
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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.
Code Review
This pull request implements a comprehensive set of responsive layout fixes across the Next.js frontend based on a responsive layout audit, addressing issues like text touching screen edges on tablets and overflow on mobile devices. Additionally, it introduces a new "Group Participants" section on the group detail page, featuring infinite scrolling, follow/unfollow capabilities, and role badges. Feedback on the changes suggests using encodeURIComponent when linking to user profiles in FollowItem.tsx to prevent routing issues with special characters in nicknames.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| <div className="flex w-full max-w-[1040px] p-[20px] justify-between items-center rounded-[8px] border border-Subbrown-4 bg-White"> | ||
| <Link href={`/profile/${user.nickname}`} className="flex items-center gap-[12px] cursor-pointer"> | ||
| <div className="flex w-full max-w-[1040px] p-[20px] justify-between items-center gap-[12px] rounded-[8px] border border-Subbrown-4 bg-White transition-colors hover:bg-Subbrown-5"> | ||
| <Link href={`/profile/${user.nickname}`} className="group flex items-center gap-[12px] cursor-pointer min-w-0"> |
There was a problem hiding this comment.
닉네임에 특수문자나 공백, 한글 등이 포함되어 있을 경우 URL 경로가 깨지거나 잘못된 라우트로 연결될 수 있습니다. 안전한 라우팅을 위해 encodeURIComponent를 사용하여 닉네임을 인코딩하는 것이 좋습니다.
| <Link href={`/profile/${user.nickname}`} className="group flex items-center gap-[12px] cursor-pointer min-w-0"> | |
| <Link href={`/profile/${encodeURIComponent(user.nickname)}`} className="group flex items-center gap-[12px] cursor-pointer min-w-0"> |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/app/(main)/groups/[id]/GroupDetailClient.tsx (1)
131-143: 🚀 Performance & Scalability | 🔵 Trivial | ⚖️ Poor tradeoffFollow toggle has no optimistic feedback in this list.
useToggleFollowMutationperforms optimistic updates only on the recommend/story/follow caches, not onclubhomeKeys.participants. Here the participant button state changes only afteronSettledinvalidation refetches, so it feels unresponsive and a fast second click can fire an out-of-order toggle. Consider an optimisticsetQueryDataon the participants infinite cache, or disabling the button whiletoggleFollowMutation.isPending.🤖 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/app/`(main)/groups/[id]/GroupDetailClient.tsx around lines 131 - 143, The follow toggle in GroupDetailClient’s handleToggleFollow is only updating clubhomeKeys.participants after onSettled, so the participant row/button has no immediate feedback and can be clicked again before the refetch lands. Add an optimistic update for the participants infinite cache with queryClient.setQueryData in handleToggleFollow (or in the toggleFollowMutation lifecycle) so the local participant follow state flips immediately, and keep the invalidation as a fallback; alternatively also disable the follow button while toggleFollowMutation.isPending to prevent out-of-order toggles.
🤖 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/components/common/find-account/PrimaryButton.tsx`:
- Line 11: The new w-full behavior in PrimaryButton causes paired buttons in
horizontal flex containers to overflow on mobile. Update the consuming button
container in FindAccountPageClient (and any similar callers) to use flex-wrap or
switch to a flex-col layout below the breakpoint, so two PrimaryButton siblings
can stack instead of forcing viewport overflow. Keep the existing PrimaryButton
sizing, and fix the layout at the parent container level where the buttons are
rendered together.
---
Nitpick comments:
In `@src/app/`(main)/groups/[id]/GroupDetailClient.tsx:
- Around line 131-143: The follow toggle in GroupDetailClient’s
handleToggleFollow is only updating clubhomeKeys.participants after onSettled,
so the participant row/button has no immediate feedback and can be clicked again
before the refetch lands. Add an optimistic update for the participants infinite
cache with queryClient.setQueryData in handleToggleFollow (or in the
toggleFollowMutation lifecycle) so the local participant follow state flips
immediately, and keep the invalidation as a fallback; alternatively also disable
the follow button while toggleFollowMutation.isPending to prevent out-of-order
toggles.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7a1cf522-2569-43b4-8e3a-74d4b56c8e20
⛔ Files ignored due to path filters (59)
docs/hyunil/responsive-shots/bookcase-detail-d-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/bookcase-detail-d-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/bookcase-detail-m-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/bookcase-detail-m-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/bookcase-detail-t-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/bookcase-detail-t-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/find-account-d-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/find-account-d-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/find-account-m-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/find-account-m-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/find-account-t-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/find-account-t-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/find-password-d-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/find-password-d-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/find-password-m-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/find-password-m-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/find-password-t-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/find-password-t-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/groups-d-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/groups-d-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/groups-m-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/groups-m-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/groups-t-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/groups-t-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/meeting-d-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/meeting-d-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/meeting-m-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/meeting-m-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/meeting-t-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/meeting-t-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/notice-detail-d-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/notice-detail-d-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/notice-detail-m-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/notice-detail-m-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/notice-detail-t-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/notice-detail-t-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/notif-dropdown-d-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/notif-dropdown-m-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/notif-dropdown-t-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/report-modal-m-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/report-modal-m-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/setting-news-d-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/setting-news-d-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/setting-news-m-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/setting-news-m-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/setting-news-t-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/setting-news-t-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/setting-report-d-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/setting-report-d-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/setting-report-m-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/setting-report-m-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/setting-report-t-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/setting-report-t-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/story-detail-d-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/story-detail-d-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/story-detail-m-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/story-detail-m-before.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/story-detail-t-after.pngis excluded by!**/*.pngdocs/hyunil/responsive-shots/story-detail-t-before.pngis excluded by!**/*.png
📒 Files selected for processing (24)
RESPONSIVE_AUDIT_REPORT.mddocs/hyunil/responsive-audit.mddocs/hyunil/responsive-shots/README.mdsrc/app/(main)/groups/GroupsPageClient.tsxsrc/app/(main)/groups/[id]/GroupDetailClient.tsxsrc/app/(main)/groups/[id]/bookcase/[bookId]/meeting/MeetingPageClient.tsxsrc/app/(main)/groups/[id]/notice/[noticeId]/NoticeDetailPageClient.tsxsrc/app/(main)/stories/[id]/StoryDetailClient.tsxsrc/app/(public)/find-account/FindAccountPageClient.tsxsrc/app/(public)/find-account/result/FindAccountResultPageClient.tsxsrc/app/(public)/find-password/FindPasswordPageClient.tsxsrc/components/base-ui/Bookcase/bookid/TeamSection.tsxsrc/components/base-ui/Group/notice_detail.tsxsrc/components/base-ui/Profile/Follow/FollowItem.tsxsrc/components/base-ui/Settings/setting_news_list.tsxsrc/components/base-ui/Settings/setting_report_list.tsxsrc/components/base-ui/home/Club/home_bookclub.tsxsrc/components/common/find-account/PrimaryButton.tsxsrc/components/common/modals/report-block/ReportModal.tsxsrc/components/layout/NotificationDropdown.tsxsrc/hooks/queries/useClubhomeQueries.tssrc/lib/api/endpoints/Clubs.tssrc/services/clubService.tssrc/types/groups/grouphome.ts
| return ( | ||
| <button | ||
| className={`flex w-[258px] h-[48px] px-[16px] py-[12px] justify-center items-center rounded-lg bg-primary-1 text-White body_1_1 hover:bg-primary-3 transition-colors cursor-pointer ${className}`} | ||
| className={`flex w-full t:w-[258px] h-[48px] px-[16px] py-[12px] justify-center items-center rounded-lg bg-primary-1 text-White body_1_1 hover:bg-primary-3 transition-colors cursor-pointer ${className}`} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Two full-width buttons in a horizontal flex row will overflow on mobile.
PrimaryButton now uses w-full below the t: breakpoint. It's consumed in pairs inside a flex row with gap-[10px] (e.g., FindAccountPageClient.tsx:85). Without flex-wrap or flex-col, two w-full siblings will overflow the viewport on mobile.
Add flex-wrap or switch to flex-col on the parent button container in the consuming pages.
🔧 Suggested fix for button container
In consuming pages (e.g., FindAccountPageClient.tsx:85):
- <div className="flex items-center gap-[10px] justify-center w-full">
+ <div className="flex flex-col t:flex-row items-center gap-[10px] justify-center w-full">🤖 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/components/common/find-account/PrimaryButton.tsx` at line 11, The new
w-full behavior in PrimaryButton causes paired buttons in horizontal flex
containers to overflow on mobile. Update the consuming button container in
FindAccountPageClient (and any similar callers) to use flex-wrap or switch to a
flex-col layout below the breakpoint, so two PrimaryButton siblings can stack
instead of forcing viewport overflow. Keep the existing PrimaryButton sizing,
and fix the layout at the parent container level where the buttons are rendered
together.
충돌 해결: src/components/common/modals/report-block/ReportModal.tsx - 신고 API 개편(main: 사유/REPORT_REASONS/reason)과 반응형 수정(dev: flex-wrap/flex-1 버튼, w-full max-w 모달)을 모두 반영 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Chore/merge main into dev
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/components/common/modals/report-block/ReportModal.tsx (1)
25-44: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winAvoid
setStatedirectly inuseEffectbodyLines 34 and 37 call
setReason(defaultReason)synchronously inside the effect, which triggers cascading renders. The ESLint rulereact-hooks/set-state-in-effectcorrectly flags this.Move the reset logic into the event flow that causes the open/close transition (e.g., reset in
onCloseoronOpencallbacks) rather than reacting toisOpenin an effect. If that’s not feasible, split into two effects: one for the Escape key subscription, one for the reset—but prefer callback-based reset.♻️ Suggested refactor
- useEffect(() => { - const handleEscape = (e: KeyboardEvent) => { - if (e.key === "Escape" && isOpen) { - onClose(); - } - }; - - if (isOpen) { - document.addEventListener("keydown", handleEscape); - setReason(defaultReason); - } else { - // Reset state when closed - setReason(defaultReason); - setReportContent(""); - } - - return () => { - document.removeEventListener("keydown", handleEscape); - }; - }, [isOpen, onClose, defaultReason]); + useEffect(() => { + const handleEscape = (e: KeyboardEvent) => { + if (e.key === "Escape" && isOpen) { + onClose(); + } + }; + + if (isOpen) { + document.addEventListener("keydown", handleEscape); + } + + return () => { + document.removeEventListener("keydown", handleEscape); + }; + }, [isOpen, onClose]); + + // Reset state when modal closes + useEffect(() => { + if (!isOpen) { + setReason(defaultReason); + setReportContent(""); + } + }, [isOpen, defaultReason]);🤖 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/components/common/modals/report-block/ReportModal.tsx` around lines 25 - 44, The `ReportModal` effect is doing synchronous state resets with `setReason` and `setReportContent` inside `useEffect`, which triggers the `react-hooks/set-state-in-effect` warning. Move the reset logic out of the `useEffect` in `ReportModal` and into the open/close event flow, such as the `onClose` path or whatever callback toggles `isOpen`; keep the effect only for the Escape key listener, or split it so the listener and reset are handled separately.
🤖 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/components/common/modals/report-block/ReportModal.tsx`:
- Around line 25-44: The `ReportModal` effect is doing synchronous state resets
with `setReason` and `setReportContent` inside `useEffect`, which triggers the
`react-hooks/set-state-in-effect` warning. Move the reset logic out of the
`useEffect` in `ReportModal` and into the open/close event flow, such as the
`onClose` path or whatever callback toggles `isOpen`; keep the effect only for
the Escape key listener, or split it so the listener and reset are handled
separately.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3727172a-d216-4ec2-b277-8f1bbab87e29
📒 Files selected for processing (1)
src/components/common/modals/report-block/ReportModal.tsx
Summary by CodeRabbit
New Features
Bug Fixes
Documentation