137 lines
5.2 KiB
TypeScript
137 lines
5.2 KiB
TypeScript
import { Inbox } from "lucide-react";
|
||
import type { Client } from "../../entities/client/types";
|
||
import type { MeResponse, ProfileOption } from "../../shared/api/mockApi";
|
||
import { initials } from "../../shared/lib/format";
|
||
import { NodeDcProfileMenu, NodeDcSelect } from "../../shared/nodedc-ui";
|
||
|
||
export function TopBar({
|
||
me,
|
||
clients,
|
||
profileOptions,
|
||
activeProfileId,
|
||
activeClientId,
|
||
adminOpen,
|
||
onProfileChange,
|
||
onClientChange,
|
||
onToggleAdmin,
|
||
onOpenShowcase,
|
||
onOpenProfileSettings,
|
||
onLogout,
|
||
brandLinkUrl = "/",
|
||
}: {
|
||
me: MeResponse;
|
||
clients: Client[];
|
||
profileOptions: ProfileOption[];
|
||
activeProfileId: string;
|
||
activeClientId: string;
|
||
adminOpen: boolean;
|
||
onProfileChange: (userId: string) => void;
|
||
onClientChange: (clientId: string) => void;
|
||
onToggleAdmin: () => void;
|
||
onOpenShowcase: () => void;
|
||
onOpenProfileSettings: () => void;
|
||
onLogout?: () => void;
|
||
brandLinkUrl?: string;
|
||
}) {
|
||
const availableClientIds = new Set(me.memberships.map((membership) => membership.clientId));
|
||
const availableClients = clients.filter((client) => availableClientIds.has(client.id));
|
||
const activeClient = availableClients.find((client) => client.id === activeClientId);
|
||
const clientOptions = availableClients.map((client) => ({
|
||
value: client.id,
|
||
label: client.name,
|
||
description: client.legalName ?? undefined,
|
||
}));
|
||
|
||
return (
|
||
<header className="nodedc-expanded-toolbar-shell">
|
||
<div className="nodedc-expanded-toolbar">
|
||
<div className="nodedc-expanded-toolbar-top">
|
||
<div className="nodedc-expanded-toolbar-left">
|
||
<a href={brandLinkUrl} className="nodedc-expanded-brand-link" aria-label="NODE.DC">
|
||
<img src="/nodedc-logo.svg" alt="NODE DC" className="nodedc-expanded-brand-logo" />
|
||
</a>
|
||
</div>
|
||
|
||
<div className="nodedc-expanded-toolbar-center">
|
||
<NodeDcSelect
|
||
value={activeClientId}
|
||
options={clientOptions}
|
||
label="Выбрать клиента"
|
||
searchable
|
||
minMenuWidth={248}
|
||
onChange={(clientId) => onClientChange(clientId)}
|
||
trigger={({ open, toggle, setTriggerRef }) => (
|
||
<button
|
||
ref={setTriggerRef}
|
||
className="nodedc-expanded-workspace-button"
|
||
title={activeClient?.name ?? "Клиент"}
|
||
type="button"
|
||
aria-label="Выбрать клиента"
|
||
aria-expanded={open}
|
||
onClick={toggle}
|
||
>
|
||
{activeClient?.avatarUrl ? <img src={activeClient.avatarUrl} alt="" className="nodedc-expanded-workspace-avatar" /> : null}
|
||
</button>
|
||
)}
|
||
/>
|
||
|
||
<nav className="nodedc-expanded-nav-group" aria-label="Навигация лаунчера">
|
||
<button className="nodedc-expanded-nav-button" type="button" data-active={!adminOpen} onClick={onOpenShowcase}>
|
||
<span>Витрина</span>
|
||
</button>
|
||
|
||
{me.permissions.canOpenAdmin ? (
|
||
<button className="nodedc-expanded-nav-button" type="button" data-active={adminOpen} onClick={onToggleAdmin}>
|
||
<span>Администрирование</span>
|
||
</button>
|
||
) : null}
|
||
</nav>
|
||
</div>
|
||
|
||
<div className="nodedc-expanded-toolbar-right">
|
||
<NodeDcProfileMenu
|
||
user={me.user}
|
||
onSettings={onOpenProfileSettings}
|
||
onLogout={onLogout}
|
||
trigger={({ open, toggle, setTriggerRef }) => (
|
||
<div
|
||
ref={setTriggerRef}
|
||
className="nodedc-expanded-user-group"
|
||
title={`${me.user.name} · ${me.user.email}`}
|
||
role="button"
|
||
tabIndex={0}
|
||
aria-label="Профиль пользователя"
|
||
aria-expanded={open}
|
||
onClick={toggle}
|
||
onKeyDown={(event) => {
|
||
if (event.key === "Enter" || event.key === " ") {
|
||
event.preventDefault();
|
||
toggle();
|
||
}
|
||
}}
|
||
>
|
||
<span className="nodedc-expanded-nav-button" data-active="false">
|
||
<span>Профиль</span>
|
||
</span>
|
||
<span className="nodedc-toolbar-icon-button nodedc-expanded-notification-button" data-active="false" aria-hidden="true">
|
||
<span className="nodedc-toolbar-icon-active-dot">
|
||
<Inbox size={20} strokeWidth={1.7} />
|
||
</span>
|
||
</span>
|
||
<span className="nodedc-expanded-user-avatar-button" aria-hidden="true">
|
||
{me.user.avatarUrl ? (
|
||
<img className="nodedc-expanded-user-avatar" src={me.user.avatarUrl} alt="" style={{ objectFit: "cover" }} />
|
||
) : (
|
||
<span className="nodedc-expanded-user-avatar">{initials(me.user.name)}</span>
|
||
)}
|
||
</span>
|
||
</div>
|
||
)}
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
);
|
||
}
|