code fixes and adding modal
This commit is contained in:
1
TODO.txt
1
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??
|
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.
|
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?
|
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:
|
FYI:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/MAV-Blue.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>MAV | In Car System</title>
|
<title>MAV | In Car System</title>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
18
public/MAV-Blue.svg
Normal file
18
public/MAV-Blue.svg
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 231.27 52.63">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #20456f;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g id="Layer_2-2" data-name="Layer_2">
|
||||||
|
<g>
|
||||||
|
<g id="Layer_1-2">
|
||||||
|
<path class="cls-1" d="M150.57,0h-40.57c-7.53,0-13.64,6.11-13.64,13.64v38.99h13.64v-13.68h40.57v13.68h13.64V13.64c0-7.53-6.11-13.64-13.64-13.64ZM110,28.55v-12.59c0-1.72,1.39-3.11,3.11-3.11h34.34c1.72,0,3.11,1.39,3.11,3.11v12.59h-40.57,0ZM88.45,13.64v38.99h-13.64V15.96c0-1.72-1.39-3.11-3.11-3.11h-17.5c-1.72,0-3.11,1.39-3.11,3.11v36.67h-13.73V15.96c0-1.72-1.39-3.11-3.11-3.11h-17.49c-1.72,0-3.11,1.39-3.11,3.11v36.67H0V13.64C0,6.11,6.11,0,13.64,0h23.55c2.72,0,5.18,1.05,7.03,2.76,1.85-1.71,4.32-2.76,7.03-2.76h23.55c7.53,0,13.64,6.11,13.64,13.64h.01ZM193.88,52.63c-1.19,0-2.28-.68-2.8-1.75L166.25,0h13.16c1.19,0,2.28.68,2.8,1.75,0,0,12.25,25.11,16.55,33.92,4.3-8.81,16.55-33.92,16.55-33.92.53-1.07,1.61-1.75,2.8-1.75h13.16l-24.83,50.88c-.52,1.07-1.61,1.75-2.8,1.75h-9.78.02Z"/>
|
||||||
|
</g>
|
||||||
|
<path class="cls-1" d="M222.79,48.39c0-2.36,1.9-4.24,4.24-4.24s4.24,1.88,4.24,4.24-1.88,4.24-4.24,4.24-4.24-1.9-4.24-4.24ZM223.45,48.39c0,1.96,1.6,3.58,3.58,3.58s3.56-1.62,3.56-3.58-1.58-3.56-3.56-3.56-3.58,1.56-3.58,3.56ZM228.17,50.83l-1.26-1.92h-.8v1.92h-.72v-4.86h1.98c.9,0,1.62.58,1.62,1.48,0,1.08-.96,1.44-1.24,1.44l1.3,1.94h-.88ZM226.11,46.57v1.72h1.26c.5,0,.88-.34.88-.84,0-.54-.38-.88-.88-.88h-1.26Z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -5,13 +5,13 @@ import type {
|
|||||||
} from "../../types/types";
|
} from "../../types/types";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
const CameraSettingFields = () => {
|
const CameraSettingFields = ({ initialData, updateCameraConfig }) => {
|
||||||
const initialValues: CameraSettingValues = {
|
const initialValues: CameraSettingValues = {
|
||||||
friendlyName: "",
|
friendlyName: initialData?.propLEDDriverControlURI?.value,
|
||||||
cameraAddress: "",
|
cameraAddress: "",
|
||||||
userName: "",
|
userName: "",
|
||||||
password: "",
|
password: "",
|
||||||
setupCamera: 1,
|
id: initialData?.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
const validateValues = (values: CameraSettingValues) => {
|
const validateValues = (values: CameraSettingValues) => {
|
||||||
@@ -29,7 +29,7 @@ const CameraSettingFields = () => {
|
|||||||
|
|
||||||
const handleSubmit = (values: CameraSettingValues) => {
|
const handleSubmit = (values: CameraSettingValues) => {
|
||||||
// post values to endpoint
|
// post values to endpoint
|
||||||
console.log(values);
|
updateCameraConfig(values);
|
||||||
toast("Settings Saved");
|
toast("Settings Saved");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,28 @@
|
|||||||
|
import { useFetchCameraConfig } from "../../hooks/useCameraConfig";
|
||||||
import Card from "../UI/Card";
|
import Card from "../UI/Card";
|
||||||
import CardHeader from "../UI/CardHeader";
|
import CardHeader from "../UI/CardHeader";
|
||||||
import CameraSettingFields from "./CameraSettingFields";
|
import CameraSettingFields from "./CameraSettingFields";
|
||||||
import { faWrench } from "@fortawesome/free-solid-svg-icons";
|
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 (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
|
{isError && <>Cannot Fetch camera config</>}
|
||||||
|
|
||||||
|
{isPending ? (
|
||||||
|
<>Loading</>
|
||||||
|
) : (
|
||||||
<div className="relative flex flex-col space-y-3 h-full">
|
<div className="relative flex flex-col space-y-3 h-full">
|
||||||
<CardHeader title={title} icon={faWrench} />
|
<CardHeader title={title} icon={faWrench} />
|
||||||
<CameraSettingFields />
|
<CameraSettingFields
|
||||||
|
initialData={data}
|
||||||
|
updateCameraConfig={updateCameraConfig}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
export async function handleSystemSave(deviceName: string, sntpServer: string, sntpInterval: number, timeZone: string) {
|
export async function handleSystemSave(
|
||||||
const payload = { // Build JSON
|
deviceName: string,
|
||||||
|
sntpServer: string,
|
||||||
|
sntpInterval: number,
|
||||||
|
timeZone: string
|
||||||
|
) {
|
||||||
|
const payload = {
|
||||||
|
// Build JSON
|
||||||
id: "GLOBAL--Device",
|
id: "GLOBAL--Device",
|
||||||
fields: [
|
fields: [
|
||||||
{ property: "propDeviceName", value: deviceName },
|
{ property: "propDeviceName", value: deviceName },
|
||||||
{ property: "propSNTPServer", value: sntpServer },
|
{ property: "propSNTPServer", value: sntpServer },
|
||||||
{ property: "propSNTPIntervalMinutes", value: Number(sntpInterval) },
|
{ property: "propSNTPIntervalMinutes", value: Number(sntpInterval) },
|
||||||
{ property: "propLocalTimeZone", value: timeZone }
|
{ property: "propLocalTimeZone", value: timeZone },
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -14,14 +20,18 @@ export async function handleSystemSave(deviceName: string, sntpServer: string, s
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Accept": "application/json"
|
Accept: "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify(payload)
|
body: JSON.stringify(payload),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const text = await response.text().catch(() => "");
|
const text = await response.text().catch(() => "");
|
||||||
throw new Error(`HTTP ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`);
|
throw new Error(
|
||||||
|
`HTTP ${response.status} ${response.statusText}${
|
||||||
|
text ? ` - ${text}` : ""
|
||||||
|
}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
alert("System Settings Saved Successfully!");
|
alert("System Settings Saved Successfully!");
|
||||||
@@ -39,13 +49,17 @@ export async function handleSystemRecall() {
|
|||||||
try {
|
try {
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: { "Accept": "application/json" },
|
headers: { Accept: "application/json" },
|
||||||
signal: controller.signal
|
signal: controller.signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const text = await response.text().catch(() => "");
|
const text = await response.text().catch(() => "");
|
||||||
throw new Error(`HTTP ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`);
|
throw new Error(
|
||||||
|
`HTTP ${response.status} ${response.statusText}${
|
||||||
|
text ? ` - ${text}` : ""
|
||||||
|
}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
@@ -54,7 +68,7 @@ export async function handleSystemRecall() {
|
|||||||
const sntpServer = data?.propSNTPServer?.value ?? null;
|
const sntpServer = data?.propSNTPServer?.value ?? null;
|
||||||
const timeZone = data?.propLocalTimeZone?.value ?? null;
|
const timeZone = data?.propLocalTimeZone?.value ?? null;
|
||||||
|
|
||||||
let sntpIntervalRaw = data?.propSNTPIntervalMinutes?.value;
|
const sntpIntervalRaw = data?.propSNTPIntervalMinutes?.value;
|
||||||
let sntpInterval =
|
let sntpInterval =
|
||||||
typeof sntpIntervalRaw === "number"
|
typeof sntpIntervalRaw === "number"
|
||||||
? sntpIntervalRaw
|
? sntpIntervalRaw
|
||||||
|
|||||||
32
src/components/SightingModal/SightingModal.tsx
Normal file
32
src/components/SightingModal/SightingModal.tsx
Normal file
@@ -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 (
|
||||||
|
<ModalComponent isModalOpen={isSightingModalOpen} close={handleClose}>
|
||||||
|
<button onClick={handleClose}>close</button>
|
||||||
|
<div>
|
||||||
|
<h2>{sighting?.vrm}</h2>
|
||||||
|
<NumberPlate vrm={sighting?.vrm} motion={motionAway} />
|
||||||
|
<img
|
||||||
|
src={sighting?.plateUrlInfrared}
|
||||||
|
height={48}
|
||||||
|
alt="infrared patch"
|
||||||
|
className={"opacity-60"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ModalComponent>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SightingModal;
|
||||||
@@ -5,15 +5,15 @@ import { useOverviewOverlay } from "../../hooks/useOverviewOverlay";
|
|||||||
import { useSightingFeedContext } from "../../context/SightingFeedContext";
|
import { useSightingFeedContext } from "../../context/SightingFeedContext";
|
||||||
import { useHiDPICanvas } from "../../hooks/useHiDPICanvas";
|
import { useHiDPICanvas } from "../../hooks/useHiDPICanvas";
|
||||||
import NavigationArrow from "../UI/NavigationArrow";
|
import NavigationArrow from "../UI/NavigationArrow";
|
||||||
import { useSwipeable } from "react-swipeable";
|
// import { useSwipeable } from "react-swipeable";
|
||||||
import { useNavigate } from "react-router";
|
// import { useNavigate } from "react-router";
|
||||||
|
|
||||||
const SightingOverview = () => {
|
const SightingOverview = () => {
|
||||||
const navigate = useNavigate();
|
// const navigate = useNavigate();
|
||||||
const handlers = useSwipeable({
|
// const handlers = useSwipeable({
|
||||||
onSwipedRight: () => navigate("/front-camera-settings"),
|
// onSwipedRight: () => navigate("/front-camera-settings"),
|
||||||
trackMouse: true,
|
// trackMouse: true,
|
||||||
});
|
// });
|
||||||
const [overlayMode, setOverlayMode] = useState<0 | 1 | 2>(0);
|
const [overlayMode, setOverlayMode] = useState<0 | 1 | 2>(0);
|
||||||
|
|
||||||
const imgRef = useRef<HTMLImageElement | null>(null);
|
const imgRef = useRef<HTMLImageElement | null>(null);
|
||||||
@@ -23,18 +23,19 @@ const SightingOverview = () => {
|
|||||||
setOverlayMode((m) => ((m + 1) % 3) as 0 | 1 | 2);
|
setOverlayMode((m) => ((m + 1) % 3) as 0 | 1 | 2);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { effectiveSelected, side, mostRecent, noSighting, isPending } =
|
const { effectiveSelected, side, mostRecent } = useSightingFeedContext();
|
||||||
useSightingFeedContext();
|
|
||||||
|
|
||||||
useOverviewOverlay(mostRecent, overlayMode, imgRef, canvasRef);
|
useOverviewOverlay(mostRecent, overlayMode, imgRef, canvasRef);
|
||||||
|
|
||||||
const { sync } = useHiDPICanvas(imgRef, canvasRef);
|
const { sync } = useHiDPICanvas(imgRef, canvasRef);
|
||||||
|
|
||||||
if (noSighting || isPending) return <p>loading</p>;
|
// if (noSighting || isPending) return <p>loading</p>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mt-2 grid gap-3">
|
<div className="flex flex-col">
|
||||||
<div className="inline-block w-[90%] mx-auto" {...handlers}>
|
<div className="grid gap-3">
|
||||||
<NavigationArrow side={side} />
|
<NavigationArrow side={side} />
|
||||||
|
<div className="inline-block w-full mx-auto">
|
||||||
<div className="relative aspect-[1280/800]">
|
<div className="relative aspect-[1280/800]">
|
||||||
<img
|
<img
|
||||||
ref={imgRef}
|
ref={imgRef}
|
||||||
@@ -56,9 +57,6 @@ const SightingOverview = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SightingWidgetDetails effectiveSelected={effectiveSelected} />
|
|
||||||
|
|
||||||
<div className="text-xs opacity-80">
|
<div className="text-xs opacity-80">
|
||||||
Overlay:{" "}
|
Overlay:{" "}
|
||||||
{overlayMode === 0
|
{overlayMode === 0
|
||||||
@@ -69,6 +67,8 @@ const SightingOverview = () => {
|
|||||||
(click image to toggle)
|
(click image to toggle)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<SightingWidgetDetails effectiveSelected={effectiveSelected} />
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
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 { BLANK_IMG, capitalize, formatAge } from "../../utils/utils";
|
||||||
import NumberPlate from "../PlateStack/NumberPlate";
|
import NumberPlate from "../PlateStack/NumberPlate";
|
||||||
import Card from "../UI/Card";
|
import Card from "../UI/Card";
|
||||||
import CardHeader from "../UI/CardHeader";
|
import CardHeader from "../UI/CardHeader";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { useSightingFeedContext } from "../../context/SightingFeedContext";
|
import { useSightingFeedContext } from "../../context/SightingFeedContext";
|
||||||
|
import SightingModal from "../SightingModal/SightingModal";
|
||||||
|
|
||||||
function useNow(tickMs = 1000) {
|
function useNow(tickMs = 1000) {
|
||||||
const [, setNow] = useState(() => Date.now());
|
const [, setNow] = useState(() => Date.now());
|
||||||
@@ -29,21 +30,32 @@ export default function SightingHistoryWidget({
|
|||||||
className,
|
className,
|
||||||
}: SightingHistoryWidgetProps) {
|
}: SightingHistoryWidgetProps) {
|
||||||
useNow(1000);
|
useNow(1000);
|
||||||
const { sightings, selectedRef, setSelectedRef } = useSightingFeedContext();
|
|
||||||
|
const {
|
||||||
|
sightings,
|
||||||
|
setSelectedSighting,
|
||||||
|
setSightingModalOpen,
|
||||||
|
isSightingModalOpen,
|
||||||
|
selectedSighting,
|
||||||
|
} = useSightingFeedContext();
|
||||||
|
|
||||||
const onRowClick = useCallback(
|
const onRowClick = useCallback(
|
||||||
(ref: number) => {
|
(sighting: SightingType) => {
|
||||||
setSelectedRef(ref);
|
if (!sighting) return;
|
||||||
|
setSightingModalOpen(!isSightingModalOpen);
|
||||||
|
setSelectedSighting(sighting);
|
||||||
},
|
},
|
||||||
[setSelectedRef]
|
[isSightingModalOpen, setSelectedSighting, setSightingModalOpen]
|
||||||
);
|
);
|
||||||
|
|
||||||
const rows = useMemo(
|
const rows = useMemo(
|
||||||
() => sightings?.filter(Boolean) as SightingWidgetType[],
|
() => sightings?.filter(Boolean) as SightingWidgetType[],
|
||||||
[sightings]
|
[sightings]
|
||||||
);
|
);
|
||||||
|
const handleClose = () => {
|
||||||
|
setSightingModalOpen(false);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<Card className={clsx("overflow-y-auto h-100", className)}>
|
<Card className={clsx("overflow-y-auto h-100", className)}>
|
||||||
<CardHeader title="Front Camera Sightings" />
|
<CardHeader title="Front Camera Sightings" />
|
||||||
<div className="flex flex-col gap-3 ">
|
<div className="flex flex-col gap-3 ">
|
||||||
@@ -51,17 +63,15 @@ export default function SightingHistoryWidget({
|
|||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
{rows?.map((obj, idx) => {
|
{rows?.map((obj, idx) => {
|
||||||
const isNPEDHit = obj?.metadata?.npedJSON?.status_code === 201;
|
const isNPEDHit = obj?.metadata?.npedJSON?.status_code === 201;
|
||||||
const isSelected = obj?.ref === selectedRef;
|
|
||||||
const motionAway = (obj?.motion ?? "").toUpperCase() === "AWAY";
|
const motionAway = (obj?.motion ?? "").toUpperCase() === "AWAY";
|
||||||
const primaryIsColour = obj?.srcCam === 1;
|
const primaryIsColour = obj?.srcCam === 1;
|
||||||
const secondaryMissing = (obj?.vrmSecondary ?? "") === "";
|
const secondaryMissing = (obj?.vrmSecondary ?? "") === "";
|
||||||
|
console.log(obj);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={idx}
|
key={idx}
|
||||||
className={`border border-neutral-700 rounded-md mb-2 p-2 cursor-pointer ${
|
className={`border border-neutral-700 rounded-md mb-2 p-2 cursor-pointer`}
|
||||||
isSelected ? "ring-2 ring-blue-400" : ""
|
onClick={() => onRowClick(obj)}
|
||||||
}`}
|
|
||||||
onClick={() => onRowClick(obj.ref)}
|
|
||||||
>
|
>
|
||||||
{/* Info bar */}
|
{/* Info bar */}
|
||||||
<div className="flex items-center gap-3 text-xs bg-neutral-900 px-2 py-1 rounded">
|
<div className="flex items-center gap-3 text-xs bg-neutral-900 px-2 py-1 rounded">
|
||||||
@@ -121,5 +131,11 @@ export default function SightingHistoryWidget({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
<SightingModal
|
||||||
|
isSightingModalOpen={isSightingModalOpen}
|
||||||
|
handleClose={handleClose}
|
||||||
|
sighting={selectedSighting}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { SightingWidgetType } from "../../types/types";
|
import type { SightingWidgetType } from "../../types/types";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
type SightingWidgetDetailsProps = {
|
type SightingWidgetDetailsProps = {
|
||||||
effectiveSelected: SightingWidgetType | null;
|
effectiveSelected: SightingWidgetType | null;
|
||||||
@@ -7,16 +8,19 @@ type SightingWidgetDetailsProps = {
|
|||||||
const SightingWidgetDetails = ({
|
const SightingWidgetDetails = ({
|
||||||
effectiveSelected,
|
effectiveSelected,
|
||||||
}: SightingWidgetDetailsProps) => {
|
}: SightingWidgetDetailsProps) => {
|
||||||
|
const [advancedDetailsEnabled, setAdvancedDetailsEnabled] = useState(false);
|
||||||
|
|
||||||
|
const handleDetailsClick = () =>
|
||||||
|
setAdvancedDetailsEnabled(!advancedDetailsEnabled);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 text-sm">
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 text-sm">
|
||||||
<div>
|
<div>
|
||||||
VRM:{" "}
|
VRM:{" "}
|
||||||
<span className="opacity-90">{effectiveSelected?.vrm ?? "—"}</span>
|
<span className="opacity-90">{effectiveSelected?.vrm ?? "—"}</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
Timestamp:{" "}
|
|
||||||
<span className="opacity-90">{effectiveSelected?.timeStamp ?? "—"}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
Make:{" "}
|
Make:{" "}
|
||||||
<span className="opacity-90">{effectiveSelected?.make ?? "—"}</span>
|
<span className="opacity-90">{effectiveSelected?.make ?? "—"}</span>
|
||||||
@@ -25,9 +29,24 @@ const SightingWidgetDetails = ({
|
|||||||
Model:{" "}
|
Model:{" "}
|
||||||
<span className="opacity-90">{effectiveSelected?.model ?? "—"}</span>
|
<span className="opacity-90">{effectiveSelected?.model ?? "—"}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
Colour:{" "}
|
||||||
|
<span className="opacity-90">{effectiveSelected?.color ?? "—"}</span>
|
||||||
|
</div>
|
||||||
|
<div className="col-span-4">
|
||||||
|
Timestamp:{" "}
|
||||||
|
<span className="opacity-90">
|
||||||
|
{effectiveSelected?.timeStamp ?? "—"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{advancedDetailsEnabled && (
|
||||||
|
<>
|
||||||
|
{" "}
|
||||||
<div>
|
<div>
|
||||||
Country:{" "}
|
Country:{" "}
|
||||||
<span className="opacity-90">{effectiveSelected?.countryCode ?? "—"}</span>
|
<span className="opacity-90">
|
||||||
|
{effectiveSelected?.countryCode ?? "—"}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Seen:{" "}
|
Seen:{" "}
|
||||||
@@ -35,13 +54,11 @@ const SightingWidgetDetails = ({
|
|||||||
{effectiveSelected?.seenCount ?? "—"}
|
{effectiveSelected?.seenCount ?? "—"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
Colour:{" "}
|
|
||||||
<span className="opacity-90">{effectiveSelected?.color ?? "—"}</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
Category:{" "}
|
Category:{" "}
|
||||||
<span className="opacity-90">{effectiveSelected?.category ?? "—"}</span>
|
<span className="opacity-90">
|
||||||
|
{effectiveSelected?.category ?? "—"}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Char Ht:{" "}
|
Char Ht:{" "}
|
||||||
@@ -61,18 +78,18 @@ const SightingWidgetDetails = ({
|
|||||||
{effectiveSelected?.overviewSize ?? "—"}
|
{effectiveSelected?.overviewSize ?? "—"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{effectiveSelected?.detailsUrl ? (
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="col-span-half">
|
<div className="col-span-half">
|
||||||
<a
|
<p
|
||||||
href={effectiveSelected.detailsUrl}
|
onClick={handleDetailsClick}
|
||||||
target="_blank"
|
className="underline text-blue-300 hover:cursor-pointer"
|
||||||
className="underline text-blue-300"
|
|
||||||
>
|
>
|
||||||
Sighting Details
|
Sighting Details
|
||||||
</a>
|
</p>
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,17 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|||||||
import { faGear, faListCheck } from "@fortawesome/free-solid-svg-icons";
|
import { faGear, faListCheck } from "@fortawesome/free-solid-svg-icons";
|
||||||
import type { VersionFieldType } from "../../types/types";
|
import type { VersionFieldType } from "../../types/types";
|
||||||
|
|
||||||
async function fetchVersions(signal?: AbortSignal): Promise<VersionFieldType> {
|
async function fetchVersions(
|
||||||
|
signal?: AbortSignal
|
||||||
|
): Promise<VersionFieldType | undefined> {
|
||||||
|
try {
|
||||||
const res = await fetch("http://192.168.75.11/api/versions", { signal });
|
const res = await fetch("http://192.168.75.11/api/versions", { signal });
|
||||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||||
return res.json();
|
return res.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const pad = (n: number) => String(n).padStart(2, "0");
|
const pad = (n: number) => String(n).padStart(2, "0");
|
||||||
@@ -33,10 +40,14 @@ export default function Header() {
|
|||||||
const ac = new AbortController();
|
const ac = new AbortController();
|
||||||
fetchVersions(ac.signal)
|
fetchVersions(ac.signal)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
const serverMs = normalizeToMs(data.timeStamp);
|
if (!data) throw new Error("No data");
|
||||||
|
const serverMs = normalizeToMs(data?.timeStamp);
|
||||||
setOffsetMs(serverMs - Date.now());
|
setOffsetMs(serverMs - Date.now());
|
||||||
})
|
})
|
||||||
return () => ac.abort();
|
.catch((err) => {
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
return () => ac.abort("failed");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@@ -69,7 +80,7 @@ export default function Header() {
|
|||||||
<h2>Local: {localStr}</h2>
|
<h2>Local: {localStr}</h2>
|
||||||
<h2>UTC: {utcStr}</h2>
|
<h2>UTC: {utcStr}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="flex flex-row space-x-2">
|
||||||
<Link to={"/session-settings"}>
|
<Link to={"/session-settings"}>
|
||||||
<FontAwesomeIcon className="text-white" icon={faListCheck} />
|
<FontAwesomeIcon className="text-white" icon={faListCheck} />
|
||||||
</Link>
|
</Link>
|
||||||
@@ -78,5 +89,6 @@ export default function Header() {
|
|||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
26
src/components/UI/ModalComponent.tsx
Normal file
26
src/components/UI/ModalComponent.tsx
Normal file
@@ -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 (
|
||||||
|
<Modal
|
||||||
|
isOpen={isModalOpen}
|
||||||
|
onRequestClose={close}
|
||||||
|
className="bg-[#1e2a38] p-6 rounded-lg shadow-lg max-w-[65%] mx-auto mt-20 w-full h-[75%] z-100"
|
||||||
|
overlayClassName="fixed inset-0 bg-[#1e2a38]/70 flex justify-center items-start z-100"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalComponent;
|
||||||
@@ -29,13 +29,13 @@ const NavigationArrow = ({ side, settingsPage }: NavigationArrowProps) => {
|
|||||||
{side === "CameraFront" ? (
|
{side === "CameraFront" ? (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faArrowRight}
|
icon={faArrowRight}
|
||||||
className="absolute top-[50%] right-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce"
|
className="absolute top-[50%] right-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce z-30"
|
||||||
onClick={() => navigationDest(side)}
|
onClick={() => navigationDest(side)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faArrowLeft}
|
icon={faArrowLeft}
|
||||||
className="absolute top-[50%] left-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce"
|
className="absolute top-[50%] left-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce z-30"
|
||||||
onClick={() => navigationDest(side)}
|
onClick={() => navigationDest(side)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -47,13 +47,13 @@ const NavigationArrow = ({ side, settingsPage }: NavigationArrowProps) => {
|
|||||||
{side === "Front" ? (
|
{side === "Front" ? (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faArrowLeft}
|
icon={faArrowLeft}
|
||||||
className="absolute top-[50%] left-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce"
|
className="absolute top-[50%] left-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce z-30"
|
||||||
onClick={() => navigationDest(side)}
|
onClick={() => navigationDest(side)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faArrowRight}
|
icon={faArrowRight}
|
||||||
className="absolute top-[50%] right-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce"
|
className="absolute top-[50%] right-[2%] backdrop-blur-md hover:cursor-pointer animate-bounce z-30"
|
||||||
onClick={() => navigationDest(side)}
|
onClick={() => navigationDest(side)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import { createContext, useContext } from "react";
|
import { createContext, useContext } from "react";
|
||||||
import type { SightingWidgetType } from "../types/types";
|
import type { SightingType, SightingWidgetType } from "../types/types";
|
||||||
|
|
||||||
type SightingFeedContextType = {
|
type SightingFeedContextType = {
|
||||||
sightings: (SightingWidgetType | null | undefined)[];
|
sightings: (SightingWidgetType | null | undefined)[];
|
||||||
selectedRef: number | null;
|
selectedRef: number | null;
|
||||||
setSelectedRef: (ref: number | null) => void;
|
setSelectedRef: (ref: number | null) => void;
|
||||||
effectiveSelected: SightingWidgetType | null;
|
// effectiveSelected: SightingWidgetType | null;
|
||||||
mostRecent: SightingWidgetType | null;
|
mostRecent: SightingWidgetType | null;
|
||||||
side: string;
|
side: string;
|
||||||
isPending: boolean;
|
selectedSighting: SightingType | null;
|
||||||
noSighting: boolean;
|
setSelectedSighting: (sighting: SightingType | null) => void;
|
||||||
|
setSightingModalOpen: (isSightingModalOpen: boolean) => void;
|
||||||
|
isSightingModalOpen: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SightingFeedContext = createContext<
|
export const SightingFeedContext = createContext<
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { ReactNode } from "react";
|
import { useState, type ReactNode } from "react";
|
||||||
import { useSightingFeed } from "../../hooks/useSightingFeed";
|
import { useSightingFeed } from "../../hooks/useSightingFeed";
|
||||||
import { SightingFeedContext } from "../SightingFeedContext";
|
import { SightingFeedContext } from "../SightingFeedContext";
|
||||||
|
|
||||||
@@ -17,23 +17,25 @@ export const SightingFeedProvider = ({
|
|||||||
sightings,
|
sightings,
|
||||||
selectedRef,
|
selectedRef,
|
||||||
setSelectedRef,
|
setSelectedRef,
|
||||||
effectiveSelected,
|
// effectiveSelected,
|
||||||
|
setSelectedSighting,
|
||||||
|
selectedSighting,
|
||||||
mostRecent,
|
mostRecent,
|
||||||
isPending,
|
|
||||||
noSighting,
|
|
||||||
} = useSightingFeed(url);
|
} = useSightingFeed(url);
|
||||||
|
const [isSightingModalOpen, setSightingModalOpen] = useState(false);
|
||||||
return (
|
return (
|
||||||
<SightingFeedContext.Provider
|
<SightingFeedContext.Provider
|
||||||
value={{
|
value={{
|
||||||
sightings,
|
sightings,
|
||||||
selectedRef,
|
selectedRef,
|
||||||
setSelectedRef,
|
setSelectedRef,
|
||||||
effectiveSelected,
|
setSelectedSighting,
|
||||||
|
selectedSighting,
|
||||||
|
setSightingModalOpen,
|
||||||
|
isSightingModalOpen,
|
||||||
|
// effectiveSelected,
|
||||||
mostRecent,
|
mostRecent,
|
||||||
side,
|
side,
|
||||||
isPending,
|
|
||||||
noSighting,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
52
src/hooks/useCameraConfig.ts
Normal file
52
src/hooks/useCameraConfig.ts
Normal file
@@ -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,
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -7,6 +7,7 @@ export const useGetConfigs = () => {
|
|||||||
async function getConfigs() {
|
async function getConfigs() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${apiUrl}/api/config-ids`);
|
const response = await fetch(`${apiUrl}/api/config-ids`);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
console.log("failed fetching");
|
console.log("failed fetching");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ const apiUrl = import.meta.env.VITE_BASEURL;
|
|||||||
|
|
||||||
async function fetchSnapshot(cameraSide: string) {
|
async function fetchSnapshot(cameraSide: string) {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
// `http://100.116.253.81/Colour-preview`
|
`http://100.116.253.81/Colour-preview`
|
||||||
`${apiUrl}/${cameraSide}-preview`
|
// `${apiUrl}/${cameraSide}-preview`
|
||||||
);
|
);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error("Cannot reach endpoint");
|
throw new Error("Cannot reach endpoint");
|
||||||
}
|
}
|
||||||
|
|
||||||
return await response.blob();
|
return await response.blob();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ async function signIn(loginDetails: NPEDFieldType) {
|
|||||||
{ property: "propClientID", value: clientId },
|
{ property: "propClientID", value: clientId },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
console.log(frontId);
|
||||||
const frontCameraResponse = await fetch(NPEDLoginURLFront, {
|
const frontCameraResponse = await fetch(NPEDLoginURLFront, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify(frontCameraPayload),
|
body: JSON.stringify(frontCameraPayload),
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
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<SightingWidgetType> {
|
async function fetchSighting(
|
||||||
|
url: string,
|
||||||
|
ref: number
|
||||||
|
): Promise<SightingWidgetType> {
|
||||||
const res = await fetch(`${url}${ref}`);
|
const res = await fetch(`${url}${ref}`);
|
||||||
if (!res.ok) throw new Error(String(res.status));
|
if (!res.ok) throw new Error(String(res.status));
|
||||||
return await res.json();
|
return await res.json();
|
||||||
@@ -11,6 +14,9 @@ export function useSightingFeed(url: string) {
|
|||||||
const [sightings, setSightings] = useState<SightingWidgetType[]>([]);
|
const [sightings, setSightings] = useState<SightingWidgetType[]>([]);
|
||||||
const [selectedRef, setSelectedRef] = useState<number | null>(null);
|
const [selectedRef, setSelectedRef] = useState<number | null>(null);
|
||||||
const [mostRecent, setMostRecent] = useState<SightingWidgetType | null>(null);
|
const [mostRecent, setMostRecent] = useState<SightingWidgetType | null>(null);
|
||||||
|
const [selectedSighting, setSelectedSighting] = useState<SightingType | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
const currentRef = useRef<number>(-1);
|
const currentRef = useRef<number>(-1);
|
||||||
const pollingTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
|
const pollingTimeout = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
@@ -35,7 +41,7 @@ export function useSightingFeed(url: string) {
|
|||||||
currentRef.current = data.ref;
|
currentRef.current = data.ref;
|
||||||
lastValidTimestamp.current = now;
|
lastValidTimestamp.current = now;
|
||||||
|
|
||||||
setSightings(prev => {
|
setSightings((prev) => {
|
||||||
const updated = [data, ...prev].slice(0, 7);
|
const updated = [data, ...prev].slice(0, 7);
|
||||||
return updated;
|
return updated;
|
||||||
});
|
});
|
||||||
@@ -58,13 +64,15 @@ export function useSightingFeed(url: string) {
|
|||||||
};
|
};
|
||||||
}, [url]);
|
}, [url]);
|
||||||
|
|
||||||
const selected = sightings.find(s => s?.ref === selectedRef) ?? mostRecent;
|
// const selected = sightings.find(s => s?.ref === selectedRef) ?? mostRecent;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sightings,
|
sightings,
|
||||||
selectedRef,
|
selectedRef,
|
||||||
setSelectedRef,
|
setSelectedRef,
|
||||||
mostRecent,
|
mostRecent,
|
||||||
effectiveSelected: selected,
|
setSelectedSighting,
|
||||||
|
selectedSighting,
|
||||||
|
// effectiveSelected: selected,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import FrontCameraOverviewCard from "../components/FrontCameraOverview/FrontCameraOverviewCard";
|
import FrontCameraOverviewCard from "../components/FrontCameraOverview/FrontCameraOverviewCard";
|
||||||
import RearCameraOverviewCard from "../components/RearCameraOverview/RearCameraOverviewCard";
|
import RearCameraOverviewCard from "../components/RearCameraOverview/RearCameraOverviewCard";
|
||||||
|
|
||||||
import SightingHistoryWidget from "../components/SightingsWidget/SightingWidget";
|
import SightingHistoryWidget from "../components/SightingsWidget/SightingWidget";
|
||||||
|
import ModalComponent from "../components/UI/ModalComponent";
|
||||||
import { SightingFeedProvider } from "../context/providers/SightingFeedProvider";
|
import { SightingFeedProvider } from "../context/providers/SightingFeedProvider";
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
@@ -9,16 +9,23 @@ const Dashboard = () => {
|
|||||||
<div className="mx-auto grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-2 gap-2 px-1 sm:px-2 lg:px-0 w-full">
|
<div className="mx-auto grid grid-cols-1 sm:grid-cols-1 lg:grid-cols-2 gap-2 px-1 sm:px-2 lg:px-0 w-full">
|
||||||
<SightingFeedProvider
|
<SightingFeedProvider
|
||||||
url={
|
url={
|
||||||
"http://192.168.75.11/SightingListFront/sightingSummary?mostRecentRef="
|
"http://100.116.253.81/mergedHistory/sightingSummary?mostRecentRef="
|
||||||
|
// "http://192.168.75.11/SightingListFront/sightingSummary?mostRecentRef="
|
||||||
}
|
}
|
||||||
side="Front"
|
side="Front"
|
||||||
>
|
>
|
||||||
<FrontCameraOverviewCard className="order-1" />
|
<FrontCameraOverviewCard className="order-1" />
|
||||||
<SightingHistoryWidget className="order-3" />
|
<SightingHistoryWidget className="order-5" />
|
||||||
|
<ModalComponent>
|
||||||
|
<div className="text-black">Hello</div>
|
||||||
|
</ModalComponent>
|
||||||
</SightingFeedProvider>
|
</SightingFeedProvider>
|
||||||
|
|
||||||
<SightingFeedProvider
|
<SightingFeedProvider
|
||||||
url="http://192.168.75.11/SightingListRear/sightingSummary?mostRecentRef="
|
url={
|
||||||
|
"http://100.116.253.81/mergedHistory/sightingSummary?mostRecentRef="
|
||||||
|
// "http://192.168.75.11/SightingListRear/sightingSummary?mostRecentRef="
|
||||||
|
}
|
||||||
side="Rear"
|
side="Rear"
|
||||||
>
|
>
|
||||||
<RearCameraOverviewCard className="order-2" />
|
<RearCameraOverviewCard className="order-2" />
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const FrontCamera = () => {
|
|||||||
side="CameraFront"
|
side="CameraFront"
|
||||||
settingsPage={true}
|
settingsPage={true}
|
||||||
/>
|
/>
|
||||||
<CameraSettings title="Front Camera Settings" />
|
<CameraSettings title="Front Camera Settings" side="CameraFront" />
|
||||||
<Toaster />
|
<Toaster />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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"
|
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}
|
{...handlers}
|
||||||
>
|
>
|
||||||
<CameraSettings title="Rear Camera Settings" />
|
<CameraSettings title="Rear Camera Settings" side={"CameraRear"} />
|
||||||
<OverviewVideoContainer
|
<OverviewVideoContainer
|
||||||
title={"Rear Camera"}
|
title={"Rear Camera"}
|
||||||
side={"CameraRear"}
|
side={"CameraRear"}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export type CameraSettingValues = {
|
|||||||
cameraAddress: string;
|
cameraAddress: string;
|
||||||
userName: string;
|
userName: string;
|
||||||
password: string;
|
password: string;
|
||||||
setupCamera: number;
|
id: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CameraSettingErrorValues = Partial<
|
export type CameraSettingErrorValues = Partial<
|
||||||
@@ -113,7 +113,6 @@ export type VersionFieldType = {
|
|||||||
"Model No.": string;
|
"Model No.": string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type Metadata = {
|
export type Metadata = {
|
||||||
npedJSON: NpedJSON;
|
npedJSON: NpedJSON;
|
||||||
"hotlist-matches": HotlistMatches;
|
"hotlist-matches": HotlistMatches;
|
||||||
|
|||||||
Reference in New Issue
Block a user