"use client"; import { useMemo, useRef } from "react"; import { useVirtualizer } from "@tanstack/react-virtual"; import { useGridStore } from "@/features/grid/store/use-grid-store"; import type { ProductRow } from "@/types/grid"; import { PackageX } from "lucide-react"; function getNoSalesIn6Months(rows: ProductRow[]): ProductRow[] { const sixMonthsAgo = new Date(); sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6); return rows.filter(row => { if (row.derniereVente) { return new Date(row.derniereVente) < sixMonthsAgo; } // Fallback : calcul depuis sales12m const salesMonths = Object.entries(row.sales12m || {}) .filter(([, qty]) => qty > 0) .map(([m]) => m) .sort(); if (salesMonths.length === 0) return true; const last = salesMonths[salesMonths.length - 1]; const lastDate = new Date(parseInt(last.slice(0, 4)), parseInt(last.slice(4, 6)) - 1, 1); return lastDate < sixMonthsAgo; }); } function formatDate(iso?: string): string { if (!iso) return "—"; return new Date(iso).toLocaleDateString("fr-FR", { day: "2-digit", month: "2-digit", year: "numeric" }); } function daysSince(iso?: string): number | null { if (!iso) return null; return Math.floor((Date.now() - new Date(iso).getTime()) / 86_400_000); } function formatCa(ca: number): string { return ca.toLocaleString("fr-FR", { style: "currency", currency: "EUR", maximumFractionDigits: 0 }); } const GAMME_COLORS: Record = { A: "bg-emerald-500/15 text-emerald-700", B: "bg-blue-500/15 text-blue-700", C: "bg-amber-500/15 text-amber-700", Y: "bg-purple-500/15 text-purple-700", Z: "bg-red-500/15 text-red-700", }; export function NoSalesTab() { const rows = useGridStore(s => s.rows); const scrollRef = useRef(null); const filtered = useMemo(() => { const result = getNoSalesIn6Months(rows); // Tri : produits les plus anciens en premier, puis par stock décroissant return result.sort((a, b) => { const da = a.derniereVente ? new Date(a.derniereVente).getTime() : 0; const db = b.derniereVente ? new Date(b.derniereVente).getTime() : 0; if (da !== db) return da - db; return (b.stockActuel ?? 0) - (a.stockActuel ?? 0); }); }, [rows]); // eslint-disable-next-line react-hooks/incompatible-library const rowVirtualizer = useVirtualizer({ count: filtered.length, getScrollElement: () => scrollRef.current, estimateSize: () => 36, overscan: 12, }); if (rows.length === 0) { return (
Chargement des données en cours…
); } if (filtered.length === 0) { return (

Aucun produit sans vente depuis 6 mois.

); } return (
{/* Résumé */}
{filtered.length.toLocaleString("fr-FR")} {" "}produit{filtered.length > 1 ? "s" : ""} sans vente depuis plus de 6 mois {" "}({rows.length.toLocaleString("fr-FR")} références au total)
{/* Tableau */}
{["Code", "Libellé", "Réf.", "Gamme", "Dernière vente", "Jours", "Stock", "CA 12m", "Mag."].map(col => (
{col}
))}
{rowVirtualizer.getVirtualItems().map((virtualRow) => { const row = filtered[virtualRow.index]; const i = virtualRow.index; const days = daysSince(row.derniereVente); const gamme = row.codeGamme?.trim().toUpperCase() ?? "—"; const gammeClass = GAMME_COLORS[gamme] ?? "bg-zinc-500/15 text-zinc-600"; return (
{row.codein}
{row.libelle1}
{row.reference || "—"}
{gamme}
{formatDate(row.derniereVente)}
365 ? "var(--color-red, #ef4444)" : "var(--text-secondary)" }}> {days != null ? days.toLocaleString("fr-FR") : "—"}
{row.stockActuel != null ? row.stockActuel.toLocaleString("fr-FR") : "—"}
{row.totalCa > 0 ? formatCa(row.totalCa) : "—"}
{row.nbMagasinsReseau != null ? row.nbMagasinsReseau : "—"}
); })}
); } /** Compte les produits sans vente depuis 6 mois — utilisé pour le badge dans le switcher */ export function countNoSalesIn6Months(rows: ProductRow[]): number { return getNoSalesIn6Months(rows).length; }