From b13c23367976858d8f7eba469d9f84a459f924da Mon Sep 17 00:00:00 2001 From: Denozordec Date: Fri, 31 Jul 2026 13:07:26 +0700 Subject: [PATCH] feat(analytics): remove OperationsAnalyticsCard and update imports Deleted the OperationsAnalyticsCard component to streamline analytics functionality. Updated the analytics index file to reflect this change by removing the export for OperationsAnalyticsCard. Adjusted the QuickActionGrid component to enhance user interaction with new keyboard handling and badge functionality. Co-authored-by: Cursor --- apps/web/src/components/analytics/index.ts | 1 - .../analytics/operations-analytics-card.tsx | 65 ----- .../components/reui-kit/quick-action-grid.tsx | 94 +++++-- apps/web/src/routes/_auth/operations.tsx | 234 ++++++++++++------ apps/web/tsconfig.tsbuildinfo | 2 +- 5 files changed, 239 insertions(+), 157 deletions(-) delete mode 100644 apps/web/src/components/analytics/operations-analytics-card.tsx diff --git a/apps/web/src/components/analytics/index.ts b/apps/web/src/components/analytics/index.ts index 23e271d..4649688 100644 --- a/apps/web/src/components/analytics/index.ts +++ b/apps/web/src/components/analytics/index.ts @@ -10,4 +10,3 @@ export { DashboardOperationsFlowCard } from './dashboard-operations-flow-card' export { DashboardPlatformCard } from './dashboard-platform-card' export { MonitoringHealthCard } from './monitoring-health-card' export { NetworkOverviewAnalyticsCard } from './network-overview-analytics-card' -export { OperationsAnalyticsCard } from './operations-analytics-card' diff --git a/apps/web/src/components/analytics/operations-analytics-card.tsx b/apps/web/src/components/analytics/operations-analytics-card.tsx deleted file mode 100644 index 8bbcc8b..0000000 --- a/apps/web/src/components/analytics/operations-analytics-card.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { AnalyticsCardShell } from '@/components/analytics/analytics-card-shell' -import { AnalyticsKpiRow } from '@/components/analytics/analytics-kpi-row' -import { ChartDonutMetric } from '@/components/analytics/chart-donut-metric' -import { jobStatusBreakdown } from '@/lib/metrics' -import type { JobRow, RevisionRow } from '@/types/api' - -export function OperationsAnalyticsCard({ - jobs, - revisions, - loading, -}: { - jobs: JobRow[] - revisions: RevisionRow[] - loading?: boolean -}) { - const running = jobs.filter((j) => ['running', 'queued'].includes(j.status.toLowerCase())).length - const failed = jobs.filter((j) => - ['failed', 'error', 'cancelled'].includes(j.status.toLowerCase()), - ).length - const slices = jobStatusBreakdown(jobs) - const total = slices.reduce((sum, slice) => sum + slice.count, 0) - - return ( - - 0 ? 'up' : 'neutral', - label: running > 0 ? 'выполняются' : 'очередь пуста', - tone: (running > 0 ? 'warning' : 'muted') as 'warning' | 'muted', - }, - }, - { - label: 'С ошибкой', - value: loading ? '—' : String(failed), - delta: { - direction: failed > 0 ? 'down' : 'up', - label: failed > 0 ? 'требуют внимания' : 'в норме', - tone: failed > 0 ? 'destructive' : 'success', - }, - }, - ]} - /> - {loading ? ( -
- Загрузка… -
- ) : ( - - )} -
- ) -} diff --git a/apps/web/src/components/reui-kit/quick-action-grid.tsx b/apps/web/src/components/reui-kit/quick-action-grid.tsx index 0358511..b0c283f 100644 --- a/apps/web/src/components/reui-kit/quick-action-grid.tsx +++ b/apps/web/src/components/reui-kit/quick-action-grid.tsx @@ -1,4 +1,4 @@ -import type { ReactNode } from 'react' +import type { KeyboardEvent, ReactNode } from 'react' import { Link } from '@tanstack/react-router' import { Frame, @@ -12,16 +12,23 @@ import { Item, ItemMedia } from '@evobgp/ui/components/item' import { cn } from '@evobgp/ui/lib/utils' import { kpiCols } from './kpi-cols' -export interface QuickActionItem { +type QuickActionBase = { id: string title: string description: string - to: string - search?: Record icon?: ReactNode iconClassName?: string + /** Override badge label (default: «Перейти» for links, «Выполнить» for onClick). */ + badge?: string + disabled?: boolean } +export type QuickActionItem = QuickActionBase & + ( + | { to: string; search?: Record; onClick?: never } + | { onClick: () => void; to?: never; search?: never } + ) + interface QuickActionGridProps { actions: QuickActionItem[] title?: string @@ -31,6 +38,18 @@ interface QuickActionGridProps { const DEFAULT_ICON_CLASS = 'text-muted-foreground [&_svg]:text-current' +function resolveBadge(action: QuickActionItem): string { + if (action.badge) return action.badge + return action.onClick ? 'Выполнить' : 'Перейти' +} + +function handleActionKeyDown(onActivate: () => void, event: KeyboardEvent) { + if (event.key === 'Enter' || event.key === ' ') { + event.preventDefault() + onActivate() + } +} + function QuickActionBody({ action }: { action: QuickActionItem }) { return (
@@ -51,7 +70,7 @@ function QuickActionBody({ action }: { action: QuickActionItem }) {
{action.title} - Перейти + {resolveBadge(action)}

@@ -62,6 +81,15 @@ function QuickActionBody({ action }: { action: QuickActionItem }) { ) } +function panelClassName(disabled?: boolean) { + return cn( + 'relative isolate flex h-full flex-col transition-colors', + disabled + ? 'cursor-not-allowed opacity-60' + : 'hover:bg-muted/40 focus-within:ring-ring cursor-pointer focus-within:ring-2', + ) +} + /** * KPI-like quick actions strip (horizontal Frame tiles). * Preview: https://reui.io/preview/base/stats-12 @@ -83,21 +111,51 @@ export function QuickActionGrid({ )}

- {actions.map((action) => ( - - { + const label = `${action.title}: ${action.description}` + + if ('to' in action && action.to) { + return ( + + {action.disabled ? ( +
+ +
+ ) : ( + + + + )} +
+ ) + } + + const onClick = action.onClick + const onActivate = () => { + if (action.disabled || !onClick) return + onClick() + } + + return ( + handleActionKeyDown(onActivate, e)} > - - - ))} +
+ ) + })}
) diff --git a/apps/web/src/routes/_auth/operations.tsx b/apps/web/src/routes/_auth/operations.tsx index a94e319..cf2938b 100644 --- a/apps/web/src/routes/_auth/operations.tsx +++ b/apps/web/src/routes/_auth/operations.tsx @@ -1,14 +1,20 @@ import { createFileRoute, useSearch } from '@tanstack/react-router' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' -import { RefreshCw } from 'lucide-react' +import { AlertTriangle, GitBranch, ListTodo, Play, RefreshCw, RotateCcw } from 'lucide-react' import { toast } from 'sonner' -import { useState, useMemo } from 'react' +import { useMemo, useState } from 'react' import { Button } from '@evobgp/ui/components/button' import { PanelCard } from '@/components/panel-card' import { BadgeTabs, TabsContent } from '@/components/badge-tabs' -import { FrameDataGrid } from '@/components/reui-kit' -import { OperationsAnalyticsCard } from '@/components/analytics' +import { + FrameDataGrid, + KpiStatGrid, + QuickActionGrid, + type KpiStatItem, + type QuickActionItem, +} from '@/components/reui-kit' +import { Badge } from '@/components/reui/badge' import { SelectMenu } from '@/components/select-field' import { OperationsJobsCard } from '@/components/operations/operations-jobs-card' import { OperationsRevisionsGrid } from '@/components/operations/operations-revisions-grid' @@ -16,9 +22,14 @@ import { PageHeader } from '@/components/page-header' import { QueryState } from '@/components/query-state' import { ConfirmDialog } from '@/components/confirm-dialog' -import { operationsJobsQueryOptions, operationsRevisionsQueryOptions, operationsDiffQueryOptions } from '@/queries/operations' +import { + operationsDiffQueryOptions, + operationsJobsQueryOptions, + operationsRevisionsQueryOptions, +} from '@/queries/operations' import { moduleNameById, overviewModulesQueryOptions } from '@/queries/overview' import { apiMutate, waitForJob } from '@/lib/api-client' +import type { JobRow, RevisionRow } from '@/types/api' export const Route = createFileRoute('/_auth/operations')({ component: OperationsComponent, @@ -30,20 +41,74 @@ export const Route = createFileRoute('/_auth/operations')({ }), }) +function buildOperationsKpiItems(revisions: RevisionRow[], jobs: JobRow[]): KpiStatItem[] { + const running = jobs.filter((j) => ['running', 'queued'].includes(j.status.toLowerCase())).length + const failed = jobs.filter((j) => + ['failed', 'error', 'cancelled'].includes(j.status.toLowerCase()), + ).length + + return [ + { + id: 'revisions', + label: 'Ревизий', + value: revisions.length, + icon: , + iconClassName: 'text-primary', + footer: ( + + в выборке + + ), + }, + { + id: 'active-jobs', + label: 'Активных задач', + value: running, + icon: , + iconClassName: running > 0 ? 'text-warning' : 'text-muted-foreground', + variant: running > 0 ? 'warning' : 'default', + footer: ( + 0 ? 'warning-light' : 'outline'} size="sm"> + {running > 0 ? 'выполняются' : 'очередь пуста'} + + ), + }, + { + id: 'failed-jobs', + label: 'С ошибкой', + value: failed, + icon: , + iconClassName: failed > 0 ? 'text-destructive' : 'text-muted-foreground', + variant: failed > 0 ? 'destructive' : 'default', + footer: ( + 0 ? 'destructive-light' : 'success-light'} size="sm"> + {failed > 0 ? 'требуют внимания' : 'в норме'} + + ), + }, + ] +} + function OperationsComponent() { const search = useSearch({ from: '/_auth/operations' }) const navigate = Route.useNavigate() const qc = useQueryClient() + const [applyOpen, setApplyOpen] = useState(false) + const [reloadOpen, setReloadOpen] = useState(false) + const revisionsQ = useQuery(operationsRevisionsQueryOptions()) const jobsQ = useQuery(operationsJobsQueryOptions()) const modulesQ = useQuery(overviewModulesQueryOptions()) - const revisions = revisionsQ.data?.items ?? [] - const jobs = jobsQ.data?.items ?? [] + const revisions = useMemo(() => revisionsQ.data?.items ?? [], [revisionsQ.data?.items]) + const jobs = useMemo(() => jobsQ.data?.items ?? [], [jobsQ.data?.items]) const nameById = moduleNameById(modulesQ.data?.items ?? []) const refreshing = revisionsQ.isFetching || jobsQ.isFetching + const kpiLoading = revisionsQ.isLoading || jobsQ.isLoading + + const kpiItems = useMemo(() => buildOperationsKpiItems(revisions, jobs), [revisions, jobs]) function refetchAll() { void revisionsQ.refetch() @@ -63,6 +128,7 @@ function OperationsComponent() { }, onSuccess: () => { toast.success('Конфигурация успешно применена') + setApplyOpen(false) void qc.invalidateQueries({ queryKey: ['operations'] }) }, onError: (e) => toast.error(e instanceof Error ? e.message : 'Не удалось применить'), @@ -76,12 +142,41 @@ function OperationsComponent() { if (job.status !== 'succeeded') throw new Error(job.error ?? job.status) return job }, - onSuccess: () => toast.success('Команда birdc configure выполнена'), + onSuccess: () => { + toast.success('Команда birdc configure выполнена') + setReloadOpen(false) + }, onError: (e) => toast.error(e instanceof Error ? e.message : 'Не удалось перезагрузить'), }) + const quickActions: QuickActionItem[] = useMemo( + () => [ + { + id: 'apply', + title: 'Применить', + description: 'Деплой текущей ревизии на все BIRD-агенты.', + icon: , + iconClassName: 'text-primary', + badge: 'Выполнить', + disabled: applyMutation.isPending || revisions.length === 0, + onClick: () => setApplyOpen(true), + }, + { + id: 'bird-reload', + title: 'Перезагрузка BIRD', + description: 'Выполнить birdc configure на локальном демоне.', + icon: , + iconClassName: 'text-warning', + badge: 'Выполнить', + disabled: birdReloadMutation.isPending, + onClick: () => setReloadOpen(true), + }, + ], + [applyMutation.isPending, birdReloadMutation.isPending, revisions.length], + ) + return ( -
+
-
- - Применить - - } - title="Применить конфигурацию на всех спикерах?" - description="Текущая конфигурация будет применена на всех BIRD-агентах. Требуется роль оператора." - confirmLabel="Применить" - onConfirm={() => applyMutation.mutate()} - /> - - Перезагрузка BIRD - - } - title="Перезагрузить BIRD?" - description="BIRD перезагрузит конфигурацию. Требуется роль оператора." - confirmLabel="Перезагрузить" - onConfirm={() => birdReloadMutation.mutate()} - /> -
+ - {revisionsQ.isLoading ? ( - - ) : ( - - )} + + + applyMutation.mutate()} + /> + birdReloadMutation.mutate()} + /> -
-
- Ревизия A - v && setA(v)} - /> -
-
- Ревизия B - v && setB(v)} - /> -
- +
+
+ Ревизия A + v && setA(v)} + />
+
+ Ревизия B + v && setB(v)} + /> +
+ +
- diffQ.refetch()} - > - {(diff) => } - + diffQ.refetch()} + > + {(diff) => } + ) } diff --git a/apps/web/tsconfig.tsbuildinfo b/apps/web/tsconfig.tsbuildinfo index be41137..41e9ea2 100644 --- a/apps/web/tsconfig.tsbuildinfo +++ b/apps/web/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/main.tsx","./src/routetree.gen.ts","./src/vite-env.d.ts","./src/components/app-switcher.tsx","./src/components/badge-tabs.tsx","./src/components/category-badge.tsx","./src/components/confirm-dialog.tsx","./src/components/counted-line-tabs.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/kpi-stat-grid.tsx","./src/components/loading-button.tsx","./src/components/page-header.tsx","./src/components/page-shell.tsx","./src/components/panel-card.tsx","./src/components/query-state.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-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-2.tsx","./src/components/examples/c-tabs-6.tsx","./src/components/examples/c-tabs-7.tsx","./src/components/layout/app-shell.tsx","./src/components/layout/apps-menu.tsx","./src/components/layout/command-palette.tsx","./src/components/layout/nav-user.tsx","./src/components/layout/system-monitor-popover.tsx","./src/components/lookup/lookup-matches-grid.tsx","./src/components/lookup/lookup-search-form.tsx","./src/components/lookup/lookup-summary-kpi.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-discovered-peers-card.tsx","./src/components/network/network-kpi.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/alert.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/reui-kit/detail-panel.tsx","./src/components/reui-kit/filter-utils.ts","./src/components/reui-kit/frame-data-grid.tsx","./src/components/reui-kit/index.ts","./src/components/reui-kit/kpi-cols.ts","./src/components/reui-kit/kpi-stat-grid.tsx","./src/components/reui-kit/ops-dashboard.tsx","./src/components/reui-kit/quick-action-grid.tsx","./src/components/reui-kit/resource-page.tsx","./src/components/reui-kit/settings-shell.tsx","./src/components/schedule/schedule-agenda-panel.tsx","./src/components/schedule/schedule-calendar-view.tsx","./src/components/schedule/schedule-jobs-card.tsx","./src/components/schedule/schedule-jobs-grid.tsx","./src/components/schedule/schedule-modules-grid.tsx","./src/components/settings/appearance-settings-tab.tsx","./src/components/settings/connection-settings-tab.tsx","./src/components/settings/sections-settings-tab.tsx","./src/components/settings/session-settings-tab.tsx","./src/components/settings/settings-kv-grid.tsx","./src/components/settings/settings-page-shell.tsx","./src/components/settings/settings-setting-field.tsx","./src/components/settings/settings-tabs-data.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-app-switcher.ts","./src/hooks/use-client-data-grid.ts","./src/hooks/use-copy-to-clipboard.ts","./src/hooks/use-file-upload.ts","./src/hooks/use-mobile.ts","./src/lib/api-client.test.ts","./src/lib/api-client.ts","./src/lib/app-switcher-config.ts","./src/lib/auth.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/app-switcher.ts","./src/queries/auth.ts","./src/queries/directories.ts","./src/queries/lookup.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/auth.callback.tsx","./src/routes/index.tsx","./src/routes/_auth/access.tsx","./src/routes/_auth/dashboard.tsx","./src/routes/_auth/directories.tsx","./src/routes/_auth/lookup.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.gen.ts","./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/vite-env.d.ts","./src/components/app-switcher.tsx","./src/components/badge-tabs.tsx","./src/components/category-badge.tsx","./src/components/confirm-dialog.tsx","./src/components/counted-line-tabs.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/kpi-stat-grid.tsx","./src/components/loading-button.tsx","./src/components/page-header.tsx","./src/components/page-shell.tsx","./src/components/panel-card.tsx","./src/components/query-state.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/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-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-2.tsx","./src/components/examples/c-tabs-6.tsx","./src/components/examples/c-tabs-7.tsx","./src/components/layout/app-shell.tsx","./src/components/layout/apps-menu.tsx","./src/components/layout/command-palette.tsx","./src/components/layout/nav-user.tsx","./src/components/layout/system-monitor-popover.tsx","./src/components/lookup/lookup-matches-grid.tsx","./src/components/lookup/lookup-search-form.tsx","./src/components/lookup/lookup-summary-kpi.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-discovered-peers-card.tsx","./src/components/network/network-kpi.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/alert.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/reui-kit/detail-panel.tsx","./src/components/reui-kit/filter-utils.ts","./src/components/reui-kit/frame-data-grid.tsx","./src/components/reui-kit/index.ts","./src/components/reui-kit/kpi-cols.ts","./src/components/reui-kit/kpi-stat-grid.tsx","./src/components/reui-kit/ops-dashboard.tsx","./src/components/reui-kit/quick-action-grid.tsx","./src/components/reui-kit/resource-page.tsx","./src/components/reui-kit/settings-shell.tsx","./src/components/schedule/schedule-agenda-panel.tsx","./src/components/schedule/schedule-calendar-view.tsx","./src/components/schedule/schedule-jobs-card.tsx","./src/components/schedule/schedule-jobs-grid.tsx","./src/components/schedule/schedule-modules-grid.tsx","./src/components/settings/appearance-settings-tab.tsx","./src/components/settings/connection-settings-tab.tsx","./src/components/settings/sections-settings-tab.tsx","./src/components/settings/session-settings-tab.tsx","./src/components/settings/settings-kv-grid.tsx","./src/components/settings/settings-page-shell.tsx","./src/components/settings/settings-setting-field.tsx","./src/components/settings/settings-tabs-data.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-app-switcher.ts","./src/hooks/use-client-data-grid.ts","./src/hooks/use-copy-to-clipboard.ts","./src/hooks/use-file-upload.ts","./src/hooks/use-mobile.ts","./src/lib/api-client.test.ts","./src/lib/api-client.ts","./src/lib/app-switcher-config.ts","./src/lib/auth.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/app-switcher.ts","./src/queries/auth.ts","./src/queries/directories.ts","./src/queries/lookup.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/auth.callback.tsx","./src/routes/index.tsx","./src/routes/_auth/access.tsx","./src/routes/_auth/dashboard.tsx","./src/routes/_auth/directories.tsx","./src/routes/_auth/lookup.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.gen.ts","./src/types/api.ts","./vite.config.ts"],"version":"5.9.3"} \ No newline at end of file