import { useState, useEffect } from "react"; import { Plus, Trash2, Pencil, RefreshCcw } from "lucide-react"; import MaterialTable from "@material-table/core"; import { getAllRoles, Role as ApiRole } from "../api/roles"; import { createUser, getAllUsers, CreateUserInput, User as ApiUser } from "../api/users"; interface User { id: string; name: string; email: string; roleId: string; roleName: string; status: "ACTIVE" | "INACTIVE"; createdAt: string; } interface UserForm { firstName: string; lastName: string; email: string; password?: string; roleId: string; status: "ACTIVE" | "INACTIVE"; createdAt: string; } export default function UsersPage() { const [users, setUsers] = useState([]); const [activeUser, setActiveUser] = useState(null); const [search, setSearch] = useState(""); const [showModal, setShowModal] = useState(false); const [editingId, setEditingId] = useState(null); const [roles, setRoles] = useState([]); const [loadingRoles, setLoadingRoles] = useState(true); const [loadingUsers, setLoadingUsers] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const emptyUser: UserForm = { firstName: "", lastName: "", email: "", roleId: "", password: "", status: "ACTIVE", createdAt: new Date().toISOString().slice(0,10) }; const [form, setForm] = useState(emptyUser); // Fetch roles and users from API on component mount useEffect(() => { const fetchRoles = async () => { try { setLoadingRoles(true); const rolesData = await getAllRoles(); console.log('Roles fetched:', rolesData); setRoles(rolesData); } catch (error) { console.error('Failed to fetch roles:', error); } finally { setLoadingRoles(false); } }; const fetchUsers = async () => { try { setLoadingUsers(true); const usersResponse = await getAllUsers(); console.log('Users API response:', usersResponse); // Map API users to UI format const mappedUsers: User[] = usersResponse.data.map((apiUser: ApiUser) => ({ id: apiUser.id, name: `${apiUser.first_name} ${apiUser.last_name}`, email: apiUser.email, roleId: apiUser.role_id, roleName: apiUser.role?.name || '', status: apiUser.is_active ? "ACTIVE" : "INACTIVE", createdAt: new Date(apiUser.created_at).toISOString().slice(0, 10) })); console.log('Mapped users:', mappedUsers); setUsers(mappedUsers); } catch (error) { console.error('Failed to fetch users:', error); // Keep empty array on error setUsers([]); } finally { setLoadingUsers(false); } }; fetchRoles(); fetchUsers(); }, []); const handleSave = async () => { setError(null); // Validation if (!form.firstName || !form.lastName || !form.email || !form.roleId) { setError("Please fill in all required fields"); return; } if (!editingId && !form.password) { setError("Password is required for new users"); return; } if (form.password && form.password.length < 8) { setError("Password must be at least 8 characters"); return; } try { setSaving(true); if (editingId) { // TODO: Implement update user const roleName = roles.find(r => r.id === form.roleId)?.name || ""; const fullName = `${form.firstName} ${form.lastName}`; setUsers(prev => prev.map(u => u.id === editingId ? { id: editingId, name: fullName, email: form.email, roleId: form.roleId, roleName, status: form.status, createdAt: form.createdAt } : u)); } else { // Create new user const roleIdNum = parseInt(form.roleId, 10); if (isNaN(roleIdNum)) { setError("Please select a valid role"); return; } const createData: CreateUserInput = { email: form.email, password: form.password!, first_name: form.firstName, last_name: form.lastName, role_id: roleIdNum, is_active: form.status === "ACTIVE", }; const newUser = await createUser(createData); const roleName = roles.find(r => r.id === form.roleId)?.name || ""; // Add the new user to the list const apiUser: ApiUser = newUser; setUsers(prev => [...prev, { id: apiUser.id, name: `${apiUser.first_name} ${apiUser.last_name}`, email: apiUser.email, roleId: apiUser.role_id, roleName: roleName, status: apiUser.is_active ? "ACTIVE" : "INACTIVE", createdAt: new Date(apiUser.created_at).toISOString().slice(0, 10) }]); } setShowModal(false); setEditingId(null); setForm(emptyUser); } catch (err) { console.error('Failed to save user:', err); setError(err instanceof Error ? err.message : 'Failed to save user'); } finally { setSaving(false); } }; const handleRefresh = async () => { try { setLoadingUsers(true); const usersResponse = await getAllUsers(); const mappedUsers: User[] = usersResponse.data.map((apiUser: ApiUser) => ({ id: apiUser.id, name: `${apiUser.first_name} ${apiUser.last_name}`, email: apiUser.email, roleId: apiUser.role_id, roleName: apiUser.role?.name || '', status: apiUser.is_active ? "ACTIVE" : "INACTIVE", createdAt: new Date(apiUser.created_at).toISOString().slice(0, 10) })); setUsers(mappedUsers); } catch (error) { console.error('Failed to refresh users:', error); } finally { setLoadingUsers(false); } }; const handleDelete = () => { if (!activeUser) return; setUsers(prev => prev.filter(u => u.id !== activeUser.id)); setActiveUser(null); }; const filtered = users.filter(u => u.name.toLowerCase().includes(search.toLowerCase()) || u.email.toLowerCase().includes(search.toLowerCase())); return (
{/* LEFT INFO SIDEBAR */}

Project Information

Usuarios disponibles y sus roles.

{/* MAIN */}
{/* HEADER */}

User Management

Usuarios registrados

{/* SEARCH */} setSearch(e.target.value)} /> {/* TABLE */} {loadingUsers ? (

Loading users...

) : ( {rowData.status} }, { title: "Created", field: "createdAt", type: "date" } ]} data={filtered} onRowClick={(_, rowData) => setActiveUser(rowData as User)} options={{ actionsColumnIndex: -1, search: false, paging: true, sorting: true, rowStyle: rowData => ({ backgroundColor: activeUser?.id === (rowData as User).id ? "#EEF2FF" : "#FFFFFF" }) }} /> )}
{/* MODAL */} {showModal && (

{editingId ? "Edit User" : "Add User"}

{error && (
{error}
)} setForm({...form, firstName: e.target.value})} disabled={saving} /> setForm({...form, lastName: e.target.value})} disabled={saving} /> setForm({...form, email: e.target.value})} disabled={saving} /> {!editingId && ( setForm({...form, password: e.target.value})} disabled={saving} /> )}
)}
); }