Enhance routing in SvelteKit project by adding new receipt-related routes, updating existing route parameters, and improving layout handling. Refactor components to streamline UI interactions and remove unused imports for better performance.
CI / build (push) Has been skipped
CI / deploy (push) Successful in 1m19s

This commit is contained in:
eewing
2026-02-18 10:51:09 -06:00
parent a82c89674a
commit 809c784209
31 changed files with 253 additions and 104 deletions
+13 -7
View File
@@ -9,19 +9,25 @@ export const nodes = [
() => import('./nodes/5'), () => import('./nodes/5'),
() => import('./nodes/6'), () => import('./nodes/6'),
() => import('./nodes/7'), () => import('./nodes/7'),
() => import('./nodes/8') () => import('./nodes/8'),
() => import('./nodes/9'),
() => import('./nodes/10'),
() => import('./nodes/11')
]; ];
export const server_loads = []; export const server_loads = [];
export const dictionary = { export const dictionary = {
"/": [~2], "/": [~2],
"/accounts": [5], "/accounts": [8],
"/items": [~6], "/items": [~9],
"/items/[id]": [~7], "/items/[id]": [~10],
"/login": [~8], "/login": [~11],
"/logout": [~3], "/logout": [~6],
"/transactions": [4] "/receipts": [~3],
"/receipts/new": [4],
"/receipts/[id]": [5],
"/transactions": [7]
}; };
export const hooks = { export const hooks = {
+1
View File
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/items/[id]/+page.svelte";
+1
View File
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/login/+page.svelte";
+1
View File
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/receipts/+page.svelte";
+1 -1
View File
@@ -1 +1 @@
export { default as component } from "../../../../src/routes/transactions/+page.svelte"; export { default as component } from "../../../../src/routes/receipts/new/+page.svelte";
+1 -1
View File
@@ -1 +1 @@
export { default as component } from "../../../../src/routes/accounts/+page.svelte"; export { default as component } from "../../../../src/routes/receipts/[id]/+page.svelte";
-1
View File
@@ -1 +0,0 @@
export { default as component } from "../../../../src/routes/items/+page.svelte";
+1 -1
View File
@@ -1 +1 @@
export { default as component } from "../../../../src/routes/items/[id]/+page.svelte"; export { default as component } from "../../../../src/routes/transactions/+page.svelte";
+1 -1
View File
@@ -1 +1 @@
export { default as component } from "../../../../src/routes/login/+page.svelte"; export { default as component } from "../../../../src/routes/accounts/+page.svelte";
+1
View File
@@ -0,0 +1 @@
export { default as component } from "../../../../src/routes/items/+page.svelte";
+1 -1
View File
@@ -21,7 +21,7 @@ export const options = {
service_worker: false, service_worker: false,
service_worker_options: undefined, service_worker_options: undefined,
templates: { templates: {
app: ({ head, body, assets, nonce, env }) => "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <link rel=\"icon\" href=\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='4' fill='%231e293b'/><text x='16' y='22' font-size='18' font-family='system-ui' fill='%2394a3b8' text-anchor='middle'>S</text></svg>\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\" />\n <meta name=\"theme-color\" content=\"#0f172a\" />\n <meta name=\"apple-mobile-web-app-capable\" content=\"yes\" />\n <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\" />\n " + head + "\n </head>\n <body data-sveltekit-preload-data=\"hover\">\n <div style=\"display: contents\">" + body + "</div>\n </body>\n</html>\n", app: ({ head, body, assets, nonce, env }) => "<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <link rel=\"icon\" href=\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='4' fill='%231e293b'/><text x='16' y='22' font-size='18' font-family='system-ui' fill='%2394a3b8' text-anchor='middle'>S</text></svg>\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=5, viewport-fit=cover\" />\n <meta name=\"theme-color\" content=\"#0f172a\" />\n <meta name=\"apple-mobile-web-app-capable\" content=\"yes\" />\n <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\" />\n " + head + "\n </head>\n <body data-sveltekit-preload-data=\"hover\">\n <div style=\"display: contents\">" + body + "</div>\n </body>\n</html>\n",
error: ({ status, message }) => "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<title>" + message + "</title>\n\n\t\t<style>\n\t\t\tbody {\n\t\t\t\t--bg: white;\n\t\t\t\t--fg: #222;\n\t\t\t\t--divider: #ccc;\n\t\t\t\tbackground: var(--bg);\n\t\t\t\tcolor: var(--fg);\n\t\t\t\tfont-family:\n\t\t\t\t\tsystem-ui,\n\t\t\t\t\t-apple-system,\n\t\t\t\t\tBlinkMacSystemFont,\n\t\t\t\t\t'Segoe UI',\n\t\t\t\t\tRoboto,\n\t\t\t\t\tOxygen,\n\t\t\t\t\tUbuntu,\n\t\t\t\t\tCantarell,\n\t\t\t\t\t'Open Sans',\n\t\t\t\t\t'Helvetica Neue',\n\t\t\t\t\tsans-serif;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.error {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tmax-width: 32rem;\n\t\t\t\tmargin: 0 1rem;\n\t\t\t}\n\n\t\t\t.status {\n\t\t\t\tfont-weight: 200;\n\t\t\t\tfont-size: 3rem;\n\t\t\t\tline-height: 1;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: -0.05rem;\n\t\t\t}\n\n\t\t\t.message {\n\t\t\t\tborder-left: 1px solid var(--divider);\n\t\t\t\tpadding: 0 0 0 1rem;\n\t\t\t\tmargin: 0 0 0 1rem;\n\t\t\t\tmin-height: 2.5rem;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t}\n\n\t\t\t.message h1 {\n\t\t\t\tfont-weight: 400;\n\t\t\t\tfont-size: 1em;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t@media (prefers-color-scheme: dark) {\n\t\t\t\tbody {\n\t\t\t\t\t--bg: #222;\n\t\t\t\t\t--fg: #ddd;\n\t\t\t\t\t--divider: #666;\n\t\t\t\t}\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<div class=\"error\">\n\t\t\t<span class=\"status\">" + status + "</span>\n\t\t\t<div class=\"message\">\n\t\t\t\t<h1>" + message + "</h1>\n\t\t\t</div>\n\t\t</div>\n\t</body>\n</html>\n" error: ({ status, message }) => "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<title>" + message + "</title>\n\n\t\t<style>\n\t\t\tbody {\n\t\t\t\t--bg: white;\n\t\t\t\t--fg: #222;\n\t\t\t\t--divider: #ccc;\n\t\t\t\tbackground: var(--bg);\n\t\t\t\tcolor: var(--fg);\n\t\t\t\tfont-family:\n\t\t\t\t\tsystem-ui,\n\t\t\t\t\t-apple-system,\n\t\t\t\t\tBlinkMacSystemFont,\n\t\t\t\t\t'Segoe UI',\n\t\t\t\t\tRoboto,\n\t\t\t\t\tOxygen,\n\t\t\t\t\tUbuntu,\n\t\t\t\t\tCantarell,\n\t\t\t\t\t'Open Sans',\n\t\t\t\t\t'Helvetica Neue',\n\t\t\t\t\tsans-serif;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.error {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tmax-width: 32rem;\n\t\t\t\tmargin: 0 1rem;\n\t\t\t}\n\n\t\t\t.status {\n\t\t\t\tfont-weight: 200;\n\t\t\t\tfont-size: 3rem;\n\t\t\t\tline-height: 1;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: -0.05rem;\n\t\t\t}\n\n\t\t\t.message {\n\t\t\t\tborder-left: 1px solid var(--divider);\n\t\t\t\tpadding: 0 0 0 1rem;\n\t\t\t\tmargin: 0 0 0 1rem;\n\t\t\t\tmin-height: 2.5rem;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t}\n\n\t\t\t.message h1 {\n\t\t\t\tfont-weight: 400;\n\t\t\t\tfont-size: 1em;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t@media (prefers-color-scheme: dark) {\n\t\t\t\tbody {\n\t\t\t\t\t--bg: #222;\n\t\t\t\t\t--fg: #ddd;\n\t\t\t\t\t--divider: #666;\n\t\t\t\t}\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<div class=\"error\">\n\t\t\t<span class=\"status\">" + status + "</span>\n\t\t\t<div class=\"message\">\n\t\t\t\t<h1>" + message + "</h1>\n\t\t\t</div>\n\t\t</div>\n\t</body>\n</html>\n"
}, },
version_hash: "ekcm0i" version_hash: "ekcm0i"
+7 -3
View File
@@ -27,9 +27,10 @@ export {};
declare module "$app/types" { declare module "$app/types" {
export interface AppTypes { export interface AppTypes {
RouteId(): "/" | "/access-denied" | "/accounts" | "/items" | "/items/[id]" | "/login" | "/logout" | "/transactions"; RouteId(): "/" | "/access-denied" | "/accounts" | "/items" | "/items/[id]" | "/login" | "/logout" | "/receipts" | "/receipts/new" | "/receipts/[id]" | "/transactions";
RouteParams(): { RouteParams(): {
"/items/[id]": { id: string } "/items/[id]": { id: string };
"/receipts/[id]": { id: string }
}; };
LayoutParams(): { LayoutParams(): {
"/": { id?: string }; "/": { id?: string };
@@ -39,9 +40,12 @@ declare module "$app/types" {
"/items/[id]": { id: string }; "/items/[id]": { id: string };
"/login": Record<string, never>; "/login": Record<string, never>;
"/logout": Record<string, never>; "/logout": Record<string, never>;
"/receipts": { id?: string };
"/receipts/new": Record<string, never>;
"/receipts/[id]": { id: string };
"/transactions": Record<string, never> "/transactions": Record<string, never>
}; };
Pathname(): "/" | "/accounts" | "/items" | `/items/${string}` & {} | "/login" | "/logout" | "/transactions"; Pathname(): "/" | "/accounts" | "/items" | `/items/${string}` & {} | "/login" | "/logout" | "/receipts" | "/receipts/new" | `/receipts/${string}` & {} | "/transactions";
ResolvedPathname(): `${"" | `/${string}`}${ReturnType<AppTypes['Pathname']>}`; ResolvedPathname(): `${"" | `/${string}`}${ReturnType<AppTypes['Pathname']>}`;
Asset(): string & {}; Asset(): string & {};
} }
+10
View File
@@ -23,6 +23,16 @@
"src/routes/logout/+page.server.ts", "src/routes/logout/+page.server.ts",
"src/routes/+layout.ts" "src/routes/+layout.ts"
], ],
"/receipts": [
"src/routes/receipts/+page.server.ts",
"src/routes/+layout.ts"
],
"/receipts/new": [
"src/routes/+layout.ts"
],
"/receipts/[id]": [
"src/routes/+layout.ts"
],
"/transactions": [ "/transactions": [
"src/routes/+layout.ts" "src/routes/+layout.ts"
] ]
+1 -1
View File
@@ -13,7 +13,7 @@ type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends
export type Snapshot<T = any> = Kit.Snapshot<T>; export type Snapshot<T = any> = Kit.Snapshot<T>;
type PageServerParentData = EnsureDefined<LayoutServerData>; type PageServerParentData = EnsureDefined<LayoutServerData>;
type PageParentData = EnsureDefined<LayoutData>; type PageParentData = EnsureDefined<LayoutData>;
type LayoutRouteId = RouteId | "/" | "/logout" | "/transactions" | "/accounts" | "/items" | "/items/[id]" | "/login" | null type LayoutRouteId = RouteId | "/" | "/receipts" | "/receipts/new" | "/receipts/[id]" | "/logout" | "/transactions" | "/accounts" | "/items" | "/items/[id]" | "/login" | null
type LayoutParams = RouteParams & { id?: string } type LayoutParams = RouteParams & { id?: string }
type LayoutParentData = EnsureDefined<{}>; type LayoutParentData = EnsureDefined<{}>;
+25
View File
@@ -0,0 +1,25 @@
import type * as Kit from '@sveltejs/kit';
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
// @ts-ignore
type MatcherParam<M> = M extends (param : string) => param is infer U ? U extends string ? U : string : string;
type RouteParams = { };
type RouteId = '/receipts';
type MaybeWithVoid<T> = {} extends T ? T | void : T;
export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];
type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>
type EnsureDefined<T> = T extends null | undefined ? {} : T;
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
export type Snapshot<T = any> = Kit.Snapshot<T>;
type PageServerParentData = EnsureDefined<import('../$types.js').LayoutServerData>;
type PageParentData = EnsureDefined<import('../$types.js').LayoutData>;
export type PageServerLoad<OutputData extends OutputDataShape<PageServerParentData> = OutputDataShape<PageServerParentData>> = Kit.ServerLoad<RouteParams, PageServerParentData, OutputData, RouteId>;
export type PageServerLoadEvent = Parameters<PageServerLoad>[0];
export type ActionData = unknown;
export type PageServerData = Expand<OptionalUnion<EnsureDefined<Kit.LoadProperties<Awaited<ReturnType<typeof import('./proxy+page.server.js').load>>>>>>;
export type PageData = Expand<Omit<PageParentData, keyof PageServerData> & EnsureDefined<PageServerData>>;
export type Action<OutputData extends Record<string, any> | void = Record<string, any> | void> = Kit.Action<RouteParams, OutputData, RouteId>
export type Actions<OutputData extends Record<string, any> | void = Record<string, any> | void> = Kit.Actions<RouteParams, OutputData, RouteId>
export type PageProps = { params: RouteParams; data: PageData; form: ActionData }
export type RequestEvent = Kit.RequestEvent<RouteParams, RouteId>;
+19
View File
@@ -0,0 +1,19 @@
import type * as Kit from '@sveltejs/kit';
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
// @ts-ignore
type MatcherParam<M> = M extends (param : string) => param is infer U ? U extends string ? U : string : string;
type RouteParams = { id: string };
type RouteId = '/receipts/[id]';
type MaybeWithVoid<T> = {} extends T ? T | void : T;
export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];
type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>
type EnsureDefined<T> = T extends null | undefined ? {} : T;
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
export type Snapshot<T = any> = Kit.Snapshot<T>;
type PageParentData = EnsureDefined<import('../../$types.js').LayoutData>;
export type EntryGenerator = () => Promise<Array<RouteParams>> | Array<RouteParams>;
export type PageServerData = null;
export type PageData = Expand<PageParentData>;
export type PageProps = { params: RouteParams; data: PageData }
+18
View File
@@ -0,0 +1,18 @@
import type * as Kit from '@sveltejs/kit';
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
// @ts-ignore
type MatcherParam<M> = M extends (param : string) => param is infer U ? U extends string ? U : string : string;
type RouteParams = { };
type RouteId = '/receipts/new';
type MaybeWithVoid<T> = {} extends T ? T | void : T;
export type RequiredKeys<T> = { [K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K; }[keyof T];
type OutputDataShape<T> = MaybeWithVoid<Omit<App.PageData, RequiredKeys<T>> & Partial<Pick<App.PageData, keyof T & keyof App.PageData>> & Record<string, any>>
type EnsureDefined<T> = T extends null | undefined ? {} : T;
type OptionalUnion<U extends Record<string, any>, A extends keyof U = U extends U ? keyof U : never> = U extends unknown ? { [P in Exclude<A, keyof U>]?: never } & U : never;
export type Snapshot<T = any> = Kit.Snapshot<T>;
type PageParentData = EnsureDefined<import('../../$types.js').LayoutData>;
export type PageServerData = null;
export type PageData = Expand<PageParentData>;
export type PageProps = { params: RouteParams; data: PageData }
@@ -0,0 +1,8 @@
// @ts-nocheck
import type { PageServerLoad } from "./$types";
export const load = async () => {
// Placeholder: no PocketBase receipts collection yet. Return empty list.
return { receipts: [] as { id: string }[] };
};
;null as any as PageServerLoad;
+1 -1
View File
@@ -3,7 +3,7 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='4' fill='%231e293b'/><text x='16' y='22' font-size='18' font-family='system-ui' fill='%2394a3b8' text-anchor='middle'>S</text></svg>" /> <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='4' fill='%231e293b'/><text x='16' y='22' font-size='18' font-family='system-ui' fill='%2394a3b8' text-anchor='middle'>S</text></svg>" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" /> <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=5, viewport-fit=cover" />
<meta name="theme-color" content="#0f172a" /> <meta name="theme-color" content="#0f172a" />
<meta name="apple-mobile-web-app-capable" content="yes" /> <meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
@@ -2,7 +2,6 @@
import { Button } from "$lib/components/ui/button"; import { Button } from "$lib/components/ui/button";
import { Input } from "$lib/components/ui/input"; import { Input } from "$lib/components/ui/input";
import * as Dialog from "$lib/components/ui/dialog"; import * as Dialog from "$lib/components/ui/dialog";
import { Search, X } from "@lucide/svelte";
let { let {
open = $bindable(false), open = $bindable(false),
@@ -152,7 +151,6 @@
class="min-h-11 flex-1" class="min-h-11 flex-1"
/> />
<Button type="button" class="min-h-11 shrink-0" onclick={submitSearch} disabled={!searchInput.trim()}> <Button type="button" class="min-h-11 shrink-0" onclick={submitSearch} disabled={!searchInput.trim()}>
<Search class="mr-2 size-4" />
Search Search
</Button> </Button>
</div> </div>
@@ -176,7 +174,6 @@
class="min-h-12 flex-1" class="min-h-12 flex-1"
onclick={() => { stopScan(); open = false; }} onclick={() => { stopScan(); open = false; }}
> >
<X class="mr-2 size-4" />
Cancel Cancel
</Button> </Button>
</div> </div>
+14 -41
View File
@@ -4,8 +4,6 @@
import ItemForm from "./items/ItemForm.svelte"; import ItemForm from "./items/ItemForm.svelte";
import BarcodeScanDialog from "$lib/components/BarcodeScanDialog.svelte"; import BarcodeScanDialog from "$lib/components/BarcodeScanDialog.svelte";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import { Camera, Package, Receipt, Wallet } from "@lucide/svelte";
let { let {
data, data,
}: { }: {
@@ -37,58 +35,33 @@
<h1 class="w-full max-w-xs text-center text-2xl font-semibold">Stackq</h1> <h1 class="w-full max-w-xs text-center text-2xl font-semibold">Stackq</h1>
<div class="flex w-full max-w-xs flex-col gap-3"> <div class="flex w-full max-w-xs flex-col gap-3">
<p class="text-muted-foreground text-center text-sm font-medium">Quick actions</p> <Button class="h-16 min-h-16 w-full" onclick={() => (scanDialogOpen = true)}>Scan</Button>
<Button class="h-16 min-h-16 w-full" onclick={() => (scanDialogOpen = true)}>
<span class="flex w-full flex-col items-center justify-center gap-1">
<Camera class="size-5" />
<span class="text-sm font-medium leading-none">Scan</span>
</span>
</Button>
<a href="/transactions" class="block"> <a href="/transactions" class="block">
<Button class="h-16 min-h-16 w-full" variant="outline"> <Button class="h-16 min-h-16 w-full" variant="outline">New Transaction</Button>
<span class="flex w-full flex-col items-center justify-center gap-1">
<Receipt class="size-5" />
<span class="text-sm font-medium leading-none">New Transaction</span>
</span>
</Button>
</a> </a>
<Button class="h-16 min-h-16 w-full" variant="outline" onclick={() => (dialogOpen = true)}> <a href="/receipts/new" class="block">
<span class="flex w-full flex-col items-center justify-center gap-1"> <Button class="h-16 min-h-16 w-full" variant="outline">New receipt</Button>
<Package class="size-5" /> </a>
<span class="text-sm font-medium leading-none">New Item</span> <Button class="h-16 min-h-16 w-full" variant="outline" onclick={() => (dialogOpen = true)}>New Item</Button>
</span>
</Button>
</div> </div>
<div class="text-muted-foreground w-full max-w-xs border-t pt-4 text-center text-sm">Go to</div> <div class="w-full max-w-xs border-t pt-4"></div>
<div class="flex w-full max-w-xs flex-col gap-2"> <div class="flex w-full max-w-xs flex-col gap-2">
<a href="/items" class="block"> <a href="/items" class="block">
<Button class="h-16 min-h-16 w-full" variant="outline"> <Button class="h-16 min-h-16 w-full" variant="outline">Items</Button>
<span class="flex w-full flex-col items-center justify-center gap-1">
<Package class="size-4" />
<span class="text-sm font-medium leading-none">Items</span>
</span>
</Button>
</a> </a>
<a href="/transactions" class="block"> <a href="/transactions" class="block">
<Button class="h-16 min-h-16 w-full" variant="outline"> <Button class="h-16 min-h-16 w-full" variant="outline">Transactions</Button>
<span class="flex w-full flex-col items-center justify-center gap-1"> </a>
<Receipt class="size-4" /> <a href="/receipts" class="block">
<span class="text-sm font-medium leading-none">Transactions</span> <Button class="h-16 min-h-16 w-full" variant="outline">Receipts</Button>
</span>
</Button>
</a> </a>
<a href="/accounts" class="block"> <a href="/accounts" class="block">
<Button class="h-16 min-h-16 w-full" variant="outline"> <Button class="h-16 min-h-16 w-full" variant="outline">Accounts</Button>
<span class="flex w-full flex-col items-center justify-center gap-1">
<Wallet class="size-4" />
<span class="text-sm font-medium leading-none">Accounts</span>
</span>
</Button>
</a> </a>
<a href="/logout" class="block"> <a href="/logout" class="block">
<Button class="h-16 min-h-16 w-full justify-center" variant="outline">Log out</Button> <Button class="h-16 min-h-16 w-full" variant="outline">Log out</Button>
</a> </a>
</div> </div>
</main> </main>
+1 -4
View File
@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Button } from "$lib/components/ui/button"; import { Button } from "$lib/components/ui/button";
import { Card, CardContent } from "$lib/components/ui/card"; import { Card, CardContent } from "$lib/components/ui/card";
import { Home } from "@lucide/svelte";
</script> </script>
<svelte:head> <svelte:head>
@@ -10,9 +9,7 @@
<main class="container mx-auto flex flex-col gap-6 px-4 py-6 sm:px-6 sm:py-8"> <main class="container mx-auto flex flex-col gap-6 px-4 py-6 sm:px-6 sm:py-8">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<a href="/" class="touch-target text-muted-foreground hover:text-foreground shrink-0" title="Back to main"> <a href="/" class="text-muted-foreground hover:text-foreground shrink-0 text-sm" title="Back to main">Back</a>
<Home class="size-5" />
</a>
<h1 class="text-xl font-semibold sm:text-2xl">Accounts</h1> <h1 class="text-xl font-semibold sm:text-2xl">Accounts</h1>
</div> </div>
+1 -5
View File
@@ -3,7 +3,6 @@
import { Input } from "$lib/components/ui/input"; import { Input } from "$lib/components/ui/input";
import { Label } from "$lib/components/ui/label"; import { Label } from "$lib/components/ui/label";
import { enhance } from "$app/forms"; import { enhance } from "$app/forms";
import { Camera, X } from "@lucide/svelte";
import { POCKETBASE_BASE_URL } from "$lib/pocketbase"; import { POCKETBASE_BASE_URL } from "$lib/pocketbase";
import { UOM_OPTIONS, CATAGORY_OPTIONS, SUB_CATAGORY_OPTIONS } from "$lib/constants/items"; import { UOM_OPTIONS, CATAGORY_OPTIONS, SUB_CATAGORY_OPTIONS } from "$lib/constants/items";
import type { StackqItem } from "$lib/types/items"; import type { StackqItem } from "$lib/types/items";
@@ -169,9 +168,7 @@
<Label for="SKU">SKU</Label> <Label for="SKU">SKU</Label>
<div class="flex gap-2"> <div class="flex gap-2">
<Input id="SKU" name="SKU" bind:value={skuValue} class="flex-1" /> <Input id="SKU" name="SKU" bind:value={skuValue} class="flex-1" />
<Button type="button" variant="outline" size="icon" onclick={startScan} title="Scan barcode"> <Button type="button" variant="outline" onclick={startScan} title="Scan barcode">Scan</Button>
<Camera class="size-4" />
</Button>
</div> </div>
</div> </div>
</div> </div>
@@ -195,7 +192,6 @@
{/if} {/if}
<div class="flex gap-2"> <div class="flex gap-2">
<Button type="button" variant="outline" class="flex-1" onclick={stopScan}> <Button type="button" variant="outline" class="flex-1" onclick={stopScan}>
<X class="mr-2 size-4" />
Cancel Cancel
</Button> </Button>
</div> </div>
+7 -20
View File
@@ -9,7 +9,6 @@
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import BarcodeScanDialog from "$lib/components/BarcodeScanDialog.svelte"; import BarcodeScanDialog from "$lib/components/BarcodeScanDialog.svelte";
import ItemForm from "./ItemForm.svelte"; import ItemForm from "./ItemForm.svelte";
import { Camera, Ellipsis, ExternalLink, Home, Pencil, Plus, Search, Trash2 } from "@lucide/svelte";
import type { StackqItem } from "$lib/types/items"; import type { StackqItem } from "$lib/types/items";
let { let {
@@ -118,19 +117,16 @@
<main class="container mx-auto flex flex-col gap-4 px-4 py-6 sm:gap-6 sm:px-6 sm:py-8"> <main class="container mx-auto flex flex-col gap-4 px-4 py-6 sm:gap-6 sm:px-6 sm:py-8">
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"> <div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div class="flex items-center gap-2 min-w-0"> <div class="flex items-center gap-2 min-w-0">
<a href="/" class="touch-target text-muted-foreground hover:text-foreground shrink-0" title="Back to main"> <a href="/" class="text-muted-foreground hover:text-foreground shrink-0 text-sm" title="Back to main">Back</a>
<Home class="size-5" />
</a>
<h1 class="text-xl font-semibold truncate sm:text-2xl">Stackq Items</h1> <h1 class="text-xl font-semibold truncate sm:text-2xl">Stackq Items</h1>
</div> </div>
<div class="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center sm:gap-2"> <div class="flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center sm:gap-2">
<div class="relative w-full sm:w-64 sm:min-w-0"> <div class="w-full sm:w-64 sm:min-w-0">
<Search class="text-muted-foreground absolute left-3 top-1/2 size-4 -translate-y-1/2" />
<Input <Input
type="search" type="search"
placeholder="Search items, SKU…" placeholder="Search items, SKU…"
bind:value={searchQuery} bind:value={searchQuery}
class="h-11 w-full pl-9 sm:h-9" class="h-11 w-full sm:h-9"
/> />
</div> </div>
<span class="text-muted-foreground text-sm shrink-0"> <span class="text-muted-foreground text-sm shrink-0">
@@ -138,11 +134,9 @@
</span> </span>
<div class="flex gap-2"> <div class="flex gap-2">
<Button variant="outline" onclick={() => (scanDialogOpen = true)} title="Scan barcode to look up item" class="min-h-11 flex-1 sm:flex-none"> <Button variant="outline" onclick={() => (scanDialogOpen = true)} title="Scan barcode to look up item" class="min-h-11 flex-1 sm:flex-none">
<Camera class="mr-2 size-4 shrink-0" />
Scan Scan
</Button> </Button>
<Button onclick={openCreate} class="min-h-11 flex-1 sm:flex-none"> <Button onclick={openCreate} class="min-h-11 flex-1 sm:flex-none">
<Plus class="mr-2 size-4 shrink-0" />
New item New item
</Button> </Button>
</div> </div>
@@ -197,20 +191,14 @@
</div> </div>
<DropdownMenu.Root> <DropdownMenu.Root>
<DropdownMenu.Trigger <DropdownMenu.Trigger
class="border-0 bg-transparent hover:bg-muted inline-flex min-h-9 min-w-9 shrink-0 items-center justify-center rounded-md" class="border-0 bg-transparent hover:bg-muted inline-flex min-h-9 min-w-9 shrink-0 items-center justify-center rounded-md text-sm"
> >
<Ellipsis class="size-4" /> Actions
</DropdownMenu.Trigger> </DropdownMenu.Trigger>
<DropdownMenu.Portal> <DropdownMenu.Portal>
<DropdownMenu.Content align="end"> <DropdownMenu.Content align="end">
<DropdownMenu.Item onclick={() => goto("/items/" + item.id)}> <DropdownMenu.Item onclick={() => goto("/items/" + item.id)}>View</DropdownMenu.Item>
<ExternalLink class="mr-2 size-4" /> <DropdownMenu.Item onclick={() => openEdit(item)}>Edit</DropdownMenu.Item>
View
</DropdownMenu.Item>
<DropdownMenu.Item onclick={() => openEdit(item)}>
<Pencil class="mr-2 size-4" />
Edit
</DropdownMenu.Item>
<form <form
id="delete-form-{item.id}" id="delete-form-{item.id}"
method="POST" method="POST"
@@ -228,7 +216,6 @@
} }
}} }}
> >
<Trash2 class="mr-2 size-4" />
Delete Delete
</DropdownMenu.Item> </DropdownMenu.Item>
</DropdownMenu.Content> </DropdownMenu.Content>
+2 -9
View File
@@ -6,7 +6,6 @@
import * as Dialog from "$lib/components/ui/dialog"; import * as Dialog from "$lib/components/ui/dialog";
import { enhance } from "$app/forms"; import { enhance } from "$app/forms";
import ItemForm from "../ItemForm.svelte"; import ItemForm from "../ItemForm.svelte";
import { ArrowLeft, Home, Pencil, Trash2 } from "@lucide/svelte";
let { let {
data, data,
@@ -63,17 +62,12 @@
{#if item} {#if item}
<main class="container mx-auto flex flex-col gap-4 px-4 py-6 sm:gap-6 sm:px-6 sm:py-8"> <main class="container mx-auto flex flex-col gap-4 px-4 py-6 sm:gap-6 sm:px-6 sm:py-8">
<div class="flex flex-wrap items-center gap-2 sm:gap-4 min-w-0"> <div class="flex flex-wrap items-center gap-2 sm:gap-4 min-w-0">
<a href="/" class="touch-target text-muted-foreground hover:text-foreground shrink-0" title="Back to main"> <a href="/" class="text-muted-foreground hover:text-foreground shrink-0 text-sm" title="Back to main">Home</a>
<Home class="size-5" /> <a href="/items" class="text-muted-foreground hover:text-foreground shrink-0 text-sm" title="Back to items">Back to items</a>
</a>
<a href="/items" class="touch-target text-muted-foreground hover:text-foreground shrink-0" title="Back to items">
<ArrowLeft class="size-5" />
</a>
<h1 class="text-xl font-semibold truncate min-w-0 flex-1 sm:text-2xl">{item.Item ?? "—"}</h1> <h1 class="text-xl font-semibold truncate min-w-0 flex-1 sm:text-2xl">{item.Item ?? "—"}</h1>
{#if browser} {#if browser}
<div class="flex gap-2 w-full sm:w-auto"> <div class="flex gap-2 w-full sm:w-auto">
<Button variant="outline" onclick={() => (dialogOpen = true)} class="min-h-11 flex-1 sm:flex-none"> <Button variant="outline" onclick={() => (dialogOpen = true)} class="min-h-11 flex-1 sm:flex-none">
<Pencil class="mr-2 size-4" />
Edit Edit
</Button> </Button>
<form <form
@@ -93,7 +87,6 @@
} }
}} }}
> >
<Trash2 class="mr-2 size-4" />
Delete Delete
</Button> </Button>
</form> </form>
+7
View File
@@ -102,6 +102,13 @@
* { * {
@apply border-border; @apply border-border;
} }
html {
/* Match device text scaling (prevent iOS from inflating text) */
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
/* Slightly smaller base so layout scales better on small screens */
font-size: clamp(14px, 2.5vw + 12px, 16px);
}
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
/* Safe area for notched devices */ /* Safe area for notched devices */
+6
View File
@@ -0,0 +1,6 @@
import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async () => {
// Placeholder: no PocketBase receipts collection yet. Return empty list.
return { receipts: [] as { id: string }[] };
};
+48
View File
@@ -0,0 +1,48 @@
<script lang="ts">
import { Button } from "$lib/components/ui/button";
import { Card, CardContent } from "$lib/components/ui/card";
let { data }: { data: { receipts: { id: string }[] } } = $props();
const receipts = $derived(data?.receipts ?? []);
</script>
<svelte:head>
<title>Receipts Stackq</title>
</svelte:head>
<main class="container mx-auto flex flex-col gap-6 px-4 py-6 sm:px-6 sm:py-8">
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div class="flex items-center gap-3">
<a href="/" class="text-muted-foreground hover:text-foreground shrink-0 text-sm" title="Back to main">Back</a>
<h1 class="text-xl font-semibold sm:text-2xl">Receipts</h1>
</div>
<a href="/receipts/new" class="block shrink-0">
<Button class="w-full sm:w-auto">New receipt</Button>
</a>
</div>
{#if receipts.length === 0}
<Card>
<CardContent class="flex flex-col items-center justify-center gap-4 py-12">
<p class="text-muted-foreground text-center text-sm">No receipts yet.</p>
<p class="text-muted-foreground text-center text-xs">Create a receipt to get started.</p>
<a href="/receipts/new">
<Button variant="outline">New receipt</Button>
</a>
<a href="/">
<Button variant="ghost">Back to home</Button>
</a>
</CardContent>
</Card>
{:else}
<div class="flex flex-col gap-2">
{#each receipts as receipt (receipt.id)}
<Card>
<CardContent class="p-4">
<a href="/receipts/{receipt.id}" class="font-medium hover:underline">Receipt {receipt.id}</a>
</CardContent>
</Card>
{/each}
</div>
{/if}
</main>
+26
View File
@@ -0,0 +1,26 @@
<script lang="ts">
import { Button } from "$lib/components/ui/button";
import { Card, CardContent } from "$lib/components/ui/card";
let { data, params }: { data: unknown; params: { id: string } } = $props();
</script>
<svelte:head>
<title>Receipt Stackq</title>
</svelte:head>
<main class="container mx-auto flex flex-col gap-6 px-4 py-6 sm:px-6 sm:py-8">
<div class="flex items-center gap-3">
<a href="/receipts" class="text-muted-foreground hover:text-foreground shrink-0 text-sm" title="Back to receipts">Back</a>
<h1 class="text-xl font-semibold sm:text-2xl">Receipt</h1>
</div>
<Card>
<CardContent class="flex flex-col gap-4 py-8">
<p class="text-muted-foreground text-sm">Receipt {params.id}</p>
<a href="/receipts">
<Button variant="outline">Back to receipts</Button>
</a>
</CardContent>
</Card>
</main>
+29
View File
@@ -0,0 +1,29 @@
<script lang="ts">
import { Button } from "$lib/components/ui/button";
import { Card, CardContent } from "$lib/components/ui/card";
</script>
<svelte:head>
<title>New receipt Stackq</title>
</svelte:head>
<main class="container mx-auto flex flex-col gap-6 px-4 py-6 sm:px-6 sm:py-8">
<div class="flex items-center gap-3">
<a href="/receipts" class="text-muted-foreground hover:text-foreground shrink-0 text-sm" title="Back to receipts">Back</a>
<h1 class="text-xl font-semibold sm:text-2xl">New receipt</h1>
</div>
<Card>
<CardContent class="flex flex-col gap-4 py-8">
<p class="text-muted-foreground text-center text-sm">Receipt creation will be available here.</p>
<div class="flex justify-center gap-2">
<a href="/receipts">
<Button variant="outline">Back to receipts</Button>
</a>
<a href="/">
<Button variant="ghost">Home</Button>
</a>
</div>
</CardContent>
</Card>
</main>
+1 -4
View File
@@ -1,7 +1,6 @@
<script lang="ts"> <script lang="ts">
import { Button } from "$lib/components/ui/button"; import { Button } from "$lib/components/ui/button";
import { Card, CardContent } from "$lib/components/ui/card"; import { Card, CardContent } from "$lib/components/ui/card";
import { Home } from "@lucide/svelte";
</script> </script>
<svelte:head> <svelte:head>
@@ -10,9 +9,7 @@
<main class="container mx-auto flex flex-col gap-6 px-4 py-6 sm:px-6 sm:py-8"> <main class="container mx-auto flex flex-col gap-6 px-4 py-6 sm:px-6 sm:py-8">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<a href="/" class="touch-target text-muted-foreground hover:text-foreground shrink-0" title="Back to main"> <a href="/" class="text-muted-foreground hover:text-foreground shrink-0 text-sm" title="Back to main">Back</a>
<Home class="size-5" />
</a>
<h1 class="text-xl font-semibold sm:text-2xl">Transactions</h1> <h1 class="text-xl font-semibold sm:text-2xl">Transactions</h1>
</div> </div>