Files
EvoFirewall/apps/web/src/lib/nav.ts
T
Denozordec 3dc8e6d5e2 refactor(web): общие форматтеры, единая навигация, RBAC-гейтинг и чистка мёртвого кода
- lib/format.ts: ru-RU форматтеры дат/чисел в одном месте — убраны дубли
  из 8 компонентов (agent-card, fleet-grid, blocked-ips/ports, facts,
  lifecycle, host-firewall, lists-columns, system-monitor)
- lib/nav.ts: единый конфиг маршрутов — sidebar, ⌘K-поиск и breadcrumbs
  рендерятся из одного источника (было 3 расходящихся копии)
- lib/permissions.ts + useCan: клиентский RBAC — кнопки создания/подтверждения
  скрываются без fw:*:write/admin (сервер остаётся авторитетным)
- удалён мёртвый код ~2500 строк: stepper, data-grid dnd/virtual/visibility
  варианты, settings-shell
2026-09-20 19:36:37 +07:00

96 lines
2.3 KiB
TypeScript

import {
BarChart3Icon,
LayoutDashboardIcon,
ListIcon,
ServerIcon,
SettingsIcon,
ShieldIcon,
} from 'lucide-react'
/**
* Single source of the app's route structure: sidebar sections, ⌘K search and
* header breadcrumbs all render from this config.
*/
export type NavSectionId = 'overview' | 'ops' | 'system'
export type NavItem = {
to: string
label: string
/** Exact active-state match (root only); prefix match otherwise. */
exact?: boolean
keywords: string[]
icon: typeof ServerIcon
section: NavSectionId
}
export const NAV_ITEMS: readonly NavItem[] = [
{
to: '/',
label: 'Панель управления',
exact: true,
keywords: ['dashboard', 'панель', 'обзор'],
icon: LayoutDashboardIcon,
section: 'overview',
},
{
to: '/agents',
label: 'Агенты',
keywords: ['agents', 'агенты', 'nodes'],
icon: ServerIcon,
section: 'ops',
},
{
to: '/lists',
label: 'Списки',
keywords: ['lists', 'списки', 'blocklist'],
icon: ListIcon,
section: 'ops',
},
{
to: '/rules',
label: 'Наборы правил',
keywords: ['rules', 'правила', 'policy', 'наборы', 'sets'],
icon: ShieldIcon,
section: 'ops',
},
{
to: '/stats',
label: 'Статистика',
keywords: ['stats', 'статистика', 'packets'],
icon: BarChart3Icon,
section: 'ops',
},
{
to: '/settings',
label: 'Настройки',
keywords: ['settings', 'настройки'],
icon: SettingsIcon,
section: 'system',
},
]
export const NAV_SECTIONS = [
{ id: 'overview', label: 'Обзор' },
{ id: 'ops', label: 'Операции' },
{ id: 'system', label: 'Система' },
] as const satisfies readonly { id: NavSectionId; label: string }[]
export function navItemsForSection(section: NavSectionId): readonly NavItem[] {
return NAV_ITEMS.filter((item) => item.section === section)
}
export function navLabel(to: string): string | undefined {
return NAV_ITEMS.find((item) => item.to === to)?.label
}
/** Detail routes (…/:id) that nest under a nav route. */
const DETAIL_PARENT_RE = /^\/(agents|lists|rules)\/[^/]+$/
export function navParentForDetail(
pathname: string,
): string | undefined {
const m = pathname.match(DETAIL_PARENT_RE)
return m ? `/${m[1]}` : undefined
}