From 2066552bdc667b3e32abf830cf9d20a34f6b78d6 Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Mon, 12 Jan 2026 12:13:56 +0000 Subject: [PATCH] - updated camera friendly names and improve camera settings functionality - bugfixes for plate sizing --- package.json | 2 +- .../CameraSettings/CameraSettingFields.tsx | 101 ++++++------------ .../CameraSettings/CameraSettings.tsx | 12 +-- src/components/SessionForm/SessionCard.tsx | 16 +-- .../SightingsWidget/SightingWidget.tsx | 10 +- src/components/UI/ModalComponent.tsx | 2 +- .../providers/IntegrationsContextProvider.tsx | 24 +++++ .../reducers/IntegrationsContextReducer.ts | 14 +++ src/hooks/useCameraConfig.ts | 2 +- src/pages/FrontCamera.tsx | 13 ++- src/pages/RearCamera.tsx | 5 +- src/types/types.ts | 2 + 12 files changed, 111 insertions(+), 92 deletions(-) diff --git a/package.json b/package.json index 9db8683..d7859aa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "in-car-system-fe", "private": true, - "version": "1.0.25", + "version": "1.0.26", "type": "module", "scripts": { "dev": "vite", diff --git a/src/components/CameraSettings/CameraSettingFields.tsx b/src/components/CameraSettings/CameraSettingFields.tsx index 90a91e4..de8694a 100644 --- a/src/components/CameraSettings/CameraSettingFields.tsx +++ b/src/components/CameraSettings/CameraSettingFields.tsx @@ -1,28 +1,22 @@ import { Formik, Field, Form } from "formik"; import type { CameraConfig, CameraSettingErrorValues, CameraSettingValues, ZoomInOptions } from "../../types/types"; -import { useEffect, useMemo, useState } from "react"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { faEye, faEyeSlash } from "@fortawesome/free-regular-svg-icons"; +import { useEffect, useMemo } from "react"; import CardHeader from "../UI/CardHeader"; import { useCameraMode, useCameraZoom } from "../../hooks/useCameraZoom"; import { capitalize, parseRTSPUrl, reverseZoomMapping, zoomMapping } from "../../utils/utils"; +import { useIntegrationsContext } from "../../context/IntegrationsContext"; +import { useCameraBlackboard } from "../../hooks/useCameraBlackboard"; +import { toast } from "sonner"; type CameraSettingsProps = { initialData: CameraConfig; - updateCameraConfig: (values: CameraSettingValues) => Promise | void; zoomLevel?: number; onZoomLevelChange?: (level: number | undefined) => void; - updateCameraConfigError: null | Error; }; -const CameraSettingFields = ({ - initialData, - updateCameraConfig, - zoomLevel, - onZoomLevelChange, -}: CameraSettingsProps) => { - const [showPwd, setShowPwd] = useState(false); +const CameraSettingFields = ({ initialData, zoomLevel, onZoomLevelChange }: CameraSettingsProps) => { const cameraControllerSide = initialData?.id === "CameraA" ? "CameraControllerA" : "CameraControllerB"; + const { state, dispatch } = useIntegrationsContext(); const { mutation, query } = useCameraZoom({ camera: cameraControllerSide }); const { cameraModeQuery, cameraModeMutation } = useCameraMode({ camera: cameraControllerSide }); const zoomOptions = [1, 2, 4]; @@ -30,6 +24,8 @@ const CameraSettingFields = ({ const apiZoom = reverseZoomMapping(magnification); const parsed = parseRTSPUrl(initialData?.propURI?.value); const cameraMode = cameraModeQuery?.data?.propDayNightMode?.value; + const friendlyName = initialData?.id === "CameraA" ? state.cameraAFriendlyName : state.cameraBFriendlyName; + const { mutation: blackboardMutation } = useCameraBlackboard(); useEffect(() => { if (!query?.data) return; @@ -38,7 +34,7 @@ const CameraSettingFields = ({ const initialValues = useMemo( () => ({ - friendlyName: initialData?.id ?? "", + friendlyName: friendlyName, cameraAddress: initialData?.propURI?.value ?? "", userName: parsed?.username ?? "", password: parsed?.password ?? "", @@ -47,7 +43,15 @@ const CameraSettingFields = ({ zoom: apiZoom, }), - [initialData?.id, initialData?.propURI?.value, parsed?.username, parsed?.password, cameraMode, apiZoom] + [ + friendlyName, + initialData?.propURI?.value, + initialData?.id, + parsed?.username, + parsed?.password, + cameraMode, + apiZoom, + ] ); const validateValues = (values: CameraSettingValues) => { @@ -57,8 +61,17 @@ const CameraSettingFields = ({ return errors; }; - const handleSubmit = (values: CameraSettingValues) => { - updateCameraConfig(values); + const handleSubmit = async (values: CameraSettingValues) => { + const cameraSide = initialData?.id === "CameraA" ? "UPDATE_FRIENDLYNAMEA" : "UPDATE_FRIENDLYNAMEB"; + dispatch({ type: cameraSide, payload: values.friendlyName }); + const result = await blackboardMutation.mutateAsync({ + operation: "INSERT", + path: cameraSide, + value: values.friendlyName, + }); + if (result.reason === "OK") { + toast.success("Camera settings updated successfully"); + } }; const handleRadioButtonChange = async (levelNumber: number) => { @@ -101,54 +114,6 @@ const CameraSettingFields = ({
- - {touched.cameraAddress && errors.cameraAddress && ( - {errors.cameraAddress} - )} - -
- -
- - {touched.userName && errors.userName && ( - {errors.userName} - )} - -
- -
- - {touched.password && errors.password && ( - {errors.password} - )} -
- - setShowPwd((s) => !s)} - icon={showPwd ? faEyeSlash : faEye} - /> -
@@ -212,8 +177,12 @@ const CameraSettingFields = ({
{ - }
diff --git a/src/components/CameraSettings/CameraSettings.tsx b/src/components/CameraSettings/CameraSettings.tsx index 22156fc..6ef78bc 100644 --- a/src/components/CameraSettings/CameraSettings.tsx +++ b/src/components/CameraSettings/CameraSettings.tsx @@ -15,21 +15,13 @@ const CameraSettings = ({ zoomLevel?: number; onZoomLevelChange?: (level: number | undefined) => void; }) => { - const { data, updateCameraConfig, updateCameraConfigError } = useFetchCameraConfig(side); + const { data } = useFetchCameraConfig(side); return (
- { - - } + {}
); diff --git a/src/components/SessionForm/SessionCard.tsx b/src/components/SessionForm/SessionCard.tsx index e0b5d7a..1b75f72 100644 --- a/src/components/SessionForm/SessionCard.tsx +++ b/src/components/SessionForm/SessionCard.tsx @@ -18,17 +18,17 @@ const SessionCard = () => { const sightings = [...new Map(sessionList?.map((vehicle) => [vehicle.vrm, vehicle]))]; - const dedupedSightings = sightings.map((sighting) => sighting[1]); + const dedupedSightings = sightings?.map((sighting) => sighting[1]); - const vehicles = dedupedSightings.reduce>( + const vehicles = dedupedSightings?.reduce>( (acc, item) => { const hotlisthit = Object.values(item.metadata?.hotlistMatches ?? {}).includes(true); - if (item.metadata?.npedJSON["NPED CATEGORY"] === "A") acc.npedCatA.push(item); - if (item.metadata?.npedJSON["NPED CATEGORY"] === "B") acc.npedCatB.push(item); - if (item.metadata?.npedJSON["NPED CATEGORY"] === "C") acc.npedCatC.push(item); - if (item.metadata?.npedJSON["NPED CATEGORY"] === "D") acc.npedCatD.push(item); - if (item.metadata?.npedJSON["TAX STATUS"] === false) acc.notTaxed.push(item); - if (item.metadata?.npedJSON["MOT STATUS"] === false) acc.notMOT.push(item); + if (item?.metadata?.npedJSON["NPED CATEGORY"] === "A") acc.npedCatA.push(item); + if (item?.metadata?.npedJSON["NPED CATEGORY"] === "B") acc.npedCatB.push(item); + if (item?.metadata?.npedJSON["NPED CATEGORY"] === "C") acc.npedCatC.push(item); + if (item?.metadata?.npedJSON["NPED CATEGORY"] === "D") acc.npedCatD.push(item); + if (item?.metadata?.npedJSON["TAX STATUS"] === false) acc.notTaxed.push(item); + if (item?.metadata?.npedJSON["MOT STATUS"] === false) acc.notMOT.push(item); if (hotlisthit) acc.hotlistHit.push(item); acc.vehicles.push(item); return acc; diff --git a/src/components/SightingsWidget/SightingWidget.tsx b/src/components/SightingsWidget/SightingWidget.tsx index 8cf5d9a..ef897d1 100644 --- a/src/components/SightingsWidget/SightingWidget.tsx +++ b/src/components/SightingsWidget/SightingWidget.tsx @@ -198,6 +198,7 @@ export default function SightingHistoryWidget({ className, title }: SightingHist setSightingModalOpen(false); setModalQueue((q) => q.slice(1)); }; + return ( <> @@ -225,8 +226,13 @@ export default function SightingHistoryWidget({ className, title }: SightingHist onClick={() => onRowClick(obj)} >
-
- colour patch +
+ colour patch
{isHotListHit && ( hotlistHit diff --git a/src/components/UI/ModalComponent.tsx b/src/components/UI/ModalComponent.tsx index f06f7cc..bb5ca71 100644 --- a/src/components/UI/ModalComponent.tsx +++ b/src/components/UI/ModalComponent.tsx @@ -14,7 +14,7 @@ const ModalComponent = ({ isModalOpen, children, close }: ModalComponentProps) = isOpen={isModalOpen} onRequestClose={close} className={clsx( - "bg-[#1e2a38] p-3 rounded-lg shadow-lg w-[95%] mt-[2%] md:w-[60%] h-[100%] md:h-[95%] lg:h-[80%] z-[100] overflow-y-hidden border border-gray-500" + "bg-[#1e2a38] p-3 rounded-lg shadow-lg w-[95%] mt-[2%] md:w-[65%] h-[100%] md:h-[98%] lg:h-[80%] z-[100] overflow-y-hidden border border-gray-500" )} overlayClassName="fixed inset-0 bg-[#1e2a38]/70 flex justify-center items-start z-100 " style={{ diff --git a/src/context/providers/IntegrationsContextProvider.tsx b/src/context/providers/IntegrationsContextProvider.tsx index d3f2ec0..c330ad6 100644 --- a/src/context/providers/IntegrationsContextProvider.tsx +++ b/src/context/providers/IntegrationsContextProvider.tsx @@ -68,6 +68,30 @@ export const IntegrationsProvider = ({ children }: IntegrationsProviderType) => fetchHotlistData(); }, [query?.data]); + useEffect(() => { + async function fetchCameraNames() { + const cameraAResult = await mutation.mutateAsync({ + operation: "VIEW", + path: "UPDATE_FRIENDLYNAMEA", + }); + + const cameraBResult = await mutation.mutateAsync({ + operation: "VIEW", + path: "UPDATE_FRIENDLYNAMEB", + }); + + if (cameraAResult?.result && typeof cameraAResult.result === "string") { + console.log(cameraAResult?.result); + dispatch({ type: "UPDATE_FRIENDLYNAMEA", payload: cameraAResult.result }); + } + if (cameraBResult?.result && typeof cameraBResult.result === "string") { + console.log(cameraBResult?.result); + dispatch({ type: "UPDATE_FRIENDLYNAMEB", payload: cameraBResult.result }); + } + } + fetchCameraNames(); + }, []); + return ( hotlist.filename !== action.payload), }; + + case "UPDATE_FRIENDLYNAMEA": + return { + ...state, + cameraAFriendlyName: action.payload, + }; + + case "UPDATE_FRIENDLYNAMEB": + return { + ...state, + cameraBFriendlyName: action.payload, + }; default: return { ...state }; } diff --git a/src/hooks/useCameraConfig.ts b/src/hooks/useCameraConfig.ts index cd78a43..0b881d2 100644 --- a/src/hooks/useCameraConfig.ts +++ b/src/hooks/useCameraConfig.ts @@ -31,7 +31,7 @@ const updateCamerasideConfig = async (data: { id: string | number; friendlyName: method: "POST", body: JSON.stringify(updateConfigPayload), }); - if (!response.ok) throw new Error("Please make sure fields are filled in correctly"); + if (!response.ok) throw new Error("Cannot update settings"); }; export const useFetchCameraConfig = (cameraSide: string) => { diff --git a/src/pages/FrontCamera.tsx b/src/pages/FrontCamera.tsx index b3ce215..a8aaa5a 100644 --- a/src/pages/FrontCamera.tsx +++ b/src/pages/FrontCamera.tsx @@ -2,19 +2,28 @@ import { useState } from "react"; import CameraSettings from "../components/CameraSettings/CameraSettings"; import OverviewVideoContainer from "../components/FrontCameraSettings/OverviewVideoContainer"; import { Toaster } from "sonner"; +import { useIntegrationsContext } from "../context/IntegrationsContext"; const FrontCamera = () => { const [zoomLevel, setZoomLevel] = useState(1); + const { state } = useIntegrationsContext(); + + const friendlyName = state.cameraAFriendlyName || "Camera A"; return (
- +
); diff --git a/src/pages/RearCamera.tsx b/src/pages/RearCamera.tsx index 1306908..df447da 100644 --- a/src/pages/RearCamera.tsx +++ b/src/pages/RearCamera.tsx @@ -2,13 +2,16 @@ import OverviewVideoContainer from "../components/FrontCameraSettings/OverviewVi import CameraSettings from "../components/CameraSettings/CameraSettings"; import { Toaster } from "sonner"; import { useState } from "react"; +import { useIntegrationsContext } from "../context/IntegrationsContext"; const RearCamera = () => { const [zoomLevel, setZoomLevel] = useState(1); + const { state } = useIntegrationsContext(); + const friendlyName = state.cameraBFriendlyName || "Camera B"; return (