Skip to content

[Fix] WTH-355: 어드민 QA - 게시판 관리 수정#83

Merged
JIN921 merged 12 commits intodevelopfrom
fix/WTH-355-어드민-QA-게시판-관리-수정
Apr 29, 2026

Hidden character warning

The head ref may contain hidden characters: "fix/WTH-355-\uc5b4\ub4dc\ubbfc-QA-\uac8c\uc2dc\ud310-\uad00\ub9ac-\uc218\uc815"
Merged

[Fix] WTH-355: 어드민 QA - 게시판 관리 수정#83
JIN921 merged 12 commits intodevelopfrom
fix/WTH-355-어드민-QA-게시판-관리-수정

Conversation

@JIN921
Copy link
Copy Markdown
Collaborator

@JIN921 JIN921 commented Apr 29, 2026

✅ PR 유형

어떤 변경 사항이 있었나요?

  • 새로운 기능 추가
  • 버그 수정
  • 코드에 영향을 주지 않는 변경사항(오타 수정, 탭 사이즈 변경, 변수명 변경)
  • 코드 리팩토링
  • 주석 추가 및 수정
  • 문서 수정
  • 빌드 부분 혹은 패키지 매니저 수정
  • 파일 혹은 폴더명 수정
  • 파일 혹은 폴더 삭제

📌 관련 이슈번호

  • WTH-355 (어드민 QA — 게시판 관리 수정 외)

✅ Key Changes

게시판 관리

  • 게시판 카드에 댓글 허용 스위치 추가, NOTICE에서도 토글 노출 (ALL 게시판만 제외)
  • 고정 게시판 렌더링 순서 보장: 전체 → 공지사항 → 커스텀
  • 공지 게시판 댓글 토글 PATCH 시 name 미포함 — 토글 전용 body/API/훅 분리 (UpdateBoardCommentBody, adminBoardApi.updateBoardComment, useToggleBoardCommentMutation)
  • boardMapper에서 NOTICE의 commentEnabled를 보존하도록 수정 — 토글 후 invalidate 시 화면 미반영 캐시 문제 해결
  • 빠른 연속 토글 시 race 차단: in-flight boardId 추적해 해당 스위치만 disabled, mutation onSuccess에서 await invalidateQueries 후 pending 해제
  • 게시판 생성/수정/삭제/댓글 토글 성공 시 success 토스트 추가
  • BoardAdminSkeleton 분리, mutation 훅에 mutationKey 부여 (devtools 식별용)
  • DUPLICATE_NAME 에러 처리/카드 prop 전달 헬퍼 추출
  • 알림 문구 정리

📸 스크린샷 or 실행영상

Video.Project.4.mp4

🎸 기타 사항 or 추가 코멘트

  • useUpdateBoardMutation은 일반 수정 경로에서, useToggleBoardCommentMutation은 댓글 토글 경로에서 분리 사용됩니다. 동일 PATCH 엔드포인트지만 body 타입과 race 처리(await invalidateQueries)가 달라 의도적으로 훅을 분리했습니다.

하 이전 #81 pr에서 수정사항 충돌 날 거 백퍼라 해당 브랜치로 분기 팠더니 file changed 난리네요.........
아래 12개 파일만 봐주시면 됩니다 정말 암쏘쏘리..

image

🤖 Generated with Claude Code

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 리더 권한 이양 기능 추가
    • 게시판 댓글 토글 기능 개선
    • 기수 선택 UI 재설계 (드래그 스크롤, 전체 선택 옵션 추가)
  • 개선 사항

    • 전체 서비스에서 "관리자" 용어를 "운영진"으로 통일
    • 게시판 관리 페이지 로딩 상태 개선
    • 멤버 관리 페이지 테이블 레이아웃 최적화
    • 오류 처리 및 유효성 검사 강화
  • 버그 수정

    • UI 컴포넌트 스타일 미세 조정 (Switch, Table)

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

Warning

Rate limit exceeded

@JIN921 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 29 minutes and 39 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 374e3cf0-274f-40f9-8fb8-2e3f0c2eb398

📥 Commits

Reviewing files that changed from the base of the PR and between e66fa32 and 0a33ce8.

📒 Files selected for processing (7)
  • src/components/admin/member/CardinalPillList.tsx
  • src/components/admin/member/modal/MemberDetailModal.tsx
  • src/hooks/queries/admin/useCreateBoardMutation.ts
  • src/hooks/queries/admin/useDeleteBoardMutation.ts
  • src/hooks/queries/admin/useToggleBoardCommentMutation.ts
  • src/hooks/queries/admin/useUpdateBoardMutation.ts
  • src/hooks/queries/admin/useUpdateBoardOrderMutation.ts
