Skip to content

feat(FR-2525): add project-admin tier to admin menu and project selector#6653

Merged
graphite-app[bot] merged 1 commit intomainfrom
04-13-feat_fr-2209_add_project-admin_tier_to_admin_menu_and_project_selector
Apr 14, 2026
Merged

feat(FR-2525): add project-admin tier to admin menu and project selector#6653
graphite-app[bot] merged 1 commit intomainfrom
04-13-feat_fr-2209_add_project-admin_tier_to_admin_menu_and_project_selector

Conversation

@yomybaby
Copy link
Copy Markdown
Member

@yomybaby yomybaby commented Apr 13, 2026

Resolves #6644 (FR-2552)

Part of FR-2209 Project Admin Management stack (PR-1a).
Stacks on #6651.

Summary

  • useWebUIMenuItems now gates admin-category keys by useEffectiveAdminRole(); project admins see only Session/Serving/Data/Members
  • WebUISider uses 3-tier hasAdminCategoryRole = useEffectiveAdminRole() !== 'none'
  • ProjectSelect renders a "Project Admin" badge on admin-scoped projects for project-admin-only users

Verification

  • bash scripts/verify.sh -> === ALL PASS ===

Copilot AI review requested due to automatic review settings April 13, 2026 14:02
Copy link
Copy Markdown
Member Author

yomybaby commented Apr 13, 2026


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • flow:merge-queue - adds this PR to the back of the merge queue
  • flow:hotfix - for urgent changes, fast-track this PR to the front of the merge queue

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has required the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 13, 2026

Coverage report for ./react

St.
Category Percentage Covered / Total
🔴 Statements
9.01% (-0% 🔻)
1771/19647
🔴 Branches
8.13% (-0.01% 🔻)
1115/13708
🔴 Functions
5.38% (+0.02% 🔼)
287/5337
🔴 Lines
8.71% (-0% 🔻)
1661/19076

Test suite run success

859 tests passing in 39 suites.

Report generated by 🧪jest coverage report action from 8f0d619

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds UI support for a new “project-admin” tier by (1) adjusting Admin menu visibility/gating and (2) showing a “Project Admin” badge in the project selector, backed by a new i18n string.

Changes:

  • Added projectSelect.ProjectAdminBadge translation key (en/ko).
  • Introduced useEffectiveAdminRole()-based gating for the Admin category/menu in useWebUIMenuItems and WebUISider.
  • Updated ProjectSelect option rendering to display a per-project “Project Admin” badge.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
resources/i18n/en.json Adds English translation for the Project Admin badge label.
resources/i18n/ko.json Adds Korean translation for the Project Admin badge label.
react/src/hooks/useWebUIMenuItems.tsx Adds project-admin tier menu filtering and exports project-admin page key list.
react/src/components/ProjectSelect.tsx Displays a “Project Admin” badge next to projects where the user has project-admin scope.
react/src/components/MainLayout/WebUISider.tsx Shows Admin Settings entry for any effective admin role (including project admin).

Comment thread resources/i18n/en.json
Comment thread react/src/hooks/useWebUIMenuItems.tsx
Comment thread react/src/hooks/useWebUIMenuItems.tsx
Comment thread react/src/hooks/useWebUIMenuItems.tsx
Comment thread react/src/components/ProjectSelect.tsx
Comment thread react/src/components/MainLayout/WebUISider.tsx
@yomybaby
Copy link
Copy Markdown
Member Author

Review Summary — PR #6653 (FR-2209 PR-1a)

Verification: bash scripts/verify.sh=== ALL PASS === confirmed locally.

Spec compliance (FR-2314 / FR-2209 PR-1a)

Criterion Status
Project-admin-only user sees Admin menu category PASS (via hasAdminCategoryRole = useEffectiveAdminRole() !== 'none' in WebUISider.tsx:58)
Project admin sees only Session/Serving/Data/Members pages PARTIAL — filtering logic correct (useWebUIMenuItems.tsx:486-493); admin-members key is forward-declared but no menu item emits that key yet (delivered in PR-2d)
Domain admin / superadmin always see Admin menu PASS
Badge shows on admin projects for project-admin users only PASS (shouldShowProjectAdminBadge === 'projectAdmin' gating on ProjectSelect.tsx:55)

Findings

🟠 HIGH — Route-guard mismatch for project admins (spec-deferred)

File: react/src/hooks/useWebUIMenuItems.tsx:807-828 (isCurrentPageUnauthorized)

The unauthorized check still uses legacy currentUserRole only. A project-admin user (with baiClient.is_admin === false) who clicks /admin-session from the menu will render as 401 Unauthorized because the branch currentUserRole !== 'admin' && currentUserRole !== 'superadmin' && ALL_ADMIN_PAGE_KEYS.has(...) fires before reading effectiveAdminRole.

