refactor: centralize form and record helpers

This commit is contained in:
2026-06-24 13:40:01 +01:00
parent 8b346acadc
commit 1ea8ce2135
25 changed files with 1390 additions and 1557 deletions
+47 -64
View File
@@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import GalleryVerticalEndIcon from '@lucide/svelte/icons/gallery-vertical-end'; import GalleryVerticalEndIcon from '@lucide/svelte/icons/gallery-vertical-end';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js'; import * as Field from '$lib/components/ui/field/index.js';
import { superForm, type SuperValidated } from 'sveltekit-superforms'; import { superForm, type SuperValidated } from 'sveltekit-superforms';
import { zod4Client } from 'sveltekit-superforms/adapters'; import { zod4Client } from 'sveltekit-superforms/adapters';
@@ -9,6 +9,7 @@
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
import { cn, type WithElementRef } from '$lib/utils.js'; import { cn, type WithElementRef } from '$lib/utils.js';
import { firstFormError } from '$lib/form-feedback';
import { firstAdminSchema, type FirstAdminSchema } from '$lib/schemas/auth'; import { firstAdminSchema, type FirstAdminSchema } from '$lib/schemas/auth';
let { let {
@@ -27,7 +28,7 @@
resetForm: false, resetForm: false,
onUpdated: ({ form }) => { onUpdated: ({ form }) => {
if (!form.valid) { if (!form.valid) {
toast.error(form.message ?? firstError(form.errors), { toast.error(form.message ?? firstFormError(form.errors), {
id: 'first-admin-error', id: 'first-admin-error',
position: 'top-center' position: 'top-center'
}); });
@@ -42,16 +43,6 @@
}); });
const { form: formData, enhance } = firstAdminForm; const { form: formData, enhance } = firstAdminForm;
function firstError(errors: Record<string, unknown>) {
for (const value of Object.values(errors)) {
if (Array.isArray(value) && typeof value[0] === 'string') {
return value[0];
}
}
return 'Check the highlighted fields.';
}
</script> </script>
<div class={cn('flex flex-col gap-6', className)} bind:this={ref} {...restProps}> <div class={cn('flex flex-col gap-6', className)} bind:this={ref} {...restProps}>
@@ -71,58 +62,50 @@
Set up the initial Better Auth admin account for this workspace. Set up the initial Better Auth admin account for this workspace.
</Field.Description> </Field.Description>
</div> </div>
<FormField form={firstAdminForm} name="name"> <Form.Field form={firstAdminForm} name="name">
<Field.Field> <Form.Control id="name-{id}">
<Control id="name-{id}"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input
<Input {...props}
{...props} autocomplete="name"
autocomplete="name" placeholder="Administrator"
placeholder="Administrator" bind:value={$formData.name}
bind:value={$formData.name} />
/> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={firstAdminForm} name="email">
</FormField> <Form.Control id="email-{id}">
<FormField form={firstAdminForm} name="email"> {#snippet children({ props })}
<Field.Field> <Form.Label>Email</Form.Label>
<Control id="email-{id}"> <Input
{#snippet children({ props })} {...props}
<Field.Label>Email</Field.Label> type="email"
<Input autocomplete="email"
{...props} placeholder="admin@example.com"
type="email" bind:value={$formData.email}
autocomplete="email" />
placeholder="admin@example.com" {/snippet}
bind:value={$formData.email} </Form.Control>
/> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={firstAdminForm} name="password">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="password-{id}">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Password</Form.Label>
<FormField form={firstAdminForm} name="password"> <Input
<Field.Field> {...props}
<Control id="password-{id}"> type="password"
{#snippet children({ props })} autocomplete="new-password"
<Field.Label>Password</Field.Label> bind:value={$formData.password}
<Input />
{...props} {/snippet}
type="password" </Form.Control>
autocomplete="new-password" <Form.FieldErrors />
bind:value={$formData.password} </Form.Field>
/> <Button type="submit" class="w-full">Create admin</Button>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Field.Field>
<Button type="submit" class="w-full">Create admin</Button>
</Field.Field>
</Field.Group> </Field.Group>
</form> </form>
<Field.Description class="px-6 text-center"> <Field.Description class="px-6 text-center">
+42 -55
View File
@@ -1,14 +1,15 @@
<script lang="ts"> <script lang="ts">
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import GalleryVerticalEndIcon from "@lucide/svelte/icons/gallery-vertical-end"; import GalleryVerticalEndIcon from '@lucide/svelte/icons/gallery-vertical-end';
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js'; import * as Field from '$lib/components/ui/field/index.js';
import { superForm, type SuperValidated } from 'sveltekit-superforms'; import { superForm, type SuperValidated } from 'sveltekit-superforms';
import { zod4Client } from 'sveltekit-superforms/adapters'; import { zod4Client } from 'sveltekit-superforms/adapters';
import type { HTMLAttributes } from "svelte/elements"; import type { HTMLAttributes } from 'svelte/elements';
import { Input } from "$lib/components/ui/input/index.js"; import { Input } from '$lib/components/ui/input/index.js';
import { Button } from "$lib/components/ui/button/index.js"; import { Button } from '$lib/components/ui/button/index.js';
import { cn, type WithElementRef } from "$lib/utils.js"; import { cn, type WithElementRef } from '$lib/utils.js';
import { firstFormError } from '$lib/form-feedback';
import { loginSchema, type LoginSchema } from '$lib/schemas/auth'; import { loginSchema, type LoginSchema } from '$lib/schemas/auth';
let { let {
@@ -27,7 +28,7 @@
resetForm: false, resetForm: false,
onUpdated: ({ form }) => { onUpdated: ({ form }) => {
if (!form.valid) { if (!form.valid) {
toast.error(form.message ?? firstError(form.errors), { toast.error(form.message ?? firstFormError(form.errors), {
id: 'login-error', id: 'login-error',
position: 'top-center' position: 'top-center'
}); });
@@ -42,24 +43,16 @@
}); });
const { form: formData, enhance } = loginForm; const { form: formData, enhance } = loginForm;
function firstError(errors: Record<string, unknown>) {
for (const value of Object.values(errors)) {
if (Array.isArray(value) && typeof value[0] === 'string') {
return value[0];
}
}
return 'Check the highlighted fields.';
}
</script> </script>
<div class={cn("flex flex-col gap-6", className)} bind:this={ref} {...restProps}> <div class={cn('flex flex-col gap-6', className)} bind:this={ref} {...restProps}>
<form action="?/login" method="POST" use:enhance> <form action="?/login" method="POST" use:enhance>
<Field.Group> <Field.Group>
<div class="flex flex-col items-center gap-2 text-center"> <div class="flex flex-col items-center gap-2 text-center">
<div class="flex flex-col items-center gap-2 font-medium"> <div class="flex flex-col items-center gap-2 font-medium">
<div class="bg-primary text-primary-foreground flex size-9 items-center justify-center rounded-md"> <div
class="flex size-9 items-center justify-center rounded-md bg-primary text-primary-foreground"
>
<GalleryVerticalEndIcon class="size-6" /> <GalleryVerticalEndIcon class="size-6" />
</div> </div>
<span class="sr-only">Clearity</span> <span class="sr-only">Clearity</span>
@@ -69,45 +62,39 @@
Use the administrator account created for this workspace. Use the administrator account created for this workspace.
</Field.Description> </Field.Description>
</div> </div>
<FormField form={loginForm} name="email"> <Form.Field form={loginForm} name="email">
<Field.Field> <Form.Control id="email-{id}">
<Control id="email-{id}"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Email</Form.Label>
<Field.Label>Email</Field.Label> <Input
<Input {...props}
{...props} type="email"
type="email" autocomplete="email"
autocomplete="email" placeholder="admin@example.com"
placeholder="admin@example.com" bind:value={$formData.email}
bind:value={$formData.email} />
/> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={loginForm} name="password">
</FormField> <Form.Control id="password-{id}">
<FormField form={loginForm} name="password"> {#snippet children({ props })}
<Field.Field> <Form.Label>Password</Form.Label>
<Control id="password-{id}"> <Input
{#snippet children({ props })} {...props}
<Field.Label>Password</Field.Label> type="password"
<Input autocomplete="current-password"
{...props} bind:value={$formData.password}
type="password" />
autocomplete="current-password" {/snippet}
bind:value={$formData.password} </Form.Control>
/> <Form.FieldErrors />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
<Field.Description> <Field.Description>
New users are created manually by an administrator; public registration is disabled. New users are created manually by an administrator; public registration is disabled.
</Field.Description> </Field.Description>
</FormField> </Form.Field>
<Field.Field> <Button type="submit" class="w-full">Sign in</Button>
<Button type="submit" class="w-full">Sign in</Button>
</Field.Field>
</Field.Group> </Field.Group>
</form> </form>
<Field.Description class="px-6 text-center"> <Field.Description class="px-6 text-center">
+23 -26
View File
@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import * as InputGroup from '$lib/components/ui/input-group/index.js'; import * as InputGroup from '$lib/components/ui/input-group/index.js';
let { let {
@@ -19,27 +18,25 @@
} = $props(); } = $props();
</script> </script>
<FormField {form} {name}> <Form.Field {form} {name}>
<Field.Field> <Form.Control {id}>
<Control {id}> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>{label}</Form.Label>
<Field.Label>{label}</Field.Label> <InputGroup.Root>
<InputGroup.Root> <InputGroup.Addon>
<InputGroup.Addon> <InputGroup.Text>£</InputGroup.Text>
<InputGroup.Text>£</InputGroup.Text> </InputGroup.Addon>
</InputGroup.Addon> <InputGroup.Input
<InputGroup.Input {...props}
{...props} type="number"
type="number" min="0"
min="0" step="0.01"
step="0.01" inputmode="decimal"
inputmode="decimal" placeholder="0.00"
placeholder="0.00" bind:value={$data[name]}
bind:value={$data[name]} />
/> </InputGroup.Root>
</InputGroup.Root> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field>
</FormField>
+42
View File
@@ -0,0 +1,42 @@
import { toast } from 'svelte-sonner';
type FormFeedback = {
valid: boolean;
message?: string;
errors: Record<string, unknown>;
};
export function handleFormToast(form: FormFeedback, id: string, onSuccess: () => void) {
if (form.valid) {
if (form.message) toast.success(form.message, { id });
onSuccess();
return;
}
toast.error(form.message ?? firstFormError(form.errors), { id });
}
export function firstFormError(errors: Record<string, unknown>) {
return findFirstError(errors) ?? 'Check the highlighted fields.';
}
function findFirstError(value: unknown): string | undefined {
if (typeof value === 'string') return value;
if (Array.isArray(value)) {
for (const item of value) {
const error = findFirstError(item);
if (error) return error;
}
return undefined;
}
if (value && typeof value === 'object') {
for (const item of Object.values(value)) {
const error = findFirstError(item);
if (error) return error;
}
}
return undefined;
}
+79
View File
@@ -0,0 +1,79 @@
type NamedRecord = Partial<Record<'name' | 'title' | 'invoiceNumber' | 'label', unknown>>;
type RelationOption = {
label: string;
value: string;
};
const gbpFormatter = new Intl.NumberFormat('en-GB', {
style: 'currency',
currency: 'GBP'
});
const dateFormatter = new Intl.DateTimeFormat('en-GB', {
day: '2-digit',
month: 'short',
year: 'numeric'
});
const dateTimeFormatter = new Intl.DateTimeFormat('en-GB', {
day: '2-digit',
month: 'short',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
export function formatValue(value: unknown) {
if (value === null || value === undefined || value === '') return 'Not set';
return String(value);
}
export function formatMoney(value: unknown, fallback = 'Not set') {
if (value === null || value === undefined || value === '') return fallback;
const amount = typeof value === 'number' ? value : Number(value);
return gbpFormatter.format(Number.isFinite(amount) ? amount : 0);
}
export function formatDate(value: unknown, withTime = false) {
if (!value) return 'Not set';
const date = value instanceof Date ? value : new Date(String(value));
if (Number.isNaN(date.getTime())) return 'Not set';
return (withTime ? dateTimeFormatter : dateFormatter).format(date);
}
export function recordName(record: object | undefined, fallback: string) {
const item = record as NamedRecord | undefined;
return String(item?.name ?? item?.title ?? item?.invoiceNumber ?? item?.label ?? fallback);
}
export function relationLabel<Options extends object, Key extends keyof Options & string>(
options: Options,
value: unknown,
optionsKey: Key
) {
if (typeof value !== 'string') return 'Not set';
const optionList = options[optionsKey] as RelationOption[] | undefined;
return optionList?.find((option) => option.value === value)?.label ?? value;
}
export function filterRecords<T>(records: readonly T[], query: string) {
const normalizedQuery = query.trim().toLowerCase();
if (!normalizedQuery) return [...records];
return records.filter((record) => searchableText(record).includes(normalizedQuery));
}
function searchableText(value: unknown): string {
if (value === null || value === undefined) return '';
if (value instanceof Date) return value.toISOString().toLowerCase();
if (typeof value !== 'object') return String(value).toLowerCase();
if (Array.isArray(value)) return value.map(searchableText).join(' ');
return Object.values(value).map(searchableText).join(' ');
}
@@ -1,8 +1,7 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus'; import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -35,58 +34,50 @@
<Dialog.Description>Add a manually managed Better Auth user.</Dialog.Description> <Dialog.Description>Add a manually managed Better Auth user.</Dialog.Description>
</Dialog.Header> </Dialog.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate> <form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="name"> <Form.Field form={createForm} name="name">
<Field.Field> <Form.Control id="create-name">
<Control id="create-name"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input {...props} autocomplete="name" bind:value={$createData.name} />
<Input {...props} autocomplete="name" bind:value={$createData.name} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={createForm} name="email">
</FormField> <Form.Control id="create-email">
<FormField form={createForm} name="email"> {#snippet children({ props })}
<Field.Field> <Form.Label>Email</Form.Label>
<Control id="create-email"> <Input {...props} type="email" autocomplete="email" bind:value={$createData.email} />
{#snippet children({ props })} {/snippet}
<Field.Label>Email</Field.Label> </Form.Control>
<Input {...props} type="email" autocomplete="email" bind:value={$createData.email} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={createForm} name="password">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="create-password">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Password</Form.Label>
<FormField form={createForm} name="password"> <Input
<Field.Field> {...props}
<Control id="create-password"> type="password"
{#snippet children({ props })} autocomplete="new-password"
<Field.Label>Password</Field.Label> bind:value={$createData.password}
<Input />
{...props} {/snippet}
type="password" </Form.Control>
autocomplete="new-password" <Form.FieldErrors />
bind:value={$createData.password} </Form.Field>
/> <Form.Field form={createForm} name="role">
{/snippet} <Form.Control id="create-role">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Role</Form.Label>
</Field.Field> <NativeSelect.Root {...props} bind:value={$createData.role} class="w-full">
</FormField> <NativeSelect.Option value="admin">Admin</NativeSelect.Option>
<FormField form={createForm} name="role"> <NativeSelect.Option value="user">User</NativeSelect.Option>
<Field.Field> </NativeSelect.Root>
<Control id="create-role"> {/snippet}
{#snippet children({ props })} </Form.Control>
<Field.Label>Role</Field.Label> <Form.FieldErrors />
<NativeSelect.Root {...props} bind:value={$createData.role} class="w-full"> </Form.Field>
<NativeSelect.Option value="admin">Admin</NativeSelect.Option>
<NativeSelect.Option value="user">User</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close> <Dialog.Close>
{#snippet child({ props })} {#snippet child({ props })}
@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -30,59 +29,51 @@
{#if editingUser} {#if editingUser}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit> <form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} /> <input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="name"> <Form.Field form={editForm} name="name">
<Field.Field> <Form.Control id="edit-name">
<Control id="edit-name"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input {...props} autocomplete="name" bind:value={$editData.name} />
<Input {...props} autocomplete="name" bind:value={$editData.name} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={editForm} name="email">
</FormField> <Form.Control id="edit-email">
<FormField form={editForm} name="email"> {#snippet children({ props })}
<Field.Field> <Form.Label>Email</Form.Label>
<Control id="edit-email"> <Input {...props} type="email" autocomplete="email" bind:value={$editData.email} />
{#snippet children({ props })} {/snippet}
<Field.Label>Email</Field.Label> </Form.Control>
<Input {...props} type="email" autocomplete="email" bind:value={$editData.email} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={editForm} name="password">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="edit-password">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>New password</Form.Label>
<FormField form={editForm} name="password"> <Input
<Field.Field> {...props}
<Control id="edit-password"> type="password"
{#snippet children({ props })} autocomplete="new-password"
<Field.Label>New password</Field.Label> placeholder="Leave blank to keep current password"
<Input bind:value={$editData.password}
{...props} />
type="password" {/snippet}
autocomplete="new-password" </Form.Control>
placeholder="Leave blank to keep current password" <Form.FieldErrors />
bind:value={$editData.password} </Form.Field>
/> <Form.Field form={editForm} name="role">
{/snippet} <Form.Control id="edit-role">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Role</Form.Label>
</Field.Field> <NativeSelect.Root {...props} bind:value={$editData.role} class="w-full">
</FormField> <NativeSelect.Option value="admin">Admin</NativeSelect.Option>
<FormField form={editForm} name="role"> <NativeSelect.Option value="user">User</NativeSelect.Option>
<Field.Field> </NativeSelect.Root>
<Control id="edit-role"> {/snippet}
{#snippet children({ props })} </Form.Control>
<Field.Label>Role</Field.Label> <Form.FieldErrors />
<NativeSelect.Root {...props} bind:value={$editData.role} class="w-full"> </Form.Field>
<NativeSelect.Option value="admin">Admin</NativeSelect.Option>
<NativeSelect.Option value="user">User</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close> <Dialog.Close>
{#snippet child({ props })} {#snippet child({ props })}
@@ -2,12 +2,12 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import { page } from '$app/state'; import { page } from '$app/state';
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import { superForm } from 'sveltekit-superforms'; import { superForm } from 'sveltekit-superforms';
import { zod4Client } from 'sveltekit-superforms/adapters'; import { zod4Client } from 'sveltekit-superforms/adapters';
import { clientEditSchema } from '$lib/schemas/clients.schema'; import { clientEditSchema } from '$lib/schemas/clients.schema';
import { handleFormToast } from '$lib/form-feedback';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -50,28 +50,6 @@
const { form: editData, enhance: enhanceEdit } = editForm; const { form: editData, enhance: enhanceEdit } = editForm;
function handleFormToast(
form: { valid: boolean; message?: string; errors: Record<string, unknown> },
id: string,
onSuccess: () => void
) {
if (form.valid) {
if (form.message) toast.success(form.message, { id });
onSuccess();
return;
}
toast.error(form.message ?? firstError(form.errors), { id });
}
function firstError(errors: Record<string, unknown>) {
for (const value of Object.values(errors)) {
if (Array.isArray(value) && typeof value[0] === 'string') return value[0];
}
return 'Check the highlighted fields.';
}
function navigateToTab(tab: TabValue) { function navigateToTab(tab: TabValue) {
goto(resolve(`/dashboard/clients/[id]/${tab}`, { id: data.client.id })); goto(resolve(`/dashboard/clients/[id]/${tab}`, { id: data.client.id }));
} }
@@ -103,76 +81,66 @@
</Dialog.Header> </Dialog.Header>
<form method="POST" action="/dashboard/clients?/edit" class="grid gap-2" use:enhanceEdit> <form method="POST" action="/dashboard/clients?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} /> <input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="name"> <Form.Field form={editForm} name="name">
<Field.Field> <Form.Control id="edit-client-name">
<Control id="edit-client-name"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input {...props} type="text" bind:value={$editData.name} />
<Input {...props} type="text" bind:value={$editData.name} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={editForm} name="type">
</FormField> <Form.Control id="edit-client-type">
<FormField form={editForm} name="type"> {#snippet children({ props })}
<Field.Field> <Form.Label>Type</Form.Label>
<Control id="edit-client-type"> <NativeSelect.Root {...props} class="w-full" bind:value={$editData.type}>
{#snippet children({ props })} <NativeSelect.Option value="">Not set</NativeSelect.Option>
<Field.Label>Type</Field.Label> <NativeSelect.Option value="business">Business</NativeSelect.Option>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.type}> <NativeSelect.Option value="sport">Sport</NativeSelect.Option>
<NativeSelect.Option value="">Not set</NativeSelect.Option> <NativeSelect.Option value="individual">Individual</NativeSelect.Option>
<NativeSelect.Option value="business">Business</NativeSelect.Option> </NativeSelect.Root>
<NativeSelect.Option value="sport">Sport</NativeSelect.Option> {/snippet}
<NativeSelect.Option value="individual">Individual</NativeSelect.Option> </Form.Control>
</NativeSelect.Root> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={editForm} name="status">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="edit-client-status">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Status</Form.Label>
<FormField form={editForm} name="status"> <NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<Field.Field> <NativeSelect.Option value="">Not set</NativeSelect.Option>
<Control id="edit-client-status"> <NativeSelect.Option value="active">Active</NativeSelect.Option>
{#snippet children({ props })} <NativeSelect.Option value="prospect">Prospect</NativeSelect.Option>
<Field.Label>Status</Field.Label> <NativeSelect.Option value="paused">Paused</NativeSelect.Option>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}> </NativeSelect.Root>
<NativeSelect.Option value="">Not set</NativeSelect.Option> {/snippet}
<NativeSelect.Option value="active">Active</NativeSelect.Option> </Form.Control>
<NativeSelect.Option value="prospect">Prospect</NativeSelect.Option> <Form.FieldErrors />
<NativeSelect.Option value="paused">Paused</NativeSelect.Option> </Form.Field>
</NativeSelect.Root> <Form.Field form={editForm} name="website">
{/snippet} <Form.Control id="edit-client-website">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Website</Form.Label>
</Field.Field> <Input
</FormField> {...props}
<FormField form={editForm} name="website"> type="url"
<Field.Field> placeholder="https://example.com"
<Control id="edit-client-website"> bind:value={$editData.website}
{#snippet children({ props })} />
<Field.Label>Website</Field.Label> {/snippet}
<Input </Form.Control>
{...props} <Form.FieldErrors />
type="url" </Form.Field>
placeholder="https://example.com" <Form.Field form={editForm} name="notes">
bind:value={$editData.website} <Form.Control id="edit-client-notes">
/> {#snippet children({ props })}
{/snippet} <Form.Label>Notes</Form.Label>
</Control> <Textarea {...props} bind:value={$editData.notes} />
<Field.Error><FieldErrors /></Field.Error> {/snippet}
</Field.Field> </Form.Control>
</FormField> <Form.FieldErrors />
<FormField form={editForm} name="notes"> </Form.Field>
<Field.Field>
<Control id="edit-client-notes">
{#snippet children({ props })}
<Field.Label>Notes</Field.Label>
<Textarea {...props} bind:value={$editData.notes} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus'; import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -32,98 +31,82 @@
<Dialog.Description>Add a new address record.</Dialog.Description> <Dialog.Description>Add a new address record.</Dialog.Description>
</Dialog.Header> </Dialog.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate> <form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="label"> <Form.Field form={createForm} name="label">
<Field.Field> <Form.Control id="create-label">
<Control id="create-label"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Label</Form.Label>
<Field.Label>Label</Field.Label> <Input {...props} type="text" bind:value={$createData.label} />
<Input {...props} type="text" bind:value={$createData.label} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={createForm} name="line1">
</FormField> <Form.Control id="create-line1">
<FormField form={createForm} name="line1"> {#snippet children({ props })}
<Field.Field> <Form.Label>Address line 1</Form.Label>
<Control id="create-line1"> <Input {...props} type="text" bind:value={$createData.line1} />
{#snippet children({ props })} {/snippet}
<Field.Label>Address line 1</Field.Label> </Form.Control>
<Input {...props} type="text" bind:value={$createData.line1} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={createForm} name="line2">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="create-line2">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Address line 2</Form.Label>
<FormField form={createForm} name="line2"> <Input {...props} type="text" bind:value={$createData.line2} />
<Field.Field> {/snippet}
<Control id="create-line2"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Address line 2</Field.Label> </Form.Field>
<Input {...props} type="text" bind:value={$createData.line2} /> <Form.Field form={createForm} name="city">
{/snippet} <Form.Control id="create-city">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>City</Form.Label>
</Field.Field> <Input {...props} type="text" bind:value={$createData.city} />
</FormField> {/snippet}
<FormField form={createForm} name="city"> </Form.Control>
<Field.Field> <Form.FieldErrors />
<Control id="create-city"> </Form.Field>
{#snippet children({ props })} <Form.Field form={createForm} name="region">
<Field.Label>City</Field.Label> <Form.Control id="create-region">
<Input {...props} type="text" bind:value={$createData.city} /> {#snippet children({ props })}
{/snippet} <Form.Label>Region / county</Form.Label>
</Control> <Input {...props} type="text" bind:value={$createData.region} />
<Field.Error><FieldErrors /></Field.Error> {/snippet}
</Field.Field> </Form.Control>
</FormField> <Form.FieldErrors />
<FormField form={createForm} name="region"> </Form.Field>
<Field.Field> <Form.Field form={createForm} name="postcode">
<Control id="create-region"> <Form.Control id="create-postcode">
{#snippet children({ props })} {#snippet children({ props })}
<Field.Label>Region / county</Field.Label> <Form.Label>Postcode</Form.Label>
<Input {...props} type="text" bind:value={$createData.region} /> <Input {...props} type="text" bind:value={$createData.postcode} />
{/snippet} {/snippet}
</Control> </Form.Control>
<Field.Error><FieldErrors /></Field.Error> <Form.FieldErrors />
</Field.Field> </Form.Field>
</FormField> <Form.Field form={createForm} name="country">
<FormField form={createForm} name="postcode"> <Form.Control id="create-country">
<Field.Field> {#snippet children({ props })}
<Control id="create-postcode"> <Form.Label>Country</Form.Label>
{#snippet children({ props })} <Input {...props} type="text" bind:value={$createData.country} />
<Field.Label>Postcode</Field.Label> {/snippet}
<Input {...props} type="text" bind:value={$createData.postcode} /> </Form.Control>
{/snippet} <Form.FieldErrors />
</Control> </Form.Field>
<Field.Error><FieldErrors /></Field.Error> <Form.Field form={createForm} name="isPrimary">
</Field.Field> <Form.Control id="create-isPrimary">
</FormField> {#snippet children({ props })}
<FormField form={createForm} name="country"> <Form.Label>Primary address</Form.Label>
<Field.Field> <NativeSelect.Root {...props} class="w-full" bind:value={$createData.isPrimary}>
<Control id="create-country"> <NativeSelect.Option value="">Not set</NativeSelect.Option>
{#snippet children({ props })} <NativeSelect.Option value="false">No</NativeSelect.Option>
<Field.Label>Country</Field.Label> <NativeSelect.Option value="true">Yes</NativeSelect.Option>
<Input {...props} type="text" bind:value={$createData.country} /> </NativeSelect.Root>
{/snippet} {/snippet}
</Control> </Form.Control>
<Field.Error><FieldErrors /></Field.Error> <Form.FieldErrors />
</Field.Field> </Form.Field>
</FormField>
<FormField form={createForm} name="isPrimary">
<Field.Field>
<Control id="create-isPrimary">
{#snippet children({ props })}
<Field.Label>Primary address</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.isPrimary}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="false">No</NativeSelect.Option>
<NativeSelect.Option value="true">Yes</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -30,98 +29,82 @@
{#if editingRecord} {#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit> <form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} /> <input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="label"> <Form.Field form={editForm} name="label">
<Field.Field> <Form.Control id="edit-label">
<Control id="edit-label"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Label</Form.Label>
<Field.Label>Label</Field.Label> <Input {...props} type="text" bind:value={$editData.label} />
<Input {...props} type="text" bind:value={$editData.label} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={editForm} name="line1">
</FormField> <Form.Control id="edit-line1">
<FormField form={editForm} name="line1"> {#snippet children({ props })}
<Field.Field> <Form.Label>Address line 1</Form.Label>
<Control id="edit-line1"> <Input {...props} type="text" bind:value={$editData.line1} />
{#snippet children({ props })} {/snippet}
<Field.Label>Address line 1</Field.Label> </Form.Control>
<Input {...props} type="text" bind:value={$editData.line1} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={editForm} name="line2">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="edit-line2">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Address line 2</Form.Label>
<FormField form={editForm} name="line2"> <Input {...props} type="text" bind:value={$editData.line2} />
<Field.Field> {/snippet}
<Control id="edit-line2"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Address line 2</Field.Label> </Form.Field>
<Input {...props} type="text" bind:value={$editData.line2} /> <Form.Field form={editForm} name="city">
{/snippet} <Form.Control id="edit-city">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>City</Form.Label>
</Field.Field> <Input {...props} type="text" bind:value={$editData.city} />
</FormField> {/snippet}
<FormField form={editForm} name="city"> </Form.Control>
<Field.Field> <Form.FieldErrors />
<Control id="edit-city"> </Form.Field>
{#snippet children({ props })} <Form.Field form={editForm} name="region">
<Field.Label>City</Field.Label> <Form.Control id="edit-region">
<Input {...props} type="text" bind:value={$editData.city} /> {#snippet children({ props })}
{/snippet} <Form.Label>Region / county</Form.Label>
</Control> <Input {...props} type="text" bind:value={$editData.region} />
<Field.Error><FieldErrors /></Field.Error> {/snippet}
</Field.Field> </Form.Control>
</FormField> <Form.FieldErrors />
<FormField form={editForm} name="region"> </Form.Field>
<Field.Field> <Form.Field form={editForm} name="postcode">
<Control id="edit-region"> <Form.Control id="edit-postcode">
{#snippet children({ props })} {#snippet children({ props })}
<Field.Label>Region / county</Field.Label> <Form.Label>Postcode</Form.Label>
<Input {...props} type="text" bind:value={$editData.region} /> <Input {...props} type="text" bind:value={$editData.postcode} />
{/snippet} {/snippet}
</Control> </Form.Control>
<Field.Error><FieldErrors /></Field.Error> <Form.FieldErrors />
</Field.Field> </Form.Field>
</FormField> <Form.Field form={editForm} name="country">
<FormField form={editForm} name="postcode"> <Form.Control id="edit-country">
<Field.Field> {#snippet children({ props })}
<Control id="edit-postcode"> <Form.Label>Country</Form.Label>
{#snippet children({ props })} <Input {...props} type="text" bind:value={$editData.country} />
<Field.Label>Postcode</Field.Label> {/snippet}
<Input {...props} type="text" bind:value={$editData.postcode} /> </Form.Control>
{/snippet} <Form.FieldErrors />
</Control> </Form.Field>
<Field.Error><FieldErrors /></Field.Error> <Form.Field form={editForm} name="isPrimary">
</Field.Field> <Form.Control id="edit-isPrimary">
</FormField> {#snippet children({ props })}
<FormField form={editForm} name="country"> <Form.Label>Primary address</Form.Label>
<Field.Field> <NativeSelect.Root {...props} class="w-full" bind:value={$editData.isPrimary}>
<Control id="edit-country"> <NativeSelect.Option value="">Not set</NativeSelect.Option>
{#snippet children({ props })} <NativeSelect.Option value="false">No</NativeSelect.Option>
<Field.Label>Country</Field.Label> <NativeSelect.Option value="true">Yes</NativeSelect.Option>
<Input {...props} type="text" bind:value={$editData.country} /> </NativeSelect.Root>
{/snippet} {/snippet}
</Control> </Form.Control>
<Field.Error><FieldErrors /></Field.Error> <Form.FieldErrors />
</Field.Field> </Form.Field>
</FormField>
<FormField form={editForm} name="isPrimary">
<Field.Field>
<Control id="edit-isPrimary">
{#snippet children({ props })}
<Field.Label>Primary address</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.isPrimary}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="false">No</NativeSelect.Option>
<NativeSelect.Option value="true">Yes</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus'; import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -35,87 +34,75 @@
<Dialog.Description>Add a new booking record.</Dialog.Description> <Dialog.Description>Add a new booking record.</Dialog.Description>
</Dialog.Header> </Dialog.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate> <form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="roomId"> <Form.Field form={createForm} name="roomId">
<Field.Field> <Form.Control id="create-roomId">
<Control id="create-roomId"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Room</Form.Label>
<Field.Label>Room</Field.Label> <NativeSelect.Root {...props} class="w-full" bind:value={$createData.roomId}>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.roomId}> {#each data.options.rooms as option (option.value)}
{#each data.options.rooms as option (option.value)} <NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option>
<NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option> {/each}
{/each} </NativeSelect.Root>
</NativeSelect.Root> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={createForm} name="serviceId">
</FormField> <Form.Control id="create-serviceId">
<FormField form={createForm} name="serviceId"> {#snippet children({ props })}
<Field.Field> <Form.Label>Service</Form.Label>
<Control id="create-serviceId"> <NativeSelect.Root {...props} class="w-full" bind:value={$createData.serviceId}>
{#snippet children({ props })} <NativeSelect.Option value="">Not set</NativeSelect.Option>
<Field.Label>Service</Field.Label> {#each data.options.services as option (option.value)}
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.serviceId}> <NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option>
<NativeSelect.Option value="">Not set</NativeSelect.Option> {/each}
{#each data.options.services as option (option.value)} </NativeSelect.Root>
<NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option> {/snippet}
{/each} </Form.Control>
</NativeSelect.Root> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={createForm} name="startsAt">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="create-startsAt">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Starts at</Form.Label>
<FormField form={createForm} name="startsAt"> <Input {...props} type="datetime-local" bind:value={$createData.startsAt} />
<Field.Field> {/snippet}
<Control id="create-startsAt"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Starts at</Field.Label> </Form.Field>
<Input {...props} type="datetime-local" bind:value={$createData.startsAt} /> <Form.Field form={createForm} name="endsAt">
{/snippet} <Form.Control id="create-endsAt">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Ends at</Form.Label>
</Field.Field> <Input {...props} type="datetime-local" bind:value={$createData.endsAt} />
</FormField> {/snippet}
<FormField form={createForm} name="endsAt"> </Form.Control>
<Field.Field> <Form.FieldErrors />
<Control id="create-endsAt"> </Form.Field>
{#snippet children({ props })} <Form.Field form={createForm} name="status">
<Field.Label>Ends at</Field.Label> <Form.Control id="create-status">
<Input {...props} type="datetime-local" bind:value={$createData.endsAt} /> {#snippet children({ props })}
{/snippet} <Form.Label>Status</Form.Label>
</Control> <NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}>
<Field.Error><FieldErrors /></Field.Error> <NativeSelect.Option value="">Not set</NativeSelect.Option>
</Field.Field> <NativeSelect.Option value="booked">Booked</NativeSelect.Option>
</FormField> <NativeSelect.Option value="confirmed">Confirmed</NativeSelect.Option>
<FormField form={createForm} name="status"> <NativeSelect.Option value="completed">Completed</NativeSelect.Option>
<Field.Field> <NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option>
<Control id="create-status"> </NativeSelect.Root>
{#snippet children({ props })} {/snippet}
<Field.Label>Status</Field.Label> </Form.Control>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}> <Form.FieldErrors />
<NativeSelect.Option value="">Not set</NativeSelect.Option> </Form.Field>
<NativeSelect.Option value="booked">Booked</NativeSelect.Option> <Form.Field form={createForm} name="notes">
<NativeSelect.Option value="confirmed">Confirmed</NativeSelect.Option> <Form.Control id="create-notes">
<NativeSelect.Option value="completed">Completed</NativeSelect.Option> {#snippet children({ props })}
<NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option> <Form.Label>Notes</Form.Label>
</NativeSelect.Root> <Textarea {...props} bind:value={$createData.notes} />
{/snippet} {/snippet}
</Control> </Form.Control>
<Field.Error><FieldErrors /></Field.Error> <Form.FieldErrors />
</Field.Field> </Form.Field>
</FormField>
<FormField form={createForm} name="notes">
<Field.Field>
<Control id="create-notes">
{#snippet children({ props })}
<Field.Label>Notes</Field.Label>
<Textarea {...props} bind:value={$createData.notes} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -33,87 +32,75 @@
{#if editingRecord} {#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit> <form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} /> <input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="roomId"> <Form.Field form={editForm} name="roomId">
<Field.Field> <Form.Control id="edit-roomId">
<Control id="edit-roomId"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Room</Form.Label>
<Field.Label>Room</Field.Label> <NativeSelect.Root {...props} class="w-full" bind:value={$editData.roomId}>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.roomId}> {#each data.options.rooms as option (option.value)}
{#each data.options.rooms as option (option.value)} <NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option>
<NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option> {/each}
{/each} </NativeSelect.Root>
</NativeSelect.Root> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={editForm} name="serviceId">
</FormField> <Form.Control id="edit-serviceId">
<FormField form={editForm} name="serviceId"> {#snippet children({ props })}
<Field.Field> <Form.Label>Service</Form.Label>
<Control id="edit-serviceId"> <NativeSelect.Root {...props} class="w-full" bind:value={$editData.serviceId}>
{#snippet children({ props })} <NativeSelect.Option value="">Not set</NativeSelect.Option>
<Field.Label>Service</Field.Label> {#each data.options.services as option (option.value)}
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.serviceId}> <NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option>
<NativeSelect.Option value="">Not set</NativeSelect.Option> {/each}
{#each data.options.services as option (option.value)} </NativeSelect.Root>
<NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option> {/snippet}
{/each} </Form.Control>
</NativeSelect.Root> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={editForm} name="startsAt">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="edit-startsAt">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Starts at</Form.Label>
<FormField form={editForm} name="startsAt"> <Input {...props} type="datetime-local" bind:value={$editData.startsAt} />
<Field.Field> {/snippet}
<Control id="edit-startsAt"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Starts at</Field.Label> </Form.Field>
<Input {...props} type="datetime-local" bind:value={$editData.startsAt} /> <Form.Field form={editForm} name="endsAt">
{/snippet} <Form.Control id="edit-endsAt">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Ends at</Form.Label>
</Field.Field> <Input {...props} type="datetime-local" bind:value={$editData.endsAt} />
</FormField> {/snippet}
<FormField form={editForm} name="endsAt"> </Form.Control>
<Field.Field> <Form.FieldErrors />
<Control id="edit-endsAt"> </Form.Field>
{#snippet children({ props })} <Form.Field form={editForm} name="status">
<Field.Label>Ends at</Field.Label> <Form.Control id="edit-status">
<Input {...props} type="datetime-local" bind:value={$editData.endsAt} /> {#snippet children({ props })}
{/snippet} <Form.Label>Status</Form.Label>
</Control> <NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<Field.Error><FieldErrors /></Field.Error> <NativeSelect.Option value="">Not set</NativeSelect.Option>
</Field.Field> <NativeSelect.Option value="booked">Booked</NativeSelect.Option>
</FormField> <NativeSelect.Option value="confirmed">Confirmed</NativeSelect.Option>
<FormField form={editForm} name="status"> <NativeSelect.Option value="completed">Completed</NativeSelect.Option>
<Field.Field> <NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option>
<Control id="edit-status"> </NativeSelect.Root>
{#snippet children({ props })} {/snippet}
<Field.Label>Status</Field.Label> </Form.Control>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}> <Form.FieldErrors />
<NativeSelect.Option value="">Not set</NativeSelect.Option> </Form.Field>
<NativeSelect.Option value="booked">Booked</NativeSelect.Option> <Form.Field form={editForm} name="notes">
<NativeSelect.Option value="confirmed">Confirmed</NativeSelect.Option> <Form.Control id="edit-notes">
<NativeSelect.Option value="completed">Completed</NativeSelect.Option> {#snippet children({ props })}
<NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option> <Form.Label>Notes</Form.Label>
</NativeSelect.Root> <Textarea {...props} bind:value={$editData.notes} />
{/snippet} {/snippet}
</Control> </Form.Control>
<Field.Error><FieldErrors /></Field.Error> <Form.FieldErrors />
</Field.Field> </Form.Field>
</FormField>
<FormField form={editForm} name="notes">
<Field.Field>
<Control id="edit-notes">
{#snippet children({ props })}
<Field.Label>Notes</Field.Label>
<Textarea {...props} bind:value={$editData.notes} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus'; import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -33,76 +32,64 @@
<Dialog.Description>Add a new contact record.</Dialog.Description> <Dialog.Description>Add a new contact record.</Dialog.Description>
</Dialog.Header> </Dialog.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate> <form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="name"> <Form.Field form={createForm} name="name">
<Field.Field> <Form.Control id="create-name">
<Control id="create-name"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input {...props} type="text" bind:value={$createData.name} />
<Input {...props} type="text" bind:value={$createData.name} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={createForm} name="role">
</FormField> <Form.Control id="create-role">
<FormField form={createForm} name="role"> {#snippet children({ props })}
<Field.Field> <Form.Label>Role</Form.Label>
<Control id="create-role"> <Input {...props} type="text" bind:value={$createData.role} />
{#snippet children({ props })} {/snippet}
<Field.Label>Role</Field.Label> </Form.Control>
<Input {...props} type="text" bind:value={$createData.role} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={createForm} name="email">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="create-email">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Email</Form.Label>
<FormField form={createForm} name="email"> <Input {...props} type="email" bind:value={$createData.email} />
<Field.Field> {/snippet}
<Control id="create-email"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Email</Field.Label> </Form.Field>
<Input {...props} type="email" bind:value={$createData.email} /> <Form.Field form={createForm} name="phone">
{/snippet} <Form.Control id="create-phone">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Phone</Form.Label>
</Field.Field> <Input {...props} type="text" bind:value={$createData.phone} />
</FormField> {/snippet}
<FormField form={createForm} name="phone"> </Form.Control>
<Field.Field> <Form.FieldErrors />
<Control id="create-phone"> </Form.Field>
{#snippet children({ props })} <Form.Field form={createForm} name="isPrimary">
<Field.Label>Phone</Field.Label> <Form.Control id="create-isPrimary">
<Input {...props} type="text" bind:value={$createData.phone} /> {#snippet children({ props })}
{/snippet} <Form.Label>Primary contact</Form.Label>
</Control> <NativeSelect.Root {...props} class="w-full" bind:value={$createData.isPrimary}>
<Field.Error><FieldErrors /></Field.Error> <NativeSelect.Option value="">Not set</NativeSelect.Option>
</Field.Field> <NativeSelect.Option value="false">No</NativeSelect.Option>
</FormField> <NativeSelect.Option value="true">Yes</NativeSelect.Option>
<FormField form={createForm} name="isPrimary"> </NativeSelect.Root>
<Field.Field> {/snippet}
<Control id="create-isPrimary"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Primary contact</Field.Label> </Form.Field>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.isPrimary}> <Form.Field form={createForm} name="notes">
<NativeSelect.Option value="">Not set</NativeSelect.Option> <Form.Control id="create-notes">
<NativeSelect.Option value="false">No</NativeSelect.Option> {#snippet children({ props })}
<NativeSelect.Option value="true">Yes</NativeSelect.Option> <Form.Label>Notes</Form.Label>
</NativeSelect.Root> <Textarea {...props} bind:value={$createData.notes} />
{/snippet} {/snippet}
</Control> </Form.Control>
<Field.Error><FieldErrors /></Field.Error> <Form.FieldErrors />
</Field.Field> </Form.Field>
</FormField>
<FormField form={createForm} name="notes">
<Field.Field>
<Control id="create-notes">
{#snippet children({ props })}
<Field.Label>Notes</Field.Label>
<Textarea {...props} bind:value={$createData.notes} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -31,76 +30,64 @@
{#if editingRecord} {#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit> <form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} /> <input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="name"> <Form.Field form={editForm} name="name">
<Field.Field> <Form.Control id="edit-name">
<Control id="edit-name"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input {...props} type="text" bind:value={$editData.name} />
<Input {...props} type="text" bind:value={$editData.name} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={editForm} name="role">
</FormField> <Form.Control id="edit-role">
<FormField form={editForm} name="role"> {#snippet children({ props })}
<Field.Field> <Form.Label>Role</Form.Label>
<Control id="edit-role"> <Input {...props} type="text" bind:value={$editData.role} />
{#snippet children({ props })} {/snippet}
<Field.Label>Role</Field.Label> </Form.Control>
<Input {...props} type="text" bind:value={$editData.role} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={editForm} name="email">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="edit-email">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Email</Form.Label>
<FormField form={editForm} name="email"> <Input {...props} type="email" bind:value={$editData.email} />
<Field.Field> {/snippet}
<Control id="edit-email"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Email</Field.Label> </Form.Field>
<Input {...props} type="email" bind:value={$editData.email} /> <Form.Field form={editForm} name="phone">
{/snippet} <Form.Control id="edit-phone">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Phone</Form.Label>
</Field.Field> <Input {...props} type="text" bind:value={$editData.phone} />
</FormField> {/snippet}
<FormField form={editForm} name="phone"> </Form.Control>
<Field.Field> <Form.FieldErrors />
<Control id="edit-phone"> </Form.Field>
{#snippet children({ props })} <Form.Field form={editForm} name="isPrimary">
<Field.Label>Phone</Field.Label> <Form.Control id="edit-isPrimary">
<Input {...props} type="text" bind:value={$editData.phone} /> {#snippet children({ props })}
{/snippet} <Form.Label>Primary contact</Form.Label>
</Control> <NativeSelect.Root {...props} class="w-full" bind:value={$editData.isPrimary}>
<Field.Error><FieldErrors /></Field.Error> <NativeSelect.Option value="">Not set</NativeSelect.Option>
</Field.Field> <NativeSelect.Option value="false">No</NativeSelect.Option>
</FormField> <NativeSelect.Option value="true">Yes</NativeSelect.Option>
<FormField form={editForm} name="isPrimary"> </NativeSelect.Root>
<Field.Field> {/snippet}
<Control id="edit-isPrimary"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Primary contact</Field.Label> </Form.Field>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.isPrimary}> <Form.Field form={editForm} name="notes">
<NativeSelect.Option value="">Not set</NativeSelect.Option> <Form.Control id="edit-notes">
<NativeSelect.Option value="false">No</NativeSelect.Option> {#snippet children({ props })}
<NativeSelect.Option value="true">Yes</NativeSelect.Option> <Form.Label>Notes</Form.Label>
</NativeSelect.Root> <Textarea {...props} bind:value={$editData.notes} />
{/snippet} {/snippet}
</Control> </Form.Control>
<Field.Error><FieldErrors /></Field.Error> <Form.FieldErrors />
</Field.Field> </Form.Field>
</FormField>
<FormField form={editForm} name="notes">
<Field.Field>
<Control id="edit-notes">
{#snippet children({ props })}
<Field.Label>Notes</Field.Label>
<Textarea {...props} bind:value={$editData.notes} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,9 +1,8 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus'; import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import MoneyField from '$lib/components/money-field.svelte'; import MoneyField from '$lib/components/money-field.svelte';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -34,56 +33,48 @@
<Dialog.Description>Add a new contract record.</Dialog.Description> <Dialog.Description>Add a new contract record.</Dialog.Description>
</Dialog.Header> </Dialog.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate> <form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="title"> <Form.Field form={createForm} name="title">
<Field.Field> <Form.Control id="create-title">
<Control id="create-title"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Title</Form.Label>
<Field.Label>Title</Field.Label> <Input {...props} type="text" bind:value={$createData.title} />
<Input {...props} type="text" bind:value={$createData.title} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={createForm} name="startDate">
</FormField> <Form.Control id="create-startDate">
<FormField form={createForm} name="startDate"> {#snippet children({ props })}
<Field.Field> <Form.Label>Start date</Form.Label>
<Control id="create-startDate"> <Input {...props} type="date" bind:value={$createData.startDate} />
{#snippet children({ props })} {/snippet}
<Field.Label>Start date</Field.Label> </Form.Control>
<Input {...props} type="date" bind:value={$createData.startDate} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={createForm} name="endDate">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="create-endDate">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>End date</Form.Label>
<FormField form={createForm} name="endDate"> <Input {...props} type="date" bind:value={$createData.endDate} />
<Field.Field> {/snippet}
<Control id="create-endDate"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>End date</Field.Label> </Form.Field>
<Input {...props} type="date" bind:value={$createData.endDate} /> <Form.Field form={createForm} name="status">
{/snippet} <Form.Control id="create-status">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Status</Form.Label>
</Field.Field> <NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}>
</FormField> <NativeSelect.Option value="">Not set</NativeSelect.Option>
<FormField form={createForm} name="status"> <NativeSelect.Option value="draft">Draft</NativeSelect.Option>
<Field.Field> <NativeSelect.Option value="active">Active</NativeSelect.Option>
<Control id="create-status"> <NativeSelect.Option value="expired">Expired</NativeSelect.Option>
{#snippet children({ props })} <NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option>
<Field.Label>Status</Field.Label> </NativeSelect.Root>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}> {/snippet}
<NativeSelect.Option value="">Not set</NativeSelect.Option> </Form.Control>
<NativeSelect.Option value="draft">Draft</NativeSelect.Option> <Form.FieldErrors />
<NativeSelect.Option value="active">Active</NativeSelect.Option> </Form.Field>
<NativeSelect.Option value="expired">Expired</NativeSelect.Option>
<NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<MoneyField <MoneyField
form={createForm} form={createForm}
data={createData} data={createData}
@@ -91,17 +82,15 @@
label="Value" label="Value"
id="create-valueGbp" id="create-valueGbp"
/> />
<FormField form={createForm} name="notes"> <Form.Field form={createForm} name="notes">
<Field.Field> <Form.Control id="create-notes">
<Control id="create-notes"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Notes</Form.Label>
<Field.Label>Notes</Field.Label> <Textarea {...props} bind:value={$createData.notes} />
<Textarea {...props} bind:value={$createData.notes} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import MoneyField from '$lib/components/money-field.svelte'; import MoneyField from '$lib/components/money-field.svelte';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -32,56 +31,48 @@
{#if editingRecord} {#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit> <form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} /> <input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="title"> <Form.Field form={editForm} name="title">
<Field.Field> <Form.Control id="edit-title">
<Control id="edit-title"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Title</Form.Label>
<Field.Label>Title</Field.Label> <Input {...props} type="text" bind:value={$editData.title} />
<Input {...props} type="text" bind:value={$editData.title} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={editForm} name="startDate">
</FormField> <Form.Control id="edit-startDate">
<FormField form={editForm} name="startDate"> {#snippet children({ props })}
<Field.Field> <Form.Label>Start date</Form.Label>
<Control id="edit-startDate"> <Input {...props} type="date" bind:value={$editData.startDate} />
{#snippet children({ props })} {/snippet}
<Field.Label>Start date</Field.Label> </Form.Control>
<Input {...props} type="date" bind:value={$editData.startDate} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={editForm} name="endDate">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="edit-endDate">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>End date</Form.Label>
<FormField form={editForm} name="endDate"> <Input {...props} type="date" bind:value={$editData.endDate} />
<Field.Field> {/snippet}
<Control id="edit-endDate"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>End date</Field.Label> </Form.Field>
<Input {...props} type="date" bind:value={$editData.endDate} /> <Form.Field form={editForm} name="status">
{/snippet} <Form.Control id="edit-status">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Status</Form.Label>
</Field.Field> <NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
</FormField> <NativeSelect.Option value="">Not set</NativeSelect.Option>
<FormField form={editForm} name="status"> <NativeSelect.Option value="draft">Draft</NativeSelect.Option>
<Field.Field> <NativeSelect.Option value="active">Active</NativeSelect.Option>
<Control id="edit-status"> <NativeSelect.Option value="expired">Expired</NativeSelect.Option>
{#snippet children({ props })} <NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option>
<Field.Label>Status</Field.Label> </NativeSelect.Root>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}> {/snippet}
<NativeSelect.Option value="">Not set</NativeSelect.Option> </Form.Control>
<NativeSelect.Option value="draft">Draft</NativeSelect.Option> <Form.FieldErrors />
<NativeSelect.Option value="active">Active</NativeSelect.Option> </Form.Field>
<NativeSelect.Option value="expired">Expired</NativeSelect.Option>
<NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<MoneyField <MoneyField
form={editForm} form={editForm}
data={editData} data={editData}
@@ -89,17 +80,15 @@
label="Value" label="Value"
id="edit-valueGbp" id="edit-valueGbp"
/> />
<FormField form={editForm} name="notes"> <Form.Field form={editForm} name="notes">
<Field.Field> <Form.Control id="edit-notes">
<Control id="edit-notes"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Notes</Form.Label>
<Field.Label>Notes</Field.Label> <Textarea {...props} bind:value={$editData.notes} />
<Textarea {...props} bind:value={$editData.notes} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,9 +1,8 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus'; import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import MoneyField from '$lib/components/money-field.svelte'; import MoneyField from '$lib/components/money-field.svelte';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -34,57 +33,49 @@
<Dialog.Description>Add a new invoice record.</Dialog.Description> <Dialog.Description>Add a new invoice record.</Dialog.Description>
</Dialog.Header> </Dialog.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate> <form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="invoiceNumber"> <Form.Field form={createForm} name="invoiceNumber">
<Field.Field> <Form.Control id="create-invoiceNumber">
<Control id="create-invoiceNumber"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Invoice number</Form.Label>
<Field.Label>Invoice number</Field.Label> <Input {...props} type="text" bind:value={$createData.invoiceNumber} />
<Input {...props} type="text" bind:value={$createData.invoiceNumber} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={createForm} name="issueDate">
</FormField> <Form.Control id="create-issueDate">
<FormField form={createForm} name="issueDate"> {#snippet children({ props })}
<Field.Field> <Form.Label>Issue date</Form.Label>
<Control id="create-issueDate"> <Input {...props} type="date" bind:value={$createData.issueDate} />
{#snippet children({ props })} {/snippet}
<Field.Label>Issue date</Field.Label> </Form.Control>
<Input {...props} type="date" bind:value={$createData.issueDate} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={createForm} name="dueDate">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="create-dueDate">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Due date</Form.Label>
<FormField form={createForm} name="dueDate"> <Input {...props} type="date" bind:value={$createData.dueDate} />
<Field.Field> {/snippet}
<Control id="create-dueDate"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Due date</Field.Label> </Form.Field>
<Input {...props} type="date" bind:value={$createData.dueDate} /> <Form.Field form={createForm} name="status">
{/snippet} <Form.Control id="create-status">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Status</Form.Label>
</Field.Field> <NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}>
</FormField> <NativeSelect.Option value="">Not set</NativeSelect.Option>
<FormField form={createForm} name="status"> <NativeSelect.Option value="draft">Draft</NativeSelect.Option>
<Field.Field> <NativeSelect.Option value="sent">Sent</NativeSelect.Option>
<Control id="create-status"> <NativeSelect.Option value="paid">Paid</NativeSelect.Option>
{#snippet children({ props })} <NativeSelect.Option value="overdue">Overdue</NativeSelect.Option>
<Field.Label>Status</Field.Label> <NativeSelect.Option value="void">Void</NativeSelect.Option>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}> </NativeSelect.Root>
<NativeSelect.Option value="">Not set</NativeSelect.Option> {/snippet}
<NativeSelect.Option value="draft">Draft</NativeSelect.Option> </Form.Control>
<NativeSelect.Option value="sent">Sent</NativeSelect.Option> <Form.FieldErrors />
<NativeSelect.Option value="paid">Paid</NativeSelect.Option> </Form.Field>
<NativeSelect.Option value="overdue">Overdue</NativeSelect.Option>
<NativeSelect.Option value="void">Void</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<MoneyField <MoneyField
form={createForm} form={createForm}
data={createData} data={createData}
@@ -106,17 +97,15 @@
label="Total" label="Total"
id="create-totalGbp" id="create-totalGbp"
/> />
<FormField form={createForm} name="notes"> <Form.Field form={createForm} name="notes">
<Field.Field> <Form.Control id="create-notes">
<Control id="create-notes"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Notes</Form.Label>
<Field.Label>Notes</Field.Label> <Textarea {...props} bind:value={$createData.notes} />
<Textarea {...props} bind:value={$createData.notes} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import MoneyField from '$lib/components/money-field.svelte'; import MoneyField from '$lib/components/money-field.svelte';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -32,57 +31,49 @@
{#if editingRecord} {#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit> <form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} /> <input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="invoiceNumber"> <Form.Field form={editForm} name="invoiceNumber">
<Field.Field> <Form.Control id="edit-invoiceNumber">
<Control id="edit-invoiceNumber"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Invoice number</Form.Label>
<Field.Label>Invoice number</Field.Label> <Input {...props} type="text" bind:value={$editData.invoiceNumber} />
<Input {...props} type="text" bind:value={$editData.invoiceNumber} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={editForm} name="issueDate">
</FormField> <Form.Control id="edit-issueDate">
<FormField form={editForm} name="issueDate"> {#snippet children({ props })}
<Field.Field> <Form.Label>Issue date</Form.Label>
<Control id="edit-issueDate"> <Input {...props} type="date" bind:value={$editData.issueDate} />
{#snippet children({ props })} {/snippet}
<Field.Label>Issue date</Field.Label> </Form.Control>
<Input {...props} type="date" bind:value={$editData.issueDate} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={editForm} name="dueDate">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="edit-dueDate">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Due date</Form.Label>
<FormField form={editForm} name="dueDate"> <Input {...props} type="date" bind:value={$editData.dueDate} />
<Field.Field> {/snippet}
<Control id="edit-dueDate"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Due date</Field.Label> </Form.Field>
<Input {...props} type="date" bind:value={$editData.dueDate} /> <Form.Field form={editForm} name="status">
{/snippet} <Form.Control id="edit-status">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Status</Form.Label>
</Field.Field> <NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
</FormField> <NativeSelect.Option value="">Not set</NativeSelect.Option>
<FormField form={editForm} name="status"> <NativeSelect.Option value="draft">Draft</NativeSelect.Option>
<Field.Field> <NativeSelect.Option value="sent">Sent</NativeSelect.Option>
<Control id="edit-status"> <NativeSelect.Option value="paid">Paid</NativeSelect.Option>
{#snippet children({ props })} <NativeSelect.Option value="overdue">Overdue</NativeSelect.Option>
<Field.Label>Status</Field.Label> <NativeSelect.Option value="void">Void</NativeSelect.Option>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}> </NativeSelect.Root>
<NativeSelect.Option value="">Not set</NativeSelect.Option> {/snippet}
<NativeSelect.Option value="draft">Draft</NativeSelect.Option> </Form.Control>
<NativeSelect.Option value="sent">Sent</NativeSelect.Option> <Form.FieldErrors />
<NativeSelect.Option value="paid">Paid</NativeSelect.Option> </Form.Field>
<NativeSelect.Option value="overdue">Overdue</NativeSelect.Option>
<NativeSelect.Option value="void">Void</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<MoneyField <MoneyField
form={editForm} form={editForm}
data={editData} data={editData}
@@ -98,17 +89,15 @@
label="Total" label="Total"
id="edit-totalGbp" id="edit-totalGbp"
/> />
<FormField form={editForm} name="notes"> <Form.Field form={editForm} name="notes">
<Field.Field> <Form.Control id="edit-notes">
<Control id="edit-notes"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Notes</Form.Label>
<Field.Label>Notes</Field.Label> <Textarea {...props} bind:value={$editData.notes} />
<Textarea {...props} bind:value={$editData.notes} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus'; import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -59,215 +58,181 @@
</Stepper.Root> </Stepper.Root>
<div class={['grid gap-2', createStep !== 0 && 'hidden']}> <div class={['grid gap-2', createStep !== 0 && 'hidden']}>
<FormField form={createForm} name="name"> <Form.Field form={createForm} name="name">
<Field.Field> <Form.Control id="create-name">
<Control id="create-name"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input {...props} type="text" bind:value={$createData.name} />
<Input {...props} type="text" bind:value={$createData.name} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={createForm} name="type">
</FormField> <Form.Control id="create-type">
<FormField form={createForm} name="type"> {#snippet children({ props })}
<Field.Field> <Form.Label>Type</Form.Label>
<Control id="create-type"> <NativeSelect.Root {...props} class="w-full" bind:value={$createData.type}>
{#snippet children({ props })} <NativeSelect.Option value="">Not set</NativeSelect.Option>
<Field.Label>Type</Field.Label> <NativeSelect.Option value="business">Business</NativeSelect.Option>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.type}> <NativeSelect.Option value="sport">Sport</NativeSelect.Option>
<NativeSelect.Option value="">Not set</NativeSelect.Option> <NativeSelect.Option value="individual">Individual</NativeSelect.Option>
<NativeSelect.Option value="business">Business</NativeSelect.Option> </NativeSelect.Root>
<NativeSelect.Option value="sport">Sport</NativeSelect.Option> {/snippet}
<NativeSelect.Option value="individual">Individual</NativeSelect.Option> </Form.Control>
</NativeSelect.Root> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={createForm} name="status">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="create-status">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Status</Form.Label>
<FormField form={createForm} name="status"> <NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}>
<Field.Field> <NativeSelect.Option value="">Not set</NativeSelect.Option>
<Control id="create-status"> <NativeSelect.Option value="active">Active</NativeSelect.Option>
{#snippet children({ props })} <NativeSelect.Option value="prospect">Prospect</NativeSelect.Option>
<Field.Label>Status</Field.Label> <NativeSelect.Option value="paused">Paused</NativeSelect.Option>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}> </NativeSelect.Root>
<NativeSelect.Option value="">Not set</NativeSelect.Option> {/snippet}
<NativeSelect.Option value="active">Active</NativeSelect.Option> </Form.Control>
<NativeSelect.Option value="prospect">Prospect</NativeSelect.Option> <Form.FieldErrors />
<NativeSelect.Option value="paused">Paused</NativeSelect.Option> </Form.Field>
</NativeSelect.Root> <Form.Field form={createForm} name="website">
{/snippet} <Form.Control id="create-website">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Website</Form.Label>
</Field.Field> <Input
</FormField> {...props}
<FormField form={createForm} name="website"> type="url"
<Field.Field> placeholder="https://example.com"
<Control id="create-website"> bind:value={$createData.website}
{#snippet children({ props })} />
<Field.Label>Website</Field.Label> {/snippet}
<Input </Form.Control>
{...props} <Form.FieldErrors />
type="url" </Form.Field>
placeholder="https://example.com" <Form.Field form={createForm} name="notes">
bind:value={$createData.website} <Form.Control id="create-notes">
/> {#snippet children({ props })}
{/snippet} <Form.Label>Notes</Form.Label>
</Control> <Textarea {...props} bind:value={$createData.notes} />
<Field.Error><FieldErrors /></Field.Error> {/snippet}
</Field.Field> </Form.Control>
</FormField> <Form.FieldErrors />
<FormField form={createForm} name="notes"> </Form.Field>
<Field.Field>
<Control id="create-notes">
{#snippet children({ props })}
<Field.Label>Notes</Field.Label>
<Textarea {...props} bind:value={$createData.notes} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
</div> </div>
<div class={['grid gap-2', createStep !== 1 && 'hidden']}> <div class={['grid gap-2', createStep !== 1 && 'hidden']}>
<FormField form={createForm} name="primaryContactName"> <Form.Field form={createForm} name="primaryContactName">
<Field.Field> <Form.Control id="create-primary-contact-name">
<Control id="create-primary-contact-name"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input {...props} type="text" bind:value={$createData.primaryContactName} />
<Input {...props} type="text" bind:value={$createData.primaryContactName} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={createForm} name="primaryContactRole">
</FormField> <Form.Control id="create-primary-contact-role">
<FormField form={createForm} name="primaryContactRole"> {#snippet children({ props })}
<Field.Field> <Form.Label>Role</Form.Label>
<Control id="create-primary-contact-role"> <Input {...props} type="text" bind:value={$createData.primaryContactRole} />
{#snippet children({ props })} {/snippet}
<Field.Label>Role</Field.Label> </Form.Control>
<Input {...props} type="text" bind:value={$createData.primaryContactRole} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={createForm} name="primaryContactEmail">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="create-primary-contact-email">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Email</Form.Label>
<FormField form={createForm} name="primaryContactEmail"> <Input {...props} type="email" bind:value={$createData.primaryContactEmail} />
<Field.Field> {/snippet}
<Control id="create-primary-contact-email"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Email</Field.Label> </Form.Field>
<Input {...props} type="email" bind:value={$createData.primaryContactEmail} /> <Form.Field form={createForm} name="primaryContactPhone">
{/snippet} <Form.Control id="create-primary-contact-phone">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Phone</Form.Label>
</Field.Field> <Input {...props} type="text" bind:value={$createData.primaryContactPhone} />
</FormField> {/snippet}
<FormField form={createForm} name="primaryContactPhone"> </Form.Control>
<Field.Field> <Form.FieldErrors />
<Control id="create-primary-contact-phone"> </Form.Field>
{#snippet children({ props })} <Form.Field form={createForm} name="primaryContactNotes">
<Field.Label>Phone</Field.Label> <Form.Control id="create-primary-contact-notes">
<Input {...props} type="text" bind:value={$createData.primaryContactPhone} /> {#snippet children({ props })}
{/snippet} <Form.Label>Notes</Form.Label>
</Control> <Textarea {...props} bind:value={$createData.primaryContactNotes} />
<Field.Error><FieldErrors /></Field.Error> {/snippet}
</Field.Field> </Form.Control>
</FormField> <Form.FieldErrors />
<FormField form={createForm} name="primaryContactNotes"> </Form.Field>
<Field.Field>
<Control id="create-primary-contact-notes">
{#snippet children({ props })}
<Field.Label>Notes</Field.Label>
<Textarea {...props} bind:value={$createData.primaryContactNotes} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
</div> </div>
<div class={['grid gap-2', createStep !== 2 && 'hidden']}> <div class={['grid gap-2', createStep !== 2 && 'hidden']}>
<FormField form={createForm} name="primaryAddressLabel"> <Form.Field form={createForm} name="primaryAddressLabel">
<Field.Field> <Form.Control id="create-primary-address-label">
<Control id="create-primary-address-label"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Label</Form.Label>
<Field.Label>Label</Field.Label> <Input {...props} type="text" bind:value={$createData.primaryAddressLabel} />
<Input {...props} type="text" bind:value={$createData.primaryAddressLabel} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={createForm} name="primaryAddressLine1">
</FormField> <Form.Control id="create-primary-address-line-1">
<FormField form={createForm} name="primaryAddressLine1"> {#snippet children({ props })}
<Field.Field> <Form.Label>Address line 1</Form.Label>
<Control id="create-primary-address-line-1"> <Input {...props} type="text" bind:value={$createData.primaryAddressLine1} />
{#snippet children({ props })} {/snippet}
<Field.Label>Address line 1</Field.Label> </Form.Control>
<Input {...props} type="text" bind:value={$createData.primaryAddressLine1} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={createForm} name="primaryAddressLine2">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="create-primary-address-line-2">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Address line 2</Form.Label>
<FormField form={createForm} name="primaryAddressLine2"> <Input {...props} type="text" bind:value={$createData.primaryAddressLine2} />
<Field.Field> {/snippet}
<Control id="create-primary-address-line-2"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Address line 2</Field.Label> </Form.Field>
<Input {...props} type="text" bind:value={$createData.primaryAddressLine2} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<div class="grid gap-2 sm:grid-cols-2"> <div class="grid gap-2 sm:grid-cols-2">
<FormField form={createForm} name="primaryAddressCity"> <Form.Field form={createForm} name="primaryAddressCity">
<Field.Field> <Form.Control id="create-primary-address-city">
<Control id="create-primary-address-city"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>City</Form.Label>
<Field.Label>City</Field.Label> <Input {...props} type="text" bind:value={$createData.primaryAddressCity} />
<Input {...props} type="text" bind:value={$createData.primaryAddressCity} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={createForm} name="primaryAddressRegion">
</FormField> <Form.Control id="create-primary-address-region">
<FormField form={createForm} name="primaryAddressRegion"> {#snippet children({ props })}
<Field.Field> <Form.Label>Region</Form.Label>
<Control id="create-primary-address-region"> <Input {...props} type="text" bind:value={$createData.primaryAddressRegion} />
{#snippet children({ props })} {/snippet}
<Field.Label>Region</Field.Label> </Form.Control>
<Input {...props} type="text" bind:value={$createData.primaryAddressRegion} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
</div> </div>
<div class="grid gap-2 sm:grid-cols-2"> <div class="grid gap-2 sm:grid-cols-2">
<FormField form={createForm} name="primaryAddressPostcode"> <Form.Field form={createForm} name="primaryAddressPostcode">
<Field.Field> <Form.Control id="create-primary-address-postcode">
<Control id="create-primary-address-postcode"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Postcode</Form.Label>
<Field.Label>Postcode</Field.Label> <Input {...props} type="text" bind:value={$createData.primaryAddressPostcode} />
<Input {...props} type="text" bind:value={$createData.primaryAddressPostcode} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={createForm} name="primaryAddressCountry">
</FormField> <Form.Control id="create-primary-address-country">
<FormField form={createForm} name="primaryAddressCountry"> {#snippet children({ props })}
<Field.Field> <Form.Label>Country</Form.Label>
<Control id="create-primary-address-country"> <Input {...props} type="text" bind:value={$createData.primaryAddressCountry} />
{#snippet children({ props })} {/snippet}
<Field.Label>Country</Field.Label> </Form.Control>
<Input {...props} type="text" bind:value={$createData.primaryAddressCountry} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
</div> </div>
</div> </div>
@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -31,76 +30,66 @@
{#if editingRecord} {#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit> <form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} /> <input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="name"> <Form.Field form={editForm} name="name">
<Field.Field> <Form.Control id="edit-name">
<Control id="edit-name"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input {...props} type="text" bind:value={$editData.name} />
<Input {...props} type="text" bind:value={$editData.name} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={editForm} name="type">
</FormField> <Form.Control id="edit-type">
<FormField form={editForm} name="type"> {#snippet children({ props })}
<Field.Field> <Form.Label>Type</Form.Label>
<Control id="edit-type"> <NativeSelect.Root {...props} class="w-full" bind:value={$editData.type}>
{#snippet children({ props })} <NativeSelect.Option value="">Not set</NativeSelect.Option>
<Field.Label>Type</Field.Label> <NativeSelect.Option value="business">Business</NativeSelect.Option>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.type}> <NativeSelect.Option value="sport">Sport</NativeSelect.Option>
<NativeSelect.Option value="">Not set</NativeSelect.Option> <NativeSelect.Option value="individual">Individual</NativeSelect.Option>
<NativeSelect.Option value="business">Business</NativeSelect.Option> </NativeSelect.Root>
<NativeSelect.Option value="sport">Sport</NativeSelect.Option> {/snippet}
<NativeSelect.Option value="individual">Individual</NativeSelect.Option> </Form.Control>
</NativeSelect.Root> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={editForm} name="status">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="edit-status">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Status</Form.Label>
<FormField form={editForm} name="status"> <NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<Field.Field> <NativeSelect.Option value="">Not set</NativeSelect.Option>
<Control id="edit-status"> <NativeSelect.Option value="active">Active</NativeSelect.Option>
{#snippet children({ props })} <NativeSelect.Option value="prospect">Prospect</NativeSelect.Option>
<Field.Label>Status</Field.Label> <NativeSelect.Option value="paused">Paused</NativeSelect.Option>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}> </NativeSelect.Root>
<NativeSelect.Option value="">Not set</NativeSelect.Option> {/snippet}
<NativeSelect.Option value="active">Active</NativeSelect.Option> </Form.Control>
<NativeSelect.Option value="prospect">Prospect</NativeSelect.Option> <Form.FieldErrors />
<NativeSelect.Option value="paused">Paused</NativeSelect.Option> </Form.Field>
</NativeSelect.Root> <Form.Field form={editForm} name="website">
{/snippet} <Form.Control id="edit-website">
</Control> {#snippet children({ props })}
<Field.Error><FieldErrors /></Field.Error> <Form.Label>Website</Form.Label>
</Field.Field> <Input
</FormField> {...props}
<FormField form={editForm} name="website"> type="url"
<Field.Field> placeholder="https://example.com"
<Control id="edit-website"> bind:value={$editData.website}
{#snippet children({ props })} />
<Field.Label>Website</Field.Label> {/snippet}
<Input </Form.Control>
{...props} <Form.FieldErrors />
type="url" </Form.Field>
placeholder="https://example.com" <Form.Field form={editForm} name="notes">
bind:value={$editData.website} <Form.Control id="edit-notes">
/> {#snippet children({ props })}
{/snippet} <Form.Label>Notes</Form.Label>
</Control> <Textarea {...props} bind:value={$editData.notes} />
<Field.Error><FieldErrors /></Field.Error> {/snippet}
</Field.Field> </Form.Control>
</FormField> <Form.FieldErrors />
<FormField form={editForm} name="notes"> </Form.Field>
<Field.Field>
<Control id="edit-notes">
{#snippet children({ props })}
<Field.Label>Notes</Field.Label>
<Textarea {...props} bind:value={$editData.notes} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus'; import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -32,17 +31,15 @@
<Dialog.Description>Add a new room record.</Dialog.Description> <Dialog.Description>Add a new room record.</Dialog.Description>
</Dialog.Header> </Dialog.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate> <form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="name"> <Form.Field form={createForm} name="name">
<Field.Field> <Form.Control id="create-name">
<Control id="create-name"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input {...props} type="text" bind:value={$createData.name} />
<Input {...props} type="text" bind:value={$createData.name} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field>
</FormField>
<RoomTypeFields form={createForm} data={createData} idPrefix="create" /> <RoomTypeFields form={createForm} data={createData} idPrefix="create" />
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -30,17 +29,15 @@
{#if editingRecord} {#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit> <form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} /> <input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="name"> <Form.Field form={editForm} name="name">
<Field.Field> <Form.Control id="edit-name">
<Control id="edit-name"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input {...props} type="text" bind:value={$editData.name} />
<Input {...props} type="text" bind:value={$editData.name} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field>
</FormField>
<RoomTypeFields form={editForm} data={editData} idPrefix="edit" /> <RoomTypeFields form={editForm} data={editData} idPrefix="edit" />
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
@@ -1,8 +1,7 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import MoneyField from '$lib/components/money-field.svelte'; import MoneyField from '$lib/components/money-field.svelte';
import * as Field from '$lib/components/ui/field/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
import * as Tabs from '$lib/components/ui/tabs/index.js'; import * as Tabs from '$lib/components/ui/tabs/index.js';
@@ -33,65 +32,36 @@
</script> </script>
{#snippet numberField(name: string, label: string, min = 0)} {#snippet numberField(name: string, label: string, min = 0)}
<FormField {form} {name}> <Form.Field {form} {name}>
<Field.Field> <Form.Control id={`${idPrefix}-${name}`}>
<Control id={`${idPrefix}-${name}`}> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>{label}</Form.Label>
<Field.Label>{label}</Field.Label> <Input {...props} type="number" {min} bind:value={$data[name]} />
<Input {...props} type="number" {min} bind:value={$data[name]} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field>
</FormField>
{/snippet} {/snippet}
<FormField {form} name="type"> <Form.Field {form} name="type">
<Field.Field> <Form.Control id={`${idPrefix}-type`}>
<Control id={`${idPrefix}-type`}> {#snippet children({ props })}
{#snippet children({ props })} <input {...props} type="hidden" bind:value={$data.type} />
<input {...props} type="hidden" bind:value={$data.type} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.Label>Type</Form.Label>
<Field.Label>Type</Field.Label> <Tabs.Root bind:value={$data.type} class="gap-3">
<Tabs.Root bind:value={$data.type} class="gap-3"> <Tabs.List class="grid h-9 w-full grid-cols-3">
<Tabs.List class="grid h-9 w-full grid-cols-3"> {#each roomTypes as roomType (roomType.value)}
{#each roomTypes as roomType (roomType.value)} <Tabs.Trigger value={roomType.value} class="h-full px-2 text-center">
<Tabs.Trigger value={roomType.value} class="h-full px-2 text-center"> {roomType.label}
{roomType.label} </Tabs.Trigger>
</Tabs.Trigger> {/each}
{/each} </Tabs.List>
</Tabs.List>
<Tabs.Content value="meeting_room" class="grid gap-2"> <Tabs.Content value="meeting_room" class="grid gap-2">
{@render numberField('sqFt', 'Sq ft.', 1)} {@render numberField('sqFt', 'Sq ft.', 1)}
<div class="grid gap-2 sm:grid-cols-2"> <div class="grid gap-2 sm:grid-cols-2">
{#each bookablePriceFields as field (field.name)}
<MoneyField
{form}
{data}
name={field.name}
label={field.label}
id={`${idPrefix}-${field.name}`}
/>
{/each}
</div>
{@render numberField('maxAttendees', 'Max attendees', 1)}
</Tabs.Content>
<Tabs.Content value="private_office" class="grid gap-2">
{@render numberField('sqFt', 'Sq ft.', 1)}
{@render numberField('workstations', 'Number of workstations', 1)}
<MoneyField
{form}
{data}
name="pricePerMonthGbp"
label="Price per month"
id={`${idPrefix}-pricePerMonthGbp`}
/>
</Tabs.Content>
<Tabs.Content value="coworking_desk" class="grid gap-2 sm:grid-cols-2">
{#each bookablePriceFields as field (field.name)} {#each bookablePriceFields as field (field.name)}
<MoneyField <MoneyField
{form} {form}
@@ -101,8 +71,33 @@
id={`${idPrefix}-${field.name}`} id={`${idPrefix}-${field.name}`}
/> />
{/each} {/each}
</Tabs.Content> </div>
</Tabs.Root> {@render numberField('maxAttendees', 'Max attendees', 1)}
<Field.Error><FieldErrors /></Field.Error> </Tabs.Content>
</Field.Field>
</FormField> <Tabs.Content value="private_office" class="grid gap-2">
{@render numberField('sqFt', 'Sq ft.', 1)}
{@render numberField('workstations', 'Number of workstations', 1)}
<MoneyField
{form}
{data}
name="pricePerMonthGbp"
label="Price per month"
id={`${idPrefix}-pricePerMonthGbp`}
/>
</Tabs.Content>
<Tabs.Content value="coworking_desk" class="grid gap-2 sm:grid-cols-2">
{#each bookablePriceFields as field (field.name)}
<MoneyField
{form}
{data}
name={field.name}
label={field.label}
id={`${idPrefix}-${field.name}`}
/>
{/each}
</Tabs.Content>
</Tabs.Root>
<Form.FieldErrors />
</Form.Field>
@@ -1,9 +1,8 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus'; import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import MoneyField from '$lib/components/money-field.svelte'; import MoneyField from '$lib/components/money-field.svelte';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -33,39 +32,33 @@
<Dialog.Description>Add a new service record.</Dialog.Description> <Dialog.Description>Add a new service record.</Dialog.Description>
</Dialog.Header> </Dialog.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate> <form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="name"> <Form.Field form={createForm} name="name">
<Field.Field> <Form.Control id="create-name">
<Control id="create-name"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input {...props} type="text" bind:value={$createData.name} />
<Input {...props} type="text" bind:value={$createData.name} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={createForm} name="category">
</FormField> <Form.Control id="create-category">
<FormField form={createForm} name="category"> {#snippet children({ props })}
<Field.Field> <Form.Label>Category</Form.Label>
<Control id="create-category"> <Input {...props} type="text" bind:value={$createData.category} />
{#snippet children({ props })} {/snippet}
<Field.Label>Category</Field.Label> </Form.Control>
<Input {...props} type="text" bind:value={$createData.category} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={createForm} name="unit">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="create-unit">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Unit</Form.Label>
<FormField form={createForm} name="unit"> <Input {...props} type="text" bind:value={$createData.unit} />
<Field.Field> {/snippet}
<Control id="create-unit"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Unit</Field.Label> </Form.Field>
<Input {...props} type="text" bind:value={$createData.unit} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<MoneyField <MoneyField
form={createForm} form={createForm}
data={createData} data={createData}
@@ -73,17 +66,15 @@
label="Price" label="Price"
id="create-priceGbp" id="create-priceGbp"
/> />
<FormField form={createForm} name="description"> <Form.Field form={createForm} name="description">
<Field.Field> <Form.Control id="create-description">
<Control id="create-description"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Description</Form.Label>
<Field.Label>Description</Field.Label> <Textarea {...props} bind:value={$createData.description} />
<Textarea {...props} bind:value={$createData.description} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts"> <script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap'; import * as Form from '$lib/components/ui/form/index.js';
import MoneyField from '$lib/components/money-field.svelte'; import MoneyField from '$lib/components/money-field.svelte';
import * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js'; import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js'; import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js'; import { Input } from '$lib/components/ui/input/index.js';
@@ -31,39 +30,33 @@
{#if editingRecord} {#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit> <form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} /> <input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="name"> <Form.Field form={editForm} name="name">
<Field.Field> <Form.Control id="edit-name">
<Control id="edit-name"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Name</Form.Label>
<Field.Label>Name</Field.Label> <Input {...props} type="text" bind:value={$editData.name} />
<Input {...props} type="text" bind:value={$editData.name} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field> <Form.Field form={editForm} name="category">
</FormField> <Form.Control id="edit-category">
<FormField form={editForm} name="category"> {#snippet children({ props })}
<Field.Field> <Form.Label>Category</Form.Label>
<Control id="edit-category"> <Input {...props} type="text" bind:value={$editData.category} />
{#snippet children({ props })} {/snippet}
<Field.Label>Category</Field.Label> </Form.Control>
<Input {...props} type="text" bind:value={$editData.category} /> <Form.FieldErrors />
{/snippet} </Form.Field>
</Control> <Form.Field form={editForm} name="unit">
<Field.Error><FieldErrors /></Field.Error> <Form.Control id="edit-unit">
</Field.Field> {#snippet children({ props })}
</FormField> <Form.Label>Unit</Form.Label>
<FormField form={editForm} name="unit"> <Input {...props} type="text" bind:value={$editData.unit} />
<Field.Field> {/snippet}
<Control id="edit-unit"> </Form.Control>
{#snippet children({ props })} <Form.FieldErrors />
<Field.Label>Unit</Field.Label> </Form.Field>
<Input {...props} type="text" bind:value={$editData.unit} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<MoneyField <MoneyField
form={editForm} form={editForm}
data={editData} data={editData}
@@ -71,17 +64,15 @@
label="Price" label="Price"
id="edit-priceGbp" id="edit-priceGbp"
/> />
<FormField form={editForm} name="description"> <Form.Field form={editForm} name="description">
<Field.Field> <Form.Control id="edit-description">
<Control id="edit-description"> {#snippet children({ props })}
{#snippet children({ props })} <Form.Label>Description</Form.Label>
<Field.Label>Description</Field.Label> <Textarea {...props} bind:value={$editData.description} />
<Textarea {...props} bind:value={$editData.description} /> {/snippet}
{/snippet} </Form.Control>
</Control> <Form.FieldErrors />
<Field.Error><FieldErrors /></Field.Error> </Form.Field>
</Field.Field>
</FormField>
<Dialog.Footer> <Dialog.Footer>
<Dialog.Close <Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button >{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button