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>
8.4 KiB
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()ruftapi.getPhotoBlobUrl(rel, 'small')auf →photo.php?size=smallphoto.phpliefert nur dann ein Thumbnail, wenn Dolibarr eines unterthumbs/<name>_small.<ext>vorgefertigt hat- Die PWA lädt ihre Fotos aber per
orders.php?action=upload_photohoch, und dort wird nievignette()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.
- [API]
photo.phpumsize=thumb&w=<px>erweitern →bericht_attachment_thumb(), mit ETag aus mtime undCache-Control: private, max-age=…, immutable loadThumbs()umbauen:<img src="…api/photo.php?…&size=thumb">direkt setzen statt fetch → Blob → ObjectURL. Damit greifen HTTP-Cache undloading="lazy"wirklich; heute sind beide wirkungslos, weil der Blob schon vorher komplett geladen wird.- 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. - Doppel-Ladung entfernen: schlägt
size=smallfehl, 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.
- [API]
taken_atje Datei ausgeben - 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.
- [API]
'title' => $p->titleinreports.phpergä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_pathnur eines von bis zu sechs Bildern -
Bei
title_onlygibt es gar keine Quelle → ❌-Kachel -
[API]
composite_pathinreports.phpmit ausgeben -
PWA zeigt
composite_path, wenn vorhanden, sonstsource_path -
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 |
- 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.
- Eigener Runtime-Cache
baustelle-mediafürphoto.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. - 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.
- „📝 Notizen (n)" am Auftrag, direkt unter der Sprachnotiz
- Liste mit Betreff, zweizeiliger Vorschau und Zeitstempel; Antippen öffnet zum Lesen und Ändern, 🗑 löscht (mit Rückfrage)
- Editor: Betreff-Feld + Textfeld über die volle Höhe, Speichern über ✓ in der Kopfzeile, Rückfrage beim Verlassen mit ungespeicherten Änderungen
- Ablage als
notiz_<betreff>_<datum>.txtim Auftragsordner (api/note.php) — genau der Weg der Sprachnotiz. Damit auch im Dolibarr-Auftrag und in der Anhänge-Spalte des Bericht-Editors sichtbar. - 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.htmlfragteapi.getToken()— ein JWT in IndexedDB, das es seit der SSO-Umstellung gar nicht mehr gibt (api.login()legt nur nochuserab, die Sitzung steckt im HttpOnly-Cookieawl_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".
-
Anmeldung über
api/verify.php(Cookie) statt über das nicht mehr existierende Token -
Anmelden in der Teilen-Seite (Passwort + Passkey) — das Geteilte bleibt dabei gesichert
-
Service Worker sichert
title,textundurlmit; Weiterleitung auch ohne Datei -
Auswahl-Seite: Bildvorschau, Betreff + Text zum Nachbessern, Auftragssuche (ohne Suchbegriff die offenen Aufträge, mit Suchbegriff alle)
-
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 -
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-btnund.modal-bodyhatten ü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_MEDIAan 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.