📝 Walkthrough

Walkthrough

이 PR은 애플리케이션 전역에서 "관리자"라는 용어를 "운영진"으로 변경하고, 새로운 리더 이양 기능, 게시판 댓글 토글 기능, 개선된 카디널 선택 UI(CardinalPillList), 벌크 뮤테이션 유틸리티를 추가합니다. 기존 회원 역할 시스템을 재구성하고 API 및 상태 관리를 확장합니다.

Changes

Cohort / File(s) Summary
용어 업데이트 (관리자 → 운영진)
src/app/(public)/landing/page.tsx, src/components/attendance/AttendanceTodayCard.tsx, src/components/home/HomeBoardContent.tsx, src/components/layout/Footer.tsx, src/components/layout/header/DefaultActions.tsx, src/components/layout/header/Header.tsx, src/components/layout/header/MobileNavSheet.tsx, src/components/admin/layout/LNB.tsx, src/components/admin/board/modal/constants.ts, src/constants/home/tutorial.tsx, src/constants/landing/landing.ts, src/constants/term.ts
전역적으로 "관리자"를 "운영진"으로 변경. 정책, 네비게이션, UI 레이블, 튜토리얼 등 모든 사용자 표시 텍스트 업데이트
Admin 레이아웃 & 데이터 하이드레이션
src/app/(private)/[clubId]/admin/layout.tsx
서버사이드에서 getDashboard() 호출하여 사용자 정보와 클럽 정보 추출. 컴포넌트 트리를 UserHydrator로 래핑하여 userInfoclubInfo props 전달
회원 관리 시스템 (컴포넌트)
src/components/admin/member/CardinalCard.tsx, src/components/admin/member/CardinalPillList.tsx, src/components/admin/member/MemberPageContent.tsx, src/components/admin/member/MemberTable.tsx, src/components/admin/member/MemberTopBar.tsx, src/components/admin/member/modal/MemberDetailModal.tsx
CardinalPillList 컴포넌트 추가, 기존 드래그/모달 로직 교체. 리더 이양 콜백 추가. 활동 기수 표시 UI 개선. 테이블 헤더 스티키 처리 및 상태 범례 제거
회원 관리 시스템 (상수 & 유틸리티)
src/constants/admin/memberDetailModal.constants.ts, src/constants/admin/memberTable.constants.ts, src/constants/admin/memberTopBar.constants.ts, src/utils/admin/memberMapper.ts
리더 이양 액션 추가. 상태 레이블 및 역할 필드 제거. role 필드 제거, position역할로 통일. ROLE_MAP 내보내기
회원 역할 타입 정의
src/types/admin/member.d.ts
Member 인터페이스에서 role 필드 제거
게시판 관리 시스템
src/components/admin/board/BoardAdminSkeleton.tsx, src/components/admin/board/BoardCard.tsx, src/components/admin/board/BoardPageContent.tsx, src/components/admin/board/SortableBoardCard.tsx
BoardAdminSkeleton 추가. 댓글 토글 pending 상태 관리. kind 기반 조건부 렌더링. 스켈레톤 로딩 UI 통합
게시판 API & 뮤테이션
src/lib/apis/adminBoard.ts, src/hooks/queries/admin/useCreateBoardMutation.ts, src/hooks/queries/admin/useDeleteBoardMutation.ts, src/hooks/queries/admin/useToggleBoardCommentMutation.ts, src/hooks/queries/admin/useUpdateBoardMutation.ts, src/hooks/queries/admin/useUpdateBoardOrderMutation.ts, src/utils/admin/boardMapper.ts
useToggleBoardCommentMutation 훅 추가. 모든 게시판 뮤테이션에 mutationKey 추가. updateBoardComment API 메서드 추가. commentEnabled 매핑 로직 업데이트
카디널 & 리더 이양 기능
src/components/admin/CardinalDropdown.tsx, src/components/admin/index.ts, src/components/admin/schedule/general/SchedulePageContent.tsx, src/hooks/mutations/admin/useAdminCardinalMutations.ts, src/hooks/mutations/admin/useAdminMemberMutations.ts, src/hooks/mutations/admin/index.ts, src/lib/apis/cardinal.ts, src/lib/apis/adminMember.ts
onSelectAll 콜백 추가. 새 setCurrentCardinal 뮤테이션. 새 transferLead 뮤테이션 및 API 메서드 추가. 카디널 선택 UI 개선
벌크 뮤테이션 유틸리티
src/utils/shared/runBulkMutation.ts, src/utils/shared/getApiErrorCode.ts, src/utils/shared/index.ts
runBulkMutation 헬퍼 추가. 새 getApiErrorCode 유틸리티 추가. 공유 유틸리티로 내보내기
에러 코드 & 파싱 유틸리티
src/constants/errorCode.ts, src/utils/admin/parseCardinals.ts
새 에러 코드(MEMBER_ROLE_ERROR_CODE, CARDINAL_ERROR_CODE) 추가. 새 parseCardinals 유틸리티 추가
사용자 저장소 & 드래그 재정렬
src/stores/useUserStore.ts, src/hooks/admin/useBoardDragReorder.ts
role 타입을 공유 Role 타입으로 마이그레이션. 새 setRole 액션 추가. 드래그 재정렬 실패시 쿼리 무효화 로직으로 변경
UI 컴포넌트 & 레이아웃
src/components/ui/Switch.tsx, src/components/ui/table.tsx
Switch 의사 요소 inset-x 값 조정. 테이블 드래그 스크롤 로직 제거, wrapperClassName prop 추가
서버 액션 & 게시판 타입
src/lib/actions/club.ts, src/types/admin/board.d.ts
revalidateDashboard 서버 액션 추가. 게시판 commentEnabled JSDoc 주석 업데이트

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

