"use client"; import { useEffect, useState } from "react"; import { useTranslations } from "next-intl"; import { Button } from "@/shared/components"; function relativeTime(ts: number): string { const diffMs = Date.now() - ts * 1000; const diffSec = Math.round(diffMs / 1000); if (diffSec < 60) return `${diffSec}s ago`; const diffMin = Math.round(diffSec / 60); if (diffMin < 60) return `${diffMin}m ago`; const diffHr = Math.round(diffMin / 60); if (diffHr < 24) return `${diffHr}h ago`; const diffDays = Math.round(diffHr / 24); return `${diffDays}d ago`; } function relativeExpiration(ts: number | null): string { if (!ts) return "Never"; const diffMs = ts * 1000 - Date.now(); if (diffMs <= 0) return "Expired"; const diffSec = Math.round(diffMs / 1000); if (diffSec < 60) return `${diffSec}s`; const diffMin = Math.round(diffSec / 60); if (diffMin < 60) return `${diffMin}m`; const diffHr = Math.round(diffMin / 60); if (diffHr < 24) return `${diffHr}h`; const diffDays = Math.round(diffHr / 24); return `${diffDays}d`; } function formatBytes(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / 1024 / 1024).toFixed(2)} MB`; } interface FileRecord { id: string; filename: string; bytes: number; purpose: string; createdAt: number; expiresAt?: number | null; } interface BatchRecord { id: string; endpoint: string; status: string; inputFileId: string; outputFileId?: string | null; errorFileId?: string | null; model?: string | null; } interface FileDetailModalProps { file: FileRecord; contents: string | null; loading: boolean; batches?: BatchRecord[]; onClose: () => void; } export default function FileDetailModal({ file, contents, loading, batches, onClose, }: Readonly) { const t = useTranslations("common"); const [copied, setCopied] = useState(false); const relatedBatches = (batches ?? []).filter( (b) => b.inputFileId === file.id || b.outputFileId === file.id || b.errorFileId === file.id ); useEffect(() => { const handler = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", handler); return () => document.removeEventListener("keydown", handler); }, [onClose]); const handleDownload = () => { const a = document.createElement("a"); a.href = `/api/v1/files/${file.id}/content`; a.download = file.filename; document.body.appendChild(a); a.click(); a.remove(); }; const handleCopy = () => { if (contents) { navigator.clipboard.writeText(contents); setCopied(true); setTimeout(() => setCopied(false), 2000); } }; const createdAtTs = file.createdAt; const expiresAtTs = file.expiresAt; const lineCount = contents ? contents.split("\n").filter((l) => l.trim()).length : 0; const isTruncated = lineCount > 1000; const displayedLines = contents ? contents .split("\n") .filter((l) => l.trim()) .slice(0, 1000) : []; return (
{/* Overlay */}