enhancement/dashboardFeedback #11
@@ -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 { 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 (
|
||||
<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 };
|
||||
};
|
||||
Reference in New Issue
Block a user