From 55eb2a6c892527a7c08c1fc69e49e63cb8b705aa Mon Sep 17 00:00:00 2001 From: Denozordec Date: Thu, 9 Jul 2026 21:58:39 +0700 Subject: [PATCH] refactor: enhance dashboard layout and settings components for improved user experience Updated the AnalyticsDashboardSkeleton to utilize a new grid layout for KPI tiles, increasing the number of displayed items and adjusting their size for better visibility. Modified the SettingsSettingField component to accept a ReactNode for the description, allowing for richer content. Refactored the Settings component to improve the organization of API token management and session status display, enhancing clarity and usability. Additionally, integrated new UI elements for theme selection and improved layout consistency across components. --- .../dashboard/dashboard-kpi-grid.tsx | 183 +++++++++++++++++ .../settings/settings-setting-field.tsx | 2 +- apps/web/src/components/skeletons.tsx | 7 +- apps/web/src/lib/ui-surface.ts | 4 + apps/web/src/routes/_auth/dashboard.tsx | 4 +- apps/web/src/routes/_auth/settings.tsx | 186 ++++++++++++------ apps/web/tsconfig.tsbuildinfo | 2 +- 7 files changed, 323 insertions(+), 65 deletions(-) create mode 100644 apps/web/src/components/dashboard/dashboard-kpi-grid.tsx diff --git a/apps/web/src/components/dashboard/dashboard-kpi-grid.tsx b/apps/web/src/components/dashboard/dashboard-kpi-grid.tsx new file mode 100644 index 0000000..3a69c72 --- /dev/null +++ b/apps/web/src/components/dashboard/dashboard-kpi-grid.tsx @@ -0,0 +1,183 @@ +import { + AlertTriangle, + Boxes, + ListChecks, + Network, + ServerCog, + Share2, +} from 'lucide-react' +import type { ReactNode } from 'react' + +import { Badge } from '@/components/reui/badge' +import { dashboardKpiGridClassName, kpiCardContentClassName } from '@/lib/ui-surface' +import { Card, CardContent } from '@evobgp/ui/components/card' +import { cn } from '@evobgp/ui/lib/utils' +import { Item, ItemMedia } from '@evobgp/ui/components/item' + +import { aggregateNetworkMetrics, runningJobCount } from '@/queries/overview' +import type { JobRow, ModuleRow, PeerRow, SpeakerRow } from '@/types/api' + +type KpiCard = { + icon: ReactNode + iconClass: string + value: string + label: string + badge: ReactNode +} + +function buildKpis({ + modules, + peers, + speakers, + jobs, + loading, +}: { + modules: ModuleRow[] + peers: PeerRow[] + speakers: SpeakerRow[] + jobs: JobRow[] + loading?: boolean +}): KpiCard[] { + const enabledModules = modules.filter((m) => m.enabled !== false).length + const network = aggregateNetworkMetrics(peers, speakers) + const peersEnabled = network.peersEnabled + const bgpPct = + peersEnabled > 0 ? Math.round((network.peersEstablished / peersEnabled) * 100) : null + const running = runningJobCount(jobs) + const failedJobs = jobs.filter((j) => + ['failed', 'error', 'cancelled'].includes(j.status.toLowerCase()), + ).length + const offlineSpeakers = Math.max(0, speakers.length - network.speakersOnline) + const riskCount = network.peersMismatch + failedJobs + offlineSpeakers + + return [ + { + icon: , + iconClass: 'text-primary', + value: loading ? '—' : `${enabledModules}/${modules.length || 0}`, + label: 'Модули активны', + badge: ( + + {loading ? '…' : `${modules.length} всего`} + + ), + }, + { + icon: , + iconClass: 'text-info', + value: loading || bgpPct === null ? '—' : `${bgpPct}%`, + label: 'BGP готовность', + badge: ( + = 90 + ? 'success-light' + : bgpPct !== null && bgpPct < 70 + ? 'warning-light' + : 'outline' + } + size="sm" + > + {loading || bgpPct === null + ? 'нет включённых пиров' + : `${network.peersEstablished} установлено`} + + ), + }, + { + icon: , + iconClass: 'text-success', + value: loading ? '—' : `${network.peersEstablished}/${peersEnabled}`, + label: 'Пиры Established', + badge: ( + + {loading ? '…' : `${network.peersTotal} в каталоге`} + + ), + }, + { + icon: , + iconClass: 'text-warning', + value: loading ? '—' : `${network.speakersOnline}/${network.speakersTotal}`, + label: 'Спикеры online', + badge: ( + + {loading ? '…' : 'live-снимок'} + + ), + }, + { + icon: , + iconClass: 'text-focus', + value: loading ? '—' : String(running), + label: 'Активные задачи', + badge: ( + 0 ? 'info-light' : 'outline'} size="sm"> + {loading ? '…' : `${jobs.length} в выборке`} + + ), + }, + { + icon: , + iconClass: 'text-destructive', + value: loading ? '—' : String(riskCount), + label: 'Риски', + badge: ( + 0 ? 'destructive-light' : 'success-light'} size="sm"> + {loading + ? '…' + : riskCount > 0 + ? `${failedJobs} задач · ${network.peersMismatch} расхождений` + : 'в норме'} + + ), + }, + ] +} + +export function DashboardKpiGrid({ + modules, + peers, + speakers, + jobs, + loading, +}: { + modules: ModuleRow[] + peers: PeerRow[] + speakers: SpeakerRow[] + jobs: JobRow[] + loading?: boolean +}) { + const cards = buildKpis({ modules, peers, speakers, jobs, loading }) + + return ( +
+ {cards.map((card) => ( + + + + + {card.icon} + + +
+
+ {card.value} +
+
{card.label}
+
+ {card.badge} +
+
+ ))} +
+ ) +} diff --git a/apps/web/src/components/settings/settings-setting-field.tsx b/apps/web/src/components/settings/settings-setting-field.tsx index 9deaa61..8dac7f1 100644 --- a/apps/web/src/components/settings/settings-setting-field.tsx +++ b/apps/web/src/components/settings/settings-setting-field.tsx @@ -22,7 +22,7 @@ export function SettingsSettingField({ contentClassName, }: { title: string - description: string + description: ReactNode badge?: { label: string; variant: ComponentProps['variant'] } children: ReactNode last?: boolean diff --git a/apps/web/src/components/skeletons.tsx b/apps/web/src/components/skeletons.tsx index db0a505..169fa71 100644 --- a/apps/web/src/components/skeletons.tsx +++ b/apps/web/src/components/skeletons.tsx @@ -3,6 +3,7 @@ import { Skeleton } from '@evobgp/ui/components/skeleton' import { panelCardInsetClassName } from '@/components/panel-card' import { chartPanelGridClassName, + dashboardKpiGridClassName, dashboardMainSidebarClassName, kpiGridClassName, } from '@/lib/ui-surface' @@ -24,9 +25,9 @@ export function SectionCardsSkeleton({ count = 4 }: { count?: number }) { export function AnalyticsDashboardSkeleton() { return (
-
- {Array.from({ length: 4 }).map((_, i) => ( - +
+ {Array.from({ length: 6 }).map((_, i) => ( + ))}
diff --git a/apps/web/src/lib/ui-surface.ts b/apps/web/src/lib/ui-surface.ts index 5da44d4..c0f58b1 100644 --- a/apps/web/src/lib/ui-surface.ts +++ b/apps/web/src/lib/ui-surface.ts @@ -15,6 +15,10 @@ export const UI_SURFACE = 'card' as const /** Shared padding for KPI / metric tiles inside PanelCard. */ export const kpiCardContentClassName = 'flex flex-col items-start gap-4 p-5' +/** Compact 6-tile KPI row on dashboard overview. */ +export const dashboardKpiGridClassName = + 'grid grid-cols-2 gap-3 sm:grid-cols-3 xl:grid-cols-6' + /** Grid for dashboard-5 style KPI sparkline row (4 columns at xl). */ export const kpiGridClassName = 'grid grid-cols-1 gap-4 @3xl:grid-cols-2 @6xl:grid-cols-4' diff --git a/apps/web/src/routes/_auth/dashboard.tsx b/apps/web/src/routes/_auth/dashboard.tsx index cab046f..9a85a1a 100644 --- a/apps/web/src/routes/_auth/dashboard.tsx +++ b/apps/web/src/routes/_auth/dashboard.tsx @@ -8,7 +8,7 @@ import { Skeleton } from '@evobgp/ui/components/skeleton' import { DashboardActivityTimeline } from '@/components/dashboard/dashboard-activity-timeline' import { DashboardFramePanel } from '@/components/dashboard/dashboard-frame-panel' -import { DashboardKpiSparklineRow } from '@/components/dashboard/dashboard-kpi-sparkline-row' +import { DashboardKpiGrid } from '@/components/dashboard/dashboard-kpi-grid' import { DashboardModulesGrid } from '@/components/dashboard/dashboard-modules-grid' import { DashboardNetworkHealth } from '@/components/dashboard/dashboard-network-health' import { DashboardOperationsBreakdown } from '@/components/dashboard/dashboard-operations-breakdown' @@ -92,7 +92,7 @@ function DashboardComponent() { ) : ( <> - - Для доступа к разделу нужен API-токен. Сохраните токен ниже или нажмите «Использовать dev». + Для доступа к разделу нужен API-токен. Сохраните токен ниже или нажмите «Использовать + dev». ) : null} + + + + Сохранить токен + +
+ } > -
- + + + Ключ для заголовка Authorization. Управление + ключами tenant — в разделе{' '} + + Права доступа + + . + + } + badge={{ label: 'localStorage', variant: 'outline' }} + labelFor="token" + last={!showSessionRow} + > setTokenValue(e.target.value)} placeholder="dev или API-ключ" /> -
-
- - - Сохранить токен - - -
- {session ? ( -

- Активная сессия: tenant {session.tenant_id}, роль{' '} - {session.role}. -

- ) : null} - {sessionError ? ( -

- {sessionQueryError instanceof Error - ? sessionQueryError.message - : 'Не удалось проверить сессию'} - . Для токена dev нужен demo-seed ( - EVOBGP_SEED_DEMO ≠ 0) и запущенный API. -

+ + + {showSessionRow ? ( + + {session ? ( +
+

+ Tenant:{' '} + {session.tenant_id} +

+

+ Роль: {session.role} +

+
+ ) : ( +

+ {sessionErrorMessage}. Для токена dev нужен + demo-seed (EVOBGP_SEED_DEMO ≠ 0) и запущенный + API. +

+ )} +
) : null} + - v && setTheme(v)} - /> + + + { + if (value.length > 0) setTheme(value[0]) + }} + variant="outline" + size="sm" + aria-label="Тема интерфейса" + className="w-full sm:w-auto" + > + {THEME_OPTIONS.map((option) => { + const Icon = option.icon + return ( + + + {option.label} + + ) + })} + + +
) diff --git a/apps/web/tsconfig.tsbuildinfo b/apps/web/tsconfig.tsbuildinfo index ed1837e..1bc9647 100644 --- a/apps/web/tsconfig.tsbuildinfo +++ b/apps/web/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/main.tsx","./src/routetree.gen.ts","./src/components/badge-tabs.tsx","./src/components/category-badge.tsx","./src/components/confirm-dialog.tsx","./src/components/data-grid-cell.tsx","./src/components/data-grid-shell.tsx","./src/components/data-grid-toolbar.tsx","./src/components/drawer-layout.tsx","./src/components/empty-state.tsx","./src/components/form-drawer.tsx","./src/components/loading-button.tsx","./src/components/mode-toggle.tsx","./src/components/page-header.tsx","./src/components/page-shell.tsx","./src/components/panel-card.tsx","./src/components/query-state.tsx","./src/components/section-cards.tsx","./src/components/select-field.tsx","./src/components/skeletons.tsx","./src/components/status-badge.tsx","./src/components/theme-provider.tsx","./src/components/truncated-text.tsx","./src/components/access/access-api-keys-card.tsx","./src/components/access/access-api-keys-grid.tsx","./src/components/access/api-key-create-dialog.tsx","./src/components/access/api-key-token-dialog.tsx","./src/components/analytics/analytics-activity-list.tsx","./src/components/analytics/analytics-card-shell.tsx","./src/components/analytics/analytics-kpi-row.tsx","./src/components/analytics/analytics-progress.tsx","./src/components/analytics/analytics-segment-control.tsx","./src/components/analytics/chart-bar-strip.tsx","./src/components/analytics/chart-donut-metric.tsx","./src/components/analytics/dashboard-network-capacity-card.tsx","./src/components/analytics/dashboard-operations-flow-card.tsx","./src/components/analytics/dashboard-platform-card.tsx","./src/components/analytics/index.ts","./src/components/analytics/monitoring-health-card.tsx","./src/components/analytics/network-overview-analytics-card.tsx","./src/components/analytics/operations-analytics-card.tsx","./src/components/dashboard/card-dot-field.tsx","./src/components/dashboard/dashboard-activity-timeline.tsx","./src/components/dashboard/dashboard-frame-panel.tsx","./src/components/dashboard/dashboard-kpi-sparkline-row.tsx","./src/components/dashboard/dashboard-modules-grid.tsx","./src/components/dashboard/dashboard-network-health.tsx","./src/components/dashboard/dashboard-network-panel.tsx","./src/components/dashboard/dashboard-operations-breakdown.tsx","./src/components/dashboard/dashboard-quick-link-card.tsx","./src/components/dashboard/dashboard-quick-links.tsx","./src/components/dashboard/dashboard-recent-jobs-grid.tsx","./src/components/dashboard/dashboard-recent-revisions-grid.tsx","./src/components/directories/directories-communities-grid.tsx","./src/components/directories/directories-doh-grid.tsx","./src/components/examples/c-input-group-37.tsx","./src/components/examples/c-select-4.tsx","./src/components/examples/c-tabs-6.tsx","./src/components/examples/c-tabs-7.tsx","./src/components/firewall/firewall-clients-grid.tsx","./src/components/firewall/firewall-rule-create-dialog.tsx","./src/components/firewall/firewall-rules-grid.tsx","./src/components/layout/app-shell.tsx","./src/components/layout/command-palette.tsx","./src/components/layout/system-monitor-popover.tsx","./src/components/modules/community-select.tsx","./src/components/modules/module-as-entry-dialog.tsx","./src/components/modules/module-cdn-source-dialog.tsx","./src/components/modules/module-domain-entry-dialog.tsx","./src/components/modules/module-entries-grid.tsx","./src/components/modules/module-entries-section.tsx","./src/components/modules/module-ip-range-entry-dialog.tsx","./src/components/modules/module-kpi-cards.tsx","./src/components/modules/modules-list-grid.tsx","./src/components/monitoring/monitoring-ready-grid.tsx","./src/components/network/network-peers-card.tsx","./src/components/network/network-peers-grid.tsx","./src/components/network/network-speakers-card.tsx","./src/components/network/network-speakers-grid.tsx","./src/components/network/peer-form-dialog.tsx","./src/components/network/speaker-form-dialog.tsx","./src/components/operations/operations-jobs-card.tsx","./src/components/operations/operations-jobs-grid.tsx","./src/components/operations/operations-revisions-grid.tsx","./src/components/patterns/donut-breakdown-card.tsx","./src/components/patterns/illustrated-empty-state.tsx","./src/components/patterns/index.ts","./src/components/patterns/kpi-sparkline-card.tsx","./src/components/patterns/metric-tone-styles.ts","./src/components/patterns/panel-corners.tsx","./src/components/patterns/projects-empty-state.tsx","./src/components/patterns/segmented-progress-card.tsx","./src/components/reui/autocomplete.tsx","./src/components/reui/badge.tsx","./src/components/reui/date-selector.tsx","./src/components/reui/filters.tsx","./src/components/reui/frame.tsx","./src/components/reui/icon-stack.tsx","./src/components/reui/number-field.tsx","./src/components/reui/rating.tsx","./src/components/reui/timeline.tsx","./src/components/reui/data-grid/data-grid-column-filter.tsx","./src/components/reui/data-grid/data-grid-column-header.tsx","./src/components/reui/data-grid/data-grid-column-visibility.tsx","./src/components/reui/data-grid/data-grid-pagination.tsx","./src/components/reui/data-grid/data-grid-scroll-area.tsx","./src/components/reui/data-grid/data-grid-table-dnd-rows.tsx","./src/components/reui/data-grid/data-grid-table-dnd.tsx","./src/components/reui/data-grid/data-grid-table-virtual.tsx","./src/components/reui/data-grid/data-grid-table.tsx","./src/components/reui/data-grid/data-grid.tsx","./src/components/schedule/schedule-agenda-panel.tsx","./src/components/schedule/schedule-jobs-grid.tsx","./src/components/schedule/schedule-modules-grid.tsx","./src/components/settings/settings-kv-grid.tsx","./src/components/settings/settings-setting-field.tsx","./src/components/ui/svgs/anthropicblack.tsx","./src/components/ui/svgs/anthropicwhite.tsx","./src/components/ui/svgs/convex.tsx","./src/components/ui/svgs/discord.tsx","./src/components/ui/svgs/gemini.tsx","./src/components/ui/svgs/googlecloud.tsx","./src/components/ui/svgs/hono.tsx","./src/components/ui/svgs/loom.tsx","./src/components/ui/svgs/mintlify.tsx","./src/components/ui/svgs/n8n.tsx","./src/components/ui/svgs/neon.tsx","./src/components/ui/svgs/openai.tsx","./src/components/ui/svgs/openaidark.tsx","./src/components/ui/svgs/paper.tsx","./src/components/ui/svgs/planetscale.tsx","./src/components/ui/svgs/planetscaledark.tsx","./src/components/ui/svgs/prisma.tsx","./src/components/ui/svgs/prismadark.tsx","./src/components/ui/svgs/remixdark.tsx","./src/components/ui/svgs/remixlight.tsx","./src/components/ui/svgs/resendiconblack.tsx","./src/components/ui/svgs/resendiconwhite.tsx","./src/components/ui/svgs/slack.tsx","./src/components/ui/svgs/stripe.tsx","./src/components/ui/svgs/supabase.tsx","./src/components/ui/svgs/zoom.tsx","./src/hooks/use-client-data-grid.ts","./src/hooks/use-copy-to-clipboard.ts","./src/hooks/use-mobile.ts","./src/lib/api-client.ts","./src/lib/data-grid-defaults.ts","./src/lib/queryclient.ts","./src/lib/router.ts","./src/lib/ui-labels.ts","./src/lib/ui-surface.ts","./src/lib/access/api-key-labels.ts","./src/lib/metrics/deployment-progress.ts","./src/lib/metrics/index.ts","./src/lib/metrics/job-status-breakdown.ts","./src/lib/metrics/module-type-breakdown.ts","./src/lib/metrics/peer-capacity-bars.ts","./src/lib/metrics/peer-session-breakdown.ts","./src/lib/metrics/readiness-breakdown.ts","./src/lib/metrics/recent-platform-activity.ts","./src/lib/metrics/types.ts","./src/lib/modules/display.ts","./src/lib/modules/helpers.ts","./src/queries/api-keys.ts","./src/queries/auth.ts","./src/queries/directories.ts","./src/queries/firewall.ts","./src/queries/modules.ts","./src/queries/monitoring.ts","./src/queries/network.ts","./src/queries/operations.ts","./src/queries/overview.ts","./src/queries/settings.ts","./src/routes/__root.tsx","./src/routes/_auth.tsx","./src/routes/index.tsx","./src/routes/_auth/access.tsx","./src/routes/_auth/dashboard.tsx","./src/routes/_auth/directories.tsx","./src/routes/_auth/firewall.tsx","./src/routes/_auth/monitoring.tsx","./src/routes/_auth/network.tsx","./src/routes/_auth/operations.tsx","./src/routes/_auth/schedule.tsx","./src/routes/_auth/settings.tsx","./src/routes/_auth/tenant-settings.tsx","./src/routes/_auth/modules/$moduleid.tsx","./src/routes/_auth/modules/index.tsx","./src/routes/_auth/modules/new.tsx","./src/types/api.ts","./vite.config.ts"],"version":"5.9.3"} \ No newline at end of file +{"root":["./src/main.tsx","./src/routetree.gen.ts","./src/components/badge-tabs.tsx","./src/components/category-badge.tsx","./src/components/confirm-dialog.tsx","./src/components/data-grid-cell.tsx","./src/components/data-grid-shell.tsx","./src/components/data-grid-toolbar.tsx","./src/components/drawer-layout.tsx","./src/components/empty-state.tsx","./src/components/form-drawer.tsx","./src/components/loading-button.tsx","./src/components/mode-toggle.tsx","./src/components/page-header.tsx","./src/components/page-shell.tsx","./src/components/panel-card.tsx","./src/components/query-state.tsx","./src/components/section-cards.tsx","./src/components/select-field.tsx","./src/components/skeletons.tsx","./src/components/status-badge.tsx","./src/components/theme-provider.tsx","./src/components/truncated-text.tsx","./src/components/access/access-api-keys-card.tsx","./src/components/access/access-api-keys-grid.tsx","./src/components/access/api-key-create-dialog.tsx","./src/components/access/api-key-token-dialog.tsx","./src/components/analytics/analytics-activity-list.tsx","./src/components/analytics/analytics-card-shell.tsx","./src/components/analytics/analytics-kpi-row.tsx","./src/components/analytics/analytics-progress.tsx","./src/components/analytics/analytics-segment-control.tsx","./src/components/analytics/chart-bar-strip.tsx","./src/components/analytics/chart-donut-metric.tsx","./src/components/analytics/dashboard-network-capacity-card.tsx","./src/components/analytics/dashboard-operations-flow-card.tsx","./src/components/analytics/dashboard-platform-card.tsx","./src/components/analytics/index.ts","./src/components/analytics/monitoring-health-card.tsx","./src/components/analytics/network-overview-analytics-card.tsx","./src/components/analytics/operations-analytics-card.tsx","./src/components/dashboard/card-dot-field.tsx","./src/components/dashboard/dashboard-activity-timeline.tsx","./src/components/dashboard/dashboard-frame-panel.tsx","./src/components/dashboard/dashboard-kpi-grid.tsx","./src/components/dashboard/dashboard-kpi-sparkline-row.tsx","./src/components/dashboard/dashboard-modules-grid.tsx","./src/components/dashboard/dashboard-network-health.tsx","./src/components/dashboard/dashboard-network-panel.tsx","./src/components/dashboard/dashboard-operations-breakdown.tsx","./src/components/dashboard/dashboard-quick-link-card.tsx","./src/components/dashboard/dashboard-quick-links.tsx","./src/components/dashboard/dashboard-recent-jobs-grid.tsx","./src/components/dashboard/dashboard-recent-revisions-grid.tsx","./src/components/directories/directories-communities-grid.tsx","./src/components/directories/directories-doh-grid.tsx","./src/components/examples/c-input-group-37.tsx","./src/components/examples/c-select-4.tsx","./src/components/examples/c-tabs-6.tsx","./src/components/examples/c-tabs-7.tsx","./src/components/firewall/firewall-clients-grid.tsx","./src/components/firewall/firewall-rule-create-dialog.tsx","./src/components/firewall/firewall-rules-grid.tsx","./src/components/layout/app-shell.tsx","./src/components/layout/command-palette.tsx","./src/components/layout/system-monitor-popover.tsx","./src/components/modules/community-select.tsx","./src/components/modules/module-as-entry-dialog.tsx","./src/components/modules/module-cdn-source-dialog.tsx","./src/components/modules/module-domain-entry-dialog.tsx","./src/components/modules/module-entries-grid.tsx","./src/components/modules/module-entries-section.tsx","./src/components/modules/module-ip-range-entry-dialog.tsx","./src/components/modules/module-kpi-cards.tsx","./src/components/modules/modules-list-grid.tsx","./src/components/monitoring/monitoring-ready-grid.tsx","./src/components/network/network-peers-card.tsx","./src/components/network/network-peers-grid.tsx","./src/components/network/network-speakers-card.tsx","./src/components/network/network-speakers-grid.tsx","./src/components/network/peer-form-dialog.tsx","./src/components/network/speaker-form-dialog.tsx","./src/components/operations/operations-jobs-card.tsx","./src/components/operations/operations-jobs-grid.tsx","./src/components/operations/operations-revisions-grid.tsx","./src/components/patterns/donut-breakdown-card.tsx","./src/components/patterns/illustrated-empty-state.tsx","./src/components/patterns/index.ts","./src/components/patterns/kpi-sparkline-card.tsx","./src/components/patterns/metric-tone-styles.ts","./src/components/patterns/panel-corners.tsx","./src/components/patterns/projects-empty-state.tsx","./src/components/patterns/segmented-progress-card.tsx","./src/components/reui/autocomplete.tsx","./src/components/reui/badge.tsx","./src/components/reui/date-selector.tsx","./src/components/reui/filters.tsx","./src/components/reui/frame.tsx","./src/components/reui/icon-stack.tsx","./src/components/reui/number-field.tsx","./src/components/reui/rating.tsx","./src/components/reui/timeline.tsx","./src/components/reui/data-grid/data-grid-column-filter.tsx","./src/components/reui/data-grid/data-grid-column-header.tsx","./src/components/reui/data-grid/data-grid-column-visibility.tsx","./src/components/reui/data-grid/data-grid-pagination.tsx","./src/components/reui/data-grid/data-grid-scroll-area.tsx","./src/components/reui/data-grid/data-grid-table-dnd-rows.tsx","./src/components/reui/data-grid/data-grid-table-dnd.tsx","./src/components/reui/data-grid/data-grid-table-virtual.tsx","./src/components/reui/data-grid/data-grid-table.tsx","./src/components/reui/data-grid/data-grid.tsx","./src/components/schedule/schedule-agenda-panel.tsx","./src/components/schedule/schedule-jobs-grid.tsx","./src/components/schedule/schedule-modules-grid.tsx","./src/components/settings/settings-kv-grid.tsx","./src/components/settings/settings-setting-field.tsx","./src/components/ui/svgs/anthropicblack.tsx","./src/components/ui/svgs/anthropicwhite.tsx","./src/components/ui/svgs/convex.tsx","./src/components/ui/svgs/discord.tsx","./src/components/ui/svgs/gemini.tsx","./src/components/ui/svgs/googlecloud.tsx","./src/components/ui/svgs/hono.tsx","./src/components/ui/svgs/loom.tsx","./src/components/ui/svgs/mintlify.tsx","./src/components/ui/svgs/n8n.tsx","./src/components/ui/svgs/neon.tsx","./src/components/ui/svgs/openai.tsx","./src/components/ui/svgs/openaidark.tsx","./src/components/ui/svgs/paper.tsx","./src/components/ui/svgs/planetscale.tsx","./src/components/ui/svgs/planetscaledark.tsx","./src/components/ui/svgs/prisma.tsx","./src/components/ui/svgs/prismadark.tsx","./src/components/ui/svgs/remixdark.tsx","./src/components/ui/svgs/remixlight.tsx","./src/components/ui/svgs/resendiconblack.tsx","./src/components/ui/svgs/resendiconwhite.tsx","./src/components/ui/svgs/slack.tsx","./src/components/ui/svgs/stripe.tsx","./src/components/ui/svgs/supabase.tsx","./src/components/ui/svgs/zoom.tsx","./src/hooks/use-client-data-grid.ts","./src/hooks/use-copy-to-clipboard.ts","./src/hooks/use-mobile.ts","./src/lib/api-client.ts","./src/lib/data-grid-defaults.ts","./src/lib/queryclient.ts","./src/lib/router.ts","./src/lib/ui-labels.ts","./src/lib/ui-surface.ts","./src/lib/access/api-key-labels.ts","./src/lib/metrics/deployment-progress.ts","./src/lib/metrics/index.ts","./src/lib/metrics/job-status-breakdown.ts","./src/lib/metrics/module-type-breakdown.ts","./src/lib/metrics/peer-capacity-bars.ts","./src/lib/metrics/peer-session-breakdown.ts","./src/lib/metrics/readiness-breakdown.ts","./src/lib/metrics/recent-platform-activity.ts","./src/lib/metrics/types.ts","./src/lib/modules/display.ts","./src/lib/modules/helpers.ts","./src/queries/api-keys.ts","./src/queries/auth.ts","./src/queries/directories.ts","./src/queries/firewall.ts","./src/queries/modules.ts","./src/queries/monitoring.ts","./src/queries/network.ts","./src/queries/operations.ts","./src/queries/overview.ts","./src/queries/settings.ts","./src/routes/__root.tsx","./src/routes/_auth.tsx","./src/routes/index.tsx","./src/routes/_auth/access.tsx","./src/routes/_auth/dashboard.tsx","./src/routes/_auth/directories.tsx","./src/routes/_auth/firewall.tsx","./src/routes/_auth/monitoring.tsx","./src/routes/_auth/network.tsx","./src/routes/_auth/operations.tsx","./src/routes/_auth/schedule.tsx","./src/routes/_auth/settings.tsx","./src/routes/_auth/tenant-settings.tsx","./src/routes/_auth/modules/$moduleid.tsx","./src/routes/_auth/modules/index.tsx","./src/routes/_auth/modules/new.tsx","./src/types/api.ts","./vite.config.ts"],"version":"5.9.3"} \ No newline at end of file