Skip to content

[Feat] WTH-235: 어드민 네비게이션 UI 변경#44

Merged
JIN921 merged 14 commits intodevelopfrom
feat/WTH-235-어드민-네비게이션-UI-변경
Apr 11, 2026

Hidden character warning

The head ref may contain hidden characters: "feat/WTH-235-\uc5b4\ub4dc\ubbfc-\ub124\ube44\uac8c\uc774\uc158-UI-\ubcc0\uacbd"
Merged

[Feat] WTH-235: 어드민 네비게이션 UI 변경#44
JIN921 merged 14 commits intodevelopfrom
feat/WTH-235-어드민-네비게이션-UI-변경

Conversation

@JIN921
Copy link
Copy Markdown
Collaborator

@JIN921 JIN921 commented Apr 9, 2026

✅ PR 유형

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

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

📌 관련 이슈번호

  • Closed #235

✅ Key Changes

  • LNB 전면 리뉴얼: 디자인 변경에 맞춰 헤더(토글+Weeth admin), 동아리 정보(아바타+학교명+동아리명), 관리 메뉴(멤버/일정/출석/게시판), 동아리 정보 메뉴, 이동 섹션으로 재구성
  • 사이드바 토글: 접기/펼치기 기능 추가 (w-56w-14), 접힌 상태에서 아이콘만 표시 + 툴팁
  • 테마 모드 셀렉터: LNB 하단에 라이트/다크/자동 모드 드롭다운 추가 (기존 DropdownMenu + useThemeStore 활용)
  • 컴포넌트 분리: LNB를 LNBHeader, LNBClubInfo, NavItem, NavSection, CollapsedDivider, ThemeModeSelector 6개 컴포넌트로 분리
  • 동아리 API 연결: useAdminClubQuery + adminClubApi 추가, LNBClubInfo에서 동아리 정보 표시
  • Avatar 확장: size={40} variant, color variant (default/primary/secondary) 추가
  • 아이콘 추가/수정: 피그마 아이콘으로 교체, SVG fill을 currentColor로 통일

📸 스크린샷 or 실행영상


🎸 기타 사항 or 추가 코멘트

  • clubId는 로그인 시 스토어에 세팅되는 구조이므로, 로그인을 타지 않은 개발 환경에서는 localStorage에 수동 세팅 필요: localStorage.setItem('clubId', JSON.stringify({ state: { clubId: "ID값" }, version: 0 }))
    -> 요게 이전 pr인 [Feat] WTH-249 : 출석 api 연결 #43 에서 클럽 아이디 이슈 해결 되면.. 없애도 되는 이슈입니다...

  • 툴팁 같은 경우는 디자인이랑 조금 다른데 네비 토글 툴팁을 디자인이랑 맞추면 다른 아이콘 툴팁이랑 조금 달라져서.. 그냥 임의로 맞췃습니다.. 너무 구리면 알려주세요옹

Summary by CodeRabbit

  • 새로운 기능

    • 관리자 사이드바 축소/확장 토글 및 관련 UI(헤더, 클럽 정보, 구분선, 네비게이션 섹션/항목) 추가
    • 테마 모드 선택기 추가(자동/라이트/다크)
    • 관리자용 아이콘 5종 및 네비게이션 토글 아이콘 추가
    • 관리자용 새 페이지(게시판, 클럽정보, 일정) 추가
  • 버그 수정 / 변경

    • 아바타가 프로필 이미지 필드를 사용하도록 업데이트
    • 프리런치 리디렉션 비활성화
  • 스타일

    • 아바타 색상·크기 변형 옵션 및 관리자 버튼 글꼴/라인 높이 조정

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

PR 테스트 결과

Jest: 통과

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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

🤖 Claude 테스트 제안

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

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

src/app/(private)/(main)/board/(with-nav)/BoardNavClient.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)/(main)/board/(with-nav)/[id]/PostDetailContent.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)/(main)/board/edit/[id]/EditClientEditor.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)/(main)/board/edit/[id]/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)/(main)/board/write/ClientEditor.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

github-actions bot commented Apr 9, 2026

PR 검증 결과

TypeScript: 실패
ESLint: 통과
Prettier: 실패
Build: 실패

⚠️ 일부 검증에 실패했습니다. 확인 후 수정해주세요.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: be7250f2-0881-4dd5-9d3f-adb8886e5397

📥 Commits

Reviewing files that changed from the base of the PR and between c29bd74 and 1f25780.

📒 Files selected for processing (5)
  • .claude/settings.local.json
  • src/app/globals.css
  • src/assets/icons/admin/index.ts
  • src/lib/apis/index.ts
  • src/proxy.ts
✅ Files skipped from review due to trivial changes (3)
  • src/lib/apis/index.ts
  • src/app/globals.css
  • src/assets/icons/admin/index.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • .claude/settings.local.json
  • src/proxy.ts

📝 Walkthrough

Walkthrough

관리자 사이드바(LNB)를 컴포지션 기반으로 재구성하고 축소/확장 토글 및 서브컴포넌트들을 추가했습니다. 클럽 조회 API와 훅이 도입되었고 Club 타입·아바타 참조가 profileImageUrl로 변경되었습니다. PRE_LAUNCH 플래그가 false로 전환되었습니다.

Changes

