Skip to content

feat: kuratierte externe Kalender (Kult 41) + clickable Freund:innen-Logos#14

Merged
bmmmm merged 6 commits into
mainfrom
feat/calendar-filtered-sources
May 20, 2026
Merged

feat: kuratierte externe Kalender (Kult 41) + clickable Freund:innen-Logos#14
bmmmm merged 6 commits into
mainfrom
feat/calendar-filtered-sources

Conversation

@bmmmm
Copy link
Copy Markdown
Owner

@bmmmm bmmmm commented May 20, 2026

Summary

Calendar-Pipeline und Homepage-Slider ausgebaut, plus Refactor der Config-Struktur.

Calendar-Pipeline

  • One-file-per-source unter calendars/ mit config.json als Manifest. Eintrag aus sources raus = Quelle deaktiviert, ohne die Datei zu löschen. Konsistent für bitcircus, datenburg UND kuratierte externals.
  • Neue Quell-Typen:
    • ics-single — eine ICS-URL pro Event (z.B. kult41.de/events/foo/ical/). Aktuell: Theater Tumult bei Kult 41.
    • ics-filtered — full calendar mit categoryAllow/Deny, titleAllow/Deny Listen.
  • Parser erweitert: UID, URL, TZID aus VEVENTs. firstSeen und RSS-guid laufen auf UID mit date|title-Fallback (Migration ohne RSS-Spam).
  • Non-Europe/Berlin TZIDs werden geloggt (warn-once pro source/zone), Zeiten bleiben floating local — kein Behavior-Change für die Nextcloud-Quellen.
  • events.js: externe eventUrl werden direkt verlinkt statt /timeGridDay/<datum> anzuhängen — sonst 404 auf kult41.de.
  • Optional cap pro Source (Default 30), verhindert dass ein dichter Kalender alle Slots frisst.

Freund:innen-Logos

  • Neue images/logo-slider/logos.json mapped Dateinamen → display name + optionale URL.
  • build-logo-slider.mjs wrappt <img> in <a target=_blank rel=noopener> mit aria-label, wenn URL gesetzt.
  • Zweiter (aria-hidden duplicate) Slider-Set ist jetzt tabindex="-1" — sonst doppelte Tab-Stops.
  • Verlinkt: FrOSCon, FSFE, Kult 41. Vier weitere Logos (di.day, inlaws, leihbar, BSB Selbstwerk) haben url:"" als Platzhalter zum Nachtragen.

Tests

  • Inline-Funktionskopien rausgezogen, Tests importieren echte Symbole aus sync-events.mjs (keine Drift mehr).
  • +12 neue Tests: applyFilter (Allow/Deny/Title/Category), UID/URL-Parsing, TZID-Warn-once, eventUrl-Fallback, cap-Override, calTags-Merge.
  • 55/55 grün.

Workflow

  • sync-events.yml zieht jetzt scripts/ calendars/ aus main (statt scripts/ calendars.json), damit externals auf live verfügbar sind.

Test plan

  • npm run test:quick — 55/55 pass
  • Live-Sync gegen alle 3 Quellen: bitcircus 170 VEVENT, datenburg 199 VEVENT, kult41 1 VEVENT (single event)
  • npm run build:layout + npm run build:logos — kein unerwarteter HTML-Drift
  • Lokal Browser-Check: Logo-Slider Klick auf Kult 41 / FrOSCon / FSFE, externer Link öffnet in neuem Tab
  • Lokal Events-Seite: Theater Tumult sichtbar, "→ link" pill öffnet kult41.de/events/...
  • CI Playwright e2e läuft beim Merge zu main

bmmmm added 6 commits May 20, 2026 13:19
Neuer Quell-Typ ics-filtered in calendars.json mit categoryAllow/categoryDeny
und titleAllow/titleDeny. Erste Anwendung: Kult 41 (Hauptkalender
events.ics, Konzerte gefiltert, cap 15). Zweiter Typ ics-single bleibt vorbereitet
für künftige Single-Event-ICS-URLs.

Parser-Erweiterungen: extrahiert UID, URL, TZID. Non-Europe/Berlin TZIDs
werden geloggt (warn-once) statt umgerechnet — bisheriges Verhalten bleibt.
firstSeen und RSS-GUID nutzen jetzt UID statt date|summary, mit
date|title-Fallback für Migration von alten events-data.json-Einträgen.

