From 0120c4bfb8c846454cf2f8fbe05da4d716a4804a Mon Sep 17 00:00:00 2001 From: javier Date: Wed, 17 Jun 2026 18:51:59 +0200 Subject: [PATCH] feat(roles/users): add-user form on role view + per-user direct permissions form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Role view (Details tab): a small form to add users to the role (select of users not yet in the role + Add) and a per-row remove button. Uses assignRole/removeRole. 2. User view (Permissions tab): the same grouped, collapsible permissions form with switches — operating on the user's DIRECT permissions (givePermissionTo/revokePermissionTo). Permissions inherited from a role show as checked+disabled with a 'from role' tag; per-group All/None too. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/Livewire/RoleView.php | 31 +++++++-- app/Livewire/UserView.php | 50 +++++++++++++- .../livewire/project-table.blade.php.backup | 3 - .../livewire/projects/project-map.blade.php | 10 +-- .../role-permission-manager.blade.php | 4 +- .../views/livewire/roles/role-view.blade.php | 57 +++++++++++----- resources/views/livewire/user-view.blade.php | 66 ++++++++++++++++++- 7 files changed, 188 insertions(+), 33 deletions(-) delete mode 100644 resources/views/livewire/project-table.blade.php.backup diff --git a/app/Livewire/RoleView.php b/app/Livewire/RoleView.php index f08071a..fb437fe 100644 --- a/app/Livewire/RoleView.php +++ b/app/Livewire/RoleView.php @@ -6,6 +6,7 @@ use Livewire\Component; use Livewire\Attributes\Layout; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Str; +use App\Models\User; use Spatie\Permission\Models\Role; use Spatie\Permission\Models\Permission; use Spatie\Permission\PermissionRegistrar; @@ -15,6 +16,7 @@ class RoleView extends Component { public Role $role; public string $tab = 'ficha'; // ficha | permisos + public $newUserId = ''; private const PROTECTED_ROLES = ['Admin']; private const CORE_PERMISSION = 'manage all'; @@ -51,6 +53,23 @@ class RoleView extends Component $this->dispatch('notify', 'Permisos actualizados'); } + public function addUser(): void + { + $this->validate(['newUserId' => 'required|exists:users,id'], [], ['newUserId' => 'usuario']); + + User::findOrFail($this->newUserId)->assignRole($this->role->name); + $this->newUserId = ''; + app(PermissionRegistrar::class)->forgetCachedPermissions(); + $this->dispatch('notify', 'Usuario añadido al rol'); + } + + public function removeUser(int $userId): void + { + User::findOrFail($userId)->removeRole($this->role->name); + app(PermissionRegistrar::class)->forgetCachedPermissions(); + $this->dispatch('notify', 'Usuario quitado del rol'); + } + public function setGroup(string $group, bool $enabled): void { $names = Permission::where('group', $group)->pluck('name'); @@ -110,11 +129,15 @@ class RoleView extends Component return $i === false ? 999 : $i; }); + $availableUsers = User::whereDoesntHave('roles', fn ($q) => $q->where('roles.id', $this->role->id)) + ->orderBy('first_name')->orderBy('name')->get(); + return view('livewire.roles.role-view', [ - 'users' => $users, - 'grouped' => $grouped, - 'rolePerms' => $this->role->permissions->pluck('name')->toArray(), - 'isProtected' => in_array($this->role->name, self::PROTECTED_ROLES, true), + 'users' => $users, + 'availableUsers' => $availableUsers, + 'grouped' => $grouped, + 'rolePerms' => $this->role->permissions->pluck('name')->toArray(), + 'isProtected' => in_array($this->role->name, self::PROTECTED_ROLES, true), ]); } } diff --git a/app/Livewire/UserView.php b/app/Livewire/UserView.php index b34e0eb..538ee9c 100644 --- a/app/Livewire/UserView.php +++ b/app/Livewire/UserView.php @@ -9,6 +9,8 @@ use App\Models\Project; use App\Models\Inspection; use App\Models\Issue; use Illuminate\Support\Facades\Auth; +use Spatie\Permission\Models\Permission; +use Spatie\Permission\PermissionRegistrar; #[Layout('layouts.app')] class UserView extends Component @@ -93,6 +95,36 @@ class UserView extends Component $this->dispatch('notify', 'Proyecto desasignado.'); } + // ── Permissions (direct, per user) ───────────────────────────────────────── + + public function togglePermission(string $name): void + { + if ($this->user->hasDirectPermission($name)) { + $this->user->revokePermissionTo($name); + } else { + $this->user->givePermissionTo($name); + } + app(PermissionRegistrar::class)->forgetCachedPermissions(); + $this->user->load('roles', 'permissions'); + $this->dispatch('notify', 'Permisos del usuario actualizados'); + } + + public function setUserGroup(string $group, bool $enabled): void + { + foreach (Permission::where('group', $group)->pluck('name') as $name) { + if ($enabled) { + if (! $this->user->hasPermissionTo($name)) { + $this->user->givePermissionTo($name); + } + } elseif ($this->user->hasDirectPermission($name)) { + $this->user->revokePermissionTo($name); + } + } + app(PermissionRegistrar::class)->forgetCachedPermissions(); + $this->user->load('roles', 'permissions'); + $this->dispatch('notify', $enabled ? 'Permisos del grupo concedidos' : 'Permisos directos del grupo quitados'); + } + // ── Notes ───────────────────────────────────────────────────────────────── public function saveNotes(): void @@ -105,6 +137,22 @@ class UserView extends Component public function render() { - return view('livewire.user-view'); + $order = [ + 'Proyectos', 'Fases y progreso', 'Capas y elementos', 'Inspecciones', + 'Incidencias', 'Empresas', 'Usuarios', 'Roles', 'Informes', 'Archivos', 'General', + ]; + + $grouped = Permission::orderBy('name')->get() + ->groupBy(fn ($perm) => $perm->group ?: 'General') + ->sortBy(function ($perms, $section) use ($order) { + $i = array_search($section, $order, true); + return $i === false ? 999 : $i; + }); + + return view('livewire.user-view', [ + 'grouped' => $grouped, + 'directPerms' => $this->user->getDirectPermissions()->pluck('name')->toArray(), + 'rolePerms' => $this->user->getPermissionsViaRoles()->pluck('name')->toArray(), + ]); } } diff --git a/resources/views/livewire/project-table.blade.php.backup b/resources/views/livewire/project-table.blade.php.backup deleted file mode 100644 index fd5ed6b..0000000 --- a/resources/views/livewire/project-table.blade.php.backup +++ /dev/null @@ -1,3 +0,0 @@ -
- {{-- Nothing in the world is as soft and yielding as water. --}} -
diff --git a/resources/views/livewire/projects/project-map.blade.php b/resources/views/livewire/projects/project-map.blade.php index d68cdc0..d2b7bbe 100644 --- a/resources/views/livewire/projects/project-map.blade.php +++ b/resources/views/livewire/projects/project-map.blade.php @@ -100,13 +100,13 @@
-
+
- - - - + + + +
+ @error('newUserId')

{{ $message }}

@enderror
@@ -59,6 +76,7 @@ + @@ -89,9 +107,16 @@ @endphp {{ $label }} + @empty - + @endforelse
{{ __('Name') }} {{ __('Last name') }} {{ __('Status') }}
+ +
{{ __('No users with this role') }}
{{ __('No users with this role') }}
@@ -134,7 +159,7 @@ @endif
name, $rolePerms, true)) wire:click="togglePermission('{{ $perm->name }}')" /> diff --git a/resources/views/livewire/user-view.blade.php b/resources/views/livewire/user-view.blade.php index 9c4a680..cb4336f 100644 --- a/resources/views/livewire/user-view.blade.php +++ b/resources/views/livewire/user-view.blade.php @@ -130,7 +130,7 @@
-
+
@endif + {{-- Permisos directos del usuario --}} +
+
+

+ + Permisos +

+

+ Los permisos heredados de un rol aparecen marcados y bloqueados. Aquí puedes conceder permisos extra directamente a este usuario. +

+ +
+ @forelse($grouped as $section => $perms) +
+
+ +
+ + +
+
+
+ @foreach($perms as $perm) + @php + $viaRole = in_array($perm->name, $rolePerms, true); + $direct = in_array($perm->name, $directPerms, true); + @endphp + + @endforeach +
+
+ @empty +

{{ __('No permissions') }}

+ @endforelse +
+
+
+
@endif @@ -496,7 +558,7 @@ TAB: NOTAS ════════════════════════════════════════════════════════════════════ --}} @if($activeTab === 'notes') -
+