From 9102ffd8b4ecb8f4f461fc2d727a5c1ef41c83bf Mon Sep 17 00:00:00 2001
From: grabowski
Date: Tue, 7 Apr 2026 17:07:08 +0700
Subject: [PATCH] Show parent > child location on detail pages, add location
move
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Device/component detail pages show "Parent › Child" for locations
- Device list cards show full location path
- Location edit form now includes a Parent selector to move locations
between parents or make them top-level
- Prevents setting a location as its own parent
Co-Authored-By: Claude Opus 4.6 (1M context)
---
.../(app)/components/[id]/+page.server.ts | 12 ++++++++++-
src/routes/(app)/components/[id]/+page.svelte | 2 +-
src/routes/(app)/devices/+page.server.ts | 21 +++++++++++++++++--
src/routes/(app)/devices/+page.svelte | 4 ++--
src/routes/(app)/devices/[id]/+page.server.ts | 12 +++++++++++
src/routes/(app)/devices/[id]/+page.svelte | 4 +++-
src/routes/(app)/locations/+page.server.ts | 6 +++++-
src/routes/(app)/locations/+page.svelte | 12 +++++++++++
8 files changed, 65 insertions(+), 8 deletions(-)
diff --git a/src/routes/(app)/components/[id]/+page.server.ts b/src/routes/(app)/components/[id]/+page.server.ts
index 9d405d7..3162150 100644
--- a/src/routes/(app)/components/[id]/+page.server.ts
+++ b/src/routes/(app)/components/[id]/+page.server.ts
@@ -22,6 +22,7 @@ export const load: PageServerLoad = async ({ params }) => {
deviceTitle: devices.title,
locationId: components.locationId,
locationName: locations.name,
+ locationParentId: locations.parentId,
createdAt: components.createdAt,
updatedAt: components.updatedAt
})
@@ -32,6 +33,15 @@ export const load: PageServerLoad = async ({ params }) => {
if (!component) error(404, 'Component not found');
+ let parentLocationName: string | null = null;
+ if (component.locationParentId) {
+ const [parent] = await db
+ .select({ name: locations.name })
+ .from(locations)
+ .where(eq(locations.id, component.locationParentId));
+ parentLocationName = parent?.name ?? null;
+ }
+
const images = await db
.select()
.from(componentImages)
@@ -57,7 +67,7 @@ export const load: PageServerLoad = async ({ params }) => {
.where(eq(installationLog.componentId, params.id))
.orderBy(desc(installationLog.performedAt));
- return { component, images, documents, history };
+ return { component, parentLocationName, images, documents, history };
};
export const actions: Actions = {
diff --git a/src/routes/(app)/components/[id]/+page.svelte b/src/routes/(app)/components/[id]/+page.svelte
index 5521732..17b62f6 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 {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/+page.server.ts b/src/routes/(app)/devices/+page.server.ts
index 61625f4..bd0b15f 100644
--- a/src/routes/(app)/devices/+page.server.ts
+++ b/src/routes/(app)/devices/+page.server.ts
@@ -47,7 +47,8 @@ export const load: PageServerLoad = async ({ url }) => {
model: devices.model,
condition: devices.condition,
year: devices.year,
- locationName: locations.name
+ locationName: locations.name,
+ locationParentId: locations.parentId
})
.from(devices)
.leftJoin(locations, eq(devices.locationId, locations.id))
@@ -56,6 +57,17 @@ export const load: PageServerLoad = async ({ url }) => {
.limit(pageSize)
.offset((page - 1) * pageSize);
+ // Resolve parent location names
+ const parentIds = [...new Set(deviceList.filter(d => d.locationParentId).map(d => d.locationParentId!))];
+ let parentNameMap: Record = {};
+ if (parentIds.length > 0) {
+ const parents = await db
+ .select({ id: locations.id, name: locations.name })
+ .from(locations)
+ .where(sql`${locations.id} IN ${parentIds}`);
+ for (const p of parents) parentNameMap[p.id] = p.name;
+ }
+
// Fetch first image for each device
const deviceIds = deviceList.map((d) => d.id);
let imageMap: Record = {};
@@ -80,7 +92,12 @@ export const load: PageServerLoad = async ({ url }) => {
return {
devices: deviceList.map((d) => ({
...d,
- thumbnail: imageMap[d.id] ?? null
+ thumbnail: imageMap[d.id] ?? null,
+ fullLocation: d.locationName
+ ? (d.locationParentId && parentNameMap[d.locationParentId]
+ ? `${parentNameMap[d.locationParentId]} › ${d.locationName}`
+ : d.locationName)
+ : null
})),
total,
page,
diff --git a/src/routes/(app)/devices/+page.svelte b/src/routes/(app)/devices/+page.svelte
index ec683d8..45f22ab 100644
--- a/src/routes/(app)/devices/+page.svelte
+++ b/src/routes/(app)/devices/+page.svelte
@@ -117,8 +117,8 @@
{device.year}
{/if}
- {#if device.locationName}
- {device.locationName}
+ {#if device.fullLocation}
+ {device.fullLocation}
{/if}
diff --git a/src/routes/(app)/devices/[id]/+page.server.ts b/src/routes/(app)/devices/[id]/+page.server.ts
index e627de1..6940a1c 100644
--- a/src/routes/(app)/devices/[id]/+page.server.ts
+++ b/src/routes/(app)/devices/[id]/+page.server.ts
@@ -38,6 +38,7 @@ export const load: PageServerLoad = async ({ params }) => {
generalNotes: devices.generalNotes,
locationId: devices.locationId,
locationName: locations.name,
+ locationParentId: locations.parentId,
disabled: devices.disabled,
createdAt: devices.createdAt,
updatedAt: devices.updatedAt
@@ -49,6 +50,16 @@ export const load: PageServerLoad = async ({ params }) => {
if (!device) error(404, 'Device not found');
if (device.disabled) error(404, 'Device not found');
+ // Resolve parent location name
+ let parentLocationName: string | null = null;
+ if (device.locationParentId) {
+ const [parent] = await db
+ .select({ name: locations.name })
+ .from(locations)
+ .where(eq(locations.id, device.locationParentId));
+ parentLocationName = parent?.name ?? null;
+ }
+
// Computer details
let compDetails = null;
if (device.category === 'Computer') {
@@ -138,6 +149,7 @@ export const load: PageServerLoad = async ({ params }) => {
return {
device,
+ parentLocationName,
computerDetails: compDetails,
images,
documents,
diff --git a/src/routes/(app)/devices/[id]/+page.svelte b/src/routes/(app)/devices/[id]/+page.svelte
index 80102b3..bce58da 100644
--- a/src/routes/(app)/devices/[id]/+page.svelte
+++ b/src/routes/(app)/devices/[id]/+page.svelte
@@ -544,7 +544,9 @@
{#if data.device.locationName}
Location
- {data.device.locationName}
+
+ {#if data.parentLocationName}{data.parentLocationName} › {/if}{data.device.locationName}
+
{/if}
diff --git a/src/routes/(app)/locations/+page.server.ts b/src/routes/(app)/locations/+page.server.ts
index 86ca66e..ab5ad01 100644
--- a/src/routes/(app)/locations/+page.server.ts
+++ b/src/routes/(app)/locations/+page.server.ts
@@ -73,11 +73,15 @@ export const actions: Actions = {
const id = formData.get('id') as string;
const name = (formData.get('name') as string)?.trim();
const description = (formData.get('description') as string)?.trim();
+ const parentId = (formData.get('parentId') as string) || null;
if (!name) return fail(400, { error: 'Name is required' });
+ // Prevent setting self as parent
+ if (parentId === id) return fail(400, { error: 'Cannot set location as its own parent' });
+
await db
.update(locations)
- .set({ name, description: description || null, updatedAt: new Date() })
+ .set({ name, description: description || null, parentId, updatedAt: new Date() })
.where(eq(locations.id, id));
return { renamed: true };
},
diff --git a/src/routes/(app)/locations/+page.svelte b/src/routes/(app)/locations/+page.svelte
index 8f3aa62..2b512bf 100644
--- a/src/routes/(app)/locations/+page.svelte
+++ b/src/routes/(app)/locations/+page.svelte
@@ -102,6 +102,18 @@
+
+
+
+