Mindflux: Sicherheit, Datenintegrität und Navigation härten

This commit is contained in:
AliceDarkest
2026-07-12 21:47:37 +02:00
parent db6b5bbb1a
commit 005d2d7d8c
73 changed files with 2495 additions and 650 deletions
@@ -30,12 +30,14 @@
<div class="mf-container">
<div class="mf-page-head">
<a href="/profile" class="mf-back">⟵ Zurück zum Profil</a>
<a th:href="@{/dashboard}" class="mf-back">⟵ Zurück zum Dashboard</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>
<span class="mf-eyebrow">Deine Erfolge</span>
<h1 style="color:var(--heading); font-size:1.9rem; font-weight:800; margin:.3rem 0 .25rem;">🏆 Deine Erfolge</h1>
<p style="color:var(--muted); margin-bottom:1.4rem;">Sammle sie alle und werde zur Legende!</p>
<nav th:replace="~{fragments/layout :: profileTabs('erfolge')}"></nav>
<div class="mf-panel">
<div class="mf-ach-grid">
<div class="achievement-card"
+11 -69
View File
@@ -35,68 +35,8 @@
<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>
<!-- ===== Sidebar (gemeinsames Fragment) ===== -->
<aside th:replace="~{fragments/layout :: sidebar('dashboard')}"></aside>
<!-- ===== Topbar ===== -->
<header class="mf-topbar">
@@ -111,7 +51,9 @@
<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>
<form th:action="@{/logout}" method="post" style="display:inline">
<button type="submit" class="mf-iconbtn" 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></button>
</form>
</div>
</header>
@@ -186,12 +128,12 @@
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'],
['Dev Lab','lab','Das Entwicklerlabor bündelt Java, SQL, CSS, PHP, HTML, Git, REST und Regex.','/devlab'],
['Syntax Saga','terminal','Interaktive IT-Geschichten mit Lückentexten – wie ein Textadventure zum Mitdenken.','/syntaxsaga'],
['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'],
['DevLab','lab','Das Entwicklerlabor bündelt Java, SQL, CSS, PHP, HTML, Git, REST und Regex.','/devlab'],
['SyntaxSaga','terminal','Interaktive IT-Geschichten mit Lückentexten – wie ein Textadventure zum Mitdenken.','/syntaxsaga'],
['CodeEscape','lock','Entkomme mit IT-Rätseln aus digitalen Räumen – sei schnell und clever.','/comingsoon'],
['ServiceClash','sword','Der chaotische Admin-Alltag als Spiel – meistere Anfragen & halte Systeme am Laufen!','/comingsoon'],
['BugHunter','bug','Finde Fehler im Code, denke wie ein Debugger und löse knifflige Bugs.','/comingsoon'],
['NetWorker','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])=>`
@@ -18,7 +18,9 @@
</div>
<div class="dlg-header-tools">
<button type="button" class="mf-theme"></button>
<a href="/logout" class="dlg-logout">Logout</a>
<form th:action="@{/logout}" method="post" style="display:inline">
<button type="submit" class="dlg-logout" style="background:none;border:0;cursor:pointer;font:inherit">Logout</button>
</form>
</div>
</header>
+4 -2
View File
@@ -41,12 +41,14 @@
<header class="mf-topbar dl-topbar">
<div class="mf-hello">
<p class="mf-eyebrow">Entwicklerlabor</p>
<h1>Dev Lab</h1>
<h1>DevLab</h1>
<p>Ein zentraler Hub für die Spiele aus Syntax, Debugging und visueller Praxis.</p>
</div>
<div class="mf-tools">
<button type="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>
<form th:action="@{/logout}" method="post" style="display:inline">
<button type="submit" class="mf-iconbtn" 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></button>
</form>
</div>
</header>
+31
View File
@@ -0,0 +1,31 @@
<!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 – Fehler</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:640px; margin:10vh auto 0;">
<div class="mf-panel" style="text-align:center; padding:2.5rem 2rem;">
<div style="font-size:2.6rem; margin-bottom:.6rem;">⚡</div>
<h1 style="color:var(--heading); font-size:1.6rem; font-weight:800; margin-bottom:.4rem;">
Ups – da ist etwas schiefgelaufen
</h1>
<p style="color:var(--muted); margin-bottom:1rem;">
<span th:if="${status != null}" th:text="'Fehlercode ' + ${status}">Fehlercode</span>
<span th:unless="${status != null}">Unerwarteter Fehler</span>
</p>
<p th:if="${friendlyMessage != null}" th:text="${friendlyMessage}"
style="color:var(--text); margin-bottom:1.4rem;"></p>
<a th:href="@{/dashboard}" class="mf-btn">Zum Dashboard</a>
</div>
</div>
</body>
</html>
+12 -2
View File
@@ -16,9 +16,9 @@
<button class="mf-theme"></button>
</div>
<h1>FinalFlux</h1>
<p>AP1 Informatik nach aktueller IHK-Ausbildungsordnung.</p>
<p>AP1 &amp; AP2 nach aktueller IHK-Ausbildungsordnung – die gesamte Fachinformatiker-Ausbildung.</p>
<nav class="finalflux-nav" aria-label="FinalFlux AP1 Navigation">
<nav class="finalflux-nav" aria-label="FinalFlux Navigation">
<section>
<h2>Grundlagen</h2>
<a href="/finalflux/ap1/zahlensysteme.html">Zahlensysteme & Speicher</a>
@@ -58,6 +58,16 @@
<a href="/finalflux/wirtschaft/harvard_konzept.html">Harvard-Konzept</a>
<a href="/finalflux/wirtschaft/win_win_strategie.html">Win-Win-Strategie</a>
</section>
<section>
<h2>AP2 Anwendungsentwicklung</h2>
<a href="/finalflux/ap2-fiae/entwurfsmuster.html">Entwurfsmuster</a>
</section>
<section>
<h2>AP2 Systemintegration</h2>
<a href="/finalflux/ap2-fisi/hochverfuegbarkeit.html">Hochverfügbarkeit</a>
</section>
</nav>
</aside>
@@ -0,0 +1,97 @@
<!DOCTYPE html>
<!-- Gemeinsame Layout-Fragmente: App-Sidebar, Profil-Tab-Leiste, Footer.
Einbinden mit th:replace="~{fragments/layout :: sidebar('dashboard')}" usw. -->
<html lang="de" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<body>
<!-- ===== App-Sidebar (Dashboard-Shell). 'active' markiert den aktuellen Eintrag.
Die XP/Streak-Widgets erscheinen nur, wenn die Seite 'overall' (ProgressData) liefert. ===== -->
<aside class="mf-sidebar" th:fragment="sidebar(active)">
<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}" th:classappend="${active == 'dashboard'} ? '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')" th:classappend="${active == 'admin'} ? 'active'">
<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}" th:classappend="${active == 'profil'} ? 'active'">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4 3.6-6.5 8-6.5S20 17 20 21"/></svg>
Profil
</a>
<a th:href="@{/profile/knowledge}" th:classappend="${active == 'knowledge'} ? 'active'">
<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}" th:classappend="${active == 'statistiken'} ? 'active'">
<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}" th:classappend="${active == 'erfolge'} ? 'active'">
<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>
Erfolge
</a>
<a th:href="@{/profile/settings}" th:classappend="${active == 'einstellungen'} ? 'active'">
<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>
<!-- Profil-Shortcut (ohne Dropdown-Pfeil – es gibt kein Dropdown) -->
<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">Benutzer</b>
<span class="role" sec:authorize="hasRole('ADMIN')">Administrator</span>
<span class="role" sec:authorize="!hasRole('ADMIN')">Mitglied</span>
</div>
</div>
</a>
<div class="mf-meta" th:if="${overall != null}">
<div class="lvl">LEVEL <b th:text="${overall.userLevel}">1</b></div>
<div class="xp" th:text="${overall.currentXp} + ' / ' + ${overall.xpForNextLevel} + ' XP'">0 / 1000 XP</div>
<div class="mf-bar"><i th:style="'width:' + ${overall.progressPercent} + '%'"></i></div>
</div>
<div class="mf-streak" th:if="${overall != null}">
<span class="fire">🔥</span>
<span><b>STREAK</b><small th:text="${overall.streakDays} + (${overall.streakDays == 1} ? ' Tag' : ' Tage') + ' in Folge'">0 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>
<!-- ===== Profilbereich: Tab-Leiste, auf ALLEN Unterseiten identisch ===== -->
<nav class="mf-tab-nav" th:fragment="profileTabs(active)">
<a th:href="@{/profile}" class="mf-tab" th:classappend="${active == 'profil'} ? ' active'">Profil</a>
<a th:href="@{/progress}" class="mf-tab" th:classappend="${active == 'statistiken'} ? ' active'">Statistiken</a>
<a th:href="@{/profile/achievements}" class="mf-tab" th:classappend="${active == 'erfolge'} ? ' active'">Erfolge</a>
<a th:href="@{/soziales}" class="mf-tab" th:classappend="${active == 'soziales'} ? ' active'">Soziales</a>
<a th:href="@{/profile/settings}" class="mf-tab" th:classappend="${active == 'einstellungen'} ? ' active'">Einstellungen</a>
</nav>
<!-- ===== Footer ===== -->
<footer class="mf-footer" th:fragment="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>
+3 -1
View File
@@ -59,6 +59,8 @@
<div th:if="${param.registerError}" class="mf-alert err">Benutzername bereits vergeben.</div>
<div th:if="${param.passwordMismatch}" class="mf-alert err">Die Passwörter stimmen nicht überein. Bitte erneut eingeben.</div>
<div th:if="${param.passwordPolicy}" class="mf-alert err">Das Passwort muss mindestens 8 Zeichen lang sein.</div>
<div th:if="${param.emailInvalid}" class="mf-alert err">Bitte gib eine gültige E-Mail-Adresse an.</div>
<form th:action="@{/register}" method="post">
<div class="mf-field">
@@ -71,7 +73,7 @@
</div>
<div class="mf-field">
<label class="mf-label" for="reg-password">Passwort</label>
<input id="reg-password" class="mf-input" type="password" name="password" placeholder="Passwort" required>
<input id="reg-password" class="mf-input" type="password" name="password" placeholder="Passwort (min. 8 Zeichen)" required minlength="8">
</div>
<div class="mf-field">
<label class="mf-label" for="reg-password2">Passwort wiederholen</label>
-67
View File
@@ -1,67 +0,0 @@
<!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>
+3 -1
View File
@@ -18,7 +18,9 @@
<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>
<form th:action="@{/logout}" method="post" style="display:inline">
<button type="submit" class="mm-logout" style="background:none;border:0;cursor:pointer;font:inherit">Logout</button>
</form>
</div>
</header>
+1 -11
View File
@@ -7,10 +7,6 @@
<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; }
@@ -32,13 +28,7 @@
<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>
<nav th:replace="~{fragments/layout :: profileTabs('profil')}"></nav>
<!-- Erfolgsmeldung -->
<div th:if="${message}" class="mf-panel accent mf-success" id="successMessage">
+5 -3
View File
@@ -33,12 +33,14 @@
<div class="mf-container">
<div class="mf-page-head">
<a href="/profile" class="mf-back">⟵ Zurück zum Profil</a>
<a th:href="@{/dashboard}" class="mf-back">⟵ Zurück zum Dashboard</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>
<span class="mf-eyebrow">Deine Statistiken</span>
<h1 style="color:var(--heading); font-size:2.1rem; font-weight:800; margin:.3rem 0 .25rem;">Deine Statistiken</h1>
<p style="color:var(--muted); margin-bottom:1.4rem;">Bleib am Ball – jeder Schritt zählt!</p>
<nav th:replace="~{fragments/layout :: profileTabs('statistiken')}"></nav>
<!-- Gesamtfortschritt -->
<section class="mf-panel accent" style="margin-bottom:1.2rem;">
<div class="mf-progress-summary">
@@ -1,79 +0,0 @@
<!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">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body>
</html>
+22 -27
View File
@@ -23,50 +23,33 @@
<div class="mf-container">
<div class="mf-page-head">
<a th:href="@{/profile}" class="mf-back">⟵ Zurück zum Profil</a>
<a th:href="@{/dashboard}" class="mf-back">⟵ Zurück zum Dashboard</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>
<nav th:replace="~{fragments/layout :: profileTabs('einstellungen')}"></nav>
<!-- Flash-Meldungen (z. B. nach Passwortänderung) -->
<div th:if="${message}" class="mf-panel accent mf-success" style="margin-bottom:1.2rem;">
<p th:text="${message}" style="color:var(--accent); margin:0;"></p>
</div>
<div th:if="${error}" class="mf-panel mf-error" style="margin-bottom:1.2rem;">
<p th:text="${error}" style="color:#c0392b; margin:0;"></p>
<p th:text="${error}" style="color:var(--danger); margin:0;"></p>
</div>
<!-- Theme-Einstellung (Dummy) -->
<!-- Theme-Einstellung (an theme.js gekoppelt, wird in localStorage gespeichert) -->
<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>
<label class="mf-label" for="theme-select">Modus auswählen:</label>
<select class="mf-select" id="theme-select">
<option value="dark">Standard (Dark)</option>
<option value="light">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>
<small class="mf-hint">Die Auswahl gilt sofort und wird auf diesem Gerät gespeichert.</small>
</section>
<!-- Passwort ändern -->
@@ -88,6 +71,18 @@
</div>
<script>
// Theme-Select mit dem zentralen Umschalter (theme.js) koppeln.
window.addEventListener('DOMContentLoaded', function () {
var select = document.getElementById('theme-select');
if (!select || !window.MindfluxTheme) return;
select.value = window.MindfluxTheme.current() === 'light' ? 'light' : 'dark';
select.addEventListener('change', function () {
window.MindfluxTheme.set(select.value);
});
});
</script>
<footer class="mf-footer">
<a href="#">Support</a>
<a href="#">Feedback</a>
+4 -2
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="de" data-theme="dark">
<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">
@@ -21,9 +21,11 @@
<div class="mf-container">
<div class="mf-page-head">
<a href="/profile" class="mf-back">⟵ Zurück zum Profil</a>
<a th:href="@{/dashboard}" class="mf-back">⟵ Zurück zum Dashboard</a>
</div>
<nav th:replace="~{fragments/layout :: profileTabs('soziales')}"></nav>
<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>
+4 -2
View File
@@ -13,10 +13,12 @@
<div class="ss-shell">
<header class="ss-header">
<a href="/dashboard" class="ss-back">⟵ Dashboard</a>
<h1 class="ss-title">Syntax Saga</h1>
<h1 class="ss-title">SyntaxSaga</h1>
<div class="ss-header-tools">
<button type="button" class="mf-theme"></button>
<a href="/logout" class="ss-logout">Logout</a>
<form th:action="@{/logout}" method="post" style="display:inline">
<button type="submit" class="ss-logout" style="background:none;border:0;cursor:pointer;font:inherit">Logout</button>
</form>
</div>
</header>
@@ -16,7 +16,9 @@
<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>
<form th:action="@{/logout}" method="post" style="display:inline">
<button type="submit" class="tt-logout" style="background:none;border:0;cursor:pointer;font:inherit">Logout</button>
</form>
</div>
</header>
@@ -1,73 +0,0 @@
<!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">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body>
</html>
@@ -1,46 +0,0 @@
<!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">Nutzungsbedingungen</a>
<a href="/richtlinien">Richtlinien</a>
<a href="/datenschutz">Datenschutz</a>
<a href="/impressum">Impressum</a>
</footer>
</body>
</html>