From 8f2a22e4b1f1af3f851e2681c5263ed6887c5e42 Mon Sep 17 00:00:00 2001 From: Manus Date: Tue, 28 Apr 2026 04:33:20 -0400 Subject: [PATCH] =?UTF-8?q?Checkpoint:=20Ajout=20bouton=20"Purger=20les=20?= =?UTF-8?q?donn=C3=A9es"=20(admin=20uniquement)=20avec=20bo=C3=AEte=20de?= =?UTF-8?q?=20dialogue=20de=20confirmation=20sur=20VeilleDashboard=20et=20?= =?UTF-8?q?AAPDashboard.=20Proc=C3=A9dures=20tRPC=20veille.purge=20et=20aa?= =?UTF-8?q?p.purge=20ajout=C3=A9es=20c=C3=B4t=C3=A9=20serveur.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/public/__manus__/version.json | 4 +- client/src/pages/AAPDashboard.tsx | 62 +++++++++++++++++++++++++++ client/src/pages/VeilleDashboard.tsx | 63 +++++++++++++++++++++++++++- server/db.ts | 15 +++++++ server/routers.ts | 16 +++++-- todo.md | 7 ++++ 6 files changed, 160 insertions(+), 7 deletions(-) diff --git a/client/public/__manus__/version.json b/client/public/__manus__/version.json index 7da5bf7..f216d14 100644 --- a/client/public/__manus__/version.json +++ b/client/public/__manus__/version.json @@ -1,4 +1,4 @@ { - "version": "30c14d7e", - "timestamp": 1777149849085 + "version": "a13a3f60", + "timestamp": 1777365200860 } \ No newline at end of file diff --git a/client/src/pages/AAPDashboard.tsx b/client/src/pages/AAPDashboard.tsx index 10202ad..e348c00 100644 --- a/client/src/pages/AAPDashboard.tsx +++ b/client/src/pages/AAPDashboard.tsx @@ -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("all"); const [page, setPage] = useState(1); @@ -155,6 +183,40 @@ export default function AAPDashboard() { + {isAdmin && ( + + + + + + + + + Purger tous les appels à projets + + +
+

Cette action va supprimer définitivement tous les appels à projets (Handicap, PA, Enfance, Précarité, Sanitaire et Autre).

+

Cette opération est irréversible. Les données ne pourront pas être récupérées.

+
+
+
+ + Annuler + purgeMutation.mutate()} + disabled={purgeMutation.isPending} + > + {purgeMutation.isPending ? "Purge en cours..." : "Oui, purger tous les appels à projets"} + + +
+
+ )} diff --git a/client/src/pages/VeilleDashboard.tsx b/client/src/pages/VeilleDashboard.tsx index 24535a4..b6f7580 100644 --- a/client/src/pages/VeilleDashboard.tsx +++ b/client/src/pages/VeilleDashboard.tsx @@ -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("all"); const [page, setPage] = useState(1); @@ -284,9 +312,42 @@ export default function VeilleDashboard() { + {isAdmin && ( + + + + + + + + + Purger toutes les données de veille + + +
+

Cette action va supprimer définitivement toutes les entrées de la veille stratégique (réglementaire, concurrentielle, technologique et générale).

+

Cette opération est irréversible. Les données ne pourront pas être récupérées.

+
+
+
+ + Annuler + purgeMutation.mutate()} + disabled={purgeMutation.isPending} + > + {purgeMutation.isPending ? "Purge en cours..." : "Oui, purger toutes les données"} + + +
+
+ )} - {/* Onglets */} { setActiveTab(v as TypeVeille | "all"); setPage(1); }}> diff --git a/server/db.ts b/server/db.ts index c93a6f6..4620667 100644 --- a/server/db.ts +++ b/server/db.ts @@ -433,3 +433,18 @@ export async function saveRssSettings(data: Partial { + const db = await getDb(); + if (!db) throw new Error("Database not available"); + const result = await db.delete(veilleItems); + return (result as any).affectedRows ?? 0; +} + +export async function purgeAapItems(): Promise { + const db = await getDb(); + if (!db) throw new Error("Database not available"); + const result = await db.delete(aapItems); + return (result as any).affectedRows ?? 0; +} diff --git a/server/routers.ts b/server/routers.ts index 740ba13..a2dd539 100644 --- a/server/routers.ts +++ b/server/routers.ts @@ -9,6 +9,8 @@ import { getVeilleDistinctValues, getAapItems, getAapDistinctValues, + purgeVeilleItems, + purgeAapItems, getAllSettings, setSettings, getImportLogs, @@ -98,9 +100,12 @@ export const appRouter = router({ filters: publicProcedure.query(async () => { return getVeilleDistinctValues(); }), + purge: adminProcedure.mutation(async () => { + const count = await purgeVeilleItems(); + return { success: true, deleted: count }; + }), }), - - // ─── AAP ──────────────────────────────────────────────────────────────────── + // ─── AAPP ──────────────────────────────────────────────────────────────────── aap: router({ list: publicProcedure .input( @@ -124,9 +129,12 @@ export const appRouter = router({ filters: publicProcedure.query(async () => { return getAapDistinctValues(); }), + purge: adminProcedure.mutation(async () => { + const count = await purgeAapItems(); + return { success: true, deleted: count }; + }), }), - - // ─── Import ───────────────────────────────────────────────────────────────── + // ─── Importt ───────────────────────────────────────────────────────────────── import: router({ run: adminProcedure .input(z.object({ type: z.enum(["veille", "aap", "all"]).default("all") })) diff --git a/todo.md b/todo.md index 93b6190..f21943e 100644 --- a/todo.md +++ b/todo.md @@ -73,3 +73,10 @@ - [x] Page RssFeeds.tsx : liste des flux, ajout/édition/suppression, config fréquence, règles d'automatisme - [x] Navigation : ajouter l'entrée RSS dans le menu latéral (DashboardLayout) - [ ] Déploiement VPS via Gitea CI/CD + +## Purge des données +- [ ] Procédures tRPC : veille.purge et aap.purge (adminProcedure) +- [ ] Bouton "Purger les données" en haut à droite de VeilleDashboard.tsx (admin uniquement) +- [ ] Bouton "Purger les données" en haut à droite de AAPDashboard.tsx (admin uniquement) +- [ ] Boîte de dialogue de confirmation avec message d'avertissement +- [ ] Déploiement VPS