From 32f9a275633e3dca6b191a19f899018ccda79ed7 Mon Sep 17 00:00:00 2001 From: R0m1k3 <10197253+R0m1k3@users.noreply.github.com> Date: Mon, 22 Jun 2026 12:12:48 +0000 Subject: [PATCH] Fix Qlik network column sorting --- src/features/grid/components/heatmap-grid.tsx | 69 +++++++++++++------ 1 file changed, 48 insertions(+), 21 deletions(-) diff --git a/src/features/grid/components/heatmap-grid.tsx b/src/features/grid/components/heatmap-grid.tsx index 32b4ef1..eab6461 100644 --- a/src/features/grid/components/heatmap-grid.tsx +++ b/src/features/grid/components/heatmap-grid.tsx @@ -92,6 +92,27 @@ interface HeatmapGridProps { const EMPTY_DRAFT_CHANGES: Record = {}; +const QLIK_NETWORK_COLUMN_IDS = new Set([ + "caReseau", + "qteReseau", + "nbMagasinsReseau", + "tauxPresenceReseau", + "caParMagasinReseau", + "margePctReseau", +]); + +function getQlikNetworkSortValue(value: number | null | undefined, columnId: string, sorting: SortingState): number | undefined { + if (value != null) return value; + + const activeSort = sorting.find((sort) => sort.id === columnId); + if (!activeSort) return undefined; + + // TanStack inverse le résultat en tri descendant. On utilise donc une + // sentinelle dépendante du sens de tri pour garder les valeurs absentes + // toujours en bas, en ascendant comme en descendant. + return activeSort.desc ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY; +} + // ========================================================================= // OPTIMISATION PERFORMANCES (React.memo + Zustand Selectors granulaires) // ========================================================================= @@ -178,7 +199,7 @@ const GridRow = React.memo(({ virtualRow, row, rowHeight, isSelected, columnVisi > {row.getVisibleCells().map((cell: Cell) => { const isFlexible = cell.column.id === "libelle1" || cell.column.id === "libelle3"; - const isCenter = cell.column.id === "totalQuantite" || cell.column.id === "totalCa" || cell.column.id === "totalMarge" || cell.column.id.startsWith("month_") || cell.column.id === "gammeInitial" || cell.column.id === "caReseau" || cell.column.id === "qteReseau" || cell.column.id === "nbMagasinsReseau" || cell.column.id === "caParMagasinReseau" || cell.column.id === "margePctReseau" || cell.column.id === "tauxPresenceReseau" || cell.column.id === "gamme"; + const isCenter = cell.column.id === "totalQuantite" || cell.column.id === "totalCa" || cell.column.id === "totalMarge" || cell.column.id.startsWith("month_") || cell.column.id === "gammeInitial" || QLIK_NETWORK_COLUMN_IDS.has(cell.column.id) || cell.column.id === "gamme"; const size = cell.column.getSize(); return ( getQlikNetworkSortValue(row.caReseau, "caReseau", sorting), header: () =>
CA
Réseau
, size: 90, - cell: ({ getValue }) => { - const val = getValue(); + cell: ({ row }) => { + const val = row.original.caReseau; return (
{val != null ? val.toLocaleString("fr-FR", { style: "currency", currency: "EUR", maximumFractionDigits: 0 }) : "-"} @@ -519,11 +541,12 @@ export function HeatmapGrid({ onSelectionChange, isAdmin }: HeatmapGridProps) { }, }, { - accessorKey: "qteReseau", + id: "qteReseau", + accessorFn: (row) => getQlikNetworkSortValue(row.qteReseau, "qteReseau", sorting), header: () =>
Qté
Réseau
, size: 80, - cell: ({ getValue }) => { - const val = getValue(); + cell: ({ row }) => { + const val = row.original.qteReseau; return (
{val != null ? Math.round(val).toLocaleString("fr-FR") : "-"} @@ -532,11 +555,12 @@ export function HeatmapGrid({ onSelectionChange, isAdmin }: HeatmapGridProps) { }, }, { - accessorKey: "nbMagasinsReseau", + id: "nbMagasinsReseau", + accessorFn: (row) => getQlikNetworkSortValue(row.nbMagasinsReseau, "nbMagasinsReseau", sorting), header: () =>
Magasins
/ 270
, size: 80, - cell: ({ getValue }) => { - const val = getValue(); + cell: ({ row }) => { + const val = row.original.nbMagasinsReseau; return (
{val != null ? val : "-"} @@ -545,11 +569,12 @@ export function HeatmapGrid({ onSelectionChange, isAdmin }: HeatmapGridProps) { }, }, { - accessorKey: "tauxPresenceReseau", + id: "tauxPresenceReseau", + accessorFn: (row) => getQlikNetworkSortValue(row.tauxPresenceReseau, "tauxPresenceReseau", sorting), header: () =>
% Prés.
Réseau
, size: 70, - cell: ({ getValue }) => { - const val = getValue(); + cell: ({ row }) => { + const val = row.original.tauxPresenceReseau; if (val == null) return
-
; const pct = Math.round(val * 100); const color = pct >= 66 ? "text-emerald-500" : pct >= 33 ? "text-amber-500" : "text-rose-500"; @@ -561,11 +586,12 @@ export function HeatmapGrid({ onSelectionChange, isAdmin }: HeatmapGridProps) { }, }, { - accessorKey: "caParMagasinReseau", + id: "caParMagasinReseau", + accessorFn: (row) => getQlikNetworkSortValue(row.caParMagasinReseau, "caParMagasinReseau", sorting), header: () =>
CA / Mag
Réseau
, size: 85, - cell: ({ getValue }) => { - const val = getValue(); + cell: ({ row }) => { + const val = row.original.caParMagasinReseau; return (
{val != null ? val.toLocaleString("fr-FR", { style: "currency", currency: "EUR", maximumFractionDigits: 0 }) : "-"} @@ -574,11 +600,12 @@ export function HeatmapGrid({ onSelectionChange, isAdmin }: HeatmapGridProps) { }, }, { - accessorKey: "margePctReseau", + id: "margePctReseau", + accessorFn: (row) => getQlikNetworkSortValue(row.margePctReseau, "margePctReseau", sorting), header: () =>
Marge %
Réseau
, size: 75, - cell: ({ getValue }) => { - const val = getValue(); + cell: ({ row }) => { + const val = row.original.margePctReseau; if (val == null) return
-
; const pct = Math.abs(val) <= 1 ? val * 100 : val; const color = pct >= 30 ? "text-emerald-500" : pct >= 15 ? "text-amber-500" : "text-rose-500"; @@ -696,7 +723,7 @@ export function HeatmapGrid({ onSelectionChange, isAdmin }: HeatmapGridProps) { size: 110, cell: ({ row }) => , }, - ], [MONTHS_12, activeMagasin, isAdmin]); // activeMagasin déclenche re-render des cellules mensuelles et totaux + ], [MONTHS_12, activeMagasin, isAdmin, sorting]); // activeMagasin déclenche re-render des cellules mensuelles et totaux const table = useReactTable({ data: filteredData, @@ -807,7 +834,7 @@ export function HeatmapGrid({ onSelectionChange, isAdmin }: HeatmapGridProps) { {headerGroup.headers.map((header) => { const isFlexible = header.column.id === "libelle1" || header.column.id === "libelle3"; - const isCenter = header.column.id === "totalQuantite" || header.column.id === "totalCa" || header.column.id === "totalMarge" || header.column.id.startsWith("month_") || header.column.id === "gammeInitial" || header.column.id === "caReseau" || header.column.id === "qteReseau" || header.column.id === "nbMagasinsReseau" || header.column.id === "caParMagasinReseau" || header.column.id === "margePctReseau" || header.column.id === "tauxPresenceReseau" || header.column.id === "gamme"; + const isCenter = header.column.id === "totalQuantite" || header.column.id === "totalCa" || header.column.id === "totalMarge" || header.column.id.startsWith("month_") || header.column.id === "gammeInitial" || QLIK_NETWORK_COLUMN_IDS.has(header.column.id) || header.column.id === "gamme"; const size = header.getSize(); return (