feat(shared-ui): user menu dropdown + integration on portal-shell + portal-admin #149
Reference in New Issue
Block a user
Delete Branch "feat/shared-user-menu-and-integration"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
PR 1 of 3 from the user-menu / profile / cross-app-link chantier per the agreed staging:
UserMenudropdown component + integration onportal-shellandportal-admin; anonymous Sign-in button moves torounded-md./profilepages on both apps./api/me/capabilitiesendpoint + real role surfacing on the sidebar widget + cross-app links in the menu (both directions).Lands the gitea / github-shaped avatar dropdown so admins and end users get the familiar "Signed in as / Profile / Settings / Sign out" pattern. Future entries (cross-app link, role-based visibility) plug into the same
itemsinput without touching the shared component.What lands
Shared component —
libs/shared/ui/src/lib/user-menu/cdkMenuTriggerForopening the panel in an overlay portal. Same primitivesThemeSwitcherandLocaleSwitcheralready use — keyboard nav, focus management and Escape-to-close come for free.displayName→username→ separator → caller-supplieditems→ separator → dedicated Sign out row at the bottom.UserMenuItemshape supportsrouterLink(intra-app) andhref(cross-app / external) so PR 3's "Open Portal Admin" entry can land without re-shaping the API.aria-disabledrows with a right-aligned badge — same Soon-chip pattern the admin sidebar already uses for not-yet-shipped entries.signOutoutput. Avoids special-casing item types and keeps the destructive action visually + structurally distinct.--user-menu-avatar-bg/--user-menu-avatar-fgCSS custom properties so each host header can re-skin without forking the component (portal-admin uses translucent white over the brand-primary-600 header; portal-shell keeps the default brand-primary-500).Portal-shell integration —
apps/portal-shell/src/app/components/header/<lib-user-menu>.rounded-full→rounded-mdper the reference image. Loading + error chips follow for visual consistency with the new square-ish avatar.messages.fr.xlf:header.userMenu.{profile,settings,signedInAs,signOut,trigger.aria}+common.badge.soon.Portal-admin integration —
apps/portal-admin/src/app/components/header/.btn--primary/.btn--secondaryfor anonymous + error states (no search bar / notification cluster, per ADR-0020), only the authenticated state goes through the menu.--user-menu-avatar-bg/--user-menu-avatar-fginheader.scssunder.auth-widget lib-user-menuso the avatar reads on the brand-primary-600 background. No FR labels (no admin locale in v1 per ADR-0020).Notes for the reviewer
Sign outoutside theitemsarray? It's the only destructive action in the panel + always present + emits an event rather than navigates. Keeping it special-cased lets theitemsAPI stay tight ({ label, icon?, routerLink? | href?, disabled?, badge? }) and gives each app a single typed entry point (signOutoutput) rather than a brittleitems[].action === 'sign-out'discriminator.data-testid="user-menu"on the component host? The shared spec already covers panel internals; each app's spec needs a top-level handle to reach the trigger without coupling to the avatar CSS class. Same pattern as the existingdata-testid="sign-in-button"./profileon both apps in this PR. Portal-shell already has a demo/profileroute, so the link works; portal-admin will 404 until PR 2 lands the actual page. Interim cost is acceptable given PR 2 lands directly behind this.libs/shared/ui— same tier asIcon. Promotion criteria, dark-mode, a11y, and i18n all follow the existing ADRs already in force (ADR-0004 + 0016 + 0019).Test plan
pnpm nx test shared-ui— 8 specs pass (was 3, +5 forUserMenu).pnpm nx test portal-shell— 35 specs pass (was 34, +1 for the new "opens menu" assertion).pnpm nx test portal-admin— 46 specs pass (was 45, +1 for the new "opens menu" assertion).pnpm nx run-many -t lint test build --projects=portal-shell,portal-admin,shared-ui,shared-state,feature-auth— 15/15 tasks green, including the i18n-strictportal-shell:build:production./profile, Settings appears greyed with a "Soon" badge, Sign out triggers the BFF logout. Repeat on portal-admin (the avatar background should pick the translucent-white override over the brand-primary-600 header).What's next
PR 2 picks up directly: builds a real
/profilepage on each app, both reading fromfeature-auth'scurrentUser()signal.