From 1ada8d0966ea9b957e39309af31e76e77b59e435 Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Thu, 27 Nov 2025 10:43:56 +0000 Subject: [PATCH 1/5] - Implement CameraFeed context and provider with reducer for state management - able to switch footage on tab clicks --- src/app/context/CameraFeedContext.ts | 16 ++++++++++ src/app/providers/AppProviders.tsx | 5 ++- src/app/providers/CameraFeedProvider.tsx | 9 ++++++ src/app/reducers/cameraFeedReducer.ts | 17 ++++++++++ .../components/CameraSettings/CameraPanel.tsx | 32 +++++++++++++++++++ .../CameraSettings/CameraSettings.tsx | 7 ++-- src/features/cameras/hooks/useGetVideoFeed.ts | 11 ++++--- .../cameras/hooks/useGetvideoSnapshots.ts | 5 ++- src/types/types.ts | 9 ++++++ 9 files changed, 101 insertions(+), 10 deletions(-) create mode 100644 src/app/context/CameraFeedContext.ts create mode 100644 src/app/providers/CameraFeedProvider.tsx create mode 100644 src/app/reducers/cameraFeedReducer.ts create mode 100644 src/features/cameras/components/CameraSettings/CameraPanel.tsx diff --git a/src/app/context/CameraFeedContext.ts b/src/app/context/CameraFeedContext.ts new file mode 100644 index 0000000..0373a14 --- /dev/null +++ b/src/app/context/CameraFeedContext.ts @@ -0,0 +1,16 @@ +import { createContext, useContext } from "react"; +import type { CameraFeedAction, CameraFeedState } from "../../types/types"; + +type CameraFeedContextType = { + state: CameraFeedState; + // check and refactor + dispatch: (state: CameraFeedAction) => void; +}; + +export const CameraFeedContext = createContext(null); + +export const useCameraFeedContext = () => { + const ctx = useContext(CameraFeedContext); + if (!ctx) throw new Error("useCameraFeedContext must be used inside "); + return ctx; +}; diff --git a/src/app/providers/AppProviders.tsx b/src/app/providers/AppProviders.tsx index 7a73155..90672e7 100644 --- a/src/app/providers/AppProviders.tsx +++ b/src/app/providers/AppProviders.tsx @@ -1,11 +1,14 @@ import type { PropsWithChildren } from "react"; import { QueryProvider } from "./QueryProviders"; import { WebSocketProvider } from "./WebSocketProvider"; +import { CameraFeedProvider } from "./CameraFeedProvider"; export const AppProviders = ({ children }: PropsWithChildren) => { return ( - {children} + + {children} + ); }; diff --git a/src/app/providers/CameraFeedProvider.tsx b/src/app/providers/CameraFeedProvider.tsx new file mode 100644 index 0000000..b983b8e --- /dev/null +++ b/src/app/providers/CameraFeedProvider.tsx @@ -0,0 +1,9 @@ +import { useReducer, type ReactNode } from "react"; +import { CameraFeedContext } from "../context/CameraFeedContext"; +import { initialState, reducer } from "../reducers/cameraFeedReducer"; + +export const CameraFeedProvider = ({ children }: { children: ReactNode }) => { + const [state, dispatch] = useReducer(reducer, initialState); + + return {children}; +}; diff --git a/src/app/reducers/cameraFeedReducer.ts b/src/app/reducers/cameraFeedReducer.ts new file mode 100644 index 0000000..99882c3 --- /dev/null +++ b/src/app/reducers/cameraFeedReducer.ts @@ -0,0 +1,17 @@ +import type { CameraFeedAction, CameraFeedState } from "../../types/types"; + +export const initialState: CameraFeedState = { + cameraFeedID: "A", +}; + +export function reducer(state: CameraFeedState, action: CameraFeedAction) { + switch (action.type) { + case "SET_CAMERA_FEED": + return { + ...state, + cameraFeedID: action.payload, + }; + default: + return state; + } +} diff --git a/src/features/cameras/components/CameraSettings/CameraPanel.tsx b/src/features/cameras/components/CameraSettings/CameraPanel.tsx new file mode 100644 index 0000000..804a4cd --- /dev/null +++ b/src/features/cameras/components/CameraSettings/CameraPanel.tsx @@ -0,0 +1,32 @@ +import { useEffect } from "react"; +import { useCameraFeedContext } from "../../../../app/context/CameraFeedContext"; + +type CameraPanelProps = { + tabIndex: number; +}; + +const CameraPanel = ({ tabIndex }: CameraPanelProps) => { + const { dispatch } = useCameraFeedContext(); + const mapIndextoCameraId = () => { + switch (tabIndex) { + case 1: + return "A"; + case 2: + return "B"; + case 3: + return "C"; + default: + return null; + } + }; + + useEffect(() => { + const cameraId = mapIndextoCameraId(); + + dispatch({ type: "SET_CAMERA_FEED", payload: cameraId }); + }, [tabIndex]); + + return
CameraPanel
; +}; + +export default CameraPanel; diff --git a/src/features/cameras/components/CameraSettings/CameraSettings.tsx b/src/features/cameras/components/CameraSettings/CameraSettings.tsx index d7f1593..4a4d8d7 100644 --- a/src/features/cameras/components/CameraSettings/CameraSettings.tsx +++ b/src/features/cameras/components/CameraSettings/CameraSettings.tsx @@ -4,6 +4,7 @@ import "react-tabs/style/react-tabs.css"; import RegionSelector from "./RegionSelector"; import type { PaintedCell, Region } from "../../../../types/types"; import type { RefObject } from "react"; +import CameraPanel from "./CameraPanel"; type CameraSettingsProps = { regions: Region[]; @@ -59,13 +60,13 @@ const CameraSettings = ({ /> -
Camera details {tabIndex}
+
-
Camera details {tabIndex}
+
-
Camera details {tabIndex}
+
diff --git a/src/features/cameras/hooks/useGetVideoFeed.ts b/src/features/cameras/hooks/useGetVideoFeed.ts index bedbaeb..03b9603 100644 --- a/src/features/cameras/hooks/useGetVideoFeed.ts +++ b/src/features/cameras/hooks/useGetVideoFeed.ts @@ -1,7 +1,8 @@ import { useQuery } from "@tanstack/react-query"; +import { CAMBASE } from "../../../utils/config"; -const getfeed = async () => { - const response = await fetch(`http://100.115.148.59/TargetDetectionColour-preview`, { +const getfeed = async (cameraFeedID: "A" | "B" | "C" | null) => { + const response = await fetch(`${CAMBASE}TargetDetectionColour${cameraFeedID}-preview`, { signal: AbortSignal.timeout(300000), cache: "no-store", }); @@ -11,10 +12,10 @@ const getfeed = async () => { return response.blob(); }; -export const useGetVideoFeed = () => { +export const useGetVideoFeed = (cameraFeedID: "A" | "B" | "C" | null) => { const videoQuery = useQuery({ - queryKey: ["getfeed"], - queryFn: getfeed, + queryKey: ["getfeed", cameraFeedID], + queryFn: () => getfeed(cameraFeedID), refetchInterval: 500, }); diff --git a/src/features/cameras/hooks/useGetvideoSnapshots.ts b/src/features/cameras/hooks/useGetvideoSnapshots.ts index 4c9679c..9bf941d 100644 --- a/src/features/cameras/hooks/useGetvideoSnapshots.ts +++ b/src/features/cameras/hooks/useGetvideoSnapshots.ts @@ -1,9 +1,12 @@ import { useEffect, useRef } from "react"; import { useGetVideoFeed } from "./useGetVideoFeed"; +import { useCameraFeedContext } from "../../../app/context/CameraFeedContext"; export const useCreateVideoSnapshot = () => { + const { state } = useCameraFeedContext(); + const cameraFeedID = state?.cameraFeedID; const latestBitmapRef = useRef(null); - const { videoQuery } = useGetVideoFeed(); + const { videoQuery } = useGetVideoFeed(cameraFeedID); const snapShot = videoQuery?.data; const isloading = videoQuery.isPending; diff --git a/src/types/types.ts b/src/types/types.ts index cf5bbb7..52ff556 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -95,3 +95,12 @@ export type OptionalBOF2LaneIDs = { LID2?: string; LID3?: string; }; + +export type CameraFeedState = { + cameraFeedID: "A" | "B" | "C" | null; +}; + +export type CameraFeedAction = { + type: string; + payload: "A" | "B" | "C" | null; +}; From f7dbde4511317fc6ecef3bb13800c8b8e77eaf88 Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Thu, 27 Nov 2025 11:43:10 +0000 Subject: [PATCH 2/5] - implemented isolated colouring depending on camera --- src/app/reducers/cameraFeedReducer.ts | 7 ++++- .../components/CameraSettings/CameraPanel.tsx | 29 ++++++++++--------- .../components/Video/VideoFeedGridPainter.tsx | 10 +++++-- src/types/types.ts | 9 ++++-- 4 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/app/reducers/cameraFeedReducer.ts b/src/app/reducers/cameraFeedReducer.ts index 99882c3..64f8f36 100644 --- a/src/app/reducers/cameraFeedReducer.ts +++ b/src/app/reducers/cameraFeedReducer.ts @@ -1,7 +1,12 @@ -import type { CameraFeedAction, CameraFeedState } from "../../types/types"; +import type { CameraFeedAction, CameraFeedState, PaintedCell } from "../../types/types"; export const initialState: CameraFeedState = { cameraFeedID: "A", + paintedCells: { + A: new Map(), + B: new Map(), + C: new Map(), + }, }; export function reducer(state: CameraFeedState, action: CameraFeedAction) { diff --git a/src/features/cameras/components/CameraSettings/CameraPanel.tsx b/src/features/cameras/components/CameraSettings/CameraPanel.tsx index 804a4cd..c2938d2 100644 --- a/src/features/cameras/components/CameraSettings/CameraPanel.tsx +++ b/src/features/cameras/components/CameraSettings/CameraPanel.tsx @@ -7,24 +7,25 @@ type CameraPanelProps = { const CameraPanel = ({ tabIndex }: CameraPanelProps) => { const { dispatch } = useCameraFeedContext(); - const mapIndextoCameraId = () => { - switch (tabIndex) { - case 1: - return "A"; - case 2: - return "B"; - case 3: - return "C"; - default: - return null; - } - }; useEffect(() => { - const cameraId = mapIndextoCameraId(); + const mapIndextoCameraId = () => { + switch (tabIndex) { + case 1: + return "A"; + case 2: + return "B"; + case 3: + return "C"; + default: + return null; + } + }; + const cameraId = mapIndextoCameraId(); + console.log(cameraId); dispatch({ type: "SET_CAMERA_FEED", payload: cameraId }); - }, [tabIndex]); + }, [dispatch, tabIndex]); return
CameraPanel
; }; diff --git a/src/features/cameras/components/Video/VideoFeedGridPainter.tsx b/src/features/cameras/components/Video/VideoFeedGridPainter.tsx index 1706412..a355473 100644 --- a/src/features/cameras/components/Video/VideoFeedGridPainter.tsx +++ b/src/features/cameras/components/Video/VideoFeedGridPainter.tsx @@ -4,6 +4,7 @@ import type { KonvaEventObject } from "konva/lib/Node"; import { useCreateVideoSnapshot } from "../../hooks/useGetvideoSnapshots"; import type { PaintedCell, Region } from "../../../../types/types"; import Card from "../../../../ui/Card"; +import { useCameraFeedContext } from "../../../../app/context/CameraFeedContext"; const rows = 40; const cols = 40; @@ -17,7 +18,10 @@ type VideoFeedGridPainterProps = { paintedCells: RefObject>; }; -const VideoFeedGridPainter = ({ regions, selectedRegionIndex, mode, paintedCells }: VideoFeedGridPainterProps) => { +const VideoFeedGridPainter = ({ regions, selectedRegionIndex, mode }: VideoFeedGridPainterProps) => { + const { state, dispatch } = useCameraFeedContext(); + const cameraFeedID = state.cameraFeedID; + const paintedCells = state.paintedCells[cameraFeedID]; const { latestBitmapRef, isloading } = useCreateVideoSnapshot(); const [stageSize, setStageSize] = useState({ width: 740, height: 460 }); const isDrawingRef = useRef(false); @@ -47,7 +51,7 @@ const VideoFeedGridPainter = ({ regions, selectedRegionIndex, mode, paintedCells const key = `${row}-${col}`; const currentColour = regions[selectedRegionIndex].brushColour; - const map = paintedCells.current; + const map = paintedCells; const existing = map.get(key); if (mode === "eraser") { @@ -121,7 +125,7 @@ const VideoFeedGridPainter = ({ regions, selectedRegionIndex, mode, paintedCells { - const cells = paintedCells.current; + const cells = paintedCells; cells.forEach((cell, key) => { const [rowStr, colStr] = key.split("-"); const row = Number(rowStr); diff --git a/src/types/types.ts b/src/types/types.ts index 52ff556..95e249a 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -97,10 +97,15 @@ export type OptionalBOF2LaneIDs = { }; export type CameraFeedState = { - cameraFeedID: "A" | "B" | "C" | null; + cameraFeedID: "A" | "B" | "C"; + paintedCells: { + A: Map; + B: Map; + C: Map; + }; }; export type CameraFeedAction = { type: string; - payload: "A" | "B" | "C" | null; + payload: "A" | "B" | "C"; }; From bf31f94b32d26071cc6b00e1ebdb1bc088a60710 Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Thu, 27 Nov 2025 16:16:15 +0000 Subject: [PATCH 3/5] - Enhanced camera feed state management with region handling and mode changes --- src/app/reducers/cameraFeedReducer.ts | 73 +++++++++++++++++ .../cameras/components/CameraGrid.tsx | 43 +--------- .../components/CameraSettings/CameraPanel.tsx | 26 +++++-- .../CameraSettings/CameraSettings.tsx | 46 +---------- .../CameraSettings/RegionSelector.tsx | 78 +++++++++++-------- .../components/Video/VideoFeedGridPainter.tsx | 16 ++-- src/types/types.ts | 43 +++++++++- 7 files changed, 190 insertions(+), 135 deletions(-) diff --git a/src/app/reducers/cameraFeedReducer.ts b/src/app/reducers/cameraFeedReducer.ts index 64f8f36..eca19d0 100644 --- a/src/app/reducers/cameraFeedReducer.ts +++ b/src/app/reducers/cameraFeedReducer.ts @@ -7,6 +7,25 @@ export const initialState: CameraFeedState = { B: new Map(), C: new Map(), }, + regionsByCamera: { + A: [ + { name: "Region 1", brushColour: "#ff0000" }, + { name: "Region 2", brushColour: "#00ff00" }, + { name: "Region 3", brushColour: "#0400ff" }, + ], + B: [ + { name: "Region 1", brushColour: "#ff0000" }, + { name: "Region 2", brushColour: "#00ff00" }, + ], + C: [{ name: "Region 1", brushColour: "#ff0000" }], + }, + + selectedRegionIndex: 0, + modeByCamera: { + A: "brush", + B: "brush", + C: "brush", + }, }; export function reducer(state: CameraFeedState, action: CameraFeedAction) { @@ -16,6 +35,60 @@ export function reducer(state: CameraFeedState, action: CameraFeedAction) { ...state, cameraFeedID: action.payload, }; + case "CHANGE_MODE": + return { + ...state, + modeByCamera: { + ...state.modeByCamera, + [action.payload.cameraFeedID]: action.payload.mode, + }, + }; + case "SET_SELECTED_REGION_INDEX": + return { + ...state, + selectedRegionIndex: action.payload, + }; + case "SET_SELECTED_REGION_COLOUR": + return { + ...state, + regionsByCamera: { + ...state.regionsByCamera, + [action.payload.cameraFeedID]: state.regionsByCamera[action.payload.cameraFeedID].map((region) => + region.name === action.payload.regionName ? { ...region, brushColour: action.payload.newColour } : region, + ), + }, + }; + case "ADD_NEW_REGION": + return { + ...state, + regionsByCamera: { + ...state.regionsByCamera, + [action.payload.cameraFeedID]: [ + ...state.regionsByCamera[action.payload.cameraFeedID], + { name: action.payload.regionName, brushColour: action.payload.brushColour }, + ], + }, + }; + case "REMOVE_REGION": + console.log(action.payload); + return { + ...state, + regionsByCamera: { + ...state.regionsByCamera, + [action.payload.cameraFeedID]: state.regionsByCamera[action.payload.cameraFeedID].filter( + (region) => region.name !== action.payload.regionName, + ), + }, + }; + case "RESET_PAINTED_CELLS": + return { + ...state, + paintedCells: { + ...state.paintedCells, + [state.cameraFeedID]: new Map(), + }, + }; + default: return state; } diff --git a/src/features/cameras/components/CameraGrid.tsx b/src/features/cameras/components/CameraGrid.tsx index 649f8a3..fc430aa 100644 --- a/src/features/cameras/components/CameraGrid.tsx +++ b/src/features/cameras/components/CameraGrid.tsx @@ -1,52 +1,17 @@ -import { useRef, useState } from "react"; +import { useState } from "react"; import VideoFeedGridPainter from "./Video/VideoFeedGridPainter"; import CameraSettings from "./CameraSettings/CameraSettings"; -import type { PaintedCell, Region } from "../../../types/types"; + import PlatePatch from "./PlatePatch/PlatePatch"; const CameraGrid = () => { - const [regions, setRegions] = useState([ - { name: "Region 1", brushColour: "#ff0000" }, - { name: "Region 2", brushColour: "#00ff00" }, - { name: "Region 3", brushColour: "#0400ff" }, - ]); - const [selectedRegionIndex, setSelectedRegionIndex] = useState(0); - const [mode, setMode] = useState(""); const [tabIndex, setTabIndex] = useState(0); - const updateRegionColour = (index: number, newColour: string) => { - setRegions((prev) => prev.map((r, i) => (i === index ? { ...r, brushColour: newColour } : r))); - }; - - const paintedCellsRef = useRef>(new Map()); - return (
- - { - setRegions((prev) => [...prev, { name: `Region ${prev.length + 1}`, brushColour: "#ffffff" }]); - }} - OnRemoveRegion={() => { - setRegions((prev) => prev.filter((_, i) => i !== selectedRegionIndex)); - setSelectedRegionIndex((prev) => (prev > 0 ? prev - 1 : 0)); - }} - /> + +
); diff --git a/src/features/cameras/components/CameraSettings/CameraPanel.tsx b/src/features/cameras/components/CameraSettings/CameraPanel.tsx index c2938d2..fff0e53 100644 --- a/src/features/cameras/components/CameraSettings/CameraPanel.tsx +++ b/src/features/cameras/components/CameraSettings/CameraPanel.tsx @@ -1,33 +1,45 @@ import { useEffect } from "react"; import { useCameraFeedContext } from "../../../../app/context/CameraFeedContext"; +import RegionSelector from "./RegionSelector"; type CameraPanelProps = { tabIndex: number; }; const CameraPanel = ({ tabIndex }: CameraPanelProps) => { - const { dispatch } = useCameraFeedContext(); + const { state, dispatch } = useCameraFeedContext(); + const cameraFeedID = state.cameraFeedID; + const regions = state.regionsByCamera[cameraFeedID]; + + const selectedRegionIndex = state.selectedRegionIndex; + const mode = state.modeByCamera[cameraFeedID]; useEffect(() => { const mapIndextoCameraId = () => { switch (tabIndex) { - case 1: + case 0: return "A"; - case 2: + case 1: return "B"; - case 3: + case 2: return "C"; default: - return null; + return "A"; } }; const cameraId = mapIndextoCameraId(); - console.log(cameraId); dispatch({ type: "SET_CAMERA_FEED", payload: cameraId }); }, [dispatch, tabIndex]); - return
CameraPanel
; + return ( + + ); }; export default CameraPanel; diff --git a/src/features/cameras/components/CameraSettings/CameraSettings.tsx b/src/features/cameras/components/CameraSettings/CameraSettings.tsx index 4a4d8d7..b36f5f7 100644 --- a/src/features/cameras/components/CameraSettings/CameraSettings.tsx +++ b/src/features/cameras/components/CameraSettings/CameraSettings.tsx @@ -1,38 +1,14 @@ import Card from "../../../../ui/Card"; import { Tab, Tabs, TabList, TabPanel } from "react-tabs"; import "react-tabs/style/react-tabs.css"; -import RegionSelector from "./RegionSelector"; -import type { PaintedCell, Region } from "../../../../types/types"; -import type { RefObject } from "react"; import CameraPanel from "./CameraPanel"; type CameraSettingsProps = { - regions: Region[]; - selectedRegionIndex: number; - onSelectRegion: (index: number) => void; - onChangeRegionColour: (index: number, colour: string) => void; - mode: string; - onSelectMode: (mode: string) => void; setTabIndex: (tabIndex: number) => void; tabIndex: number; - paintedCells: RefObject>; - onAddRegion: () => void; - OnRemoveRegion: () => void; }; -const CameraSettings = ({ - regions, - selectedRegionIndex, - onSelectRegion, - onChangeRegionColour, - mode, - onSelectMode, - tabIndex, - setTabIndex, - paintedCells, - onAddRegion, - OnRemoveRegion, -}: CameraSettingsProps) => { +const CameraSettings = ({ tabIndex, setTabIndex }: CameraSettingsProps) => { return ( setTabIndex(index)} > - Target Detection - Camera 1 - Camera 2 - Camera 3 + Camera A + Camera B + Camera C - - - diff --git a/src/features/cameras/components/CameraSettings/RegionSelector.tsx b/src/features/cameras/components/CameraSettings/RegionSelector.tsx index bddbf2e..6918f2f 100644 --- a/src/features/cameras/components/CameraSettings/RegionSelector.tsx +++ b/src/features/cameras/components/CameraSettings/RegionSelector.tsx @@ -1,45 +1,57 @@ -import ColourPicker from "./ColourPicker"; import type { PaintedCell, Region } from "../../../../types/types"; -import type { RefObject } from "react"; +import ColourPicker from "./ColourPicker"; +import { useCameraFeedContext } from "../../../../app/context/CameraFeedContext"; type RegionSelectorProps = { regions: Region[]; selectedRegionIndex: number; - onSelectRegion: (index: number) => void; - onChangeRegionColour: (index: number, colour: string) => void; mode: string; - onSelectMode: (mode: string) => void; - paintedCells: RefObject>; - onAddRegion: () => void; - OnRemoveRegion: () => void; + cameraFeedID: "A" | "B" | "C"; }; -const RegionSelector = ({ - regions, - selectedRegionIndex, - onSelectRegion, - onChangeRegionColour, - mode, - onSelectMode, - paintedCells, - onAddRegion, - OnRemoveRegion, -}: RegionSelectorProps) => { +const RegionSelector = ({ regions, selectedRegionIndex, mode, cameraFeedID }: RegionSelectorProps) => { + const { dispatch } = useCameraFeedContext(); const handleChange = (e: { target: { value: string } }) => { - onSelectMode(e.target.value); + dispatch({ type: "CHANGE_MODE", payload: { cameraFeedID: cameraFeedID, mode: e.target.value } }); }; - const handleAddClick = () => { - onAddRegion(); + const handleAddRegionClick = () => { + const regionName = `Region ${regions.length + 1}`; + dispatch({ + type: "ADD_NEW_REGION", + payload: { cameraFeedID: cameraFeedID, regionName: regionName, brushColour: "#ffffff" }, + }); }; - const handleResetClick = () => { - const map = paintedCells.current; - map.clear(); + const handleResetRegion = () => { + dispatch({ + type: "RESET_PAINTED_CELLS", + payload: { cameraFeedID: cameraFeedID, paintedCells: new Map() }, + }); }; const handleRemoveClick = () => { - OnRemoveRegion(); + dispatch({ + type: "REMOVE_REGION", + payload: { cameraFeedID: cameraFeedID, regionName: regions[selectedRegionIndex].name }, + }); + }; + + const handleModeChange = (newMode: string) => { + dispatch({ type: "CHANGE_MODE", payload: { cameraFeedID: cameraFeedID, mode: newMode } }); + }; + + const handleRegionSelect = (index: number) => { + dispatch({ type: "SET_SELECTED_REGION_INDEX", payload: index }); + }; + + const handleRegionColourChange = (index: number, newColour: string) => { + const regionName = regions[index].name; + + dispatch({ + type: "SET_SELECTED_REGION_COLOUR", + payload: { cameraFeedID: cameraFeedID, regionName: regionName, newColour: newColour }, + }); }; return ( @@ -84,7 +96,7 @@ const RegionSelector = ({

Region Select

<> - {regions.map((region, idx) => { + {regions?.map((region, idx) => { const isSelected = selectedRegionIndex === idx; const inputId = `region-${idx}`; return ( @@ -102,20 +114,20 @@ const RegionSelector = ({ name="region" className="sr-only" onChange={() => { - onSelectMode("painter"); - onSelectRegion(idx); + handleModeChange("painter"); + handleRegionSelect(idx); }} /> {region.name}
- onChangeRegionColour(idx, c)} /> + handleRegionColourChange(idx, c)} />

{region.brushColour}

); })}
-
diff --git a/src/features/cameras/components/Video/VideoFeedGridPainter.tsx b/src/features/cameras/components/Video/VideoFeedGridPainter.tsx index a355473..6aa48ef 100644 --- a/src/features/cameras/components/Video/VideoFeedGridPainter.tsx +++ b/src/features/cameras/components/Video/VideoFeedGridPainter.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef, useState, type RefObject } from "react"; import { Stage, Layer, Image, Shape } from "react-konva"; import type { KonvaEventObject } from "konva/lib/Node"; import { useCreateVideoSnapshot } from "../../hooks/useGetvideoSnapshots"; -import type { PaintedCell, Region } from "../../../../types/types"; + import Card from "../../../../ui/Card"; import { useCameraFeedContext } from "../../../../app/context/CameraFeedContext"; @@ -11,17 +11,13 @@ const cols = 40; const size = 20; const gap = 0; -type VideoFeedGridPainterProps = { - regions: Region[]; - selectedRegionIndex: number; - mode: string; - paintedCells: RefObject>; -}; - -const VideoFeedGridPainter = ({ regions, selectedRegionIndex, mode }: VideoFeedGridPainterProps) => { - const { state, dispatch } = useCameraFeedContext(); +const VideoFeedGridPainter = () => { + const { state } = useCameraFeedContext(); const cameraFeedID = state.cameraFeedID; const paintedCells = state.paintedCells[cameraFeedID]; + const regions = state.regionsByCamera[cameraFeedID]; + const selectedRegionIndex = state.selectedRegionIndex; + const mode = state.modeByCamera[cameraFeedID]; const { latestBitmapRef, isloading } = useCreateVideoSnapshot(); const [stageSize, setStageSize] = useState({ width: 740, height: 460 }); const isDrawingRef = useRef(false); diff --git a/src/types/types.ts b/src/types/types.ts index 95e249a..e012fca 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -103,9 +103,44 @@ export type CameraFeedState = { B: Map; C: Map; }; + regionsByCamera: { + A: Region[]; + B: Region[]; + C: Region[]; + }; + selectedRegionIndex: number; + modeByCamera: { + A: string; + B: string; + C: string; + }; + + tabIndex?: number; }; -export type CameraFeedAction = { - type: string; - payload: "A" | "B" | "C"; -}; +export type CameraFeedAction = + | { + type: "SET_CAMERA_FEED"; + payload: "A" | "B" | "C"; + } + | { + type: "CHANGE_MODE"; + payload: { cameraFeedID: "A" | "B" | "C"; mode: string }; + } + | { type: "SET_SELECTED_REGION_INDEX"; payload: number } + | { + type: "SET_SELECTED_REGION_COLOUR"; + payload: { cameraFeedID: "A" | "B" | "C"; regionName: string; newColour: string }; + } + | { + type: "ADD_NEW_REGION"; + payload: { cameraFeedID: "A" | "B" | "C"; regionName: string; brushColour: string }; + } + | { + type: "REMOVE_REGION"; + payload: { cameraFeedID: "A" | "B" | "C"; regionName: string }; + } + | { + type: "RESET_PAINTED_CELLS"; + payload: { cameraFeedID: "A" | "B" | "C"; paintedCells: Map }; + }; From 6428a8fa39d261b6bbbb409cef57337b75278ba6 Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Fri, 28 Nov 2025 09:00:55 +0000 Subject: [PATCH 4/5] - added tabs for camera controls --- .../components/CameraSettings/CameraPanel.tsx | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/features/cameras/components/CameraSettings/CameraPanel.tsx b/src/features/cameras/components/CameraSettings/CameraPanel.tsx index fff0e53..bacfc8b 100644 --- a/src/features/cameras/components/CameraSettings/CameraPanel.tsx +++ b/src/features/cameras/components/CameraSettings/CameraPanel.tsx @@ -1,3 +1,4 @@ +import { Tabs, Tab, TabList, TabPanel } from "react-tabs"; import { useEffect } from "react"; import { useCameraFeedContext } from "../../../../app/context/CameraFeedContext"; import RegionSelector from "./RegionSelector"; @@ -33,12 +34,27 @@ const CameraPanel = ({ tabIndex }: CameraPanelProps) => { }, [dispatch, tabIndex]); return ( - + + + Target Detection + Camera Controls + + + + + + +
+

Camera Controls

+

Controls for camera {cameraFeedID} will go here.

+
+
+
); }; From 690043e9f73729d0f601026f1ec5ae35bab67bef Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Fri, 28 Nov 2025 09:18:46 +0000 Subject: [PATCH 5/5] - updated ws endpoint --- src/app/config/wsconfig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/config/wsconfig.ts b/src/app/config/wsconfig.ts index 74c8421..d6fa33d 100644 --- a/src/app/config/wsconfig.ts +++ b/src/app/config/wsconfig.ts @@ -1,5 +1,5 @@ export const wsConfig = { - infoBar: "ws://100.115.148.59/websocket-infobar", + infoBar: "ws://100.115.125.56/websocket-infobar", }; export type SocketKey = keyof typeof wsConfig;