Files
bifrost/ui/app/workspace/dashboard/components/exportPopover.tsx
Beyhan Oğur 880f412e2c first commit
2026-04-26 21:52:23 +03:00

88 lines
2.8 KiB
TypeScript

import { Button } from "@/components/ui/button";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdownMenu";
import { buildCSV, downloadCSV } from "@/lib/utils/csv";
import { Download, FileSpreadsheet, FileText, Loader2 } from "lucide-react";
import { useCallback, useState } from "react";
import { type DashboardData, getCSVSections } from "../utils/exportUtils";
const PDF_TAB_LABELS = ["Overview", "Provider Usage", "Model Rankings", "MCP Usage"];
interface ExportPopoverProps {
getData: () => DashboardData;
onPreloadData: () => Promise<void>;
onPdfExport: () => Promise<HTMLElement[]>;
onPdfExportDone: () => void;
}
export function ExportPopover({ getData, onPreloadData, onPdfExport, onPdfExportDone }: ExportPopoverProps) {
const [exporting, setExporting] = useState(false);
const handleCsvExport = useCallback(async () => {
setExporting(true);
try {
await onPreloadData();
const sections = getCSVSections(getData(), "all");
const parts: string[] = [];
for (const section of sections) {
if (section.csv.rows.length === 0) continue;
parts.push(`# ${section.name}`);
parts.push(buildCSV(section.csv.headers, section.csv.rows));
parts.push("");
}
if (parts.length > 0) {
downloadCSV(parts.join("\n"), "dashboard-export");
}
} finally {
setExporting(false);
}
}, [getData, onPreloadData]);
const handlePdfExport = useCallback(async () => {
setExporting(true);
// Yield a frame so the spinner renders before heavy work starts
await new Promise((r) => requestAnimationFrame(r));
try {
const { generatePdf } = await import("@/lib/utils/pdf");
const elements = await onPdfExport();
const sections = elements.map((element, i) => ({
element,
label: PDF_TAB_LABELS[i],
}));
await generatePdf(sections, "dashboard-export", {
branding: {
logoSrc: "/bifrost-logo.webp",
text: "Powered by",
},
});
} finally {
onPdfExportDone();
setExporting(false);
}
}, [onPdfExport, onPdfExportDone]);
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="default" disabled={exporting} data-testid="dashboard-export-trigger">
{exporting ? <Loader2 className="h-4 w-4 animate-spin" /> : <Download className="h-4 w-4" />}
{exporting ? "Exporting..." : "Export"}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={handleCsvExport} data-testid="export-csv-item">
<FileSpreadsheet className="h-4 w-4" />
CSV
</DropdownMenuItem>
<DropdownMenuItem onClick={handlePdfExport} data-testid="export-pdf-item">
<FileText className="h-4 w-4" />
PDF
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}