All checks were successful
Deploy baustelle-pwa / deploy (push) Successful in 13s
Jeder Teilen-Vorgang endete bisher in einer Sackgasse — Login-Hinweis oder "Keine geteilten Fotos gefunden", und danach war das Geteilte weg. Zwei Ursachen: 1. share.html prüfte api.getToken() — ein JWT in IndexedDB, das es seit der SSO-Umstellung nicht mehr gibt (api.login() legt nur noch 'user' ab, die Sitzung steckt im HttpOnly-Cookie awl_sso). Der Wert war immer leer, also kam IMMER "Bitte zuerst anmelden"; der Weg "Zur App" verwarf das Geteilte. Jetzt: Cookie-Prüfung über api/verify.php wie in ensureAuth(), und die Anmeldung (Passwort + Passkey) läuft IN der Teilen-Seite — das Geteilte bleibt dabei gesichert und der Ablauf geht danach direkt weiter. 2. Der Service Worker sicherte nur Dateien. Titel, Text und Link aus dem share_target wurden weggeworfen, ein reiner Text-Share schrieb gar nichts. Jetzt wird das ganze Paket gesichert und immer weitergeleitet (absolute Redirect-URL, leere File-Platzhalter fliegen raus). Die Teilen-Seite zeigt Bildvorschau, Betreff und Text zum Nachbessern und eine Auftragssuche (ohne Suchbegriff die offenen Aufträge, mit Suchbegriff alle). Beim Ablegen gehen die Bilder über die normale Upload-Queue (Persist-First), der Text wird als notiz_*.txt über api/note.php gespeichert. Scheitert die Notiz (kein Netz), bleibt der Text gesichert und der Vorgang ist wiederholbar. Dazu ein Banner in der App, wenn noch ein Paket unabgelegt bereitliegt — abgebrochene Vorgänge findet sonst niemand wieder. share.html liest auch den alten IndexedDB-Schlüssel, damit der erste Share nach dem Deploy (noch alter Service Worker) nicht verlorengeht. Lokal durchgespielt: Bild+Text, nur Text, und abgemeldet teilen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
171 lines
8.4 KiB
Markdown
171 lines
8.4 KiB
Markdown
# ROADMAP — Baustelle PWA
|
||
|
||
Stand: 2026-08-22 · Gegenstück: `data/bericht` (Modul-Version 1.5.1)
|
||
|
||
Die PWA hat kein eigenes Backend — alles läuft über `custom/bericht/api/`. Punkte, die eine
|
||
API-Änderung brauchen, sind mit **[API]** markiert und stehen zusätzlich in der ROADMAP des
|
||
Bericht-Moduls.
|
||
|
||
---
|
||
|
||
## 1. Nachgezogen aus Bericht 1.5.0 ✅ (2026-08-22, Modul 1.5.1)
|
||
|
||
Das Modul hatte mit 1.5.0 Infrastruktur bekommen, von der die PWA nichts hatte.
|
||
Die Beschreibungen unten schildern jeweils den Zustand davor.
|
||
|
||
### 1.1 Echte Thumbnails statt Vollbilder ✅ [API]
|
||
|
||
Bis dahin lud die PWA für **jede Kachel das komplette Foto**:
|
||
|
||
- `loadThumbs()` ruft `api.getPhotoBlobUrl(rel, 'small')` auf → `photo.php?size=small`
|
||
- `photo.php` liefert nur dann ein Thumbnail, wenn Dolibarr eines unter
|
||
`thumbs/<name>_small.<ext>` **vorgefertigt** hat
|
||
- Die PWA lädt ihre Fotos aber per `orders.php?action=upload_photo` hoch, und dort wird
|
||
**nie `vignette()` aufgerufen** → es existiert nie ein Thumb → es kommt immer das Original
|
||
- Bei 30 Baustellenfotos sind das im Mobilfunknetz 30 × 0,5–1 MB (bei über die
|
||
Dolibarr-Oberfläche hochgeladenen Originalen entsprechend mehr)
|
||
|
||
Das Modul kann das seit 1.5.0 längst: `bericht_attachment_thumb($full, $maxside)` in
|
||
`lib/bericht.lib.php` (GD, Cache unter `bericht/thumbs/`, EXIF-Rotation, Speicher-Notbremse).
|
||
`api/photo.php` lädt diese lib bereits.
|
||
|
||
- [x] **[API]** `photo.php` um `size=thumb&w=<px>` erweitern → `bericht_attachment_thumb()`,
|
||
mit ETag aus mtime und `Cache-Control: private, max-age=…, immutable`
|
||
- [x] `loadThumbs()` umbauen: `<img src="…api/photo.php?…&size=thumb">` **direkt** setzen statt
|
||
fetch → Blob → ObjectURL. Damit greifen HTTP-Cache und `loading="lazy"` wirklich; heute
|
||
sind beide wirkungslos, weil der Blob schon vorher komplett geladen wird.
|
||
- [x] Sequenzielle Schleife auflösen — `for (const t of thumbs) { await … }` lädt Foto für Foto
|
||
nacheinander. Beim `<img>`-Weg erledigt der Browser das parallel von selbst.
|
||
- [x] Doppel-Ladung entfernen: schlägt `size=small` fehl, wird das Original **noch einmal**
|
||
geladen.
|
||
|
||
### 1.2 Aufnahmezeit statt Dateidatum ✅ [API]
|
||
|
||
1.5.0 hat einen eigenen EXIF-Parser bekommen (`bericht_jpeg_taken_at()` /
|
||
`bericht_file_taken_at()`), weil im Prod-Container das `exif`-Modul fehlt. Der Editor sortiert
|
||
Anhänge damit nach Aufnahmezeit. `orders.php?action=photos` lieferte nur `filemtime`.
|
||
|
||
- [x] **[API]** `taken_at` je Datei ausgeben
|
||
- [x] Fotoliste nach Aufnahmezeit sortieren (Auswahl gemerkt, wie im Editor)
|
||
|
||
### 1.3 Seiten-Titel kommt nie an ✅ [API]
|
||
|
||
`api/pages.php` **kann** `title` einer Seite setzen, die PWA rendert ihn (`page-title-badge`,
|
||
`openPageActionsModal(…, title)`) — aber `api/reports.php` gibt `title` im `pages`-Array
|
||
**nicht aus**. Ein im Editor gesetzter Titel ist in der PWA also unsichtbar, und ein in der PWA
|
||
gesetzter Titel scheint nach dem Neuladen verschwunden.
|
||
|
||
- [x] **[API]** `'title' => $p->title` in `reports.php` ergänzen (eine Zeile)
|
||
|
||
### 1.4 Bearbeitete Seiten und Raster-Layouts ✅ [API]
|
||
|
||
Seit 1.5.0 hat eine Seite `composite_path` (das im Editor gebaute Seitenbild) und `layout`
|
||
(`single`, `grid_2`, `grid_4`, `before_after`, `title_only`, …). Die PWA zeigt stur
|
||
`source_path`:
|
||
|
||
- Nach Bearbeitung im Desktop-Editor zeigt die PWA weiter das **Rohbild** ohne Anmerkungen
|
||
- Bei einer Rasterseite ist `source_path` nur **eines von bis zu sechs** Bildern
|
||
- Bei `title_only` gibt es gar keine Quelle → ❌-Kachel
|
||
|
||
- [x] **[API]** `composite_path` in `reports.php` mit ausgeben
|
||
- [x] PWA zeigt `composite_path`, wenn vorhanden, sonst `source_path`
|
||
- [x] Layout-Kennzeichen an der Kachel (z. B. „⊞ 4er-Raster"), damit klar ist, warum dort
|
||
mehr drauf ist als das eine Foto
|
||
|
||
---
|
||
|
||
## 2. Verbesserungen unabhängig vom Modul
|
||
|
||
### 2.1 Browser-Dialoge raus (10 Stück) ✅
|
||
|
||
`confirm()` / `alert()` / `prompt()` widersprechen der Hausregel und sehen in der
|
||
Standalone-PWA auf Android wie ein Systemfehler aus. Ein sauberes Muster liegt schon vor:
|
||
das „Bericht löschen"-Modal in `router.on('/reports/:id')`.
|
||
|
||
| Stelle | Zeile (vor dem Umbau) |
|
||
|---|---|
|
||
| PIN stimmt nicht überein | `alert` 295 |
|
||
| Bericht finalisieren | `confirm` 1111 |
|
||
| PIN-Schutz deaktivieren | `confirm` 1264 |
|
||
| Material-Eintrag löschen | `confirm` 1432 |
|
||
| Seite aus Bericht entfernen | `confirm` 1809 |
|
||
| Foto löschen | `confirm` 2701 |
|
||
| Nicht hochgeladenes Foto löschen | `confirm` 3019 |
|
||
| Mess-Skala: Länge eingeben | `prompt` 3409 |
|
||
| Ungültiges Längenformat | `alert` 3419 |
|
||
| „Zuerst kalibrieren" | `alert` 3440 |
|
||
|
||
- [x] Eine `confirmDialog({title, text, danger})`-Hilfsfunktion (Promise) + `inputDialog()`
|
||
für die Längeneingabe, alle zehn Stellen darauf umstellen
|
||
|
||
### 2.2 Fotos offline sichtbar halten ✅
|
||
|
||
Der Service Worker reichte alles unter `/custom/bericht/api/` ungecacht durch. Auf der Baustelle
|
||
ohne Netz ist damit **kein einziges** schon angesehenes Foto verfügbar.
|
||
|
||
- [x] Eigener Runtime-Cache `baustelle-media` für `photo.php`-GETs, cache-first mit
|
||
Größenbegrenzung (LRU, z. B. 200 Einträge). Die Thumb-URLs aus 1.1 sind dafür der
|
||
richtige Kandidat — klein und unveränderlich.
|
||
- [x] Vom App-Shell-Cache getrennt halten, damit ein Deploy die Medien nicht wegräumt
|
||
|
||
---
|
||
|
||
## 3. Textnotizen ✅ (2026-08-22)
|
||
|
||
Getippte Notiz zum Auftrag — für alles, was man lieber schreibt als diktiert: Zählernummern,
|
||
Absprachen, Maße.
|
||
|
||
- [x] „📝 Notizen (n)" am Auftrag, direkt unter der Sprachnotiz
|
||
- [x] Liste mit Betreff, zweizeiliger Vorschau und Zeitstempel; Antippen öffnet zum Lesen
|
||
und Ändern, 🗑 löscht (mit Rückfrage)
|
||
- [x] Editor: Betreff-Feld + Textfeld über die volle Höhe, Speichern über ✓ in der Kopfzeile,
|
||
Rückfrage beim Verlassen mit ungespeicherten Änderungen
|
||
- [x] Ablage als `notiz_<betreff>_<datum>.txt` im Auftragsordner (`api/note.php`) — genau der
|
||
Weg der Sprachnotiz. Damit auch im Dolibarr-Auftrag und in der Anhänge-Spalte des
|
||
Bericht-Editors sichtbar.
|
||
- [x] Notizen sind aus „Weitere Dokumente" ausgefiltert — sie haben ihre eigene Sektion und
|
||
stünden dort sonst doppelt
|
||
|
||
**Nicht** umgesetzt (und bewusst verworfen): eine Zeichenfläche für echte Handschrift. Am
|
||
Handy ohne Stift ist das unbrauchbar.
|
||
|
||
## 4. Teilen aus WhatsApp ✅ (2026-08-30)
|
||
|
||
Bilder und Beschreibungstexte, die die Kundin per WhatsApp schickt, landen direkt im Auftrag.
|
||
Vorher endete jeder Teilen-Vorgang in einer Sackgasse:
|
||
|
||
- `share.html` fragte `api.getToken()` — ein JWT in IndexedDB, das es **seit der SSO-Umstellung
|
||
gar nicht mehr gibt** (`api.login()` legt nur noch `user` ab, die Sitzung steckt im
|
||
HttpOnly-Cookie `awl_sso`). Ergebnis: immer „Bitte zuerst anmelden", und der Weg „Zur App"
|
||
verwarf das Geteilte.
|
||
- Der Service Worker sicherte **nur Dateien**. Ein Text-Share (oder Bild + Bildunterschrift)
|
||
schrieb nichts in IndexedDB → „Keine geteilten Fotos gefunden".
|
||
|
||
- [x] Anmeldung über `api/verify.php` (Cookie) statt über das nicht mehr existierende Token
|
||
- [x] Anmelden **in** der Teilen-Seite (Passwort + Passkey) — das Geteilte bleibt dabei gesichert
|
||
- [x] Service Worker sichert `title`, `text` und `url` mit; Weiterleitung auch ohne Datei
|
||
- [x] Auswahl-Seite: Bildvorschau, Betreff + Text zum Nachbessern, Auftragssuche
|
||
(ohne Suchbegriff die offenen Aufträge, mit Suchbegriff alle)
|
||
- [x] Ablegen: Bilder über die normale Upload-Queue (Persist-First), Text als Notiz über
|
||
`api/note.php`. Schlägt die Notiz fehl (kein Netz), bleibt der Text gesichert und der
|
||
Vorgang ist wiederholbar
|
||
- [x] Banner in der App, wenn noch etwas Geteiltes unabgelegt bereitliegt
|
||
|
||
## Erledigt
|
||
|
||
### 2026-08-22
|
||
Abschnitte 1, 2 und 3 vollständig. Dazu nebenbei behoben:
|
||
|
||
- `.modal-header`, `.close-btn` und `.modal-body` hatten **überhaupt kein CSS**, obwohl das
|
||
Markup sie seit Längerem nutzt (u. a. beim Löschen eines Berichts) — der Schließen-Knopf
|
||
saß dadurch unter der Überschrift statt daneben.
|
||
- Das Laden eines Thumbnails überschrieb den kompletten Kachel-Inhalt (`t.innerHTML = …`)
|
||
und nahm die Overlays mit: Auswahl-Häkchen in der Mehrfachauswahl und Seitennummer der
|
||
Bericht-Seiten waren weg, sobald das Bild da war.
|
||
- Beim Abmelden bleiben keine Kundenfotos mehr auf dem Gerät (`CLEAR_MEDIA` an den Service
|
||
Worker + `clearPhotoCache()`).
|
||
|
||
### Davor
|
||
Siehe README (Feature-Liste) und die Commit-Historie: SSO-Migration auf awlauth,
|
||
WebAuthn-Login, Neuer-Auftrag-Formular, Live-Kamera mit Persist-First-Queue,
|
||
Lieferschein-Unterschrift.
|