522 lines
24 KiB
PHP
522 lines
24 KiB
PHP
<?php
|
|
$app = app();
|
|
$vm = \App\AccountPages::dashboard($app);
|
|
extract($vm, EXTR_OVERWRITE);
|
|
$editing = isset($editEvent) && $editEvent !== null;
|
|
$actionEvent = $editing ? 'event_update' : 'event_add';
|
|
$startVal = $editEvent ? date('Y-m-d\TH:i', strtotime((string)$editEvent['starts_at'])) : '';
|
|
$allowNoKidsChecked = $editEvent ? ((int)$editEvent['allow_kids'] === 0) : false;
|
|
?>
|
|
<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" id="events">
|
|
<div class="card dash-card">
|
|
<div class="badge">Deine Events</div>
|
|
<div class="flex gap-12" style="margin:10px 0 16px 0; flex-wrap: wrap;">
|
|
<button class="btn" type="button" data-modal-open="modalEvent">Event anlegen</button>
|
|
</div>
|
|
<?php if (!$eventsUpcoming): ?>
|
|
<p class="muted small">Keine zukünftigen Events angelegt.</p>
|
|
<?php else: ?>
|
|
<ul class="dash-list" style="margin-top:10px;">
|
|
<?php foreach ($eventsUpcoming as $e): ?>
|
|
<li>
|
|
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center; flex-wrap: wrap;">
|
|
<div>
|
|
<strong><?= htmlspecialchars($e['title'], ENT_QUOTES) ?></strong>
|
|
<?php if ($e['status'] === 'cancelled'): ?>
|
|
<span class="badge" style="background:#fee2e2; color:#991b1b;">Abgesagt</span>
|
|
<?php endif; ?>
|
|
<div class="muted small" style="margin-top:4px;">
|
|
<?= htmlspecialchars($e['city'], ENT_QUOTES) ?> · <?= htmlspecialchars($e['starts_at'], ENT_QUOTES) ?> · <?= htmlspecialchars($e['visibility'], ENT_QUOTES) ?>
|
|
· Anmeldungen: <?= (int)$e['participant_count'] ?>
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-8" style="flex-wrap: wrap;">
|
|
<a class="btn ghost" href="/dashboard?edit_event=<?= (int)$e['id'] ?>#events">Bearbeiten</a>
|
|
<?php if ((int)$e['participant_count'] === 0): ?>
|
|
<form method="post" action="/dashboard#events" onsubmit="return confirm('Event wirklich löschen?');">
|
|
<input type="hidden" name="action" value="event_delete">
|
|
<input type="hidden" name="event_id" value="<?= (int)$e['id'] ?>">
|
|
<button class="btn ghost" type="submit">Löschen</button>
|
|
</form>
|
|
<?php else: ?>
|
|
<?php if ($e['status'] !== 'cancelled'): ?>
|
|
<form method="post" action="/dashboard#events" onsubmit="return confirm('Event für alle absagen?');">
|
|
<input type="hidden" name="action" value="event_cancel">
|
|
<input type="hidden" name="event_id" value="<?= (int)$e['id'] ?>">
|
|
<button class="btn ghost" type="submit">Absagen</button>
|
|
</form>
|
|
<?php endif; ?>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
<details style="margin-top:12px;">
|
|
<summary style="cursor:pointer;">Vergangene Events anzeigen</summary>
|
|
<?php if (!$eventsPast): ?>
|
|
<p class="muted small">Keine vergangenen Events.</p>
|
|
<?php else: ?>
|
|
<ul class="dash-list" style="margin-top:10px;">
|
|
<?php foreach ($eventsPast as $e): ?>
|
|
<li>
|
|
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center; flex-wrap: wrap;">
|
|
<div>
|
|
<strong><?= htmlspecialchars($e['title'], ENT_QUOTES) ?></strong>
|
|
<?php if ($e['status'] === 'cancelled'): ?>
|
|
<span class="badge" style="background:#fee2e2; color:#991b1b;">Abgesagt</span>
|
|
<?php endif; ?>
|
|
<div class="muted small" style="margin-top:4px;">
|
|
<?= htmlspecialchars($e['city'], ENT_QUOTES) ?> · <?= htmlspecialchars($e['starts_at'], ENT_QUOTES) ?> · <?= htmlspecialchars($e['visibility'], ENT_QUOTES) ?>
|
|
· Anmeldungen: <?= (int)$e['participant_count'] ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php endif; ?>
|
|
</details>
|
|
</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><?= $editing ? 'Event bearbeiten' : '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="<?= htmlspecialchars($actionEvent, ENT_QUOTES) ?>">
|
|
<?php if ($editing && $editEvent): ?>
|
|
<input type="hidden" name="event_id" value="<?= (int)$editEvent['id'] ?>">
|
|
<?php endif; ?>
|
|
<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 value="<?= htmlspecialchars($editEvent['title'] ?? '', ENT_QUOTES) ?>">
|
|
</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 value="<?= htmlspecialchars($editEvent['teaser_public'] ?? '', ENT_QUOTES) ?>">
|
|
</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><?= htmlspecialchars($editEvent['description'] ?? '', ENT_QUOTES) ?></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 value="<?= htmlspecialchars($startVal, ENT_QUOTES) ?>">
|
|
</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é" value="<?= htmlspecialchars($editEvent['location_label'] ?? '', ENT_QUOTES) ?>">
|
|
</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" value="<?= htmlspecialchars($editEvent['zip'] ?? '', ENT_QUOTES) ?>">
|
|
</div>
|
|
<div class="stack gap-6">
|
|
<label class="label" for="evCity">Stadt</label>
|
|
<input id="evCity" name="city" class="input" value="<?= htmlspecialchars($editEvent['city'] ?? '', ENT_QUOTES) ?>">
|
|
</div>
|
|
</div>
|
|
<div class="form-grid">
|
|
<div class="stack gap-6">
|
|
<label class="label" for="evStreet">Straße / Adresse</label>
|
|
<input id="evStreet" name="street" class="input" placeholder="z. B. Musterstraße 12" value="<?= htmlspecialchars($editEvent['street'] ?? '', ENT_QUOTES) ?>">
|
|
<p class="muted small">Wird zur Karten-/Umkreissuche genutzt.</p>
|
|
</div>
|
|
<div class="stack gap-6">
|
|
<label class="label" for="evRegion">Region/Bezirk</label>
|
|
<input id="evRegion" name="region" class="input" value="<?= htmlspecialchars($editEvent['region'] ?? '', ENT_QUOTES) ?>">
|
|
</div>
|
|
</div>
|
|
<div class="flex gap-8" style="flex-wrap:wrap; align-items:center;">
|
|
<button class="btn ghost" type="button" id="btnAddrToMap">Adresse auf Karte setzen</button>
|
|
<span class="muted small">Hält Karte und Adresse synchron.</span>
|
|
</div>
|
|
<input type="hidden" id="evLat" name="lat" value="<?= htmlspecialchars($editEvent['lat'] ?? '', ENT_QUOTES) ?>">
|
|
<input type="hidden" id="evLng" name="lng" value="<?= htmlspecialchars($editEvent['lng'] ?? '', ENT_QUOTES) ?>">
|
|
<div class="stack gap-6">
|
|
<button class="btn ghost" type="button" id="btnMap">Auf Karte suchen</button>
|
|
<div id="mapWrapper" class="map-wrapper" hidden>
|
|
<div class="form-row">
|
|
<label class="label" for="mapSearch">Adresse suchen (optional)</label>
|
|
<div class="flex gap-8">
|
|
<input id="mapSearch" class="input" placeholder="Straße, PLZ, Ort">
|
|
<button class="btn ghost" type="button" id="btnMapSearch">Suchen</button>
|
|
</div>
|
|
<p class="muted small">Klick auf die Karte setzt den Treffpunkt. Zoom/Scroll möglich.</p>
|
|
</div>
|
|
<div id="mapContainer" class="map-container"></div>
|
|
</div>
|
|
</div>
|
|
<div class="form-grid">
|
|
<div class="stack gap-6">
|
|
<label class="label" for="evVis">Sichtbarkeit</label>
|
|
<select id="evVis" name="visibility" class="select">
|
|
<option value="public" <?= (($editEvent['visibility'] ?? '') === 'public') ? 'selected' : '' ?>>Öffentlich</option>
|
|
<option value="members" <?= (($editEvent['visibility'] ?? '') === 'members') ? 'selected' : '' ?>>Nur Mitglieder</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<label class="label" style="display:flex; align-items:center; gap:8px;">
|
|
<input type="checkbox" name="allow_kids" <?= $allowNoKidsChecked ? 'checked' : '' ?>> Treffen ohne Kinder
|
|
</label>
|
|
<div class="flex gap-12">
|
|
<button class="btn ghost" type="button" data-modal-close>Abbrechen</button>
|
|
<button class="btn" type="submit"><?= $editing ? 'Event speichern' : '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');
|
|
});
|
|
});
|
|
|
|
// Map picker for events (Leaflet)
|
|
const btnMap = document.getElementById('btnMap');
|
|
const mapWrapper = document.getElementById('mapWrapper');
|
|
const mapContainer = document.getElementById('mapContainer');
|
|
const btnMapSearch = document.getElementById('btnMapSearch');
|
|
const btnAddrToMap = document.getElementById('btnAddrToMap');
|
|
const mapSearch = document.getElementById('mapSearch');
|
|
const latInput = document.getElementById('evLat');
|
|
const lngInput = document.getElementById('evLng');
|
|
const streetInput = document.getElementById('evStreet');
|
|
const zipInput = document.getElementById('evZip');
|
|
const cityInput = document.getElementById('evCity');
|
|
const regionInput = document.getElementById('evRegion');
|
|
let map, marker;
|
|
|
|
function ensureLeaflet(callback) {
|
|
if (window.L) { callback(); return; }
|
|
const css = document.createElement('link');
|
|
css.rel = 'stylesheet';
|
|
css.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
|
|
document.head.appendChild(css);
|
|
const script = document.createElement('script');
|
|
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
|
|
script.onload = callback;
|
|
document.body.appendChild(script);
|
|
}
|
|
|
|
function updateAddressFields(addr) {
|
|
if (!addr) return;
|
|
if (streetInput) {
|
|
const street = [addr.road || '', addr.house_number || ''].filter(Boolean).join(' ').trim();
|
|
if (street) streetInput.value = street;
|
|
}
|
|
if (zipInput && addr.postcode) zipInput.value = addr.postcode;
|
|
if (cityInput && (addr.city || addr.town || addr.village)) cityInput.value = addr.city || addr.town || addr.village;
|
|
if (regionInput && (addr.suburb || addr.state || addr.county)) regionInput.value = addr.suburb || addr.state || addr.county;
|
|
}
|
|
|
|
function setLatLngInputs(latlng) {
|
|
latInput.value = latlng.lat.toFixed(7);
|
|
lngInput.value = latlng.lng.toFixed(7);
|
|
}
|
|
|
|
function setMarker(latlng, center = true) {
|
|
if (!map) return;
|
|
if (marker) {
|
|
marker.setLatLng(latlng);
|
|
} else {
|
|
marker = L.marker(latlng, { draggable: true }).addTo(map);
|
|
marker.on('dragend', () => {
|
|
const ll = marker.getLatLng();
|
|
setLatLngInputs(ll);
|
|
reverseGeocode(ll.lat, ll.lng);
|
|
});
|
|
}
|
|
setLatLngInputs(latlng);
|
|
if (center) {
|
|
map.setView(latlng, Math.max(map.getZoom(), 14));
|
|
}
|
|
}
|
|
|
|
function reverseGeocode(lat, lng) {
|
|
fetch(`https://nominatim.openstreetmap.org/reverse?format=jsonv2&lat=${encodeURIComponent(lat)}&lon=${encodeURIComponent(lng)}`, {
|
|
headers: { 'Accept-Language': 'de', 'User-Agent': 'papa-kind-treff/1.0' },
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => updateAddressFields(data.address))
|
|
.catch(() => {});
|
|
}
|
|
|
|
function geocodeAndPlace(query) {
|
|
fetch('https://nominatim.openstreetmap.org/search?format=jsonv2&limit=1&q=' + encodeURIComponent(query), {
|
|
headers: { 'Accept-Language': 'de', 'User-Agent': 'papa-kind-treff/1.0' },
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (Array.isArray(data) && data[0]) {
|
|
const lat = parseFloat(data[0].lat);
|
|
const lng = parseFloat(data[0].lon);
|
|
ensureLeaflet(() => {
|
|
mapWrapper.hidden = false;
|
|
initMap();
|
|
if (!Number.isNaN(lat) && !Number.isNaN(lng)) {
|
|
setMarker({ lat, lng });
|
|
updateAddressFields(data[0].address);
|
|
}
|
|
});
|
|
}
|
|
})
|
|
.catch(() => {});
|
|
}
|
|
|
|
function initMap() {
|
|
if (map) { map.invalidateSize(); return; }
|
|
map = L.map(mapContainer).setView([51.1657, 10.4515], 6);
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
maxZoom: 19,
|
|
attribution: '© OpenStreetMap',
|
|
}).addTo(map);
|
|
|
|
map.on('click', (e) => {
|
|
setMarker(e.latlng);
|
|
reverseGeocode(e.latlng.lat, e.latlng.lng);
|
|
});
|
|
|
|
const lat = parseFloat(latInput.value);
|
|
const lng = parseFloat(lngInput.value);
|
|
if (!Number.isNaN(lat) && !Number.isNaN(lng)) {
|
|
setMarker({ lat, lng });
|
|
map.setView({ lat, lng }, 14);
|
|
}
|
|
}
|
|
|
|
btnMap?.addEventListener('click', () => {
|
|
mapWrapper.hidden = false;
|
|
ensureLeaflet(() => {
|
|
setTimeout(() => initMap(), 50);
|
|
});
|
|
});
|
|
|
|
btnMapSearch?.addEventListener('click', () => {
|
|
const q = (mapSearch?.value || '').trim();
|
|
if (!q) return;
|
|
geocodeAndPlace(q);
|
|
});
|
|
|
|
btnAddrToMap?.addEventListener('click', () => {
|
|
const parts = [
|
|
streetInput?.value || '',
|
|
zipInput?.value || '',
|
|
cityInput?.value || '',
|
|
regionInput?.value || '',
|
|
].map(v => v.trim()).filter(Boolean);
|
|
if (!parts.length) return;
|
|
geocodeAndPlace(parts.join(', '));
|
|
});
|
|
|
|
<?php if ($editing): ?>
|
|
(function(){
|
|
const modal = document.getElementById('modalEvent');
|
|
if (modal) { modal.classList.add('open'); }
|
|
})();
|
|
<?php endif; ?>
|
|
</script>
|