Spiel-Events, Kalenderdateien, Patches-Seite und neues Design

- Kalender: Termine als Spiel-Event markieren (nur diese zählen in der
  Statistik), optionale Uhrzeiten, Termine bearbeiten
- .ics-Download für einzelne und alle kommenden Termine
- Neue Seite Patches: Upload (PNG, JPEG, WebP, geprüftes SVG),
  Status "Gibt es schon" / "In Planung", Bilder in data/patches/
- Volle Seitenbreite mit Spaltenlayouts, neues Design mit selbst
  gehosteten Schriften, Startseite mit Zoom und drehendem Zahnrad
- Bestehende Daten werden beim Laden ergänzt, keine Migration nötig

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
guyg1474andClaude Opus 5.5 committed 2026-10-12 00:13:52 +02:00
1 parent 43356a120c
commit 486e6f6784
23 files changed
+1757 -387

No files matched your search

+24 -4
View File
@@ -80,11 +80,13 @@ const NAV = [
{ href: '/fahrt', key: 'fahrt', label: 'Fahrtplaner' },
{ href: '/gruppe', key: 'gruppe', label: 'Gruppe' },
{ href: '/statistik', key: 'statistik', label: 'Statistik' },
{ href: '/patches', key: 'patches', label: 'Patches' },
];
export function renderHeader(active) {
// overlay: Kopfzeile liegt durchsichtig über dem Hero und wird erst beim Scrollen fest (Startseite)
export function renderHeader(active, { overlay = false } = {}) {
const header = document.createElement('header');
header.className = 'site-header';
header.className = overlay ? 'site-header overlay' : 'site-header';
header.innerHTML = `
<div class="inner">
<a class="brand" href="/"><img src="/img/logo.png" alt="" width="40" height="40">HCRB<small>Highcap Rasselbande</small></a>
@@ -98,6 +100,11 @@ export function renderHeader(active) {
</nav>
</div>`;
document.body.prepend(header);
if (overlay) {
const update = () => header.classList.toggle('scrolled', scrollY > 40);
addEventListener('scroll', update, { passive: true });
update();
}
// Auf schmalen Bildschirmen klappt das Menü über die Schaltfläche auf
const toggle = header.querySelector('.nav-toggle');
toggle.addEventListener('click', () => {
@@ -114,7 +121,7 @@ export function renderHeader(active) {
export function renderFooter() {
const footer = document.createElement('footer');
footer.innerHTML = `
HCRB · Highcap Rasselbande · Berlin
<span class="f-brand"><img src="/img/logo.png" alt="" width="32" height="32">HCRB · Highcap Rasselbande · Berlin</span>
<div class="footer-links">
<a href="https://www.youtube.com/@highcaprasselbande" target="_blank" rel="noopener">YouTube</a>
<a href="https://www.instagram.com/hcrb.airsoft" target="_blank" rel="noopener">Instagram</a>
@@ -122,8 +129,15 @@ export function renderFooter() {
document.body.append(footer);
}
// Einmal pro Seite abfragen, ob jemand angemeldet ist
let mePromise;
export function currentUser() {
mePromise ??= api('GET', '/me').then(({ user }) => user || null).catch(() => null);
return mePromise;
}
async function renderAuthLink(nav) {
const { user } = await api('GET', '/me').catch(() => ({}));
const user = await currentUser();
if (!user) {
nav.insertAdjacentHTML('beforeend', '<a href="/login">Anmelden</a>');
return;
@@ -138,3 +152,9 @@ async function renderAuthLink(nav) {
});
nav.append(btn);
}
// Kurzes Zeitformat für Termine: "09:00 Uhr" oder "09:00–15:00 Uhr"
export function formatTime(ev) {
if (!ev.time) return '';
return `${ev.time}${ev.endTime ? `–${ev.endTime}` : ''} Uhr`;
}