Files
Bookra/apps/frontend/src/components/dashboard/export-csv-button.tsx
T
Tomas DvorakandDevin 4674203502 feat(export): CSV export for bookings and customers
GET /v1/export/bookings.csv (optional from/to bounds) and
/v1/export/customers.csv — UTF-8 with BOM for Excel, labels resolved
at export time, buffered responses so auth failures stay JSON.
Export buttons on the bookings and customers pages.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-18 16:15:36 +02:00

39 lines
1.3 KiB
TypeScript

import { createSignal, Accessor } from "solid-js";
import { apiBaseUrl } from "../../lib/api-client";
import { useI18n } from "../../providers/i18n-provider";
import { DownloadIcon } from "./icons";
// Downloads an authenticated CSV endpoint — fetch + blob because the
// generated api-client is typed for JSON, not file responses.
export function ExportCsvButton(props: { path: string; filename: string; token: Accessor<string | null | undefined> }) {
const i18n = useI18n();
const [busy, setBusy] = createSignal(false);
const handleClick = async () => {
const bearer = props.token?.();
if (!bearer || busy()) return;
setBusy(true);
try {
const response = await fetch(`${apiBaseUrl}${props.path}`, {
headers: { Authorization: `Bearer ${bearer}` },
});
if (!response.ok) return;
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const anchor = document.createElement("a");
anchor.href = url;
anchor.download = props.filename;
anchor.click();
URL.revokeObjectURL(url);
} finally {
setBusy(false);
}
};
return (
<button onClick={handleClick} disabled={busy()} class="btn-secondary text-sm shrink-0">
<DownloadIcon /> {i18n.t("dashboard.exportCsv")}
</button>
);
}