Feature: add game cover upload with compact thumbnails
This commit is contained in:
50
app.js
50
app.js
@@ -24,6 +24,8 @@ const barcodeInput = document.getElementById("barcodeInput");
|
||||
const versionInput = document.getElementById("versionInput");
|
||||
const genreInput = document.getElementById("genreInput");
|
||||
const publisherInput = document.getElementById("publisherInput");
|
||||
const coverFileInput = document.getElementById("coverFileInput");
|
||||
const coverUrlInput = document.getElementById("coverUrlInput");
|
||||
const yearInput = document.getElementById("yearInput");
|
||||
const valueInput = document.getElementById("valueInput");
|
||||
const purchasePriceInput = document.getElementById("purchasePriceInput");
|
||||
@@ -78,6 +80,30 @@ let scannerLoopId = null;
|
||||
let scannerLastCodeValue = "";
|
||||
let scannerLastCodeAt = 0;
|
||||
|
||||
coverFileInput.addEventListener("change", async (event) => {
|
||||
const input = event.target;
|
||||
const file = input.files && input.files[0] ? input.files[0] : null;
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.type.startsWith("image/")) {
|
||||
alert("Le fichier doit etre une image.");
|
||||
input.value = "";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const dataUrl = await fileToDataUrl(file);
|
||||
coverUrlInput.value = dataUrl;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert("Impossible de charger cette image.");
|
||||
} finally {
|
||||
input.value = "";
|
||||
}
|
||||
});
|
||||
|
||||
toolsToggleBtn.addEventListener("click", () => {
|
||||
gamesDrawer.classList.remove("open");
|
||||
toolsDrawer.classList.toggle("open");
|
||||
@@ -246,6 +272,7 @@ gameForm.addEventListener("submit", async (event) => {
|
||||
version: versionInput.value.trim(),
|
||||
genre: genreInput.value.trim(),
|
||||
publisher: publisherInput.value.trim(),
|
||||
coverUrl: coverUrlInput.value.trim(),
|
||||
isDuplicate: isDuplicateInput.checked,
|
||||
year: yearInput.value ? Number(yearInput.value) : null,
|
||||
purchasePrice: purchasePriceInput.value ? Number(purchasePriceInput.value) : null,
|
||||
@@ -289,6 +316,7 @@ gameForm.addEventListener("submit", async (event) => {
|
||||
version: versionInput.value.trim(),
|
||||
genre: genreInput.value.trim(),
|
||||
publisher: publisherInput.value.trim(),
|
||||
coverUrl: coverUrlInput.value.trim(),
|
||||
isDuplicate: isDuplicateInput.checked,
|
||||
year: yearInput.value ? Number(yearInput.value) : null,
|
||||
purchasePrice: purchasePriceInput.value ? Number(purchasePriceInput.value) : null,
|
||||
@@ -305,6 +333,7 @@ gameForm.addEventListener("submit", async (event) => {
|
||||
version: versionInput.value.trim(),
|
||||
genre: genreInput.value.trim(),
|
||||
publisher: publisherInput.value.trim(),
|
||||
coverUrl: coverUrlInput.value.trim(),
|
||||
isDuplicate: isDuplicateInput.checked,
|
||||
year: yearInput.value ? Number(yearInput.value) : null,
|
||||
purchasePrice: purchasePriceInput.value ? Number(purchasePriceInput.value) : null,
|
||||
@@ -694,6 +723,7 @@ function buildGamePayload(game, brand, consoleName, overrides = {}) {
|
||||
version: game.version || "",
|
||||
genre: game.genre || "",
|
||||
publisher: game.publisher || "",
|
||||
coverUrl: game.coverUrl || "",
|
||||
isDuplicate: Boolean(game.isDuplicate),
|
||||
year: game.year != null ? Number(game.year) : null,
|
||||
purchasePrice: game.purchasePrice != null ? Number(game.purchasePrice) : null,
|
||||
@@ -882,6 +912,15 @@ function renderGames() {
|
||||
].filter(Boolean);
|
||||
|
||||
card.querySelector(".game-meta").textContent = metaParts.join(" | ") || "Aucune information complementaire";
|
||||
const coverEl = card.querySelector(".game-cover");
|
||||
const coverUrl = normalizeText(game.coverUrl);
|
||||
if (coverUrl) {
|
||||
coverEl.src = coverUrl;
|
||||
coverEl.classList.remove("hidden");
|
||||
} else {
|
||||
coverEl.removeAttribute("src");
|
||||
coverEl.classList.add("hidden");
|
||||
}
|
||||
|
||||
card.querySelector(".game-loan").textContent = game.loanedTo
|
||||
? `Pret en cours: ${game.loanedTo}`
|
||||
@@ -908,6 +947,7 @@ function startEditMode(game) {
|
||||
versionInput.value = game.version || "";
|
||||
genreInput.value = game.genre || "";
|
||||
publisherInput.value = game.publisher || "";
|
||||
coverUrlInput.value = game.coverUrl || "";
|
||||
isDuplicateInput.checked = Boolean(game.isDuplicate);
|
||||
yearInput.value = game.year || "";
|
||||
purchasePriceInput.value = game.purchasePrice != null ? game.purchasePrice : "";
|
||||
@@ -923,10 +963,20 @@ function startEditMode(game) {
|
||||
function resetEditMode() {
|
||||
editingGameId = null;
|
||||
gameForm.reset();
|
||||
coverUrlInput.value = "";
|
||||
gameSubmitBtn.textContent = "Ajouter le jeu";
|
||||
cancelEditBtn.classList.add("hidden");
|
||||
}
|
||||
|
||||
function fileToDataUrl(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => resolve(String(reader.result || ""));
|
||||
reader.onerror = () => reject(new Error("read failed"));
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
}
|
||||
|
||||
function updateScannerStatus(message) {
|
||||
if (scannerStatus) {
|
||||
scannerStatus.textContent = message;
|
||||
|
||||
Reference in New Issue
Block a user