diff --git a/README.md b/README.md
index 68216c880..5505f72fe 100644
--- a/README.md
+++ b/README.md
@@ -107,7 +107,7 @@ If you have any questions, suggestions or comments, please use our [Discord chan
## 🤝 Contributing
-We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 3700+ stars and attracted 45+ contributors from around the globe.
+We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 4.4K+ stars and attracted 50+ contributors from around the globe.
Our repo is starred by employees from **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes and NEC**, so don’t hold back — jump in, contribute and learn with us!
diff --git a/package-lock.json b/package-lock.json
index 1a7fcdabc..432f710eb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,7 +16,7 @@
"@mui/lab": "6.0.0-beta.29",
"@mui/material": "6.4.6",
"@mui/x-charts": "^7.5.1",
- "@mui/x-data-grid": "7.27.1",
+ "@mui/x-data-grid": "7.27.2",
"@mui/x-date-pickers": "7.27.1",
"@reduxjs/toolkit": "2.6.0",
"@solana/wallet-adapter-base": "0.9.23",
@@ -3858,9 +3858,9 @@
}
},
"node_modules/@mui/x-data-grid": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.27.1.tgz",
- "integrity": "sha512-dDgrCIiIcb3XPxUVxXQsk9yjXFSAXBunqW1m1jjcr0D3jj3qHibBtGslSKnBmmcoT6XLAXdbZOu1fFC+xYhyGA==",
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.27.2.tgz",
+ "integrity": "sha512-Nj6PWp6WN0gkps676j1Jai3mP1fQotJbqMTvllU7LwQp09S/RE/uJicYdqfeWQLxtQipWm5dnAY63gzsTiAHGA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.25.7",
@@ -4713,9 +4713,9 @@
}
},
"node_modules/@remix-run/router": {
- "version": "1.22.0",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz",
- "integrity": "sha512-MBOl8MeOzpK0HQQQshKB7pABXbmyHizdTpqnrIseTbsv0nAepwC2ENZa1aaBExNQcpLoXmWthhak8SABLzvGPw==",
+ "version": "1.23.0",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
+ "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
@@ -16305,12 +16305,12 @@
}
},
"node_modules/react-router": {
- "version": "6.29.0",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.29.0.tgz",
- "integrity": "sha512-DXZJoE0q+KyeVw75Ck6GkPxFak63C4fGqZGNijnWgzB/HzSP1ZfTlBj5COaGWwhrMQ/R8bXiq5Ooy4KG+ReyjQ==",
+ "version": "6.30.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz",
+ "integrity": "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==",
"license": "MIT",
"dependencies": {
- "@remix-run/router": "1.22.0"
+ "@remix-run/router": "1.23.0"
},
"engines": {
"node": ">=14.0.0"
@@ -16320,13 +16320,13 @@
}
},
"node_modules/react-router-dom": {
- "version": "6.29.0",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.29.0.tgz",
- "integrity": "sha512-pkEbJPATRJ2iotK+wUwHfy0xs2T59YPEN8BQxVCPeBZvK7kfPESRc/nyxzdcxR17hXgUPYx2whMwl+eo9cUdnQ==",
+ "version": "6.30.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.0.tgz",
+ "integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==",
"license": "MIT",
"dependencies": {
- "@remix-run/router": "1.22.0",
- "react-router": "6.29.0"
+ "@remix-run/router": "1.23.0",
+ "react-router": "6.30.0"
},
"engines": {
"node": ">=14.0.0"
diff --git a/package.json b/package.json
index fe51f7003..9684ff111 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"@fontsource/roboto": "^5.0.13",
"@hello-pangea/dnd": "^18.0.0",
"@mui/x-charts": "^7.5.1",
- "@mui/x-data-grid": "7.27.1",
+ "@mui/x-data-grid": "7.27.2",
"@mui/x-date-pickers": "7.27.1",
"@mui/icons-material": "6.4.6",
"@mui/lab": "6.0.0-beta.29",
diff --git a/src/Components/Charts/ChartBox/EmptyView.jsx b/src/Components/Charts/ChartBox/EmptyView.jsx
new file mode 100644
index 000000000..94b1af7b0
--- /dev/null
+++ b/src/Components/Charts/ChartBox/EmptyView.jsx
@@ -0,0 +1,108 @@
+// Components
+import { Typography, Stack } from "@mui/material";
+import PropTypes from "prop-types";
+import { useTheme } from "@emotion/react";
+import IconBox from "../../IconBox";
+
+/**
+ * `EmptyView` is a functional React component that displays an empty state view with an optional icon, header, and message.
+ *
+ * @component
+ * @param {Object} props - The properties that define the `EmptyView` component.
+ * @param {React.ReactNode} [props.icon] - An optional icon to display at the top of the empty view.
+ * @param {string} [props.header] - An optional header text displayed next to the icon.
+ * @param {string} [props.message="No Data"] - The message to be displayed in the empty view.
+ * @param {'h1' | 'h2' | 'h3'} [props.headingLevel="h2"] - The heading level for the message text.
+ * @param {string} [props.justifyContent="flex-start"] - The CSS `justify-content` value to align elements vertically.
+ * @param {string} [props.height="100%"] - The height of the empty view container.
+ *
+ * @example
+ * // Example usage of EmptyView component:
+ * }
+ * header="Average Response Time"
+ * message="No Response Time Available"
+ * headingLevel="h2"
+ * justifyContent="center"
+ * height="50%"
+ * />
+ *
+ * @returns {React.Element} The `EmptyView` component with customizable icon, header, and message.
+ */
+
+const EmptyView = ({
+ icon,
+ header,
+ message = "No Data",
+ headingLevel = "h2",
+ justifyContent = "flex-start",
+ height = "100%"
+}) => {
+ const theme = useTheme();
+ return (
+
+
+
+ {icon && {icon}}
+ {header && {header}}
+
+
+
+ {message}
+
+
+
+
+ );
+};
+
+EmptyView.propTypes = {
+ message: PropTypes.string,
+ icon: PropTypes.node,
+ header: PropTypes.string,
+ headingLevel: PropTypes.oneOf(['h1', 'h2', 'h3']),
+ justifyContent: PropTypes.string,
+ height: PropTypes.string
+};
+
+export default EmptyView;
diff --git a/src/Components/Charts/ChartBox/index.jsx b/src/Components/Charts/ChartBox/index.jsx
index bd27d4775..4b72fe16a 100644
--- a/src/Components/Charts/ChartBox/index.jsx
+++ b/src/Components/Charts/ChartBox/index.jsx
@@ -1,6 +1,7 @@
import { Stack, Typography } from "@mui/material";
import { useTheme } from "@emotion/react";
import IconBox from "../../IconBox";
+import EmptyView from "./EmptyView";
import PropTypes from "prop-types";
const ChartBox = ({
@@ -12,8 +13,13 @@ const ChartBox = ({
Legend,
borderRadiusRight = 4,
sx,
+ noDataMessage,
+ isEmpty = false,
}) => {
const theme = useTheme();
+ if (isEmpty) {
+ return ;
+ }
return (
-
+
{authState.user?.firstName} {authState.user?.lastName}
@@ -706,12 +715,21 @@ function Sidebar() {
}}
>
{collapsed && (
-
);
};
diff --git a/src/Pages/DistributedUptimeStatus/Status/index.jsx b/src/Pages/DistributedUptimeStatus/Status/index.jsx
index 4a8610036..033174b0c 100644
--- a/src/Pages/DistributedUptimeStatus/Status/index.jsx
+++ b/src/Pages/DistributedUptimeStatus/Status/index.jsx
@@ -18,10 +18,11 @@ import InfoBox from "../../../Components/InfoBox";
import StatusHeader from "../../DistributedUptime/Details/Components/StatusHeader";
import MonitorsList from "./Components/MonitorsList";
import { RowContainer } from "../../../Components/StandardContainer";
+import ThemeSwitch from "../../../Components/ThemeSwitch";
//Utils
import { useTheme } from "@mui/material/styles";
-import { useState, useRef } from "react";
+import { useState, useEffect, useRef } from "react";
import { useParams } from "react-router-dom";
import { useSubscribeToDetails } from "../../DistributedUptime/Details/Hooks/useSubscribeToDetails";
import { useDUStatusPageFetchByUrl } from "./Hooks/useDUStatusPageFetchByUrl";
@@ -31,6 +32,8 @@ import SubHeader from "../../../Components/Subheader";
import { safelyParseFloat } from "../../../Utils/utils";
import { useNavigate, useLocation } from "react-router-dom";
import { useTranslation } from "react-i18next";
+import { useSelector, useDispatch } from "react-redux";
+import { setMode } from "../../../Features/UI/uiSlice";
// Responsive
import { useMediaQuery } from "@mui/material";
@@ -40,8 +43,12 @@ const DistributedUptimeStatus = () => {
const location = useLocation();
const { t } = useTranslation();
const isPublic = location.pathname.startsWith("/status/distributed/public");
-
const elementToCapture = useRef(null);
+
+ // Redux state
+ const mode = useSelector((state) => state.ui.mode);
+ const originalModeRef = useRef(null);
+
// Local State
const [dateRange, setDateRange] = useState("day");
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
@@ -50,6 +57,7 @@ const DistributedUptimeStatus = () => {
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm"));
const navigate = useNavigate();
+ const dispatch = useDispatch();
const [
statusPageIsLoading,
@@ -85,6 +93,28 @@ const DistributedUptimeStatus = () => {
};
}
+ // Default to dark mode
+ useEffect(() => {
+ const cleanup = () => {
+ if (originalModeRef.current === null) {
+ originalModeRef.current = mode;
+ }
+
+ if (isPublic) {
+ dispatch(setMode(originalModeRef.current));
+ }
+ };
+
+ if (isPublic) {
+ dispatch(setMode("dark"));
+ }
+
+ window.addEventListener("beforeunload", cleanup);
+ return () => {
+ window.removeEventListener("beforeunload", cleanup);
+ };
+ }, [dispatch, isPublic]);
+
// Done loading, a status page doesn't exist
if (!statusPageIsLoading && typeof statusPage === "undefined") {
return (
@@ -158,9 +188,10 @@ const DistributedUptimeStatus = () => {
}}
>
{!isPublic && }
+
{
- {t("distributedRightCatagoryTitle")}
-
-
- {t("distributedRightCatagoryDescription")}
+ {t("distributedRightCategoryTitle")}
+ {statusPage.companyName}
diff --git a/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx b/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx
index 9eacf9b36..f534e73b3 100644
--- a/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx
+++ b/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx
@@ -2,7 +2,7 @@
import { Box, Stack, Typography, Button } from "@mui/material";
import Image from "../../../../../Components/Image";
import SettingsIcon from "../../../../../assets/icons/settings-bold.svg?react";
-
+import ThemeSwitch from "../../../../../Components/ThemeSwitch";
//Utils
import { useTheme } from "@mui/material/styles";
import { useNavigate } from "react-router-dom";
@@ -76,6 +76,7 @@ Controls.propTypes = {
const ControlsHeader = ({
statusPage,
+ isPublic,
isDeleting,
isDeleteOpen,
setIsDeleteOpen,
@@ -105,7 +106,16 @@ const ControlsHeader = ({
maxHeight={"50px"}
base64={statusPage?.logo?.data}
/>
- {statusPage?.companyName}
+
+ {statusPage?.companyName}
+
+ {isPublic && }
);
};
@@ -121,6 +132,7 @@ const ControlsHeader = ({
ControlsHeader.propTypes = {
url: PropTypes.string,
statusPage: PropTypes.object,
+ isPublic: PropTypes.bool,
isDeleting: PropTypes.bool,
isDeleteOpen: PropTypes.bool.isRequired,
setIsDeleteOpen: PropTypes.func.isRequired,
diff --git a/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx b/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx
index 746d10f42..7c951d420 100644
--- a/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx
+++ b/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx
@@ -17,16 +17,40 @@ const StatusPagesTable = ({ data }) => {
},
{
id: "url",
- content: "URL",
+ content: "Public URL",
+ onClick: (e, row) => {
+ if (row.isPublished) {
+ e.stopPropagation();
+ const url =
+ row.type === "distributed"
+ ? `/status/distributed/public/${row.url}`
+ : `/status/uptime/public/${row.url}`;
+ navigate(url);
+ }
+ },
render: (row) => {
+ const content = row.isPublished ? `/${row.url}` : "Unpublished";
return (
- {`/${row.url}`}
-
+ {content}
+ {row.isPublished && }
);
},
diff --git a/src/Pages/StatusPage/StatusPages/index.jsx b/src/Pages/StatusPage/StatusPages/index.jsx
index 9027467a4..54a41f850 100644
--- a/src/Pages/StatusPage/StatusPages/index.jsx
+++ b/src/Pages/StatusPage/StatusPages/index.jsx
@@ -51,6 +51,7 @@ const StatusPages = () => {
/>
);
}
+
return (
diff --git a/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx b/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx
index 99d17b6f8..1875ef760 100644
--- a/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx
+++ b/src/Pages/Uptime/Details/Components/ChartBoxes/index.jsx
@@ -35,6 +35,7 @@ const ChartBoxes = ({
const denominator =
totalUpChecks + totalDownChecks > 0 ? totalUpChecks + totalDownChecks : 1;
const groupedUptimePercentage = (totalUpChecks / denominator) * 100;
+ const noIncidentsMessage = "Great. No Incidents, yet!";
return (
}
header="Uptime"
+ isEmpty={monitor?.uptimePercentage === 0 && !monitor?.upChecks?.length}
>
}
header="Incidents"
+ noDataMessage={noIncidentsMessage}
+ isEmpty={monitor?.groupedDownChecks?.length === 0}
>
diff --git a/src/Utils/Theme/constants.js b/src/Utils/Theme/constants.js
index 36ab0a0f4..cd2b2e316 100644
--- a/src/Utils/Theme/constants.js
+++ b/src/Utils/Theme/constants.js
@@ -282,7 +282,7 @@ const newSemanticColors = {
},
chatbot: {
background: {
- light: "#112B2B",
+ light: newColors.gray200,
dark: "#112B2B",
},
textAccent: {
diff --git a/src/Validation/validation.js b/src/Validation/validation.js
index 68c0b5f12..a243b37e8 100644
--- a/src/Validation/validation.js
+++ b/src/Validation/validation.js
@@ -7,11 +7,18 @@ const nameSchema = joi
.string()
.max(50)
.trim()
- .pattern(/^[A-Za-z]+$/)
+ .pattern(/^(?=.*[\p{L}\p{Sc}])[\p{L}\p{Sc}\s']+$/u, {
+ name: "name.containsLetterOrSymbol",
+ })
+ .pattern(/^[\p{L}\p{Sc}\s']+$/u, {
+ name: "name.validCharacters",
+ })
.messages({
"string.empty": "Name is required",
- "string.max": "Name must be less than 50 characters long",
- "string.pattern.base": "Name must contain only letters",
+ "string.max": "Name must be less than 50 characters",
+ "string.pattern.name": "Name must contain at least 1 letter or currency symbol",
+ "string.pattern.base.validCharacters":
+ "Can only contain letters, spaces, apostrophes, and currency symbols",
});
const passwordSchema = joi
diff --git a/src/locales/gb.json b/src/locales/gb.json
index 2de3fa6f8..e12309469 100644
--- a/src/locales/gb.json
+++ b/src/locales/gb.json
@@ -63,8 +63,7 @@
"webhookUnsupportedPlatform": "Unsupported platform: {platform}",
"distributedStatusHeaderText": "Real-time, real-device coverage",
"distributedStatusSubHeaderText": "Powered by millions devices worldwide, view a system performance by global region, country or city",
- "distributedRightCatagoryTitle": "Monitor",
- "distributedRightCatagoryDescription": "Mainnet Beta Cluster",
+ "distributedRightCategoryTitle": "Monitor",
"distributedStatusServerMonitors": "Server Monitors",
"distributedStatusServerMonitorsDescription": "Monitor status of related servers"
}
diff --git a/src/locales/tr.json b/src/locales/tr.json
index 7632c31fd..5ff3448b7 100644
--- a/src/locales/tr.json
+++ b/src/locales/tr.json
@@ -58,6 +58,5 @@
"authRegisterBySigningUp": "Kayıt olarak, aşağıdaki şartları kabul ediyorsunuz:",
"distributedStatusHeaderText": "Gerçek zamanlı, Gerçek cihazlar kapsamı",
"distributedStatusSubHeaderText": "Dünya çapında milyonlarca cihaz tarafından desteklenen sistem performansını küresel bölgeye, ülkeye veya şehre göre görüntüleyin",
- "distributedRightCatagoryTitle": "Monitör",
- "distributedRightCatagoryDescription": "Mainnet Beta Kümesi"
+ "distributedRightCategoryTitle": "Monitör"
}