Files
cloudflare-domain-manager/apps/web/src/components/cert-kpi-stats.tsx
T
DenozordecandCursor 8b028d04b4
CD / update-wiki (push) Successful in 5s
quality / commitlint (push) Skipped
quality / changes (push) Successful in 9s
quality / api (push) Skipped
quality / docker-check (push) Skipped
quality / web (push) Successful in 52s
CD / quality (push) Successful in 1m3s
CD / publish (push) Successful in 1m38s
feat(web): выровнять палитру и KPI под макет
Светлая тема и сайдбар совпадают с артбордом, показатели собраны в одну рамку с цветом статуса.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-02 21:36:39 +07:00

104 lines
3.0 KiB
TypeScript

import { useMemo } from 'react'
import {
AlertTriangleIcon,
CheckCircle2Icon,
ShieldAlertIcon,
ShieldCheckIcon,
} from 'lucide-react'
import { KpiStatGrid, type KpiStatCard } from '@/components/reui-kit/kpi-stat-grid'
const ACTIVE_STATUSES = new Set(['active', 'ok', 'synced'])
const PROBLEM_STATUSES = new Set(['expired', 'error', 'conflict'])
function countFromSummary(
summary: [string, number][] | undefined,
statuses: Set<string>,
): number {
if (!summary) return 0
return summary
.filter(([status]) => statuses.has(status))
.reduce((sum, [, count]) => sum + count, 0)
}
function totalFromSummary(summary: [string, number][] | undefined): number {
if (!summary) return 0
return summary.reduce((sum, [, count]) => sum + count, 0)
}
interface CertKpiStatsProps {
summary?: [string, number][]
total?: number
activeTab: string
isLoading?: boolean
onSelectTab: (tabId: string) => void
}
/** KPI strip — ReUI PRO [stats-12](https://reui.io/preview/base/stats-12) */
export function CertKpiStats({
summary,
total,
activeTab,
isLoading,
onSelectTab,
}: CertKpiStatsProps) {
const cards = useMemo<KpiStatCard[]>(() => {
const allCount = total ?? totalFromSummary(summary)
const okCount = countFromSummary(summary, ACTIVE_STATUSES)
const warningCount = countFromSummary(summary, new Set(['warning']))
const problemCount = countFromSummary(summary, PROBLEM_STATUSES)
return [
{
id: 'all',
label: 'Всего',
value: allCount,
hint: 'Проверенные хосты',
icon: <ShieldCheckIcon aria-hidden />,
iconClassName: 'text-info',
onSelect: () => onSelectTab('all'),
selected: activeTab === 'all',
},
{
id: 'active',
label: 'OK',
value: okCount,
hint: 'Активные сертификаты',
icon: <CheckCircle2Icon aria-hidden />,
iconClassName: 'text-success',
onSelect: () => onSelectTab('active'),
selected: activeTab === 'active',
},
{
id: 'warning',
label: 'Предупреждение',
value: warningCount,
hint: 'Истекают скоро',
icon: <AlertTriangleIcon aria-hidden />,
iconClassName: 'text-warning',
variant: warningCount > 0 ? 'warning' : 'default',
onSelect: () => onSelectTab('warning'),
selected: activeTab === 'warning',
},
{
id: 'expired',
label: 'Проблемы',
value: problemCount,
hint: 'Ошибка или истёк',
icon: <ShieldAlertIcon aria-hidden />,
iconClassName: 'text-destructive',
variant: problemCount > 0 ? 'destructive' : 'default',
onSelect: () => onSelectTab('expired'),
selected: activeTab === 'expired',
},
]
}, [summary, total, activeTab, onSelectTab])
return (
<KpiStatGrid
cards={cards}
isLoading={isLoading}
skeletonCount={4}
/>
)
}