From 7877173f563b35f0b493a51199049d5fa6a78e6c Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Tue, 23 Dec 2025 14:58:27 +0000 Subject: [PATCH 1/3] Add corner radius to video feed layer for improved aesthetics --- src/features/cameras/components/Video/VideoFeedGridPainter.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/features/cameras/components/Video/VideoFeedGridPainter.tsx b/src/features/cameras/components/Video/VideoFeedGridPainter.tsx index 7386495..0273172 100644 --- a/src/features/cameras/components/Video/VideoFeedGridPainter.tsx +++ b/src/features/cameras/components/Video/VideoFeedGridPainter.tsx @@ -218,6 +218,7 @@ const VideoFeedGridPainter = () => { height={stageSize.height} classname={"rounded-lg"} onClick={(e) => handleZoomClick(e, cameraFeedID)} + cornerRadius={10} /> From 61c85fdc3f5dcd4cc67dc7e6bb5feaf022b4359c Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Tue, 23 Dec 2025 15:00:09 +0000 Subject: [PATCH 2/3] updated version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 520161d..283ed79 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bayiq-ui", "private": true, - "version": "1.0.3", + "version": "1.0.4", "type": "module", "scripts": { "dev": "vite", From eb5eb69c28d40c00266d286458f6f9582c5578ed Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Mon, 12 Jan 2026 15:10:40 +0000 Subject: [PATCH 3/3] - added zoom options per camera and oneshot button - enhanced to remove painting when in zoom mode --- package.json | 2 +- src/app/providers/CameraZoomFetcher.tsx | 13 +++-- src/app/reducers/cameraFeedReducer.ts | 2 +- .../components/CameraSettings/CameraPanel.tsx | 8 ++-- .../CameraSettings/RegionSelector.tsx | 9 +++- .../cameraControls/CameraControls.tsx | 48 +++++++++++++++---- .../components/Video/VideoFeedGridPainter.tsx | 1 + src/features/cameras/hooks/useCameraZoom.ts | 11 +++-- .../components/systemHealth/SystemHealth.tsx | 2 +- 9 files changed, 73 insertions(+), 23 deletions(-) diff --git a/package.json b/package.json index 283ed79..fee22eb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "bayiq-ui", "private": true, - "version": "1.0.4", + "version": "1.0.5", "type": "module", "scripts": { "dev": "vite", diff --git a/src/app/providers/CameraZoomFetcher.tsx b/src/app/providers/CameraZoomFetcher.tsx index e0c2157..657f7f6 100644 --- a/src/app/providers/CameraZoomFetcher.tsx +++ b/src/app/providers/CameraZoomFetcher.tsx @@ -14,16 +14,23 @@ const CameraZoomFetcher = ({ cameraId, dispatch }: CameraZoomFetcherProps) => { useEffect(() => { const fetchZoomLevel = async () => { const result = await cameraZoomQuery.refetch(); - if (result.data && typeof result.data.zoomLevel === "number") { + + if (result?.data) { + const currentZoomLevel = result?.data["propPhysCurrent"].value; + const minLevel = result?.data["propPhysMin"].value; + const maxLevel = result?.data["propPhysMax"].value; + + const normalizedZoomLevel = ((currentZoomLevel - minLevel) / (maxLevel - minLevel)).toFixed(2); + dispatch({ type: "SET_ZOOM_LEVEL", - payload: { cameraFeedID: cameraId, zoomLevel: result.data.zoomLevel }, + payload: { cameraFeedID: cameraId, zoomLevel: parseFloat(normalizedZoomLevel) }, }); } }; fetchZoomLevel(); - }, [cameraId, cameraZoomQuery, dispatch]); + }, [cameraId]); return null; }; diff --git a/src/app/reducers/cameraFeedReducer.ts b/src/app/reducers/cameraFeedReducer.ts index 9903f51..f2d59a1 100644 --- a/src/app/reducers/cameraFeedReducer.ts +++ b/src/app/reducers/cameraFeedReducer.ts @@ -28,7 +28,7 @@ export const initialState: CameraFeedState = { ), zoomLevel: CAMERA_IDS.reduce( (acc, id) => { - acc[id] = 1; + acc[id] = 0; return acc; }, {} as Record, diff --git a/src/features/cameras/components/CameraSettings/CameraPanel.tsx b/src/features/cameras/components/CameraSettings/CameraPanel.tsx index 7a4f779..56d2d99 100644 --- a/src/features/cameras/components/CameraSettings/CameraPanel.tsx +++ b/src/features/cameras/components/CameraSettings/CameraPanel.tsx @@ -1,5 +1,5 @@ import { Tabs, Tab, TabList, TabPanel } from "react-tabs"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useCameraFeedContext } from "../../../../app/context/CameraFeedContext"; import RegionSelector from "./RegionSelector"; import CameraControls from "./cameraControls/CameraControls"; @@ -13,6 +13,7 @@ type CameraPanelProps = { const CameraPanel = ({ tabIndex, isResetAllModalOpen, handleClose, setIsResetModalOpen }: CameraPanelProps) => { const { state, dispatch } = useCameraFeedContext(); + const [subTabIndex, setSubTabIndex] = useState(0); const cameraFeedID = state.cameraFeedID; const regions = state.regionsByCamera[cameraFeedID]; @@ -39,7 +40,7 @@ const CameraPanel = ({ tabIndex, isResetAllModalOpen, handleClose, setIsResetMod }, [dispatch, tabIndex]); return ( - + setSubTabIndex(index)}> Target Detection Camera Controls @@ -53,10 +54,11 @@ const CameraPanel = ({ tabIndex, isResetAllModalOpen, handleClose, setIsResetMod isResetAllModalOpen={isResetAllModalOpen} handleClose={handleClose} setIsResetModalOpen={setIsResetModalOpen} + subTabIndex={subTabIndex} /> - + ); diff --git a/src/features/cameras/components/CameraSettings/RegionSelector.tsx b/src/features/cameras/components/CameraSettings/RegionSelector.tsx index 99058fc..f095ec5 100644 --- a/src/features/cameras/components/CameraSettings/RegionSelector.tsx +++ b/src/features/cameras/components/CameraSettings/RegionSelector.tsx @@ -6,6 +6,7 @@ import { useBlackBoard } from "../../../../hooks/useBlackBoard"; import { toast } from "sonner"; import { useCameraFeedSocket } from "../../../../app/context/WebSocketContext"; import type { CameraID } from "../../../../app/config/cameraConfig"; +import { useEffect } from "react"; type RegionSelectorProps = { regions: Region[]; @@ -15,6 +16,7 @@ type RegionSelectorProps = { isResetAllModalOpen: boolean; handleClose: () => void; setIsResetModalOpen: React.Dispatch>; + subTabIndex?: number; }; const RegionSelector = ({ @@ -23,7 +25,7 @@ const RegionSelector = ({ mode, cameraFeedID, isResetAllModalOpen, - + subTabIndex, setIsResetModalOpen, }: RegionSelectorProps) => { const { colourMutation } = useColourDectection(); @@ -31,6 +33,11 @@ const RegionSelector = ({ const { blackboardMutation } = useBlackBoard(); const paintedCells = state.paintedCells[cameraFeedID]; const cameraSocket = useCameraFeedSocket(); + useEffect(() => { + if (subTabIndex === 0) { + dispatch({ type: "CHANGE_MODE", payload: { cameraFeedID, mode: "painter" } }); + } + }, [cameraFeedID, dispatch, subTabIndex]); const getCurrentSocket = () => { switch (cameraFeedID) { diff --git a/src/features/cameras/components/CameraSettings/cameraControls/CameraControls.tsx b/src/features/cameras/components/CameraSettings/cameraControls/CameraControls.tsx index 0e4187d..9fe78ef 100644 --- a/src/features/cameras/components/CameraSettings/cameraControls/CameraControls.tsx +++ b/src/features/cameras/components/CameraSettings/cameraControls/CameraControls.tsx @@ -1,3 +1,4 @@ +import { useEffect } from "react"; import type { CameraID } from "../../../../../app/config/cameraConfig"; import { useCameraFeedContext } from "../../../../../app/context/CameraFeedContext"; import SliderComponent from "../../../../../ui/SliderComponent"; @@ -6,13 +7,25 @@ import { useDebouncedCallback } from "use-debounce"; type CameraControlsProps = { cameraFeedID: CameraID; + subTabIndex?: number; }; -const CameraControls = ({ cameraFeedID }: CameraControlsProps) => { +const CameraControls = ({ cameraFeedID, subTabIndex }: CameraControlsProps) => { const { state, dispatch } = useCameraFeedContext(); - const { cameraZoomMutation } = useCameraZoom(cameraFeedID); + const { cameraZoomMutation, cameraZoomQuery } = useCameraZoom(cameraFeedID); + + const zoomLevel = state.zoomLevel ? state.zoomLevel[cameraFeedID] : 0; + const currentZoomLevel = cameraZoomQuery.data ? cameraZoomQuery.data["propPhysCurrent"].value : 0; + const minLevel = cameraZoomQuery.data ? cameraZoomQuery.data["propPhysMin"].value : 0; + const maxLevel = cameraZoomQuery.data ? cameraZoomQuery.data["propPhysMax"].value : 0; + + const normalizedZoomLevel = ((currentZoomLevel - minLevel) / (maxLevel - minLevel)).toFixed(2); + useEffect(() => { + if (subTabIndex === 1) { + dispatch({ type: "CHANGE_MODE", payload: { cameraFeedID, mode: "controller" } }); + } + }, [cameraFeedID, dispatch, subTabIndex]); - const zoomLevel = state.zoomLevel ? state.zoomLevel[cameraFeedID] : 1; const debouncedMutation = useDebouncedCallback(async (value) => { await cameraZoomMutation.mutateAsync({ cameraFeedID, @@ -24,17 +37,34 @@ const CameraControls = ({ cameraFeedID }: CameraControlsProps) => { const newZoom = value as number; dispatch({ type: "SET_ZOOM_LEVEL", - payload: { cameraFeedID: cameraFeedID, zoomLevel: value as number }, + payload: { cameraFeedID: cameraFeedID, zoomLevel: newZoom }, }); debouncedMutation(newZoom); }; + const handleOneShotClick = () => { + debouncedMutation(normalizedZoomLevel); + }; + return ( -
-

Camera {cameraFeedID}

-
- - +
+
+

Camera {cameraFeedID}

+
+ + +
+
+ +
+

One Shot

+
); diff --git a/src/features/cameras/components/Video/VideoFeedGridPainter.tsx b/src/features/cameras/components/Video/VideoFeedGridPainter.tsx index 0273172..f7733d6 100644 --- a/src/features/cameras/components/Video/VideoFeedGridPainter.tsx +++ b/src/features/cameras/components/Video/VideoFeedGridPainter.tsx @@ -86,6 +86,7 @@ const VideoFeedGridPainter = () => { const image = draw(latestBitmapRef); const paintCell = (x: number, y: number) => { + if (mode === "controller" || mode === "zoom" || mode === "magnify") return; const col = Math.floor(x / (size + gap)); const row = Math.floor(y / (size + gap)); diff --git a/src/features/cameras/hooks/useCameraZoom.ts b/src/features/cameras/hooks/useCameraZoom.ts index 56a166c..f060c4e 100644 --- a/src/features/cameras/hooks/useCameraZoom.ts +++ b/src/features/cameras/hooks/useCameraZoom.ts @@ -23,10 +23,13 @@ const postZoomLevel = async (zoomConfig: CameraZoomConfig) => { id: `Camera${zoomConfig.cameraFeedID}-onvif-controller`, fields, }; - const response = await fetch(`${CAMBASE}/api/update-config`, { - method: "POST", - body: JSON.stringify(zoomPayload), - }); + const response = await fetch( + `${CAMBASE}/Camera${zoomConfig.cameraFeedID}-camera-control?command=setAbsoluteZoom&zoomLevel=${zoomConfig.zoomLevel}`, + { + method: "POST", + body: JSON.stringify(zoomPayload), + }, + ); if (!response.ok) { throw new Error("Network response was not ok"); diff --git a/src/features/dashboard/components/systemHealth/SystemHealth.tsx b/src/features/dashboard/components/systemHealth/SystemHealth.tsx index 37f62db..6902c96 100644 --- a/src/features/dashboard/components/systemHealth/SystemHealth.tsx +++ b/src/features/dashboard/components/systemHealth/SystemHealth.tsx @@ -39,7 +39,7 @@ const SystemHealth = ({ startTime, uptime, statuses, isLoading, isError, dateUpd } return (
-
+

Start Time

{startTime}