import { useMemo, useState } from 'react' import { cn } from '@cfdm/ui/lib/utils' import { Button } from '@cfdm/ui/components/button' import { Badge } from '@cfdm/ui/components/badge' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@cfdm/ui/components/tooltip' import { ConfirmDialog } from '@/components/confirm-dialog' import { DataGridCard, columnDefFromDataGrid, } from '@/components/data-grid-card' import type { DataGridColumn } from '@/components/data-grid-types' import { ListFiltersBar } from '@/components/list-filters-bar' import { StatusBadge } from '@/components/status-badge' import type { DnsRecord, IpHealthStatus } from '@/lib/schemas' import { formatDate } from '@/lib/format' interface DnsRecordsDataGridProps { records: DnsRecord[] onDelete: (recordId: number) => void isDeleting?: boolean healthByIp?: Record search?: string onSearchChange?: (value: string) => void hideSearch?: boolean } const healthDotClass: Record = { up: 'bg-success', degraded: 'bg-warning', down: 'bg-destructive', unknown: 'bg-muted-foreground/40', } const healthLabel: Record = { up: 'OK', degraded: 'Деград.', down: 'Down', unknown: '—', } function IpHealthDot({ health }: { health: IpHealthStatus }) { const tooltipParts: string[] = [`Статус: ${healthLabel[health.status]}`] if (health.latency_ms != null) tooltipParts.push(`Задержка: ${health.latency_ms} мс`) if (health.last_checked_at) tooltipParts.push(`Проверка: ${formatDate(health.last_checked_at)}`) if (health.last_error) tooltipParts.push(`Ошибка: ${health.last_error}`) return ( } /> {tooltipParts.join('\n')} ) } export function DnsRecordsDataGrid({ records, onDelete, isDeleting = false, healthByIp, search: controlledSearch, onSearchChange, hideSearch = false, }: DnsRecordsDataGridProps) { const [internalSearch, setInternalSearch] = useState('') const search = controlledSearch ?? internalSearch const setSearch = onSearchChange ?? setInternalSearch const filteredRecords = useMemo(() => { const query = search.trim().toLowerCase() if (!query) return records return records.filter( (r) => r.name.toLowerCase().includes(query) || r.content.toLowerCase().includes(query) || r.record_type.toLowerCase().includes(query), ) }, [records, search]) const columns = useMemo[]>( () => [ { key: 'record_type', header: 'Тип', sortable: true, sortValue: (row) => row.record_type, cell: (row) => {row.record_type}, }, { key: 'name', header: 'Имя', sortable: true, sortValue: (row) => row.name, cell: (row) => {row.name}, }, { key: 'content', header: 'Значение', sortable: true, sortValue: (row) => row.content, cell: (row) => { const health = healthByIp?.[row.content] return (
{health ? : null} {row.content}
) }, }, { key: 'ttl', header: 'TTL', sortable: true, sortValue: (row) => row.ttl, className: 'tabular-nums', cell: (row) => row.ttl, }, { key: 'sync_status', header: 'Синхронизация', cell: (row) => , }, { key: 'actions', header: '', enableHiding: false, className: 'text-right', cell: (row) => ( Удалить } title="Удалить DNS-запись?" description={`Запись ${row.name} (${row.record_type}) будет удалена из зоны.`} onConfirm={() => onDelete(row.id)} /> ), }, ], [healthByIp, isDeleting, onDelete], ) return ( String(row.id)} emptyTitle="DNS-записи не найдены" emptyDescription="Создайте запись или измените фильтры" pinLastColumn actions={ hideSearch ? undefined : ( setSearch('')} /> ) } /> ) }