From c335fd6f5c105ea9c8c1345fbf505d8fc6e85a9f Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Fri, 31 Jul 2026 21:45:19 +0200 Subject: [PATCH] csadas --- .projektstructure.txt | 1 + PROJECT_CONTEXT.md | 1 + README.md | 1 + partials/landing/account/dashboard.php | 214 +++++++++++++++---------- public/assets/css/app.css | 20 ++- src/App/AccountPages.php | 9 ++ 6 files changed, 158 insertions(+), 88 deletions(-) diff --git a/.projektstructure.txt b/.projektstructure.txt index d9b97f1..863c0ce 100755 --- a/.projektstructure.txt +++ b/.projektstructure.txt @@ -20,6 +20,7 @@ Aktueller Projektstand - Der Hinweis auf die verschluesselte Speicherung personenbezogener Profilangaben ist auf der Datenschutz-Seite jetzt zusaetzlich deutlich hervorgehoben. - Das Profil besitzt jetzt zusaetzlich eine vollstaendige Adresse mit verschluesselter Strasse/Hausnummer sowie eine optionale Adress-Lokalisierung ueber die vorhandenen externen Karten-/Geocoding-Dienste. - Die Profiladresse unterstuetzt jetzt ausserdem eine Auswahl moeglicher Adress-Treffer, die Uebernahme aus dem Browser-Standort und einen kurzen Hinweisdialog zur lokalen Relevanz. +- Der Profil-Editor im Mitgliederbereich ist jetzt kompakter aufgebaut, in ausklappbare Bereiche gegliedert und enthaelt die bearbeitbare Konto-E-Mail direkt im Formular. - Fuer Umkreis- und Distanzfunktionen bleiben nur `lat`/`lng` technisch im Klartext; die eigentliche Strasse/Hausnummer ist verschluesselt. - Alte statische Avatar-Presets und frühere Layer-Assets sind entfernt. - Änderungen an Funktionen mit Cookies, LocalStorage, SessionStorage, Geolocation, Tracking oder Drittanbietern müssen immer auch in Datenschutz-/Cookie-Hinweisen und im Consent-Flow berücksichtigt werden. diff --git a/PROJECT_CONTEXT.md b/PROJECT_CONTEXT.md index 18b4f99..87e6e9e 100644 --- a/PROJECT_CONTEXT.md +++ b/PROJECT_CONTEXT.md @@ -40,6 +40,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter. Kernbereiche sind l - Der Hinweis auf die verschluesselte Speicherung personenbezogener Profilangaben ist auf der Datenschutz-Seite jetzt zusaetzlich deutlich hervorgehoben. - Im Profil koennen jetzt auch Strasse/Hausnummer, PLZ, Ort und Region gepflegt werden; fuer die Adress-Lokalisierung werden die bereits vorhandenen externen Karten-/Geocoding-Dienste wiederverwendet. - Die Profiladresse bietet jetzt zusaetzlich eine Trefferauswahl fuer die Suche, eine Uebernahme aus dem Browser-Standort und einen kurzen Hinweisdialog zur Relevanz fuer lokale Termin-Hinweise. +- Der Profil-Editor ist jetzt kompakter gegliedert, nutzt ausklappbare Bereiche fuer Basisdaten, Adresse, Sprache/Beruf und Kurzvorstellung und enthaelt die bearbeitbare Konto-E-Mail direkt im Formular. - Im Profil kann der Nutzer aktuell Komponenten wie Augen, Augenbrauen, Mund, Brille, Haare, Bart und Ohrringe direkt per Vorschaubild auswählen. - Der Button `Random Profilbild` setzt Seed und Varianten des aktiven Styles zufaellig neu und aktualisiert die Vorschau sofort. - Beim Oeffnen des Avatar-Generators wird nur der aktive Style-/Komponenten-Tab geladen; weitere Varianten werden erst beim Wechsel nachgeladen. diff --git a/README.md b/README.md index c27b185..9f852a6 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Papa-Kind-Treff ist eine PHP-basierte Plattform für Väter mit Fokus auf lokale - der Hinweis auf die verschluesselte Speicherung personenbezogener Profilangaben ist jetzt auch auf der Datenschutz-Seite deutlich hervorgehoben - das Profil unterstuetzt jetzt eine vollstaendige Adresse mit verschluesselter Strasse/Hausnummer sowie optionaler Adress-Lokalisierung ueber die bereits vorhandenen Karten-/Geocoding-Dienste - bei der Profiladresse gibt es jetzt eine Trefferauswahl fuer Adresssuche, eine Uebernahme aus dem Browser-Standort sowie einen mobilen Hinweisdialog, warum die Adresse fuer lokale Termine relevant ist +- der Profil-Editor im Mitgliederbereich ist jetzt kompakter gruppiert, nutzt ausklappbare Abschnitte und enthaelt die bearbeitbare Konto-E-Mail wieder direkt im Formular - Debug-Floating-Button nur für `site_admin` im Debug-Modus ## Technik diff --git a/partials/landing/account/dashboard.php b/partials/landing/account/dashboard.php index 67d487f..79664b6 100755 --- a/partials/landing/account/dashboard.php +++ b/partials/landing/account/dashboard.php @@ -78,16 +78,31 @@ $sectionLinks = [ -
-
Name
-
Anzeigename
-
Adresse
-
E-Mail
-
Telefon
-
Beruf
-
Sprachen
-
Über dich
-
+
+
+ Basis + +
+
+ Standort + +
+
+ +
+ Über dich +

