Unterseiten: Fußball-Übersicht, Mannschaften, Jugend, Verein, Rechtliches
Neue Seiten + Routen: fussball, fussball/{erste-mannschaft,zweite-mannschaft,
damen}, jugendfussball, verein, impressum, datenschutz. Dazu die Komponenten
team, team-cards/-roster/-photo/-bento, faq/faq-contact, key-facts, age-groups,
cta-band, training-location und der contact-slider.
Daten in teams/fussball/jugend/verein.json; club.json um Vorstand, Bankdaten,
Datenschutz-Kontakt erweitert. person-grid bekommt $names-Filter, partner-grid
einen Intro-Stil. Seiten setzen $meta['schema'] via team_/page_schema.
Styles für alle neuen Komponenten in components.css.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDQZ6FuchNUh2bxSW9PeNv
This commit is contained in:
47
app/components/age-groups.php
Normal file
47
app/components/age-groups.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Jugend-Altersklassen als Karten-Grid. Props:
|
||||
* $groups array [{code, name, photo?{base,widths,alt}, training?, note?}, …]
|
||||
* $title string (optional, Default 'Unsere Jugendmannschaften')
|
||||
* $anchor string (optional, Default 'mannschaften')
|
||||
* Fehlt ein Foto, greift ein Platzhalter; fehlt die Trainingszeit, entfällt die Zeile.
|
||||
*/
|
||||
$title = $title ?? 'Unsere Jugendmannschaften';
|
||||
$anchor = $anchor ?? 'mannschaften';
|
||||
$groups = $groups ?? [];
|
||||
if ($groups === []) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<section class="section age-groups" id="<?= e($anchor) ?>">
|
||||
<div class="container">
|
||||
<h2><?= e($title) ?></h2>
|
||||
<ul class="age-groups__grid">
|
||||
<?php foreach ($groups as $g): ?>
|
||||
<li class="age-group">
|
||||
<div class="age-group__media">
|
||||
<?php if (!empty($g['photo']['base'])): ?>
|
||||
<?php component('img', ['image' => $g['photo'], 'sizes' => '(max-width: 600px) 100vw, 320px', 'class' => 'age-group__img']); ?>
|
||||
<?php else: ?>
|
||||
<img class="age-group__img" src="<?= e(asset('img/persons/platzhalter.jpg')) ?>" alt="" width="400" height="300" loading="lazy" decoding="async">
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($g['code'])): ?>
|
||||
<span class="age-group__badge" aria-hidden="true"><?= e($g['code']) ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="age-group__body">
|
||||
<h3 class="age-group__name"><?= e($g['name']) ?></h3>
|
||||
<?php if (!empty($g['training'])): ?>
|
||||
<p class="age-group__training"><?= icon('clock', 'age-group__icon') ?><span><?= e($g['training']) ?></span></p>
|
||||
<?php elseif (!empty($g['note'])): ?>
|
||||
<p class="age-group__training text-muted"><?= e($g['note']) ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
30
app/components/cta-band.php
Normal file
30
app/components/cta-band.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Vollbreites Call-to-Action-Band. Props:
|
||||
* $band array{title, text?, ctas: [{label, slug, style?}]}
|
||||
* $anchor string (optional)
|
||||
* Rendert nichts ohne CTAs. Nutzt das bestehende cta-/btn-Muster.
|
||||
*/
|
||||
$anchor = $anchor ?? null;
|
||||
if (empty($band['ctas'])) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<section class="section section--tinted cta-band"<?= $anchor ? ' id="' . e($anchor) . '"' : '' ?>>
|
||||
<div class="container cta-band__inner">
|
||||
<?php if (!empty($band['title'])): ?>
|
||||
<h2 class="cta-band__title"><?= e($band['title']) ?></h2>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($band['text'])): ?>
|
||||
<p class="cta-band__text"><?= e($band['text']) ?></p>
|
||||
<?php endif; ?>
|
||||
<div class="cta-band__actions">
|
||||
<?php foreach ($band['ctas'] as $cta): ?>
|
||||
<?php component('cta', ['cta' => $cta]); ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
76
app/components/faq-contact.php
Normal file
76
app/components/faq-contact.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Kombi-Sektion: FAQ (2/3) + einzelner Ansprechpartner (1/3) nebeneinander.
|
||||
* Sinnvoll, wenn es pro Mannschaft nur einen Kontakt gibt. Props:
|
||||
* $faq array [{q, a}]
|
||||
* $persons array — ein oder mehrere Ansprechpartner (person-card). Leer → FAQ volle Breite.
|
||||
* Bei mehreren: Mini-Slider (ohne JS alle Karten gestapelt sichtbar).
|
||||
* $faqTitle string (optional, Default 'Häufige Fragen')
|
||||
* $faqIntro string (optional) — gedämpfte Subline unter der Überschrift
|
||||
* $contactTitle string (optional, Default 'Dein Ansprechpartner')
|
||||
* $anchor string (optional, Default 'kontakt')
|
||||
* Natives <details>/<summary> (ohne JS bedienbar); FAQPage-Schema liefert die Seite.
|
||||
*/
|
||||
$faqTitle = $faqTitle ?? 'Häufige Fragen';
|
||||
$faqIntro = $faqIntro ?? '';
|
||||
$contactTitle = $contactTitle ?? 'Dein Ansprechpartner';
|
||||
$anchor = $anchor ?? 'kontakt';
|
||||
$faq = array_values(array_filter(
|
||||
$faq ?? [],
|
||||
static fn (array $i): bool => !empty($i['q']) && !empty($i['a'])
|
||||
));
|
||||
$persons = array_values(array_filter($persons ?? [], static fn ($p): bool => !empty($p)));
|
||||
$hasPerson = $persons !== [];
|
||||
$isSlider = count($persons) > 1;
|
||||
if ($faq === [] && !$hasPerson) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<section class="section faq-contact<?= $hasPerson ? '' : ' faq-contact--solo' ?>" id="<?= e($anchor) ?>">
|
||||
<div class="container">
|
||||
<h2 class="faq-contact__title"><?= e($faqTitle) ?></h2>
|
||||
<?php if ($faqIntro !== ''): ?>
|
||||
<p class="faq-contact__intro text-muted"><?= e($faqIntro) ?></p>
|
||||
<?php endif; ?>
|
||||
<div class="faq-contact__inner">
|
||||
<div class="faq-contact__main">
|
||||
<div class="faq__list">
|
||||
<?php foreach ($faq as $item): ?>
|
||||
<details class="faq__item">
|
||||
<summary class="faq__q"><span><?= e($item['q']) ?></span><?= icon('chevron-down', 'faq__chevron') ?></summary>
|
||||
<div class="faq__a"><p><?= e($item['a']) ?></p></div>
|
||||
</details>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($hasPerson): ?>
|
||||
<aside class="faq-contact__aside">
|
||||
<?php if ($contactTitle !== ''): ?>
|
||||
<h2><?= e($contactTitle) ?></h2>
|
||||
<?php endif; ?>
|
||||
<?php if ($isSlider): ?>
|
||||
<div class="contact-slider" data-contact-slider>
|
||||
<ul class="contact-slider__track">
|
||||
<?php foreach ($persons as $p): ?>
|
||||
<li class="contact-slider__slide" data-contact-slide>
|
||||
<?php component('person-card', ['person' => $p]); ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<div class="contact-slider__dots">
|
||||
<?php foreach ($persons as $i => $p): ?>
|
||||
<button type="button" class="contact-slider__dot" data-contact-dot<?= $i === 0 ? ' aria-current="true"' : '' ?> aria-label="Ansprechpartner <?= $i + 1 ?>: <?= e($p['name']) ?>"></button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php component('person-card', ['person' => $persons[0]]); ?>
|
||||
<?php endif; ?>
|
||||
</aside>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
35
app/components/faq.php
Normal file
35
app/components/faq.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* FAQ-Accordion. Props:
|
||||
* $faq array [{q, a}, …]
|
||||
* $title string (optional, Default 'Häufige Fragen')
|
||||
* $anchor string (optional, Default 'faq')
|
||||
* Natives <details>/<summary> → ohne JS bedienbar. Das FAQPage-Schema liefert
|
||||
* die Seite über $meta['schema'] (helpers: faq_schema/team_schema), nicht diese Komponente.
|
||||
*/
|
||||
$title = $title ?? 'Häufige Fragen';
|
||||
$anchor = $anchor ?? 'faq';
|
||||
$faq = array_values(array_filter(
|
||||
$faq ?? [],
|
||||
static fn (array $i): bool => !empty($i['q']) && !empty($i['a'])
|
||||
));
|
||||
if ($faq === []) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<section class="section faq" id="<?= e($anchor) ?>">
|
||||
<div class="container faq__inner">
|
||||
<h2><?= e($title) ?></h2>
|
||||
<div class="faq__list">
|
||||
<?php foreach ($faq as $item): ?>
|
||||
<details class="faq__item">
|
||||
<summary class="faq__q"><span><?= e($item['q']) ?></span><?= icon('chevron-down', 'faq__chevron') ?></summary>
|
||||
<div class="faq__a"><p><?= e($item['a']) ?></p></div>
|
||||
</details>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
41
app/components/key-facts.php
Normal file
41
app/components/key-facts.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Kompakte Faktenleiste (Icon + Label + Wert). Props:
|
||||
* $facts array [{icon, label, value}, …] — Einträge mit leerem 'value' werden übersprungen.
|
||||
* $anchor string (optional) — Sektions-ID.
|
||||
* $tinted bool (optional, Default false) — aufgehelltes Band.
|
||||
* GEO/SEO: kompakte, zitierfähige Fakten. Rendert nichts, wenn keine Werte vorliegen.
|
||||
*/
|
||||
$anchor = $anchor ?? null;
|
||||
$tinted = $tinted ?? false;
|
||||
|
||||
$facts = array_values(array_filter(
|
||||
$facts ?? [],
|
||||
static fn (array $f): bool => isset($f['value']) && $f['value'] !== ''
|
||||
));
|
||||
if ($facts === []) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<section class="section key-facts<?= $tinted ? ' section--tinted' : '' ?>"<?= $anchor ? ' id="' . e($anchor) . '"' : '' ?>>
|
||||
<div class="container">
|
||||
<ul class="key-facts__grid">
|
||||
<?php foreach ($facts as $fact): ?>
|
||||
<li class="key-facts__item">
|
||||
<?php if (!empty($fact['icon'])): ?>
|
||||
<span class="key-facts__icon"><?= icon($fact['icon']) ?></span>
|
||||
<?php endif; ?>
|
||||
<span class="key-facts__text">
|
||||
<?php if (!empty($fact['label'])): ?>
|
||||
<span class="key-facts__label"><?= e($fact['label']) ?></span>
|
||||
<?php endif; ?>
|
||||
<span class="key-facts__value"><?= e($fact['value']) ?></span>
|
||||
</span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
@@ -12,7 +12,7 @@ $partners = json_load('partners')['partners'] ?? [];
|
||||
<section class="section partners" id="partner">
|
||||
<div class="container">
|
||||
<h2><?= e($intro['title']) ?></h2>
|
||||
<p><?= e($intro['text']) ?></p>
|
||||
<p class="partners__intro text-muted"><?= e($intro['text']) ?></p>
|
||||
<ul class="partners__grid">
|
||||
<?php foreach ($partners as $partner): ?>
|
||||
<li class="partners__item">
|
||||
|
||||
@@ -6,6 +6,7 @@ declare(strict_types=1);
|
||||
* Ansprechpartner-Grid aus data/ansprechpartner.json (Single Source of Truth).
|
||||
* Alle Props optional:
|
||||
* $departments array — nur diese Abteilungen zeigen (z. B. ['Herrenfußball','Damenfußball']).
|
||||
* $names array — zusätzlich auf diese Namen einschränken (z. B. ['Benedikt Höfner']).
|
||||
* $title string — Überschrift überschreiben (Default aus JSON).
|
||||
* $intro string — Intro-Text überschreiben (Default aus JSON; '' = ausblenden).
|
||||
* $anchor string — Sektions-ID (Default 'ansprechpartner').
|
||||
@@ -24,6 +25,12 @@ if (!empty($departments)) {
|
||||
static fn (array $p): bool => in_array($p['department'] ?? '', $departments, true)
|
||||
));
|
||||
}
|
||||
if (!empty($names)) {
|
||||
$persons = array_values(array_filter(
|
||||
$persons,
|
||||
static fn (array $p): bool => in_array($p['name'] ?? '', $names, true)
|
||||
));
|
||||
}
|
||||
|
||||
if ($persons === []) {
|
||||
return;
|
||||
|
||||
82
app/components/team-bento.php
Normal file
82
app/components/team-bento.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Team-Dashboard als Bento-Grid: kombiniert Bild, Einleitung, Trainingszeiten,
|
||||
* Spielklasse und Spielort in einem Kachel-Layout. Props:
|
||||
* $team array — Eintrag aus data/teams.json.
|
||||
* $anchor string (optional, Default 'team-info')
|
||||
* Spielort kommt aus data/club.json (NAP-Single-Source). Deko-Icons sind dekorativ.
|
||||
*/
|
||||
$anchor = $anchor ?? 'team-info';
|
||||
$about = $team['about'] ?? [];
|
||||
$sessions = $team['training']['sessions'] ?? [];
|
||||
$venueOverride = $team['training']['venue'] ?? null; // mannschaftsspezifischer Spielort (z. B. SG-Heimat)
|
||||
$showVenue = !empty($team['training']['venue_from_club']) || !empty($venueOverride);
|
||||
$highlight = !empty($team['league'])
|
||||
? ['label' => 'Spielklasse', 'value' => $team['league']]
|
||||
: ['label' => 'Bereich', 'value' => $team['category'] ?? ''];
|
||||
|
||||
$club = json_load('club');
|
||||
$addr = $club['address'] ?? [];
|
||||
$mapsQuery = !empty($venueOverride['maps_query'])
|
||||
? rawurlencode($venueOverride['maps_query'])
|
||||
: rawurlencode(trim(($addr['venue'] ?? '') . ', ' . ($addr['street'] ?? '') . ', ' . ($addr['zip'] ?? '') . ' ' . ($addr['city'] ?? ''), ', '));
|
||||
?>
|
||||
<section class="section team-bento-section" id="<?= e($anchor) ?>">
|
||||
<div class="container">
|
||||
<div class="team-bento">
|
||||
<article class="bento__tile team-bento__about">
|
||||
<h2><?= e($about['title'] ?? 'Über die Mannschaft') ?></h2>
|
||||
<p><?= e($about['text'] ?? '') ?></p>
|
||||
</article>
|
||||
|
||||
<article class="bento__tile team-bento__training">
|
||||
<h2 class="bento__heading"><?= icon('clock', 'bento__hicon') ?>Trainingszeiten</h2>
|
||||
<?php if ($sessions !== []): ?>
|
||||
<ul class="bento__sessions">
|
||||
<?php foreach ($sessions as $s): ?>
|
||||
<li>
|
||||
<span class="bento__day"><?= e($s['day']) ?></span>
|
||||
<span class="bento__time"><?= e($s['time']) ?></span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<p class="text-muted">Die genauen Trainingszeiten erfährst du direkt bei unserem Ansprechpartner.</p>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($team['training']['note'])): ?>
|
||||
<p class="bento__note text-muted"><?= e($team['training']['note']) ?></p>
|
||||
<?php endif; ?>
|
||||
<span class="bento__deco" aria-hidden="true"><?= icon('clock') ?></span>
|
||||
</article>
|
||||
|
||||
<?php if (($highlight['value'] ?? '') !== ''): ?>
|
||||
<article class="bento__tile bento__tile--accent team-bento__klasse">
|
||||
<span class="bento__stat-label"><?= e($highlight['label']) ?></span>
|
||||
<span class="bento__stat-value"><?= e($highlight['value']) ?></span>
|
||||
</article>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($showVenue && ($venueOverride || $addr !== [])): ?>
|
||||
<article class="bento__tile team-bento__venue">
|
||||
<div class="team-bento__venue-main">
|
||||
<h2 class="bento__heading"><?= icon('geo-alt', 'bento__hicon') ?>Spielort</h2>
|
||||
<?php if ($venueOverride): ?>
|
||||
<p class="bento__address">
|
||||
<?php if (!empty($venueOverride['name'])): ?><strong><?= e($venueOverride['name']) ?></strong><?php endif; ?><?php if (!empty($venueOverride['locality'])): ?> · <?= e($venueOverride['locality']) ?><?php endif; ?>
|
||||
</p>
|
||||
<?php else: ?>
|
||||
<p class="bento__address">
|
||||
<?php if (!empty($addr['venue'])): ?><strong><?= e($addr['venue']) ?></strong> · <?php endif; ?><?= e($addr['street']) ?>, <?= e($addr['zip']) ?> <?= e($addr['city']) ?>
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<a class="btn btn--outline team-bento__route" href="https://www.google.com/maps/search/?api=1&query=<?= e($mapsQuery) ?>" target="_blank" rel="noopener">Route planen<span class="visually-hidden"> (öffnet Google Maps in neuem Tab)</span></a>
|
||||
<span class="bento__deco" aria-hidden="true"><?= icon('geo-alt') ?></span>
|
||||
</article>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
55
app/components/team-cards.php
Normal file
55
app/components/team-cards.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Mannschafts-Karten, die auf die Team-Unterseiten verlinken. Props:
|
||||
* $teams array [{name, slug, category, league?, image?{base,widths,alt}}, …]
|
||||
* $title string (optional, Default 'Unsere Mannschaften')
|
||||
* $title_hidden bool (optional) — H2 für SEO/A11y im DOM, aber visuell versteckt
|
||||
* $intro string (optional)
|
||||
* $intro_below bool (optional) — Intro als „Klärtext" UNTER die Kacheln statt darüber
|
||||
* $anchor string (optional, Default 'mannschaften')
|
||||
* Ganze Karte ist ein Link. Ohne Bild greift ein Platzhalter.
|
||||
*/
|
||||
$title = $title ?? 'Unsere Mannschaften';
|
||||
$title_hidden = $title_hidden ?? false;
|
||||
$anchor = $anchor ?? 'mannschaften';
|
||||
$intro = $intro ?? '';
|
||||
$intro_below = $intro_below ?? false;
|
||||
$teams = $teams ?? [];
|
||||
if ($teams === []) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<section class="section team-cards" id="<?= e($anchor) ?>">
|
||||
<div class="container">
|
||||
<h2<?= $title_hidden ? ' class="visually-hidden"' : '' ?>><?= e($title) ?></h2>
|
||||
<?php if ($intro !== '' && !$intro_below): ?>
|
||||
<p class="team-cards__intro text-muted"><?= e($intro) ?></p>
|
||||
<?php endif; ?>
|
||||
<ul class="team-cards__grid">
|
||||
<?php foreach ($teams as $t): ?>
|
||||
<li>
|
||||
<a class="team-card" href="<?= e(url($t['slug'])) ?>">
|
||||
<div class="team-card__media">
|
||||
<?php if (!empty($t['image']['base'])): ?>
|
||||
<?php component('img', ['image' => $t['image'], 'sizes' => '(max-width: 600px) 100vw, 360px', 'class' => 'team-card__img']); ?>
|
||||
<?php else: ?>
|
||||
<img class="team-card__img" src="<?= e(asset('img/persons/platzhalter.jpg')) ?>" alt="" width="400" height="300" loading="lazy" decoding="async">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="team-card__body">
|
||||
<h3 class="team-card__name"><?= e($t['name']) ?></h3>
|
||||
<p class="team-card__meta"><?= e(trim($t['category'] . (!empty($t['league']) ? ' · ' . $t['league'] : ''))) ?></p>
|
||||
<span class="team-card__cta">Mehr erfahren <?= icon('arrow-right', 'team-card__arrow') ?></span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php if ($intro !== '' && $intro_below): ?>
|
||||
<p class="team-cards__intro team-cards__intro--below text-muted"><?= e($intro) ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
27
app/components/team-photo.php
Normal file
27
app/components/team-photo.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Mannschaftsfoto in voller Breite (Querformat-Gruppenbild). Props:
|
||||
* $photo array{ image: {base,widths,alt} | {src,width,height,alt}, caption? }
|
||||
* $anchor string (optional, Default 'mannschaftsfoto')
|
||||
* Zeigt das Bild im natürlichen Seitenverhältnis, abgerundet; optionale Bildunterschrift.
|
||||
*/
|
||||
$anchor = $anchor ?? 'mannschaftsfoto';
|
||||
$image = $photo['image'] ?? null;
|
||||
if (empty($image)) {
|
||||
return;
|
||||
}
|
||||
$caption = $photo['caption'] ?? '';
|
||||
?>
|
||||
<section class="section team-photo" id="<?= e($anchor) ?>">
|
||||
<div class="container">
|
||||
<figure class="team-photo__figure">
|
||||
<?php component('img', ['image' => $image, 'sizes' => '(max-width: 1100px) 100vw, 1040px', 'class' => 'team-photo__img']); ?>
|
||||
<?php if ($caption !== ''): ?>
|
||||
<figcaption class="team-photo__caption"><?= e($caption) ?></figcaption>
|
||||
<?php endif; ?>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
78
app/components/team-roster.php
Normal file
78
app/components/team-roster.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Kader-/Trainerstab-Raster einer Mannschaft. Props:
|
||||
* $roster array{
|
||||
* title?, intro?,
|
||||
* variant? : 'below' (Name unter dem Foto, Default) | 'overlay' (Name im Foto, Coolvetica),
|
||||
* groups? : [{ title?, members:[…] }, …] ODER
|
||||
* members? : [{ name, role?, image? }, …] (eine Gruppe ohne Überschrift)
|
||||
* }
|
||||
* $anchor string (optional, Default 'kader')
|
||||
* Gruppen mit Titel bekommen eine Zwischenüberschrift (h3); leere Gruppen entfallen.
|
||||
* Ohne Foto greift der Platzhalter-Avatar.
|
||||
*/
|
||||
$anchor = $anchor ?? 'kader';
|
||||
|
||||
// Gruppen normalisieren: entweder explizite groups oder flache members als eine Gruppe.
|
||||
$groups = $roster['groups'] ?? null;
|
||||
if ($groups === null) {
|
||||
$flat = $roster['members'] ?? [];
|
||||
$groups = $flat === [] ? [] : [['title' => '', 'members' => $flat]];
|
||||
}
|
||||
// Leere Gruppen rauswerfen.
|
||||
$groups = array_values(array_filter($groups, static fn ($g): bool => !empty($g['members'])));
|
||||
if ($groups === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
$variant = ($roster['variant'] ?? 'below') === 'overlay' ? 'overlay' : 'below';
|
||||
$title = $roster['title'] ?? 'Kader';
|
||||
$intro = $roster['intro'] ?? '';
|
||||
$cardMod = $variant === 'overlay' ? ' roster-card--overlay' : '';
|
||||
?>
|
||||
<section class="section team-roster" id="<?= e($anchor) ?>">
|
||||
<div class="container">
|
||||
<h2><?= e($title) ?></h2>
|
||||
<?php if ($intro !== ''): ?>
|
||||
<p class="team-roster__intro text-muted"><?= e($intro) ?></p>
|
||||
<?php endif; ?>
|
||||
<?php foreach ($groups as $group): ?>
|
||||
<?php if (!empty($group['title'])): ?>
|
||||
<h3 class="team-roster__group"><?= e($group['title']) ?></h3>
|
||||
<?php endif; ?>
|
||||
<ul class="team-roster__grid">
|
||||
<?php foreach ($group['members'] as $m): ?>
|
||||
<?php $src = !empty($m['image']) ? $m['image'] : 'img/persons/platzhalter-spieler.jpg'; ?>
|
||||
<?php $alt = !empty($m['image']) ? e($m['name']) : ''; ?>
|
||||
<?php $staffMod = !empty($m['role']) ? ' roster-card--staff' : ''; ?>
|
||||
<li class="roster-card<?= $cardMod . $staffMod ?>">
|
||||
<?php if ($variant === 'overlay'): ?>
|
||||
<img class="roster-card__img" src="<?= e(asset($src)) ?>" alt="<?= $alt ?>"
|
||||
width="480" height="600" loading="lazy" decoding="async">
|
||||
<div class="roster-card__caption">
|
||||
<?php if (!empty($m['role'])): ?>
|
||||
<span class="roster-card__role"><?= e($m['role']) ?></span>
|
||||
<?php endif; ?>
|
||||
<p class="roster-card__name"><?= e($m['name']) ?></p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="roster-card__media">
|
||||
<img src="<?= e(asset($src)) ?>" alt="<?= $alt ?>"
|
||||
width="480" height="600" loading="lazy" decoding="async">
|
||||
</div>
|
||||
<div class="roster-card__body">
|
||||
<?php if (!empty($m['role'])): ?>
|
||||
<span class="roster-card__role"><?= e($m['role']) ?></span>
|
||||
<?php endif; ?>
|
||||
<p class="roster-card__name"><?= e($m['name']) ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</section>
|
||||
46
app/components/team.php
Normal file
46
app/components/team.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Mannschafts-Seite (DRY-Template für 1./2. Herren + Damen). Props:
|
||||
* $team array — Eintrag aus data/teams.json.
|
||||
* $slug string — voller Seiten-Slug (für Anker-Links).
|
||||
* Aufbau: Hero → Team-Bento (Bild, Einleitung, Training, Spielort) → FAQ + Ansprechpartner.
|
||||
* Leere Datenblöcke (Training, FAQ, fehlender Kontakt) blenden sich selbst aus.
|
||||
*/
|
||||
$slug = $slug ?? '';
|
||||
|
||||
// Ansprechpartner aus der Single-Source auflösen (ein oder mehrere, Reihenfolge wie angegeben).
|
||||
$allPersons = json_load('ansprechpartner')['persons'] ?? [];
|
||||
$wantDept = $team['contact_department'] ?? '';
|
||||
$wantNames = $team['contact_names'] ?? (isset($team['contact_name']) ? [$team['contact_name']] : []);
|
||||
$contacts = [];
|
||||
foreach ($wantNames as $wn) {
|
||||
foreach ($allPersons as $p) {
|
||||
if (($p['department'] ?? '') === $wantDept && ($p['name'] ?? '') === $wn) {
|
||||
$contacts[] = $p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component('hero', ['hero' => $team['hero']]);
|
||||
|
||||
if (!empty($team['team_photo']['image'])) {
|
||||
component('team-photo', ['photo' => $team['team_photo'], 'anchor' => 'mannschaftsfoto']);
|
||||
}
|
||||
|
||||
component('team-bento', ['team' => $team, 'anchor' => 'team-info']);
|
||||
|
||||
if (!empty($team['roster']['members']) || !empty($team['roster']['groups'])) {
|
||||
component('team-roster', ['roster' => $team['roster'], 'anchor' => 'kader']);
|
||||
}
|
||||
|
||||
component('faq-contact', [
|
||||
'faq' => $team['faq'] ?? [],
|
||||
'faqIntro' => 'Die wichtigsten Fragen rund um die ' . ($team['name'] ?? 'Mannschaft') . ' – kurz beantwortet.',
|
||||
'persons' => $contacts,
|
||||
'contactTitle' => '',
|
||||
'anchor' => 'kontakt',
|
||||
]);
|
||||
73
app/components/training-location.php
Normal file
73
app/components/training-location.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Trainings-/Spielort-„Dashboard" als Bento-Grid. Props:
|
||||
* $training array{sessions: [{day, time}], venue_from_club?: bool, note?: string}
|
||||
* $highlight array{label, value} (optional) — kleiner Akzent-Tile (z. B. Spielklasse).
|
||||
* $anchor string (optional, Default 'training')
|
||||
* Spielort kommt aus data/club.json (NAP-Single-Source). Ohne Daten: nichts.
|
||||
*/
|
||||
$anchor = $anchor ?? 'training';
|
||||
$sessions = $training['sessions'] ?? [];
|
||||
$showVenue = !empty($training['venue_from_club']);
|
||||
$highlight = $highlight ?? null;
|
||||
|
||||
if ($sessions === [] && !$showVenue) {
|
||||
return;
|
||||
}
|
||||
|
||||
$club = json_load('club');
|
||||
$addr = $club['address'] ?? [];
|
||||
$mapsQuery = rawurlencode(trim(($addr['venue'] ?? '') . ', ' . ($addr['street'] ?? '') . ', ' . ($addr['zip'] ?? '') . ' ' . ($addr['city'] ?? ''), ', '));
|
||||
?>
|
||||
<section class="section training" id="<?= e($anchor) ?>">
|
||||
<div class="container">
|
||||
<div class="bento">
|
||||
<article class="bento__tile bento__tile--times">
|
||||
<h2 class="bento__heading"><?= icon('clock', 'bento__hicon') ?>Trainingszeiten</h2>
|
||||
<?php if ($sessions !== []): ?>
|
||||
<ul class="bento__sessions">
|
||||
<?php foreach ($sessions as $s): ?>
|
||||
<li>
|
||||
<span class="bento__day"><?= e($s['day']) ?></span>
|
||||
<span class="bento__time"><?= e($s['time']) ?></span>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<p class="text-muted">Die genauen Trainingszeiten erfährst du direkt bei unserem Ansprechpartner.</p>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($training['note'])): ?>
|
||||
<p class="bento__note text-muted"><?= e($training['note']) ?></p>
|
||||
<?php endif; ?>
|
||||
<span class="bento__deco" aria-hidden="true"><?= icon('clock') ?></span>
|
||||
</article>
|
||||
|
||||
<?php if ($showVenue && $addr !== []): ?>
|
||||
<article class="bento__tile bento__tile--venue">
|
||||
<h2 class="bento__heading"><?= icon('geo-alt', 'bento__hicon') ?>Spielort</h2>
|
||||
<p class="bento__address">
|
||||
<?php if (!empty($addr['venue'])): ?><strong><?= e($addr['venue']) ?></strong><br><?php endif; ?>
|
||||
<?= e($addr['street']) ?><br>
|
||||
<?= e($addr['zip']) ?> <?= e($addr['city']) ?>
|
||||
</p>
|
||||
<span class="bento__deco" aria-hidden="true"><?= icon('geo-alt') ?></span>
|
||||
</article>
|
||||
|
||||
<article class="bento__tile bento__tile--route">
|
||||
<span class="bento__route-label">Anfahrt</span>
|
||||
<a class="btn btn--outline" href="https://www.google.com/maps/search/?api=1&query=<?= e($mapsQuery) ?>" target="_blank" rel="noopener">Route planen<span class="visually-hidden"> (öffnet Google Maps in neuem Tab)</span></a>
|
||||
</article>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($highlight !== null && ($highlight['value'] ?? '') !== ''): ?>
|
||||
<article class="bento__tile bento__tile--accent">
|
||||
<span class="bento__stat-label"><?= e($highlight['label'] ?? '') ?></span>
|
||||
<span class="bento__stat-value"><?= e($highlight['value']) ?></span>
|
||||
</article>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
261
app/pages/datenschutz.php
Normal file
261
app/pages/datenschutz.php
Normal file
@@ -0,0 +1,261 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Datenschutzerklärung — zugeschnitten auf den tatsächlichen Stack:
|
||||
* komplett self-hosted, keine Tracker/Analyse/Werbe-Dienste, keine externen
|
||||
* Fonts/Maps/Embeds. Einzige Verarbeitungen: Server-Logs (Hoster),
|
||||
* Kontaktformular (Versand via Brevo SMTP), server-seitig gecachter
|
||||
* Instagram-Feed (Bilder von unserer Domain). Vereinsdaten aus club.json.
|
||||
*/
|
||||
$club = json_load('club');
|
||||
$addr = $club['address'] ?? [];
|
||||
$dpo = $club['data_protection_contact'] ?? '';
|
||||
|
||||
$meta = [
|
||||
'title' => 'Datenschutzerklärung',
|
||||
'description' => 'Datenschutzerklärung des TSV 1908 Kulmbach e. V. – welche Daten beim Besuch unserer Website verarbeitet werden. Komplett self-hosted, ohne Tracking und ohne Drittanbieter-Dienste im Browser.',
|
||||
'schema' => page_schema([
|
||||
['name' => 'Startseite', 'slug' => ''],
|
||||
['name' => 'Datenschutz', 'slug' => 'datenschutz'],
|
||||
]),
|
||||
];
|
||||
?>
|
||||
<section class="section legal">
|
||||
<div class="container">
|
||||
<div class="legal__head">
|
||||
<p class="legal__kicker">Rechtliches</p>
|
||||
<h1>Datenschutzerklärung</h1>
|
||||
<p class="legal__lead">
|
||||
Der Schutz deiner persönlichen Daten ist uns wichtig. Diese Website ist
|
||||
bewusst datensparsam gebaut: Sie lädt keine externen Schriften, Karten,
|
||||
Skripte oder Tracking-Dienste – dein Browser kontaktiert ausschließlich
|
||||
unseren eigenen Server.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="legal__body">
|
||||
<section>
|
||||
<h2>Verantwortlicher</h2>
|
||||
<p>Verantwortlich für die Datenverarbeitung auf dieser Website ist:</p>
|
||||
<div class="legal__card">
|
||||
<address>
|
||||
<strong><?= e($club['legal_name']) ?></strong><br>
|
||||
<?= e($addr['street']) ?><br>
|
||||
<?= e($addr['zip']) ?> <?= e($addr['city']) ?><br>
|
||||
<br>
|
||||
E-Mail: <a href="mailto:<?= e($club['email']) ?>"><?= e($club['email']) ?></a>
|
||||
</address>
|
||||
</div>
|
||||
<?php if ($dpo !== ''): ?>
|
||||
<p class="legal__muted">
|
||||
Ansprechpartner für Fragen zum Datenschutz: <?= e($dpo) ?>,
|
||||
erreichbar unter <a href="mailto:<?= e($club['email']) ?>"><?= e($club['email']) ?></a>.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Grundsätzliches</h2>
|
||||
<p>
|
||||
Wir verarbeiten personenbezogene Daten nur im Rahmen der geltenden
|
||||
Datenschutzgesetze, insbesondere der Datenschutz-Grundverordnung (DSGVO)
|
||||
und des Bundesdatenschutzgesetzes (BDSG). „Personenbezogene Daten“ sind
|
||||
alle Informationen, die sich auf eine identifizierte oder identifizierbare
|
||||
Person beziehen. Eine Nutzung unserer Website ist grundsätzlich ohne Angabe
|
||||
personenbezogener Daten möglich.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Aufruf der Website & Server-Logfiles</h2>
|
||||
<p>
|
||||
Unsere Website wird bei folgendem Dienstleister gehostet:
|
||||
</p>
|
||||
<div class="legal__card">
|
||||
<address>
|
||||
<strong>ALL-INKL.COM – Neue Medien Münnich</strong><br>
|
||||
Inhaber: René Münnich<br>
|
||||
Hauptstraße 68<br>
|
||||
02742 Friedersdorf
|
||||
</address>
|
||||
</div>
|
||||
<p>
|
||||
Bei jedem Aufruf unserer Website werden durch unseren Webhoster automatisch
|
||||
Informationen erfasst, die dein Browser übermittelt und die technisch
|
||||
erforderlich sind, um die Seite auszuliefern. Erfasst werden:
|
||||
</p>
|
||||
<ul>
|
||||
<li>die aufgerufene Adresse (URL) und die zuvor besuchte Seite (Referrer)</li>
|
||||
<li>Datum und Uhrzeit des Zugriffs</li>
|
||||
<li>verwendeter Browser und Betriebssystem</li>
|
||||
<li>die IP-Adresse des anfragenden Geräts</li>
|
||||
</ul>
|
||||
<p>
|
||||
Diese Daten dienen ausschließlich dem technischen Betrieb, der Sicherheit
|
||||
und der Stabilität der Website und werden nicht mit anderen Datenquellen
|
||||
zusammengeführt. Rechtsgrundlage ist unser berechtigtes Interesse an einer
|
||||
sicheren und funktionsfähigen Website (Art. 6 Abs. 1 lit. f DSGVO). Die
|
||||
Logdaten werden nach kurzer Zeit automatisch gelöscht.
|
||||
</p>
|
||||
<p class="legal__muted">
|
||||
Unser Webhoster verarbeitet diese Daten in unserem Auftrag auf Grundlage
|
||||
eines Vertrags zur Auftragsverarbeitung (Art. 28 DSGVO).
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Verschlüsselung (SSL/TLS)</h2>
|
||||
<p>
|
||||
Diese Website nutzt aus Sicherheitsgründen eine SSL- bzw. TLS-Verschlüsselung.
|
||||
Eine verschlüsselte Verbindung erkennst du am „https://“ in der Adresszeile
|
||||
deines Browsers. Daten, die du an uns übermittelst, können dadurch nicht ohne
|
||||
Weiteres von Dritten mitgelesen werden.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Cookies</h2>
|
||||
<p>
|
||||
Diese Website setzt <strong>keine Cookies zu Analyse-, Marketing- oder
|
||||
Tracking-Zwecken</strong> und bindet keine Dienste ein, die solche Cookies
|
||||
setzen würden. Aus diesem Grund benötigen wir auch kein Cookie-Banner.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Kontaktformular & Kontaktaufnahme</h2>
|
||||
<p>
|
||||
Wenn du uns über das Kontaktformular oder per E-Mail kontaktierst, verarbeiten
|
||||
wir die von dir angegebenen Daten, um deine Anfrage zu bearbeiten. Über das
|
||||
Formular sind das:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Name (Pflichtangabe)</li>
|
||||
<li>E-Mail-Adresse (Pflichtangabe)</li>
|
||||
<li>Telefonnummer (freiwillig)</li>
|
||||
<li>dein Interessensgebiet, Betreff und deine Nachricht</li>
|
||||
</ul>
|
||||
<p>
|
||||
Rechtsgrundlage ist deine Einwilligung (Art. 6 Abs. 1 lit. a DSGVO), die du
|
||||
mit dem Absenden erteilst, sowie unser berechtigtes Interesse bzw. die
|
||||
Anbahnung eines (Mitgliedschafts-)Verhältnisses an der Beantwortung deiner
|
||||
Anfrage (Art. 6 Abs. 1 lit. f und b DSGVO). Die Daten werden gespeichert, bis
|
||||
deine Anfrage abschließend bearbeitet ist und keine gesetzlichen
|
||||
Aufbewahrungspflichten entgegenstehen.
|
||||
</p>
|
||||
<p>
|
||||
Für den technischen Versand der Formular-Nachrichten als E-Mail nutzen wir den
|
||||
Dienstleister Brevo (Sendinblue GmbH, Köln) als Auftragsverarbeiter. Dabei
|
||||
werden die im Formular angegebenen Daten an die Mailserver von Brevo
|
||||
übermittelt. Die Übertragung erfolgt verschlüsselt.
|
||||
</p>
|
||||
<p class="legal__muted">
|
||||
Hinweis: Zum Schutz vor automatisiertem Spam verwenden wir ein verstecktes
|
||||
Pflichtfeld sowie einen signierten Zeitstempel. Dabei werden keine
|
||||
zusätzlichen personenbezogenen Daten erhoben und keine externen Dienste
|
||||
(z. B. reCAPTCHA) eingebunden.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Keine Analyse-, Tracking- oder Werbedienste</h2>
|
||||
<p>
|
||||
Wir verzichten bewusst vollständig auf Dienste, die dein Verhalten
|
||||
auswerten oder Daten an Dritte weitergeben. Insbesondere setzen wir
|
||||
<strong>nicht</strong> ein:
|
||||
</p>
|
||||
<ul>
|
||||
<li>keine Webanalyse (z. B. Google Analytics, Matomo, Hotjar)</li>
|
||||
<li>keine externen Schriftarten – alle Fonts liegen auf unserem Server</li>
|
||||
<li>keine eingebetteten Karten (z. B. Google Maps) und keine Video-Embeds</li>
|
||||
<li>keine Social-Media-Plugins, „Like“-Buttons oder Tracking-Pixel</li>
|
||||
<li>keine Werbenetzwerke oder Conversion-Tracking</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Instagram-Inhalte auf dieser Website</h2>
|
||||
<p>
|
||||
Auf unserer Website zeigen wir aktuelle Beiträge unseres öffentlichen
|
||||
Instagram-Profils. Diese Inhalte werden <strong>nicht</strong> über ein
|
||||
Instagram-Plugin oder einen externen Einbettungscode geladen. Stattdessen
|
||||
rufen wir die Beiträge in regelmäßigen Abständen serverseitig ab, speichern
|
||||
sie auf unserem eigenen Server und liefern Bilder und Texte ausschließlich von
|
||||
unserer Domain aus. Beim Ansehen unserer Website wird dadurch
|
||||
<strong>keine Verbindung zu Instagram oder Meta hergestellt</strong> und es
|
||||
werden keine Daten an diese Anbieter übertragen. Erst wenn du aktiv einen
|
||||
Link zu Instagram anklickst, verlässt du unsere Website (siehe nächster
|
||||
Abschnitt).
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Externe Links & Social Media</h2>
|
||||
<p>
|
||||
Unsere Website enthält Links zu externen Angeboten, etwa zu unseren Profilen
|
||||
bei Instagram und Facebook oder zu einer Routenplanung über Google Maps. Wenn
|
||||
du einen solchen Link anklickst, verlässt du unsere Website und es gelten die
|
||||
Datenschutzbestimmungen des jeweiligen Anbieters. Auf deren Datenverarbeitung
|
||||
haben wir keinen Einfluss.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Foto- und Videoaufnahmen</h2>
|
||||
<p>
|
||||
Im Rahmen unseres Vereinslebens, von Spielen und Veranstaltungen entstehen
|
||||
Foto- und Videoaufnahmen, die wir zur Darstellung des Vereins auf dieser
|
||||
Website, in unseren Social-Media-Kanälen und in Printmedien verwenden. Die
|
||||
Veröffentlichung erfolgt auf Grundlage einer Einwilligung
|
||||
(Art. 6 Abs. 1 lit. a DSGVO) bzw. unseres berechtigten Interesses an der
|
||||
Vereinsöffentlichkeitsarbeit (Art. 6 Abs. 1 lit. f DSGVO). Wenn du mit der
|
||||
Veröffentlichung einer dich zeigenden Aufnahme nicht einverstanden bist,
|
||||
genügt eine formlose Nachricht an
|
||||
<a href="mailto:<?= e($club['email']) ?>"><?= e($club['email']) ?></a> – wir
|
||||
entfernen die entsprechende Aufnahme dann zeitnah.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Deine Rechte</h2>
|
||||
<p>Dir stehen hinsichtlich deiner personenbezogenen Daten folgende Rechte zu:</p>
|
||||
<ul>
|
||||
<li>Recht auf Auskunft (Art. 15 DSGVO)</li>
|
||||
<li>Recht auf Berichtigung (Art. 16 DSGVO)</li>
|
||||
<li>Recht auf Löschung (Art. 17 DSGVO)</li>
|
||||
<li>Recht auf Einschränkung der Verarbeitung (Art. 18 DSGVO)</li>
|
||||
<li>Recht auf Datenübertragbarkeit (Art. 20 DSGVO)</li>
|
||||
<li>Recht auf Widerspruch gegen die Verarbeitung (Art. 21 DSGVO)</li>
|
||||
</ul>
|
||||
<p>
|
||||
Eine erteilte Einwilligung kannst du jederzeit mit Wirkung für die Zukunft
|
||||
widerrufen. Zur Ausübung deiner Rechte genügt eine Nachricht an die oben
|
||||
genannten Kontaktdaten.
|
||||
</p>
|
||||
<p>
|
||||
Außerdem hast du das Recht, dich bei einer Datenschutz-Aufsichtsbehörde zu
|
||||
beschweren. Die für uns zuständige Behörde ist:
|
||||
</p>
|
||||
<div class="legal__card">
|
||||
<address>
|
||||
<strong>Bayerisches Landesamt für Datenschutzaufsicht (BayLDA)</strong><br>
|
||||
Promenade 18<br>
|
||||
91522 Ansbach
|
||||
</address>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Aktualität dieser Datenschutzerklärung</h2>
|
||||
<p class="legal__muted">
|
||||
Stand: <?= e(date('m/Y')) ?>. Durch die Weiterentwicklung unserer Website
|
||||
oder geänderte gesetzliche Vorgaben kann es nötig werden, diese
|
||||
Datenschutzerklärung anzupassen. Es gilt jeweils die hier veröffentlichte
|
||||
aktuelle Fassung.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
53
app/pages/fussball.php
Normal file
53
app/pages/fussball.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Fußball-Übersichtsseite (Hub). Inhalt: data/fussball.json + data/teams.json.
|
||||
* Struktur: Hero → Mannschafts-Karten → Ansprechpartner → CTA → FAQ.
|
||||
*/
|
||||
$page = json_load('fussball');
|
||||
$teams = json_load('teams');
|
||||
|
||||
// Karten verlinken auf die vier Unterseiten (3 Senior + Jugend).
|
||||
$cards = [
|
||||
['name' => '1. Mannschaft', 'slug' => 'fussball/erste-mannschaft', 'category' => 'Herren', 'league' => 'Kreisklasse', 'image' => $teams['erste-mannschaft']['team_photo']['image'] ?? $teams['erste-mannschaft']['hero']['image']],
|
||||
['name' => '2. Mannschaft', 'slug' => 'fussball/zweite-mannschaft', 'category' => 'Herren', 'image' => $teams['zweite-mannschaft']['hero']['image']],
|
||||
['name' => 'Damenmannschaft', 'slug' => 'fussball/damen', 'category' => 'Damen', 'image' => $teams['damen']['hero']['image']],
|
||||
['name' => 'Jugendfußball', 'slug' => 'jugendfussball', 'category' => 'G- bis C-Jugend', 'image' => [
|
||||
'base' => 'img/pages/jugendfussball-hero', 'widths' => [640, 1000, 1440], 'alt' => 'Jugendfußball des TSV 08 Kulmbach',
|
||||
]],
|
||||
];
|
||||
|
||||
$meta = [
|
||||
'title' => 'Fußball in Kulmbach',
|
||||
'description' => 'Herren-, Damen- und Jugendfußball beim TSV 08 Kulmbach: erste und zweite Mannschaft auf Kreisklasse-Niveau, Damenmannschaft und Nachwuchs – aktiv werden im Katzbachtal.',
|
||||
'og_image' => 'img/pages/fussball-hero-1600.jpg',
|
||||
'schema' => page_schema(
|
||||
[
|
||||
['name' => 'Startseite', 'slug' => ''],
|
||||
['name' => 'Fußball', 'slug' => 'fussball'],
|
||||
],
|
||||
$page['faq'] ?? []
|
||||
),
|
||||
];
|
||||
|
||||
component('hero', ['hero' => $page['hero']]);
|
||||
component('team-cards', [
|
||||
'teams' => $cards,
|
||||
'intro' => $page['mannschaften_intro'] ?? '',
|
||||
'title_hidden' => true,
|
||||
'intro_below' => true,
|
||||
]);
|
||||
|
||||
component('person-grid', [
|
||||
'departments' => ['Herrenfußball', 'Damenfußball'],
|
||||
// Auf dem Hub bewusst nur die beiden Haupt-Ansprechpartner (mannschaftsspezifische
|
||||
// Zusatzkontakte wie Rustem erscheinen auf der jeweiligen Team-Seite).
|
||||
'names' => ['Benedikt Höfner', 'Sam Kern'],
|
||||
'title' => 'Ansprechpartner Fußball',
|
||||
'intro' => 'Fragen zu Training, Probetraining oder einem Wechsel? Sprich direkt unsere Verantwortlichen an.',
|
||||
'anchor' => 'kontakt',
|
||||
]);
|
||||
|
||||
component('faq', ['faq' => $page['faq'] ?? []]);
|
||||
@@ -26,4 +26,5 @@ foreach ($home['sections'] as $section) {
|
||||
|
||||
component('stats', ['stats' => $home['stats']]);
|
||||
component('partner-grid', ['intro' => $home['partners']]);
|
||||
component('person-grid');
|
||||
component('contact-form', ['intro' => $home['contact']]);
|
||||
|
||||
139
app/pages/impressum.php
Normal file
139
app/pages/impressum.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Impressum — Angaben gemäß § 5 DDG, Vertretungsberechtigter Vorstand,
|
||||
* Bankverbindung, Haftungs- und Urheberrechtshinweise.
|
||||
* Alle Vereinsdaten aus data/club.json (Single Source of Truth).
|
||||
*/
|
||||
$club = json_load('club');
|
||||
$addr = $club['address'] ?? [];
|
||||
$chair = $club['board']['members'][0]['name'] ?? '';
|
||||
|
||||
$meta = [
|
||||
'title' => 'Impressum',
|
||||
'description' => 'Impressum des TSV 1908 Kulmbach e. V. – Anbieterkennzeichnung, vertretungsberechtigter Vorstand und Kontaktdaten.',
|
||||
'schema' => page_schema([
|
||||
['name' => 'Startseite', 'slug' => ''],
|
||||
['name' => 'Impressum', 'slug' => 'impressum'],
|
||||
]),
|
||||
];
|
||||
?>
|
||||
<section class="section legal">
|
||||
<div class="container">
|
||||
<div class="legal__head">
|
||||
<p class="legal__kicker">Rechtliches</p>
|
||||
<h1>Impressum</h1>
|
||||
<p class="legal__lead">Angaben gemäß § 5 Digitale-Dienste-Gesetz (DDG).</p>
|
||||
</div>
|
||||
|
||||
<div class="legal__body">
|
||||
<section>
|
||||
<h2>Anbieter</h2>
|
||||
<div class="legal__card">
|
||||
<address>
|
||||
<strong><?= e($club['legal_name']) ?></strong><br>
|
||||
<?= e($addr['street']) ?><br>
|
||||
<?= e($addr['zip']) ?> <?= e($addr['city']) ?><br>
|
||||
<br>
|
||||
E-Mail: <a href="mailto:<?= e($club['email']) ?>"><?= e($club['email']) ?></a><br>
|
||||
Web: <a href="<?= e(config('base_url')) ?>"><?= e(preg_replace('#^https?://#', '', (string) config('base_url'))) ?></a>
|
||||
</address>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Vertretungsberechtigter Vorstand</h2>
|
||||
<p><?= e($club['board']['note']) ?>:</p>
|
||||
<ul>
|
||||
<?php foreach ($club['board']['members'] as $member): ?>
|
||||
<li><?= e($member['name']) ?><?= $member['role'] !== '' ? ' – ' . e($member['role']) : '' ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Bankverbindung</h2>
|
||||
<div class="legal__cards legal__cards--2">
|
||||
<?php foreach ($club['banking'] as $bank): ?>
|
||||
<div class="legal__card">
|
||||
<p>
|
||||
<strong><?= e($bank['bank']) ?></strong><br>
|
||||
IBAN: <?= e($bank['iban']) ?><br>
|
||||
BIC: <?= e($bank['bic']) ?>
|
||||
</p>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Mitgliedschaften</h2>
|
||||
<ul>
|
||||
<?php foreach ($club['memberships'] as $membership): ?>
|
||||
<li><?= e($membership) ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Verantwortlich für den Inhalt</h2>
|
||||
<p>Verantwortlich für den Inhalt nach § 18 Abs. 2 Medienstaatsvertrag (MStV):</p>
|
||||
<div class="legal__card">
|
||||
<address>
|
||||
<?php if ($chair !== ''): ?>
|
||||
<strong><?= e($chair) ?></strong><br>
|
||||
<?php endif; ?>
|
||||
<?= e($club['legal_name']) ?><br>
|
||||
<?= e($addr['street']) ?>, <?= e($addr['zip']) ?> <?= e($addr['city']) ?>
|
||||
</address>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Urheberrecht</h2>
|
||||
<p>
|
||||
Alle Rechte vorbehalten. Sämtliche auf dieser Website veröffentlichten Bilder,
|
||||
Texte und sonstigen Dateien unterliegen dem Urheberrecht. Eine Verwendung,
|
||||
Vervielfältigung oder Weitergabe – auch auszugsweise – ist nur mit ausdrücklicher,
|
||||
vorheriger Zustimmung des TSV 1908 Kulmbach e. V. gestattet. Soweit Inhalte auf
|
||||
dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter
|
||||
beachtet und entsprechend gekennzeichnet. Sollte dir dennoch eine
|
||||
Urheberrechtsverletzung auffallen, bitten wir um einen Hinweis – bei Bekanntwerden
|
||||
entfernen wir die betreffenden Inhalte umgehend.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Haftung für Inhalte</h2>
|
||||
<p>
|
||||
Die Inhalte dieser Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit,
|
||||
Vollständigkeit und Aktualität der Inhalte können wir jedoch keine Gewähr übernehmen.
|
||||
Als Diensteanbieter sind wir gemäß § 7 Abs. 1 DDG für eigene Inhalte auf diesen Seiten
|
||||
nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 DDG sind wir als
|
||||
Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde
|
||||
Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige
|
||||
Tätigkeit hinweisen.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Haftung für Links</h2>
|
||||
<p>
|
||||
Unser Angebot enthält Links zu externen Websites Dritter, auf deren Inhalte wir keinen
|
||||
Einfluss haben. Für die Gestaltung und die Inhalte der gelinkten Seiten sind
|
||||
ausschließlich deren Betreiber verantwortlich. Zum Zeitpunkt der Verlinkung waren keine
|
||||
rechtswidrigen Inhalte erkennbar. Sollten uns Rechtsverletzungen bekannt werden,
|
||||
entfernen wir die entsprechenden Links umgehend. Hiermit distanzieren wir uns
|
||||
ausdrücklich von allen Inhalten sämtlicher verlinkter Seiten.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Gestaltung & Umsetzung</h2>
|
||||
<p class="legal__muted">Webdesign und Layout: <?= e($club['webdesign']) ?>.</p>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
80
app/pages/jugendfussball.php
Normal file
80
app/pages/jugendfussball.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Jugendfußball-Übersichtsseite. Inhalt: data/jugend.json.
|
||||
* Komposition: Hero → Intro-Bento (Bild, Mitmachen, Spielort, Altersklassen) →
|
||||
* Altersklassen-Karten → Ansprechpartner → CTA → FAQ.
|
||||
*/
|
||||
$page = json_load('jugend');
|
||||
|
||||
$club = json_load('club');
|
||||
$addr = $club['address'] ?? [];
|
||||
$mapsQuery = rawurlencode(trim(($addr['venue'] ?? '') . ', ' . ($addr['street'] ?? '') . ', ' . ($addr['zip'] ?? '') . ' ' . ($addr['city'] ?? ''), ', '));
|
||||
|
||||
$meta = [
|
||||
'title' => 'Jugendfußball in Kulmbach',
|
||||
'description' => 'Jugendfußball in Kulmbach – entdecke unsere Mannschaften von der G- bis zur C-Jugend, Betreuer und Trainingszeiten. Probiere Fußball in familiärer Atmosphäre aus.',
|
||||
'og_image' => 'img/pages/jugendfussball-hero-1440.jpg',
|
||||
'schema' => page_schema(
|
||||
[
|
||||
['name' => 'Startseite', 'slug' => ''],
|
||||
['name' => 'Fußball', 'slug' => 'fussball'],
|
||||
['name' => 'Jugendfußball', 'slug' => 'jugendfussball'],
|
||||
],
|
||||
$page['faq'] ?? []
|
||||
),
|
||||
];
|
||||
|
||||
component('hero', ['hero' => $page['hero']]);
|
||||
?>
|
||||
<section class="section" id="ueber-uns">
|
||||
<div class="container">
|
||||
<div class="intro-bento">
|
||||
<div class="bento__tile intro-bento__image">
|
||||
<?php component('img', ['image' => $page['hero']['image'], 'sizes' => '(max-width: 900px) 100vw, 40vw', 'class' => 'intro-bento__img']); ?>
|
||||
</div>
|
||||
|
||||
<article class="bento__tile intro-bento__about">
|
||||
<h2><?= e($page['about']['title']) ?></h2>
|
||||
<p><?= e($page['about']['text']) ?></p>
|
||||
</article>
|
||||
|
||||
<article class="bento__tile intro-bento__venue">
|
||||
<h2 class="bento__heading"><?= icon('geo-alt', 'bento__hicon') ?>Spielort</h2>
|
||||
<p class="bento__address">
|
||||
<?php if (!empty($addr['venue'])): ?><strong><?= e($addr['venue']) ?></strong><br><?php endif; ?>
|
||||
<?= e($addr['street']) ?>, <?= e($addr['zip']) ?> <?= e($addr['city']) ?>
|
||||
</p>
|
||||
<a class="btn btn--outline intro-bento__route" href="https://www.google.com/maps/search/?api=1&query=<?= e($mapsQuery) ?>" target="_blank" rel="noopener">Route planen<span class="visually-hidden"> (öffnet Google Maps in neuem Tab)</span></a>
|
||||
<span class="bento__deco" aria-hidden="true"><?= icon('geo-alt') ?></span>
|
||||
</article>
|
||||
|
||||
<article class="bento__tile bento__tile--accent intro-bento__stat">
|
||||
<span class="bento__stat-label">Altersklassen</span>
|
||||
<span class="bento__stat-value">G – C</span>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<?php
|
||||
component('age-groups', ['groups' => $page['groups'] ?? []]);
|
||||
|
||||
component('person-grid', [
|
||||
'departments' => ['Jugendfußball'],
|
||||
'title' => 'Ansprechpartner Jugendfußball',
|
||||
'intro' => 'Fragen zum Schnuppertraining oder zur passenden Altersgruppe? Melde dich direkt.',
|
||||
'anchor' => 'kontakt',
|
||||
]);
|
||||
|
||||
component('cta-band', ['band' => [
|
||||
'title' => 'Neugierig geworden?',
|
||||
'text' => 'Komm mit deinem Kind zum kostenlosen Schnuppertraining – ganz unverbindlich.',
|
||||
'ctas' => [
|
||||
['label' => 'Zum Ansprechpartner', 'slug' => 'jugendfussball#kontakt', 'style' => 'primary'],
|
||||
['label' => 'Mitglied werden', 'slug' => 'mitglied-werden', 'style' => 'outline'],
|
||||
],
|
||||
]]);
|
||||
|
||||
component('faq', ['faq' => $page['faq'] ?? []]);
|
||||
18
app/pages/team-damen.php
Normal file
18
app/pages/team-damen.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Damenmannschaft (Damenfußball). Inhalt: data/teams.json → damen.
|
||||
*/
|
||||
$slug = 'fussball/damen';
|
||||
$team = json_load('teams')['damen'];
|
||||
|
||||
$meta = [
|
||||
'title' => 'Damenmannschaft – Damenfußball Kulmbach',
|
||||
'description' => $team['hero']['text'],
|
||||
'og_image' => $team['hero']['image']['base'] . '-1600.jpg',
|
||||
'schema' => team_schema($team, $slug),
|
||||
];
|
||||
|
||||
component('team', ['team' => $team, 'slug' => $slug]);
|
||||
18
app/pages/team-erste.php
Normal file
18
app/pages/team-erste.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 1. Mannschaft (Herrenfußball). Inhalt: data/teams.json → erste-mannschaft.
|
||||
*/
|
||||
$slug = 'fussball/erste-mannschaft';
|
||||
$team = json_load('teams')['erste-mannschaft'];
|
||||
|
||||
$meta = [
|
||||
'title' => '1. Mannschaft – Herrenfußball Kreisklasse Kulmbach',
|
||||
'description' => $team['hero']['text'],
|
||||
'og_image' => $team['hero']['image']['base'] . '-1600.jpg',
|
||||
'schema' => team_schema($team, $slug),
|
||||
];
|
||||
|
||||
component('team', ['team' => $team, 'slug' => $slug]);
|
||||
19
app/pages/team-zwei.php
Normal file
19
app/pages/team-zwei.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* 2. Mannschaft (Herrenfußball). Inhalt: data/teams.json → zweite-mannschaft.
|
||||
*/
|
||||
$slug = 'fussball/zweite-mannschaft';
|
||||
$team = json_load('teams')['zweite-mannschaft'];
|
||||
|
||||
$meta = [
|
||||
'title' => '2. Mannschaft – Herrenfußball Kulmbach',
|
||||
'description' => $team['hero']['text'],
|
||||
'og_image' => $team['hero']['image']['base'] . '-1600.jpg',
|
||||
'scripts' => (count($team['contact_names'] ?? []) > 1) ? ['contact-slider.js'] : [],
|
||||
'schema' => team_schema($team, $slug),
|
||||
];
|
||||
|
||||
component('team', ['team' => $team, 'slug' => $slug]);
|
||||
84
app/pages/verein.php
Normal file
84
app/pages/verein.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Vereins-Übersichtsseite (Hub). Inhalt: data/verein.json + data/club.json.
|
||||
* Struktur: Hero → Eckdaten (key-facts) → Leitbild (split) → Abteilungs-Kacheln →
|
||||
* Gelände/Drohnenbild → CTA → FAQ.
|
||||
*/
|
||||
$page = json_load('verein');
|
||||
|
||||
$club = json_load('club');
|
||||
$addr = $club['address'] ?? [];
|
||||
$mapsQuery = rawurlencode(trim(($addr['venue'] ?? '') . ', ' . ($addr['street'] ?? '') . ', ' . ($addr['zip'] ?? '') . ' ' . ($addr['city'] ?? ''), ', '));
|
||||
|
||||
// Zwei Abteilungs-Kacheln. Turnen verlinkt bereits auf /turnen (Seite folgt).
|
||||
$cards = [
|
||||
[
|
||||
'name' => 'Fußball',
|
||||
'slug' => 'fussball',
|
||||
'category' => 'Herren · Damen · Jugend',
|
||||
'image' => ['base' => 'img/pages/fussball-hero', 'widths' => [640, 1000, 1600], 'alt' => 'Fußball beim TSV 08 Kulmbach'],
|
||||
],
|
||||
[
|
||||
'name' => 'Turnabteilung',
|
||||
'slug' => 'turnen',
|
||||
'category' => 'Turnen · Trampolin',
|
||||
'image' => ['base' => 'img/pages/turnen', 'widths' => [640, 1000], 'alt' => 'Turnabteilung des TSV 08 Kulmbach'],
|
||||
],
|
||||
];
|
||||
|
||||
$meta = [
|
||||
'title' => 'Der Verein',
|
||||
'description' => 'Der TSV 1908 Kulmbach e. V. – seit 1908 dein Sportverein im Katzbachtal in Kulmbach. Über 650 Mitglieder, die Abteilungen Fußball und Turnen, zuhause auf der Hans-Rausch-Sportanlage.',
|
||||
'og_image' => 'img/pages/vereinsgelaende-1600.jpg',
|
||||
'schema' => page_schema(
|
||||
[
|
||||
['name' => 'Startseite', 'slug' => ''],
|
||||
['name' => 'Der Verein', 'slug' => 'verein'],
|
||||
],
|
||||
$page['faq'] ?? []
|
||||
),
|
||||
];
|
||||
|
||||
component('hero', ['hero' => $page['hero']]);
|
||||
|
||||
component('key-facts', ['facts' => $page['facts'] ?? [], 'anchor' => 'eckdaten']);
|
||||
|
||||
component('section', ['section' => $page['about']]);
|
||||
|
||||
component('team-cards', [
|
||||
'teams' => $cards,
|
||||
'title' => 'Unsere Abteilungen',
|
||||
'intro' => $page['abteilungen_intro'] ?? '',
|
||||
'intro_below' => true,
|
||||
'anchor' => 'abteilungen',
|
||||
]);
|
||||
?>
|
||||
<section class="section facility" id="gelaende">
|
||||
<div class="container">
|
||||
<h2><?= e($page['gelaende']['title']) ?></h2>
|
||||
<p class="facility__lead"><?= e($page['gelaende']['text']) ?></p>
|
||||
<figure class="facility__figure">
|
||||
<?php component('img', ['image' => $page['gelaende']['image'], 'sizes' => '(max-width: 1200px) 100vw, 1200px', 'class' => 'facility__img']); ?>
|
||||
<?php if (!empty($page['gelaende']['caption'])): ?>
|
||||
<figcaption class="facility__caption"><?= e($page['gelaende']['caption']) ?></figcaption>
|
||||
<?php endif; ?>
|
||||
</figure>
|
||||
<p class="facility__actions">
|
||||
<a class="btn btn--outline" href="https://www.google.com/maps/search/?api=1&query=<?= e($mapsQuery) ?>" target="_blank" rel="noopener">Route planen<span class="visually-hidden"> (öffnet Google Maps in neuem Tab)</span></a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<?php
|
||||
component('cta-band', ['band' => [
|
||||
'title' => 'Werde Teil des TSV 08',
|
||||
'text' => 'Ob Fußball oder Turnen – komm unverbindlich vorbei oder schreib uns. Wir finden gemeinsam die passende Gruppe für dich.',
|
||||
'ctas' => [
|
||||
['label' => 'Mitglied werden', 'slug' => 'mitglied-werden', 'style' => 'primary'],
|
||||
['label' => 'Kontakt aufnehmen', 'slug' => '#kontakt', 'style' => 'outline'],
|
||||
],
|
||||
]]);
|
||||
|
||||
component('faq', ['faq' => $page['faq'] ?? []]);
|
||||
@@ -10,8 +10,15 @@ declare(strict_types=1);
|
||||
* Canonical und Sitemap folgen automatisch.
|
||||
*/
|
||||
return [
|
||||
'' => ['file' => 'home.php', 'sitemap' => true],
|
||||
'fussball' => ['file' => 'fussball.php', 'sitemap' => true],
|
||||
'historie' => ['file' => 'historie.php', 'sitemap' => true],
|
||||
'404' => ['file' => '404.php', 'sitemap' => false],
|
||||
'' => ['file' => 'home.php', 'sitemap' => true],
|
||||
'fussball' => ['file' => 'fussball.php', 'sitemap' => true],
|
||||
'fussball/erste-mannschaft' => ['file' => 'team-erste.php', 'sitemap' => true],
|
||||
'fussball/zweite-mannschaft' => ['file' => 'team-zwei.php', 'sitemap' => true],
|
||||
'fussball/damen' => ['file' => 'team-damen.php', 'sitemap' => true],
|
||||
'jugendfussball' => ['file' => 'jugendfussball.php', 'sitemap' => true],
|
||||
'verein' => ['file' => 'verein.php', 'sitemap' => true],
|
||||
'historie' => ['file' => 'historie.php', 'sitemap' => true],
|
||||
'impressum' => ['file' => 'impressum.php', 'sitemap' => true],
|
||||
'datenschutz' => ['file' => 'datenschutz.php', 'sitemap' => true],
|
||||
'404' => ['file' => '404.php', 'sitemap' => false],
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user