382 lines
20 KiB
HTML
382 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title data-i18n="pageTitle">Dashboard</title>
|
|
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/dashboard.css') }}">
|
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/footer.css') }}">
|
|
</head>
|
|
|
|
<body>
|
|
<nav class="dashboard-nav">
|
|
<a class="brand" href="{{ url_for('main.home') }}">
|
|
<span class="brand-mark">♛</span>
|
|
<span data-i18n="navBrand">8-Damen- und 8-Türme Solver</span>
|
|
</a>
|
|
|
|
<div class="nav-actions">
|
|
<a class="nav-link" href="{{ url_for('main.dashboard') }}" id="navDashboardLink"
|
|
data-i18n="navDashboard">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') }}"
|
|
data-i18n-aria-label="accountShortcutAria" 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" data-i18n="accountLabel">Account</span>
|
|
<span class="account-name">{{ current_user.username }}</span>
|
|
</span>
|
|
</a>
|
|
|
|
<form action="{{ url_for('login.logout') }}" method="post" class="nav-logout-form">
|
|
<button class="nav-link-button" type="submit" data-i18n="navLogout">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">Spiel-Einstellungen</span>
|
|
</button>
|
|
|
|
<div class="container">
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
{% if messages %}
|
|
<div class="flash-stack">
|
|
{% for category, message in messages %}
|
|
<p class="flash-message {{ category }}">{{ message }}</p>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
{% endwith %}
|
|
|
|
<section class="dashboard-intro card">
|
|
<p class="section-kicker" id="introKicker" data-i18n="introKicker">Puzzle-Modus</p>
|
|
<h1 id="introTitle" data-i18n="introTitle">Wähl deinen Modus und löse das Brett auf deine Art.</h1>
|
|
<p class="dashboard-intro-text" id="introText" data-i18n="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="mode-switch" data-i18n-aria-label="modeSwitchAria" aria-label="Spielmodus">
|
|
<button class="btn secondary" onclick="setMode('rooks')" id="btnRooks" type="button">
|
|
<span aria-hidden="true">♜</span>
|
|
<span data-i18n="btnRooks">Türme</span>
|
|
</button>
|
|
<button class="btn active" onclick="setMode('queens')" id="btnQueens" type="button">
|
|
<span aria-hidden="true">♛</span>
|
|
<span data-i18n="btnQueens">Damen</span>
|
|
</button>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="card game-card">
|
|
<div class="game-card-header">
|
|
<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 frei auf dem Brett. Konflikte entstehen in derselben Reihe, Spalte oder
|
|
Diagonale.
|
|
</p>
|
|
</div>
|
|
|
|
<button class="info-button" id="infoButton" onclick="toggleInfoModal(true)" type="button"
|
|
data-i18n="infoButton" data-i18n-aria-label="infoButton"
|
|
aria-label="Spielregeln und Hilfe anzeigen">Spielanleitung</button>
|
|
</div>
|
|
|
|
<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" data-i18n-aria-label="gameStatsAria" aria-label="Spielstatus">
|
|
<article class="game-stat-card">
|
|
<p class="game-stat-label" id="stepsLabel" data-i18n="stepsLabel">Schritte</p>
|
|
<p class="game-stat-value" id="stepCounterValue">0</p>
|
|
</article>
|
|
<article class="game-stat-card">
|
|
<p class="game-stat-label" id="timeLabel" data-i18n="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" id="statusLabel" data-i18n="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-stage">
|
|
<div class="board-shell">
|
|
<div id="board"></div>
|
|
</div>
|
|
|
|
<div class="board-navigator" id="boardNavigator" hidden>
|
|
<button class="btn secondary board-nav-button" onclick="previousSolverStep()" id="btnPrevStep"
|
|
type="button" data-i18n="btnPrevStep">
|
|
Zurück
|
|
</button>
|
|
<p class="solver-step-info" id="solverStepInfo">Schritt 0 / 0</p>
|
|
<button class="btn secondary board-nav-button" onclick="nextSolverStep()" id="btnNextStep"
|
|
type="button" data-i18n="btnNextStep">
|
|
Vorwärts
|
|
</button>
|
|
</div>
|
|
|
|
<p class="solver-message solver-message-inline" id="solverMessage">Lade eine Animation und steuere sie
|
|
am Brett oder spiele sie automatisch ab.</p>
|
|
</div>
|
|
|
|
<div class="controls">
|
|
<button class="btn secondary" onclick="resetBoard()" id="btnReset" type="button"
|
|
data-i18n="btnReset">Brett
|
|
zurücksetzen</button>
|
|
</div>
|
|
</div>
|
|
|
|
<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>
|
|
<p class="section-kicker" id="historyKicker" data-i18n="historyKicker">Save-Points</p>
|
|
<h3 id="historyTitle" data-i18n="historyTitle">Gespeicherte Zeiten und Fortschritte</h3>
|
|
<p class="history-subtitle" id="historySubtitle" data-i18n="historySubtitle">Die Tabelle zeigt
|
|
gespeicherte Save-Points mit
|
|
Zeit, Schritten und
|
|
Status.</p>
|
|
</div>
|
|
<button class="btn secondary" onclick="refreshTimeHistory()" id="btnRefreshHistory" type="button"
|
|
data-i18n="btnRefreshHistory">Aktualisieren</button>
|
|
</div>
|
|
|
|
<div class="history-table-wrapper">
|
|
<table class="history-table">
|
|
<thead>
|
|
<tr>
|
|
<th id="historyHeadSave" data-i18n="historyHeadSave">Save-Point</th>
|
|
<th id="historyHeadMode" data-i18n="historyHeadMode">Modus</th>
|
|
<th id="historyHeadBoard" data-i18n="historyHeadBoard">Brett</th>
|
|
<th id="historyHeadSteps" data-i18n="historyHeadSteps">Schritte</th>
|
|
<th id="historyHeadTime" data-i18n="historyHeadTime">Zeit</th>
|
|
<th id="historyHeadStatus" data-i18n="historyHeadStatus">Status</th>
|
|
<th id="historyHeadSaved" data-i18n="historyHeadSaved">Gespeichert</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="timeHistoryTableBody">
|
|
<tr>
|
|
<td colspan="7" class="history-empty" data-i18n="historyEmpty">Noch keine gespeicherten
|
|
Zeiten vorhanden.</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% include "partials/footer.html" %}
|
|
|
|
<aside class="sidebar settings-sidebar" id="settingsSidebar" aria-hidden="true">
|
|
<div class="sidebar-header">
|
|
<h3 id="settingsSidebarTitle" data-i18n="settingsSidebarTitle">Spiel-Einstellungen</h3>
|
|
<button class="btn secondary sidebar-close" onclick="toggleSidebar(false)" id="settingsSidebarClose"
|
|
type="button" data-i18n="settingsSidebarClose">Schliessen</button>
|
|
</div>
|
|
|
|
<section class="settings-section">
|
|
<p class="section-kicker" id="boardSettingsKicker" data-i18n="boardSettingsKicker">Brett</p>
|
|
<h4 id="boardSettingsTitle" data-i18n="boardSettingsTitle">Brettgrösse</h4>
|
|
<p id="settingsSidebarText" data-i18n="settingsSidebarText">Passe die Brettgrösse zwischen 4 und 13 an.</p>
|
|
|
|
<label class="sidebar-label" id="boardSizeInputLabel" for="boardSizeInput"
|
|
data-i18n="boardSizeInputLabel">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()" id="applyBoardSizeButton" type="button"
|
|
data-i18n="applyBoardSizeButton">Grösse anwenden</button>
|
|
<p class="sidebar-hint" id="settingsSidebarHint" data-i18n="settingsSidebarHint">Beim Ändern wird das Brett
|
|
zur Sicherheit zurückgesetzt. Auf
|
|
dem Handy ist 8x8 eingeplant, grössere Bretter können abgeschnitten wirken.</p>
|
|
</section>
|
|
|
|
<section class="settings-section">
|
|
<p class="section-kicker" id="saveSettingsKicker" data-i18n="saveSettingsKicker">Speichern</p>
|
|
<h4 id="saveSettingsTitle" data-i18n="saveSettingsTitle">Fortschritt speichern</h4>
|
|
<p id="saveSettingsText" data-i18n="saveSettingsText">Öffne hier die Optionen zum Speichern oder Laden
|
|
deiner Save-Points.</p>
|
|
|
|
<div class="settings-action-grid">
|
|
<button class="btn" onclick="openSavePanel()" id="btnSave" type="button"
|
|
data-i18n="btnSave">Speichern</button>
|
|
<button class="btn secondary" onclick="openSavePointsPanel()" id="btnLoad" type="button"
|
|
data-i18n="btnLoad">Save-Points</button>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="settings-section">
|
|
<p class="section-kicker" id="solutionSettingsKicker" data-i18n="solutionSettingsKicker">Lösung</p>
|
|
<h4 id="solutionSettingsTitle" data-i18n="solutionSettingsTitle">Lösungswerkzeuge</h4>
|
|
<p id="solutionSettingsText" data-i18n="solutionSettingsText">Lade eine Animation, steuere sie am Brett mit
|
|
Vorwärts und Zurück oder spiele sie automatisch ab.</p>
|
|
|
|
<div class="settings-action-grid">
|
|
<button class="btn" onclick="prepareSolutionTrace()" id="btnPrepareSolve" type="button"
|
|
data-i18n="btnPrepareSolve">Animation laden</button>
|
|
<button class="btn secondary" onclick="toggleSolverPlayback()" id="btnPlayPause" type="button"
|
|
data-i18n="btnPlayPause">Animation abspielen</button>
|
|
<button class="btn accent" onclick="instantSolve()" id="btnInstantSolve" type="button"
|
|
data-i18n="btnInstantSolve">Sofort lösen</button>
|
|
</div>
|
|
|
|
<div class="solver-speed">
|
|
<label class="solver-speed-label" id="solverSpeedLabel" for="solverSpeedInput"
|
|
data-i18n="solverSpeedLabel">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>
|
|
</aside>
|
|
|
|
<aside class="sidebar save-sidebar" id="saveSidebar" aria-hidden="true">
|
|
<div class="sidebar-header">
|
|
<h3 id="saveSidebarTitle" data-i18n="saveSidebarTitle">Speicherpunkt</h3>
|
|
<button class="btn secondary sidebar-close" onclick="toggleSavePanel(false)" id="saveSidebarClose"
|
|
type="button" data-i18n="saveSidebarClose">Schliessen</button>
|
|
</div>
|
|
|
|
<p id="saveSidebarText" data-i18n="saveSidebarText">Gib dem Spielstand einen klaren Namen und optional eine
|
|
kurze Notiz.</p>
|
|
|
|
<label class="sidebar-label" id="saveNameLabel" for="saveNameInput" data-i18n="saveNameLabel">Name</label>
|
|
<input class="sidebar-input" id="saveNameInput" type="text" maxlength="80"
|
|
placeholder="z. B. Fast gelöste Dame 8x8" data-i18n-placeholder="saveNamePlaceholder">
|
|
|
|
<label class="sidebar-label" id="saveNoteLabel" for="saveNoteInput" data-i18n="saveNoteLabel">Notiz</label>
|
|
<textarea class="sidebar-input sidebar-textarea" id="saveNoteInput" rows="4" maxlength="240"
|
|
placeholder="z. B. Gute Startposition für Türmetest" data-i18n-placeholder="saveNotePlaceholder"></textarea>
|
|
|
|
<label class="sidebar-checkbox">
|
|
<input id="saveFavoriteInput" type="checkbox">
|
|
<span id="saveFavoriteLabel" data-i18n="saveFavoriteLabel">Als Favorit markieren</span>
|
|
</label>
|
|
|
|
<button class="btn" onclick="saveGame()" id="saveNowButton" type="button" data-i18n="saveNowButton">Jetzt
|
|
speichern</button>
|
|
</aside>
|
|
|
|
<aside class="sidebar save-points-sidebar" id="savePointsSidebar" aria-hidden="true">
|
|
<div class="sidebar-header">
|
|
<h3 id="savePointsSidebarTitle" data-i18n="savePointsSidebarTitle">Save-Points</h3>
|
|
<button class="btn secondary sidebar-close" onclick="toggleSavePointsPanel(false)"
|
|
id="savePointsSidebarClose" type="button" data-i18n="savePointsSidebarClose">Schliessen</button>
|
|
</div>
|
|
|
|
<p id="savePointsSidebarText" data-i18n="savePointsSidebarText">Wähle einen Speicherstand aus der Liste.</p>
|
|
|
|
<div class="save-point-filters">
|
|
<label class="sidebar-label" id="savePointModeFilterLabel" for="savePointModeFilter"
|
|
data-i18n="savePointModeFilterLabel">Modus</label>
|
|
<select class="sidebar-input" id="savePointModeFilter" onchange="refreshSavePoints()">
|
|
<option value="all" data-i18n="filterAll">Alle</option>
|
|
<option value="queens" data-i18n="filterQueens">Damen</option>
|
|
<option value="rooks" data-i18n="filterRooks">Türme</option>
|
|
</select>
|
|
|
|
<label class="sidebar-label" id="savePointSizeFilterLabel" for="savePointSizeFilter"
|
|
data-i18n="savePointSizeFilterLabel">Brettgrösse</label>
|
|
<select class="sidebar-input" id="savePointSizeFilter" onchange="refreshSavePoints()">
|
|
<option value="all" data-i18n="filterAll">Alle</option>
|
|
{% for board_size in range(4, 14) %}
|
|
<option value="{{ board_size }}">{{ board_size }} x {{ board_size }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<label class="sidebar-label" id="savePointSortFilterLabel" for="savePointSortFilter"
|
|
data-i18n="savePointSortFilterLabel">Sortierung</label>
|
|
<select class="sidebar-input" id="savePointSortFilter" onchange="refreshSavePoints()">
|
|
<option value="newest" data-i18n="filterNewest">Neueste zuerst</option>
|
|
<option value="oldest" data-i18n="filterOldest">älteste zuerst</option>
|
|
<option value="updated" data-i18n="filterUpdated">Zuletzt bearbeitet</option>
|
|
<option value="favorites" data-i18n="filterFavorites">Favoriten zuerst</option>
|
|
</select>
|
|
|
|
<label class="sidebar-checkbox">
|
|
<input id="savePointFavoritesOnly" type="checkbox" onchange="refreshSavePoints()">
|
|
<span id="savePointFavoritesOnlyLabel" data-i18n="savePointFavoritesOnlyLabel">Nur Favoriten
|
|
anzeigen</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="save-points-list" id="savePointsList">
|
|
<p class="sidebar-hint" id="savePointsEmptyState" data-i18n="savePointsEmptyState">Noch keine Speicherstände
|
|
gefunden.</p>
|
|
</div>
|
|
</aside>
|
|
|
|
<div class="sidebar-backdrop" id="sidebarBackdrop" onclick="toggleSidebar(false)"></div>
|
|
<div class="sidebar-backdrop" id="saveBackdrop" onclick="toggleSavePanel(false)"></div>
|
|
<div class="sidebar-backdrop" id="savePointsBackdrop" onclick="toggleSavePointsPanel(false)"></div>
|
|
|
|
<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</h3>
|
|
<button class="btn secondary sidebar-close" onclick="toggleInfoModal(false)" type="button"
|
|
data-i18n="infoModalClose">Schliessen</button>
|
|
</div>
|
|
|
|
<div class="modal-content">
|
|
<p data-i18n="infoModalIntro">Das Schachbrett besteht aus vielen quadratischen Feldern. Jedes Feld kann
|
|
leer sein oder genau eine Figur enthalten. Deine Aufgabe ist es, Figuren auf das Brett zu setzen,
|
|
ohne dass sie sich gegenseitig angreifen. Das Puzzle ist erst gelöst, wenn genau so viele Figuren auf
|
|
dem Brett stehen wie das Brett Felder pro Seite hat, also zum Beispiel 8 Figuren auf einem
|
|
8x8-Brett.</p>
|
|
|
|
<h4 data-i18n="infoModalModeHeading">Grundregeln</h4>
|
|
<p id="infoModeRuleText">In diesem Modus spielst du mit Damen. Eine Dame ist die Figur mit dem Symbol ♛.
|
|
Sie darf keine andere Dame in derselben waagerechten Reihe, senkrechten Spalte oder schrägen
|
|
Diagonale treffen. Mit einem Klick setzt du eine Dame auf ein freies Feld oder entfernst sie wieder.</p>
|
|
|
|
<h4 id="infoModalProblemHeading">Was ist eine Dame?</h4>
|
|
<p id="infoModalProblemText">Eine Dame ist eine Schachfigur. In diesem Spiel musst du keine Schachregeln
|
|
lernen, sondern nur wissen: Eine Dame kann gerade und schräg über das Brett wirken. Darum müssen
|
|
Damen genug Abstand haben, damit sie sich nicht gegenseitig erreichen.</p>
|
|
|
|
<h4 data-i18n="infoModalTimeHeading">Was zeigen Zeit und Schritte?</h4>
|
|
<p data-i18n="infoModalTimeText">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/language.js') }}" defer></script>
|
|
<script src="{{ url_for('static', filename='js/dashboard.js') }}" defer></script>
|
|
</body>
|
|
|
|
</html>
|