Checkpoint: Ajout de filtres complets sur la page de gestion des inscrits : recherche par nom/prénom/email, filtre par statut d'inscription (confirmée/liste d'attente/annulée), filtre par code établissement, tri par date d'inscription ou nom alphabétique, avec compteur de résultats filtrés.

This commit is contained in:
Manus Sandbox
2025-11-12 09:21:21 -05:00
parent 8a77f62723
commit 2e75838bed
2 changed files with 137 additions and 5 deletions

View File

@@ -4,8 +4,11 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { trpc } from "@/lib/trpc"; import { trpc } from "@/lib/trpc";
import { ArrowLeft, Download, Mail } from "lucide-react"; import { ArrowLeft, Download, Mail, Search, SlidersHorizontal } from "lucide-react";
import { useLocation, useRoute } from "wouter"; import { useLocation, useRoute } from "wouter";
import { useState, useMemo } from "react";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { toast } from "sonner"; import { toast } from "sonner";
@@ -13,6 +16,12 @@ export default function AdminSessionInscrits() {
const [, setLocation] = useLocation(); const [, setLocation] = useLocation();
const [, params] = useRoute("/admin/sessions/:id/inscrits"); const [, params] = useRoute("/admin/sessions/:id/inscrits");
const sessionId = params?.id ? parseInt(params.id) : 0; const sessionId = params?.id ? parseInt(params.id) : 0;
// États pour les filtres
const [searchTerm, setSearchTerm] = useState("");
const [filterStatut, setFilterStatut] = useState<string>("all");
const [filterEtablissement, setFilterEtablissement] = useState<string>("all");
const [sortBy, setSortBy] = useState<"date" | "nom">("date");
const utils = trpc.useUtils(); const utils = trpc.useUtils();
const { data: session, isLoading: loadingSession } = trpc.sessions.getById.useQuery({ id: sessionId }); const { data: session, isLoading: loadingSession } = trpc.sessions.getById.useQuery({ id: sessionId });
@@ -115,6 +124,49 @@ export default function AdminSessionInscrits() {
return formations?.find(f => f.id === formationId)?.nom || "Formation inconnue"; return formations?.find(f => f.id === formationId)?.nom || "Formation inconnue";
}; };
// Extraire les codes établissement uniques
const uniqueEtablissements = useMemo(() => {
if (!inscriptions) return [];
const codes = inscriptions
.filter(i => i.apprenant)
.map(i => i.apprenant!.codeEtablissement);
return Array.from(new Set(codes)).sort();
}, [inscriptions]);
// Filtrer et trier les inscriptions
const filteredAndSortedInscriptions = useMemo(() => {
if (!inscriptions) return [];
let filtered = inscriptions.filter(inscription => {
// Filtre par recherche (nom, prénom, email)
const matchesSearch = searchTerm === "" ||
inscription.apprenant?.nom.toLowerCase().includes(searchTerm.toLowerCase()) ||
inscription.apprenant?.prenom.toLowerCase().includes(searchTerm.toLowerCase()) ||
inscription.apprenant?.email.toLowerCase().includes(searchTerm.toLowerCase());
// Filtre par statut
const matchesStatut = filterStatut === "all" || inscription.inscription.statut === filterStatut;
// Filtre par établissement
const matchesEtablissement = filterEtablissement === "all" ||
inscription.apprenant?.codeEtablissement === filterEtablissement;
return matchesSearch && matchesStatut && matchesEtablissement;
});
// Tri
filtered.sort((a, b) => {
if (sortBy === "date") {
return new Date(b.inscription.dateInscription).getTime() - new Date(a.inscription.dateInscription).getTime();
} else if (sortBy === "nom") {
return (a.apprenant?.nom || "").localeCompare(b.apprenant?.nom || "");
}
return 0;
});
return filtered;
}, [inscriptions, searchTerm, filterStatut, filterEtablissement, sortBy]);
const getStatutBadge = (statut: string) => { const getStatutBadge = (statut: string) => {
const styles = { const styles = {
confirmee: "bg-green-100 text-green-800", confirmee: "bg-green-100 text-green-800",
@@ -149,10 +201,88 @@ export default function AdminSessionInscrits() {
<p className="text-muted-foreground mt-2"> <p className="text-muted-foreground mt-2">
{getFormationName(session.formationId)} - {session.nom} {getFormationName(session.formationId)} - {session.nom}
</p> </p>
)} )} </div>
</div>
</div> </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 lg:grid-cols-4 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, prénom ou email..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-8"
/>
</div>
</div>
{/* Filtre par statut */}
<div className="space-y-2">
<Label htmlFor="filterStatut">Statut</Label>
<Select value={filterStatut} onValueChange={setFilterStatut}>
<SelectTrigger id="filterStatut">
<SelectValue placeholder="Tous les statuts" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">Tous les statuts</SelectItem>
<SelectItem value="confirmee">Confirmée</SelectItem>
<SelectItem value="liste_attente">Liste d'attente</SelectItem>
<SelectItem value="annulee">Annulée</SelectItem>
</SelectContent>
</Select>
</div>
{/* Filtre par établissement */}
<div className="space-y-2">
<Label htmlFor="filterEtablissement">Établissement</Label>
<Select value={filterEtablissement} onValueChange={setFilterEtablissement}>
<SelectTrigger id="filterEtablissement">
<SelectValue placeholder="Tous les établissements" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">Tous les établissements</SelectItem>
{uniqueEtablissements.map(code => (
<SelectItem key={code} value={code}>{code}</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Tri */}
<div className="space-y-2">
<Label htmlFor="sortBy">Trier par</Label>
<Select value={sortBy} onValueChange={(value: any) => setSortBy(value)}>
<SelectTrigger id="sortBy">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="date">Date d'inscription</SelectItem>
<SelectItem value="nom">Nom</SelectItem>
</SelectContent>
</Select>
</div>
</div>
{/* Compteur de résultats */}
<div className="mt-4 text-sm text-muted-foreground">
{filteredAndSortedInscriptions.length} inscrit(s) trouvé(s) sur {inscriptions?.length || 0} total
</div>
</CardContent>
</Card>
{loadingSession || loadingInscriptions ? ( {loadingSession || loadingInscriptions ? (
<div className="space-y-4"> <div className="space-y-4">
<Skeleton className="h-32 w-full" /> <Skeleton className="h-32 w-full" />
@@ -239,7 +369,7 @@ export default function AdminSessionInscrits() {
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{inscriptions && inscriptions.length > 0 ? ( {filteredAndSortedInscriptions && filteredAndSortedInscriptions.length > 0 ? (
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
@@ -253,7 +383,7 @@ export default function AdminSessionInscrits() {
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{inscriptions.map((item) => ( {filteredAndSortedInscriptions.map((item) => (
<TableRow key={item.inscription.id}> <TableRow key={item.inscription.id}>
<TableCell className="font-medium">{item.apprenant?.nom}</TableCell> <TableCell className="font-medium">{item.apprenant?.nom}</TableCell>
<TableCell>{item.apprenant?.prenom}</TableCell> <TableCell>{item.apprenant?.prenom}</TableCell>

View File

@@ -67,3 +67,5 @@
- [x] Ajouter des filtres de tri sur la page des sessions (date, lieu, places restantes) - [x] Ajouter des filtres de tri sur la page des sessions (date, lieu, places restantes)
- [x] Ajouter des filtres de recherche sur la page des formations - [x] Ajouter des filtres de recherche sur la page des formations
- [x] Ajouter des filtres de recherche sur la page des apprenants - [x] Ajouter des filtres de recherche sur la page des apprenants
- [x] Ajouter des filtres complets sur la page de gestion des inscrits (recherche, statut, établissement, tri par date)