From 2cb10b08549daa2c876832cffa5a4c545f63e1f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Bra=C3=B1a?= Date: Sat, 9 May 2026 23:32:22 +0200 Subject: [PATCH] =?UTF-8?q?Gesti=C3=B3n=20de=20usuarios=20por=20proyecto:?= =?UTF-8?q?=20ProjectUsers=20Livewire,=20AdminUsers,=20panel=20admin=20con?= =?UTF-8?q?=20roles,=20protecci=C3=B3n=20de=20rutas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Livewire/AdminUsers.php | 52 ++++++++++++ app/Livewire/ProjectUsers.php | 81 +++++++++++++++++++ lang/en.json | 11 ++- lang/es.json | 11 ++- resources/views/admin/users.blade.php | 15 ++++ .../views/livewire/admin-users.blade.php | 53 ++++++++++++ .../livewire/layout/navigation.blade.php | 10 ++- .../views/livewire/project-users.blade.php | 70 ++++++++++++++++ resources/views/projects/edit.blade.php | 5 ++ routes/web.php | 7 ++ 10 files changed, 312 insertions(+), 3 deletions(-) create mode 100644 app/Livewire/AdminUsers.php create mode 100644 app/Livewire/ProjectUsers.php create mode 100644 resources/views/admin/users.blade.php create mode 100644 resources/views/livewire/admin-users.blade.php create mode 100644 resources/views/livewire/project-users.blade.php diff --git a/app/Livewire/AdminUsers.php b/app/Livewire/AdminUsers.php new file mode 100644 index 0000000..ef3926e --- /dev/null +++ b/app/Livewire/AdminUsers.php @@ -0,0 +1,52 @@ +hasRole('Admin')) { + abort(403); + } + $this->roles = Role::all(); + $this->loadUsers(); + } + + public function loadUsers() + { + $this->users = User::with('roles')->orderBy('name')->get(); + } + + public function updateRole($userId, $roleName) + { + $user = Auth::user(); + if (!$user->hasRole('Admin')) { + session()->flash('error', 'Solo administradores.'); + return; + } + + $targetUser = User::findOrFail($userId); + if ($targetUser->id === $user->id && $targetUser->hasRole('Admin')) { + session()->flash('error', 'No puedes cambiarte el rol a ti mismo.'); + return; + } + + $targetUser->syncRoles([$roleName]); + $this->loadUsers(); + $this->dispatch('notify', 'Rol actualizado.'); + } + + public function render() + { + return view('livewire.admin-users'); + } +} \ No newline at end of file diff --git a/app/Livewire/ProjectUsers.php b/app/Livewire/ProjectUsers.php new file mode 100644 index 0000000..ab0a0a5 --- /dev/null +++ b/app/Livewire/ProjectUsers.php @@ -0,0 +1,81 @@ +project = $project; + $this->loadUsers(); + } + + public function loadUsers() + { + $this->assignedUsers = $this->project->users()->withPivot('role_in_project')->get(); + $assignedIds = $this->assignedUsers->pluck('id')->toArray(); + $this->allUsers = User::whereNotIn('id', $assignedIds)->orderBy('name')->get(); + } + + public function assignUser() + { + $user = Auth::user(); + if (!$user->can('assign users') && !$user->hasRole('Admin')) { + session()->flash('error', 'No tienes permisos para asignar usuarios.'); + return; + } + + $this->validate([ + 'selectedUserId' => 'required|exists:users,id', + 'selectedRole' => 'required|in:supervisor,consultant,client,viewer', + ]); + + $this->project->users()->attach($this->selectedUserId, [ + 'role_in_project' => $this->selectedRole + ]); + + $this->reset(['selectedUserId', 'selectedRole']); + $this->loadUsers(); + $this->dispatch('notify', 'Usuario asignado al proyecto.'); + } + + public function removeUser($userId) + { + $user = Auth::user(); + if (!$user->can('assign users') && !$user->hasRole('Admin')) { + session()->flash('error', 'Sin permisos.'); + return; + } + + $this->project->users()->detach($userId); + $this->loadUsers(); + $this->dispatch('notify', 'Usuario eliminado del proyecto.'); + } + + public function changeRole($userId, $role) + { + if (!in_array($role, ['supervisor', 'consultant', 'client', 'viewer'])) return; + + $this->project->users()->updateExistingPivot($userId, [ + 'role_in_project' => $role + ]); + $this->loadUsers(); + $this->dispatch('notify', 'Rol actualizado.'); + } + + public function render() + { + return view('livewire.project-users'); + } +} \ No newline at end of file diff --git a/lang/en.json b/lang/en.json index 9e85cf4..ef916dc 100644 --- a/lang/en.json +++ b/lang/en.json @@ -136,5 +136,14 @@ "Document": "Document", "Other": "Other", "Color": "Color", - "Upload": "Upload" + "Upload": "Upload", + "Assign": "Assign", + "Role": "Role", + "Supervisor": "Supervisor", + "Consultant": "Consultant", + "Client": "Client", + "Viewer": "Viewer", + "Remove": "Remove", + "No users assigned yet": "No users assigned yet", + "Select": "Select" } diff --git a/lang/es.json b/lang/es.json index 684e924..0bf93fe 100644 --- a/lang/es.json +++ b/lang/es.json @@ -137,5 +137,14 @@ "Document": "Documento", "Other": "Otro", "Color": "Color", - "Upload": "Subir" + "Upload": "Subir", + "Assign": "Asignar", + "Role": "Rol", + "Supervisor": "Supervisor", + "Consultant": "Consultor", + "Client": "Cliente", + "Viewer": "Espectador", + "Remove": "Eliminar", + "No users assigned yet": "Sin usuarios asignados", + "Select": "Seleccionar" } diff --git a/resources/views/admin/users.blade.php b/resources/views/admin/users.blade.php new file mode 100644 index 0000000..5d92e06 --- /dev/null +++ b/resources/views/admin/users.blade.php @@ -0,0 +1,15 @@ + + +