🔨 Refactor, 📬 API

Suggested reviewers

  • nabbang6
  • dalzzy
  • woneeeee

Poem

🐰 토끼의 축하 노래 🎉

운영진의 시대가 밝아왔고,
카디널 선택은 더욱 우아해지니,
리더 이양과 댓글 토글로 완성되어,
회원 관리는 새로운 옷을 입었네!
벌크 뮤테이션의 힘으로 빠르게,
모두가 함께하는 완벽한 구조여! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.64% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경 사항인 게시판 관리(댓글 허용 스위치, 렌더링 순서, 토글 로직)를 명확히 반영하고 있습니다.
Description check ✅ Passed PR 설명이 템플릿 구조를 따르고 있으며, 변경 사항 유형, 이슈 번호, 주요 변경 사항, 스크린샷, 추가 설명이 모두 포함되어 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/WTH-355-어드민-QA-게시판-관리-수정

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.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 29 minutes and 39 seconds.

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

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/app/(private)/(auth)/club/create/layout.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/app/(private)/(auth)/club/select/page.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/app/(private)/(auth)/hub/page.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/app/(private)/(intro)/home/layout.tsx

오류: ENOENT: no such file or directory, open 'src/app/(private)/(intro)/home/layout.tsx'


src/app/(private)/(main)/attendance/history/page.tsx

오류: ENOENT: no such file or directory, open 'src/app/(private)/(main)/attendance/history/page.tsx'


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Build: 통과

🎉 모든 검증을 통과했습니다!

@JIN921 JIN921 self-assigned this Apr 29, 2026
@JIN921 JIN921 requested review from dalzzy, nabbang6 and woneeeee April 29, 2026 04:00
@JIN921 JIN921 changed the title [Fix] WTH-355: 어드민 QA - 게시판/멤버/기수 관리 수정 [Fix] WTH-355: 어드민 QA - 게시판 관리 수정 Apr 29, 2026
@JIN921 JIN921 added the 🐞 BugFix Something isn't working label Apr 29, 2026
Copy link
Copy Markdown
Collaborator

@nabbang6 nabbang6 left a comment

Choose a reason for hiding this comment

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

확인했습니다!!! 고생하셧어용 굿꿋

Comment thread src/components/admin/board/BoardCard.tsx
Comment thread src/hooks/admin/useBoardDragReorder.ts
Comment thread src/hooks/queries/admin/useUpdateBoardOrderMutation.ts
Comment thread src/components/admin/board/BoardPageContent.tsx
@JIN921 JIN921 changed the base branch from main to develop April 29, 2026 06:32
Copy link
Copy Markdown
Member

@woneeeee woneeeee left a comment

Choose a reason for hiding this comment

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

수고하셨습니당!!

Comment thread src/components/admin/board/BoardPageContent.tsx
Copy link
Copy Markdown
Member

@dalzzy dalzzy left a comment

Choose a reason for hiding this comment

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