Cohort / File(s) Summary
아이콘 바렐 / 아이콘 추가
src/assets/icons/admin/index.ts, src/assets/icons/index.ts
관리자용 아이콘 5개(ic_admin_*) 및 nav_toggle.svg에 대한 named export 추가
LNB 재구성
src/components/admin/layout/LNB.tsx
인라인 LNB를 여러 서브컴포넌트(LNBHeader, LNBClubInfo, NavSection, NavItem, CollapsedDivider, ThemeModeSelector)로 분리하고 collapsed 상태 및 외부링크 표기 방식 변경
레이아웃 서브컴포넌트
src/components/admin/layout/LNBHeader.tsx, src/components/admin/layout/LNBClubInfo.tsx, src/components/admin/layout/NavItem.tsx, src/components/admin/layout/NavSection.tsx, src/components/admin/layout/CollapsedDivider.tsx
헤더, 클럽 정보, 네비게이션 항목·섹션, 축소 구분자 등 컴포넌트 추가(툴팁·축소 모드 대응 포함)
테마 모드 선택기
src/components/admin/layout/ThemeModeSelector.tsx
auto/light/dark 선택 드롭다운 컴포넌트 추가(글로벌 테마 동기화 포함)
Club API 및 훅
src/lib/apis/adminClub.ts, src/lib/apis/index.ts, src/hooks/queries/admin/useAdminClubQuery.ts
관리자용 클럽 상세 조회 API와 React Query 훅 추가(쿼리 키 및 캐시 설정 포함)
타입/아바타 변경
src/types/club.ts, src/components/ui/avatar.tsx
Club 타입 확장(예: profileImageUrl, 연락처 등) 및 Avatar 변형(size/colorScheme)과 그룹 기반 스타일 업데이트
아바타 소비자 업데이트
src/components/auth/.../ClubSearchDropdown.tsx, .../ClubSelectedCard.tsx, .../ClubAccessPage.tsx, .../ClubConfirmCard.tsx
아바타 이미지 소스 검사 및 렌더링을 logoUrlprofileImageUrl로 전환
프록시 플래그 토글
src/proxy.ts
PRE_LAUNCHtruefalse로 변경하여 프리런칭 리다이렉트 비활성화
UI·설정 기타
src/app/globals.css, .claude/settings.local.json, src/app/(private)/admin/...
관리자 전용 CSS 토큰 조정, 로컬 권한 패턴 추가, 관리자 라우트(간단 페이지) 추가

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant LNB as LNB (container)
    participant State as React State
    participant LNBHeader as LNBHeader
    participant LNBClubInfo as LNBClubInfo
    participant NavItem as NavItem

    User->>LNBHeader: 클릭(토글)
    LNBHeader->>State: onToggle()
    State->>LNB: collapsed 값 갱신
    LNB->>LNBClubInfo: 전달(collapsed)
    LNB->>NavItem: 전달(collapsed, isActive)
    NavItem->>User: 툴팁 또는 라벨 표시
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

✨ Feature, 📬 API

Suggested reviewers

  • nabbang6
  • dalzzy

🐰 사이드바 살며시 접고,
아이콘 반짝 더해주었네.
얼굴은 profile로 바꾸고,
토글로 너비 살짝 줄이며,
관리판에 새 숨결 불어넣네.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 주요 변경사항인 어드민 네비게이션 UI 변경을 명확하게 요약하고 있으며, 이슈 번호 WTH-235를 포함하고 있습니다.
Description check ✅ Passed PR 설명이 저장소의 템플릿 구조를 따르고 있으며, PR 유형, 이슈번호, 주요 변경사항, 추가 코멘트가 모두 포함되어 있습니다.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/WTH-235-어드민-네비게이션-UI-변경

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

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

@JIN921 JIN921 changed the title feat: 어드민 네비게이션 UI 변경 [Feat] WTH-235: 어드민 네비게이션 UI 변경 Apr 9, 2026
@JIN921 JIN921 self-assigned this Apr 9, 2026
@JIN921 JIN921 added 🎨 Html&css 마크업 & 스타일링 🔨 Refactor 코드 리팩토링 labels Apr 9, 2026
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

🧹 Nitpick comments (1)
src/hooks/queries/admin/useAdminClubQuery.ts (1)

10-11: clubId! 대신 queryFn 내부 가드를 두는 쪽이 더 안전합니다.

Line 10의 non-null assertion은 런타임 방어가 약합니다. enabled와 별개로 queryFn에서 명시적으로 검증해 주세요.

수정 예시
   return useQuery({
     queryKey: ['admin', 'club', clubId],
-    queryFn: () => adminClubApi.getDetail(clubId!).then((res) => res.data.data),
+    queryFn: async () => {
+      if (!clubId) throw new Error('clubId is required');
+      const res = await adminClubApi.getDetail(clubId);
+      return res.data.data;
+    },
     enabled: !!clubId,
     staleTime: 30 * 60 * 1000,
     gcTime: 60 * 60 * 1000,
   });
