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
This commit is contained in:
@@ -12,55 +12,41 @@ import { Separator } from '@evofw/ui/components/separator'
|
||||
import { SystemMonitorPopover } from '@/components/layout/system-monitor-popover'
|
||||
import { AppsMenu } from '@/components/layout/apps-menu'
|
||||
import { SidebarTrigger } from '@evofw/ui/components/sidebar'
|
||||
import { navLabel, navParentForDetail } from '@/lib/nav'
|
||||
|
||||
export interface RouteBreadcrumbLoaderData {
|
||||
breadcrumb?: string
|
||||
}
|
||||
|
||||
const routeTitles: Record<string, string> = {
|
||||
'/': 'Панель управления',
|
||||
'/agents': 'Агенты',
|
||||
'/lists': 'Списки',
|
||||
'/rules': 'Наборы правил',
|
||||
'/stats': 'Статистика',
|
||||
'/settings': 'Настройки',
|
||||
}
|
||||
|
||||
function getBreadcrumbs(
|
||||
pathname: string,
|
||||
dynamicLabels: Record<string, string>,
|
||||
) {
|
||||
if (pathname === '/') {
|
||||
return [{ label: 'Панель управления', href: '/' }]
|
||||
return [{ label: navLabel('/') ?? 'Панель управления', href: '/' }]
|
||||
}
|
||||
|
||||
if (pathname.match(/^\/agents\/[^/]+$/)) {
|
||||
const parentTo = navParentForDetail(pathname)
|
||||
if (parentTo) {
|
||||
const parentLabel = navLabel(parentTo)
|
||||
const fallback =
|
||||
parentTo === '/agents'
|
||||
? 'Агент'
|
||||
: parentTo === '/lists'
|
||||
? 'Список'
|
||||
: 'Набор'
|
||||
return [
|
||||
{ label: 'Агенты', href: '/agents' },
|
||||
{ label: dynamicLabels[pathname] ?? 'Агент', href: pathname },
|
||||
{ label: parentLabel ?? parentTo, href: parentTo },
|
||||
{ label: dynamicLabels[pathname] ?? fallback, href: pathname },
|
||||
]
|
||||
}
|
||||
|
||||
if (pathname.match(/^\/rules\/[^/]+$/)) {
|
||||
return [
|
||||
{ label: 'Наборы правил', href: '/rules' },
|
||||
{ label: dynamicLabels[pathname] ?? 'Набор', href: pathname },
|
||||
]
|
||||
}
|
||||
|
||||
if (pathname.match(/^\/lists\/[^/]+$/)) {
|
||||
return [
|
||||
{ label: 'Списки', href: '/lists' },
|
||||
{ label: dynamicLabels[pathname] ?? 'Список', href: pathname },
|
||||
]
|
||||
}
|
||||
|
||||
const title = routeTitles[pathname]
|
||||
const title = navLabel(pathname)
|
||||
if (title) {
|
||||
return [{ label: title, href: pathname }]
|
||||
}
|
||||
|
||||
return [{ label: 'Панель управления', href: '/' }]
|
||||
return [{ label: navLabel('/') ?? 'Панель управления', href: '/' }]
|
||||
}
|
||||
|
||||
function useDynamicBreadcrumbLabels() {
|
||||
|
||||
Reference in New Issue
Block a user