Files
tpf/docs/superpowers/specs/2026-03-12-document-scanning-design.md
2026-03-12 22:13:14 +01:00

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 id
  • getDokumente(sprechstundeId: number): Promise<Dokument[]> — all docs for an Erstgespräch
  • deleteDokument(id: number): Promise<void> — remove single doc
  • deleteDokumenteForSprechstunde(sprechstundeId: number): Promise<void> — cascade on Erstgespräch delete
  • deleteAllDokumente(): 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) in src/features/prozess/hooks.ts also calls deleteDokumenteForSprechstunde(id)
  • deleteAllData() in src/features/kontakte/hooks.ts also calls deleteAllDokumente()
  • 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