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
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:
@@ -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
|
||||
@@ -17,22 +17,11 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from '@cfdm/ui/components/dropdown-menu'
|
||||
import { AppButton } from '@/components/app-button'
|
||||
|
||||
type Density = 'compact' | 'comfortable' | 'spacious'
|
||||
|
||||
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',
|
||||
}
|
||||
|
||||
const densityLabel: Record<Density, string> = {
|
||||
compact: 'Компактная',
|
||||
comfortable: 'Обычная',
|
||||
spacious: 'Просторная',
|
||||
}
|
||||
|
||||
const pageSizeOptions = [10, 20, 50] as const
|
||||
import {
|
||||
densityLabel,
|
||||
pageSizeOptions,
|
||||
type Density,
|
||||
} from '@/components/data-table-density'
|
||||
|
||||
interface DataTableToolbarProps<TData> {
|
||||
table: Table<TData>
|
||||
@@ -137,6 +126,3 @@ export function DataTableToolbar<TData>({
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export type { Density }
|
||||
export { densityRowClass }
|
||||
|
||||
@@ -17,11 +17,8 @@ import {
|
||||
} from '@cfdm/ui/components/table'
|
||||
import { cn } from '@cfdm/ui/lib/utils'
|
||||
import { DataTablePagination } from '@/components/data-table-pagination'
|
||||
import {
|
||||
DataTableToolbar,
|
||||
densityRowClass,
|
||||
type Density,
|
||||
} from '@/components/data-table-toolbar'
|
||||
import { DataTableToolbar } from '@/components/data-table-toolbar'
|
||||
import { densityRowClass, type Density } from '@/components/data-table-density'
|
||||
|
||||
interface DataTableViewProps<TData> {
|
||||
table: TableType<TData>
|
||||
|
||||
@@ -16,7 +16,7 @@ import type { DomainListItem } from '@/lib/schemas'
|
||||
import { AppBadge } from '@/components/app-badge'
|
||||
import { AppButton } from '@/components/app-button'
|
||||
import { DataTableView } from '@/components/data-table-view'
|
||||
import type { Density } from '@/components/data-table-toolbar'
|
||||
import type { Density } from '@/components/data-table-density'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useMemo, useState } from 'react'
|
||||
import { ShieldCheckIcon } from 'lucide-react'
|
||||
import type { Certificate } from '@/lib/schemas'
|
||||
import { formatRelative } from '@/lib/format'
|
||||
@@ -20,25 +21,43 @@ import {
|
||||
import { ScrollArea } from '@cfdm/ui/components/scroll-area'
|
||||
|
||||
const WARN_DAYS = 14
|
||||
const DAY_MS = 1000 * 60 * 60 * 24
|
||||
|
||||
interface ExpiringCertsCardProps {
|
||||
certificates: Certificate[]
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export function ExpiringCertsCard({ certificates, limit = 6 }: ExpiringCertsCardProps) {
|
||||
const now = Date.now()
|
||||
const upcoming = certificates
|
||||
interface UpcomingEntry {
|
||||
cert: Certificate
|
||||
ts: number
|
||||
expired: boolean
|
||||
}
|
||||
|
||||
function selectUpcoming(
|
||||
certificates: Certificate[],
|
||||
limit: number,
|
||||
now: number,
|
||||
): UpcomingEntry[] {
|
||||
return certificates
|
||||
.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)
|
||||
.sort((a, b) => a.ts - b.ts)
|
||||
.filter((entry) => {
|
||||
const days = (entry.ts - now) / (1000 * 60 * 60 * 24)
|
||||
return days <= WARN_DAYS
|
||||
})
|
||||
.filter((entry) => (entry.ts - now) / DAY_MS <= WARN_DAYS)
|
||||
.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 (
|
||||
<AppCard>
|
||||
@@ -58,26 +77,21 @@ export function ExpiringCertsCard({ certificates, limit = 6 }: ExpiringCertsCard
|
||||
) : (
|
||||
<ScrollArea className="h-72">
|
||||
<AppItemGroup className="gap-2 pr-3">
|
||||
{upcoming.map((cert) => {
|
||||
const expired = new Date(cert.expires_at as string).getTime() < now
|
||||
return (
|
||||
<AppItem key={cert.id} variant="outline" size="sm">
|
||||
<AppItemContent className="flex flex-row items-center justify-between gap-3">
|
||||
<div className="flex min-w-0 flex-col gap-0.5">
|
||||
<AppItemTitle className="truncate font-medium">
|
||||
{cert.hostname}
|
||||
</AppItemTitle>
|
||||
<AppItemDescription className="tabular-nums">
|
||||
{formatRelative(cert.expires_at)}
|
||||
</AppItemDescription>
|
||||
</div>
|
||||
<StatusBadge
|
||||
status={expired ? 'expired' : cert.status}
|
||||
/>
|
||||
</AppItemContent>
|
||||
</AppItem>
|
||||
)
|
||||
})}
|
||||
{upcoming.map((entry) => (
|
||||
<AppItem key={entry.cert.id} variant="outline" size="sm">
|
||||
<AppItemContent className="flex flex-row items-center justify-between gap-3">
|
||||
<div className="flex min-w-0 flex-col gap-0.5">
|
||||
<AppItemTitle className="truncate font-medium">
|
||||
{entry.cert.hostname}
|
||||
</AppItemTitle>
|
||||
<AppItemDescription className="tabular-nums">
|
||||
{formatRelative(entry.cert.expires_at)}
|
||||
</AppItemDescription>
|
||||
</div>
|
||||
<StatusBadge status={entry.expired ? 'expired' : entry.cert.status} />
|
||||
</AppItemContent>
|
||||
</AppItem>
|
||||
))}
|
||||
</AppItemGroup>
|
||||
</ScrollArea>
|
||||
)}
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { MoreHorizontalIcon } from 'lucide-react'
|
||||
import { ConfirmDialog } from '@/components/confirm-dialog'
|
||||
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 { formatSubdomainServiceLinks } from '@/hooks/use-domain-page'
|
||||
import { formatDate } from '@/lib/format'
|
||||
|
||||
@@ -24,7 +24,7 @@ import { EmptyState } from '@/components/empty-state'
|
||||
import { StatusBadge } from '@/components/status-badge'
|
||||
import { DataTableCard } from '@/components/data-table-card'
|
||||
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 { AppButton } from '@/components/app-button'
|
||||
import {
|
||||
|
||||
Reference in New Issue
Block a user