Merge pull request 'enhancement/dashboardFeedback' (#11) from enhancement/dashboardFeedback into develop
Reviewed-on: #11
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import type { SystemHealthStatus } from "../../../types/types";
|
||||
import { useGetSystemHealth } from "../hooks/useGetSystemHealth";
|
||||
import CameraStatus from "./CameraStatus";
|
||||
import SystemOverview from "./SystemOverview";
|
||||
import SystemStatusCard from "./SystemStatusCard";
|
||||
import CameraStatus from "./cameraStatus/CameraStatus";
|
||||
import SystemHealthCard from "./systemHealth/SystemHealthCard";
|
||||
import SystemStatusCard from "./systemStatus/SystemStatusCard";
|
||||
|
||||
const DashboardGrid = () => {
|
||||
const { query } = useGetSystemHealth();
|
||||
@@ -37,7 +37,7 @@ const DashboardGrid = () => {
|
||||
return (
|
||||
<div className="grid grid-cols-1 md:grid-rows-2 md:grid-cols-2">
|
||||
<SystemStatusCard />
|
||||
<SystemOverview
|
||||
<SystemHealthCard
|
||||
startTime={startTime}
|
||||
uptime={uptime}
|
||||
statuses={statuses}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { SystemHealthStatus } from "../../../types/types";
|
||||
import Card from "../../../ui/Card";
|
||||
import StatusIndicators from "../../../ui/StatusIndicators";
|
||||
import { capitalize } from "../../../utils/utils";
|
||||
import type { SystemHealthStatus } from "../../../../types/types";
|
||||
import Card from "../../../../ui/Card";
|
||||
import StatusIndicators from "../../../../ui/StatusIndicators";
|
||||
import { capitalize } from "../../../../utils/utils";
|
||||
import CameraStatusGridItem from "./CameraStatusGridItem";
|
||||
|
||||
type CameraStatusProps = {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import type { SystemHealthStatus } from "../../../types/types";
|
||||
import { capitalize } from "../../../utils/utils";
|
||||
import SystemHealthModal from "./systemHealthModal/SystemHealthModal";
|
||||
import type { SystemHealthStatus } from "../../../../types/types";
|
||||
import { capitalize } from "../../../../utils/utils";
|
||||
import SystemHealthModal from "../systemHealth/systemHealthModal/SystemHealthModal";
|
||||
|
||||
type CameraStatusGridItemProps = {
|
||||
title: string;
|
||||
@@ -2,7 +2,7 @@ import { useState } from "react";
|
||||
import type { SystemHealthStatus } from "../../../../types/types";
|
||||
import StatusIndicators from "../../../../ui/StatusIndicators";
|
||||
import { capitalize } from "../../../../utils/utils";
|
||||
import SystemHealthModal from "../systemHealthModal/SystemHealthModal";
|
||||
import SystemHealthModal from "../systemHealth/systemHealthModal/SystemHealthModal";
|
||||
|
||||
type StatusGridItemProps = {
|
||||
title: string;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { SystemHealthStatus } from "../../../types/types";
|
||||
import StatusGridItem from "./statusGridItem/StatusGridItem";
|
||||
import type { SystemHealthStatus } from "../../../../types/types";
|
||||
import StatusGridItem from "../statusGridItem/StatusGridItem";
|
||||
|
||||
type SystemHealthProps = {
|
||||
startTime: string;
|
||||
@@ -1,9 +1,9 @@
|
||||
import { faArrowsRotate } from "@fortawesome/free-solid-svg-icons";
|
||||
import Card from "../../../ui/Card";
|
||||
import CardHeader from "../../../ui/CardHeader";
|
||||
import Card from "../../../../ui/Card";
|
||||
import CardHeader from "../../../../ui/CardHeader";
|
||||
|
||||
import SystemHealth from "./SystemHealth";
|
||||
import type { SystemHealthStatus } from "../../../types/types";
|
||||
import type { SystemHealthStatus } from "../../../../types/types";
|
||||
|
||||
type SystemOverviewProps = {
|
||||
startTime: string;
|
||||
@@ -15,7 +15,7 @@ type SystemOverviewProps = {
|
||||
refetch: () => void;
|
||||
};
|
||||
|
||||
const SystemOverview = ({
|
||||
const SystemHealthCard = ({
|
||||
startTime,
|
||||
uptime,
|
||||
statuses,
|
||||
@@ -39,4 +39,4 @@ const SystemOverview = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default SystemOverview;
|
||||
export default SystemHealthCard;
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { SystemHealthStatus } from "../../../../types/types";
|
||||
import Badge from "../../../../ui/Badge";
|
||||
import ModalComponent from "../../../../ui/ModalComponent";
|
||||
import StatusIndicators from "../../../../ui/StatusIndicators";
|
||||
import { capitalize } from "../../../../utils/utils";
|
||||
import type { SystemHealthStatus } from "../../../../../types/types";
|
||||
import Badge from "../../../../../ui/Badge";
|
||||
import ModalComponent from "../../../../../ui/ModalComponent";
|
||||
import StatusIndicators from "../../../../../ui/StatusIndicators";
|
||||
import { capitalize } from "../../../../../utils/utils";
|
||||
|
||||
type SystemHealthModalProps = {
|
||||
isSystemHealthModalOpen: boolean;
|
||||
@@ -0,0 +1,50 @@
|
||||
import { faDownload } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useDownloadLogFiles } from "../../../hooks/useDownloadLogFiles";
|
||||
import { toast } from "sonner";
|
||||
|
||||
const DownloadLogButton = () => {
|
||||
const { downloadLogFilesQuery } = useDownloadLogFiles();
|
||||
const isLoading = downloadLogFilesQuery?.isFetching;
|
||||
|
||||
const handleDownloadClick = async () => {
|
||||
try {
|
||||
const blob = await downloadLogFilesQuery?.refetch().then((res) => res.data);
|
||||
if (!blob) {
|
||||
throw new Error("No log file data received");
|
||||
}
|
||||
const url = window.URL.createObjectURL(new Blob([blob]));
|
||||
const link = document.createElement("a");
|
||||
if (!link) {
|
||||
throw new Error("Failed to create download link");
|
||||
} else {
|
||||
link.href = url;
|
||||
link.setAttribute("download", "FlexiAI-0.log");
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.parentNode?.removeChild(link);
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
||||
toast.error(errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<button
|
||||
className="p-3 border border-gray-700 rounded-lg hover:bg-[#233241] hover:cursor-pointer"
|
||||
onClick={handleDownloadClick}
|
||||
>
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<span className="font-bold text-xl bg-slate-700 p-1 px-2 rounded-md">
|
||||
<FontAwesomeIcon icon={faDownload} />
|
||||
</span>
|
||||
<p className="text-lg">{"Download Log Files"}</p>
|
||||
</div>
|
||||
<p className="text-slate-400 italic text-start">{isLoading ? "Downloading..." : "View logs"}</p>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default DownloadLogButton;
|
||||
@@ -0,0 +1,48 @@
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faChartSimple } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
type StatusReadsProps = {
|
||||
reads: {
|
||||
totalPending: number;
|
||||
totalActive: number;
|
||||
totalSent: number;
|
||||
totalReceived: number;
|
||||
totalLost: number;
|
||||
sanityCheck: boolean;
|
||||
sanityCheckFormula: string;
|
||||
};
|
||||
isReadsLoading?: boolean;
|
||||
};
|
||||
|
||||
const StatusReads = ({ reads, isReadsLoading }: StatusReadsProps) => {
|
||||
const totalPending = reads?.totalPending ?? 0;
|
||||
const totalActive = reads?.totalActive ?? 0;
|
||||
const totalSent = reads?.totalSent ?? 0;
|
||||
const totalLost = reads?.totalLost ?? 0;
|
||||
const totalReceived = reads?.totalReceived ?? 0;
|
||||
|
||||
if (isReadsLoading) {
|
||||
return <p className="text-slate-400">Loading reads…</p>;
|
||||
}
|
||||
return (
|
||||
<div className="p-3 border border-gray-700 rounded-lg hover:bg-[#233241]">
|
||||
<div className="flex flex-row gap-2 items-center">
|
||||
<span className="font-bold text-xl bg-slate-700 p-1 px-2 rounded-md">
|
||||
<FontAwesomeIcon icon={faChartSimple} />
|
||||
</span>
|
||||
|
||||
<p className="text-lg">Reads</p>
|
||||
</div>
|
||||
|
||||
<div className="text-slate-400 mt-1">
|
||||
Pending: <span className="text-yellow-500">{totalPending}</span> | Active:{" "}
|
||||
<span className="text-cyan-500">{totalActive}</span> | Lost: <span className="text-red-500">{totalLost}</span>
|
||||
<br />
|
||||
Sent / Received: <span className="text-blue-500">{totalSent}</span> |{" "}
|
||||
<span className="text-green-500">{totalReceived}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatusReads;
|
||||
@@ -1,13 +1,23 @@
|
||||
import { useInfoSocket } from "../../../app/context/WebSocketContext";
|
||||
import Card from "../../../ui/Card";
|
||||
import CardHeader from "../../../ui/CardHeader";
|
||||
import StatusItemCPU from "./StatusItems/StatusItemCPU";
|
||||
import { useEffect } from "react";
|
||||
import { useInfoSocket } from "../../../../app/context/WebSocketContext";
|
||||
import Card from "../../../../ui/Card";
|
||||
import CardHeader from "../../../../ui/CardHeader";
|
||||
import DownloadLogButton from "./StatusItems/DownloadLogButton";
|
||||
import StatusItemLocal from "./StatusItems/StatusItemLocal";
|
||||
import StatusItemThreads from "./StatusItems/StatusItemThreads";
|
||||
import StatusItemUTC from "./StatusItems/StatusItemUTC";
|
||||
import StatusReads from "./StatusItems/StatusReads";
|
||||
import { useGetStore } from "../../hooks/useGetStore";
|
||||
|
||||
const SystemStatusCard = () => {
|
||||
const { data: stats } = useInfoSocket();
|
||||
const { storeQuery } = useGetStore();
|
||||
|
||||
const reads = storeQuery?.data;
|
||||
const isReadsLoading = storeQuery.isFetching;
|
||||
|
||||
useEffect(() => {
|
||||
storeQuery.refetch();
|
||||
}, [reads]);
|
||||
|
||||
return (
|
||||
<Card className="p-4">
|
||||
@@ -16,8 +26,8 @@ const SystemStatusCard = () => {
|
||||
<div className="grid grid-cols-2 grid-rows-2 gap-4 col-span-2">
|
||||
<StatusItemUTC statusInfoItem={stats["system-clock-utc"]} description={"UTC Time"} />
|
||||
<StatusItemLocal statusInfoItem={stats["system-clock-local"]} description={"Local Time"} />
|
||||
<StatusItemCPU statusInfoItem={stats["memory-cpu-status"]} description={"CPU"} />
|
||||
<StatusItemThreads statusInfoItem={stats["thread-count"]} description={"Threads"} />
|
||||
<DownloadLogButton />
|
||||
<StatusReads reads={reads} isReadsLoading={isReadsLoading} />
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-slate-500">Loading system status…</span>
|
||||
20
src/features/dashboard/hooks/useDownloadLogFiles.ts
Normal file
20
src/features/dashboard/hooks/useDownloadLogFiles.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { CAMBASE } from "../../../utils/config";
|
||||
|
||||
const getDownloadLogFiles = async () => {
|
||||
const response = await fetch(`${CAMBASE}/LogView/download?filename=FlexiAI-0.log`);
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to download log files");
|
||||
}
|
||||
return response.blob();
|
||||
};
|
||||
|
||||
export const useDownloadLogFiles = () => {
|
||||
const downloadLogFilesQuery = useQuery({
|
||||
queryKey: ["downloadLogFiles"],
|
||||
queryFn: getDownloadLogFiles,
|
||||
enabled: false,
|
||||
});
|
||||
|
||||
return { downloadLogFilesQuery };
|
||||
};
|
||||
19
src/features/dashboard/hooks/useGetStore.ts
Normal file
19
src/features/dashboard/hooks/useGetStore.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { CAMBASE } from "../../../utils/config";
|
||||
|
||||
const getStoreData = async () => {
|
||||
const response = await fetch(`${CAMBASE}/Store0/diagnostics-json`);
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
return response.json();
|
||||
};
|
||||
|
||||
export const useGetStore = () => {
|
||||
const storeQuery = useQuery({
|
||||
queryKey: ["storeData"],
|
||||
queryFn: getStoreData,
|
||||
// refetchInterval: 10 * 60 * 1000,
|
||||
});
|
||||
return { storeQuery };
|
||||
};
|
||||
@@ -6,9 +6,5 @@ export const Route = createFileRoute("/")({
|
||||
});
|
||||
|
||||
function HomePage() {
|
||||
return (
|
||||
<div>
|
||||
<DashboardGrid />
|
||||
</div>
|
||||
);
|
||||
return <DashboardGrid />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user