From 0113803378115902cd4e2f96236baa8b1a74fae5 Mon Sep 17 00:00:00 2001 From: grabowski Date: Tue, 7 Apr 2026 17:10:25 +0700 Subject: [PATCH] Add location detail page showing devices and components at that location - /locations/[id] shows all devices and stored components at a location - Breadcrumb navigation with parent location - Sub-location chips for navigating to children - Location names are clickable links everywhere: location list page, device detail sidebar, component detail current location Co-Authored-By: Claude Opus 4.6 (1M context) --- src/routes/(app)/components/[id]/+page.svelte | 2 +- src/routes/(app)/devices/[id]/+page.svelte | 4 +- src/routes/(app)/locations/+page.svelte | 2 +- .../(app)/locations/[id]/+page.server.ts | 59 ++++++++ src/routes/(app)/locations/[id]/+page.svelte | 131 ++++++++++++++++++ 5 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 src/routes/(app)/locations/[id]/+page.server.ts create mode 100644 src/routes/(app)/locations/[id]/+page.svelte diff --git a/src/routes/(app)/components/[id]/+page.svelte b/src/routes/(app)/components/[id]/+page.svelte index 17b62f6..05a5ee8 100644 --- a/src/routes/(app)/components/[id]/+page.svelte +++ b/src/routes/(app)/components/[id]/+page.svelte @@ -68,7 +68,7 @@ {c.deviceTitle}

{:else if c.locationName} -

In storage at {#if data.parentLocationName}{data.parentLocationName} › {/if}{c.locationName}

+

In storage at {#if data.parentLocationName}{data.parentLocationName} › {/if}{c.locationName}

{:else}

In storage (no location set)

{/if} diff --git a/src/routes/(app)/devices/[id]/+page.svelte b/src/routes/(app)/devices/[id]/+page.svelte index bce58da..466987b 100644 --- a/src/routes/(app)/devices/[id]/+page.svelte +++ b/src/routes/(app)/devices/[id]/+page.svelte @@ -545,7 +545,9 @@
Location
- {#if data.parentLocationName}{data.parentLocationName} › {/if}{data.device.locationName} + + {#if data.parentLocationName}{data.parentLocationName} › {/if}{data.device.locationName} +
{/if} diff --git a/src/routes/(app)/locations/+page.svelte b/src/routes/(app)/locations/+page.svelte index 2b512bf..927a246 100644 --- a/src/routes/(app)/locations/+page.svelte +++ b/src/routes/(app)/locations/+page.svelte @@ -124,7 +124,7 @@ {#if loc.depth > 0} └  {/if} - {loc.name} + {loc.name} {#if loc.description}

{loc.description}

diff --git a/src/routes/(app)/locations/[id]/+page.server.ts b/src/routes/(app)/locations/[id]/+page.server.ts new file mode 100644 index 0000000..6fdf81e --- /dev/null +++ b/src/routes/(app)/locations/[id]/+page.server.ts @@ -0,0 +1,59 @@ +import type { PageServerLoad } from './$types'; +import { db } from '$lib/server/db/index.js'; +import { locations, devices, components } from '$lib/server/db/schema.js'; +import { eq, and, isNull } from 'drizzle-orm'; +import { error } from '@sveltejs/kit'; + +export const load: PageServerLoad = async ({ params }) => { + const [location] = await db + .select() + .from(locations) + .where(eq(locations.id, params.id)); + + if (!location) error(404, 'Location not found'); + + // Parent name + let parentName: string | null = null; + if (location.parentId) { + const [parent] = await db + .select({ name: locations.name }) + .from(locations) + .where(eq(locations.id, location.parentId)); + parentName = parent?.name ?? null; + } + + // Child locations + const children = await db + .select({ id: locations.id, name: locations.name }) + .from(locations) + .where(eq(locations.parentId, params.id)) + .orderBy(locations.name); + + // Devices at this location + const deviceList = await db + .select({ + id: devices.id, + title: devices.title, + category: devices.category, + brand: devices.brand, + model: devices.model, + condition: devices.condition + }) + .from(devices) + .where(and(eq(devices.locationId, params.id), eq(devices.disabled, false))) + .orderBy(devices.title); + + // Components stored at this location (not installed in a device) + const componentList = await db + .select({ + id: components.id, + title: components.title, + componentType: components.componentType, + condition: components.condition + }) + .from(components) + .where(and(eq(components.locationId, params.id), isNull(components.currentDeviceId))) + .orderBy(components.title); + + return { location, parentName, children, devices: deviceList, components: componentList }; +}; diff --git a/src/routes/(app)/locations/[id]/+page.svelte b/src/routes/(app)/locations/[id]/+page.svelte new file mode 100644 index 0000000..da0151f --- /dev/null +++ b/src/routes/(app)/locations/[id]/+page.svelte @@ -0,0 +1,131 @@ + + + + {data.location.name} - Locations - B4L Repair + + +
+
+
+ Locations + {#if data.parentName} + + {data.parentName} + {/if} + +
+

{data.location.name}

+ {#if data.location.description} +

{data.location.description}

+ {/if} +
+ + + {#if data.children.length > 0} +
+

Sub-locations

+
+ {#each data.children as child} + + {child.name} + + {/each} +
+
+ {/if} + + +
+

+ Devices ({data.devices.length}) +

+ {#if data.devices.length === 0} +

No devices at this location.

+ {:else} +
+ + + + + + + + + + {#each data.devices as device} + + + + + + {/each} + +
DeviceCategoryCondition
+ + {device.title} + + {#if device.brand || device.model} + {[device.brand, device.model].filter(Boolean).join(' ')} + {/if} + {device.category} + + {device.condition} + +
+
+ {/if} +
+ + +
+

+ Components in Storage ({data.components.length}) +

+ {#if data.components.length === 0} +

No components stored at this location.

+ {:else} +
+ + + + + + + + + + {#each data.components as comp} + + + + + + {/each} + +
ComponentTypeCondition
+ + {comp.title} + + {comp.componentType} + + {comp.condition} + +
+
+ {/if} +
+