This creates a menu-vs-page inconsistency: the item is visible (PR-1a) but the page refuses to render. Likely intended to be resolved together with the project-scoping work in PR-2a/2c (and the new PR-2d for members), but not called out in dev-plan.md PR-1a acceptance criteria. Recommend either:

  1. Document as a known gap deferred to PR-2a (acceptable per Graphite-stack sequencing).
  2. Or extend this check now: !((currentUserRole === 'admin') || (currentUserRole === 'superadmin') || (PROJECT_ADMIN_PAGE_KEY_SET.has(currentMenuKey) && effectiveAdminRole === 'projectAdmin')).

Given spec scope is honored strictly, flagging as HIGH but not auto-fixing in this PR.

🟡 MEDIUM — admin-members key is a forward declaration

File: react/src/hooks/useWebUIMenuItems.tsx:150

'admin-members' is included in PROJECT_ADMIN_PAGE_KEYS but:

  • Not present in VALID_MENU_KEYS (type MenuKeys does not include it — type cast on filter relies on a loose item.key as string which is fine but the canonical source drifts from the key set).
  • Not present in ALL_ADMIN_PAGE_KEYS (so the 401 guard won't treat it as an admin route).
  • No item in fullAdminMenu emits this key.

Comment (lines 141-145) flags this as intentional scaffolding for PR-2d — acceptable, but consider either adding to VALID_MENU_KEYS now to keep the single-source-of-truth intact, or moving PROJECT_ADMIN_PAGE_KEYS to the PR that introduces the page.

🟡 MEDIUM — UUID short-ID collision window

File: react/src/components/ProjectSelect.tsx:60-64 / useCurrentUserProjectRoles.ts:62-68

Short IDs are first 8 hex chars of a UUID (32-bit space). For a single user with ≤20 projects the birthday-collision probability is <1e-7, so practically safe. However, since the primary scopeId path already carries the full UUID (only the role-name regex fallback needs 8 hex), the hook's contract throws away precision that is otherwise available. Suggest (out of scope here, but as tech-debt for PR-0): emit a projectAdminUuids: string[] alongside projectAdminIds when the primary path is used, and compare full UUIDs in ProjectSelect.tsx. Set.has(short) there effectively downgrades to the fallback's precision.

Not blocking PR-1a; noted for a future cleanup on the base hook.

🟢 LOW — Redundant hook calls in ProjectSelect.tsx

File: react/src/components/ProjectSelect.tsx:50-51

Both useCurrentUserProjectRoles() and useEffectiveAdminRole() are called. The latter internally calls the former, so Relay runs useLazyLoadQuery twice (cache-shared, so no extra network, but two subscriptions). Consolidate:

const { projectAdminIds, isSuperAdmin, domainAdminDomains } =
  useCurrentUserProjectRoles();
const shouldShowProjectAdminBadge =
  !isSuperAdmin && domainAdminDomains.length === 0 && projectAdminIds.length > 0;

🟢 LOW — Tag vs BAITag in ProjectSelect.tsx:154-159

Project uses BAITag (outline, SM radius) for chip-style labels. This PR uses the raw Ant Tag with color="processing" for a filled blue badge — the chosen look is deliberate (high visibility inside a dropdown option), and BAITag would not render with the same prominence. Acceptable; mentioning only to confirm the decision was intentional.

Project-convention checks

  • 'use memo' preserved on WebUISider (line 46) and useWebUIMenuItems (line 204). ProjectSelect has no 'use memo' — it also didn't before this PR, so no regression (separate follow-up opportunity).
  • Ant Design v6 props: no deprecated prop usage in the diff.
  • i18n convention (camelCase namespace projectSelect + PascalCase leaf ProjectAdminBadge): correct. Other languages deferred to FR-2558 as noted.
  • Security: client-side menu gating only; backend RBAC is the true gate — acceptable since no privileged action depends solely on menu visibility.

Auto-fix decision

No CRITICAL findings; HIGH finding is spec-deferred per dev-plan.md. Not auto-fixing. Recommend the HIGH item be addressed in PR-2a (or earlier if agreed).

Label: status:review applied.

@yomybaby yomybaby force-pushed the 04-13-feat_fr-2209_add_project-admin_tier_to_admin_menu_and_project_selector branch from 004bb4d to d6757a2 Compare April 13, 2026 14:51
@github-actions github-actions Bot added size:L 100~500 LoC and removed size:M 30~100 LoC labels Apr 13, 2026
@yomybaby yomybaby force-pushed the 04-13-feat_fr-2209_add_project-admin_tier_to_admin_menu_and_project_selector branch from d6757a2 to db18202 Compare April 14, 2026 08:07
@yomybaby yomybaby force-pushed the 04-13-feat_fr-2209_add_usecurrentuserprojectroles_hook_with_myroles_rbac_query branch from 4181d2d to e0a3057 Compare April 14, 2026 08:07
@yomybaby yomybaby changed the title feat(FR-2209): add project-admin tier to admin menu and project selector feat(FR-2525): add project-admin tier to admin menu and project selector Apr 14, 2026
@yomybaby yomybaby force-pushed the 04-13-feat_fr-2209_add_usecurrentuserprojectroles_hook_with_myroles_rbac_query branch from e0a3057 to 5ac5f01 Compare April 14, 2026 08:23
@yomybaby yomybaby force-pushed the 04-13-feat_fr-2209_add_project-admin_tier_to_admin_menu_and_project_selector branch from db18202 to ac39da7 Compare April 14, 2026 08:23
@graphite-app
Copy link
Copy Markdown

graphite-app Bot commented Apr 14, 2026

Merge activity

…tor (#6653)

Resolves #6644 (FR-2552)

Part of FR-2209 Project Admin Management stack (PR-1a).
Stacks on #6651.

## Summary
- `useWebUIMenuItems` now gates admin-category keys by `useEffectiveAdminRole()`; project admins see only Session/Serving/Data/Members
- `WebUISider` uses 3-tier `hasAdminCategoryRole = useEffectiveAdminRole() !== 'none'`
- `ProjectSelect` renders a "Project Admin" badge on admin-scoped projects for project-admin-only users

## Verification
- `bash scripts/verify.sh` -> `=== ALL PASS ===`
@graphite-app graphite-app Bot force-pushed the 04-13-feat_fr-2209_add_usecurrentuserprojectroles_hook_with_myroles_rbac_query branch from 5ac5f01 to 9dc530a Compare April 14, 2026 08:41
@graphite-app graphite-app Bot force-pushed the 04-13-feat_fr-2209_add_project-admin_tier_to_admin_menu_and_project_selector branch from ac39da7 to 8f0d619 Compare April 14, 2026 08:41
Base automatically changed from 04-13-feat_fr-2209_add_usecurrentuserprojectroles_hook_with_myroles_rbac_query to main April 14, 2026 08:43
@graphite-app graphite-app Bot merged commit 8f0d619 into main Apr 14, 2026
7 checks passed
@graphite-app graphite-app Bot deleted the 04-13-feat_fr-2209_add_project-admin_tier_to_admin_menu_and_project_selector branch April 14, 2026 08:44
graphite-app Bot pushed a commit that referenced this pull request Apr 14, 2026
…itch, and fix sider go-back on admin pages (#6656)

Resolves #6645 (FR-2553)

Part of FR-2209 Project Admin Management stack (PR-1b).
Stacks on #6653.

## Summary
- In admin mode, when a project-admin user selects a project they are not an admin of in the header `ProjectSelect`, a confirm modal asks before exiting admin mode
- Confirm modal content includes the target project name (new `{{projectName}}` placeholder in `header.SwitchOutOfAdminConfirmContent`)
- While the modal is open, the selector optimistically shows the target project with a loading state; cancel reverts to the current project, confirm commits and navigates back to the last-visited general page
- Fix sider "go back" button silently doing nothing on admin category pages (e.g. `/project-admin-users`) for users whose effective admin role filters the page out of their admin menu — superadmin visiting via URL, or a project-admin whose current project lacks admin rights. Added a role-independent `isCurrentPathAdminCategory` flag (derived from `ALL_ADMIN_PAGE_KEYS` + `PROJECT_ADMIN_PAGE_KEY_SET` + `storage-settings`) and used it to guard `goBackPath` writes, so an admin path is never stored as the "last visited general page"
- Simplify `useCurrentUserProjectRoles`: drop `isProjectAdminForId`/`deriveProjectAdminIds` helpers, `MyRolesAssignmentNode` shape, and `rawAssignments`. `projectAdminIds` now contains full project UUIDs sourced from `role.scopes` (new backend schema), so call sites use plain `Array.includes` against `useCurrentProject().id`. `useEffectiveAdminRole` correctly compares `currentProjectId` against this simplified list.
- Translate the two new `header.*` keys into all 19 target languages
- Regenerate `data/schema.graphql` against core 26.4.1 (adds `AddRevisionOptions` and updates version annotations)

## Verification
- `bash scripts/verify.sh` → `=== ALL PASS ===`

## Test plan
- [ ] As a project-admin user, enter admin mode and switch the header project selector to a project where you are not an admin → confirm modal appears with the target project name
- [ ] Cancel the modal → selector reverts to the previously selected project, no navigation occurs
- [ ] Confirm the modal → project switches, admin mode exits, UI navigates to the last-visited general page
- [ ] Repeat for superadmin and domain admin: the selector remains visible and switching does not trigger the modal (no regression)
- [ ] On `/project-admin-users` as a project admin, click the sider "go back" button → navigates to the previously visited general page (not the same admin page)
- [ ] Visit `/project-admin-users` as a superadmin via URL, navigate to `/session`, then enter admin mode and use "go back" → returns to `/session` (not `/project-admin-users`)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:i18n Localization area:ux UI / UX issue. size:L 100~500 LoC status:review Under review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PR-1a: Add project-admin tier to admin menu and project selector

2 participants