|
@@ -0,0 +1,600 @@
|
|
|
|
|
+'use client';
|
|
|
|
|
+
|
|
|
|
|
+import { useState, useEffect } from 'react';
|
|
|
|
|
+import { MainLayout } from '@/components/layout/main-layout';
|
|
|
|
|
+import { Button } from '@/components/ui/button';
|
|
|
|
|
+import { Input } from '@/components/ui/input';
|
|
|
|
|
+import { Label } from '@/components/ui/label';
|
|
|
|
|
+import {
|
|
|
|
|
+ Table,
|
|
|
|
|
+ TableBody,
|
|
|
|
|
+ TableCell,
|
|
|
|
|
+ TableHead,
|
|
|
|
|
+ TableHeader,
|
|
|
|
|
+ TableRow,
|
|
|
|
|
+} from '@/components/ui/table';
|
|
|
|
|
+import {
|
|
|
|
|
+ Dialog,
|
|
|
|
|
+ DialogContent,
|
|
|
|
|
+ DialogDescription,
|
|
|
|
|
+ DialogFooter,
|
|
|
|
|
+ DialogHeader,
|
|
|
|
|
+ DialogTitle,
|
|
|
|
|
+ DialogTrigger,
|
|
|
|
|
+} from '@/components/ui/dialog';
|
|
|
|
|
+import {
|
|
|
|
|
+ Select,
|
|
|
|
|
+ SelectContent,
|
|
|
|
|
+ SelectItem,
|
|
|
|
|
+ SelectTrigger,
|
|
|
|
|
+ SelectValue,
|
|
|
|
|
+} from '@/components/ui/select';
|
|
|
|
|
+import {
|
|
|
|
|
+ AlertDialog,
|
|
|
|
|
+ AlertDialogAction,
|
|
|
|
|
+ AlertDialogCancel,
|
|
|
|
|
+ AlertDialogContent,
|
|
|
|
|
+ AlertDialogDescription,
|
|
|
|
|
+ AlertDialogFooter,
|
|
|
|
|
+ AlertDialogHeader,
|
|
|
|
|
+ AlertDialogTitle,
|
|
|
|
|
+ AlertDialogTrigger,
|
|
|
|
|
+} from '@/components/ui/alert-dialog';
|
|
|
|
|
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
|
|
|
+import { Badge } from '@/components/ui/badge';
|
|
|
|
|
+import { Plus, Pencil, Trash2, Search } from 'lucide-react';
|
|
|
|
|
+import { toast } from 'sonner';
|
|
|
|
|
+
|
|
|
|
|
+interface User {
|
|
|
|
|
+ id: string;
|
|
|
|
|
+ email: string;
|
|
|
|
|
+ role: 'ADMIN' | 'TEACHER' | 'STUDENT';
|
|
|
|
|
+ createdAt: string;
|
|
|
|
|
+ updatedAt: string;
|
|
|
|
|
+ teacher?: {
|
|
|
|
|
+ firstName: string;
|
|
|
|
|
+ lastName: string;
|
|
|
|
|
+ cedula: string;
|
|
|
|
|
+ phone: string;
|
|
|
|
|
+ };
|
|
|
|
|
+ student?: {
|
|
|
|
|
+ firstName: string;
|
|
|
|
|
+ lastName: string;
|
|
|
|
|
+ cedula: string;
|
|
|
|
|
+ phone: string;
|
|
|
|
|
+ admissionNumber: string;
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+interface UserFormData {
|
|
|
|
|
+ email: string;
|
|
|
|
|
+ password: string;
|
|
|
|
|
+ role: 'ADMIN' | 'TEACHER' | 'STUDENT';
|
|
|
|
|
+ firstName?: string;
|
|
|
|
|
+ lastName?: string;
|
|
|
|
|
+ cedula?: string;
|
|
|
|
|
+ phone?: string;
|
|
|
|
|
+ admissionNumber?: string;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+export default function UsersPage() {
|
|
|
|
|
+ const [users, setUsers] = useState<User[]>([]);
|
|
|
|
|
+ const [loading, setLoading] = useState(true);
|
|
|
|
|
+ const [searchTerm, setSearchTerm] = useState('');
|
|
|
|
|
+ const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
|
|
|
|
|
+ const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
|
|
|
|
|
+ const [selectedUser, setSelectedUser] = useState<User | null>(null);
|
|
|
|
|
+ const [formData, setFormData] = useState<UserFormData>({
|
|
|
|
|
+ email: '',
|
|
|
|
|
+ password: '',
|
|
|
|
|
+ role: 'STUDENT',
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ fetchUsers();
|
|
|
|
|
+ }, []);
|
|
|
|
|
+
|
|
|
|
|
+ const fetchUsers = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await fetch('/api/admin/users');
|
|
|
|
|
+ if (response.ok) {
|
|
|
|
|
+ const data = await response.json();
|
|
|
|
|
+ setUsers(data);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ toast.error('Error al cargar usuarios');
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ toast.error('Error al cargar usuarios');
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ setLoading(false);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleCreateUser = async () => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await fetch('/api/admin/users', {
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
|
|
+ },
|
|
|
|
|
+ body: JSON.stringify(formData),
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (response.ok) {
|
|
|
|
|
+ toast.success('Usuario creado exitosamente');
|
|
|
|
|
+ setIsCreateDialogOpen(false);
|
|
|
|
|
+ resetForm();
|
|
|
|
|
+ fetchUsers();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const error = await response.json();
|
|
|
|
|
+ toast.error(error.message || 'Error al crear usuario');
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ toast.error('Error al crear usuario');
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleUpdateUser = async () => {
|
|
|
|
|
+ if (!selectedUser) return;
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await fetch(`/api/admin/users/${selectedUser.id}`, {
|
|
|
|
|
+ method: 'PUT',
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
|
|
+ },
|
|
|
|
|
+ body: JSON.stringify(formData),
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (response.ok) {
|
|
|
|
|
+ toast.success('Usuario actualizado exitosamente');
|
|
|
|
|
+ setIsEditDialogOpen(false);
|
|
|
|
|
+ resetForm();
|
|
|
|
|
+ fetchUsers();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const error = await response.json();
|
|
|
|
|
+ toast.error(error.message || 'Error al actualizar usuario');
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ toast.error('Error al actualizar usuario');
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleDeleteUser = async (userId: string) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const response = await fetch(`/api/admin/users/${userId}`, {
|
|
|
|
|
+ method: 'DELETE',
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (response.ok) {
|
|
|
|
|
+ toast.success('Usuario eliminado exitosamente');
|
|
|
|
|
+ fetchUsers();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const error = await response.json();
|
|
|
|
|
+ toast.error(error.message || 'Error al eliminar usuario');
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ toast.error('Error al eliminar usuario');
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const resetForm = () => {
|
|
|
|
|
+ setFormData({
|
|
|
|
|
+ email: '',
|
|
|
|
|
+ password: '',
|
|
|
|
|
+ role: 'STUDENT',
|
|
|
|
|
+ });
|
|
|
|
|
+ setSelectedUser(null);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const openEditDialog = (user: User) => {
|
|
|
|
|
+ setSelectedUser(user);
|
|
|
|
|
+ setFormData({
|
|
|
|
|
+ email: user.email,
|
|
|
|
|
+ password: '',
|
|
|
|
|
+ role: user.role,
|
|
|
|
|
+ firstName: user.teacher?.firstName || user.student?.firstName || '',
|
|
|
|
|
+ lastName: user.teacher?.lastName || user.student?.lastName || '',
|
|
|
|
|
+ cedula: user.teacher?.cedula || user.student?.cedula || '',
|
|
|
|
|
+ phone: user.teacher?.phone || user.student?.phone || '',
|
|
|
|
|
+ admissionNumber: user.student?.admissionNumber || '',
|
|
|
|
|
+ });
|
|
|
|
|
+ setIsEditDialogOpen(true);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const filteredUsers = users.filter(user =>
|
|
|
|
|
+ user.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
|
|
|
+ (user.teacher && `${user.teacher.firstName} ${user.teacher.lastName}`.toLowerCase().includes(searchTerm.toLowerCase())) ||
|
|
|
|
|
+ (user.student && `${user.student.firstName} ${user.student.lastName}`.toLowerCase().includes(searchTerm.toLowerCase()))
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ const getRoleBadgeVariant = (role: string) => {
|
|
|
|
|
+ switch (role) {
|
|
|
|
|
+ case 'ADMIN':
|
|
|
|
|
+ return 'destructive';
|
|
|
|
|
+ case 'TEACHER':
|
|
|
|
|
+ return 'default';
|
|
|
|
|
+ case 'STUDENT':
|
|
|
|
|
+ return 'secondary';
|
|
|
|
|
+ default:
|
|
|
|
|
+ return 'outline';
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const getUserDisplayName = (user: User) => {
|
|
|
|
|
+ if (user.teacher) {
|
|
|
|
|
+ return `${user.teacher.firstName} ${user.teacher.lastName}`;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (user.student) {
|
|
|
|
|
+ return `${user.student.firstName} ${user.student.lastName}`;
|
|
|
|
|
+ }
|
|
|
|
|
+ return user.email;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <MainLayout requiredRole="ADMIN" title="Gestión de Usuarios">
|
|
|
|
|
+ <div className="space-y-6">
|
|
|
|
|
+ <div className="flex items-center justify-between">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <h1 className="text-3xl font-bold tracking-tight">Gestión de Usuarios</h1>
|
|
|
|
|
+ <p className="text-muted-foreground">
|
|
|
|
|
+ Administra usuarios del sistema
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
|
|
|
|
|
+ <DialogTrigger asChild>
|
|
|
|
|
+ <Button onClick={resetForm}>
|
|
|
|
|
+ <Plus className="mr-2 h-4 w-4" />
|
|
|
|
|
+ Nuevo Usuario
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </DialogTrigger>
|
|
|
|
|
+ <DialogContent className="sm:max-w-[425px]">
|
|
|
|
|
+ <DialogHeader>
|
|
|
|
|
+ <DialogTitle>Crear Nuevo Usuario</DialogTitle>
|
|
|
|
|
+ <DialogDescription>
|
|
|
|
|
+ Completa los datos para crear un nuevo usuario en el sistema.
|
|
|
|
|
+ </DialogDescription>
|
|
|
|
|
+ </DialogHeader>
|
|
|
|
|
+ <div className="grid gap-4 py-4">
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="email" className="text-right">
|
|
|
|
|
+ Email
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="email"
|
|
|
|
|
+ type="email"
|
|
|
|
|
+ value={formData.email}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="password" className="text-right">
|
|
|
|
|
+ Contraseña
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="password"
|
|
|
|
|
+ type="password"
|
|
|
|
|
+ value={formData.password}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="role" className="text-right">
|
|
|
|
|
+ Rol
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ value={formData.role}
|
|
|
|
|
+ onValueChange={(value: 'ADMIN' | 'TEACHER' | 'STUDENT') =>
|
|
|
|
|
+ setFormData({ ...formData, role: value })
|
|
|
|
|
+ }
|
|
|
|
|
+ >
|
|
|
|
|
+ <SelectTrigger className="col-span-3">
|
|
|
|
|
+ <SelectValue />
|
|
|
|
|
+ </SelectTrigger>
|
|
|
|
|
+ <SelectContent>
|
|
|
|
|
+ <SelectItem value="ADMIN">Administrador</SelectItem>
|
|
|
|
|
+ <SelectItem value="TEACHER">Profesor</SelectItem>
|
|
|
|
|
+ <SelectItem value="STUDENT">Estudiante</SelectItem>
|
|
|
|
|
+ </SelectContent>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {(formData.role === 'TEACHER' || formData.role === 'STUDENT') && (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="firstName" className="text-right">
|
|
|
|
|
+ Nombres
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="firstName"
|
|
|
|
|
+ value={formData.firstName || ''}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, firstName: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="lastName" className="text-right">
|
|
|
|
|
+ Apellidos
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="lastName"
|
|
|
|
|
+ value={formData.lastName || ''}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, lastName: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="cedula" className="text-right">
|
|
|
|
|
+ Cédula
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="cedula"
|
|
|
|
|
+ value={formData.cedula || ''}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, cedula: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="phone" className="text-right">
|
|
|
|
|
+ Teléfono
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="phone"
|
|
|
|
|
+ value={formData.phone || ''}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, phone: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {formData.role === 'STUDENT' && (
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="admissionNumber" className="text-right">
|
|
|
|
|
+ Matrícula
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="admissionNumber"
|
|
|
|
|
+ value={formData.admissionNumber || ''}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, admissionNumber: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <DialogFooter>
|
|
|
|
|
+ <Button type="submit" onClick={handleCreateUser}>
|
|
|
|
|
+ Crear Usuario
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </DialogFooter>
|
|
|
|
|
+ </DialogContent>
|
|
|
|
|
+ </Dialog>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <Card>
|
|
|
|
|
+ <CardHeader>
|
|
|
|
|
+ <CardTitle>Usuarios del Sistema</CardTitle>
|
|
|
|
|
+ <CardDescription>
|
|
|
|
|
+ Lista de todos los usuarios registrados en el sistema
|
|
|
|
|
+ </CardDescription>
|
|
|
|
|
+ <div className="flex items-center space-x-2">
|
|
|
|
|
+ <Search className="h-4 w-4 text-muted-foreground" />
|
|
|
|
|
+ <Input
|
|
|
|
|
+ placeholder="Buscar usuarios..."
|
|
|
|
|
+ value={searchTerm}
|
|
|
|
|
+ onChange={(e) => setSearchTerm(e.target.value)}
|
|
|
|
|
+ className="max-w-sm"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </CardHeader>
|
|
|
|
|
+ <CardContent>
|
|
|
|
|
+ {loading ? (
|
|
|
|
|
+ <div className="flex items-center justify-center py-8">
|
|
|
|
|
+ <div className="text-muted-foreground">Cargando usuarios...</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <Table>
|
|
|
|
|
+ <TableHeader>
|
|
|
|
|
+ <TableRow>
|
|
|
|
|
+ <TableHead>Nombre</TableHead>
|
|
|
|
|
+ <TableHead>Email</TableHead>
|
|
|
|
|
+ <TableHead>Rol</TableHead>
|
|
|
|
|
+ <TableHead>Cédula</TableHead>
|
|
|
|
|
+ <TableHead>Teléfono</TableHead>
|
|
|
|
|
+ <TableHead>Fecha Creación</TableHead>
|
|
|
|
|
+ <TableHead className="text-right">Acciones</TableHead>
|
|
|
|
|
+ </TableRow>
|
|
|
|
|
+ </TableHeader>
|
|
|
|
|
+ <TableBody>
|
|
|
|
|
+ {filteredUsers.map((user) => (
|
|
|
|
|
+ <TableRow key={user.id}>
|
|
|
|
|
+ <TableCell className="font-medium">
|
|
|
|
|
+ {getUserDisplayName(user)}
|
|
|
|
|
+ </TableCell>
|
|
|
|
|
+ <TableCell>{user.email}</TableCell>
|
|
|
|
|
+ <TableCell>
|
|
|
|
|
+ <Badge variant={getRoleBadgeVariant(user.role)}>
|
|
|
|
|
+ {user.role === 'ADMIN' ? 'Administrador' :
|
|
|
|
|
+ user.role === 'TEACHER' ? 'Profesor' : 'Estudiante'}
|
|
|
|
|
+ </Badge>
|
|
|
|
|
+ </TableCell>
|
|
|
|
|
+ <TableCell>
|
|
|
|
|
+ {user.teacher?.cedula || user.student?.cedula || '-'}
|
|
|
|
|
+ </TableCell>
|
|
|
|
|
+ <TableCell>
|
|
|
|
|
+ {user.teacher?.phone || user.student?.phone || '-'}
|
|
|
|
|
+ </TableCell>
|
|
|
|
|
+ <TableCell>
|
|
|
|
|
+ {new Date(user.createdAt).toLocaleDateString()}
|
|
|
|
|
+ </TableCell>
|
|
|
|
|
+ <TableCell className="text-right">
|
|
|
|
|
+ <div className="flex items-center justify-end space-x-2">
|
|
|
|
|
+ <Button
|
|
|
|
|
+ variant="outline"
|
|
|
|
|
+ size="sm"
|
|
|
|
|
+ onClick={() => openEditDialog(user)}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Pencil className="h-4 w-4" />
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ <AlertDialog>
|
|
|
|
|
+ <AlertDialogTrigger asChild>
|
|
|
|
|
+ <Button variant="outline" size="sm">
|
|
|
|
|
+ <Trash2 className="h-4 w-4" />
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </AlertDialogTrigger>
|
|
|
|
|
+ <AlertDialogContent>
|
|
|
|
|
+ <AlertDialogHeader>
|
|
|
|
|
+ <AlertDialogTitle>¿Estás seguro?</AlertDialogTitle>
|
|
|
|
|
+ <AlertDialogDescription>
|
|
|
|
|
+ Esta acción no se puede deshacer. Esto eliminará permanentemente
|
|
|
|
|
+ el usuario y todos sus datos asociados.
|
|
|
|
|
+ </AlertDialogDescription>
|
|
|
|
|
+ </AlertDialogHeader>
|
|
|
|
|
+ <AlertDialogFooter>
|
|
|
|
|
+ <AlertDialogCancel>Cancelar</AlertDialogCancel>
|
|
|
|
|
+ <AlertDialogAction
|
|
|
|
|
+ onClick={() => handleDeleteUser(user.id)}
|
|
|
|
|
+ >
|
|
|
|
|
+ Eliminar
|
|
|
|
|
+ </AlertDialogAction>
|
|
|
|
|
+ </AlertDialogFooter>
|
|
|
|
|
+ </AlertDialogContent>
|
|
|
|
|
+ </AlertDialog>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </TableCell>
|
|
|
|
|
+ </TableRow>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </TableBody>
|
|
|
|
|
+ </Table>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </CardContent>
|
|
|
|
|
+ </Card>
|
|
|
|
|
+
|
|
|
|
|
+ {/* Edit Dialog */}
|
|
|
|
|
+ <Dialog open={isEditDialogOpen} onOpenChange={setIsEditDialogOpen}>
|
|
|
|
|
+ <DialogContent className="sm:max-w-[425px]">
|
|
|
|
|
+ <DialogHeader>
|
|
|
|
|
+ <DialogTitle>Editar Usuario</DialogTitle>
|
|
|
|
|
+ <DialogDescription>
|
|
|
|
|
+ Modifica los datos del usuario seleccionado.
|
|
|
|
|
+ </DialogDescription>
|
|
|
|
|
+ </DialogHeader>
|
|
|
|
|
+ <div className="grid gap-4 py-4">
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="edit-email" className="text-right">
|
|
|
|
|
+ Email
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="edit-email"
|
|
|
|
|
+ type="email"
|
|
|
|
|
+ value={formData.email}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="edit-password" className="text-right">
|
|
|
|
|
+ Nueva Contraseña
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="edit-password"
|
|
|
|
|
+ type="password"
|
|
|
|
|
+ value={formData.password}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, password: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ placeholder="Dejar vacío para mantener actual"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="edit-role" className="text-right">
|
|
|
|
|
+ Rol
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ value={formData.role}
|
|
|
|
|
+ onValueChange={(value: 'ADMIN' | 'TEACHER' | 'STUDENT') =>
|
|
|
|
|
+ setFormData({ ...formData, role: value })
|
|
|
|
|
+ }
|
|
|
|
|
+ >
|
|
|
|
|
+ <SelectTrigger className="col-span-3">
|
|
|
|
|
+ <SelectValue />
|
|
|
|
|
+ </SelectTrigger>
|
|
|
|
|
+ <SelectContent>
|
|
|
|
|
+ <SelectItem value="ADMIN">Administrador</SelectItem>
|
|
|
|
|
+ <SelectItem value="TEACHER">Profesor</SelectItem>
|
|
|
|
|
+ <SelectItem value="STUDENT">Estudiante</SelectItem>
|
|
|
|
|
+ </SelectContent>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {(formData.role === 'TEACHER' || formData.role === 'STUDENT') && (
|
|
|
|
|
+ <>
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="edit-firstName" className="text-right">
|
|
|
|
|
+ Nombres
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="edit-firstName"
|
|
|
|
|
+ value={formData.firstName || ''}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, firstName: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="edit-lastName" className="text-right">
|
|
|
|
|
+ Apellidos
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="edit-lastName"
|
|
|
|
|
+ value={formData.lastName || ''}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, lastName: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="edit-cedula" className="text-right">
|
|
|
|
|
+ Cédula
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="edit-cedula"
|
|
|
|
|
+ value={formData.cedula || ''}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, cedula: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="edit-phone" className="text-right">
|
|
|
|
|
+ Teléfono
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="edit-phone"
|
|
|
|
|
+ value={formData.phone || ''}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, phone: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {formData.role === 'STUDENT' && (
|
|
|
|
|
+ <div className="grid grid-cols-4 items-center gap-4">
|
|
|
|
|
+ <Label htmlFor="edit-admissionNumber" className="text-right">
|
|
|
|
|
+ Matrícula
|
|
|
|
|
+ </Label>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ id="edit-admissionNumber"
|
|
|
|
|
+ value={formData.admissionNumber || ''}
|
|
|
|
|
+ onChange={(e) => setFormData({ ...formData, admissionNumber: e.target.value })}
|
|
|
|
|
+ className="col-span-3"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <DialogFooter>
|
|
|
|
|
+ <Button type="submit" onClick={handleUpdateUser}>
|
|
|
|
|
+ Actualizar Usuario
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </DialogFooter>
|
|
|
|
|
+ </DialogContent>
|
|
|
|
|
+ </Dialog>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </MainLayout>
|
|
|
|
|
+ );
|
|
|
|
|
+}
|