Compare commits
9 Commits
feature/ws
...
bugfix/das
| Author | SHA1 | Date | |
|---|---|---|---|
| 71ce2a9f91 | |||
| 3fbafbbcc7 | |||
| b38fbe132b | |||
| 9489fe2d6a | |||
| 3353ad6f8b | |||
| d1995f0a9f | |||
| e395777ae9 | |||
| ba93753df7 | |||
| f0f311f316 |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "bayiq-ui",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"version": "1.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -53,8 +53,8 @@ const RegionSelector = ({
|
||||
const getMagnificationLevel = () => {
|
||||
const test = socket.data;
|
||||
if (!socket.data) return null;
|
||||
|
||||
if (!test || !test.magnificationLevel) return "0x";
|
||||
console.log(test);
|
||||
if (!test || !test.magnificationLevel) return "1x";
|
||||
return test?.magnificationLevel;
|
||||
};
|
||||
|
||||
@@ -108,12 +108,12 @@ const RegionSelector = ({
|
||||
|
||||
const handleSaveclick = () => {
|
||||
const regions: ColourData[] = [];
|
||||
const test = Array.from(paintedCells.entries());
|
||||
const region1 = test.filter(([, cell]) => cell.region.name === "Bay 1");
|
||||
const region2 = test.filter(([, cell]) => cell.region.name === "Bay 2");
|
||||
const region3 = test.filter(([, cell]) => cell.region.name === "Bay 3");
|
||||
const region4 = test.filter(([, cell]) => cell.region.name === "Bay 4");
|
||||
const region5 = test.filter(([, cell]) => cell.region.name === "Bay 5");
|
||||
const paintedCellsArray = Array.from(paintedCells.entries());
|
||||
const region1 = paintedCellsArray.filter(([, cell]) => cell.region.name === "Bay 1");
|
||||
const region2 = paintedCellsArray.filter(([, cell]) => cell.region.name === "Bay 2");
|
||||
const region3 = paintedCellsArray.filter(([, cell]) => cell.region.name === "Bay 3");
|
||||
const region4 = paintedCellsArray.filter(([, cell]) => cell.region.name === "Bay 4");
|
||||
const region5 = paintedCellsArray.filter(([, cell]) => cell.region.name === "Bay 5");
|
||||
const region1Data = {
|
||||
id: 1,
|
||||
cells: region1.map(([key]) => [parseInt(key.split("-")[1]), parseInt(key.split("-")[0])]),
|
||||
@@ -152,7 +152,7 @@ const RegionSelector = ({
|
||||
|
||||
colourMutation.mutate({ cameraFeedID, regions: regions });
|
||||
|
||||
// Convert Map to plain object for blackboard
|
||||
// Convert map to plain object for blackboard
|
||||
const serializableState = {
|
||||
...state,
|
||||
paintedCells: {
|
||||
@@ -238,7 +238,7 @@ const RegionSelector = ({
|
||||
/>
|
||||
<div className="flex flex-col space-y-3">
|
||||
<span className="text-xl">Digital Zoom mode</span>
|
||||
<pre className="text-xs text-gray-400">{`current Zoom: ${getMagnificationLevel()}`}</pre>
|
||||
<pre className="text-xs text-gray-400">{`Current Zoom: ${getMagnificationLevel()}`}</pre>
|
||||
{mode === "zoom" && <small className={`text-gray-400 italic`}>Click image to digitally zoom</small>}
|
||||
</div>
|
||||
</label>
|
||||
@@ -282,10 +282,16 @@ const RegionSelector = ({
|
||||
})}
|
||||
</>
|
||||
<div className="flex flex-col gap-4 mt-4">
|
||||
<button className="border border-blue-900 bg-blue-700 px-4 py-1 rounded-md" onClick={handleAddRegionClick}>
|
||||
<button
|
||||
className="border border-blue-900 bg-blue-700 px-4 py-1 rounded-md hover:bg-blue-800 hover:cursor-pointer"
|
||||
onClick={handleAddRegionClick}
|
||||
>
|
||||
Add Bay
|
||||
</button>
|
||||
<button className="border border-red-900 bg-red-700 px-4 py-1 rounded-md" onClick={handleRemoveClick}>
|
||||
<button
|
||||
className="border border-red-900 bg-red-700 px-4 py-1 rounded-md hover:bg-red-800 hover:cursor-pointer"
|
||||
onClick={handleRemoveClick}
|
||||
>
|
||||
Remove Bay
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
import { useState } from "react";
|
||||
import { useCameraFeedContext } from "../../../../app/context/CameraFeedContext";
|
||||
import type { DecodeReading } from "../../../../types/types";
|
||||
import { useSightingEntryAndExit } from "../../hooks/useSightingEntryAndExit";
|
||||
|
||||
import PlatePatchModal from "./platePatchModal/PlatePatchModal";
|
||||
|
||||
const SightingEntryTable = () => {
|
||||
const { state } = useCameraFeedContext();
|
||||
const [isPlatePatchModalOpen, setIsPlatePatchModalOpen] = useState(false);
|
||||
const [currentPatch, setCurrentPatch] = useState<DecodeReading | null>(null);
|
||||
const cameraFeedID = state.cameraFeedID;
|
||||
const { entryQuery } = useSightingEntryAndExit(cameraFeedID);
|
||||
|
||||
const isLoading = entryQuery?.isFetching;
|
||||
const readings = entryQuery?.data?.decodes;
|
||||
|
||||
const handleRowClick = (reading: DecodeReading) => {
|
||||
setCurrentPatch(reading);
|
||||
setIsPlatePatchModalOpen(true);
|
||||
};
|
||||
|
||||
if (isLoading) return <span className="text-slate-500">Loading Sighting data…</span>;
|
||||
return (
|
||||
<>
|
||||
<div className="border border-gray-600 rounded-lg m-2">
|
||||
{/* Desktop Table */}
|
||||
<div className="hidden md:block overflow-y-auto">
|
||||
@@ -27,7 +38,11 @@ const SightingEntryTable = () => {
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-700">
|
||||
{readings?.map((reading: DecodeReading) => (
|
||||
<tr className="hover:bg-gray-800/30 transition-colors" key={reading?.id}>
|
||||
<tr
|
||||
className="hover:bg-gray-800/30 transition-colors hover:cursor-pointer"
|
||||
key={reading?.id}
|
||||
onClick={() => handleRowClick(reading)}
|
||||
>
|
||||
<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>
|
||||
@@ -39,12 +54,13 @@ const SightingEntryTable = () => {
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Mobile Cards */}
|
||||
{/* Mobile */}
|
||||
<div className="md:hidden overflow-y-auto space-y-3 p-3">
|
||||
{readings?.map((reading: DecodeReading) => (
|
||||
<div
|
||||
key={reading?.id}
|
||||
className="bg-gray-800/30 rounded-lg p-4 space-y-2 border border-gray-700 hover:border-gray-600 transition-colors"
|
||||
onClick={() => handleRowClick(reading)}
|
||||
>
|
||||
<div className="flex justify-between items-start">
|
||||
<span className="font-mono font-semibold text-blue-400 text-xl">{reading?.vrm}</span>
|
||||
@@ -68,6 +84,13 @@ const SightingEntryTable = () => {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<PlatePatchModal
|
||||
isPlatePatchModalOpen={isPlatePatchModalOpen}
|
||||
handleClose={() => setIsPlatePatchModalOpen(false)}
|
||||
currentPatch={currentPatch}
|
||||
direction={"entry"}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { useState } from "react";
|
||||
import { useCameraFeedContext } from "../../../../app/context/CameraFeedContext";
|
||||
import type { DecodeReading } from "../../../../types/types";
|
||||
import { useSightingEntryAndExit } from "../../hooks/useSightingEntryAndExit";
|
||||
import PlatePatchModal from "./platePatchModal/PlatePatchModal";
|
||||
|
||||
const SightingExitTable = () => {
|
||||
const [isPlatePatchModalOpen, setIsPlatePatchModalOpen] = useState(false);
|
||||
const [currentPatch, setCurrentPatch] = useState<DecodeReading | null>(null);
|
||||
const { state } = useCameraFeedContext();
|
||||
const cameraFeedID = state.cameraFeedID;
|
||||
const { exitQuery } = useSightingEntryAndExit(cameraFeedID);
|
||||
@@ -10,8 +14,14 @@ const SightingExitTable = () => {
|
||||
const isLoading = exitQuery?.isFetching;
|
||||
const readings = exitQuery?.data?.decodes;
|
||||
|
||||
const handleRowClick = (reading: DecodeReading) => {
|
||||
setCurrentPatch(reading);
|
||||
setIsPlatePatchModalOpen(true);
|
||||
};
|
||||
|
||||
if (isLoading) return <span className="text-slate-500">Loading Sighting data…</span>;
|
||||
return (
|
||||
<>
|
||||
<div className="border border-gray-600 rounded-lg m-2">
|
||||
{/* Desktop Table */}
|
||||
<div className="hidden md:block overflow-y-auto">
|
||||
@@ -27,7 +37,11 @@ const SightingExitTable = () => {
|
||||
</thead>
|
||||
<tbody className="divide-y divide-gray-700">
|
||||
{readings?.map((reading: DecodeReading) => (
|
||||
<tr className="hover:bg-gray-800/30 transition-colors" key={reading?.id}>
|
||||
<tr
|
||||
className="hover:bg-gray-800/30 transition-colors hover:cursor-pointer"
|
||||
key={reading?.id}
|
||||
onClick={() => handleRowClick(reading)}
|
||||
>
|
||||
<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>
|
||||
@@ -45,6 +59,7 @@ const SightingExitTable = () => {
|
||||
<div
|
||||
key={reading?.id}
|
||||
className="bg-gray-800/30 rounded-lg p-4 space-y-2 border border-gray-700 hover:border-gray-600 transition-colors"
|
||||
onClick={() => handleRowClick(reading)}
|
||||
>
|
||||
<div className="flex justify-between items-start">
|
||||
<span className="font-mono font-semibold text-red-400 text-xl">{reading?.vrm}</span>
|
||||
@@ -68,6 +83,13 @@ const SightingExitTable = () => {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<PlatePatchModal
|
||||
isPlatePatchModalOpen={isPlatePatchModalOpen}
|
||||
handleClose={() => setIsPlatePatchModalOpen(false)}
|
||||
currentPatch={currentPatch}
|
||||
direction={"exit"}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { DecodeReading } from "../../../../../types/types";
|
||||
import ModalComponent from "../../../../../ui/ModalComponent";
|
||||
import PlatePatchModalContent from "./PlatePatchModalContent";
|
||||
|
||||
type PlatePatchModalProps = {
|
||||
isPlatePatchModalOpen: boolean;
|
||||
handleClose: () => void;
|
||||
currentPatch: DecodeReading | null;
|
||||
direction?: "entry" | "exit";
|
||||
};
|
||||
|
||||
const PlatePatchModal = ({ isPlatePatchModalOpen, handleClose, currentPatch, direction }: PlatePatchModalProps) => {
|
||||
return (
|
||||
<ModalComponent isModalOpen={isPlatePatchModalOpen} close={handleClose}>
|
||||
<PlatePatchModalContent currentPatch={currentPatch} direction={direction} />
|
||||
</ModalComponent>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlatePatchModal;
|
||||
@@ -0,0 +1,64 @@
|
||||
import type { DecodeReading } from "../../../../../types/types";
|
||||
|
||||
type PlatePatchModalContentProps = {
|
||||
currentPatch: DecodeReading | null;
|
||||
direction?: "entry" | "exit";
|
||||
};
|
||||
|
||||
const PlatePatchModalContent = ({ currentPatch, direction }: PlatePatchModalContentProps) => {
|
||||
const imageSrc = `data:image/png;base64,${currentPatch?.plate || ""}`;
|
||||
const imageUrl = currentPatch ? imageSrc : "";
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between border-b border-gray-600 pb-3">
|
||||
<h2
|
||||
className={`font-mono font-bold text-3xl tracking-wide
|
||||
${direction === "entry" ? "text-blue-400" : "text-red-400"}`}
|
||||
>
|
||||
{currentPatch?.vrm}
|
||||
</h2>
|
||||
<span
|
||||
className={`px-3 py-1 rounded-full text-xs font-semibold uppercase
|
||||
${direction === "entry" ? "bg-blue-500/20 text-blue-400" : "bg-red-500/20 text-red-400"}`}
|
||||
>
|
||||
{direction === "entry" ? "Entry" : "Exit"}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="border border-gray-600 rounded-2xl">
|
||||
<div className="flex bg-gray-800/50 rounded-lg p-4">
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt={`${direction === "entry" ? "Entry" : "Exit"} Image for ${currentPatch?.vrm || "N/A"}`}
|
||||
className="rounded-lg border border-gray-600 max-w-full h-auto shadow-lg"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4 bg-gray-800/30 rounded-lg p-4">
|
||||
<div className="space-y-1">
|
||||
<p className="text-gray-400 text-xs uppercase tracking-wider">Bay ID</p>
|
||||
<p className="text-gray-200 font-semibold text-lg">{currentPatch?.laneID || "N/A"}</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1">
|
||||
<p className="text-gray-400 text-xs uppercase tracking-wider">Seen Count</p>
|
||||
<p className="text-gray-200 font-semibold text-lg">{currentPatch?.seenCount || "N/A"}</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1 col-span-2">
|
||||
<p className="text-gray-400 text-xs uppercase tracking-wider">First Seen</p>
|
||||
<p className="text-gray-300 text-sm">{currentPatch?.firstSeenTimeHumane || "N/A"}</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1 col-span-2">
|
||||
<p className="text-gray-400 text-xs uppercase tracking-wider">Last Seen</p>
|
||||
<p className="text-gray-300 text-sm">{currentPatch?.lastSeenTimeHumane || "N/A"}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlatePatchModalContent;
|
||||
@@ -35,12 +35,12 @@ const VideoFeedGridPainter = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stageRef = useRef<any>(null);
|
||||
|
||||
const currentScale = stageSize.width / BACKEND_WIDTH;
|
||||
const size = BACKEND_CELL_SIZE * currentScale;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const paintLayerRef = useRef<any>(null);
|
||||
|
||||
const currentScale = stageSize.width / BACKEND_WIDTH;
|
||||
const size = BACKEND_CELL_SIZE * currentScale;
|
||||
|
||||
const cameraASocket = useCameraFeedASocket();
|
||||
const cameraBSocket = useCameraFeedBSocket();
|
||||
const cameraCSocket = useCameraFeedCSocket();
|
||||
@@ -206,7 +206,7 @@ const VideoFeedGridPainter = () => {
|
||||
onMouseLeave={handleStageMouseUp}
|
||||
className={`max-w-[55%] md:row-span-3 md:col-span-3 ${mode === "painter" ? "hover:cursor-crosshair" : ""} ${
|
||||
mode === "eraser" ? "hover:cursor-pointer" : ""
|
||||
}`}
|
||||
} ${mode === "zoom" ? "hover:cursor-zoom-in" : ""}`}
|
||||
>
|
||||
<Layer
|
||||
scaleX={scale}
|
||||
|
||||
@@ -24,7 +24,11 @@ export const useCreateVideoSnapshot = () => {
|
||||
if (!snapShot) return;
|
||||
|
||||
try {
|
||||
const bitmap = await createImageBitmap(snapShot);
|
||||
const bitmap = await createImageBitmap(snapShot, {
|
||||
resizeWidth: 720,
|
||||
resizeHeight: 1080,
|
||||
resizeQuality: "high",
|
||||
});
|
||||
if (!bitmap) return;
|
||||
latestBitmapRef.current = bitmap;
|
||||
} catch (error) {
|
||||
|
||||
@@ -11,10 +11,14 @@ type CameraStatusProps = {
|
||||
};
|
||||
|
||||
const CameraStatus = ({ title, category, isError }: CameraStatusProps) => {
|
||||
const isAllGood = category && category.length > 0 && category.every((status) => status.tags.includes("RUNNING"));
|
||||
// check if some are down
|
||||
// check if all are down
|
||||
//check if offline
|
||||
const isAllGood =
|
||||
category &&
|
||||
category.length > 0 &&
|
||||
category.every((status) => {
|
||||
const allowedTags = ["RUNNING", "VIDEO-PLAYING"];
|
||||
return status.tags.every((tag) => allowedTags.includes(tag));
|
||||
});
|
||||
|
||||
return (
|
||||
<Card className="p-4">
|
||||
<div className="border-b border-gray-600">
|
||||
|
||||
@@ -10,7 +10,10 @@ type CameraStatusGridItemProps = {
|
||||
|
||||
const CameraStatusGridItem = ({ title, statusCategory }: CameraStatusGridItemProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const isAllGood = statusCategory?.every((status) => status.tags.includes("RUNNING"));
|
||||
const isAllGood = statusCategory?.every((status) => {
|
||||
const allowedTags = ["RUNNING", "VIDEO-PLAYING"];
|
||||
return status.tags.every((tag) => allowedTags.includes(tag));
|
||||
});
|
||||
|
||||
const handleClick = () => {
|
||||
setIsOpen(false);
|
||||
|
||||
17
src/hooks/useGetVersions.ts
Normal file
17
src/hooks/useGetVersions.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { CAMBASE } from "../utils/config";
|
||||
|
||||
const fetchVersions = async () => {
|
||||
const response = await fetch(`${CAMBASE}/api/versions`);
|
||||
if (!response.ok) throw new Error("Cannot get Versions");
|
||||
return response.json();
|
||||
};
|
||||
|
||||
export const useGetVersions = () => {
|
||||
const versionsQuery = useQuery({
|
||||
queryKey: ["getversions"],
|
||||
queryFn: fetchVersions,
|
||||
});
|
||||
|
||||
return { versionsQuery };
|
||||
};
|
||||
@@ -6,7 +6,10 @@ import { AppProviders } from "./app/providers/AppProviders";
|
||||
import "./index.css";
|
||||
import Modal from "react-modal";
|
||||
|
||||
const router = createRouter({ routeTree });
|
||||
const router = createRouter({
|
||||
routeTree,
|
||||
basepath: "/bayiq",
|
||||
});
|
||||
|
||||
Modal.setAppElement("#root");
|
||||
|
||||
|
||||
@@ -202,6 +202,7 @@ export type DecodeReading = {
|
||||
duplicate?: true;
|
||||
firstSeenTimeHumane: string;
|
||||
lastSeenTimeHumane: string;
|
||||
plate?: string;
|
||||
};
|
||||
|
||||
export type ColourData = {
|
||||
@@ -242,3 +243,16 @@ export type BlackBoardOptions = {
|
||||
};
|
||||
|
||||
export type CameraZoomConfig = { cameraFeedID: string; zoomLevel: number };
|
||||
|
||||
export type versionInfo = {
|
||||
version: string;
|
||||
revision: string;
|
||||
buildtime: string;
|
||||
appname: string;
|
||||
MAC: string;
|
||||
timeStamp: number;
|
||||
UUID: string;
|
||||
proquint: string;
|
||||
"Serial No.": string;
|
||||
"Model No.": string;
|
||||
};
|
||||
|
||||
76
src/ui/DevModal.tsx
Normal file
76
src/ui/DevModal.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import type { versionInfo } from "../types/types";
|
||||
import ModalComponent from "./ModalComponent";
|
||||
|
||||
type DevModalProps = {
|
||||
isDevModalOpen: boolean;
|
||||
handleClose: () => void;
|
||||
data: versionInfo;
|
||||
};
|
||||
|
||||
const DevModal = ({ isDevModalOpen, handleClose, data }: DevModalProps) => {
|
||||
const uiName = __APP_NAME__;
|
||||
const uiVersion = __APP_VERSION__;
|
||||
const commitID = __GIT_COMMIT__;
|
||||
const commitTimeStamp = __GIT_TIMESTAMP__;
|
||||
|
||||
return (
|
||||
<ModalComponent isModalOpen={isDevModalOpen} close={handleClose}>
|
||||
<div className="space-y-6">
|
||||
<div className="border-b border-gray-600 pb-3">
|
||||
<h2 className="text-2xl font-bold text-gray-100">System Information</h2>
|
||||
<p className="text-sm text-gray-400 mt-1">Application version details</p>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-sm font-semibold text-gray-400 uppercase tracking-wide">Frontend (UI)</h3>
|
||||
<div className="bg-gray-800/50 rounded-lg p-4 space-y-3">
|
||||
<div className="flex justify-between items-center border-b border-gray-700 pb-2">
|
||||
<span className="text-gray-400 text-sm">Name</span>
|
||||
<span className="text-gray-200 font-mono font-semibold">{uiName}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-400 text-sm">Version</span>
|
||||
<span className="text-gray-200 font-mono font-semibold">{uiVersion}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-400 text-sm">Revision (Commit ID)</span>
|
||||
<span className="bg-[#233241] p-2 rounded-md text-gray-200 font-mono text-sm">{commitID}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-400 text-sm">Build Time</span>
|
||||
<span className="text-gray-200 font-mono text-sm">{commitTimeStamp}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<h3 className="text-sm font-semibold text-gray-400 uppercase tracking-wide">Backend</h3>
|
||||
<div className="bg-gray-800/50 rounded-lg p-4 space-y-3">
|
||||
<div className="flex justify-between items-center border-b border-gray-700 pb-2">
|
||||
<span className="text-gray-400 text-sm"> Name</span>
|
||||
<span className="text-gray-200 font-mono font-semibold">{data?.appname || "N/A"}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center pb-2">
|
||||
<span className="text-gray-400 text-sm">Version</span>
|
||||
<span className="text-gray-200 font-mono font-semibold">{data?.version || "N/A"}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center pb-2">
|
||||
<span className="text-gray-400 text-sm">Revision (Commit ID)</span>
|
||||
<span className="bg-[#233241] p-2 rounded-md text-gray-200 font-mono text-sm">
|
||||
{data?.revision || "N/A"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center pb-2">
|
||||
<span className="text-gray-400 text-sm">Build Time</span>
|
||||
<span className="text-gray-200 font-mono text-sm">{data?.buildtime || "N/A"}</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center">
|
||||
<span className="text-gray-400 text-sm">MAC Address</span>
|
||||
<span className="text-gray-200 font-mono text-sm">{data?.MAC || "N/A"}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ModalComponent>
|
||||
);
|
||||
};
|
||||
|
||||
export default DevModal;
|
||||
@@ -1,11 +1,25 @@
|
||||
import { useState } from "react";
|
||||
import Logo from "/MAV.svg";
|
||||
import DevModal from "./DevModal";
|
||||
import { useGetVersions } from "../hooks/useGetVersions";
|
||||
|
||||
const Footer = () => {
|
||||
const [isDevModalOpen, setDevModalOpen] = useState(false);
|
||||
const { versionsQuery } = useGetVersions();
|
||||
|
||||
const versionData = versionsQuery?.data;
|
||||
|
||||
const handleClick = () => {
|
||||
setDevModalOpen(true);
|
||||
};
|
||||
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} />
|
||||
<img src={Logo} alt="Logo" width={100} height={100} onClick={handleClick} />
|
||||
<p className="text-sm">{new Date().getFullYear()} MAV Systems © All rights reserved.</p>
|
||||
</footer>
|
||||
<DevModal isDevModalOpen={isDevModalOpen} handleClose={() => setDevModalOpen(false)} data={versionData} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ const ModalComponent = ({ isModalOpen, children, close }: ModalComponentProps) =
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
onClick={close}
|
||||
className="bg-gray-700 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded-lg mb-4"
|
||||
className="bg-gray-700 hover:bg-gray-600 text-white font-bold py-2 px-4 rounded-lg mb-4 hover:cursor-pointer"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
export const CAMBASE = import.meta.env.VITE_BASEURL;
|
||||
export const cambase = import.meta.env.VITE_BASEURL;
|
||||
|
||||
export const CAMBASE_WS = import.meta.env.VITE_BASE_WS;
|
||||
export const CAMBASEWS = import.meta.env.VITE_BASE_WS;
|
||||
|
||||
const environment = import.meta.env.MODE;
|
||||
|
||||
export const CAMBASE = environment === "development" ? cambase : window.location.origin;
|
||||
|
||||
export const CAMBASE_WS = environment === "development" ? CAMBASEWS : window.location.origin.replace(/^http/, "ws");
|
||||
|
||||
6
src/vite-env.d.ts
vendored
Normal file
6
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare const __APP_NAME__: string;
|
||||
declare const __APP_VERSION__: string;
|
||||
declare const __GIT_COMMIT__: string;
|
||||
declare const __GIT_TIMESTAMP__: string;
|
||||
@@ -2,13 +2,38 @@ import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import { tanstackRouter } from "@tanstack/router-plugin/vite";
|
||||
import pkg from "./package.json";
|
||||
import { execSync } from "child_process";
|
||||
|
||||
const gitCommitHash = (() => {
|
||||
try {
|
||||
return execSync("git rev-parse --short HEAD").toString().trim();
|
||||
} catch {
|
||||
return "unknown";
|
||||
}
|
||||
})();
|
||||
|
||||
const gitCommitTimeStamp = (() => {
|
||||
try {
|
||||
return execSync("git log -1 --format=%cd --date=iso").toString().trim();
|
||||
} catch {
|
||||
return "unknown";
|
||||
}
|
||||
})();
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
base: "/bayiq",
|
||||
define: {
|
||||
__APP_NAME__: JSON.stringify(pkg.name),
|
||||
__APP_VERSION__: JSON.stringify(pkg.version),
|
||||
__GIT_COMMIT__: JSON.stringify(gitCommitHash),
|
||||
__GIT_TIMESTAMP__: JSON.stringify(gitCommitTimeStamp),
|
||||
},
|
||||
plugins: [
|
||||
tanstackRouter({
|
||||
target: "react",
|
||||
autoCodeSplitting: true,
|
||||
autoCodeSplitting: false,
|
||||
}),
|
||||
react(),
|
||||
tailwindcss(),
|
||||
@@ -21,4 +46,11 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user