-address fixes and changes per feedback from Matt and Brad

This commit is contained in:
2025-11-05 16:30:27 +00:00
parent 861f2dd31d
commit d57ad1003a
15 changed files with 215 additions and 63 deletions

View File

@@ -4,7 +4,7 @@ import { useEffect, useMemo, useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faEye, faEyeSlash } from "@fortawesome/free-regular-svg-icons";
import CardHeader from "../UI/CardHeader";
import { useCameraZoom } from "../../hooks/useCameraZoom";
import { useCameraMode, useCameraZoom } from "../../hooks/useCameraZoom";
import { parseRTSPUrl, reverseZoomMapping, zoomMapping } from "../../utils/utils";
type CameraSettingsProps = {
@@ -20,16 +20,21 @@ const CameraSettingFields = ({
updateCameraConfig,
zoomLevel,
onZoomLevelChange,
updateCameraConfigError,
}: CameraSettingsProps) => {
const [showPwd, setShowPwd] = useState(false);
const cameraControllerSide = initialData?.id === "CameraA" ? "CameraControllerA" : "CameraControllerB";
const { mutation, query } = useCameraZoom({ camera: cameraControllerSide });
const { cameraModeQuery, cameraModeMutation } = useCameraMode({ camera: cameraControllerSide });
const zoomOptions = [1, 2, 4];
const magnification = query?.data?.propMagnification?.value;
const apiZoom = reverseZoomMapping(magnification);
const parsed = parseRTSPUrl(initialData?.propURI?.value);
const cameraMode = cameraModeQuery?.data?.propDayNightMode?.value;
const handleModeClick = async (mode: string) => {
await cameraModeMutation.mutate({ camera: cameraControllerSide, mode });
};
useEffect(() => {
if (!query?.data) return;
onZoomLevelChange?.(apiZoom);
@@ -194,7 +199,30 @@ const CameraSettingFields = ({
))}
</div>
</div>
<div className="mt-3">
<div>
<CardHeader title="Mode" />
<div className="mx-auto grid grid-cols-2 place-items-center">
<button
type="button"
onClick={() => handleModeClick("day")}
className={`px-8 py-2 rounded-md border border-gray-300 ${
cameraMode === "day" ? "bg-gray-100 text-blue-600" : ""
}`}
>
Day
</button>
<button
type="button"
onClick={() => handleModeClick("night")}
className={`px-8 py-2 rounded-md border border-gray-300 ${
cameraMode === "night" ? "bg-gray-100 text-blue-600" : ""
}`}
>
Night
</button>
</div>
</div>
{/* <div className="mt-3">
{updateCameraConfigError ? (
<button className="bg-red-500 text-white rounded-lg p-2 mx-auto h-[100%] w-full" disabled>
Retry
@@ -205,11 +233,10 @@ const CameraSettingFields = ({
className="bg-blue-700 text-white rounded-lg p-2 mx-auto h-[100%] w-full"
disabled
>
{/* {isSubmitting ? "Saving" : "Save settings"} bg-[#26B170] */}
{"Disabled: Coming soon"}
{isSubmitting ? "Saving" : "Save settings"}
</button>
)}
</div>
</div> */}
</div>
</Form>
)}

View File

