Feature: add global anti-duplicate game search
This commit is contained in:
99
app.js
99
app.js
@@ -46,10 +46,13 @@ const backupBtn = document.getElementById("backupBtn");
|
||||
const restoreMergeBtn = document.getElementById("restoreMergeBtn");
|
||||
const restoreReplaceBtn = document.getElementById("restoreReplaceBtn");
|
||||
const restoreFileInput = document.getElementById("restoreFileInput");
|
||||
const quickSearchInput = document.getElementById("quickSearchInput");
|
||||
const quickSearchResults = document.getElementById("quickSearchResults");
|
||||
const gamesList = document.getElementById("gamesList");
|
||||
const gameCardTemplate = document.getElementById("gameCardTemplate");
|
||||
let editingGameId = null;
|
||||
let pendingRestoreMode = "merge";
|
||||
let quickSearchTerm = "";
|
||||
|
||||
toolsToggleBtn.addEventListener("click", () => {
|
||||
toolsDrawer.classList.toggle("open");
|
||||
@@ -78,6 +81,11 @@ document.addEventListener("keydown", (event) => {
|
||||
}
|
||||
});
|
||||
|
||||
quickSearchInput.addEventListener("input", (event) => {
|
||||
quickSearchTerm = event.target.value.trim();
|
||||
renderSearchResults();
|
||||
});
|
||||
|
||||
platformForm.addEventListener("submit", async (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -430,6 +438,7 @@ function render() {
|
||||
renderBrandTabs();
|
||||
renderConsoleTabs();
|
||||
renderGames();
|
||||
renderSearchResults();
|
||||
}
|
||||
|
||||
function renderDataMode() {
|
||||
@@ -482,6 +491,96 @@ function renderDataMode() {
|
||||
}
|
||||
}
|
||||
|
||||
function findBrandByConsole(consoleName) {
|
||||
for (const [brand, consoles] of Object.entries(state.brands)) {
|
||||
if (Array.isArray(consoles) && consoles.includes(consoleName)) {
|
||||
return brand;
|
||||
}
|
||||
}
|
||||
return "INCONNUE";
|
||||
}
|
||||
|
||||
function collectAllGames() {
|
||||
const all = [];
|
||||
for (const [consoleName, games] of Object.entries(state.gamesByConsole)) {
|
||||
const brand = findBrandByConsole(consoleName);
|
||||
for (const game of games || []) {
|
||||
all.push({ ...game, consoleName, brand });
|
||||
}
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
function renderSearchResults() {
|
||||
if (!quickSearchResults) {
|
||||
return;
|
||||
}
|
||||
|
||||
const query = quickSearchTerm.trim().toLowerCase();
|
||||
if (!query) {
|
||||
quickSearchResults.innerHTML =
|
||||
'<p class="empty">Saisis un titre pour verifier si tu possedes deja le jeu.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
const normalizedQuery = query.replace(/\s+/g, " ");
|
||||
const allGames = collectAllGames();
|
||||
const matches = allGames.filter((game) => {
|
||||
const title = String(game.title || "").toLowerCase();
|
||||
return title.includes(normalizedQuery);
|
||||
});
|
||||
|
||||
const exactMatches = allGames.filter((game) => {
|
||||
const title = String(game.title || "")
|
||||
.toLowerCase()
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
return title === normalizedQuery;
|
||||
});
|
||||
|
||||
if (!matches.length) {
|
||||
quickSearchResults.innerHTML =
|
||||
'<p class="search-status">Aucun jeu trouve dans ta collection.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
const maxShown = 20;
|
||||
const shown = matches.slice(0, maxShown);
|
||||
const header =
|
||||
exactMatches.length > 0
|
||||
? `<p class="search-status">Deja possede: OUI (${exactMatches.length} correspondance${exactMatches.length > 1 ? "s" : ""} exacte${exactMatches.length > 1 ? "s" : ""}).</p>`
|
||||
: `<p class="search-status">Jeu similaire trouve: ${matches.length} resultat${matches.length > 1 ? "s" : ""}.</p>`;
|
||||
|
||||
const items = shown
|
||||
.map((game) => {
|
||||
const meta = [
|
||||
`${game.brand} / ${game.consoleName}`,
|
||||
game.version ? `Version: ${game.version}` : null,
|
||||
game.isDuplicate ? "Double: OUI" : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" | ");
|
||||
return `<article class="search-hit"><strong>${escapeHtml(game.title || "")}</strong><p>${escapeHtml(meta)}</p></article>`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
const more =
|
||||
matches.length > maxShown
|
||||
? `<p class="empty">+${matches.length - maxShown} autre(s) resultat(s).</p>`
|
||||
: "";
|
||||
|
||||
quickSearchResults.innerHTML = `${header}${items}${more}`;
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
return String(text)
|
||||
.replaceAll("&", "&")
|
||||
.replaceAll("<", "<")
|
||||
.replaceAll(">", ">")
|
||||
.replaceAll('"', """)
|
||||
.replaceAll("'", "'");
|
||||
}
|
||||
|
||||
function renderBrandTabs() {
|
||||
const brands = Object.keys(state.brands);
|
||||
brandTabs.innerHTML = "";
|
||||
|
||||
Reference in New Issue
Block a user