started redesign on dashbaord. changed vite basename to /Mobile, currently folkstone address

This commit is contained in:
2025-09-26 11:42:12 +01:00
parent 80b407943f
commit aee898abd5
14 changed files with 99 additions and 94 deletions

2
.env
View File

@@ -1,5 +1,5 @@
VITE_BASEURL=http://192.168.75.11/ VITE_BASEURL=http://192.168.75.11/
VITE_CAM_BASE=http://100.72.72.70 VITE_CAM_BASE=http://100.72.72.70:8080
VITE_FOLKESTONE_BASE=http://100.116.253.81 VITE_FOLKESTONE_BASE=http://100.116.253.81
VITE_TESTURL=http://100.82.205.44/SightingListRear/sightingSummary?mostRecentRef=-1 VITE_TESTURL=http://100.82.205.44/SightingListRear/sightingSummary?mostRecentRef=-1
VITE_OUTSIDE_BASEURL=http://100.82.205.44 VITE_OUTSIDE_BASEURL=http://100.82.205.44

View File

@@ -15,7 +15,7 @@ const CameraSettings = ({ title, side }: { title: string; side: string }) => {
console.log(updateCameraConfigError); console.log(updateCameraConfigError);
return ( return (
<Card> <Card className="px-4 py-4">
{isPending && <>Loading camera config</>} {isPending && <>Loading camera config</>}
{isError && <>Error fetching camera config</>} {isError && <>Error fetching camera config</>}
<div className="relative flex flex-col space-y-3 h-full"> <div className="relative flex flex-col space-y-3 h-full">

View File

@@ -1,16 +1,11 @@
import clsx from "clsx"; import clsx from "clsx";
import Card from "../UI/Card"; import Card from "../UI/Card";
import CardHeader from "../UI/CardHeader";
import { faCamera } from "@fortawesome/free-regular-svg-icons";
import { useSwipeable } from "react-swipeable"; import { useSwipeable } from "react-swipeable";
import { useNavigate } from "react-router"; import { useNavigate } from "react-router";
import { useOverviewVideo } from "../../hooks/useOverviewVideo"; import { useOverviewVideo } from "../../hooks/useOverviewVideo";
import SightingOverview from "../SightingOverview/SightingOverview"; import SightingOverview from "../SightingOverview/SightingOverview";
import { useSightingFeedContext } from "../../context/SightingFeedContext";
type CardProps = React.HTMLAttributes<HTMLDivElement>; const FrontCameraOverviewCard = () => {
const FrontCameraOverviewCard = ({ className }: CardProps) => {
useOverviewVideo(); useOverviewVideo();
const navigate = useNavigate(); const navigate = useNavigate();
const handlers = useSwipeable({ const handlers = useSwipeable({
@@ -18,23 +13,15 @@ const FrontCameraOverviewCard = ({ className }: CardProps) => {
trackMouse: true, trackMouse: true,
}); });
const { mostRecent } = useSightingFeedContext();
return ( return (
<Card <Card
className={clsx( className={clsx(
"relative min-h-[40vh] md:min-h-[60vh] h-auto", "relative min-h-[40vh] md:min-h-[60vh] max-h-[80vh] lg:w-[70%] overflow-y-hidden"
className
)} )}
> >
<div className="flex flex-col space-y-3 h-full" {...handlers}> <div className="lg:overflow-hidden" {...handlers}>
<CardHeader
title="Front Overview"
icon={faCamera}
sighting={mostRecent}
/>
<SightingOverview /> <SightingOverview />
{/* <SnapshotContainer side="TargetDetectionFront" /> */}
</div> </div>
</Card> </Card>
); );

View File

@@ -14,7 +14,11 @@ const OverviewVideoContainer = ({
settingsPage?: boolean; settingsPage?: boolean;
}) => { }) => {
return ( return (
<Card className={clsx("min-h-[40vh] md:min-h-[60vh] h-auto")}> <Card
className={clsx(
"min-h-[40vh] md:min-h-[60vh] max-h-[80vh] h-auto overflow-y-hidden"
)}
>
<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={faCamera} /> <CardHeader title={title} icon={faCamera} />
<SnapshotContainer side={side} settingsPage={settingsPage} /> <SnapshotContainer side={side} settingsPage={settingsPage} />

View File

@@ -4,21 +4,53 @@ import { formatNumberPlate } from "../../utils/utils";
type NumberPlateProps = { type NumberPlateProps = {
vrm?: string | undefined; vrm?: string | undefined;
motion?: boolean; motion?: boolean;
size?: "xs" | "sm" | "md" | "lg";
}; };
const NumberPlate = ({ motion, vrm }: NumberPlateProps) => { const NumberPlate = ({ motion, vrm, size }: NumberPlateProps) => {
let options = {
plateWidth: "w-[14rem]",
textSize: "text-2xl",
borderWidth: "border-6",
};
switch (size) {
case "xs":
options = {
plateWidth: "w-[8rem]",
textSize: "text-md",
borderWidth: "border-4",
};
break;
case "sm":
options = {
plateWidth: "w-[10rem]",
textSize: "text-lg",
borderWidth: "border-4",
};
break;
case "lg":
options = {
plateWidth: "w-[16rem]",
textSize: "text-3xl",
borderWidth: "border-6",
};
break;
}
return ( return (
<div <div
className={`relative w-[16rem] border-6 border-black rounded-xl text-nowrap className={`relative ${options.plateWidth} ${
options.borderWidth
} border-black rounded-xl text-nowrap
text-black px-6 py-2 text-black px-6 py-2
${motion ? "bg-yellow-400" : "bg-white"} ${motion ? "bg-yellow-400" : "bg-white"}`}
`}
> >
<div> <div>
<div className="absolute inset-y-0 left-0 bg-blue-600 w-8 flex flex-col"> <div className="absolute inset-y-0 left-0 bg-blue-600 w-8 flex flex-col">
<GB /> <GB />
</div> </div>
<p className="pl-4 font-extrabold text-3xl text-right"> <p className={`pl-4 font-extrabold ${options.textSize} text-right`}>
{vrm && formatNumberPlate(vrm)} {vrm && formatNumberPlate(vrm)}
</p> </p>
</div> </div>

View File

@@ -1,10 +1,10 @@
import { useCallback, useRef, useState } from "react"; import { useCallback, useRef, useState } from "react";
import { BLANK_IMG } from "../../utils/utils"; import { BLANK_IMG } from "../../utils/utils";
import SightingWidgetDetails from "../SightingsWidget/SightingWidgetDetails";
import { useOverviewOverlay } from "../../hooks/useOverviewOverlay"; 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 NumberPlate from "../PlateStack/NumberPlate";
const SightingOverview = () => { const SightingOverview = () => {
const [overlayMode, setOverlayMode] = useState<0 | 1 | 2>(0); const [overlayMode, setOverlayMode] = useState<0 | 1 | 2>(0);
@@ -27,11 +27,15 @@ const SightingOverview = () => {
if (isError) return <p>An error occurred, Cannot display footage</p>; if (isError) return <p>An error occurred, Cannot display footage</p>;
return ( return (
<div className="flex flex-col"> <div className="flex flex-col md:flex-row">
<div className="grid gap-3"> {mostRecent && (
<div className="z-30 px-1 pt-2">
<NumberPlate vrm={mostRecent?.vrm} size="sm" />
</div>
)}
<NavigationArrow side={side} /> <NavigationArrow side={side} />
<div className="inline-block w-full mx-auto"> <div className="w-full">
<div className="relative aspect-[1280/800]">
<img <img
ref={imgRef} ref={imgRef}
onLoad={() => { onLoad={() => {
@@ -40,7 +44,7 @@ const SightingOverview = () => {
}} }}
src={mostRecent?.overviewUrl || BLANK_IMG} src={mostRecent?.overviewUrl || BLANK_IMG}
alt="overview" alt="overview"
className="absolute inset-0 w-full h-full object-contain cursor-pointer z-10 " className="absolute inset-0 object-contain cursor-pointer z-10 min-h-[100%] rounded-lg"
onClick={onOverviewClick} onClick={onOverviewClick}
style={{ style={{
display: mostRecent?.overviewUrl ? "block" : "none", display: mostRecent?.overviewUrl ? "block" : "none",
@@ -48,22 +52,10 @@ const SightingOverview = () => {
/> />
<canvas <canvas
ref={canvasRef} ref={canvasRef}
className="absolute inset-0 w-full h-full object-contain z-20 pointer-events-none " className="absolute inset-0 object-contain z-20 pointer-events-none "
/> />
</div> </div>
</div> </div>
<div className="text-xs opacity-80">
Overlay:{" "}
{overlayMode === 0
? "Off"
: overlayMode === 1
? "Plate box"
: "Track + box"}{" "}
(click image to toggle)
</div>
</div>
<SightingWidgetDetails effectiveSelected={mostRecent} />
</div>
); );
}; };

View File

@@ -32,7 +32,7 @@ type SightingHistoryProps = {
pollMs?: number; pollMs?: number;
autoSelectLatest?: boolean; autoSelectLatest?: boolean;
title: string; title: string;
className: string; className?: string;
}; };
export default function SightingHistoryWidget({ export default function SightingHistoryWidget({
@@ -113,7 +113,12 @@ export default function SightingHistoryWidget({
}; };
return ( return (
<> <>
<Card className={clsx("overflow-y-auto h-100", className)}> <Card
className={clsx(
"overflow-y-auto min-h-[40vh] md:min-h-[60vh] max-h-[80vh] lg:w-[40%]",
className
)}
>
<CardHeader title={title} /> <CardHeader title={title} />
<div className="flex flex-col gap-3 "> <div className="flex flex-col gap-3 ">
{/* Rows */} {/* Rows */}

View File

@@ -10,7 +10,7 @@ const Card = ({ children, className }: CardProps) => {
return ( return (
<div <div
className={clsx( className={clsx(
"bg-[#253445] rounded-lg mt-4 mx-2 px-4 py-4 shadow-2xl overflow-y-auto md:row-span-1", "bg-[#253445] rounded-lg mt-4 mx-2 shadow-2xl overflow-x-hidden md:row-span-1 px-2",
className className
)} )}
> >

View File

@@ -5,7 +5,7 @@ import NumberPlate from "../PlateStack/NumberPlate";
import type { SightingType } from "../../types/types"; import type { SightingType } from "../../types/types";
type CameraOverviewHeaderProps = { type CameraOverviewHeaderProps = {
title: string; title?: string;
icon?: IconProp; icon?: IconProp;
img?: string; img?: string;
sighting?: SightingType | null; sighting?: SightingType | null;

View File

@@ -1,8 +1,8 @@
import { useRef, useCallback, useEffect } from "react"; import { useRef, useCallback, useEffect } from "react";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { CAM_BASE } from "../utils/config"; import { OUTSIDE_CAM_BASE } from "../utils/config";
const apiUrl = CAM_BASE; const apiUrl = OUTSIDE_CAM_BASE;
async function fetchSnapshot(cameraSide: string) { async function fetchSnapshot(cameraSide: string) {
const response = await fetch(`${apiUrl}/${cameraSide}-preview`); const response = await fetch(`${apiUrl}/${cameraSide}-preview`);

View File

@@ -13,7 +13,7 @@ Modal.setAppElement("#root");
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(
<StrictMode> <StrictMode>
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<BrowserRouter basename="/InCarTest"> <BrowserRouter basename="/Mobile">
<App /> <App />
</BrowserRouter> </BrowserRouter>
</QueryClientProvider> </QueryClientProvider>

View File

@@ -1,34 +1,18 @@
import FrontCameraOverviewCard from "../components/FrontCameraOverview/FrontCameraOverviewCard"; import FrontCameraOverviewCard from "../components/FrontCameraOverview/FrontCameraOverviewCard";
import RearCameraOverviewCard from "../components/RearCameraOverview/RearCameraOverviewCard";
import SightingHistoryWidget from "../components/SightingsWidget/SightingWidget"; import SightingHistoryWidget from "../components/SightingsWidget/SightingWidget";
import { SightingFeedProvider } from "../context/providers/SightingFeedProvider"; import { SightingFeedProvider } from "../context/providers/SightingFeedProvider";
// import { OUTSIDE_CAM_BASE } from "../utils/config";
import { CAM_BASE } from "../utils/config";
const Dashboard = () => { const Dashboard = () => {
const dev_REAR_URL = `${CAM_BASE}/SightingListRear/sightingSummary?mostRecentRef=`; // const dev_OUTSIDE_URL = `${OUTSIDE_CAM_BASE}/SightingListFront/sightingSummary?mostRecentRef=`;
const dev_FRONT_URL = `${CAM_BASE}/SightingListFront/sightingSummary?mostRecentRef=`; const folkestone_OUTSIDE_URL = `http://100.116.253.81/mergedHistory/sightingSummary?mostRecentRef=`;
console.log(CAM_BASE);
return ( return (
<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 url={folkestone_OUTSIDE_URL} side="Front">
<SightingFeedProvider url={dev_FRONT_URL} side="Front"> <div className="mx-auto flex flex-col lg:flex-row gap-2 px-1 sm:px-2 lg:px-0 w-full min-h-screen">
<FrontCameraOverviewCard className="order-1" /> <FrontCameraOverviewCard />
<SightingHistoryWidget <SightingHistoryWidget title="Sightings" />
className="order-3"
title="Front Camera Sightings"
/>
</SightingFeedProvider>
<SightingFeedProvider url={dev_REAR_URL} side="Rear">
<RearCameraOverviewCard className="order-2" />
<SightingHistoryWidget
className="order-4"
title="Rear Camera Sightings"
/>
</SightingFeedProvider>
</div> </div>
</SightingFeedProvider>
); );
}; };

View File

@@ -1,6 +1,7 @@
const rawCamBase = import.meta.env.VITE_CAM_BASE; const rawCamBase = import.meta.env.VITE_CAM_BASE;
export const CAM_BASE = export const CAM_BASE =
rawCamBase && rawCamBase.trim().length > 0 rawCamBase && rawCamBase.trim().length > 0
? rawCamBase ? rawCamBase
: window.location.origin; : window.location.origin;
export const OUTSIDE_CAM_BASE = import.meta.env.VITE_OUTSIDE_BASEURL;

View File

@@ -6,5 +6,5 @@ import tailwindcss from "@tailwindcss/vite";
export default defineConfig({ export default defineConfig({
plugins: [react(), tailwindcss()], plugins: [react(), tailwindcss()],
server: { host: true }, server: { host: true },
base: "/InCarTest", base: "/Mobile",
}); });