import { useTranslation } from 'react-i18next'
import { Link, useLocation } from '@tanstack/react-router'
import { useDictionaries, useDictionaryDetail } from '@/api/queries'
import { VersionBadge } from '@/components/version/VersionBadge'
import { Bell, Menu } from 'lucide-react'
/**
* TopBar — sticky header (h=56) per handoff design.
*
* Structure:
* - Left: dynamic breadcrumb based on current route
* - Center: global SearchInput → submits → navigates to /search?q=
* + ⌘K / Ctrl+K shortcut focuses input from anywhere
* - Right: VersionBadge · ThemeSwitch · LanguageSwitch · Docs · AuthBadge
*/
export function TopBar({ onMenuClick }: { onMenuClick?: () => void }) {
const { t } = useTranslation()
const breadcrumb = useBreadcrumb()
return (
{/* Hamburger (lg:hidden) — toggle MobileSidebar */}
{onMenuClick && (
)}
{/* Breadcrumb per redesign prototype:
* - Single crumb (catalog): "Справочники" (bold) + "40 шт." count
* - Detail route: "← Справочники / Космические аппараты satellites · v1.0.0"
* с back-arrow prefix на parent crumb (clickable). */}
{/* TopBar search убран per user — catalog имеет свой input в toolbar,
* для records search используется /search route через sidebar nav.
* "Search из TopBar убран — все верно". */}
{/* Right cluster — broadcast slot per user: "руководство и версию
* оставить в топбар чтобы подсвечивать новинки. туда же и сообщения".
* Docs + VersionBadge + Notifications. ThemeSwitch / Lang / Auth
* переехали в Sidebar footer. */}
)
}
/**
* Placeholder notifications bell — будет показывать N unread сообщений
* (webhook errors, draft reviews, system notices). Click → drawer/popup.
* Сейчас static bell без counter — wire'ится когда backend notifications
* stream появится.
*/
function NotificationsBell() {
const { t } = useTranslation()
return (
)
}
type Crumb = {
label: string
to?: string
/** Mono-styled secondary text (editor route: "name · v1.0.0"). */
subtitle?: string
/** Cap-styled count badge (catalog route: "40 шт."). */
caption?: string
}
/**
* Breadcrumb из current route. Для editor route ('/dictionaries/$name')
* добавляет displayName + version (mono subtitle) per redesign prototype.
*/
function useBreadcrumb(): Crumb[] {
const { t } = useTranslation()
const location = useLocation()
const path = location.pathname
// Editor route: fetch dict detail для display name + schema version.
const editorMatch = path.match(/^\/dictionaries\/([^/]+)/)
const editorName = editorMatch ? decodeURIComponent(editorMatch[1]) : undefined
const editorDict = useDictionaryDetail(editorName)
// Catalog route: total dict count для inline "N шт." subtitle на breadcrumb.
// Запрос cached глобально (useDictionaries) — не вызывает extra fetch.
const isCatalog = path === '/dictionaries'
const dictsQuery = useDictionaries()
const dictsCount = dictsQuery.data?.length
if (path === '/' || path === '') {
return [{ label: t('nav.home') }]
}
const segments = path.split('/').filter(Boolean)
const first = segments[0]
const ROOT_LABELS: Record = {
dictionaries: t('nav.dictionaries'),
search: t('nav.search'),
graph: t('nav.graph'),
'my-drafts': t('nav.myDrafts'),
reviews: t('nav.reviews'),
audit: t('nav.audit'),
outbox: t('nav.outbox'),
webhooks: t('nav.webhooks'),
}
const rootLabel = ROOT_LABELS[first] ?? first
// Catalog: title "Справочники" + inline caption "N шт." per user feedback.
if (isCatalog) {
return [
{
label: rootLabel,
caption:
typeof dictsCount === 'number'
? t('dict.list.countShort', {
count: dictsCount,
defaultValue: `${dictsCount} шт.`,
})
: undefined,
},
]
}
const crumbs: Crumb[] = [
{ label: rootLabel, to: segments.length === 1 ? undefined : `/${first}` },
]
if (segments.length >= 2) {
// Editor route enrichment: displayName + version
if (editorName && editorDict.data) {
crumbs.push({
label: editorDict.data.displayName ?? editorDict.data.name,
subtitle: `${editorDict.data.name} · v${editorDict.data.schemaVersion}`,
})
} else {
crumbs.push({ label: decodeURIComponent(segments[1]) })
}
}
return crumbs
}