made dashboard style consistent to landingpage and login, also added translation for english and german

This commit is contained in:
2026-04-05 02:18:31 +02:00
parent 19a6bbd392
commit aa569e6cff
5 changed files with 1490 additions and 537 deletions
+177 -110
View File
@@ -10,18 +10,39 @@
</head>
<body>
<nav class="dashboard-nav">
<a class="brand" href="{{ url_for('main.home') }}">
<span class="brand-mark">♛</span>
<span>8-Damen- und 8-Türme Solver</span>
</a>
<div class="nav-actions">
<a class="nav-link" href="{{ url_for('main.dashboard') }}" id="navDashboardLink">Dashboard</a>
<button class="nav-link-button lang-switch" id="langSwitch" type="button"
aria-label="Sprache wechseln">DE</button>
<a class="account-shortcut" href="{{ url_for('login.login_settings') }}"
aria-label="Konto und Login-Einstellungen öffnen">
<span class="account-avatar" aria-hidden="true">{{ current_user.username[:1]|upper }}</span>
<span class="account-copy">
<span class="account-label" id="accountLabel">Account</span>
<span class="account-name">{{ current_user.username }}</span>
</span>
</a>
<nav class="navbar">
<div class="nav-left">♛ 8-Queens App</div>
<div class="nav-right">
<a href="{{ url_for('main.dashboard') }}">Dashboard</a>
<a href="{{ url_for('login.login_settings') }}">Login settings</a>
<form action="{{ url_for('login.logout') }}" method="post" class="nav-logout-form">
<button class="nav-link-button" type="submit">Logout</button>
</form>
</div>
</nav>
<button class="settings-trigger" id="settingsTrigger" onclick="toggleSidebar()" type="button"
aria-controls="settingsSidebar" aria-expanded="false">
<span class="settings-trigger-arrow" id="settingsTriggerArrow">→</span>
<span class="settings-trigger-label" id="settingsTriggerLabel">Brett-Einstellungen</span>
</button>
<div class="container">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
@@ -33,116 +54,157 @@
{% endif %}
{% endwith %}
<div class="card">
<h2>👋 Willkommen, {{ current_user.username }}</h2>
<section class="dashboard-intro card">
<p class="section-kicker" id="introKicker">Puzzle-Modus</p>
<h1 id="introTitle">Wähl deinen Modus und löse das Brett auf deine Art.</h1>
<p class="dashboard-intro-text" id="introText">
Wechsle zwischen Damen und Türmen, nutze Animation oder Sofort-Lösung und öffne die
Brett-Einstellungen jederzeit über den linken Seiten-Tab.
</p>
<div class="user-info">
<p><strong>ID:</strong> {{ current_user.id }}</p>
<p><strong>Username:</strong> {{ current_user.username }}</p>
<p><strong>2FA:</strong> {% if current_user.two_factor_enabled %}Authenticator app enabled{% else %}Disabled{% endif %}</p>
<p><strong>Erstellt:</strong> {{ current_user.created_at }}</p>
<div class="mode-switch" aria-label="Spielmodus">
<button class="btn active" onclick="setMode('queens')" id="btnQueens" type="button">♛ Damen</button>
<button class="btn secondary" onclick="setMode('rooks')" id="btnRooks" type="button">♜ Tuerme</button>
</div>
<div class="inline-actions">
<a class="btn secondary" href="{{ url_for('login.login_settings') }}">Open login settings</a>
<form action="{{ url_for('login.logout') }}" method="post">
<button class="btn danger" type="submit">Logout</button>
</form>
</div>
</div>
<div class="mode-switch">
<button class="btn active" onclick="setMode('queens')" id="btnQueens">♛ Damen</button>
<button class="btn secondary" onclick="setMode('rooks')" id="btnRooks">♜ Türme</button>
<button class="btn secondary" onclick="toggleSidebar()" id="btnSidebar">Board Settings</button>
</div>
</section>
<div class="card game-card">
<div class="game-card-header">
<h2>♟️ Damen- und Tuerme-Spiel</h2>
<div class="game-card-copy">
<p class="section-kicker" id="gameModeEyebrow">Damen-Modus</p>
<h2 id="gameTitle">Damen-Spielbrett</h2>
<p class="board-mode-text" id="boardModeText">
Platziere Damen so, dass keine zwei dieselbe Spalte oder Diagonale teilen.
</p>
</div>
<button class="info-button" id="infoButton" onclick="toggleInfoModal(true)" type="button"
aria-label="Spielanleitung und Erklaerung anzeigen">i</button>
aria-label="Spielregeln und Hilfe anzeigen">Regeln & Hilfe</button>
</div>
<p class="board-size-info">Aktuelle Brettgroesse: <span id="boardSizeValue">8</span> x <span
id="boardSizeValueMirror">8</span></p>
<p class="board-size-info" id="boardSizeInfo"><span id="boardSizeInfoPrefix">Aktuelle Brettgrösse im
Damen-Modus:</span>
<span id="boardSizeValue">8</span> x <span id="boardSizeValueMirror">8</span>
</p>
<div class="game-stats" aria-label="Spielstatus">
<article class="game-stat-card">
<p class="game-stat-label">Schritte</p>
<p class="game-stat-label" id="stepsLabel">Schritte</p>
<p class="game-stat-value" id="stepCounterValue">0</p>
</article>
<article class="game-stat-card">
<p class="game-stat-label">Zeit</p>
<p class="game-stat-label" id="timeLabel">Zeit</p>
<p class="game-stat-value" id="timerValue">00:00:00</p>
</article>
<article class="game-stat-card">
<p class="game-stat-label">Status</p>
<p class="game-stat-value game-stat-status" id="gameProgressState">Laeuft</p>
<p class="game-stat-label" id="statusLabel">Status</p>
<p class="game-stat-value game-stat-status" id="gameProgressState">Läuft</p>
</article>
</div>
<p id="statusMessage"></p>
<div class="solved-celebration" id="solvedCelebration" aria-live="polite" hidden>
<p class="solved-celebration-title" id="solvedCelebrationTitle">Richtig gelöst!</p>
<p class="solved-celebration-text" id="solvedCelebrationText">Stark gemacht. Das Brett ist konfliktfrei.
</p>
</div>
<div class="board" id="board"></div>
<div class="controls">
<button class="btn secondary" onclick="resetBoard()" id="btnReset" type="button">Brett
zuruecksetzen</button>
<div class="controls-save-row">
<button class="btn" onclick="openSavePanel()" id="btnSave" type="button">Speichern</button>
<button class="btn secondary" onclick="openSavePointsPanel()" id="btnLoad"
type="button">Save-Points</button>
</div>
</div>
<div class="solver-panel">
<div class="solver-panel-header">
<div>
<h3>Solver-Ablauf</h3>
<p class="solver-message" id="solverMessage">Bereite einen Ablauf vor und spiele ihn Schritt fuer Schritt ab.</p>
<p class="section-kicker" id="solverKicker">Solver</p>
<h3 id="solverTitle">Lösungswerkzeuge</h3>
<p class="solver-message" id="solverMessage">Bereite einen Ablauf vor und spiele ihn Schritt
für Schritt ab.</p>
</div>
<p class="solver-step-info" id="solverStepInfo">Schritt 0 / 0</p>
</div>
<div class="solver-controls">
<button class="btn" onclick="prepareSolutionTrace()" id="btnPrepareSolve" type="button">Solve Step-by-Step</button>
<button class="btn secondary" onclick="instantSolve()" id="btnInstantSolve" type="button">Instant Solve</button>
<button class="btn secondary" onclick="nextSolverStep()" id="btnNextStep" type="button">Next Step</button>
<button class="btn secondary" onclick="toggleSolverPlayback()" id="btnPlayPause" type="button">Play Animation</button>
<div class="solver-groups">
<section class="solver-group">
<h4 id="animationSolverTitle">Animations-Solver</h4>
<p class="solver-group-text" id="animationSolverText">
Bereite den Lösungsweg vor, gehe ihn manüll Schritt für Schritt durch oder spiele die
Animation automatisch ab.
</p>
<div class="solver-controls">
<button class="btn" onclick="prepareSolutionTrace()" id="btnPrepareSolve"
type="button">Animation vorbereiten</button>
<button class="btn secondary" onclick="nextSolverStep()" id="btnNextStep"
type="button">Nächster Schritt</button>
<button class="btn secondary" onclick="toggleSolverPlayback()" id="btnPlayPause"
type="button">Animation abspielen</button>
</div>
<div class="solver-speed">
<label class="solver-speed-label" id="solverSpeedLabel"
for="solverSpeedInput">Animationsgeschwindigkeit</label>
<input id="solverSpeedInput" type="range" min="80" max="900" step="20" value="320"
oninput="updateSolverSpeed(this.value)">
<span class="solver-speed-value" id="solverSpeedValue">320 ms</span>
</div>
</section>
<section class="solver-group instant-solver-group">
<h4 id="instantSolverTitle">Sofort-Lösung</h4>
<p class="solver-group-text" id="instantSolverText">
Lade direkt eine gültige Lösung, ohne Zwischenschritte oder Animation.
</p>
<div class="instant-solver-actions">
<button class="btn accent" onclick="instantSolve()" id="btnInstantSolve"
type="button">Sofort lösen</button>
</div>
</section>
</div>
<div class="solver-speed">
<label class="solver-speed-label" for="solverSpeedInput">Speed</label>
<input id="solverSpeedInput" type="range" min="80" max="900" step="20" value="320"
oninput="updateSolverSpeed(this.value)">
<span class="solver-speed-value" id="solverSpeedValue">320 ms</span>
</div>
</div>
<div class="controls">
<button class="btn" onclick="checkSolution()">Check</button>
<button class="btn secondary" onclick="resetBoard()">Reset</button>
<button class="btn" onclick="openSavePanel()">💾 Save</button>
<button class="btn" onclick="openSavePointsPanel()">📂 Load</button>
</div>
</div>
<div class="card">
<h3>📋 Figuren Positionen</h3>
<div class="card piece-card">
<div class="section-header">
<h3 id="pieceListTitle">Positionen der Damen</h3>
<span class="section-badge" id="pieceCountBadge">0 gesetzt</span>
</div>
<ul id="queenList"></ul>
</div>
<div class="card">
<div class="history-header">
<div>
<h3>⏱️ Bisherige Zeiten</h3>
<p class="history-subtitle">Die Tabelle zeigt gespeicherte Save-Points mit Zeit und Schritten.</p>
<p class="section-kicker" id="historyKicker">Save-Points</p>
<h3 id="historyTitle">Gespeicherte Zeiten und Fortschritte</h3>
<p class="history-subtitle" id="historySubtitle">Die Tabelle zeigt gespeicherte Save-Points mit
Zeit, Schritten und
Status.</p>
</div>
<button class="btn secondary" onclick="refreshTimeHistory()" type="button">Aktualisieren</button>
<button class="btn secondary" onclick="refreshTimeHistory()" id="btnRefreshHistory"
type="button">Aktualisieren</button>
</div>
<div class="history-table-wrapper">
<table class="history-table">
<thead>
<tr>
<th>Save-Point</th>
<th>Modus</th>
<th>Brett</th>
<th>Schritte</th>
<th>Zeit</th>
<th>Status</th>
<th>Gespeichert</th>
<th id="historyHeadSave">Save-Point</th>
<th id="historyHeadMode">Modus</th>
<th id="historyHeadBoard">Brett</th>
<th id="historyHeadSteps">Schritte</th>
<th id="historyHeadTime">Zeit</th>
<th id="historyHeadStatus">Status</th>
<th id="historyHeadSaved">Gespeichert</th>
</tr>
</thead>
<tbody id="timeHistoryTableBody">
@@ -155,64 +217,69 @@
</div>
</div>
<aside class="sidebar" id="settingsSidebar" aria-hidden="true">
<aside class="sidebar settings-sidebar" id="settingsSidebar" aria-hidden="true">
<div class="sidebar-header">
<h3>Brettgroesse</h3>
<button class="btn secondary sidebar-close" onclick="toggleSidebar(false)" type="button">Schliessen</button>
<h3 id="settingsSidebarTitle">Brett-Einstellungen</h3>
<button class="btn secondary sidebar-close" onclick="toggleSidebar(false)" id="settingsSidebarClose"
type="button">Schliessen</button>
</div>
<p>Wähle eine Grösse zwischen 4 und 13.</p>
<p id="settingsSidebarText">Passe die Brettgrösse zwischen 4 und 13 an.</p>
<label class="sidebar-label" for="boardSizeInput">Groesse</label>
<label class="sidebar-label" id="boardSizeInputLabel" for="boardSizeInput">Grösse</label>
<input id="boardSizeInput" type="range" min="4" max="13" value="8" oninput="previewBoardSize(this.value)">
<div class="sidebar-size-row">
<strong><span id="sidebarBoardSizeValue">8</span> x <span id="sidebarBoardSizeValueMirror">8</span></strong>
</div>
<button class="btn" onclick="applyBoardSize()" type="button">Apply Size</button>
<p class="sidebar-hint">Das Brett wird beim Ändern zur Sicherheit zurückgesetzt.</p>
<button class="btn" onclick="applyBoardSize()" id="applyBoardSizeButton" type="button">Grösse anwenden</button>
<p class="sidebar-hint" id="settingsSidebarHint">Beim Ändern wird das Brett zur Sicherheit zurückgesetzt. Auf
dem Handy ist 8x8 eingeplant, grössere Bretter können abgeschnitten wirken.</p>
</aside>
<aside class="sidebar save-sidebar" id="saveSidebar" aria-hidden="true">
<div class="sidebar-header">
<h3>Speicherpunkt</h3>
<button class="btn secondary sidebar-close" onclick="toggleSavePanel(false)" type="button">Schliessen</button>
<h3 id="saveSidebarTitle">Speicherpunkt</h3>
<button class="btn secondary sidebar-close" onclick="toggleSavePanel(false)" id="saveSidebarClose"
type="button">Schliessen</button>
</div>
<p>Gib dem Spielstand einen klaren Namen und optional eine kurze Notiz.</p>
<p id="saveSidebarText">Gib dem Spielstand einen klaren Namen und optional eine kurze Notiz.</p>
<label class="sidebar-label" for="saveNameInput">Name</label>
<input class="sidebar-input" id="saveNameInput" type="text" maxlength="80" placeholder="z. B. Fast geloeste Dame 8x8">
<label class="sidebar-label" id="saveNameLabel" for="saveNameInput">Name</label>
<input class="sidebar-input" id="saveNameInput" type="text" maxlength="80"
placeholder="z. B. Fast gelöste Dame 8x8">
<label class="sidebar-label" for="saveNoteInput">Notiz</label>
<label class="sidebar-label" id="saveNoteLabel" for="saveNoteInput">Notiz</label>
<textarea class="sidebar-input sidebar-textarea" id="saveNoteInput" rows="4" maxlength="240"
placeholder="z. B. Gute Startposition fuer Tuermetest"></textarea>
placeholder="z. B. Gute Startposition für Türmetest"></textarea>
<label class="sidebar-checkbox">
<input id="saveFavoriteInput" type="checkbox">
Als Favorit markieren
<span id="saveFavoriteLabel">Als Favorit markieren</span>
</label>
<button class="btn" onclick="saveGame()" type="button">Jetzt speichern</button>
<button class="btn" onclick="saveGame()" id="saveNowButton" type="button">Jetzt speichern</button>
</aside>
<aside class="sidebar save-points-sidebar" id="savePointsSidebar" aria-hidden="true">
<div class="sidebar-header">
<h3>Save Points</h3>
<button class="btn secondary sidebar-close" onclick="toggleSavePointsPanel(false)" type="button">Schliessen</button>
<h3 id="savePointsSidebarTitle">Save-Points</h3>
<button class="btn secondary sidebar-close" onclick="toggleSavePointsPanel(false)"
id="savePointsSidebarClose" type="button">Schliessen</button>
</div>
<p>Waehle einen Speicherstand aus der Liste.</p>
<p id="savePointsSidebarText">Wähle einen Speicherstand aus der Liste.</p>
<div class="save-point-filters">
<label class="sidebar-label" for="savePointModeFilter">Modus</label>
<label class="sidebar-label" id="savePointModeFilterLabel" for="savePointModeFilter">Modus</label>
<select class="sidebar-input" id="savePointModeFilter" onchange="refreshSavePoints()">
<option value="all">Alle</option>
<option value="queens">Damen</option>
<option value="rooks">Tuerme</option>
<option value="rooks">Türme</option>
</select>
<label class="sidebar-label" for="savePointSizeFilter">Brettgroesse</label>
<label class="sidebar-label" id="savePointSizeFilterLabel" for="savePointSizeFilter">Brettgrösse</label>
<select class="sidebar-input" id="savePointSizeFilter" onchange="refreshSavePoints()">
<option value="all">Alle</option>
{% for board_size in range(4, 14) %}
@@ -220,22 +287,22 @@
{% endfor %}
</select>
<label class="sidebar-label" for="savePointSortFilter">Sortierung</label>
<label class="sidebar-label" id="savePointSortFilterLabel" for="savePointSortFilter">Sortierung</label>
<select class="sidebar-input" id="savePointSortFilter" onchange="refreshSavePoints()">
<option value="newest">Neueste zuerst</option>
<option value="oldest">Aelteste zuerst</option>
<option value="oldest">älteste zuerst</option>
<option value="updated">Zuletzt bearbeitet</option>
<option value="favorites">Favoriten zuerst</option>
</select>
<label class="sidebar-checkbox">
<input id="savePointFavoritesOnly" type="checkbox" onchange="refreshSavePoints()">
Nur Favoriten anzeigen
<span id="savePointFavoritesOnlyLabel">Nur Favoriten anzeigen</span>
</label>
</div>
<div class="save-points-list" id="savePointsList">
<p class="sidebar-hint">Noch keine Speicherstaende gefunden.</p>
<p class="sidebar-hint" id="savePointsEmptyState">Noch keine Speicherstände gefunden.</p>
</div>
</aside>
@@ -246,31 +313,31 @@
<div class="modal-backdrop" id="infoModalBackdrop" onclick="toggleInfoModal(false)"></div>
<section class="modal" id="infoModal" aria-hidden="true" aria-labelledby="infoModalTitle">
<div class="modal-header">
<h3 id="infoModalTitle">Spielanleitung und Acht-Damen-Problem</h3>
<button class="btn secondary sidebar-close" onclick="toggleInfoModal(false)" type="button">Schliessen</button>
<h3 id="infoModalTitle">Spielregeln und Acht-Damen-Problem</h3>
<button class="btn secondary sidebar-close" onclick="toggleInfoModal(false)"
type="button">Schliessen</button>
</div>
<div class="modal-content">
<p>Setze pro Reihe genau eine Figur auf das Brett. Eine gueltige Loesung hat keine Konflikte.</p>
<p>Setze pro Reihe genau eine Figur auf das Brett. Eine gültige Lösung hat keine Konflikte.</p>
<h4>So funktioniert das Spiel</h4>
<p>Im Damen-Modus duerfen sich Damen weder in derselben Spalte noch diagonal angreifen. Im Tuerme-Modus
zaehlen nur Spalten-Konflikte. Mit einem Klick setzt oder entfernst du eine Figur.</p>
<h4>So funktioniert der aktuelle Modus</h4>
<p id="infoModeRuleText">Im Damen-Modus dürfen sich Damen weder in derselben Spalte noch diagonal
angreifen. Mit einem Klick setzt oder entfernst du eine Figur.</p>
<h4>Was ist das Acht-Damen-Problem?</h4>
<p>Das klassische Acht-Damen-Problem fragt nach einer Anordnung von acht Damen auf einem 8x8-Brett, bei
der sich keine zwei Damen schlagen. Es ist ein bekanntes Beispiel fuer kombinatorische Suche und
Backtracking.</p>
<h4 id="infoModalProblemHeading">Was ist das Acht-Damen-Problem?</h4>
<p id="infoModalProblemText">Das klassische Acht-Damen-Problem fragt nach einer Anordnung von acht Damen
auf einem 8x8-Brett, bei der sich keine zwei Damen schlagen. Es ist ein bekanntes Beispiel für
kombinatorische Suche und Backtracking.</p>
<h4>Was zeigen Zeit und Schritte?</h4>
<p>Der Schrittzaehler erfasst deine Zuege auf dem Brett. Die Zeit laeuft fuer das aktuelle Spiel weiter,
auch wenn du die Seite neu laedst. Beim Speichern werden Zeit und Schritte zusammen mit dem Save-Point
abgelegt.</p>
<p>Der Schrittzähler erfasst deine Züge auf dem Brett. Die Zeit läuft für das aktuelle Spiel weiter,
auch wenn du die Seite neu lädst. Beim Speichern werden Zeit und Schritte zusammen mit dem
Save-Point abgelegt.</p>
</div>
</section>
<script src="{{ url_for('static', filename='js/dashboard.js') }}"></script>
</body>
</html>
</html>