Show parent > child location on detail pages, add location move
Deploy to LXC / deploy (push) Successful in 18s

- 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) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 17:07:08 +07:00
parent 948617a285
commit 9102ffd8b4
8 changed files with 65 additions and 8 deletions
+19 -2
View File
@@ -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<string, string> = {};
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<string, string> = {};
@@ -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,
+2 -2
View File
@@ -117,8 +117,8 @@
<span class="text-xs text-gray-400 dark:text-gray-500">{device.year}</span>
{/if}
</div>
{#if device.locationName}
<p class="mt-1 text-xs text-gray-400 dark:text-gray-500">{device.locationName}</p>
{#if device.fullLocation}
<p class="mt-1 text-xs text-gray-400 dark:text-gray-500">{device.fullLocation}</p>
{/if}
</div>
</a>
@@ -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,
+3 -1
View File
@@ -544,7 +544,9 @@
{#if data.device.locationName}
<div>
<dt class="text-gray-500 dark:text-gray-400">Location</dt>
<dd class="text-gray-900 dark:text-white">{data.device.locationName}</dd>
<dd class="text-gray-900 dark:text-white">
{#if data.parentLocationName}{data.parentLocationName} &rsaquo; {/if}{data.device.locationName}
</dd>
</div>
{/if}
<div>