From 7886e72f386b6d66dce8edd93ded70a30d79d39f Mon Sep 17 00:00:00 2001 From: root Date: Tue, 14 Apr 2026 14:06:13 -0500 Subject: [PATCH] Add Controls tab with lights, locks, alarm, thermostats overlay The five-tab nav now includes a Controls tab between Home and Media. Opens a full-screen overlay with the alarm panel, each configured thermostat, lights, and locks tiled in a responsive 2-column grid. --- src/App.tsx | 3 ++ src/components/controls/ControlsOverlay.tsx | 54 +++++++++++++++++++++ src/components/controls/index.ts | 1 + src/components/layout/Header.tsx | 40 +++++++++++---- src/stores/uiStore.ts | 9 ++++ 5 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 src/components/controls/ControlsOverlay.tsx create mode 100644 src/components/controls/index.ts diff --git a/src/App.tsx b/src/App.tsx index 2098f3a..0700957 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,6 +3,7 @@ import { Dashboard } from '@/components/layout'; import { ThermostatOverlay } from '@/components/climate'; import { LightsOverlay } from '@/components/lights'; import { LocksOverlay } from '@/components/locks'; +import { ControlsOverlay } from '@/components/controls'; import { CalendarWidget } from '@/components/calendar'; import { TodoWidget } from '@/components/todo'; import { SettingsPanel, ConnectionModal } from '@/components/settings'; @@ -124,6 +125,7 @@ export default function App() { const locksOverlayOpen = useUIStore((state) => state.locksOverlayOpen); const thermostatsOverlayOpen = useUIStore((state) => state.thermostatsOverlayOpen); const mediaOverlayOpen = useUIStore((state) => state.mediaOverlayOpen); + const controlsOverlayOpen = useUIStore((state) => state.controlsOverlayOpen); const { isOpen: cameraOverlayOpen } = useCameraOverlay(); const isIdle = useIdle(env.photoFrameIdleTimeout); @@ -273,6 +275,7 @@ export default function App() { {locksOverlayOpen && } {thermostatsOverlayOpen && } {mediaOverlayOpen && } + {controlsOverlayOpen && } {cameraOverlayOpen && } {settingsOpen && } {isIdle && !alertCamera && } diff --git a/src/components/controls/ControlsOverlay.tsx b/src/components/controls/ControlsOverlay.tsx new file mode 100644 index 0000000..9d47222 --- /dev/null +++ b/src/components/controls/ControlsOverlay.tsx @@ -0,0 +1,54 @@ +import { useUIStore } from '@/stores/uiStore'; +import { useSettingsStore } from '@/stores/settingsStore'; +import { LightsWidget } from '@/components/lights'; +import { LocksWidget } from '@/components/locks'; +import { ThermostatWidget } from '@/components/climate'; +import { AlarmoPanel } from '@/components/alarm'; + +export function ControlsOverlay() { + const closeControlsOverlay = useUIStore((s) => s.closeControlsOverlay); + const config = useSettingsStore((s) => s.config); + + return ( +
+
+

Controls

+ +
+ +
+
+ {config.alarm && ( +
+ +
+ )} + {config.thermostats.map((thermostat) => ( +
+ +
+ ))} + {config.lights.length > 0 && ( +
+ +
+ )} + {config.locks.length > 0 && ( +
+ +
+ )} +
+
+
+ ); +} diff --git a/src/components/controls/index.ts b/src/components/controls/index.ts new file mode 100644 index 0000000..b197bab --- /dev/null +++ b/src/components/controls/index.ts @@ -0,0 +1 @@ +export { ControlsOverlay } from './ControlsOverlay'; diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 1b93707..b384a03 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -84,38 +84,54 @@ export function Header() { const connectionState = useConnectionState(); const cameraOverlayOpen = useUIStore((state) => state.cameraOverlayOpen); const mediaOverlayOpen = useUIStore((state) => state.mediaOverlayOpen); + const controlsOverlayOpen = useUIStore((state) => state.controlsOverlayOpen); const settingsOpen = useUIStore((state) => state.settingsOpen); const openCameraOverlay = useUIStore((state) => state.openCameraOverlay); const closeCameraOverlay = useUIStore((state) => state.closeCameraOverlay); const openMediaOverlay = useUIStore((state) => state.openMediaOverlay); const closeMediaOverlay = useUIStore((state) => state.closeMediaOverlay); + const openControlsOverlay = useUIStore((state) => state.openControlsOverlay); + const closeControlsOverlay = useUIStore((state) => state.closeControlsOverlay); const openSettings = useUIStore((state) => state.openSettings); const closeSettings = useUIStore((state) => state.closeSettings); const people = useSettingsStore((state) => state.config.people); const cameras = useSettingsStore((state) => state.config.cameras); const [currentTime, setCurrentTime] = useState(new Date()); - const activeTab: 'home' | 'media' | 'cameras' | 'settings' = - settingsOpen ? 'settings' : cameraOverlayOpen ? 'cameras' : mediaOverlayOpen ? 'media' : 'home'; + const activeTab: 'home' | 'controls' | 'media' | 'cameras' | 'settings' = + settingsOpen + ? 'settings' + : cameraOverlayOpen + ? 'cameras' + : mediaOverlayOpen + ? 'media' + : controlsOverlayOpen + ? 'controls' + : 'home'; - const goHome = () => { + const closeAll = () => { closeCameraOverlay(); closeMediaOverlay(); + closeControlsOverlay(); closeSettings(); }; + const goHome = () => { + closeAll(); + }; + const goControls = () => { + closeAll(); + openControlsOverlay(); + }; const goMedia = () => { - closeCameraOverlay(); - closeSettings(); + closeAll(); openMediaOverlay(); }; const goCameras = () => { - closeMediaOverlay(); - closeSettings(); + closeAll(); openCameraOverlay(); }; const goSettings = () => { - closeMediaOverlay(); - closeCameraOverlay(); + closeAll(); openSettings(); }; @@ -193,6 +209,12 @@ export function Header() { Home +