mirror of
https://github.com/BetterCorp/BetterFrame.git
synced 2026-05-26 23:26:34 +00:00
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.
This commit is contained in:
parent
bfb5028001
commit
faaa2cef39
6 changed files with 32 additions and 2 deletions
|
|
@ -973,6 +973,7 @@ export function registerAdminRoutes(app: H3, deps: AdminDeps): void {
|
||||||
default_layout_id: validatedDefault,
|
default_layout_id: validatedDefault,
|
||||||
idle_timeout_seconds: parseInt(body?.["idle_timeout_seconds"] ?? "0", 10),
|
idle_timeout_seconds: parseInt(body?.["idle_timeout_seconds"] ?? "0", 10),
|
||||||
sleep_timeout_seconds: parseInt(body?.["sleep_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);
|
} as any);
|
||||||
notifyKiosks();
|
notifyKiosks();
|
||||||
return new Response(null, { status: 302, headers: { location: `/admin/displays/${id}` } });
|
return new Response(null, { status: 302, headers: { location: `/admin/displays/${id}` } });
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,7 @@ export function rowToDisplay(r: Row): Display {
|
||||||
desired_power_state: s(r["desired_power_state"]) as DesiredPowerState,
|
desired_power_state: s(r["desired_power_state"]) as DesiredPowerState,
|
||||||
state_check_enabled: b(r["state_check_enabled"]),
|
state_check_enabled: b(r["state_check_enabled"]),
|
||||||
state_check_interval_seconds: n(r["state_check_interval_seconds"]),
|
state_check_interval_seconds: n(r["state_check_interval_seconds"]),
|
||||||
|
is_enabled: b(r["is_enabled"]),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -652,4 +652,9 @@ export const MIGRATIONS: readonly MigrationEntry[] = [
|
||||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||||
) STRICT`,
|
) STRICT`,
|
||||||
`CREATE INDEX IF NOT EXISTS idx_kiosk_gpio_bindings_kiosk ON kiosk_gpio_bindings(kiosk_id)`,
|
`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");
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -116,10 +116,12 @@ export function generateBundle(
|
||||||
// Find all displays for this kiosk (displays now point to kiosks via kiosk_id)
|
// Find all displays for this kiosk (displays now point to kiosks via kiosk_id)
|
||||||
const kioskDisplays = repo.listDisplaysForKiosk(kioskId);
|
const kioskDisplays = repo.listDisplaysForKiosk(kioskId);
|
||||||
// Fall back to legacy kiosk.display_id if no displays point to this kiosk yet
|
// 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
|
? kioskDisplays
|
||||||
: (kiosk.display_id ? [repo.getDisplayById(kiosk.display_id)].filter((d): d is NonNullable<typeof d> => d != null) : []);
|
: (kiosk.display_id ? [repo.getDisplayById(kiosk.display_id)].filter((d): d is NonNullable<typeof d> => 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;
|
if (displays.length === 0) return null;
|
||||||
|
|
||||||
// Collect camera IDs across ALL displays' layouts (de-duped).
|
// Collect camera IDs across ALL displays' layouts (de-duped).
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ export interface Display {
|
||||||
desired_power_state: DesiredPowerState;
|
desired_power_state: DesiredPowerState;
|
||||||
state_check_enabled: boolean;
|
state_check_enabled: boolean;
|
||||||
state_check_interval_seconds: number;
|
state_check_interval_seconds: number;
|
||||||
|
is_enabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Camera {
|
export interface Camera {
|
||||||
|
|
|
||||||
|
|
@ -2328,6 +2328,21 @@ export function DisplayEditPage(props: DisplayEditPageProps) {
|
||||||
<input id="name" name="name" type="text" class="form-input" value={d.name} required />
|
<input id="name" name="name" type="text" class="form-input" value={d.name} required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label style="display:flex; align-items:center; gap:0.5rem; cursor:pointer">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
name="is_enabled"
|
||||||
|
value="on"
|
||||||
|
checked={d.is_enabled}
|
||||||
|
/>
|
||||||
|
<span>Enabled</span>
|
||||||
|
</label>
|
||||||
|
<div class="form-hint">
|
||||||
|
When disabled, the kiosk will not open a window on this display. Display stays in the list so you can re-enable it later.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="default_layout_id">Default Layout</label>
|
<label for="default_layout_id">Default Layout</label>
|
||||||
<select id="default_layout_id" name="default_layout_id" class="form-input">
|
<select id="default_layout_id" name="default_layout_id" class="form-input">
|
||||||
|
|
@ -2407,7 +2422,12 @@ export function DisplaysPage(props: DisplaysPageProps) {
|
||||||
) : (
|
) : (
|
||||||
props.displays.map((d) => (
|
props.displays.map((d) => (
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href={`/admin/displays/${d.id}`}><strong>{d.name}</strong></a></td>
|
<td>
|
||||||
|
<a href={`/admin/displays/${d.id}`}><strong>{d.name}</strong></a>
|
||||||
|
{!d.is_enabled && (
|
||||||
|
<span style="margin-left:0.5rem; padding:0.1rem 0.4rem; font-size:0.7rem; background:#fee; color:#a00; border-radius:3px">disabled</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
<td style="color:#666">{String(d.width_px)}x{String(d.height_px)} — index {String(d.index)}</td>
|
<td style="color:#666">{String(d.width_px)}x{String(d.height_px)} — index {String(d.index)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue