refactor: Consolidate density-related logic by moving densityRowClass and densityLabel to a dedicated data-table-density component, improving code organization and reusability across data table components
Build, Test, and Push CFDM Docker Image / test (push) Successful in 4m12s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 2m1s
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

This commit is contained in:
Denozordec
2026-06-25 17:27:39 +07:00
parent 4e536c3b7e
commit 063761539c
7 changed files with 68 additions and 56 deletions
@@ -0,0 +1,15 @@
export type Density = 'compact' | 'comfortable' | 'spacious'
export const densityRowClass: Record<Density, string> = {
compact: '[&_tr]:h-9 [&_tr_td]:py-1 [&_tr_th]:py-1 text-xs',
comfortable: '[&_tr]:h-10 [&_tr_td]:py-2 [&_tr_th]:py-2 text-sm',
spacious: '[&_tr]:h-12 [&_tr_td]:py-3 [&_tr_th]:py-3 text-sm',
}
export const densityLabel: Record<Density, string> = {
compact: 'Компактная',
comfortable: 'Обычная',
spacious: 'Просторная',
}
export const pageSizeOptions = [10, 20, 50] as const
+5 -19
View File
@@ -17,22 +17,11 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
} from '@cfdm/ui/components/dropdown-menu' } from '@cfdm/ui/components/dropdown-menu'
import { AppButton } from '@/components/app-button' import { AppButton } from '@/components/app-button'
import {
type Density = 'compact' | 'comfortable' | 'spacious' densityLabel,
pageSizeOptions,
const densityRowClass: Record<Density, string> = { type Density,
compact: '[&_tr]:h-9 [&_tr_td]:py-1 [&_tr_th]:py-1 text-xs', } from '@/components/data-table-density'
comfortable: '[&_tr]:h-10 [&_tr_td]:py-2 [&_tr_th]:py-2 text-sm',
spacious: '[&_tr]:h-12 [&_tr_td]:py-3 [&_tr_th]:py-3 text-sm',
}
const densityLabel: Record<Density, string> = {
compact: 'Компактная',
comfortable: 'Обычная',
spacious: 'Просторная',
}
const pageSizeOptions = [10, 20, 50] as const
interface DataTableToolbarProps<TData> { interface DataTableToolbarProps<TData> {
table: Table<TData> table: Table<TData>
@@ -137,6 +126,3 @@ export function DataTableToolbar<TData>({
</div> </div>
) )
} }
export type { Density }
export { densityRowClass }
+2 -5
View File
@@ -17,11 +17,8 @@ import {
} from '@cfdm/ui/components/table' } from '@cfdm/ui/components/table'
import { cn } from '@cfdm/ui/lib/utils' import { cn } from '@cfdm/ui/lib/utils'
import { DataTablePagination } from '@/components/data-table-pagination' import { DataTablePagination } from '@/components/data-table-pagination'
import { import { DataTableToolbar } from '@/components/data-table-toolbar'
DataTableToolbar, import { densityRowClass, type Density } from '@/components/data-table-density'
densityRowClass,
type Density,
} from '@/components/data-table-toolbar'
interface DataTableViewProps<TData> { interface DataTableViewProps<TData> {
table: TableType<TData> table: TableType<TData>
@@ -16,7 +16,7 @@ import type { DomainListItem } from '@/lib/schemas'
import { AppBadge } from '@/components/app-badge' import { AppBadge } from '@/components/app-badge'
import { AppButton } from '@/components/app-button' import { AppButton } from '@/components/app-button'
import { DataTableView } from '@/components/data-table-view' import { DataTableView } from '@/components/data-table-view'
import type { Density } from '@/components/data-table-toolbar' import type { Density } from '@/components/data-table-density'
import { import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
+43 -29
View File
@@ -1,3 +1,4 @@
import { useMemo, useState } from 'react'
import { ShieldCheckIcon } from 'lucide-react' import { ShieldCheckIcon } from 'lucide-react'
import type { Certificate } from '@/lib/schemas' import type { Certificate } from '@/lib/schemas'
import { formatRelative } from '@/lib/format' import { formatRelative } from '@/lib/format'
@@ -20,25 +21,43 @@ import {
import { ScrollArea } from '@cfdm/ui/components/scroll-area' import { ScrollArea } from '@cfdm/ui/components/scroll-area'
const WARN_DAYS = 14 const WARN_DAYS = 14
const DAY_MS = 1000 * 60 * 60 * 24
interface ExpiringCertsCardProps { interface ExpiringCertsCardProps {
certificates: Certificate[] certificates: Certificate[]
limit?: number limit?: number
} }
export function ExpiringCertsCard({ certificates, limit = 6 }: ExpiringCertsCardProps) { interface UpcomingEntry {
const now = Date.now() cert: Certificate
const upcoming = certificates ts: number
expired: boolean
}
function selectUpcoming(
certificates: Certificate[],
limit: number,
now: number,
): UpcomingEntry[] {
return certificates
.filter((c) => c.expires_at) .filter((c) => c.expires_at)
.map((c) => ({ cert: c, ts: new Date(c.expires_at as string).getTime() })) .map((c) => {
const ts = new Date(c.expires_at as string).getTime()
return { cert: c, ts, expired: ts < now }
})
.filter((entry) => Number.isNaN(entry.ts) === false) .filter((entry) => Number.isNaN(entry.ts) === false)
.sort((a, b) => a.ts - b.ts) .sort((a, b) => a.ts - b.ts)
.filter((entry) => { .filter((entry) => (entry.ts - now) / DAY_MS <= WARN_DAYS)
const days = (entry.ts - now) / (1000 * 60 * 60 * 24)
return days <= WARN_DAYS
})
.slice(0, limit) .slice(0, limit)
.map((entry) => entry.cert) }
export function ExpiringCertsCard({ certificates, limit = 6 }: ExpiringCertsCardProps) {
const [now] = useState(() => Date.now())
const upcoming = useMemo(
() => selectUpcoming(certificates, limit, now),
[certificates, limit, now],
)
return ( return (
<AppCard> <AppCard>
@@ -58,26 +77,21 @@ export function ExpiringCertsCard({ certificates, limit = 6 }: ExpiringCertsCard
) : ( ) : (
<ScrollArea className="h-72"> <ScrollArea className="h-72">
<AppItemGroup className="gap-2 pr-3"> <AppItemGroup className="gap-2 pr-3">
{upcoming.map((cert) => { {upcoming.map((entry) => (
const expired = new Date(cert.expires_at as string).getTime() < now <AppItem key={entry.cert.id} variant="outline" size="sm">
return ( <AppItemContent className="flex flex-row items-center justify-between gap-3">
<AppItem key={cert.id} variant="outline" size="sm"> <div className="flex min-w-0 flex-col gap-0.5">
<AppItemContent className="flex flex-row items-center justify-between gap-3"> <AppItemTitle className="truncate font-medium">
<div className="flex min-w-0 flex-col gap-0.5"> {entry.cert.hostname}
<AppItemTitle className="truncate font-medium"> </AppItemTitle>
{cert.hostname} <AppItemDescription className="tabular-nums">
</AppItemTitle> {formatRelative(entry.cert.expires_at)}
<AppItemDescription className="tabular-nums"> </AppItemDescription>
{formatRelative(cert.expires_at)} </div>
</AppItemDescription> <StatusBadge status={entry.expired ? 'expired' : entry.cert.status} />
</div> </AppItemContent>
<StatusBadge </AppItem>
status={expired ? 'expired' : cert.status} ))}
/>
</AppItemContent>
</AppItem>
)
})}
</AppItemGroup> </AppItemGroup>
</ScrollArea> </ScrollArea>
)} )}
+1 -1
View File
@@ -12,7 +12,7 @@ import {
import { MoreHorizontalIcon } from 'lucide-react' import { MoreHorizontalIcon } from 'lucide-react'
import { ConfirmDialog } from '@/components/confirm-dialog' import { ConfirmDialog } from '@/components/confirm-dialog'
import { DataTableView } from '@/components/data-table-view' import { DataTableView } from '@/components/data-table-view'
import type { Density } from '@/components/data-table-toolbar' import type { Density } from '@/components/data-table-density'
import type { SubdomainTableRow } from '@/hooks/use-domain-page' import type { SubdomainTableRow } from '@/hooks/use-domain-page'
import { formatSubdomainServiceLinks } from '@/hooks/use-domain-page' import { formatSubdomainServiceLinks } from '@/hooks/use-domain-page'
import { formatDate } from '@/lib/format' import { formatDate } from '@/lib/format'
+1 -1
View File
@@ -24,7 +24,7 @@ import { EmptyState } from '@/components/empty-state'
import { StatusBadge } from '@/components/status-badge' import { StatusBadge } from '@/components/status-badge'
import { DataTableCard } from '@/components/data-table-card' import { DataTableCard } from '@/components/data-table-card'
import { DataTableView } from '@/components/data-table-view' import { DataTableView } from '@/components/data-table-view'
import type { Density } from '@/components/data-table-toolbar' import type { Density } from '@/components/data-table-density'
import { ChartCard } from '@/components/chart-card' import { ChartCard } from '@/components/chart-card'
import { AppButton } from '@/components/app-button' import { AppButton } from '@/components/app-button'
import { import {