From 1639ba40f3ce120b56e8bbdb1eedb0379d15395b Mon Sep 17 00:00:00 2001 From: Denozordec Date: Fri, 17 Jul 2026 20:34:25 +0700 Subject: [PATCH] refactor: update dashboard components to integrate QuickActionGrid and enhance settings Removed the deprecated DashboardQuickLinkCard and replaced it with QuickActionGrid in the DashboardQuickLinks component for improved organization and user experience. Updated the AppearanceSettingsTab to include a toggle for displaying quick actions on the dashboard, enhancing user customization options. Adjusted the SystemMonitorPopover layout for better alignment and spacing. Updated documentation to reflect the new UI preferences for quick actions. --- .../dashboard/dashboard-quick-link-card.tsx | 69 ----------- .../dashboard/dashboard-quick-links.tsx | 69 +++++------ .../layout/system-monitor-popover.tsx | 2 +- apps/web/src/components/reui-kit/index.ts | 1 + .../components/reui-kit/quick-action-grid.tsx | 113 ++++++++++++++++++ .../settings/appearance-settings-tab.tsx | 49 ++++++++ apps/web/src/routes/_auth/dashboard.tsx | 14 ++- apps/web/tsconfig.tsbuildinfo | 2 +- docs/openapi.yaml | 4 + docs/ui-design-contract.md | 14 +++ 10 files changed, 223 insertions(+), 114 deletions(-) delete mode 100644 apps/web/src/components/dashboard/dashboard-quick-link-card.tsx create mode 100644 apps/web/src/components/reui-kit/quick-action-grid.tsx diff --git a/apps/web/src/components/dashboard/dashboard-quick-link-card.tsx b/apps/web/src/components/dashboard/dashboard-quick-link-card.tsx deleted file mode 100644 index 00fdb1c..0000000 --- a/apps/web/src/components/dashboard/dashboard-quick-link-card.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { Link } from '@tanstack/react-router' -import { ChevronRight } from 'lucide-react' -import type { ReactNode } from 'react' - -import { CardDotField } from '@/components/dashboard/card-dot-field' -import { Card, CardContent } from '@evobgp/ui/components/card' -import { cn } from '@evobgp/ui/lib/utils' -import { Item, ItemMedia } from '@evobgp/ui/components/item' - -export type QuickLinkCardProps = { - icon: ReactNode - label: string - description: string - to: string - search?: Record - iconClass: string -} - -export function DashboardQuickLinkCard({ - icon, - label, - description, - to, - search, - iconClass, -}: QuickLinkCardProps) { - return ( - - - - - - - {icon} - - -
- {label} -

{description}

- - Перейти - - -
-
-
- - ) -} diff --git a/apps/web/src/components/dashboard/dashboard-quick-links.tsx b/apps/web/src/components/dashboard/dashboard-quick-links.tsx index 6ebe194..56f7bf1 100644 --- a/apps/web/src/components/dashboard/dashboard-quick-links.tsx +++ b/apps/web/src/components/dashboard/dashboard-quick-links.tsx @@ -1,78 +1,67 @@ import { Gauge, Network, Play, Plus, Share2, Tags } from 'lucide-react' -import type { ReactNode } from 'react' -import { DashboardQuickLinkCard } from '@/components/dashboard/dashboard-quick-link-card' -import { PanelCard } from '@/components/panel-card' +import { QuickActionGrid, type QuickActionItem } from '@/components/reui-kit' -type QuickLink = { - icon: ReactNode - label: string - description: string - to: string - search?: Record - iconClass: string -} - -const LINKS: QuickLink[] = [ +const ACTIONS: QuickActionItem[] = [ { - icon: , - label: 'Создать модуль', + id: 'new-module', + title: 'Создать модуль', description: 'Новый модуль маршрутизации и источники префиксов.', to: '/modules/new', - iconClass: 'bg-primary text-primary-foreground [&_svg]:text-primary-foreground', + icon: , + iconClassName: 'bg-primary text-primary-foreground [&_svg]:text-primary-foreground', }, { - icon: , - label: 'BGP-сообщества', + id: 'communities', + title: 'BGP-сообщества', description: 'Справочник communities для политик экспорта.', to: '/directories', - iconClass: 'bg-info text-info-foreground [&_svg]:text-info-foreground', + icon: , + iconClassName: 'bg-info text-info-foreground [&_svg]:text-info-foreground', }, { - icon: , - label: 'Сеть', + id: 'network', + title: 'Сеть', description: 'Обзор пиров, спикеров и live-сессий BGP.', to: '/network', search: { tab: 'overview' }, - iconClass: 'bg-success text-success-foreground [&_svg]:text-success-foreground', + icon: , + iconClassName: 'bg-success text-success-foreground [&_svg]:text-success-foreground', }, { - icon: , - label: 'Добавить пира', + id: 'add-peer', + title: 'Добавить пира', description: 'Настройка BGP-соседа и шаблонов сессии.', to: '/network', search: { tab: 'peers' }, - iconClass: 'bg-warning text-warning-foreground [&_svg]:text-warning-foreground', + icon: , + iconClassName: 'bg-warning text-warning-foreground [&_svg]:text-warning-foreground', }, { - icon: , - label: 'Деплой', + id: 'deploy', + title: 'Деплой', description: 'Ревизии конфигурации и применение на нодах.', to: '/operations', search: { tab: 'revisions' }, - iconClass: 'bg-focus text-focus-foreground [&_svg]:text-focus-foreground', + icon: , + iconClassName: 'bg-focus text-focus-foreground [&_svg]:text-focus-foreground', }, { - icon: , - label: 'Мониторинг', + id: 'monitoring', + title: 'Мониторинг', description: 'Состояние системы, BIRD и PostgreSQL.', to: '/monitoring', search: { tab: 'system' }, - iconClass: 'bg-destructive text-destructive-foreground [&_svg]:text-destructive-foreground', + icon: , + iconClassName: 'bg-destructive text-destructive-foreground [&_svg]:text-destructive-foreground', }, ] export function DashboardQuickLinks() { return ( - - {LINKS.map((link) => ( - - ))} - + /> ) } diff --git a/apps/web/src/components/layout/system-monitor-popover.tsx b/apps/web/src/components/layout/system-monitor-popover.tsx index cd3065e..a000fe8 100644 --- a/apps/web/src/components/layout/system-monitor-popover.tsx +++ b/apps/web/src/components/layout/system-monitor-popover.tsx @@ -181,7 +181,7 @@ export function SystemMonitorPopover() { - +
Монитор EvoBGP diff --git a/apps/web/src/components/reui-kit/index.ts b/apps/web/src/components/reui-kit/index.ts index c5c7b13..7f24e89 100644 --- a/apps/web/src/components/reui-kit/index.ts +++ b/apps/web/src/components/reui-kit/index.ts @@ -20,6 +20,7 @@ export { type KpiStatVariant, type OpsKpiCard, } from './kpi-stat-grid' +export { QuickActionGrid, type QuickActionItem } from './quick-action-grid' export { OpsDashboard } from './ops-dashboard' export { DetailPanel, type DetailMetricCard } from './detail-panel' export { SettingsShell, type SettingsTabConfig } from './settings-shell' diff --git a/apps/web/src/components/reui-kit/quick-action-grid.tsx b/apps/web/src/components/reui-kit/quick-action-grid.tsx new file mode 100644 index 0000000..43c976f --- /dev/null +++ b/apps/web/src/components/reui-kit/quick-action-grid.tsx @@ -0,0 +1,113 @@ +import type { ReactNode } from 'react' +import { Link } from '@tanstack/react-router' +import { + Frame, + FrameDescription, + FrameHeader, + FramePanel, + FrameTitle, +} from '@/components/reui/frame' +import { Badge } from '@/components/reui/badge' +import { Item, ItemMedia } from '@evobgp/ui/components/item' +import { cn } from '@evobgp/ui/lib/utils' + +export interface QuickActionItem { + id: string + title: string + description: string + to: string + search?: Record + icon?: ReactNode + iconClassName?: string +} + +interface QuickActionGridProps { + actions: QuickActionItem[] + title?: string + description?: string + className?: string +} + +const DEFAULT_ICON_CLASS = 'text-muted-foreground [&_svg]:text-current' + +function kpiCols(count: number): string { + if (count <= 1) return 'grid-cols-1' + if (count === 2) return 'grid-cols-1 @xl:grid-cols-2' + if (count === 3) return 'grid-cols-1 @3xl:grid-cols-3' + if (count === 4) return 'grid-cols-1 @3xl:grid-cols-2 @6xl:grid-cols-4' + if (count === 5) return 'grid-cols-2 @3xl:grid-cols-3 xl:grid-cols-5' + if (count === 6) return 'grid-cols-2 sm:grid-cols-3 xl:grid-cols-6' + return 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-4' +} + +function QuickActionBody({ action }: { action: QuickActionItem }) { + return ( +
+ {action.icon ? ( + + + {action.icon} + + + ) : null} + +
+
+ {action.title} + + Перейти + +
+

+ {action.description} +

+
+
+ ) +} + +/** + * KPI-like quick actions strip (horizontal Frame tiles). + * Preview: https://reui.io/preview/base/stats-12 + */ +export function QuickActionGrid({ + actions, + title = 'Быстрые действия', + description, + className, +}: QuickActionGridProps) { + if (actions.length === 0) return null + + return ( + + {(title || description) && ( + + {title ? {title} : null} + {description ? {description} : null} + + )} +
+ {actions.map((action) => ( + + + + + + ))} +
+ + ) +} diff --git a/apps/web/src/components/settings/appearance-settings-tab.tsx b/apps/web/src/components/settings/appearance-settings-tab.tsx index 257bbc2..9bb186c 100644 --- a/apps/web/src/components/settings/appearance-settings-tab.tsx +++ b/apps/web/src/components/settings/appearance-settings-tab.tsx @@ -1,5 +1,7 @@ import { Moon, Sun, SunMoon } from 'lucide-react' import { useTheme } from 'next-themes' +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { toast } from 'sonner' import { SettingRow } from '@/components/blocks/settings-7/components/setting-row' import { SettingsCard } from '@/components/blocks/settings-7/components/settings-card' @@ -9,6 +11,9 @@ import { ToggleGroup, ToggleGroupItem, } from '@evobgp/ui/components/toggle-group' +import { Switch } from '@evobgp/ui/components/switch' +import { apiMutate } from '@/lib/api-client' +import { settingsKeys, settingsQueryOptions } from '@/queries/settings' const THEME_OPTIONS = [ { value: 'light', label: 'Светлая', icon: Sun }, @@ -16,8 +21,27 @@ const THEME_OPTIONS = [ { value: 'system', label: 'Система', icon: SunMoon }, ] as const +function parseShowQuickActions(value: unknown): boolean { + if (value === false || value === 0 || value === 'false' || value === '0') return false + return true +} + export function AppearanceSettingsTab() { const { theme, setTheme } = useTheme() + const qc = useQueryClient() + const settingsQ = useQuery(settingsQueryOptions()) + const showQuickActions = parseShowQuickActions(settingsQ.data?.ui_show_quick_actions) + + const patchMut = useMutation({ + mutationFn: (payload: Record) => + apiMutate('/v1/settings', 'PATCH', payload), + onSuccess: () => { + toast.success('Настройки интерфейса сохранены') + void qc.invalidateQueries({ queryKey: settingsKeys.all }) + }, + onError: (e) => + toast.error(e instanceof Error ? e.message : 'Не удалось сохранить'), + }) return (
@@ -63,6 +87,31 @@ export function AppearanceSettingsTab() { + + + + + + patchMut.mutate({ ui_show_quick_actions: checked }) + } + aria-label="Показывать быстрые действия" + /> + + +
) } diff --git a/apps/web/src/routes/_auth/dashboard.tsx b/apps/web/src/routes/_auth/dashboard.tsx index 9a85a1a..ba9ea5f 100644 --- a/apps/web/src/routes/_auth/dashboard.tsx +++ b/apps/web/src/routes/_auth/dashboard.tsx @@ -1,5 +1,5 @@ import { createFileRoute } from '@tanstack/react-router' -import { useQueries } from '@tanstack/react-query' +import { useQueries, useQuery } from '@tanstack/react-query' import { RefreshCw } from 'lucide-react' import { useState } from 'react' @@ -27,13 +27,21 @@ import { overviewRevisionsQueryOptions, overviewSpeakersQueryOptions, } from '@/queries/overview' +import { settingsQueryOptions } from '@/queries/settings' export const Route = createFileRoute('/_auth/dashboard')({ component: DashboardComponent, }) +function parseShowQuickActions(value: unknown): boolean { + if (value === false || value === 0 || value === 'false' || value === '0') return false + return true +} + function DashboardComponent() { const [lastUpdated, setLastUpdated] = useState(null) + const settingsQ = useQuery(settingsQueryOptions()) + const showQuickActions = parseShowQuickActions(settingsQ.data?.ui_show_quick_actions) const results = useQueries({ queries: [ @@ -86,8 +94,6 @@ function DashboardComponent() { } /> - - {initialLoading ? ( ) : ( @@ -99,6 +105,8 @@ function DashboardComponent() { jobs={jobs} /> + {showQuickActions ? : null} +
diff --git a/apps/web/tsconfig.tsbuildinfo b/apps/web/tsconfig.tsbuildinfo index 560ea78..ac905f4 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/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/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-2.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/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/index.ts","./src/components/reui-kit/kpi-stat-grid.tsx","./src/components/reui-kit/ops-dashboard.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-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.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/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/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-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/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/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/index.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-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.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 diff --git a/docs/openapi.yaml b/docs/openapi.yaml index a385e65..c870070 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -1455,6 +1455,10 @@ components: description: UTC cron для автоочистки (по умолчанию `0 */6 * * *`). runtime_logs_auto_mode: $ref: "#/components/schemas/RuntimeLogCleanupMode" + ui_show_quick_actions: + type: boolean + description: Показывать блок «Быстрые действия» на дашборде (UI preference). + default: true additionalProperties: true RevisionDiff: diff --git a/docs/ui-design-contract.md b/docs/ui-design-contract.md index e7c0ead..3223602 100644 --- a/docs/ui-design-contract.md +++ b/docs/ui-design-contract.md @@ -32,11 +32,25 @@ Ops / list / dashboard / detail / settings — только **Frame**, не shad |-----------|------| | `ResourcePage` | Frame + line tabs + Filters + DataGrid | | `KpiStatGrid` | horizontal compact hybrid KPI tiles (`variant`, Badge) | +| `QuickActionGrid` | KPI-like quick action tiles under KPI (gated by `ui_show_quick_actions`) | | `OpsDashboard` | KPI + charts + attention queue | | `SettingsShell` | settings nav + Outlet | | `DetailPanel` | detail Frame sections | | `filter-utils` | apply/clear ReUI Filters | +## Dashboard layout + +| App | Section order | +|-----|---------------| +| EvoBGP / CFDM | KPI → **QuickActionGrid** → charts / rest | +| vps-tracker | banner → KPI → charts → attention → **QuickActionGrid** → CSV | + +Gating: KV `ui_show_quick_actions` in `global_settings` via `PATCH /v1/settings` (default `true`). + +## System monitor + +`SystemMonitorPopover` in app-shell header next to `ModeToggle`. Preview: https://reui.io/preview/base/app-shell-12 · https://reui.io/preview/base/app-shell-7 + ## MCP workflow 1. MCP `user-reui` — `search` / `get_block` / `get_component` with `surface: "frame"`