+
+
@@ -484,91 +499,116 @@ $sectionLinks = [

Profil bearbeiten

-
+ -
-
- - +

Persoenliche Angaben werden app-seitig verschluesselt gespeichert. Nur fuer Login und Kontofunktionen wird die E-Mail intern ueber einen separaten Lookup-Hash adressiert.

+
+ Basisdaten +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + +
-
- - +
+
+ Adresse und Umgebung +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + + +
+

Für Suche und Browser-Übernahme werden dieselben Karten-/Adressdienste wie bei Events genutzt und nur nach Einwilligung in externe Dienste.

+
+
+ + Gewaehlte Adresse: + +
+
-
-
-
- - + +
+ Sprache und Beruf +
+
+ +
+ + + + +
+ + +
+
+ + +
-
- - +
+
+ Kurzvorstellung +
+
+ + +
-
-
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
-
- - - -
-

Für Suche und Browser-Übernahme werden dieselben Karten-/Adressdienste wie bei Events genutzt und nur nach Einwilligung in externe Dienste.

-
-
- - Gewaehlte Adresse: - -
- -
- -
- - - - -
- - -
-
- - -
-
- - -
+
diff --git a/public/assets/css/app.css b/public/assets/css/app.css index b150b80..b8e94de 100755 --- a/public/assets/css/app.css +++ b/public/assets/css/app.css @@ -241,12 +241,27 @@ body { .account-panel__body { padding: 24px; } .account-kv { display: grid; gap: 10px; margin: 0; } .account-kv div { display: grid; grid-template-columns: 190px minmax(0, 1fr); gap: 14px; } +.account-kv--compact { gap: 8px; } +.account-kv--compact div { grid-template-columns: 120px minmax(0, 1fr); gap: 10px; } .account-kv dt { font-weight: 700; color: var(--color-muted); } .account-kv dd { margin: 0; } .profile-avatar-panel { display: grid; grid-template-columns: 160px minmax(0, 1fr); gap: 22px; align-items: center; margin-bottom: 24px; padding: 20px; background: linear-gradient(135deg, #fffdfa, #f7f0e6); border: 1px solid var(--color-border); border-radius: 18px; } .profile-avatar-panel__preview { display: flex; justify-content: center; } .profile-avatar-panel__content h3 { margin: 0 0 8px; } .profile-avatar-panel__content p { margin: 0 0 14px; } +.profile-summary-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; } +.profile-summary-card { padding: 18px; background: #fffdfa; border: 1px solid var(--color-border); border-radius: 18px; } +.profile-summary-card__eyebrow { display: inline-block; margin-bottom: 12px; font-size: 12px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase; color: var(--color-highlight); } +.profile-about-card { margin-top: 16px; padding: 18px; background: linear-gradient(135deg, #fff, #f8f4ec); border: 1px solid var(--color-border); border-radius: 18px; } +.profile-about-card strong { display: block; margin-bottom: 8px; } +.profile-about-card p { margin: 0; line-height: 1.65; color: var(--color-text); } +.profile-edit-form { max-width: 100%; } +.profile-edit-section { border: 1px solid var(--color-border); border-radius: 18px; background: #fffdfa; overflow: hidden; } +.profile-edit-section summary { cursor: pointer; list-style: none; padding: 16px 18px; font-weight: 800; color: var(--color-text); display: flex; align-items: center; justify-content: space-between; gap: 12px; } +.profile-edit-section summary::-webkit-details-marker { display: none; } +.profile-edit-section summary::after { content: "+"; width: 28px; height: 28px; border-radius: 999px; border: 1px solid var(--color-border); display: inline-flex; align-items: center; justify-content: center; color: var(--color-primary); background: #fff; font-size: 18px; line-height: 1; flex: 0 0 auto; } +.profile-edit-section[open] summary::after { content: "−"; } +.profile-edit-section__body { display: grid; gap: 14px; padding: 0 18px 18px; border-top: 1px solid rgba(196, 169, 120, 0.22); } .avatar-builder { height: 100%; min-height: 0; } .avatar-builder__shell { height: 100%; min-height: 0; display: grid; grid-template-rows: auto minmax(0, 1fr); gap: 14px; } .avatar-builder__top { display: grid; gap: 14px; padding: 14px; background: linear-gradient(180deg, #fffdfa, #f8f4ec); border: 1px solid var(--color-border); border-radius: 18px; } @@ -287,7 +302,7 @@ body { .avatar-component-card__label { font-size: 12px; font-weight: 700; color: var(--color-text); text-align: center; } .avatar-component-card:has(input:checked) { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(52,72,90,0.14); } @media (max-width: 980px){ .avatar-component-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } } -@media (max-width: 860px){ .avatar-builder__hero { grid-template-columns: 1fr; } .avatar-builder__actions { justify-content: center; } .avatar-component-colors { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } .avatar-component-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } } +@media (max-width: 860px){ .avatar-builder__hero { grid-template-columns: 1fr; } .avatar-builder__actions { justify-content: center; } .avatar-component-colors { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); } .avatar-component-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } .profile-summary-grid { grid-template-columns: 1fr; } } .avatar-swatch-group { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; } .avatar-swatch { display: grid; grid-template-columns: 20px 18px minmax(0, 1fr); gap: 10px; align-items: center; padding: 10px 12px; border: 1px solid var(--color-border); border-radius: 14px; background: #fff; cursor: pointer; } .avatar-swatch__color { width: 18px; height: 18px; border-radius: 999px; background: var(--swatch-color); border: 1px solid rgba(0,0,0,0.12); } @@ -393,6 +408,9 @@ body { .cookie-consent__inner { grid-template-columns: 1fr; } .cookie-consent__actions { justify-content: stretch; } .account-kv div { grid-template-columns: 1fr; gap: 4px; } + .account-kv--compact div { grid-template-columns: 1fr; gap: 4px; } + .profile-edit-section summary, + .profile-edit-section__body { padding-left: 14px; padding-right: 14px; } } /* Auth & Dashboard */ diff --git a/src/App/AccountPages.php b/src/App/AccountPages.php index a2ef66d..fd1c2b7 100755 --- a/src/App/AccountPages.php +++ b/src/App/AccountPages.php @@ -182,10 +182,14 @@ final class AccountPages try { if ($action === 'profile') { $crypto = self::requireCrypto($crypto, 'Profil'); + $email = trim((string)($_POST['email'] ?? '')); $languages = $_POST['languages'] ?? ''; if (is_array($languages)) { $languages = implode(', ', array_map('trim', $languages)); } + if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + throw new \RuntimeException('Bitte gib eine gueltige E-Mail-Adresse an.'); + } $firstNameEnc = self::encryptOptionalProfileField($crypto, trim((string)$_POST['first_name'])); $lastNameEnc = self::encryptOptionalProfileField($crypto, trim((string)$_POST['last_name'])); $streetEnc = self::encryptOptionalProfileField($crypto, trim((string)($_POST['street'] ?? ''))); @@ -249,6 +253,11 @@ final class AccountPages 'locationPref' => in_array($locationPreference, ['disabled', 'prompt', 'enabled'], true) ? $locationPreference : 'prompt', 'id' => $userId, ]); + if ($pdo) { + $emailStore = new UserEmailStore($pdo); + $emailStore->ensureSchema(); + $emailStore->updateUserEmail($userId, $email); + } $info = 'Profil gespeichert.'; } elseif ($action === 'avatar_update') { if ($profileSettings) {