Feature: improve loan flow and add games quick dashboard

This commit is contained in:
Ponte
2026-02-12 23:51:52 +01:00
parent 37912557d6
commit e31ec831b3
3 changed files with 230 additions and 13 deletions

157
app.js
View File

@@ -40,6 +40,11 @@ const storageState = document.getElementById("storageState");
const toolsDrawer = document.getElementById("toolsDrawer");
const toolsToggleBtn = document.getElementById("toolsToggleBtn");
const toolsCloseBtn = document.getElementById("toolsCloseBtn");
const gamesDrawer = document.getElementById("gamesDrawer");
const gamesToggleBtn = document.getElementById("gamesToggleBtn");
const gamesCloseBtn = document.getElementById("gamesCloseBtn");
const totalGamesCount = document.getElementById("totalGamesCount");
const totalGamesValue = document.getElementById("totalGamesValue");
const migrateBtn = document.getElementById("migrateBtn");
const backupControls = document.getElementById("backupControls");
const backupBtn = document.getElementById("backupBtn");
@@ -52,14 +57,17 @@ const googleBackupBtn = document.getElementById("googleBackupBtn");
const googleRestoreBtn = document.getElementById("googleRestoreBtn");
const quickSearchInput = document.getElementById("quickSearchInput");
const quickSearchResults = document.getElementById("quickSearchResults");
const loanedFilterBtn = document.getElementById("loanedFilterBtn");
const gamesList = document.getElementById("gamesList");
const gameCardTemplate = document.getElementById("gameCardTemplate");
let editingGameId = null;
let pendingRestoreMode = "merge";
let quickSearchTerm = "";
let googleStatus = { configured: false, connected: false, email: "" };
let showLoanedOnly = false;
toolsToggleBtn.addEventListener("click", () => {
gamesDrawer.classList.remove("open");
toolsDrawer.classList.toggle("open");
});
@@ -67,22 +75,37 @@ toolsCloseBtn.addEventListener("click", () => {
toolsDrawer.classList.remove("open");
});
gamesToggleBtn.addEventListener("click", () => {
toolsDrawer.classList.remove("open");
gamesDrawer.classList.toggle("open");
});
gamesCloseBtn.addEventListener("click", () => {
gamesDrawer.classList.remove("open");
});
document.addEventListener("click", (event) => {
if (!(event.target instanceof Element)) {
return;
}
if (!toolsDrawer.classList.contains("open")) {
const toolsOpen = toolsDrawer.classList.contains("open");
const gamesOpen = gamesDrawer.classList.contains("open");
if (!toolsOpen && !gamesOpen) {
return;
}
if (toolsDrawer.contains(event.target) || toolsToggleBtn.contains(event.target)) {
const clickedInsideTools = toolsDrawer.contains(event.target) || toolsToggleBtn.contains(event.target);
const clickedInsideGames = gamesDrawer.contains(event.target) || gamesToggleBtn.contains(event.target);
if (clickedInsideTools || clickedInsideGames) {
return;
}
toolsDrawer.classList.remove("open");
gamesDrawer.classList.remove("open");
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
toolsDrawer.classList.remove("open");
gamesDrawer.classList.remove("open");
}
});
@@ -133,6 +156,11 @@ quickSearchInput.addEventListener("input", (event) => {
renderSearchResults();
});
loanedFilterBtn.addEventListener("click", () => {
showLoanedOnly = !showLoanedOnly;
renderGames();
});
platformForm.addEventListener("submit", async (event) => {
event.preventDefault();
@@ -317,18 +345,22 @@ gamesList.addEventListener("click", async (event) => {
const action = target.dataset.action;
const id = target.dataset.id;
if (!action || !id || !state.selectedConsole) {
if (!action || !id) {
return;
}
const games = state.gamesByConsole[state.selectedConsole] || [];
const idx = games.findIndex((game) => game.id === id);
if (idx === -1) {
const gameRef = findGameById(id);
if (!gameRef) {
return;
}
const { game, games, idx, consoleName, brand } = gameRef;
if (action === "edit") {
startEditMode(games[idx]);
state.selectedBrand = brand;
state.selectedConsole = consoleName;
startEditMode(game);
persist();
render();
return;
}
@@ -342,7 +374,24 @@ gamesList.addEventListener("click", async (event) => {
}
if (action === "toggle-loan") {
await apiRequest(`/api/catalog/games/${id}/toggle-loan`, { method: "POST" });
if (game.loanedTo) {
await apiRequest(`/api/catalog/games/${id}/toggle-loan`, { method: "POST" });
} else {
const borrower = window.prompt("Nom de la personne a qui tu pretes ce jeu :");
if (borrower === null) {
return;
}
const loanedTo = borrower.trim();
if (!loanedTo) {
alert("Le nom est obligatoire pour marquer le jeu comme prete.");
return;
}
const payload = buildGamePayload(game, brand, consoleName, { loanedTo });
await apiRequest(`/api/catalog/games/${id}`, {
method: "PUT",
body: payload,
});
}
}
await refreshFromApi(state.selectedBrand, state.selectedConsole);
@@ -361,7 +410,20 @@ gamesList.addEventListener("click", async (event) => {
}
if (action === "toggle-loan") {
games[idx].loanedTo = games[idx].loanedTo ? "" : "A renseigner";
if (games[idx].loanedTo) {
games[idx].loanedTo = "";
} else {
const borrower = window.prompt("Nom de la personne a qui tu pretes ce jeu :");
if (borrower === null) {
return;
}
const loanedTo = borrower.trim();
if (!loanedTo) {
alert("Le nom est obligatoire pour marquer le jeu comme prete.");
return;
}
games[idx].loanedTo = loanedTo;
}
}
persist();
@@ -487,6 +549,7 @@ function render() {
renderConsoleTabs();
renderGames();
renderSearchResults();
renderCollectionStats();
}
function renderDataMode() {
@@ -576,6 +639,40 @@ function findBrandByConsole(consoleName) {
return "INCONNUE";
}
function findGameById(id) {
for (const [consoleName, games] of Object.entries(state.gamesByConsole)) {
const idx = (games || []).findIndex((game) => game.id === id);
if (idx !== -1) {
return {
consoleName,
brand: findBrandByConsole(consoleName),
games,
idx,
game: games[idx],
};
}
}
return null;
}
function buildGamePayload(game, brand, consoleName, overrides = {}) {
return {
brand,
consoleName,
title: game.title || "",
version: game.version || "",
genre: game.genre || "",
publisher: game.publisher || "",
isDuplicate: Boolean(game.isDuplicate),
year: game.year != null ? Number(game.year) : null,
purchasePrice: game.purchasePrice != null ? Number(game.purchasePrice) : null,
value: game.value != null ? Number(game.value) : null,
condition: game.condition != null ? Number(game.condition) : null,
loanedTo: game.loanedTo || "",
...overrides,
};
}
function collectAllGames() {
const all = [];
for (const [consoleName, games] of Object.entries(state.gamesByConsole)) {
@@ -587,6 +684,22 @@ function collectAllGames() {
return all;
}
function renderCollectionStats() {
if (!totalGamesCount || !totalGamesValue) {
return;
}
const allGames = collectAllGames();
const totalCount = allGames.length;
const totalValue = allGames.reduce((sum, game) => {
const value = typeof game.value === "number" ? game.value : Number(game.value);
return Number.isFinite(value) ? sum + value : sum;
}, 0);
totalGamesCount.textContent = String(totalCount);
totalGamesValue.textContent = `${totalValue.toFixed(2)} EUR`;
}
function renderSearchResults() {
if (!quickSearchResults) {
return;
@@ -691,17 +804,27 @@ function renderConsoleTabs() {
function renderGames() {
const selectedConsole = state.selectedConsole;
gameSectionTitle.textContent = selectedConsole ? `Jeux - ${selectedConsole}` : "Jeux";
gameSectionTitle.textContent = showLoanedOnly
? "Jeux pretes - Toutes consoles"
: selectedConsole
? `Jeux - ${selectedConsole}`
: "Jeux";
gamesList.innerHTML = "";
loanedFilterBtn.textContent = showLoanedOnly ? "Voir tous les jeux" : "Voir jeux pretes";
if (!selectedConsole) {
if (!showLoanedOnly && !selectedConsole) {
gamesList.innerHTML = '<p class="empty">Ajoute une section pour commencer.</p>';
return;
}
const games = state.gamesByConsole[selectedConsole] || [];
const games = showLoanedOnly
? collectAllGames().filter((game) => normalizeText(game.loanedTo))
: state.gamesByConsole[selectedConsole] || [];
if (!games.length) {
gamesList.innerHTML = '<p class="empty">Aucun jeu sur cette console pour le moment.</p>';
gamesList.innerHTML = showLoanedOnly
? '<p class="empty">Aucun jeu prete actuellement.</p>'
: '<p class="empty">Aucun jeu sur cette console pour le moment.</p>';
return;
}
@@ -715,6 +838,7 @@ function renderGames() {
card.querySelector(".game-title").textContent = game.title;
const metaParts = [
showLoanedOnly ? `${game.brand} / ${game.consoleName}` : null,
game.version ? `Version: ${game.version}` : null,
game.genre ? `Genre: ${game.genre}` : null,
game.publisher ? `Editeur: ${game.publisher}` : null,
@@ -770,6 +894,13 @@ function resetEditMode() {
cancelEditBtn.classList.add("hidden");
}
function normalizeText(value) {
if (value == null) {
return "";
}
return String(value).trim();
}
function loadState() {
try {
const raw = localStorage.getItem(STORAGE_KEY);