90 lines
2.6 KiB
Svelte
90 lines
2.6 KiB
Svelte
<script lang="ts">
|
|
import { createListSearch } from '$lib/list-search.svelte';
|
|
import ListSearch from '$lib/components/list-search.svelte';
|
|
import InvoicesTable from './invoices-table.svelte';
|
|
import ArchiveInvoiceDialog from './archive-invoice-dialog.svelte';
|
|
import { toast } from 'svelte-sonner';
|
|
import { superForm } from 'sveltekit-superforms';
|
|
import { zod4Client } from 'sveltekit-superforms/adapters';
|
|
import { archiveSchema } from '$lib/schemas/shared.schema';
|
|
import {
|
|
formatValue,
|
|
formatMoney,
|
|
formatDate,
|
|
recordName as getRecordName,
|
|
relationLabel as getRelationLabel
|
|
} from '$lib/record-utils';
|
|
import { handleFormToast } from '$lib/form-feedback';
|
|
import type { PageData } from './$types';
|
|
type RecordRow = PageData['records'][number];
|
|
let { data }: { data: PageData } = $props();
|
|
|
|
const listSearch = createListSearch(() => data.records);
|
|
const listData = $derived({ ...data, records: listSearch.filtered });
|
|
|
|
let archivingId = $state<string | null>(null);
|
|
|
|
const archivingRecord = $derived(data.records.find((record) => record.id === archivingId));
|
|
|
|
// svelte-ignore state_referenced_locally
|
|
const archiveForm = superForm(data.archiveForm, {
|
|
validators: zod4Client(archiveSchema),
|
|
resetForm: false,
|
|
onUpdated: ({ form }) => handleFormToast(form, 'invoices-archive', () => (archivingId = null)),
|
|
onError: ({ result }) => toast.error(result.error.message, { id: 'invoices-archive' })
|
|
});
|
|
|
|
const { form: archiveData, enhance: enhanceArchive } = archiveForm;
|
|
|
|
function relationLabel(value: unknown, optionsKey: 'clients') {
|
|
return getRelationLabel(data.options, value, optionsKey);
|
|
}
|
|
|
|
function openArchive(record: RecordRow) {
|
|
archiveForm.reset({ data: { id: record.id } });
|
|
archivingId = record.id;
|
|
}
|
|
|
|
function recordName(record: Partial<RecordRow> | undefined) {
|
|
return getRecordName(record, 'this invoice');
|
|
}
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Invoices | Clearity</title>
|
|
</svelte:head>
|
|
|
|
<div class="flex flex-col gap-6">
|
|
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
<div>
|
|
<h1 class="text-2xl font-semibold tracking-tight">Invoices</h1>
|
|
<p class="text-sm text-muted-foreground">Manage invoice records and payment status.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<ListSearch
|
|
bind:value={listSearch.params.q}
|
|
placeholder="Search invoices..."
|
|
totalCount={data.records.length}
|
|
resultCount={listSearch.filtered.length}
|
|
/>
|
|
|
|
<InvoicesTable
|
|
data={listData}
|
|
{openArchive}
|
|
{formatValue}
|
|
{formatDate}
|
|
{formatMoney}
|
|
{relationLabel}
|
|
{recordName}
|
|
/>
|
|
</div>
|
|
|
|
<ArchiveInvoiceDialog
|
|
{archivingRecord}
|
|
{archiveData}
|
|
{enhanceArchive}
|
|
{recordName}
|
|
onClose={() => (archivingId = null)}
|
|
/>
|