177 lines
4.6 KiB
Svelte
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>
|