From 2a013ae9fec54ab14fc75a00f76531cf23ebea97 Mon Sep 17 00:00:00 2001 From: dschlueter Date: Thu, 25 Jun 2026 15:48:51 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Notruf-Frontend=20=E2=80=94=20Geopositi?= =?UTF-8?q?on=20+=20Admin-Notfallprofil=20(Phase=202)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 🆘-Handler erfasst beim Auslösen die Geoposition (One-Shot, navigator. geolocation, enableHighAccuracy) und hängt sie als geo:{lat,lon,accuracy,ts} an /api/emergency. Timeout-Race (8 s), damit hängendes GPS den Alarm nie blockiert; nur mit Freigabe (location_consent). - loadMe: Prefs global gehalten; bei location_consent Standort-Erlaubnis vorab angefragt (kein Browser-Dialog im Panikmoment). - Admin-User-Karte: einklappbares „Notfall-Profil" zum Pflegen von Name, Adresse, Telefonen, Geburtsdatum, med. Hinweisen, Kontakt-E-Mail/-Telefon und der Standort-Freigabe; speichert via PUT /api/admin/users/{id}/prefs. Nutzt das in Phase 1+3 gebaute Backend; keine Backend-Änderung. app.js per node --check validiert. Co-Authored-By: Claude Opus 4.8 --- app/web/app.js | 100 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/app/web/app.js b/app/web/app.js index 199859c..42f0b34 100644 --- a/app/web/app.js +++ b/app/web/app.js @@ -316,6 +316,7 @@ let activeSources = []; // laufende AudioBufferSourceNodes (stoppbar per Barge- // /api/me abgeleitet; bis dahin null -> ensureSession() wartet auf loadMe(). let sessionId = null; let mePromise = null; +let myPrefs = {}; // Prefs des aktuellen Nutzers (u. a. location_consent für den Notruf-Standort) let currentUserId = null; const sessionKey = (uid) => "va-session-" + uid; @@ -368,6 +369,14 @@ function loadMe() { } // Gespeicherte Nutzer-Präferenzen in die Dropdowns laden. const prefs = me.prefs || {}; + myPrefs = prefs; + // Standort-Erlaubnis ggf. vorab anfragen, damit im Notfall kein Browser-Dialog kommt. + if (prefs.location_consent && navigator.geolocation) { + try { + navigator.geolocation.getCurrentPosition(() => {}, () => {}, + { maximumAge: 600000, timeout: 8000 }); + } catch (e) { /* ignore */ } + } applyAllowedLanguages(prefs); applyPlaybackChoice(prefs.tts_provider); applyGenderChoice(prefs.voice_gender || 'f'); @@ -1073,6 +1082,36 @@ function buildUserCard(u) {
+ +
+ + +
+