226 lines
7.9 KiB
JavaScript
226 lines
7.9 KiB
JavaScript
document.addEventListener('DOMContentLoaded', () => {
|
||
const body = document.body;
|
||
const isLoggedIn = body.dataset.auth === '1';
|
||
const childGender = body.dataset.childGender || '';
|
||
const header = document.querySelector('.site-header');
|
||
const headerSentinel = document.getElementById('headerSentinel');
|
||
|
||
// Logo-Logik
|
||
const pickLogo = (gender) => {
|
||
if (gender === 'female') return 'logo_female.png';
|
||
if (gender === 'male') return 'logo_male.png';
|
||
return Math.random() < 0.5 ? 'logo_female.png' : 'logo_male.png';
|
||
};
|
||
const chosenLogo = pickLogo(childGender);
|
||
document.querySelectorAll('[data-logo-img]').forEach(img => {
|
||
img.src = `/assets/bilder/${chosenLogo}`;
|
||
});
|
||
|
||
// Header shrink via IntersectionObserver (no flicker around threshold)
|
||
if (header && headerSentinel && 'IntersectionObserver' in window) {
|
||
const observer = new IntersectionObserver(([entry]) => {
|
||
header.classList.toggle('is-scrolled', !entry.isIntersecting);
|
||
}, {
|
||
rootMargin: '-1px 0px 0px 0px',
|
||
threshold: 0,
|
||
});
|
||
observer.observe(headerSentinel);
|
||
} else if (header) {
|
||
// Fallback with simple threshold
|
||
const limit = 120;
|
||
const onScroll = () => header.classList.toggle('is-scrolled', window.scrollY > limit);
|
||
onScroll();
|
||
window.addEventListener('scroll', onScroll, { passive: true });
|
||
}
|
||
|
||
// Mobile Menü
|
||
const mobileMenu = document.getElementById('mobileMenu');
|
||
document.querySelectorAll('.menu-toggle').forEach(btn => {
|
||
btn.addEventListener('click', () => {
|
||
mobileMenu?.classList.toggle('open');
|
||
});
|
||
});
|
||
|
||
// Scroll zu Events
|
||
const scrollBtn = document.getElementById('scrollToEvents');
|
||
if (scrollBtn) {
|
||
scrollBtn.addEventListener('click', () => {
|
||
document.getElementById('events')?.scrollIntoView({ behavior: 'smooth' });
|
||
});
|
||
}
|
||
|
||
// 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',
|
||
},
|
||
];
|
||
|
||
const state = { topic: 'all', age: '', region: '', query: '' };
|
||
const el = {
|
||
list: document.getElementById('eventList'),
|
||
chips: document.querySelectorAll('[data-filter]'),
|
||
locInput: document.getElementById('locInput'),
|
||
topicSelect: document.getElementById('topicSelect'),
|
||
ageSelect: document.getElementById('ageSelect'),
|
||
btnSearch: document.getElementById('btnSearch'),
|
||
btnGeo: document.getElementById('btnGeo'),
|
||
};
|
||
|
||
const fmtDate = (iso) => {
|
||
const d = new Date(iso);
|
||
return d.toLocaleDateString('de-DE', { weekday: 'short', day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' });
|
||
};
|
||
|
||
const tag = (label) => `<span class="badge">${label}</span>`;
|
||
|
||
const renderCard = (item) => {
|
||
const guest = !isLoggedIn;
|
||
const access = item.visibility === 'members' && guest ? '<div class="event__access">Nur für Mitglieder</div>' : '';
|
||
const desc = guest ? `<p class="muted">Melde dich an, um die volle Beschreibung zu sehen.</p>` : `<p>${item.description}</p>`;
|
||
const contact = !guest ? `<div class="muted small">Kontakt: ${item.contact}</div>` : '';
|
||
const kids = item.allowKids ? 'Mit Kindern' : 'Ohne Kinder';
|
||
const tags = [
|
||
tag(item.topic === 'kaffee' ? 'Kaffee & Austausch' : item.topic.charAt(0).toUpperCase() + item.topic.slice(1)),
|
||
tag(kids),
|
||
item.ageGroup ? tag(`Alter: ${item.ageGroup}`) : '',
|
||
].filter(Boolean).join('');
|
||
|
||
return `
|
||
<article class="card">
|
||
<div class="event__body">
|
||
${access}
|
||
<div class="event__meta">
|
||
<span>${fmtDate(item.startsAt)}</span>
|
||
<span>📍 ${item.region || item.city}</span>
|
||
<span>${item.visibility === 'public' ? 'Öffentlich' : 'Mitglieder'}</span>
|
||
</div>
|
||
<h3>${item.title}</h3>
|
||
<p class="muted">${guest ? item.teaser : item.description.slice(0, 140) + '...'}</p>
|
||
${guest ? '' : desc}
|
||
<div class="event__tags">${tags}</div>
|
||
${contact}
|
||
<div class="flex gap-12">
|
||
<button class="btn ghost">Details</button>
|
||
${!guest ? '<button class="btn">Teilnehmen</button>' : '<button class="btn">Anmelden</button>'}
|
||
</div>
|
||
</div>
|
||
</article>`;
|
||
};
|
||
|
||
const matchesFilter = (ev) => {
|
||
const topicOk = state.topic === 'all' || state.topic === ev.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);
|
||
return topicOk && ageOk && regionOk && queryOk;
|
||
};
|
||
|
||
const renderEvents = () => {
|
||
if (!el.list) return;
|
||
const filtered = events.filter(matchesFilter);
|
||
el.list.innerHTML = filtered.map(renderCard).join('') || '<p class="muted">Keine Events gefunden.</p>';
|
||
};
|
||
|
||
if (el.chips.length > 0) {
|
||
el.chips[0].classList.add('active');
|
||
}
|
||
|
||
el.chips.forEach(chip => {
|
||
chip.addEventListener('click', () => {
|
||
el.chips.forEach(c => c.classList.remove('active'));
|
||
chip.classList.add('active');
|
||
state.topic = chip.dataset.filter;
|
||
renderEvents();
|
||
});
|
||
});
|
||
|
||
if (el.btnSearch) {
|
||
el.btnSearch.addEventListener('click', () => {
|
||
state.region = (el.locInput?.value || '').trim().toLowerCase();
|
||
state.topic = el.topicSelect?.value || 'all';
|
||
state.age = el.ageSelect?.value || '';
|
||
renderEvents();
|
||
});
|
||
}
|
||
|
||
if (el.btnGeo) {
|
||
el.btnGeo.addEventListener('click', () => {
|
||
if (!navigator.geolocation) {
|
||
alert('Geolocation wird nicht unterstützt.');
|
||
return;
|
||
}
|
||
navigator.geolocation.getCurrentPosition(
|
||
() => {
|
||
state.region = 'nähe';
|
||
renderEvents();
|
||
},
|
||
() => alert('Standort konnte nicht ermittelt werden.')
|
||
);
|
||
});
|
||
}
|
||
|
||
renderEvents();
|
||
});
|