[Fix] WTH-355: 어드민 QA - 게시판 관리 수정#83
Conversation
|
Warning Rate limit exceeded
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 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthrough이 PR은 애플리케이션 전역에서 "관리자"라는 용어를 "운영진"으로 변경하고, 새로운 리더 이양 기능, 게시판 댓글 토글 기능, 개선된 카디널 선택 UI(CardinalPillList), 벌크 뮤테이션 유틸리티를 추가합니다. 기존 회원 역할 시스템을 재구성하고 API 및 상태 관리를 확장합니다. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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. Review rate limit: 0/1 reviews remaining, refill in 29 minutes and 39 seconds.Comment |
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
dalzzy
left a comment
There was a problem hiding this comment.
확인햇습니다~!! 어드민 qa하느라 고생하셧습니다,,,,,,
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
|
구현한 기능 Preview: https://weeth-leenq53y7-weethsite-4975s-projects.vercel.app |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
There was a problem hiding this comment.
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:UpdateBoardCommentBody는Omit으로 파생하는 편이 안전합니다.지금 선언은
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
📒 Files selected for processing (55)
src/app/(private)/[clubId]/admin/layout.tsxsrc/app/(public)/landing/page.tsxsrc/components/admin/CardinalDropdown.tsxsrc/components/admin/board/BoardAdminSkeleton.tsxsrc/components/admin/board/BoardCard.tsxsrc/components/admin/board/BoardPageContent.tsxsrc/components/admin/board/SortableBoardCard.tsxsrc/components/admin/board/modal/constants.tssrc/components/admin/index.tssrc/components/admin/layout/LNB.tsxsrc/components/admin/member/CardinalCard.tsxsrc/components/admin/member/CardinalPillList.tsxsrc/components/admin/member/MemberPageContent.tsxsrc/components/admin/member/MemberTable.tsxsrc/components/admin/member/MemberTopBar.tsxsrc/components/admin/member/modal/MemberDetailModal.tsxsrc/components/admin/schedule/general/SchedulePageContent.tsxsrc/components/attendance/AttendanceTodayCard.tsxsrc/components/home/HomeBoardContent.tsxsrc/components/layout/Footer.tsxsrc/components/layout/header/DefaultActions.tsxsrc/components/layout/header/Header.tsxsrc/components/layout/header/MobileNavSheet.tsxsrc/components/ui/Switch.tsxsrc/components/ui/table.tsxsrc/constants/admin/memberDetailModal.constants.tssrc/constants/admin/memberTable.constants.tssrc/constants/admin/memberTopBar.constants.tssrc/constants/errorCode.tssrc/constants/home/tutorial.tsxsrc/constants/landing/landing.tssrc/constants/term.tssrc/hooks/admin/useBoardDragReorder.tssrc/hooks/mutations/admin/index.tssrc/hooks/mutations/admin/useAdminCardinalMutations.tssrc/hooks/mutations/admin/useAdminMemberMutations.tssrc/hooks/queries/admin/index.tssrc/hooks/queries/admin/useCreateBoardMutation.tssrc/hooks/queries/admin/useDeleteBoardMutation.tssrc/hooks/queries/admin/useToggleBoardCommentMutation.tssrc/hooks/queries/admin/useUpdateBoardMutation.tssrc/hooks/queries/admin/useUpdateBoardOrderMutation.tssrc/lib/actions/club.tssrc/lib/apis/adminBoard.tssrc/lib/apis/adminMember.tssrc/lib/apis/cardinal.tssrc/stores/useUserStore.tssrc/types/admin/board.d.tssrc/types/admin/member.d.tssrc/utils/admin/boardMapper.tssrc/utils/admin/memberMapper.tssrc/utils/admin/parseCardinals.tssrc/utils/shared/getApiErrorCode.tssrc/utils/shared/index.tssrc/utils/shared/runBulkMutation.ts
💤 Files with no reviewable changes (1)
- src/types/admin/member.d.ts
| <p className="typo-body2 text-text-alternative min-w-0 flex-1"> | ||
| 추가 게시판은 최대 {MAX_CUSTOM_BOARDS}개입니다. | ||
| 게시판 추가는 최대 {MAX_CUSTOM_BOARDS}개까지 가능 합니다. |
There was a problem hiding this comment.
안내 문구 띄어쓰기를 다듬어 주세요.
가능 합니다는 가능합니다로 붙여 쓰는 편이 자연스럽습니다. 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
"가능합니다".
| <Table className="w-max min-w-full" wrapperClassName="max-h-[600px] overflow-auto"> | ||
| <TableHeader className="bg-container-neutral sticky top-0 z-10"> |
There was a problem hiding this comment.
하드코딩된 최대 높이 값은 토큰 규칙 위반입니다.
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.
| 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', |
There was a problem hiding this comment.
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].
| 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 }, | ||
| ]; |
There was a problem hiding this comment.
리더에게도 실패가 보장된 역할 변경 액션이 노출됩니다.
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.
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
|
구현한 기능 Preview: https://weeth-lmsxzqiaz-weethsite-4975s-projects.vercel.app |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
✅ PR 유형
어떤 변경 사항이 있었나요?
📌 관련 이슈번호
✅ Key Changes
게시판 관리
전체 → 공지사항 → 커스텀name미포함 — 토글 전용 body/API/훅 분리 (UpdateBoardCommentBody,adminBoardApi.updateBoardComment,useToggleBoardCommentMutation)boardMapper에서 NOTICE의commentEnabled를 보존하도록 수정 — 토글 후 invalidate 시 화면 미반영 캐시 문제 해결boardId추적해 해당 스위치만 disabled, mutationonSuccess에서await invalidateQueries후 pending 해제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개 파일만 봐주시면 됩니다 정말 암쏘쏘리..
🤖 Generated with Claude Code
Summary by CodeRabbit
릴리스 노트
새로운 기능
개선 사항
버그 수정