1e51ef7def
- Konzept/, didaktik_geografie/, didaktik_simulation/, v2-modules/, v2-platform/ - 12 code-workspace-Files - STATUS-*.md - viele M/D/R-Änderungen an bereits getrackten Files - .gitignore verstärkt: **/.humaninput/, **/secret_keys.txt Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
480 lines
19 KiB
PHP
480 lines
19 KiB
PHP
<?php
|
||
/**
|
||
* Simulationen — Einstiegs-Grid
|
||
* -----------------------------
|
||
* Übersicht aller Module aus `module_info`. Zwei CTAs pro Card:
|
||
* "Mehr erfahren" → modul-*.php Detailseite · "Simulation starten" → play_url.
|
||
*
|
||
* URL: http://localhost/geograsim/App/simulationen
|
||
* ?status=aktiv vorgewählter Status-Filter
|
||
* ?theme=klima vorgewähltes Kompetenz-Thema
|
||
*/
|
||
|
||
require_once __DIR__ . '/../php/config/app.php';
|
||
require_once __DIR__ . '/../php/config/db.php';
|
||
require_once __DIR__ . '/../php/lib/EasyLang.php';
|
||
require_once __DIR__ . '/../php/lib/Country.php';
|
||
|
||
$db = getDB();
|
||
|
||
// Filter aus URL
|
||
$qStatus = preg_replace('/[^a-z]/', '', strtolower($_GET['status'] ?? ''));
|
||
$qTheme = preg_replace('/[^a-z0-9_-]/', '', strtolower($_GET['theme'] ?? ''));
|
||
|
||
// Alle Module laden
|
||
$modules = $db->query('
|
||
SELECT module_id, title, subtitle, short_desc, short_desc_easy,
|
||
duration_min, age_min, age_max, icon, card_image,
|
||
page_url, play_url, status, available_from, sort_order
|
||
FROM module_info
|
||
ORDER BY FIELD(status, "aktiv", "beta", "geplant", "archiv"), sort_order, title
|
||
')->fetchAll();
|
||
|
||
// Kompetenz-Zuordnung je Modul (fuer Theme-Chip + Thema-Filter)
|
||
$kompMap = [];
|
||
$kompQ = $db->query('
|
||
SELECT km.module_id, k.key_slug, k.title, k.category, km.strength
|
||
FROM kompetenz_modules km
|
||
INNER JOIN kompetenzen k ON k.id = km.kompetenz_id
|
||
ORDER BY km.strength, k.sort_order
|
||
');
|
||
foreach ($kompQ as $r) {
|
||
$kompMap[$r['module_id']][] = $r;
|
||
}
|
||
|
||
// Alle moeglichen Themen / Kompetenzen einsammeln (Filter-Chips)
|
||
$themes = $db->query('SELECT key_slug, title, category FROM kompetenzen ORDER BY sort_order')->fetchAll();
|
||
|
||
// Zaehlungen pro Status
|
||
$counts = ['aktiv' => 0, 'beta' => 0, 'geplant' => 0, 'archiv' => 0];
|
||
foreach ($modules as $m) {
|
||
$counts[$m['status']] = ($counts[$m['status']] ?? 0) + 1;
|
||
}
|
||
|
||
$bp = BASE_PATH;
|
||
$easyActive = EasyLang::isActive();
|
||
|
||
function esc(?string $s): string { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
|
||
|
||
$statusMeta = [
|
||
'aktiv' => ['label' => 'Aktiv', 'tone' => 'moss', 'icon' => '✓'],
|
||
'beta' => ['label' => 'Beta', 'tone' => 'sand', 'icon' => '⚠'],
|
||
'geplant' => ['label' => 'In Entw.', 'tone' => 'fjord', 'icon' => '🔒'],
|
||
'archiv' => ['label' => 'Archiv', 'tone' => 'muted', 'icon' => '□'],
|
||
];
|
||
?><!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||
<title>Simulationen — Lehrplan geograsim.at</title>
|
||
<meta name="description" content="Alle GeoGraSim-Simulationen auf einen Blick — Klimawächter, Flussmanagement, Heli-Navigation, Stadt & Raumplanung und weitere in Entwicklung.">
|
||
<link rel="icon" type="image/svg+xml" href="<?= $bp ?>/favicon.svg">
|
||
<link rel="icon" type="image/png" href="<?= $bp ?>/favicon-96x96.png" sizes="96x96">
|
||
<link rel="stylesheet" href="<?= $bp ?>/assets/fonts/inter.css">
|
||
<link rel="stylesheet" href="<?= $bp ?>/assets/css/design-system.css">
|
||
<style>
|
||
body { overflow: auto; height: auto; min-height: 100dvh; background: var(--ggs-bg); }
|
||
|
||
/* Topbar identisch zu lehrplan.php */
|
||
.sp-topbar {
|
||
background: var(--ggs-white); padding: .55rem 1.2rem;
|
||
display: flex; align-items: center; gap: .8rem;
|
||
border-bottom: 1px solid var(--ggs-border);
|
||
position: sticky; top: 0; z-index: 50;
|
||
}
|
||
.sp-topbar a { text-decoration: none; }
|
||
.sp-topbar-brand img { height: 40px; display: block; }
|
||
.sp-topbar .spacer { flex: 1; }
|
||
.sp-btn-sm {
|
||
padding: .5rem .85rem; border: 1px solid var(--ggs-border);
|
||
border-radius: var(--ggs-radius-sm); background: var(--ggs-white);
|
||
font-size: .78rem; font-weight: 600; cursor: pointer;
|
||
color: var(--ggs-text); font-family: inherit; text-decoration: none;
|
||
display: inline-flex; align-items: center; min-height: 40px;
|
||
}
|
||
.sp-btn-sm:hover { background: var(--ggs-fjord-light); color: var(--ggs-fjord-dark); border-color: var(--ggs-fjord); }
|
||
.sp-btn-sm.sp-btn-primary { background: var(--ggs-fjord); color: var(--ggs-white); border-color: var(--ggs-fjord); }
|
||
.sp-easy-pill { background: var(--ggs-moss-light); color: var(--ggs-moss-dark); border-color: var(--ggs-moss); font-weight: 700; }
|
||
|
||
/* Hero */
|
||
.sp-hero {
|
||
background: linear-gradient(135deg, var(--ggs-fjord-dark) 0%, var(--ggs-fjord) 100%);
|
||
color: var(--ggs-white); padding: 2.5rem 1.5rem 3rem; text-align: center;
|
||
}
|
||
.sp-hero h1 { font-size: clamp(1.8rem, 4vw, 2.6rem); font-weight: 900; letter-spacing: -.03em; margin-bottom: .5rem; }
|
||
.sp-hero p { font-size: 1.02rem; color: rgba(255,255,255,.88); max-width: 640px; margin: 0 auto 1.4rem; }
|
||
.sp-hero-stats { display: flex; justify-content: center; gap: 2rem; flex-wrap: wrap; margin-top: 1.2rem; }
|
||
.sp-hero-stat { text-align: center; }
|
||
.sp-hero-stat-num { font-size: 1.8rem; font-weight: 900; display: block; }
|
||
.sp-hero-stat-lbl { font-size: .72rem; opacity: .8; text-transform: uppercase; letter-spacing: .08em; }
|
||
|
||
/* Filter-Bar */
|
||
.sp-filters {
|
||
max-width: 1200px; margin: -1.4rem auto 1.5rem;
|
||
padding: 1rem 1.2rem; background: var(--ggs-white);
|
||
border-radius: var(--ggs-radius-lg); box-shadow: var(--ggs-shadow-lg);
|
||
border: 1px solid var(--ggs-border);
|
||
position: relative; z-index: 5;
|
||
display: flex; flex-direction: column; gap: .9rem;
|
||
}
|
||
.sp-filter-row { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; }
|
||
.sp-filter-label {
|
||
font-size: .72rem; font-weight: 800; text-transform: uppercase;
|
||
letter-spacing: .08em; color: var(--ggs-text-muted); min-width: 60px;
|
||
}
|
||
.sp-chip {
|
||
display: inline-flex; align-items: center; gap: .3rem;
|
||
padding: .55rem 1rem; border-radius: var(--ggs-radius-pill);
|
||
background: var(--ggs-white); border: 1.5px solid var(--ggs-border);
|
||
font-size: .88rem; font-weight: 600; color: var(--ggs-text-muted);
|
||
cursor: pointer; transition: all .2s var(--ggs-ease); font-family: inherit;
|
||
min-height: 40px; min-width: 40px;
|
||
}
|
||
.sp-chip:hover { border-color: var(--ggs-fjord); color: var(--ggs-fjord); }
|
||
.sp-chip:active { transform: scale(0.96); }
|
||
.sp-chip.active { background: var(--ggs-fjord); color: var(--ggs-white); border-color: var(--ggs-fjord); }
|
||
.sp-chip-badge {
|
||
font-size: .7rem; padding: 1px 6px; border-radius: var(--ggs-radius-pill);
|
||
background: var(--ggs-bg-dark); color: var(--ggs-text-muted); font-weight: 700;
|
||
}
|
||
.sp-chip.active .sp-chip-badge { background: rgba(255,255,255,.22); color: var(--ggs-white); }
|
||
|
||
/* Grid */
|
||
.sp-wrap { max-width: 1200px; margin: 0 auto 3rem; padding: 0 1.2rem; }
|
||
.sp-results-info {
|
||
font-size: .82rem; color: var(--ggs-text-muted);
|
||
margin-bottom: 1rem;
|
||
}
|
||
.sp-grid {
|
||
display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||
gap: 1.4rem;
|
||
}
|
||
|
||
/* Modul-Card */
|
||
.sp-card {
|
||
background: var(--ggs-white); border: 1px solid var(--ggs-border);
|
||
border-radius: var(--ggs-radius-lg); overflow: hidden;
|
||
display: flex; flex-direction: column;
|
||
transition: transform .25s var(--ggs-ease), box-shadow .25s var(--ggs-ease),
|
||
border-color .25s var(--ggs-ease);
|
||
box-shadow: var(--ggs-shadow-sm);
|
||
}
|
||
.sp-card:hover { transform: translateY(-3px); box-shadow: var(--ggs-shadow-md);
|
||
border-color: var(--ggs-fjord-light); }
|
||
.sp-card.sp-card-geplant { opacity: .72; filter: grayscale(.55); transition: filter .25s, opacity .25s; }
|
||
.sp-card.sp-card-geplant:hover { opacity: .92; filter: grayscale(.15); }
|
||
.sp-card.sp-card-geplant .sp-pill-pending { filter: grayscale(0); opacity: 1; }
|
||
.sp-card-media {
|
||
aspect-ratio: 16 / 10; overflow: hidden; position: relative;
|
||
background: var(--ggs-fjord-light);
|
||
}
|
||
.sp-card-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||
.sp-card-badge {
|
||
position: absolute; top: 10px; right: 10px;
|
||
font-size: .7rem; font-weight: 700; padding: 4px 10px;
|
||
border-radius: var(--ggs-radius-pill); letter-spacing: .04em;
|
||
text-transform: uppercase;
|
||
}
|
||
.sp-card-badge.moss { background: var(--ggs-moss); color: var(--ggs-white); }
|
||
.sp-card-badge.sand { background: var(--ggs-sand-dark); color: var(--ggs-white); }
|
||
.sp-card-badge.fjord { background: var(--ggs-fjord); color: var(--ggs-white); opacity: .85; }
|
||
.sp-card-badge.muted { background: var(--ggs-text-muted); color: var(--ggs-white); }
|
||
.sp-pill-pending {
|
||
display: inline-flex; align-items: center; gap: .4rem;
|
||
background: var(--ggs-sand-light, #f5efd9); color: var(--ggs-fjord-dark);
|
||
border: 1px dashed var(--ggs-sand-dark, #d4b76a);
|
||
font-size: .82rem; font-weight: 700; padding: .55rem .9rem;
|
||
border-radius: var(--ggs-radius-pill); letter-spacing: .01em;
|
||
}
|
||
|
||
.sp-card-body {
|
||
padding: 1rem 1.2rem 1.2rem; display: flex; flex-direction: column; gap: .6rem;
|
||
flex: 1;
|
||
}
|
||
.sp-card-title {
|
||
font-size: 1.2rem; font-weight: 800; color: var(--ggs-fjord-dark);
|
||
letter-spacing: -.02em; display: flex; align-items: center; gap: .4rem;
|
||
line-height: 1.2;
|
||
}
|
||
.sp-card-icon { font-size: 1.4rem; line-height: 1; }
|
||
.sp-card-subtitle {
|
||
font-size: .88rem; color: var(--ggs-text); line-height: 1.5;
|
||
flex: 1;
|
||
}
|
||
.sp-card-meta {
|
||
display: flex; flex-wrap: wrap; gap: .4rem;
|
||
margin-top: .3rem;
|
||
}
|
||
.sp-card-meta-item {
|
||
font-size: .72rem; color: var(--ggs-text-muted);
|
||
background: var(--ggs-bg); padding: 3px 9px;
|
||
border-radius: var(--ggs-radius-pill);
|
||
display: inline-flex; align-items: center; gap: .25rem;
|
||
}
|
||
.sp-card-themes {
|
||
display: flex; flex-wrap: wrap; gap: .3rem;
|
||
margin-top: .2rem;
|
||
}
|
||
.sp-card-theme {
|
||
font-size: .7rem; color: var(--ggs-fjord-dark);
|
||
background: var(--ggs-fjord-light); padding: 2px 8px;
|
||
border-radius: var(--ggs-radius-pill); font-weight: 600;
|
||
}
|
||
.sp-card-cta {
|
||
display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .4rem;
|
||
}
|
||
.sp-btn {
|
||
display: inline-flex; align-items: center; justify-content: center;
|
||
gap: .35rem; padding: .7rem 1rem;
|
||
border-radius: var(--ggs-radius-sm);
|
||
font-weight: 700; font-size: .88rem;
|
||
text-decoration: none; border: 1.5px solid transparent;
|
||
transition: all .2s var(--ggs-ease); min-height: 44px;
|
||
flex: 1; text-align: center;
|
||
}
|
||
.sp-btn-primary { background: var(--ggs-fjord); color: var(--ggs-white); }
|
||
.sp-btn-primary:hover { background: var(--ggs-fjord-dark); }
|
||
.sp-btn-primary:active { transform: scale(0.98); }
|
||
.sp-btn-secondary {
|
||
background: var(--ggs-white); color: var(--ggs-fjord);
|
||
border-color: var(--ggs-fjord);
|
||
}
|
||
.sp-btn-secondary:hover { background: var(--ggs-fjord-light); }
|
||
.sp-btn-secondary:active { transform: scale(0.98); }
|
||
.sp-btn-disabled {
|
||
background: var(--ggs-bg); color: var(--ggs-text-muted);
|
||
border-color: var(--ggs-border); cursor: not-allowed;
|
||
}
|
||
|
||
.sp-empty {
|
||
grid-column: 1 / -1;
|
||
text-align: center; padding: 3rem 1rem;
|
||
color: var(--ggs-text-muted); font-style: italic;
|
||
}
|
||
|
||
/* Footer */
|
||
.sp-footer {
|
||
text-align: center; padding: 2rem 1rem 2.5rem;
|
||
font-size: .78rem; color: var(--ggs-text-muted);
|
||
border-top: 1px solid var(--ggs-border);
|
||
background: var(--ggs-white); margin-top: 2rem;
|
||
}
|
||
.sp-footer img { height: 36px; opacity: .5; margin-bottom: .5rem; }
|
||
.sp-footer a { color: var(--ggs-fjord); text-decoration: none; padding: .3rem .2rem; }
|
||
|
||
@media (max-width: 900px) {
|
||
.sp-filter-row { flex-direction: column; align-items: flex-start; gap: .3rem; }
|
||
.sp-filter-label { min-width: 0; }
|
||
.sp-grid { grid-template-columns: 1fr; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<?php
|
||
/* Standard-Hauptnav + Kontext-Sub-Nav (N.5.1 — Simulationen-Übersicht ohne Country-Picker,
|
||
weil Sims country-agnostisch sind) */
|
||
$activeMain = 'sims';
|
||
$activeSub = 'simulationen';
|
||
$showCountryPicker = false;
|
||
include __DIR__ . '/_partials/public_topnav.php';
|
||
include __DIR__ . '/_partials/erkundungs_subnav.php';
|
||
?>
|
||
|
||
<section class="sp-hero">
|
||
<h1>Alle Simulationen</h1>
|
||
<p>
|
||
Zu jedem Modul gibt es eine Detailseite mit Lernzielen, Glossar-Begriffen
|
||
und Lehrplan-Bezug für Österreich, Deutschland, Schweiz und Liechtenstein.
|
||
Zum Starten brauchst du einen Klassen-Zugang von deiner Lehrperson —
|
||
<a href="<?= $bp ?>/login.html" style="color:#fff;text-decoration:underline;font-weight:700">jetzt anmelden</a>
|
||
oder das Lehrer-Cockpit ausprobieren.
|
||
</p>
|
||
<div class="sp-hero-stats">
|
||
<div class="sp-hero-stat"><span class="sp-hero-stat-num"><?= count($modules) ?></span><span class="sp-hero-stat-lbl">Module</span></div>
|
||
<div class="sp-hero-stat"><span class="sp-hero-stat-num"><?= $counts['aktiv'] ?></span><span class="sp-hero-stat-lbl">Aktiv</span></div>
|
||
<div class="sp-hero-stat"><span class="sp-hero-stat-num"><?= $counts['geplant'] ?></span><span class="sp-hero-stat-lbl">In Entwicklung</span></div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="sp-filters" id="sp-filters">
|
||
<div class="sp-filter-row">
|
||
<span class="sp-filter-label">Status</span>
|
||
<button type="button" class="sp-chip<?= !$qStatus ? ' active' : '' ?>" data-filter="status" data-value="">
|
||
Alle <span class="sp-chip-badge"><?= count($modules) ?></span>
|
||
</button>
|
||
<?php foreach (['aktiv','beta','geplant'] as $s):
|
||
if (!($counts[$s] ?? 0)) continue; ?>
|
||
<button type="button" class="sp-chip<?= $qStatus === $s ? ' active' : '' ?>"
|
||
data-filter="status" data-value="<?= esc($s) ?>">
|
||
<?= esc($statusMeta[$s]['label']) ?>
|
||
<span class="sp-chip-badge"><?= (int)$counts[$s] ?></span>
|
||
</button>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
|
||
<div class="sp-filter-row">
|
||
<span class="sp-filter-label">Thema</span>
|
||
<button type="button" class="sp-chip<?= !$qTheme ? ' active' : '' ?>" data-filter="theme" data-value="">
|
||
Alle
|
||
</button>
|
||
<?php foreach ($themes as $t): ?>
|
||
<button type="button" class="sp-chip<?= $qTheme === $t['key_slug'] ? ' active' : '' ?>"
|
||
data-filter="theme" data-value="<?= esc($t['key_slug']) ?>">
|
||
<?= esc($t['title']) ?>
|
||
</button>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sp-wrap">
|
||
<div class="sp-results-info" id="sp-results-info"></div>
|
||
|
||
<div class="sp-grid" id="sp-grid">
|
||
<?php foreach ($modules as $m):
|
||
$shortText = EasyLang::pick($m['short_desc'] ?? null, $m['short_desc_easy'] ?? null);
|
||
$displayText = $shortText ?: ($m['subtitle'] ?? '');
|
||
$status = $m['status'];
|
||
$statusInfo = $statusMeta[$status] ?? $statusMeta['beta'];
|
||
$themesForModule = $kompMap[$m['module_id']] ?? [];
|
||
$themeSlugs = array_map(fn($r) => $r['key_slug'], $themesForModule);
|
||
$themeAttr = implode(',', $themeSlugs);
|
||
$canPlay = in_array($status, ['aktiv','beta'], true);
|
||
$isPending = !empty($m['available_from']) && $m['available_from'] > date('Y-m-d');
|
||
$pendingDate = '';
|
||
if ($isPending) {
|
||
$ts = strtotime($m['available_from']);
|
||
$pendingDate = date('d.m.', $ts);
|
||
}
|
||
?>
|
||
<article class="sp-card sp-card-<?= esc($status) ?>"
|
||
data-status="<?= esc($status) ?>"
|
||
data-themes="<?= esc($themeAttr) ?>">
|
||
<div class="sp-card-media">
|
||
<?php if (!empty($m['card_image'])): ?>
|
||
<img src="<?= $bp ?>/assets/img/<?= esc($m['card_image']) ?>" alt="<?= esc($m['title']) ?>">
|
||
<?php endif; ?>
|
||
<span class="sp-card-badge <?= esc($statusInfo['tone']) ?>">
|
||
<?= esc($statusInfo['icon']) ?> <?= esc($statusInfo['label']) ?>
|
||
</span>
|
||
</div>
|
||
<div class="sp-card-body">
|
||
<h2 class="sp-card-title">
|
||
<span class="sp-card-icon"><?= esc($m['icon']) ?></span>
|
||
<?= esc($m['title']) ?>
|
||
</h2>
|
||
<p class="sp-card-subtitle"><?= esc($displayText) ?></p>
|
||
|
||
<div class="sp-card-meta">
|
||
<?php if (!empty($m['duration_min'])): ?>
|
||
<span class="sp-card-meta-item">⏱ ca. <?= (int)$m['duration_min'] ?> Min</span>
|
||
<?php endif; ?>
|
||
<?php if (!empty($m['age_min']) && !empty($m['age_max'])): ?>
|
||
<span class="sp-card-meta-item">👧 <?= (int)$m['age_min'] ?>–<?= (int)$m['age_max'] ?> J</span>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<?php if (!empty($themesForModule)): ?>
|
||
<div class="sp-card-themes">
|
||
<?php foreach ($themesForModule as $t): ?>
|
||
<span class="sp-card-theme" title="Kompetenzbereich"><?= esc($t['title']) ?></span>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="sp-card-cta">
|
||
<?php if ($isPending): ?>
|
||
<span class="sp-pill-pending">🗓 ab <?= esc($pendingDate) ?> verfügbar!</span>
|
||
<?php else: ?>
|
||
<?php if (!empty($m['page_url'])): ?>
|
||
<a class="sp-btn sp-btn-secondary" href="<?= $bp ?>/<?= esc($m['page_url']) ?>">
|
||
📖 Mehr erfahren
|
||
</a>
|
||
<?php endif; ?>
|
||
<?php if ($canPlay): ?>
|
||
<a class="sp-btn sp-btn-primary" href="<?= $bp ?>/login.html"
|
||
title="Anmelden, um diese Simulation mit einer Lehrperson zu nutzen">
|
||
🔑 Mit Lehrperson nutzen
|
||
</a>
|
||
<?php else: ?>
|
||
<span class="sp-btn sp-btn-disabled" aria-disabled="true">
|
||
🔒 In Entwicklung
|
||
</span>
|
||
<?php endif; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
</article>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<footer class="sp-footer">
|
||
<img src="<?= $bp ?>/assets/img/bildLogo.png" alt="">
|
||
<div>
|
||
Lehrplan geograsim.at ·
|
||
<a href="<?= $bp ?>/pages/lehrplan.php">Lehrplan</a> ·
|
||
<a href="<?= $bp ?>/glossar">Glossar</a> ·
|
||
<a href="<?= $bp ?>/ueber.html">Über</a> ·
|
||
<a href="<?= $bp ?>/impressum.html">Impressum</a> ·
|
||
<a href="<?= $bp ?>/datenschutz.html">Datenschutz</a>
|
||
</div>
|
||
<?php include __DIR__ . '/_partials/country_footer.php'; ?>
|
||
</footer>
|
||
|
||
<script>
|
||
(function () {
|
||
const bar = document.getElementById('sp-filters');
|
||
const info = document.getElementById('sp-results-info');
|
||
const cards = document.querySelectorAll('.sp-card');
|
||
const total = cards.length;
|
||
|
||
const state = {
|
||
status: <?= json_encode($qStatus) ?>,
|
||
theme: <?= json_encode($qTheme) ?>,
|
||
};
|
||
|
||
bar.addEventListener('click', (e) => {
|
||
const btn = e.target.closest('.sp-chip');
|
||
if (!btn) return;
|
||
const f = btn.dataset.filter;
|
||
const v = btn.dataset.value;
|
||
state[f] = v;
|
||
bar.querySelectorAll(`.sp-chip[data-filter="${f}"]`).forEach(c => {
|
||
c.classList.toggle('active', c.dataset.value === v);
|
||
});
|
||
apply();
|
||
const params = new URLSearchParams();
|
||
if (state.status) params.set('status', state.status);
|
||
if (state.theme) params.set('theme', state.theme);
|
||
const q = params.toString();
|
||
history.replaceState(null, '', q ? '?' + q : location.pathname);
|
||
});
|
||
|
||
function apply() {
|
||
let shown = 0;
|
||
cards.forEach(c => {
|
||
let hide = false;
|
||
if (state.status && c.dataset.status !== state.status) hide = true;
|
||
if (state.theme) {
|
||
const ts = (c.dataset.themes || '').split(',').filter(Boolean);
|
||
if (!ts.includes(state.theme)) hide = true;
|
||
}
|
||
c.style.display = hide ? 'none' : '';
|
||
if (!hide) shown++;
|
||
});
|
||
if (shown === total && !state.status && !state.theme) {
|
||
info.textContent = '';
|
||
} else {
|
||
info.textContent = `${shown} von ${total} Modulen sichtbar`;
|
||
}
|
||
}
|
||
|
||
apply();
|
||
})();
|
||
</script>
|
||
|
||
</body>
|
||
</html>
|