Feature: add game cover upload with compact thumbnails
This commit is contained in:
@@ -121,6 +121,7 @@ async function runMigrations() {
|
|||||||
condition_score NUMERIC(4,2),
|
condition_score NUMERIC(4,2),
|
||||||
loaned_to TEXT,
|
loaned_to TEXT,
|
||||||
barcode TEXT,
|
barcode TEXT,
|
||||||
|
cover_url TEXT,
|
||||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
);
|
);
|
||||||
@@ -130,6 +131,7 @@ async function runMigrations() {
|
|||||||
await pool.query("ALTER TABLE games ADD COLUMN IF NOT EXISTS purchase_price NUMERIC(10,2);");
|
await pool.query("ALTER TABLE games ADD COLUMN IF NOT EXISTS purchase_price NUMERIC(10,2);");
|
||||||
await pool.query("ALTER TABLE games ADD COLUMN IF NOT EXISTS condition_score NUMERIC(4,2);");
|
await pool.query("ALTER TABLE games ADD COLUMN IF NOT EXISTS condition_score NUMERIC(4,2);");
|
||||||
await pool.query("ALTER TABLE games ADD COLUMN IF NOT EXISTS barcode TEXT;");
|
await pool.query("ALTER TABLE games ADD COLUMN IF NOT EXISTS barcode TEXT;");
|
||||||
|
await pool.query("ALTER TABLE games ADD COLUMN IF NOT EXISTS cover_url TEXT;");
|
||||||
|
|
||||||
await pool.query(`
|
await pool.query(`
|
||||||
CREATE TABLE IF NOT EXISTS backup_snapshots (
|
CREATE TABLE IF NOT EXISTS backup_snapshots (
|
||||||
@@ -248,6 +250,7 @@ async function getCatalogFull() {
|
|||||||
g.condition_score,
|
g.condition_score,
|
||||||
g.loaned_to,
|
g.loaned_to,
|
||||||
g.barcode,
|
g.barcode,
|
||||||
|
g.cover_url,
|
||||||
g.created_at
|
g.created_at
|
||||||
FROM games g
|
FROM games g
|
||||||
JOIN consoles c ON c.id = g.console_id
|
JOIN consoles c ON c.id = g.console_id
|
||||||
@@ -280,6 +283,7 @@ async function getCatalogFull() {
|
|||||||
condition: row.condition_score != null ? Number(row.condition_score) : null,
|
condition: row.condition_score != null ? Number(row.condition_score) : null,
|
||||||
loanedTo: row.loaned_to || "",
|
loanedTo: row.loaned_to || "",
|
||||||
barcode: row.barcode || "",
|
barcode: row.barcode || "",
|
||||||
|
coverUrl: row.cover_url || "",
|
||||||
createdAt: row.created_at,
|
createdAt: row.created_at,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -320,6 +324,7 @@ async function exportCatalogDumpWithClient(client) {
|
|||||||
g.condition_score,
|
g.condition_score,
|
||||||
g.loaned_to,
|
g.loaned_to,
|
||||||
g.barcode,
|
g.barcode,
|
||||||
|
g.cover_url,
|
||||||
g.created_at,
|
g.created_at,
|
||||||
g.updated_at
|
g.updated_at
|
||||||
FROM games g
|
FROM games g
|
||||||
@@ -360,6 +365,7 @@ async function exportCatalogDumpWithClient(client) {
|
|||||||
condition: row.condition_score != null ? Number(row.condition_score) : null,
|
condition: row.condition_score != null ? Number(row.condition_score) : null,
|
||||||
loanedTo: row.loaned_to || "",
|
loanedTo: row.loaned_to || "",
|
||||||
barcode: row.barcode || "",
|
barcode: row.barcode || "",
|
||||||
|
coverUrl: row.cover_url || "",
|
||||||
createdAt: row.created_at,
|
createdAt: row.created_at,
|
||||||
updatedAt: row.updated_at,
|
updatedAt: row.updated_at,
|
||||||
})),
|
})),
|
||||||
@@ -504,6 +510,7 @@ async function restoreCatalogDump(mode, dump) {
|
|||||||
const publisher = normalizeText(gameEntry && gameEntry.publisher) || null;
|
const publisher = normalizeText(gameEntry && gameEntry.publisher) || null;
|
||||||
const loanedTo = normalizeText(gameEntry && gameEntry.loanedTo) || null;
|
const loanedTo = normalizeText(gameEntry && gameEntry.loanedTo) || null;
|
||||||
const barcode = normalizeText(gameEntry && gameEntry.barcode) || null;
|
const barcode = normalizeText(gameEntry && gameEntry.barcode) || null;
|
||||||
|
const coverUrl = normalizeText(gameEntry && gameEntry.coverUrl) || null;
|
||||||
const isDuplicate = Boolean(gameEntry && gameEntry.isDuplicate);
|
const isDuplicate = Boolean(gameEntry && gameEntry.isDuplicate);
|
||||||
const purchasePrice =
|
const purchasePrice =
|
||||||
gameEntry && gameEntry.purchasePrice != null && gameEntry.purchasePrice !== ""
|
gameEntry && gameEntry.purchasePrice != null && gameEntry.purchasePrice !== ""
|
||||||
@@ -518,9 +525,9 @@ async function restoreCatalogDump(mode, dump) {
|
|||||||
`
|
`
|
||||||
INSERT INTO games(
|
INSERT INTO games(
|
||||||
console_id, title, genre, publisher, game_version, is_duplicate, release_year, purchase_price,
|
console_id, title, genre, publisher, game_version, is_duplicate, release_year, purchase_price,
|
||||||
estimated_value, condition_score, loaned_to, created_at
|
estimated_value, condition_score, loaned_to, barcode, cover_url, created_at
|
||||||
)
|
)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, COALESCE($13::timestamptz, NOW()));
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, COALESCE($14::timestamptz, NOW()));
|
||||||
`,
|
`,
|
||||||
[
|
[
|
||||||
consoleId,
|
consoleId,
|
||||||
@@ -535,6 +542,7 @@ async function restoreCatalogDump(mode, dump) {
|
|||||||
condition,
|
condition,
|
||||||
loanedTo,
|
loanedTo,
|
||||||
barcode,
|
barcode,
|
||||||
|
coverUrl,
|
||||||
createdAt,
|
createdAt,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -589,6 +597,7 @@ async function createGame(payload) {
|
|||||||
const isDuplicate = Boolean(payload.isDuplicate);
|
const isDuplicate = Boolean(payload.isDuplicate);
|
||||||
const loanedTo = normalizeText(payload.loanedTo) || null;
|
const loanedTo = normalizeText(payload.loanedTo) || null;
|
||||||
const barcode = normalizeText(payload.barcode) || null;
|
const barcode = normalizeText(payload.barcode) || null;
|
||||||
|
const coverUrl = normalizeText(payload.coverUrl) || null;
|
||||||
const year = payload.year != null && payload.year !== "" ? Number(payload.year) : null;
|
const year = payload.year != null && payload.year !== "" ? Number(payload.year) : null;
|
||||||
const purchasePrice =
|
const purchasePrice =
|
||||||
payload.purchasePrice != null && payload.purchasePrice !== "" ? Number(payload.purchasePrice) : null;
|
payload.purchasePrice != null && payload.purchasePrice !== "" ? Number(payload.purchasePrice) : null;
|
||||||
@@ -599,9 +608,9 @@ async function createGame(payload) {
|
|||||||
`
|
`
|
||||||
INSERT INTO games(
|
INSERT INTO games(
|
||||||
console_id, title, genre, publisher, game_version, is_duplicate, release_year, purchase_price,
|
console_id, title, genre, publisher, game_version, is_duplicate, release_year, purchase_price,
|
||||||
estimated_value, condition_score, loaned_to, barcode
|
estimated_value, condition_score, loaned_to, barcode, cover_url
|
||||||
)
|
)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
|
||||||
RETURNING id::text AS id;
|
RETURNING id::text AS id;
|
||||||
`,
|
`,
|
||||||
[
|
[
|
||||||
@@ -617,6 +626,7 @@ async function createGame(payload) {
|
|||||||
condition,
|
condition,
|
||||||
loanedTo,
|
loanedTo,
|
||||||
barcode,
|
barcode,
|
||||||
|
coverUrl,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -647,6 +657,7 @@ async function updateGame(id, payload) {
|
|||||||
const isDuplicate = Boolean(payload.isDuplicate);
|
const isDuplicate = Boolean(payload.isDuplicate);
|
||||||
const loanedTo = normalizeText(payload.loanedTo) || null;
|
const loanedTo = normalizeText(payload.loanedTo) || null;
|
||||||
const barcode = normalizeText(payload.barcode) || null;
|
const barcode = normalizeText(payload.barcode) || null;
|
||||||
|
const coverUrl = normalizeText(payload.coverUrl) || null;
|
||||||
const year = payload.year != null && payload.year !== "" ? Number(payload.year) : null;
|
const year = payload.year != null && payload.year !== "" ? Number(payload.year) : null;
|
||||||
const purchasePrice =
|
const purchasePrice =
|
||||||
payload.purchasePrice != null && payload.purchasePrice !== "" ? Number(payload.purchasePrice) : null;
|
payload.purchasePrice != null && payload.purchasePrice !== "" ? Number(payload.purchasePrice) : null;
|
||||||
@@ -668,7 +679,8 @@ async function updateGame(id, payload) {
|
|||||||
estimated_value = $10,
|
estimated_value = $10,
|
||||||
condition_score = $11,
|
condition_score = $11,
|
||||||
loaned_to = $12,
|
loaned_to = $12,
|
||||||
barcode = $13
|
barcode = $13,
|
||||||
|
cover_url = $14
|
||||||
WHERE id = $1::uuid
|
WHERE id = $1::uuid
|
||||||
RETURNING id::text AS id;
|
RETURNING id::text AS id;
|
||||||
`,
|
`,
|
||||||
@@ -686,6 +698,7 @@ async function updateGame(id, payload) {
|
|||||||
condition,
|
condition,
|
||||||
loanedTo,
|
loanedTo,
|
||||||
barcode,
|
barcode,
|
||||||
|
coverUrl,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -904,6 +917,7 @@ async function importCatalog(payload) {
|
|||||||
const publisher = normalizeText(game && game.publisher) || null;
|
const publisher = normalizeText(game && game.publisher) || null;
|
||||||
const loanedTo = normalizeText(game && game.loanedTo) || null;
|
const loanedTo = normalizeText(game && game.loanedTo) || null;
|
||||||
const barcode = normalizeText(game && game.barcode) || null;
|
const barcode = normalizeText(game && game.barcode) || null;
|
||||||
|
const coverUrl = normalizeText(game && game.coverUrl) || null;
|
||||||
const year = game && game.year != null && game.year !== "" ? Number(game.year) : null;
|
const year = game && game.year != null && game.year !== "" ? Number(game.year) : null;
|
||||||
const purchasePrice =
|
const purchasePrice =
|
||||||
game && game.purchasePrice != null && game.purchasePrice !== "" ? Number(game.purchasePrice) : null;
|
game && game.purchasePrice != null && game.purchasePrice !== "" ? Number(game.purchasePrice) : null;
|
||||||
@@ -931,9 +945,9 @@ async function importCatalog(payload) {
|
|||||||
`
|
`
|
||||||
INSERT INTO games(
|
INSERT INTO games(
|
||||||
console_id, title, genre, publisher, game_version, is_duplicate, release_year, purchase_price,
|
console_id, title, genre, publisher, game_version, is_duplicate, release_year, purchase_price,
|
||||||
estimated_value, condition_score, loaned_to, barcode
|
estimated_value, condition_score, loaned_to, barcode, cover_url
|
||||||
)
|
)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12);
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13);
|
||||||
`,
|
`,
|
||||||
[
|
[
|
||||||
consoleId,
|
consoleId,
|
||||||
@@ -948,6 +962,7 @@ async function importCatalog(payload) {
|
|||||||
condition,
|
condition,
|
||||||
loanedTo,
|
loanedTo,
|
||||||
barcode,
|
barcode,
|
||||||
|
coverUrl,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
50
app.js
50
app.js
@@ -24,6 +24,8 @@ const barcodeInput = document.getElementById("barcodeInput");
|
|||||||
const versionInput = document.getElementById("versionInput");
|
const versionInput = document.getElementById("versionInput");
|
||||||
const genreInput = document.getElementById("genreInput");
|
const genreInput = document.getElementById("genreInput");
|
||||||
const publisherInput = document.getElementById("publisherInput");
|
const publisherInput = document.getElementById("publisherInput");
|
||||||
|
const coverFileInput = document.getElementById("coverFileInput");
|
||||||
|
const coverUrlInput = document.getElementById("coverUrlInput");
|
||||||
const yearInput = document.getElementById("yearInput");
|
const yearInput = document.getElementById("yearInput");
|
||||||
const valueInput = document.getElementById("valueInput");
|
const valueInput = document.getElementById("valueInput");
|
||||||
const purchasePriceInput = document.getElementById("purchasePriceInput");
|
const purchasePriceInput = document.getElementById("purchasePriceInput");
|
||||||
@@ -78,6 +80,30 @@ let scannerLoopId = null;
|
|||||||
let scannerLastCodeValue = "";
|
let scannerLastCodeValue = "";
|
||||||
let scannerLastCodeAt = 0;
|
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", () => {
|
toolsToggleBtn.addEventListener("click", () => {
|
||||||
gamesDrawer.classList.remove("open");
|
gamesDrawer.classList.remove("open");
|
||||||
toolsDrawer.classList.toggle("open");
|
toolsDrawer.classList.toggle("open");
|
||||||
@@ -246,6 +272,7 @@ gameForm.addEventListener("submit", async (event) => {
|
|||||||
version: versionInput.value.trim(),
|
version: versionInput.value.trim(),
|
||||||
genre: genreInput.value.trim(),
|
genre: genreInput.value.trim(),
|
||||||
publisher: publisherInput.value.trim(),
|
publisher: publisherInput.value.trim(),
|
||||||
|
coverUrl: coverUrlInput.value.trim(),
|
||||||
isDuplicate: isDuplicateInput.checked,
|
isDuplicate: isDuplicateInput.checked,
|
||||||
year: yearInput.value ? Number(yearInput.value) : null,
|
year: yearInput.value ? Number(yearInput.value) : null,
|
||||||
purchasePrice: purchasePriceInput.value ? Number(purchasePriceInput.value) : null,
|
purchasePrice: purchasePriceInput.value ? Number(purchasePriceInput.value) : null,
|
||||||
@@ -289,6 +316,7 @@ gameForm.addEventListener("submit", async (event) => {
|
|||||||
version: versionInput.value.trim(),
|
version: versionInput.value.trim(),
|
||||||
genre: genreInput.value.trim(),
|
genre: genreInput.value.trim(),
|
||||||
publisher: publisherInput.value.trim(),
|
publisher: publisherInput.value.trim(),
|
||||||
|
coverUrl: coverUrlInput.value.trim(),
|
||||||
isDuplicate: isDuplicateInput.checked,
|
isDuplicate: isDuplicateInput.checked,
|
||||||
year: yearInput.value ? Number(yearInput.value) : null,
|
year: yearInput.value ? Number(yearInput.value) : null,
|
||||||
purchasePrice: purchasePriceInput.value ? Number(purchasePriceInput.value) : null,
|
purchasePrice: purchasePriceInput.value ? Number(purchasePriceInput.value) : null,
|
||||||
@@ -305,6 +333,7 @@ gameForm.addEventListener("submit", async (event) => {
|
|||||||
version: versionInput.value.trim(),
|
version: versionInput.value.trim(),
|
||||||
genre: genreInput.value.trim(),
|
genre: genreInput.value.trim(),
|
||||||
publisher: publisherInput.value.trim(),
|
publisher: publisherInput.value.trim(),
|
||||||
|
coverUrl: coverUrlInput.value.trim(),
|
||||||
isDuplicate: isDuplicateInput.checked,
|
isDuplicate: isDuplicateInput.checked,
|
||||||
year: yearInput.value ? Number(yearInput.value) : null,
|
year: yearInput.value ? Number(yearInput.value) : null,
|
||||||
purchasePrice: purchasePriceInput.value ? Number(purchasePriceInput.value) : null,
|
purchasePrice: purchasePriceInput.value ? Number(purchasePriceInput.value) : null,
|
||||||
@@ -694,6 +723,7 @@ function buildGamePayload(game, brand, consoleName, overrides = {}) {
|
|||||||
version: game.version || "",
|
version: game.version || "",
|
||||||
genre: game.genre || "",
|
genre: game.genre || "",
|
||||||
publisher: game.publisher || "",
|
publisher: game.publisher || "",
|
||||||
|
coverUrl: game.coverUrl || "",
|
||||||
isDuplicate: Boolean(game.isDuplicate),
|
isDuplicate: Boolean(game.isDuplicate),
|
||||||
year: game.year != null ? Number(game.year) : null,
|
year: game.year != null ? Number(game.year) : null,
|
||||||
purchasePrice: game.purchasePrice != null ? Number(game.purchasePrice) : null,
|
purchasePrice: game.purchasePrice != null ? Number(game.purchasePrice) : null,
|
||||||
@@ -882,6 +912,15 @@ function renderGames() {
|
|||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
card.querySelector(".game-meta").textContent = metaParts.join(" | ") || "Aucune information complementaire";
|
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
|
card.querySelector(".game-loan").textContent = game.loanedTo
|
||||||
? `Pret en cours: ${game.loanedTo}`
|
? `Pret en cours: ${game.loanedTo}`
|
||||||
@@ -908,6 +947,7 @@ function startEditMode(game) {
|
|||||||
versionInput.value = game.version || "";
|
versionInput.value = game.version || "";
|
||||||
genreInput.value = game.genre || "";
|
genreInput.value = game.genre || "";
|
||||||
publisherInput.value = game.publisher || "";
|
publisherInput.value = game.publisher || "";
|
||||||
|
coverUrlInput.value = game.coverUrl || "";
|
||||||
isDuplicateInput.checked = Boolean(game.isDuplicate);
|
isDuplicateInput.checked = Boolean(game.isDuplicate);
|
||||||
yearInput.value = game.year || "";
|
yearInput.value = game.year || "";
|
||||||
purchasePriceInput.value = game.purchasePrice != null ? game.purchasePrice : "";
|
purchasePriceInput.value = game.purchasePrice != null ? game.purchasePrice : "";
|
||||||
@@ -923,10 +963,20 @@ function startEditMode(game) {
|
|||||||
function resetEditMode() {
|
function resetEditMode() {
|
||||||
editingGameId = null;
|
editingGameId = null;
|
||||||
gameForm.reset();
|
gameForm.reset();
|
||||||
|
coverUrlInput.value = "";
|
||||||
gameSubmitBtn.textContent = "Ajouter le jeu";
|
gameSubmitBtn.textContent = "Ajouter le jeu";
|
||||||
cancelEditBtn.classList.add("hidden");
|
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) {
|
function updateScannerStatus(message) {
|
||||||
if (scannerStatus) {
|
if (scannerStatus) {
|
||||||
scannerStatus.textContent = message;
|
scannerStatus.textContent = message;
|
||||||
|
|||||||
@@ -167,6 +167,10 @@
|
|||||||
Éditeur
|
Éditeur
|
||||||
<input id="publisherInput" placeholder="Square Enix" />
|
<input id="publisherInput" placeholder="Square Enix" />
|
||||||
</label>
|
</label>
|
||||||
|
<label>
|
||||||
|
Pochette (image)
|
||||||
|
<input id="coverFileInput" type="file" accept="image/*" />
|
||||||
|
</label>
|
||||||
<label>
|
<label>
|
||||||
Année
|
Année
|
||||||
<input id="yearInput" type="number" min="1970" max="2100" placeholder="2001" />
|
<input id="yearInput" type="number" min="1970" max="2100" placeholder="2001" />
|
||||||
@@ -194,6 +198,7 @@
|
|||||||
<button id="gameSubmitBtn" type="submit">Ajouter le jeu</button>
|
<button id="gameSubmitBtn" type="submit">Ajouter le jeu</button>
|
||||||
<button id="cancelEditBtn" type="button" class="btn-secondary hidden">Annuler edition</button>
|
<button id="cancelEditBtn" type="button" class="btn-secondary hidden">Annuler edition</button>
|
||||||
</form>
|
</form>
|
||||||
|
<input id="coverUrlInput" type="hidden" />
|
||||||
|
|
||||||
<div id="gamesList" class="games-list"></div>
|
<div id="gamesList" class="games-list"></div>
|
||||||
</section>
|
</section>
|
||||||
@@ -201,6 +206,7 @@
|
|||||||
|
|
||||||
<template id="gameCardTemplate">
|
<template id="gameCardTemplate">
|
||||||
<article class="game-card">
|
<article class="game-card">
|
||||||
|
<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>
|
||||||
<p class="game-meta"></p>
|
<p class="game-meta"></p>
|
||||||
|
|||||||
20
styles.css
20
styles.css
@@ -471,6 +471,21 @@ button {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game-cover {
|
||||||
|
width: 56px;
|
||||||
|
height: 76px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #ccd8e6;
|
||||||
|
object-fit: cover;
|
||||||
|
background: #f0f4f9;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-main {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.game-card.editing {
|
.game-card.editing {
|
||||||
border-color: color-mix(in hsl, var(--accent-2), white 35%);
|
border-color: color-mix(in hsl, var(--accent-2), white 35%);
|
||||||
box-shadow: 0 0 0 2px color-mix(in hsl, var(--accent-2), white 80%);
|
box-shadow: 0 0 0 2px color-mix(in hsl, var(--accent-2), white 80%);
|
||||||
@@ -557,6 +572,11 @@ button {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game-cover {
|
||||||
|
width: 48px;
|
||||||
|
height: 64px;
|
||||||
|
}
|
||||||
|
|
||||||
.game-actions {
|
.game-actions {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
|
|||||||
Reference in New Issue
Block a user