Checkpoint: Ajout d'un système complet de filtres et de recherche : filtres par date, lieu, statut et capacité pour les sessions ; recherche par nom/description pour les formations ; recherche multi-critères pour les apprenants. Compteurs de résultats inclus.

This commit is contained in:
Manus Sandbox
2025-11-12 09:11:45 -05:00
parent 2c5bc825af
commit 8a77f62723
4 changed files with 269 additions and 20 deletions

View File

@@ -6,16 +6,19 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import { Switch } from "@/components/ui/switch";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { trpc } from "@/lib/trpc";
import { Plus, Pencil, Trash2, ExternalLink } from "lucide-react";
import { useState } from "react";
import { Plus, Pencil, Trash2, ExternalLink, Search, SlidersHorizontal } from "lucide-react";
import { useState, useMemo } from "react";
import { toast } from "sonner";
import { Skeleton } from "@/components/ui/skeleton";
export default function AdminFormations() {
const [open, setOpen] = useState(false);
const [editingId, setEditingId] = useState<number | null>(null);
const [searchTerm, setSearchTerm] = useState("");
const [filterActif, setFilterActif] = useState<string>("all");
const [formData, setFormData] = useState({
nom: "",
description: "",
@@ -94,6 +97,25 @@ export default function AdminFormations() {
return `${window.location.origin}/inscription/${lien}`;
};
// Filtrer les formations
const filteredFormations = useMemo(() => {
if (!formations) return [];
return formations.filter(formation => {
// Filtre par recherche
const matchesSearch = searchTerm === "" ||
formation.nom.toLowerCase().includes(searchTerm.toLowerCase()) ||
(formation.description && formation.description.toLowerCase().includes(searchTerm.toLowerCase()));
// Filtre par statut actif
const matchesActif = filterActif === "all" ||
(filterActif === "actif" && formation.actif) ||
(filterActif === "inactif" && !formation.actif);
return matchesSearch && matchesActif;
});
}, [formations, searchTerm, filterActif]);
return (
<DashboardLayout>
<div className="space-y-6">
@@ -180,6 +202,54 @@ export default function AdminFormations() {
</Dialog>
</div>
{/* Filtres et recherche */}
<Card className="mb-6">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<SlidersHorizontal className="w-5 h-5" />
Filtres et recherche
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Recherche */}
<div className="space-y-2">
<Label htmlFor="search">Rechercher</Label>
<div className="relative">
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
<Input
id="search"
placeholder="Nom ou description..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-8"
/>
</div>
</div>
{/* Filtre par statut */}
<div className="space-y-2">
<Label htmlFor="filterActif">Statut</Label>
<Select value={filterActif} onValueChange={setFilterActif}>
<SelectTrigger id="filterActif">
<SelectValue placeholder="Tous les statuts" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">Tous les statuts</SelectItem>
<SelectItem value="actif">Active</SelectItem>
<SelectItem value="inactif">Inactive</SelectItem>
</SelectContent>
</Select>
</div>
</div>
{/* Compteur de résultats */}
<div className="mt-4 text-sm text-muted-foreground">
{filteredFormations.length} formation(s) trouvée(s)
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Liste des formations</CardTitle>
@@ -194,7 +264,7 @@ export default function AdminFormations() {
<Skeleton className="h-10 w-full" />
<Skeleton className="h-10 w-full" />
</div>
) : formations && formations.length > 0 ? (
) : filteredFormations && filteredFormations.length > 0 ? (
<Table>
<TableHeader>
<TableRow>
@@ -206,7 +276,7 @@ export default function AdminFormations() {
</TableRow>
</TableHeader>
<TableBody>
{formations.map((formation) => (
{filteredFormations.map((formation) => (
<TableRow key={formation.id}>
<TableCell className="font-medium">{formation.nom}</TableCell>
<TableCell className="max-w-xs truncate">{formation.description || "-"}</TableCell>