확인햇습니다~!! 어드민 qa하느라 고생하셧습니다,,,,,,

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/app/(private)/[clubId]/admin/layout.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/app/(public)/landing/page.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/CardinalDropdown.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/board/BoardAdminSkeleton.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/board/BoardCard.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-leenq53y7-weethsite-4975s-projects.vercel.app

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Build: 통과

🎉 모든 검증을 통과했습니다!

Copy link
Copy Markdown

@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: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/layout/header/Header.tsx (1)

71-77: ⚠️ Potential issue | 🟠 Major

운영진 버튼에 isAdmin 노출 가드가 없어 일반 사용자에게도 보입니다.
현재 Line 71~77 블록은 isAdmin 조건 없이 /admin 이동 버튼을 렌더링합니다. 같은 모바일 내비게이션(MobileNavSheet)에서는 운영진 항목을 isAdmin으로 필터링하고 있어 UX/권한 진입점이 불일치합니다.

🔧 제안 수정안
 import { useParams, usePathname, useRouter } from 'next/navigation';
+import { useIsAdmin } from '@/hooks/shared';
@@
   const { clubId } = useParams<{ clubId: string }>();
+  const { isAdmin } = useIsAdmin();
@@
-        {isMain && clubId && (
+        {isMain && clubId && isAdmin && (
           <div className="flex items-center justify-center gap-200">
             <button
               type="button"
               aria-label="운영진 페이지로 이동"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/layout/header/Header.tsx` around lines 71 - 77, The admin
navigation button in Header.tsx is rendered when isMain && clubId but lacks an
isAdmin guard; update the render condition to require isAdmin (e.g., change the
JSX conditional to isMain && clubId && isAdmin) or wrap the button inside an
explicit if (isAdmin) check so only admins see the button that calls
router.push(`/${clubId}/admin`), keeping behavior consistent with
MobileNavSheet's isAdmin filtering.
🧹 Nitpick comments (2)
src/utils/shared/runBulkMutation.ts (1)

14-23: 빈 작업 목록에서도 성공 토스트가 표시됩니다.

Line 14~22 기준 args.length === 0일 때도 성공 토스트가 떠서 사용자에게 오해를 줄 수 있습니다. 빈 입력은 조용히 종료하는 가드를 권장합니다.

권장 수정안
 export async function runBulkMutation<TArg, TResult>(
   args: TArg[],
   mutateAsync: (arg: TArg) => Promise<TResult>,
   messages: BulkMutationMessages,
   resolveErrorMessage?: (errors: unknown[]) => string | undefined,
 ): Promise<void> {
+  if (args.length === 0) return;
+
   const results = await Promise.allSettled(args.map(mutateAsync));
   const errors = results
     .filter((r): r is PromiseRejectedResult => r.status === 'rejected')
     .map((r) => r.reason);
   if (errors.length > 0) {
     toastError(resolveErrorMessage?.(errors) ?? messages.error);
     return;
   }
   toastSuccess(messages.success);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/shared/runBulkMutation.ts` around lines 14 - 23, Add an
early-return guard to avoid showing a success toast for an empty input: in
runBulkMutation (the function that calls Promise.allSettled with
args.map(mutateAsync)), check if args.length === 0 and return quietly before
running Promise.allSettled so neither toastSuccess nor toastError is invoked;
keep existing error handling (results filter/map, resolveErrorMessage, messages)
unchanged for non-empty args.
src/lib/apis/adminBoard.ts (1)

56-62: UpdateBoardCommentBodyOmit으로 파생하는 편이 안전합니다.

지금 선언은 UpdateBoardBody와 거의 동일해서, 이후 필드 추가나 이름 변경이 들어오면 두 타입이 쉽게 어긋납니다. name만 제외한 파생 타입으로 묶어두면 PATCH body 계약이 한 군데에서만 관리됩니다.

♻️ 제안 코드
-/** 댓글 허용 토글 전용 body. 공지 게시판도 안전하게 PATCH 가능하도록 name 미포함 */
-export interface UpdateBoardCommentBody {
-  description: string;
-  commentEnabled: boolean;
-  writePermission: AdminBoardWritePermission;
-  isPrivate: boolean;
-}
+/** 댓글 허용 토글 전용 body. 공지 게시판도 안전하게 PATCH 가능하도록 name 미포함 */
+export type UpdateBoardCommentBody = Omit<UpdateBoardBody, 'name'>;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/apis/adminBoard.ts` around lines 56 - 62, UpdateBoardCommentBody is
duplicating UpdateBoardBody and will diverge when fields change; replace the
manual interface with a derived type using Omit to exclude only the name field
(e.g., make UpdateBoardCommentBody = Omit<UpdateBoardBody, 'name'>) so the PATCH
body contract is maintained in one place and future field additions/renames to
UpdateBoardBody automatically apply; update any imports/exports that reference
UpdateBoardCommentBody accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/admin/board/BoardPageContent.tsx`:
- Around line 323-324: Fix the spacing in the user-facing sentence inside
BoardPageContent.tsx: change the string "게시판 추가는 최대 {MAX_CUSTOM_BOARDS}개까지 가능
합니다." to use the correct combined form "가능합니다" so it reads "게시판 추가는 최대
{MAX_CUSTOM_BOARDS}개까지 가능합니다."; update the JSX where MAX_CUSTOM_BOARDS is
rendered (inside the <p className="typo-body2 text-text-alternative min-w-0
flex-1">) to replace the spaced "가능 합니다" with "가능합니다".

In `@src/components/admin/member/CardinalPillList.tsx`:
- Line 4: CardinalPillList currently imports AddCardinalButton,
AddCardinalModal, and CardinalCard via the admin barrel which creates a circular
dependency through admin/index re-export; update the import in
CardinalPillList.tsx to stop using the barrel and instead import
AddCardinalButton, AddCardinalModal, and CardinalCard directly from each
component's own module (replace the single '@/components/admin' barrel import
with explicit per-component imports) to break the cycle.

In `@src/components/admin/member/MemberTable.tsx`:
- Around line 89-90: Remove the hardcoded `max-h-[600px]` in the Table's
`wrapperClassName` and replace it with the appropriate design-token class (use
the existing height/max-height token class from our design tokens) so the Table
component (`Table` with `wrapperClassName`) uses token-based sizing; if no
suitable token exists, stop and request agreement to add a new design token (do
not introduce a new hardcoded value), then update the `wrapperClassName` to use
that new token and keep `TableHeader` classes unchanged.

In `@src/components/admin/member/modal/MemberDetailModal.tsx`:
- Around line 77-82: Header cardinal display in MemberDetailModal incorrectly
uses parseInt on member.cardinal (truncating multi-cardinal strings like "1,
2"); replace that logic with the same helper used elsewhere (e.g.,
parseCardinals) so the header renders the full cardinal string and keep the
fallback ('-') and the suffix '기' consistent; locate the span in
MemberDetailModal that currently reads {parseInt(member.cardinal, 10) ||
member.cardinal || '-'}기 and swap to using parseCardinals(member.cardinal) (or a
trimmed/normalized value from the same helper) while preserving existing styling
and fallback behavior.

In `@src/components/ui/Switch.tsx`:
- Line 17: In Switch.tsx replace the hardcoded utility before:inset-x-[2px]
inside the class string with the appropriate existing spacing/design-token class
(e.g., before:inset-x-0.5 or whatever project spacing token maps to 2px) so
tokens are used first; if no token exists, do not hardcode—propose and add a new
spacing token via design token agreement, then use that new token class in place
of before:inset-x-[2px].

In `@src/constants/admin/memberDetailModal.constants.ts`:
- Around line 49-62: The actions array shows a "change to admin" action for
LEADs which always fails (LEAD_TRANSFER_ONLY); update the logic around
memberRole/isAdmin to detect LEAD (e.g., const isLead = memberRole === 'LEAD')
and do not render the role-change action for leads (or render a
disabled/alternative action) so the onChangeRole handler is not exposed for
LEADs; adjust the actions construction (referencing memberRole, isAdmin,
isBanned, actions, onChangeRole) so only non-LEAD, non-admin members get the
"운영진으로 변경" action and LEADs don't show a broken button.

In `@src/constants/term.ts`:
- Line 61: Fix the awkward Korean particle usage in the term strings: locate
occurrences of the phrase like '동아리 운영상 필요한' (e.g. the string starting with
'“동아리 운영진”란 동아리를 생성하거나 회사 또는 동아리 운영상 필요한 권한을 부여받은 이용자를 말합니다.') and change '운영상
필요한' to '운영에 필요한'; verify similar lines (the other occurrences noted) are
updated consistently and keep the surrounding wording (e.g., '권한을 부여받은 이용자/자를
말합니다.') uniform across the constants.

In `@src/hooks/queries/admin/useToggleBoardCommentMutation.ts`:
- Around line 9-17: The declared callback type MutationCallbacks<AxiosError> in
useToggleBoardCommentMutation is inconsistent with the plain Error thrown in
mutationFn when clubId is missing; update the signature to
MutationCallbacks<unknown> (or MutationCallbacks<Error>) so the generic matches
actual thrown errors across this hook (and mirror the same change in sibling
hooks like useCreateBoardMutation/useDeleteBoardMutation), or alternatively
replace the thrown Error in mutationFn with an AxiosError (imported from axios)
so it matches MutationCallbacks<AxiosError>; adjust
useToggleBoardCommentMutation, its mutationFn and the MutationCallbacks generic
accordingly for consistency.

---

Outside diff comments:
In `@src/components/layout/header/Header.tsx`:
- Around line 71-77: The admin navigation button in Header.tsx is rendered when
isMain && clubId but lacks an isAdmin guard; update the render condition to
require isAdmin (e.g., change the JSX conditional to isMain && clubId &&
isAdmin) or wrap the button inside an explicit if (isAdmin) check so only admins
see the button that calls router.push(`/${clubId}/admin`), keeping behavior
consistent with MobileNavSheet's isAdmin filtering.

---

Nitpick comments:
In `@src/lib/apis/adminBoard.ts`:
- Around line 56-62: UpdateBoardCommentBody is duplicating UpdateBoardBody and
will diverge when fields change; replace the manual interface with a derived
type using Omit to exclude only the name field (e.g., make
UpdateBoardCommentBody = Omit<UpdateBoardBody, 'name'>) so the PATCH body
contract is maintained in one place and future field additions/renames to
UpdateBoardBody automatically apply; update any imports/exports that reference
UpdateBoardCommentBody accordingly.

In `@src/utils/shared/runBulkMutation.ts`:
- Around line 14-23: Add an early-return guard to avoid showing a success toast
for an empty input: in runBulkMutation (the function that calls
Promise.allSettled with args.map(mutateAsync)), check if args.length === 0 and
return quietly before running Promise.allSettled so neither toastSuccess nor
toastError is invoked; keep existing error handling (results filter/map,
resolveErrorMessage, messages) unchanged for non-empty args.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8f3fe815-ecc8-4425-a1e4-f5680d5e3084

📥 Commits

Reviewing files that changed from the base of the PR and between 8c7007b and e66fa32.

📒 Files selected for processing (55)
  • src/app/(private)/[clubId]/admin/layout.tsx
  • src/app/(public)/landing/page.tsx
  • src/components/admin/CardinalDropdown.tsx
  • src/components/admin/board/BoardAdminSkeleton.tsx
  • src/components/admin/board/BoardCard.tsx
  • src/components/admin/board/BoardPageContent.tsx
  • src/components/admin/board/SortableBoardCard.tsx
  • src/components/admin/board/modal/constants.ts
  • src/components/admin/index.ts
  • src/components/admin/layout/LNB.tsx
  • src/components/admin/member/CardinalCard.tsx
  • src/components/admin/member/CardinalPillList.tsx
  • src/components/admin/member/MemberPageContent.tsx
  • src/components/admin/member/MemberTable.tsx
  • src/components/admin/member/MemberTopBar.tsx
  • src/components/admin/member/modal/MemberDetailModal.tsx
  • src/components/admin/schedule/general/SchedulePageContent.tsx
  • src/components/attendance/AttendanceTodayCard.tsx
  • src/components/home/HomeBoardContent.tsx
  • src/components/layout/Footer.tsx
  • src/components/layout/header/DefaultActions.tsx
  • src/components/layout/header/Header.tsx
  • src/components/layout/header/MobileNavSheet.tsx
  • src/components/ui/Switch.tsx
  • src/components/ui/table.tsx
  • src/constants/admin/memberDetailModal.constants.ts
  • src/constants/admin/memberTable.constants.ts
  • src/constants/admin/memberTopBar.constants.ts
  • src/constants/errorCode.ts
  • src/constants/home/tutorial.tsx
  • src/constants/landing/landing.ts
  • src/constants/term.ts
  • src/hooks/admin/useBoardDragReorder.ts
  • src/hooks/mutations/admin/index.ts
  • src/hooks/mutations/admin/useAdminCardinalMutations.ts
  • src/hooks/mutations/admin/useAdminMemberMutations.ts
  • src/hooks/queries/admin/index.ts
  • src/hooks/queries/admin/useCreateBoardMutation.ts
  • src/hooks/queries/admin/useDeleteBoardMutation.ts
  • src/hooks/queries/admin/useToggleBoardCommentMutation.ts
  • src/hooks/queries/admin/useUpdateBoardMutation.ts
  • src/hooks/queries/admin/useUpdateBoardOrderMutation.ts
  • src/lib/actions/club.ts
  • src/lib/apis/adminBoard.ts
  • src/lib/apis/adminMember.ts
  • src/lib/apis/cardinal.ts
  • src/stores/useUserStore.ts
  • src/types/admin/board.d.ts
  • src/types/admin/member.d.ts
  • src/utils/admin/boardMapper.ts
  • src/utils/admin/memberMapper.ts
  • src/utils/admin/parseCardinals.ts
  • src/utils/shared/getApiErrorCode.ts
  • src/utils/shared/index.ts
  • src/utils/shared/runBulkMutation.ts
💤 Files with no reviewable changes (1)
  • src/types/admin/member.d.ts

Comment on lines 323 to +324
<p className="typo-body2 text-text-alternative min-w-0 flex-1">
추가 게시판은 최대 {MAX_CUSTOM_BOARDS}개입니다.
게시판 추가는 최대 {MAX_CUSTOM_BOARDS}개까지 가능 합니다.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

안내 문구 띄어쓰기를 다듬어 주세요.

가능 합니다가능합니다로 붙여 쓰는 편이 자연스럽습니다. PR에서 문구를 정리하는 흐름이라 여기만 남으면 눈에 띕니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/board/BoardPageContent.tsx` around lines 323 - 324, Fix
the spacing in the user-facing sentence inside BoardPageContent.tsx: change the
string "게시판 추가는 최대 {MAX_CUSTOM_BOARDS}개까지 가능 합니다." to use the correct combined
form "가능합니다" so it reads "게시판 추가는 최대 {MAX_CUSTOM_BOARDS}개까지 가능합니다."; update the
JSX where MAX_CUSTOM_BOARDS is rendered (inside the <p className="typo-body2
text-text-alternative min-w-0 flex-1">) to replace the spaced "가능 합니다" with
"가능합니다".

Comment thread src/components/admin/member/CardinalPillList.tsx Outdated
Comment on lines +89 to +90
<Table className="w-max min-w-full" wrapperClassName="max-h-[600px] overflow-auto">
<TableHeader className="bg-container-neutral sticky top-0 z-10">
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

하드코딩된 최대 높이 값은 토큰 규칙 위반입니다.

Line 89의 max-h-[600px]는 하드코딩된 arbitrary 값입니다. 기존 디자인 토큰 클래스가 있으면 그 값을 사용하고, 없다면 토큰 추가 합의 후 반영해 주세요.

As per coding guidelines **/*.{ts,tsx,css}: Always use design token classes first; no hardcoded values. Ask user before adding new tokens.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/member/MemberTable.tsx` around lines 89 - 90, Remove the
hardcoded `max-h-[600px]` in the Table's `wrapperClassName` and replace it with
the appropriate design-token class (use the existing height/max-height token
class from our design tokens) so the Table component (`Table` with
`wrapperClassName`) uses token-based sizing; if no suitable token exists, stop
and request agreement to add a new design token (do not introduce a new
hardcoded value), then update the `wrapperClassName` to use that new token and
keep `TableHeader` classes unchanged.

Comment thread src/components/admin/member/modal/MemberDetailModal.tsx Outdated
className={cn(
'relative inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-[10px] disabled:cursor-not-allowed disabled:opacity-50',
'before:absolute before:inset-x-0 before:top-1/2 before:h-[15px] before:-translate-y-1/2 before:rounded-[10px] before:transition-colors',
'before:absolute before:inset-x-[2px] before:top-1/2 before:h-[15px] before:-translate-y-1/2 before:rounded-[10px] before:transition-colors',
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Line 17의 임의 px 값은 토큰 우선 규칙과 충돌합니다.

before:inset-x-[2px] 대신 기존 spacing/design token 클래스를 우선 사용해 주세요. 필요한 토큰이 없다면 토큰 추가 합의 후 반영하는 편이 안전합니다.

As per coding guidelines, "Always use design token classes first; no hardcoded values. Ask user before adding new tokens".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/Switch.tsx` at line 17, In Switch.tsx replace the hardcoded
utility before:inset-x-[2px] inside the class string with the appropriate
existing spacing/design-token class (e.g., before:inset-x-0.5 or whatever
project spacing token maps to 2px) so tokens are used first; if no token exists,
do not hardcode—propose and add a new spacing token via design token agreement,
then use that new token class in place of before:inset-x-[2px].

Comment on lines 49 to 62
const isAdmin = memberRole === 'ADMIN';
const isBanned = status === 'BANNED';
return [
// TODO: 가입 승인 api 열리면 열기
// { label: '가입 승인', title: '1명의 멤버 가입을 승인하시겠습니까?', handler: onApprove },
const actions: FooterAction[] = [
{
label: isAdmin ? '사용자로 변경' : '관리자로 변경',
label: isAdmin ? '사용자로 변경' : '운영진으로 변경',
title: isAdmin
? '1명의 멤버 역할을 사용자로\n변경하시겠습니까?'
: '1명의 멤버 역할을 관리자로\n변경하시겠습니까?',
: '1명의 멤버 역할을 운영진으로\n변경하시겠습니까?',
handler: onChangeRole,
},
// TODO: 비번 변경 api 열리면 열기
// {
// label: '비밀번호 초기화',
// title: '1명의 멤버 비밀번호를 초기화\n시키시겠습니까?',
// handler: onResetPassword,
// },
isBanned
? { label: '유저 복구', title: '1명의 멤버를 복구하시겠습니까?', handler: onRestore }
: { label: '유저 추방', title: '1명의 멤버를 추방하시겠습니까?', handler: onBan },
];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

리더에게도 실패가 보장된 역할 변경 액션이 노출됩니다.

LEAD도 현재는 non-ADMIN 분기로 들어가서 "운영진으로 변경" 액션이 생성됩니다. 그런데 상세 모달 쪽 핸들러는 이 경우 ADMIN으로 role change를 보내고, 같은 PR에서 추가한 LEAD_TRANSFER_ONLY 에러로 거절되기 때문에 리더 상세 화면에 깨진 버튼이 생깁니다.

🔧 제안 수정
 export function getFooterActions({
   memberRole,
   status,
   onChangeRole,
   onBan,
   onRestore,
   onTransferLead,
 }: FooterActionHandlers): FooterAction[] {
   const isAdmin = memberRole === 'ADMIN';
+  const isLead = memberRole === 'LEAD';
   const isBanned = status === 'BANNED';
   const actions: FooterAction[] = [
-    {
-      label: isAdmin ? '사용자로 변경' : '운영진으로 변경',
-      title: isAdmin
-        ? '1명의 멤버 역할을 사용자로\n변경하시겠습니까?'
-        : '1명의 멤버 역할을 운영진으로\n변경하시겠습니까?',
-      handler: onChangeRole,
-    },
+    ...(!isLead
+      ? [
+          {
+            label: isAdmin ? '사용자로 변경' : '운영진으로 변경',
+            title: isAdmin
+              ? '1명의 멤버 역할을 사용자로\n변경하시겠습니까?'
+              : '1명의 멤버 역할을 운영진으로\n변경하시겠습니까?',
+            handler: onChangeRole,
+          },
+        ]
+      : []),
     isBanned
       ? { label: '유저 복구', title: '1명의 멤버를 복구하시겠습니까?', handler: onRestore }
       : { label: '유저 추방', title: '1명의 멤버를 추방하시겠습니까?', handler: onBan },
   ];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/constants/admin/memberDetailModal.constants.ts` around lines 49 - 62, The
actions array shows a "change to admin" action for LEADs which always fails
(LEAD_TRANSFER_ONLY); update the logic around memberRole/isAdmin to detect LEAD
(e.g., const isLead = memberRole === 'LEAD') and do not render the role-change
action for leads (or render a disabled/alternative action) so the onChangeRole
handler is not exposed for LEADs; adjust the actions construction (referencing
memberRole, isAdmin, isBanned, actions, onChangeRole) so only non-LEAD,
non-admin members get the "운영진으로 변경" action and LEADs don't show a broken
button.

Comment thread src/constants/term.ts Outdated
Comment thread src/hooks/queries/admin/useToggleBoardCommentMutation.ts Outdated
@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/components/admin/board/BoardAdminSkeleton.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/board/BoardCard.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/board/BoardPageContent.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/board/SortableBoardCard.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/member/CardinalPillList.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-lmsxzqiaz-weethsite-4975s-projects.vercel.app

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Build: 통과

🎉 모든 검증을 통과했습니다!

@JIN921 JIN921 merged commit 971b481 into develop Apr 29, 2026
5 checks passed
@JIN921 JIN921 deleted the fix/WTH-355-어드민-QA-게시판-관리-수정 branch April 29, 2026 11:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🐞 BugFix Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants