feat: add CSV import/export for gear collection
Some checks failed
CI / ci (pull_request) Failing after 22s
CI / e2e (pull_request) Has been skipped

Adds export (GET /api/items/export) and import (POST /api/items/import) routes
backed by a pure csv.service with no external deps, plus useExportItems/useImportItems
hooks and an Import/Export section in the Settings page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-03 18:12:07 +02:00
parent 8c1fe47a99
commit 15f146ee89
6 changed files with 645 additions and 2 deletions

View File

@@ -144,4 +144,64 @@ describe("Item Routes", () => {
});
expect(res.status).toBe(404);
});
it("GET /api/items/export returns CSV with correct content-type", async () => {
// Create an item first
await app.request("/api/items", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: "Tent",
weightGrams: 1200,
priceCents: 35000,
categoryId: 1,
}),
});
const res = await app.request("/api/items/export");
expect(res.status).toBe(200);
expect(res.headers.get("Content-Type")).toContain("text/csv");
const text = await res.text();
const lines = text.split("\n");
expect(lines[0]).toBe(
"name,quantity,weightGrams,priceCents,category,notes,productUrl",
);
expect(lines.length).toBeGreaterThanOrEqual(2);
expect(lines[1]).toContain("Tent");
});
it("POST /api/items/import with CSV file creates items", async () => {
const csvContent = [
"name,quantity,weightGrams,priceCents,category,notes,productUrl",
"Sleeping Bag,1,800,25000,Camping,,",
].join("\n");
const formData = new FormData();
formData.append(
"file",
new Blob([csvContent], { type: "text/csv" }),
"import.csv",
);
const res = await app.request("/api/items/import", {
method: "POST",
body: formData,
});
expect(res.status).toBe(200);
const body = await res.json();
expect(body.imported).toBe(1);
expect(body.errors).toHaveLength(0);
});
it("POST /api/items/import with no file returns 400", async () => {
const res = await app.request("/api/items/import", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({}),
});
expect(res.status).toBe(400);
});
});