1bfbd53062
- Footer-Fragment (fragments/layout :: footer) ueberall ausgerollt: 24 duplizierte Inline-Footer ersetzt, 11 bisher footerlose Seiten (Fehler-/Auth-Seiten + 8 Spielseiten) ergaenzt -> Rechtslinks von jeder Seite erreichbar. Support/Feedback von href="#" auf mailto-Platzhalter umgestellt. - Datenschutzerklaerung vervollstaendigt: NeuralNet-Messaging als Verarbeitungs- taetigkeit (Inhalte, Sender/Empfaenger, Lesebestaetigung, Loeschung via Kaskade), E-Mail-Token (SHA-256, 24h) + SMTP ueber mail.sysnaptik.de, konkrete Speicher- fristen, korrekte Drittland-/CDN-Aussage. - Impressum strukturiert: Pflichtfelder nach § 5 DDG (Name, Anschrift, Vertretung, Kontakt, MStV) als auffaellige Platzhalter (--warn). - Datenexport (Art. 20): GET /profile/export -> ProfileExportService sammelt Stammdaten, Scores, Achievements und NeuralNet-Daten (ohne Passwort) als JSON-Download; drei Repo-Query-Methoden ergaenzt. - V22-Migration: Demo-User NeuralTest entfernt (NeuralNet-Daten per Kaskade). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WFxcjtc9HNP4VECT6Ur1tE
133 lines
6.1 KiB
HTML
133 lines
6.1 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="de" data-theme="dark"
|
||
xmlns:th="http://www.thymeleaf.org"
|
||
xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity6">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title th:text="${category.title} + ' – TechTacular'">TechTacular-Kategorie</title>
|
||
<link rel="stylesheet" href="/css/theme.css?v=4">
|
||
<script src="/js/theme.js?v=5"></script>
|
||
<style>
|
||
.ttcat { max-width: 1000px; margin: 0 auto; padding: 0 1rem; width: 100%; }
|
||
.ttcat .q {
|
||
background: var(--surface);
|
||
border: 1px solid var(--border);
|
||
border-radius: 12px; padding: 0.9rem 1.1rem; margin-bottom: 0.7rem;
|
||
box-shadow: var(--g-soft);
|
||
}
|
||
.ttcat .q-head { display: flex; align-items: center; gap: 0.6rem; justify-content: space-between; }
|
||
.ttcat .points {
|
||
background: var(--glow-w); color: var(--accent); font-weight: 800;
|
||
border: 1px solid var(--border-accent);
|
||
border-radius: 7px; padding: 0.14rem 0.5rem; font-size: 0.85rem;
|
||
}
|
||
.ttcat .q-text { color: var(--heading); font-weight: 600; margin: 0.2rem 0 0.5rem; }
|
||
.ttcat ul.ans { list-style: none; padding: 0; margin: 0; }
|
||
.ttcat ul.ans li { color: var(--text); padding: 0.15rem 0; }
|
||
.ttcat ul.ans li.correct { color: var(--accent); font-weight: 700; }
|
||
.ttcat ul.ans li.correct::before { content: "✓ "; }
|
||
.ttcat .expl { color: var(--muted); font-size: 0.85rem; margin-top: 0.4rem; }
|
||
.ttcat .addbox { margin-top: 1.6rem; }
|
||
.ttcat .field { margin-bottom: 0.7rem; display: flex; flex-direction: column; gap: 0.35rem; }
|
||
.ttcat .field > label { color: var(--accent); font-weight: 700; font-size: 0.85rem; }
|
||
.ttcat .ans-row { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.5rem; }
|
||
.ttcat .ans-row input[type="text"] { flex: 1; }
|
||
.ttcat .ans-row label { color: var(--accent); font-size: 0.82rem; white-space: nowrap; display: flex; align-items: center; gap: 0.3rem; }
|
||
.ttcat .hint { color: var(--muted); font-size: 0.82rem; margin-bottom: 0.8rem; line-height: 1.5; }
|
||
.ttcat .hint strong { color: var(--text); }
|
||
.ttcat .empty { color: var(--danger); }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="mf-theme-float"><button class="mf-theme"></button></div>
|
||
|
||
<div class="mf-container" style="max-width:1040px">
|
||
|
||
<div class="mf-page-head">
|
||
<a th:href="@{/admin/techtacular}" class="mf-back">⟵ Zurück zur Kategorienliste</a>
|
||
</div>
|
||
|
||
<header style="margin-bottom:1.4rem">
|
||
<div class="mf-eyebrow">TechTacular · Kategorie</div>
|
||
<h1 style="color:var(--heading); font-size:1.75rem; font-weight:800; margin-top:.35rem" th:text="'🎯 ' + ${category.title}">Kategorie</h1>
|
||
</header>
|
||
|
||
<main class="ttcat">
|
||
|
||
<!-- Vorhandene Fragen -->
|
||
<div class="q" th:each="q : ${category.questions}">
|
||
<div class="q-head">
|
||
<span class="points" th:text="${q.points} + ' Pkt'">100 Pkt</span>
|
||
<form th:action="@{'/admin/techtacular/questions/' + ${q.id} + '/delete'}" method="post"
|
||
onsubmit="return confirm('Frage löschen?');">
|
||
<input type="hidden" name="categoryId" th:value="${category.id}">
|
||
<button type="submit" class="mf-btn danger">✕ Frage löschen</button>
|
||
</form>
|
||
</div>
|
||
<p class="q-text" th:text="${q.text}">Fragetext</p>
|
||
<ul class="ans">
|
||
<li th:each="a : ${q.answers}" th:text="${a.text}" th:classappend="${a.correct} ? 'correct' : ''">Antwort</li>
|
||
</ul>
|
||
<p class="expl" th:if="${q.explanation}" th:text="'Erklärung: ' + ${q.explanation}">Erklärung</p>
|
||
</div>
|
||
|
||
<p th:if="${category.questions.isEmpty()}" class="empty">Noch keine Fragen – unten hinzufügen.</p>
|
||
|
||
<!-- Frage hinzufügen -->
|
||
<div class="mf-panel accent addbox">
|
||
<h4>Hinweis hinzufügen</h4>
|
||
<p class="hint">Jeopardy umgekehrt: Der <strong>Hinweis</strong> ist eine Aussage. Trage die gesuchten
|
||
<strong>Begriffe</strong> ein und markiere die <strong>Lösung</strong> mit „richtig“. Im Spiel
|
||
erscheinen die Begriffe als „Was ist …?“. Mindestens zwei Begriffe.</p>
|
||
<form th:action="@{'/admin/techtacular/categories/' + ${category.id} + '/questions'}" method="post">
|
||
<div class="field">
|
||
<label>Hinweis (Aussage)</label>
|
||
<input class="mf-input" type="text" name="text" placeholder="z. B. Dieses Protokoll übersetzt Domainnamen in IP-Adressen." required>
|
||
</div>
|
||
<div class="field">
|
||
<label>Erklärung (optional)</label>
|
||
<input class="mf-input" type="text" name="explanation" placeholder="kurze Begründung">
|
||
</div>
|
||
<div class="field">
|
||
<label>Punkte (Schwierigkeit)</label>
|
||
<select class="mf-select" name="points">
|
||
<option value="100">100</option>
|
||
<option value="200">200</option>
|
||
<option value="300">300</option>
|
||
<option value="400">400</option>
|
||
<option value="500">500</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="ans-row">
|
||
<label><input type="radio" name="correct" value="0" checked> Lösung</label>
|
||
<input class="mf-input" type="text" name="answer1" placeholder="Begriff 1 (z. B. DNS)" required>
|
||
</div>
|
||
<div class="ans-row">
|
||
<label><input type="radio" name="correct" value="1"> Lösung</label>
|
||
<input class="mf-input" type="text" name="answer2" placeholder="Begriff 2" required>
|
||
</div>
|
||
<div class="ans-row">
|
||
<label><input type="radio" name="correct" value="2"> Lösung</label>
|
||
<input class="mf-input" type="text" name="answer3" placeholder="Begriff 3 (optional)">
|
||
</div>
|
||
<div class="ans-row">
|
||
<label><input type="radio" name="correct" value="3"> Lösung</label>
|
||
<input class="mf-input" type="text" name="answer4" placeholder="Begriff 4 (optional)">
|
||
</div>
|
||
|
||
<button type="submit" class="mf-btn" style="margin-top:.4rem">➕ Hinweis speichern</button>
|
||
</form>
|
||
</div>
|
||
|
||
</main>
|
||
|
||
</div>
|
||
|
||
<footer th:replace="~{fragments/layout :: footer}"></footer>
|
||
|
||
</body>
|
||
</html>
|