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">
import GalleryVerticalEndIcon from '@lucide/svelte/icons/gallery-vertical-end';
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 { superForm, type SuperValidated } from 'sveltekit-superforms';
import { zod4Client } from 'sveltekit-superforms/adapters';
@@ -9,6 +9,7 @@
import { Button } from '$lib/components/ui/button/index.js';
import { Input } from '$lib/components/ui/input/index.js';
import { cn, type WithElementRef } from '$lib/utils.js';
import { firstFormError } from '$lib/form-feedback';
import { firstAdminSchema, type FirstAdminSchema } from '$lib/schemas/auth';
let {
@@ -27,7 +28,7 @@
resetForm: false,
onUpdated: ({ form }) => {
if (!form.valid) {
toast.error(form.message ?? firstError(form.errors), {
toast.error(form.message ?? firstFormError(form.errors), {
id: 'first-admin-error',
position: 'top-center'
});
@@ -42,16 +43,6 @@
});
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>
<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.
</Field.Description>
</div>
<FormField form={firstAdminForm} name="name">
<Field.Field>
<Control id="name-{id}">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input
{...props}
autocomplete="name"
placeholder="Administrator"
bind:value={$formData.name}
/>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={firstAdminForm} name="email">
<Field.Field>
<Control id="email-{id}">
{#snippet children({ props })}
<Field.Label>Email</Field.Label>
<Input
{...props}
type="email"
autocomplete="email"
placeholder="admin@example.com"
bind:value={$formData.email}
/>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={firstAdminForm} name="password">
<Field.Field>
<Control id="password-{id}">
{#snippet children({ props })}
<Field.Label>Password</Field.Label>
<Input
{...props}
type="password"
autocomplete="new-password"
bind:value={$formData.password}
/>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Field.Field>
<Button type="submit" class="w-full">Create admin</Button>
</Field.Field>
<Form.Field form={firstAdminForm} name="name">
<Form.Control id="name-{id}">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input
{...props}
autocomplete="name"
placeholder="Administrator"
bind:value={$formData.name}
/>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={firstAdminForm} name="email">
<Form.Control id="email-{id}">
{#snippet children({ props })}
<Form.Label>Email</Form.Label>
<Input
{...props}
type="email"
autocomplete="email"
placeholder="admin@example.com"
bind:value={$formData.email}
/>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={firstAdminForm} name="password">
<Form.Control id="password-{id}">
{#snippet children({ props })}
<Form.Label>Password</Form.Label>
<Input
{...props}
type="password"
autocomplete="new-password"
bind:value={$formData.password}
/>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Button type="submit" class="w-full">Create admin</Button>
</Field.Group>
</form>
<Field.Description class="px-6 text-center">
+42 -55
View File
@@ -1,14 +1,15 @@
<script lang="ts">
import { toast } from 'svelte-sonner';
import GalleryVerticalEndIcon from "@lucide/svelte/icons/gallery-vertical-end";
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import GalleryVerticalEndIcon from '@lucide/svelte/icons/gallery-vertical-end';
import * as Form from '$lib/components/ui/form/index.js';
import * as Field from '$lib/components/ui/field/index.js';
import { superForm, type SuperValidated } from 'sveltekit-superforms';
import { zod4Client } from 'sveltekit-superforms/adapters';
import type { HTMLAttributes } from "svelte/elements";
import { Input } from "$lib/components/ui/input/index.js";
import { Button } from "$lib/components/ui/button/index.js";
import { cn, type WithElementRef } from "$lib/utils.js";
import type { HTMLAttributes } from 'svelte/elements';
import { Input } from '$lib/components/ui/input/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import { cn, type WithElementRef } from '$lib/utils.js';
import { firstFormError } from '$lib/form-feedback';
import { loginSchema, type LoginSchema } from '$lib/schemas/auth';
let {
@@ -27,7 +28,7 @@
resetForm: false,
onUpdated: ({ form }) => {
if (!form.valid) {
toast.error(form.message ?? firstError(form.errors), {
toast.error(form.message ?? firstFormError(form.errors), {
id: 'login-error',
position: 'top-center'
});
@@ -42,24 +43,16 @@
});
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>
<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>
<Field.Group>
<div class="flex flex-col items-center gap-2 text-center">
<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" />
</div>
<span class="sr-only">Clearity</span>
@@ -69,45 +62,39 @@
Use the administrator account created for this workspace.
</Field.Description>
</div>
<FormField form={loginForm} name="email">
<Field.Field>
<Control id="email-{id}">
{#snippet children({ props })}
<Field.Label>Email</Field.Label>
<Input
{...props}
type="email"
autocomplete="email"
placeholder="admin@example.com"
bind:value={$formData.email}
/>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={loginForm} name="password">
<Field.Field>
<Control id="password-{id}">
{#snippet children({ props })}
<Field.Label>Password</Field.Label>
<Input
{...props}
type="password"
autocomplete="current-password"
bind:value={$formData.password}
/>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
<Form.Field form={loginForm} name="email">
<Form.Control id="email-{id}">
{#snippet children({ props })}
<Form.Label>Email</Form.Label>
<Input
{...props}
type="email"
autocomplete="email"
placeholder="admin@example.com"
bind:value={$formData.email}
/>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={loginForm} name="password">
<Form.Control id="password-{id}">
{#snippet children({ props })}
<Form.Label>Password</Form.Label>
<Input
{...props}
type="password"
autocomplete="current-password"
bind:value={$formData.password}
/>
{/snippet}
</Form.Control>
<Form.FieldErrors />
<Field.Description>
New users are created manually by an administrator; public registration is disabled.
</Field.Description>
</FormField>
<Field.Field>
<Button type="submit" class="w-full">Sign in</Button>
</Field.Field>
</Form.Field>
<Button type="submit" class="w-full">Sign in</Button>
</Field.Group>
</form>
<Field.Description class="px-6 text-center">
+23 -26
View File
@@ -1,7 +1,6 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import * as InputGroup from '$lib/components/ui/input-group/index.js';
let {
@@ -19,27 +18,25 @@
} = $props();
</script>
<FormField {form} {name}>
<Field.Field>
<Control {id}>
{#snippet children({ props })}
<Field.Label>{label}</Field.Label>
<InputGroup.Root>
<InputGroup.Addon>
<InputGroup.Text>£</InputGroup.Text>
</InputGroup.Addon>
<InputGroup.Input
{...props}
type="number"
min="0"
step="0.01"
inputmode="decimal"
placeholder="0.00"
bind:value={$data[name]}
/>
</InputGroup.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Form.Field {form} {name}>
<Form.Control {id}>
{#snippet children({ props })}
<Form.Label>{label}</Form.Label>
<InputGroup.Root>
<InputGroup.Addon>
<InputGroup.Text>£</InputGroup.Text>
</InputGroup.Addon>
<InputGroup.Input
{...props}
type="number"
min="0"
step="0.01"
inputmode="decimal"
placeholder="0.00"
bind:value={$data[name]}
/>
</InputGroup.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
+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">
/* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/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.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="name">
<Field.Field>
<Control id="create-name">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input {...props} autocomplete="name" bind:value={$createData.name} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="email">
<Field.Field>
<Control id="create-email">
{#snippet children({ props })}
<Field.Label>Email</Field.Label>
<Input {...props} type="email" autocomplete="email" bind:value={$createData.email} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="password">
<Field.Field>
<Control id="create-password">
{#snippet children({ props })}
<Field.Label>Password</Field.Label>
<Input
{...props}
type="password"
autocomplete="new-password"
bind:value={$createData.password}
/>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="role">
<Field.Field>
<Control id="create-role">
{#snippet children({ props })}
<Field.Label>Role</Field.Label>
<NativeSelect.Root {...props} bind:value={$createData.role} class="w-full">
<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>
<Form.Field form={createForm} name="name">
<Form.Control id="create-name">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input {...props} autocomplete="name" bind:value={$createData.name} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="email">
<Form.Control id="create-email">
{#snippet children({ props })}
<Form.Label>Email</Form.Label>
<Input {...props} type="email" autocomplete="email" bind:value={$createData.email} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="password">
<Form.Control id="create-password">
{#snippet children({ props })}
<Form.Label>Password</Form.Label>
<Input
{...props}
type="password"
autocomplete="new-password"
bind:value={$createData.password}
/>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="role">
<Form.Control id="create-role">
{#snippet children({ props })}
<Form.Label>Role</Form.Label>
<NativeSelect.Root {...props} bind:value={$createData.role} class="w-full">
<NativeSelect.Option value="admin">Admin</NativeSelect.Option>
<NativeSelect.Option value="user">User</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close>
{#snippet child({ props })}
@@ -1,7 +1,6 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js';
@@ -30,59 +29,51 @@
{#if editingUser}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="name">
<Field.Field>
<Control id="edit-name">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input {...props} autocomplete="name" bind:value={$editData.name} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="email">
<Field.Field>
<Control id="edit-email">
{#snippet children({ props })}
<Field.Label>Email</Field.Label>
<Input {...props} type="email" autocomplete="email" bind:value={$editData.email} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="password">
<Field.Field>
<Control id="edit-password">
{#snippet children({ props })}
<Field.Label>New password</Field.Label>
<Input
{...props}
type="password"
autocomplete="new-password"
placeholder="Leave blank to keep current password"
bind:value={$editData.password}
/>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="role">
<Field.Field>
<Control id="edit-role">
{#snippet children({ props })}
<Field.Label>Role</Field.Label>
<NativeSelect.Root {...props} bind:value={$editData.role} class="w-full">
<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>
<Form.Field form={editForm} name="name">
<Form.Control id="edit-name">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input {...props} autocomplete="name" bind:value={$editData.name} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="email">
<Form.Control id="edit-email">
{#snippet children({ props })}
<Form.Label>Email</Form.Label>
<Input {...props} type="email" autocomplete="email" bind:value={$editData.email} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="password">
<Form.Control id="edit-password">
{#snippet children({ props })}
<Form.Label>New password</Form.Label>
<Input
{...props}
type="password"
autocomplete="new-password"
placeholder="Leave blank to keep current password"
bind:value={$editData.password}
/>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="role">
<Form.Control id="edit-role">
{#snippet children({ props })}
<Form.Label>Role</Form.Label>
<NativeSelect.Root {...props} bind:value={$editData.role} class="w-full">
<NativeSelect.Option value="admin">Admin</NativeSelect.Option>
<NativeSelect.Option value="user">User</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close>
{#snippet child({ props })}
@@ -2,12 +2,12 @@
import { goto } from '$app/navigation';
import { resolve } from '$app/paths';
import { page } from '$app/state';
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { toast } from 'svelte-sonner';
import { superForm } from 'sveltekit-superforms';
import { zod4Client } from 'sveltekit-superforms/adapters';
import { clientEditSchema } from '$lib/schemas/clients.schema';
import { handleFormToast } from '$lib/form-feedback';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js';
@@ -50,28 +50,6 @@
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) {
goto(resolve(`/dashboard/clients/[id]/${tab}`, { id: data.client.id }));
}
@@ -103,76 +81,66 @@
</Dialog.Header>
<form method="POST" action="/dashboard/clients?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="name">
<Field.Field>
<Control id="edit-client-name">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input {...props} type="text" bind:value={$editData.name} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="type">
<Field.Field>
<Control id="edit-client-type">
{#snippet children({ props })}
<Field.Label>Type</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.type}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="business">Business</NativeSelect.Option>
<NativeSelect.Option value="sport">Sport</NativeSelect.Option>
<NativeSelect.Option value="individual">Individual</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="status">
<Field.Field>
<Control id="edit-client-status">
{#snippet children({ props })}
<Field.Label>Status</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="active">Active</NativeSelect.Option>
<NativeSelect.Option value="prospect">Prospect</NativeSelect.Option>
<NativeSelect.Option value="paused">Paused</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="website">
<Field.Field>
<Control id="edit-client-website">
{#snippet children({ props })}
<Field.Label>Website</Field.Label>
<Input
{...props}
type="url"
placeholder="https://example.com"
bind:value={$editData.website}
/>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="notes">
<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>
<Form.Field form={editForm} name="name">
<Form.Control id="edit-client-name">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input {...props} type="text" bind:value={$editData.name} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="type">
<Form.Control id="edit-client-type">
{#snippet children({ props })}
<Form.Label>Type</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.type}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="business">Business</NativeSelect.Option>
<NativeSelect.Option value="sport">Sport</NativeSelect.Option>
<NativeSelect.Option value="individual">Individual</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="status">
<Form.Control id="edit-client-status">
{#snippet children({ props })}
<Form.Label>Status</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="active">Active</NativeSelect.Option>
<NativeSelect.Option value="prospect">Prospect</NativeSelect.Option>
<NativeSelect.Option value="paused">Paused</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="website">
<Form.Control id="edit-client-website">
{#snippet children({ props })}
<Form.Label>Website</Form.Label>
<Input
{...props}
type="url"
placeholder="https://example.com"
bind:value={$editData.website}
/>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="notes">
<Form.Control id="edit-client-notes">
{#snippet children({ props })}
<Form.Label>Notes</Form.Label>
<Textarea {...props} bind:value={$editData.notes} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/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.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="label">
<Field.Field>
<Control id="create-label">
{#snippet children({ props })}
<Field.Label>Label</Field.Label>
<Input {...props} type="text" bind:value={$createData.label} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="line1">
<Field.Field>
<Control id="create-line1">
{#snippet children({ props })}
<Field.Label>Address line 1</Field.Label>
<Input {...props} type="text" bind:value={$createData.line1} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="line2">
<Field.Field>
<Control id="create-line2">
{#snippet children({ props })}
<Field.Label>Address line 2</Field.Label>
<Input {...props} type="text" bind:value={$createData.line2} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="city">
<Field.Field>
<Control id="create-city">
{#snippet children({ props })}
<Field.Label>City</Field.Label>
<Input {...props} type="text" bind:value={$createData.city} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="region">
<Field.Field>
<Control id="create-region">
{#snippet children({ props })}
<Field.Label>Region / county</Field.Label>
<Input {...props} type="text" bind:value={$createData.region} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="postcode">
<Field.Field>
<Control id="create-postcode">
{#snippet children({ props })}
<Field.Label>Postcode</Field.Label>
<Input {...props} type="text" bind:value={$createData.postcode} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="country">
<Field.Field>
<Control id="create-country">
{#snippet children({ props })}
<Field.Label>Country</Field.Label>
<Input {...props} type="text" bind:value={$createData.country} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.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>
<Form.Field form={createForm} name="label">
<Form.Control id="create-label">
{#snippet children({ props })}
<Form.Label>Label</Form.Label>
<Input {...props} type="text" bind:value={$createData.label} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="line1">
<Form.Control id="create-line1">
{#snippet children({ props })}
<Form.Label>Address line 1</Form.Label>
<Input {...props} type="text" bind:value={$createData.line1} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="line2">
<Form.Control id="create-line2">
{#snippet children({ props })}
<Form.Label>Address line 2</Form.Label>
<Input {...props} type="text" bind:value={$createData.line2} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="city">
<Form.Control id="create-city">
{#snippet children({ props })}
<Form.Label>City</Form.Label>
<Input {...props} type="text" bind:value={$createData.city} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="region">
<Form.Control id="create-region">
{#snippet children({ props })}
<Form.Label>Region / county</Form.Label>
<Input {...props} type="text" bind:value={$createData.region} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="postcode">
<Form.Control id="create-postcode">
{#snippet children({ props })}
<Form.Label>Postcode</Form.Label>
<Input {...props} type="text" bind:value={$createData.postcode} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="country">
<Form.Control id="create-country">
{#snippet children({ props })}
<Form.Label>Country</Form.Label>
<Input {...props} type="text" bind:value={$createData.country} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="isPrimary">
<Form.Control id="create-isPrimary">
{#snippet children({ props })}
<Form.Label>Primary address</Form.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}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,7 +1,6 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js';
@@ -30,98 +29,82 @@
{#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="label">
<Field.Field>
<Control id="edit-label">
{#snippet children({ props })}
<Field.Label>Label</Field.Label>
<Input {...props} type="text" bind:value={$editData.label} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="line1">
<Field.Field>
<Control id="edit-line1">
{#snippet children({ props })}
<Field.Label>Address line 1</Field.Label>
<Input {...props} type="text" bind:value={$editData.line1} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="line2">
<Field.Field>
<Control id="edit-line2">
{#snippet children({ props })}
<Field.Label>Address line 2</Field.Label>
<Input {...props} type="text" bind:value={$editData.line2} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="city">
<Field.Field>
<Control id="edit-city">
{#snippet children({ props })}
<Field.Label>City</Field.Label>
<Input {...props} type="text" bind:value={$editData.city} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="region">
<Field.Field>
<Control id="edit-region">
{#snippet children({ props })}
<Field.Label>Region / county</Field.Label>
<Input {...props} type="text" bind:value={$editData.region} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="postcode">
<Field.Field>
<Control id="edit-postcode">
{#snippet children({ props })}
<Field.Label>Postcode</Field.Label>
<Input {...props} type="text" bind:value={$editData.postcode} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="country">
<Field.Field>
<Control id="edit-country">
{#snippet children({ props })}
<Field.Label>Country</Field.Label>
<Input {...props} type="text" bind:value={$editData.country} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.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>
<Form.Field form={editForm} name="label">
<Form.Control id="edit-label">
{#snippet children({ props })}
<Form.Label>Label</Form.Label>
<Input {...props} type="text" bind:value={$editData.label} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="line1">
<Form.Control id="edit-line1">
{#snippet children({ props })}
<Form.Label>Address line 1</Form.Label>
<Input {...props} type="text" bind:value={$editData.line1} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="line2">
<Form.Control id="edit-line2">
{#snippet children({ props })}
<Form.Label>Address line 2</Form.Label>
<Input {...props} type="text" bind:value={$editData.line2} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="city">
<Form.Control id="edit-city">
{#snippet children({ props })}
<Form.Label>City</Form.Label>
<Input {...props} type="text" bind:value={$editData.city} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="region">
<Form.Control id="edit-region">
{#snippet children({ props })}
<Form.Label>Region / county</Form.Label>
<Input {...props} type="text" bind:value={$editData.region} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="postcode">
<Form.Control id="edit-postcode">
{#snippet children({ props })}
<Form.Label>Postcode</Form.Label>
<Input {...props} type="text" bind:value={$editData.postcode} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="country">
<Form.Control id="edit-country">
{#snippet children({ props })}
<Form.Label>Country</Form.Label>
<Input {...props} type="text" bind:value={$editData.country} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="isPrimary">
<Form.Control id="edit-isPrimary">
{#snippet children({ props })}
<Form.Label>Primary address</Form.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}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/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.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="roomId">
<Field.Field>
<Control id="create-roomId">
{#snippet children({ props })}
<Field.Label>Room</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.roomId}>
{#each data.options.rooms as option (option.value)}
<NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option>
{/each}
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="serviceId">
<Field.Field>
<Control id="create-serviceId">
{#snippet children({ props })}
<Field.Label>Service</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.serviceId}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
{#each data.options.services as option (option.value)}
<NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option>
{/each}
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="startsAt">
<Field.Field>
<Control id="create-startsAt">
{#snippet children({ props })}
<Field.Label>Starts at</Field.Label>
<Input {...props} type="datetime-local" bind:value={$createData.startsAt} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="endsAt">
<Field.Field>
<Control id="create-endsAt">
{#snippet children({ props })}
<Field.Label>Ends at</Field.Label>
<Input {...props} type="datetime-local" bind:value={$createData.endsAt} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="status">
<Field.Field>
<Control id="create-status">
{#snippet children({ props })}
<Field.Label>Status</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="booked">Booked</NativeSelect.Option>
<NativeSelect.Option value="confirmed">Confirmed</NativeSelect.Option>
<NativeSelect.Option value="completed">Completed</NativeSelect.Option>
<NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.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>
<Form.Field form={createForm} name="roomId">
<Form.Control id="create-roomId">
{#snippet children({ props })}
<Form.Label>Room</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.roomId}>
{#each data.options.rooms as option (option.value)}
<NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option>
{/each}
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="serviceId">
<Form.Control id="create-serviceId">
{#snippet children({ props })}
<Form.Label>Service</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.serviceId}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
{#each data.options.services as option (option.value)}
<NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option>
{/each}
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="startsAt">
<Form.Control id="create-startsAt">
{#snippet children({ props })}
<Form.Label>Starts at</Form.Label>
<Input {...props} type="datetime-local" bind:value={$createData.startsAt} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="endsAt">
<Form.Control id="create-endsAt">
{#snippet children({ props })}
<Form.Label>Ends at</Form.Label>
<Input {...props} type="datetime-local" bind:value={$createData.endsAt} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="status">
<Form.Control id="create-status">
{#snippet children({ props })}
<Form.Label>Status</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="booked">Booked</NativeSelect.Option>
<NativeSelect.Option value="confirmed">Confirmed</NativeSelect.Option>
<NativeSelect.Option value="completed">Completed</NativeSelect.Option>
<NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="notes">
<Form.Control id="create-notes">
{#snippet children({ props })}
<Form.Label>Notes</Form.Label>
<Textarea {...props} bind:value={$createData.notes} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,7 +1,6 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js';
@@ -33,87 +32,75 @@
{#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="roomId">
<Field.Field>
<Control id="edit-roomId">
{#snippet children({ props })}
<Field.Label>Room</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.roomId}>
{#each data.options.rooms as option (option.value)}
<NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option>
{/each}
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="serviceId">
<Field.Field>
<Control id="edit-serviceId">
{#snippet children({ props })}
<Field.Label>Service</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.serviceId}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
{#each data.options.services as option (option.value)}
<NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option>
{/each}
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="startsAt">
<Field.Field>
<Control id="edit-startsAt">
{#snippet children({ props })}
<Field.Label>Starts at</Field.Label>
<Input {...props} type="datetime-local" bind:value={$editData.startsAt} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="endsAt">
<Field.Field>
<Control id="edit-endsAt">
{#snippet children({ props })}
<Field.Label>Ends at</Field.Label>
<Input {...props} type="datetime-local" bind:value={$editData.endsAt} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="status">
<Field.Field>
<Control id="edit-status">
{#snippet children({ props })}
<Field.Label>Status</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="booked">Booked</NativeSelect.Option>
<NativeSelect.Option value="confirmed">Confirmed</NativeSelect.Option>
<NativeSelect.Option value="completed">Completed</NativeSelect.Option>
<NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.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>
<Form.Field form={editForm} name="roomId">
<Form.Control id="edit-roomId">
{#snippet children({ props })}
<Form.Label>Room</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.roomId}>
{#each data.options.rooms as option (option.value)}
<NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option>
{/each}
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="serviceId">
<Form.Control id="edit-serviceId">
{#snippet children({ props })}
<Form.Label>Service</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.serviceId}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
{#each data.options.services as option (option.value)}
<NativeSelect.Option value={option.value}>{option.label}</NativeSelect.Option>
{/each}
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="startsAt">
<Form.Control id="edit-startsAt">
{#snippet children({ props })}
<Form.Label>Starts at</Form.Label>
<Input {...props} type="datetime-local" bind:value={$editData.startsAt} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="endsAt">
<Form.Control id="edit-endsAt">
{#snippet children({ props })}
<Form.Label>Ends at</Form.Label>
<Input {...props} type="datetime-local" bind:value={$editData.endsAt} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="status">
<Form.Control id="edit-status">
{#snippet children({ props })}
<Form.Label>Status</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="booked">Booked</NativeSelect.Option>
<NativeSelect.Option value="confirmed">Confirmed</NativeSelect.Option>
<NativeSelect.Option value="completed">Completed</NativeSelect.Option>
<NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="notes">
<Form.Control id="edit-notes">
{#snippet children({ props })}
<Form.Label>Notes</Form.Label>
<Textarea {...props} bind:value={$editData.notes} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/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.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="name">
<Field.Field>
<Control id="create-name">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input {...props} type="text" bind:value={$createData.name} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="role">
<Field.Field>
<Control id="create-role">
{#snippet children({ props })}
<Field.Label>Role</Field.Label>
<Input {...props} type="text" bind:value={$createData.role} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="email">
<Field.Field>
<Control id="create-email">
{#snippet children({ props })}
<Field.Label>Email</Field.Label>
<Input {...props} type="email" bind:value={$createData.email} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="phone">
<Field.Field>
<Control id="create-phone">
{#snippet children({ props })}
<Field.Label>Phone</Field.Label>
<Input {...props} type="text" bind:value={$createData.phone} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="isPrimary">
<Field.Field>
<Control id="create-isPrimary">
{#snippet children({ props })}
<Field.Label>Primary contact</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>
<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>
<Form.Field form={createForm} name="name">
<Form.Control id="create-name">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input {...props} type="text" bind:value={$createData.name} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="role">
<Form.Control id="create-role">
{#snippet children({ props })}
<Form.Label>Role</Form.Label>
<Input {...props} type="text" bind:value={$createData.role} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="email">
<Form.Control id="create-email">
{#snippet children({ props })}
<Form.Label>Email</Form.Label>
<Input {...props} type="email" bind:value={$createData.email} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="phone">
<Form.Control id="create-phone">
{#snippet children({ props })}
<Form.Label>Phone</Form.Label>
<Input {...props} type="text" bind:value={$createData.phone} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="isPrimary">
<Form.Control id="create-isPrimary">
{#snippet children({ props })}
<Form.Label>Primary contact</Form.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}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="notes">
<Form.Control id="create-notes">
{#snippet children({ props })}
<Form.Label>Notes</Form.Label>
<Textarea {...props} bind:value={$createData.notes} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,7 +1,6 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js';
@@ -31,76 +30,64 @@
{#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="name">
<Field.Field>
<Control id="edit-name">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input {...props} type="text" bind:value={$editData.name} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="role">
<Field.Field>
<Control id="edit-role">
{#snippet children({ props })}
<Field.Label>Role</Field.Label>
<Input {...props} type="text" bind:value={$editData.role} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="email">
<Field.Field>
<Control id="edit-email">
{#snippet children({ props })}
<Field.Label>Email</Field.Label>
<Input {...props} type="email" bind:value={$editData.email} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="phone">
<Field.Field>
<Control id="edit-phone">
{#snippet children({ props })}
<Field.Label>Phone</Field.Label>
<Input {...props} type="text" bind:value={$editData.phone} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="isPrimary">
<Field.Field>
<Control id="edit-isPrimary">
{#snippet children({ props })}
<Field.Label>Primary contact</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>
<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>
<Form.Field form={editForm} name="name">
<Form.Control id="edit-name">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input {...props} type="text" bind:value={$editData.name} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="role">
<Form.Control id="edit-role">
{#snippet children({ props })}
<Form.Label>Role</Form.Label>
<Input {...props} type="text" bind:value={$editData.role} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="email">
<Form.Control id="edit-email">
{#snippet children({ props })}
<Form.Label>Email</Form.Label>
<Input {...props} type="email" bind:value={$editData.email} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="phone">
<Form.Control id="edit-phone">
{#snippet children({ props })}
<Form.Label>Phone</Form.Label>
<Input {...props} type="text" bind:value={$editData.phone} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="isPrimary">
<Form.Control id="edit-isPrimary">
{#snippet children({ props })}
<Form.Label>Primary contact</Form.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}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="notes">
<Form.Control id="edit-notes">
{#snippet children({ props })}
<Form.Label>Notes</Form.Label>
<Textarea {...props} bind:value={$editData.notes} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,9 +1,8 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
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 * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/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.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="title">
<Field.Field>
<Control id="create-title">
{#snippet children({ props })}
<Field.Label>Title</Field.Label>
<Input {...props} type="text" bind:value={$createData.title} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="startDate">
<Field.Field>
<Control id="create-startDate">
{#snippet children({ props })}
<Field.Label>Start date</Field.Label>
<Input {...props} type="date" bind:value={$createData.startDate} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="endDate">
<Field.Field>
<Control id="create-endDate">
{#snippet children({ props })}
<Field.Label>End date</Field.Label>
<Input {...props} type="date" bind:value={$createData.endDate} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="status">
<Field.Field>
<Control id="create-status">
{#snippet children({ props })}
<Field.Label>Status</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="draft">Draft</NativeSelect.Option>
<NativeSelect.Option value="active">Active</NativeSelect.Option>
<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>
<Form.Field form={createForm} name="title">
<Form.Control id="create-title">
{#snippet children({ props })}
<Form.Label>Title</Form.Label>
<Input {...props} type="text" bind:value={$createData.title} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="startDate">
<Form.Control id="create-startDate">
{#snippet children({ props })}
<Form.Label>Start date</Form.Label>
<Input {...props} type="date" bind:value={$createData.startDate} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="endDate">
<Form.Control id="create-endDate">
{#snippet children({ props })}
<Form.Label>End date</Form.Label>
<Input {...props} type="date" bind:value={$createData.endDate} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="status">
<Form.Control id="create-status">
{#snippet children({ props })}
<Form.Label>Status</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="draft">Draft</NativeSelect.Option>
<NativeSelect.Option value="active">Active</NativeSelect.Option>
<NativeSelect.Option value="expired">Expired</NativeSelect.Option>
<NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<MoneyField
form={createForm}
data={createData}
@@ -91,17 +82,15 @@
label="Value"
id="create-valueGbp"
/>
<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>
<Form.Field form={createForm} name="notes">
<Form.Control id="create-notes">
{#snippet children({ props })}
<Form.Label>Notes</Form.Label>
<Textarea {...props} bind:value={$createData.notes} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts">
/* 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 * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js';
@@ -32,56 +31,48 @@
{#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="title">
<Field.Field>
<Control id="edit-title">
{#snippet children({ props })}
<Field.Label>Title</Field.Label>
<Input {...props} type="text" bind:value={$editData.title} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="startDate">
<Field.Field>
<Control id="edit-startDate">
{#snippet children({ props })}
<Field.Label>Start date</Field.Label>
<Input {...props} type="date" bind:value={$editData.startDate} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="endDate">
<Field.Field>
<Control id="edit-endDate">
{#snippet children({ props })}
<Field.Label>End date</Field.Label>
<Input {...props} type="date" bind:value={$editData.endDate} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="status">
<Field.Field>
<Control id="edit-status">
{#snippet children({ props })}
<Field.Label>Status</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="draft">Draft</NativeSelect.Option>
<NativeSelect.Option value="active">Active</NativeSelect.Option>
<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>
<Form.Field form={editForm} name="title">
<Form.Control id="edit-title">
{#snippet children({ props })}
<Form.Label>Title</Form.Label>
<Input {...props} type="text" bind:value={$editData.title} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="startDate">
<Form.Control id="edit-startDate">
{#snippet children({ props })}
<Form.Label>Start date</Form.Label>
<Input {...props} type="date" bind:value={$editData.startDate} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="endDate">
<Form.Control id="edit-endDate">
{#snippet children({ props })}
<Form.Label>End date</Form.Label>
<Input {...props} type="date" bind:value={$editData.endDate} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="status">
<Form.Control id="edit-status">
{#snippet children({ props })}
<Form.Label>Status</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="draft">Draft</NativeSelect.Option>
<NativeSelect.Option value="active">Active</NativeSelect.Option>
<NativeSelect.Option value="expired">Expired</NativeSelect.Option>
<NativeSelect.Option value="cancelled">Cancelled</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<MoneyField
form={editForm}
data={editData}
@@ -89,17 +80,15 @@
label="Value"
id="edit-valueGbp"
/>
<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>
<Form.Field form={editForm} name="notes">
<Form.Control id="edit-notes">
{#snippet children({ props })}
<Form.Label>Notes</Form.Label>
<Textarea {...props} bind:value={$editData.notes} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,9 +1,8 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
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 * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/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.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="invoiceNumber">
<Field.Field>
<Control id="create-invoiceNumber">
{#snippet children({ props })}
<Field.Label>Invoice number</Field.Label>
<Input {...props} type="text" bind:value={$createData.invoiceNumber} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="issueDate">
<Field.Field>
<Control id="create-issueDate">
{#snippet children({ props })}
<Field.Label>Issue date</Field.Label>
<Input {...props} type="date" bind:value={$createData.issueDate} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="dueDate">
<Field.Field>
<Control id="create-dueDate">
{#snippet children({ props })}
<Field.Label>Due date</Field.Label>
<Input {...props} type="date" bind:value={$createData.dueDate} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="status">
<Field.Field>
<Control id="create-status">
{#snippet children({ props })}
<Field.Label>Status</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="draft">Draft</NativeSelect.Option>
<NativeSelect.Option value="sent">Sent</NativeSelect.Option>
<NativeSelect.Option value="paid">Paid</NativeSelect.Option>
<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>
<Form.Field form={createForm} name="invoiceNumber">
<Form.Control id="create-invoiceNumber">
{#snippet children({ props })}
<Form.Label>Invoice number</Form.Label>
<Input {...props} type="text" bind:value={$createData.invoiceNumber} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="issueDate">
<Form.Control id="create-issueDate">
{#snippet children({ props })}
<Form.Label>Issue date</Form.Label>
<Input {...props} type="date" bind:value={$createData.issueDate} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="dueDate">
<Form.Control id="create-dueDate">
{#snippet children({ props })}
<Form.Label>Due date</Form.Label>
<Input {...props} type="date" bind:value={$createData.dueDate} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="status">
<Form.Control id="create-status">
{#snippet children({ props })}
<Form.Label>Status</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="draft">Draft</NativeSelect.Option>
<NativeSelect.Option value="sent">Sent</NativeSelect.Option>
<NativeSelect.Option value="paid">Paid</NativeSelect.Option>
<NativeSelect.Option value="overdue">Overdue</NativeSelect.Option>
<NativeSelect.Option value="void">Void</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<MoneyField
form={createForm}
data={createData}
@@ -106,17 +97,15 @@
label="Total"
id="create-totalGbp"
/>
<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>
<Form.Field form={createForm} name="notes">
<Form.Control id="create-notes">
{#snippet children({ props })}
<Form.Label>Notes</Form.Label>
<Textarea {...props} bind:value={$createData.notes} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts">
/* 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 * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js';
@@ -32,57 +31,49 @@
{#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="invoiceNumber">
<Field.Field>
<Control id="edit-invoiceNumber">
{#snippet children({ props })}
<Field.Label>Invoice number</Field.Label>
<Input {...props} type="text" bind:value={$editData.invoiceNumber} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="issueDate">
<Field.Field>
<Control id="edit-issueDate">
{#snippet children({ props })}
<Field.Label>Issue date</Field.Label>
<Input {...props} type="date" bind:value={$editData.issueDate} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="dueDate">
<Field.Field>
<Control id="edit-dueDate">
{#snippet children({ props })}
<Field.Label>Due date</Field.Label>
<Input {...props} type="date" bind:value={$editData.dueDate} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="status">
<Field.Field>
<Control id="edit-status">
{#snippet children({ props })}
<Field.Label>Status</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="draft">Draft</NativeSelect.Option>
<NativeSelect.Option value="sent">Sent</NativeSelect.Option>
<NativeSelect.Option value="paid">Paid</NativeSelect.Option>
<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>
<Form.Field form={editForm} name="invoiceNumber">
<Form.Control id="edit-invoiceNumber">
{#snippet children({ props })}
<Form.Label>Invoice number</Form.Label>
<Input {...props} type="text" bind:value={$editData.invoiceNumber} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="issueDate">
<Form.Control id="edit-issueDate">
{#snippet children({ props })}
<Form.Label>Issue date</Form.Label>
<Input {...props} type="date" bind:value={$editData.issueDate} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="dueDate">
<Form.Control id="edit-dueDate">
{#snippet children({ props })}
<Form.Label>Due date</Form.Label>
<Input {...props} type="date" bind:value={$editData.dueDate} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="status">
<Form.Control id="edit-status">
{#snippet children({ props })}
<Form.Label>Status</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="draft">Draft</NativeSelect.Option>
<NativeSelect.Option value="sent">Sent</NativeSelect.Option>
<NativeSelect.Option value="paid">Paid</NativeSelect.Option>
<NativeSelect.Option value="overdue">Overdue</NativeSelect.Option>
<NativeSelect.Option value="void">Void</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<MoneyField
form={editForm}
data={editData}
@@ -98,17 +89,15 @@
label="Total"
id="edit-totalGbp"
/>
<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>
<Form.Field form={editForm} name="notes">
<Form.Control id="edit-notes">
{#snippet children({ props })}
<Form.Label>Notes</Form.Label>
<Textarea {...props} bind:value={$editData.notes} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js';
@@ -59,215 +58,181 @@
</Stepper.Root>
<div class={['grid gap-2', createStep !== 0 && 'hidden']}>
<FormField form={createForm} name="name">
<Field.Field>
<Control id="create-name">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input {...props} type="text" bind:value={$createData.name} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="type">
<Field.Field>
<Control id="create-type">
{#snippet children({ props })}
<Field.Label>Type</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.type}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="business">Business</NativeSelect.Option>
<NativeSelect.Option value="sport">Sport</NativeSelect.Option>
<NativeSelect.Option value="individual">Individual</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="status">
<Field.Field>
<Control id="create-status">
{#snippet children({ props })}
<Field.Label>Status</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="active">Active</NativeSelect.Option>
<NativeSelect.Option value="prospect">Prospect</NativeSelect.Option>
<NativeSelect.Option value="paused">Paused</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="website">
<Field.Field>
<Control id="create-website">
{#snippet children({ props })}
<Field.Label>Website</Field.Label>
<Input
{...props}
type="url"
placeholder="https://example.com"
bind:value={$createData.website}
/>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.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>
<Form.Field form={createForm} name="name">
<Form.Control id="create-name">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input {...props} type="text" bind:value={$createData.name} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="type">
<Form.Control id="create-type">
{#snippet children({ props })}
<Form.Label>Type</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.type}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="business">Business</NativeSelect.Option>
<NativeSelect.Option value="sport">Sport</NativeSelect.Option>
<NativeSelect.Option value="individual">Individual</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="status">
<Form.Control id="create-status">
{#snippet children({ props })}
<Form.Label>Status</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$createData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="active">Active</NativeSelect.Option>
<NativeSelect.Option value="prospect">Prospect</NativeSelect.Option>
<NativeSelect.Option value="paused">Paused</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="website">
<Form.Control id="create-website">
{#snippet children({ props })}
<Form.Label>Website</Form.Label>
<Input
{...props}
type="url"
placeholder="https://example.com"
bind:value={$createData.website}
/>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="notes">
<Form.Control id="create-notes">
{#snippet children({ props })}
<Form.Label>Notes</Form.Label>
<Textarea {...props} bind:value={$createData.notes} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
</div>
<div class={['grid gap-2', createStep !== 1 && 'hidden']}>
<FormField form={createForm} name="primaryContactName">
<Field.Field>
<Control id="create-primary-contact-name">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input {...props} type="text" bind:value={$createData.primaryContactName} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="primaryContactRole">
<Field.Field>
<Control id="create-primary-contact-role">
{#snippet children({ props })}
<Field.Label>Role</Field.Label>
<Input {...props} type="text" bind:value={$createData.primaryContactRole} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="primaryContactEmail">
<Field.Field>
<Control id="create-primary-contact-email">
{#snippet children({ props })}
<Field.Label>Email</Field.Label>
<Input {...props} type="email" bind:value={$createData.primaryContactEmail} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="primaryContactPhone">
<Field.Field>
<Control id="create-primary-contact-phone">
{#snippet children({ props })}
<Field.Label>Phone</Field.Label>
<Input {...props} type="text" bind:value={$createData.primaryContactPhone} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="primaryContactNotes">
<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>
<Form.Field form={createForm} name="primaryContactName">
<Form.Control id="create-primary-contact-name">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input {...props} type="text" bind:value={$createData.primaryContactName} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="primaryContactRole">
<Form.Control id="create-primary-contact-role">
{#snippet children({ props })}
<Form.Label>Role</Form.Label>
<Input {...props} type="text" bind:value={$createData.primaryContactRole} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="primaryContactEmail">
<Form.Control id="create-primary-contact-email">
{#snippet children({ props })}
<Form.Label>Email</Form.Label>
<Input {...props} type="email" bind:value={$createData.primaryContactEmail} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="primaryContactPhone">
<Form.Control id="create-primary-contact-phone">
{#snippet children({ props })}
<Form.Label>Phone</Form.Label>
<Input {...props} type="text" bind:value={$createData.primaryContactPhone} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="primaryContactNotes">
<Form.Control id="create-primary-contact-notes">
{#snippet children({ props })}
<Form.Label>Notes</Form.Label>
<Textarea {...props} bind:value={$createData.primaryContactNotes} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
</div>
<div class={['grid gap-2', createStep !== 2 && 'hidden']}>
<FormField form={createForm} name="primaryAddressLabel">
<Field.Field>
<Control id="create-primary-address-label">
{#snippet children({ props })}
<Field.Label>Label</Field.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressLabel} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="primaryAddressLine1">
<Field.Field>
<Control id="create-primary-address-line-1">
{#snippet children({ props })}
<Field.Label>Address line 1</Field.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressLine1} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="primaryAddressLine2">
<Field.Field>
<Control id="create-primary-address-line-2">
{#snippet children({ props })}
<Field.Label>Address line 2</Field.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressLine2} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Form.Field form={createForm} name="primaryAddressLabel">
<Form.Control id="create-primary-address-label">
{#snippet children({ props })}
<Form.Label>Label</Form.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressLabel} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="primaryAddressLine1">
<Form.Control id="create-primary-address-line-1">
{#snippet children({ props })}
<Form.Label>Address line 1</Form.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressLine1} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="primaryAddressLine2">
<Form.Control id="create-primary-address-line-2">
{#snippet children({ props })}
<Form.Label>Address line 2</Form.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressLine2} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<div class="grid gap-2 sm:grid-cols-2">
<FormField form={createForm} name="primaryAddressCity">
<Field.Field>
<Control id="create-primary-address-city">
{#snippet children({ props })}
<Field.Label>City</Field.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressCity} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="primaryAddressRegion">
<Field.Field>
<Control id="create-primary-address-region">
{#snippet children({ props })}
<Field.Label>Region</Field.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressRegion} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Form.Field form={createForm} name="primaryAddressCity">
<Form.Control id="create-primary-address-city">
{#snippet children({ props })}
<Form.Label>City</Form.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressCity} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="primaryAddressRegion">
<Form.Control id="create-primary-address-region">
{#snippet children({ props })}
<Form.Label>Region</Form.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressRegion} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
</div>
<div class="grid gap-2 sm:grid-cols-2">
<FormField form={createForm} name="primaryAddressPostcode">
<Field.Field>
<Control id="create-primary-address-postcode">
{#snippet children({ props })}
<Field.Label>Postcode</Field.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressPostcode} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="primaryAddressCountry">
<Field.Field>
<Control id="create-primary-address-country">
{#snippet children({ props })}
<Field.Label>Country</Field.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressCountry} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Form.Field form={createForm} name="primaryAddressPostcode">
<Form.Control id="create-primary-address-postcode">
{#snippet children({ props })}
<Form.Label>Postcode</Form.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressPostcode} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="primaryAddressCountry">
<Form.Control id="create-primary-address-country">
{#snippet children({ props })}
<Form.Label>Country</Form.Label>
<Input {...props} type="text" bind:value={$createData.primaryAddressCountry} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
</div>
</div>
@@ -1,7 +1,6 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js';
@@ -31,76 +30,66 @@
{#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="name">
<Field.Field>
<Control id="edit-name">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input {...props} type="text" bind:value={$editData.name} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="type">
<Field.Field>
<Control id="edit-type">
{#snippet children({ props })}
<Field.Label>Type</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.type}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="business">Business</NativeSelect.Option>
<NativeSelect.Option value="sport">Sport</NativeSelect.Option>
<NativeSelect.Option value="individual">Individual</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="status">
<Field.Field>
<Control id="edit-status">
{#snippet children({ props })}
<Field.Label>Status</Field.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="active">Active</NativeSelect.Option>
<NativeSelect.Option value="prospect">Prospect</NativeSelect.Option>
<NativeSelect.Option value="paused">Paused</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="website">
<Field.Field>
<Control id="edit-website">
{#snippet children({ props })}
<Field.Label>Website</Field.Label>
<Input
{...props}
type="url"
placeholder="https://example.com"
bind:value={$editData.website}
/>
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.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>
<Form.Field form={editForm} name="name">
<Form.Control id="edit-name">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input {...props} type="text" bind:value={$editData.name} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="type">
<Form.Control id="edit-type">
{#snippet children({ props })}
<Form.Label>Type</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.type}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="business">Business</NativeSelect.Option>
<NativeSelect.Option value="sport">Sport</NativeSelect.Option>
<NativeSelect.Option value="individual">Individual</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="status">
<Form.Control id="edit-status">
{#snippet children({ props })}
<Form.Label>Status</Form.Label>
<NativeSelect.Root {...props} class="w-full" bind:value={$editData.status}>
<NativeSelect.Option value="">Not set</NativeSelect.Option>
<NativeSelect.Option value="active">Active</NativeSelect.Option>
<NativeSelect.Option value="prospect">Prospect</NativeSelect.Option>
<NativeSelect.Option value="paused">Paused</NativeSelect.Option>
</NativeSelect.Root>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="website">
<Form.Control id="edit-website">
{#snippet children({ props })}
<Form.Label>Website</Form.Label>
<Input
{...props}
type="url"
placeholder="https://example.com"
bind:value={$editData.website}
/>
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="notes">
<Form.Control id="edit-notes">
{#snippet children({ props })}
<Form.Label>Notes</Form.Label>
<Textarea {...props} bind:value={$editData.notes} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import PlusIcon from '@lucide/svelte/icons/plus';
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/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.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="name">
<Field.Field>
<Control id="create-name">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input {...props} type="text" bind:value={$createData.name} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Form.Field form={createForm} name="name">
<Form.Control id="create-name">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input {...props} type="text" bind:value={$createData.name} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<RoomTypeFields form={createForm} data={createData} idPrefix="create" />
<Dialog.Footer>
<Dialog.Close
@@ -1,7 +1,6 @@
<script lang="ts">
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Field as FormField, Control, FieldErrors } from 'formsnap';
import * as Field from '$lib/components/ui/field/index.js';
import * as Form from '$lib/components/ui/form/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js';
@@ -30,17 +29,15 @@
{#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="name">
<Field.Field>
<Control id="edit-name">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input {...props} type="text" bind:value={$editData.name} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Form.Field form={editForm} name="name">
<Form.Control id="edit-name">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input {...props} type="text" bind:value={$editData.name} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<RoomTypeFields form={editForm} data={editData} idPrefix="edit" />
<Dialog.Footer>
<Dialog.Close
@@ -1,8 +1,7 @@
<script lang="ts">
/* 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 * as Field from '$lib/components/ui/field/index.js';
import { Input } from '$lib/components/ui/input/index.js';
import * as Tabs from '$lib/components/ui/tabs/index.js';
@@ -33,65 +32,36 @@
</script>
{#snippet numberField(name: string, label: string, min = 0)}
<FormField {form} {name}>
<Field.Field>
<Control id={`${idPrefix}-${name}`}>
{#snippet children({ props })}
<Field.Label>{label}</Field.Label>
<Input {...props} type="number" {min} bind:value={$data[name]} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Form.Field {form} {name}>
<Form.Control id={`${idPrefix}-${name}`}>
{#snippet children({ props })}
<Form.Label>{label}</Form.Label>
<Input {...props} type="number" {min} bind:value={$data[name]} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
{/snippet}
<FormField {form} name="type">
<Field.Field>
<Control id={`${idPrefix}-type`}>
{#snippet children({ props })}
<input {...props} type="hidden" bind:value={$data.type} />
{/snippet}
</Control>
<Field.Label>Type</Field.Label>
<Tabs.Root bind:value={$data.type} class="gap-3">
<Tabs.List class="grid h-9 w-full grid-cols-3">
{#each roomTypes as roomType (roomType.value)}
<Tabs.Trigger value={roomType.value} class="h-full px-2 text-center">
{roomType.label}
</Tabs.Trigger>
{/each}
</Tabs.List>
<Form.Field {form} name="type">
<Form.Control id={`${idPrefix}-type`}>
{#snippet children({ props })}
<input {...props} type="hidden" bind:value={$data.type} />
{/snippet}
</Form.Control>
<Form.Label>Type</Form.Label>
<Tabs.Root bind:value={$data.type} class="gap-3">
<Tabs.List class="grid h-9 w-full grid-cols-3">
{#each roomTypes as roomType (roomType.value)}
<Tabs.Trigger value={roomType.value} class="h-full px-2 text-center">
{roomType.label}
</Tabs.Trigger>
{/each}
</Tabs.List>
<Tabs.Content value="meeting_room" class="grid gap-2">
{@render numberField('sqFt', 'Sq ft.', 1)}
<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">
<Tabs.Content value="meeting_room" class="grid gap-2">
{@render numberField('sqFt', 'Sq ft.', 1)}
<div class="grid gap-2 sm:grid-cols-2">
{#each bookablePriceFields as field (field.name)}
<MoneyField
{form}
@@ -101,8 +71,33 @@
id={`${idPrefix}-${field.name}`}
/>
{/each}
</Tabs.Content>
</Tabs.Root>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
</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)}
<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">
/* eslint-disable @typescript-eslint/no-explicit-any */
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 * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/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.Header>
<form method="POST" action="?/create" class="grid gap-2" use:enhanceCreate>
<FormField form={createForm} name="name">
<Field.Field>
<Control id="create-name">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input {...props} type="text" bind:value={$createData.name} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="category">
<Field.Field>
<Control id="create-category">
{#snippet children({ props })}
<Field.Label>Category</Field.Label>
<Input {...props} type="text" bind:value={$createData.category} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={createForm} name="unit">
<Field.Field>
<Control id="create-unit">
{#snippet children({ props })}
<Field.Label>Unit</Field.Label>
<Input {...props} type="text" bind:value={$createData.unit} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Form.Field form={createForm} name="name">
<Form.Control id="create-name">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input {...props} type="text" bind:value={$createData.name} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="category">
<Form.Control id="create-category">
{#snippet children({ props })}
<Form.Label>Category</Form.Label>
<Input {...props} type="text" bind:value={$createData.category} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={createForm} name="unit">
<Form.Control id="create-unit">
{#snippet children({ props })}
<Form.Label>Unit</Form.Label>
<Input {...props} type="text" bind:value={$createData.unit} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<MoneyField
form={createForm}
data={createData}
@@ -73,17 +66,15 @@
label="Price"
id="create-priceGbp"
/>
<FormField form={createForm} name="description">
<Field.Field>
<Control id="create-description">
{#snippet children({ props })}
<Field.Label>Description</Field.Label>
<Textarea {...props} bind:value={$createData.description} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Form.Field form={createForm} name="description">
<Form.Control id="create-description">
{#snippet children({ props })}
<Form.Label>Description</Form.Label>
<Textarea {...props} bind:value={$createData.description} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button
@@ -1,8 +1,7 @@
<script lang="ts">
/* 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 * as Field from '$lib/components/ui/field/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Input } from '$lib/components/ui/input/index.js';
@@ -31,39 +30,33 @@
{#if editingRecord}
<form method="POST" action="?/edit" class="grid gap-2" use:enhanceEdit>
<input type="hidden" name="id" value={$editData.id} />
<FormField form={editForm} name="name">
<Field.Field>
<Control id="edit-name">
{#snippet children({ props })}
<Field.Label>Name</Field.Label>
<Input {...props} type="text" bind:value={$editData.name} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="category">
<Field.Field>
<Control id="edit-category">
{#snippet children({ props })}
<Field.Label>Category</Field.Label>
<Input {...props} type="text" bind:value={$editData.category} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<FormField form={editForm} name="unit">
<Field.Field>
<Control id="edit-unit">
{#snippet children({ props })}
<Field.Label>Unit</Field.Label>
<Input {...props} type="text" bind:value={$editData.unit} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Form.Field form={editForm} name="name">
<Form.Control id="edit-name">
{#snippet children({ props })}
<Form.Label>Name</Form.Label>
<Input {...props} type="text" bind:value={$editData.name} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="category">
<Form.Control id="edit-category">
{#snippet children({ props })}
<Form.Label>Category</Form.Label>
<Input {...props} type="text" bind:value={$editData.category} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Form.Field form={editForm} name="unit">
<Form.Control id="edit-unit">
{#snippet children({ props })}
<Form.Label>Unit</Form.Label>
<Input {...props} type="text" bind:value={$editData.unit} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<MoneyField
form={editForm}
data={editData}
@@ -71,17 +64,15 @@
label="Price"
id="edit-priceGbp"
/>
<FormField form={editForm} name="description">
<Field.Field>
<Control id="edit-description">
{#snippet children({ props })}
<Field.Label>Description</Field.Label>
<Textarea {...props} bind:value={$editData.description} />
{/snippet}
</Control>
<Field.Error><FieldErrors /></Field.Error>
</Field.Field>
</FormField>
<Form.Field form={editForm} name="description">
<Form.Control id="edit-description">
{#snippet children({ props })}
<Form.Label>Description</Form.Label>
<Textarea {...props} bind:value={$editData.description} />
{/snippet}
</Form.Control>
<Form.FieldErrors />
</Form.Field>
<Dialog.Footer>
<Dialog.Close
>{#snippet child({ props })}<Button variant="outline" {...props}>Cancel</Button