From faaa2cef39e7da1c9d2e5284dd9c2ace29528ab6 Mon Sep 17 00:00:00 2001 From: Mitchell R Date: Wed, 13 May 2026 02:59:28 +0200 Subject: [PATCH] feat(display): admin enable/disable toggle is_enabled column on displays (default 1). Disabled displays are filtered from the kiosk bundle so the kiosk never opens a window on them. Admin edit page exposes a checkbox; list page shows a "disabled" badge. --- .../service-admin-http/routes-admin.ts | 1 + server/src/plugins/service-store/mappers.ts | 1 + .../src/plugins/service-store/migrations.ts | 5 +++++ server/src/shared/bundle.ts | 4 +++- server/src/shared/types.ts | 1 + server/src/web-templates/admin-pages.tsx | 22 ++++++++++++++++++- 6 files changed, 32 insertions(+), 2 deletions(-) diff --git a/server/src/plugins/service-admin-http/routes-admin.ts b/server/src/plugins/service-admin-http/routes-admin.ts index b31c195..0365de6 100644 --- a/server/src/plugins/service-admin-http/routes-admin.ts +++ b/server/src/plugins/service-admin-http/routes-admin.ts @@ -973,6 +973,7 @@ export function registerAdminRoutes(app: H3, deps: AdminDeps): void { default_layout_id: validatedDefault, idle_timeout_seconds: parseInt(body?.["idle_timeout_seconds"] ?? "0", 10), sleep_timeout_seconds: parseInt(body?.["sleep_timeout_seconds"] ?? "0", 10), + is_enabled: body?.["is_enabled"] === "on" || body?.["is_enabled"] === "1" ? 1 : 0, } as any); notifyKiosks(); return new Response(null, { status: 302, headers: { location: `/admin/displays/${id}` } }); diff --git a/server/src/plugins/service-store/mappers.ts b/server/src/plugins/service-store/mappers.ts index ba971a8..c39e7ec 100644 --- a/server/src/plugins/service-store/mappers.ts +++ b/server/src/plugins/service-store/mappers.ts @@ -127,6 +127,7 @@ export function rowToDisplay(r: Row): Display { desired_power_state: s(r["desired_power_state"]) as DesiredPowerState, state_check_enabled: b(r["state_check_enabled"]), state_check_interval_seconds: n(r["state_check_interval_seconds"]), + is_enabled: b(r["is_enabled"]), }; } diff --git a/server/src/plugins/service-store/migrations.ts b/server/src/plugins/service-store/migrations.ts index 414d1af..f87b92d 100644 --- a/server/src/plugins/service-store/migrations.ts +++ b/server/src/plugins/service-store/migrations.ts @@ -652,4 +652,9 @@ export const MIGRATIONS: readonly MigrationEntry[] = [ created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) ) STRICT`, `CREATE INDEX IF NOT EXISTS idx_kiosk_gpio_bindings_kiosk ON kiosk_gpio_bindings(kiosk_id)`, + + // ---- displays.is_enabled — admin toggle to suppress window on a display ---- + (db: DatabaseSync) => { + addColumnIfNotExists(db, "displays", "is_enabled", "INTEGER NOT NULL DEFAULT 1"); + }, ]; diff --git a/server/src/shared/bundle.ts b/server/src/shared/bundle.ts index 2aae8c5..1f6218d 100644 --- a/server/src/shared/bundle.ts +++ b/server/src/shared/bundle.ts @@ -116,10 +116,12 @@ export function generateBundle( // Find all displays for this kiosk (displays now point to kiosks via kiosk_id) const kioskDisplays = repo.listDisplaysForKiosk(kioskId); // Fall back to legacy kiosk.display_id if no displays point to this kiosk yet - const displays = kioskDisplays.length > 0 + const allDisplays = kioskDisplays.length > 0 ? kioskDisplays : (kiosk.display_id ? [repo.getDisplayById(kiosk.display_id)].filter((d): d is NonNullable => d != null) : []); + // Admin can disable a display — kiosk must never open a window on it. + const displays = allDisplays.filter((d) => d.is_enabled); if (displays.length === 0) return null; // Collect camera IDs across ALL displays' layouts (de-duped). diff --git a/server/src/shared/types.ts b/server/src/shared/types.ts index a2c34e4..c220887 100644 --- a/server/src/shared/types.ts +++ b/server/src/shared/types.ts @@ -99,6 +99,7 @@ export interface Display { desired_power_state: DesiredPowerState; state_check_enabled: boolean; state_check_interval_seconds: number; + is_enabled: boolean; } export interface Camera { diff --git a/server/src/web-templates/admin-pages.tsx b/server/src/web-templates/admin-pages.tsx index 0e65be5..71112ab 100644 --- a/server/src/web-templates/admin-pages.tsx +++ b/server/src/web-templates/admin-pages.tsx @@ -2328,6 +2328,21 @@ export function DisplayEditPage(props: DisplayEditPageProps) { +
+ +
+ When disabled, the kiosk will not open a window on this display. Display stays in the list so you can re-enable it later. +
+
+