diff --git a/TODO.txt b/TODO.txt index e2ff0e0..8f76759 100644 --- a/TODO.txt +++ b/TODO.txt @@ -11,6 +11,7 @@ The selected sighting in the sighting stack seems a tad buggy. Sometimes multipl Can the selected sighting be shown in full detail. How this will look is still up for debate. Either as a pop up card as in AiQ Flexi, or in the OVerview card?? How do you know if the time has sync? Make UTC red if not sync. Can the relative aspect ratio in SightingOverview.tsx be the ratio of image pixel size of the image to best take advantage of the space? +obscure details on dashboard to a toggle FYI: diff --git a/index.html b/index.html index 118720a..c50d2a6 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,7 @@ - + MAV | In Car System diff --git a/public/MAV-Blue.svg b/public/MAV-Blue.svg new file mode 100644 index 0000000..99dc9b2 --- /dev/null +++ b/public/MAV-Blue.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/CameraSettings/CameraSettingFields.tsx b/src/components/CameraSettings/CameraSettingFields.tsx index 1d1f98d..4654d2d 100644 --- a/src/components/CameraSettings/CameraSettingFields.tsx +++ b/src/components/CameraSettings/CameraSettingFields.tsx @@ -5,13 +5,13 @@ import type { } from "../../types/types"; import { toast } from "sonner"; -const CameraSettingFields = () => { +const CameraSettingFields = ({ initialData, updateCameraConfig }) => { const initialValues: CameraSettingValues = { - friendlyName: "", + friendlyName: initialData?.propLEDDriverControlURI?.value, cameraAddress: "", userName: "", password: "", - setupCamera: 1, + id: initialData?.id, }; const validateValues = (values: CameraSettingValues) => { @@ -29,7 +29,7 @@ const CameraSettingFields = () => { const handleSubmit = (values: CameraSettingValues) => { // post values to endpoint - console.log(values); + updateCameraConfig(values); toast("Settings Saved"); }; diff --git a/src/components/CameraSettings/CameraSettings.tsx b/src/components/CameraSettings/CameraSettings.tsx index 3e714bf..cdbf4f7 100644 --- a/src/components/CameraSettings/CameraSettings.tsx +++ b/src/components/CameraSettings/CameraSettings.tsx @@ -1,15 +1,28 @@ +import { useFetchCameraConfig } from "../../hooks/useCameraConfig"; import Card from "../UI/Card"; import CardHeader from "../UI/CardHeader"; import CameraSettingFields from "./CameraSettingFields"; import { faWrench } from "@fortawesome/free-solid-svg-icons"; -const CameraSettings = ({ title }: { title: string }) => { +const CameraSettings = ({ title, side }: { title: string; side: string }) => { + const { data, isError, isPending, updateCameraConfig } = + useFetchCameraConfig(side); + return ( -
- - -
+ {isError && <>Cannot Fetch camera config} + + {isPending ? ( + <>Loading + ) : ( +
+ + +
+ )}
); }; diff --git a/src/components/SettingForms/System/SettingSaveRecall.tsx b/src/components/SettingForms/System/SettingSaveRecall.tsx index e5cbaab..beb2b68 100644 --- a/src/components/SettingForms/System/SettingSaveRecall.tsx +++ b/src/components/SettingForms/System/SettingSaveRecall.tsx @@ -1,74 +1,88 @@ -export async function handleSystemSave(deviceName: string, sntpServer: string, sntpInterval: number, timeZone: string) { - const payload = { // Build JSON - id: "GLOBAL--Device", - fields: [ - { property: "propDeviceName", value: deviceName }, - { property: "propSNTPServer", value: sntpServer }, - { property: "propSNTPIntervalMinutes", value: Number(sntpInterval) }, - { property: "propLocalTimeZone", value: timeZone } - ] - }; +export async function handleSystemSave( + deviceName: string, + sntpServer: string, + sntpInterval: number, + timeZone: string +) { + const payload = { + // Build JSON + id: "GLOBAL--Device", + fields: [ + { property: "propDeviceName", value: deviceName }, + { property: "propSNTPServer", value: sntpServer }, + { property: "propSNTPIntervalMinutes", value: Number(sntpInterval) }, + { property: "propLocalTimeZone", value: timeZone }, + ], + }; - try { - const response = await fetch("http://192.168.75.11/api/update-config", { - method: "POST", - headers: { - "Content-Type": "application/json", - "Accept": "application/json" - }, - body: JSON.stringify(payload) - }); + try { + const response = await fetch("http://192.168.75.11/api/update-config", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify(payload), + }); - if (!response.ok) { - const text = await response.text().catch(() => ""); - throw new Error(`HTTP ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`); - } - - alert("System Settings Saved Successfully!"); - } catch (err) { - console.error(err); + if (!response.ok) { + const text = await response.text().catch(() => ""); + throw new Error( + `HTTP ${response.status} ${response.statusText}${ + text ? ` - ${text}` : "" + }` + ); } + + alert("System Settings Saved Successfully!"); + } catch (err) { + console.error(err); + } } export async function handleSystemRecall() { - const url = "http://192.168.75.11/api/fetch-config?id=GLOBAL--Device"; + const url = "http://192.168.75.11/api/fetch-config?id=GLOBAL--Device"; - const controller = new AbortController(); - const timeoutId = setTimeout(() => controller.abort(), 7000); + const controller = new AbortController(); + const timeoutId = setTimeout(() => controller.abort(), 7000); - try { - const response = await fetch(url, { - method: "GET", - headers: { "Accept": "application/json" }, - signal: controller.signal - }); + try { + const response = await fetch(url, { + method: "GET", + headers: { Accept: "application/json" }, + signal: controller.signal, + }); - if (!response.ok) { - const text = await response.text().catch(() => ""); - throw new Error(`HTTP ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`); - } - - const data = await response.json(); - - const deviceName = data?.propDeviceName?.value ?? null; - const sntpServer = data?.propSNTPServer?.value ?? null; - const timeZone = data?.propLocalTimeZone?.value ?? null; - - let sntpIntervalRaw = data?.propSNTPIntervalMinutes?.value; - let sntpInterval = - typeof sntpIntervalRaw === "number" - ? sntpIntervalRaw - : Number.parseInt(String(sntpIntervalRaw).trim(), 10); - - if (!Number.isFinite(sntpInterval)) { - sntpInterval = 60; - } - - return { deviceName, sntpServer, sntpInterval, timeZone }; - } catch (err) { - console.error(err); - return null; - } finally { - clearTimeout(timeoutId); + if (!response.ok) { + const text = await response.text().catch(() => ""); + throw new Error( + `HTTP ${response.status} ${response.statusText}${ + text ? ` - ${text}` : "" + }` + ); } -} \ No newline at end of file + + const data = await response.json(); + + const deviceName = data?.propDeviceName?.value ?? null; + const sntpServer = data?.propSNTPServer?.value ?? null; + const timeZone = data?.propLocalTimeZone?.value ?? null; + + const sntpIntervalRaw = data?.propSNTPIntervalMinutes?.value; + let sntpInterval = + typeof sntpIntervalRaw === "number" + ? sntpIntervalRaw + : Number.parseInt(String(sntpIntervalRaw).trim(), 10); + + if (!Number.isFinite(sntpInterval)) { + sntpInterval = 60; + } + + return { deviceName, sntpServer, sntpInterval, timeZone }; + } catch (err) { + console.error(err); + return null; + } finally { + clearTimeout(timeoutId); + } +} diff --git a/src/components/SightingModal/SightingModal.tsx b/src/components/SightingModal/SightingModal.tsx new file mode 100644 index 0000000..59e19b1 --- /dev/null +++ b/src/components/SightingModal/SightingModal.tsx @@ -0,0 +1,32 @@ +import NumberPlate from "../PlateStack/NumberPlate"; +import ModalComponent from "../UI/ModalComponent"; + +type SightingModalProps = { + isSightingModalOpen: boolean; + handleClose: () => void; +}; + +const SightingModal = ({ + isSightingModalOpen, + handleClose, + sighting, +}: SightingModalProps) => { + const motionAway = (sighting?.motion ?? "").toUpperCase() === "AWAY"; + return ( + + +
+

{sighting?.vrm}

+ + infrared patch +
+
+ ); +}; + +export default SightingModal; diff --git a/src/components/SightingOverview/SightingOverview.tsx b/src/components/SightingOverview/SightingOverview.tsx index 47aad61..ceca05d 100644 --- a/src/components/SightingOverview/SightingOverview.tsx +++ b/src/components/SightingOverview/SightingOverview.tsx @@ -5,15 +5,15 @@ import { useOverviewOverlay } from "../../hooks/useOverviewOverlay"; import { useSightingFeedContext } from "../../context/SightingFeedContext"; import { useHiDPICanvas } from "../../hooks/useHiDPICanvas"; import NavigationArrow from "../UI/NavigationArrow"; -import { useSwipeable } from "react-swipeable"; -import { useNavigate } from "react-router"; +// import { useSwipeable } from "react-swipeable"; +// import { useNavigate } from "react-router"; const SightingOverview = () => { - const navigate = useNavigate(); - const handlers = useSwipeable({ - onSwipedRight: () => navigate("/front-camera-settings"), - trackMouse: true, - }); + // const navigate = useNavigate(); + // const handlers = useSwipeable({ + // onSwipedRight: () => navigate("/front-camera-settings"), + // trackMouse: true, + // }); const [overlayMode, setOverlayMode] = useState<0 | 1 | 2>(0); const imgRef = useRef(null); @@ -23,51 +23,51 @@ const SightingOverview = () => { setOverlayMode((m) => ((m + 1) % 3) as 0 | 1 | 2); }, []); - const { effectiveSelected, side, mostRecent, noSighting, isPending } = - useSightingFeedContext(); + const { effectiveSelected, side, mostRecent } = useSightingFeedContext(); useOverviewOverlay(mostRecent, overlayMode, imgRef, canvasRef); const { sync } = useHiDPICanvas(imgRef, canvasRef); - if (noSighting || isPending) return

loading

; + // if (noSighting || isPending) return

loading

; + return ( -
-
+
+
-
- { - sync(); - setOverlayMode((m) => m); - }} - src={mostRecent?.overviewUrl || BLANK_IMG} - alt="overview" - className="absolute inset-0 w-full h-full object-contain cursor-pointer z-10" - onClick={onOverviewClick} - style={{ - display: mostRecent?.overviewUrl ? "block" : "none", - }} - /> - +
+
+ { + sync(); + setOverlayMode((m) => m); + }} + src={mostRecent?.overviewUrl || BLANK_IMG} + alt="overview" + className="absolute inset-0 w-full h-full object-contain cursor-pointer z-10 " + onClick={onOverviewClick} + style={{ + display: mostRecent?.overviewUrl ? "block" : "none", + }} + /> + +
+
+
+ Overlay:{" "} + {overlayMode === 0 + ? "Off" + : overlayMode === 1 + ? "Plate box" + : "Track + box"}{" "} + (click image to toggle)
- - -
- Overlay:{" "} - {overlayMode === 0 - ? "Off" - : overlayMode === 1 - ? "Plate box" - : "Track + box"}{" "} - (click image to toggle) -
); }; diff --git a/src/components/SightingsWidget/SightingWidget.tsx b/src/components/SightingsWidget/SightingWidget.tsx index ff76aab..7ba2ef8 100644 --- a/src/components/SightingsWidget/SightingWidget.tsx +++ b/src/components/SightingsWidget/SightingWidget.tsx @@ -1,11 +1,12 @@ import { useCallback, useEffect, useMemo, useState } from "react"; -import type { SightingWidgetType } from "../../types/types"; +import type { SightingType, SightingWidgetType } from "../../types/types"; import { BLANK_IMG, capitalize, formatAge } from "../../utils/utils"; import NumberPlate from "../PlateStack/NumberPlate"; import Card from "../UI/Card"; import CardHeader from "../UI/CardHeader"; import clsx from "clsx"; import { useSightingFeedContext } from "../../context/SightingFeedContext"; +import SightingModal from "../SightingModal/SightingModal"; function useNow(tickMs = 1000) { const [, setNow] = useState(() => Date.now()); @@ -29,97 +30,112 @@ export default function SightingHistoryWidget({ className, }: SightingHistoryWidgetProps) { useNow(1000); - const { sightings, selectedRef, setSelectedRef } = useSightingFeedContext(); + + const { + sightings, + setSelectedSighting, + setSightingModalOpen, + isSightingModalOpen, + selectedSighting, + } = useSightingFeedContext(); const onRowClick = useCallback( - (ref: number) => { - setSelectedRef(ref); + (sighting: SightingType) => { + if (!sighting) return; + setSightingModalOpen(!isSightingModalOpen); + setSelectedSighting(sighting); }, - [setSelectedRef] + [isSightingModalOpen, setSelectedSighting, setSightingModalOpen] ); - const rows = useMemo( () => sightings?.filter(Boolean) as SightingWidgetType[], [sightings] ); - + const handleClose = () => { + setSightingModalOpen(false); + }; return ( - - -
- {/* Rows */} -
- {rows?.map((obj, idx) => { - 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 ( -
onRowClick(obj.ref)} - > - {/* Info bar */} -
-
- CH: {obj ? obj.charHeight : "—"} -
-
- Seen: {obj ? obj.seenCount : "—"} -
-
- {obj ? capitalize(obj.motion) : "—"} -
-
- {obj ? formatAge(obj.timeStampMillis) : "—"} -
-
- - {/* Patch row */} + <> + + +
+ {/* Rows */} +
+ {rows?.map((obj, idx) => { + const isNPEDHit = obj?.metadata?.npedJSON?.status_code === 201; + const motionAway = (obj?.motion ?? "").toUpperCase() === "AWAY"; + const primaryIsColour = obj?.srcCam === 1; + const secondaryMissing = (obj?.vrmSecondary ?? "") === ""; + console.log(obj); + return (
onRowClick(obj)} + > + {/* Info bar */} +
+
+ CH: {obj ? obj.charHeight : "—"} +
+
+ Seen: {obj ? obj.seenCount : "—"} +
+
+ {obj ? capitalize(obj.motion) : "—"} +
+
+ {obj ? formatAge(obj.timeStampMillis) : "—"} +
+
+ + {/* Patch row */} +
-
- infrared patch +
+ infrared patch +
+
+ colour patch +
+
-
- colour patch -
-
-
- ); - })} + ); + })} +
-
- + + + ); } diff --git a/src/components/SightingsWidget/SightingWidgetDetails.tsx b/src/components/SightingsWidget/SightingWidgetDetails.tsx index 2bde728..4225148 100644 --- a/src/components/SightingsWidget/SightingWidgetDetails.tsx +++ b/src/components/SightingsWidget/SightingWidgetDetails.tsx @@ -1,4 +1,5 @@ import type { SightingWidgetType } from "../../types/types"; +import { useState } from "react"; type SightingWidgetDetailsProps = { effectiveSelected: SightingWidgetType | null; @@ -7,72 +8,88 @@ type SightingWidgetDetailsProps = { const SightingWidgetDetails = ({ effectiveSelected, }: SightingWidgetDetailsProps) => { + const [advancedDetailsEnabled, setAdvancedDetailsEnabled] = useState(false); + + const handleDetailsClick = () => + setAdvancedDetailsEnabled(!advancedDetailsEnabled); + return ( -
-
- VRM:{" "} - {effectiveSelected?.vrm ?? "—"} -
-
- Timestamp:{" "} - {effectiveSelected?.timeStamp ?? "—"} -
-
- Make:{" "} - {effectiveSelected?.make ?? "—"} -
-
- Model:{" "} - {effectiveSelected?.model ?? "—"} -
-
- Country:{" "} - {effectiveSelected?.countryCode ?? "—"} -
-
- Seen:{" "} - - {effectiveSelected?.seenCount ?? "—"} - -
-
- Colour:{" "} - {effectiveSelected?.color ?? "—"} -
-
- Category:{" "} - {effectiveSelected?.category ?? "—"} -
-
- Char Ht:{" "} - - {effectiveSelected?.charHeight ?? "—"} - -
-
- Plate Size:{" "} - - {effectiveSelected?.plateSize ?? "—"} - -
-
- Overview Size:{" "} - - {effectiveSelected?.overviewSize ?? "—"} - -
- {effectiveSelected?.detailsUrl ? ( -
- - Sighting Details - + <> +
+
+ VRM:{" "} + {effectiveSelected?.vrm ?? "—"}
- ) : null} -
+ +
+ Make:{" "} + {effectiveSelected?.make ?? "—"} +
+
+ Model:{" "} + {effectiveSelected?.model ?? "—"} +
+
+ Colour:{" "} + {effectiveSelected?.color ?? "—"} +
+
+ Timestamp:{" "} + + {effectiveSelected?.timeStamp ?? "—"} + +
+ {advancedDetailsEnabled && ( + <> + {" "} +
+ Country:{" "} + + {effectiveSelected?.countryCode ?? "—"} + +
+
+ Seen:{" "} + + {effectiveSelected?.seenCount ?? "—"} + +
+
+ Category:{" "} + + {effectiveSelected?.category ?? "—"} + +
+
+ Char Ht:{" "} + + {effectiveSelected?.charHeight ?? "—"} + +
+
+ Plate Size:{" "} + + {effectiveSelected?.plateSize ?? "—"} + +
+
+ Overview Size:{" "} + + {effectiveSelected?.overviewSize ?? "—"} + +
+ + )} +
+
+

+ Sighting Details +

+
+ ); }; diff --git a/src/components/UI/Header.tsx b/src/components/UI/Header.tsx index 4d71685..a0f298d 100644 --- a/src/components/UI/Header.tsx +++ b/src/components/UI/Header.tsx @@ -5,10 +5,17 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faGear, faListCheck } from "@fortawesome/free-solid-svg-icons"; import type { VersionFieldType } from "../../types/types"; -async function fetchVersions(signal?: AbortSignal): Promise { - const res = await fetch("http://192.168.75.11/api/versions", { signal }); - if (!res.ok) throw new Error(`HTTP ${res.status}`); - return res.json(); +async function fetchVersions( + signal?: AbortSignal +): Promise { + try { + const res = await fetch("http://192.168.75.11/api/versions", { signal }); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + return res.json(); + } catch (error) { + console.log(error); + return undefined; + } } const pad = (n: number) => String(n).padStart(2, "0"); @@ -33,10 +40,14 @@ export default function Header() { const ac = new AbortController(); fetchVersions(ac.signal) .then((data) => { - const serverMs = normalizeToMs(data.timeStamp); + if (!data) throw new Error("No data"); + const serverMs = normalizeToMs(data?.timeStamp); setOffsetMs(serverMs - Date.now()); }) - return () => ac.abort(); + .catch((err) => { + console.log(err); + }); + return () => ac.abort("failed"); }, []); React.useEffect(() => { @@ -69,13 +80,14 @@ export default function Header() {

Local: {localStr}

UTC: {utcStr}

- - - - - - - +
+ + + + + + +
); diff --git a/src/components/UI/ModalComponent.tsx b/src/components/UI/ModalComponent.tsx new file mode 100644 index 0000000..c85e90c --- /dev/null +++ b/src/components/UI/ModalComponent.tsx @@ -0,0 +1,26 @@ +import type React from "react"; +import Modal from "react-modal"; + +type ModalComponentProps = { + isModalOpen: boolean; + children: React.ReactNode; +}; + +const ModalComponent = ({ + isModalOpen, + children, + close, +}: ModalComponentProps) => { + return ( + + {children} + + ); +}; + +export default ModalComponent; diff --git a/src/components/UI/NavigationArrow.tsx b/src/components/UI/NavigationArrow.tsx index ebdc43e..6b7a4dc 100644 --- a/src/components/UI/NavigationArrow.tsx +++ b/src/components/UI/NavigationArrow.tsx @@ -29,13 +29,13 @@ const NavigationArrow = ({ side, settingsPage }: NavigationArrowProps) => { {side === "CameraFront" ? ( navigationDest(side)} /> ) : ( navigationDest(side)} /> )} @@ -47,13 +47,13 @@ const NavigationArrow = ({ side, settingsPage }: NavigationArrowProps) => { {side === "Front" ? ( navigationDest(side)} /> ) : ( navigationDest(side)} /> )} diff --git a/src/context/SightingFeedContext.tsx b/src/context/SightingFeedContext.tsx index 69605c6..37797cf 100644 --- a/src/context/SightingFeedContext.tsx +++ b/src/context/SightingFeedContext.tsx @@ -1,15 +1,17 @@ import { createContext, useContext } from "react"; -import type { SightingWidgetType } from "../types/types"; +import type { SightingType, SightingWidgetType } from "../types/types"; type SightingFeedContextType = { sightings: (SightingWidgetType | null | undefined)[]; selectedRef: number | null; setSelectedRef: (ref: number | null) => void; - effectiveSelected: SightingWidgetType | null; + // effectiveSelected: SightingWidgetType | null; mostRecent: SightingWidgetType | null; side: string; - isPending: boolean; - noSighting: boolean; + selectedSighting: SightingType | null; + setSelectedSighting: (sighting: SightingType | null) => void; + setSightingModalOpen: (isSightingModalOpen: boolean) => void; + isSightingModalOpen: boolean; }; export const SightingFeedContext = createContext< diff --git a/src/context/providers/SightingFeedProvider.tsx b/src/context/providers/SightingFeedProvider.tsx index a0708b1..e689f5d 100644 --- a/src/context/providers/SightingFeedProvider.tsx +++ b/src/context/providers/SightingFeedProvider.tsx @@ -1,4 +1,4 @@ -import type { ReactNode } from "react"; +import { useState, type ReactNode } from "react"; import { useSightingFeed } from "../../hooks/useSightingFeed"; import { SightingFeedContext } from "../SightingFeedContext"; @@ -17,23 +17,25 @@ export const SightingFeedProvider = ({ sightings, selectedRef, setSelectedRef, - effectiveSelected, + // effectiveSelected, + setSelectedSighting, + selectedSighting, mostRecent, - isPending, - noSighting, } = useSightingFeed(url); - + const [isSightingModalOpen, setSightingModalOpen] = useState(false); return ( {children} diff --git a/src/hooks/useCameraConfig.ts b/src/hooks/useCameraConfig.ts new file mode 100644 index 0000000..427de7d --- /dev/null +++ b/src/hooks/useCameraConfig.ts @@ -0,0 +1,52 @@ +// Used to fetch and load the configs for the camera side + +import { useMutation, useQuery } from "@tanstack/react-query"; + +const base_url = import.meta.env.VITE_OUTSIDE_BASEURL; + +const fetchCameraSideConfig = async ({ queryKey }) => { + const [, cameraSide] = queryKey; + const fetchUrl = `${base_url}/fetch-config?id=${cameraSide}`; + const response = await fetch(fetchUrl); + if (!response.ok) throw new Error("cannot react cameraSide "); + return response.json(); +}; + +const updateCamerasideConfig = async (data) => { + const updateUrl = `${base_url}/update-config?id=${data.id}`; + + const updateConfigPayload = { + id: data.id, + fields: [ + { + property: "propLEDDriverControlURI", + value: data.friendlyName, + }, + ], + }; + console.log(updateConfigPayload); + const response = await fetch(updateUrl, { + method: "POST", + body: JSON.stringify(updateConfigPayload), + }); + if (!response.ok) throw new Error("Cannot reach update camera endpoint"); +}; + +export const useFetchCameraConfig = (cameraSide: string) => { + const fetchedConfigQuery = useQuery({ + queryKey: ["cameraSideConfig", cameraSide], + queryFn: fetchCameraSideConfig, + }); + + const updateConfigMutation = useMutation({ + mutationKey: ["cameraSideConfigUpdate"], + mutationFn: updateCamerasideConfig, + }); + + return { + data: fetchedConfigQuery.data, + isPending: fetchedConfigQuery.isPending, + isError: fetchedConfigQuery.isError, + updateCameraConfig: updateConfigMutation.mutate, + }; +}; diff --git a/src/hooks/useGetConfigs.ts b/src/hooks/useGetConfigs.ts index 921fd57..909db71 100644 --- a/src/hooks/useGetConfigs.ts +++ b/src/hooks/useGetConfigs.ts @@ -7,6 +7,7 @@ export const useGetConfigs = () => { async function getConfigs() { try { const response = await fetch(`${apiUrl}/api/config-ids`); + if (!response.ok) { console.log("failed fetching"); } diff --git a/src/hooks/useGetOverviewSnapshot.ts b/src/hooks/useGetOverviewSnapshot.ts index 553a15b..ce04026 100644 --- a/src/hooks/useGetOverviewSnapshot.ts +++ b/src/hooks/useGetOverviewSnapshot.ts @@ -5,12 +5,13 @@ const apiUrl = import.meta.env.VITE_BASEURL; async function fetchSnapshot(cameraSide: string) { const response = await fetch( - // `http://100.116.253.81/Colour-preview` - `${apiUrl}/${cameraSide}-preview` + `http://100.116.253.81/Colour-preview` + // `${apiUrl}/${cameraSide}-preview` ); if (!response.ok) { throw new Error("Cannot reach endpoint"); } + return await response.blob(); } diff --git a/src/hooks/useNPEDAuth.ts b/src/hooks/useNPEDAuth.ts index 3e411cc..f173a84 100644 --- a/src/hooks/useNPEDAuth.ts +++ b/src/hooks/useNPEDAuth.ts @@ -35,7 +35,7 @@ async function signIn(loginDetails: NPEDFieldType) { { property: "propClientID", value: clientId }, ], }; - + console.log(frontId); const frontCameraResponse = await fetch(NPEDLoginURLFront, { method: "POST", body: JSON.stringify(frontCameraPayload), diff --git a/src/hooks/useSightingFeed.ts b/src/hooks/useSightingFeed.ts index d117335..f6df627 100644 --- a/src/hooks/useSightingFeed.ts +++ b/src/hooks/useSightingFeed.ts @@ -1,7 +1,10 @@ import { useEffect, useRef, useState } from "react"; -import type { SightingWidgetType } from "../types/types"; +import type { SightingType, SightingWidgetType } from "../types/types"; -async function fetchSighting(url: string, ref: number): Promise { +async function fetchSighting( + url: string, + ref: number +): Promise { const res = await fetch(`${url}${ref}`); if (!res.ok) throw new Error(String(res.status)); return await res.json(); @@ -11,6 +14,9 @@ export function useSightingFeed(url: string) { const [sightings, setSightings] = useState([]); const [selectedRef, setSelectedRef] = useState(null); const [mostRecent, setMostRecent] = useState(null); + const [selectedSighting, setSelectedSighting] = useState( + null + ); const currentRef = useRef(-1); const pollingTimeout = useRef | null>(null); @@ -35,7 +41,7 @@ export function useSightingFeed(url: string) { currentRef.current = data.ref; lastValidTimestamp.current = now; - setSightings(prev => { + setSightings((prev) => { const updated = [data, ...prev].slice(0, 7); return updated; }); @@ -58,13 +64,15 @@ export function useSightingFeed(url: string) { }; }, [url]); - const selected = sightings.find(s => s?.ref === selectedRef) ?? mostRecent; + // const selected = sightings.find(s => s?.ref === selectedRef) ?? mostRecent; return { sightings, selectedRef, setSelectedRef, mostRecent, - effectiveSelected: selected, + setSelectedSighting, + selectedSighting, + // effectiveSelected: selected, }; } diff --git a/src/pages/Dashboard.tsx b/src/pages/Dashboard.tsx index 8b1cad9..029ac9c 100644 --- a/src/pages/Dashboard.tsx +++ b/src/pages/Dashboard.tsx @@ -1,24 +1,31 @@ import FrontCameraOverviewCard from "../components/FrontCameraOverview/FrontCameraOverviewCard"; import RearCameraOverviewCard from "../components/RearCameraOverview/RearCameraOverviewCard"; - import SightingHistoryWidget from "../components/SightingsWidget/SightingWidget"; +import ModalComponent from "../components/UI/ModalComponent"; import { SightingFeedProvider } from "../context/providers/SightingFeedProvider"; const Dashboard = () => { return ( -
+
- + + +
Hello
+
diff --git a/src/pages/FrontCamera.tsx b/src/pages/FrontCamera.tsx index 054c335..712d825 100644 --- a/src/pages/FrontCamera.tsx +++ b/src/pages/FrontCamera.tsx @@ -21,7 +21,7 @@ const FrontCamera = () => { side="CameraFront" settingsPage={true} /> - +
); diff --git a/src/pages/RearCamera.tsx b/src/pages/RearCamera.tsx index 501f595..a450a16 100644 --- a/src/pages/RearCamera.tsx +++ b/src/pages/RearCamera.tsx @@ -16,7 +16,7 @@ const RearCamera = () => { className="mx-auto grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-2 gap-4 px-2 sm:px-4 lg:px-0 w-full order-first" {...handlers} > - +