Files
warren/frontend/components/admin/AddUserWarrenDialog.vue
409 a1c9832515 migrate to sqlite
NOTE: extension loading crashes docker (for some reason)
2025-09-07 15:09:14 +02:00

318 lines
12 KiB
Vue

<script setup lang="ts">
import {
Combobox,
ComboboxAnchor,
ComboboxEmpty,
ComboboxGroup,
ComboboxInput,
ComboboxItem,
ComboboxItemIndicator,
ComboboxList,
} from '@/components/ui/combobox';
import {
AlertDialog,
AlertDialogTrigger,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogDescription,
AlertDialogContent,
AlertDialogFooter,
AlertDialogCancel,
} from '@/components/ui/alert-dialog';
import { toTypedSchema } from '@vee-validate/yup';
import { useForm } from 'vee-validate';
import { userWarrenSchema } from '~/lib/schemas/admin';
import type { AuthUserWithWarrens } from '~/shared/types/admin';
import { createUserWarren } from '~/lib/api/admin/createUserWarren';
const adminStore = useAdminStore();
const { user } = defineProps<{
user: AuthUserWithWarrens;
}>();
const availableWarrens = computed<typeof adminStore.resources.warrens>(() =>
Object.entries(adminStore.resources.warrens)
.filter(
([_, warren]) =>
user.warrens.findIndex((uw) => uw.warrenId === warren.id) === -1
)
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
);
const creating = ref(false);
const open = ref(false);
const form = useForm({
validationSchema: toTypedSchema(userWarrenSchema),
initialValues: {
userId: user.id,
canListFiles: false,
canReadFiles: false,
canModifyFiles: false,
canDeleteFiles: false,
canListShares: false,
canCreateShares: false,
canModifyShares: false,
canDeleteShares: false,
},
});
const onSubmit = form.handleSubmit(async (values) => {
if (creating.value) {
return;
}
creating.value = true;
const result = await createUserWarren(values);
if (result.success) {
form.resetForm();
open.value = false;
}
creating.value = false;
});
</script>
<template>
<AlertDialog v-model:open="open">
<AlertDialogTrigger as-child>
<slot />
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Add user warren</AlertDialogTitle>
<AlertDialogDescription
>Assign {{ user.name }} current user to an existing
warren</AlertDialogDescription
>
</AlertDialogHeader>
<div class="flex flex-col">
<Combobox>
<ComboboxAnchor as-child>
<ComboboxTrigger as-child>
<Button variant="outline" class="justify-between">
{{
(form.values.warrenId
? adminStore.resources.warrens[
form.values.warrenId
].name
: undefined) ?? 'Select warren'
}}
<Icon
name="lucide:chevrons-up-down"
class="ml-2 h-4 w-4 shrink-0 opacity-50"
/>
</Button>
</ComboboxTrigger>
</ComboboxAnchor>
<ComboboxList>
<div class="relative w-full max-w-sm items-center">
<ComboboxInput
class="h-10 rounded-none border-0 focus-visible:ring-0"
placeholder="Select warren..."
:display-value="(warren) => warren?.name"
/>
<span
class="absolute inset-y-0 start-0 flex items-center justify-center px-3"
>
<Icon
name="lucide:search"
class="text-muted-foreground size-4"
/>
</span>
</div>
<ComboboxEmpty>No warrens found</ComboboxEmpty>
<ComboboxGroup>
<ComboboxItem
v-for="warren in availableWarrens"
:key="warren.id"
:value="warren"
@select="
() => {
form.setFieldValue(
'warrenId',
warren.id
);
}
"
>
{{ warren.name }}
<ComboboxItemIndicator>
<Icon
name="lucide:check"
class="ml-auto h-4 w-4"
/>
</ComboboxItemIndicator>
</ComboboxItem>
</ComboboxGroup>
</ComboboxList>
</Combobox>
<span
v-if="form.errors.value.warrenId"
class="text-destructive-foreground text-sm"
>{{ form.errors.value.warrenId }}</span
>
</div>
<form
id="add-user-warren-form"
class="flex w-full flex-col gap-4"
@submit.prevent="onSubmit"
>
<FormField type="hidden" name="userId" :value="user.id" />
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<FormField
v-slot="{ value, handleChange }"
name="canListFiles"
>
<FormItem class="flex flex-row justify-between">
<FormLabel class="grow">List files</FormLabel>
<FormControl>
<Switch
:model-value="value"
@update:model-value="handleChange"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<FormField
v-slot="{ value, handleChange }"
name="canReadFiles"
>
<FormItem class="flex flex-row justify-between">
<FormLabel class="grow">Read files</FormLabel>
<FormControl>
<Switch
:model-value="value"
@update:model-value="handleChange"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<FormField
v-slot="{ value, handleChange }"
name="canModifyFiles"
>
<FormItem class="flex flex-row justify-between">
<FormLabel class="grow">Modify files</FormLabel>
<FormControl>
<Switch
:model-value="value"
@update:model-value="handleChange"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<FormField
v-slot="{ value, handleChange }"
name="canDeleteFiles"
>
<FormItem class="flex flex-row justify-between">
<FormLabel class="grow">Delete files</FormLabel>
<FormControl>
<Switch
:model-value="value"
@update:model-value="handleChange"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<FormField
v-slot="{ value, handleChange }"
name="canListShares"
>
<FormItem class="flex flex-row justify-between">
<FormLabel class="grow">List shares</FormLabel>
<FormControl>
<Switch
:model-value="value"
@update:model-value="handleChange"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<FormField
v-slot="{ value, handleChange }"
name="canCreateShares"
>
<FormItem class="flex flex-row justify-between">
<FormLabel class="grow">Create shares</FormLabel>
<FormControl>
<Switch
:model-value="value"
@update:model-value="handleChange"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<FormField
v-slot="{ value, handleChange }"
name="canModifyShares"
>
<FormItem class="flex flex-row justify-between">
<FormLabel class="grow">Modify shares</FormLabel>
<FormControl>
<Switch
:model-value="value"
@update:model-value="handleChange"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
<FormField
v-slot="{ value, handleChange }"
name="canDeleteShares"
>
<FormItem class="flex flex-row justify-between">
<FormLabel class="grow">Delete shares</FormLabel>
<FormControl>
<Switch
:model-value="value"
@update:model-value="handleChange"
/>
</FormControl>
<FormMessage />
</FormItem>
</FormField>
</div>
</form>
<AlertDialogFooter class="space-y-0">
<AlertDialogCancel>Cancel</AlertDialogCancel>
<Button
type="submit"
form="add-user-warren-form"
:disabled="creating"
>Add</Button
>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</template>