-
+
filterJobs(jobs, tab), [jobs, tab])
return (
-
+
setTab(v as JobTab)} className="w-full">
@@ -72,6 +72,6 @@ export function ScheduleJobsCard({
/>
)}
-
+
)
}
diff --git a/apps/web/src/components/section-cards.tsx b/apps/web/src/components/section-cards.tsx
deleted file mode 100644
index c8a291f..0000000
--- a/apps/web/src/components/section-cards.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import type { ReactElement, ReactNode } from 'react'
-
-import { Badge } from '@/components/reui/badge'
-import { KpiStatGrid, type KpiStatItem } from '@/components/kpi-stat-grid'
-
-export interface SectionCardItem {
- label: ReactNode
- value: string | number | ReactElement
- hint?: ReactNode
- icon?: ReactNode
- badge?: ReactNode
- variant?: 'default' | 'warning' | 'destructive'
- active?: boolean
- onClick?: () => void
-}
-
-const VARIANT_ICON_CLASS: Record, string> = {
- default: 'text-muted-foreground',
- warning: 'text-warning',
- destructive: 'text-destructive',
-}
-
-function hintToFooter(hint: ReactNode) {
- if (typeof hint === 'string') {
- return (
-
- {hint}
-
- )
- }
- return hint
-}
-
-function toKpiStatItem(item: SectionCardItem, index: number): KpiStatItem {
- const variant = item.variant ?? 'default'
- const footer =
- item.badge ?? (item.hint ? hintToFooter(item.hint) : undefined)
-
- return {
- id: typeof item.label === 'string' ? item.label : `section-${index}`,
- icon: item.icon,
- iconClassName: VARIANT_ICON_CLASS[variant],
- value: item.value,
- label: item.label,
- variant,
- footer,
- active: item.active,
- onClick: item.onClick,
- }
-}
-
-/** KPI row for route sections — ReUI stats-12 / dashboard PRO pattern. */
-export function SectionCards({ items, className }: { items: SectionCardItem[]; className?: string }) {
- return (
-
- )
-}
diff --git a/apps/web/src/routes/_auth/access.tsx b/apps/web/src/routes/_auth/access.tsx
index 674d051..9b8cfd8 100644
--- a/apps/web/src/routes/_auth/access.tsx
+++ b/apps/web/src/routes/_auth/access.tsx
@@ -9,7 +9,7 @@ import { PanelCard } from '@/components/panel-card'
import { AccessApiKeysCard } from '@/components/access/access-api-keys-card'
import { PageHeader } from '@/components/page-header'
import { Badge } from '@/components/reui/badge'
-import { SectionCards, type SectionCardItem } from '@/components/section-cards'
+import { KpiStatGrid, type KpiStatItem } from '@/components/reui-kit'
import { SectionCardsSkeleton } from '@/components/skeletons'
import { sessionCanManageApiKeys } from '@/lib/auth'
import { authSessionQueryOptions } from '@/queries/auth'
@@ -35,13 +35,13 @@ function AccessComponent() {
const refreshing = sessionQuery.isFetching || keysQuery.isFetching
- const kpiItems: SectionCardItem[] = useMemo(
+ const kpiItems: KpiStatItem[] = useMemo(
() => [
{
label: 'Всего ключей',
value: keys.length,
icon: ,
- badge: (
+ footer: (
в tenant
@@ -51,7 +51,7 @@ function AccessComponent() {
label: 'Активных',
value: activeCount,
icon: ,
- badge: (
+ footer: (
не отозваны
@@ -62,7 +62,7 @@ function AccessComponent() {
value: revokedCount,
icon: ,
variant: revokedCount > 0 ? 'warning' : 'default',
- badge: (
+ footer: (
0 ? 'warning-light' : 'outline'} size="sm">
revoked
@@ -156,7 +156,7 @@ function AccessComponent() {
{keysQuery.isLoading ? (
) : (
-
+
)}
(null)
const settingsQ = useQuery(settingsQueryOptions())
@@ -74,39 +77,28 @@ function DashboardComponent() {
const jobs = jobsQ.data?.items ?? []
const nameById = moduleNameById(modules)
- const activityLoading =
- refreshing && jobs.length === 0 && revisions.length === 0
+ const activityLoading = refreshing && jobs.length === 0 && revisions.length === 0
+ const kpiCards = buildDashboardKpiCards({ modules, peers, speakers, jobs })
return (
-
-
-
- Обновить
-
- }
- />
-
- {initialLoading ? (
-
- ) : (
+
+
+ Обновить
+
+ }
+ kpiCards={kpiCards}
+ isLoading={initialLoading}
+ afterKpi={showQuickActions ? : null}
+ charts={
<>
-
-
- {showQuickActions ? : null}
-
@@ -120,39 +112,40 @@ function DashboardComponent() {
/>
-
>
- )}
-
-
-
- {activityLoading ? (
-
- ) : (
-
- )}
-
-
-
- {activityLoading ? (
-
- ) : (
-
- )}
-
-
-
+ }
+ queueTitle="Недавняя активность"
+ queueDescription="Задачи и ревизии плоскости управления"
+ queue={
+
+
+ {activityLoading ? (
+
+ ) : (
+
+ )}
+
+
+ {activityLoading ? (
+
+ ) : (
+
+ )}
+
+
+ }
+ />
)
}
diff --git a/apps/web/src/routes/_auth/directories.tsx b/apps/web/src/routes/_auth/directories.tsx
index aa84716..1a8da1f 100644
--- a/apps/web/src/routes/_auth/directories.tsx
+++ b/apps/web/src/routes/_auth/directories.tsx
@@ -5,12 +5,7 @@ import { BookText, Globe, RefreshCw, Tags } from 'lucide-react'
import { Button } from '@evobgp/ui/components/button'
import { Badge } from '@/components/reui/badge'
import { BadgeTabs, TabsContent } from '@/components/badge-tabs'
-import { DataGridCard } from '@/components/data-grid-shell'
-import { DirectoriesCommunitiesGrid } from '@/components/directories/directories-communities-grid'
-import { DirectoriesDohGrid } from '@/components/directories/directories-doh-grid'
-import { PageHeader } from '@/components/page-header'
-import { QueryState } from '@/components/query-state'
-import { SectionCards, type SectionCardItem } from '@/components/section-cards'
+import { FrameDataGrid, KpiStatGrid, type KpiStatItem } from '@/components/reui-kit'
import { SectionCardsSkeleton, TableSkeleton } from '@/components/skeletons'
import { directoriesCommunitiesQueryOptions, directoriesDohQueryOptions } from '@/queries/directories'
@@ -26,12 +21,12 @@ function DirectoriesComponent() {
const dohProfiles = dohQ.data?.items ?? []
const loading = communitiesQ.isLoading || dohQ.isLoading
- const items: SectionCardItem[] = [
+ const items: KpiStatItem[] = [
{
label: 'Сообщества BGP',
value: communities.length,
icon: ,
- badge: (
+ footer: (
теги префиксов
@@ -41,7 +36,7 @@ function DirectoriesComponent() {
label: 'DoH профили',
value: dohProfiles.length,
icon: ,
- badge: (
+ footer: (
резолвинг доменов
@@ -51,7 +46,7 @@ function DirectoriesComponent() {
label: 'Справочники',
value: 'Общие',
icon: ,
- badge: (
+ footer: (
все модули tenant
@@ -80,7 +75,7 @@ function DirectoriesComponent() {
}
/>
- {loading ? : }
+ {loading ? : }
-
@@ -111,11 +106,11 @@ function DirectoriesComponent() {
/>
)}
-
+
-
+
)}
-
+
diff --git a/apps/web/src/routes/_auth/modules/index.tsx b/apps/web/src/routes/_auth/modules/index.tsx
index 8bfe081..78c793e 100644
--- a/apps/web/src/routes/_auth/modules/index.tsx
+++ b/apps/web/src/routes/_auth/modules/index.tsx
@@ -4,7 +4,7 @@ import { Plus, RefreshCw } from 'lucide-react'
import { Button } from '@evobgp/ui/components/button'
-import { DataGridCard } from '@/components/data-grid-shell'
+import { FrameDataGrid } from '@/components/reui-kit'
import { ProjectsEmptyState } from '@/components/patterns/projects-empty-state'
import { ModulesListGrid } from '@/components/modules/modules-list-grid'
import { PageHeader } from '@/components/page-header'
@@ -39,7 +39,7 @@ function ModulesListComponent() {
}
/>
- }>
@@ -65,7 +65,7 @@ function ModulesListComponent() {
/>
)}
-
+
)
}
diff --git a/apps/web/src/routes/_auth/monitoring.tsx b/apps/web/src/routes/_auth/monitoring.tsx
index a86ecf4..591adb9 100644
--- a/apps/web/src/routes/_auth/monitoring.tsx
+++ b/apps/web/src/routes/_auth/monitoring.tsx
@@ -5,7 +5,7 @@ import { AlertTriangle, Bird, Database, FileText, RefreshCw } from 'lucide-react
import { Button } from '@evobgp/ui/components/button'
import { PanelCard } from '@/components/panel-card'
import { BadgeTabs, TabsContent } from '@/components/badge-tabs'
-import { DataGridCard } from '@/components/data-grid-shell'
+import { FrameDataGrid } from '@/components/reui-kit'
import { StatusBadge } from '@/components/status-badge'
import { jobKindRu } from '@/lib/ui-labels'
import {
@@ -125,7 +125,7 @@ function MonitoringComponent() {
)}
-
@@ -139,7 +139,7 @@ function MonitoringComponent() {
>
{(ready) => }
-
+
-
+
)}
-
+
diff --git a/apps/web/src/routes/_auth/schedule.tsx b/apps/web/src/routes/_auth/schedule.tsx
index 4f3a68c..96f1eae 100644
--- a/apps/web/src/routes/_auth/schedule.tsx
+++ b/apps/web/src/routes/_auth/schedule.tsx
@@ -6,13 +6,13 @@ import { useState } from 'react'
import { Button } from '@evobgp/ui/components/button'
import { Badge } from '@/components/reui/badge'
-import { DataGridCard } from '@/components/data-grid-shell'
+import { FrameDataGrid } from '@/components/reui-kit'
import { ScheduleAgendaPanel } from '@/components/schedule/schedule-agenda-panel'
import { ScheduleJobsCard } from '@/components/schedule/schedule-jobs-card'
import { ScheduleModulesGrid } from '@/components/schedule/schedule-modules-grid'
import { PageHeader } from '@/components/page-header'
import { QueryState } from '@/components/query-state'
-import { SectionCards, type SectionCardItem } from '@/components/section-cards'
+import { KpiStatGrid, type KpiStatItem } from '@/components/reui-kit'
import { SectionCardsSkeleton } from '@/components/skeletons'
import { operationsJobsQueryOptions } from '@/queries/operations'
@@ -37,12 +37,12 @@ function ScheduleComponent() {
['failed', 'error', 'cancelled'].includes(j.status.toLowerCase()),
).length
- const items: SectionCardItem[] = [
+ const items: KpiStatItem[] = [
{
label: 'Всего задач',
value: jobs.length,
icon: ,
- badge: (
+ footer: (
в выборке
@@ -52,7 +52,7 @@ function ScheduleComponent() {
label: 'В работе',
value: running,
icon: ,
- badge: (
+ footer: (
0 ? 'info-light' : 'outline'} size="sm">
очередь и выполнение
@@ -63,7 +63,7 @@ function ScheduleComponent() {
value: failed,
icon: ,
variant: failed > 0 ? 'warning' : 'default',
- badge: (
+ footer: (
0 ? 'warning-light' : 'success-light'} size="sm">
{failed > 0 ? 'требуют внимания' : 'без ошибок'}
@@ -108,11 +108,11 @@ function ScheduleComponent() {
}
/>
- {loading ? : }
+ {loading ? : }
-
@@ -134,7 +134,7 @@ function ScheduleComponent() {
/>
)}
-
+
-
@@ -356,7 +356,7 @@ function TenantSettingsComponent() {
/>
)}
-
+