feat: purger les contenus expirés ou anciens
This commit is contained in:
@@ -41,6 +41,7 @@ import { fr } from "date-fns/locale";
|
||||
|
||||
type AAPCategorie = "Handicap" | "PA" | "Enfance" | "Précarité" | "Sanitaire" | "Autre";
|
||||
type ReadFilter = "unread" | "all" | "read";
|
||||
type AapPurgeScope = "all" | "expired" | "older_than";
|
||||
|
||||
interface AAPItem {
|
||||
id: number;
|
||||
@@ -78,6 +79,7 @@ const CAT_ACCENT: Record<string, string> = {
|
||||
};
|
||||
|
||||
const PAGE_SIZE = 24;
|
||||
const PURGE_MONTH_OPTIONS = [1, 3, 6, 12, 18, 24] as const;
|
||||
|
||||
function formatDate(d: Date | null | undefined): string | null {
|
||||
if (!d) return null;
|
||||
@@ -137,12 +139,16 @@ export default function AAPDashboard() {
|
||||
});
|
||||
const unreadCountQuery = trpc.aap.unreadCount.useQuery();
|
||||
const unreadCount = unreadCountQuery.data?.count ?? 0;
|
||||
const [purgeScope, setPurgeScope] = useState<AapPurgeScope>("expired");
|
||||
const [purgeMonths, setPurgeMonths] = useState<(typeof PURGE_MONTH_OPTIONS)[number]>(6);
|
||||
|
||||
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();
|
||||
utils.aap.unreadCount.invalidate();
|
||||
utils.aap.getReadIds.invalidate();
|
||||
},
|
||||
onError: (err) => {
|
||||
toast.error(`Erreur lors de la purge : ${err.message}`);
|
||||
@@ -258,11 +264,58 @@ export default function AAPDashboard() {
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle className="flex items-center gap-2">
|
||||
<AlertTriangle size={20} className="text-destructive" />
|
||||
Purger tous les appels à projets
|
||||
Purger les appels à projets
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription asChild>
|
||||
<div className="space-y-2">
|
||||
<p>Cette action va <strong>supprimer définitivement</strong> tous les appels à projets.</p>
|
||||
<div className="space-y-4">
|
||||
<p>Choisissez les AAP à <strong>supprimer définitivement</strong>.</p>
|
||||
<div className="grid gap-2">
|
||||
{([
|
||||
["expired", "AAP clôturés"],
|
||||
["older_than", "AAP plus anciens"],
|
||||
["all", "Tous les AAP"],
|
||||
] as const).map(([scope, label]) => (
|
||||
<button
|
||||
key={scope}
|
||||
type="button"
|
||||
onClick={() => setPurgeScope(scope)}
|
||||
className={cn(
|
||||
"rounded-lg border-2 px-3 py-2 text-left text-sm font-medium transition-colors",
|
||||
purgeScope === scope
|
||||
? "border-primary bg-primary/5 text-primary"
|
||||
: "border-border hover:border-primary/40 text-foreground",
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{purgeScope === "older_than" && (
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{PURGE_MONTH_OPTIONS.map((months) => (
|
||||
<button
|
||||
key={months}
|
||||
type="button"
|
||||
onClick={() => setPurgeMonths(months)}
|
||||
className={cn(
|
||||
"rounded-lg border-2 px-2 py-2 text-sm font-medium transition-colors",
|
||||
purgeMonths === months
|
||||
? "border-primary bg-primary/5 text-primary"
|
||||
: "border-border hover:border-primary/40 text-foreground",
|
||||
)}
|
||||
>
|
||||
{months} mois
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<p>
|
||||
{purgeScope === "expired"
|
||||
? "Seuls les AAP dont la date de clôture est dépassée seront supprimés."
|
||||
: purgeScope === "all"
|
||||
? "Tous les appels à projets seront supprimés."
|
||||
: `Les AAP publiés, ou importés sans date de publication, il y a plus de ${purgeMonths} mois seront supprimés.`}
|
||||
</p>
|
||||
<p className="text-destructive font-medium">Cette opération est irréversible.</p>
|
||||
</div>
|
||||
</AlertDialogDescription>
|
||||
@@ -271,10 +324,14 @@ export default function AAPDashboard() {
|
||||
<AlertDialogCancel>Annuler</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
onClick={() => purgeMutation.mutate()}
|
||||
onClick={() => purgeMutation.mutate(
|
||||
purgeScope === "older_than"
|
||||
? { scope: "older_than", months: purgeMonths }
|
||||
: { scope: purgeScope },
|
||||
)}
|
||||
disabled={purgeMutation.isPending}
|
||||
>
|
||||
{purgeMutation.isPending ? "Purge en cours..." : "Oui, purger tous les appels à projets"}
|
||||
{purgeMutation.isPending ? "Purge en cours..." : "Oui, supprimer définitivement"}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
|
||||
@@ -48,6 +48,7 @@ import { fr } from "date-fns/locale";
|
||||
|
||||
type TypeVeille = "reglementaire" | "concurrentielle" | "technologique" | "informationnelle";
|
||||
type ReadFilter = "unread" | "all" | "read";
|
||||
type VeillePurgeScope = "all" | "older_than";
|
||||
|
||||
interface VeilleItem {
|
||||
id: number;
|
||||
@@ -97,6 +98,7 @@ const TYPE_HEADER_BG: Record<string, string> = {
|
||||
};
|
||||
|
||||
const PAGE_SIZE = 24;
|
||||
const PURGE_MONTH_OPTIONS = [1, 3, 6, 12, 18, 24] as const;
|
||||
|
||||
function formatDate(d: Date | null | undefined): string | null {
|
||||
if (!d) return null;
|
||||
@@ -254,12 +256,16 @@ export default function VeilleDashboard() {
|
||||
});
|
||||
const unreadCountQuery = trpc.veille.unreadCount.useQuery();
|
||||
const unreadCount = unreadCountQuery.data?.count ?? 0;
|
||||
const [purgeScope, setPurgeScope] = useState<VeillePurgeScope>("older_than");
|
||||
const [purgeMonths, setPurgeMonths] = useState<(typeof PURGE_MONTH_OPTIONS)[number]>(6);
|
||||
|
||||
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();
|
||||
utils.veille.unreadCount.invalidate();
|
||||
utils.veille.getReadIds.invalidate();
|
||||
},
|
||||
onError: (err) => {
|
||||
toast.error(`Erreur lors de la purge : ${err.message}`);
|
||||
@@ -381,11 +387,55 @@ export default function VeilleDashboard() {
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle className="flex items-center gap-2">
|
||||
<AlertTriangle size={20} className="text-destructive" />
|
||||
Purger toutes les données de veille
|
||||
Purger la veille stratégique
|
||||
</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.</p>
|
||||
<div className="space-y-4">
|
||||
<p>Choisissez les articles à <strong>supprimer définitivement</strong>.</p>
|
||||
<div className="grid gap-2">
|
||||
{([
|
||||
["older_than", "Articles plus anciens"],
|
||||
["all", "Tous les articles"],
|
||||
] as const).map(([scope, label]) => (
|
||||
<button
|
||||
key={scope}
|
||||
type="button"
|
||||
onClick={() => setPurgeScope(scope)}
|
||||
className={cn(
|
||||
"rounded-lg border-2 px-3 py-2 text-left text-sm font-medium transition-colors",
|
||||
purgeScope === scope
|
||||
? "border-primary bg-primary/5 text-primary"
|
||||
: "border-border hover:border-primary/40 text-foreground",
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{purgeScope === "older_than" && (
|
||||
<div className="grid grid-cols-3 gap-2">
|
||||
{PURGE_MONTH_OPTIONS.map((months) => (
|
||||
<button
|
||||
key={months}
|
||||
type="button"
|
||||
onClick={() => setPurgeMonths(months)}
|
||||
className={cn(
|
||||
"rounded-lg border-2 px-2 py-2 text-sm font-medium transition-colors",
|
||||
purgeMonths === months
|
||||
? "border-primary bg-primary/5 text-primary"
|
||||
: "border-border hover:border-primary/40 text-foreground",
|
||||
)}
|
||||
>
|
||||
{months} mois
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<p>
|
||||
{purgeScope === "all"
|
||||
? "Tous les articles de veille seront supprimés."
|
||||
: `Les articles publiés, ou importés sans date de publication, il y a plus de ${purgeMonths} mois seront supprimés.`}
|
||||
</p>
|
||||
<p className="text-destructive font-medium">Cette opération est irréversible.</p>
|
||||
</div>
|
||||
</AlertDialogDescription>
|
||||
@@ -394,10 +444,14 @@ export default function VeilleDashboard() {
|
||||
<AlertDialogCancel>Annuler</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
onClick={() => purgeMutation.mutate()}
|
||||
onClick={() => purgeMutation.mutate(
|
||||
purgeScope === "all"
|
||||
? { scope: "all" }
|
||||
: { scope: "older_than", months: purgeMonths },
|
||||
)}
|
||||
disabled={purgeMutation.isPending}
|
||||
>
|
||||
{purgeMutation.isPending ? "Purge en cours..." : "Oui, purger toutes les données"}
|
||||
{purgeMutation.isPending ? "Purge en cours..." : "Oui, supprimer définitivement"}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
|
||||
Reference in New Issue
Block a user