From a810f541539e738a7e34b17695ab0b92ff5bfd46 Mon Sep 17 00:00:00 2001 From: Koda Date: Fri, 22 May 2026 13:07:20 +0000 Subject: [PATCH] Add admin user edit flow --- .../Admin/UserManagementController.php | 28 +++ .../Admin/UpdateManagedUserRequest.php | 35 ++++ resources/js/components/PasswordInput.vue | 3 + resources/js/pages/admin/users/Edit.vue | 161 ++++++++++++++++++ resources/js/pages/admin/users/Index.vue | 99 ++++++----- resources/js/types/page-props.ts | 4 + routes/web/admin.php | 2 + .../Admin/UserManagementControllerTest.php | 73 ++++++++ 8 files changed, 362 insertions(+), 43 deletions(-) create mode 100644 app/Http/Requests/Admin/UpdateManagedUserRequest.php create mode 100644 resources/js/pages/admin/users/Edit.vue diff --git a/app/Http/Controllers/Admin/UserManagementController.php b/app/Http/Controllers/Admin/UserManagementController.php index 6e17615..2f0d53a 100644 --- a/app/Http/Controllers/Admin/UserManagementController.php +++ b/app/Http/Controllers/Admin/UserManagementController.php @@ -6,6 +6,7 @@ use App\Actions\Permissions\PermissionService; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\StoreManagedUserRequest; use App\Http\Requests\Admin\SyncUserGlobalPermissionsRequest; +use App\Http\Requests\Admin\UpdateManagedUserRequest; use App\Http\Resources\PermissionOptionResource; use App\Http\Resources\UserResource; use App\Models\User; @@ -56,6 +57,33 @@ class UserManagementController extends Controller ->with('status', 'User was created.'); } + public function edit(User $user): Response + { + return Inertia::render('admin/users/Edit', [ + 'user' => UserResource::make($user)->resolve(), + ]); + } + + public function save(UpdateManagedUserRequest $request, User $user): RedirectResponse + { + $validated = $request->validated(); + + $user->fill([ + 'name' => Arr::get($validated, 'name'), + 'email' => Arr::get($validated, 'email'), + 'must_change_password_on_next_login' => (bool) Arr::get($validated, 'must_change_password_on_next_login', true), + ]); + + if (filled(Arr::get($validated, 'password'))) { + $user->password = Arr::get($validated, 'password'); + } + + $user->save(); + + return to_route('admin.users.index') + ->with('status', "{$user->name} was updated."); + } + public function update(SyncUserGlobalPermissionsRequest $request, User $user): RedirectResponse { $validated = $request->validated(); diff --git a/app/Http/Requests/Admin/UpdateManagedUserRequest.php b/app/Http/Requests/Admin/UpdateManagedUserRequest.php new file mode 100644 index 0000000..e3470f3 --- /dev/null +++ b/app/Http/Requests/Admin/UpdateManagedUserRequest.php @@ -0,0 +1,35 @@ +user()?->can('access-admin') ?? false; + } + + /** + * Get the validation rules that apply to the request. + * + * @return array|string> + */ + public function rules(): array + { + return [ + ...$this->profileRules($this->route('user')?->id), + 'must_change_password_on_next_login' => ['required', 'boolean'], + 'password' => ['nullable', 'string', Password::default(), 'confirmed'], + ]; + } +} diff --git a/resources/js/components/PasswordInput.vue b/resources/js/components/PasswordInput.vue index f2694ed..74a90bd 100644 --- a/resources/js/components/PasswordInput.vue +++ b/resources/js/components/PasswordInput.vue @@ -11,6 +11,8 @@ const props = defineProps<{ class?: HTMLAttributes['class']; }>(); +const model = defineModel(); + const showPassword = ref(false); const inputRef = useTemplateRef('inputRef'); @@ -24,6 +26,7 @@ defineExpose({
+import { Head, Link, useForm } from '@inertiajs/vue3'; +import Heading from '@/components/Heading.vue'; +import InputError from '@/components/InputError.vue'; +import PasswordInput from '@/components/PasswordInput.vue'; +import { Button } from '@/components/ui/button'; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import AppLayout from '@/layouts/AppLayout.vue'; +import { edit, index, save } from '@/routes/admin/users'; +import type { AdminUserEditPageProps, BreadcrumbItem } from '@/types'; + +const props = defineProps(); + +const breadcrumbs: BreadcrumbItem[] = [ + { + title: 'User management', + href: index(), + }, + { + title: 'Edit user', + href: edit(props.user.id), + }, +]; + +const form = useForm({ + name: props.user.name, + email: props.user.email, + password: '', + password_confirmation: '', + must_change_password_on_next_login: + props.user.must_change_password_on_next_login, +}); + +const submit = (): void => { + form.patch(save(props.user.id), { + preserveScroll: true, + }); +}; + + + diff --git a/resources/js/pages/admin/users/Index.vue b/resources/js/pages/admin/users/Index.vue index cbf1efc..9c1ce93 100644 --- a/resources/js/pages/admin/users/Index.vue +++ b/resources/js/pages/admin/users/Index.vue @@ -1,6 +1,6 @@