initial commit

This commit is contained in:
2025-08-13 14:23:48 +01:00
commit 92e3617335
44 changed files with 2936 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
import { faCamera } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import clsx from "clsx";
type CameraOverviewHeaderProps = {
title: string;
};
const CameraOverviewHeader = ({ title }: CameraOverviewHeaderProps) => {
return (
<div
className={clsx(
"w-full border-b border-gray-600 flex flex-row items-center space-x-2 md:mb-6"
)}
>
<FontAwesomeIcon icon={faCamera} className="size-4" />
<h2 className="text-xl">{title}</h2>
</div>
);
};
export default CameraOverviewHeader;

View File

@@ -0,0 +1,32 @@
import { faArrowLeft, faArrowRight } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useGetOverviewSnapshot } from "../../hooks/useGetOverviewSnapshot";
import { useNavigate } from "react-router";
type SnapshotContainerProps = {
side: string;
};
export const SnapshotContainer = ({ side }: SnapshotContainerProps) => {
const { canvasRef } = useGetOverviewSnapshot(side);
const navigate = useNavigate();
return (
<div className="relative w-full aspect-video">
{side === "CameraFront" || side === "Rear" ? (
<FontAwesomeIcon
icon={faArrowLeft}
className="absolute top-[50%] left-[2%] backdrop-blur-md hover:cursor-pointer"
onClick={() => navigate("/front-camera-settings")}
/>
) : (
<FontAwesomeIcon
icon={faArrowRight}
className="absolute top-[50%] right-[2%] backdrop-blur-md"
/>
)}
<canvas ref={canvasRef} className="w-full h-full object-contain block" />
</div>
);
};

View File

@@ -0,0 +1,5 @@
const CameraSettingFields = () => {
return <div>CameraSettingFields</div>;
};
export default CameraSettingFields;

View File

@@ -0,0 +1,14 @@
import Card from "../UI/Card";
import CameraOverviewHeader from "../CameraOverview/CameraOverviewHeader";
const CameraSettings = ({ title }: { title: string }) => {
return (
<Card>
<div className="relative flex flex-col space-y-3 h-full">
<CameraOverviewHeader title={title} />
</div>
</Card>
);
};
export default CameraSettings;

View File

@@ -0,0 +1,27 @@
import clsx from "clsx";
import Card from "../UI/Card";
import CameraOverviewHeader from "../CameraOverview/CameraOverviewHeader";
import { SnapshotContainer } from "../CameraOverview/SnapshotContainer";
import { useSwipeable } from "react-swipeable";
import { useNavigate } from "react-router";
import { useOverviewVideo } from "../../hooks/useOverviewVideo";
const FrontCameraOverviewCard = () => {
useOverviewVideo();
const navigate = useNavigate();
const handlers = useSwipeable({
onSwipedRight: () => navigate("/front-camera-settings"),
trackMouse: true,
});
return (
<Card className={clsx("relative min-h-[40vh] md:min-h-[60vh] h-auto")}>
<div className="flex flex-col space-y-3 h-full" {...handlers}>
<CameraOverviewHeader title="Front Overiew" />
<SnapshotContainer side="CameraFront" />
</div>
</Card>
);
};
export default FrontCameraOverviewCard;

View File

@@ -0,0 +1,23 @@
import clsx from "clsx";
import { SnapshotContainer } from "../CameraOverview/SnapshotContainer";
import Card from "../UI/Card";
import CameraOverviewHeader from "../CameraOverview/CameraOverviewHeader";
const OverviewVideoContainer = ({
title,
side,
}: {
title: string;
side: string;
}) => {
return (
<Card className={clsx("min-h-[40vh] md:min-h-[60vh] h-auto")}>
<div className="relative flex flex-col space-y-3 h-full">
<CameraOverviewHeader title={title} />
<SnapshotContainer side={side} />
</div>
</Card>
);
};
export default OverviewVideoContainer;

View File

@@ -0,0 +1,29 @@
import { GB } from "country-flag-icons/react/3x2";
import { formatNumberPlate } from "../../utils/utils";
import type { Sighting } from "../../types/types";
type NumberPlateProps = {
sighting: Sighting;
};
const NumberPlate = ({ sighting }: NumberPlateProps) => {
return (
<div
className={`relative w-[8rem] border-4 border-black rounded-lg text-nowrap
text-black px-3
${sighting?.motion !== "towards" ? "bg-yellow-400" : "bg-white"}
`}
>
<div className="">
<div className="absolute inset-y-0 left-0 bg-blue-600 w-4 flex flex-col">
<GB />
</div>
<p className=" pl-2 font-extrabold text-right">
{formatNumberPlate(sighting?.vrm)}
</p>
</div>
</div>
);
};
export default NumberPlate;

View File

