diff --git a/src/components/CameraSettings/CameraSettingFields.tsx b/src/components/CameraSettings/CameraSettingFields.tsx index 3d6bdf6..f81f1e4 100644 --- a/src/components/CameraSettings/CameraSettingFields.tsx +++ b/src/components/CameraSettings/CameraSettingFields.tsx @@ -26,7 +26,6 @@ const CameraSettingFields = () => { const handleSubmit = (values: CameraSettingValues) => { // post values to endpoint toast("Settings Saved"); - console.log(values); }; return ( diff --git a/src/components/SettingForms/BearerType/BearerTypeFields.tsx b/src/components/SettingForms/BearerType/BearerTypeFields.tsx index 9270860..5dee2d4 100644 --- a/src/components/SettingForms/BearerType/BearerTypeFields.tsx +++ b/src/components/SettingForms/BearerType/BearerTypeFields.tsx @@ -8,7 +8,6 @@ export const ValuesComponent = () => { const BearerTypeFields = () => { const { values } = useFormikContext(); - console.log(values); return (
diff --git a/src/components/SettingForms/NPED/NPEDCard.tsx b/src/components/SettingForms/NPED/NPEDCard.tsx index c60ddba..e7f222f 100644 --- a/src/components/SettingForms/NPED/NPEDCard.tsx +++ b/src/components/SettingForms/NPED/NPEDCard.tsx @@ -1,11 +1,8 @@ import Card from "../../UI/Card"; import CardHeader from "../../UI/CardHeader"; import NPEDFields from "./NPEDFields"; -import { useNPEDContext } from "../../../context/NPEDUserContext"; const NPEDCard = () => { - const { user } = useNPEDContext(); - console.log(user); return ( diff --git a/src/components/SettingForms/NPED/NPEDFields.tsx b/src/components/SettingForms/NPED/NPEDFields.tsx index e8a1293..dd1cff1 100644 --- a/src/components/SettingForms/NPED/NPEDFields.tsx +++ b/src/components/SettingForms/NPED/NPEDFields.tsx @@ -5,22 +5,30 @@ import { useNPEDAuth } from "../../../hooks/useNPEDAuth"; import { toast } from "sonner"; const NPEDFields = () => { - const { signIn } = useNPEDAuth(); + const { signIn, user, signOut } = useNPEDAuth(); - const initialValues = { - username: "", - password: "", - clientId: "", - frontId: "NPEDFront", - rearId: "NPEDRear", - }; + const initialValues = user + ? { + username: user.propUsername.value, + password: "", + clientId: user.propClientID.value, + frontId: "NPED", + rearId: "NPED", + } + : { + username: "", + password: "", + clientId: "", + frontId: "NPED", + rearId: "NPED", + }; const handleSubmit = (values: NPEDFieldType) => { const valuesToSend = { ...values, }; signIn(valuesToSend); - toast("Signed in successfully"); + toast.success("Signed in successfully"); }; const validateValues = (values: NPEDFieldType) => { @@ -31,6 +39,10 @@ const NPEDFields = () => { return errors; }; + const handleLogoutClick = () => { + signOut(); + }; + return ( { className="p-1.5 border border-gray-400 rounded-lg" /> - + {!user?.propClientID?.value ? ( + + ) : ( + + )} )} diff --git a/src/components/SightingOverview/SightingOverview.tsx b/src/components/SightingOverview/SightingOverview.tsx index 07b54e5..7092e08 100644 --- a/src/components/SightingOverview/SightingOverview.tsx +++ b/src/components/SightingOverview/SightingOverview.tsx @@ -23,13 +23,14 @@ const SightingOverview = () => { setOverlayMode((m) => ((m + 1) % 3) as 0 | 1 | 2); }, []); - const { effectiveSelected, side, mostRecent, noSighting } = + const { effectiveSelected, side, mostRecent, noSighting, isPending } = useSightingFeedContext(); useOverviewOverlay(mostRecent, overlayMode, imgRef, canvasRef); const { sync } = useHiDPICanvas(imgRef, canvasRef); - if (noSighting) return

loading

; + + if (noSighting || isPending) return

loading

; return (
diff --git a/src/components/SightingsWidget/SightingWidget.tsx b/src/components/SightingsWidget/SightingWidget.tsx index 0fea298..65d5d20 100644 --- a/src/components/SightingsWidget/SightingWidget.tsx +++ b/src/components/SightingsWidget/SightingWidget.tsx @@ -50,11 +50,12 @@ export default function SightingHistoryWidget({ {/* Rows */}
{rows?.map((obj, idx) => { + console.log(obj); + const isNPEDHit = obj?.metadata?.npedJSON?.status_code === 201; const isSelected = obj?.ref === selectedRef; const motionAway = (obj?.motion ?? "").toUpperCase() === "AWAY"; const primaryIsColour = obj?.srcCam === 1; const secondaryMissing = (obj?.vrmSecondary ?? "") === ""; - return (
{/* Patch row */} -
+
{ + return
; +}; + +export default SkeletonBox; diff --git a/src/context/NPEDUserContext.tsx b/src/context/NPEDUserContext.tsx index 1acce93..a21e3e3 100644 --- a/src/context/NPEDUserContext.tsx +++ b/src/context/NPEDUserContext.tsx @@ -1,8 +1,8 @@ import { createContext, useContext, type SetStateAction } from "react"; -import type { NPEDUser } from "../types/types"; +import type { NPEDCameraConfig, NPEDUser } from "../types/types"; type UserContextValue = { - user: NPEDUser | null; + user: NPEDCameraConfig | null; setUser: React.Dispatch>; }; diff --git a/src/hooks/useGetOverviewSnapshot.ts b/src/hooks/useGetOverviewSnapshot.ts index 0f454d4..5db572c 100644 --- a/src/hooks/useGetOverviewSnapshot.ts +++ b/src/hooks/useGetOverviewSnapshot.ts @@ -4,7 +4,6 @@ import { useQuery } from "@tanstack/react-query"; const apiUrl = import.meta.env.VITE_BASEURL; async function fetchSnapshot(cameraSide: string) { - console.log(`${apiUrl}/${cameraSide}-preview`); const response = await fetch( // `http://100.116.253.81/Colour-preview` `${apiUrl}/${cameraSide}-preview` diff --git a/src/hooks/useLatestSighting.ts b/src/hooks/useLatestSighting.ts index f0c5123..ab68acb 100644 --- a/src/hooks/useLatestSighting.ts +++ b/src/hooks/useLatestSighting.ts @@ -3,6 +3,7 @@ import { useCallback, useEffect, useRef } from "react"; async function fetchSighting() { const response = await fetch( + // `http://100.82.205.44/api` `http://100.116.253.81/mergedHistory/sightingSummary?mostRecentRef=-1` ); if (!response.ok) throw new Error("Failed to fetch sighting"); diff --git a/src/hooks/useNPEDAuth.ts b/src/hooks/useNPEDAuth.ts index fb93de6..962d075 100644 --- a/src/hooks/useNPEDAuth.ts +++ b/src/hooks/useNPEDAuth.ts @@ -1,15 +1,22 @@ -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQuery } from "@tanstack/react-query"; import type { NPEDFieldType } from "../types/types"; import { useNPEDContext } from "../context/NPEDUserContext"; +import { useEffect } from "react"; const base_url = import.meta.env.VITE_OUTSIDE_BASEURL; +async function fetchNPEDDetails() { + const fetchUrl = `${base_url}/fetch-config?id=NPED`; + const response = await fetch(fetchUrl); + if (!response.ok) throw new Error("Cannot reach fetch-config endpoint"); + const data = await response.json(); + console.log(data); + return response.json(); +} async function signIn(loginDetails: NPEDFieldType) { const { frontId, rearId, username, password, clientId } = loginDetails; - const NPEDLoginURLFront = `${base_url}/update-config?id=${frontId}`; const NPEDLoginURLRear = `${base_url}/update-config?id=${rearId}`; - const frontCameraPayload = { id: frontId, fields: [ @@ -43,8 +50,6 @@ async function signIn(loginDetails: NPEDFieldType) { if (!frontCameraResponse.ok) throw new Error("cannot reach NPED endpoint"); if (!rearCameraResponse.ok) throw new Error("cannot reach NPED endpoint"); - console.log(frontCameraResponse); - console.log(rearCameraResponse); return { frontResponse: frontCameraResponse.json(), rearResponse: rearCameraResponse.json(), @@ -52,13 +57,28 @@ async function signIn(loginDetails: NPEDFieldType) { } async function signOut() { - const response = await fetch(url, { method: "POST" }); + const nullPayload = { + id: "NPED", + fields: [ + { property: "propEnabled", value: false }, + { property: "propUsername", value: "" }, + { property: "propPassword", value: "" }, + { property: "propClientID", value: "" }, + ], + }; + const NPEDLoginURLFront = `${base_url}/update-config?id=NPED`; + const response = await fetch(NPEDLoginURLFront, { + method: "POST", + body: JSON.stringify(nullPayload), + }); + if (!response.ok) throw new Error("cannot reach NPED sign out endpoint"); + return response.json(); } export const useNPEDAuth = () => { - const { setUser } = useNPEDContext(); + const { setUser, user } = useNPEDContext(); const signInMutation = useMutation({ mutationKey: ["NPEDSignin"], @@ -69,9 +89,24 @@ export const useNPEDAuth = () => { const signOutMutation = useMutation({ mutationKey: ["auth", "NPEDSignOut"], mutationFn: signOut, - onSuccess: () => setUser(null), }); + const fetchdataQuery = useQuery({ + queryKey: ["auth", "NPEDFetch"], + queryFn: fetchNPEDDetails, + }); + + useEffect(() => { + if (fetchdataQuery.isSuccess && fetchdataQuery.data) { + setUser(fetchdataQuery.data); + } else if ( + !fetchdataQuery?.data?.propUsername?.value && + !fetchdataQuery?.data?.propClientID?.value + ) { + setUser(null); + } + }, [fetchdataQuery.data, fetchdataQuery.isSuccess, setUser]); + return { signIn: signInMutation.mutate, signInAsync: signInMutation.mutateAsync, @@ -79,7 +114,8 @@ export const useNPEDAuth = () => { isError: signInMutation.isError, error: signInMutation.error, data: signInMutation.data, - + user, + setUser, signOut: signOutMutation.mutate, }; }; diff --git a/src/hooks/useSightingFeed.ts b/src/hooks/useSightingFeed.ts index dc92028..0a0764e 100644 --- a/src/hooks/useSightingFeed.ts +++ b/src/hooks/useSightingFeed.ts @@ -6,6 +6,7 @@ import { useQuery } from "@tanstack/react-query"; async function fetchSighting(url: string, ref: number, signal?: AbortSignal) { const dynamicUrl = `${url}${ref}`; + const res = await fetch(dynamicUrl, { signal }); if (!res.ok) throw new Error(String(res.status)); return (await res.json()) as SightingWidgetType; @@ -20,12 +21,13 @@ export function useSightingFeed(url: string) { const [mostRecent, setMostRecent] = useState(null); const mostRecentRef = useRef(-1); + const lastSeenRef = useRef(null); const { data, isPending } = useQuery({ queryKey: ["sighting"], queryFn: ({ signal }) => fetchSighting(url, mostRecentRef.current, signal), - refetchInterval: 200, + refetchInterval: 2000, refetchIntervalInBackground: true, refetchOnWindowFocus: false, staleTime: 0, diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index 4ad4f56..05f537b 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -9,7 +9,8 @@ const Dashboard = () => {
diff --git a/src/pages/SystemSettings.tsx b/src/pages/SystemSettings.tsx index 11ca42b..0ef5ec7 100644 --- a/src/pages/SystemSettings.tsx +++ b/src/pages/SystemSettings.tsx @@ -4,8 +4,11 @@ import NPEDCard from "../components/SettingForms/NPED/NPEDCard"; import SettingForms from "../components/SettingForms/SettingForms/SettingForms"; import NPEDHotlistCard from "../components/SettingForms/NPED/NPEDHotlistCard"; import { Toaster } from "sonner"; +import { useNPEDAuth } from "../hooks/useNPEDAuth"; const SystemSettings = () => { + const { user } = useNPEDAuth(); + console.log(user); return (
diff --git a/src/types/types.ts b/src/types/types.ts index 3ecc360..6f2e18c 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -97,7 +97,25 @@ export type SightingWidgetType = { plateUrlInfrared?: string; plateUrlColour?: string; overviewPlateRect?: [number, number, number, number]; // [x,y,w,h] normalized 0..1 - plateTrack?: [number, number, number, number][]; // list of rects normalized 0..1 + plateTrack?: [number, number, number, number][]; + metadata?: Metadata; + // list of rects normalized 0..1 +}; + +export type Metadata = { + npedJSON: NpedJSON; + "hotlist-matches": HotlistMatches; +}; + +export type HotlistMatches = { + Hotlist0: boolean; + Hotlist1: boolean; + Hotlist2: boolean; +}; + +export type NpedJSON = { + status_code: number; + reason_phrase: string; }; export type NPEDUser = { @@ -111,3 +129,22 @@ export type NPEDErrorValues = { password?: string | undefined; clientId?: string | undefined; }; + +export type NPEDCameraConfig = { + id: string; + configHash: string; + propEnabled: Prop; + propUsername: Prop; + propPassword: Prop; + propClientID: Prop; + propConnectTimeoutSeconds: Prop; + propReadTimeoutSeconds: Prop; + propNPEDAuthTokenURL: Prop; + propNPEDLookupURL: Prop; + propVerbose: Prop; +}; + +export interface Prop { + value: string; + datatype: string; +}