diff --git a/src/features/dashboard/components/DashboardGrid.tsx b/src/features/dashboard/components/DashboardGrid.tsx index 1d75e5b..1ac2c99 100644 --- a/src/features/dashboard/components/DashboardGrid.tsx +++ b/src/features/dashboard/components/DashboardGrid.tsx @@ -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 (
- void; }; -const SystemOverview = ({ +const SystemHealthCard = ({ startTime, uptime, statuses, @@ -39,4 +39,4 @@ const SystemOverview = ({ ); }; -export default SystemOverview; +export default SystemHealthCard; diff --git a/src/features/dashboard/components/systemHealthModal/SystemHealthModal.tsx b/src/features/dashboard/components/systemHealth/systemHealthModal/SystemHealthModal.tsx similarity index 81% rename from src/features/dashboard/components/systemHealthModal/SystemHealthModal.tsx rename to src/features/dashboard/components/systemHealth/systemHealthModal/SystemHealthModal.tsx index fb43cb2..eeebe0d 100644 --- a/src/features/dashboard/components/systemHealthModal/SystemHealthModal.tsx +++ b/src/features/dashboard/components/systemHealth/systemHealthModal/SystemHealthModal.tsx @@ -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; diff --git a/src/features/dashboard/components/systemStatus/StatusItems/DownloadLogButton.tsx b/src/features/dashboard/components/systemStatus/StatusItems/DownloadLogButton.tsx new file mode 100644 index 0000000..33e51be --- /dev/null +++ b/src/features/dashboard/components/systemStatus/StatusItems/DownloadLogButton.tsx @@ -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 ( + + ); +}; + +export default DownloadLogButton; diff --git a/src/features/dashboard/components/StatusItems/StatusItemCPU.tsx b/src/features/dashboard/components/systemStatus/StatusItems/StatusItemCPU.tsx similarity index 100% rename from src/features/dashboard/components/StatusItems/StatusItemCPU.tsx rename to src/features/dashboard/components/systemStatus/StatusItems/StatusItemCPU.tsx diff --git a/src/features/dashboard/components/StatusItems/StatusItemLocal.tsx b/src/features/dashboard/components/systemStatus/StatusItems/StatusItemLocal.tsx similarity index 100% rename from src/features/dashboard/components/StatusItems/StatusItemLocal.tsx rename to src/features/dashboard/components/systemStatus/StatusItems/StatusItemLocal.tsx diff --git a/src/features/dashboard/components/StatusItems/StatusItemThreads.tsx b/src/features/dashboard/components/systemStatus/StatusItems/StatusItemThreads.tsx similarity index 100% rename from src/features/dashboard/components/StatusItems/StatusItemThreads.tsx rename to src/features/dashboard/components/systemStatus/StatusItems/StatusItemThreads.tsx diff --git a/src/features/dashboard/components/StatusItems/StatusItemUTC.tsx b/src/features/dashboard/components/systemStatus/StatusItems/StatusItemUTC.tsx similarity index 100% rename from src/features/dashboard/components/StatusItems/StatusItemUTC.tsx rename to src/features/dashboard/components/systemStatus/StatusItems/StatusItemUTC.tsx diff --git a/src/features/dashboard/components/systemStatus/StatusItems/StatusReads.tsx b/src/features/dashboard/components/systemStatus/StatusItems/StatusReads.tsx new file mode 100644 index 0000000..287c18c --- /dev/null +++ b/src/features/dashboard/components/systemStatus/StatusItems/StatusReads.tsx @@ -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

Loading reads…

; + } + return ( +
+
+ + + + +

Reads

+
+ +
+ Pending: {totalPending} | Active:{" "} + {totalActive} | Lost: {totalLost} +
+ Sent / Received: {totalSent} |{" "} + {totalReceived} +
+
+ ); +}; + +export default StatusReads; diff --git a/src/features/dashboard/components/SystemStatusCard.tsx b/src/features/dashboard/components/systemStatus/SystemStatusCard.tsx similarity index 53% rename from src/features/dashboard/components/SystemStatusCard.tsx rename to src/features/dashboard/components/systemStatus/SystemStatusCard.tsx index e3307b4..3017367 100644 --- a/src/features/dashboard/components/SystemStatusCard.tsx +++ b/src/features/dashboard/components/systemStatus/SystemStatusCard.tsx @@ -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 ( @@ -16,8 +26,8 @@ const SystemStatusCard = () => {
- - + +
) : ( Loading system status… diff --git a/src/features/dashboard/hooks/useDownloadLogFiles.ts b/src/features/dashboard/hooks/useDownloadLogFiles.ts new file mode 100644 index 0000000..75bdc15 --- /dev/null +++ b/src/features/dashboard/hooks/useDownloadLogFiles.ts @@ -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 }; +}; diff --git a/src/features/dashboard/hooks/useGetStore.ts b/src/features/dashboard/hooks/useGetStore.ts new file mode 100644 index 0000000..887176f --- /dev/null +++ b/src/features/dashboard/hooks/useGetStore.ts @@ -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 }; +}; diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 301f601..3f2d9cb 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -6,9 +6,5 @@ export const Route = createFileRoute("/")({ }); function HomePage() { - return ( -
- -
- ); + return ; }