diff --git a/index.html b/index.html
index fd0bb43..aec7947 100644
--- a/index.html
+++ b/index.html
@@ -1,10 +1,10 @@
-
+
+
);
};
diff --git a/src/features/dashboard/components/StatusItems/StatusItemCPU.tsx b/src/features/dashboard/components/StatusItems/StatusItemCPU.tsx
new file mode 100644
index 0000000..3ae97f6
--- /dev/null
+++ b/src/features/dashboard/components/StatusItems/StatusItemCPU.tsx
@@ -0,0 +1,23 @@
+import { faHardDrive } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+type StatusItemProps = {
+ statusInfoItem: string;
+ description: string;
+};
+
+const StatusItemCPU = ({ statusInfoItem, description }: StatusItemProps) => {
+ return (
+
+
+
+
+
+
{statusInfoItem}
+
+
{description}
+
+ );
+};
+
+export default StatusItemCPU;
diff --git a/src/features/dashboard/components/StatusItems/StatusItemLocal.tsx b/src/features/dashboard/components/StatusItems/StatusItemLocal.tsx
new file mode 100644
index 0000000..f99cf40
--- /dev/null
+++ b/src/features/dashboard/components/StatusItems/StatusItemLocal.tsx
@@ -0,0 +1,31 @@
+import { faClock } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+type StatusItemProps = {
+ statusInfoItem: string;
+ description: string;
+};
+
+const StatusItemLocal = ({ statusInfoItem, description }: StatusItemProps) => {
+ const humanReadable = (string: string) => {
+ if (description.toLowerCase().includes("local")) {
+ const text = string.slice(0, statusInfoItem.length - 5);
+ return text;
+ }
+ };
+
+ return (
+
+
+
+
+
+
{description.toLowerCase().includes("local") && humanReadable(statusInfoItem)}
+
+
+
{description}
+
+ );
+};
+
+export default StatusItemLocal;
diff --git a/src/features/dashboard/components/StatusItems/StatusItemThreads.tsx b/src/features/dashboard/components/StatusItems/StatusItemThreads.tsx
new file mode 100644
index 0000000..69ce8c8
--- /dev/null
+++ b/src/features/dashboard/components/StatusItems/StatusItemThreads.tsx
@@ -0,0 +1,24 @@
+import { faMicrochip } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+type StatusItemProps = {
+ statusInfoItem: string;
+ description: string;
+};
+
+const StatusItemThreads = ({ statusInfoItem, description }: StatusItemProps) => {
+ return (
+
+
+
+
+
+
{statusInfoItem}
+
+
+
{description}
+
+ );
+};
+
+export default StatusItemThreads;
diff --git a/src/features/dashboard/components/StatusItems/StatusItemUTC.tsx b/src/features/dashboard/components/StatusItems/StatusItemUTC.tsx
new file mode 100644
index 0000000..b4adef8
--- /dev/null
+++ b/src/features/dashboard/components/StatusItems/StatusItemUTC.tsx
@@ -0,0 +1,32 @@
+import { faClock } from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+type StatusItemProps = {
+ statusInfoItem: string;
+ description: string;
+};
+
+const StatusItemUTC = ({ statusInfoItem, description }: StatusItemProps) => {
+ const humanReadable = (string: string) => {
+ if (description.includes("UTC")) {
+ const text = string.slice(0, statusInfoItem.length - 3);
+ return text;
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
{description.toLowerCase().includes("utc") && humanReadable(statusInfoItem)}
+
+
+
{description}
+
+ );
+};
+
+export default StatusItemUTC;
diff --git a/src/features/dashboard/components/SystemHealth.tsx b/src/features/dashboard/components/SystemHealth.tsx
new file mode 100644
index 0000000..6f1e12b
--- /dev/null
+++ b/src/features/dashboard/components/SystemHealth.tsx
@@ -0,0 +1,38 @@
+import type { SystemHealthStatus } from "../../../types/types";
+import Badge from "../../../ui/Badge";
+
+type SystemHealthProps = {
+ startTime: string;
+ uptime: string;
+ statuses: SystemHealthStatus[];
+ isLoading: boolean;
+};
+
+const SystemHealth = ({ startTime, uptime, statuses, isLoading }: SystemHealthProps) => {
+ if (isLoading) {
+ return
Loading system health…;
+ }
+
+ return (
+
+
+
+
Start Time
{startTime}
+
+
+
Up Time
{uptime}
+
+
+
+
+ {statuses?.map((status: SystemHealthStatus) => (
+
+ {status.id}
+
+ ))}
+
+
+ );
+};
+
+export default SystemHealth;
diff --git a/src/features/dashboard/components/SystemOverview.tsx b/src/features/dashboard/components/SystemOverview.tsx
new file mode 100644
index 0000000..2a6559d
--- /dev/null
+++ b/src/features/dashboard/components/SystemOverview.tsx
@@ -0,0 +1,22 @@
+import { faArrowsRotate } from "@fortawesome/free-solid-svg-icons";
+import Card from "../../../ui/Card";
+import CardHeader from "../../../ui/CardHeader";
+import { useGetSystemHealth } from "../hooks/useGetSystemHealth";
+import SystemHealth from "./SystemHealth";
+
+const SystemOverview = () => {
+ const { query } = useGetSystemHealth();
+
+ const startTime = query?.data?.StartTimeHumane;
+ const uptime = query?.data?.UptimeHumane;
+ const statuses = query?.data?.Status;
+ const isLoading = query?.isLoading;
+ return (
+
+
+
+
+ );
+};
+
+export default SystemOverview;
diff --git a/src/features/dashboard/components/SystemStatusCard.tsx b/src/features/dashboard/components/SystemStatusCard.tsx
index e7d6c09..e3307b4 100644
--- a/src/features/dashboard/components/SystemStatusCard.tsx
+++ b/src/features/dashboard/components/SystemStatusCard.tsx
@@ -1,20 +1,24 @@
import { useInfoSocket } from "../../../app/context/WebSocketContext";
import Card from "../../../ui/Card";
import CardHeader from "../../../ui/CardHeader";
+import StatusItemCPU from "./StatusItems/StatusItemCPU";
+import StatusItemLocal from "./StatusItems/StatusItemLocal";
+import StatusItemThreads from "./StatusItems/StatusItemThreads";
+import StatusItemUTC from "./StatusItems/StatusItemUTC";
const SystemStatusCard = () => {
const { data: stats } = useInfoSocket();
return (
-
-
+
+
{stats ? (
- <>
- UTC: {stats["system-clock-utc"]}
- Local: {stats["system-clock-local"]}
- CPU: {stats["memory-cpu-status"]}
- Threads: {stats["thread-count"]}
- >
+
+
+
+
+
+
) : (
Loading system status…
)}
diff --git a/src/features/dashboard/hooks/useGetSystemHealth.ts b/src/features/dashboard/hooks/useGetSystemHealth.ts
new file mode 100644
index 0000000..d9599d6
--- /dev/null
+++ b/src/features/dashboard/hooks/useGetSystemHealth.ts
@@ -0,0 +1,15 @@
+import { useQuery } from "@tanstack/react-query";
+
+const fetchData = async () => {
+ const response = await fetch(`http://100.115.148.59/api/system-health`);
+ if (!response.ok) throw new Error("Cannot get System overview");
+ return response.json();
+};
+
+export const useGetSystemHealth = () => {
+ const query = useQuery({
+ queryKey: ["fetchSystemData"],
+ queryFn: fetchData,
+ });
+ return { query };
+};
diff --git a/src/routes/baywatch.tsx b/src/routes/baywatch.tsx
index f7401ab..0cb9718 100644
--- a/src/routes/baywatch.tsx
+++ b/src/routes/baywatch.tsx
@@ -9,7 +9,6 @@ export const Route = createFileRoute("/baywatch")({
function RouteComponent() {
return (
-
Cameras
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
index 8cd7e69..301f601 100644
--- a/src/routes/index.tsx
+++ b/src/routes/index.tsx
@@ -8,7 +8,6 @@ export const Route = createFileRoute("/")({
function HomePage() {
return (
-
Dashboard
);
diff --git a/src/types/types.ts b/src/types/types.ts
index e838896..b130f22 100644
--- a/src/types/types.ts
+++ b/src/types/types.ts
@@ -11,7 +11,13 @@ export type InfoBarData = {
"thread-count": string;
};
+export type StatusIndicator = "neutral-quaternary" | "dark" | "info" | "success" | "warning" | "danger";
export type Region = {
name: string;
brushColour: string;
};
+
+export type SystemHealthStatus = {
+ id: string;
+ tags: string[];
+};
diff --git a/src/ui/Badge.tsx b/src/ui/Badge.tsx
new file mode 100644
index 0000000..7e088af
--- /dev/null
+++ b/src/ui/Badge.tsx
@@ -0,0 +1,24 @@
+import type { Icon, IconDefinition } from "@fortawesome/fontawesome-svg-core";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { capitalize } from "../utils/utils";
+
+type BadgeProps = {
+ icon?: Icon | IconDefinition;
+ text: string;
+};
+
+const Badge = ({ icon, text }: BadgeProps) => {
+ const lowerCaseWord = text.toLowerCase();
+ return (
+
+ {icon && }
+ {capitalize(lowerCaseWord)}
+
+ );
+};
+
+export default Badge;
diff --git a/src/ui/CardHeader.tsx b/src/ui/CardHeader.tsx
index 027ecae..3d6c3b2 100644
--- a/src/ui/CardHeader.tsx
+++ b/src/ui/CardHeader.tsx
@@ -1,18 +1,27 @@
import clsx from "clsx";
+import StatusIndicators from "./StatusIndicators";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import type { IconProp } from "@fortawesome/fontawesome-svg-core";
type CameraOverviewHeaderProps = {
title?: string;
+ status?: string;
+ refetch?: () => void;
+ icon?: IconProp;
};
-const CardHeader = ({ title }: CameraOverviewHeaderProps) => {
+const CardHeader = ({ title, status, icon, refetch }: CameraOverviewHeaderProps) => {
return (
-
- {/* {icon &&
} */}
-
{title}
+
+
+ {status && }
+ {title}
+
+ {icon && }
);
diff --git a/src/ui/StatusIndicators.tsx b/src/ui/StatusIndicators.tsx
new file mode 100644
index 0000000..5d4d529
--- /dev/null
+++ b/src/ui/StatusIndicators.tsx
@@ -0,0 +1,9 @@
+import clsx from "clsx";
+
+type StatusIndicatorsProps = { status: string };
+
+const StatusIndicators = ({ status }: StatusIndicatorsProps) => {
+ return
;
+};
+
+export default StatusIndicators;
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
new file mode 100644
index 0000000..23c5527
--- /dev/null
+++ b/src/utils/utils.ts
@@ -0,0 +1,3 @@
+export function capitalize(s?: string) {
+ return s ? s.charAt(0).toUpperCase() + s.slice(1) : "";
+}