286 lines
8.8 KiB
TypeScript
286 lines
8.8 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useMemo } from "react";
|
|
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "@/components/ui/table";
|
|
import { Button } from "@/components/ui/button";
|
|
import { useSession } from "next-auth/react";
|
|
import { AddUserButton } from "./add-user-button";
|
|
import { SimplePagination } from "@/components/ui/pagination";
|
|
import { Input } from "@/components/ui/input";
|
|
import { MoreHorizontal, Trash, Edit, UserPlus } from "lucide-react";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuLabel,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu";
|
|
|
|
interface User {
|
|
id: string;
|
|
username: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
email: string;
|
|
createdTimestamp: number;
|
|
roles: string[];
|
|
}
|
|
|
|
interface UsersTableProps {
|
|
userRole?: string[];
|
|
}
|
|
|
|
// Constants for role names
|
|
const ROLES = {
|
|
ADMIN: "Admin",
|
|
TEACHER: "Teacher",
|
|
STUDENT: "Students"
|
|
} as const;
|
|
|
|
const ITEMS_PER_PAGE = 10;
|
|
|
|
export function UsersTable({ userRole = [] }: UsersTableProps) {
|
|
const { data: session, status } = useSession();
|
|
const [users, setUsers] = useState<User[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [currentPage, setCurrentPage] = useState(1);
|
|
const [searchTerm, setSearchTerm] = useState("");
|
|
|
|
useEffect(() => {
|
|
fetchUsers();
|
|
}, []);
|
|
|
|
const fetchUsers = async () => {
|
|
try {
|
|
setLoading(true);
|
|
const response = await fetch("/api/users");
|
|
const data = await response.json();
|
|
console.log("Fetched users:", data);
|
|
console.log("Current user role:", session?.user?.role); // Debug log
|
|
setUsers(data);
|
|
} catch (error) {
|
|
console.error("Error fetching users:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const filterUsers = (users: User[]) => {
|
|
console.log("Filtering users with role:", userRole); // Debug log
|
|
|
|
if (!Array.isArray(users)) return [];
|
|
|
|
// If no role specified or user is admin, show all users
|
|
if (!userRole?.length || userRole.includes(ROLES.ADMIN)) {
|
|
console.log("Showing all users - admin or no role");
|
|
return users;
|
|
}
|
|
|
|
// If user is teacher, show teachers and students
|
|
if (userRole.includes(ROLES.TEACHER)) {
|
|
console.log("Filtering for teacher view");
|
|
return users.filter(user =>
|
|
user.roles?.includes(ROLES.TEACHER) || user.roles?.includes(ROLES.STUDENT)
|
|
);
|
|
}
|
|
|
|
// If user is student, show only students
|
|
if (userRole.includes(ROLES.STUDENT)) {
|
|
console.log("Filtering for student view");
|
|
return users.filter(user => user.roles?.includes(ROLES.STUDENT));
|
|
}
|
|
|
|
// Default: show all users
|
|
console.log("Default case: showing all users");
|
|
return users;
|
|
};
|
|
|
|
const canDelete = (targetUserRole: string[]) => {
|
|
if (!userRole?.length) return false;
|
|
|
|
if (userRole.includes(ROLES.ADMIN)) return true;
|
|
if (userRole.includes(ROLES.TEACHER)) {
|
|
return targetUserRole.includes(ROLES.STUDENT);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const handleDelete = async (userId: string) => {
|
|
try {
|
|
const response = await fetch(`/api/users/${userId}`, {
|
|
method: "DELETE",
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (!response.ok) {
|
|
console.error("Delete error:", data);
|
|
// You might want to show an error message to the user here
|
|
return;
|
|
}
|
|
|
|
// Remove the user from the local state
|
|
setUsers(prevUsers => prevUsers.filter(user => user.id !== userId));
|
|
|
|
// Optional: Show success message
|
|
console.log("User deleted successfully");
|
|
} catch (error) {
|
|
console.error("Error deleting user:", error);
|
|
// You might want to show an error message to the user here
|
|
}
|
|
};
|
|
|
|
const filteredUsers = useMemo(() => {
|
|
let filtered = filterUsers(users);
|
|
|
|
// Apply search filter
|
|
if (searchTerm) {
|
|
filtered = filtered.filter(user =>
|
|
user.username.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
user.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
user.firstName.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
user.lastName.toLowerCase().includes(searchTerm.toLowerCase())
|
|
);
|
|
}
|
|
|
|
return filtered;
|
|
}, [users, searchTerm, userRole]);
|
|
|
|
// Calculate pagination
|
|
const totalPages = Math.ceil(filteredUsers.length / ITEMS_PER_PAGE);
|
|
const paginatedUsers = filteredUsers.slice(
|
|
(currentPage - 1) * ITEMS_PER_PAGE,
|
|
currentPage * ITEMS_PER_PAGE
|
|
);
|
|
|
|
const handlePageChange = (page: number) => {
|
|
setCurrentPage(page);
|
|
};
|
|
|
|
const handleAddUser = (newUser: User) => {
|
|
setUsers(prev => {
|
|
const updated = [...prev, newUser];
|
|
// Sort users by username
|
|
return updated.sort((a, b) => a.username.localeCompare(b.username));
|
|
});
|
|
// Reset to first page when adding new user
|
|
setCurrentPage(1);
|
|
};
|
|
|
|
// First, let's debug the roles
|
|
console.log("Current session:", {
|
|
role: session?.user?.role,
|
|
isAdmin: session?.user?.role?.includes("Admin"),
|
|
isTeacher: session?.user?.role?.includes("Teacher")
|
|
});
|
|
|
|
// Add this function for editing user
|
|
const handleEdit = async (userId: string) => {
|
|
// TODO: Implement edit functionality
|
|
console.log("Edit user:", userId);
|
|
};
|
|
|
|
// Add this function for managing roles
|
|
const handleManageRoles = async (userId: string) => {
|
|
// TODO: Implement role management
|
|
console.log("Manage roles for user:", userId);
|
|
};
|
|
|
|
if (!session) return null;
|
|
if (loading) return <div className="text-center p-4">Loading...</div>;
|
|
|
|
return (
|
|
<div>
|
|
{/* Search and Add User row */}
|
|
<div className="flex justify-between items-center mb-6">
|
|
<input
|
|
type="text"
|
|
placeholder="Rechercher un utilisateur..."
|
|
className="bg-black/20 border-0 rounded-md text-white/80 w-96"
|
|
/>
|
|
<Button variant="secondary">
|
|
Ajouter un utilisateur
|
|
</Button>
|
|
</div>
|
|
|
|
{/* Table content */}
|
|
<div className="space-y-4">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>Nom d'utilisateur</TableHead>
|
|
<TableHead>Prénom</TableHead>
|
|
<TableHead>Nom</TableHead>
|
|
<TableHead>Email</TableHead>
|
|
<TableHead>Date d'inscription</TableHead>
|
|
<TableHead>Roles</TableHead>
|
|
<TableHead className="text-right">Actions</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{paginatedUsers.map((user) => (
|
|
<TableRow key={user.id}>
|
|
<TableCell>{user.username}</TableCell>
|
|
<TableCell>{user.firstName}</TableCell>
|
|
<TableCell>{user.lastName}</TableCell>
|
|
<TableCell>{user.email}</TableCell>
|
|
<TableCell>
|
|
{new Date(user.createdTimestamp).toLocaleDateString()}
|
|
</TableCell>
|
|
<TableCell>{user.roles.join(", ")}</TableCell>
|
|
<TableCell className="text-right">
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" className="h-8 w-8 p-0">
|
|
<span className="sr-only">Open menu</span>
|
|
<MoreHorizontal className="h-4 w-4" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end">
|
|
<DropdownMenuLabel>Actions</DropdownMenuLabel>
|
|
<DropdownMenuItem onClick={() => handleEdit(user.id)}>
|
|
<Edit className="mr-2 h-4 w-4" />
|
|
Modifier
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem onClick={() => handleManageRoles(user.id)}>
|
|
<UserPlus className="mr-2 h-4 w-4" />
|
|
Gérer les rôles
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
<DropdownMenuItem
|
|
className="text-red-600"
|
|
onClick={() => handleDelete(user.id)}
|
|
>
|
|
<Trash className="mr-2 h-4 w-4" />
|
|
Supprimer
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
|
|
<SimplePagination
|
|
currentPage={currentPage}
|
|
totalPages={totalPages}
|
|
onPageChange={handlePageChange}
|
|
/>
|
|
|
|
<div className="text-sm text-gray-500 text-center mt-2">
|
|
Affichage de {Math.min(currentPage * ITEMS_PER_PAGE, filteredUsers.length)} sur {filteredUsers.length} utilisateurs
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|