@@ -0,0 +1,20 @@
import NumberPlate from "./NumberPlate";
import SightingCanvas from "../SightingOverview/SightingCanvas";
import type { Sighting } from "../../types/types";
type SightingProps = {
sighting: Sighting;
};
const Sighting = ({ sighting }: SightingProps) => {
return (
<div className="bg-gray-700 flex flex-col md:flex-row m-1 items-center justify-between w-full rounded-md p-4 space-y-4">
<SightingCanvas />
<div className="flex flex-row m-1 items-center space-x-4">
<NumberPlate sighting={sighting} />
</div>
</div>
);
};
export default Sighting;

View File

@@ -0,0 +1,17 @@
import { faEye } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
type SightingHeaderProps = {
title: string;
};
const SightingHeader = ({ title }: SightingHeaderProps) => {
return (
<div className="w-full border-b border-gray-600 flex flex-row items-center space-x-2 md:mb-6">
<FontAwesomeIcon icon={faEye} className="size-4" />
<h2 className="text-xl">{title}</h2>
</div>
);
};
export default SightingHeader;

View File

@@ -0,0 +1,27 @@
import { useEffect, useState } from "react";
import Card from "../UI/Card";
import SightingHeader from "./SightingHeader";
import Sighting from "./Sighting";
import { useLatestSighting } from "../../hooks/useLatestSighting";
type SightingProps = {
title: string;
};
const Sightings = ({ title }: SightingProps) => {
const [sightings, setSightings] = useState([]);
const { data } = useLatestSighting();
// console.log(data);
// useEffect(() => {
// setSightings([...sightings, data]);
// }, [data]);
return (
<Card className="h-[10rem] md:h-[15rem] overflow-x-hidden">
<SightingHeader title={title} />
<Sighting sighting={data} />
</Card>
);
};
export default Sightings;

View File

@@ -0,0 +1,25 @@
import clsx from "clsx";
import Card from "../UI/Card";
import { SnapshotContainer } from "../CameraOverview/SnapshotContainer";
import { useSwipeable } from "react-swipeable";
import { useNavigate } from "react-router";
import CameraOverviewHeader from "../CameraOverview/CameraOverviewHeader";
const RearCameraOverviewCard = () => {
const navigate = useNavigate();
const handlers = useSwipeable({
onSwipedLeft: () => navigate("/rear-camera-settings"),
trackMouse: true,
});
return (
<Card className={clsx("min-h-[40vh] md:min-h-[60vh] h-auto")}>
<div className="flex flex-col space-y-3 h-full" {...handlers}>
<CameraOverviewHeader title="Rear Overiew" />
<SnapshotContainer side="TargetDetectionRear" />
</div>
</Card>
);
};
export default RearCameraOverviewCard;

View File

@@ -0,0 +1,15 @@
import { useLatestSighting } from "../../hooks/useLatestSighting";
const SightingCanvas = () => {
const { canvasRef } = useLatestSighting();
return (
<div className="w-70 flex flex-col">
<canvas
ref={canvasRef}
className="items-center w-full h-10 object-contain block"
/>
</div>
);
};
export default SightingCanvas;

View File

@@ -0,0 +1,22 @@
import React from "react";
import clsx from "clsx";
type CardProps = {
children: React.ReactNode;
className?: string;
};
const Card = ({ children, className }: CardProps) => {
return (
<div
className={clsx(
"bg-[#253445] rounded-lg mt-6 mx-4 px-4 py-4 shadow-2xl overflow-y-auto md:row-span-1",
className
)}
>
{children}
</div>
);
};
export default Card;

View File

@@ -0,0 +1,15 @@
import Header from "./Header";
import Footer from "./Footer";
import { Outlet } from "react-router";
const Container = () => {
return (
<div>
<Header />
<Outlet />
<Footer />
</div>
);
};
export default Container;

View File

@@ -0,0 +1,14 @@
import Logo from "/MAV.svg";
const Footer = () => {
return (
<footer className="bg-gray-900 border-t border-gray-700 text-white py-5 text-left p-8 h-30 mt-5 flex flex-col space-y-4">
<img src={Logo} alt="Logo" width={100} height={100} />
<p className="text-sm">
{new Date().getFullYear()} MAV Systems &copy; All rights reserved.
</p>
</footer>
);
};
export default Footer;

View File

@@ -0,0 +1,16 @@
import { Link } from "react-router";
import Logo from "/MAV.svg";
const Header = () => {
return (
<div className="relative bg-[#253445] border-b border-gray-500 items-center mx-auto px-2 sm:px-6 lg:px-8 p-4">
<div className="w-30">
<Link to={"/"}>
<img src={Logo} alt="Logo" width={100} height={100} />
</Link>
</div>
</div>
);
};
export default Header;