From 2d3a90d6c7b09a231f3d0a8c42ef1704191628ed Mon Sep 17 00:00:00 2001 From: R0m1k3 Date: Thu, 12 Mar 2026 14:25:39 +0100 Subject: [PATCH] feat: add floating summary bar component with grid statistics, draft management, snapshot, and export functionality. --- .../grid/components/floating-summary-bar.tsx | 160 +++++++++++++----- 1 file changed, 114 insertions(+), 46 deletions(-) diff --git a/src/features/grid/components/floating-summary-bar.tsx b/src/features/grid/components/floating-summary-bar.tsx index 010b689..760f509 100644 --- a/src/features/grid/components/floating-summary-bar.tsx +++ b/src/features/grid/components/floating-summary-bar.tsx @@ -3,10 +3,16 @@ import { useAiCopilotStore } from "@/features/ai-copilot/store/use-ai-copilot-st import { BulkAiAnalyzer } from "./bulk-ai-analyzer"; import { useSaveDrafts } from "@/features/grid/hooks/use-save-drafts"; -import { Loader2, CheckCircle, AlertCircle, RotateCcw, Camera } from "lucide-react"; +import { Loader2, CheckCircle, AlertCircle, RotateCcw, Camera, ChevronDown } from "lucide-react"; import { useState, useTransition } from "react"; import { saveSnapshot } from "@/features/snapshots/api/save-snapshot"; import { SuccessModal } from "@/components/shared/success-modal"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; function Stat({ label, value, sub }: { label: string; value: string; sub?: string }) { // Determine color based on label to match the prototype @@ -167,59 +173,121 @@ export function FloatingSummaryBar() {
- + + + { + const currentState = useGridStore.getState(); + const currentDrafts = currentState.draftChanges; + const currentRows = currentState.rows; - const modifiedRows = currentRows.filter(r => { - const currentGamme = currentDrafts[r.codein] ?? r.codeGamme; - return currentGamme !== r.codeGammeInit; - }); + const modifiedRows = currentRows.filter(r => { + const currentGamme = currentDrafts[r.codein] ?? r.codeGamme; + return currentGamme !== r.codeGammeInit; + }); - if (modifiedRows.length === 0) { - alert("Aucun changement détecté par rapport à l'état initial."); - return; - } + if (modifiedRows.length === 0) { + alert("Aucun changement détecté par rapport à l'état initial."); + return; + } - const changes = modifiedRows.map(r => ({ - codein: r.codein, - gtin: r.gtin, - codeFournisseur: r.codeFournisseur, - gamme: (currentDrafts[r.codein] ?? r.codeGamme) as string, - })); + const changes = modifiedRows.map(r => ({ + codein: r.codein, + gtin: r.gtin, + codeFournisseur: r.codeFournisseur, + gamme: (currentDrafts[r.codein] ?? r.codeGamme) as string, + })); - const nomFournisseur = modifiedRows[0].nomFournisseur; + const nomFournisseur = modifiedRows[0].nomFournisseur; - // Auto-save snapshot for history - await handleSnapshot(`Export ${nomFournisseur} — ${new Date().toLocaleTimeString()}`, "export"); + // Auto-save snapshot for history + await handleSnapshot(`Export ${nomFournisseur} — ${new Date().toLocaleTimeString()}`, "export"); - try { - const res = await fetch("/api/export/modified-gammes", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ nomFournisseur, changes }), - }); + try { + const res = await fetch("/api/export/modified-gammes", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ nomFournisseur, changes }), + }); - if (!res.ok) throw new Error("Erreur lors de l'export"); + if (!res.ok) throw new Error("Erreur lors de l'export"); - const blob = await res.blob(); - const url = window.URL.createObjectURL(blob); - const a = document.createElement("a"); - a.href = url; - a.download = `Modifications_Gammes_${nomFournisseur.replace(/\s+/g, "_")}_${new Date().toISOString().slice(0, 10)}.xlsx`; - a.click(); - window.URL.revokeObjectURL(url); - } catch (error) { - console.error(error); - alert("Une erreur s'est produite lors de l'export Excel."); - } - }} - className="btn-action btn-action-secondary" - > - Export Excel - + const blob = await res.blob(); + const url = window.URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `Modifications_Gammes_${nomFournisseur.replace(/\s+/g, "_")}_${new Date().toISOString().slice(0, 10)}.xlsx`; + a.click(); + window.URL.revokeObjectURL(url); + } catch (error) { + console.error(error); + alert("Une erreur s'est produite lors de l'export Excel."); + } + }} + className="cursor-pointer" + > +
+ Seulement les changements + Exporter uniquement les gammes modifiées +
+
+ { + const currentState = useGridStore.getState(); + const currentDrafts = currentState.draftChanges; + const currentRows = currentState.rows; + + if (currentRows.length === 0) { + alert("Aucune donnée à exporter."); + return; + } + + const nomFournisseur = currentRows[0].nomFournisseur; + + // Toutes les lignes au format Excel (CODE FOURNISSEUR, GENCOD, GAMME) + const changes = currentRows.map(r => ({ + codein: r.codein, + gtin: r.gtin, + codeFournisseur: r.codeFournisseur, + gamme: (currentDrafts[r.codein] ?? r.codeGamme) as string, + })); + + try { + const res = await fetch("/api/export/modified-gammes", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ nomFournisseur, changes }), + }); + + if (!res.ok) throw new Error("Erreur lors de l'export"); + + const blob = await res.blob(); + const url = window.URL.createObjectURL(blob); + const a = document.createElement("a"); + a.href = url; + a.download = `Export_Complet_${nomFournisseur.replace(/\s+/g, "_")}_${new Date().toISOString().slice(0, 10)}.xlsx`; + a.click(); + window.URL.revokeObjectURL(url); + } catch (error) { + console.error(error); + alert("Une erreur s'est produite lors de l'export Excel."); + } + }} + className="cursor-pointer" + > +
+ Export complet + Exporter toutes les lignes du fournisseur +
+
+
+