nexus/frontend/src/lib/components/forms/AccountCreateForm.svelte
2026-01-26 11:58:04 -05:00

177 lines
4.6 KiB
Svelte

<script lang="ts">
import { onMount } from 'svelte';
import { client } from '$lib/graphql/client';
import { CREATE_ACCOUNT, type CreateAccountInput } from '$lib/graphql/mutations/account';
import { CUSTOMERS_QUERY, type CustomersQueryResult } from '$lib/graphql/queries/customers';
interface Props {
onSuccess: (accountId: string) => void;
onCancel: () => void;
}
let { onSuccess, onCancel }: Props = $props();
// Customer list for picker
let customers = $state<{ id: string; name: string }[]>([]);
let customersLoading = $state(true);
// Form state
let customerId = $state('');
let name = $state('');
let status = $state<'ACTIVE' | 'INACTIVE' | 'PENDING'>('ACTIVE');
let startDate = $state(new Date().toISOString().split('T')[0]);
let endDate = $state('');
let loading = $state(false);
let error = $state<string | null>(null);
onMount(async () => {
try {
const result = await client.query<CustomersQueryResult>({
query: CUSTOMERS_QUERY,
variables: { filter: { isActive: true } }
});
customers = (result.data?.customers ?? [])
.map((c) => ({ id: c.id, name: c.name }))
.sort((a, b) => a.name.localeCompare(b.name));
} catch (err) {
error = 'Failed to load customers';
} finally {
customersLoading = false;
}
});
async function handleSubmit(event: SubmitEvent) {
event.preventDefault();
loading = true;
error = null;
try {
const input: CreateAccountInput = {
customerId,
name,
status,
startDate: startDate || undefined,
endDate: endDate || undefined
};
const result = await client.mutate<{ createAccount: { id: string } }>({
mutation: CREATE_ACCOUNT,
variables: { input }
});
const accountId = result.data?.createAccount?.id;
if (!accountId) {
throw new Error('Failed to create account');
}
onSuccess(accountId);
} catch (err) {
error = err instanceof Error ? err.message : 'An error occurred';
} finally {
loading = false;
}
}
</script>
<form onsubmit={handleSubmit} class="space-y-5">
{#if error}
<div class="rounded-lg border border-error-500/30 bg-error-50/50 p-3 dark:bg-error-950/30">
<p class="text-sm text-error-700 dark:text-error-400">{error}</p>
</div>
{/if}
<div>
<label for="customerId" class="form-label">
Customer <span class="required-indicator">*</span>
</label>
{#if customersLoading}
<div class="flex input-base items-center text-theme-muted">
<svg class="mr-2 h-4 w-4 animate-spin" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"
></circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
Loading customers...
</div>
{:else}
<select
id="customerId"
bind:value={customerId}
class="select-base"
required
disabled={loading}
>
<option value="">Select a customer...</option>
{#each customers as customer (customer.id)}
<option value={customer.id}>{customer.name}</option>
{/each}
</select>
{/if}
</div>
<div>
<label for="name" class="form-label">
Account Name <span class="required-indicator">*</span>
</label>
<input
id="name"
type="text"
bind:value={name}
class="input-base"
required
disabled={loading}
placeholder="e.g., Main Office, Building A"
/>
</div>
<div>
<label for="status" class="form-label">Status</label>
<select id="status" bind:value={status} class="select-base" disabled={loading}>
<option value="ACTIVE">Active</option>
<option value="INACTIVE">Inactive</option>
<option value="PENDING">Pending</option>
</select>
</div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<label for="startDate" class="form-label">Start Date</label>
<input
id="startDate"
type="date"
bind:value={startDate}
class="input-base"
disabled={loading}
/>
</div>
<div>
<label for="endDate" class="form-label">End Date</label>
<input
id="endDate"
type="date"
bind:value={endDate}
class="input-base"
min={startDate || undefined}
disabled={loading}
/>
<p class="mt-1 text-xs text-theme-muted">Leave blank for ongoing account</p>
</div>
</div>
<div class="form-actions border-t border-theme pt-5">
<button type="button" class="btn-cancel" onclick={onCancel} disabled={loading}>Cancel</button>
<button type="submit" class="btn-submit" disabled={loading || customersLoading || !customerId}>
{#if loading}
Creating...
{:else}
Create Account
{/if}
</button>
</div>
</form>