76 lines
2.2 KiB
TypeScript
76 lines
2.2 KiB
TypeScript
import { NavLink } from "react-router-dom";
|
|
import type { ReactNode } from "react";
|
|
import { APP_NAME } from "../../brand";
|
|
import { SiteFooter } from "../../legal/SiteFooter";
|
|
import { AppAvatar, AppButton, AppText } from "../../ui";
|
|
import { PRIMARY_NAV } from "./nav";
|
|
|
|
type Props = {
|
|
brand?: string;
|
|
userLabel: string;
|
|
onLogout: () => void;
|
|
children: ReactNode;
|
|
};
|
|
|
|
function initials(label: string): string {
|
|
const parts = label.trim().split(/\s+/).filter(Boolean);
|
|
if (parts.length === 0) return "•";
|
|
if (parts.length === 1) return parts[0].slice(0, 1).toUpperCase();
|
|
return (parts[0][0] + parts[1][0]).toUpperCase();
|
|
}
|
|
|
|
export function AppShell({
|
|
brand = APP_NAME,
|
|
userLabel,
|
|
onLogout,
|
|
children,
|
|
}: Props) {
|
|
return (
|
|
<div className="app-shell">
|
|
<header className="app-shell__header">
|
|
<AppText variant="headline" tone="accent">
|
|
{brand}
|
|
</AppText>
|
|
<div className="app-shell__user">
|
|
<AppAvatar initials={initials(userLabel)} />
|
|
<span className="app-shell__chip" title={userLabel}>
|
|
{userLabel}
|
|
</span>
|
|
<AppButton buttonStyle="plain" size="small" expanded={false} onClick={onLogout}>
|
|
Выйти
|
|
</AppButton>
|
|
</div>
|
|
</header>
|
|
|
|
<nav className="app-shell__nav" aria-label="Основное меню">
|
|
<p className="app-section-header">Продукт</p>
|
|
<ul className="app-shell__nav-list">
|
|
{PRIMARY_NAV.map((item) => (
|
|
<li key={item.id}>
|
|
{item.soon ? (
|
|
<span className="app-shell__nav-link is-disabled" aria-disabled="true">
|
|
{item.label}
|
|
<span className="app-shell__badge">скоро</span>
|
|
</span>
|
|
) : (
|
|
<NavLink
|
|
to={item.to}
|
|
end={item.to === "/"}
|
|
className={({ isActive }) =>
|
|
`app-shell__nav-link${isActive ? " is-active" : ""}`
|
|
}
|
|
>
|
|
{item.label}
|
|
</NavLink>
|
|
)}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<SiteFooter />
|
|
</nav>
|
|
|
|
<div className="app-shell__main">{children}</div>
|
|
</div>
|
|
);
|
|
}
|