blob: ed3725b04b558b539b162f85ed6482f89347cb34 [file] [log] [blame]
<script lang="ts">
import type { CloseModalFn } from '$lib/types/utilTypes';
import Listbox from '../Listbox.svelte';
import Input from '../Input.svelte';
import { z } from 'zod';
import ModalBase from './ModalBase.svelte';
import { setError, superForm, defaults } from 'sveltekit-superforms/client';
import { zod } from 'sveltekit-superforms/adapters';
import PasswordInput from '../PasswordInput.svelte';
import Button from '../Button.svelte';
import PermissionsManager from '../PermissionsManager.svelte';
import type { Stream } from '$lib/domain/Stream';
import { fetchRouteApi } from '$lib/api/fetchRouteApi';
import { showToast } from '../AppToasts.svelte';
import { customInvalidateAll } from '../PeriodicInvalidator.svelte';
import { dataHas } from '$lib/utils/dataHas';
interface Props {
closeModal: CloseModalFn;
streams: Stream[];
}
let { closeModal, streams }: Props = $props();
const schema = z.object({
username: z.string().min(1, 'Username is required').trim(),
password: z.string().min(4, { message: 'Password must contain at least 4 characters' }).trim(),
status: z.enum(['active', 'inactive']).default('active'),
permissions: z.any().nullable().default(null)
});
const { form, errors, enhance, constraints } = superForm(defaults(zod(schema)), {
SPA: true,
validators: zod(schema),
taintedMessage: false,
async onUpdate({ form }) {
if (!form.valid) return;
const { data, ok } = await fetchRouteApi({
method: 'POST',
path: '/users',
body: {
username: form.data.username,
password: form.data.password,
status: form.data.status,
permissions: form.data.permissions
}
});
if (dataHas(data, 'field', 'reason')) {
return setError(form, data.field, data.reason);
}
if (ok) {
closeModal(async () => {
await customInvalidateAll();
await showToast({
type: 'success',
description: `User ${form.data.username} has been created.`,
duration: 3500
});
});
}
}
});
</script>
<ModalBase {closeModal} title="Add User">
<form method="POST" class="flex flex-col" use:enhance>
<div class="grid grid-cols-3 gap-4 min-w-[800px]">
<Input
label="Username"
name="username"
bind:value={$form.username}
{...$constraints.username}
errorMessage={$errors.username?.join(',')}
/>
<PasswordInput
label="Password"
name="password"
errorMessage={$errors.password?.join(',')}
bind:value={$form.password}
{...$constraints.password}
/>
<Listbox
label="Status"
options={[
{ name: 'Active', value: 'active' },
{ name: 'Inactive', value: 'inactive' }
]}
selectedValue={$form.status}
on:selectedValue={(e) => $form.status = e.detail as 'active' | 'inactive'}
/>
</div>
<PermissionsManager
{streams}
bind:value={$form.permissions}
/>
<div class="flex justify-end gap-3 mt-16 w-[350px] ml-auto">
<Button variant="text" type="button" class="w-2/5" onclick={() => closeModal()}>
Cancel
</Button>
<Button type="submit" variant="contained" class="w-2/5">Add</Button>
</div>
</form>
</ModalBase>