diff --git a/README.md b/README.md
index 129953fb9..30b406684 100644
--- a/README.md
+++ b/README.md
@@ -79,9 +79,8 @@ You can see the memory footprint of MongoDB and Redis on the same server (398Mb
If you have any questions, suggestions or comments, you have several options:
-- [Discord channel](https://discord.gg/NAb6H3UTjK)
-- [GitHub Discussions](https://github.com/bluewave-labs/bluewave-uptime/discussions)
-- [Reddit group](https://www.reddit.com/r/CheckmateMonitoring/)
+- [Discord channel](https://discord.gg/NAb6H3UTjK) (preferred)
+- [GitHub Discussions](https://github.com/bluewave-labs/bluewave-uptime/discussions) (we check here from time to time)
Feel free to ask questions or share your ideas - we'd love to hear from you!
diff --git a/client/src/Components/v2/design-elements/Gauge.tsx b/client/src/Components/v2/design-elements/Gauge.tsx
new file mode 100644
index 000000000..34c57487d
--- /dev/null
+++ b/client/src/Components/v2/design-elements/Gauge.tsx
@@ -0,0 +1,106 @@
+import Box from "@mui/material/Box";
+import Typography from "@mui/material/Typography";
+
+import { useTheme } from "@mui/material/styles";
+import { useMemo, useState, useEffect } from "react";
+import { getInfraGaugeColor } from "@/Utils/MonitorUtils";
+
+const MINIMUM_VALUE = 0;
+const MAXIMUM_VALUE = 100;
+
+export const Gauge = ({
+ isLoading = false,
+ progress = 0,
+ radius = 70,
+ strokeWidth = 15,
+ precision = 1,
+ unit = "%",
+}: {
+ isLoading?: boolean;
+ progress?: number;
+ radius?: number;
+ strokeWidth?: number;
+ precision?: number;
+ unit?: string;
+}) => {
+ const theme = useTheme();
+ const progressWithinRange = Math.max(MINIMUM_VALUE, Math.min(progress, MAXIMUM_VALUE));
+
+ // Calculate the length of the stroke for the circle
+ const { circumference, totalSize, strokeLength } = useMemo(
+ () => ({
+ circumference: 2 * Math.PI * radius,
+ totalSize: radius * 2 + strokeWidth * 2,
+ strokeLength: (progress / 100) * (2 * Math.PI * radius),
+ }),
+ [radius, strokeWidth, progress]
+ );
+
+ const [offset, setOffset] = useState(circumference);
+ useEffect(() => {
+ setOffset(circumference);
+ const timer = setTimeout(() => {
+ setOffset(circumference - strokeLength);
+ }, 100);
+
+ return () => clearTimeout(timer);
+ }, [progress, circumference, strokeLength]);
+
+ const fillColor = getInfraGaugeColor(progressWithinRange, theme);
+
+ if (isLoading) {
+ return;
+ }
+
+ return (
+
+
+
+
+ {`${progressWithinRange.toFixed(precision)}${unit}`}
+
+
+ );
+};
diff --git a/client/src/Components/v2/design-elements/Tabs.tsx b/client/src/Components/v2/design-elements/Tabs.tsx
new file mode 100644
index 000000000..149c0bd93
--- /dev/null
+++ b/client/src/Components/v2/design-elements/Tabs.tsx
@@ -0,0 +1,64 @@
+import MuiTabs from "@mui/material/Tabs";
+import type { TabsProps } from "@mui/material/Tabs";
+import { useTheme } from "@mui/material/styles";
+interface CustomTabsProps extends TabsProps {}
+
+export const Tabs = (props: CustomTabsProps) => {
+ const theme = useTheme();
+ return (
+
+ {props.children}
+
+ );
+};
+
+import MuiTab from "@mui/material/Tab";
+import type { TabProps } from "@mui/material/Tab";
+interface CustomTabProps extends TabProps {}
+
+export const Tab = (props: CustomTabProps) => {
+ const theme = useTheme();
+ return (
+
+ );
+};
diff --git a/client/src/Components/v2/design-elements/index.tsx b/client/src/Components/v2/design-elements/index.tsx
index 6ebffc624..33248a345 100644
--- a/client/src/Components/v2/design-elements/index.tsx
+++ b/client/src/Components/v2/design-elements/index.tsx
@@ -15,3 +15,5 @@ export { default as Icon } from "./Icon";
export * from "./Tooltip";
export * from "./StatBox";
export * from "./BaseChart";
+export * from "./Gauge";
+export * from "./Tabs";
diff --git a/client/src/Components/v2/monitors/ControlsFilter.tsx b/client/src/Components/v2/monitors/ControlsFilter.tsx
index d7eecefb5..34eedd190 100644
--- a/client/src/Components/v2/monitors/ControlsFilter.tsx
+++ b/client/src/Components/v2/monitors/ControlsFilter.tsx
@@ -11,6 +11,7 @@ const statuses = ["up", "down"];
const states = ["active", "paused"];
export const ControlsFilter = ({
+ showTypes = true,
selectedTypes,
setSelectedTypes,
selectedStatus,
@@ -19,8 +20,9 @@ export const ControlsFilter = ({
setSelectedState,
onClearFilters,
}: {
- selectedTypes: MonitorType[];
- setSelectedTypes: React.Dispatch>;
+ showTypes?: boolean;
+ selectedTypes?: MonitorType[];
+ setSelectedTypes?: React.Dispatch>;
selectedStatus: string;
setSelectedStatus: React.Dispatch>;
selectedState: string;
@@ -30,27 +32,29 @@ export const ControlsFilter = ({
const theme = useTheme();
const isSmall = useMediaQuery(theme.breakpoints.down("md"));
const isFilterActive =
- selectedTypes.length > 0 || selectedStatus !== "" || selectedState !== "";
+ (selectedTypes?.length ?? 0) > 0 || selectedStatus !== "" || selectedState !== "";
return (
-
+ {showTypes && setSelectedTypes && (
+
+ )}