From 948617a285381fa1c4028568917e814829bd898b Mon Sep 17 00:00:00 2001 From: grabowski Date: Tue, 7 Apr 2026 16:59:49 +0700 Subject: [PATCH] Add cascading location picker: select parent first, then child LocationPicker component shows parent locations first. Once a parent is selected, a second dropdown appears with its children. If the parent has no children, it's selected directly. Used in device create/edit, component create/edit, and installation log forms. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/lib/components/ui/LocationPicker.svelte | 71 +++++++++++++++++++ .../components/[id]/edit/+page.server.ts | 2 +- .../(app)/components/[id]/edit/+page.svelte | 9 +-- .../(app)/components/new/+page.server.ts | 5 +- src/routes/(app)/components/new/+page.svelte | 11 +-- .../(app)/devices/[id]/edit/+page.server.ts | 2 +- .../(app)/devices/[id]/edit/+page.svelte | 11 +-- src/routes/(app)/devices/new/+page.server.ts | 2 +- src/routes/(app)/devices/new/+page.svelte | 11 +-- .../(app)/installations/new/+page.server.ts | 5 +- .../(app)/installations/new/+page.svelte | 11 +-- 11 files changed, 94 insertions(+), 46 deletions(-) create mode 100644 src/lib/components/ui/LocationPicker.svelte diff --git a/src/lib/components/ui/LocationPicker.svelte b/src/lib/components/ui/LocationPicker.svelte new file mode 100644 index 0000000..6d2344e --- /dev/null +++ b/src/lib/components/ui/LocationPicker.svelte @@ -0,0 +1,71 @@ + + +
+ + + {#if selectedParent && children.length > 0} + + {/if} + + +
diff --git a/src/routes/(app)/components/[id]/edit/+page.server.ts b/src/routes/(app)/components/[id]/edit/+page.server.ts index 1d0e9fc..51a6d8d 100644 --- a/src/routes/(app)/components/[id]/edit/+page.server.ts +++ b/src/routes/(app)/components/[id]/edit/+page.server.ts @@ -22,7 +22,7 @@ export const load: PageServerLoad = async ({ params }) => { const [component] = await db.select().from(components).where(eq(components.id, params.id)); if (!component) error(404, 'Component not found'); - const locationList = await db.select({ id: locations.id, name: locations.name }).from(locations); + const locationList = await db.select({ id: locations.id, name: locations.name, parentId: locations.parentId }).from(locations).orderBy(locations.name); return { component, locations: locationList }; }; diff --git a/src/routes/(app)/components/[id]/edit/+page.svelte b/src/routes/(app)/components/[id]/edit/+page.svelte index c4b53c5..aecebdc 100644 --- a/src/routes/(app)/components/[id]/edit/+page.svelte +++ b/src/routes/(app)/components/[id]/edit/+page.svelte @@ -1,6 +1,7 @@ @@ -99,14 +100,8 @@
- - + Storage Location +
diff --git a/src/routes/(app)/devices/[id]/edit/+page.server.ts b/src/routes/(app)/devices/[id]/edit/+page.server.ts index 984e93e..082a0df 100644 --- a/src/routes/(app)/devices/[id]/edit/+page.server.ts +++ b/src/routes/(app)/devices/[id]/edit/+page.server.ts @@ -35,7 +35,7 @@ export const load: PageServerLoad = async ({ params }) => { compDetails = cd ?? null; } - const locationList = await db.select({ id: locations.id, name: locations.name }).from(locations); + const locationList = await db.select({ id: locations.id, name: locations.name, parentId: locations.parentId }).from(locations).orderBy(locations.name); return { device, computerDetails: compDetails, locations: locationList }; }; diff --git a/src/routes/(app)/devices/[id]/edit/+page.svelte b/src/routes/(app)/devices/[id]/edit/+page.svelte index 20d8668..61bd095 100644 --- a/src/routes/(app)/devices/[id]/edit/+page.svelte +++ b/src/routes/(app)/devices/[id]/edit/+page.svelte @@ -2,6 +2,7 @@ import { enhance } from '$app/forms'; import { DEVICE_CATEGORIES, DEVICE_CONDITIONS, VOLTAGE_OPTIONS, FREQUENCY_OPTIONS } from '$lib/constants.js'; import AutocompleteInput from '$lib/components/ui/AutocompleteInput.svelte'; + import LocationPicker from '$lib/components/ui/LocationPicker.svelte'; let { data, form } = $props(); @@ -157,14 +158,8 @@

Location & Notes

- - + Location +
diff --git a/src/routes/(app)/devices/new/+page.server.ts b/src/routes/(app)/devices/new/+page.server.ts index 733ff30..dd2cbf2 100644 --- a/src/routes/(app)/devices/new/+page.server.ts +++ b/src/routes/(app)/devices/new/+page.server.ts @@ -27,7 +27,7 @@ const deviceSchema = z.object({ }); export const load: PageServerLoad = async () => { - const locationList = await db.select({ id: locations.id, name: locations.name }).from(locations); + const locationList = await db.select({ id: locations.id, name: locations.name, parentId: locations.parentId }).from(locations).orderBy(locations.name); return { locations: locationList }; }; diff --git a/src/routes/(app)/devices/new/+page.svelte b/src/routes/(app)/devices/new/+page.svelte index 55979bd..6586dcd 100644 --- a/src/routes/(app)/devices/new/+page.svelte +++ b/src/routes/(app)/devices/new/+page.svelte @@ -2,6 +2,7 @@ import { enhance } from '$app/forms'; import { DEVICE_CATEGORIES, DEVICE_CONDITIONS, VOLTAGE_OPTIONS, FREQUENCY_OPTIONS } from '$lib/constants.js'; import AutocompleteInput from '$lib/components/ui/AutocompleteInput.svelte'; + import LocationPicker from '$lib/components/ui/LocationPicker.svelte'; let { data, form } = $props(); @@ -179,14 +180,8 @@

Location & Notes

- - + Location +
diff --git a/src/routes/(app)/installations/new/+page.server.ts b/src/routes/(app)/installations/new/+page.server.ts index bffa69a..bc3a738 100644 --- a/src/routes/(app)/installations/new/+page.server.ts +++ b/src/routes/(app)/installations/new/+page.server.ts @@ -32,8 +32,9 @@ export const load: PageServerLoad = async ({ url }) => { .from(components) .orderBy(components.title); const locationList = await db - .select({ id: locations.id, name: locations.name }) - .from(locations); + .select({ id: locations.id, name: locations.name, parentId: locations.parentId }) + .from(locations) + .orderBy(locations.name); return { devices: deviceList, diff --git a/src/routes/(app)/installations/new/+page.svelte b/src/routes/(app)/installations/new/+page.svelte index 27763c3..9c83238 100644 --- a/src/routes/(app)/installations/new/+page.svelte +++ b/src/routes/(app)/installations/new/+page.svelte @@ -1,6 +1,7 @@