Spaces:
Sleeping
Sleeping
| "use client"; | |
| import { useState } from "react"; | |
| import { Check, Copy, FileJson, FileSpreadsheet, FileText } from "lucide-react"; | |
| import { api } from "@/lib/api"; | |
| import type { DocumentDetail } from "@/lib/types"; | |
| const FORMATS = [ | |
| { | |
| id: "json", | |
| label: "JSON", | |
| desc: "Full structured payload — fields, tables, classification, anomalies.", | |
| icon: FileJson, | |
| tint: "from-amber-500/20 to-amber-600/5 text-amber-300 ring-amber-500/20", | |
| }, | |
| { | |
| id: "csv", | |
| label: "CSV", | |
| desc: "Flat field / value / confidence table for spreadsheets.", | |
| icon: FileText, | |
| tint: "from-emerald-500/20 to-emerald-600/5 text-emerald-300 ring-emerald-500/20", | |
| }, | |
| { | |
| id: "xlsx", | |
| label: "Excel", | |
| desc: "Multi-sheet workbook with fields and extracted tables.", | |
| icon: FileSpreadsheet, | |
| tint: "from-green-500/20 to-green-600/5 text-green-300 ring-green-500/20", | |
| }, | |
| ]; | |
| export default function ExportPanel({ detail }: { detail: DocumentDetail }) { | |
| const ready = detail.status === "ready"; | |
| const [copied, setCopied] = useState(false); | |
| const ex = detail.extraction; | |
| const previewObj: Record<string, unknown> = { | |
| doc_type: detail.classification?.doc_type, | |
| confidence: detail.classification?.confidence, | |
| }; | |
| if (ex && ex.fields.length > 0) { | |
| previewObj.fields = Object.fromEntries(ex.fields.map((f) => [f.name, f.value])); | |
| } | |
| if (ex && ex.records?.length > 0) { | |
| previewObj.record_type = ex.record_type; | |
| previewObj.records = ex.records.slice(0, 5).map((r) => | |
| Object.fromEntries(r.fields.map((f) => [f.name, f.value])), | |
| ); | |
| if (ex.records.length > 5) previewObj["…"] = `${ex.records.length - 5} more records`; | |
| } | |
| const preview = JSON.stringify(previewObj, null, 2); | |
| return ( | |
| <div className="h-full overflow-y-auto p-5"> | |
| <h3 className="mb-1 text-sm font-semibold text-white">Export extracted data</h3> | |
| <p className="mb-5 text-[11px] text-white/40"> | |
| Download structured output for downstream systems — ERP, accounting, CRM. | |
| </p> | |
| <div className="grid gap-3 sm:grid-cols-3"> | |
| {FORMATS.map((f) => { | |
| const Icon = f.icon; | |
| return ( | |
| <a | |
| key={f.id} | |
| href={ready ? api.exportUrl(detail.id, f.id) : undefined} | |
| onClick={(e) => !ready && e.preventDefault()} | |
| className={`card group flex flex-col gap-3 rounded-xl p-4 transition ${ | |
| ready | |
| ? "hover:-translate-y-0.5 hover:ring-brand-500/30" | |
| : "cursor-not-allowed opacity-50" | |
| }`} | |
| > | |
| <div | |
| className={`flex h-10 w-10 items-center justify-center rounded-lg bg-gradient-to-br ring-1 ${f.tint}`} | |
| > | |
| <Icon size={18} /> | |
| </div> | |
| <div> | |
| <p className="text-sm font-semibold text-white">{f.label}</p> | |
| <p className="mt-0.5 text-[11px] leading-snug text-white/45">{f.desc}</p> | |
| </div> | |
| <span className="mt-auto text-xs font-medium text-brand-300 opacity-0 transition group-hover:opacity-100"> | |
| Download → | |
| </span> | |
| </a> | |
| ); | |
| })} | |
| </div> | |
| {!ready && ( | |
| <p className="mt-4 text-center text-[11px] text-amber-300/80"> | |
| Export becomes available once processing completes. | |
| </p> | |
| )} | |
| {detail.extraction && ( | |
| <div className="mt-6"> | |
| <div className="mb-2 flex items-center justify-between"> | |
| <p className="text-[11px] font-semibold uppercase tracking-wider text-white/40"> | |
| Preview | |
| </p> | |
| <button | |
| onClick={() => { | |
| navigator.clipboard.writeText(preview); | |
| setCopied(true); | |
| setTimeout(() => setCopied(false), 1200); | |
| }} | |
| className="flex items-center gap-1 text-[11px] text-white/45 transition hover:text-white" | |
| > | |
| {copied ? <Check size={11} className="text-emerald-400" /> : <Copy size={11} />} | |
| Copy JSON | |
| </button> | |
| </div> | |
| <pre className="card max-h-72 overflow-auto rounded-xl p-4 text-[11px] leading-relaxed text-white/70"> | |
| {preview} | |
| </pre> | |
| </div> | |
| )} | |
| </div> | |
| ); | |
| } | |