Files
papa-kind-treff.info/partials/landing/account/dashboard.php
2025-12-27 02:02:42 +01:00

402 lines
18 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
if (!isset($_SESSION['user_id'])) {
redirect('/login');
}
$app = app();
$pdo = $app->pdo();
$flash = $app->flash()->get();
$userId = (int)$_SESSION['user_id'];
$error = '';
$info = '';
$crypto = null;
try { $crypto = new \App\Crypto($app->config()); } catch (\Throwable) {}
// POST Aktionen
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
try {
if ($action === 'profile') {
$languages = $_POST['languages'] ?? '';
if (is_array($languages)) {
$languages = implode(', ', array_map('trim', $languages));
}
$phoneEnc = $crypto ? $crypto->encrypt(trim((string)$_POST['contact_phone'])) : trim((string)$_POST['contact_phone']);
$stmt = $pdo->prepare('UPDATE user_profiles SET display_name=:name, first_name=:fname, last_name=:lname, zip=:zip, city=:city, profession=:prof, languages=:langs, about=:about, contact_phone=:phone, updated_at=NOW() WHERE user_id=:id');
$stmt->execute([
'name' => trim((string)$_POST['display_name']),
'fname' => trim((string)$_POST['first_name']),
'lname' => trim((string)$_POST['last_name']),
'zip' => trim((string)$_POST['zip']),
'city' => trim((string)$_POST['city']),
'prof' => trim((string)$_POST['profession']),
'langs' => trim((string)$languages),
'about' => trim((string)$_POST['about']),
'phone' => $phoneEnc,
'id' => $userId,
]);
$info = 'Profil gespeichert.';
} elseif ($action === 'child_add') {
$firstNameEnc = $crypto ? $crypto->encrypt(trim((string)$_POST['first_name'])) : trim((string)$_POST['first_name']);
$noteEnc = $crypto ? $crypto->encrypt(trim((string)$_POST['note'])) : trim((string)$_POST['note']);
$stmt = $pdo->prepare('INSERT INTO children (user_id, gender, birthdate, age_years, encrypted_first_name, note, created_at, updated_at) VALUES (:uid, :gender, :birthdate, :age, :name, :note, NOW(), NOW())');
$stmt->execute([
'uid' => $userId,
'gender' => $_POST['gender'] ?? 'unknown',
'birthdate' => $_POST['birthdate'] ?: null,
'age' => $_POST['age_years'] ?: null,
'name' => $firstNameEnc,
'note' => $noteEnc,
]);
$info = 'Kind hinzugefügt.';
} elseif ($action === 'event_add') {
$stmt = $pdo->prepare('INSERT INTO events (created_by, title, teaser_public, description, location_label, zip, city, region, lat, lng, starts_at, allow_kids, visibility, status, created_at, updated_at) VALUES (:uid, :title, :teaser, :descr, :loc, :zip, :city, :region, NULL, NULL, :start, :allow, :vis, :status, NOW(), NOW())');
$stmt->execute([
'uid' => $userId,
'title' => trim((string)$_POST['title']),
'teaser' => trim((string)$_POST['teaser']),
'descr' => trim((string)$_POST['description']),
'loc' => trim((string)$_POST['location_label']),
'zip' => trim((string)$_POST['zip']),
'city' => trim((string)$_POST['city']),
'region' => trim((string)$_POST['region']),
'start' => $_POST['starts_at'] ?? null,
'allow' => isset($_POST['allow_kids']) ? 1 : 0,
'vis' => $_POST['visibility'] ?? 'public',
'status' => 'published',
]);
$info = 'Event gespeichert.';
}
} catch (Throwable $e) {
$error = $e->getMessage();
}
}
// Daten laden
$profile = [
'display_name' => '',
'first_name' => '',
'last_name' => '',
'zip' => '',
'city' => '',
'profession' => '',
'languages' => '',
'about' => '',
'email' => '',
'contact_phone' => '',
];
$stmt = $pdo->prepare('SELECT u.email, u.status, p.display_name, p.first_name, p.last_name, p.zip, p.city, p.profession, p.languages, p.about, p.contact_phone FROM users u LEFT JOIN user_profiles p ON p.user_id = u.id WHERE u.id = :id LIMIT 1');
$stmt->execute(['id' => $userId]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row) {
$profile = array_merge($profile, array_filter($row, fn($v) => $v !== null));
if ($crypto && !empty($profile['contact_phone'])) {
$profile['contact_phone'] = $crypto->decrypt((string)$profile['contact_phone']) ?: '';
}
}
$children = [];
$stmt = $pdo->prepare('SELECT id, encrypted_first_name AS first_name, note, gender, birthdate, age_years FROM children WHERE user_id = :id ORDER BY id DESC');
$stmt->execute(['id' => $userId]);
$childrenRaw = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
foreach ($childrenRaw as $c) {
if ($crypto) {
$c['first_name'] = $crypto->decrypt((string)$c['first_name']) ?: '';
$c['note'] = $crypto->decrypt((string)($c['note'] ?? '')) ?: '';
}
$children[] = $c;
}
$events = [];
$stmt = $pdo->prepare('SELECT id, title, teaser_public, starts_at, city, visibility FROM events WHERE created_by = :id ORDER BY starts_at DESC');
$stmt->execute(['id' => $userId]);
$events = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
?>
<main class="section">
<div class="container" style="display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:12px;">
<div>
<p class="eyebrow">Mitgliederbereich</p>
<h1>Hallo, <span style="color: var(--color-primary);"><?= htmlspecialchars($profile['display_name'] ?: 'Papa', ENT_QUOTES) ?></span>!</h1>
<p class="muted">Verwalte dein Profil, Kinder, Events und Teilnahmen.</p>
</div>
</div>
<div class="container dash-section">
<?php if ($flash): ?>
<div class="toast-bar"><?= htmlspecialchars($flash['message'], ENT_QUOTES) ?></div>
<?php endif; ?>
<?php if ($info): ?>
<div class="toast-bar" style="margin-top:10px;"><?= htmlspecialchars($info, ENT_QUOTES) ?></div>
<?php endif; ?>
<?php if ($error): ?>
<div class="toast-bar" style="margin-top:10px; border-color:#f87171; color:#991b1b;">Fehler: <?= htmlspecialchars($error, ENT_QUOTES) ?></div>
<?php endif; ?>
</div>
<div class="container dash-section">
<div class="dash-grid-2">
<div class="card dash-card">
<div class="badge">Profil</div>
<h3>Deine Angaben</h3>
<ul class="dash-list">
<li>Name: <?= htmlspecialchars(trim($profile['first_name'] . ' ' . $profile['last_name']), ENT_QUOTES) ?></li>
<li>Anzeigename: <?= htmlspecialchars($profile['display_name'], ENT_QUOTES) ?></li>
<li>Ort: <?= htmlspecialchars($profile['city'], ENT_QUOTES) ?> <?= htmlspecialchars($profile['zip'], ENT_QUOTES) ?></li>
<li>E-Mail: <?= htmlspecialchars($profile['email'], ENT_QUOTES) ?></li>
<li>Telefon: <?= htmlspecialchars($profile['contact_phone'], ENT_QUOTES) ?></li>
<li>Beruf: <?= htmlspecialchars($profile['profession'], ENT_QUOTES) ?></li>
<li>Sprachen: <?= htmlspecialchars($profile['languages'], ENT_QUOTES) ?></li>
<li>About: <?= htmlspecialchars($profile['about'], ENT_QUOTES) ?></li>
</ul>
<div class="flex gap-12" style="margin-top:12px;">
<button class="btn" type="button" data-modal-open="modalProfile">Bearbeiten</button>
</div>
</div>
<div class="card dash-card">
<div class="badge">Kinder</div>
<h3>Deine Kids</h3>
<?php if (!$children): ?>
<p class="muted small">Noch keine Kinder eingetragen.</p>
<?php else: ?>
<ul class="dash-list">
<?php foreach ($children as $c): ?>
<li><?= htmlspecialchars($c['first_name'], ENT_QUOTES) ?>, <?= htmlspecialchars($c['gender'], ENT_QUOTES) ?> <?= $c['age_years'] ? '(' . (int)$c['age_years'] . ' Jahre)' : '' ?></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<div class="flex gap-12" style="margin-top:12px;">
<button class="btn" type="button" data-modal-open="modalChild">Kind hinzufügen</button>
</div>
</div>
</div>
</div>
<div class="container dash-section">
<div class="card dash-card">
<div class="badge">Deine Events</div>
<?php if (!$events): ?>
<p class="muted small">Noch keine Events angelegt.</p>
<?php else: ?>
<ul class="dash-list">
<?php foreach ($events as $e): ?>
<li><?= htmlspecialchars($e['title'], ENT_QUOTES) ?> <?= htmlspecialchars($e['city'], ENT_QUOTES) ?>, <?= htmlspecialchars($e['starts_at'], ENT_QUOTES) ?> (<?= htmlspecialchars($e['visibility'], ENT_QUOTES) ?>)</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
</div>
<div class="container dash-section" id="events">
<div class="card dash-card">
<div class="badge">Eigenes Event</div>
<h3>Neuen Termin erstellen</h3>
<button class="btn" type="button" data-modal-open="modalEvent">Event anlegen</button>
</div>
</div>
<!-- Modals -->
<div class="modal" id="modalProfile">
<div class="panel">
<div class="head flex between center-y">
<h3>Profil bearbeiten</h3>
<button class="btn ghost" type="button" data-modal-close>✕</button>
</div>
<form method="post" class="stack gap-12">
<input type="hidden" name="action" value="profile">
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="pName">Anzeigename</label>
<input id="pName" name="display_name" class="input" value="<?= htmlspecialchars($profile['display_name'], ENT_QUOTES) ?>">
</div>
<div class="stack gap-6">
<label class="label" for="pFirst">Vorname</label>
<input id="pFirst" name="first_name" class="input" value="<?= htmlspecialchars($profile['first_name'], ENT_QUOTES) ?>">
</div>
</div>
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="pLast">Nachname</label>
<input id="pLast" name="last_name" class="input" value="<?= htmlspecialchars($profile['last_name'], ENT_QUOTES) ?>">
</div>
<div class="stack gap-6">
<label class="label" for="pCity">Ort</label>
<input id="pCity" name="city" class="input" value="<?= htmlspecialchars($profile['city'], ENT_QUOTES) ?>">
</div>
</div>
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="pZip">PLZ</label>
<input id="pZip" name="zip" class="input" value="<?= htmlspecialchars($profile['zip'], ENT_QUOTES) ?>">
</div>
<div class="stack gap-6">
<label class="label" for="pPhone">Telefon (mobil)</label>
<input id="pPhone" name="contact_phone" class="input" value="<?= htmlspecialchars($profile['contact_phone'], ENT_QUOTES) ?>">
</div>
</div>
<div class="stack gap-6">
<label class="label">Sprachen (Mehrfachauswahl)</label>
<div class="chips" style="flex-wrap: wrap;">
<?php
$langOptions = ['Deutsch','Englisch','Französisch','Spanisch','Türkisch','Arabisch','Polnisch'];
$currentLangs = array_filter(array_map('trim', explode(',', (string)$profile['languages'])));
?>
<?php foreach ($langOptions as $opt): ?>
<label class="chip" style="cursor:pointer;">
<input type="checkbox" name="languages[]" value="<?= htmlspecialchars($opt, ENT_QUOTES) ?>" <?= in_array($opt, $currentLangs, true) ? 'checked' : '' ?> style="margin-right:6px;">
<?= htmlspecialchars($opt, ENT_QUOTES) ?>
</label>
<?php endforeach; ?>
</div>
<label class="label" for="pLangCustom">Weitere Sprachen (Kommagetrennt)</label>
<input id="pLangCustom" name="languages[]" class="input" placeholder="z. B. Italienisch, Niederländisch">
</div>
<div class="stack gap-6">
<label class="label" for="pProf">Beruf</label>
<input id="pProf" name="profession" class="input" value="<?= htmlspecialchars($profile['profession'], ENT_QUOTES) ?>">
</div>
<div class="stack gap-6">
<label class="label" for="pAbout">Kurzvorstellung</label>
<textarea id="pAbout" name="about" class="textarea" rows="3"><?= htmlspecialchars($profile['about'], ENT_QUOTES) ?></textarea>
</div>
<div class="flex gap-12">
<button class="btn ghost" type="button" data-modal-close>Abbrechen</button>
<button class="btn" type="submit">Speichern</button>
</div>
</form>
</div>
</div>
<div class="modal" id="modalChild">
<div class="panel">
<div class="head flex between center-y">
<h3>Kind hinzufügen</h3>
<button class="btn ghost" type="button" data-modal-close>✕</button>
</div>
<form method="post" class="stack gap-12">
<input type="hidden" name="action" value="child_add">
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="cName">Vorname</label>
<input id="cName" name="first_name" class="input" required>
</div>
<div class="stack gap-6">
<label class="label" for="cGender">Geschlecht</label>
<select id="cGender" name="gender" class="select">
<option value="male">Männlich</option>
<option value="female">Weiblich</option>
<option value="diverse">Divers</option>
<option value="unknown">Unbekannt</option>
</select>
</div>
</div>
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="cBirth">Geburtsdatum</label>
<input id="cBirth" name="birthdate" class="input" type="date">
</div>
<div class="stack gap-6">
<label class="label" for="cAge">Alter (Jahre)</label>
<input id="cAge" name="age_years" class="input" type="number" min="0" max="18">
</div>
</div>
<div class="stack gap-6">
<label class="label" for="cNote">Notiz</label>
<input id="cNote" name="note" class="input">
</div>
<div class="flex gap-12">
<button class="btn ghost" type="button" data-modal-close>Abbrechen</button>
<button class="btn" type="submit">Speichern</button>
</div>
</form>
</div>
</div>
<div class="modal" id="modalEvent">
<div class="panel">
<div class="head flex between center-y">
<h3>Neues Event</h3>
<button class="btn ghost" type="button" data-modal-close>✕</button>
</div>
<form class="stack gap-12" style="margin-top: 10px;" method="post" action="/dashboard#events">
<input type="hidden" name="action" value="event_add">
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="evTitle">Titel</label>
<input id="evTitle" name="title" class="input" placeholder="z. B. Väter-Kaffee im Park" required>
</div>
<div class="stack gap-6">
<label class="label" for="evTeaser">Kurzbeschreibung</label>
<input id="evTeaser" name="teaser" class="input" placeholder="Kurztext für Gäste" required>
</div>
</div>
<div class="stack gap-6">
<label class="label" for="evDesc">Beschreibung (voll)</label>
<textarea id="evDesc" name="description" class="textarea" rows="3" placeholder="Was soll passieren, was mitbringen?" required></textarea>
</div>
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="evDate">Datum & Uhrzeit</label>
<input id="evDate" name="starts_at" class="input" type="datetime-local" required>
</div>
<div class="stack gap-6">
<label class="label" for "evLoc">Ort/Label</label>
<input id="evLoc" name="location_label" class="input" placeholder="Park / Café">
</div>
</div>
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="evZip">PLZ</label>
<input id="evZip" name="zip" class="input" maxlength="5">
</div>
<div class="stack gap-6">
<label class="label" for="evCity">Stadt</label>
<input id="evCity" name="city" class="input">
</div>
</div>
<div class="form-grid">
<div class="stack gap-6">
<label class="label" for="evRegion">Region/Bezirk</label>
<input id="evRegion" name="region" class="input">
</div>
<div class="stack gap-6">
<label class="label" for="evVis">Sichtbarkeit</label>
<select id="evVis" name="visibility" class="select">
<option value="public">Öffentlich</option>
<option value="members">Nur Mitglieder</option>
</select>
</div>
</div>
<label class="label" style="display:flex; align-items:center; gap:8px;">
<input type="checkbox" name="allow_kids" checked> Kinder erlaubt
</label>
<div class="flex gap-12">
<button class="btn ghost" type="button" data-modal-close>Abbrechen</button>
<button class="btn" type="submit">Event anlegen</button>
</div>
</form>
</div>
</div>
</main>
<script>
document.querySelectorAll('[data-modal-open]').forEach(btn => {
btn.addEventListener('click', () => {
const id = btn.getAttribute('data-modal-open');
const modal = document.getElementById(id);
if (modal) modal.classList.add('open');
});
});
document.querySelectorAll('[data-modal-close]').forEach(btn => {
btn.addEventListener('click', () => {
const modal = btn.closest('.modal');
if (modal) modal.classList.remove('open');
});
});
document.querySelectorAll('.modal').forEach(modal => {
modal.addEventListener('click', (e) => {
if (e.target === modal) modal.classList.remove('open');
});
});
</script>