Refactored multiple components, including PanelCard, SettingsCard, and DataGridShell, to replace Card with Frame for better organization and presentation. Enhanced the DataGridToolbar to support optional ReUI filters and improved the overall structure of the KPI stat grid. Updated .gitignore to include .env.local for local environment configurations, ensuring better management of environment variables.
104 lines
3.1 KiB
TypeScript
104 lines
3.1 KiB
TypeScript
import type { ReactNode } from 'react'
|
|
import { Link, Outlet, useRouterState } from '@tanstack/react-router'
|
|
import { ServerCogIcon, SettingsIcon } from 'lucide-react'
|
|
|
|
import { useIsMobile } from '@evobgp/ui/hooks/use-mobile'
|
|
import { cn } from '@evobgp/ui/lib/utils'
|
|
import { PageShell } from '@/components/page-shell'
|
|
import { PageHeader } from '@/components/page-header'
|
|
import { Separator } from '@evobgp/ui/components/separator'
|
|
|
|
export interface SettingsTabConfig {
|
|
id: string
|
|
to: string
|
|
label: string
|
|
icon?: ReactNode
|
|
}
|
|
|
|
/** Default nav for EvoBGP settings routes (`/settings`, `/tenant-settings`). */
|
|
const DEFAULT_TABS: SettingsTabConfig[] = [
|
|
{
|
|
id: 'ui',
|
|
to: '/settings',
|
|
label: 'Настройки UI',
|
|
icon: <SettingsIcon className="size-4" aria-hidden="true" />,
|
|
},
|
|
{
|
|
id: 'tenant',
|
|
to: '/tenant-settings',
|
|
label: 'Настройки BIRD',
|
|
icon: <ServerCogIcon className="size-4" aria-hidden="true" />,
|
|
},
|
|
]
|
|
|
|
interface SettingsShellProps {
|
|
title?: string
|
|
description?: string
|
|
tabs?: SettingsTabConfig[]
|
|
}
|
|
|
|
export function SettingsShell({
|
|
title = 'Настройки',
|
|
description = 'Подключение UI и параметры плоскости управления',
|
|
tabs = DEFAULT_TABS,
|
|
}: SettingsShellProps) {
|
|
const isMobile = useIsMobile()
|
|
const pathname = useRouterState({ select: (s) => s.location.pathname })
|
|
|
|
return (
|
|
<PageShell>
|
|
<div className="mx-auto flex w-full max-w-4xl flex-col gap-5">
|
|
<div className="flex flex-col gap-3">
|
|
<PageHeader title={title} description={description} />
|
|
<Separator />
|
|
</div>
|
|
|
|
<div
|
|
className={cn(
|
|
'flex gap-5',
|
|
isMobile ? 'flex-col' : 'flex-row items-start',
|
|
)}
|
|
>
|
|
{tabs.length > 1 ? (
|
|
<nav
|
|
aria-label="Разделы настроек"
|
|
className={cn(
|
|
'flex gap-1',
|
|
isMobile
|
|
? 'scrollbar-none -mx-1 overflow-x-auto overflow-y-hidden pb-1'
|
|
: 'w-44 shrink-0 flex-col',
|
|
)}
|
|
>
|
|
{tabs.map((tab) => {
|
|
const isActive = pathname.startsWith(tab.to)
|
|
return (
|
|
<Link
|
|
key={tab.id}
|
|
to={tab.to}
|
|
aria-current={isActive ? 'page' : undefined}
|
|
className={cn(
|
|
'flex items-center gap-2 rounded-lg px-3 py-2 text-sm transition-colors',
|
|
isMobile && 'shrink-0',
|
|
!isMobile && 'w-full',
|
|
isActive
|
|
? 'bg-muted text-foreground font-medium shadow-sm ring-1 ring-border/60'
|
|
: 'text-muted-foreground hover:bg-muted/60 hover:text-foreground',
|
|
)}
|
|
>
|
|
{tab.icon}
|
|
{tab.label}
|
|
</Link>
|
|
)
|
|
})}
|
|
</nav>
|
|
) : null}
|
|
|
|
<div className="min-w-0 flex-1">
|
|
<Outlet />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</PageShell>
|
|
)
|
|
}
|