feat: update modal and sighting components for improved layout and timestamp display
This commit is contained in:
@@ -10,7 +10,7 @@ const ModalComponent = ({ isModalOpen, children, close }: ModalComponentProps) =
|
|||||||
<Modal
|
<Modal
|
||||||
isOpen={isModalOpen}
|
isOpen={isModalOpen}
|
||||||
onRequestClose={close}
|
onRequestClose={close}
|
||||||
className="bg-[#1e2a38] p-6 rounded-lg shadow-lg w-[95%] mt-[2%] md:w-[55%] z-100 overflow-y-auto border border-gray-600 max-h-[90%]"
|
className="bg-[#1e2a38] p-6 rounded-lg shadow-lg w-[95%] mt-[2%] md:w-[60%] z-100 overflow-y-auto border border-gray-600 max-h-[90%]"
|
||||||
overlayClassName="fixed inset-0 bg-[#1e2a38]/70 flex justify-center items-start z-100"
|
overlayClassName="fixed inset-0 bg-[#1e2a38]/70 flex justify-center items-start z-100"
|
||||||
closeTimeoutMS={200}
|
closeTimeoutMS={200}
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useCameraSettingsContext } from "../../../../../app/context/CameraSettingsContext";
|
import { useCameraSettingsContext } from "../../../../../app/context/CameraSettingsContext";
|
||||||
import type { SightingType } from "../../../../../utils/types";
|
import type { SightingType } from "../../../../../utils/types";
|
||||||
|
import { timeAgo } from "../../../../../utils/utils";
|
||||||
import NumberPlate from "../../platePatch/NumberPlate";
|
import NumberPlate from "../../platePatch/NumberPlate";
|
||||||
import VideoFeed from "../../videoFeed/VideoFeed";
|
import VideoFeed from "../../videoFeed/VideoFeed";
|
||||||
|
|
||||||
@@ -11,11 +12,13 @@ const SightingModalContent = ({ sighting }: SightingModalContentProps) => {
|
|||||||
const { state: cameraSettings } = useCameraSettingsContext();
|
const { state: cameraSettings } = useCameraSettingsContext();
|
||||||
const size = cameraSettings.imageSize;
|
const size = cameraSettings.imageSize;
|
||||||
const modalImageSize = { width: size.width / 1.5, height: size.height / 1.5 };
|
const modalImageSize = { width: size.width / 1.5, height: size.height / 1.5 };
|
||||||
|
const timeStamp = timeAgo(sighting?.timeStampMillis ?? null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{sighting ? (
|
{sighting ? (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-row items-center justify-between mb-6">
|
<div className="flex flex-row items-center justify-between mb-6 border-b border-gray-600">
|
||||||
<h2 className="text-2xl font-bold">Sighting Details</h2>
|
<h2 className="text-2xl font-bold">Sighting Details</h2>
|
||||||
<NumberPlate vrm={sighting.vrm} motion={sighting.motion.toLowerCase() === "away"} size="md" />
|
<NumberPlate vrm={sighting.vrm} motion={sighting.motion.toLowerCase() === "away"} size="md" />
|
||||||
</div>
|
</div>
|
||||||
@@ -24,24 +27,32 @@ const SightingModalContent = ({ sighting }: SightingModalContentProps) => {
|
|||||||
<VideoFeed mostRecentSighting={sighting} isLoading={false} size={modalImageSize} isModal={true} />
|
<VideoFeed mostRecentSighting={sighting} isLoading={false} size={modalImageSize} isModal={true} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4 border p-4 rounded-lg border-gray-600">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600">VRM</p>
|
<p className="text-md text-gray-300">VRM</p>
|
||||||
<p className="text-lg font-semibold">{sighting.vrm}</p>
|
<p className="text-lg font-semibold">{sighting.vrm}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600">Timestamp</p>
|
<p className="text-md text-gray-300">Seen</p>
|
||||||
<p className="text-sm">{new Date(sighting.timeStampMillis).toLocaleString()}</p>
|
<p className="text-md">{timeStamp}</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-gray-600">Motion</p>
|
<p className="text-md text-gray-300">Timestamp</p>
|
||||||
|
<p className="text-md">{new Date(sighting.timeStampMillis).toLocaleString()}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-md text-gray-300">Motion</p>
|
||||||
<p className="text-lg font-semibold">{sighting.motion}</p>
|
<p className="text-lg font-semibold">{sighting.motion}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-md text-gray-300">Radar Speed</p>
|
||||||
|
<p className="text-lg font-semibold">{sighting.radarSpeed} mph</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p>No sighting data available.</p>
|
<p className="text-gray-300">No sighting data available.</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Stage, Layer, Image, Rect } from "react-konva";
|
import { Stage, Layer, Image, Rect, Text } from "react-konva";
|
||||||
import type { SightingType } from "../../../../utils/types";
|
import type { SightingType } from "../../../../utils/types";
|
||||||
import { useCreateVideoSnapshot } from "../../hooks/useCreateVideoSnapshot";
|
import { useCreateVideoSnapshot } from "../../hooks/useCreateVideoSnapshot";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
@@ -96,6 +96,23 @@ const VideoFeed = ({ mostRecentSighting, isLoading, size, modeSetting, isModal =
|
|||||||
))}
|
))}
|
||||||
</Layer>
|
</Layer>
|
||||||
)}
|
)}
|
||||||
|
<Layer>
|
||||||
|
<Rect
|
||||||
|
x={5}
|
||||||
|
y={0.955 * size.height}
|
||||||
|
width={size.width * 0.35}
|
||||||
|
height={30}
|
||||||
|
fill="rgba(255, 255, 255, 0.45)"
|
||||||
|
cornerRadius={5}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
text={`Overlay Mode: ${mode === 0 ? "None" : mode === 1 ? "Plate Highlight" : "Plate Track"}`}
|
||||||
|
x={10}
|
||||||
|
y={0.96 * size.height}
|
||||||
|
fontSize={16}
|
||||||
|
fill="#000000"
|
||||||
|
/>
|
||||||
|
</Layer>
|
||||||
</Stage>
|
</Stage>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ export const formatNumberPlate = (plate: string) => {
|
|||||||
return formattedPlate;
|
return formattedPlate;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const timeAgo = (timestampmili: number) => {
|
export const timeAgo = (timestampmili: number | null) => {
|
||||||
|
if (timestampmili === null) return "unknown";
|
||||||
const diffMs = Date.now() - new Date(timestampmili).getTime();
|
const diffMs = Date.now() - new Date(timestampmili).getTime();
|
||||||
const diffMins = Math.floor(diffMs / 60000);
|
const diffMins = Math.floor(diffMs / 60000);
|
||||||
if (diffMins < 60) {
|
if (diffMins < 60) {
|
||||||
|
|||||||
Reference in New Issue
Block a user