@@ -18,7 +18,7 @@ const CameraSettings = ({
const { data, updateCameraConfig, updateCameraConfigError } = useFetchCameraConfig(side);
return (
<Card className="overflow-hidden min-h-[40vh] md:min-h-[60vh] max-h-[80vh] lg:w-[40%] p-4">
<Card className="overflow-x-auto min-h-[40vh] md:min-h-[60vh] max-h-[83vh] lg:w-[40%] p-4">
<div className="relative flex flex-col space-y-3">
<CardHeader title={title} icon={faWrench} />

View File

@@ -115,7 +115,35 @@ const ChannelFields = ({ touched, isSubmitting, format }: ChannelFieldsProps) =>
} rounded-lg w-full md:w-60`}
/>
</FormGroup>
{/* Overview quality and scale */}
<FormGroup>
<label htmlFor="overviewQuality">Overview quality and scale</label>
<Field
name={"overviewQuality"}
as="select"
id="overviewQuality"
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
>
<option value={"HIGH"}>High</option>
<option value={"MEDIUM"}>Medium</option>
<option value={"LOW"}>Low</option>
</Field>
</FormGroup>
{/* propOverviewImageScaleFactor cropSizeFactor */}
<FormGroup>
<label htmlFor="cropSizeFactor">Crop Size Factor</label>
<Field
name={"cropSizeFactor"}
as="select"
id="cropSizeFactor"
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
>
<option value={"FULL"}>Full</option>
<option value={"3/4"}>3/4</option>
<option value={"1/2"}>1/2</option>
<option value={"1/4"}>1/4</option>
</Field>
</FormGroup>
{format?.toLowerCase() === "bof2" && (
<>
<div className="space-y-3">
@@ -176,7 +204,7 @@ const ChannelFields = ({ touched, isSubmitting, format }: ChannelFieldsProps) =>
<h2 className="font-bold">{values.format} Lane ID Config</h2>
</div>
<FormGroup>
<label htmlFor="LID1">Lane ID 1</label>
<label htmlFor="LID1">Lane ID 1 (Camera A)</label>
<Field
name={"LID1"}
type="text"
@@ -188,7 +216,7 @@ const ChannelFields = ({ touched, isSubmitting, format }: ChannelFieldsProps) =>
/>
</FormGroup>
<FormGroup>
<label htmlFor="LID2">Lane ID 2</label>
<label htmlFor="LID2">Lane ID 2 (Camera B)</label>
<Field
name={"LID2"}
type="text"

View File

@@ -12,6 +12,7 @@ import type {
import { useQueryClient } from "@tanstack/react-query";
import { useUpdateBackOfficeConfig } from "../../../hooks/useBackOfficeConfig";
import { useFormVaidate } from "../../../hooks/useFormValidate";
import { useSightingAmend } from "../../../hooks/useSightingAmend";
const SettingForms = () => {
const qc = useQueryClient();
@@ -20,10 +21,14 @@ const SettingForms = () => {
const { backOfficeMutation } = useUpdateBackOfficeConfig();
const { bof2ConstantsQuery } = useGetDispatcherConfig();
const { validateMutation } = useFormVaidate();
const { sightingAmendQuery, sightingAmendMutation } = useSightingAmend();
const format = dispatcherQuery?.data?.propFormat?.value;
const enabled = dispatcherQuery?.data?.propEnabled?.value;
const sightingQuality = sightingAmendQuery?.data?.propOverviewQuality?.value;
const cropSizeFactor = sightingAmendQuery?.data?.propOverviewImageScaleFactor?.value;
const LID1 = laneIdQuery?.data?.propLaneID1?.value;
const LID2 = laneIdQuery?.data?.propLaneID2?.value;
@@ -43,6 +48,8 @@ const SettingForms = () => {
password: "",
connectTimeoutSeconds: Number(5),
readTimeoutSeconds: Number(15),
overviewQuality: sightingQuality ?? "HIGH",
cropSizeFactor: cropSizeFactor ?? "3/4",
// Bof2 - optional constants
FFID: FFID ?? "",
@@ -91,6 +98,7 @@ const SettingForms = () => {
if (validResponse?.reason === "OK") {
await backOfficeMutation.mutateAsync(values);
await sightingAmendMutation.mutateAsync(values);
if (values.format.toLowerCase() === "bof2") {
const bof2ConstantsData: OptionalBOF2Constants = {

View File

@@ -8,9 +8,8 @@ import { useDNSSettings, useSystemConfig } from "../../../hooks/useSystemConfig"
const SystemConfigFields = () => {
const { saveSystemSettings, systemSettingsData, saveSystemSettingsLoading } = useSystemConfig();
const { softRebootMutation, hardRebootMutation } = useReboots();
const { hardRebootMutation } = useReboots();
const { dnsQuery, dnsMutation } = useDNSSettings();
console.log(dnsQuery?.data);
const dnsPrimary = dnsQuery?.data?.propNameServerPrimary?.value;
const dnsSecondary = dnsQuery?.data?.propNameServerSecondary?.value;
@@ -39,9 +38,9 @@ const SystemConfigFields = () => {
return errors;
};
const handleSoftReboot = async () => {
await softRebootMutation.mutate();
};
// const handleSoftReboot = async () => {
// await softRebootMutation.mutate();
// };
const handleHardReboot = async () => {
await hardRebootMutation.mutate();
@@ -111,34 +110,7 @@ const SystemConfigFields = () => {
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<label htmlFor="serverPrimary" className="font-medium whitespace-nowrap md:w-1/2 text-left">
Server Primary
</label>
<Field
id="serverPrimary"
name="serverPrimary"
type="text"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs"
placeholder="Enter DNS primary address"
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<label htmlFor="serverSecondary" className="font-medium whitespace-nowrap md:w-1/2 text-left">
Server Secondary
</label>
<Field
id="serverSecondary"
name="serverSecondary"
type="text"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs"
placeholder="Enter DNS secondary address"
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<label htmlFor="sntpInterval" className="font-medium whitespace-nowrap md:w-1/2 text-left">
SNTP Interval minutes
@@ -155,6 +127,34 @@ const SystemConfigFields = () => {
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs"
/>
</FormGroup>
<FormGroup>
<label htmlFor="serverPrimary" className="font-medium whitespace-nowrap md:w-1/2 text-left">
Primary DNS Server
</label>
<Field
id="serverPrimary"
name="serverPrimary"
type="text"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs"
placeholder="Enter DNS primary address"
autoComplete="off"
/>
</FormGroup>
<FormGroup>
<label htmlFor="serverSecondary" className="font-medium whitespace-nowrap md:w-1/2 text-left">
Secondary DNS Server
</label>
<Field
id="serverSecondary"
name="serverSecondary"
type="text"
className="p-2 border border-gray-400 rounded-lg w-full max-w-xs"
placeholder="Enter DNS secondary address"
autoComplete="off"
/>
</FormGroup>
<button
type="submit"
className="w-1/4 text-white bg-green-700 hover:bg-green-800 font-small rounded-lg text-sm px-2 py-2.5"
@@ -167,13 +167,13 @@ const SystemConfigFields = () => {
<p>Reboot</p>
</div>
<button
{/* <button
type="button"
className="bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700 transition w-full md:w-[50%]"
onClick={handleSoftReboot}
>
{softRebootMutation.isPending || isSubmitting ? "Rebooting..." : "Software Reboot"}
</button>
</button> */}
<button
type="button"
className="bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700 transition w-full md:w-[50%]"

View File

@@ -59,11 +59,10 @@ export default function SightingHistoryWidget({ className, title }: SightingHist
isLoading,
} = useSightingFeedContext();
const { dispatch } = useAlertHitContext();
const { dispatch, state: alertState } = useAlertHitContext();
const { state: integrationState, dispatch: integrationDispatch } = useIntegrationsContext();
const sessionStarted = integrationState.sessionStarted;
const sessionPaused = integrationState.sessionPaused;
const processedRefs = useRef<Set<number | string>>(new Set());
const hasAutoOpenedRef = useRef(false);
@@ -72,6 +71,11 @@ export default function SightingHistoryWidget({ className, title }: SightingHist
const enqueue = useCallback((sighting: SightingType, kind: HitKind) => {
const id = sighting.vrm ?? sighting.ref;
if (processedRefs.current.has(id)) return;
const inList = alertState?.alertList?.find((sighting) => sighting.vrm === id);
if (inList) {
return;
}
processedRefs.current.add(id);
setModalQueue((q) => [...q, { id, sighting, kind }]);
@@ -154,7 +158,6 @@ export default function SightingHistoryWidget({ className, title }: SightingHist
if (firstNPED) {
enqueue(firstNPED, "NPED");
npedRef.current = true;
}
@@ -173,6 +176,7 @@ export default function SightingHistoryWidget({ className, title }: SightingHist
else hotlistsound();
setSelectedSighting(next.sighting);
setSightingModalOpen(true);
}
}, [isSightingModalOpen, npedSound, hotlistsound, setSelectedSighting, setSightingModalOpen, modalQueue]);