import type { ReactNode } from 'react';
import { t } from '@/i18n';
import { cn } from '@/lib/cn';
/** Dense, plain admin primitives: heading row, key/value list, compact table, status pill. */
export function AdminSection({ title, sub, actions, children, className }: { title: string; sub?: ReactNode; actions?: ReactNode; children: ReactNode; className?: string }) {
return (
{title}
{sub ?
{sub}
: null}
{actions ?
{actions}
: null}
{children}
);
}
export function Kv({ rows, className }: { rows: Array<[string, ReactNode]>; className?: string }) {
return (
{rows.map(([k, v]) => (
- {k}
- {v ?? t('admin.na')}
))}
);
}
export interface Col {
key: string;
header: ReactNode;
cell: (row: T) => ReactNode;
numeric?: boolean;
className?: string;
}
/** Compact table with horizontal scroll on narrow screens (admin only — dense by design). */
export function AdminTable({ rows, columns, rowKey, className, empty }: { rows: T[]; columns: Col[]; rowKey: (r: T, i: number) => string; className?: string; empty?: ReactNode }) {
if (rows.length === 0) return {empty ?? t('common.noDataLong')}
;
return (
{columns.map((c) => (
|
{c.header}
|
))}
{rows.map((r, i) => (
{columns.map((c) => (
|
{c.cell(r)}
|
))}
))}
);
}
export function Pill({ tone, children }: { tone: 'ok' | 'warn' | 'bad' | 'muted'; children: ReactNode }) {
return (
{children}
);
}
export function statusTone(s: string | null | undefined): 'ok' | 'warn' | 'bad' | 'muted' {
const v = (s ?? '').toLowerCase();
if (v === 'ok' || v === 'verified') return 'ok';
if (v === 'partial' || v === 'warning' || v === 'stale') return 'warn';
if (v === 'failed' || v === 'quarantined' || v === 'error') return 'bad';
return 'muted';
}
export function bytes(n: number | null | undefined): string {
if (n == null) return t('admin.na');
if (n >= 1e9) return `${(n / 1e9).toFixed(2)} GB`;
if (n >= 1e6) return `${(n / 1e6).toFixed(1)} MB`;
if (n >= 1e3) return `${(n / 1e3).toFixed(0)} kB`;
return `${n} B`;
}
export function ts(iso: string | null | undefined): string {
if (!iso) return t('admin.na');
return iso.replace('T', ' ').replace(/\.\d+/, '').replace(/\+00:00$/, 'Z');
}