From f7964d4fc088a5f41196483fddc1d219340bc979 Mon Sep 17 00:00:00 2001 From: Toba Ojo Date: Wed, 3 Dec 2025 10:46:36 +0000 Subject: [PATCH] - added download button - added reads for number plate sightings --- .../StatusItems/DownloadLogButton.tsx | 50 +++++++++++++++++++ .../systemStatus/StatusItems/StatusReads.tsx | 48 ++++++++++++++++++ .../systemStatus/SystemStatusCard.tsx | 18 +++++-- .../dashboard/hooks/useDownloadLogFiles.ts | 20 ++++++++ src/features/dashboard/hooks/useGetStore.ts | 19 +++++++ 5 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 src/features/dashboard/components/systemStatus/StatusItems/DownloadLogButton.tsx create mode 100644 src/features/dashboard/components/systemStatus/StatusItems/StatusReads.tsx create mode 100644 src/features/dashboard/hooks/useDownloadLogFiles.ts create mode 100644 src/features/dashboard/hooks/useGetStore.ts 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/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/systemStatus/SystemStatusCard.tsx b/src/features/dashboard/components/systemStatus/SystemStatusCard.tsx index db9322d..3017367 100644 --- a/src/features/dashboard/components/systemStatus/SystemStatusCard.tsx +++ b/src/features/dashboard/components/systemStatus/SystemStatusCard.tsx @@ -1,13 +1,23 @@ +import { useEffect } from "react"; import { useInfoSocket } from "../../../../app/context/WebSocketContext"; import Card from "../../../../ui/Card"; import CardHeader from "../../../../ui/CardHeader"; -import StatusItemCPU from "./StatusItems/StatusItemCPU"; +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 }; +};