asdasd
This commit is contained in:
@@ -114,7 +114,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
'lat' => $lat,
|
||||
'lng' => $lng,
|
||||
'start' => $_POST['starts_at'] ?? null,
|
||||
'allow' => isset($_POST['allow_kids']) ? 1 : 0,
|
||||
'allow' => isset($_POST['allow_kids']) ? 0 : 1, // checkbox = Treffen ohne Kinder
|
||||
'vis' => $_POST['visibility'] ?? 'public',
|
||||
'status' => 'published',
|
||||
]);
|
||||
@@ -334,9 +334,6 @@ $eventsPast = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
|
||||
</div>
|
||||
</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>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
|
||||
@@ -1,6 +1,38 @@
|
||||
<?php
|
||||
$app = app();
|
||||
$flash = $app->flash()->get();
|
||||
|
||||
$eventsForJs = [];
|
||||
try {
|
||||
$pdo = $app->pdo();
|
||||
if ($pdo) {
|
||||
$stmt = $pdo->prepare('SELECT id, title, teaser_public, description, city, region, zip, starts_at, allow_kids, visibility, location_label, lat, lng FROM events WHERE starts_at >= NOW() AND status != "cancelled" ORDER BY starts_at ASC LIMIT 50');
|
||||
$stmt->execute();
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
|
||||
foreach ($rows as $r) {
|
||||
$eventsForJs[] = [
|
||||
'id' => (int)$r['id'],
|
||||
'title' => $r['title'],
|
||||
'teaser' => $r['teaser_public'],
|
||||
'description' => $r['description'],
|
||||
'city' => $r['city'],
|
||||
'zip' => $r['zip'],
|
||||
'region' => $r['region'],
|
||||
'topic' => 'general',
|
||||
'startsAt' => $r['starts_at'],
|
||||
'allowKids' => ((int)$r['allow_kids'] === 1),
|
||||
'ageGroup' => '',
|
||||
'visibility' => $r['visibility'],
|
||||
'contact' => '',
|
||||
'locationLabel' => $r['location_label'],
|
||||
'lat' => $r['lat'] !== null ? (float)$r['lat'] : null,
|
||||
'lng' => $r['lng'] !== null ? (float)$r['lng'] : null,
|
||||
];
|
||||
}
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
$eventsForJs = [];
|
||||
}
|
||||
?>
|
||||
<main>
|
||||
<?php if ($flash): ?>
|
||||
@@ -63,7 +95,7 @@ $flash = $app->flash()->get();
|
||||
<div class="section__head">
|
||||
<div>
|
||||
<p class="eyebrow">Termine entdecken</p>
|
||||
<h2>Upcoming Events in deiner Nähe</h2>
|
||||
<h2>Die nächsten anstehenden Events</h2>
|
||||
<p class="muted">Gäste sehen nur Basisinfos. Als Mitglied siehst du vollständige Details, kannst zusagen und neue Treffen anlegen.</p>
|
||||
</div>
|
||||
<div class="chips">
|
||||
@@ -187,3 +219,6 @@ $flash = $app->flash()->get();
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<script>
|
||||
window.__events = <?= json_encode($eventsForJs, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>;
|
||||
</script>
|
||||
|
||||
@@ -49,75 +49,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
|
||||
// Demo-Events
|
||||
const events = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'Spielplatzrunde im Park',
|
||||
teaser: 'Lockeres Treffen mit Kaffee, Sandspielzeug und Picknick.',
|
||||
description: 'Wir treffen uns am großen Spielplatz im Kiez. Bringt Snacks mit, wir teilen. Für alle Altersstufen offen.',
|
||||
city: 'Berlin',
|
||||
zip: '10437',
|
||||
region: 'Prenzlauer Berg',
|
||||
topic: 'outdoor',
|
||||
startsAt: '2025-08-10T10:00:00',
|
||||
allowKids: true,
|
||||
ageGroup: 'kids',
|
||||
visibility: 'public',
|
||||
contact: 'papa-berlin@example.com',
|
||||
locationLabel: 'Mauerpark',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: 'Väter-Kaffee & Austausch',
|
||||
teaser: 'Elternzeit, Job, Schlaf – wir reden über alles.',
|
||||
description: 'Reservierter Tisch im Café. Fokus auf Austausch unter Vätern, Kinder optional.',
|
||||
city: 'Hamburg',
|
||||
zip: '22767',
|
||||
region: 'Altona',
|
||||
topic: 'kaffee',
|
||||
startsAt: '2025-08-12T19:00:00',
|
||||
allowKids: false,
|
||||
ageGroup: '',
|
||||
visibility: 'members',
|
||||
contact: 'altona-dads@example.com',
|
||||
locationLabel: 'Café Elbseite',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: 'Sport & Spiel im Park',
|
||||
teaser: 'Ballspiele und leichtes Workout, Kinder toben mit.',
|
||||
description: 'Wir bringen Bälle und Slackline. Warm-up, danach freies Spiel. Bitte Wasser mitbringen.',
|
||||
city: 'München',
|
||||
zip: '80804',
|
||||
region: 'Schwabing',
|
||||
topic: 'sport',
|
||||
startsAt: '2025-08-15T11:00:00',
|
||||
allowKids: true,
|
||||
ageGroup: 'school',
|
||||
visibility: 'public',
|
||||
contact: 'schwabing-sport@example.com',
|
||||
locationLabel: 'Luitpoldpark',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: 'Workshop: Erste Hilfe für Kids',
|
||||
teaser: 'Erste Hilfe Basics speziell für Kinder-Unfälle.',
|
||||
description: 'Zertifizierter Trainer, kleiner Materialbeitrag. Kinder können mitgebracht werden.',
|
||||
city: 'Köln',
|
||||
zip: '50667',
|
||||
region: 'Innenstadt',
|
||||
topic: 'workshop',
|
||||
startsAt: '2025-08-20T18:30:00',
|
||||
allowKids: true,
|
||||
ageGroup: 'kids',
|
||||
visibility: 'members',
|
||||
contact: 'koeln-workshop@example.com',
|
||||
locationLabel: 'Familienzentrum Mitte',
|
||||
},
|
||||
];
|
||||
// Events from backend (injected on page); fallback to empty
|
||||
const events = Array.isArray(window.__events) ? window.__events : [];
|
||||
|
||||
const state = { topic: 'all', age: '', region: '', query: '' };
|
||||
const state = { topic: 'all', age: '', region: '', query: '', geo: null };
|
||||
const el = {
|
||||
list: document.getElementById('eventList'),
|
||||
chips: document.querySelectorAll('[data-filter]'),
|
||||
@@ -133,6 +68,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
return d.toLocaleDateString('de-DE', { weekday: 'short', day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' });
|
||||
};
|
||||
|
||||
const haversine = (lat1, lon1, lat2, lon2) => {
|
||||
const toRad = (d) => d * Math.PI / 180;
|
||||
const R = 6371e3;
|
||||
const dLat = toRad(lat2 - lat1);
|
||||
const dLon = toRad(lon2 - lon1);
|
||||
const a = Math.sin(dLat/2) ** 2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon/2) ** 2;
|
||||
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
||||
return R * c;
|
||||
};
|
||||
|
||||
const tag = (label) => `<span class="badge">${label}</span>`;
|
||||
|
||||
const renderCard = (item) => {
|
||||
@@ -170,16 +115,39 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
};
|
||||
|
||||
const matchesFilter = (ev) => {
|
||||
const topicOk = state.topic === 'all' || state.topic === ev.topic;
|
||||
const topicOk = state.topic === 'all' || state.topic === ev.topic || state.topic === '';
|
||||
const ageOk = !state.age || ev.ageGroup === state.age || (state.age === 'baby' && ev.ageGroup === 'kids'); // simple fallback
|
||||
const regionOk = !state.region || ev.region.toLowerCase().includes(state.region) || ev.city.toLowerCase().includes(state.region) || ev.zip.startsWith(state.region);
|
||||
const queryOk = !state.query || (ev.title + ev.teaser + ev.description + ev.city + ev.region).toLowerCase().includes(state.query);
|
||||
const regionField = (ev.region || '').toLowerCase();
|
||||
const cityField = (ev.city || '').toLowerCase();
|
||||
const zipField = (ev.zip || '');
|
||||
const queryHaystack = (ev.title + ev.teaser + ev.description + (ev.city || '') + (ev.region || '')).toLowerCase();
|
||||
const regionOk = !state.region || regionField.includes(state.region) || cityField.includes(state.region) || zipField.startsWith(state.region);
|
||||
const queryOk = !state.query || queryHaystack.includes(state.query);
|
||||
return topicOk && ageOk && regionOk && queryOk;
|
||||
};
|
||||
|
||||
const renderEvents = () => {
|
||||
if (!el.list) return;
|
||||
const filtered = events.filter(matchesFilter);
|
||||
let filtered = events.filter(matchesFilter);
|
||||
filtered.sort((a,b) => new Date(a.startsAt) - new Date(b.startsAt));
|
||||
|
||||
if (state.geo) {
|
||||
const withCoords = filtered.filter(ev => ev.lat !== null && ev.lng !== null);
|
||||
withCoords.forEach(ev => { ev._distance = haversine(state.geo.lat, state.geo.lng, ev.lat, ev.lng); });
|
||||
withCoords.sort((a,b) => (a._distance || Infinity) - (b._distance || Infinity));
|
||||
const near = withCoords.filter(ev => ev._distance <= 5000).slice(0,5);
|
||||
const fallback = withCoords.slice(0,5);
|
||||
filtered = (near.length ? near : fallback).map(ev => {
|
||||
const copy = { ...ev };
|
||||
if (ev._distance) {
|
||||
copy.teaser = `${ev.teaser} · ca. ${(ev._distance/1000).toFixed(1)} km entfernt`;
|
||||
}
|
||||
return copy;
|
||||
});
|
||||
} else {
|
||||
filtered = filtered.slice(0,5);
|
||||
}
|
||||
|
||||
el.list.innerHTML = filtered.map(renderCard).join('') || '<p class="muted">Keine Events gefunden.</p>';
|
||||
};
|
||||
|
||||
@@ -212,8 +180,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
return;
|
||||
}
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
() => {
|
||||
state.region = 'nähe';
|
||||
(pos) => {
|
||||
state.geo = { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
||||
renderEvents();
|
||||
},
|
||||
() => alert('Standort konnte nicht ermittelt werden.')
|
||||
|
||||
Reference in New Issue
Block a user