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,14 +6,15 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { trpc } from "@/lib/trpc";
import { Plus, Pencil, Trash2 } from "lucide-react";
import { useState } from "react";
import { Plus, Pencil, Trash2, Search, SlidersHorizontal } from "lucide-react";
import { useState, useMemo } from "react";
import { toast } from "sonner";
import { Skeleton } from "@/components/ui/skeleton";
export default function AdminApprenants() {
const [open, setOpen] = useState(false);
const [editingId, setEditingId] = useState<number | null>(null);
const [searchTerm, setSearchTerm] = useState("");
const [formData, setFormData] = useState({
nom: "",
prenom: "",
@@ -89,6 +90,21 @@ export default function AdminApprenants() {
}
};
// Filtrer les apprenants
const filteredApprenants = useMemo(() => {
if (!apprenants) return [];
return apprenants.filter(apprenant => {
const matchesSearch = searchTerm === "" ||
apprenant.nom.toLowerCase().includes(searchTerm.toLowerCase()) ||
apprenant.prenom.toLowerCase().includes(searchTerm.toLowerCase()) ||
apprenant.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
apprenant.codeEtablissement.toLowerCase().includes(searchTerm.toLowerCase());
return matchesSearch;
});
}, [apprenants, searchTerm]);
return (
<DashboardLayout>
<div className="space-y-6">
@@ -177,6 +193,36 @@ export default function AdminApprenants() {
</Dialog>
</div>
{/* Filtres et recherche */}
<Card className="mb-6">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<SlidersHorizontal className="w-5 h-5" />
Recherche
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2">
<Label htmlFor="search">Rechercher un apprenant</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, email ou code établissement..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-8"
/>
</div>
</div>
{/* Compteur de résultats */}
<div className="mt-4 text-sm text-muted-foreground">
{filteredApprenants.length} apprenant(s) trouvé(s)
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Liste des apprenants</CardTitle>
@@ -191,7 +237,7 @@ export default function AdminApprenants() {
<Skeleton className="h-10 w-full" />
<Skeleton className="h-10 w-full" />
</div>
) : apprenants && apprenants.length > 0 ? (
) : filteredApprenants && filteredApprenants.length > 0 ? (
<Table>
<TableHeader>
<TableRow>
@@ -203,7 +249,7 @@ export default function AdminApprenants() {
</TableRow>
</TableHeader>
<TableBody>
{apprenants.map((apprenant) => (
{filteredApprenants.map((apprenant) => (
<TableRow key={apprenant.id}>
<TableCell className="font-medium">{apprenant.nom}</TableCell>
<TableCell>{apprenant.prenom}</TableCell>