events.js: bei externer eventUrl wird direkt verlinkt statt
/timeGridDay/<datum> anzuhängen — sonst 404 auf kult41.de.

Tests: Inline-Funktionskopien rausgezogen, Tests importieren echte
Symbole aus sync-events.mjs. 12 neue Tests (applyFilter, UID/URL-Parsing,
TZID-Warn, eventUrl-Fallback, cap, calTags). 55/55 pass.
Bestehender Logo-Slider rendert <img> ohne Link. Neue images/logo-slider/logos.json
maps Dateinamen → display name + optionale URL. Wenn URL gesetzt: Logo wird
in <a target=_blank rel=noopener> gewrappt mit aria-label "<Name> (externer
Link)". Ohne URL: bisheriges <img>-only Verhalten (backward compat).

Der zweite slider__set (aria-hidden Duplikat für nahtloses Scrolling) bekommt
tabindex=-1, sonst doppelte Tab-Stops. CSS: .logo-slider__link nutzt
display:contents damit das Layout (.logo-slider__item img Selektor) greift,
plus focus-visible outline auf dem img.

Erste verlinkte Logos: FrOSCon (froscon.org), FSFE (fsfe.org), Kult 41
(kult41.de/veranstaltungen/programm). Die übrigen vier Einträge in
logos.json haben url:"" als Platzhalter zum Nachtragen.

Kult 41 Logo-Datei muss noch nach images/logo-slider/kult41.png — der
Eintrag in logos.json ist vorbereitet, npm run build:logos zieht ihn
automatisch ein sobald die Datei da ist.
Statt Master-Feed mit Konzert-Filter (15 Events) ziehen wir aktuell nur
das eine thematisch passende Event: Theater Tumult: K.I. und Abel + Reggae.
Der ics-single-Typ war genau dafür vorbereitet — pro Event ein JSON-Block
mit der einzelnen ICS-URL des Plugins (wp-events-plugin/Events Manager).

Output: 1 VEVENT → 1 card. Link geht direkt zur kult41.de-Event-Seite.
Weitere Events können einfach durch zusätzliche ics-single-Einträge
ergänzt werden (jeweils mit unique id).
Pro kuratiertem externen Event/Source eine eigene File statt eines wachsenden
Arrays in calendars.json. Schöneres git-diff bei Add/Remove, klare 1:1-Sicht
"Datei = Quelle".

calendars.json bleibt für die stabilen primary feeds (bitcircus + datenburg).
calendars/external/*.json ergänzt — jede Datei = ein Source-Objekt im selben
Format wie ein calendars.json-Eintrag. sync-events.mjs mergt beides über
neuen loadCalendars()-Helper, der defekte/unvollständige Files mit Warnung
überspringt statt das ganze Sync zu brechen.

Workflow zieht jetzt auch calendars/ aus main, sonst fehlen externals auf
dem live-Branch.

Erstes externes File: Theater Tumult bei Kult 41. Docs in CLAUDE.md und
README beschreiben die neue Convention plus die source types
(ics-full/ics-single/ics-filtered).
…nifest

calendars.json (root) → weg. Stattdessen lebt jede Quelle in einer eigenen
JSON-Datei unter calendars/. calendars/config.json ist das Manifest, das
auflistet welche Sources verarbeitet werden und in welcher Reihenfolge.
Eintrag rausnehmen = Source deaktivieren, ohne die Datei zu löschen.

Konsistent für stable primary feeds (bitcircus, datenburg) UND kuratierte
externals (calendars/external/*) — alles über das gleiche Pattern.

loadCalendars() liest jetzt das Manifest und lädt die referenzierten Files.
Fehlende oder defekte Files werden mit Warning übersprungen, das Sync läuft
weiter (eine kaputte Source kann nicht alles brechen).

Workflow zieht nur noch calendars/ aus main. Docs in CLAUDE.md + README
zeigen die neue Struktur und Tabelle der aktiven Sources.
@bmmmm bmmmm merged commit 76d6527 into main May 20, 2026
1 check passed
@bmmmm bmmmm deleted the feat/calendar-filtered-sources branch May 20, 2026 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant