@@ -8,6 +8,7 @@ import { useAlertHitContext } from "../../context/AlertHitContext";
|
||||
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";
|
||||
import { checkIsHotListHit } from "../../utils/utils";
|
||||
|
||||
type AlertItemProps = {
|
||||
item: SightingType;
|
||||
@@ -19,7 +20,8 @@ const AlertItem = ({ item }: AlertItemProps) => {
|
||||
|
||||
// const {d} = useCameraBlackboard();
|
||||
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 isNPEDHitB = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
|
||||
const isNPEDHitC = item?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
|
||||
|
||||
@@ -1,32 +1,80 @@
|
||||
import Card from "../UI/Card";
|
||||
import CardHeader from "../UI/CardHeader";
|
||||
import { useNPEDContext } from "../../context/NPEDUserContext";
|
||||
import type { ReducedSightingType } from "../../types/types";
|
||||
import { toast } from "sonner";
|
||||
|
||||
const SessionCard = () => {
|
||||
const { sessionStarted, setSessionStarted, sessionList } = useNPEDContext();
|
||||
|
||||
const handleStartClick = () => {
|
||||
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 (
|
||||
<Card className="p-4">
|
||||
<CardHeader title="Session" />
|
||||
<div className="flex flex-col gap-4 px-2">
|
||||
<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}
|
||||
>
|
||||
Start Session
|
||||
{sessionStarted ? "End Session" : "Start Session"}
|
||||
</button>
|
||||
|
||||
<ul className="text-white space-y-2">
|
||||
<li>Number of Vehicles: {sessionList.length} </li>
|
||||
<li>Vehicles without Tax: </li>
|
||||
<li>Vehicles without MOT: </li>
|
||||
<li>Vehicles with NPED Cat A: </li>
|
||||
<li>Vehicles with NPED Cat B: </li>
|
||||
<li>Vehicles with NPED Cat C: </li>
|
||||
<li>Number of Vehicles: {dedupedSightings.length} </li>
|
||||
<li>Vehicles without Tax: {vehicles.notTaxed.length}</li>
|
||||
<li>Vehicles without MOT: {vehicles.notMOT.length}</li>
|
||||
<li>Vehicles with NPED Cat A: {vehicles.npedCatA.length}</li>
|
||||
<li>Vehicles with NPED Cat B: {vehicles.npedCatB.length}</li>
|
||||
<li>Vehicles with NPED Cat C: {vehicles.npedCatC.length}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -2,15 +2,14 @@ import { Field, FieldArray, Form, Formik } from "formik";
|
||||
import FormGroup from "../components/FormGroup";
|
||||
import type { FormValues, Hotlist } from "../../../types/types";
|
||||
import { useSoundContext } from "../../../context/SoundContext";
|
||||
import { useCameraBlackboard } from "../../../hooks/useCameraBlackboard";
|
||||
import { toast } from "sonner";
|
||||
|
||||
const SoundSettingsFields = () => {
|
||||
const { state, dispatch } = useSoundContext();
|
||||
const hotlists: Hotlist[] = [
|
||||
{ name: "hotlist0", sound: "" },
|
||||
{ name: "hotlist1", sound: "" },
|
||||
{ name: "hotlist2", sound: "" },
|
||||
];
|
||||
const { mutation } = useCameraBlackboard();
|
||||
|
||||
const hotlists: Hotlist[] = state.hotlists;
|
||||
|
||||
const soundOptions = state?.soundOptions?.map((soundOption) => ({
|
||||
value: soundOption?.name,
|
||||
@@ -23,9 +22,18 @@ const SoundSettingsFields = () => {
|
||||
hotlists,
|
||||
};
|
||||
|
||||
const handleSubmit = (values: FormValues) => {
|
||||
const handleSubmit = async (values: FormValues) => {
|
||||
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 (
|
||||
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
|
||||
@@ -68,8 +76,8 @@ const SoundSettingsFields = () => {
|
||||
name="hotlists"
|
||||
render={() => (
|
||||
<div className="w-full m-2">
|
||||
{values.hotlists.length > 0 ? (
|
||||
values.hotlists.map((hotlist, index) => (
|
||||
{values?.hotlists?.length > 0 ? (
|
||||
values?.hotlists?.map((hotlist, index) => (
|
||||
<div
|
||||
key={hotlist.name}
|
||||
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_B from "/NPED_Cat_B.svg";
|
||||
import NPED_CAT_C from "/NPED_Cat_C.svg";
|
||||
import { checkIsHotListHit } from "../../utils/utils";
|
||||
|
||||
type SightingModalProps = {
|
||||
isSightingModalOpen: boolean;
|
||||
@@ -65,7 +66,7 @@ const SightingModal = ({
|
||||
};
|
||||
|
||||
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 isNPEDHitB = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "B";
|
||||
const isNPEDHitC = sighting?.metadata?.npedJSON?.["NPED CATEGORY"] === "C";
|
||||
|
||||
@@ -5,7 +5,6 @@ type InfoBarprops = {
|
||||
obj: SightingType;
|
||||
};
|
||||
const InfoBar = ({ obj }: InfoBarprops) => {
|
||||
// const isNPEDHit = obj?.metadata?.npedJSON?.status_code === 404;
|
||||
const isNPEDHitD = obj?.metadata?.npedJSON?.["NPED CATEGORY"] === "D";
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 NumberPlate from "../PlateStack/NumberPlate";
|
||||
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_C from "/NPED_Cat_C.svg";
|
||||
import popup from "../../assets/sounds/ui/popup_open.mp3";
|
||||
import notification from "../../assets/sounds/ui/notification.mp3";
|
||||
import { useSound } from "react-sounds";
|
||||
import { useNPEDContext } from "../../context/NPEDUserContext";
|
||||
import { useSoundContext } from "../../context/SoundContext";
|
||||
import Loading from "../UI/Loading";
|
||||
import { checkIsHotListHit } from "../../utils/utils";
|
||||
|
||||
function useNow(tickMs = 1000) {
|
||||
const [, setNow] = useState(() => Date.now());
|
||||
@@ -43,11 +45,16 @@ export default function SightingHistoryWidget({
|
||||
useNow(1000);
|
||||
const { state } = useSoundContext();
|
||||
|
||||
const soundSrc = useMemo(() => {
|
||||
const soundSrcNped = useMemo(() => {
|
||||
return getSoundFileURL(state.NPEDsound) ?? popup;
|
||||
}, [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 {
|
||||
sightings,
|
||||
setSelectedSighting,
|
||||
@@ -61,15 +68,24 @@ export default function SightingHistoryWidget({
|
||||
const { dispatch } = useAlertHitContext();
|
||||
const { sessionStarted, setSessionList, sessionList } = useNPEDContext();
|
||||
|
||||
const reduceObject = (obj: SightingType): ReducedSightingType => {
|
||||
return {
|
||||
vrm: obj.vrm,
|
||||
metadata: obj?.metadata,
|
||||
};
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (sessionStarted) {
|
||||
if (!mostRecent) return;
|
||||
setSessionList([...sessionList, mostRecent]);
|
||||
const reducedMostRecent = reduceObject(mostRecent);
|
||||
setSessionList([...sessionList, reducedMostRecent]);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [mostRecent, sessionStarted, setSessionList]);
|
||||
|
||||
const hasAutoOpenedRef = useRef(false);
|
||||
const npedRef = useRef(false);
|
||||
|
||||
const onRowClick = useCallback(
|
||||
(sighting: SightingType) => {
|
||||
@@ -98,24 +114,43 @@ export default function SightingHistoryWidget({
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [dispatch, rows]);
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hasAutoOpenedRef.current) return;
|
||||
const firstHot = rows?.find((r) => {
|
||||
const isHotListHit = r?.metadata?.hotlistMatches?.Hotlist0 === true;
|
||||
if (hasAutoOpenedRef.current || npedRef.current) return;
|
||||
const firstNPED = rows.find((r) => {
|
||||
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 || 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) {
|
||||
setSelectedSighting(firstHot);
|
||||
play();
|
||||
hotlistsound();
|
||||
setSightingModalOpen(true);
|
||||
hasAutoOpenedRef.current = true;
|
||||
}
|
||||
}, [play, rows, setSelectedSighting, setSightingModalOpen]);
|
||||
}, [
|
||||
hotlistsound,
|
||||
npedSound,
|
||||
rows,
|
||||
setSelectedSighting,
|
||||
setSightingModalOpen,
|
||||
]);
|
||||
|
||||
const handleClose = () => {
|
||||
setSightingModalOpen(false);
|
||||
@@ -144,11 +179,8 @@ export default function SightingHistoryWidget({
|
||||
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 isHotListHit =
|
||||
obj?.metadata?.hotlistMatches?.Hotlist0 === true;
|
||||
const isHotListHit = checkIsHotListHit(obj);
|
||||
return (
|
||||
<div
|
||||
key={obj.ref}
|
||||
@@ -156,9 +188,7 @@ export default function SightingHistoryWidget({
|
||||
onClick={() => onRowClick(obj)}
|
||||
>
|
||||
<div
|
||||
className={`flex items-center gap-3 mt-2 justify-between ${
|
||||
isNPEDHitD ? " border-amber-600" : ""
|
||||
}`}
|
||||
className={`flex items-center gap-3 mt-2 justify-between `}
|
||||
>
|
||||
<div className={`border p-1 `}>
|
||||
<img
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
import { faVolumeHigh, faVolumeXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useSoundEnabled } from "react-sounds";
|
||||
import { useCameraBlackboard } from "../../hooks/useCameraBlackboard";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const SoundBtn = () => {
|
||||
const { mutation, query } = useCameraBlackboard();
|
||||
const [enabled, setEnabled] = useSoundEnabled();
|
||||
|
||||
const handleClick = () => {
|
||||
setEnabled(!enabled);
|
||||
const handleClick = async () => {
|
||||
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 (
|
||||
<button onClick={handleClick}>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { createContext, useContext, type SetStateAction } from "react";
|
||||
import type { NPEDUser, SightingType } from "../types/types";
|
||||
import type { NPEDUser, ReducedSightingType } from "../types/types";
|
||||
|
||||
type UserContextValue = {
|
||||
user: NPEDUser | null;
|
||||
setUser: React.Dispatch<SetStateAction<NPEDUser | null>>;
|
||||
sessionStarted: boolean;
|
||||
setSessionStarted: React.Dispatch<SetStateAction<boolean>>;
|
||||
sessionList: SightingType[];
|
||||
setSessionList: React.Dispatch<SetStateAction<SightingType[]>>;
|
||||
sessionList: ReducedSightingType[];
|
||||
setSessionList: React.Dispatch<SetStateAction<ReducedSightingType[]>>;
|
||||
};
|
||||
|
||||
export const NPEDUserContext = createContext<UserContextValue | undefined>(
|
||||
|
||||
@@ -15,9 +15,7 @@ type SightingFeedContextType = {
|
||||
isError: boolean;
|
||||
isLoading: boolean;
|
||||
data: SightingType | undefined;
|
||||
sessionList: SightingType[];
|
||||
sessionStarted: boolean;
|
||||
setSessionStarted: (started: boolean) => void;
|
||||
};
|
||||
|
||||
export const SightingFeedContext = createContext<
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, type ReactNode } from "react";
|
||||
import type { NPEDUser, SightingType } from "../../types/types";
|
||||
import type { NPEDUser, ReducedSightingType } from "../../types/types";
|
||||
import { NPEDUserContext } from "../NPEDUserContext";
|
||||
|
||||
type NPEDUserProviderType = {
|
||||
@@ -9,7 +9,7 @@ type NPEDUserProviderType = {
|
||||
export const NPEDUserProvider = ({ children }: NPEDUserProviderType) => {
|
||||
const [user, setUser] = useState<NPEDUser | null>(null);
|
||||
const [sessionStarted, setSessionStarted] = useState(false);
|
||||
const [sessionList, setSessionList] = useState<SightingType[]>([]);
|
||||
const [sessionList, setSessionList] = useState<ReducedSightingType[]>([]);
|
||||
|
||||
return (
|
||||
<NPEDUserContext.Provider
|
||||
|
||||
@@ -23,9 +23,7 @@ export const SightingFeedProvider = ({
|
||||
setSelectedSighting,
|
||||
selectedSighting,
|
||||
mostRecent,
|
||||
sessionList,
|
||||
sessionStarted,
|
||||
setSessionStarted,
|
||||
} = useSightingFeed(url);
|
||||
|
||||
const [isSightingModalOpen, setSightingModalOpen] = useState(false);
|
||||
@@ -45,9 +43,7 @@ export const SightingFeedProvider = ({
|
||||
isLoading,
|
||||
side,
|
||||
data,
|
||||
sessionList,
|
||||
sessionStarted,
|
||||
setSessionStarted,
|
||||
}}
|
||||
>
|
||||
{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 { initialState, reducer } from "../reducers/SoundContextReducer";
|
||||
import { useCameraBlackboard } from "../../hooks/useCameraBlackboard";
|
||||
|
||||
type SoundContextProviderProps = {
|
||||
children: ReactNode;
|
||||
@@ -8,6 +9,20 @@ type SoundContextProviderProps = {
|
||||
|
||||
const SoundContextProvider = ({ children }: SoundContextProviderProps) => {
|
||||
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]);
|
||||
return (
|
||||
<SoundContext.Provider value={value}>{children}</SoundContext.Provider>
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { SoundAction, SoundState } from "../../types/types";
|
||||
export const initialState: SoundState = {
|
||||
sightingSound: "switch",
|
||||
NPEDsound: "popup",
|
||||
hotlists: [],
|
||||
hotlists: [{ name: "hotlistName", sound: "notification" }],
|
||||
soundOptions: [
|
||||
{ name: "switch (Default)", soundFile: null },
|
||||
{ name: "popup", soundFile: null },
|
||||
@@ -18,7 +18,7 @@ export function reducer(state: SoundState, action: SoundAction): SoundState {
|
||||
...state,
|
||||
sightingSound: action.payload.sightingSound,
|
||||
NPEDsound: action.payload.NPEDsound,
|
||||
hotlists: action.payload.hotlists.map((hotlist) => ({
|
||||
hotlists: action.payload.hotlists?.map((hotlist) => ({
|
||||
name: hotlist.name,
|
||||
sound: hotlist.sound,
|
||||
})),
|
||||
|
||||
@@ -45,7 +45,6 @@ export const useCameraBlackboard = () => {
|
||||
id: "viewBlackboardData",
|
||||
});
|
||||
},
|
||||
onSuccess: () => toast.success("Sighting successfully added to alert list"),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -22,7 +22,6 @@ export function useSightingFeed(url: string | undefined) {
|
||||
const [sightings, setSightings] = useState<SightingType[]>([]);
|
||||
const [selectedRef, setSelectedRef] = useState<number | null>(null);
|
||||
const [sessionStarted, setSessionStarted] = useState(false);
|
||||
const [sessionList, setSessionList] = useState<SightingType[]>([]);
|
||||
const mostRecent = sightings[0] ?? null;
|
||||
const latestRef = mostRecent?.ref ?? null;
|
||||
const [selectedSighting, setSelectedSighting] = useState<SightingType | null>(
|
||||
@@ -39,7 +38,7 @@ export function useSightingFeed(url: string | undefined) {
|
||||
return latestRef;
|
||||
}, [latestRef]);
|
||||
const soundSrc = useMemo(() => {
|
||||
return getSoundFileURL(state.sightingSound) ?? switchSound;
|
||||
return getSoundFileURL(state?.sightingSound) ?? switchSound;
|
||||
}, [state.sightingSound]);
|
||||
|
||||
//use latestref instead of trigger to revert back
|
||||
@@ -76,13 +75,6 @@ export function useSightingFeed(url: string | undefined) {
|
||||
staleTime: 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (sessionStarted) {
|
||||
if (!mostRecent) return;
|
||||
setSessionList([...sessionList, mostRecent]);
|
||||
}
|
||||
}, [mostRecent, sessionList, sessionStarted]);
|
||||
|
||||
useEffect(() => {
|
||||
const data = query.data;
|
||||
if (!data) return;
|
||||
@@ -129,7 +121,6 @@ export function useSightingFeed(url: string | undefined) {
|
||||
setSelectedRef,
|
||||
mostRecent,
|
||||
selectedSighting,
|
||||
sessionList,
|
||||
sessionStarted,
|
||||
setSessionStarted,
|
||||
setSelectedSighting,
|
||||
|
||||
@@ -30,6 +30,11 @@ export type SightingType = {
|
||||
metadata?: Metadata;
|
||||
};
|
||||
|
||||
export type ReducedSightingType = {
|
||||
vrm: string;
|
||||
metadata?: Metadata;
|
||||
};
|
||||
|
||||
export type CameraSettingValues = {
|
||||
friendlyName: string;
|
||||
cameraAddress: string;
|
||||
@@ -103,6 +108,10 @@ export type NpedJSON = {
|
||||
status_code: number;
|
||||
reason_phrase: string;
|
||||
"NPED CATEGORY": "A" | "B" | "C" | "D";
|
||||
"MOT STATUS": boolean;
|
||||
"TAX STATUS": boolean;
|
||||
vrm: string;
|
||||
"INSURANCE STATUS": string;
|
||||
};
|
||||
|
||||
export type NPEDUser = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import switchSound from "../assets/sounds/ui/switch.mp3";
|
||||
import popup from "../assets/sounds/ui/popup_open.mp3";
|
||||
import notification from "../assets/sounds/ui/notification.mp3";
|
||||
import type { SightingType } from "../types/types";
|
||||
|
||||
export function getSoundFileURL(name: string) {
|
||||
const sounds: Record<string, string> = {
|
||||
@@ -129,59 +130,12 @@ export function drawRects(
|
||||
});
|
||||
}
|
||||
|
||||
// setSelectedRef(data?.ref);
|
||||
|
||||
//setItems(data);
|
||||
|
||||
// const selected = useMemo(
|
||||
// () =>
|
||||
// selectedRef == null
|
||||
// ? 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]);
|
||||
export const checkIsHotListHit = (sigthing: SightingType | null) => {
|
||||
if (!sigthing) return;
|
||||
if (sigthing?.metadata?.hotlistMatches) {
|
||||
const isHotListHit = Object.values(
|
||||
sigthing?.metadata?.hotlistMatches
|
||||
).includes(true);
|
||||
return isHotListHit;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,6 +5,14 @@ import tailwindcss from "@tailwindcss/vite";
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
server: { host: true },
|
||||
server: {
|
||||
host: true,
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: "http://100.118.196.113:8080",
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
base: "/Mobile",
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user