Files
mindflux/src/main/resources/templates/admin/syntaxsaga-mission.html
T
AliceDarkest 4599dd12b8 Syntax Saga: neues Spiel (server-autoritativ) + 34 Seed-Missionen
Drittes Mindflux-Spiel: interaktive IT-Geschichten, in denen man den
fehlenden Code (Schlüsselwörter, Operatoren, ganze Zeilen) in eine kleine
Story tippt. Freitext-Eingabe, drei wählbare Stufen (Junior/Professional/
Senior), mehrere Lücken je Codeblock und mehrere gültige Antworten je Lücke
(optional case-insensitive). Prüfung und Score ausschließlich serverseitig –
die Lösungen verlassen nie das Backend (Anti-Cheat wie TechTacular/MindMatch).
Fehler-Mechanik: bis zu 3 Versuche mit Hinweis, dann Auflösung; kein Game-Over.

Inhalte DB-gestützt und im Admin pflegbar (/admin/syntaxsaga); 34 Seed-
Missionen (Java/SQL/Bash/Git/HTML/Python) aus syntaxsaga/content.json.

- Entities SyntaxSagaMission/Gap/Solution + Flyway V7__syntax_saga.sql
- SyntaxSagaService (Story ohne Lösung / serverseitige Prüfung / Score-Recompute)
- API /api/syntaxsaga (CSRF-ausgenommen) + Admin-CRUD
- Frontend syntaxsaga.html/js/css (Prefix ss-, theme.css-Tokens)
- Dashboard-Kachel aktiv (vorher "coming soon")

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 12:13:37 +02:00

