@@ -8,6 +8,7 @@ import { useAlertHitContext } from "../../context/AlertHitContext";
|
|||||||
import NPED_CAT_A from "/NPED_Cat_A.svg";
|
import NPED_CAT_A from "/NPED_Cat_A.svg";
|
||||||
import NPED_CAT_B from "/NPED_Cat_B.svg";
|
import NPED_CAT_B from "/NPED_Cat_B.svg";
|
||||||
import NPED_CAT_C from "/NPED_Cat_C.svg";
|
import NPED_CAT_C from "/NPED_Cat_C.svg";
|
||||||
|
import { checkIsHotListHit } from "../../utils/utils";
|
||||||
|
|
||||||
type AlertItemProps = {
|
type AlertItemProps = {
|
||||||
item: SightingType;
|
item: SightingType;
|
||||||
@@ -19,7 +20,8 @@ const AlertItem = ({ item }: AlertItemProps) => {
|
|||||||
|
|
||||||
// const {d} = useCameraBlackboard();
|
// const {d} = useCameraBlackboard();
|
||||||
const motionAway = (item?.motion ?? "").toUpperCase() === "AWAY";
|
const motionAway = (item?.motion ?? "").toUpperCase() === "AWAY";
|
||||||
const isHotListHit = item?.metadata?.hotlistMatches?.Hotlist0 === true;
|
|
||||||
|
const isHotListHit = checkIsHotListHit(item);
|
||||||
const isNPEDHitA = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
|
const isNPEDHitA = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
|
||||||
const isNPEDHitB = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
|
const isNPEDHitB = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
|
||||||
const isNPEDHitC = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
|
const isNPEDHitC = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
|
||||||
|
|||||||
@@ -1,32 +1,80 @@
|
|||||||
import Card from "../UI/Card";
|
import Card from "../UI/Card";
|
||||||
import CardHeader from "../UI/CardHeader";
|
import CardHeader from "../UI/CardHeader";
|
||||||
import { useNPEDContext } from "../../context/NPEDUserContext";
|
import { useNPEDContext } from "../../context/NPEDUserContext";
|
||||||
|
import type { ReducedSightingType } from "../../types/types";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
const SessionCard = () => {
|
const SessionCard = () => {
|
||||||
const { sessionStarted, setSessionStarted, sessionList } = useNPEDContext();
|
const { sessionStarted, setSessionStarted, sessionList } = useNPEDContext();
|
||||||
|
|
||||||
const handleStartClick = () => {
|
const handleStartClick = () => {
|
||||||
setSessionStarted(!sessionStarted);
|
setSessionStarted(!sessionStarted);
|
||||||
|
toast(
|
||||||
|
`${
|
||||||
|
sessionStarted
|
||||||
|
? "Vehicle tracking session Ended"
|
||||||
|
: "Vehicle tracking session Started"
|
||||||
|
}`
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sightings = [
|
||||||
|
...new Map(sessionList.map((vehicle) => [vehicle.vrm, vehicle])),
|
||||||
|
];
|
||||||
|
|
||||||
|
const dedupedSightings = sightings.map((sighting) => sighting[1]);
|
||||||
|
|
||||||
|
const vehicles = dedupedSightings.reduce<
|
||||||
|
Record<string, ReducedSightingType[]>
|
||||||
|
>(
|
||||||
|
(acc, 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);
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
npedCatA: [],
|
||||||
|
npedCatB: [],
|
||||||
|
npedCatC: [],
|
||||||
|
npedCatD: [],
|
||||||
|
notTaxed: [],
|
||||||
|
notMOT: [],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="p-4">
|
<Card className="p-4">
|
||||||
<CardHeader title="Session" />
|
<CardHeader title="Session" />
|
||||||
<div className="flex flex-col gap-4 px-2">
|
<div className="flex flex-col gap-4 px-2">
|
||||||
<button
|
<button
|
||||||
className="bg-[#26B170] text-white px-4 py-2 rounded hover:bg-green-700 transition w-full"
|
className={`${
|
||||||
|
sessionStarted ? "bg-red-600" : "bg-[#26B170]"
|
||||||
|
} text-white px-4 py-2 rounded ${
|
||||||
|
sessionStarted ? "hover:bg-red-700" : "hover:bg-green-700"
|
||||||
|
} transition w-full`}
|
||||||
onClick={handleStartClick}
|
onClick={handleStartClick}
|
||||||
>
|
>
|
||||||
Start Session
|
{sessionStarted ? "End Session" : "Start Session"}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<ul className="text-white space-y-2">
|
<ul className="text-white space-y-2">
|
||||||
<li>Number of Vehicles: {sessionList.length} </li>
|
<li>Number of Vehicles: {dedupedSightings.length} </li>
|
||||||
<li>Vehicles without Tax: </li>
|
<li>Vehicles without Tax: {vehicles.notTaxed.length}</li>
|
||||||
<li>Vehicles without MOT: </li>
|
<li>Vehicles without MOT: {vehicles.notMOT.length}</li>
|
||||||
<li>Vehicles with NPED Cat A: </li>
|
<li>Vehicles with NPED Cat A: {vehicles.npedCatA.length}</li>
|
||||||
<li>Vehicles with NPED Cat B: </li>
|
<li>Vehicles with NPED Cat B: {vehicles.npedCatB.length}</li>
|
||||||
<li>Vehicles with NPED Cat C: </li>
|
<li>Vehicles with NPED Cat C: {vehicles.npedCatC.length}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -2,15 +2,14 @@ import { Field, FieldArray, Form, Formik } from "formik";
|
|||||||
import FormGroup from "../components/FormGroup";
|
import FormGroup from "../components/FormGroup";
|
||||||
import type { FormValues, Hotlist } from "../../../types/types";
|
import type { FormValues, Hotlist } from "../../../types/types";
|
||||||
import { useSoundContext } from "../../../context/SoundContext";
|
import { useSoundContext } from "../../../context/SoundContext";
|
||||||
|
import { useCameraBlackboard } from "../../../hooks/useCameraBlackboard";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
const SoundSettingsFields = () => {
|
const SoundSettingsFields = () => {
|
||||||
const { state, dispatch } = useSoundContext();
|
const { state, dispatch } = useSoundContext();
|
||||||
const hotlists: Hotlist[] = [
|
const { mutation } = useCameraBlackboard();
|
||||||
{ name: "hotlist0", sound: "" },
|
|
||||||
{ name: "hotlist1", sound: "" },
|
const hotlists: Hotlist[] = state.hotlists;
|
||||||
{ name: "hotlist2", sound: "" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const soundOptions = state?.soundOptions?.map((soundOption) => ({
|
const soundOptions = state?.soundOptions?.map((soundOption) => ({
|
||||||
value: soundOption?.name,
|
value: soundOption?.name,
|
||||||
@@ -23,9 +22,18 @@ const SoundSettingsFields = () => {
|
|||||||
hotlists,
|
hotlists,
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = (values: FormValues) => {
|
const handleSubmit = async (values: FormValues) => {
|
||||||
dispatch({ type: "UPDATE", payload: values });
|
dispatch({ type: "UPDATE", payload: values });
|
||||||
toast.success("Sound settings updated");
|
const result = await mutation.mutateAsync({
|
||||||
|
operation: "INSERT",
|
||||||
|
path: "soundSettings",
|
||||||
|
value: values,
|
||||||
|
});
|
||||||
|
if (result.reason !== "OK") {
|
||||||
|
toast.error("Cannot update sound settings");
|
||||||
|
} else {
|
||||||
|
toast.success("Sound Settings successfully updated");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
||||||
@@ -68,8 +76,8 @@ const SoundSettingsFields = () => {
|
|||||||
name="hotlists"
|
name="hotlists"
|
||||||
render={() => (
|
render={() => (
|
||||||
<div className="w-full m-2">
|
<div className="w-full m-2">
|
||||||
{values.hotlists.length > 0 ? (
|
{values?.hotlists?.length > 0 ? (
|
||||||
values.hotlists.map((hotlist, index) => (
|
values?.hotlists?.map((hotlist, index) => (
|
||||||
<div
|
<div
|
||||||
key={hotlist.name}
|
key={hotlist.name}
|
||||||
className="flex items-center m-2 w-full justify-between"
|
className="flex items-center m-2 w-full justify-between"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import HotListImg from "/Hotlist_Hit.svg";
|
|||||||
import NPED_CAT_A from "/NPED_Cat_A.svg";
|
import NPED_CAT_A from "/NPED_Cat_A.svg";
|
||||||
import NPED_CAT_B from "/NPED_Cat_B.svg";
|
import NPED_CAT_B from "/NPED_Cat_B.svg";
|
||||||
import NPED_CAT_C from "/NPED_Cat_C.svg";
|
import NPED_CAT_C from "/NPED_Cat_C.svg";
|
||||||
|
import { checkIsHotListHit } from "../../utils/utils";
|
||||||
|
|
||||||
type SightingModalProps = {
|
type SightingModalProps = {
|
||||||
isSightingModalOpen: boolean;
|
isSightingModalOpen: boolean;
|
||||||
@@ -65,7 +66,7 @@ const SightingModal = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const motionAway = (sighting?.motion ?? "").toUpperCase() === "AWAY";
|
const motionAway = (sighting?.motion ?? "").toUpperCase() === "AWAY";
|
||||||
const isHotListHit = sighting?.metadata?.hotlistMatches?.Hotlist0 === true;
|
const isHotListHit = checkIsHotListHit(sighting);
|
||||||
const isNPEDHitA = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
|
const isNPEDHitA = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
|
||||||
const isNPEDHitB = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
|
const isNPEDHitB = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
|
||||||
const isNPEDHitC = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
|
const isNPEDHitC = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ type InfoBarprops = {
|
|||||||
obj: SightingType;
|
obj: SightingType;
|
||||||
};
|
};
|
||||||
const InfoBar = ({ obj }: InfoBarprops) => {
|
const InfoBar = ({ obj }: InfoBarprops) => {
|
||||||
// const isNPEDHit = obj?.metadata?.npedJSON?.status_code === 404;
|
|
||||||
const isNPEDHitD = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "D";
|
const isNPEDHitD = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "D";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import type { SightingType } from "../../types/types";
|
import type { ReducedSightingType, SightingType } from "../../types/types";
|
||||||
import { BLANK_IMG, getSoundFileURL } from "../../utils/utils";
|
import { BLANK_IMG, getSoundFileURL } from "../../utils/utils";
|
||||||
import NumberPlate from "../PlateStack/NumberPlate";
|
import NumberPlate from "../PlateStack/NumberPlate";
|
||||||
import Card from "../UI/Card";
|
import Card from "../UI/Card";
|
||||||
@@ -13,10 +13,12 @@ import NPED_CAT_A from "/NPED_Cat_A.svg";
|
|||||||
import NPED_CAT_B from "/NPED_Cat_B.svg";
|
import NPED_CAT_B from "/NPED_Cat_B.svg";
|
||||||
import NPED_CAT_C from "/NPED_Cat_C.svg";
|
import NPED_CAT_C from "/NPED_Cat_C.svg";
|
||||||
import popup from "../../assets/sounds/ui/popup_open.mp3";
|
import popup from "../../assets/sounds/ui/popup_open.mp3";
|
||||||
|
import notification from "../../assets/sounds/ui/notification.mp3";
|
||||||
import { useSound } from "react-sounds";
|
import { useSound } from "react-sounds";
|
||||||
import { useNPEDContext } from "../../context/NPEDUserContext";
|
import { useNPEDContext } from "../../context/NPEDUserContext";
|
||||||
import { useSoundContext } from "../../context/SoundContext";
|
import { useSoundContext } from "../../context/SoundContext";
|
||||||
import Loading from "../UI/Loading";
|
import Loading from "../UI/Loading";
|
||||||
|
import { checkIsHotListHit } from "../../utils/utils";
|
||||||
|
|
||||||
function useNow(tickMs = 1000) {
|
function useNow(tickMs = 1000) {
|
||||||
const [, setNow] = useState(() => Date.now());
|
const [, setNow] = useState(() => Date.now());
|
||||||
@@ -43,11 +45,16 @@ export default function SightingHistoryWidget({
|
|||||||
useNow(1000);
|
useNow(1000);
|
||||||
const { state } = useSoundContext();
|
const { state } = useSoundContext();
|
||||||
|
|
||||||
const soundSrc = useMemo(() => {
|
const soundSrcNped = useMemo(() => {
|
||||||
return getSoundFileURL(state.NPEDsound) ?? popup;
|
return getSoundFileURL(state.NPEDsound) ?? popup;
|
||||||
}, [state.NPEDsound]);
|
}, [state.NPEDsound]);
|
||||||
|
|
||||||
const { play } = useSound(soundSrc);
|
const soundSrcHotlist = useMemo(() => {
|
||||||
|
return getSoundFileURL(state?.hotlists?.[0]?.sound) ?? notification;
|
||||||
|
}, [state.hotlists]);
|
||||||
|
|
||||||
|
const { play: npedSound } = useSound(soundSrcNped);
|
||||||
|
const { play: hotlistsound } = useSound(soundSrcHotlist);
|
||||||
const {
|
const {
|
||||||
sightings,
|
sightings,
|
||||||
setSelectedSighting,
|
setSelectedSighting,
|
||||||
@@ -61,15 +68,24 @@ export default function SightingHistoryWidget({
|
|||||||
const { dispatch } = useAlertHitContext();
|
const { dispatch } = useAlertHitContext();
|
||||||
const { sessionStarted, setSessionList, sessionList } = useNPEDContext();
|
const { sessionStarted, setSessionList, sessionList } = useNPEDContext();
|
||||||
|
|
||||||
|
const reduceObject = (obj: SightingType): ReducedSightingType => {
|
||||||
|
return {
|
||||||
|
vrm: obj.vrm,
|
||||||
|
metadata: obj?.metadata,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (sessionStarted) {
|
if (sessionStarted) {
|
||||||
if (!mostRecent) return;
|
if (!mostRecent) return;
|
||||||
setSessionList([...sessionList, mostRecent]);
|
const reducedMostRecent = reduceObject(mostRecent);
|
||||||
|
setSessionList([...sessionList, reducedMostRecent]);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [mostRecent, sessionStarted, setSessionList]);
|
}, [mostRecent, sessionStarted, setSessionList]);
|
||||||
|
|
||||||
const hasAutoOpenedRef = useRef(false);
|
const hasAutoOpenedRef = useRef(false);
|
||||||
|
const npedRef = useRef(false);
|
||||||
|
|
||||||
const onRowClick = useCallback(
|
const onRowClick = useCallback(
|
||||||
(sighting: SightingType) => {
|
(sighting: SightingType) => {
|
||||||
@@ -98,24 +114,43 @@ export default function SightingHistoryWidget({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [dispatch, rows]);
|
}, [dispatch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasAutoOpenedRef.current) return;
|
if (hasAutoOpenedRef.current || npedRef.current) return;
|
||||||
const firstHot = rows?.find((r) => {
|
const firstNPED = rows.find((r) => {
|
||||||
const isHotListHit = r?.metadata?.hotlistMatches?.Hotlist0 === true;
|
|
||||||
const isNPEDHitA = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
|
const isNPEDHitA = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "A";
|
||||||
const isNPEDHitB = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
|
const isNPEDHitB = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
|
||||||
const isNPEDHitC = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
|
const isNPEDHitC = r?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
|
||||||
return isNPEDHitA || isNPEDHitB || isNPEDHitC || isHotListHit;
|
return isNPEDHitA || isNPEDHitB || isNPEDHitC;
|
||||||
});
|
});
|
||||||
|
const firstHot = rows?.find((r) => {
|
||||||
|
const isHotListHit = checkIsHotListHit(r);
|
||||||
|
|
||||||
|
return isHotListHit;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (firstNPED) {
|
||||||
|
setSelectedSighting(firstNPED);
|
||||||
|
console.log("first");
|
||||||
|
npedSound();
|
||||||
|
setSightingModalOpen(true);
|
||||||
|
npedRef.current = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (firstHot) {
|
if (firstHot) {
|
||||||
setSelectedSighting(firstHot);
|
setSelectedSighting(firstHot);
|
||||||
play();
|
hotlistsound();
|
||||||
setSightingModalOpen(true);
|
setSightingModalOpen(true);
|
||||||
hasAutoOpenedRef.current = true;
|
hasAutoOpenedRef.current = true;
|
||||||
}
|
}
|
||||||
}, [play, rows, setSelectedSighting, setSightingModalOpen]);
|
}, [
|
||||||
|
hotlistsound,
|
||||||
|
npedSound,
|
||||||
|
rows,
|
||||||
|
setSelectedSighting,
|
||||||
|
setSightingModalOpen,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setSightingModalOpen(false);
|
setSightingModalOpen(false);
|
||||||
@@ -144,11 +179,8 @@ export default function SightingHistoryWidget({
|
|||||||
obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
|
obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
|
||||||
const isNPEDHitC =
|
const isNPEDHitC =
|
||||||
obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
|
obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
|
||||||
const isNPEDHitD =
|
|
||||||
obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "D";
|
|
||||||
const motionAway = (obj?.motion ?? "").toUpperCase() === "AWAY";
|
const motionAway = (obj?.motion ?? "").toUpperCase() === "AWAY";
|
||||||
const isHotListHit =
|
const isHotListHit = checkIsHotListHit(obj);
|
||||||
obj?.metadata?.hotlistMatches?.Hotlist0 === true;
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={obj.ref}
|
key={obj.ref}
|
||||||
@@ -156,9 +188,7 @@ export default function SightingHistoryWidget({
|
|||||||
onClick={() => onRowClick(obj)}
|
onClick={() => onRowClick(obj)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`flex items-center gap-3 mt-2 justify-between ${
|
className={`flex items-center gap-3 mt-2 justify-between `}
|
||||||
isNPEDHitD ? " border-amber-600" : ""
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<div className={`border p-1 `}>
|
<div className={`border p-1 `}>
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -1,13 +1,25 @@
|
|||||||
import { faVolumeHigh, faVolumeXmark } from "@fortawesome/free-solid-svg-icons";
|
import { faVolumeHigh, faVolumeXmark } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
import { useSoundEnabled } from "react-sounds";
|
import { useSoundEnabled } from "react-sounds";
|
||||||
|
import { useCameraBlackboard } from "../../hooks/useCameraBlackboard";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
const SoundBtn = () => {
|
const SoundBtn = () => {
|
||||||
|
const { mutation, query } = useCameraBlackboard();
|
||||||
const [enabled, setEnabled] = useSoundEnabled();
|
const [enabled, setEnabled] = useSoundEnabled();
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = async () => {
|
||||||
setEnabled(!enabled);
|
const newEnabled = !enabled;
|
||||||
|
setEnabled(newEnabled);
|
||||||
|
await mutation.mutateAsync({
|
||||||
|
operation: "INSERT",
|
||||||
|
path: "soundEnabled",
|
||||||
|
value: { enabled: newEnabled },
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
setEnabled(query?.data?.soundEnabled?.enabled);
|
||||||
|
}, [query?.data?.soundEnabled?.enabled, setEnabled]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button onClick={handleClick}>
|
<button onClick={handleClick}>
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { createContext, useContext, type SetStateAction } from "react";
|
import { createContext, useContext, type SetStateAction } from "react";
|
||||||
import type { NPEDUser, SightingType } from "../types/types";
|
import type { NPEDUser, ReducedSightingType } from "../types/types";
|
||||||
|
|
||||||
type UserContextValue = {
|
type UserContextValue = {
|
||||||
user: NPEDUser | null;
|
user: NPEDUser | null;
|
||||||
setUser: React.Dispatch<SetStateAction<NPEDUser | null>>;
|
setUser: React.Dispatch<SetStateAction<NPEDUser | null>>;
|
||||||
sessionStarted: boolean;
|
sessionStarted: boolean;
|
||||||
setSessionStarted: React.Dispatch<SetStateAction<boolean>>;
|
setSessionStarted: React.Dispatch<SetStateAction<boolean>>;
|
||||||
sessionList: SightingType[];
|
sessionList: ReducedSightingType[];
|
||||||
setSessionList: React.Dispatch<SetStateAction<SightingType[]>>;
|
setSessionList: React.Dispatch<SetStateAction<ReducedSightingType[]>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NPEDUserContext = createContext<UserContextValue | undefined>(
|
export const NPEDUserContext = createContext<UserContextValue | undefined>(
|
||||||
|
|||||||
@@ -15,9 +15,7 @@ type SightingFeedContextType = {
|
|||||||
isError: boolean;
|
isError: boolean;
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
data: SightingType | undefined;
|
data: SightingType | undefined;
|
||||||
sessionList: SightingType[];
|
|
||||||
sessionStarted: boolean;
|
sessionStarted: boolean;
|
||||||
setSessionStarted: (started: boolean) => void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SightingFeedContext = createContext<
|
export const SightingFeedContext = createContext<
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState, type ReactNode } from "react";
|
import { useState, type ReactNode } from "react";
|
||||||
import type { NPEDUser, SightingType } from "../../types/types";
|
import type { NPEDUser, ReducedSightingType } from "../../types/types";
|
||||||
import { NPEDUserContext } from "../NPEDUserContext";
|
import { NPEDUserContext } from "../NPEDUserContext";
|
||||||
|
|
||||||
type NPEDUserProviderType = {
|
type NPEDUserProviderType = {
|
||||||
@@ -9,7 +9,7 @@ type NPEDUserProviderType = {
|
|||||||
export const NPEDUserProvider = ({ children }: NPEDUserProviderType) => {
|
export const NPEDUserProvider = ({ children }: NPEDUserProviderType) => {
|
||||||
const [user, setUser] = useState<NPEDUser | null>(null);
|
const [user, setUser] = useState<NPEDUser | null>(null);
|
||||||
const [sessionStarted, setSessionStarted] = useState(false);
|
const [sessionStarted, setSessionStarted] = useState(false);
|
||||||
const [sessionList, setSessionList] = useState<SightingType[]>([]);
|
const [sessionList, setSessionList] = useState<ReducedSightingType[]>([]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NPEDUserContext.Provider
|
<NPEDUserContext.Provider
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ export const SightingFeedProvider = ({
|
|||||||
setSelectedSighting,
|
setSelectedSighting,
|
||||||
selectedSighting,
|
selectedSighting,
|
||||||
mostRecent,
|
mostRecent,
|
||||||
sessionList,
|
|
||||||
sessionStarted,
|
sessionStarted,
|
||||||
setSessionStarted,
|
|
||||||
} = useSightingFeed(url);
|
} = useSightingFeed(url);
|
||||||
|
|
||||||
const [isSightingModalOpen, setSightingModalOpen] = useState(false);
|
const [isSightingModalOpen, setSightingModalOpen] = useState(false);
|
||||||
@@ -45,9 +43,7 @@ export const SightingFeedProvider = ({
|
|||||||
isLoading,
|
isLoading,
|
||||||
side,
|
side,
|
||||||
data,
|
data,
|
||||||
sessionList,
|
|
||||||
sessionStarted,
|
sessionStarted,
|
||||||
setSessionStarted,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useMemo, useReducer, type ReactNode } from "react";
|
import { useEffect, useMemo, useReducer, type ReactNode } from "react";
|
||||||
import { SoundContext } from "../SoundContext";
|
import { SoundContext } from "../SoundContext";
|
||||||
import { initialState, reducer } from "../reducers/SoundContextReducer";
|
import { initialState, reducer } from "../reducers/SoundContextReducer";
|
||||||
|
import { useCameraBlackboard } from "../../hooks/useCameraBlackboard";
|
||||||
|
|
||||||
type SoundContextProviderProps = {
|
type SoundContextProviderProps = {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -8,6 +9,20 @@ type SoundContextProviderProps = {
|
|||||||
|
|
||||||
const SoundContextProvider = ({ children }: SoundContextProviderProps) => {
|
const SoundContextProvider = ({ children }: SoundContextProviderProps) => {
|
||||||
const [state, dispatch] = useReducer(reducer, initialState);
|
const [state, dispatch] = useReducer(reducer, initialState);
|
||||||
|
const { mutation } = useCameraBlackboard();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchSound = async () => {
|
||||||
|
const result = await mutation.mutateAsync({
|
||||||
|
operation: "VIEW",
|
||||||
|
path: "soundSettings",
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch({ type: "UPDATE", payload: result.result });
|
||||||
|
};
|
||||||
|
fetchSound();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
const value = useMemo(() => ({ state, dispatch }), [state, dispatch]);
|
const value = useMemo(() => ({ state, dispatch }), [state, dispatch]);
|
||||||
return (
|
return (
|
||||||
<SoundContext.Provider value={value}>{children}</SoundContext.Provider>
|
<SoundContext.Provider value={value}>{children}</SoundContext.Provider>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { SoundAction, SoundState } from "../../types/types";
|
|||||||
export const initialState: SoundState = {
|
export const initialState: SoundState = {
|
||||||
sightingSound: "switch",
|
sightingSound: "switch",
|
||||||
NPEDsound: "popup",
|
NPEDsound: "popup",
|
||||||
hotlists: [],
|
hotlists: [{ name: "hotlistName", sound: "notification" }],
|
||||||
soundOptions: [
|
soundOptions: [
|
||||||
{ name: "switch (Default)", soundFile: null },
|
{ name: "switch (Default)", soundFile: null },
|
||||||
{ name: "popup", soundFile: null },
|
{ name: "popup", soundFile: null },
|
||||||
@@ -18,7 +18,7 @@ export function reducer(state: SoundState, action: SoundAction): SoundState {
|
|||||||
...state,
|
...state,
|
||||||
sightingSound: action.payload.sightingSound,
|
sightingSound: action.payload.sightingSound,
|
||||||
NPEDsound: action.payload.NPEDsound,
|
NPEDsound: action.payload.NPEDsound,
|
||||||
hotlists: action.payload.hotlists.map((hotlist) => ({
|
hotlists: action.payload.hotlists?.map((hotlist) => ({
|
||||||
name: hotlist.name,
|
name: hotlist.name,
|
||||||
sound: hotlist.sound,
|
sound: hotlist.sound,
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ export const useCameraBlackboard = () => {
|
|||||||
id: "viewBlackboardData",
|
id: "viewBlackboardData",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSuccess: () => toast.success("Sighting successfully added to alert list"),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ export function useSightingFeed(url: string | undefined) {
|
|||||||
const [sightings, setSightings] = useState<SightingType[]>([]);
|
const [sightings, setSightings] = useState<SightingType[]>([]);
|
||||||
const [selectedRef, setSelectedRef] = useState<number | null>(null);
|
const [selectedRef, setSelectedRef] = useState<number | null>(null);
|
||||||
const [sessionStarted, setSessionStarted] = useState(false);
|
const [sessionStarted, setSessionStarted] = useState(false);
|
||||||
const [sessionList, setSessionList] = useState<SightingType[]>([]);
|
|
||||||
const mostRecent = sightings[0] ?? null;
|
const mostRecent = sightings[0] ?? null;
|
||||||
const latestRef = mostRecent?.ref ?? null;
|
const latestRef = mostRecent?.ref ?? null;
|
||||||
const [selectedSighting, setSelectedSighting] = useState<SightingType | null>(
|
const [selectedSighting, setSelectedSighting] = useState<SightingType | null>(
|
||||||
@@ -39,7 +38,7 @@ export function useSightingFeed(url: string | undefined) {
|
|||||||
return latestRef;
|
return latestRef;
|
||||||
}, [latestRef]);
|
}, [latestRef]);
|
||||||
const soundSrc = useMemo(() => {
|
const soundSrc = useMemo(() => {
|
||||||
return getSoundFileURL(state.sightingSound) ?? switchSound;
|
return getSoundFileURL(state?.sightingSound) ?? switchSound;
|
||||||
}, [state.sightingSound]);
|
}, [state.sightingSound]);
|
||||||
|
|
||||||
//use latestref instead of trigger to revert back
|
//use latestref instead of trigger to revert back
|
||||||
@@ -76,13 +75,6 @@ export function useSightingFeed(url: string | undefined) {
|
|||||||
staleTime: 0,
|
staleTime: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (sessionStarted) {
|
|
||||||
if (!mostRecent) return;
|
|
||||||
setSessionList([...sessionList, mostRecent]);
|
|
||||||
}
|
|
||||||
}, [mostRecent, sessionList, sessionStarted]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const data = query.data;
|
const data = query.data;
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
@@ -129,7 +121,6 @@ export function useSightingFeed(url: string | undefined) {
|
|||||||
setSelectedRef,
|
setSelectedRef,
|
||||||
mostRecent,
|
mostRecent,
|
||||||
selectedSighting,
|
selectedSighting,
|
||||||
sessionList,
|
|
||||||
sessionStarted,
|
sessionStarted,
|
||||||
setSessionStarted,
|
setSessionStarted,
|
||||||
setSelectedSighting,
|
setSelectedSighting,
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ export type SightingType = {
|
|||||||
metadata?: Metadata;
|
metadata?: Metadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ReducedSightingType = {
|
||||||
|
vrm: string;
|
||||||
|
metadata?: Metadata;
|
||||||
|
};
|
||||||
|
|
||||||
export type CameraSettingValues = {
|
export type CameraSettingValues = {
|
||||||
friendlyName: string;
|
friendlyName: string;
|
||||||
cameraAddress: string;
|
cameraAddress: string;
|
||||||
@@ -103,6 +108,10 @@ export type NpedJSON = {
|
|||||||
status_code: number;
|
status_code: number;
|
||||||
reason_phrase: string;
|
reason_phrase: string;
|
||||||
"NPED CATEGORY": "A" | "B" | "C" | "D";
|
"NPED CATEGORY": "A" | "B" | "C" | "D";
|
||||||
|
"MOT STATUS": boolean;
|
||||||
|
"TAX STATUS": boolean;
|
||||||
|
vrm: string;
|
||||||
|
"INSURANCE STATUS": string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NPEDUser = {
|
export type NPEDUser = {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import switchSound from "../assets/sounds/ui/switch.mp3";
|
import switchSound from "../assets/sounds/ui/switch.mp3";
|
||||||
import popup from "../assets/sounds/ui/popup_open.mp3";
|
import popup from "../assets/sounds/ui/popup_open.mp3";
|
||||||
import notification from "../assets/sounds/ui/notification.mp3";
|
import notification from "../assets/sounds/ui/notification.mp3";
|
||||||
|
import type { SightingType } from "../types/types";
|
||||||
|
|
||||||
export function getSoundFileURL(name: string) {
|
export function getSoundFileURL(name: string) {
|
||||||
const sounds: Record<string, string> = {
|
const sounds: Record<string, string> = {
|
||||||
@@ -129,59 +130,12 @@ export function drawRects(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// setSelectedRef(data?.ref);
|
export const checkIsHotListHit = (sigthing: SightingType | null) => {
|
||||||
|
if (!sigthing) return;
|
||||||
//setItems(data);
|
if (sigthing?.metadata?.hotlistMatches) {
|
||||||
|
const isHotListHit = Object.values(
|
||||||
// const selected = useMemo(
|
sigthing?.metadata?.hotlistMatches
|
||||||
// () =>
|
).includes(true);
|
||||||
// selectedRef == null
|
return isHotListHit;
|
||||||
// ? null
|
}
|
||||||
// : items.find((x) => x?.ref === selectedRef) ?? null,
|
};
|
||||||
// [items, selectedRef]
|
|
||||||
// );
|
|
||||||
// const effectiveSelected = selected ?? mostRecent ?? null;
|
|
||||||
|
|
||||||
// useEffect(() => {
|
|
||||||
// let delay = pollMs;
|
|
||||||
// let dead = false;
|
|
||||||
// const controller = new AbortController();
|
|
||||||
|
|
||||||
// async function tick() {
|
|
||||||
// try {
|
|
||||||
// // Pause when tab hidden to save CPU/network
|
|
||||||
// if (document.hidden) {
|
|
||||||
// setTimeout(tick, Math.max(delay, 2000));
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (obj && typeof obj.ref === "number" && obj.ref > -1) {
|
|
||||||
// setItems((prev) => {
|
|
||||||
// const next = [obj, ...prev].slice(0, limit);
|
|
||||||
// // maintain selection if still present; otherwise select newest if allowed
|
|
||||||
// const stillExists =
|
|
||||||
// selectedRef != null && next.some((x) => x?.ref === selectedRef);
|
|
||||||
// if (autoSelectLatest && !stillExists) {
|
|
||||||
// setSelectedRef(obj.ref);
|
|
||||||
// }
|
|
||||||
// return next;
|
|
||||||
// });
|
|
||||||
// setMostRecent(obj);
|
|
||||||
// mostRecentRef.current = obj.ref;
|
|
||||||
// delay = pollMs; // reset backoff on success
|
|
||||||
// }
|
|
||||||
// } catch {
|
|
||||||
// // exponential backoff (max 10s)
|
|
||||||
// delay = Math.min(delay * 2, 10000);
|
|
||||||
// } finally {
|
|
||||||
// if (!dead) setTimeout(tick, delay);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const t = setTimeout(tick, pollMs);
|
|
||||||
// return () => {
|
|
||||||
// dead = true;
|
|
||||||
// controller.abort();
|
|
||||||
// clearTimeout(t);
|
|
||||||
// };
|
|
||||||
// }, [baseUrl, limit, pollMs, autoSelectLatest, selectedRef]);
|
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ import tailwindcss from "@tailwindcss/vite";
|
|||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react(), tailwindcss()],
|
plugins: [react(), tailwindcss()],
|
||||||
server: { host: true },
|
server: {
|
||||||
|
host: true,
|
||||||
|
proxy: {
|
||||||
|
"/api": {
|
||||||
|
target: "http://100.118.196.113:8080",
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
base: "/Mobile",
|
base: "/Mobile",
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user