Step 3 migration: frontend reads API with local fallback
This commit is contained in:
80
app.js
80
app.js
@@ -11,7 +11,7 @@ const initialState = {
|
||||
};
|
||||
|
||||
const state = loadState();
|
||||
normalizeState();
|
||||
let dataMode = "local";
|
||||
|
||||
const platformForm = document.getElementById("platformForm");
|
||||
const gameForm = document.getElementById("gameForm");
|
||||
@@ -29,6 +29,7 @@ const cancelEditBtn = document.getElementById("cancelEditBtn");
|
||||
const brandTabs = document.getElementById("brandTabs");
|
||||
const consoleTabs = document.getElementById("consoleTabs");
|
||||
const gameSectionTitle = document.getElementById("gameSectionTitle");
|
||||
const dataModeInfo = document.getElementById("dataModeInfo");
|
||||
const gamesList = document.getElementById("gamesList");
|
||||
const gameCardTemplate = document.getElementById("gameCardTemplate");
|
||||
let editingGameId = null;
|
||||
@@ -188,11 +189,30 @@ cancelEditBtn.addEventListener("click", () => {
|
||||
});
|
||||
|
||||
function render() {
|
||||
renderDataMode();
|
||||
renderBrandTabs();
|
||||
renderConsoleTabs();
|
||||
renderGames();
|
||||
}
|
||||
|
||||
function renderDataMode() {
|
||||
if (!dataModeInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataMode === "api") {
|
||||
dataModeInfo.textContent = "Source: API (lecture). Ecriture DB prevue a l'etape 4.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (dataMode === "local-fallback") {
|
||||
dataModeInfo.textContent = "Source: localStorage (fallback). API indisponible ou vide.";
|
||||
return;
|
||||
}
|
||||
|
||||
dataModeInfo.textContent = "Source: localStorage";
|
||||
}
|
||||
|
||||
function renderBrandTabs() {
|
||||
const brands = Object.keys(state.brands);
|
||||
brandTabs.innerHTML = "";
|
||||
@@ -334,4 +354,60 @@ function persist() {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
|
||||
}
|
||||
|
||||
render();
|
||||
function payloadHasCatalogData(payload) {
|
||||
if (!payload || typeof payload !== "object") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const brands = payload.brands && typeof payload.brands === "object" ? payload.brands : {};
|
||||
const gamesByConsole =
|
||||
payload.gamesByConsole && typeof payload.gamesByConsole === "object" ? payload.gamesByConsole : {};
|
||||
|
||||
const consolesCount = Object.values(brands).reduce((count, consoles) => {
|
||||
if (!Array.isArray(consoles)) {
|
||||
return count;
|
||||
}
|
||||
return count + consoles.length;
|
||||
}, 0);
|
||||
|
||||
const gamesCount = Object.values(gamesByConsole).reduce((count, games) => {
|
||||
if (!Array.isArray(games)) {
|
||||
return count;
|
||||
}
|
||||
return count + games.length;
|
||||
}, 0);
|
||||
|
||||
return consolesCount > 0 || gamesCount > 0;
|
||||
}
|
||||
|
||||
async function hydrateFromApi() {
|
||||
try {
|
||||
const response = await fetch("/api/catalog/full");
|
||||
if (!response.ok) {
|
||||
throw new Error(`API error ${response.status}`);
|
||||
}
|
||||
|
||||
const payload = await response.json();
|
||||
if (!payloadHasCatalogData(payload)) {
|
||||
dataMode = "local-fallback";
|
||||
return;
|
||||
}
|
||||
|
||||
state.brands = payload.brands || {};
|
||||
state.gamesByConsole = payload.gamesByConsole || {};
|
||||
state.selectedBrand = Object.keys(state.brands)[0] || "";
|
||||
state.selectedConsole = (state.brands[state.selectedBrand] || [])[0] || "";
|
||||
dataMode = "api";
|
||||
persist();
|
||||
} catch {
|
||||
dataMode = "local-fallback";
|
||||
}
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
await hydrateFromApi();
|
||||
normalizeState();
|
||||
render();
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
|
||||
Reference in New Issue
Block a user