Volle Mindflux-App ins Deploy-Repo übernehmen

Ersetzt das Hello-World-Gerüst durch die vollständige Anwendung
(com.example.mindflux: 64 Klassen, Thymeleaf-Templates, Static-Assets,
Flyway V1–V6). Deploy-Infrastruktur (.gitea, Dockerfile) bleibt erhalten.

- pom: volle Abhängigkeiten (Security, Thymeleaf, JPA, Flyway), Spring Boot 3.4.4 / Java 17
- docker-compose: SPRING_DATASOURCE_* unverändert; App lauscht auf 8080
  (nginx -> mindflux:8080); Uploads-Volume; Admin-Passwörter via Gitea-Secrets
- Workflow: Deploy-Step injiziert MINDFLUX_ADMIN*_PASSWORD aus Secrets
- .dockerignore ergänzt; Hello-World (com.mindflux) und Test-Datei 'main' entfernt

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
AliceDarkest
2026-06-26 14:51:30 +02:00
parent f3309c1574
commit 9022961762
185 changed files with 22772 additions and 142 deletions
@@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mindflux – Trophäen</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.mf-ach-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(230px,1fr)); gap:1rem; }
.achievement-card { background:var(--surface-2); border:1px solid var(--border); border-radius:13px; padding:1.3rem 1.1rem; text-align:center; transition:transform .16s ease, box-shadow .25s ease; }
.achievement-card.unlocked { border-color:var(--border-accent); box-shadow:0 0 18px var(--glow-w); }
.achievement-card.unlocked:hover { transform:translateY(-4px); box-shadow:0 0 24px var(--glow-s); }
.achievement-card.locked { opacity:.55; }
.achievement-card .trophy-icon { font-size:2.4rem; margin-bottom:.6rem; filter:drop-shadow(0 0 8px var(--glow-s)); }
.achievement-card.locked .trophy-icon { filter:grayscale(1); }
.achievement-card h2 { color:var(--heading); font-size:1.05rem; margin-bottom:.5rem; }
.achievement-card p { color:var(--muted); font-size:.84rem; line-height:1.45; }
.achievement-card .status { margin-top:.7rem; font-weight:700; color:var(--accent); }
.achievement-card.locked .status { color:var(--muted); }
.mf-footer { text-align:center; color:var(--muted); font-size:.82rem; padding:1.6rem 1rem; }
.mf-footer a { color:var(--muted); text-decoration:none; margin:0 .15rem; }
.mf-footer a:hover { color:var(--accent); }
</style>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-container">
<div class="mf-page-head">
<a href="/profile" class="mf-back">⟵ Zurück zum Profil</a>
</div>
<span class="mf-eyebrow">Deine Trophäen</span>
<h1 style="color:var(--heading); font-size:1.9rem; font-weight:800; margin:.3rem 0 .25rem;">🏆 Deine Trophäen</h1>
<p style="color:var(--muted); margin-bottom:1.4rem;">Sammle sie alle und werde zur Legende!</p>
<div class="mf-panel">
<div class="mf-ach-grid">
<div class="achievement-card"
th:each="achievement : ${achievements}"
th:classappend="${achievement.dateAchieved != null} ? ' unlocked' : ' locked'">
<div class="trophy-icon">🏆</div>
<h2 th:text="${achievement.title}">Titel des Erfolgs</h2>
<p th:text="${achievement.description}">Beschreibung des Erfolgs</p>
<p class="status"
th:text="${achievement.dateAchieved != null} ? '✔️ Freigeschaltet' : '🔒 Gesperrt'">
</p>
</div>
</div>
</div>
</div>
<!-- Footer -->
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
@@ -0,0 +1,83 @@
<!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>Mindflux Admin Dashboard</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.admin-actions { display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:1rem; margin-top:1.1rem; }
.admin-card {
display:flex; align-items:center; gap:.6rem;
background:var(--surface-2); border:1px solid var(--border-accent); border-radius:13px;
padding:1.1rem 1.2rem; color:var(--heading); text-decoration:none; font-weight:700;
box-shadow:var(--g-soft); transition:transform .16s ease, box-shadow .25s ease, background-color .2s;
}
.admin-card:hover { transform:translateY(-3px); background:var(--surface); box-shadow:0 0 18px var(--glow-s); }
.admin-footer { margin-top:2rem; text-align:center; color:var(--muted); font-size:.85rem; }
.admin-footer a { color:var(--accent); text-decoration:none; }
.admin-footer a:hover { text-decoration:underline; }
</style>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-container">
<div class="mf-page-head">
<a th:href="@{/dashboard}" class="mf-back">⟵ Zurück zum Benutzer-Dashboard</a>
</div>
<header style="margin-bottom:1.4rem">
<div class="mf-eyebrow">Control Center</div>
<h1 style="color:var(--heading); font-size:1.85rem; font-weight:800; margin-top:.35rem">✨ Admin-Dashboard ✨</h1>
<p style="color:var(--muted); margin-top:.4rem">Hier steuerst du das System wie Doc den DeLorean!</p>
</header>
<main>
<div class="mf-panel accent">
<h4>Admin Aktionen</h4>
<div class="admin-actions">
<a th:href="@{/admin/users}" class="admin-card">👤 Benutzer verwalten</a>
<a th:href="@{/admin/mindmatch}" class="admin-card">🧩 MindMatch-Decks verwalten</a>
<a th:href="@{/admin/techtacular}" class="admin-card">🎯 TechTacular-Fragen verwalten</a>
</div>
<p style="margin-top:1.1rem; color:var(--muted);">
Hier entsteht noch Großes... ✨ Bleib dran!
</p>
</div>
</main>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</div>
<script>
window.addEventListener('DOMContentLoaded', () => {
const successMessage = document.getElementById('successMessage');
if (successMessage) {
setTimeout(() => {
successMessage.style.opacity = '0';
setTimeout(() => {
successMessage.style.display = 'none';
}, 1500);
}, 3000);
}
});
</script>
</body>
</html>
@@ -0,0 +1,135 @@
<!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>MindMatch-Verwaltung – Admin</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.mmadmin { max-width: 1000px; margin: 0 auto; padding: 0 1rem; width: 100%; }
.mmadmin .deck {
background: var(--surface);
border: 1px solid var(--border);
border-left: 4px solid var(--accent);
border-radius: 14px;
padding: 1.2rem 1.4rem;
margin-bottom: 1.4rem;
box-shadow: var(--g-soft);
}
.mmadmin .deck.inactive { border-left-color: var(--danger); opacity: 0.75; }
.mmadmin .deck-head {
display: flex; align-items: center; flex-wrap: wrap; gap: 0.6rem;
justify-content: space-between; margin-bottom: 0.8rem;
}
.mmadmin .deck-head h2 { color: var(--heading); margin: 0; font-size: 1.25rem; }
.mmadmin .slug { color: var(--accent); font-size: 0.8rem; font-family: monospace; }
.mmadmin .badge {
font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em;
padding: 0.18rem 0.5rem; border-radius: 7px; border: 1px solid var(--border-accent);
}
.mmadmin .badge.on { background: var(--glow-w); color: var(--accent); }
.mmadmin .badge.off { background: rgba(255,95,107,.12); color: var(--danger); border-color: var(--danger); }
.mmadmin .pairs { list-style: none; padding: 0; margin: 0 0 0.8rem; }
.mmadmin .pairs li {
display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
padding: 0.4rem 0; border-bottom: 1px solid var(--border);
}
.mmadmin .pairs .term { color: var(--accent); font-weight: 700; min-width: 130px; }
.mmadmin .pairs .arrow { color: var(--muted); }
.mmadmin .pairs .def { flex: 1; min-width: 160px; color: var(--text); }
.mmadmin form.inline { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }
.mmadmin .add-pair { margin-top: 0.6rem; }
.mmadmin .new-deck { margin-bottom: 1.6rem; }
.mmadmin .count { color: var(--muted); font-size: 0.85rem; }
.mmadmin .empty { color: var(--muted); }
.mmadmin .empty.warn { 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}" class="mf-back">⟵ Zurück zum Admin-Dashboard</a>
</div>
<header style="margin-bottom:1.4rem">
<div class="mf-eyebrow">MindMatch</div>
<h1 style="color:var(--heading); font-size:1.75rem; font-weight:800; margin-top:.35rem">🧩 MindMatch-Verwaltung</h1>
</header>
<main class="mmadmin">
<!-- Neues Deck -->
<div class="mf-panel accent new-deck">
<h4>Neue Kategorie (Deck) anlegen</h4>
<form class="inline" th:action="@{/admin/mindmatch/decks}" method="post">
<input class="mf-input" style="width:auto; min-width:240px" type="text" name="title" placeholder="z. B. SQL-Befehle" required>
<button type="submit" class="mf-btn">➕ Deck anlegen</button>
</form>
</div>
<!-- Decks -->
<div class="deck" th:each="deck : ${decks}" th:classappend="${deck.active} ? '' : 'inactive'">
<div class="deck-head">
<div>
<h2 th:text="${deck.title}">Deck</h2>
<span class="slug" th:text="'/' + ${deck.slug}">/slug</span>
<span class="badge on" th:if="${deck.active}">aktiv</span>
<span class="badge off" th:unless="${deck.active}">inaktiv</span>
<span class="count" th:text="'· ' + ${deck.pairs.size()} + ' Paare'">· 0 Paare</span>
</div>
<div style="display:flex; gap:0.5rem;">
<form th:action="@{'/admin/mindmatch/decks/' + ${deck.id} + '/toggle'}" method="post">
<button type="submit" class="mf-btn outline" th:text="${deck.active} ? 'Deaktivieren' : 'Aktivieren'">Toggle</button>
</form>
<form th:action="@{'/admin/mindmatch/decks/' + ${deck.id} + '/delete'}" method="post"
onsubmit="return confirm('Deck inkl. aller Paare löschen?');">
<button type="submit" class="mf-btn danger">Deck löschen</button>
</form>
</div>
</div>
<ul class="pairs">
<li th:each="pair : ${deck.pairs}">
<span class="term" th:text="${pair.term}">Begriff</span>
<span class="arrow">↔</span>
<span class="def" th:text="${pair.matchText}">Bedeutung</span>
<form th:action="@{'/admin/mindmatch/pairs/' + ${pair.id} + '/delete'}" method="post">
<button type="submit" class="mf-btn danger" style="padding:.35rem .7rem">✕</button>
</form>
</li>
<li th:if="${deck.pairs.isEmpty()}" class="empty warn">Noch keine Paare – unten hinzufügen.</li>
</ul>
<!-- Paar hinzufügen -->
<form class="inline add-pair" th:action="@{'/admin/mindmatch/decks/' + ${deck.id} + '/pairs'}" method="post">
<input class="mf-input" style="width:auto; min-width:200px" type="text" name="term" placeholder="Begriff (z. B. SELECT)" required>
<span class="arrow">↔</span>
<input class="mf-input" style="width:auto; min-width:200px" type="text" name="match" placeholder="Bedeutung (z. B. Daten abfragen)" required>
<button type="submit" class="mf-btn outline">➕ Paar hinzufügen</button>
</form>
</div>
<p th:if="${decks.isEmpty()}" class="empty">Noch keine Decks vorhanden. Lege oben das erste an.</p>
</main>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
@@ -0,0 +1,139 @@
<!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">
<script src="/js/theme.js"></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 class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
@@ -0,0 +1,96 @@
<!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>TechTacular-Verwaltung – Admin</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.ttadmin { max-width: 1000px; margin: 0 auto; padding: 0 1rem; width: 100%; }
.ttadmin .new-cat { margin-bottom: 1.6rem; }
.ttadmin .cat {
display: flex; align-items: center; flex-wrap: wrap; gap: 0.6rem;
justify-content: space-between;
background: var(--surface);
border: 1px solid var(--border);
border-left: 4px solid var(--accent);
border-radius: 12px; padding: 0.9rem 1.1rem; margin-bottom: 0.7rem;
box-shadow: var(--g-soft);
}
.ttadmin .cat.inactive { border-left-color: var(--danger); opacity: 0.7; }
.ttadmin .cat h2 { color: var(--heading); margin: 0; font-size: 1.1rem; }
.ttadmin .slug { color: var(--accent); font-size: 0.78rem; font-family: monospace; }
.ttadmin .count { color: var(--muted); font-size: 0.85rem; }
.ttadmin .badge { font-size: 0.68rem; text-transform: uppercase; padding: 0.14rem 0.45rem; border-radius: 7px; border: 1px solid var(--border-accent); }
.ttadmin .badge.on { background: var(--glow-w); color: var(--accent); }
.ttadmin .badge.off { background: rgba(255,95,107,.12); color: var(--danger); border-color: var(--danger); }
.ttadmin .actions { display: flex; gap: 0.45rem; flex-wrap: wrap; align-items: center; }
.ttadmin form.inline { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }
.ttadmin .empty { color: var(--muted); }
</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}" class="mf-back">⟵ Zurück zum Admin-Dashboard</a>
</div>
<header style="margin-bottom:1.4rem">
<div class="mf-eyebrow">TechTacular</div>
<h1 style="color:var(--heading); font-size:1.75rem; font-weight:800; margin-top:.35rem">🎯 TechTacular-Verwaltung</h1>
</header>
<main class="ttadmin">
<div class="mf-panel accent new-cat">
<h4>Neue Kategorie anlegen</h4>
<form class="inline" th:action="@{/admin/techtacular/categories}" method="post">
<input class="mf-input" style="width:auto; min-width:240px" type="text" name="title" placeholder="z. B. Datenbanken" required>
<button type="submit" class="mf-btn">➕ Kategorie anlegen</button>
</form>
</div>
<div class="cat" th:each="cat : ${categories}" th:classappend="${cat.active} ? '' : 'inactive'">
<div>
<h2 th:text="${cat.title}">Kategorie</h2>
<span class="slug" th:text="'/' + ${cat.slug}">/slug</span>
<span class="badge on" th:if="${cat.active}">aktiv</span>
<span class="badge off" th:unless="${cat.active}">inaktiv</span>
<span class="count" th:text="'· ' + ${cat.questionCount} + ' Fragen'">· 0 Fragen</span>
</div>
<div class="actions">
<a class="mf-btn" th:href="@{'/admin/techtacular/categories/' + ${cat.id}}">Fragen verwalten</a>
<form th:action="@{'/admin/techtacular/categories/' + ${cat.id} + '/toggle'}" method="post">
<button type="submit" class="mf-btn outline" th:text="${cat.active} ? 'Deaktivieren' : 'Aktivieren'">Toggle</button>
</form>
<form th:action="@{'/admin/techtacular/categories/' + ${cat.id} + '/delete'}" method="post"
onsubmit="return confirm('Kategorie inkl. aller Fragen löschen?');">
<button type="submit" class="mf-btn danger">Löschen</button>
</form>
</div>
</div>
<p th:if="${categories.isEmpty()}" class="empty">Noch keine Kategorien. Lege oben die erste an.</p>
</main>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
@@ -0,0 +1,67 @@
<!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>Benutzer bearbeiten</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-container" style="max-width:560px">
<div class="mf-page-head">
<a th:href="@{/admin/users}" class="mf-back">⟵ Zurück zur Benutzerliste</a>
</div>
<header style="margin-bottom:1.4rem">
<div class="mf-eyebrow">Benutzer</div>
<h1 style="color:var(--heading); font-size:1.75rem; font-weight:800; margin-top:.35rem">✏️ Benutzer bearbeiten</h1>
</header>
<main>
<form th:action="@{/admin/users/save}" th:object="${user}" method="post" class="mf-panel">
<input type="hidden" th:field="*{id}" />
<div class="mf-field">
<label class="mf-label">Benutzername</label>
<input class="mf-input" type="text" th:field="*{username}" required/>
</div>
<div class="mf-field">
<label class="mf-label">Passwort</label>
<input class="mf-input" type="password" name="password" th:required="${user.id == null}" autocomplete="new-password"/>
</div>
<div class="mf-field">
<label class="mf-label">E-Mail</label>
<input class="mf-input" type="email" th:field="*{email}" required/>
</div>
<div class="mf-field">
<label class="mf-label">Rolle</label>
<input class="mf-input" type="text" th:field="*{role}" required/>
</div>
<button type="submit" class="mf-btn" style="margin-top:.4rem">Speichern</button>
</form>
</main>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
@@ -0,0 +1,70 @@
<!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>Benutzerverwaltung</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-container">
<div class="mf-page-head">
<a th:href="@{/dashboard}" class="mf-back">⟵ Zurück zum Dashboard</a>
</div>
<header style="display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:1rem; margin-bottom:1.4rem">
<div>
<div class="mf-eyebrow">Benutzer</div>
<h1 style="color:var(--heading); font-size:1.75rem; font-weight:800; margin-top:.35rem">👥 Benutzerverwaltung</h1>
</div>
<a th:href="@{/admin/users/new}" class="mf-btn">➕ Neuen Benutzer anlegen</a>
</header>
<main>
<div class="mf-panel">
<table class="mf-table">
<thead>
<tr>
<th>ID</th>
<th>Benutzername</th>
<th>Email</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
<tr th:each="user : ${users}">
<td th:text="${user.id}">1</td>
<td th:text="${user.username}">Max</td>
<td th:text="${user.email}">max@mindflux.de</td>
<td style="display:flex; gap:.5rem; flex-wrap:wrap;">
<a th:href="@{'/admin/users/edit/' + ${user.id}}" class="mf-btn outline">Bearbeiten</a>
<form th:action="@{'/admin/users/delete/' + ${user.id}}" method="post" style="display:inline;">
<button type="submit" class="mf-btn danger">Löschen</button>
</form>
</td>
</tr>
</tbody>
</table>
</div>
</main>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
+207
View File
@@ -0,0 +1,207 @@
<!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>Mindflux Dashboard</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
/* Dashboard-spezifische Widgets (rechte Spalte) */
.gauge { display:flex; flex-direction:column; align-items:center; gap:.2rem; }
.gauge svg { width:130px; height:130px; }
.gauge .ok { color:var(--accent); font-weight:800; font-size:1rem; letter-spacing:.05em; margin-top:.3rem; text-shadow:0 0 12px var(--glow-s); }
.gauge .op { color:var(--muted); font-size:.72rem; }
.prog-top { display:flex; justify-content:space-between; align-items:center; margin-bottom:.4rem; }
.pill { font-size:.68rem; color:var(--muted); border:1px solid var(--border); border-radius:7px; padding:.2rem .5rem; }
.prog-big { display:flex; align-items:baseline; gap:.5rem; }
.prog-big b { color:var(--heading); font-size:2rem; font-weight:800; } .prog-big b span{ font-size:1rem; color:var(--muted); }
.xpbadge { font-size:.66rem; color:var(--accent); background:var(--surface-2); border:1px solid var(--border-accent); border-radius:6px; padding:.15rem .45rem; margin-left:auto; }
.chart svg { width:100%; height:74px; } .chart .days { display:flex; justify-content:space-between; color:var(--muted); font-size:.6rem; margin-top:.25rem; }
.cat { display:flex; align-items:center; gap:.6rem; margin:.55rem 0; font-size:.78rem; }
.cat svg { width:15px; height:15px; color:var(--accent); flex:0 0 auto; }
.cat .nm { color:var(--text); width:92px; flex:0 0 auto; }
.cat .cbar { flex:1; height:6px; border-radius:99px; background:var(--surface-2); border:1px solid var(--border); overflow:hidden; }
.cat .cbar i { display:block; height:100%; background:linear-gradient(90deg,var(--accent),var(--accent-2)); box-shadow:0 0 8px var(--glow-w); }
.cat .pct { color:var(--muted); width:30px; text-align:right; }
.challenge .ch-row { display:flex; gap:.7rem; }
.challenge .bolt { color:var(--accent); flex:0 0 auto; filter:drop-shadow(0 0 6px var(--glow-s)); }
.challenge h5 { color:var(--accent); font-size:.82rem; letter-spacing:.04em; text-transform:uppercase; margin-bottom:.3rem; }
.challenge p { color:var(--muted); font-size:.78rem; line-height:1.45; }
</style>
</head>
<body>
<div class="mf-shell">
<!-- ===== Sidebar ===== -->
<aside class="mf-sidebar">
<a th:href="@{/dashboard}" class="mf-logo">
<span class="mark"></span>
<span class="txt"><b>MINDFLUX</b><span>KNOWLEDGE. CODE. WIN.</span></span>
</a>
<nav class="mf-nav">
<a th:href="@{/dashboard}" class="active">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></svg>
Dashboard
</a>
<a th:href="@{/admin}" sec:authorize="hasRole('ADMIN')">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2l8 4v6c0 5-3.5 8-8 10-4.5-2-8-5-8-10V6z"/><path d="M9 12l2 2 4-4"/></svg>
Admin-Dashboard
</a>
<a th:href="@{/profile/knowledge}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18"/></svg>
Knowledge Core
</a>
<a th:href="@{/progress}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 20V10M10 20V4M16 20v-8M22 20H2"/></svg>
Statistiken
</a>
<a th:href="@{/profile/achievements}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="9" r="6"/><path d="M9 14l-2 8 5-3 5 3-2-8"/></svg>
Achievements
</a>
<a th:href="@{/profile/settings}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3.2"/><path d="M19 12a7 7 0 0 0-.1-1l2-1.6-2-3.4-2.4 1a7 7 0 0 0-1.7-1L14.5 2h-4l-.3 2.5a7 7 0 0 0-1.7 1l-2.4-1-2 3.4 2 1.6a7 7 0 0 0 0 2l-2 1.6 2 3.4 2.4-1a7 7 0 0 0 1.7 1l.3 2.5h4l.3-2.5a7 7 0 0 0 1.7-1l2.4 1 2-3.4-2-1.6c.1-.3.1-.7.1-1z"/></svg>
Einstellungen
</a>
</nav>
<a th:href="@{/profile}" class="mf-profile" style="text-decoration:none">
<div class="row">
<span class="mf-avatar" th:text="${#authentication.name != null ? #authentication.name.substring(0,1).toUpperCase() : 'U'}">A</span>
<div>
<b sec:authentication="name">Alice Darkest</b>
<span class="role" sec:authorize="hasRole('ADMIN')">Administrator</span>
<span class="role" sec:authorize="!hasRole('ADMIN')">Mitglied</span>
</div>
<svg style="margin-left:auto;color:var(--muted);width:18px;height:18px" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9l6 6 6-6"/></svg>
</div>
</a>
<div class="mf-meta">
<div class="lvl">LEVEL <b>12</b></div>
<div class="xp">3250 / 5000 XP</div>
<div class="mf-bar"><i style="width:65%"></i></div>
</div>
<div class="mf-streak">
<span class="fire">🔥</span><span><b>STREAK</b><small>7 Tage in Folge</small></span>
</div>
<div class="mf-brain">
<svg viewBox="0 0 120 110" fill="none" stroke="var(--accent)" stroke-width="1.4">
<path d="M60 12c-14-8-34-2-36 14-10 4-12 18-4 26-4 10 2 22 14 24 4 8 18 10 26 4 8 6 22 4 26-4 12-2 18-14 14-24 8-8 6-22-4-26-2-16-22-22-36-14z"/>
<path d="M60 16v82M40 30c8 4 8 14 0 18M80 30c-8 4-8 14 0 18M34 60c10 0 14 8 14 16M86 60c-10 0-14 8-14 16M48 44h24M44 76h32"/>
</svg>
</div>
</aside>
<!-- ===== Topbar ===== -->
<header class="mf-topbar">
<div class="mf-hello">
<h1>Willkommen zurück, <span class="nm" sec:authentication="name">Alice</span>! 👋</h1>
<p>Wähle ein Spielmodus, um dein Wissen auf die Probe zu stellen.</p>
</div>
<div class="mf-pulse">
<svg viewBox="0 0 200 50" fill="none" stroke="var(--accent)" stroke-width="2"><path d="M2 26h40l8-18 10 34 9-26 7 14h26l6-12 9 22 7-24h59"/></svg>
</div>
<div class="mf-tools">
<button class="mf-iconbtn" aria-label="Benachrichtigungen"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg></button>
<div class="mf-search"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="M21 21l-4-4"/></svg><input placeholder="Suche..."></div>
<button class="mf-theme"></button>
<a class="mf-iconbtn" href="/logout" aria-label="Logout" style="color:var(--danger)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4M16 17l5-5-5-5M21 12H9"/></svg></a>
</div>
</header>
<!-- ===== Spielkarten ===== -->
<main class="mf-main"><div class="mf-grid" id="grid"></div></main>
<!-- ===== Rechte Spalte ===== -->
<aside class="mf-rightbar">
<div class="mf-panel">
<h4><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12h4l2-7 4 14 2-7h6"/></svg> System Status</h4>
<div class="gauge">
<svg viewBox="0 0 120 120" fill="none">
<circle cx="60" cy="60" r="50" stroke="var(--line)" stroke-width="6"/>
<circle cx="60" cy="60" r="50" stroke="var(--accent)" stroke-width="6" stroke-linecap="round" stroke-dasharray="300 314" transform="rotate(-90 60 60)" style="filter:drop-shadow(0 0 6px var(--glow-s))"/>
<path d="M60 38l16 6v12c0 11-8 17-16 20-8-3-16-9-16-20V44z" fill="none" stroke="var(--accent)" stroke-width="3"/>
<path d="M52 58l6 6 12-12" stroke="var(--accent)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<div class="ok">ALLE SYSTEME STABIL</div><div class="op">100% OPERATIONAL</div>
</div>
</div>
<div class="mf-panel">
<div class="prog-top"><h4 style="margin:0"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 17l6-6 4 4 8-8"/></svg> Dein Fortschritt</h4><span class="pill">Woche ▾</span></div>
<div class="op" style="color:var(--muted);font-size:.72rem;margin:.2rem 0">Gesamtfortschritt</div>
<div class="prog-big"><b>65<span>%</span></b><span class="xpbadge">+120 XP</span></div>
<div class="chart">
<svg viewBox="0 0 240 74" preserveAspectRatio="none">
<defs><linearGradient id="ar" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="var(--accent)" stop-opacity=".35"/><stop offset="1" stop-color="var(--accent)" stop-opacity="0"/></linearGradient></defs>
<path d="M0 60 L34 50 L70 56 L104 38 L140 44 L176 24 L210 30 L240 14 L240 74 L0 74 Z" fill="url(#ar)"/>
<path d="M0 60 L34 50 L70 56 L104 38 L140 44 L176 24 L210 30 L240 14" fill="none" stroke="var(--accent)" stroke-width="2" style="filter:drop-shadow(0 0 5px var(--glow-s))"/>
<circle cx="176" cy="24" r="3.5" fill="var(--accent)"/>
</svg>
<div class="days"><span>Mo</span><span>Di</span><span>Mi</span><span>Do</span><span>Fr</span><span>Sa</span><span>So</span></div>
</div>
</div>
<div class="mf-panel" id="cats">
<h4><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2l3 6 6 .5-4.5 4 1.5 6.5L12 16l-6 3 1.5-6.5L3 8.5 9 8z"/></svg> Top Kategorien</h4>
</div>
<div class="mf-panel accent challenge">
<div class="ch-row">
<svg class="bolt" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M13 2L4 14h7l-1 8 9-12h-7z"/></svg>
<div><h5>Bereit für die nächste Challenge?</h5><p>Starte jetzt ein Spiel und steigere dein Level!</p></div>
</div>
<a class="mf-btn" href="/mindmatch" style="margin-top:.8rem">LOS GEHT'S! <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M13 6l6 6-6 6"/></svg></a>
</div>
</aside>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
<script th:inline="none">
const GI = {
brain:'<path d="M28 8c-9-5-22 0-23 11-7 2-9 12-3 18-3 7 2 15 10 16 3 6 13 7 18 2M28 8c9-5 22 0 23 11 7 2 9 12 3 18 3 7-2 15-10 16-3 6-13 7-18 2M28 8v45M16 22c6 2 6 10 0 13M40 22c-6 2-6 10 0 13M12 40c7 0 10 5 10 11M44 40c-7 0-10 5-10 11"/>',
code:'<path d="M22 16L8 28l14 12M42 16l14 12-14 12"/>',
terminal:'<rect x="6" y="10" width="52" height="44" rx="4"/><path d="M16 26l9 6-9 6M32 40h14"/>',
lock:'<rect x="12" y="28" width="40" height="28" rx="5"/><path d="M20 28v-7a12 12 0 0 1 24 0v7M32 39v6"/>',
sword:'<path d="M48 8l4 4-22 22-4-4zM26 30l8 8M30 26l8 8M22 38l-6 6 6 6 6-6M16 44l-6 6"/>',
bug:'<rect x="20" y="20" width="24" height="30" rx="12"/><path d="M32 20v-6M24 12l4 6M40 12l-4 6M20 30h-8M52 30h-8M20 40H10M54 40H44M20 50l-7 7M44 50l7 7"/>',
network:'<circle cx="32" cy="14" r="6"/><circle cx="14" cy="48" r="6"/><circle cx="50" cy="48" r="6"/><path d="M30 19L17 43M34 19l13 24M20 48h24"/>',
trophy:'<path d="M18 10h28v10a14 14 0 0 1-28 0zM18 14h-7v3a9 9 0 0 0 9 9M46 14h7v3a9 9 0 0 1-9 9M26 40h12M24 40v6h16v-6M20 56h24"/>'
};
const GAMES = [
['MindMatch','brain','Trainiere dein Gedächtnis mit IT-Begriffen in einem klassischen Memory-Spiel.','/mindmatch'],
['TechTacular','code','Das IT-Quizduell im Jeopardy-Stil – teste dein Wissen in verschiedenen Kategorien.','/techtacular'],
['Syntax Saga','terminal','Interaktive IT-Geschichten mit Lückentexten – wie ein Textadventure zum Mitdenken.','/comingsoon'],
['Code Escape Room','lock','Entkomme mit IT-Rätseln aus digitalen Räumen – sei schnell und clever.','/comingsoon'],
['Service Clash','sword','Der chaotische Admin-Alltag als Spiel – meistere Anfragen & halte Systeme am Laufen!','/comingsoon'],
['Bug Hunter','bug','Finde Fehler im Code, denke wie ein Debugger und löse knifflige Bugs.','/comingsoon'],
['Netzwerker – Das Router-Rätselspiel','network','Verbinde Netzwerke, konfiguriere IPs und löse Routing-Herausforderungen.','/comingsoon'],
['FinalFlux','trophy','Dein Power-Modul für die Prüfung – gezielt lernen, strukturiert trainieren, sicher bestehen.','/finalflux']
];
document.getElementById('grid').innerHTML = GAMES.map(([t,ic,d,href])=>`
<a class="mf-card" href="${href}">
<span class="ico"><svg viewBox="0 0 64 64" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">${GI[ic]}</svg></span>
<h3>${t}</h3><p>${d}</p>
<span class="mf-play">SPIEL STARTEN <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M5 12h14M13 6l6 6-6 6"/></svg></span>
</a>`).join('');
const SVG={globe:'<circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18"/>',chart:'<path d="M4 20V10M10 20V4M16 20v-8M22 20H2"/>',lock:'<rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V8a4 4 0 0 1 8 0v3"/>',grid:'<rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/>',code:'<path d="M8 7l-5 5 5 5M16 7l5 5-5 5"/>'};
const CATS=[['Netzwerk','globe',85],['Datenbanken','chart',78],['IT-Sicherheit','lock',72],['Hardware','grid',66],['Programmierung','code',60]];
document.getElementById('cats').innerHTML += CATS.map(([n,i,p])=>`<div class="cat"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${SVG[i]}</svg><span class="nm">${n}</span><span class="cbar"><i style="width:${p}%"></i></span><span class="pct">${p}%</span></div>`).join('');
</script>
</body>
</html>
+48
View File
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mindflux – In Arbeit</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.mf-cardbox { max-width:560px; text-align:center; }
.mf-rocket { font-size:3rem; margin-bottom:.6rem; filter:drop-shadow(0 0 14px var(--glow-s)); }
.mf-cardbox h1 { color:var(--heading); font-size:1.5rem; font-weight:800; margin-bottom:.5rem; }
.mf-cardbox .slogan { color:var(--muted); font-size:.95rem; margin-bottom:1.6rem; }
.mf-cardbox .quote { color:var(--accent); font-size:1.05rem; line-height:1.6; font-style:italic;
border-left:3px solid var(--border-accent); background:var(--surface-2); padding:1rem 1.2rem;
border-radius:10px; text-align:left; margin-bottom:1.6rem; }
.mf-cardbox .mf-back { display:inline-block; }
</style>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-center">
<div class="mf-cardbox">
<div class="mf-rocket">🚀</div>
<h1>Zukunft in Arbeit</h1>
<p class="slogan">Hier entsteht etwas, das eure Zeitlinie verändern wird...</p>
<p class="quote">
"Ihr seid zwar noch nicht da, aber keine Sorge... bald rast ihr mit 88 Meilen pro Stunde hinein!"
</p>
<a th:href="@{/dashboard}" class="mf-back">⟵ Zurück zum Dashboard</a>
</div>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
+116
View File
@@ -0,0 +1,116 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark">
<head>
<meta charset="UTF-8">
<title>FinalFlux AP1 - Mindflux</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/css/style.css">
<link rel="stylesheet" href="/css/theme.css">
<link rel="stylesheet" href="/css/finalflux.css">
<script src="/js/theme.js"></script>
</head>
<body class="finalflux-shell">
<aside class="finalflux-sidebar">
<div style="display:flex;align-items:center;gap:.6rem;margin-bottom:1rem;flex-wrap:wrap">
<a href="/dashboard" class="finalflux-back" style="margin-bottom:0">Zurück zum Dashboard</a>
<button class="mf-theme"></button>
</div>
<h1>FinalFlux</h1>
<p>AP1 Informatik nach aktueller IHK-Ausbildungsordnung.</p>
<nav class="finalflux-nav" aria-label="FinalFlux AP1 Navigation">
<section>
<h2>Grundlagen</h2>
<a href="/finalflux/ap1/zahlensysteme.html">Zahlensysteme & Speicher</a>
<a href="/finalflux/ap1/ip-berechnung.html">IP-Adressberechnung</a>
<a href="/finalflux/ap1/projektmanagement.html">Projektmanagement</a>
<a href="/finalflux/ap1/datenschutz.html">Datenschutz</a>
</section>
<section>
<h2>Systeme & Netzwerke</h2>
<a href="/finalflux/systemintegration/topologien.html">Netzwerktopologien</a>
<a href="/finalflux/systemintegration/osi.html">OSI-Modell</a>
<a href="/finalflux/systemintegration/storage.html">Storage Systeme</a>
<a href="/finalflux/systemintegration/backuparten.html">Backuparten</a>
<a href="/finalflux/systemintegration/usv.html">USV</a>
<a href="/finalflux/systemintegration/datenbank.html">Datenbanksysteme</a>
<a href="/finalflux/systemintegration/dsl.html">DSL</a>
<a href="/finalflux/systemintegration/thin-client.html">Thin Clients</a>
<a href="/finalflux/systemintegration/vdi-daas.html">VDI & DaaS</a>
<a href="/finalflux/systemintegration/vlan.html">VLAN</a>
</section>
<section>
<h2>Wirtschaft & Beschaffung</h2>
<a href="/finalflux/wirtschaft/angebotsvergleich.html">Angebotsvergleich</a>
<a href="/finalflux/wirtschaft/bezugskalkulation.html">Bezugskalkulation</a>
<a href="/finalflux/wirtschaft/kosten_nutzen.html">Kosten-Nutzen-Vergleich</a>
<a href="/finalflux/wirtschaft/nutzwertanalyse.html">Nutzwertanalyse</a>
<a href="/finalflux/wirtschaft/make_or_buy.html">Make or Buy</a>
<a href="/finalflux/wirtschaft/roi.html">Return on Investment</a>
<a href="/finalflux/wirtschaft/rentabilitätsrechnung.html">Rentabilitätsrechnung</a>
<a href="/finalflux/wirtschaft/amortisation.html">Amortisation</a>
<a href="/finalflux/wirtschaft/ratendarlehen.html">Ratendarlehen</a>
<a href="/finalflux/wirtschaft/vertragsarten.html">Vertragsarten</a>
<a href="/finalflux/wirtschaft/marktformen.html">Marktformen</a>
<a href="/finalflux/wirtschaft/wirtschaftssektoren.html">Wirtschaftssektoren</a>
<a href="/finalflux/wirtschaft/harvard_konzept.html">Harvard-Konzept</a>
<a href="/finalflux/wirtschaft/win_win_strategie.html">Win-Win-Strategie</a>
</section>
</nav>
</aside>
<main class="finalflux-main">
<section class="finalflux-hero">
<p class="eyebrow">Abschlussprüfung Teil 1</p>
<h2>Einrichten eines IT-gestützten Arbeitsplatzes</h2>
<p>
FinalFlux bündelt AP1-Wissen für IT-Berufe: technische Grundlagen,
Arbeitsplatzanalyse, Netzwerke, Datenschutz, Beschaffung und einfache
Rechen- sowie Entscheidungssituationen.
</p>
</section>
<section class="finalflux-grid">
<article>
<h3>1. Lernen</h3>
<p>Jede Seite führt mit kurzer Erklärung, Prüfungsbezug und Merksätzen durch ein Thema.</p>
</article>
<article>
<h3>2. Anwenden</h3>
<p>Beispielaufgaben und kleine FluxChecks prüfen, ob der Stoff wirklich sitzt.</p>
</article>
<article>
<h3>3. Wiederholen</h3>
<p>Schwache Themen können später gezielt in Fortschritt und Training auftauchen.</p>
</article>
</section>
<section class="finalflux-panel">
<h3>AP1-Kompass</h3>
<p>
Starte mit Zahlensystemen und IP-Berechnung, wenn du Rechenaufgaben sichern willst.
Für typische Handlungssituationen sind Datenschutz, Projektmanagement,
Beschaffung, Storage, Backup und Netzwerke besonders wertvoll.
</p>
<div class="finalflux-actions">
<a href="/finalflux/ap1/zahlensysteme.html">Zahlensysteme starten</a>
<a href="/finalflux/systemintegration/osi.html">Netzwerke starten</a>
<a href="/finalflux/ap1/datenschutz.html">Datenschutz starten</a>
</div>
</section>
</main>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
+90
View File
@@ -0,0 +1,90 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mindflux – Deine Zukunft beginnt hier</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.mf-auth { width:100%; max-width:880px; display:grid; grid-template-columns:1.1fr 1fr; gap:1.4rem; align-items:stretch; }
.mf-auth .info { background:var(--surface); border:1px solid var(--border); border-radius:18px; padding:2rem; box-shadow:var(--g-soft); }
.mf-auth .info h2 { color:var(--heading); font-size:1.25rem; font-weight:800; margin-bottom:.7rem; }
.mf-auth .info p { color:var(--muted); font-size:.92rem; line-height:1.6; margin-bottom:.8rem; }
.mf-authlogo { display:flex; flex-direction:column; align-items:center; gap:.5rem; margin-bottom:1.4rem; text-align:center; }
.mf-authlogo .mark { width:64px; height:64px; background:var(--accent);
-webkit-mask:url("/img/mindflux-emblem.png") center/contain no-repeat; mask:url("/img/mindflux-emblem.png") center/contain no-repeat;
filter:drop-shadow(0 0 12px var(--glow-s)); }
.mf-authlogo b { color:var(--accent); font-weight:800; letter-spacing:.16em; font-size:1.2rem; text-shadow:0 0 14px var(--glow-s); }
.mf-authlogo span { color:var(--muted); font-size:.62rem; letter-spacing:.24em; }
.login-box h2 { color:var(--heading); font-size:1.15rem; font-weight:800; margin-bottom:1.1rem; text-align:center; }
.login-box .alt { color:var(--muted); font-size:.85rem; text-align:center; margin:1.1rem 0 .6rem; }
.login-box .mf-btn { width:100%; }
.mf-foot { margin-top:1.6rem; color:var(--muted); font-size:.78rem; text-align:center; line-height:2; }
.mf-foot a { color:var(--muted); text-decoration:none; }
.mf-foot a:hover { color:var(--accent); }
@media (max-width:760px){ .mf-auth { grid-template-columns:1fr; } }
</style>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-center">
<div>
<div class="mf-auth">
<!-- Infotext -->
<div class="info">
<p class="mf-eyebrow" style="margin-bottom:.6rem">Willkommen bei Mindflux</p>
<h2>Worum geht's bei Mindflux?</h2>
<p>
Mindflux ist deine moderne Lernplattform zur gezielten Prüfungsvorbereitung in der IT-Ausbildung.
Trainiere Fragen, baue dein Wissen auf – und das mit Stil.
Unterstützt von Zeitreise-Ästhetik, Technik-Liebe und einer ordentlichen Portion Motivation.
</p>
<p>
Registriere dich kostenlos, erstelle Quizfragen, lerne systematisch – und bring dein Wissen auf 88 Meilen pro Stunde 🚀
</p>
</div>
<!-- Login/Registrierung -->
<div class="mf-cardbox login-box">
<div class="mf-authlogo">
<span class="mark"></span>
<b>MINDFLUX</b>
<span>DEIN FLUXKOMPENSATOR FÜRS GEHIRN</span>
</div>
<h2>Login</h2>
<form th:action="@{/login}" method="post">
<div class="mf-field">
<label class="mf-label" for="username">Benutzername</label>
<input id="username" class="mf-input" type="text" name="username" placeholder="Benutzername" required />
</div>
<div class="mf-field">
<label class="mf-label" for="password">Passwort</label>
<input id="password" class="mf-input" type="password" name="password" placeholder="Passwort" required />
</div>
<button class="mf-btn" type="submit">Einloggen</button>
</form>
<p class="alt">Noch keinen Account?</p>
<form th:action="@{/register}" method="get">
<button class="mf-btn outline" type="submit">Registrieren</button>
</form>
</div>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</div>
</div>
</body>
</html>
@@ -0,0 +1,44 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="de" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mindflux – Knowledge Center</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.mf-footer { text-align:center; color:var(--muted); font-size:.82rem; padding:1.6rem 1rem; }
.mf-footer a { color:var(--muted); text-decoration:none; margin:0 .15rem; }
.mf-footer a:hover { color:var(--accent); }
</style>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-container">
<div class="mf-page-head">
<a th:href="@{/dashboard}" class="mf-back">⟵ Zurück zum Dashboard</a>
</div>
<span class="mf-eyebrow">Knowledge Core</span>
<div class="mf-panel accent" style="text-align:center; padding:2.5rem 1.5rem;">
<h1 style="color:var(--heading); font-size:1.7rem; font-weight:800; margin-bottom:.7rem;">Welcome to the Knowledge Center!</h1>
<p style="color:var(--text);">Hier entsteht dein cooles Modul 📚🧠</p>
</div>
</div>
<!-- Footer -->
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
+67
View File
@@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="de" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login – Mindflux</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 400px;
margin: 50px auto;
padding: 2rem;
border: 1px solid #ccc;
border-radius: 8px;
}
h2 {
text-align: center;
}
label {
display: block;
margin: 1rem 0 0.3rem;
}
input {
width: 100%;
padding: 0.5rem;
}
button {
margin-top: 1rem;
width: 100%;
padding: 0.7rem;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.message {
margin-top: 1rem;
text-align: center;
}
.error {
color: red;
}
</style>
</head>
<body>
<h2>Login</h2>
<form th:action="@{/login}" method="post">
<label for="username">Benutzername</label>
<input type="text" id="username" name="username" required th:value="${param.username}"/>
<label for="password">Passwort</label>
<input type="password" id="password" name="password" required/>
<div class="message error" th:if="${param.error}">
Benutzername oder Passwort sind falsch.
</div>
<div class="message" th:if="${param.logout}">
Du hast dich erfolgreich abgemeldet.
</div>
<button type="submit">Einloggen</button>
</form>
</body>
</html>
@@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MindMatch – Mindflux</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<link rel="stylesheet" href="/css/mindmatch.css?v=4">
</head>
<body class="mm-body">
<!-- Feste Viewport-Höhe, kein Scrollen: Kopf + Leiste oben, Brett füllt den Rest. -->
<div class="mm-shell">
<header class="mm-header">
<a href="/dashboard" class="mm-back">⟵ Dashboard</a>
<h1 class="mm-title">MindMatch</h1>
<div class="mm-header-tools">
<button type="button" class="mf-theme"></button>
<a href="/logout" class="mm-logout">Logout</a>
</div>
</header>
<!-- Kompakte Leiste: Steuerung links, Werte rechts -->
<div class="mm-bar">
<div class="mm-bar-group">
<label class="mm-field">
<span>Deck</span>
<select id="mm-deck"></select>
</label>
<button type="button" id="mm-new" class="mm-btn">Neues Spiel</button>
<button type="button" id="mm-cancel" class="mm-btn mm-btn-cancel" hidden>Spiel abbrechen</button>
</div>
<div class="mm-bar-group mm-hud" aria-live="polite">
<span class="mm-stat">Züge <b id="mm-moves">0</b></span>
<span class="mm-stat">Zeit <b id="mm-time">0:00</b></span>
<span class="mm-stat">Paare <b id="mm-pairs">0 / 0</b></span>
<span class="mm-stat">Bestwert <b id="mm-best">–</b></span>
</div>
</div>
<!-- Spielfeld: füllt den verbleibenden Platz, kein Scrollen -->
<main class="mm-stage">
<section id="mm-board" class="mm-board" aria-label="Spielfeld"></section>
</main>
</div>
<!-- Ergebnis-Overlay -->
<div id="mm-result" class="mm-result" hidden>
<div class="mm-result-card">
<h2 id="mm-result-title">Geschafft! 🎉</h2>
<p id="mm-result-stars" class="mm-stars"></p>
<p class="mm-result-score">Score: <strong id="mm-result-score">0</strong></p>
<p id="mm-result-detail" class="mm-result-detail"></p>
<p id="mm-result-highscore" class="mm-result-highscore" hidden>🏆 Neuer Bestwert!</p>
<div class="mm-result-actions">
<button type="button" id="mm-again" class="mm-btn">Nochmal</button>
<a href="/dashboard" class="mm-btn mm-btn-secondary">Zurück zum Dashboard</a>
</div>
</div>
</div>
<script src="/js/mindmatch.js?v=3" defer></script>
</body>
</html>
+111
View File
@@ -0,0 +1,111 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mindflux – Profil</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.mf-tab-nav { display:flex; flex-wrap:wrap; gap:.4rem; margin-bottom:1.4rem; }
.mf-tab { padding:.55rem .95rem; border-radius:10px; border:1px solid var(--border); color:var(--muted); text-decoration:none; font-size:.85rem; font-weight:700; transition:all .18s ease; }
.mf-tab:hover { color:var(--text); background:var(--surface-2); }
.mf-tab.active { color:var(--accent-ink); background:linear-gradient(100deg,var(--accent),var(--accent-2)); box-shadow:0 0 18px var(--glow-s); border-color:transparent; }
.mf-profile-pic { display:flex; justify-content:center; margin-bottom:1.3rem; }
.mf-profile-pic img { width:140px; height:140px; border-radius:50%; object-fit:cover; border:2px solid var(--border-accent); box-shadow:0 0 22px var(--glow-w); }
.mf-success { margin-bottom:1.2rem; transition:opacity 1.5s ease; }
.mf-footer { text-align:center; color:var(--muted); font-size:.82rem; padding:1.6rem 1rem; }
.mf-footer a { color:var(--muted); text-decoration:none; margin:0 .15rem; }
.mf-footer a:hover { color:var(--accent); }
</style>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-container">
<div class="mf-page-head">
<a th:href="@{/dashboard}" class="mf-back">⟵ Zurück zum Dashboard</a>
</div>
<span class="mf-eyebrow">Mein Profil</span>
<h1 style="color:var(--heading); font-size:1.9rem; font-weight:800; margin:.3rem 0 .25rem;">👤 Mein Profil</h1>
<p style="color:var(--muted); margin-bottom:1.4rem;">Alles rund um dich und deinen Fortschritt</p>
<nav class="mf-tab-nav">
<a th:href="@{/profile}" class="mf-tab active">Profil</a>
<a th:href="@{/progress}" class="mf-tab">Fortschritt</a>
<a th:href="@{/profile/achievements}" class="mf-tab">Erfolge</a>
<a th:href="@{/soziales}" class="mf-tab">Soziales</a>
<a th:href="@{/profile/settings}" class="mf-tab">Einstellungen</a>
</nav>
<!-- Erfolgsmeldung -->
<div th:if="${message}" class="mf-panel accent mf-success" id="successMessage">
<p th:text="${message}" style="color:var(--accent); margin:0;"></p>
</div>
<div class="mf-panel">
<!-- Profilbild-Vorschau -->
<div class="mf-profile-pic">
<img th:src="${user.profilePicture != null and !#strings.isEmpty(user.profilePicture)} ?
@{/uploads/profiles/{file}(file=${user.profilePicture})} :
@{/img/profiles/default-profile.png}"
alt="Profilbild">
</div>
<!-- Profil bearbeiten -->
<form th:action="@{/profile/update}" method="post" enctype="multipart/form-data">
<div class="mf-field">
<label class="mf-label">Benutzername</label>
<input class="mf-input" type="text" name="username" th:value="${user.username}" readonly>
</div>
<div class="mf-field">
<label class="mf-label">E-Mail</label>
<input class="mf-input" type="email" name="email" th:value="${user.email}" required>
</div>
<div class="mf-field">
<label class="mf-label">Profilbild hochladen</label>
<input class="mf-input" type="file" name="profilePicture" accept=".jpg,.jpeg,.png">
</div>
<button type="submit" class="mf-btn">Speichern</button>
</form>
<!-- Profilbild löschen -->
<form th:action="@{/profile/delete-picture}" method="post" style="margin-top:1rem;">
<button type="submit" class="mf-btn danger">Profilbild entfernen</button>
</form>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</div>
<script>
window.addEventListener('DOMContentLoaded', () => {
const successMessage = document.getElementById('successMessage');
if (successMessage) {
setTimeout(() => {
successMessage.style.opacity = '0';
setTimeout(() => {
successMessage.style.display = 'none';
}, 1500);
}, 3000);
}
});
</script>
</body>
</html>
+154
View File
@@ -0,0 +1,154 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fortschritt – Mindflux</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.mf-progress-summary { display:flex; flex-wrap:wrap; align-items:center; gap:2rem; }
.mf-circular { position:relative; width:180px; height:180px; flex:0 0 auto; }
.mf-circular canvas { width:180px !important; height:180px !important; }
.mf-percentage { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; color:var(--heading); font-size:1.6rem; font-weight:800; text-shadow:0 0 14px var(--glow-s); }
.mf-progress-info { flex:1; min-width:220px; }
.mf-progress-info p { color:var(--text); margin:.35rem 0; font-size:.92rem; }
.mf-progress-info strong { color:var(--heading); }
.mf-game-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(240px,1fr)); gap:1rem; }
.mf-game-card { background:var(--surface-2); border:1px solid var(--border); border-radius:12px; padding:1rem; }
.mf-game-card h3 { color:var(--heading); font-size:1.05rem; margin-bottom:.5rem; }
.mf-game-card p { color:var(--muted); font-size:.84rem; margin:.25rem 0; }
.mf-game-card p span { color:var(--text); }
.mf-section-title { color:var(--accent); font-size:.78rem; letter-spacing:.12em; text-transform:uppercase; margin-bottom:.9rem; }
.mf-footer { text-align:center; color:var(--muted); font-size:.82rem; padding:1.6rem 1rem; }
.mf-footer a { color:var(--muted); text-decoration:none; margin:0 .15rem; }
.mf-footer a:hover { color:var(--accent); }
</style>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-container">
<div class="mf-page-head">
<a href="/profile" class="mf-back">⟵ Zurück zum Profil</a>
</div>
<span class="mf-eyebrow">Dein Fortschritt</span>
<h1 style="color:var(--heading); font-size:2.1rem; font-weight:800; margin:.3rem 0 .25rem;">Dein Fortschritt</h1>
<p style="color:var(--muted); margin-bottom:1.4rem;">Bleib am Ball – jeder Schritt zählt!</p>
<!-- Gesamtfortschritt -->
<section class="mf-panel accent" style="margin-bottom:1.2rem;">
<div class="mf-progress-summary">
<div class="mf-circular">
<canvas id="progressCircle"></canvas>
<p class="mf-percentage" th:text="${xpPercentage + '%'}">78%</p>
</div>
<div class="mf-progress-info">
<p><strong>Korrekte Antworten:</strong> <span th:text="${correctAnswers}">432</span></p>
<p><strong>Level:</strong> <span th:text="${userLevel}">5</span></p>
<div class="mf-bar" style="margin:.6rem 0 .4rem;"><i th:style="'width:' + ${xpPercentage} + '%'"></i></div>
<p><span th:text="${currentXp}">2400</span> / <span th:text="${xpForNextLevel}">3000</span> XP</p>
</div>
</div>
</section>
<!-- Lernzeit pro Woche -->
<section class="mf-panel" style="margin-bottom:1.2rem;">
<h2 class="mf-section-title">Lernzeit – Diese Woche</h2>
<canvas id="learnTimeChart" width="600" height="180"></canvas>
</section>
<!-- Fortschritt pro Spiel -->
<section class="mf-panel" th:if="${gameProgress != null}" style="margin-bottom:1.2rem;">
<h2 class="mf-section-title">Spiel-Fortschritte</h2>
<div class="mf-game-grid">
<div class="mf-game-card" th:each="game : ${gameProgress}">
<h3 th:text="${game.name}">Hangman</h3>
<p>Fortschritt: <span th:text="${game.progressPercent}">65</span>%</p>
<div class="mf-bar" style="margin:.4rem 0;"><i th:style="'width:' + ${game.progressPercent} + '%'"></i></div>
<p>Beste Punktzahl: <span th:text="${game.highscore}">1200</span></p>
<p>Gespielt: <span th:text="${game.plays}">15</span>×</p>
<p>Letzter Zugriff: <span th:text="${game.lastPlayed}">14.04.2025</span></p>
</div>
</div>
</section>
</div>
<!-- Diagramme -->
<script th:inline="none">
// Theme-Tokens auslesen, damit die Charts in Dark (Grün) & Light (Magenta) passen
const css = getComputedStyle(document.documentElement);
const accent = css.getPropertyValue('--accent').trim() || '#2bd96a';
const track = css.getPropertyValue('--surface-2').trim() || '#2c2c2c';
const tick = css.getPropertyValue('--text').trim() || '#f1f1f1';
const grid = css.getPropertyValue('--border').trim() || '#333';
// Kreisdiagramm
const ctx = document.getElementById('progressCircle').getContext('2d');
new Chart(ctx, {
type: 'doughnut',
data: {
datasets: [{
data: [78, 22], // Achtung: Dummywerte, kannst du später dynamisch machen
backgroundColor: [accent, track],
borderWidth: 0
}]
},
options: {
cutout: '75%',
plugins: {
legend: { display: false }
}
}
});
// Lernzeit-Balkendiagramm
const learnCtx = document.getElementById('learnTimeChart').getContext('2d');
new Chart(learnCtx, {
type: 'bar',
data: {
labels: ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'],
datasets: [{
label: 'Lernzeit (Minuten)',
data: [30, 45, 20, 60, 50, 0, 10], // Beispielwerte
backgroundColor: accent,
borderRadius: 6
}]
},
options: {
plugins: {
legend: {
labels: { color: tick }
}
},
scales: {
x: {
ticks: { color: tick },
grid: { color: grid }
},
y: {
beginAtZero: true,
ticks: { color: tick },
grid: { color: grid }
}
}
}
});
</script>
<!-- Footer -->
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mindflux – Registrieren</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.mf-authlogo { display:flex; flex-direction:column; align-items:center; gap:.5rem; margin-bottom:1.4rem; text-align:center; }
.mf-authlogo .mark { width:60px; height:60px; background:var(--accent);
-webkit-mask:url("/img/mindflux-emblem.png") center/contain no-repeat; mask:url("/img/mindflux-emblem.png") center/contain no-repeat;
filter:drop-shadow(0 0 12px var(--glow-s)); }
.mf-authlogo b { color:var(--accent); font-weight:800; letter-spacing:.16em; font-size:1.15rem; text-shadow:0 0 14px var(--glow-s); }
.mf-authlogo span { color:var(--muted); font-size:.62rem; letter-spacing:.24em; }
.mf-cardbox h2 { color:var(--heading); font-size:1.15rem; font-weight:800; margin-bottom:1.1rem; text-align:center; }
.mf-cardbox .mf-btn { width:100%; }
.mf-alert { padding:.7rem .9rem; border-radius:10px; font-size:.86rem; margin-bottom:1rem; }
.mf-alert.err { color:var(--danger); border:1px solid var(--danger); background:rgba(255,95,107,.08); }
.mf-alert.ok { color:var(--accent); border:1px solid var(--border-accent); background:var(--surface-2); }
</style>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-center">
<div class="mf-cardbox">
<div class="mf-authlogo">
<span class="mark"></span>
<b>MINDFLUX</b>
<span>REGISTRIERUNG</span>
</div>
<h2>📝 Account erstellen</h2>
<!-- Fehler anzeigen -->
<div th:if="${error}" class="mf-alert err">
<span th:text="${error}"></span>
</div>
<!-- Erfolgreiche Registrierung anzeigen -->
<div th:if="${param.registerSuccess}" class="mf-alert ok">
Registrierung erfolgreich! Du kannst dich jetzt einloggen.
</div>
<form th:action="@{/register}" method="post">
<div class="mf-field">
<label class="mf-label" for="username">Benutzername</label>
<input id="username" class="mf-input" type="text" name="username" placeholder="Benutzername" required>
</div>
<div class="mf-field">
<label class="mf-label" for="password">Passwort</label>
<input id="password" class="mf-input" type="password" name="password" placeholder="Passwort" required>
</div>
<div class="mf-field">
<label class="mf-label" for="email">E-Mail</label>
<input id="email" class="mf-input" type="email" name="email" placeholder="E-Mail" required>
</div>
<button type="submit" class="mf-btn">Registrieren</button>
</form>
<p style="text-align:center;margin-top:1.1rem">
<a href="/" class="mf-back">⟵ Zurück zum Login</a>
</p>
</div>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
@@ -0,0 +1,93 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mindflux – Einstellungen</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.mf-section-title { color:var(--accent); font-size:.78rem; letter-spacing:.12em; text-transform:uppercase; margin-bottom:.9rem; }
.mf-check { display:flex; align-items:center; gap:.55rem; color:var(--text); font-size:.92rem; margin:.5rem 0; cursor:pointer; }
.mf-check input { width:18px; height:18px; accent-color:var(--accent); }
.mf-hint { color:var(--muted); font-size:.78rem; display:block; margin-top:.5rem; }
.mf-footer { text-align:center; color:var(--muted); font-size:.82rem; padding:1.6rem 1rem; }
.mf-footer a { color:var(--muted); text-decoration:none; margin:0 .15rem; }
.mf-footer a:hover { color:var(--accent); }
</style>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-container">
<div class="mf-page-head">
<a th:href="@{/profile}" class="mf-back">⟵ Zurück zum Profil</a>
</div>
<span class="mf-eyebrow">Einstellungen</span>
<h1 style="color:var(--heading); font-size:1.9rem; font-weight:800; margin:.3rem 0 .25rem;">⚙️ Einstellungen</h1>
<p style="color:var(--muted); margin-bottom:1.4rem;">Passe Mindflux nach deinen Wünschen an</p>
<!-- Theme-Einstellung (Dummy) -->
<section class="mf-panel" style="margin-bottom:1.2rem;">
<h2 class="mf-section-title">Theme wechseln</h2>
<div class="mf-field">
<label class="mf-label">Modus auswählen:</label>
<select class="mf-select">
<option>Standard (Dark)</option>
<option>Hell (Light)</option>
</select>
</div>
<small class="mf-hint">Hinweis: Nutze den Umschalter oben rechts, um zwischen Dark (Grün) und Light (Magenta) zu wechseln.</small>
</section>
<!-- Spracheinstellung (Dummy) -->
<section class="mf-panel" style="margin-bottom:1.2rem;">
<h2 class="mf-section-title">Sprache</h2>
<div class="mf-field">
<label class="mf-label">Sprache auswählen:</label>
<select class="mf-select">
<option>Deutsch</option>
<option>Englisch</option>
</select>
</div>
</section>
<!-- Benachrichtigungen (Dummy) -->
<section class="mf-panel" style="margin-bottom:1.2rem;">
<h2 class="mf-section-title">Benachrichtigungen</h2>
<label class="mf-check"><input type="checkbox" checked> E-Mail-Benachrichtigungen erhalten</label>
<label class="mf-check"><input type="checkbox"> Erinnerungen an Lernziele aktivieren</label>
</section>
<!-- Passwort ändern -->
<form th:action="@{/profile/change-password}" method="post" class="mf-panel" style="margin-bottom:1.2rem;">
<h2 class="mf-section-title">🔒 Passwort ändern</h2>
<div class="mf-field">
<input class="mf-input" type="password" name="oldPassword" placeholder="Aktuelles Passwort" required>
</div>
<div class="mf-field">
<input class="mf-input" type="password" name="newPassword" placeholder="Neues Passwort" required>
</div>
<button type="submit" class="mf-btn">Passwort ändern</button>
</form>
<!-- Profil löschen -->
<form th:action="@{/profile/delete}" method="post" onsubmit="return confirm('Profil wirklich löschen?');">
<button type="submit" class="mf-btn danger">Profil löschen</button>
</form>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mindflux Soziales</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.mf-soziales { text-align:center; padding:2.5rem 1.5rem; }
.mf-soziales h1 { color:var(--heading); font-size:1.6rem; line-height:1.4; margin-bottom:1rem; }
.mf-soziales p { color:var(--text); line-height:1.6; }
.mf-time-machine { font-size:3rem; margin:1.6rem 0; filter:drop-shadow(0 0 12px var(--glow-s)); }
.mf-small-hint { color:var(--muted); font-size:.86rem; margin-top:1rem; }
</style>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-container">
<div class="mf-page-head">
<a href="/profile" class="mf-back">⟵ Zurück zum Profil</a>
</div>
<div class="mf-panel accent mf-soziales">
<h1>„Straßen? Wo wir hingehen, brauchen wir keine Straßen!“ 🚀</h1>
<p>Das soziale Netzwerk für Mindflux-User befindet sich noch in Entwicklung.<br>
Bald kannst du dich hier mit anderen Fluxianern verbinden!</p>
<div class="mf-time-machine">
🚗💨💥
</div>
<p class="mf-small-hint">Zurück in die Zukunft... und bald auch ins Mindflux-Netzwerk!</p>
</div>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TechTacular – Mindflux</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<link rel="stylesheet" href="/css/techtacular.css?v=3">
</head>
<body class="tt-body">
<div class="tt-shell">
<header class="tt-header">
<a href="/dashboard" class="tt-back">⟵ Dashboard</a>
<h1 class="tt-title">TechTacular</h1>
<div class="tt-header-tools">
<button type="button" class="mf-theme"></button>
<a href="/logout" class="tt-logout">Logout</a>
</div>
</header>
<div class="tt-bar">
<div class="tt-bar-group">
<button type="button" id="tt-new" class="tt-btn">Neues Spiel</button>
<button type="button" id="tt-finish" class="tt-btn tt-btn-secondary" hidden>Runde beenden</button>
<button type="button" id="tt-cancel" class="tt-btn tt-btn-cancel" hidden>Abbrechen</button>
</div>
<div class="tt-bar-group tt-hud" aria-live="polite">
<span class="tt-stat">Punkte <b id="tt-score">0</b></span>
<span class="tt-stat">Beantwortet <b id="tt-answered">0 / 0</b></span>
<span class="tt-stat">Zeit <b id="tt-time">0:00</b></span>
<span class="tt-stat">Bestwert <b id="tt-best">–</b></span>
</div>
</div>
<main class="tt-stage">
<section id="tt-board" class="tt-board" aria-label="Jeopardy-Board"></section>
</main>
</div>
<!-- Frage-Overlay -->
<div id="tt-question" class="tt-overlay" hidden>
<div class="tt-q-card">
<div class="tt-q-head">
<span id="tt-q-cat" class="tt-q-cat">Kategorie</span>
<span id="tt-q-points" class="tt-q-points">100</span>
</div>
<p class="tt-q-instruction">Antwort – finde die passende Frage:</p>
<h2 id="tt-q-text" class="tt-q-text"></h2>
<div id="tt-q-answers" class="tt-q-answers"></div>
<p id="tt-q-feedback" class="tt-q-feedback"></p>
<div class="tt-q-actions">
<button type="button" id="tt-q-next" class="tt-btn" hidden>Weiter</button>
</div>
</div>
</div>
<!-- Ergebnis-Overlay -->
<div id="tt-result" class="tt-overlay" hidden>
<div class="tt-result-card">
<h2>Runde beendet 🎉</h2>
<p class="tt-result-score">Score: <strong id="tt-result-score">0</strong> / <span id="tt-result-max">0</span></p>
<p id="tt-result-detail" class="tt-result-detail"></p>
<p id="tt-result-high" class="tt-result-high" hidden>🏆 Neuer Bestwert!</p>
<div class="tt-result-actions">
<button type="button" id="tt-again" class="tt-btn">Nochmal</button>
<a href="/dashboard" class="tt-btn tt-btn-secondary">Zurück zum Dashboard</a>
</div>
</div>
</div>
<script src="/js/techtacular.js?v=3" defer></script>
</body>
</html>
@@ -0,0 +1,73 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Benutzer anlegen</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.mf-cardbox { max-width:480px; }
.mf-cardbox h1 { color:var(--heading); font-size:1.3rem; font-weight:800; margin-bottom:1.3rem; text-align:center; }
.mf-cardbox .mf-btn { width:100%; margin-top:.4rem; }
</style>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-center">
<div class="mf-cardbox">
<h1>Neuen Benutzer erstellen</h1>
<form th:action="@{/users/save}" th:object="${user}" method="post">
<!-- CSRF-Token für Spring Security -->
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
<div class="mf-field">
<label class="mf-label" for="username">Benutzername</label>
<input id="username" class="mf-input" type="text" th:field="*{username}" placeholder="Benutzername" required />
</div>
<div class="mf-field">
<label class="mf-label" for="password">Passwort</label>
<input id="password" class="mf-input" type="password" th:field="*{password}" placeholder="Passwort" required />
</div>
<div class="mf-field">
<label class="mf-label" for="email">E-Mail</label>
<input id="email" class="mf-input" type="email" th:field="*{email}" placeholder="E-Mail" required />
</div>
<div class="mf-field">
<label class="mf-label" for="role">Rolle</label>
<select id="role" class="mf-select" th:field="*{role}">
<option value="USER">USER</option>
<option value="ADMIN">ADMIN</option>
</select>
</div>
<!-- Optionales Profilbild-Feld, falls gewünscht -->
<!--
<div class="mf-field">
<label class="mf-label" for="profilePicture">Profilbild-URL</label>
<input id="profilePicture" class="mf-input" type="text" th:field="*{profilePicture}" placeholder="Profilbild-URL" />
</div>
-->
<button type="submit" class="mf-btn">Speichern</button>
</form>
</div>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>
@@ -0,0 +1,46 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Benutzer erfolgreich erstellt</title>
<link rel="stylesheet" href="/css/theme.css">
<script src="/js/theme.js"></script>
<style>
.mf-cardbox { max-width:480px; text-align:center; }
.mf-check { width:64px; height:64px; margin:0 auto 1rem; color:var(--accent); filter:drop-shadow(0 0 10px var(--glow-s)); }
.mf-cardbox h1 { color:var(--heading); font-size:1.3rem; font-weight:800; margin-bottom:.6rem; }
.mf-cardbox p { color:var(--muted); font-size:.92rem; line-height:1.55; margin-bottom:1.4rem; }
.mf-actions { display:flex; gap:.7rem; justify-content:center; flex-wrap:wrap; }
</style>
</head>
<body>
<div class="mf-theme-float"><button class="mf-theme"></button></div>
<div class="mf-center">
<div class="mf-cardbox">
<svg class="mf-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/><path d="M8 12l3 3 5-6"/>
</svg>
<h1>Benutzer gespeichert!</h1>
<p>Der neue Benutzer wurde erfolgreich in der Datenbank gespeichert.</p>
<div class="mf-actions">
<a href="/users/form" class="mf-btn">Neuen Benutzer anlegen</a>
<a href="/api/users" class="mf-btn outline">Alle Benutzer anzeigen (JSON)</a>
</div>
</div>
</div>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
<a href="#">Nutzungsbedingungen</a>
<a href="#">Richtlinien</a>
<a href="#">Datenschutz</a>
<a href="#">Impressum</a>
</footer>
</body>
</html>