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>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-18 16:15:36 +02:00
1 parent d86cba61e3
commit 4674203502
12 files changed
+583 -5

No files matched your search

+9 -1
View File
@@ -171,6 +171,14 @@
- demo repo seeds three varied events so the dropdown has content on first open
- verified live: feed returns seeds newest-first, public booking create lands `booking.created` with entity id, customer cancel lands `booking.cancelled`; browser-checked both locales against the demo dashboard, console clean
- remaining: live staging validation on Neon + pgx repository path; settings/CRUD edits can join the feed when a concrete need appears
- `[done/partial]` Milestone 25: CSV export
- `GET /v1/export/bookings.csv` + `GET /v1/export/customers.csv` — authed, tenant-scoped, `Content-Disposition: attachment`; bookings accept optional `from`/`to` (YYYY-MM-DD, `to` inclusive)
- UTF-8 BOM prefix so Czech diacritics open correctly in Excel; stdlib `encoding/csv` handles all quoting/escaping — no dependency
- service labels resolve at export time (service name for appointments, session title for classes, staff and location names via lookup maps — no N+1); customers export pages through the repository in 500-row chunks
- responses buffer in memory before headers — membership failures return clean JSON errors instead of a truncated stream
- shared `ExportCsvButton` (fetch + blob + object-URL download; the generated client is JSON-typed so it can't carry file responses) on the bookings and customers page headers, CS/EN label
- verified live: real booking row with comma-in-name and quotes-in-notes escaped correctly, diacritics preserved, range bounds honored (inclusive `to`), three seeded customers exported; browser download produced the real file on the bookings page, customers button present, console clean
- remaining: live staging validation on Neon + pgx repository path
## Overview
Bookra is a Czech-first booking SaaS for small local service businesses. It targets salons, massage therapists, small clinics, repair shops, and small studios/clubs that need a simple public booking flow, internal schedule management, reminders, and basic business reporting without becoming a full ERP.
@@ -425,7 +433,7 @@ Shortlist of worthwhile directions once v1 stabilizes — kept deliberately smal
- **Booking notes intake**: structured intake fields per service (e.g. "first visit?", "injuries?") instead of the single free-text notes field — still schema-light.
- **Persistent notification center**: M24 put real activity events in the dropdown, but read state is still in-memory and there's no delivery history; a `notifications` table (or read-cursor on `activity_events`) would persist both.
- **Staff performance view**: bookings/completion/utilization per staff member — the metrics primitives already exist; this is a filtered presentation layer.
- **CSV export**: bookings and customers export for accounting/backup; cheap to build, frequently requested by small businesses.
- **CSV export**: bookings and customers export for accounting/backup — landed in M25; a report-style export (per-staff utilization, revenue summaries) can follow once metrics stabilize.
## Product Principles
- calm, premium, structured UX