diff --git a/src/pages/RolesPage.tsx b/src/pages/RolesPage.tsx index 3456838..c763fe0 100644 --- a/src/pages/RolesPage.tsx +++ b/src/pages/RolesPage.tsx @@ -1,63 +1,148 @@ import { useState, useEffect } from "react"; -import { Plus, Trash2, Pencil, RefreshCcw } from "lucide-react"; +import { Plus, Trash2, Pencil, RefreshCcw, AlertCircle, Loader2 } from "lucide-react"; import MaterialTable from "@material-table/core"; +import { getAllRoles, createRole, updateRole, deleteRole, type Role } from "../api/roles"; +import ConfirmModal from "../components/layout/common/ConfirmModal"; -export interface Role { - id: string; +interface RoleForm { name: string; description: string; - status: "ACTIVE" | "INACTIVE"; - createdAt: string; + permissions?: Record>; } export default function RolesPage() { - const initialRoles: Role[] = [ - { id: "1", name: "SUPER_ADMIN", description: "Full access", status: "ACTIVE", createdAt: "2025-12-17" }, - { id: "2", name: "USER", description: "Regular user", status: "ACTIVE", createdAt: "2025-12-16" }, - ]; - - const [roles, setRoles] = useState(initialRoles); + const [roles, setRoles] = useState([]); const [activeRole, setActiveRole] = useState(null); const [search, setSearch] = useState(""); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); const [showModal, setShowModal] = useState(false); + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [editingId, setEditingId] = useState(null); - const emptyRole: Omit = { + const emptyForm: RoleForm = { name: "", description: "", - status: "ACTIVE", - createdAt: new Date().toISOString().slice(0, 10), + permissions: {}, }; - const [form, setForm] = useState>(emptyRole); + const [form, setForm] = useState(emptyForm); - const handleSave = () => { - if (editingId) { - setRoles(prev => prev.map(r => r.id === editingId ? { id: editingId, ...form } : r)); - } else { - const newId = Date.now().toString(); - setRoles(prev => [...prev, { id: newId, ...form }]); + useEffect(() => { + fetchRoles(); + }, []); + + const fetchRoles = async () => { + setLoading(true); + setError(null); + try { + const data = await getAllRoles(); + setRoles(data); + } catch (err) { + setError(err instanceof Error ? err.message : "Failed to load roles"); + console.error("Error fetching roles:", err); + } finally { + setLoading(false); } - setShowModal(false); - setEditingId(null); - setForm(emptyRole); }; - const handleDelete = () => { + const handleSave = async () => { + if (!form.name.trim()) { + setError("Role name is required"); + return; + } + + setLoading(true); + setError(null); + try { + if (editingId) { + const updated = await updateRole(editingId, form); + setRoles(prev => prev.map(r => r.id === editingId ? updated : r)); + } else { + const created = await createRole(form); + setRoles(prev => [...prev, created]); + } + setShowModal(false); + setEditingId(null); + setForm(emptyForm); + setActiveRole(null); + } catch (err) { + setError(err instanceof Error ? err.message : "Failed to save role"); + console.error("Error saving role:", err); + } finally { + setLoading(false); + } + }; + + const handleDelete = async () => { if (!activeRole) return; - setRoles(prev => prev.filter(r => r.id !== activeRole.id)); - setActiveRole(null); + + setLoading(true); + setError(null); + try { + await deleteRole(activeRole.id); + setRoles(prev => prev.filter(r => r.id !== activeRole.id)); + setActiveRole(null); + setShowDeleteConfirm(false); + } catch (err) { + setError(err instanceof Error ? err.message : "Failed to delete role"); + console.error("Error deleting role:", err); + } finally { + setLoading(false); + } }; - const filtered = roles.filter(r => r.name.toLowerCase().includes(search.toLowerCase())); + const openEditModal = () => { + if (!activeRole) return; + setEditingId(activeRole.id); + setForm({ + name: activeRole.name, + description: activeRole.description, + permissions: activeRole.permissions, + }); + setShowModal(true); + }; + + const filtered = roles.filter(r => + r.name.toLowerCase().includes(search.toLowerCase()) || + r.description?.toLowerCase().includes(search.toLowerCase()) + ); return (
{/* LEFT INFO SIDEBAR */}

Role Information

-

Aquí se listan los roles disponibles en el sistema.

+

+ Manage system roles and their permissions. Roles define what actions users can perform. +

+ + {activeRole && ( +
+

Selected Role

+
+
+

Name

+

{activeRole.name}

+
+
+

Description

+

{activeRole.description || "No description"}

+
+
+

Created

+

{new Date(activeRole.created_at).toLocaleDateString()}

+
+ {activeRole.permissions && Object.keys(activeRole.permissions).length > 0 && ( +
+

Permissions

+

{Object.keys(activeRole.permissions).length} permission groups

+
+ )} +
+
+ )}
{/* MAIN */} @@ -67,83 +152,213 @@ export default function RolesPage() { style={{ background: "linear-gradient(135deg, #4c5f9e, #2a355d, #566bb8)" }}>

Role Management

-

Roles registrados

+

{roles.length} roles registered

- - - -
+ {/* ERROR ALERT */} + {error && ( +
+ +
+

Error

+

{error}

+
+ +
+ )} + {/* SEARCH */} - setSearch(e.target.value)} /> + setSearch(e.target.value)} + /> {/* TABLE */} - ( - - {rowData.status} - - ) - }, - { title: "Created", field: "createdAt", type: "date" } - ]} - data={filtered} - onRowClick={(_, rowData) => setActiveRole(rowData as Role)} - options={{ - actionsColumnIndex: -1, - search: false, - paging: true, - sorting: true, - rowStyle: (rowData) => ({ backgroundColor: activeRole?.id === (rowData as Role).id ? "#EEF2FF" : "#FFFFFF" }) - }} - /> +
+ {loading && roles.length === 0 ? ( +
+ +
+ ) : ( + ( + {rowData.name} + ) + }, + { + title: "Description", + field: "description", + render: (rowData) => ( + {rowData.description || "—"} + ) + }, + { + title: "Permissions", + field: "permissions", + render: (rowData) => { + const count = rowData.permissions ? Object.keys(rowData.permissions).length : 0; + return ( + + {count > 0 ? `${count} groups` : "No permissions"} + + ); + } + }, + { + title: "Created", + field: "created_at", + type: "date", + render: (rowData) => ( + + {new Date(rowData.created_at).toLocaleDateString()} + + ) + } + ]} + data={filtered} + onRowClick={(_, rowData) => setActiveRole(rowData as Role)} + options={{ + actionsColumnIndex: -1, + search: false, + paging: true, + pageSize: 10, + pageSizeOptions: [10, 20, 50], + sorting: true, + rowStyle: (rowData) => ({ + backgroundColor: activeRole?.id === (rowData as Role).id ? "#EEF2FF" : "#FFFFFF", + cursor: "pointer" + }) + }} + /> + )} +
- {/* MODAL */} + {/* ADD/EDIT MODAL */} {showModal && ( -
-
-

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

- setForm({...form, name: e.target.value})} /> - setForm({...form, description: e.target.value})} /> - - setForm({...form, createdAt: e.target.value})} /> -
- - +
+
+

+ {editingId ? "Edit Role" : "Add New Role"} +

+ +
+
+ + setForm({...form, name: e.target.value})} + disabled={loading} + /> +
+ +
+ +