Lot 4 : Grille servie depuis l'instantané et requêtes allégées

- Grille : lecture de l'instantané grid_rows (gamme INIT et dernières gammes
  enregistrées reportées), recalcul en arrière-plan au-delà de 20 h, version de
  format dans chaque ligne pour écarter les anciens instantanés.
- Navigateur : lignes réutilisées au retour sur la Grille (< 10 min), Grille
  conservée pendant un changement de magasin ou une actualisation, React.memo
  sur la Grille, les filtres et la barre du bas, en-tête mémorisé.
- Fiche produit : toutes les lectures en parallèle.
- Ventes par mois et stock sans vente : plages de dates au lieu de TO_CHAR.
- Accueil : vagues de 12 appels, cache de 30 min.
- API v1 : comptage et page en parallèle, last_used_at au plus une fois par
  minute, version interne non exposée.
- Plan : suivi du lot 4, avec ce qui reste à valider sur la base réelle.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AcA129WzGTH6zxPHFBGWmn
This commit is contained in:
Claude committed 2026-10-03 21:15:33 +00:00
1 parent 220ca2263a
commit b7ee413d67
13 files changed
+393 -119

No files matched your search

+32 -3
View File
@@ -114,9 +114,33 @@ export function GridClient({ codeFournisseur, nomFournisseur, fournisseurs, maga
const forceRefresh = refreshRequest !== servedRefreshRef.current;
if (forceRefresh) params.set("refresh", "1");
const cle = `${codeFournisseur}:${magasin || "TOTAL"}`;
const { rowsMeta, rows: lignesEnMemoire, setRowsMeta } = useGridStore.getState();
// Retour sur la Grille (même fournisseur, même magasin, chargée il y a
// moins de 10 min) : les lignes sont encore en mémoire, rien à retélécharger.
if (!forceRefresh && rowsMeta?.key === cle && lignesEnMemoire.length > 0
&& Date.now() - rowsMeta.loadedAt < 10 * 60 * 1000) {
setRowsLoaded(lignesEnMemoire.length);
setTotalRows(lignesEnMemoire.length);
setIsLoadingRows(false);
return;
}
// Changement de magasin (ou rechargement) sur le même fournisseur : la
// Grille reste affichée — les lignes portent déjà le détail par magasin,
// la bascule est immédiate — et les chiffres complétés pour ce magasin
// remplacent l'affichage d'un bloc, une fois reçus.
const memeFournisseur = rowsMeta?.key.startsWith(`${codeFournisseur}:`) === true && lignesEnMemoire.length > 0;
const accumulatedRows: ProductRow[] = [];
let lastFlush = 0;
setRows([]);
if (!memeFournisseur) {
setRows([]);
// Les lignes vont être remplacées : tant que ce chargement n'est pas
// terminé, elles ne correspondent plus à aucune clé.
setRowsMeta(null);
}
setRowsLoaded(0);
setTotalRows(null);
setLoadError(null);
@@ -130,10 +154,12 @@ export function GridClient({ codeFournisseur, nomFournisseur, fournisseurs, maga
// Les lignes déjà poussées dans le store ne doivent plus bouger : on lui
// passe une copie, et on continue d'accumuler dans le tableau privé.
const flush = (loaded: number, total: number | null, final = false) => {
setRows(final ? accumulatedRows : accumulatedRows.slice());
setRowsLoaded(loaded);
setTotalRows(total);
lastFlush = Date.now();
// Même fournisseur : on garde les lignes affichées jusqu'au bout.
if (memeFournisseur && !final) return;
setRows(final ? accumulatedRows : accumulatedRows.slice());
};
async function loadRows() {
@@ -184,6 +210,7 @@ export function GridClient({ codeFournisseur, nomFournisseur, fournisseurs, maga
}
flush(loaded, total, true);
setRowsMeta({ key: cle, loadedAt: Date.now() });
if (forceRefresh) servedRefreshRef.current = refreshRequest;
} catch (error) {
if (!controller.signal.aborted) {
@@ -265,7 +292,9 @@ export function GridClient({ codeFournisseur, nomFournisseur, fournisseurs, maga
<div className="flex items-center justify-between gap-3 text-[13px] text-[var(--text-primary)]">
<span className="flex items-center gap-2">
<Loader2 className="h-4 w-4 animate-spin text-[var(--accent)]" />
Chargement des produits de {nomFournisseur}…
{nbRows > 0
? "Mise à jour des chiffres… la grille reste utilisable."
: `Chargement des produits de ${nomFournisseur}…`}
</span>
<span className="tabular-nums text-[var(--text-secondary)]">
{totalRows