+ {{ __('Administrator') }} — {{ __('Users') }} +

+
+ +
+
+
+ @livewire('admin-users') +
+
+
+
\ No newline at end of file diff --git a/resources/views/livewire/admin-users.blade.php b/resources/views/livewire/admin-users.blade.php new file mode 100644 index 0000000..58c9ea5 --- /dev/null +++ b/resources/views/livewire/admin-users.blade.php @@ -0,0 +1,53 @@ +
+ @if(session()->has('message')) +
{{ session('message') }}
+ @endif + @if(session()->has('error')) +
{{ session('error') }}
+ @endif + +
+ + + + + + + + + + + + @foreach($users as $user) + + + + + + + + @endforeach + +
{{ __('Name') }}{{ __('Email') }}{{ __('Role') }}{{ __('Language') }}{{ __('Actions') }}
{{ $user->name }}{{ $user->email }} +
+ @foreach($user->roles as $role) + + {{ __($role->name) }} + + @endforeach +
+
{{ strtoupper($user->locale ?? 'en') }} + @can('assign users') + + @endcan +
+
+
\ No newline at end of file diff --git a/resources/views/livewire/layout/navigation.blade.php b/resources/views/livewire/layout/navigation.blade.php index 1e64067..2d02048 100644 --- a/resources/views/livewire/layout/navigation.blade.php +++ b/resources/views/livewire/layout/navigation.blade.php @@ -39,9 +39,17 @@ new class extends Component + + @can('manage all') + + @endcan diff --git a/resources/views/livewire/project-users.blade.php b/resources/views/livewire/project-users.blade.php new file mode 100644 index 0000000..c1fb42f --- /dev/null +++ b/resources/views/livewire/project-users.blade.php @@ -0,0 +1,70 @@ +
+ @if(session()->has('message')) +
{{ session('message') }}
+ @endif + @if(session()->has('error')) +
{{ session('error') }}
+ @endif + + {{-- Asignar usuario --}} + @can('assign users') +
+
+ + +
+
+ + +
+ +
+ @endcan + + {{-- Lista de usuarios asignados --}} + @if($assignedUsers->isNotEmpty()) +
+ @foreach($assignedUsers as $user) +
+
+ + {{ strtoupper(substr($user->name, 0, 1)) }} + +
+ {{ $user->name }} + {{ $user->email }} +
+
+
+ @can('assign users') + + + @else + {{ ucfirst($user->pivot->role_in_project) }} + @endcan +
+
+ @endforeach +
+ @else +

{{ __('No users assigned yet') }}

+ @endif +
\ No newline at end of file diff --git a/resources/views/projects/edit.blade.php b/resources/views/projects/edit.blade.php index eec49c6..382ca6e 100644 --- a/resources/views/projects/edit.blade.php +++ b/resources/views/projects/edit.blade.php @@ -48,5 +48,10 @@

{{ __('Phases') }}

+ +
+ +

{{ __('Users') }}

+ diff --git a/routes/web.php b/routes/web.php index 239d902..6905d41 100644 --- a/routes/web.php +++ b/routes/web.php @@ -88,6 +88,13 @@ Route::middleware(['auth'])->group(function () { // Rutas para el LayerManager: Route::get('/projects/{project}/phases/{phase}/layers/manage', \App\Livewire\LayerManager::class)->name('layers.manage'); + // Admin: gestión de usuarios y roles + Route::middleware(['can:manage all'])->prefix('admin')->name('admin.')->group(function () { + Route::get('/users', function () { + return view('admin.users'); + })->name('users'); + }); + // Gestor de medios Route::get('/projects/{project}/media', function (\App\Models\Project $project) { return view('projects.media', compact('project'));