UI: move backup and migration actions into a sidebar tools menu

This commit is contained in:
Ponte
2026-02-11 15:20:24 +01:00
parent 110a6c4a1b
commit e2a7f0fdd9
4 changed files with 108 additions and 12 deletions

View File

@@ -144,6 +144,7 @@ git pull
- GET `/api/backup/export`
- POST `/api/backup/restore` (modes `merge` ou `replace`)
- snapshot auto en base avant restore `replace` (`backup_snapshots`)
- actions accessibles dans le panneau lateral `Outils`
## Licence

30
app.js
View File

@@ -32,6 +32,9 @@ const brandTabs = document.getElementById("brandTabs");
const consoleTabs = document.getElementById("consoleTabs");
const gameSectionTitle = document.getElementById("gameSectionTitle");
const dataModeInfo = document.getElementById("dataModeInfo");
const toolsDrawer = document.getElementById("toolsDrawer");
const toolsToggleBtn = document.getElementById("toolsToggleBtn");
const toolsCloseBtn = document.getElementById("toolsCloseBtn");
const migrateBtn = document.getElementById("migrateBtn");
const backupControls = document.getElementById("backupControls");
const backupBtn = document.getElementById("backupBtn");
@@ -43,6 +46,33 @@ const gameCardTemplate = document.getElementById("gameCardTemplate");
let editingGameId = null;
let pendingRestoreMode = "merge";
toolsToggleBtn.addEventListener("click", () => {
toolsDrawer.classList.toggle("open");
});
toolsCloseBtn.addEventListener("click", () => {
toolsDrawer.classList.remove("open");
});
document.addEventListener("click", (event) => {
if (!(event.target instanceof Element)) {
return;
}
if (!toolsDrawer.classList.contains("open")) {
return;
}
if (toolsDrawer.contains(event.target) || toolsToggleBtn.contains(event.target)) {
return;
}
toolsDrawer.classList.remove("open");
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
toolsDrawer.classList.remove("open");
}
});
platformForm.addEventListener("submit", async (event) => {
event.preventDefault();

View File

@@ -13,6 +13,24 @@
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<button id="toolsToggleBtn" type="button" class="tools-toggle-btn">Outils</button>
<aside id="toolsDrawer" class="tools-drawer" aria-label="Menu outils">
<div class="tools-header">
<h2>Outils</h2>
<button id="toolsCloseBtn" type="button" class="btn-secondary tools-close-btn">Fermer</button>
</div>
<p class="tools-subtitle">Sauvegarde, restauration et migration.</p>
<button id="migrateBtn" type="button" class="btn-secondary hidden">
Migrer localStorage vers DB
</button>
<div id="backupControls" class="backup-controls hidden">
<button id="backupBtn" type="button" class="btn-secondary">Sauvegarder JSON</button>
<button id="restoreMergeBtn" type="button" class="btn-secondary">Restaurer (fusion)</button>
<button id="restoreReplaceBtn" type="button" class="btn-secondary">Restaurer (remplacement)</button>
<input id="restoreFileInput" type="file" accept="application/json" class="hidden" />
</div>
</aside>
<main class="app-shell">
<header class="hero">
<div>
@@ -50,15 +68,6 @@
<div class="panel-header">
<h2 id="gameSectionTitle">Jeux</h2>
<p id="dataModeInfo" class="data-mode"></p>
<button id="migrateBtn" type="button" class="btn-secondary hidden">
Migrer localStorage vers DB
</button>
<div id="backupControls" class="backup-controls hidden">
<button id="backupBtn" type="button" class="btn-secondary">Sauvegarder JSON</button>
<button id="restoreMergeBtn" type="button" class="btn-secondary">Restaurer (fusion)</button>
<button id="restoreReplaceBtn" type="button" class="btn-secondary">Restaurer (remplacement)</button>
<input id="restoreFileInput" type="file" accept="application/json" class="hidden" />
</div>
</div>
<form id="gameForm" class="grid-form game-form">

View File

@@ -24,6 +24,57 @@ body {
min-height: 100vh;
}
.tools-toggle-btn {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 30;
background: var(--accent-2);
box-shadow: var(--shadow);
}
.tools-drawer {
position: fixed;
top: 0;
right: 0;
width: min(360px, 92vw);
height: 100vh;
background: #f9fbff;
border-left: 1px solid var(--border);
box-shadow: -8px 0 24px rgba(17, 36, 57, 0.14);
padding: 1rem;
transform: translateX(102%);
transition: transform 180ms ease;
z-index: 40;
overflow: auto;
}
.tools-drawer.open {
transform: translateX(0);
}
.tools-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.6rem;
}
.tools-header h2 {
margin: 0;
font-size: 1.05rem;
}
.tools-close-btn {
padding: 0.45rem 0.7rem;
}
.tools-subtitle {
margin: 0.6rem 0 0.9rem;
color: var(--muted);
font-size: 0.9rem;
}
.app-shell {
width: min(1100px, 94vw);
margin: 2rem auto;
@@ -81,13 +132,12 @@ h1 {
}
#migrateBtn {
width: fit-content;
width: 100%;
margin-bottom: 0.9rem;
}
.backup-controls {
display: flex;
flex-wrap: wrap;
display: grid;
gap: 0.5rem;
margin-bottom: 0.9rem;
}
@@ -256,6 +306,12 @@ button {
grid-template-columns: 1fr;
}
.tools-toggle-btn {
top: auto;
bottom: 1rem;
right: 1rem;
}
.game-card {
align-items: flex-start;
flex-direction: column;