diff --git a/src/components/shared/success-modal.tsx b/src/components/shared/success-modal.tsx
new file mode 100644
index 0000000..b340792
--- /dev/null
+++ b/src/components/shared/success-modal.tsx
@@ -0,0 +1,105 @@
+"use client";
+
+import React, { useEffect, useState } from "react";
+import { CheckCircle2, X } from "lucide-react";
+import { clsx, type ClassValue } from "clsx";
+import { twMerge } from "tailwind-merge";
+
+function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
+
+interface SuccessModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ title: string;
+ message: string;
+ duration?: number;
+}
+
+export function SuccessModal({
+ isOpen,
+ onClose,
+ title,
+ message,
+ duration = 3000
+}: SuccessModalProps) {
+ const [isVisible, setIsVisible] = useState(false);
+
+ useEffect(() => {
+ if (isOpen) {
+ setIsVisible(true);
+ const timer = setTimeout(() => {
+ handleClose();
+ }, duration);
+ return () => clearTimeout(timer);
+ }
+ }, [isOpen, duration]);
+
+ const handleClose = () => {
+ setIsVisible(false);
+ setTimeout(onClose, 300); // Wait for fade out animation
+ };
+
+ if (!isOpen && !isVisible) return null;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {title}
+
+
+ {message}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/features/grid/components/floating-summary-bar.tsx b/src/features/grid/components/floating-summary-bar.tsx
index 792cb8e..ca9fa0a 100644
--- a/src/features/grid/components/floating-summary-bar.tsx
+++ b/src/features/grid/components/floating-summary-bar.tsx
@@ -6,6 +6,7 @@ import { useSaveDrafts } from "@/features/grid/hooks/use-save-drafts";
import { Loader2, CheckCircle, AlertCircle, RotateCcw, Camera } from "lucide-react";
import { useState, useTransition } from "react";
import { saveSnapshot } from "@/features/snapshots/api/save-snapshot";
+import { SuccessModal } from "@/components/shared/success-modal";
function Stat({ label, value, sub }: { label: string; value: string; sub?: string }) {
// Determine color based on label to match the prototype
@@ -25,6 +26,11 @@ export function FloatingSummaryBar() {
const [isPending, startTransition] = useTransition();
const [isSavingSnapshot, setIsSavingSnapshot] = useState(false);
const [saveStatus, setSaveStatus] = useState<"idle" | "success" | "error">("idle");
+ const [modal, setModal] = useState<{ isOpen: boolean, title: string, message: string }>({
+ isOpen: false,
+ title: "",
+ message: ""
+ });
const visibleCodeins = rows.map(r => r.codein);
const { save, hasDrafts, count } = useSaveDrafts(filters.magasin || "TOTAL", visibleCodeins);
@@ -75,7 +81,13 @@ export function FloatingSummaryBar() {
});
if (res.success) {
- if (!labelOverride) alert("Snapshot enregistré avec succès ! Retrouvez-le dans la page Snapshots.");
+ if (!labelOverride) {
+ setModal({
+ isOpen: true,
+ title: "Snapshot Enregistré",
+ message: "Votre session de travail a été sauvegardée avec succès."
+ });
+ }
return res.snapshotId;
} else {
throw new Error(res.error);
@@ -84,7 +96,11 @@ export function FloatingSummaryBar() {
console.error(err);
if (!labelOverride) {
const msg = err instanceof Error ? err.message : String(err);
- alert(`Erreur lors de la création du snapshot : ${msg}`);
+ setModal({
+ isOpen: true,
+ title: "Erreur Snapshot",
+ message: `Impossible de créer le snapshot : ${msg}`
+ });
}
} finally {
setIsSavingSnapshot(false);
@@ -219,6 +235,13 @@ export function FloatingSummaryBar() {
{isPending ? "Validation..." : "Valider"}
+
+ setModal(prev => ({ ...prev, isOpen: false }))}
+ title={modal.title}
+ message={modal.message}
+ />
);
}
diff --git a/src/features/snapshots/components/snapshot-list.tsx b/src/features/snapshots/components/snapshot-list.tsx
index a79957a..c6fe7b5 100644
--- a/src/features/snapshots/components/snapshot-list.tsx
+++ b/src/features/snapshots/components/snapshot-list.tsx
@@ -13,8 +13,8 @@ import {
History,
ChevronRight,
Loader2,
- AlertCircle
} from "lucide-react";
+import { SuccessModal } from "@/components/shared/success-modal";
const formatDate = (date: Date) => {
return new Intl.DateTimeFormat("fr-FR", {
@@ -35,6 +35,11 @@ export function SnapshotList({ type }: SnapshotListProps) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [isDeleting, setIsDeleting] = useState(null);
+ const [modal, setModal] = useState<{ isOpen: boolean, title: string, message: string }>({
+ isOpen: false,
+ title: "",
+ message: ""
+ });
const restoreSnapshot = useGridStore(state => state.restoreSnapshot);
const router = useRouter();
@@ -62,11 +67,24 @@ export function SnapshotList({ type }: SnapshotListProps) {
const res = await deleteSnapshot(id);
if (res.success) {
setSnapshots(prev => prev.filter(s => s.id !== id));
+ setModal({
+ isOpen: true,
+ title: "Action Réussie",
+ message: "L'élément a été supprimé de votre historique."
+ });
} else {
- alert("Erreur lors de la suppression.");
+ setModal({
+ isOpen: true,
+ title: "Erreur",
+ message: "Impossible de supprimer l'élément."
+ });
}
} catch (err) {
- alert("Erreur technique lors de la suppression.");
+ setModal({
+ isOpen: true,
+ title: "Erreur Technique",
+ message: "Une erreur est survenue lors de la suppression."
+ });
} finally {
setIsDeleting(null);
}
@@ -75,8 +93,6 @@ export function SnapshotList({ type }: SnapshotListProps) {
const handleLoad = (snapshot: any) => {
if (!window.confirm(`Charger la session "${snapshot.label}" ? Cela remplacera vos brouillons actuels.`)) return;
- // Les changements sont stockés sous forme d'objet { codein: { before, after } }
- // On doit re-transformer en Record
const changes: Record = {};
Object.entries(snapshot.changes as any).forEach(([codein, delta]: [string, any]) => {
changes[codein] = delta.after;
@@ -84,7 +100,6 @@ export function SnapshotList({ type }: SnapshotListProps) {
restoreSnapshot(changes);
- // Redirection vers la grille avec les filtres du snapshot
const query = new URLSearchParams();
if (snapshot.codeFournisseur) query.set("fournisseur", snapshot.codeFournisseur);
if (snapshot.magasin) query.set("magasin", snapshot.magasin);
@@ -187,6 +202,13 @@ export function SnapshotList({ type }: SnapshotListProps) {
))}
+
+ setModal(prev => ({ ...prev, isOpen: false }))}
+ title={modal.title}
+ message={modal.message}
+ />
);
}