178 lines
8.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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="${mission.title} + ' – Syntax Saga'">Syntax-Saga-Mission</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.ssm { max-width: 1000px; margin: 0 auto; padding: 0 1rem; width: 100%; }
.ssm .grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0.7rem; }
.ssm .field { margin-bottom: 0.7rem; display: flex; flex-direction: column; gap: 0.35rem; }
.ssm .field > label { color: var(--accent); font-weight: 700; font-size: 0.85rem; }
.ssm textarea.mf-input { font-family: monospace; resize: vertical; min-height: 4.5rem; }
.ssm textarea.prose { font-family: inherit; }
.ssm .gap {
background: var(--surface);
border: 1px solid var(--border);
border-left: 4px solid var(--secondary);
border-radius: 12px; padding: 0.9rem 1.1rem; margin-bottom: 0.7rem;
box-shadow: var(--g-soft);
}
.ssm .gap-head { display: flex; align-items: center; gap: 0.6rem; justify-content: space-between; flex-wrap: wrap; }
.ssm .pos { background: var(--glow-w); color: var(--accent); font-weight: 800; border: 1px solid var(--border-accent); border-radius: 7px; padding: 0.14rem 0.55rem; font-size: 0.85rem; }
.ssm .meta { color: var(--muted); font-size: 0.82rem; }
.ssm ul.sols { list-style: none; padding: 0; margin: 0.4rem 0 0; }
.ssm ul.sols li { color: var(--secondary); font-family: monospace; padding: 0.12rem 0; }
.ssm ul.sols li::before { content: "✓ "; }
.ssm .gap-hint { color: var(--muted); font-size: 0.85rem; margin-top: 0.35rem; }
.ssm .addbox { margin-top: 1.6rem; }
.ssm .hint { color: var(--muted); font-size: 0.82rem; margin-bottom: 0.8rem; line-height: 1.5; }
.ssm .hint code { color: var(--accent); }
.ssm .empty { color: var(--danger); }
.ssm .check-row { display: flex; align-items: center; gap: 0.45rem; color: var(--accent); font-size: 0.85rem; }
@media (max-width: 600px) { .ssm .grid2 { grid-template-columns: 1fr; } }
</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/syntaxsaga}" class="mf-back">⟵ Zurück zur Missionsliste</a>
</div>
<header style="margin-bottom:1.4rem">
<div class="mf-eyebrow">Syntax Saga · Mission</div>
<h1 style="color:var(--heading); font-size:1.75rem; font-weight:800; margin-top:.35rem" th:text="'📜 ' + ${mission.title}">Mission</h1>
</header>
<main class="ssm">
<!-- Mission bearbeiten -->
<div class="mf-panel">
<h4>Mission bearbeiten</h4>
<form th:action="@{'/admin/syntaxsaga/missions/' + ${mission.id} + '/update'}" method="post">
<div class="grid2">
<div class="field">
<label>Titel</label>
<input class="mf-input" type="text" name="title" th:value="${mission.title}" required>
</div>
<div class="field">
<label>Schwierigkeit</label>
<select class="mf-select" name="difficulty">
<option value="junior" th:selected="${mission.difficulty == 'junior'}">Junior</option>
<option value="professional" th:selected="${mission.difficulty == 'professional'}">Professional</option>
<option value="senior" th:selected="${mission.difficulty == 'senior'}">Senior</option>
</select>
</div>
</div>
<div class="field">
<label>Erzähltext / Szene</label>
<textarea class="mf-input prose" name="narrative" th:text="${mission.narrative}"></textarea>
</div>
<div class="grid2">
<div class="field">
<label>Dialog (optional)</label>
<input class="mf-input" type="text" name="dialog" th:value="${mission.dialog}">
</div>
<div class="field">
<label>Sprache (optional)</label>
<input class="mf-input" type="text" name="language" th:value="${mission.language}">
</div>
</div>
<div class="field">
<label>Code mit Lücken (<code>{{1}}</code>, <code>{{2}}</code> …)</label>
<textarea class="mf-input" name="codeTemplate" rows="6" th:text="${mission.codeTemplate}" required></textarea>
</div>
<div class="field">
<label>Erfolgstext (optional)</label>
<textarea class="mf-input prose" name="successText" th:text="${mission.successText}"></textarea>
</div>
<button type="submit" class="mf-btn">💾 Mission speichern</button>
</form>
</div>
<!-- Vorhandene Lücken -->
<h4 style="color:var(--accent); margin:1.4rem 0 0.7rem; text-transform:uppercase; letter-spacing:.08em; font-size:.8rem">Lücken</h4>
<div class="gap" th:each="g : ${mission.gaps}">
<div class="gap-head">
<span class="pos" th:text="'Lücke {{' + ${g.position} + '}}'">Lücke {{1}}</span>
<span class="meta">
<span th:text="${g.points} + ' Pkt'">100 Pkt</span> ·
<span th:text="${g.caseSensitive} ? 'Groß/Klein beachten' : 'Groß/Klein egal'">Groß/Klein</span>
</span>
<form th:action="@{'/admin/syntaxsaga/gaps/' + ${g.id} + '/delete'}" method="post"
onsubmit="return confirm('Lücke löschen?');">
<input type="hidden" name="missionId" th:value="${mission.id}">
<button type="submit" class="mf-btn danger">✕ Lücke löschen</button>
</form>
</div>
<ul class="sols">
<li th:each="s : ${g.solutions}" th:text="${s.text}">Lösung</li>
</ul>
<p class="gap-hint" th:if="${g.hint}" th:text="'Hinweis: ' + ${g.hint}">Hinweis</p>
</div>
<p th:if="${mission.gaps.isEmpty()}" class="empty">Noch keine Lücken – unten hinzufügen. Ohne Lücken erscheint die Mission nicht im Spiel.</p>
<!-- Lücke hinzufügen -->
<div class="mf-panel accent addbox">
<h4>Lücke hinzufügen</h4>
<p class="hint">Die <strong>Position</strong> muss zum Marker <code>{{n}}</code> im Code passen.
Trage die gültigen <strong>Lösungen</strong> ein – eine pro Zeile (mehrere Zeilen = mehrere akzeptierte Antworten,
z. B. <code>192.168.0.1</code> und <code>localhost</code>).</p>
<form th:action="@{'/admin/syntaxsaga/missions/' + ${mission.id} + '/gaps'}" method="post">
<div class="grid2">
<div class="field">
<label>Position ({{n}})</label>
<input class="mf-input" type="number" name="position" min="1" placeholder="1" required>
</div>
<div class="field">
<label>Punkte</label>
<input class="mf-input" type="number" name="points" min="10" max="1000" value="100">
</div>
</div>
<div class="grid2">
<div class="field">
<label>Platzhalter (optional)</label>
<input class="mf-input" type="text" name="placeholder" placeholder="z. B. Methodenname">
</div>
<div class="field">
<label>Hinweis (optional)</label>
<input class="mf-input" type="text" name="hint" placeholder="kurzer Tipp nach falschem Versuch">
</div>
</div>
<div class="field">
<label>Gültige Lösungen (eine pro Zeile)</label>
<textarea class="mf-input" name="solutions" rows="3" placeholder="main" required></textarea>
</div>
<label class="check-row" style="margin-bottom:.7rem">
<input type="checkbox" name="caseSensitive" value="true" checked> Groß-/Kleinschreibung beachten (für Java-Bezeichner; bei SQL-Keywords abwählen)
</label>
<button type="submit" class="mf-btn">➕ Lücke speichern</button>
</form>
</div>
</main>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="/nutzungsbedingungen">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body>
</html>