diff --git a/frontend/src/productsApi.export.test.ts b/frontend/src/productsApi.export.test.ts new file mode 100644 index 0000000..28c936e --- /dev/null +++ b/frontend/src/productsApi.export.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, it } from "vitest"; +import { EXPORT_STATUSES, exportUrl } from "./productsApi"; + +describe("export url", () => { + it("lists the four status filters with 'all' first", () => { + expect(EXPORT_STATUSES.map((s) => s.value)).toEqual(["all", "active", "draft", "archived"]); + }); + it("builds the endpoint url with the status query", () => { + expect(exportUrl("all")).toBe("/api/products/export?status=all"); + expect(exportUrl("archived")).toBe("/api/products/export?status=archived"); + }); +}); diff --git a/frontend/src/productsApi.ts b/frontend/src/productsApi.ts index ae039fe..981f5c4 100644 --- a/frontend/src/productsApi.ts +++ b/frontend/src/productsApi.ts @@ -47,6 +47,22 @@ export function dialectLabel(d: string): string { return d === "canonical" ? "Canonical format" : d; } +export type ExportStatus = "all" | "active" | "draft" | "archived"; + +// PUC-9 status filter, 'all' first (the default). Labels drive the export menu. +export const EXPORT_STATUSES: { value: ExportStatus; label: string }[] = [ + { value: "all", label: "All products" }, + { value: "active", label: "Active" }, + { value: "draft", label: "Draft" }, + { value: "archived", label: "Archived" }, +]; + +// The export is a browser download (native save dialog + streaming), not a fetch +// wrapper — so the API module contributes a URL builder, not a request(). +export function exportUrl(status: ExportStatus): string { + return `/api/products/export?status=${status}`; +} + async function request(path: string, init?: RequestInit): Promise> { const resp = await fetch(path, { credentials: "include", ...init }); if (!resp.ok) return { ok: false, error: await errorOf(resp), status: resp.status };