chore(borne): memoisation loaders data.js + contraste a11y selection (#69)
This commit is contained in:
parent
7575d0458a
commit
6c431af197
2 changed files with 63 additions and 75 deletions
|
|
@ -1427,8 +1427,8 @@ button {
|
||||||
|
|
||||||
/* Selected state — mirrors aria-pressed=true */
|
/* Selected state — mirrors aria-pressed=true */
|
||||||
.composer-card--selected {
|
.composer-card--selected {
|
||||||
border-color: var(--color-border-active);
|
border-color: var(--color-brand-yellow-dk); /* jaune fonce : contraste a11y 1.4.11 */
|
||||||
box-shadow: 0 0 0 3px rgba(255, 199, 44, 0.45), var(--shadow-card);
|
box-shadow: 0 0 0 3px rgba(230, 168, 0, 0.55), var(--shadow-card);
|
||||||
background: rgba(255, 199, 44, 0.06);
|
background: rgba(255, 199, 44, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,13 @@ const MENUS_URL = '/api/menus';
|
||||||
* encore en place : bascule sur '/api/allergens' differee. */
|
* encore en place : bascule sur '/api/allergens' differee. */
|
||||||
const ALLERGENS_URL = 'data/allergens.json';
|
const ALLERGENS_URL = 'data/allergens.json';
|
||||||
|
|
||||||
/** @type {Array|null} — in-memory cache to avoid repeated fetches */
|
/* Memoisation par PROMESSE (pas par resultat) : N appelants concurrents au meme
|
||||||
let _categoriesCache = null;
|
* chargement partagent UNE seule requete reseau (evite les fetch /api/* redondants
|
||||||
|
* au DOMContentLoaded de products.html). Sur echec, la promesse est reinitialisee
|
||||||
/** @type {Object|null} */
|
* pour autoriser un nouvel essai. */
|
||||||
let _productsCache = null;
|
let _categoriesPromise = null;
|
||||||
|
let _productsPromise = null;
|
||||||
/** @type {Array|null} */
|
let _allergensPromise = null;
|
||||||
let _allergensCache = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recupere une collection enveloppee de l'API et renvoie le tableau `data`.
|
* Recupere une collection enveloppee de l'API et renvoie le tableau `data`.
|
||||||
|
|
@ -46,16 +45,13 @@ async function fetchCollection(url) {
|
||||||
* Fetches and caches the categories list (forme borne : id, title, slug, image).
|
* Fetches and caches the categories list (forme borne : id, title, slug, image).
|
||||||
* @returns {Promise<Array>}
|
* @returns {Promise<Array>}
|
||||||
*/
|
*/
|
||||||
export async function loadCategories() {
|
export function loadCategories() {
|
||||||
if (_categoriesCache) return _categoriesCache;
|
if (!_categoriesPromise) {
|
||||||
const rows = await fetchCollection(CATEGORIES_URL);
|
_categoriesPromise = fetchCollection(CATEGORIES_URL)
|
||||||
_categoriesCache = rows.map(c => ({
|
.then(rows => rows.map(c => ({ id: c.id, title: c.name, slug: c.slug, image: c.image_path })))
|
||||||
id: c.id,
|
.catch(e => { _categoriesPromise = null; throw e; });
|
||||||
title: c.name,
|
}
|
||||||
slug: c.slug,
|
return _categoriesPromise;
|
||||||
image: c.image_path,
|
|
||||||
}));
|
|
||||||
return _categoriesCache;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -65,52 +61,38 @@ export async function loadCategories() {
|
||||||
* prix NORMAL (le supplement Maxi est gere par le composeur cote borne).
|
* prix NORMAL (le supplement Maxi est gere par le composeur cote borne).
|
||||||
* @returns {Promise<Object>}
|
* @returns {Promise<Object>}
|
||||||
*/
|
*/
|
||||||
export async function loadProducts() {
|
export function loadProducts() {
|
||||||
if (_productsCache) return _productsCache;
|
if (_productsPromise) return _productsPromise;
|
||||||
|
|
||||||
const [categories, products, menus] = await Promise.all([
|
_productsPromise = Promise.all([
|
||||||
loadCategories(),
|
loadCategories(),
|
||||||
fetchCollection(PRODUCTS_URL),
|
fetchCollection(PRODUCTS_URL),
|
||||||
fetchCollection(MENUS_URL),
|
fetchCollection(MENUS_URL),
|
||||||
]);
|
]).then(([categories, products, menus]) => {
|
||||||
|
const slugByCategoryId = {};
|
||||||
|
const bySlug = {};
|
||||||
|
for (const cat of categories) {
|
||||||
|
slugByCategoryId[cat.id] = cat.slug;
|
||||||
|
bySlug[cat.slug] = [];
|
||||||
|
}
|
||||||
|
for (const p of products) {
|
||||||
|
const slug = slugByCategoryId[p.category_id];
|
||||||
|
if (slug === undefined) continue;
|
||||||
|
bySlug[slug].push({ id: p.id, nom: p.name, prix: p.price_cents, image: p.image_path, type: 'produit' });
|
||||||
|
}
|
||||||
|
for (const m of menus) {
|
||||||
|
const slug = slugByCategoryId[m.category_id];
|
||||||
|
if (slug === undefined) continue;
|
||||||
|
bySlug[slug].push({ id: m.id, nom: m.name, prix: m.price_normal_cents, image: m.image_path, type: 'menu' });
|
||||||
|
}
|
||||||
|
return bySlug;
|
||||||
|
}).catch(e => { _productsPromise = null; throw e; });
|
||||||
|
|
||||||
const slugByCategoryId = {};
|
return _productsPromise;
|
||||||
const bySlug = {};
|
|
||||||
for (const cat of categories) {
|
|
||||||
slugByCategoryId[cat.id] = cat.slug;
|
|
||||||
bySlug[cat.slug] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const p of products) {
|
|
||||||
const slug = slugByCategoryId[p.category_id];
|
|
||||||
if (slug === undefined) continue;
|
|
||||||
bySlug[slug].push({
|
|
||||||
id: p.id,
|
|
||||||
nom: p.name,
|
|
||||||
prix: p.price_cents,
|
|
||||||
image: p.image_path,
|
|
||||||
type: 'produit',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const m of menus) {
|
|
||||||
const slug = slugByCategoryId[m.category_id];
|
|
||||||
if (slug === undefined) continue;
|
|
||||||
bySlug[slug].push({
|
|
||||||
id: m.id,
|
|
||||||
nom: m.name,
|
|
||||||
prix: m.price_normal_cents,
|
|
||||||
image: m.image_path,
|
|
||||||
type: 'menu',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_productsCache = bySlug;
|
|
||||||
return _productsCache;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {Object|null} — cache id->produit (type 'produit' uniquement) */
|
/** @type {Promise|null} — index id->produit memoise (type 'produit' uniquement) */
|
||||||
let _productsByIdCache = null;
|
let _productsByIdPromise = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Index des PRODUITS par id (type 'produit' seulement : exclut les menus, dont
|
* Index des PRODUITS par id (type 'produit' seulement : exclut les menus, dont
|
||||||
|
|
@ -119,17 +101,19 @@ let _productsByIdCache = null;
|
||||||
* affichables. Derive de loadProducts() : aucune requete reseau supplementaire.
|
* affichables. Derive de loadProducts() : aucune requete reseau supplementaire.
|
||||||
* @returns {Promise<Object<number, Object>>}
|
* @returns {Promise<Object<number, Object>>}
|
||||||
*/
|
*/
|
||||||
export async function loadProductsById() {
|
export function loadProductsById() {
|
||||||
if (_productsByIdCache) return _productsByIdCache;
|
if (!_productsByIdPromise) {
|
||||||
const bySlug = await loadProducts();
|
_productsByIdPromise = loadProducts().then(bySlug => {
|
||||||
const byId = {};
|
const byId = {};
|
||||||
for (const slug of Object.keys(bySlug)) {
|
for (const slug of Object.keys(bySlug)) {
|
||||||
for (const item of bySlug[slug]) {
|
for (const item of bySlug[slug]) {
|
||||||
if (item.type === 'produit') byId[item.id] = item;
|
if (item.type === 'produit') byId[item.id] = item;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return byId;
|
||||||
|
}).catch(e => { _productsByIdPromise = null; throw e; });
|
||||||
}
|
}
|
||||||
_productsByIdCache = byId;
|
return _productsByIdPromise;
|
||||||
return _productsByIdCache;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -153,12 +137,16 @@ export async function loadMenu(id) {
|
||||||
* la reponse est un tableau nu (pas d'enveloppe), conserve tel quel.
|
* la reponse est un tableau nu (pas d'enveloppe), conserve tel quel.
|
||||||
* @returns {Promise<Array>}
|
* @returns {Promise<Array>}
|
||||||
*/
|
*/
|
||||||
export async function loadAllergens() {
|
export function loadAllergens() {
|
||||||
if (_allergensCache) return _allergensCache;
|
if (!_allergensPromise) {
|
||||||
const res = await fetch(ALLERGENS_URL);
|
_allergensPromise = fetch(ALLERGENS_URL)
|
||||||
if (!res.ok) throw new Error(`Failed to load allergens: HTTP ${res.status}`);
|
.then(res => {
|
||||||
_allergensCache = await res.json();
|
if (!res.ok) throw new Error(`Failed to load allergens: HTTP ${res.status}`);
|
||||||
return _allergensCache;
|
return res.json();
|
||||||
|
})
|
||||||
|
.catch(e => { _allergensPromise = null; throw e; });
|
||||||
|
}
|
||||||
|
return _allergensPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue