Enhance receipt management by adding support for image uploads and improving data handling in receipt forms. Update route parameters and type definitions for better data structure. Refactor UI components for improved user experience and responsiveness, including collapsible sections for receipt images and dynamic line item management.
CI / build (push) Has been skipped
CI / deploy (push) Successful in 1m42s

This commit is contained in:
eewing
2026-02-19 10:57:15 -06:00
parent 115eecc6f3
commit f51f9c9f81
49 changed files with 879 additions and 11651 deletions
@@ -0,0 +1,21 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
import type { Snippet } from "svelte";
import type { HTMLAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: CollapsiblePrimitive.ContentProps & HTMLAttributes<HTMLDivElement> & { children?: Snippet } = $props();
</script>
<CollapsiblePrimitive.Content
bind:ref
class={cn("overflow-hidden", className)}
{...restProps}
>
{@render children?.()}
</CollapsiblePrimitive.Content>
@@ -0,0 +1,17 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
import type { Snippet } from "svelte";
import type { HTMLButtonAttributes } from "svelte/elements";
import { cn } from "$lib/utils.js";
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: CollapsiblePrimitive.TriggerProps & HTMLButtonAttributes<HTMLButtonElement> & { children?: Snippet } = $props();
</script>
<CollapsiblePrimitive.Trigger bind:ref class={cn("flex items-center gap-2", className)} {...restProps}>
{@render children?.()}
</CollapsiblePrimitive.Trigger>
@@ -0,0 +1,17 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from "bits-ui";
import type { HTMLAttributes } from "svelte/elements";
import type { Snippet } from "svelte";
import { cn } from "$lib/utils.js";
let {
open = $bindable(false),
class: className,
children,
...restProps
}: CollapsiblePrimitive.RootProps & HTMLAttributes<HTMLDivElement> & { children?: Snippet } = $props();
</script>
<CollapsiblePrimitive.Root bind:open class={cn(className)} {...restProps}>
{@render children?.()}
</CollapsiblePrimitive.Root>
@@ -0,0 +1,5 @@
import Root from "./collapsible.svelte";
import Content from "./collapsible-content.svelte";
import Trigger from "./collapsible-trigger.svelte";
export { Root, Content, Trigger, Root as Collapsible, Content as CollapsibleContent, Trigger as CollapsibleTrigger };
+23
View File
@@ -0,0 +1,23 @@
/**
* Convert HEIC/HEIF blob to JPEG. Uses heic2any only when this function is called
* (dynamic import), so the receipt page can load even if heic2any fails to resolve.
* Suppresses the library's "Could not parse HEIF file" console errors during conversion.
*/
export async function convertHeicBlobToJpeg(blob: Blob): Promise<Blob> {
const originalError = console.error;
console.error = (...args: unknown[]) => {
const msg = args[0]?.toString?.() ?? "";
if (msg.includes("HEIF") || msg.includes("parse HEIF")) return;
originalError.apply(console, args);
};
try {
const mod = await import(/* @vite-ignore */ "heic2any");
const heic2any = (mod as unknown as {
default: (opts: { blob: Blob; toType: string; quality?: number }) => Promise<Blob | Blob[]>;
}).default;
const out = await heic2any({ blob, toType: "image/jpeg", quality: 0.92 });
return Array.isArray(out) ? out[0] : out;
} finally {
console.error = originalError;
}
}
+17 -1
View File
@@ -1,3 +1,19 @@
/** Line item stored in receipt Data.lineItems */
export interface ReceiptLineItem {
item: string;
description: string;
quantity: number;
price: number;
}
/** JSON shape stored in Stackq_Receipts.Data */
export interface ReceiptDataShape {
lineItems?: ReceiptLineItem[];
tax?: number;
/** Total printed on receipt (for validation) */
receiptTotal?: number;
}
/** PocketBase Stackq_Receipts collection record (and expanded relation). */
export interface StackqReceipt {
id: string;
@@ -5,7 +21,7 @@ export interface StackqReceipt {
Type: string;
User: string;
Image?: string;
Data?: Record<string, unknown>;
Data?: ReceiptDataShape | Record<string, unknown>;
created: string;
updated: string;
expand?: {