- added session sighting component

- add new session paused state and stop adding to session when true
This commit is contained in:
2025-10-24 12:10:10 +01:00
parent b58181e551
commit c83122cd52
6 changed files with 84 additions and 56 deletions

View File

@@ -5,19 +5,26 @@ import type { ReducedSightingType } from "../../types/types";
import { toast } from "sonner"; import { toast } from "sonner";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faFloppyDisk, faPause, faPlay, faStop } from "@fortawesome/free-solid-svg-icons"; import { faFloppyDisk, faPause, faPlay, faStop } from "@fortawesome/free-solid-svg-icons";
import VehicleSessionItem from "../UI/VehicleSessionItem";
const SessionCard = () => { const SessionCard = () => {
const { sessionStarted, setSessionStarted, sessionList } = useNPEDContext(); const { sessionStarted, setSessionStarted, sessionList, setSessionPaused, sessionPaused } = useNPEDContext();
const handleStartClick = () => { const handleStartClick = () => {
setSessionStarted(!sessionStarted); setSessionStarted(!sessionStarted);
toast(`${sessionStarted ? "Vehicle tracking session Ended" : "Vehicle tracking session Started"}`); setSessionPaused(false);
toast(`${sessionStarted ? "Vehicle tracking session ended" : "Vehicle tracking session started"}`);
}; };
const handleSaveCick = () => { const handleSaveCick = () => {
console.log("clicked"); console.log("clicked");
}; };
const handlepauseClick = () => {
setSessionPaused(!sessionPaused);
toast(`${sessionStarted ? "Vehicle tracking session paused" : "Vehicle tracking session resumed"}`);
};
const sightings = [...new Map(sessionList.map((vehicle) => [vehicle.vrm, vehicle]))]; const sightings = [...new Map(sessionList.map((vehicle) => [vehicle.vrm, vehicle]))];
const dedupedSightings = sightings.map((sighting) => sighting[1]); const dedupedSightings = sightings.map((sighting) => sighting[1]);
@@ -75,45 +82,52 @@ const SessionCard = () => {
{sessionStarted && ( {sessionStarted && (
<button <button
className={`bg-gray-300 text-gray-800 px-4 py-2 rounded transition w-full lg:w-[50%]`} className={`bg-gray-300 text-gray-800 px-4 py-2 rounded transition w-full lg:w-[50%]`}
onClick={handleSaveCick} onClick={handlepauseClick}
> >
<div className="flex flex-row gap-3 items-center justify-self-center"> <div className="flex flex-row gap-3 items-center justify-self-center">
<FontAwesomeIcon icon={faPause} /> <FontAwesomeIcon icon={sessionPaused ? faPlay : faPause} />
<p>Pause session</p> <p>{sessionPaused ? "Resume session" : "Pause session"}</p>
</div> </div>
</button> </button>
)} )}
</div> </div>
<ul className="text-white space-y-2"> <ul className="text-white space-y-2">
<li className="rounded-xl border border-slate-800 bg-slate-800/60 p-3 shadow-sm flex flex-row justify-between"> <VehicleSessionItem
<p>Number of Vehicles sightings:</p> sessionNumber={dedupedSightings.length}
<span className="font-bold text-green-600 text-xl">{dedupedSightings.length}</span> textColour="text-green-400"
</li> vehicleTag={"Number of Vehicles sightings:"}
<li className="rounded-xl border border-slate-800 bg-slate-800/60 p-3 shadow-sm flex flex-row justify-between"> />
<p>Vehicles without Tax:</p> <VehicleSessionItem
<span className="font-bold text-amber-600 text-xl">{vehicles.notTaxed.length}</span> sessionNumber={vehicles.notTaxed.length}
</li> textColour="text-amber-400"
<li className="rounded-xl border border-slate-800 bg-slate-800/60 p-3 shadow-sm flex flex-row justify-between"> vehicleTag={"Vehicles without Tax:"}
<p>Vehicles without MOT:</p>{" "} />
<span className="font-bold text-red-500 text-xl">{vehicles.notMOT.length}</span> <VehicleSessionItem
</li> sessionNumber={vehicles.notMOT.length}
<li className="rounded-xl border border-slate-800 bg-slate-800/60 p-3 shadow-sm flex flex-row justify-between"> textColour="text-red-500"
<p>Vehicles on Hotlists:</p>{" "} vehicleTag={"Vehicles without MOT:"}
<span className="font-bold text-blue-500 text-xl">{vehicles.hotlistHit.length}</span> />
</li> <VehicleSessionItem
<li className="rounded-xl border border-slate-800 bg-slate-800/60 p-3 shadow-sm flex flex-row justify-between"> sessionNumber={vehicles.hotlistHit.length}
<p>Vehicles with NPED Cat A:</p> textColour="text-blue-400"
<span className="font-bold text-gray-300 text-xl">{vehicles.npedCatA.length}</span> vehicleTag={"Vehicles on Hotlists:"}
</li> />
<li className="rounded-xl border border-slate-800 bg-slate-800/60 p-3 shadow-sm flex flex-row justify-between"> <VehicleSessionItem
<p>Vehicles with NPED Cat B:</p>{" "} sessionNumber={vehicles.npedCatA.length}
<span className="font-bold text-gray-300 text-xl">{vehicles.npedCatB.length}</span> textColour="text-gray-300"
</li> vehicleTag={"Vehicles with NPED Cat A:"}
<li className="rounded-xl border border-slate-800 bg-slate-800/60 p-3 shadow-sm flex flex-row justify-between"> />
Vehicles with NPED Cat C:{" "} <VehicleSessionItem
<span className="font-bold text-gray-300 text-xl">{vehicles.npedCatC.length}</span> sessionNumber={vehicles.npedCatB.length}
</li> textColour="text-gray-300"
vehicleTag={"Vehicles with NPED Cat B:"}
/>
<VehicleSessionItem
sessionNumber={vehicles.npedCatC.length}
textColour="text-gray-300"
vehicleTag={"Vehicles with NPED Cat C:"}
/>
</ul> </ul>
</div> </div>
</Card> </Card>

View File

@@ -71,7 +71,7 @@ export default function SightingHistoryWidget({ className, title }: SightingHist
} = useSightingFeedContext(); } = useSightingFeedContext();
const { dispatch } = useAlertHitContext(); const { dispatch } = useAlertHitContext();
const { sessionStarted, setSessionList, sessionList } = useNPEDContext(); const { sessionStarted, setSessionList, sessionList, sessionPaused } = useNPEDContext();
const processedRefs = useRef<Set<number | string>>(new Set()); const processedRefs = useRef<Set<number | string>>(new Set());
@@ -88,6 +88,7 @@ export default function SightingHistoryWidget({ className, title }: SightingHist
useEffect(() => { useEffect(() => {
if (sessionStarted) { if (sessionStarted) {
if (!mostRecent) return; if (!mostRecent) return;
if (sessionPaused) return;
const reducedMostRecent = reduceObject(mostRecent); const reducedMostRecent = reduceObject(mostRecent);
setSessionList([...sessionList, reducedMostRecent]); setSessionList([...sessionList, reducedMostRecent]);
} }

View File

@@ -1,21 +1,14 @@
import { Link } from "react-router"; import { Link } from "react-router";
import Logo from "/MAV.svg"; import Logo from "/MAV.svg";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { import { faGear, faHome, faListCheck, faMaximize, faMinimize, faRotate } from "@fortawesome/free-solid-svg-icons";
faGear,
faHome,
faListCheck,
faMaximize,
faMinimize,
faRotate,
} from "@fortawesome/free-solid-svg-icons";
import { useState } from "react"; import { useState } from "react";
import SoundBtn from "./SoundBtn"; import SoundBtn from "./SoundBtn";
import { useNPEDContext } from "../../context/NPEDUserContext"; import { useNPEDContext } from "../../context/NPEDUserContext";
export default function Header() { export default function Header() {
const [isFullscreen, setIsFullscreen] = useState(false); const [isFullscreen, setIsFullscreen] = useState(false);
const { sessionStarted } = useNPEDContext(); const { sessionStarted, sessionPaused } = useNPEDContext();
const toggleFullscreen = () => { const toggleFullscreen = () => {
if (!document.fullscreenElement) { if (!document.fullscreenElement) {
@@ -39,9 +32,13 @@ export default function Header() {
</Link> </Link>
</div> </div>
<div className="flex flex-col lg:flex-row items-center space-x-24 justify-items-center"> <div className="flex flex-col lg:flex-row items-center space-x-24 justify-items-center">
{sessionStarted && ( <div className="flex flex-row lg:flex-row space-x-2">
<div className="text-green-400 font-bold">Session Active</div> {sessionStarted && sessionPaused ? (
)} <p className="text-gray-400 font-bold">Session Paused</p>
) : (
sessionStarted && <p className="text-green-400 font-bold">Session Active</p>
)}
</div>
<div className="flex flex-row space-x-8"> <div className="flex flex-row space-x-8">
<Link to={"/"}> <Link to={"/"}>
@@ -59,11 +56,7 @@ export default function Header() {
</div> </div>
<SoundBtn /> <SoundBtn />
<Link to={"/session-settings"}> <Link to={"/session-settings"}>
<FontAwesomeIcon <FontAwesomeIcon className="text-white" icon={faListCheck} size="2x" />
className="text-white"
icon={faListCheck}
size="2x"
/>
</Link> </Link>
<Link to={"/system-settings"}> <Link to={"/system-settings"}>

View File

@@ -0,0 +1,18 @@
import clsx from "clsx";
type VehicleSessionItemProps = {
sessionNumber: number;
textColour: string;
vehicleTag: string;
};
const VehicleSessionItem = ({ sessionNumber, textColour, vehicleTag }: VehicleSessionItemProps) => {
return (
<li className="rounded-xl border border-slate-800 bg-slate-800/60 p-3 shadow-sm flex flex-row justify-between items-center">
<p>{vehicleTag}</p>
<span className={`font-bold text-xl bg-slate-700 px-2 rounded-md ${clsx(textColour)}`}>{sessionNumber}</span>
</li>
);
};
export default VehicleSessionItem;

View File

@@ -5,17 +5,16 @@ type UserContextValue = {
user: NPEDUser | null; user: NPEDUser | null;
setUser: React.Dispatch<SetStateAction<NPEDUser | null>>; setUser: React.Dispatch<SetStateAction<NPEDUser | null>>;
sessionStarted: boolean; sessionStarted: boolean;
sessionPaused: boolean;
setSessionPaused: React.Dispatch<SetStateAction<boolean>>;
setSessionStarted: React.Dispatch<SetStateAction<boolean>>; setSessionStarted: React.Dispatch<SetStateAction<boolean>>;
sessionList: ReducedSightingType[]; sessionList: ReducedSightingType[];
setSessionList: React.Dispatch<SetStateAction<ReducedSightingType[]>>; setSessionList: React.Dispatch<SetStateAction<ReducedSightingType[]>>;
}; };
export const NPEDUserContext = createContext<UserContextValue | undefined>( export const NPEDUserContext = createContext<UserContextValue | undefined>(undefined);
undefined
);
export const useNPEDContext = () => { export const useNPEDContext = () => {
const ctx = useContext(NPEDUserContext); const ctx = useContext(NPEDUserContext);
if (!ctx) if (!ctx) throw new Error("useNPEDContext must be used within <NPEDUserProvider>");
throw new Error("useNPEDContext must be used within <NPEDUserProvider>");
return ctx; return ctx;
}; };

View File

@@ -10,6 +10,7 @@ export const NPEDUserProvider = ({ children }: NPEDUserProviderType) => {
const [user, setUser] = useState<NPEDUser | null>(null); const [user, setUser] = useState<NPEDUser | null>(null);
const [sessionStarted, setSessionStarted] = useState(false); const [sessionStarted, setSessionStarted] = useState(false);
const [sessionList, setSessionList] = useState<ReducedSightingType[]>([]); const [sessionList, setSessionList] = useState<ReducedSightingType[]>([]);
const [sessionPaused, setSessionPaused] = useState(false);
return ( return (
<NPEDUserContext.Provider <NPEDUserContext.Provider
@@ -20,6 +21,8 @@ export const NPEDUserProvider = ({ children }: NPEDUserProviderType) => {
sessionStarted, sessionStarted,
sessionList, sessionList,
setSessionList, setSessionList,
sessionPaused,
setSessionPaused,
}} }}
> >
{children} {children}