FerreMarket uses a three-tier role-based access control (RBAC) system to govern what each user can see and do inside the application. Roles are stored in theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/ProyectoFerretek/FerreMarket/llms.txt
Use this file to discover all available pages before exploring further.
usuarios table in Supabase and loaded into memory at startup by cargarPermisosTrabajador(). All permission checks across the app are synchronous comparisons against this in-memory object — there are no per-request server-side checks from the React client beyond the initial load. Understanding how roles are assigned, loaded, and evaluated is essential for both configuring user accounts correctly and extending the permission system.
Available Roles
FerreMarket defines exactly three roles. EveryUsuario record must have one of the following values in its rol column:
| Role | Display Name | Description | Key Permissions |
|---|---|---|---|
admin | Administrador | Full system access. | Manage users, create/edit/delete records in all modules, view all reports, trigger password recovery, access the Usuarios page. |
usuario | Usuario | Standard store operator. | Create and manage sales, products, clients, and promotions. Cannot access the Usuarios management page or perform user-related actions. |
cliente | Cliente | Customer-facing role. | Reserved for future customer portal features. Currently has the most limited access within the admin panel. |
Role values in the Supabase
usuarios table are stored in lowercase (admin, usuario, cliente). When cargarPermisosTrabajador() loads the data it passes the value through capitalizeWords(), which means the in-memory usuarioActual.rol is capitalised (e.g. 'Admin', 'Usuario', 'Cliente'). All permission guard functions compare against the capitalised form. Passing a lowercase string will cause the check to fail silently — see the note at the bottom of this page.Permission Guards
All permission logic is centralised insrc/utils/auth.ts. The module maintains a private usuarioActual object that is populated once at startup and read synchronously by every guard function.
esAdministrador()
true if the current user’s role is 'Admin' (capitalised). This is the base check used by all other guards. Components and pages that are limited to administrators should call this function first.
puedeGestionarUsuarios()
true only when the current user is both an administrator and has an active account (estado === 'Activo'). This is the guard evaluated at the top of GestionUsuarios — if it returns false, the entire Users page is replaced with an access-denied screen.
puedeRealizarAccion(accion)
Admin role; eliminar additionally requires estado === 'Activo'. The accepted action values are:
| Action | Description |
|---|---|
'ver' | View a resource’s detail or open it in read-only mode. |
'crear' | Create a new record (e.g. add a new user). |
'editar' | Modify an existing record. |
'recuperar' | Trigger a password recovery email for a user. |
'eliminar' | Permanently delete a record. Requires active admin status. |
obtenerUsuarioActual()
{ id, rol, estado, nombre }). Useful for displaying the logged-in user’s name or role in UI components without re-querying the database.
cargarPermisosTrabajador()
usuarios table using their Supabase Auth UUID (uid). It then capitalises the rol, estado, and nombre values via the private capitalizeWords helper before storing them in usuarioActual. Must be called after an active session is confirmed.
obtenerUsuarioIdByUUID(uuid)
id in the usuarios table. Returns null if no matching record is found or if an error occurs. Useful for operations that need the database-level ID (e.g. linking sales or audit records to a user) when only the Auth UUID is available from the session.
Calling cargarPermisosTrabajador
This function must be called once at application startup, after Supabase Auth has confirmed there is an active session. Calling it too early (before the session resolves) will result in an empty usuarioActual and all permission guards returning false.
esAdministrador(), puedeGestionarUsuarios(), etc.) are synchronous and safe to call anywhere in the component tree.
Checking Permissions in a Component
Import and call the relevant guard function directly in your component logic:puedeRealizarAccion to conditionally render buttons:
Protected Routes
At the routing level, FerreMarket uses aProtectedRoute wrapper component defined in src/App.tsx. It reads the session value from AuthContext and redirects unauthenticated visitors to /admin/login:
ProtectedRoute handles authentication (is there a valid session?). Role-based authorisation (can this authenticated user see this page?) is handled separately by the puedeGestionarUsuarios() and puedeRealizarAccion() guards inside each page component.
Role capitalisation matters. The
usuarios table stores roles as lowercase strings (admin, usuario, cliente), but cargarPermisosTrabajador capitalises them before storing in usuarioActual. All guard functions check for the capitalised value — 'Admin', not 'admin'. If you query usuarioActual.rol directly and compare against the raw database value, the comparison will always be false. Always use the exported guard functions rather than inspecting usuarioActual directly.