Implement receipt management features including creation, loading, and display of receipts. Refactor related components and enhance UI with dialogs for adding new receipts. Update type definitions and improve error handling for better user experience.
This commit is contained in:
@@ -2,18 +2,44 @@
|
||||
import { Button } from "$lib/components/ui/button";
|
||||
import { Card, CardContent } from "$lib/components/ui/card";
|
||||
import { Input } from "$lib/components/ui/input";
|
||||
import * as Dialog from "$lib/components/ui/dialog";
|
||||
import ReceiptForm from "./ReceiptForm.svelte";
|
||||
import type { StackqReceipt } from "$lib/types/receipts";
|
||||
import type { ActionData } from "./$types";
|
||||
|
||||
let { data }: { data: { receipts: { id: string }[] } } = $props();
|
||||
let { data, form }: { data: { receipts: StackqReceipt[] }; form?: ActionData } = $props();
|
||||
let searchQuery = $state("");
|
||||
let createDialogOpen = $state(false);
|
||||
|
||||
const receipts = $derived(data?.receipts ?? []);
|
||||
const filteredReceipts = $derived(
|
||||
(() => {
|
||||
const q = searchQuery.trim().toLowerCase();
|
||||
if (!q) return receipts;
|
||||
return receipts.filter((r) => r.id.toLowerCase().includes(q));
|
||||
return receipts.filter(
|
||||
(r) =>
|
||||
r.id.toLowerCase().includes(q) ||
|
||||
(r.Status ?? "").toLowerCase().includes(q) ||
|
||||
(r.Type ?? "").toLowerCase().includes(q)
|
||||
);
|
||||
})()
|
||||
);
|
||||
|
||||
function formatDate(iso: string): string {
|
||||
try {
|
||||
const d = new Date(iso);
|
||||
return d.toLocaleDateString(undefined, {
|
||||
dateStyle: "short",
|
||||
timeStyle: "short",
|
||||
});
|
||||
} catch {
|
||||
return iso;
|
||||
}
|
||||
}
|
||||
|
||||
function onReceiptCreateSuccess() {
|
||||
createDialogOpen = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -38,9 +64,9 @@
|
||||
<span class="text-muted-foreground text-sm shrink-0">
|
||||
{filteredReceipts.length}{filteredReceipts.length !== receipts.length ? ` of ${receipts.length}` : ""} receipts
|
||||
</span>
|
||||
<a href="/receipts/new" class="block shrink-0">
|
||||
<Button class="w-full sm:w-auto min-h-11">New receipt</Button>
|
||||
</a>
|
||||
<Button class="w-full sm:w-auto min-h-11" onclick={() => (createDialogOpen = true)}>
|
||||
New receipt
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -53,9 +79,7 @@
|
||||
<p class="text-muted-foreground text-center text-xs">
|
||||
{receipts.length === 0 ? "Create a receipt to get started." : "Try a different search."}
|
||||
</p>
|
||||
<a href="/receipts/new">
|
||||
<Button variant="outline">New receipt</Button>
|
||||
</a>
|
||||
<Button variant="outline" onclick={() => (createDialogOpen = true)}>New receipt</Button>
|
||||
<a href="/">
|
||||
<Button variant="ghost">Back to home</Button>
|
||||
</a>
|
||||
@@ -66,10 +90,30 @@
|
||||
{#each filteredReceipts as receipt (receipt.id)}
|
||||
<Card>
|
||||
<CardContent class="p-4">
|
||||
<a href="/receipts/{receipt.id}" class="font-medium hover:underline">Receipt {receipt.id}</a>
|
||||
<a href="/receipts/{receipt.id}" class="font-medium hover:underline">
|
||||
{receipt.Status ?? "—"} · {receipt.Type ?? "—"} · {formatDate(receipt.created)}
|
||||
</a>
|
||||
<p class="text-muted-foreground mt-1 text-xs">ID: {receipt.id}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
<Dialog.Root bind:open={createDialogOpen}>
|
||||
<Dialog.Content class="max-h-[90dvh] w-[calc(100vw-2rem)] max-w-[500px] overflow-y-auto rounded-lg sm:w-full">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>New receipt</Dialog.Title>
|
||||
<Dialog.Description>Add a new receipt. Optionally attach an image.</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
<ReceiptForm
|
||||
action="/receipts?/create"
|
||||
submitLabel="Create"
|
||||
error={form?.error}
|
||||
defaultStatus={form?.Status ?? "New"}
|
||||
defaultType={form?.Type ?? "Purchase"}
|
||||
onSuccess={onReceiptCreateSuccess}
|
||||
/>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
|
||||
Reference in New Issue
Block a user