- Der Service-Worker-Cache wurde nie invalidiert: die letzte Runde hat
offline.html in allen vier Zielen ersetzt, sw.js aber nicht angefasst.
Ein Browser installiert einen Service Worker nur neu, wenn sich sw.js
selbst aendert — bestehende Installationen haetten die alte, englische,
ungestaltete Offline-Seite dauerhaft behalten. CACHE_NAME hochgezaehlt.
Dabei faengt der Install jetzt einen fehlgeschlagenen Abruf ab; vorher
wurde bei einem Fehler der ganze Service Worker nicht aktiv.
- Die Leaflet-Marker kamen von cdnjs.cloudflare.com — in einer PWA, die im
Wald ohne Empfang benutzt wird, waren sie damit kaputt, und die IP jedes
Besuchers ging an ein fremdes CDN. Verwiesen wurde ausserdem auf Leaflet
1.7.1 bei installiertem 1.9.4. Die Bilder liegen im Paket und werden jetzt
gebuendelt; Vite bettet sie als data:-URI ein (zusammen ~4 KB).
- Die Adress-Autovervollstaendigung im Benutzerformular rief Nominatim
weiterhin direkt aus dem Browser auf, obwohl d9fecb6 dafuer einen Proxy
eingefuehrt hat — nur die PLZ-Suche nutzte ihn. Neu: searchAddresses() in
utils/geocode.js (teilt Wartezeit und Cache mit geocodeAddress) plus
GET /api/public/geocode/search unter demselben geocodeLimiter. Die Antwort
ist auf die Felder beschraenkt, die das Formular liest. Der Debounce steigt
von 400 auf 600 ms, weil die Anfragen jetzt durch die serverseitige
Mindestwartezeit laufen.
Geprueft: im Bundle kein cdnjs und kein nominatim mehr; der neue Endpunkt
liefert Treffer mit den erwarteten Feldern, zu kurze Begriffe 400.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die visuelle Ebene war bisher nie geprüft. Zwei Entscheidungen lagen dem
zugrunde: die Apps folgen der Bildsprache des Portals, und es gibt eine
Nachtansicht über prefers-color-scheme — Nachsuchen finden in der
Dämmerung und nachts statt, ein weißes Vollbild blendet dann.
Design-Tokens (App.css ist jetzt die einzige Farbquelle):
- Palette, Georgia-Serif für die Überschriftenebene und die kantigen
2px-Ecken vom Portal übernommen; Sans bleibt für funktionale UI-Texte,
wie es das Portal ebenfalls handhabt.
- Aktionsfarben haben jetzt eine zugehörige --color-on-*-Textfarbe. Ohne
das wäre in der Nachtansicht weißer Text auf hellgrünem Grund gelandet.
- 307 fest verdrahtete Farbwerte pro App auf Tokens umgestellt,
eigenschafts-abhängig (color/background/border getrennt). Das Schlüssel-
wort "white" war dabei der gefährlichste Fall: als Textfarbe auf einer
Vollton-Fläche musste es zu --color-on-* werden, als Fläche zu
--color-surface.
- theme-color war dreifach verschieden (#2d6a2d gehörte zu keiner der
beiden Paletten); Portal und alle Apps nutzen jetzt einen Wert.
Behobene Kontrastfehler (alle nachgerechnet, nicht geschätzt):
- .btn-success 3,13:1 und .btn-primary 4,33:1 lagen unter AA — der
Hauptaktionsbutton der gesamten Anwendung.
- Feldrahmen 2,27:1 und Kartenrahmen 1,60:1 (Soll 3,0 für Bedienelemente).
- .badge-restore 3,46:1 und .badge-password 2,49:1 im Audit-Log.
Geprüft: 36 Paare in hell UND dunkel, 0 Verstöße.
Weitere visuelle Befunde:
- .type-badge war in PublicUserList.css und UserCard.css doppelt
definiert. CSS ist global, die Admin-Variante gewann — die öffentliche
Liste zeigte die falsche Badge-Optik. Fünf solcher Kollisionen zwischen
öffentlicher Liste und Admin-Karte sind jetzt auf ihren Container
eingegrenzt.
- Formularfelder hatten keine Hintergrundfarbe und nahmen den Browser-
Standard: in der Nachtansicht leuchtete jedes Eingabefeld weiß. Fiel
erst im Screenshot auf.
- Die Kartenkacheln sind immer taghell und waren nachts die mit Abstand
hellste Fläche. Sie werden jetzt abgedunkelt, Marker bleiben hell.
- Die Badge-Definitionen im Audit-Log standen doppelt in derselben Datei
(8 Regeln, identische Werte).
Mobiltauglichkeit:
- .user-phone war ein 14 px hoher Textlink — der tel:-Link ist der Zweck
der öffentlichen Liste. Jetzt ein Ziel mit 44 px Mindesthöhe.
- Kopfzeile: Logo verkleinert, .app-title umbricht statt nowrap,
Navigationsschaltflächen auf 44 px, Breakpoint für ≤480px ergänzt,
auf schmalen Geräten nicht mehr klebend.
- 100vh -> 100dvh (Adressleiste erzeugte sonst Überlauf).
- :focus-visible für Buttons; prefers-reduced-motion respektiert.
- Geprüft mit Screenshots bei 360 px und 768 px, hell und dunkel:
kein horizontaler Überlauf.
Aufgeräumt:
- logo-fallingbostel.png und logo-ljn.png entfernt: 898 KB pro App, von
keiner Zeile Code referenziert, 60 % der Buildgröße und fast doppelt so
groß wie der gesamte Anwendungscode. Build 1,5 MB -> 632 KB.
- Tote Stile (.login-prompt, .btn-success, .panel, .user-gps u. a.),
doppelte body-Regel in index.css, Variablentippfehler --color-bg-muted.
- Die Offline-Seite der Apps war lang="en" und ungestaltet — genau die
Seite, die im Funkloch erscheint. Jetzt gestaltet, deutsch, mit
Nachtansicht, für Portal und Apps identisch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Build läuft über Vite ("build": "vite build"), der Code las
Umgebungsvariablen aber über process.env – das gibt es im Browser-Bundle
nicht. Damit war der Build-Arg REACT_APP_API_URL wirkungslos und der
Zugriff selbst lief in "process is not defined".
- constants.js nutzt import.meta.env und leitet den Deployment-Basispfad
aus import.meta.env.BASE_URL ab (Fallback: Laufzeit-Erkennung).
REACT_APP_* heißt überall VITE_* (Dockerfile, Compose, .env.example,
Doku).
- /verwaltung und /passwort-zuruecksetzen wurden gegen
window.location.pathname ohne Basispfad verglichen und trafen unter
/nachsuche/ nie zu – die Passwort-Reset-Seite war produktiv nicht
erreichbar. Neue Helfer withBase()/normalizePath().
- Der Service Worker wurde als '/sw.js' registriert und war damit der des
Portals mit Scope '/'. Registrierung läuft jetzt über BASE_URL, inkl.
passendem Scope.
- index.html verlinkte '/manifest.json' absolut, also das Portal-Manifest:
die App hätte sich als Portal installiert. Jetzt %BASE_URL%.
- public/index.html (CRA-Rest) kollidierte im Vite-Build mit der
index.html im Projektwurzelverzeichnis und wurde entfernt.
- manifest.json verwies auf ein favicon.ico, das es nicht gibt.
- package.json: Scripts auf Vite umgestellt. react-scripts,
@testing-library/* und web-vitals entfernt – nichts davon wird
importiert, und react-scripts stand im Widerspruch zum Build.
- Die ungenutzten Konstanten USER_TYPES/USER_TYPE_LABELS/RULES sind
entfallen (die echten Werte kommen aus der Datenbank).
- Header bekam eine Prop onXLogin, die er gar nicht entgegennimmt und die
den Logout-Handler durchreichte.
Verifiziert: alle drei Frontends bauen, im Bundle kein process.env mehr,
alle Pfade korrekt auf den jeweiligen Basispfad.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Dockerfile: icon-192.png und icon-512.png in Container aufgenommen
- manifest.json: favicon.ico entfernt (wurde als text/html geliefert)
- manifest.json: Cache-Busting ?v=2 für alle Icons
- index.html: Logo-Bild auf ?v=2 aktualisiert
- extra8002.conf: manifest.json Content-Type auf application/manifest+json gesetzt
- Alle Sub-App manifest.json: scope von '/' auf jeweiligen Pfad korrigiert
(/nachsuche/, /drohnenfuehrer/, /stoeberhunde/)
- icon-192.png und icon-512.png: aus logo-fallingbostel.png generiert