Files
Rpa_Skills/rpa-dashboard-ai/templates/reference/web/components/BrandHeader.tsx
T

126 lines
4.8 KiB
TypeScript

import { ThemeSwitcher } from '../theme'
import { ROLE_LABEL } from '../auth'
/**
* The app shell's top bar: brand mark, title, and the signed-in user's
* controls. The board tab strip that used to live below it is now the left
* sidebar (`Sidebar.tsx`) — this component no longer owns navigation.
*
* The gradient is the one saturated surface in the product. Everything on it
* is white or a white alpha rather than a palette token, because a
* token-coloured control on the band reads as a hole punched through it —
* see `.sb-appbar` and `.sb-ghost-btn` in index.css.
*/
/** 'admin' is never a single global role any more — computed here from the
* two per-project flags so the header names exactly what this account can
* reach, rather than falling back to "Operator" for someone who is in fact
* an admin of one or both projects. */
function roleLabel(user: {
role: string
ecw_role?: string | null
tebra_role?: string | null
}): string {
if (user.role === 'super_admin') return ROLE_LABEL.super_admin
const ecw = user.ecw_role === 'admin'
const tebra = user.tebra_role === 'admin'
if (ecw && tebra) return 'Admin · Both'
if (ecw) return 'Admin · ECW'
if (tebra) return 'Admin · Tebra'
return ROLE_LABEL.operator
}
export function BrandHeader({
user,
onLogout,
sidebarOpen,
onToggleSidebar,
}: {
user: {
username: string
real_name: string | null
role: string
ecw_role?: string | null
tebra_role?: string | null
}
onLogout: () => void
/** Present only on screens that have the left sidebar. */
sidebarOpen?: boolean
onToggleSidebar?: () => void
}) {
return (
<header className="sb-appbar shrink-0">
<ShieldMark />
{onToggleSidebar && (
<button
type="button"
onClick={onToggleSidebar}
title={sidebarOpen ? 'Hide sidebar' : 'Show sidebar'}
aria-label={sidebarOpen ? 'Hide sidebar' : 'Show sidebar'}
aria-expanded={sidebarOpen}
className="sb-ghost-btn"
style={{ padding: '3px', lineHeight: 0 }}
>
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor"
strokeWidth="1.6" aria-hidden="true">
<rect x="3.5" y="4.5" width="17" height="15" rx="2" />
<line x1="9" y1="4.5" x2="9" y2="19.5" />
</svg>
</button>
)}
{/* What the tool is, matching the browser tab and the sign-in screen.
The brand and EMR edition — "Superbill Automation for ECW" — live in
the sidebar's own group headers instead (`Sidebar.tsx`), which is
where the rail has room to state them without competing here. */}
<div className="min-w-0">
<div className="sb-appbar-title truncate">Extraction and Automation Tool</div>
<div className="sb-appbar-sub truncate">Extract · correct · post to eClinicalWorks</div>
</div>
<div className="ml-auto flex shrink-0 items-center gap-3">
<div className="sb-who hidden sm:block">
<b>{user.real_name ?? user.username}</b>
<span>{roleLabel(user)}</span>
</div>
<button type="button" onClick={onLogout} title="Sign out" className="sb-ghost-btn">
Sign out
</button>
<ThemeSwitcher />
</div>
</header>
)
}
/**
* The product mark: shield plus three ascending bars, drawn as vector.
*
* The same artwork the sign-in screen carries, so the two screens read as one
* product — but flat here, with no bob and no glow: on the sign-in screen it
* is the subject, and in a header it is a label for the page.
*
* Vector rather than the `logo-mark.png` raster it replaced. The raster could
* not take the header's drop-shadow without fringing along its antialiased
* edge, and it could not follow a theme.
*
* Colours are literal on purpose. The shield and its bars are artwork — the
* mark is the same mark in light and dark, the way a printed logo is — so
* these four values are deliberately NOT theme tokens. `--shield`, `--clean`,
* `--verify` and `--review` hold the same hexes for anything that needs to
* reference them.
*
* `aria-hidden` because the product is named in text immediately beside it.
*/
export function ShieldMark({ size = 34 }: { size?: number }) {
return (
<span className="sb-appbar-mark" style={{ width: size, height: size }}>
<svg viewBox="0 0 64 64" fill="none" aria-hidden="true" style={{ width: size, height: size }}>
<path
d="M32 5 L53 13 V34 C53 45.5 43.5 53.5 32 58.5 C20.5 53.5 11 45.5 11 34 V13 Z"
fill="#21395a"
/>
<rect x="19.5" y="32" width="7" height="10" rx="1.6" fill="#51a68f" />
<rect x="30" y="24" width="7" height="18" rx="1.6" fill="#e39b34" />
<rect x="40.5" y="18" width="7" height="24" rx="1.6" fill="#de5243" />
</svg>
</span>
)
}