can(self::CORE_PERMISSION), 403); } private function flushCache(): void { app(PermissionRegistrar::class)->forgetCachedPermissions(); } public function updatedSelectAll($value): void { $this->selected = $value ? Role::pluck('id')->map(fn ($id) => (string) $id)->toArray() : []; } // ── Create / Edit ──────────────────────────────────────────────────────── public function openCreate(): void { $this->resetForm(); $this->showForm = true; } public function openEdit(int $id): void { $role = Role::with('permissions')->findOrFail($id); $this->editingRole = $role->id; $this->name = $role->name; $this->description = $role->description ?? ''; $this->rolePermissions = $role->permissions->pluck('name')->toArray(); $this->resetErrorBag(); $this->viewingRole = null; // close the view modal if open $this->showForm = true; } public function save(): void { $this->validate([ 'name' => 'required|string|max:50|unique:roles,name' . ($this->editingRole ? ',' . $this->editingRole : ''), 'description' => 'nullable|string|max:255', ], [], ['name' => 'nombre', 'description' => 'descripción']); if ($this->editingRole) { $role = Role::findOrFail($this->editingRole); $isProtected = in_array($role->name, self::PROTECTED_ROLES, true); // Protected roles can't be renamed if (! $isProtected) { $role->name = $this->name; } $role->description = $this->description ?: null; $role->save(); $perms = $this->rolePermissions; // Admin always keeps the core permission if ($role->name === 'Admin' && ! in_array(self::CORE_PERMISSION, $perms, true)) { $perms[] = self::CORE_PERMISSION; } $role->syncPermissions($perms); } else { $role = Role::create([ 'name' => $this->name, 'description' => $this->description ?: null, ]); if (! empty($this->rolePermissions)) { $role->syncPermissions($this->rolePermissions); } } $this->flushCache(); $this->closeForm(); $this->dispatch('notify', 'Rol guardado correctamente'); } public function closeForm(): void { $this->showForm = false; $this->resetForm(); } private function resetForm(): void { $this->reset(['editingRole', 'name', 'description', 'rolePermissions']); $this->resetErrorBag(); } // ── View ───────────────────────────────────────────────────────────────── public function openView(int $id): void { $this->viewingRole = $id; } public function closeView(): void { $this->viewingRole = null; } // ── Delete (single / bulk) ───────────────────────────────────────────────── public function delete(int $id): void { $role = Role::findOrFail($id); if (in_array($role->name, self::PROTECTED_ROLES, true)) { $this->dispatch('notify', "El rol '{$role->name}' está protegido y no se puede borrar."); return; } $role->delete(); $this->selected = array_values(array_diff($this->selected, [(string) $id, $id])); $this->flushCache(); $this->dispatch('notify', 'Rol eliminado'); } public function bulkDelete(): void { $roles = Role::whereIn('id', $this->selected)->get(); $deleted = 0; $skipped = 0; foreach ($roles as $role) { if (in_array($role->name, self::PROTECTED_ROLES, true)) { $skipped++; continue; } $role->delete(); $deleted++; } $this->selected = []; $this->flushCache(); $msg = "{$deleted} rol(es) eliminados"; if ($skipped) $msg .= " ({$skipped} protegido(s) omitido(s))"; $this->dispatch('notify', $msg); } public function render() { return view('livewire.role-manager', [ 'roles' => Role::with('permissions')->withCount('users')->orderBy('name')->get(), 'permissions' => Permission::orderBy('name')->get(), 'viewing' => $this->viewingRole ? Role::with('permissions')->withCount('users')->find($this->viewingRole) : null, ]); } }