Refactor environment variable declarations and update asset references in SvelteKit project. Removed unused variables and updated client entry points and stylesheets for improved performance.
CI / build (push) Has been skipped
CI / deploy (push) Successful in 1m40s

This commit is contained in:
eewing
2026-02-17 14:45:13 -06:00
parent 6cc5928770
commit cff20a652a
277 changed files with 1358 additions and 5724 deletions
+1 -5
View File
@@ -2,7 +2,7 @@
import { browser } from "$app/environment";
import * as Table from "$lib/components/ui/table";
import { Badge } from "$lib/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "$lib/components/ui/card";
import { Card, CardContent } from "$lib/components/ui/card";
import type { StackqItem } from "$lib/types/items";
let { data }: { data: { items: StackqItem[]; itemsForParent: { id: string; Item: string; SKU: string }[] } } = $props();
@@ -46,10 +46,6 @@
<span class="text-muted-foreground text-sm">{items.length} items</span>
</div>
<Card>
<CardHeader>
<CardTitle>Stackq_Items</CardTitle>
<p class="text-muted-foreground text-sm">PocketBase collection. Enable JS for create/edit/delete.</p>
</CardHeader>
<CardContent class="p-0">
<Table.Root>
<Table.Header>
+111 -1
View File
@@ -3,6 +3,7 @@
import { Input } from "$lib/components/ui/input";
import { Label } from "$lib/components/ui/label";
import { enhance } from "$app/forms";
import { Camera, X } from "@lucide/svelte";
import { UOM_OPTIONS, CATAGORY_OPTIONS, SUB_CATAGORY_OPTIONS } from "$lib/constants/items";
import type { StackqItem } from "$lib/types/items";
@@ -37,6 +38,83 @@
const defaultParent = $derived(item?.Parent ?? "");
const defaultHasParent = $derived(!!item?.Has_Parent);
const defaultStock = $derived(!!item?.Stock);
let skuValue = $state("");
let showScanner = $state(false);
let scanError = $state<string | null>(null);
let videoEl = $state<HTMLVideoElement | undefined>(undefined);
let stream: MediaStream | null = null;
let scanRAF = 0;
$effect(() => {
skuValue = defaultSku;
});
const hasBarcodeDetector = $derived(typeof globalThis !== "undefined" && "BarcodeDetector" in globalThis);
async function startScan() {
scanError = null;
if (!hasBarcodeDetector) {
scanError = "Barcode scan is not supported in this browser. Try Chrome or Edge.";
showScanner = true;
return;
}
try {
stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } });
showScanner = true;
} catch (e) {
scanError = e instanceof Error ? e.message : "Could not access camera.";
showScanner = true;
}
}
$effect(() => {
if (!showScanner || !stream || !videoEl) return;
videoEl.srcObject = stream;
videoEl.play().then(() => scanLoop()).catch(() => {});
return () => {
if (videoEl.srcObject) videoEl.srcObject = null;
};
});
function scanLoop() {
if (!showScanner || !videoEl || !("BarcodeDetector" in globalThis)) return;
const detector = new (globalThis as unknown as { BarcodeDetector: typeof BarcodeDetector }).BarcodeDetector();
async function detect() {
if (!showScanner || !videoEl || videoEl.readyState < 2) {
scanRAF = requestAnimationFrame(detect);
return;
}
try {
const barcodes = await detector.detect(videoEl);
if (barcodes.length > 0) {
skuValue = barcodes[0].rawValue;
stopScan();
return;
}
} catch {
// ignore frame errors
}
scanRAF = requestAnimationFrame(detect);
}
detect();
}
function stopScan() {
showScanner = false;
scanError = null;
if (scanRAF) {
cancelAnimationFrame(scanRAF);
scanRAF = 0;
}
if (stream) {
stream.getTracks().forEach((t) => t.stop());
stream = null;
}
if (videoEl && videoEl.srcObject) {
videoEl.srcObject = null;
}
}
</script>
<form
@@ -61,10 +139,42 @@
</div>
<div class="space-y-2">
<Label for="SKU">SKU</Label>
<Input id="SKU" name="SKU" value={defaultSku} />
<div class="flex gap-2">
<Input id="SKU" name="SKU" bind:value={skuValue} class="flex-1" />
<Button type="button" variant="outline" size="icon" onclick={startScan} title="Scan barcode">
<Camera class="size-4" />
</Button>
</div>
</div>
</div>
{#if showScanner}
<div
class="bg-background/95 fixed inset-0 z-50 flex flex-col items-center justify-center gap-4 p-4 backdrop-blur-sm"
role="dialog"
aria-label="Scan barcode"
>
<div class="flex w-full max-w-sm flex-col gap-2">
{#if scanError}
<p class="text-destructive text-sm">{scanError}</p>
{:else}
<video
bind:this={videoEl}
class="border-input w-full rounded-lg border"
muted
playsinline
></video>
{/if}
<div class="flex gap-2">
<Button type="button" variant="outline" class="flex-1" onclick={stopScan}>
<X class="mr-2 size-4" />
Cancel
</Button>
</div>
</div>
</div>
{/if}
<div class="space-y-2">
<Label for="Description">Description</Label>
<Input id="Description" name="Description" value={defaultDesc} />
+1 -7
View File
@@ -2,7 +2,7 @@
import * as Table from "$lib/components/ui/table";
import { Badge } from "$lib/components/ui/badge";
import { Button } from "$lib/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "$lib/components/ui/card";
import { Card, CardContent } from "$lib/components/ui/card";
import * as Dialog from "$lib/components/ui/dialog";
import * as DropdownMenu from "$lib/components/ui/dropdown-menu";
import { enhance } from "$app/forms";
@@ -88,12 +88,6 @@
</div>
<Card>
<CardHeader>
<CardTitle>Stackq_Items</CardTitle>
<p class="text-muted-foreground text-sm">
PocketBase collection: full CRUD.
</p>
</CardHeader>
<CardContent class="p-0">
<Table.Root>
<Table.Header>
+2 -2
View File
@@ -3,8 +3,8 @@ import type { Actions, PageServerLoad } from "./$types";
function safeRedirectTo(searchParams: URLSearchParams): string {
const to = searchParams.get("redirectTo");
if (!to || typeof to !== "string") return "/";
if (!to.startsWith("/") || to.startsWith("//")) return "/";
if (!to || typeof to !== "string") return "/items";
if (!to.startsWith("/") || to.startsWith("//")) return "/items";
return to;
}