refactor(frontend): gate analytics + admin nav on permissions - #558
Conversation
77ad884 to
32cc715
Compare
32cc715 to
98c3b89
Compare
98c3b89 to
712e99a
Compare
712e99a to
49bf0c5
Compare
49bf0c5 to
ca82116
Compare
|
@claude-review |
|
Claude finished @zaira-bibi's task in 2m 22s —— View job Claude Code ReviewReviewed the diff against ✅ Strengths
🟡 Suggestions (non-blocking)Medium — Low — infinite spinner when Low — duplicated spinner JSX SecurityNo concerns. UI gating is correctly framed as convenience, not enforcement; every gated entry still fails closed, and the backend enforces each endpoint independently. VerdictApprove-worthy pending the maintainer's call on the Medium suggestion. No blocking issues; the fetch-timing item is a small correctness/efficiency polish rather than a defect. |
regisb
left a comment
There was a problem hiding this comment.
Again, I'm definitely not the right person to review this but as far as I'm concerned, this is good to go.
ca82116 to
5364ec0
Compare
5364ec0 to
6a61ed2
Compare
6be06db to
494a62d
Compare
494a62d to
c9260c0
Compare
Render the Analytics and Admin sidebar entries through a small config-driven, permission-gated mechanism that mirrors how plugin entries render, instead of the ad-hoc canViewAnalytics boolean (Analytics) and the coarse is_admin flag (Admin). A GATED_NAV config declares each entry's (permission, scope); resolveNavPermissions resolves them once per token via GET /permissions/can (one check per item, failing closed) into a map the dashboard layout threads to the sidebars, which render the permitted entries through a shared NavItem. Admin now gates on email.whitelist.read (whitelist scope) rather than is_admin, and the whitelist page's own client guard is aligned to the same permission so nav visibility and page access agree. UI gating fails closed and is not a security boundary — endpoints still enforce their own permissions and return 403. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The layout test added alongside the analytics dashboard mocks checkPermission and asserts the canViewAnalytics boolean, both of which this branch replaces with resolveNavPermissions and a navPermissions map — so all three cases failed once the branch was rebased onto it. Assert the resolved map threaded to the sidebar instead, keeping each original intent, including the guard that a failed re-resolve after a token change cannot leave the previous user's entries in place. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
c9260c0 to
05b8440
Compare
What
Render the Analytics and Admin sidebar entries through a small config-driven, permission-gated mechanism that mirrors how plugin entries render — replacing the ad-hoc
canViewAnalyticsboolean (Analytics) and the coarseis_adminflag (Admin).Changes
GATED_NAVconfig +resolveNavPermissions— resolves each gated entry once per token viaGET /permissions/can(one check per item, failing closed) into aRecord<key, boolean>NavItemcomponent (extracted from the repeated nav-link blocks)navPermissionsonce and threads it to both sidebars, which renderGATED_NAV.filter(i => navPermissions?.[i.key]).map(NavItem);canViewAnalyticsremovedemail.whitelist.read(whitelist scope) instead ofis_adminemail.whitelist.readcheck (was!is_admin), so nav visibility and page access agreeresolveNavPermissions,NavItem, rewrittenAppSidebargating (per key),WhitelistPageguardHow to Test
make test.frontend.vitest→ green (190 tests).cd frontend && bun run typecheck,make lint.frontend,make test.frontend.api→ all clean.analytics.readsees the Analytics entry; a user withemail.whitelist.readsees the Admin entry and can open/dashboard/admin/whitelist; a user with neither sees neither entry, and direct navigation to the whitelist page redirects to/dashboard.Notes
gh stack sync --pruneonce feat(frontend): add analytics dashboard at /dashboard/analytics #539 lands).generated.tschange; no new dependencies. Single permission check per gated entry (no batch endpoint).This PR description was written with the assistance of an LLM (Claude).