From 9a59213da051bb8d27409a29a385751abf9c271a Mon Sep 17 00:00:00 2001 From: grabowski Date: Thu, 9 Apr 2026 14:50:18 +0700 Subject: [PATCH] Add soft delete for components (disabled flag) - disabled boolean on components table (default false) - Component list filters out disabled components - Component detail returns 404 for disabled - Delete button with confirmation on component detail page - Installation form filters out instances of disabled components Co-Authored-By: Claude Opus 4.6 (1M context) --- src/lib/server/db/schema.ts | 1 + src/routes/(app)/components/+page.server.ts | 2 +- .../(app)/components/[id]/+page.server.ts | 12 ++++++++++- src/routes/(app)/components/[id]/+page.svelte | 20 +++++++++++++++++++ .../(app)/installations/new/+page.server.ts | 1 + 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index e5ace3d..2921b49 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -138,6 +138,7 @@ export const components = pgTable( defaultCondition: text('default_condition').notNull().default('Working'), defaultFirmwareVersion: text('default_firmware_version'), defaultLocationId: uuid('default_location_id').references(() => locations.id), + disabled: boolean('disabled').default(false).notNull(), createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull() }, diff --git a/src/routes/(app)/components/+page.server.ts b/src/routes/(app)/components/+page.server.ts index ce97d65..43ac77e 100644 --- a/src/routes/(app)/components/+page.server.ts +++ b/src/routes/(app)/components/+page.server.ts @@ -9,7 +9,7 @@ export const load: PageServerLoad = async ({ url }) => { const page = Math.max(1, Number(url.searchParams.get('page') ?? 1)); const pageSize = 24; - const conditions = []; + const conditions = [eq(components.disabled, false)]; if (type) conditions.push(eq(components.componentType, type)); if (search) { conditions.push( diff --git a/src/routes/(app)/components/[id]/+page.server.ts b/src/routes/(app)/components/[id]/+page.server.ts index cc29211..17fa59a 100644 --- a/src/routes/(app)/components/[id]/+page.server.ts +++ b/src/routes/(app)/components/[id]/+page.server.ts @@ -2,7 +2,7 @@ import type { PageServerLoad, Actions } from './$types'; import { db } from '$lib/server/db/index.js'; import { components, componentInstances, devices, locations, installationLog, componentImages, componentDocuments } from '$lib/server/db/schema.js'; import { eq, desc } from 'drizzle-orm'; -import { error, fail } from '@sveltejs/kit'; +import { error, fail, redirect } from '@sveltejs/kit'; import { saveDocument, deleteFile } from '$lib/server/uploads.js'; export const load: PageServerLoad = async ({ params }) => { @@ -12,6 +12,7 @@ export const load: PageServerLoad = async ({ params }) => { .where(eq(components.id, params.id)); if (!component) error(404, 'Component not found'); + if (component.disabled) error(404, 'Component not found'); // All instances with their device/location info const instances = await db @@ -161,5 +162,14 @@ export const actions: Actions = { } return { documentDeleted: true }; + }, + + disable: async ({ params }) => { + await db + .update(components) + .set({ disabled: true, updatedAt: new Date() }) + .where(eq(components.id, params.id)); + + redirect(303, '/components'); } }; diff --git a/src/routes/(app)/components/[id]/+page.svelte b/src/routes/(app)/components/[id]/+page.svelte index 6db224a..43bc9a3 100644 --- a/src/routes/(app)/components/[id]/+page.svelte +++ b/src/routes/(app)/components/[id]/+page.svelte @@ -9,6 +9,7 @@ let editingInstanceId = $state(null); let showAddInstances = $state(false); let showDocForm = $state(false); + let showDeleteConfirm = $state(false); const totalCount = $derived(data.instances.length); const installedCount = $derived(data.instances.filter((i) => i.currentDeviceId).length); @@ -56,9 +57,28 @@ class="rounded-md border border-gray-300 px-3 py-1.5 text-sm text-gray-600 hover:bg-gray-100 dark:border-gray-600 dark:text-gray-400 dark:hover:bg-gray-700"> Print + + {#if showDeleteConfirm} +
+

Are you sure you want to delete {c.title}? This will hide it from all listings.

+
+
+ +
+ +
+
+ {/if} + {#if form?.error}
{form.error}
{/if} diff --git a/src/routes/(app)/installations/new/+page.server.ts b/src/routes/(app)/installations/new/+page.server.ts index 39a6698..56c5db9 100644 --- a/src/routes/(app)/installations/new/+page.server.ts +++ b/src/routes/(app)/installations/new/+page.server.ts @@ -35,6 +35,7 @@ export const load: PageServerLoad = async ({ url }) => { }) .from(componentInstances) .innerJoin(components, eq(componentInstances.componentId, components.id)) + .where(eq(components.disabled, false)) .orderBy(components.title, componentInstances.instanceNumber); const locationList = await db