feat(web): enhance certificate management and UI components
Build, Test, and Push CFDM Docker Image / test (push) Successful in 4m0s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 2m17s
Build, Test, and Push CFDM Docker Image / update-wiki (push) Successful in 6s
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped

- Updated the CertificatesPage to include a new CertKpiCards component for improved KPI visualization.
- Refactored filter handling and state management for better user experience.
- Introduced new status options and enhanced filtering capabilities in certificates columns.
- Updated the OpsDashboard to utilize KpiStatGrid for displaying key metrics.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-07-16 22:53:01 +07:00
co-authored by Cursor
parent d44baab8d4
commit 3e326a4509
10 changed files with 436 additions and 116 deletions
@@ -2,15 +2,17 @@ import { useMemo } from 'react'
import type { ColumnDef } from '@tanstack/react-table'
import { GlobeIcon, SearchIcon } from 'lucide-react'
import { Badge } from '@/components/reui/badge'
import { DataGridColumnHeader } from '@/components/reui/data-grid/data-grid-column-header'
import { createFilter, type FilterFieldConfig } from '@/components/reui/filters'
import { StatusBadge } from '@/components/status-badge'
import { renderSingleSelectedLabel } from '@/components/reui-kit/filter-utils'
import type { Certificate } from '@/lib/schemas'
import { formatDate, formatRelative } from '@/lib/format'
export const CERT_TABS = [
{ id: 'all', label: 'Все' },
{ id: 'active', label: 'Активен' },
{ id: 'active', label: 'OK' },
{ id: 'warning', label: 'Предупреждение' },
{ id: 'expired', label: 'Истёк' },
] as const
@@ -18,6 +20,17 @@ export const CERT_TABS = [
const ACTIVE_STATUSES = new Set(['active', 'ok', 'synced'])
const EXPIRED_STATUSES = new Set(['expired', 'error', 'conflict'])
const CERT_STATUS_OPTIONS = [
{ value: 'ok', label: 'OK' },
{ value: 'active', label: 'Активен' },
{ value: 'synced', label: 'Синхронизировано' },
{ value: 'warning', label: 'Предупреждение' },
{ value: 'expired', label: 'Истёк' },
{ value: 'error', label: 'Ошибка' },
{ value: 'conflict', label: 'Конфликт' },
{ value: 'unknown', label: 'Неизвестно' },
]
export function certTabFilter(item: Certificate, tabId: string) {
if (tabId === 'active') return ACTIVE_STATUSES.has(item.status)
if (tabId === 'warning') return item.status === 'warning'
@@ -26,7 +39,10 @@ export function certTabFilter(item: Certificate, tabId: string) {
}
export function createDefaultCertFilters() {
return [createFilter('hostname', 'contains', [''])]
return [
createFilter('hostname', 'contains', ['']),
createFilter('status', 'is', ['']),
]
}
export function useCertFilterFields() {
@@ -40,16 +56,47 @@ export function useCertFilterFields() {
className: 'w-52',
placeholder: 'Поиск по хосту…',
},
{
key: 'status',
label: 'Статус',
type: 'select',
searchable: true,
className: 'w-[168px]',
options: CERT_STATUS_OPTIONS,
customValueRenderer: (values) =>
renderSingleSelectedLabel(values, CERT_STATUS_OPTIONS),
},
],
[],
)
}
export function certFilterFieldValue(item: Certificate, field: string) {
if (field === 'hostname') {
return `${item.hostname} ${item.status}`.toLowerCase()
switch (field) {
case 'hostname':
return `${item.hostname} ${item.status}`.toLowerCase()
case 'status':
return item.status
default:
return ''
}
return ''
}
function certRelativeBadge(status: string, expiresAt: string | null) {
const relative = formatRelative(expiresAt)
if (!expiresAt) {
return <span className="text-muted-foreground tabular-nums"></span>
}
if (EXPIRED_STATUSES.has(status)) {
return <Badge variant="destructive-light">{relative}</Badge>
}
if (status === 'warning') {
return <Badge variant="warning-light">{relative}</Badge>
}
if (ACTIVE_STATUSES.has(status)) {
return <Badge variant="success-light">{relative}</Badge>
}
return <span className="text-muted-foreground tabular-nums">{relative}</span>
}
export function useCertificateColumns() {
@@ -85,11 +132,8 @@ export function useCertificateColumns() {
{
id: 'relative',
header: 'Срок',
cell: ({ row }) => (
<span className="text-muted-foreground tabular-nums">
{formatRelative(row.original.expires_at)}
</span>
),
cell: ({ row }) =>
certRelativeBadge(row.original.status, row.original.expires_at),
},
{
id: 'last_checked_at',