added NPED cat sorting

This commit is contained in:
2025-09-22 09:26:45 +01:00
parent 50cedaf2c4
commit 69eb5cc7be
15 changed files with 212 additions and 46 deletions

5
.env
View File

@@ -1,5 +1,5 @@
VITE_BASEURL=http://192.168.75.11/
VITE_CAM_BASE=http://100.72.72.70:8080
VITE_CAM_BASE=http://100.113.222.39
VITE_FOLKESTONE_BASE=http://100.116.253.81
VITE_TESTURL=http://100.82.205.44/SightingListRear/sightingSummary?mostRecentRef=-1
VITE_OUTSIDE_BASEURL=http://100.82.205.44/api
@@ -17,4 +17,5 @@ VITE_AGX_FRONT_BASE=http://100.72.72.70:8080/
VITE_LOCAL=http://10.42.0.1:8080/SightingListRear/sightingSummary?mostRecentRef=
VITE_LOCAL_FRONT=http://10.42.0.1:8080/SightingListFront/sightingSummary?mostRecentRef=
VITE_LOCAL_BASE=http://10.42.0.1:8080/
VITE_LOCAL_BASE=http://10.42.0.1:8080/
VITE_LOCAL_BASE_NEW=http://100.113.222.39

30
public/NPED_Cat_A.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

31
public/NPED_Cat_B.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 25 KiB

29
public/NPED_Cat_C.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

View File

@@ -1,6 +1,6 @@
import Container from "./components/UI/Container";
import Dashboard from "./pages/Dashboard";
import { Route, Routes } from "react-router";
import { Navigate, Route, Routes } from "react-router";
import FrontCamera from "./pages/FrontCamera";
import RearCamera from "./pages/RearCamera";
import SystemSettings from "./pages/SystemSettings";
@@ -19,6 +19,7 @@ function App() {
<Route path="rear-camera-settings" element={<RearCamera />} />
<Route path="system-settings" element={<SystemSettings />} />
<Route path="session-settings" element={<Session />} />
<Route path="*" element={<Navigate to="/" replace />} />
</Route>
</Routes>
</AlertHitProvider>

View File

@@ -13,7 +13,6 @@ const CameraSettings = ({ title, side }: { title: string; side: string }) => {
updateCameraConfigError,
} = useFetchCameraConfig(side);
console.log(updateCameraConfigError);
console.log(isPending);
return (
<Card>

View File

@@ -1,5 +1,4 @@
import { Field, useFormikContext } from "formik";
import FormToggle from "../components/FormToggle";
export const ValuesComponent = () => {
@@ -11,13 +10,13 @@ const BearerTypeFields = () => {
return (
<div className="flex flex-col space-y-4">
<div className="flex items-center gap-3">
<div className="flex items-center gap-3 justify-between">
<label htmlFor="format">Format</label>
<Field
as="select"
name="format"
id="format"
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445]"
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
>
<option value="JSON">JSON</option>
<option value="BOF2">BOF2</option>

View File

@@ -18,7 +18,7 @@ const ChannelFields = () => {
type="text"
id="backoffice"
placeholder="https://www.backoffice.com"
className="p-1.5 border border-gray-400 rounded-lg"
className="p-1.5 border border-gray-400 rounded-lg w-full md:w-60"
/>
</FormGroup>
<FormGroup>
@@ -28,7 +28,7 @@ const ChannelFields = () => {
type="text"
id="username"
placeholder="Back office username"
className="p-1.5 border border-gray-400 rounded-lg"
className="p-1.5 border border-gray-400 rounded-lg w-full md:w-60"
/>
</FormGroup>
<FormGroup>
@@ -38,7 +38,7 @@ const ChannelFields = () => {
type={showPwd ? "text" : "password"}
id="password"
placeholder="Back office password"
className="p-1.5 border border-gray-400 rounded-lg"
className="p-1.5 border border-gray-400 rounded-lg w-full md:w-60"
/>
<FontAwesomeIcon
type="button"
@@ -53,7 +53,7 @@ const ChannelFields = () => {
name={"connectTimeoutSeconds"}
type="number"
id="connectTimeoutSeconds"
className="p-1.5 border border-gray-400 rounded-lg"
className="p-1.5 border border-gray-400 rounded-lg w-full md:w-60"
/>
</FormGroup>
<FormGroup>
@@ -63,7 +63,7 @@ const ChannelFields = () => {
type="number"
id="readTimeoutSeconds"
placeholder="https://example.com"
className="p-1.5 border border-gray-400 rounded-lg"
className="p-1.5 border border-gray-400 rounded-lg w-full md:w-60"
/>
</FormGroup>
</div>

View File

@@ -2,7 +2,7 @@ import { Field } from "formik";
const FormToggle = ({ name, label }: { name: string; label?: string }) => {
return (
<label className="flex items-center gap-3 cursor-pointer select-none w-50">
<label className="flex items-center gap-3 cursor-pointer select-none w-50 justify-between">
<span className="text-sm">{label}</span>
<Field id={name} type="checkbox" name={name} className="sr-only peer" />
<div

View File

@@ -6,6 +6,9 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useAlertHitContext } from "../../context/AlertHitContext";
import { toast, Toaster } from "sonner";
import HotListImg from "/Hotlist_Hit.svg";
import NPED_CAT_A from "/NPED_Cat_A.svg";
import NPED_CAT_B from "/NPED_Cat_B.svg";
import NPED_CAT_C from "/NPED_Cat_C.svg";
type SightingModalProps = {
isSightingModalOpen: boolean;
@@ -33,6 +36,10 @@ const SightingModal = ({
const motionAway = (sighting?.motion ?? "").toUpperCase() === "AWAY";
const isHotListHit = sighting?.metadata?.hotlistMatches?.Hotlist0 === true;
const isNPEDHitA = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
const isNPEDHitB = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
const isNPEDHitC = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
const isNPEDHitD = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "D";
return (
<>
@@ -60,6 +67,34 @@ const SightingModal = ({
className="h-20 object-contain rounded-md"
/>
)}
{isNPEDHitA && (
<img
src={NPED_CAT_A}
alt="hotlistHit"
className="h-20 object-contain rounded-md"
/>
)}
{isNPEDHitB && (
<img
src={NPED_CAT_B}
alt="hotlistHit"
className="h-20 object-contain rounded-md"
/>
)}
{isNPEDHitC && (
<img
src={NPED_CAT_C}
alt="hotlistHit"
className="h-20 object-contain rounded-md"
/>
)}
{isNPEDHitD && (
<img
src={NPED_CAT_A}
alt="hotlistHit"
className="h-20 object-contain rounded-md"
/>
)}
</div>
<div className="flex flex-col md:flex-row items-center gap-3">
<img

View File

@@ -6,6 +6,7 @@ type InfoBarprops = {
};
const InfoBar = ({ obj }: InfoBarprops) => {
// const isNPEDHit = obj?.metadata?.npedJSON?.status_code === 404;
const isNPEDHitD = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "D";
return (
<div className="flex items-center gap-3 text-xs bg-neutral-900 px-2 py-1 rounded justify-between">
@@ -19,11 +20,11 @@ const InfoBar = ({ obj }: InfoBarprops) => {
</div>
<div className="min-w-14 opacity-80 ">
{/* {isNPEDHit ? (
<span className="text-red-500 font-semibold">NPED HIT</span>
{isNPEDHitD ? (
<span className="text-amber-500 font-semibold">NPED HIT CAT D</span>
) : (
""
)} */}
)}
</div>
</div>
);

View File

@@ -7,9 +7,12 @@ import CardHeader from "../UI/CardHeader";
import clsx from "clsx";
import { useSightingFeedContext } from "../../context/SightingFeedContext";
import SightingModal from "../SightingModal/SightingModal";
// import { useAlertHitContext } from "../../context/AlertHitContext";
import { useAlertHitContext } from "../../context/AlertHitContext";
import InfoBar from "./InfoBar";
import HotListImg from "/Hotlist_Hit.svg";
import NPED_CAT_A from "/NPED_Cat_A.svg";
import NPED_CAT_B from "/NPED_Cat_B.svg";
import NPED_CAT_C from "/NPED_Cat_C.svg";
function useNow(tickMs = 1000) {
const [, setNow] = useState(() => Date.now());
@@ -17,16 +20,16 @@ function useNow(tickMs = 1000) {
const id = setInterval(() => setNow(Date.now()), tickMs);
return () => clearInterval(id);
}, [tickMs]);
return undefined;
return null;
}
type SightingHistoryProps = {
baseUrl?: string;
entries?: number; // number of rows to show
pollMs?: number; // poll frequency
entries?: number;
pollMs?: number;
autoSelectLatest?: boolean;
title: string;
className: React.HTMLAttributes<HTMLDivElement> | string;
className: string;
};
// /type SightingHistoryWidgetProps = React.HTMLAttributes<HTMLDivElement>;
@@ -45,17 +48,17 @@ export default function SightingHistoryWidget({
selectedSighting,
} = useSightingFeedContext();
// const { dispatch } = useAlertHitContext();
const { dispatch } = useAlertHitContext();
const hasAutoOpenedRef = useRef(false);
const onRowClick = useCallback(
(sighting: SightingType) => {
if (!sighting) return;
setSightingModalOpen(!isSightingModalOpen);
setSightingModalOpen(true);
setSelectedSighting(sighting);
},
[isSightingModalOpen, setSelectedSighting, setSightingModalOpen]
[setSelectedSighting, setSightingModalOpen]
);
const rows = useMemo(
@@ -63,28 +66,34 @@ export default function SightingHistoryWidget({
[sightings]
);
// useEffect(() => {
// rows?.forEach((obj) => {
// const isNPEDHit = obj?.metadata?.npedJSON?.status_code === 404;
useEffect(() => {
rows?.forEach((obj) => {
const isNPEDHitA = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
const isNPEDHitB = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
const isNPEDHitC = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
// if (isNPEDHit) {
// dispatch({
// type: "ADD",
// payload: obj,
// });
// }
// });
// }, [rows, dispatch]);
if (isNPEDHitA || isNPEDHitB || isNPEDHitC) {
dispatch({
type: "ADD",
payload: obj,
});
}
});
}, [rows, dispatch]);
useEffect(() => {
if (hasAutoOpenedRef.current) return;
const firstHot = rows?.find(
(r) => r?.metadata?.hotlistMatches?.Hotlist0 === true
);
const firstHot = rows?.find((r) => {
// const hotlistHit = r?.metadata?.hotlistMatches?.Hotlist0 === true;
const isNPEDHitA = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
const isNPEDHitB = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
const isNPEDHitC = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
return isNPEDHitA || isNPEDHitB || isNPEDHitC;
});
if (firstHot) {
setSelectedSighting(firstHot);
setSightingModalOpen(true);
hasAutoOpenedRef.current = true; // prevent future auto-opens
hasAutoOpenedRef.current = true;
}
}, [rows, setSelectedSighting, setSightingModalOpen]);
@@ -98,8 +107,15 @@ export default function SightingHistoryWidget({
<div className="flex flex-col gap-3 ">
{/* Rows */}
<div className="flex flex-col">
{rows?.map((obj, idx) => {
// const isNPEDHit = obj?.metadata?.npedJSON?.status_code === 404;
{rows?.map((obj) => {
const isNPEDHitA =
obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
const isNPEDHitB =
obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
const isNPEDHitC =
obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
const isNPEDHitD =
obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "D";
const motionAway = (obj?.motion ?? "").toUpperCase() === "AWAY";
const primaryIsColour = obj?.srcCam === 1;
const secondaryMissing = (obj?.vrmSecondary ?? "") === "";
@@ -107,14 +123,16 @@ export default function SightingHistoryWidget({
obj?.metadata?.hotlistMatches?.Hotlist0 === true;
return (
<div
key={idx}
className={`border border-neutral-700 rounded-md mb-2 p-2 cursor-pointer`}
key={obj.ref}
className={`border border-neutral-700 rounded-md mb-2 p-2 cursor-pointer `}
onClick={() => onRowClick(obj)}
>
<InfoBar obj={obj} />
{/* Patch row */}
<div
className={`flex items-center gap-3 mt-2 justify-between`}
className={`flex items-center gap-3 mt-2 justify-between ${
isNPEDHitD ? "border border-amber-600" : ""
}`}
>
<div
className={`border p-1 ${
@@ -135,6 +153,28 @@ export default function SightingHistoryWidget({
className="h-20 object-contain rounded-md"
/>
)}
{isNPEDHitA && (
<img
src={NPED_CAT_A}
alt="hotlistHit"
className="h-20 object-contain rounded-md"
/>
)}
{isNPEDHitB && (
<img
src={NPED_CAT_B}
alt="hotlistHit"
className="h-20 object-contain rounded-md"
/>
)}
{isNPEDHitC && (
<img
src={NPED_CAT_C}
alt="hotlistHit"
className="h-20 object-contain rounded-md"
/>
)}
<NumberPlate motion={motionAway} vrm={obj?.vrm} />
</div>
</div>

View File

@@ -41,7 +41,6 @@ const SightingWidgetDetails = ({
</div>
{advancedDetailsEnabled && (
<>
{" "}
<div>
Country:{" "}
<span className="opacity-90">

View File

@@ -97,7 +97,7 @@ export default function Header() {
</Link>
</div>
<div className="flex flex-col md:flex-row items-center space-x-24 justify-items-center">
<div className="flex flex-col leading-tight text-white tabular-nums text-xl text-right mx-auto md:mx-10">
<div className="flex flex-col leading-tight text-white tabular-nums text-xl text-right mx-auto md:mx-10 space-y-1 my-2">
<h2>Local: {localStr}</h2>
<h2>UTC: {utcStr}</h2>
</div>

View File

@@ -97,6 +97,7 @@ export type HotlistMatches = {
export type NpedJSON = {
status_code: number;
reason_phrase: string;
"NPED CATEGORY": "A" | "B" | "C" | "D";
};
export type NPEDUser = {