Commit Graph

6 Commits

Author SHA1 Message Date
thomas dd39a7aff2 feat(design): Bildsprache des Portals übernehmen, Nachtansicht ergänzen
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>
2026-08-25 19:43:57 +02:00
thomas f4939ef6c5 fix(frontend): Reset-Link auswerten, Absturz bei defektem localStorage verhindern
- PasswordReset las den Query-Parameter token nie aus. Wer auf den Link
  in der Reset-Mail klickte, landete wieder auf Schritt 1 mit leerem
  Token-Feld — der Mailversand aus d9fecb6 lief damit ins Leere. Der
  Token wird jetzt aus der URL übernommen und direkt Schritt 2 gezeigt.
- "Zurück zum Login" und die Weiterleitung nach dem Zurücksetzen zeigten
  auf "/" und damit beim Unterpfad-Deployment auf das Portal.
- PublicUserList rief JSON.parse(localStorage.getItem(...)) ungeschützt
  im Render-Pfad auf. Ein beschädigter Eintrag — oder ein Browser, der
  Site-Data blockiert — ließ die gesamte öffentliche Liste weiß werden.
  Lesen und Schreiben laufen jetzt über try/catch, und der Wert wird auf
  Plausibilität geprüft.
- Der Typfilter-Effekt feuerte auch beim Mounten, obwohl useUsers bereits
  selbst lädt: jeder Aufruf der öffentlichen Liste setzte zwei identische
  Anfragen ab. Der erste Lauf wird jetzt übersprungen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 19:42:37 +02:00
thomas 81b21da499 fix(frontend): kaputtes UserCard-JSX reparieren, Einladungs-Token erzeugbar machen
- UserCard.js ließ sich nicht übersetzen: die öffnende Hälfte des Ternary
  um den GPS-Bereich ({isEditingGPS ? () fehlte, während das ") : (" und
  das ")}" stehen geblieben waren. isEditingGPS wurde dadurch nur noch
  gesetzt, nie gelesen. Da die Datei in allen drei Apps identisch ist,
  scheiterte überall der Build.
- Der Einladungs-Flow aus 8384ad9 war nur zur Hälfte da: Backend und
  Login-Formular verlangen einen Invite-Token, aber kein Frontend hat
  /invite-token je aufgerufen – der Admin konnte gar keinen erzeugen und
  ein Führer ohne Passwort kam nicht ins System. Die Benutzerkarte hat
  jetzt einen Button dafür (nur bei hinterlegter E-Mail), verdrahtet über
  UserList und AdminPanel.
- api.js: bei einem Fehler ohne Request-Config lief der Retry-Zweig in
  "config.retry = 2" auf undefined und verdeckte den echten Fehler.
- useUsers: "mehr laden" hat die aktiven Filter verworfen und die
  ungefilterte zweite Seite angehängt. Mit dem serverseitigen Typfilter
  aus d9fecb6 fällt das jetzt deutlich stärker auf.
- Die öffentliche Liste zeigte statt des Telefon-Emojis ein kaputtes
  Ersatzzeichen.
- Der Logo-Upload nannte SVG als erlaubtes Format, das Backend lehnt es
  bewusst ab.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 08:11:55 +02:00
User d9fecb6914 security: implement audit fixes and backend optimizations
- L-03: PasswordReset.js minLength 6 -> 12 for all 3 apps
- B-01: PLZ geocoding proxy endpoint (GET /api/public/geocode) in all 3 backends;
  frontend PublicUserList now uses backend instead of direct Nominatim calls
- B-02: type filter server-side via onRefetch useEffect; removed redundant local
  available/type filters from PublicUserList useMemo
- audit fixes: app-specific JWT secrets, bcrypt cost 12, LRU geocode cache,
  auth middleware app-claim check, nginx CSP script-src cleanup,
  nginx.conf renamed to nginx.conf.dev, geocode-cache Docker volume
- add mailer.js utility (password reset emails)
2026-05-03 11:20:45 +02:00
thomas 770b0b1d38 Fix app labels: replace Nachsuchen with app-specific names in drohnenfuehrer and stoeberhunde
- drohnenfuehrer: all Nachsuchenführer/Hundeführer labels replaced with Drohnenführer
- stoeberhunde: all Nachsuchenführer/Hundeführer labels replaced with Stöberhundeführer
- Fixed backend config, controllers, logger, env, package.json, seed.js
- Fixed frontend components: Header, UserList, UserForm, PublicUserList, HandlerLogin, AdminPanel, RulesDisplay
- Fixed Dockerfiles (PUBLIC_URL), nginx configs, podman-compose.yml, CONTAINER.md, docs
- Fixed service worker registration path: /sw.js -> ./sw.js
- Fixed portal/index.html
2026-05-03 08:24:28 +02:00
thomas 5eb14a7826 init: Jagd Apps Heidekreis – Portal, Nachsuche, Drohnenführer, Stöberhunde 2026-05-02 17:31:20 +02:00