3.0 KiB
PTV11 Document Capture & Storage — Design Spec
Goal: Allow users to photograph or upload PTV11 documents and associate them with an Erstgespräch (sprechstunde). Multiple documents per Erstgespräch. Stored locally, viewable in-app.
Architecture: Thin IndexedDB wrapper for blob storage, separate from PGlite. No new SQL migrations. UI integrated into the existing ErstgespraechCard in the process stepper.
Tech Stack: IndexedDB (raw API), <input type="file"> with native document scanner, URL.createObjectURL() for previews.
Storage Layer
New module: src/shared/hooks/dokument-store.ts
Opens a dedicated IndexedDB database tpf-dokumente with a single object store dokumente (auto-increment key, indexed on sprechstundeId).
Record Shape
interface Dokument {
id: number; // auto-incremented
sprechstundeId: number;
name: string; // original filename
mimeType: string; // "image/jpeg", "application/pdf", etc.
blob: Blob;
erstelltAm: string; // ISO date
}
API
saveDokument(sprechstundeId: number, file: File): Promise<number>— store file, return idgetDokumente(sprechstundeId: number): Promise<Dokument[]>— all docs for an ErstgesprächdeleteDokument(id: number): Promise<void>— remove single docdeleteDokumenteForSprechstunde(sprechstundeId: number): Promise<void>— cascade on Erstgespräch deletedeleteAllDokumente(): Promise<void>— for "delete all data" in settings
Plain async functions (not React hooks). Components call imperatively and manage their own state.
UI Integration
Location
Inside the existing ErstgespraechCard in src/features/prozess/components/process-stepper.tsx, below diagnosis/Dringlichkeitscode info.
New Component
src/features/prozess/components/dokument-liste.tsx
Props: { sprechstundeId: number }
Manages its own state: loads documents on mount, refreshes after add/delete.
Upload
<input type="file" accept="image/*,application/pdf" multiple> styled as a button labeled "Dokument hinzufügen". On mobile this triggers the system picker which includes "Scan Documents" on iOS and camera/files on Android.
Thumbnails
Grid of small previews:
- Images:
URL.createObjectURL()thumbnails - PDFs: generic PDF icon with filename
Each thumbnail has a delete button (X) with confirmation.
Full-size Viewing
- Images: modal overlay, image scaled to fit viewport, tap outside or X to close
- PDFs: open in new browser tab via
URL.createObjectURL()(native PDF viewing)
Cleanup Integration
deleteErstgespraech(id)insrc/features/prozess/hooks.tsalso callsdeleteDokumenteForSprechstunde(id)deleteAllData()insrc/features/kontakte/hooks.tsalso callsdeleteAllDokumente()- Scenario seeding unchanged — dev scenarios don't need documents
Out of Scope
- PDF export integration (documents into the Absagenliste PDF) — deferred to a future sub-project
- OCR / data extraction from PTV11
- Cloud sync / backup of documents