Compare commits
2 Commits
feature/ta
...
feature/ca
| Author | SHA1 | Date | |
|---|---|---|---|
| ddb1fa1bf1 | |||
| c910a3dd50 |
@@ -22,9 +22,9 @@ export const initialState: CameraFeedState = {
|
|||||||
|
|
||||||
selectedRegionIndex: 0,
|
selectedRegionIndex: 0,
|
||||||
modeByCamera: {
|
modeByCamera: {
|
||||||
A: "brush",
|
A: "painter",
|
||||||
B: "brush",
|
B: "painter",
|
||||||
C: "brush",
|
C: "painter",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -70,7 +70,6 @@ export function reducer(state: CameraFeedState, action: CameraFeedAction) {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
case "REMOVE_REGION":
|
case "REMOVE_REGION":
|
||||||
console.log(action.payload);
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
regionsByCamera: {
|
regionsByCamera: {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useState } from "react";
|
|||||||
import VideoFeedGridPainter from "./Video/VideoFeedGridPainter";
|
import VideoFeedGridPainter from "./Video/VideoFeedGridPainter";
|
||||||
import CameraSettings from "./CameraSettings/CameraSettings";
|
import CameraSettings from "./CameraSettings/CameraSettings";
|
||||||
|
|
||||||
import PlatePatch from "./PlatePatch/PlatePatch";
|
import PlatePatch from "./PlatePatch/SightingPatch";
|
||||||
|
|
||||||
const CameraGrid = () => {
|
const CameraGrid = () => {
|
||||||
const [tabIndex, setTabIndex] = useState(0);
|
const [tabIndex, setTabIndex] = useState(0);
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
import Card from "../../../../ui/Card";
|
|
||||||
|
|
||||||
const PlatePatch = () => {
|
|
||||||
return <Card className="md:row-start-4 md:col-span-2">PlatePatch</Card>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PlatePatch;
|
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { useCameraFeedContext } from "../../../../app/context/CameraFeedContext";
|
||||||
|
import type { DecodeReading } from "../../../../types/types";
|
||||||
|
import { useSightingEntryAndExit } from "../../hooks/useSightingEntryAndExit";
|
||||||
|
|
||||||
|
const SightingEntryTable = () => {
|
||||||
|
const { state } = useCameraFeedContext();
|
||||||
|
const cameraFeedID = state.cameraFeedID;
|
||||||
|
const { entryQuery } = useSightingEntryAndExit(cameraFeedID);
|
||||||
|
|
||||||
|
const isLoading = entryQuery?.isFetching;
|
||||||
|
const readings = entryQuery?.data?.decodes;
|
||||||
|
|
||||||
|
if (isLoading) return <span className="text-slate-500">Loading Sighting data…</span>;
|
||||||
|
return (
|
||||||
|
<div className="border border-gray-600 rounded-lg overflow-hidden m-2">
|
||||||
|
<div className="overflow-y-auto ">
|
||||||
|
<table className="w-full text-left text-sm">
|
||||||
|
<thead className="bg-gray-700/50 text-gray-200 sticky top-0">
|
||||||
|
<tr>
|
||||||
|
<th className="px-4 py-3 font-semibold">VRM</th>
|
||||||
|
<th className="px-4 py-3 font-semibold">Lane ID</th>
|
||||||
|
<th className="px-4 py-3 font-semibold text-center">Seen Count</th>
|
||||||
|
<th className="px-4 py-3 font-semibold">First Seen</th>
|
||||||
|
<th className="px-4 py-3 font-semibold">Last Seen</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-gray-700">
|
||||||
|
{readings?.map((reading: DecodeReading) => (
|
||||||
|
<tr className="hover:bg-gray-800/30 transition-colors" key={reading?.id}>
|
||||||
|
<td className="px-4 py-3 font-mono font-semibold text-blue-400 text-lg">{reading?.vrm}</td>
|
||||||
|
<td className="px-4 py-3 text-gray-300">{reading?.laneID}</td>
|
||||||
|
<td className="px-4 py-3 text-center text-gray-300">{reading?.seenCount}</td>
|
||||||
|
<td className="px-4 py-3 text-gray-400 text-md">{reading?.firstSeenTimeHumane}</td>
|
||||||
|
<td className="px-4 py-3 text-gray-400 text-md">{reading?.lastSeenTimeHumane}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SightingEntryTable;
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { useCameraFeedContext } from "../../../../app/context/CameraFeedContext";
|
||||||
|
import type { DecodeReading } from "../../../../types/types";
|
||||||
|
import { useSightingEntryAndExit } from "../../hooks/useSightingEntryAndExit";
|
||||||
|
|
||||||
|
const SightingExitTable = () => {
|
||||||
|
const { state } = useCameraFeedContext();
|
||||||
|
const cameraFeedID = state.cameraFeedID;
|
||||||
|
const { exitQuery } = useSightingEntryAndExit(cameraFeedID);
|
||||||
|
|
||||||
|
const isLoading = exitQuery?.isFetching;
|
||||||
|
const readings = exitQuery?.data?.decodes;
|
||||||
|
|
||||||
|
if (isLoading) return <span className="text-slate-500">Loading Sighting data…</span>;
|
||||||
|
return (
|
||||||
|
<div className="border border-gray-600 rounded-lg overflow-hidden m-2">
|
||||||
|
<div className="overflow-y-auto ">
|
||||||
|
<table className="w-full text-left text-sm">
|
||||||
|
<thead className="bg-gray-700/50 text-gray-200 sticky top-0">
|
||||||
|
<tr>
|
||||||
|
<th className="px-4 py-3 font-semibold">VRM</th>
|
||||||
|
<th className="px-4 py-3 font-semibold">Lane ID</th>
|
||||||
|
<th className="px-4 py-3 font-semibold text-center">Seen Count</th>
|
||||||
|
<th className="px-4 py-3 font-semibold">First Seen</th>
|
||||||
|
<th className="px-4 py-3 font-semibold">Last Seen</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-gray-700">
|
||||||
|
{readings?.map((reading: DecodeReading) => (
|
||||||
|
<tr className="hover:bg-gray-800/30 transition-colors" key={reading?.id}>
|
||||||
|
<td className="px-4 py-3 font-mono font-semibold text-red-400 text-lg">{reading?.vrm}</td>
|
||||||
|
<td className="px-4 py-3 text-gray-300">{reading?.laneID}</td>
|
||||||
|
<td className="px-4 py-3 text-center text-gray-300">{reading?.seenCount}</td>
|
||||||
|
<td className="px-4 py-3 text-gray-400 text-md">{reading?.firstSeenTimeHumane}</td>
|
||||||
|
<td className="px-4 py-3 text-gray-400 text-md">{reading?.lastSeenTimeHumane}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SightingExitTable;
|
||||||
27
src/features/cameras/components/PlatePatch/SightingPatch.tsx
Normal file
27
src/features/cameras/components/PlatePatch/SightingPatch.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
|
||||||
|
import Card from "../../../../ui/Card";
|
||||||
|
import CardHeader from "../../../../ui/CardHeader";
|
||||||
|
import SightingEntryTable from "./SightingEntryTable";
|
||||||
|
import SightingExitTable from "./SightingExitTable";
|
||||||
|
|
||||||
|
const PlatePatch = () => {
|
||||||
|
return (
|
||||||
|
<Card className="md:row-start-4 md:col-span-2 p-4 h-[190%]">
|
||||||
|
<CardHeader title="Entry / Exit" />
|
||||||
|
<Tabs>
|
||||||
|
<TabList>
|
||||||
|
<Tab>Entry Sightings</Tab>
|
||||||
|
<Tab>Exit Sightings</Tab>
|
||||||
|
</TabList>
|
||||||
|
<TabPanel>
|
||||||
|
<SightingEntryTable />
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel>
|
||||||
|
<SightingExitTable />
|
||||||
|
</TabPanel>
|
||||||
|
</Tabs>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PlatePatch;
|
||||||
@@ -88,7 +88,7 @@ const VideoFeedGridPainter = () => {
|
|||||||
const width = window.innerWidth;
|
const width = window.innerWidth;
|
||||||
|
|
||||||
const aspectRatio = 740 / 460;
|
const aspectRatio = 740 / 460;
|
||||||
const newWidth = width * 0.36;
|
const newWidth = width * 0.39;
|
||||||
const newHeight = newWidth / aspectRatio;
|
const newHeight = newWidth / aspectRatio;
|
||||||
setStageSize({ width: newWidth, height: newHeight });
|
setStageSize({ width: newWidth, height: newHeight });
|
||||||
};
|
};
|
||||||
|
|||||||
28
src/features/cameras/hooks/useSightingEntryAndExit.ts
Normal file
28
src/features/cameras/hooks/useSightingEntryAndExit.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { CAMBASE } from "../../../utils/config";
|
||||||
|
|
||||||
|
const fetchEntrySightings = async (cameraFeedID: string) => {
|
||||||
|
const response = await fetch(`${CAMBASE}/EntrySightingCreator${cameraFeedID}-list-proto-sightings`);
|
||||||
|
if (!response.ok) throw new Error("Cannot reach sighing entry endpoint");
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchExitSightings = async (cameraFeedID: string) => {
|
||||||
|
const response = await fetch(`${CAMBASE}/ExitSightingCreator${cameraFeedID}-list-proto-sightings`);
|
||||||
|
if (!response.ok) throw new Error("Cannot reach sighing exit endpoint");
|
||||||
|
return response.json();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSightingEntryAndExit = (cameraFeedID: string) => {
|
||||||
|
const entryQuery = useQuery({
|
||||||
|
queryKey: ["Entry Sightings", cameraFeedID],
|
||||||
|
queryFn: () => fetchEntrySightings(cameraFeedID),
|
||||||
|
});
|
||||||
|
|
||||||
|
const exitQuery = useQuery({
|
||||||
|
queryKey: ["Exit Sightings", cameraFeedID],
|
||||||
|
queryFn: () => fetchExitSightings(cameraFeedID),
|
||||||
|
});
|
||||||
|
|
||||||
|
return { entryQuery, exitQuery };
|
||||||
|
};
|
||||||
@@ -144,3 +144,15 @@ export type CameraFeedAction =
|
|||||||
type: "RESET_PAINTED_CELLS";
|
type: "RESET_PAINTED_CELLS";
|
||||||
payload: { cameraFeedID: "A" | "B" | "C"; paintedCells: Map<string, PaintedCell> };
|
payload: { cameraFeedID: "A" | "B" | "C"; paintedCells: Map<string, PaintedCell> };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DecodeReading = {
|
||||||
|
id: number;
|
||||||
|
vrm: string;
|
||||||
|
laneID: number;
|
||||||
|
seenCount: number;
|
||||||
|
firstSeenTime?: number;
|
||||||
|
lastSeenTime?: number;
|
||||||
|
duplicate?: true;
|
||||||
|
firstSeenTimeHumane: string;
|
||||||
|
lastSeenTimeHumane: string;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user