Files
cloudflare-domain-manager/apps/web/src/components/layout/site-header.tsx
T
DenozordecandCursor 2418344551
CD / update-wiki (push) Successful in 6s
quality / commitlint (push) Skipped
quality / changes (push) Successful in 11s
quality / api (push) Skipped
quality / docker-check (push) Skipped
quality / web (push) Failing after 52s
CD / quality (push) Failing after 1m5s
CD / publish (push) Skipped
feat(web): собрать экраны из блоков ReUI PRO
Подключить stats-12, card-12, settings-3 и empty-state-12 к оболочке, спискам, карточкам и настройкам, чтобы метрики и пустые состояния шли из одних блоков.

Co-authored-by: Cursor <[email protected]>
2026-11-02 19:40:18 +07:00

94 lines
3.2 KiB
TypeScript

import { Fragment, useMemo } from 'react'
import { Link, useMatches, useRouterState } from '@tanstack/react-router'
import { MenuIcon } from 'lucide-react'
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@cfdm/ui/components/breadcrumb'
import { Separator } from '@cfdm/ui/components/separator'
import { AppsMenu } from '@/components/layout/apps-menu'
import { DataFreshness } from '@/components/layout/data-freshness'
import { SystemMonitorPopover } from '@/components/layout/system-monitor-popover'
import { Button } from '@cfdm/ui/components/button'
import { SidebarTrigger } from '@cfdm/ui/components/sidebar'
import { OPEN_COMMAND_PALETTE_EVENT } from '@/components/layout/command-palette'
import { getBreadcrumbs } from '@/lib/breadcrumbs'
export interface RouteBreadcrumbLoaderData {
breadcrumb?: string
}
function useDynamicBreadcrumbLabels() {
const matches = useMatches()
return useMemo(() => {
const labels: Record<string, string> = {}
for (const match of matches) {
const data = match.loaderData as RouteBreadcrumbLoaderData | undefined
if (data?.breadcrumb && match.pathname) {
labels[match.pathname] = data.breadcrumb
}
}
return labels
}, [matches])
}
/** Header chrome — freshness + AppsMenu + SystemMonitor; theme in NavUser (app-shell-1). */
export function SiteHeader() {
const pathname = useRouterState({ select: (s) => s.location.pathname })
const dynamicLabels = useDynamicBreadcrumbLabels()
const crumbs = useMemo(
() => getBreadcrumbs(pathname, dynamicLabels),
[pathname, dynamicLabels],
)
return (
<header className="bg-background sticky top-0 z-10 flex h-12 shrink-0 items-center gap-2 border-b px-4 md:px-6">
<SidebarTrigger className="-ml-1" />
<Button
type="button"
variant="ghost"
size="icon"
aria-label="Меню и поиск"
onClick={() => window.dispatchEvent(new Event(OPEN_COMMAND_PALETTE_EVENT))}
>
<MenuIcon aria-hidden />
</Button>
<Separator orientation="vertical" className="mr-2 data-[orientation=vertical]:h-4" />
<Breadcrumb>
<BreadcrumbList>
{crumbs.map((crumb, index) => {
const isLast = index === crumbs.length - 1
return (
<Fragment key={`${index}-${crumb.href}`}>
{index > 0 ? (
<BreadcrumbSeparator className="hidden md:block" />
) : null}
<BreadcrumbItem
className={index === 0 && !isLast ? 'hidden md:block' : undefined}
>
{isLast ? (
<BreadcrumbPage>{crumb.label}</BreadcrumbPage>
) : (
<BreadcrumbLink render={<Link to={crumb.href} />}>
{crumb.label}
</BreadcrumbLink>
)}
</BreadcrumbItem>
</Fragment>
)
})}
</BreadcrumbList>
</Breadcrumb>
<div className="ml-auto flex items-center gap-2">
<DataFreshness />
<AppsMenu />
<SystemMonitorPopover />
</div>
</header>
)
}