Files
CollectFlow/src/features/grid/components/sync-qlik-button.tsx
T
Claude 7da4c6d5fd perf: lot 1 — chargements plus rapides et corrections, sans changement visuel
Grille (navigateur)
- « Rafraîchir » ne passe plus par l'URL : le paramètre _refresh restait
  mémorisé et chaque retour par le menu forçait un recalcul serveur complet
  (migration du store pour nettoyer les valeurs déjà enregistrées).
- Lignes reçues regroupées toutes les 300 ms au lieu de reconstruire tout le
  tableau à chaque paquet de 150 (chargement quadratique).
- Sélection indexée par code article (getRowId) : après un filtre, l'action
  groupée visait d'autres produits. Seules les lignes affichées comptent.
- Recherche : un passage par ligne au lieu d'un par colonne ; formateurs de
  nombres partagés (lib/format.ts) ; colonnes mensuelles mémorisées ;
  plus de transition-all sur les lignes positionnées par transform.
- exceljs, jspdf et xlsx chargés au clic seulement.

Grille (serveur)
- Requêtes Qlik et snapshot lancées en parallèle de la phase SQL.
- Cache mémoire borné (LRU) ; calcul en cours réutilisé même en forcé ;
  flux interrompu quand le client part.
- Après un enregistrement, les gammes sont reportées dans le cache et dans
  grid_rows au lieu d'invalider (recalcul de ~40 s évité).

Données et API
- lib/ff-cache.ts : cache mémoire 30 min des lectures FF (stock, hit-parade,
  CA mensuel, fournisseurs, dernière réception, publicités), vidé en fin de
  synchro nocturne ; erreurs jamais gardées.
- Pool PostgreSQL unique (globalThis), réglage sans workers parallèles posé
  une fois par connexion au lieu d'une transaction autour de chaque requête.
- Délai maximal sur tous les appels à l'API FF.
- Index session_snapshots ; commandes : plus de double rechargement ;
  synchro : saisies regroupées ; paramètres : configuration lue une fois ;
  journal de capture en ajout seul, rien de formaté sans capture ouverte.

Corrections
- Historique : l'échec de chargement s'affiche (au lieu de « Aucun snapshot »).
- Sessions et validations enregistrent le magasin affiché, plus « TOTAL ».
- Publicités : libellé du statut « passées ».

Plan complet : docs/plan-optimisation-webui.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AcA129WzGTH6zxPHFBGWmn
2026-10-03 20:28:14 +00:00

71 lines
3.0 KiB
TypeScript

"use client";
import { useGridStore } from "@/features/grid/store/use-grid-store";
import { Loader2, RefreshCw, CheckCircle, AlertCircle } from "lucide-react";
import { useQlikSyncJob } from "@/features/qlik-sync/use-qlik-sync-job";
interface SyncQlikButtonProps {
/** Code fournisseur affiché (pour sync ciblée) */
codeFournisseur?: string;
/** Date ISO de dernière maj Qlik pour ce fournisseur (max des lignes) */
lastUpdate?: string | null;
}
/** Date + heure de dernière synchro, ou "jamais". */
function formatLastUpdate(iso?: string | null): string {
if (!iso) return "jamais";
const d = new Date(iso);
if (isNaN(d.getTime())) return "jamais";
return d.toLocaleString("fr-FR", { day: "2-digit", month: "2-digit", year: "numeric", hour: "2-digit", minute: "2-digit" });
}
/**
* Bouton admin : synchronise depuis Qlik les métriques réseau du fournisseur affiché
* (POST /api/qlik/sync?fournisseur=…) puis recharge la grille. Affiche la dernière maj.
*
* Toute la mécanique de job (POST puis polling GET, reprise après reload) vit
* dans `useQlikSyncJob`, partagée avec la fiche produit.
*/
export function SyncQlikButton({ codeFournisseur, lastUpdate }: SyncQlikButtonProps) {
/** Recharge la grille en ignorant le cache serveur (la barre de résumé suit). */
const requestRefresh = useGridStore((s) => s.requestRefresh);
const { status, message, start } = useQlikSyncJob({
target: { mode: "fournisseur", fournisseur: codeFournisseur },
onSuccess: requestRefresh,
});
return (
<div className="flex items-center gap-2">
<span
className="text-[10px] leading-tight text-right whitespace-nowrap hidden lg:block"
style={{ color: status === "error" ? "var(--accent-error)" : "var(--text-muted)" }}
title={message || undefined}
>
{status === "error"
? message
: status === "running"
? (message || `Extraction Qlik…`)
: `MAJ Qlik · ${formatLastUpdate(lastUpdate)}`}
</span>
<button
onClick={start}
disabled={status === "running"}
className="btn-action btn-action-secondary flex items-center gap-1.5 disabled:opacity-60"
title={message || `Synchroniser les données réseau Qlik · MAJ ${formatLastUpdate(lastUpdate)}`}
>
{status === "running" ? (
<Loader2 className="w-3.5 h-3.5 animate-spin" />
) : status === "success" ? (
<CheckCircle className="w-3.5 h-3.5 text-emerald-500" />
) : status === "error" ? (
<AlertCircle className="w-3.5 h-3.5 text-rose-500" />
) : (
<RefreshCw className="w-3.5 h-3.5" />
)}
{status === "running" ? "Sync en cours…" : "Sync Qlik"}
</button>
</div>
);
}