Release: promote preprod to prod (bulk actions + pagination)
This commit is contained in:
269
app.js
269
app.js
@@ -61,6 +61,16 @@ const googleRestoreBtn = document.getElementById("googleRestoreBtn");
|
|||||||
const quickSearchInput = document.getElementById("quickSearchInput");
|
const quickSearchInput = document.getElementById("quickSearchInput");
|
||||||
const quickSearchResults = document.getElementById("quickSearchResults");
|
const quickSearchResults = document.getElementById("quickSearchResults");
|
||||||
const loanedFilterBtn = document.getElementById("loanedFilterBtn");
|
const loanedFilterBtn = document.getElementById("loanedFilterBtn");
|
||||||
|
const bulkActionsBar = document.getElementById("bulkActionsBar");
|
||||||
|
const bulkSelectPage = document.getElementById("bulkSelectPage");
|
||||||
|
const bulkSelectionInfo = document.getElementById("bulkSelectionInfo");
|
||||||
|
const bulkLoanBtn = document.getElementById("bulkLoanBtn");
|
||||||
|
const bulkReturnBtn = document.getElementById("bulkReturnBtn");
|
||||||
|
const bulkDeleteBtn = document.getElementById("bulkDeleteBtn");
|
||||||
|
const paginationBar = document.getElementById("paginationBar");
|
||||||
|
const prevPageBtn = document.getElementById("prevPageBtn");
|
||||||
|
const nextPageBtn = document.getElementById("nextPageBtn");
|
||||||
|
const pageInfo = document.getElementById("pageInfo");
|
||||||
const v2Toolbar = document.getElementById("v2Toolbar");
|
const v2Toolbar = document.getElementById("v2Toolbar");
|
||||||
const v2SearchInput = document.getElementById("v2SearchInput");
|
const v2SearchInput = document.getElementById("v2SearchInput");
|
||||||
const v2ConsoleFilter = document.getElementById("v2ConsoleFilter");
|
const v2ConsoleFilter = document.getElementById("v2ConsoleFilter");
|
||||||
@@ -90,6 +100,10 @@ let scannerRunning = false;
|
|||||||
let scannerLoopId = null;
|
let scannerLoopId = null;
|
||||||
let scannerLastCodeValue = "";
|
let scannerLastCodeValue = "";
|
||||||
let scannerLastCodeAt = 0;
|
let scannerLastCodeAt = 0;
|
||||||
|
let selectedGameIds = new Set();
|
||||||
|
let currentPage = 1;
|
||||||
|
let currentPageGameIds = [];
|
||||||
|
let currentTotalPages = 1;
|
||||||
// V2 is now the default UI. Use ?ui=v1 to force legacy mode if needed.
|
// V2 is now the default UI. Use ?ui=v1 to force legacy mode if needed.
|
||||||
const uiParam = new URLSearchParams(window.location.search).get("ui");
|
const uiParam = new URLSearchParams(window.location.search).get("ui");
|
||||||
const uiV2Enabled = uiParam !== "v1";
|
const uiV2Enabled = uiParam !== "v1";
|
||||||
@@ -218,12 +232,72 @@ quickSearchInput.addEventListener("input", (event) => {
|
|||||||
|
|
||||||
loanedFilterBtn.addEventListener("click", () => {
|
loanedFilterBtn.addEventListener("click", () => {
|
||||||
showLoanedOnly = !showLoanedOnly;
|
showLoanedOnly = !showLoanedOnly;
|
||||||
|
resetPaging();
|
||||||
|
selectedGameIds.clear();
|
||||||
|
renderGames();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (bulkSelectPage) {
|
||||||
|
bulkSelectPage.addEventListener("change", (event) => {
|
||||||
|
const checked = Boolean(event.target.checked);
|
||||||
|
if (checked) {
|
||||||
|
for (const id of currentPageGameIds) {
|
||||||
|
selectedGameIds.add(id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const id of currentPageGameIds) {
|
||||||
|
selectedGameIds.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renderGames();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bulkLoanBtn) {
|
||||||
|
bulkLoanBtn.addEventListener("click", async () => {
|
||||||
|
await performBulkAction("loan");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bulkReturnBtn) {
|
||||||
|
bulkReturnBtn.addEventListener("click", async () => {
|
||||||
|
await performBulkAction("return");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bulkDeleteBtn) {
|
||||||
|
bulkDeleteBtn.addEventListener("click", async () => {
|
||||||
|
await performBulkAction("delete");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevPageBtn) {
|
||||||
|
prevPageBtn.addEventListener("click", () => {
|
||||||
|
if (currentPage > 1) {
|
||||||
|
currentPage -= 1;
|
||||||
|
renderGames();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextPageBtn) {
|
||||||
|
nextPageBtn.addEventListener("click", () => {
|
||||||
|
if (currentPage < currentTotalPages) {
|
||||||
|
currentPage += 1;
|
||||||
|
renderGames();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", () => {
|
||||||
renderGames();
|
renderGames();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (v2SearchInput) {
|
if (v2SearchInput) {
|
||||||
v2SearchInput.addEventListener("input", (event) => {
|
v2SearchInput.addEventListener("input", (event) => {
|
||||||
v2SearchTerm = event.target.value.trim();
|
v2SearchTerm = event.target.value.trim();
|
||||||
|
resetPaging();
|
||||||
|
selectedGameIds.clear();
|
||||||
renderGames();
|
renderGames();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -231,6 +305,8 @@ if (v2SearchInput) {
|
|||||||
if (v2ConsoleFilter) {
|
if (v2ConsoleFilter) {
|
||||||
v2ConsoleFilter.addEventListener("change", (event) => {
|
v2ConsoleFilter.addEventListener("change", (event) => {
|
||||||
v2ConsoleValue = event.target.value;
|
v2ConsoleValue = event.target.value;
|
||||||
|
resetPaging();
|
||||||
|
selectedGameIds.clear();
|
||||||
renderGames();
|
renderGames();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -238,6 +314,8 @@ if (v2ConsoleFilter) {
|
|||||||
if (v2GenreFilter) {
|
if (v2GenreFilter) {
|
||||||
v2GenreFilter.addEventListener("change", (event) => {
|
v2GenreFilter.addEventListener("change", (event) => {
|
||||||
v2GenreValue = event.target.value;
|
v2GenreValue = event.target.value;
|
||||||
|
resetPaging();
|
||||||
|
selectedGameIds.clear();
|
||||||
renderGames();
|
renderGames();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -245,6 +323,8 @@ if (v2GenreFilter) {
|
|||||||
if (v2SortSelect) {
|
if (v2SortSelect) {
|
||||||
v2SortSelect.addEventListener("change", (event) => {
|
v2SortSelect.addEventListener("change", (event) => {
|
||||||
v2SortValue = event.target.value || "title_asc";
|
v2SortValue = event.target.value || "title_asc";
|
||||||
|
resetPaging();
|
||||||
|
selectedGameIds.clear();
|
||||||
renderGames();
|
renderGames();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -409,6 +489,8 @@ gameForm.addEventListener("submit", async (event) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resetEditMode();
|
resetEditMode();
|
||||||
|
resetPaging();
|
||||||
|
selectedGameIds.clear();
|
||||||
persist();
|
persist();
|
||||||
markLocalDataForImport();
|
markLocalDataForImport();
|
||||||
render();
|
render();
|
||||||
@@ -433,6 +515,8 @@ brandTabs.addEventListener("click", (event) => {
|
|||||||
const consoles = state.brands[brand] || [];
|
const consoles = state.brands[brand] || [];
|
||||||
state.selectedConsole = consoles[0] || "";
|
state.selectedConsole = consoles[0] || "";
|
||||||
resetEditMode();
|
resetEditMode();
|
||||||
|
resetPaging();
|
||||||
|
selectedGameIds.clear();
|
||||||
persist();
|
persist();
|
||||||
render();
|
render();
|
||||||
});
|
});
|
||||||
@@ -453,6 +537,8 @@ consoleTabs.addEventListener("click", (event) => {
|
|||||||
|
|
||||||
state.selectedConsole = consoleName;
|
state.selectedConsole = consoleName;
|
||||||
resetEditMode();
|
resetEditMode();
|
||||||
|
resetPaging();
|
||||||
|
selectedGameIds.clear();
|
||||||
persist();
|
persist();
|
||||||
render();
|
render();
|
||||||
});
|
});
|
||||||
@@ -574,6 +660,7 @@ gamesList.addEventListener("click", async (event) => {
|
|||||||
try {
|
try {
|
||||||
if (action === "delete") {
|
if (action === "delete") {
|
||||||
await apiRequest(`/api/catalog/games/${id}`, { method: "DELETE" });
|
await apiRequest(`/api/catalog/games/${id}`, { method: "DELETE" });
|
||||||
|
selectedGameIds.delete(id);
|
||||||
if (editingGameId === id) {
|
if (editingGameId === id) {
|
||||||
resetEditMode();
|
resetEditMode();
|
||||||
}
|
}
|
||||||
@@ -615,6 +702,7 @@ gamesList.addEventListener("click", async (event) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
games.splice(idx, 1);
|
games.splice(idx, 1);
|
||||||
|
selectedGameIds.delete(id);
|
||||||
if (editingGameId === id) {
|
if (editingGameId === id) {
|
||||||
resetEditMode();
|
resetEditMode();
|
||||||
}
|
}
|
||||||
@@ -643,6 +731,26 @@ gamesList.addEventListener("click", async (event) => {
|
|||||||
showToast("Action enregistree.", "success");
|
showToast("Action enregistree.", "success");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gamesList.addEventListener("change", (event) => {
|
||||||
|
if (!(event.target instanceof Element)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const target = event.target.closest('input[type="checkbox"][data-action="select"]');
|
||||||
|
if (!(target instanceof HTMLInputElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const id = target.dataset.id;
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (target.checked) {
|
||||||
|
selectedGameIds.add(id);
|
||||||
|
} else {
|
||||||
|
selectedGameIds.delete(id);
|
||||||
|
}
|
||||||
|
renderGames();
|
||||||
|
});
|
||||||
|
|
||||||
cancelEditBtn.addEventListener("click", () => {
|
cancelEditBtn.addEventListener("click", () => {
|
||||||
resetEditMode();
|
resetEditMode();
|
||||||
});
|
});
|
||||||
@@ -1040,6 +1148,55 @@ function conditionBadgeClass(conditionValue) {
|
|||||||
return "status-low";
|
return "status-low";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pageSizeForViewport() {
|
||||||
|
return window.innerWidth <= 640 ? 12 : 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetPaging() {
|
||||||
|
currentPage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBulkAndPaginationUi(pageGames, totalFilteredCount) {
|
||||||
|
const pageIds = pageGames.map((game) => game.id);
|
||||||
|
const selectedOnPage = pageIds.filter((id) => selectedGameIds.has(id)).length;
|
||||||
|
const hasAnySelection = selectedGameIds.size > 0;
|
||||||
|
|
||||||
|
currentPageGameIds = pageIds;
|
||||||
|
if (bulkSelectionInfo) {
|
||||||
|
bulkSelectionInfo.textContent = `${selectedGameIds.size} selectionne${selectedGameIds.size > 1 ? "s" : ""}`;
|
||||||
|
}
|
||||||
|
if (bulkLoanBtn) {
|
||||||
|
bulkLoanBtn.disabled = !hasAnySelection;
|
||||||
|
}
|
||||||
|
if (bulkReturnBtn) {
|
||||||
|
bulkReturnBtn.disabled = !hasAnySelection;
|
||||||
|
}
|
||||||
|
if (bulkDeleteBtn) {
|
||||||
|
bulkDeleteBtn.disabled = !hasAnySelection;
|
||||||
|
}
|
||||||
|
if (bulkSelectPage) {
|
||||||
|
bulkSelectPage.checked = pageIds.length > 0 && selectedOnPage === pageIds.length;
|
||||||
|
bulkSelectPage.indeterminate = selectedOnPage > 0 && selectedOnPage < pageIds.length;
|
||||||
|
bulkSelectPage.disabled = pageIds.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageInfo) {
|
||||||
|
pageInfo.textContent = `Page ${currentPage}/${currentTotalPages}`;
|
||||||
|
}
|
||||||
|
if (prevPageBtn) {
|
||||||
|
prevPageBtn.disabled = currentPage <= 1;
|
||||||
|
}
|
||||||
|
if (nextPageBtn) {
|
||||||
|
nextPageBtn.disabled = currentPage >= currentTotalPages;
|
||||||
|
}
|
||||||
|
if (paginationBar) {
|
||||||
|
paginationBar.classList.toggle("hidden", totalFilteredCount <= pageSizeForViewport());
|
||||||
|
}
|
||||||
|
if (bulkActionsBar) {
|
||||||
|
bulkActionsBar.classList.toggle("hidden", totalFilteredCount === 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderSearchResults() {
|
function renderSearchResults() {
|
||||||
if (!quickSearchResults) {
|
if (!quickSearchResults) {
|
||||||
return;
|
return;
|
||||||
@@ -1142,6 +1299,95 @@ function renderConsoleTabs() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function performBulkAction(action) {
|
||||||
|
const selectedIds = Array.from(selectedGameIds);
|
||||||
|
if (!selectedIds.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let loanedTo = "";
|
||||||
|
if (action === "loan") {
|
||||||
|
const borrower = window.prompt("Nom de la personne a qui tu pretes ces jeux :");
|
||||||
|
if (borrower === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loanedTo = borrower.trim();
|
||||||
|
if (!loanedTo) {
|
||||||
|
alert("Le nom est obligatoire pour marquer les jeux comme pretes.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === "delete") {
|
||||||
|
const confirmed = window.confirm(`Supprimer ${selectedIds.length} jeu(x) selectionne(s) ?`);
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (apiReachable && dataMode !== "local-pending-import") {
|
||||||
|
try {
|
||||||
|
for (const id of selectedIds) {
|
||||||
|
const gameRef = findGameById(id);
|
||||||
|
if (!gameRef) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const { game, brand, consoleName } = gameRef;
|
||||||
|
if (action === "delete") {
|
||||||
|
await apiRequest(`/api/catalog/games/${id}`, { method: "DELETE" });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (action === "return") {
|
||||||
|
if (!game.loanedTo) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const payload = buildGamePayload(game, brand, consoleName, { loanedTo: "" });
|
||||||
|
await apiRequest(`/api/catalog/games/${id}`, { method: "PUT", body: payload });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (action === "loan") {
|
||||||
|
const payload = buildGamePayload(game, brand, consoleName, { loanedTo });
|
||||||
|
await apiRequest(`/api/catalog/games/${id}`, { method: "PUT", body: payload });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedGameIds.clear();
|
||||||
|
await refreshFromApi(state.selectedBrand, state.selectedConsole);
|
||||||
|
showToast("Action groupée enregistree.", "success");
|
||||||
|
return;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
alert("Action groupée impossible via l'API.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const id of selectedIds) {
|
||||||
|
const gameRef = findGameById(id);
|
||||||
|
if (!gameRef) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const { games, idx } = gameRef;
|
||||||
|
if (action === "delete") {
|
||||||
|
games.splice(idx, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (action === "return") {
|
||||||
|
games[idx].loanedTo = "";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (action === "loan") {
|
||||||
|
games[idx].loanedTo = loanedTo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedGameIds.clear();
|
||||||
|
persist();
|
||||||
|
markLocalDataForImport();
|
||||||
|
render();
|
||||||
|
showToast("Action groupée enregistree.", "success");
|
||||||
|
}
|
||||||
|
|
||||||
function renderGames() {
|
function renderGames() {
|
||||||
const selectedConsole = state.selectedConsole;
|
const selectedConsole = state.selectedConsole;
|
||||||
const inV2 = uiV2Enabled;
|
const inV2 = uiV2Enabled;
|
||||||
@@ -1157,6 +1403,7 @@ function renderGames() {
|
|||||||
|
|
||||||
if (!inV2 && !showLoanedOnly && !selectedConsole) {
|
if (!inV2 && !showLoanedOnly && !selectedConsole) {
|
||||||
gamesList.innerHTML = '<p class="empty">Ajoute une section pour commencer.</p>';
|
gamesList.innerHTML = '<p class="empty">Ajoute une section pour commencer.</p>';
|
||||||
|
updateBulkAndPaginationUi([], 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1210,14 +1457,27 @@ function renderGames() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!games.length) {
|
const allCurrentIds = new Set(collectAllGames().map((game) => game.id));
|
||||||
|
selectedGameIds = new Set(Array.from(selectedGameIds).filter((id) => allCurrentIds.has(id)));
|
||||||
|
|
||||||
|
const totalFilteredCount = games.length;
|
||||||
|
const pageSize = pageSizeForViewport();
|
||||||
|
currentTotalPages = Math.max(1, Math.ceil(totalFilteredCount / pageSize));
|
||||||
|
if (currentPage > currentTotalPages) {
|
||||||
|
currentPage = currentTotalPages;
|
||||||
|
}
|
||||||
|
const startIdx = (currentPage - 1) * pageSize;
|
||||||
|
const pageGames = games.slice(startIdx, startIdx + pageSize);
|
||||||
|
updateBulkAndPaginationUi(pageGames, totalFilteredCount);
|
||||||
|
|
||||||
|
if (!totalFilteredCount) {
|
||||||
gamesList.innerHTML = showLoanedOnly
|
gamesList.innerHTML = showLoanedOnly
|
||||||
? '<p class="empty">Aucun jeu prete actuellement.</p>'
|
? '<p class="empty">Aucun jeu prete actuellement.</p>'
|
||||||
: '<p class="empty">Aucun jeu pour ces filtres.</p>';
|
: '<p class="empty">Aucun jeu pour ces filtres.</p>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const game of games) {
|
for (const game of pageGames) {
|
||||||
const card = gameCardTemplate.content.cloneNode(true);
|
const card = gameCardTemplate.content.cloneNode(true);
|
||||||
const article = card.querySelector(".game-card");
|
const article = card.querySelector(".game-card");
|
||||||
article.classList.add(consoleThemeClass(game.consoleName));
|
article.classList.add(consoleThemeClass(game.consoleName));
|
||||||
@@ -1282,6 +1542,7 @@ function renderGames() {
|
|||||||
const editBtn = card.querySelector('[data-action="edit"]');
|
const editBtn = card.querySelector('[data-action="edit"]');
|
||||||
const toggleBtn = card.querySelector('[data-action="toggle-loan"]');
|
const toggleBtn = card.querySelector('[data-action="toggle-loan"]');
|
||||||
const deleteBtn = card.querySelector('[data-action="delete"]');
|
const deleteBtn = card.querySelector('[data-action="delete"]');
|
||||||
|
const selectInput = card.querySelector('input[type="checkbox"][data-action="select"]');
|
||||||
|
|
||||||
editBtn.dataset.id = game.id;
|
editBtn.dataset.id = game.id;
|
||||||
editBtn.textContent = "✏️ Editer";
|
editBtn.textContent = "✏️ Editer";
|
||||||
@@ -1296,6 +1557,10 @@ function renderGames() {
|
|||||||
deleteBtn.textContent = "🗑️ Supprimer";
|
deleteBtn.textContent = "🗑️ Supprimer";
|
||||||
deleteBtn.title = "Supprimer ce jeu";
|
deleteBtn.title = "Supprimer ce jeu";
|
||||||
deleteBtn.setAttribute("aria-label", "Supprimer ce jeu");
|
deleteBtn.setAttribute("aria-label", "Supprimer ce jeu");
|
||||||
|
if (selectInput instanceof HTMLInputElement) {
|
||||||
|
selectInput.dataset.id = game.id;
|
||||||
|
selectInput.checked = selectedGameIds.has(game.id);
|
||||||
|
}
|
||||||
|
|
||||||
if (inlineEditingGameId === game.id) {
|
if (inlineEditingGameId === game.id) {
|
||||||
const editor = document.createElement("div");
|
const editor = document.createElement("div");
|
||||||
|
|||||||
19
index.html
19
index.html
@@ -132,6 +132,16 @@
|
|||||||
<div class="games-actions-bar">
|
<div class="games-actions-bar">
|
||||||
<button id="loanedFilterBtn" type="button" class="btn-secondary">Voir jeux pretes</button>
|
<button id="loanedFilterBtn" type="button" class="btn-secondary">Voir jeux pretes</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="bulkActionsBar" class="bulk-actions-bar">
|
||||||
|
<label class="checkbox-row">
|
||||||
|
<input id="bulkSelectPage" type="checkbox" />
|
||||||
|
Tout selectionner (page)
|
||||||
|
</label>
|
||||||
|
<span id="bulkSelectionInfo" class="bulk-selection-info">0 selectionne</span>
|
||||||
|
<button id="bulkLoanBtn" type="button" class="btn-secondary" disabled>Marquer prete</button>
|
||||||
|
<button id="bulkReturnBtn" type="button" class="btn-secondary" disabled>Marquer rendu</button>
|
||||||
|
<button id="bulkDeleteBtn" type="button" class="btn-inline danger" disabled>Supprimer</button>
|
||||||
|
</div>
|
||||||
<div class="scanner-zone">
|
<div class="scanner-zone">
|
||||||
<div class="scanner-header">
|
<div class="scanner-header">
|
||||||
<strong>Scan camera (mobile)</strong>
|
<strong>Scan camera (mobile)</strong>
|
||||||
@@ -210,12 +220,21 @@
|
|||||||
<input id="coverUrlInput" type="hidden" />
|
<input id="coverUrlInput" type="hidden" />
|
||||||
|
|
||||||
<div id="gamesList" class="games-list"></div>
|
<div id="gamesList" class="games-list"></div>
|
||||||
|
<div id="paginationBar" class="pagination-bar">
|
||||||
|
<button id="prevPageBtn" type="button" class="btn-secondary">Precedent</button>
|
||||||
|
<span id="pageInfo" class="page-info">Page 1/1</span>
|
||||||
|
<button id="nextPageBtn" type="button" class="btn-secondary">Suivant</button>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<div id="toastContainer" class="toast-container" aria-live="polite" aria-atomic="true"></div>
|
<div id="toastContainer" class="toast-container" aria-live="polite" aria-atomic="true"></div>
|
||||||
|
|
||||||
<template id="gameCardTemplate">
|
<template id="gameCardTemplate">
|
||||||
<article class="game-card">
|
<article class="game-card">
|
||||||
|
<label class="game-select">
|
||||||
|
<input type="checkbox" data-action="select" />
|
||||||
|
<span>Selection</span>
|
||||||
|
</label>
|
||||||
<img class="game-cover hidden" alt="Pochette du jeu" loading="lazy" />
|
<img class="game-cover hidden" alt="Pochette du jeu" loading="lazy" />
|
||||||
<div class="game-main">
|
<div class="game-main">
|
||||||
<h3 class="game-title"></h3>
|
<h3 class="game-title"></h3>
|
||||||
|
|||||||
64
styles.css
64
styles.css
@@ -244,6 +244,24 @@ h1 {
|
|||||||
margin-bottom: 0.8rem;
|
margin-bottom: 0.8rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bulk-actions-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
padding: 0.55rem 0.65rem;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #f7faff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-selection-info {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.scanner-zone {
|
.scanner-zone {
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -439,6 +457,20 @@ button {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game-select {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--muted);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-select input {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.console-theme-default {
|
.console-theme-default {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
}
|
}
|
||||||
@@ -520,6 +552,21 @@ button {
|
|||||||
gap: 0.45rem;
|
gap: 0.45rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pagination-bar {
|
||||||
|
margin-top: 0.8rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-info {
|
||||||
|
font-size: 0.88rem;
|
||||||
|
color: var(--muted);
|
||||||
|
min-width: 98px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
.inline-editor {
|
.inline-editor {
|
||||||
margin-top: 0.75rem;
|
margin-top: 0.75rem;
|
||||||
border-top: 1px dashed #ccd7e4;
|
border-top: 1px dashed #ccd7e4;
|
||||||
@@ -750,6 +797,10 @@ body.ui-v2 .game-card {
|
|||||||
transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease;
|
transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.ui-v2 .game-select span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
body.ui-v2 .game-card:hover {
|
body.ui-v2 .game-card:hover {
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
border-color: #d3cef9;
|
border-color: #d3cef9;
|
||||||
@@ -814,6 +865,15 @@ body.ui-v2 .hero {
|
|||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bulk-actions-bar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bulk-selection-info {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.grid-form,
|
.grid-form,
|
||||||
.game-form {
|
.game-form {
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
@@ -864,4 +924,8 @@ body.ui-v2 .hero {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pagination-bar {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user