Enhance image upload experience in ReceiptForm and +page components by integrating a loading state with a spinner icon during image processing. Update styles for better accessibility and user feedback, ensuring a smoother interaction when converting images to WebP format.
CI / build (push) Has been skipped
CI / deploy (push) Successful in 1m33s

This commit is contained in:
eewing
2026-02-19 11:33:57 -06:00
parent 8af1ee8bd4
commit d06e585bb5
18 changed files with 184 additions and 145 deletions
+25 -6
View File
@@ -9,6 +9,7 @@
import { enhance } from "$app/forms";
import ChevronDownIcon from "@lucide/svelte/icons/chevron-down";
import ChevronUpIcon from "@lucide/svelte/icons/chevron-up";
import Loader2Icon from "@lucide/svelte/icons/loader-2";
import type { StackqReceipt } from "$lib/types/receipts";
import type { ReceiptLineItem } from "$lib/types/receipts";
@@ -122,6 +123,7 @@
let imageFileName = $state<string | null>(null);
let selectedImageFile = $state<File | null>(null);
let imageError = $state(false);
let imageConverting = $state(false);
/** On small screens image is collapsed by default; on lg+ expanded. */
let imageOpen = $state(false);
@@ -141,6 +143,10 @@
imageFileName = null;
return;
}
imageConverting = true;
selectedImageFile = null;
imageFileName = null;
await new Promise((r) => setTimeout(r, 0));
try {
const webpFile = await convertImageToWebP(file);
selectedImageFile = webpFile;
@@ -149,6 +155,8 @@
selectedImageFile = null;
imageFileName = null;
if (input) input.value = "";
} finally {
imageConverting = false;
}
}
function imageLoadError() {
@@ -247,12 +255,23 @@
tabindex="-1"
aria-label={receipt.Image ? "Replace image" : "Add image"}
/>
<label
for="receipt-image-input"
class="border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex h-8 cursor-pointer items-center justify-center rounded-md border px-3 text-sm font-medium shadow-xs transition-colors"
>
{receipt.Image ? "Replace image" : "Add image"}
</label>
{#if imageConverting}
<div
class="border-input bg-background inline-flex h-8 cursor-not-allowed items-center justify-center gap-2 rounded-md border px-3 text-sm font-medium opacity-80"
aria-busy="true"
aria-live="polite"
>
<Loader2Icon class="size-4 shrink-0 animate-spin" aria-hidden />
<span>Processing image…</span>
</div>
{:else}
<label
for="receipt-image-input"
class="border-input bg-background hover:bg-accent hover:text-accent-foreground inline-flex h-8 cursor-pointer items-center justify-center rounded-md border px-3 text-sm font-medium shadow-xs transition-colors"
>
{receipt.Image ? "Replace image" : "Add image"}
</label>
{/if}
{#if imageFileName}
<span class="text-muted-foreground text-xs">New: {imageFileName}</span>
{/if}