'use client'; import { useState } from 'react'; import { Badge } from '@/components/ui/badge'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import type { AccessLogItem } from '@/lib/services/openflare'; import { formatDateTime } from '@/lib/utils'; import { formatBytes } from '@/lib/utils/metrics'; import { cacheOutcomeLabel, resolveCacheOutcome } from './access-log-utils'; function DetailField({ label, value, mono, full, }: { label: string; value: React.ReactNode; mono?: boolean; full?: boolean; }) { return (

{label}

{value}
); } function formatRequestTimeMs(value: number | undefined | null) { if (value == null || !Number.isFinite(value) || value < 0) { return '—'; } if (value < 1000) { return `${Math.round(value)} ms`; } return `${(value / 1000).toFixed(2)} s`; } export function AccessLogDetailDialog({ open, item, onOpenChange, }: { open: boolean; item: AccessLogItem | null; onOpenChange: (open: boolean) => void; }) { // Keep last selected item while the dialog closes to avoid empty-state flash. const [displayItem, setDisplayItem] = useState(item); if (item && item !== displayItem) { setDisplayItem(item); } const activeItem = item ?? displayItem; return ( 访问日志详情 本条请求的全部业务字段。IP 访问分析请前往「IP 明细」。 {activeItem ? (
{activeItem.status_code} } /> {cacheOutcomeLabel( resolveCacheOutcome(activeItem.cache_status), )} {activeItem.cache_status || '—'}
} /> ) : null}
); }