import { useEffect, useRef, useState } from 'react' import { useZino } from '../api/provider.jsx' import './UserMenu.css' /** First letters of the first two words — a name is all we have for an avatar. */ function initials(user) { const from = user?.name || user?.email || '' const parts = from.replace(/@.*$/, '').split(/[\s._-]+/).filter(Boolean) if (!parts.length) return '?' return (parts[0][0] + (parts[1]?.[0] || '')).toUpperCase() } /** * The signed-in identity, with sign-out folded inside it. * * The role stays visible on the trigger rather than only inside the menu: RBAC is * the demo, so who you are signed in as should never take a click to find out. */ export default function UserMenu() { const { user, signOut } = useZino() const [open, setOpen] = useState(false) const wrapRef = useRef(null) const triggerRef = useRef(null) // Close on an outside click or Escape. Bound only while open, so the shell // costs nothing at rest. useEffect(() => { if (!open) return function onDown(e) { if (!wrapRef.current?.contains(e.target)) setOpen(false) } function onKey(e) { if (e.key !== 'Escape') return setOpen(false) triggerRef.current?.focus() } document.addEventListener('mousedown', onDown) document.addEventListener('keydown', onKey) return () => { document.removeEventListener('mousedown', onDown) document.removeEventListener('keydown', onKey) } }, [open]) const name = user?.name || user?.email || 'Signed in' // Role slugs read as titles: partner_agent -> Partner agent. const roles = (user?.roles || []) // `sme_underwriter` → "SME underwriter": the acronyms the roles here use stay upper. .map((r) => String(r).replace(/_/g, ' ').replace(/^./, (c) => c.toUpperCase()) .replace(/\b(sme|uw|rm|posp|csr|ops|kyc)\b/gi, (m) => m.toUpperCase())) .join(', ') return (