Skip to content

fix(W4-A #18): keep main nav visible when entering /admin/*#186

Merged
haksungjang merged 1 commit into
mainfrom
feat/w4-a-admin-nested-layout
May 26, 2026
Merged

fix(W4-A #18): keep main nav visible when entering /admin/*#186
haksungjang merged 1 commit into
mainfrom
feat/w4-a-admin-nested-layout

Conversation

@haksungjang

Copy link
Copy Markdown
Contributor

Summary

  • /admin/*이 자체 AdminLayout(독자 sidebar + header)으로 마운트되어 Admin 진입 시 일반 메뉴(Dashboard / Projects / Scans / Approvals / Policies / Integrations)가 사라지는 P0 버그 수정.
  • 라우트 테이블에서 /admin/*AppShell 하위로 nest. AppShell sidebar는 이미 super-admin에게 "Administration" 섹션을 표시하므로(L188-199) 일반 메뉴와 admin 메뉴가 항상 공존.
  • AdminLayout을 super-admin 가드 + <Outlet />로 축소(자체 chrome 제거). data-testid="admin-layout" 은 harness 호환을 위해 유지.

Why

사용자 핸즈온(2026-05-26) #18 — super-admin이 Admin 진입 후 일반 portal로 못 돌아오는 dead-end. 사용자 기대: "사이드바는 항상 일반 메뉴 + admin 섹션 동시 표시".

Changes

  • apps/frontend/src/router.tsx: 전체 admin Route 그룹을 / AppShell 부모 안으로 이동.
  • apps/frontend/src/features/admin/AdminLayout.tsx: chrome(sidebar/header/logout/signed-in-as) 삭제. 가드만 유지.
  • apps/frontend/tests/unit/features/admin/AdminLayout.test.tsx: 제거된 chrome 어서션 삭제(admin-sidebar/admin-nav-*/admin-logout). 가드 3 시나리오(super-admin / developer / no-user) 유지.
  • apps/frontend/src/locales/{en,ko}/admin.json: 미사용 admin.layout.{title, signed_in_as} 키 제거.
  • docs/plan-w4-ui-ia-overhaul.md: 신규 W4-A~W4-D 단일 진실 계획 문서.
  • docs/post-ga-execution-tracker.md: W4-A "PR 대기" 표시 + 변경 요지 기록.

Behavior

  • super-admin/admin/users 진입 시 사이드바에 Dashboard / Projects / Scans / Approvals / Policies / Integrations + Administration 섹션(Users / Teams / DT / Scans / Disk / Audit / Health) 모두 표시. 일반 portal로 한 클릭 복귀 가능.
  • developer / unauthenticated → 기존 existence-hide 동작 유지(AdminNotFound 렌더, URL 보존).
  • AppShell logout / language toggle / notification bell → admin 페이지에서도 동일하게 작동.

Test plan

  • npm run typecheck clean
  • npm run lint 0 errors (23 pre-existing warnings)
  • npx vitest run 930 tests pass (admin layout unit 3 tests 포함)
  • npm run i18n:check OK (EN/KO 동기)
  • super-admin 로그인 → /admin/users 진입 → 사이드바에 Dashboard 등 일반 메뉴 표시 확인
  • developer 로그인 → admin 섹션이 사이드바에 안 보임 + /admin/users 직접 접근 시 404 확인

🤖 Generated with Claude Code

`/admin/*` previously mounted under its own `AdminLayout` which rendered a
full 224px sidebar + 48px header in place of the AppShell. Entering Admin
therefore unmounted Dashboard / Projects / Scans / Approvals / Policies /
Integrations from the sidebar — users hit a dead-end where they couldn't
return to the rest of the portal without typing a URL.

Restructure the route table so `/admin/*` nests inside the AppShell route:

- `router.tsx`: move the entire admin Route group under the same `/`
  AppShell parent. The AppShell sidebar already renders an "Administration"
  section for super-admins (lines 188-199), so admin nav remains reachable
  alongside the main nav at all times.
- `AdminLayout.tsx`: strip the now-redundant chrome (sidebar / header /
  logout button / signed-in-as line). Reduced to the existence-hide guard
  (returns `AdminNotFound` for non-super-admins) wrapping `<Outlet />`.
  The `data-testid="admin-layout"` marker is preserved for harness compat.
- `AdminLayout.test.tsx`: drop assertions on the removed chrome
  (`admin-sidebar`, `admin-nav-*`, `admin-logout`); retain the three guard
  scenarios (super-admin / developer / no-user).
- `en/admin.json`, `ko/admin.json`: remove the unused `admin.layout.{title,
  signed_in_as}` keys (no other call sites).
- `docs/plan-w4-ui-ia-overhaul.md`: new plan doc — single source of truth
  for W4-A through W4-D.
- `docs/post-ga-execution-tracker.md`: mark W4-A "PR 대기".

Gates: typecheck clean · lint 0 errors · vitest 930 pass · i18n:check OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@haksungjang haksungjang merged commit 0c77338 into main May 26, 2026
@haksungjang haksungjang deleted the feat/w4-a-admin-nested-layout branch May 26, 2026 14:47
haksungjang added a commit that referenced this pull request May 26, 2026
…/ license split (#187)

Lands the three shared primitives that W4-B (#16 + #17 + #19) will consume.
No callers wired yet — keeps this PR small and reviewable, and lets W4-B's
diff stay focused on the per-tab UX changes.

Primitives
----------
1. `SortableColumnHeader` (`src/components/ui/sortable-column-header.tsx`)
   - 3-state click cycle: unset → asc → desc → unset.
   - Exposes `aria-sort` ("none"/"ascending"/"descending") and a
     `data-sort-order` attribute mirroring the same state for tests.
   - Replaces the existing "Sort by ▼ + Order ▼" toolbar pair. State stays
     in the caller (no internal `useState`), so URL params keep flowing
     through `useSearchParams` as today.
   - `nextSortState(column, current)` helper exported for reducer / URL
     normalization use.

2. Chart `onSegmentClick` prop (Severity + License distribution charts)
   - Optional `onSegmentClick?: (key) => void`. When provided, both the
     stacked-bar segment and the matching legend row become `<button>`s
     and forward the bucket key to the callback (Overview will use this
     for `?tab=vulnerabilities&severity=critical` style deep-links).
   - Backwards-compatible: when omitted, the segments + legend keep
     rendering as the original `<div>` / `<li>` elements with identical
     class names and test IDs. Buckets with `count === 0` stay
     non-interactive even when a callback is set.

3. `LicenseColumnCell` (`src/features/projects/components/`)
   - Single render primitive that surfaces both the SPDX identifier
     (`MIT`, `Apache-2.0`, …) and the policy category badge in a stacked
     layout — required so Allowed-MIT vs Allowed-Apache-2.0 are
     distinguishable in tables. W4-B will swap this into ComponentsTab
     and VulnerabilitiesTab.
   - Exposes `data-license-spdx` + `data-license-category` for filter
     plumbing and Playwright assertions.

i18n
----
- `common.json` (EN/KO): new `sort.aria_{ascending,descending,unsorted}`
  keys for the SortableColumnHeader `aria-label`.
- `project_detail.json` (EN/KO): new `components.license.unknown_dash`
  ("—") used by LicenseColumnCell when the SPDX field is null.

Gates
-----
- typecheck clean
- lint 0 errors (+1 react-refresh warning on the new helper export,
  matching the project-wide pattern — 22 pre-existing instances)
- vitest 951 pass (+21 new: 12 SortableColumnHeader, 3 LicenseColumnCell,
  5 chart interaction, +1 absorbed; +12 + 3 + 5 + 1 = 21 incremental)
- i18n:check OK

Tracker
-------
The `docs/post-ga-execution-tracker.md` W4 section was introduced in
PR #186 (W4-A), which is still open. A tiny doc-only follow-up will
mark "W4-B-prep ✅" once W4-A merges and the W4 lines land on main.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
haksungjang added a commit that referenced this pull request May 26, 2026
…bilities UX overhaul (#188)

* feat(W4-B-prep): shared primitives for chart deep-link / column sort / license split

Lands the three shared primitives that W4-B (#16 + #17 + #19) will consume.
No callers wired yet — keeps this PR small and reviewable, and lets W4-B's
diff stay focused on the per-tab UX changes.

Primitives
----------
1. `SortableColumnHeader` (`src/components/ui/sortable-column-header.tsx`)
   - 3-state click cycle: unset → asc → desc → unset.
   - Exposes `aria-sort` ("none"/"ascending"/"descending") and a
     `data-sort-order` attribute mirroring the same state for tests.
   - Replaces the existing "Sort by ▼ + Order ▼" toolbar pair. State stays
     in the caller (no internal `useState`), so URL params keep flowing
     through `useSearchParams` as today.
   - `nextSortState(column, current)` helper exported for reducer / URL
     normalization use.

2. Chart `onSegmentClick` prop (Severity + License distribution charts)
   - Optional `onSegmentClick?: (key) => void`. When provided, both the
     stacked-bar segment and the matching legend row become `<button>`s
     and forward the bucket key to the callback (Overview will use this
     for `?tab=vulnerabilities&severity=critical` style deep-links).
   - Backwards-compatible: when omitted, the segments + legend keep
     rendering as the original `<div>` / `<li>` elements with identical
     class names and test IDs. Buckets with `count === 0` stay
     non-interactive even when a callback is set.

3. `LicenseColumnCell` (`src/features/projects/components/`)
   - Single render primitive that surfaces both the SPDX identifier
     (`MIT`, `Apache-2.0`, …) and the policy category badge in a stacked
     layout — required so Allowed-MIT vs Allowed-Apache-2.0 are
     distinguishable in tables. W4-B will swap this into ComponentsTab
     and VulnerabilitiesTab.
   - Exposes `data-license-spdx` + `data-license-category` for filter
     plumbing and Playwright assertions.

i18n
----
- `common.json` (EN/KO): new `sort.aria_{ascending,descending,unsorted}`
  keys for the SortableColumnHeader `aria-label`.
- `project_detail.json` (EN/KO): new `components.license.unknown_dash`
  ("—") used by LicenseColumnCell when the SPDX field is null.

Gates
-----
- typecheck clean
- lint 0 errors (+1 react-refresh warning on the new helper export,
  matching the project-wide pattern — 22 pre-existing instances)
- vitest 951 pass (+21 new: 12 SortableColumnHeader, 3 LicenseColumnCell,
  5 chart interaction, +1 absorbed; +12 + 3 + 5 + 1 = 21 incremental)
- i18n:check OK

Tracker
-------
The `docs/post-ga-execution-tracker.md` W4 section was introduced in
PR #186 (W4-A), which is still open. A tiny doc-only follow-up will
mark "W4-B-prep ✅" once W4-A merges and the W4 lines land on main.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(W4-B #16/#17/#19): Overview chart deep-link + Components/Vulnerabilities UX overhaul

W4-B-prep (PR #187, commit 07a8c9f) shipped three shared primitives —
SortableColumnHeader, LicenseColumnCell, and the chart onSegmentClick
prop — without wiring them. This PR consumes them across the three
tabs the user identified in #16/#17/#19.

#16 — Overview tab
- Drop the Risk Score card (the header's RiskGauge already surfaces the
  score). The #35 Surface B "vuln data unavailable" alert survives as a
  standalone amber banner at the top of the tab so 0 CVEs is never
  silently misread as "safe".
- Severity + License charts become deep-links: clicking a segment writes
  ?tab=vulnerabilities&severity=<key> or ?tab=licenses&license_category=
  <key>. The PDF setTab helper already preserves severity / license_
  category when entering those tabs (PDP lines 196-218), so the link
  lands on the filtered list.
- Recent Scans row click branches on status:
    succeeded / failed / cancelled → onJumpToComponents (pin snapshot +
      jump to Components — final results are the actionable view)
    queued / running               → onSelectScan (re-open progress
      drawer)

#17 — Components tab
- License column replaced with LicenseColumnCell (SPDX above, policy
  category badge below). Two Allowed components are now distinguishable
  at a glance.
- Severity + License MultiSelect drops removed from ComponentsToolbar.
  The Overview chart deep-links populate the URL params; the new
  ActiveFilterChips row above the table surfaces each selected value as
  a removable chip so the filter stays visible.
- Sort + Order <select> drops removed. SortableColumnHeader on the
  Name / License / Severity headers cycles unset → asc → desc → unset.
  URL ?sort= / ?order= already mirrored the state.

#19 — Vulnerabilities tab
- License column uses LicenseColumnCell (BE list schema has no `license`
  SPDX field yet — we render the dash above the category badge so the
  visual matches Components without inventing data. SPDX surfacing is a
  follow-up that needs a BE schema bump).
- Severity / License / Sort / Order toolbar controls removed; replaced by
  ActiveFilterChips + SortableColumnHeader on Severity / CVSS / EPSS /
  Reachability / Status. Column order tightened to CVE / Severity /
  CVSS / EPSS / Reachability / License / Title / Affected / Status —
  Discovered moved to the drawer to reduce horizontal scroll on a 13"
  laptop.
- Component@Version column NOT added: VulnerabilityListItem (BE) only
  exposes affected_component_count, no name/version. Follow-up requires
  BE schema bump.

i18n: 6 new keys (active_filters.*) added in EN + KO. 13 stale toolbar
keys (severity_label / license_label / sort_label / sort_by_* /
order_label / order_*) removed from EN + KO. i18n:check green.

Gates: typecheck clean · lint 0 errors · vitest 958 passing
(OverviewTab 11, ComponentsTab 16, VulnerabilitiesTab 44 — 11 net new) ·
i18n:check OK.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
haksungjang added a commit that referenced this pull request May 26, 2026
Session of 2026-05-26→27: six PRs merged sequentially —
- #186 W4-A (admin layout)
- #187 W4-B-prep (3 primitives)
- #188 W4-B (Overview/Components/Vulnerabilities UX)
- #189 W4-C (IA reorganize 11→8 tabs)
- #190 W4-D (npm lockfile fallback for TYPE/USAGE)
- #191 follow-up (Vulnerability list-schema bump)

Wave 4 work bundle now closed. Next session reverts to user-feedback
intake mode; operational verification steps for #190 + #191 documented
in the handoff for the orchestrator to run on the next demo seed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant