Files
warren/frontend/components/admin/EditUserDialog.vue
2025-07-21 19:27:41 +02:00

238 lines
8.4 KiB
Vue

<script setup lang="ts">
import { Accordion } from '@/components/ui/accordion';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from '@/components/ui/alert-dialog';
import { useForm } from 'vee-validate';
import { toTypedSchema } from '@vee-validate/yup';
import { editUserSchema } from '~/lib/schemas/admin';
import { editUser } from '~/lib/api/admin/editUser';
import type { AuthUserWithWarrens } from '~/shared/types/admin';
const adminStore = useAdminStore();
const isValid = computed(() => Object.keys(form.errors.value).length < 1);
// We'll only update this value if there is a user to prevent layout shifts on close
const user = ref<AuthUserWithWarrens>();
const editing = ref(false);
const isChanged = computed(() => {
if (user.value == null) {
return false;
}
try {
const values = editUserSchema.validateSync(form.controlledValues.value);
return (
values.name !== user.value.name ||
values.email !== user.value.email ||
values.password != null ||
values.admin !== user.value.admin
);
} catch {
return true;
}
});
function close() {
adminStore.closeEditUserDialog();
}
const form = useForm({
validationSchema: toTypedSchema(editUserSchema),
});
adminStore.$subscribe((_mutation, state) => {
if (state.editUserDialog != null && !editing.value) {
user.value = state.resources.users.find(
(u) => u.id === state.editUserDialog?.user.id
);
if (user.value != null) {
form.setValues(user.value);
}
}
});
const onSubmit = form.handleSubmit(async (values) => {
if (user.value == null || !isChanged.value || editing.value) {
return;
}
editing.value = true;
const result = await editUser({
id: user.value.id,
name: values.name,
email: values.email,
password: values.password ?? null,
admin: values.admin,
});
if (result.success) {
const newUser: AuthUserWithWarrens = {
id: result.user.id,
name: result.user.name,
email: result.user.email,
admin: result.user.admin,
warrens: user.value.warrens,
};
adminStore.setEditUserDialog(newUser);
}
editing.value = false;
});
</script>
<template>
<AlertDialog :open="adminStore.editUserDialog != null">
<AlertDialogTrigger><slot /></AlertDialogTrigger>
<AlertDialogContent
v-if="user != null"
class="flex max-h-[95vh] flex-col"
@escape-key-down="close"
>
<AlertDialogHeader class="shrink">
<AlertDialogTitle>Edit user</AlertDialogTitle>
<AlertDialogDescription>
Modify the user's fields, manage permissions or assign
warrens
</AlertDialogDescription>
</AlertDialogHeader>
<ScrollArea
class="flex h-full grow flex-col justify-start overflow-hidden"
>
<form
id="edit-user-form"
class="flex flex-col gap-2"
@submit.prevent="onSubmit"
>
<FormField v-slot="{ componentField }" name="name">
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input
v-bind="componentField"
id="username"
type="text"
placeholder="confused-cat"
autocomplete="off"
data-1p-ignore
data-protonpass-ignore
data-bwignore
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<FormField v-slot="{ componentField }" name="email">
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input
v-bind="componentField"
id="email"
type="email"
placeholder="confusedcat@example.com"
autocomplete="off"
data-1p-ignore
data-protonpass-ignore
data-bwignore
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<FormField v-slot="{ componentField }" name="password">
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
v-bind="componentField"
id="password"
type="password"
autocomplete="off"
data-1p-ignore
data-protonpass-ignore
data-bwignore
/>
</FormControl>
<FormMessage />
<FormDescription
>Leave empty to keep the current
password</FormDescription
>
</FormItem>
</FormField>
<FormField v-slot="{ value, handleChange }" name="admin">
<FormItem>
<FormLabel>Admin</FormLabel>
<FormControl>
<Switch
id="admin"
:model-value="value"
@update:model-value="handleChange"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<AlertDialogAction
type="submit"
form="edit-user-form"
:disabled="!isChanged || !isValid"
>Save</AlertDialogAction
>
</form>
<Separator orientation="horizontal" class="my-4" />
<div class="flex w-full flex-col gap-2">
<AdminAddUserWarrenDialog :user>
<Button variant="outline" size="icon">
<Icon name="lucide:folder-plus" />
</Button>
</AdminAddUserWarrenDialog>
<Accordion
v-if="user.warrens.length > 0"
class="flex w-full flex-col gap-2"
collapsible
type="single"
>
<AdminUserWarrenListing
v-for="warren in user.warrens"
:key="warren.warrenId"
:user-warren="warren"
/>
</Accordion>
<div v-else class="flex flex-col gap-4">
<span class="text-muted-foreground"
>This user is not assigned to any warrens</span
>
</div>
</div>
<Separator orientation="horizontal" class="mt-4" />
</ScrollArea>
<AlertDialogFooter class="gap-y-0">
<AlertDialogCancel @click="close">Close</AlertDialogCancel>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</template>