Checkpoint: Ajout bouton "Purger les données" (admin uniquement) avec boîte de dialogue de confirmation sur VeilleDashboard et AAPDashboard. Procédures tRPC veille.purge et aap.purge ajoutées côté serveur.

This commit is contained in:
Manus
2026-04-28 04:33:20 -04:00
parent c3e1720e83
commit 8f2a22e4b1
6 changed files with 160 additions and 7 deletions

View File

@@ -1,4 +1,6 @@
import { useState, useMemo } from "react";
import { useLocalAuth } from "@/contexts/LocalAuthContext";
import { toast } from "sonner";
import { trpc } from "@/lib/trpc";
import { FilterBar } from "@/components/FilterBar";
import { Button } from "@/components/ui/button";
@@ -17,7 +19,20 @@ import {
ChevronRight,
AlertCircle,
Clock,
Trash2,
AlertTriangle,
} from "lucide-react";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { cn } from "@/lib/utils";
import { format, isPast, differenceInDays } from "date-fns";
import { fr } from "date-fns/locale";
@@ -88,6 +103,19 @@ function ClotureStatus({ date }: { date: Date | null | undefined }) {
}
export default function AAPDashboard() {
const { user } = useLocalAuth();
const isAdmin = user?.role === "admin";
const utils = trpc.useUtils();
const purgeMutation = trpc.aap.purge.useMutation({
onSuccess: (data) => {
toast.success(`Purge effectuée — ${data.deleted} entrée(s) supprimée(s)`);
utils.aap.list.invalidate();
utils.aap.filters.invalidate();
},
onError: (err) => {
toast.error(`Erreur lors de la purge : ${err.message}`);
},
});
const [viewMode, setViewMode] = useState<"list" | "grid">("list");
const [activeTab, setActiveTab] = useState<AAPCategorie | "all">("all");
const [page, setPage] = useState(1);
@@ -155,6 +183,40 @@ export default function AAPDashboard() {
<Button variant={viewMode === "grid" ? "default" : "outline"} size="sm" onClick={() => setViewMode("grid")} className="gap-2">
<LayoutGrid size={15} />Vignettes
</Button>
{isAdmin && (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline" size="sm" className="gap-2 border-destructive/50 text-destructive hover:bg-destructive hover:text-destructive-foreground ml-2">
<Trash2 size={15} />
Purger les données
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle className="flex items-center gap-2">
<AlertTriangle size={20} className="text-destructive" />
Purger tous les appels à projets
</AlertDialogTitle>
<AlertDialogDescription asChild>
<div className="space-y-2">
<p>Cette action va <strong>supprimer définitivement</strong> tous les appels à projets (Handicap, PA, Enfance, Précarité, Sanitaire et Autre).</p>
<p className="text-destructive font-medium">Cette opération est irréversible. Les données ne pourront pas être récupérées.</p>
</div>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Annuler</AlertDialogCancel>
<AlertDialogAction
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
onClick={() => purgeMutation.mutate()}
disabled={purgeMutation.isPending}
>
{purgeMutation.isPending ? "Purge en cours..." : "Oui, purger tous les appels à projets"}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)}
</div>
</div>

View File

@@ -1,4 +1,6 @@
import { useState, useMemo } from "react";
import { useLocalAuth } from "@/contexts/LocalAuthContext";
import { toast } from "sonner";
import { trpc } from "@/lib/trpc";
import { FilterBar } from "@/components/FilterBar";
import { Button } from "@/components/ui/button";
@@ -26,7 +28,20 @@ import {
Eye,
Globe,
BookOpen,
Trash2,
AlertTriangle,
} from "lucide-react";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { cn } from "@/lib/utils";
import { format } from "date-fns";
import { fr } from "date-fns/locale";
@@ -214,6 +229,19 @@ function VeilleDetailDialog({
// ─── Composant principal ──────────────────────────────────────────────────────
export default function VeilleDashboard() {
const { user } = useLocalAuth();
const isAdmin = user?.role === "admin";
const utils = trpc.useUtils();
const purgeMutation = trpc.veille.purge.useMutation({
onSuccess: (data) => {
toast.success(`Purge effectuée — ${data.deleted} entrée(s) supprimée(s)`);
utils.veille.list.invalidate();
utils.veille.filters.invalidate();
},
onError: (err) => {
toast.error(`Erreur lors de la purge : ${err.message}`);
},
});
const [viewMode, setViewMode] = useState<"list" | "grid">("list");
const [activeTab, setActiveTab] = useState<TypeVeille | "all">("all");
const [page, setPage] = useState(1);
@@ -284,9 +312,42 @@ export default function VeilleDashboard() {
<Button variant={viewMode === "grid" ? "default" : "outline"} size="sm" onClick={() => setViewMode("grid")} className="gap-2">
<LayoutGrid size={15} />Vignettes
</Button>
{isAdmin && (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="outline" size="sm" className="gap-2 border-destructive/50 text-destructive hover:bg-destructive hover:text-destructive-foreground ml-2">
<Trash2 size={15} />
Purger les données
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle className="flex items-center gap-2">
<AlertTriangle size={20} className="text-destructive" />
Purger toutes les données de veille
</AlertDialogTitle>
<AlertDialogDescription asChild>
<div className="space-y-2">
<p>Cette action va <strong>supprimer définitivement</strong> toutes les entrées de la veille stratégique (réglementaire, concurrentielle, technologique et générale).</p>
<p className="text-destructive font-medium">Cette opération est irréversible. Les données ne pourront pas être récupérées.</p>
</div>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Annuler</AlertDialogCancel>
<AlertDialogAction
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
onClick={() => purgeMutation.mutate()}
disabled={purgeMutation.isPending}
>
{purgeMutation.isPending ? "Purge en cours..." : "Oui, purger toutes les données"}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)}
</div>
</div>
{/* Onglets */}
<Tabs value={activeTab} onValueChange={(v) => { setActiveTab(v as TypeVeille | "all"); setPage(1); }}>
<TabsList className="bg-muted/50">