diff --git a/client/src/Pages/Settings/SettingsAbout.jsx b/client/src/Pages/Settings/SettingsAbout.jsx
deleted file mode 100644
index 7b0a4ad2c..000000000
--- a/client/src/Pages/Settings/SettingsAbout.jsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import Box from "@mui/material/Box";
-import Typography from "@mui/material/Typography";
-import ConfigBox from "@/Components/v1/ConfigBox/index.jsx";
-// Utils
-import { useTheme } from "@emotion/react";
-import { useTranslation } from "react-i18next";
-import Link from "@/Components/v1/Link/index.jsx";
-
-const SettingsAbout = () => {
- const theme = useTheme();
- const { t } = useTranslation();
- return (
-
-
-
- {t("pages.settings.aboutSettings.title")}
-
-
-
-
- {t("common.appName")} {__APP_VERSION__}
-
-
- {t("pages.settings.aboutSettings.labelDevelopedBy")}
-
-
-
-
- );
-};
-
-export default SettingsAbout;
diff --git a/client/src/Pages/Settings/SettingsDemoMonitors.jsx b/client/src/Pages/Settings/SettingsDemoMonitors.jsx
deleted file mode 100644
index d2e7baea0..000000000
--- a/client/src/Pages/Settings/SettingsDemoMonitors.jsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import Box from "@mui/material/Box";
-import Typography from "@mui/material/Typography";
-import Button from "@mui/material/Button";
-import ConfigBox from "@/Components/v1/ConfigBox/index.jsx";
-// Utils
-import { useTheme } from "@emotion/react";
-import { PropTypes } from "prop-types";
-import { useTranslation } from "react-i18next";
-import Dialog from "@/Components/v1/Dialog/index.jsx";
-import { useState } from "react";
-import { useDelete, usePost } from "@/Hooks/UseApi";
-
-const SettingsDemoMonitors = ({ isAdmin, HEADER_SX, isLoading }) => {
- const { t } = useTranslation();
- const theme = useTheme();
- // Local state
- const [isOpen, setIsOpen] = useState(false);
-
- if (!isAdmin) {
- return null;
- }
- const { post: postDemoMonitors } = usePost();
- const { deleteFn: deleteAllMonitorsFn } = useDelete();
- return (
- <>
-
-
-
- {t("pages.settings.demoMonitorsSettings.title")}
-
-
- {t("pages.settings.demoMonitorsSettings.description")}
-
-
-
-
-
-
-
-
-
- {t("pages.settings.systemResetSettings.title")}
-
-
- {t("pages.settings.systemResetSettings.description")}
-
-
-
-
-
-
- >
- );
-};
-
-SettingsDemoMonitors.propTypes = {
- isAdmin: PropTypes.bool,
- HEADER_SX: PropTypes.object,
-};
-
-export default SettingsDemoMonitors;
diff --git a/client/src/Pages/Settings/SettingsEmail.jsx b/client/src/Pages/Settings/SettingsEmail.jsx
deleted file mode 100644
index 180b7f2d9..000000000
--- a/client/src/Pages/Settings/SettingsEmail.jsx
+++ /dev/null
@@ -1,369 +0,0 @@
-import Box from "@mui/material/Box";
-import Typography from "@mui/material/Typography";
-import ConfigBox from "@/Components/v1/ConfigBox/index.jsx";
-import TextInput from "@/Components/v1/Inputs/TextInput/index.jsx";
-import Button from "@mui/material/Button";
-import Stack from "@mui/material/Stack";
-import { Switch } from "@mui/material";
-import TextLink from "@/Components/v1/TextLink/index.jsx";
-// Utils
-import { useTheme } from "@emotion/react";
-import { PropTypes } from "prop-types";
-import { useState } from "react";
-import { useTranslation } from "react-i18next";
-import { PasswordEndAdornment } from "@/Components/v1/Inputs/TextInput/Adornments/index.jsx";
-import { usePost } from "@/Hooks/UseApi";
-import { useSelector } from "react-redux";
-import { createToast } from "@/Utils/toastUtils.jsx";
-import { Controller } from "react-hook-form";
-
-const SettingsEmail = ({
- isAdmin,
- HEADER_SX,
- isEmailPasswordSet,
- emailPasswordHasBeenReset,
- setEmailPasswordHasBeenReset,
- control,
- defaults,
- formValues,
- setValue,
-}) => {
- // Setup
- const { t } = useTranslation();
- const theme = useTheme();
-
- // Destructure settings with default values
- const {
- systemEmailHost = "",
- systemEmailPort = "",
- systemEmailSecure = false,
- systemEmailPool = false,
- systemEmailUser = "",
- systemEmailAddress = "",
- systemEmailPassword = "",
- systemEmailTLSServername = "",
- systemEmailConnectionHost = "localhost",
- systemEmailIgnoreTLS = false,
- systemEmailRequireTLS = false,
- systemEmailRejectUnauthorized = true,
- } = formValues || {};
-
- // Network
- const { post: sendTestEmailFn, loading: isSending } = usePost();
- const user = useSelector((state) => state.auth.user);
-
- // Handlers
-
- /**
- * Handle sending test email with current form values
- */
- const handleSendTestEmail = () => {
- // Basic validation
- if (
- !systemEmailHost ||
- !systemEmailPort ||
- !systemEmailAddress ||
- !systemEmailPassword
- ) {
- createToast({
- body: t("pages.settings.emailSettings.toastEmailRequiredFieldsError"),
- variant: "error",
- });
- return;
- }
-
- // Send test email - only include optional fields if they have values
- sendTestEmailFn("/settings/test-email", {
- to: user.email,
- systemEmailHost,
- systemEmailPort,
- systemEmailAddress,
- systemEmailPassword,
- systemEmailSecure,
- systemEmailPool,
- systemEmailIgnoreTLS,
- systemEmailRequireTLS,
- systemEmailRejectUnauthorized,
- ...(systemEmailUser && { systemEmailUser }),
- ...(systemEmailTLSServername && { systemEmailTLSServername }),
- ...(systemEmailConnectionHost && { systemEmailConnectionHost }),
- });
- };
-
- if (!isAdmin) {
- return null;
- }
-
- return (
-
-
-
- {t("pages.settings.emailSettings.title")}
-
-
- {t("pages.settings.emailSettings.description")}
-
-
-
-
-
- (
-
- )}
- />
-
-
- (
-
- )}
- />
-
-
- (
-
- )}
- />
-
-
- (
-
- )}
- />
-
- {(isEmailPasswordSet === false || emailPasswordHasBeenReset === true) && (
-
- (
- }
- error={!!fieldState.error}
- helperText={fieldState.error?.message}
- />
- )}
- />
-
- )}
-
- {isEmailPasswordSet === true && emailPasswordHasBeenReset === false && (
-
- {t("pages.settings.emailSettings.labelPasswordSet")}
-
-
- )}
-
- (
-
- )}
- />
-
-
- (
-
- )}
- />
-
-
- {[
- [
- "pages.settings.emailSettings.labelSecure",
- "systemEmailSecure",
- systemEmailSecure,
- ],
- [
- "pages.settings.emailSettings.labelPool",
- "systemEmailPool",
- systemEmailPool,
- ],
- [
- "pages.settings.emailSettings.labelIgnoreTLS",
- "systemEmailIgnoreTLS",
- systemEmailIgnoreTLS,
- ],
- [
- "pages.settings.emailSettings.labelRequireTLS",
- "systemEmailRequireTLS",
- systemEmailRequireTLS,
- ],
- [
- "pages.settings.emailSettings.labelRejectUnauthorized",
- "systemEmailRejectUnauthorized",
- systemEmailRejectUnauthorized,
- ],
- ].map(([labelKey, name, value]) => (
- (
-
- {t(labelKey)}
-
-
-
- )}
- />
- ))}
-
-
-
-
- {JSON.stringify(
- {
- host: systemEmailHost,
- port: systemEmailPort,
- secure: systemEmailSecure,
- auth: {
- user: systemEmailUser || systemEmailAddress,
- pass: "",
- },
- name: systemEmailConnectionHost || "localhost",
- pool: systemEmailPool,
- tls: {
- rejectUnauthorized: systemEmailRejectUnauthorized,
- ignoreTLS: systemEmailIgnoreTLS,
- requireTLS: systemEmailRequireTLS,
- servername: systemEmailTLSServername,
- },
- },
- null,
- 2
- )}
-
-
-
-
-
- {systemEmailHost &&
- systemEmailPort &&
- systemEmailAddress &&
- systemEmailPassword && (
-
- )}
-
-
-
- );
-};
-
-SettingsEmail.propTypes = {
- isAdmin: PropTypes.bool,
- HEADER_SX: PropTypes.object,
- isPasswordSet: PropTypes.bool,
-};
-
-export default SettingsEmail;
diff --git a/client/src/Pages/Settings/SettingsExport.jsx b/client/src/Pages/Settings/SettingsExport.jsx
deleted file mode 100644
index b289592d1..000000000
--- a/client/src/Pages/Settings/SettingsExport.jsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import Box from "@mui/material/Box";
-import Typography from "@mui/material/Typography";
-import Button from "@mui/material/Button";
-import ConfigBox from "@/Components/v1/ConfigBox/index.jsx";
-// Utils
-import { useTheme } from "@emotion/react";
-import { PropTypes } from "prop-types";
-import { useTranslation } from "react-i18next";
-import Dialog from "@/Components/v1/Dialog/index.jsx";
-import { useState } from "react";
-import { useLazyGet } from "@/Hooks/UseApi";
-
-const SettingsDemoMonitors = ({ isAdmin, HEADER_SX, isLoading }) => {
- const { t } = useTranslation();
- const theme = useTheme();
- // Local state
- const [isOpen, setIsOpen] = useState(false);
-
- if (!isAdmin) {
- return null;
- }
- const { get: fetchJson } = useLazyGet();
-
- const handleExport = async () => {
- const res = await fetchJson("/monitors/export/json");
- const json = res?.data ?? [];
- if (!json || json.length === 0) {
- return;
- }
-
- const blob = new Blob([JSON.stringify(json, null, 2)], {
- type: "application/json",
- });
- const url = URL.createObjectURL(blob);
-
- const link = document.createElement("a");
- link.href = url;
- link.download = "monitors.json";
- document.body.appendChild(link);
- link.click();
- document.body.removeChild(link);
- URL.revokeObjectURL(url);
- return;
- };
-
- return (
- <>
-
-
-
- Export monitors to JSON
-
-
- Export your monitors data as a JSON file for backup or transfer.
-
-
-
-
-
-
- >
- );
-};
-
-SettingsDemoMonitors.propTypes = {
- isAdmin: PropTypes.bool,
- HEADER_SX: PropTypes.object,
-};
-
-export default SettingsDemoMonitors;
diff --git a/client/src/Pages/Settings/SettingsGlobalThresholds.jsx b/client/src/Pages/Settings/SettingsGlobalThresholds.jsx
deleted file mode 100644
index c2bc671f1..000000000
--- a/client/src/Pages/Settings/SettingsGlobalThresholds.jsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import Box from "@mui/material/Box";
-import Stack from "@mui/material/Stack";
-import Typography from "@mui/material/Typography";
-import ConfigBox from "@/Components/v1/ConfigBox/index.jsx";
-import TextInput from "@/Components/v1/Inputs/TextInput/index.jsx";
-
-import { useTheme } from "@emotion/react";
-import { PropTypes } from "prop-types";
-import { useTranslation } from "react-i18next";
-import { Controller } from "react-hook-form";
-
-const SettingsGlobalThresholds = ({
- isAdmin,
- HEADING_SX,
-
- control,
- defaults,
-}) => {
- const { t } = useTranslation(); // For language translation
- const theme = useTheme(); // MUI theme access
-
- // Only render this section for admins
- if (!isAdmin) return null;
-
- return (
-
- {/* Header and description */}
-
-
- {t("pages.settings.globalThresholds.title", "Global Thresholds")}
-
-
- {t(
- "pages.settings.globalThresholds.description",
- "Configure global CPU, Memory, Disk, and Temperature thresholds."
- )}
-
-
-
- {/* Threshold inputs */}
-
- {[
- ["CPU Threshold (%)", "cpu", 1, 100],
- ["Memory Threshold (%)", "memory", 1, 100],
- ["Disk Threshold (%)", "disk", 1, 100],
- ["Temperature Threshold (°C)", "temperature", 1, 150],
- ].map(([label, name, min, max]) => (
- (
-
- )}
- />
- ))}
-
-
- );
-};
-
-// Prop types
-SettingsGlobalThresholds.propTypes = {
- isAdmin: PropTypes.bool,
- HEADING_SX: PropTypes.object,
-};
-
-export default SettingsGlobalThresholds;
diff --git a/client/src/Pages/Settings/SettingsPagespeed.jsx b/client/src/Pages/Settings/SettingsPagespeed.jsx
deleted file mode 100644
index a479ff660..000000000
--- a/client/src/Pages/Settings/SettingsPagespeed.jsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import Box from "@mui/material/Box";
-import Stack from "@mui/material/Stack";
-import Typography from "@mui/material/Typography";
-import Button from "@mui/material/Button";
-import ConfigBox from "@/Components/v1/ConfigBox/index.jsx";
-import TextInput from "@/Components/v1/Inputs/TextInput/index.jsx";
-import { PasswordEndAdornment } from "@/Components/v1/Inputs/TextInput/Adornments/index.jsx";
-// Utils
-import { useTheme } from "@emotion/react";
-import { PropTypes } from "prop-types";
-import { useState } from "react";
-import { useTranslation } from "react-i18next";
-import { Controller } from "react-hook-form";
-
-const SettingsPagespeed = ({
- isAdmin,
- HEADING_SX,
- isApiKeySet,
- apiKeyHasBeenReset,
- setApiKeyHasBeenReset,
- defaults,
- control,
- setValue,
-}) => {
- const { t } = useTranslation();
- const theme = useTheme();
-
- // Local state
- const [apiKey, setApiKey] = useState("");
-
- if (!isAdmin) {
- return null;
- }
-
- return (
-
-
-
- {t("pages.settings.pageSpeedSettings.title")}
-
-
- {t("pages.settings.pageSpeedSettings.description")}
-
-
-
- {(isApiKeySet === false || apiKeyHasBeenReset === true) && (
- (
- }
- error={!!fieldState.error}
- helperText={fieldState.error?.message}
- />
- )}
- />
- )}
-
- {isApiKeySet === true && apiKeyHasBeenReset === false && (
-
-
- {t("pages.settings.pageSpeedSettings.labelApiKeySet")}
-
-
-
- )}
-
-
- );
-};
-
-SettingsPagespeed.propTypes = {
- isAdmin: PropTypes.bool,
- HEADING_SX: PropTypes.object,
- isApiKeySet: PropTypes.bool,
- setIsApiKeySet: PropTypes.func,
- apiKeyHasBeenReset: PropTypes.bool,
- setApiKeyHasBeenReset: PropTypes.func,
-};
-
-export default SettingsPagespeed;
diff --git a/client/src/Pages/Settings/SettingsStats.jsx b/client/src/Pages/Settings/SettingsStats.jsx
deleted file mode 100644
index 45735ee4d..000000000
--- a/client/src/Pages/Settings/SettingsStats.jsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import Box from "@mui/material/Box";
-import Typography from "@mui/material/Typography";
-import Button from "@mui/material/Button";
-import Stack from "@mui/material/Stack";
-import ConfigBox from "@/Components/v1/ConfigBox/index.jsx";
-import TextInput from "@/Components/v1/Inputs/TextInput/index.jsx";
-import Dialog from "@/Components/v1/Dialog/index.jsx";
-
-// Utils
-import { useTheme } from "@emotion/react";
-import { PropTypes } from "prop-types";
-import { useTranslation } from "react-i18next";
-import { useState } from "react";
-import { useDelete } from "@/Hooks/UseApi";
-
-const SettingsStats = ({ isAdmin, HEADING_SX, errors }) => {
- const theme = useTheme();
- const { t } = useTranslation();
- const [isOpen, setIsOpen] = useState(false);
-
- if (!isAdmin) {
- return null;
- }
- const { deleteFn: deleteMonitorStatsFn } = useDelete();
-
- return (
-
-
-
- {t("pages.settings.statsSettings.title")}
-
-
- {t("pages.settings.statsSettings.description")}
-
-
-
-
-
- {t("pages.settings.statsSettings.clearAllStatsDescription")}
-
-
-
-
-
- );
-};
-
-SettingsStats.propTypes = {
- isAdmin: PropTypes.bool,
- HEADING_SX: PropTypes.object,
-};
-
-export default SettingsStats;
diff --git a/client/src/Pages/Settings/SettingsTimeZone.jsx b/client/src/Pages/Settings/SettingsTimeZone.jsx
deleted file mode 100644
index 444088c8a..000000000
--- a/client/src/Pages/Settings/SettingsTimeZone.jsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import Box from "@mui/material/Box";
-import Stack from "@mui/material/Stack";
-import Typography from "@mui/material/Typography";
-import ConfigBox from "@/Components/v1/ConfigBox/index.jsx";
-import Search from "@/Components/v1/Inputs/Search/index.jsx";
-import timezones from "@/Utils/timezones.json";
-
-// Utils
-import { useTheme } from "@emotion/react";
-import { useSelector, useDispatch } from "react-redux";
-import { setTimezone } from "@/Features/UI/uiSlice.js";
-import { PropTypes } from "prop-types";
-import { useTranslation } from "react-i18next";
-import { useCallback, useMemo, useState } from "react";
-const SettingsTimeZone = ({ HEADING_SX }) => {
- const theme = useTheme();
- const { t } = useTranslation();
- const [rawInput, setRawInput] = useState("");
- const dispatch = useDispatch();
- const { timezone } = useSelector((state) => state.ui);
-
- const selectedTimezone = useMemo(
- () => timezones.find((tz) => tz._id === timezone) ?? null,
- [timezone]
- );
-
- const handleTimezoneChange = (newValue) => {
- setRawInput("");
- const newId = newValue?._id ?? "";
- dispatch(setTimezone({ timezone: newId }));
- };
-
- return (
-
-
-
- {t("pages.settings.timezoneSettings.title")}
-
-
-
- {t("pages.settings.timezoneSettings.description")}
-
-
-
-
- setRawInput(val)}
- handleChange={handleTimezoneChange}
- isAdorned={true}
- unit="timezone"
- />
-
-
- );
-};
-
-SettingsTimeZone.propTypes = {
- HEADING_SX: PropTypes.object,
-};
-
-export default SettingsTimeZone;
diff --git a/client/src/Pages/Settings/SettingsUI.jsx b/client/src/Pages/Settings/SettingsUI.jsx
deleted file mode 100644
index aa58a67d9..000000000
--- a/client/src/Pages/Settings/SettingsUI.jsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import Box from "@mui/material/Box";
-import Stack from "@mui/material/Stack";
-import Typography from "@mui/material/Typography";
-import ConfigBox from "@/Components/v1/ConfigBox/index.jsx";
-import Select from "@/Components/v1/Inputs/Select/index.jsx";
-import DummyChart from "./DummyChart";
-import { ThemeProvider } from "@mui/material/styles";
-import { lightTheme, darkTheme } from "@/Utils/Theme/v2Theme";
-
-// Utils
-import { useTheme } from "@emotion/react";
-import { PropTypes } from "prop-types";
-import { useTranslation } from "react-i18next";
-import { useSelector, useDispatch } from "react-redux";
-import { setMode, setLanguage, setChartType } from "@/Features/UI/uiSlice.js";
-
-const SettingsUI = ({ HEADING_SX }) => {
- const {
- mode,
- language = "en",
- chartType = "histogram",
- } = useSelector((state) => state.ui);
- const { t, i18n } = useTranslation();
- const theme = useTheme();
- const languages = Object.keys(i18n.options.resources || {});
- const dispatch = useDispatch();
- const v2Theme = mode === "dark" ? darkTheme : lightTheme;
- return (
-
-
-
- {t("pages.settings.uiSettings.title")}
-
-
- {t("pages.settings.uiSettings.description")}
-
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-SettingsUI.propTypes = {
- HEADING_SX: PropTypes.object,
-};
-
-export default SettingsUI;
diff --git a/client/src/Pages/Settings/SettingsURL.jsx b/client/src/Pages/Settings/SettingsURL.jsx
deleted file mode 100644
index 11e7ea821..000000000
--- a/client/src/Pages/Settings/SettingsURL.jsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import Box from "@mui/material/Box";
-import Stack from "@mui/material/Stack";
-import Typography from "@mui/material/Typography";
-import ConfigBox from "@/Components/v1/ConfigBox/index.jsx";
-import Select from "@/Components/v1/Inputs/Select/index.jsx";
-import { Controller } from "react-hook-form";
-
-// Utils
-import { useTheme } from "@emotion/react";
-import { PropTypes } from "prop-types";
-import { useTranslation } from "react-i18next";
-
-const SettingsURL = ({ HEADING_SX, control, defaults }) => {
- const { t } = useTranslation();
- const theme = useTheme();
- return (
-
-
-
- {t("pages.settings.urlSettings.title")}
-
-
- {t("pages.settings.urlSettings.description")}
-
-
-
- (
-
- )}
- />
-
-
- );
-};
-
-SettingsURL.propTypes = {
- HEADING_SX: PropTypes.object,
-};
-
-export default SettingsURL;
diff --git a/client/src/Pages/Settings/index.tsx b/client/src/Pages/Settings/index.tsx
index 427eb3f73..5e01e02f1 100644
--- a/client/src/Pages/Settings/index.tsx
+++ b/client/src/Pages/Settings/index.tsx
@@ -1,13 +1,13 @@
import { BasePage, ConfigBox, TextLink } from "@/Components/v2/design-elements";
import { Autocomplete, Select, Dialog, SwitchComponent } from "@/Components/v2/inputs";
-import { Stack, useTheme, MenuItem, type SelectChangeEvent } from "@mui/material";
+import { Stack, useTheme, MenuItem, Link, type SelectChangeEvent } from "@mui/material";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useTranslation } from "react-i18next";
import { useSelector, useDispatch } from "react-redux";
import DummyChart from "@/Pages/Settings/DummyChart";
-import { useGet, usePatch, usePost } from "@/Hooks/UseApi";
+import { useGet, usePatch, usePost, useLazyGet } from "@/Hooks/UseApi";
import { useSettingsForm } from "@/Hooks/useSettingsForm";
import { useIsAdmin } from "@/Hooks/useIsAdmin.js";
import type { SettingsFormData } from "@/Validation/settings";
@@ -69,6 +69,8 @@ export const SettingsPage = () => {
// Local state for clear stats dialog
const [isStatsDialogOpen, setIsStatsDialogOpen] = useState(false);
const { deleteFn: deleteStats, loading: isDeletingStats } = useDelete();
+ // Export monitors functionality
+ const { get: fetchMonitorsJson } = useLazyGet();
// Initialize form with schema and defaults
const { schema, defaults } = useSettingsForm({ data: fetchedSettings?.settings });
@@ -179,6 +181,27 @@ export const SettingsPage = () => {
setIsStatsDialogOpen(false);
};
+ const handleExportMonitors = async () => {
+ const res = await fetchMonitorsJson("/monitors/export/json");
+ const json = res?.data ?? [];
+ if (!json || json.length === 0) {
+ return;
+ }
+
+ const blob = new Blob([JSON.stringify(json, null, 2)], {
+ type: "application/json",
+ });
+ const url = URL.createObjectURL(blob);
+
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = "monitors.json";
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+ };
+
const onSubmit = async (data: SettingsFormData) => {
// Don't send pagespeedApiKey if it's already set and user hasn't clicked reset
const dataToSend = { ...data };
@@ -840,6 +863,50 @@ export const SettingsPage = () => {
/>
)}
+ {/* Export Monitors - Admin Only */}
+ {isAdmin && (
+
+
+
+ }
+ />
+ )}
+
+ {/* About */}
+
+
+ {t("common.appName")} {__APP_VERSION__}
+
+
+ {t("pages.settings.form.about.developedBy")}
+
+
+ https://github.com/bluewave-labs/checkmate
+
+
+ }
+ />
+
{/* Clear Stats Confirmation Dialog */}