Módulo: Usuarios¶
Este módulo implementa listado, edición y acciones de activación/eliminación de usuarios siguiendo el mismo patrón que roles/*.
Columnas (SSOT)¶
id(ordenable)name(ordenable)email(ordenable)roles_count(ordenable)is_active(ordenable)created_at(ordenable)
Las columnas están definidas en resources/js/pages/users/columns.tsx y son la fuente de verdad también para exportaciones (vía UserService::defaultExportColumns).
Filtros¶
Implementados con FilterSheet y FilterBadges (mismo UX de Roles):
name(texto, LIKE case-insensitive)email(texto, LIKE case-insensitive)role_id(select de roles disponibles)is_active(select all/active/inactive)created_between(rango de fechas)
Archivo: resources/js/pages/users/filters.tsx.
Permisos¶
Definidos en config/permissions/users.php y agregados por config/permissions.php:
users.viewusers.createusers.updateusers.deleteusers.restoreusers.forceDeleteusers.exportusers.setActive
Policy: App\Policies\UserPolicy (extiende BaseResourcePolicy con prefijo users).
Exportación¶
- Endpoint:
GET /users/export?format=csv|xlsx|json - Permiso:
users.export - Columns SSOT desde
UserService::defaultExportColumns()
Backend (resumen)¶
- Request:
App\Http\Requests\UserIndexRequest(extiendeBaseIndexRequest) - Repository:
App\Repositories\UserRepository(extiendeBaseRepository)searchable():name,emailallowedSorts():id,name,email,is_active,created_atwithRelations():withCount('roles')filterMap():name,email,role_id,is_active,created_between
- Service:
App\Services\UserService(extiendeBaseService)toRow():{ id, name, email, is_active, roles_count, created_at }defaultExportColumns()ydefaultExportFilename()
- Controller:
App\Http\Controllers\UsersController(extiendeBaseIndexController)view():users/indexindexRequestClass():UserIndexRequestallowedExportFormats():csv,xlsx,json- Extra:
availableRolespara el filtro
-
Rutas:
routes/users.phpGET /users→ indexGET /users/export→ exportPOST /users/bulk→ operaciones masivas (delete,restore,forceDelete,setActive)GET /users/selected→ subset de IDsGET /users/{user}→ showGET /users/{user}/edit→ editPUT /users/{user}→ updatePATCH /users/{user}/active→ setActive (independiente de update)DELETE /users/{user}→ destroy
En Laravel 12, las rutas están reforzadas con middleware de Spatie (
permission:*) además de las Policies, p. ej.permission:users.view,permission:users.export,permission:users.setActive. Asegúrate de registrar los aliases enbootstrap/app.php(ver guía "Permisos (permission-first)").
Frontend (resumen)¶
- Vista:
resources/js/pages/users/index.tsx- TanStack v8 en modo manual:
manualPagination,manualSorting,rowCount - Inertia partial reloads:
only: ['rows', 'meta'],preserveState,preserveScroll - Bulk actions: delete (si
auth.can['users.delete']) - Export:
users.export - Stats cards: tarjetas dinámicas con
stats.total,stats.inactive(backend las provee)
- TanStack v8 en modo manual:
- Columnas:
resources/js/pages/users/columns.tsx - Filtros:
resources/js/pages/users/filters.tsx - Vista Show:
resources/js/pages/users/show.tsx- Botón Eliminar con
ConfirmAlert→DELETE /users/{id}(toasts y redirección) - Consistencia con Roles Show
- Botón Eliminar con
UX/Comportamiento clave¶
- Misma UI/estilos que Roles
- Controles visibles aunque no haya resultados
- Densidad de tabla persistida en
localStoragebajousers_table_density - Navegación lateral: ítem "Usuarios" visible sólo si
auth.can['users.view']
Reglas de negocio (configurables)¶
- Archivo:
config/permissions/users.phpusers.activation.block_self_deactivate(bool, default true): impide desactivar tu propio usuariousers.activation.block_deactivate_if_last_admin(bool): impide desactivar al último administradorusers.activation.admin_role_name(string): nombre del rol considerado adminusers.deletion.require_inactive(bool): requiere inactivo antes de permitir eliminarusers.deletion.block_if_last_admin(bool): impide eliminar al último administrador
Estas reglas se aplican en los FormRequest:
SetUserActiveRequest(PATCH/users/{id}/active)DeleteUsersRequest(DELETE/users/{id})