mirror of
https://github.com/R0m1k3/CollectFlow.git
synced 2026-10-23 15:08:12 +02:00
feat: add floating summary bar component with grid statistics, draft management, snapshot, and export functionality.
This commit is contained in:
1 parent
9faea9df07
commit
2d3a90d6c7
1 file changed
+114
-46
@@ -3,10 +3,16 @@ import { useAiCopilotStore } from "@/features/ai-copilot/store/use-ai-copilot-st
|
|||||||
|
|
||||||
import { BulkAiAnalyzer } from "./bulk-ai-analyzer";
|
import { BulkAiAnalyzer } from "./bulk-ai-analyzer";
|
||||||
import { useSaveDrafts } from "@/features/grid/hooks/use-save-drafts";
|
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 { useState, useTransition } from "react";
|
||||||
import { saveSnapshot } from "@/features/snapshots/api/save-snapshot";
|
import { saveSnapshot } from "@/features/snapshots/api/save-snapshot";
|
||||||
import { SuccessModal } from "@/components/shared/success-modal";
|
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 }) {
|
function Stat({ label, value, sub }: { label: string; value: string; sub?: string }) {
|
||||||
// Determine color based on label to match the prototype
|
// Determine color based on label to match the prototype
|
||||||
@@ -167,59 +173,121 @@ export function FloatingSummaryBar() {
|
|||||||
<div className="flex space-x-3 items-center">
|
<div className="flex space-x-3 items-center">
|
||||||
<BulkAiAnalyzer />
|
<BulkAiAnalyzer />
|
||||||
|
|
||||||
<button
|
<DropdownMenu>
|
||||||
onClick={async () => {
|
<DropdownMenuTrigger asChild>
|
||||||
const currentState = useGridStore.getState();
|
<button className="btn-action btn-action-secondary flex items-center gap-1.5">
|
||||||
const currentDrafts = currentState.draftChanges;
|
Export Excel
|
||||||
const currentRows = currentState.rows;
|
<ChevronDown className="w-3.5 h-3.5" />
|
||||||
|
</button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end" className="w-64">
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={async () => {
|
||||||
|
const currentState = useGridStore.getState();
|
||||||
|
const currentDrafts = currentState.draftChanges;
|
||||||
|
const currentRows = currentState.rows;
|
||||||
|
|
||||||
const modifiedRows = currentRows.filter(r => {
|
const modifiedRows = currentRows.filter(r => {
|
||||||
const currentGamme = currentDrafts[r.codein] ?? r.codeGamme;
|
const currentGamme = currentDrafts[r.codein] ?? r.codeGamme;
|
||||||
return currentGamme !== r.codeGammeInit;
|
return currentGamme !== r.codeGammeInit;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (modifiedRows.length === 0) {
|
if (modifiedRows.length === 0) {
|
||||||
alert("Aucun changement détecté par rapport à l'état initial.");
|
alert("Aucun changement détecté par rapport à l'état initial.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const changes = modifiedRows.map(r => ({
|
const changes = modifiedRows.map(r => ({
|
||||||
codein: r.codein,
|
codein: r.codein,
|
||||||
gtin: r.gtin,
|
gtin: r.gtin,
|
||||||
codeFournisseur: r.codeFournisseur,
|
codeFournisseur: r.codeFournisseur,
|
||||||
gamme: (currentDrafts[r.codein] ?? r.codeGamme) as string,
|
gamme: (currentDrafts[r.codein] ?? r.codeGamme) as string,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const nomFournisseur = modifiedRows[0].nomFournisseur;
|
const nomFournisseur = modifiedRows[0].nomFournisseur;
|
||||||
|
|
||||||
// Auto-save snapshot for history
|
// Auto-save snapshot for history
|
||||||
await handleSnapshot(`Export ${nomFournisseur} — ${new Date().toLocaleTimeString()}`, "export");
|
await handleSnapshot(`Export ${nomFournisseur} — ${new Date().toLocaleTimeString()}`, "export");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch("/api/export/modified-gammes", {
|
const res = await fetch("/api/export/modified-gammes", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ nomFournisseur, changes }),
|
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 blob = await res.blob();
|
||||||
const url = window.URL.createObjectURL(blob);
|
const url = window.URL.createObjectURL(blob);
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = `Modifications_Gammes_${nomFournisseur.replace(/\s+/g, "_")}_${new Date().toISOString().slice(0, 10)}.xlsx`;
|
a.download = `Modifications_Gammes_${nomFournisseur.replace(/\s+/g, "_")}_${new Date().toISOString().slice(0, 10)}.xlsx`;
|
||||||
a.click();
|
a.click();
|
||||||
window.URL.revokeObjectURL(url);
|
window.URL.revokeObjectURL(url);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
alert("Une erreur s'est produite lors de l'export Excel.");
|
alert("Une erreur s'est produite lors de l'export Excel.");
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="btn-action btn-action-secondary"
|
className="cursor-pointer"
|
||||||
>
|
>
|
||||||
Export Excel
|
<div className="flex flex-col">
|
||||||
</button>
|
<span className="font-medium">Seulement les changements</span>
|
||||||
|
<span className="text-xs text-muted-foreground">Exporter uniquement les gammes modifiées</span>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem
|
||||||
|
onClick={async () => {
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="font-medium">Export complet</span>
|
||||||
|
<span className="text-xs text-muted-foreground">Exporter toutes les lignes du fournisseur</span>
|
||||||
|
</div>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
|
|||||||
Reference in new issue
Block a user