250 lines
8.1 KiB
TypeScript
250 lines
8.1 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect } from "react";
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "@/components/ui/table";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Plus, MoreHorizontal, Trash, Edit, Users } from "lucide-react";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
} from "@/components/ui/dialog";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu";
|
|
import { Label } from "@/components/ui/label";
|
|
import { toast } from "@/components/ui/use-toast";
|
|
|
|
interface Group {
|
|
id: string;
|
|
name: string;
|
|
path: string;
|
|
membersCount: number;
|
|
}
|
|
|
|
interface ApiError {
|
|
message: string;
|
|
}
|
|
|
|
interface GroupsTableProps {
|
|
userRole?: string[];
|
|
}
|
|
|
|
export function GroupsTable({ userRole = [] }: GroupsTableProps) {
|
|
const [groups, setGroups] = useState<Group[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [searchTerm, setSearchTerm] = useState("");
|
|
const [newGroupDialog, setNewGroupDialog] = useState(false);
|
|
const [newGroupName, setNewGroupName] = useState("");
|
|
|
|
useEffect(() => {
|
|
fetchGroups();
|
|
}, []);
|
|
|
|
const fetchGroups = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const response = await fetch("/api/groups");
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) {
|
|
throw new Error(data.message || "Erreur lors de la récupération des groupes");
|
|
}
|
|
|
|
setGroups(Array.isArray(data) ? data : []);
|
|
} catch (error) {
|
|
toast({
|
|
title: "Erreur",
|
|
description: error instanceof Error ? error.message : "Une erreur est survenue",
|
|
variant: "destructive",
|
|
});
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleCreateGroup = async () => {
|
|
try {
|
|
if (!newGroupName.trim()) {
|
|
throw new Error("Le nom du groupe est requis");
|
|
}
|
|
|
|
const response = await fetch("/api/groups", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ name: newGroupName }),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) {
|
|
throw new Error(data.message || "Erreur lors de la création du groupe");
|
|
}
|
|
|
|
setGroups(prev => [...prev, data]);
|
|
setNewGroupDialog(false);
|
|
setNewGroupName("");
|
|
toast({
|
|
title: "Succès",
|
|
description: "Le groupe a été créé avec succès",
|
|
});
|
|
} catch (error) {
|
|
toast({
|
|
title: "Erreur",
|
|
description: error instanceof Error ? error.message : "Une erreur est survenue",
|
|
variant: "destructive",
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleDeleteGroup = async (groupId: string) => {
|
|
try {
|
|
const response = await fetch(`/api/groups/${groupId}`, {
|
|
method: "DELETE",
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error("Erreur lors de la suppression du groupe");
|
|
}
|
|
|
|
setGroups(prev => prev.filter(group => group.id !== groupId));
|
|
toast({
|
|
title: "Succès",
|
|
description: "Le groupe a été supprimé avec succès",
|
|
});
|
|
} catch (error) {
|
|
toast({
|
|
title: "Erreur",
|
|
description: error instanceof Error ? error.message : "Une erreur est survenue",
|
|
variant: "destructive",
|
|
});
|
|
}
|
|
};
|
|
|
|
const filteredGroups = groups.filter(group =>
|
|
group.name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
);
|
|
|
|
return (
|
|
<div className='flex flex-col space-y-4'>
|
|
<div className='flex justify-between items-center mb-6'>
|
|
<h1 className='text-2xl font-semibold text-white'>Gestion des groupes</h1>
|
|
<div className="flex items-center space-x-4">
|
|
<div className="relative w-64">
|
|
<Input
|
|
type="text"
|
|
placeholder="Rechercher un groupe..."
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
className="w-full bg-black/20 border-0 text-white placeholder:text-gray-400"
|
|
/>
|
|
</div>
|
|
<Dialog open={newGroupDialog} onOpenChange={setNewGroupDialog}>
|
|
<DialogTrigger asChild>
|
|
<Button variant="outline" className="bg-blue-600 text-white hover:bg-blue-700 border-0">
|
|
<Plus className="mr-2 h-4 w-4" />
|
|
Nouveau groupe
|
|
</Button>
|
|
</DialogTrigger>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>Créer un nouveau groupe</DialogTitle>
|
|
</DialogHeader>
|
|
<div className="space-y-4">
|
|
<div className="space-y-2">
|
|
<Label htmlFor="name">Nom du groupe</Label>
|
|
<Input
|
|
id="name"
|
|
value={newGroupName}
|
|
onChange={(e) => setNewGroupName(e.target.value)}
|
|
placeholder="Entrez le nom du groupe"
|
|
/>
|
|
</div>
|
|
<Button onClick={handleCreateGroup} className="w-full">
|
|
Créer le groupe
|
|
</Button>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="rounded-lg overflow-hidden">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow className="bg-black/20 border-0">
|
|
<TableHead className="text-gray-400">Nom du groupe</TableHead>
|
|
<TableHead className="text-gray-400">Chemin</TableHead>
|
|
<TableHead className="text-gray-400">Nombre de membres</TableHead>
|
|
<TableHead className="text-gray-400 text-right">Actions</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{filteredGroups.map((group) => (
|
|
<TableRow
|
|
key={group.id}
|
|
className="border-0 bg-black/10 hover:bg-black/20"
|
|
>
|
|
<TableCell className="text-white">{group.name}</TableCell>
|
|
<TableCell className="text-white">{group.path}</TableCell>
|
|
<TableCell className="text-white">{group.membersCount}</TableCell>
|
|
<TableCell className="text-right">
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" className="h-8 w-8 p-0 text-white hover:bg-black/20">
|
|
<span className="sr-only">Open menu</span>
|
|
<MoreHorizontal className="h-4 w-4" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="bg-black/90 border-gray-700">
|
|
<DropdownMenuLabel className="text-gray-400">Actions</DropdownMenuLabel>
|
|
<DropdownMenuItem
|
|
onClick={() => console.log("Edit", group.id)}
|
|
className="text-white hover:bg-black/50"
|
|
>
|
|
<Edit className="mr-2 h-4 w-4" />
|
|
Modifier
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem
|
|
onClick={() => console.log("Manage members", group.id)}
|
|
className="text-white hover:bg-black/50"
|
|
>
|
|
<Users className="mr-2 h-4 w-4" />
|
|
Gérer les membres
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator className="bg-gray-700" />
|
|
<DropdownMenuItem
|
|
className="text-red-400 hover:bg-black/50"
|
|
onClick={() => handleDeleteGroup(group.id)}
|
|
>
|
|
<Trash className="mr-2 h-4 w-4" />
|
|
Supprimer
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|