Sportfest-Ausgabe der Stadionzeitung zum Heimspiel gegen ATS Wartenfels
Neue Live-Seiten, geschaltet vom chat-gepflegten Feld `veranstaltung` (Slug aus data/veranstaltungen.json) — Programm und Turnierpläne kommen damit aus derselben Quelle wie die Website, nicht als Kopie ins Heft: - Programm-Seite (programm[], rendert zugleich auf /veranstaltungen/sportfest-2026) - vier Turnierplan-Seiten (turniere[]) mit leerer Ergebnisspalte und leerer Abschlusstabelle zum Eintragen am Platz - Gegnerseite je Mannschaft mit Heimspiel am Fest statt nur der tragenden: stadionzeitung_gegner_fixtures() ist die einzige Stelle, die das entscheidet, Seitenplanung und Snapshot lesen beide dort. `gegner` ist deshalb eine Map je Team-Key; die Vorschau überspringt jedes Spiel mit eigener Gegnerseite - Danke-Seite mit Partner-Logo-Wand aus data/partners.json und QR auf /partner-werden, dazu das Anzeigen-Feld `platzierung` für Motive mit festem Platz auf einer Live-Seite (Emons über der Logo-Wand). Vorher klebte der Dank als leere Fläche unten an einem Anzeigen-Bild - Cover einer Sonderausgabe: das Fest als Titelgeschichte statt einer der drei Begegnungen, dazu das Hauptplakat schräg gelegt an Stelle des Spielerfotos Ausgabe 2026-09-04-ats-wartenfels als ENTWURF angelegt, 48 Seiten (12 Bögen), Tabellen auf gerader Seite, längster Anzeigenblock zwei. Veröffentlicht wird auf dem Server, dort liegt der frische Matchcenter-Stand. Nebenbei behoben: bin/anzeige-add.php räumte beim Anlegen alle Motive weg, deren Slug mit demselben Text beginnt (`emons` löschte `emons-partnerdank` und `emons-stuebinger`). Muster jetzt auf die Breiten-Endung eingeschränkt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KubsajzuV6N6QsSd3xKNjC
This commit is contained in:
@@ -36,11 +36,12 @@ $issues = $issues ?? [];
|
||||
<time datetime="<?= e($issue['date']) ?>"><?= e(german_date($issue['date'])) ?></time>
|
||||
· <?= (int) $issue['pages'] ?> Seiten
|
||||
</p>
|
||||
<?php /* Coverzeile des Interviews als Teaser — dieselbe Quelle, die auch auf dem
|
||||
Cover unter „Im Heft" steht. Sie füllt die breite Zeile mit echtem Inhalt
|
||||
statt Leerfläche und sagt, warum man die Ausgabe öffnen sollte. Trägt eine
|
||||
Ausgabe kein Interview, entfällt die Zeile (wie bei .veranstaltung-card__text). */
|
||||
$kartenTeaser = trim((string) ($issue['interview']['teaser'] ?? '')); ?>
|
||||
<?php /* Coverzeile als Teaser — dieselbe Quelle (stadionzeitung_teaser()), die auch
|
||||
auf dem Cover unter „Im Heft" steht: cover.teaser einer Sonderausgabe, sonst
|
||||
die Coverzeile des Interviews. Sie füllt die breite Zeile mit echtem Inhalt
|
||||
statt Leerfläche und sagt, warum man die Ausgabe öffnen sollte. Gibt es
|
||||
beides nicht, entfällt die Zeile (wie bei .veranstaltung-card__text). */
|
||||
$kartenTeaser = stadionzeitung_teaser($issue); ?>
|
||||
<?php if ($kartenTeaser !== ''): ?>
|
||||
<p class="stadionzeitung-card__text"><?= e($kartenTeaser) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -69,18 +69,64 @@ if ($match !== null) {
|
||||
|
||||
// Gedruckte Zeile neben dem QR-Code — immer die echte Domain (club_domain()).
|
||||
$linkText = club_domain();
|
||||
$kicker = trim($season . ($date !== '' ? ' · ' . german_date($date) : ''));
|
||||
// Aufmacher unten links: normal die Begegnung (Wappen + „Heimspiel gegen …").
|
||||
// Zeigt die Ausgabe auf eine Veranstaltung (Sportfest-Ausgabe), tritt an deren
|
||||
// Stelle das FEST als Titelgeschichte — denn ein Fest bringt mehrere Heimspiele
|
||||
// mit (Erste, Zweite, Frauen), und ein einzelnes davon aufs Cover zu heben wäre
|
||||
// falsch (Felix, 03.09.2026). Titel und Zeitraum kommen aus
|
||||
// data/veranstaltungen.json, nie neu getippt.
|
||||
$feature = null;
|
||||
$featureV = veranstaltung_find((string) ($issue['veranstaltung'] ?? ''));
|
||||
if ($featureV !== null) {
|
||||
$feature = [
|
||||
'title' => trim((string) ($featureV['title'] ?? '')),
|
||||
'range' => german_date_range((string) ($featureV['date_start'] ?? ''), $featureV['date_end'] ?? null),
|
||||
];
|
||||
}
|
||||
|
||||
// Bildmotiv rechts: normal das freigestellte Spielerfoto. Eine Sonderausgabe mit
|
||||
// Veranstaltung zeigt stattdessen deren HAUPTPLAKAT (erstes Plakat, dieselbe
|
||||
// Quelle wie /veranstaltungen/<slug>, nie eine Kopie) — leicht schräg gelegt mit
|
||||
// zwei abgerundeten Ecken, wie ein aufs Cover gelegter Aushang (Felix,
|
||||
// 03.09.2026). Ohne Plakat bleibt es beim Spielerfoto, ohne beides trägt der
|
||||
// Hintergrund die Seite allein.
|
||||
$poster = null;
|
||||
if ($featureV !== null) {
|
||||
$ersteseite = $featureV['plakate'][0] ?? null;
|
||||
if (is_array($ersteseite) && !empty($ersteseite['base'])) {
|
||||
$poster = $ersteseite;
|
||||
}
|
||||
}
|
||||
|
||||
// Kicker: Saison · Datum, auf jedem Cover gleich. Eine Sonderausgabe bekommt
|
||||
// bewusst KEINE zusätzliche Kennung im Kopf: ein roter Kasten „Sportfest-Ausgabe"
|
||||
// war gebaut und am 03.09.2026 wieder entfernt (Felix) — er stach aus dem
|
||||
// ruhigen Kopf heraus, und die Ausgabe kennzeichnet sich unten links ohnehin
|
||||
// selbst durch den großen Fest-Titel.
|
||||
$kicker = implode(' · ', array_filter([
|
||||
$season,
|
||||
$date !== '' ? german_date($date) : '',
|
||||
], static fn (string $s): bool => $s !== ''));
|
||||
|
||||
// Zweite Coverzeile (Magazin-Praxis: Teaser auf die Geschichte im Heft) —
|
||||
// erscheint automatisch, sobald die Ausgabe ein Interview trägt
|
||||
// (interview-Feld, chat-gepflegt wie das Vorwort).
|
||||
$teaser = trim((string) ($issue['interview']['teaser'] ?? ''));
|
||||
// aus stadionzeitung_teaser(): `cover.teaser` einer Sonderausgabe, sonst die
|
||||
// Coverzeile des Interviews. Dieselbe Funktion speist die Archivkarte.
|
||||
$teaser = stadionzeitung_teaser($issue);
|
||||
?>
|
||||
<div class="stadionzeitung-cover">
|
||||
<div class="stadionzeitung-cover<?= $poster !== null ? ' stadionzeitung-cover--poster' : '' ?>">
|
||||
<?php if ($heroImage): ?>
|
||||
<?php component('img', ['image' => $heroImage, 'sizes' => '480px', 'class' => 'stadionzeitung-cover__bg']); ?>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($playerImage)): ?>
|
||||
<?php if ($poster !== null): ?>
|
||||
<?php /* Alt-Text bewusst leer: der Plakat-Alt-Text aus veranstaltungen.json
|
||||
beschreibt die Termine, die drei Zeilen weiter unten als Text stehen —
|
||||
auf dem Cover wäre er eine Dopplung fürs Screenreader-Ohr. */ ?>
|
||||
<?php component('img', [
|
||||
'image' => ['base' => $poster['base'], 'widths' => $poster['widths'] ?? [], 'alt' => ''],
|
||||
'class' => 'stadionzeitung-cover__poster',
|
||||
'sizes' => '(max-width: 600px) 55vw, 260px',
|
||||
]); ?>
|
||||
<?php elseif (!empty($playerImage)): ?>
|
||||
<?php
|
||||
/* Varianten-Set (base/widths/ext=png) statt der Originaldatei. Das Original ist
|
||||
ein Freisteller mit 2584x5093 Pixeln und 12 MB: iOS Safari verwirft Bilder,
|
||||
@@ -112,6 +158,17 @@ $teaser = trim((string) ($issue['interview']['teaser'] ?? ''));
|
||||
<p class="stadionzeitung-cover__tagline">Rund ums Heimspiel im Katzbachtal</p>
|
||||
</header>
|
||||
<div class="stadionzeitung-cover__corner">
|
||||
<?php if ($feature !== null): ?>
|
||||
<?php /* Sonderausgabe: das Fest ist die Titelgeschichte, nicht eine der
|
||||
Begegnungen. Titel in der Display-Schrift (Versalien wie der Masthead),
|
||||
Zeitraum darunter als gesperrte Zeile. */ ?>
|
||||
<div class="stadionzeitung-cover__feature">
|
||||
<p class="stadionzeitung-cover__feature-title"><?= e($feature['title']) ?></p>
|
||||
<?php if ($feature['range'] !== ''): ?>
|
||||
<p class="stadionzeitung-cover__meta"><?= e($feature['range']) ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="stadionzeitung-cover__headline">
|
||||
<?php /* Bewusst ohne Trennzeichen zwischen den Wappen (Felix, 29.07.2026:
|
||||
ruhiger) — die beiden Logos sprechen für sich. */ ?>
|
||||
@@ -124,6 +181,7 @@ $teaser = trim((string) ($issue['interview']['teaser'] ?? ''));
|
||||
<p class="stadionzeitung-cover__meta"><?= e(trim($teamName . ($league !== '' ? ' · ' . $league : ''))) ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if ($teaser !== ''): ?>
|
||||
<p class="stadionzeitung-cover__teaser">
|
||||
<span class="stadionzeitung-cover__teaser-label">Im Heft</span>
|
||||
|
||||
65
app/components/stadionzeitung-danke.php
Normal file
65
app/components/stadionzeitung-danke.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Danke-Seite der Stadionzeitung (Live-Seite): der Dank an unsere Partner mit
|
||||
* der Logo-Wand aus data/partners.json (Single Source, dieselbe Quelle wie die
|
||||
* Startseite) und dem QR auf /partner-werden. Gegenstück zur Akquise-Seite
|
||||
* stadionzeitung-partner.php: hier stehen die, die schon dabei sind.
|
||||
*
|
||||
* Vorgeschichte: der Dank war Teil eines Canva-Bilds, das unten an die
|
||||
* Emons-Anzeige geklebt war — eine leere Fläche mit Wappen und zwei Zeilen
|
||||
* (Felix, 03.09.2026: „sieht schrecklich aus"). Seitdem eine echte Seite, die
|
||||
* die Partner auch zeigt statt nur zu erwähnen.
|
||||
*
|
||||
* Die Anzeige oben ist KEIN Bestandteil der Komponente, sondern kommt als Prop:
|
||||
* bin/stadionzeitung-add.php nimmt Anzeigen mit `platzierung: danke` aus dem
|
||||
* Streu-Pool und schreibt das Motiv in den Seiteneintrag. Dadurch ist es in
|
||||
* images[] eingefroren wie jede andere Anzeige — eine veröffentlichte Ausgabe
|
||||
* ändert sich nicht mehr, wenn der Sponsor später ein neues Motiv liefert.
|
||||
*
|
||||
* Props: $anzeige (Varianten-Set {base,widths,alt} oder null)
|
||||
*/
|
||||
$anzeige = (is_array($anzeige ?? null) && !empty($anzeige['base'])) ? $anzeige : null;
|
||||
$partners = json_load('partners')['partners'] ?? [];
|
||||
$qr = is_file(PUBLIC_PATH . '/assets/img/stadionzeitung/qr-partner-werden.png')
|
||||
? 'img/stadionzeitung/qr-partner-werden.png'
|
||||
: null;
|
||||
?>
|
||||
<div class="stadionzeitung-danke">
|
||||
<?php if ($anzeige !== null): ?>
|
||||
<?php component('img', ['image' => $anzeige, 'sizes' => '420px', 'class' => 'stadionzeitung-danke__anzeige']); ?>
|
||||
<?php endif; ?>
|
||||
<header class="stadionzeitung-page__head stadionzeitung-danke__head">
|
||||
<p class="stadionzeitung-page__kicker">Unsere Partner</p>
|
||||
<?php /* Überschrift bewusst OHNE Anrede: Partner sind Unternehmen, die wir mit
|
||||
„Sie" ansprechen (Sprachregel, CLAUDE.md), ein „danke, dass ihr dabei
|
||||
seid" würde sie duzen. „Danke für die Unterstützung" trägt den Dank
|
||||
ohne diesen Konflikt, der CTA darunter bleibt in der Sie-Form. */ ?>
|
||||
<h2 class="stadionzeitung-page__heading">Danke für die Unterstützung</h2>
|
||||
</header>
|
||||
<?php if ($partners !== []): ?>
|
||||
<ul class="stadionzeitung-danke__logos">
|
||||
<?php foreach ($partners as $partner): ?>
|
||||
<?php if (empty($partner['logo'])) { continue; } ?>
|
||||
<li class="stadionzeitung-danke__logo">
|
||||
<img src="<?= e(asset((string) $partner['logo'])) ?>" alt="<?= e((string) ($partner['name'] ?? '')) ?>"<?= img_intrinsic_attrs((string) $partner['logo']) ?> loading="lazy" decoding="async">
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endif; ?>
|
||||
<div class="stadionzeitung-page__cta">
|
||||
<div class="stadionzeitung-page__cta-text">
|
||||
<p class="stadionzeitung-page__cta-label">Mitmachen</p>
|
||||
<p class="stadionzeitung-page__cta-title">Werden Sie unser Partner</p>
|
||||
<p class="stadionzeitung-page__cta-link"><?= e(club_domain('partner-werden')) ?></p>
|
||||
</div>
|
||||
<?php if ($qr !== null): ?>
|
||||
<div class="stadionzeitung-page__cta-abriss">
|
||||
<img class="stadionzeitung-page__cta-qr" src="<?= e(asset($qr)) ?>" alt="" width="60" height="60" loading="lazy" decoding="async">
|
||||
<span class="stadionzeitung-page__cta-scan">Scan mich</span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -7,9 +7,14 @@ declare(strict_types=1);
|
||||
* aufgebauten Stadionzeitungs-Snapshot; die Komponente rendert nur. Dadurch
|
||||
* sind Entwurf und veröffentlichte Druckausgabe konsistent und archivfest.
|
||||
*
|
||||
* Props: $info stadionzeitung_live_data($issue)['gegner']
|
||||
* Props: $info stadionzeitung_live_data($issue)['gegner'][team] — ein Eintrag der
|
||||
* Map je Team-Key (team_name/opponent/league/season/fixture/own/
|
||||
* opponent_stats). Normal nur die tragende Mannschaft; in der
|
||||
* Sportfest-Ausgabe eine Seite je Heimspiel am Fest, deshalb
|
||||
* nennt der Kicker die Mannschaft.
|
||||
*/
|
||||
$info = is_array($info ?? null) ? $info : [];
|
||||
$teamName = trim((string) ($info['team_name'] ?? ''));
|
||||
$fixture = is_array($info['fixture'] ?? null) ? $info['fixture'] : [];
|
||||
$own = is_array($info['own'] ?? null) ? $info['own'] : null;
|
||||
$opponent = is_array($info['opponent_stats'] ?? null) ? $info['opponent_stats'] : null;
|
||||
@@ -39,7 +44,7 @@ $format = static function (?array $row, string $key, bool $signed = false): stri
|
||||
?>
|
||||
<div class="stadionzeitung-gegner">
|
||||
<header class="stadionzeitung-page__head">
|
||||
<p class="stadionzeitung-page__kicker">Unser Gegner</p>
|
||||
<p class="stadionzeitung-page__kicker"><?= e($teamName !== '' ? 'Unser Gegner · ' . $teamName : 'Unser Gegner') ?></p>
|
||||
<h2 class="stadionzeitung-page__heading"><?= e($opponentName) ?></h2>
|
||||
<?php if (!empty($info['league']) || !empty($info['season'])): ?>
|
||||
<p class="stadionzeitung-page__sub"><?= e(trim(implode(' · ', array_filter([(string) ($info['league'] ?? ''), (string) ($info['season'] ?? '')])))) ?></p>
|
||||
@@ -70,9 +75,12 @@ $format = static function (?array $row, string $key, bool $signed = false): stri
|
||||
<?php if ($own !== null && $opponent !== null): ?>
|
||||
<div class="stadionzeitung-gegner__vergleich" role="table" aria-label="Tabellenvergleich">
|
||||
<div class="stadionzeitung-gegner__vergleich-head" role="row">
|
||||
<span role="columnheader">TSV 08</span>
|
||||
<?php /* Spaltenköpfe generisch: vorher standen hier „TSV 08" und „ASV" fest im
|
||||
Markup — das Kürzel des Marktschorgast-Gegners wäre in jeder Ausgabe
|
||||
mitgedruckt worden. Die Überschrift nennt den Gegner ohnehin. */ ?>
|
||||
<span role="columnheader">Wir</span>
|
||||
<span role="columnheader">Vergleich</span>
|
||||
<span role="columnheader">ASV</span>
|
||||
<span role="columnheader">Gegner</span>
|
||||
</div>
|
||||
<?php foreach ($rows as $row): ?>
|
||||
<div class="stadionzeitung-gegner__vergleich-row" role="row">
|
||||
|
||||
71
app/components/stadionzeitung-programm.php
Normal file
71
app/components/stadionzeitung-programm.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Programm-Seite der Stadionzeitung (Live-Seite, Sportfest-Ausgabe): das
|
||||
* Tagesprogramm einer Veranstaltung, je Tag eine Gruppe (Wochentag + Datum),
|
||||
* darunter Zeit | Titel (+ Zusatz). Kopf im Muster der Geschwister-Seiten.
|
||||
*
|
||||
* Quelle ist AUSSCHLIESSLICH data/veranstaltungen.json → programm[] der
|
||||
* Veranstaltung, auf die die Ausgabe zeigt (`veranstaltung`-Feld, Slug). Dieselbe
|
||||
* Liste rendert die Website-Detailseite über veranstaltung-programm.php — eine
|
||||
* Quelle für Heft und Website, hier nur eine zweite Darstellung fürs A5-Blatt
|
||||
* (cqi-Maße statt Website-Tokens). Bewusst live, kein Snapshot: chat-gepflegte
|
||||
* Daten wie bei kontakt/aufruf, kein Sync räumt hier etwas weg.
|
||||
*
|
||||
* Props: $issue (Eintrag aus data/stadionzeitung.json)
|
||||
*/
|
||||
$issue = $issue ?? [];
|
||||
$v = veranstaltung_find((string) ($issue['veranstaltung'] ?? ''));
|
||||
$programm = is_array($v['programm'] ?? null) ? $v['programm'] : [];
|
||||
if ($v === null || $programm === []) {
|
||||
return;
|
||||
}
|
||||
$zeitraum = german_date_range((string) ($v['date_start'] ?? ''), $v['date_end'] ?? null);
|
||||
$sub = trim(implode(' · ', array_filter([$zeitraum, (string) ($v['location'] ?? '')])));
|
||||
?>
|
||||
<div class="stadionzeitung-programm">
|
||||
<header class="stadionzeitung-page__head">
|
||||
<p class="stadionzeitung-page__kicker"><?= e((string) ($v['title'] ?? 'Veranstaltung')) ?></p>
|
||||
<h2 class="stadionzeitung-page__heading">Programm</h2>
|
||||
<?php if ($sub !== ''): ?>
|
||||
<p class="stadionzeitung-page__sub"><?= e($sub) ?></p>
|
||||
<?php endif; ?>
|
||||
</header>
|
||||
<span class="stadionzeitung-page__rule" aria-hidden="true"></span>
|
||||
<?php foreach ($programm as $tag): ?>
|
||||
<?php
|
||||
$tagIso = (string) ($tag['tag'] ?? '');
|
||||
$weekday = $tagIso !== '' ? german_weekday($tagIso) : '';
|
||||
$tagText = trim(($weekday !== '' ? $weekday . ', ' : '') . ($tagIso !== '' ? german_date($tagIso) : ''));
|
||||
$punkte = is_array($tag['punkte'] ?? null) ? $tag['punkte'] : [];
|
||||
if ($punkte === []) {
|
||||
continue;
|
||||
}
|
||||
?>
|
||||
<section class="stadionzeitung-programm__tag">
|
||||
<?php if ($tagText !== ''): ?>
|
||||
<h3 class="stadionzeitung-programm__tag-titel"><time datetime="<?= e($tagIso) ?>"><?= e($tagText) ?></time></h3>
|
||||
<?php endif; ?>
|
||||
<ul class="stadionzeitung-programm__liste">
|
||||
<?php foreach ($punkte as $punkt): ?>
|
||||
<?php $zeit = trim((string) ($punkt['zeit'] ?? '')); ?>
|
||||
<li class="stadionzeitung-programm__punkt">
|
||||
<?php if ($zeit !== ''): ?>
|
||||
<span class="stadionzeitung-programm__zeit"><time datetime="<?= e($tagIso !== '' ? $tagIso . 'T' . $zeit : $zeit) ?>"><?= e($zeit) ?></time></span>
|
||||
<?php else: ?>
|
||||
<span aria-hidden="true"></span>
|
||||
<?php endif; ?>
|
||||
<span class="stadionzeitung-programm__body">
|
||||
<span class="stadionzeitung-programm__titel"><?= e((string) ($punkt['titel'] ?? '')) ?></span>
|
||||
<?php if (!empty($punkt['text'])): ?>
|
||||
<span class="stadionzeitung-programm__text"><?= e((string) $punkt['text']) ?></span>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</section>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
@@ -9,9 +9,12 @@ declare(strict_types=1);
|
||||
* anderer Anordnung (Lesereihenfolge vs. Broschüren-Reihenfolge). Props:
|
||||
* $entry Eintrag aus issue.images[]:
|
||||
* type 'image' (base/widths/alt),
|
||||
* type 'live' (page: cover|vorwort|interview|aufruf|tabelle|termine|
|
||||
* news|ergebnisse|gegner|vorschau|kontakt; tabelle optional
|
||||
* mit team: Team-Key aus matchcenter.json),
|
||||
* type 'live' (page: cover|vorwort|interview|programm|aufruf|turnier|
|
||||
* tabelle|termine|news|ergebnisse|gegner|vorschau|kontakt|
|
||||
* mitglied|historie|sportheim|partner|danke|momente|impressum;
|
||||
* tabelle und gegner optional mit team: Team-Key aus
|
||||
* matchcenter.json; turnier mit turnier: Key aus
|
||||
* veranstaltungen.json → turniere[] der Ausgaben-Veranstaltung),
|
||||
* type 'leer' (weiße Füllseite, nur Druckfassung — hält die
|
||||
* Seitenzahl auf einem Vielfachen von 4)
|
||||
* $issue Eintrag aus data/stadionzeitung.json
|
||||
@@ -56,7 +59,19 @@ $type = $entry['type'] ?? 'image';
|
||||
<?php component('stadionzeitung-ergebnisse', ['groups' => $data['ergebnisse'] ?? []]); ?>
|
||||
<?php break; ?>
|
||||
<?php case 'gegner': ?>
|
||||
<?php component('stadionzeitung-gegner', ['info' => $data['gegner'] ?? null]); ?>
|
||||
<?php
|
||||
// `gegner` ist eine Map je Team-Key (wie `tabellen`), seit die Sportfest-
|
||||
// Ausgabe je Heimspiel eine Gegnerseite hat. Ohne team-Feld (Ausgaben vor
|
||||
// dieser Änderung) greift die tragende Mannschaft.
|
||||
$gegnerTeam = (string) ($entry['team'] ?? $issue['team_key'] ?? '');
|
||||
component('stadionzeitung-gegner', ['info' => $data['gegner'][$gegnerTeam] ?? null]);
|
||||
?>
|
||||
<?php break; ?>
|
||||
<?php case 'programm': ?>
|
||||
<?php component('stadionzeitung-programm', ['issue' => $issue]); ?>
|
||||
<?php break; ?>
|
||||
<?php case 'turnier': ?>
|
||||
<?php component('stadionzeitung-turnier', ['issue' => $issue, 'turnier' => (string) ($entry['turnier'] ?? '')]); ?>
|
||||
<?php break; ?>
|
||||
<?php case 'vorschau': ?>
|
||||
<?php component('stadionzeitung-vorschau', ['items' => $data['vorschau'] ?? []]); ?>
|
||||
@@ -76,6 +91,11 @@ $type = $entry['type'] ?? 'image';
|
||||
<?php case 'partner': ?>
|
||||
<?php component('stadionzeitung-partner', []); ?>
|
||||
<?php break; ?>
|
||||
<?php case 'danke': ?>
|
||||
<?php /* Das Anzeigenmotiv steht im Seiteneintrag (platzierung: danke), damit es
|
||||
wie jede andere Anzeige in images[] eingefroren ist. */ ?>
|
||||
<?php component('stadionzeitung-danke', ['anzeige' => $entry['anzeige'] ?? null]); ?>
|
||||
<?php break; ?>
|
||||
<?php case 'momente': ?>
|
||||
<?php component('stadionzeitung-momente', ['items' => $data['momente'] ?? []]); ?>
|
||||
<?php break; ?>
|
||||
|
||||
158
app/components/stadionzeitung-turnier.php
Normal file
158
app/components/stadionzeitung-turnier.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Turnierplan-Seite der Stadionzeitung (Live-Seite, Sportfest-Ausgabe): EIN
|
||||
* Jugendturnier pro Seite — Kopf (Veranstaltung · Tag / „Turnier F1-Jugend" /
|
||||
* Beginn · Spieldauer · Felder · Modus), Teilnehmer, Spielplan mit LEERER
|
||||
* Ergebnisspalte und eine leere Abschlusstabelle. Das Blatt ist zum
|
||||
* Beschreiben gedacht: am Platz trägt man Ergebnisse und Plätze von Hand ein.
|
||||
*
|
||||
* Quelle ist AUSSCHLIESSLICH data/veranstaltungen.json → turniere[] der
|
||||
* Veranstaltung, auf die die Ausgabe zeigt (`veranstaltung`-Feld); welches
|
||||
* Turnier, sagt der `turnier`-Key des Seiteneintrags. Bewusst live, kein
|
||||
* Snapshot (chat-gepflegte Daten, wie kontakt/aufruf/programm).
|
||||
*
|
||||
* Eintrag: key, title, date, start, spieldauer (Minuten), felder? (Zahl),
|
||||
* modus? (Freitext), teams[], spiele[]{nr, zeit, feld?, heim, gast}
|
||||
*
|
||||
* Eigenes Team wie league-table tr.is-own hervorgehoben (Tönung + Abel-Fett);
|
||||
* erkannt über den Vereinsnamen aus data/club.json als Präfix, damit auch
|
||||
* „TSV 08 Kulmbach 2" getroffen wird.
|
||||
*
|
||||
* Props: $issue (Eintrag aus data/stadionzeitung.json), $turnier (Key)
|
||||
*/
|
||||
$issue = $issue ?? [];
|
||||
$turnierKey = trim((string) ($turnier ?? ''));
|
||||
$v = veranstaltung_find((string) ($issue['veranstaltung'] ?? ''));
|
||||
$t = null;
|
||||
foreach ($v['turniere'] ?? [] as $eintrag) {
|
||||
if (is_array($eintrag) && (string) ($eintrag['key'] ?? '') === $turnierKey) {
|
||||
$t = $eintrag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($v === null || $t === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$clubName = trim((string) (json_load('club')['name'] ?? 'TSV 08 Kulmbach'));
|
||||
$isOwn = static fn (string $team): bool => $clubName !== ''
|
||||
&& str_starts_with(mb_strtolower(trim($team)), mb_strtolower($clubName));
|
||||
|
||||
$date = (string) ($t['date'] ?? '');
|
||||
$weekday = $date !== '' ? german_weekday($date) : '';
|
||||
$kicker = trim(implode(' · ', array_filter([
|
||||
(string) ($v['title'] ?? ''),
|
||||
trim(($weekday !== '' ? $weekday . ', ' : '') . ($date !== '' ? german_date($date) : '')),
|
||||
])));
|
||||
|
||||
$teams = array_values(array_filter(
|
||||
array_map(static fn ($x): string => trim((string) $x), $t['teams'] ?? []),
|
||||
static fn (string $x): bool => $x !== ''
|
||||
));
|
||||
$spiele = array_values(array_filter($t['spiele'] ?? [], 'is_array'));
|
||||
$felder = (int) ($t['felder'] ?? 0);
|
||||
$hatFeld = $felder > 1;
|
||||
|
||||
$meta = [];
|
||||
if (!empty($t['start'])) {
|
||||
$meta[] = 'Beginn ' . (string) $t['start'] . ' Uhr';
|
||||
}
|
||||
if (!empty($t['spieldauer'])) {
|
||||
$meta[] = 'Spieldauer ' . (int) $t['spieldauer'] . ' Min.';
|
||||
}
|
||||
if ($hatFeld) {
|
||||
$meta[] = $felder . ' Felder';
|
||||
}
|
||||
if (!empty($t['modus'])) {
|
||||
$meta[] = (string) $t['modus'];
|
||||
}
|
||||
$sub = trim(implode(' · ', $meta));
|
||||
$title = trim((string) ($t['title'] ?? $turnierKey));
|
||||
// Ab neun Spielen (E-Jugend: zehn) rücken die Zeilen enger, sonst läuft der
|
||||
// Spielplan mit der Abschlusstabelle über die Blattkante.
|
||||
$dicht = count($spiele) > 8;
|
||||
?>
|
||||
<div class="stadionzeitung-turnier<?= $dicht ? ' stadionzeitung-turnier--dicht' : '' ?>">
|
||||
<header class="stadionzeitung-page__head">
|
||||
<?php if ($kicker !== ''): ?>
|
||||
<p class="stadionzeitung-page__kicker"><?= e($kicker) ?></p>
|
||||
<?php endif; ?>
|
||||
<h2 class="stadionzeitung-page__heading"><?= e('Turnier ' . $title) ?></h2>
|
||||
<?php if ($sub !== ''): ?>
|
||||
<p class="stadionzeitung-page__sub"><?= e($sub) ?></p>
|
||||
<?php endif; ?>
|
||||
</header>
|
||||
<span class="stadionzeitung-page__rule" aria-hidden="true"></span>
|
||||
<?php if ($teams !== []): ?>
|
||||
<p class="stadionzeitung-turnier__label">Teilnehmer</p>
|
||||
<p class="stadionzeitung-turnier__teams">
|
||||
<?php foreach ($teams as $i => $team): ?>
|
||||
<?= $i > 0 ? ' · ' : '' ?><span class="stadionzeitung-turnier__team<?= $isOwn($team) ? ' is-own' : '' ?>"><?= e($team) ?></span>
|
||||
<?php endforeach; ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
<?php if ($spiele !== []): ?>
|
||||
<table class="stadionzeitung-turnier__spiele">
|
||||
<?php /* Sichtbares Label IST die Tabellenbeschriftung (caption), nicht zusätzlich
|
||||
eine versteckte: sonst liest ein Screenreader beides und damit doppelt. */ ?>
|
||||
<caption class="stadionzeitung-turnier__label">Spielplan</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Nr.</th>
|
||||
<th scope="col">Zeit</th>
|
||||
<?php if ($hatFeld): ?>
|
||||
<th scope="col">Feld</th>
|
||||
<?php endif; ?>
|
||||
<th scope="col">Begegnung</th>
|
||||
<th scope="col" class="stadionzeitung-turnier__ergebnis">Ergebnis</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($spiele as $i => $spiel): ?>
|
||||
<?php
|
||||
$heim = trim((string) ($spiel['heim'] ?? ''));
|
||||
$gast = trim((string) ($spiel['gast'] ?? ''));
|
||||
$zeit = trim((string) ($spiel['zeit'] ?? ''));
|
||||
?>
|
||||
<tr>
|
||||
<td class="stadionzeitung-turnier__nr"><?= e((string) ($spiel['nr'] ?? ($i + 1))) ?></td>
|
||||
<td class="stadionzeitung-turnier__zeit"><?php if ($zeit !== ''): ?><time datetime="<?= e($date !== '' ? $date . 'T' . $zeit : $zeit) ?>"><?= e($zeit) ?></time><?php endif; ?></td>
|
||||
<?php if ($hatFeld): ?>
|
||||
<td class="stadionzeitung-turnier__nr"><?= e((string) ($spiel['feld'] ?? '')) ?></td>
|
||||
<?php endif; ?>
|
||||
<td class="stadionzeitung-turnier__paarung"><span class="stadionzeitung-turnier__team<?= $isOwn($heim) ? ' is-own' : '' ?>"><?= e($heim) ?></span> <span class="stadionzeitung-turnier__vs">gegen</span> <span class="stadionzeitung-turnier__team<?= $isOwn($gast) ? ' is-own' : '' ?>"><?= e($gast) ?></span></td>
|
||||
<td class="stadionzeitung-turnier__ergebnis"><span class="stadionzeitung-turnier__blank" aria-hidden="true"></span><span class="stadionzeitung-turnier__colon">:</span><span class="stadionzeitung-turnier__blank" aria-hidden="true"></span></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
<?php if ($teams !== []): ?>
|
||||
<table class="stadionzeitung-turnier__tabelle">
|
||||
<caption class="stadionzeitung-turnier__label">Platzierung zum Eintragen</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Pl.</th>
|
||||
<th scope="col">Teilnehmer</th>
|
||||
<th scope="col"><abbr title="Tore">T</abbr></th>
|
||||
<th scope="col"><abbr title="Tordifferenz">TD</abbr></th>
|
||||
<th scope="col"><abbr title="Punkte">Pkt</abbr></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php for ($i = 1; $i <= count($teams); $i++): ?>
|
||||
<tr>
|
||||
<td class="stadionzeitung-turnier__nr"><?= $i ?>.</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<?php endfor; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
/**
|
||||
* Swipe-Viewer für eine Stadionzeitung-Ausgabe. Props:
|
||||
* $issue Eintrag aus data/stadionzeitung.json (opponent, team_key, status,
|
||||
* images[]{type: 'image'{base,widths,alt} | 'live'{page[,team]}})
|
||||
* images[]{type: 'image'{base,widths,alt} | 'live'{page[,team][,turnier]}})
|
||||
*
|
||||
* Ohne JS: alle Seiten normal gestapelt (stadionzeitung-seite-Komponente) —
|
||||
* voll lesbar, sequentiell auch per Screenreader. Mit JS
|
||||
|
||||
Reference in New Issue
Block a user