Based on learnings: "Applies to src/**/*.{ts,tsx} : Use tanstack query for data fetching and Zustand for client state management"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/queries/admin/useAdminClubQuery.ts` around lines 10 - 11, The
queryFn currently uses a non-null assertion (clubId!) which is unsafe; modify
the queryFn in useAdminClubQuery to explicitly check clubId at runtime and
either throw an error or return a rejected Promise when clubId is missing,
instead of relying solely on enabled, and remove the non-null assertion. Locate
the query configuration that calls adminClubApi.getDetail(clubId!) and replace
it with a guarded implementation that verifies clubId (the clubId variable and
the queryFn calling adminClubApi.getDetail) before invoking the API.
🤖 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/layout/LNB.tsx`:
- Around line 90-104: The ThemeModeSelector currently renders directly under the
moveNavItems inside NavSection so it flows with content instead of sticking to
the sidebar bottom; extract ThemeModeSelector out of the NavSection and render
it after the NavSection in its own wrapper that applies a bottom-sticky layout
(e.g., a wrapper with class "mt-auto" or equivalent CSS), and ensure the sidebar
root container (the parent around NavSection and the new wrapper) is a
full-height flex column (flex flex-col h-full) so the mt-auto spacer pushes
ThemeModeSelector to the bottom; keep CollapsedDivider/NavItem rendering
unchanged and place CollapsedDivider before the new wrapper so the divider
remains above the bottom selector.

In `@src/components/admin/layout/LNBHeader.tsx`:
- Around line 19-24: The icon-only toggle button in LNBHeader.tsx lacks
accessible name/state and an explicit button type; update the <button> that
renders the Icon (using NavToggleIcon and onToggle) to include type="button", an
aria-label (e.g., "Toggle navigation"), and aria-expanded bound to the
component's open state (for example aria-expanded={isOpen} or
aria-expanded={navOpen} depending on the prop/state used by LNBHeader); if the
component currently doesn't expose a boolean open prop, add or derive one (e.g.,
isOpen/navOpen) so aria-expanded reflects the current collapsed/expanded state.

In `@src/components/admin/layout/NavItem.tsx`:
- Around line 49-69: The icon-only nav items lack accessible labels; update
NavItem.tsx to add aria-label={label} to the interactive elements (the button
created in the window.open branch and both Link elements in the external and
internal branches) so assistive tech can read the hidden text—use the existing
variables (label, collapsed, cls, path, external, iconEl, el) and conditionally
or unconditionally set aria-label={label} on those elements when the visible
text is hidden.
- Around line 7-13: The import for PeopleIcon should be a regular value import,
not a type-only import; replace "import type { PeopleIcon }" with a normal
import so the value exported from "@/assets/icons" is available at runtime,
keeping the NavItemProps interface and its "icon: typeof PeopleIcon" type query
unchanged; update the import statement that provides PeopleIcon to a standard
import to ensure the SVG component value is imported while leaving the rest of
NavItemProps intact.

In `@src/components/admin/layout/ThemeModeSelector.tsx`:
- Around line 39-55: The component currently only stores isDark in useThemeStore
so selecting "auto" only applies once; modify the theme store (useThemeStore) to
also persist a ThemeMode (e.g., mode) and update ThemeModeSelector to read/write
that mode instead of only local state (replace the local useState<ThemeMode>
initialization with the store's mode and setMode). In handleSelect, save the
selected mode to the store (not just call setDark) and when mode === 'auto'
attach a window.matchMedia('(prefers-color-scheme: dark)') change listener that
calls setDark(matcher.matches) to keep the UI in sync; remove that listener when
mode changes away from 'auto' and ensure the store restores mode on reload so
"auto" remains active across refreshes.

In `@src/lib/apis/adminClub.ts`:
- Around line 6-7: The getDetail API call embeds clubId raw into the path which
can break for special characters; update the getDetail implementation (the
apiClient.get call in adminClub's getDetail) to URL-encode clubId with
encodeURIComponent before constructing the `/admin/clubs/${...}` path so the
request target is always a valid URL.

In `@src/proxy.ts`:
- Line 7: The hardcoded PRE_LAUNCH constant should be replaced with an
environment-driven flag: read an env var (e.g., process.env.PRE_LAUNCH or
process.env.PRE_LAUNCH_ENABLED) and parse it into a boolean with a safe default
(false) so different deployments can toggle the gate; update the declaration of
PRE_LAUNCH in src/proxy.ts (replace the const PRE_LAUNCH = false) to compute its
value from the env var and accept common truthy values like "true" or "1".

In `@src/types/club.ts`:
- Around line 4-11: The shared Club type has removed logoUrl but consumers still
reference club.logoUrl (notably in ClubSearchDropdown.tsx reading club.logoUrl),
causing type errors; either restore a backward-compatible optional logoUrl field
on the Club interface or update all consumers in this PR to use profileImageUrl
instead—specifically modify the Club type (add logoUrl?: string) or replace
occurrences of club.logoUrl in components like ClubSearchDropdown.tsx to read
club.profileImageUrl and adjust any UI/prop names to match the new field.

---

Nitpick comments:
In `@src/hooks/queries/admin/useAdminClubQuery.ts`:
- Around line 10-11: The queryFn currently uses a non-null assertion (clubId!)
which is unsafe; modify the queryFn in useAdminClubQuery to explicitly check
clubId at runtime and either throw an error or return a rejected Promise when
clubId is missing, instead of relying solely on enabled, and remove the non-null
assertion. Locate the query configuration that calls
adminClubApi.getDetail(clubId!) and replace it with a guarded implementation
that verifies clubId (the clubId variable and the queryFn calling
adminClubApi.getDetail) before invoking the API.
🪄 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: abc14900-d89b-4abe-ad51-c3105f9564d4

📥 Commits

Reviewing files that changed from the base of the PR and between 865d0d2 and 188feec.

⛔ Files ignored due to path filters (12)
  • src/assets/icons/admin/ic_admin_attendance.svg is excluded by !**/*.svg
  • src/assets/icons/admin/ic_admin_calendar.svg is excluded by !**/*.svg
  • src/assets/icons/admin/ic_admin_due.svg is excluded by !**/*.svg
  • src/assets/icons/admin/ic_admin_fileout.svg is excluded by !**/*.svg
  • src/assets/icons/admin/ic_admin_forum.svg is excluded by !**/*.svg
  • src/assets/icons/admin/ic_admin_light.svg is excluded by !**/*.svg
  • src/assets/icons/admin/ic_admin_manual.svg is excluded by !**/*.svg
  • src/assets/icons/admin/ic_admin_penalty.svg is excluded by !**/*.svg
  • src/assets/icons/admin/ic_admin_service_transfer.svg is excluded by !**/*.svg
  • src/assets/icons/admin/ic_admin_setting.svg is excluded by !**/*.svg
  • src/assets/icons/admin/ic_admin_user.svg is excluded by !**/*.svg
  • src/assets/icons/nav_toggle.svg is excluded by !**/*.svg
📒 Files selected for processing (15)
  • src/assets/icons/admin/index.ts
  • src/assets/icons/index.ts
  • src/components/admin/layout/CollapsedDivider.tsx
  • src/components/admin/layout/LNB.tsx
  • src/components/admin/layout/LNBClubInfo.tsx
  • src/components/admin/layout/LNBHeader.tsx
  • src/components/admin/layout/NavItem.tsx
  • src/components/admin/layout/NavSection.tsx
  • src/components/admin/layout/ThemeModeSelector.tsx
  • src/components/ui/avatar.tsx
  • src/hooks/queries/admin/useAdminClubQuery.ts
  • src/lib/apis/adminClub.ts
  • src/lib/apis/index.ts
  • src/proxy.ts
  • src/types/club.ts

Comment on lines +90 to +104
<NavSection label="이동" collapsed={collapsed}>
{moveNavItems.map(({ id, icon, label, path, external, openInWindow }) => (
<NavItem
key={id}
icon={icon}
label={label}
path={path}
collapsed={collapsed}
external={external}
openInWindow={openInWindow}
/>
))}
<CollapsedDivider collapsed={collapsed} />
<ThemeModeSelector collapsed={collapsed} />
</NavSection>
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

테마 셀렉터가 아직 "하단 고정"되지 않습니다.

지금 구조에서는 이동 메뉴 바로 아래에 이어서 렌더링될 뿐이라, 남는 높이가 있어도 sidebar bottom에 붙지 않습니다. ThemeModeSelectormt-auto가 걸린 별도 wrapper로 빼서 항상 하단에 고정하는 편이 요구사항과 맞습니다.

가능한 정리 예시
         <NavSection label="이동" collapsed={collapsed}>
           {moveNavItems.map(({ id, icon, label, path, external, openInWindow }) => (
             <NavItem
               key={id}
               icon={icon}
               label={label}
               path={path}
               collapsed={collapsed}
               external={external}
               openInWindow={openInWindow}
             />
           ))}
-          <CollapsedDivider collapsed={collapsed} />
-          <ThemeModeSelector collapsed={collapsed} />
         </NavSection>
+        <div className="mt-auto">
+          <CollapsedDivider collapsed={collapsed} />
+          <ThemeModeSelector collapsed={collapsed} />
+        </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<NavSection label="이동" collapsed={collapsed}>
{moveNavItems.map(({ id, icon, label, path, external, openInWindow }) => (
<NavItem
key={id}
icon={icon}
label={label}
path={path}
collapsed={collapsed}
external={external}
openInWindow={openInWindow}
/>
))}
<CollapsedDivider collapsed={collapsed} />
<ThemeModeSelector collapsed={collapsed} />
</NavSection>
<NavSection label="이동" collapsed={collapsed}>
{moveNavItems.map(({ id, icon, label, path, external, openInWindow }) => (
<NavItem
key={id}
icon={icon}
label={label}
path={path}
collapsed={collapsed}
external={external}
openInWindow={openInWindow}
/>
))}
</NavSection>
<div className="mt-auto">
<CollapsedDivider collapsed={collapsed} />
<ThemeModeSelector collapsed={collapsed} />
</div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/layout/LNB.tsx` around lines 90 - 104, The
ThemeModeSelector currently renders directly under the moveNavItems inside
NavSection so it flows with content instead of sticking to the sidebar bottom;
extract ThemeModeSelector out of the NavSection and render it after the
NavSection in its own wrapper that applies a bottom-sticky layout (e.g., a
wrapper with class "mt-auto" or equivalent CSS), and ensure the sidebar root
container (the parent around NavSection and the new wrapper) is a full-height
flex column (flex flex-col h-full) so the mt-auto spacer pushes
ThemeModeSelector to the bottom; keep CollapsedDivider/NavItem rendering
unchanged and place CollapsedDivider before the new wrapper so the divider
remains above the bottom selector.

Comment on lines +39 to +55
const [mode, setMode] = useState<ThemeMode>(() => {
if (typeof window === 'undefined') return 'light';
return useThemeStore.getState().isDark ? 'dark' : 'light';
});

const handleSelect = (value: ThemeMode) => {
setMode(value);

if (value === 'light') {
setDark(false);
} else if (value === 'dark') {
setDark(true);
} else {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
setDark(prefersDark);
}
};
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

auto 모드가 실제로는 자동 추적되지 않습니다.

지금 구현은 store에 isDark만 저장하고, auto를 선택해도 현재 OS 테마 값을 한 번 반영하는 데서 끝납니다. 그래서 새로고침하면 선택 상태가 auto로 복원되지 않고, 이후 시스템 테마가 바뀌어도 UI가 따라가지 않습니다. mode 자체를 Zustand에 저장하고, auto일 때만 matchMedia의 change 이벤트로 setDark를 동기화해야 합니다.

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

In `@src/components/admin/layout/ThemeModeSelector.tsx` around lines 39 - 55, The
component currently only stores isDark in useThemeStore so selecting "auto" only
applies once; modify the theme store (useThemeStore) to also persist a ThemeMode
(e.g., mode) and update ThemeModeSelector to read/write that mode instead of
only local state (replace the local useState<ThemeMode> initialization with the
store's mode and setMode). In handleSelect, save the selected mode to the store
(not just call setDark) and when mode === 'auto' attach a
window.matchMedia('(prefers-color-scheme: dark)') change listener that calls
setDark(matcher.matches) to keep the UI in sync; remove that listener when mode
changes away from 'auto' and ensure the store restores mode on reload so "auto"
remains active across refreshes.

Comment on lines +6 to +7
getDetail: (clubId: string) =>
apiClient.get<ApiResponse<Club>>(`/admin/clubs/${clubId}`),
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

clubId는 URL 인코딩 후 경로에 넣어 주세요.

Line 7처럼 raw 문자열을 경로에 넣으면 특수문자 포함 시 잘못된 endpoint로 요청될 수 있습니다.

수정 예시
 export const adminClubApi = {
   getDetail: (clubId: string) =>
-    apiClient.get<ApiResponse<Club>>(`/admin/clubs/${clubId}`),
+    apiClient.get<ApiResponse<Club>>(`/admin/clubs/${encodeURIComponent(clubId)}`),
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
getDetail: (clubId: string) =>
apiClient.get<ApiResponse<Club>>(`/admin/clubs/${clubId}`),
getDetail: (clubId: string) =>
apiClient.get<ApiResponse<Club>>(`/admin/clubs/${encodeURIComponent(clubId)}`),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/apis/adminClub.ts` around lines 6 - 7, The getDetail API call embeds
clubId raw into the path which can break for special characters; update the
getDetail implementation (the apiClient.get call in adminClub's getDetail) to
URL-encode clubId with encodeURIComponent before constructing the
`/admin/clubs/${...}` path so the request target is always a valid URL.


// TODO: 런칭 후 PRE_LAUNCH 플래그 및 관련 분기 제거
const PRE_LAUNCH = true;
const PRE_LAUNCH = false;
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 7에서 PRE_LAUNCH = false 고정은 모든 배포 환경에서 즉시 게이트를 해제합니다. 운영 안정성을 위해 환경별로 제어 가능해야 합니다.

제안 변경안
-const PRE_LAUNCH = false;
+const PRE_LAUNCH = process.env.PRE_LAUNCH === 'true';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const PRE_LAUNCH = false;
const PRE_LAUNCH = process.env.PRE_LAUNCH === 'true';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/proxy.ts` at line 7, The hardcoded PRE_LAUNCH constant should be replaced
with an environment-driven flag: read an env var (e.g., process.env.PRE_LAUNCH
or process.env.PRE_LAUNCH_ENABLED) and parse it into a boolean with a safe
default (false) so different deployments can toggle the gate; update the
declaration of PRE_LAUNCH in src/proxy.ts (replace the const PRE_LAUNCH = false)
to compute its value from the env var and accept common truthy values like
"true" or "1".

JIN921 and others added 2 commits April 10, 2026 00:11
…충돌 해결

- logoUrl을 profileImageUrl로 일괄 변경
- Radix AvatarProps의 color와 cva color variant 충돌 → colorScheme으로 rename

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

🤖 Claude 테스트 제안

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

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

src/app/(private)/(main)/board/(with-nav)/BoardNavClient.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)/(main)/board/(with-nav)/[id]/PostDetailContent.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)/(main)/board/edit/[id]/EditClientEditor.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)/(main)/board/edit/[id]/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)/(main)/board/write/ClientEditor.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

github-actions bot commented Apr 9, 2026

PR 테스트 결과

Jest: 통과

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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

PR 검증 결과

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

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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

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

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.

♻️ Duplicate comments (1)
src/components/admin/layout/LNB.tsx (1)

96-110: ⚠️ Potential issue | 🟡 Minor

테마 셀렉터를 NavSection 밖으로 빼서 하단에 고정해주세요.

지금 구조에서는 ThemeModeSelector가 이동 메뉴의 마지막 아이템처럼 흐르기 때문에, 사이드바 높이가 남아도 하단에 붙지 않습니다. nav는 이미 flex h-full flex-col이므로, 별도 wrapper에 mt-auto를 주는 방식으로 분리하는 편이 맞습니다.

가능한 수정 예시
         <NavSection label="이동" collapsed={collapsed}>
           {moveNavItems.map(({ id, icon, label, path, external, openInWindow }) => (
             <NavItem
               key={id}
               icon={icon}
               label={label}
               path={path}
               collapsed={collapsed}
               external={external}
               openInWindow={openInWindow}
             />
           ))}
-          <CollapsedDivider collapsed={collapsed} />
-          <ThemeModeSelector collapsed={collapsed} />
         </NavSection>
+        <div className="mt-auto">
+          <CollapsedDivider collapsed={collapsed} />
+          <ThemeModeSelector collapsed={collapsed} />
+        </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/layout/LNB.tsx` around lines 96 - 110, Move
ThemeModeSelector out of the NavSection so it’s fixed to the bottom: remove
ThemeModeSelector from inside the NavSection block (the map over moveNavItems
and CollapsedDivider) and instead render it after the closing </NavSection>
wrapped in a container with mt-auto (e.g., <div
className="mt-auto"><ThemeModeSelector collapsed={collapsed} /></div>). Keep
NavSection (and CollapsedDivider) as-is; rely on the parent nav being flex
h-full flex-col so the new mt-auto wrapper will push ThemeModeSelector to the
bottom.
🧹 Nitpick comments (1)
src/components/ui/avatar.tsx (1)

15-19: size=40 추가가 하위 컴포넌트 사이즈 매핑까지는 아직 안 맞춰졌습니다.

AvatarFallback은 40 사이즈를 지원하지만 AvatarBadge, AvatarGroupCount는 24/64/128만 정의되어 있어 size={40} 조합에서 비주얼 불일치가 생길 수 있습니다.

변경 예시
 function AvatarBadge({ className, ...props }: React.ComponentProps<'span'>) {
   return (
     <span
@@
       className={cn(
         'bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full ring-2 select-none',
         'group-data-[size="24"]/avatar:size-2 group-data-[size="24"]/avatar:[&>svg]:hidden',
+        'group-data-[size="40"]/avatar:size-2.5 group-data-[size="40"]/avatar:[&>svg]:size-2',
         'group-data-[size="64"]/avatar:size-2.5 group-data-[size="64"]/avatar:[&>svg]:size-2',
         'group-data-[size="128"]/avatar:size-3 group-data-[size="128"]/avatar:[&>svg]:size-2',
         className,
       )}
@@
 function AvatarGroupCount({ className, ...props }: React.ComponentProps<'div'>) {
@@
       className={cn(
         'ring-background bg-container-neutral text-text-alternative relative flex shrink-0 items-center justify-center rounded-full ring-2',
         'group-has-data-[size="24"]/avatar-group:size-6 group-has-data-[size="24"]/avatar-group:text-xs',
+        'group-has-data-[size="40"]/avatar-group:size-10 group-has-data-[size="40"]/avatar-group:text-sm',
         'group-has-data-[size="64"]/avatar-group:size-16 group-has-data-[size="64"]/avatar-group:text-sm',
         'group-has-data-[size="128"]/avatar-group:size-32 group-has-data-[size="128"]/avatar-group:text-base',
         className,
       )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/ui/avatar.tsx` around lines 15 - 19, Avatar components have
inconsistent size mappings: AvatarFallback supports size=40 but AvatarBadge and
AvatarGroupCount only map 24/64/128, causing visual mismatch for size={40}.
Update the size-to-class mappings used by AvatarBadge and AvatarGroupCount to
include the 40 key (matching the class used by AvatarFallback), and ensure any
centralized sizeMap/sizeClass utility (or constants in AvatarBadge,
AvatarGroupCount) includes 40 so all avatar subcomponents render consistent
classes for size={40}.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/components/admin/layout/LNB.tsx`:
- Around line 96-110: Move ThemeModeSelector out of the NavSection so it’s fixed
to the bottom: remove ThemeModeSelector from inside the NavSection block (the
map over moveNavItems and CollapsedDivider) and instead render it after the
closing </NavSection> wrapped in a container with mt-auto (e.g., <div
className="mt-auto"><ThemeModeSelector collapsed={collapsed} /></div>). Keep
NavSection (and CollapsedDivider) as-is; rely on the parent nav being flex
h-full flex-col so the new mt-auto wrapper will push ThemeModeSelector to the
bottom.

---

Nitpick comments:
In `@src/components/ui/avatar.tsx`:
- Around line 15-19: Avatar components have inconsistent size mappings:
AvatarFallback supports size=40 but AvatarBadge and AvatarGroupCount only map
24/64/128, causing visual mismatch for size={40}. Update the size-to-class
mappings used by AvatarBadge and AvatarGroupCount to include the 40 key
(matching the class used by AvatarFallback), and ensure any centralized
sizeMap/sizeClass utility (or constants in AvatarBadge, AvatarGroupCount)
includes 40 so all avatar subcomponents render consistent classes for size={40}.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dbde6570-c45a-459d-8e32-bcf149da73eb

📥 Commits

Reviewing files that changed from the base of the PR and between 188feec and bda904c.

📒 Files selected for processing (10)
  • .claude/settings.local.json
  • src/components/admin/layout/LNB.tsx
  • src/components/admin/layout/LNBClubInfo.tsx
  • src/components/admin/layout/LNBHeader.tsx
  • src/components/auth/hub/ClubSearchDropdown.tsx
  • src/components/auth/hub/ClubSelectedCard.tsx
  • src/components/auth/invite/ClubAccessPage.tsx
  • src/components/auth/invite/ClubConfirmCard.tsx
  • src/components/ui/avatar.tsx
  • src/lib/apis/adminClub.ts
✅ Files skipped from review due to trivial changes (6)
  • src/components/auth/hub/ClubSelectedCard.tsx
  • src/components/auth/invite/ClubConfirmCard.tsx
  • src/lib/apis/adminClub.ts
  • src/components/auth/hub/ClubSearchDropdown.tsx
  • .claude/settings.local.json
  • src/components/auth/invite/ClubAccessPage.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/components/admin/layout/LNBHeader.tsx
  • src/components/admin/layout/LNBClubInfo.tsx

code: string;
schoolName: string;
description: string;
logoUrl?: string;
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

이거 동아리 정보 api dto 보니까 profileImageUrl 이길래.. 이걸로 설정햇습니다,,,

@JIN921 JIN921 requested review from dalzzy, nabbang6 and woneeeee April 9, 2026 15:22
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.

수고하셨습니당!! 현재 Tooltip 저는 괜찮은 것 같은데 나중에 예진님한테도 한 번 여쭤보면 좋을 것 같아욤 ☺️

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

피그마에서 사용되고 있는 width랑 현재 구현되어있는 width가 다른 것 같습니닷 ㅜㅜ 확인 부탁드려욤

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.

수고하셨습니당!! 피그마랑 타이포,아이콘 다른 부분만 수정해주시면 좋을 것 같습니다!!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Image

icon이 피그마랑 다른 것들이 있는 것 같아서 아이콘 교체해주시면 좋을 것 같습니당

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.

수고하셨습니다~~!! ㅠ.ㅠ 리뷰가 늦어 죄송합니다,,,
다른 분들이 말씀해주신 것처럼 피그마랑 상이한 부분만 수정해주심 좋을 것 같슴니당!! 👍 최고최고,,

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: 1

♻️ Duplicate comments (2)
src/components/admin/layout/ThemeModeSelector.tsx (1)

39-55: ⚠️ Potential issue | 🟠 Major

auto 모드가 실제 모드로 저장되지 않습니다.

지금 구현은 auto를 고르면 현재 시스템 테마로 setDark()만 한 번 호출하고, 선택한 모드 자체는 로컬 state에만 남깁니다. 그래서 새로고침 후에는 persisted 된 isDark 값만 보고 dark/light로 복원되고, 이후 OS 테마 변경도 따라가지 않습니다. src/stores/theme-store.ts:1-19mode를 persisted 상태로 올리고, src/providers/theme-provider.tsx:1-14 같은 루트 effect에서 auto일 때만 matchMedia 변경을 구독하는 구조로 바꾸는 편이 안전합니다.

#!/bin/bash
# 기대 결과:
# 1) src/stores/theme-store.ts 에 mode / setMode 가 없으면 auto 선택이 복원되지 않습니다.
# 2) src/providers/theme-provider.tsx 또는 동등한 루트 effect에 matchMedia change 구독이 없으면 OS 테마 변경을 추적하지 못합니다.
rg -n -C2 '\b(isDark|setDark|mode|setMode|matchMedia)\b' \
  src/stores/theme-store.ts \
  src/providers/theme-provider.tsx \
  src/components/admin/layout/ThemeModeSelector.tsx
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/layout/ThemeModeSelector.tsx` around lines 39 - 55, The
selector only persists isDark, so choosing "auto" is lost on refresh and OS
theme changes aren't tracked; update the theme store (e.g., add persisted mode
and setMode in useThemeStore) so the selected ThemeMode (light|dark|auto) is
saved, modify ThemeModeSelector (mode state and handleSelect) to call
setMode(value) on selection instead of only setDark, and add a root effect in
the theme provider (e.g., in theme-provider.tsx) that, when mode === 'auto',
subscribes to window.matchMedia('(prefers-color-scheme: dark)') changes to call
setDark(true/false) so OS changes are followed.
src/components/admin/layout/LNB.tsx (1)

96-112: ⚠️ Potential issue | 🟡 Minor

테마 셀렉터가 아직 사이드바 하단에 고정되지 않습니다.

navflex-col h-full인 건 맞지만, 이 블록 자체에는 mt-auto가 없어 화면 높이가 남는 경우 "이동" 섹션 바로 아래에서 멈춥니다. 요구사항대로 항상 하단에 붙이려면 ThemeModeSelector를 별도 wrapper로 빼서 밀어주는 편이 안전합니다.

정리 예시
-        <CollapsedDivider collapsed={collapsed} />
-        <NavSection label="모드" collapsed={collapsed}>
-          <ThemeModeSelector collapsed={collapsed} />
-        </NavSection>
+        <div className="mt-auto">
+          <CollapsedDivider collapsed={collapsed} />
+          <NavSection label="모드" collapsed={collapsed}>
+            <ThemeModeSelector collapsed={collapsed} />
+          </NavSection>
+        </div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/layout/LNB.tsx` around lines 96 - 112, The
ThemeModeSelector is not pinned to the bottom because the block lacks a flex
spacer; wrap ThemeModeSelector (currently inside the NavSection with label "모드")
in a dedicated wrapper that applies a bottom-pushing style (e.g., className
"mt-auto" or a flex spacer) so it always sits at the bottom of the flex-col
container; update the JSX around NavSection/CollapsedDivider/ThemeModeSelector
(referencing NavSection, CollapsedDivider, ThemeModeSelector and the collapsed
prop) to move ThemeModeSelector into that wrapper so it is pushed to the bottom
when the sidebar has extra height.
🤖 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/layout/ThemeModeSelector.tsx`:
- Around line 60-77: The trigger button becomes icon-only when collapsed and
lacks an accessible name; update the DropdownMenuTrigger's button in
ThemeModeSelector (the trigger constant) to include an aria-label (or
aria-labelledby) using the current label text from TRIGGER_LABELS[mode] whenever
collapsed is true (or simply always) so assistive tech can announce the control;
ensure the same fix is applied to the similar button instance used in the menu
(the code around the other DropdownMenuTrigger / button usage referenced in the
comment).

---

Duplicate comments:
In `@src/components/admin/layout/LNB.tsx`:
- Around line 96-112: The ThemeModeSelector is not pinned to the bottom because
the block lacks a flex spacer; wrap ThemeModeSelector (currently inside the
NavSection with label "모드") in a dedicated wrapper that applies a bottom-pushing
style (e.g., className "mt-auto" or a flex spacer) so it always sits at the
bottom of the flex-col container; update the JSX around
NavSection/CollapsedDivider/ThemeModeSelector (referencing NavSection,
CollapsedDivider, ThemeModeSelector and the collapsed prop) to move
ThemeModeSelector into that wrapper so it is pushed to the bottom when the
sidebar has extra height.

In `@src/components/admin/layout/ThemeModeSelector.tsx`:
- Around line 39-55: The selector only persists isDark, so choosing "auto" is
lost on refresh and OS theme changes aren't tracked; update the theme store
(e.g., add persisted mode and setMode in useThemeStore) so the selected
ThemeMode (light|dark|auto) is saved, modify ThemeModeSelector (mode state and
handleSelect) to call setMode(value) on selection instead of only setDark, and
add a root effect in the theme provider (e.g., in theme-provider.tsx) that, when
mode === 'auto', subscribes to window.matchMedia('(prefers-color-scheme: dark)')
changes to call setDark(true/false) so OS changes are followed.
🪄 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: 2bc7850c-08cc-4efe-b552-9e0a615a574d

📥 Commits

Reviewing files that changed from the base of the PR and between bda904c and c29bd74.

📒 Files selected for processing (8)
  • src/app/(private)/admin/board/page.tsx
  • src/app/(private)/admin/club-info/page.tsx
  • src/app/(private)/admin/schedule/page.tsx
  • src/app/globals.css
  • src/components/admin/layout/LNB.tsx
  • src/components/admin/layout/LNBHeader.tsx
  • src/components/admin/layout/NavItem.tsx
  • src/components/admin/layout/ThemeModeSelector.tsx
✅ Files skipped from review due to trivial changes (1)
  • src/components/admin/layout/LNBHeader.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/components/admin/layout/NavItem.tsx

Comment on lines +60 to +77
const trigger = (
<DropdownMenuTrigger asChild>
<button
className={cn(
'text-text-normal flex h-12 w-full cursor-pointer items-center border-none transition-colors',
'hover:bg-container-neutral-interaction',
collapsed ? 'justify-center px-200' : 'gap-300 px-300',
)}
>
<TriggerIcon className="h-6 w-6 shrink-0" />
{!collapsed && (
<>
<span className="typo-button2 flex-1 text-left">{TRIGGER_LABELS[mode]}</span>
<ChevronDown className="h-5 w-5 shrink-0" />
</>
)}
</button>
</DropdownMenuTrigger>
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

접힌 상태의 트리거 버튼에 접근 가능한 이름이 없습니다.

collapsed일 때는 텍스트가 빠져서 사실상 아이콘-only 버튼이 되는데, 현재 버튼에 aria-label이 없어 보조기기에서는 용도를 알기 어렵습니다. 툴팁은 대체 이름이 아니니 현재 모드 기반 label을 버튼에 직접 넣어 주세요.

간단한 보완 예시
     <DropdownMenuTrigger asChild>
       <button
+        type="button"
+        aria-label={TRIGGER_LABELS[mode]}
         className={cn(
           'text-text-normal flex h-12 w-full cursor-pointer items-center border-none transition-colors',
           'hover:bg-container-neutral-interaction',

Also applies to: 82-85

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

In `@src/components/admin/layout/ThemeModeSelector.tsx` around lines 60 - 77, The
trigger button becomes icon-only when collapsed and lacks an accessible name;
update the DropdownMenuTrigger's button in ThemeModeSelector (the trigger
constant) to include an aria-label (or aria-labelledby) using the current label
text from TRIGGER_LABELS[mode] whenever collapsed is true (or simply always) so
assistive tech can announce the control; ensure the same fix is applied to the
similar button instance used in the menu (the code around the other
DropdownMenuTrigger / button usage referenced in the comment).

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

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

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

src/components/admin/layout/CollapsedDivider.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/layout/LNB.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

PR 검증 결과

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

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

@github-actions
Copy link
Copy Markdown

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

@JIN921 JIN921 merged commit b5a0aa7 into develop Apr 11, 2026
5 checks passed
@JIN921 JIN921 deleted the feat/WTH-235-어드민-네비게이션-UI-변경 branch April 11, 2026 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🎨 Html&css 마크업 & 스타일링 🔨 Refactor 코드 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants