diff --git a/Client/src/Pages/DistributedUptime/CreateStatus/Components/VisuallyHiddenInput/index.jsx b/Client/src/Pages/DistributedUptime/CreateStatus/Components/VisuallyHiddenInput/index.jsx
new file mode 100644
index 000000000..fd002d2a4
--- /dev/null
+++ b/Client/src/Pages/DistributedUptime/CreateStatus/Components/VisuallyHiddenInput/index.jsx
@@ -0,0 +1,22 @@
+const VisuallyHiddenInput = ({ onChange }) => {
+ return (
+
+ );
+};
+
+export default VisuallyHiddenInput;
diff --git a/Client/src/Pages/DistributedUptime/CreateStatus/index.jsx b/Client/src/Pages/DistributedUptime/CreateStatus/index.jsx
new file mode 100644
index 000000000..d244bfe38
--- /dev/null
+++ b/Client/src/Pages/DistributedUptime/CreateStatus/index.jsx
@@ -0,0 +1,217 @@
+// Components
+import { Stack, Typography, Button, Box } from "@mui/material";
+import ConfigBox from "../../../Components/ConfigBox";
+import Checkbox from "../../../Components/Inputs/Checkbox";
+import TextInput from "../../../Components/Inputs/TextInput";
+import VisuallyHiddenInput from "./Components/VisuallyHiddenInput";
+import Image from "../../../Components/Image";
+import LogoPlaceholder from "../../../assets/Images/logo_placeholder.svg";
+import Breadcrumbs from "../../../Components/Breadcrumbs";
+// Utils
+import { useTheme } from "@emotion/react";
+import { useState } from "react";
+import { useParams } from "react-router-dom";
+import { useCreateStatusPage } from "../../StatusPage/Create/Hooks/useCreateStatusPage";
+import { useLocation } from "react-router-dom";
+import { statusPageValidation } from "../../../Validation/validation";
+import { buildErrors } from "../../../Validation/error";
+import { createToast } from "../../../Utils/toastUtils";
+import { useNavigate } from "react-router-dom";
+
+const CreateStatus = () => {
+ const theme = useTheme();
+ const { monitorId } = useParams();
+ const navigate = useNavigate();
+ const location = useLocation();
+ const isCreate = location.pathname.startsWith("/distributed-uptime/status/create");
+ const [createStatusPage, isLoading, networkError] = useCreateStatusPage(isCreate);
+ const BREADCRUMBS = [
+ { name: "distributed uptime", path: "/distributed-uptime" },
+ { name: "details", path: `/distributed-uptime/${monitorId}` },
+ { name: "create status page", path: `` },
+ ];
+ // Local state
+ const [form, setForm] = useState({
+ isPublished: false,
+ url: Math.floor(Math.random() * 1000000).toFixed(0),
+ logo: undefined,
+ companyName: "",
+ monitors: [monitorId],
+ });
+ const [errors, setErrors] = useState({});
+
+ const handleFormChange = (e) => {
+ const { name, value, checked, type } = e.target;
+
+ // Check for errors
+ const { error } = statusPageValidation.validate(
+ { [name]: value },
+ { abortEarly: false }
+ );
+
+ setErrors((prev) => buildErrors(prev, name, error));
+
+ if (type === "checkbox") {
+ setForm({ ...form, [name]: checked });
+ return;
+ }
+ setForm({ ...form, [name]: value });
+ };
+
+ const handleImageUpload = (e) => {
+ const img = e.target?.files?.[0];
+ setForm((prev) => ({
+ ...prev,
+ logo: img,
+ }));
+ };
+ const handleSubmit = async () => {
+ let logoToSubmit = undefined;
+
+ // Handle image
+ if (typeof form.logo !== "undefined") {
+ logoToSubmit = {
+ src: URL.createObjectURL(form.logo),
+ name: form.logo.name,
+ type: form.logo.type,
+ size: form.logo.size,
+ };
+ }
+ const formToSubmit = { ...form };
+ if (typeof logoToSubmit !== "undefined") {
+ formToSubmit.logo = logoToSubmit;
+ }
+
+ // Validate
+ const { error } = statusPageValidation.validate(formToSubmit, { abortEarly: false });
+ if (typeof error === "undefined") {
+ const success = await createStatusPage({ form: formToSubmit });
+ if (success) {
+ createToast({ body: "Status page created successfully" });
+ navigate(`/distributed-uptime/status/${form.url}`);
+ }
+ return;
+ }
+ const newErrors = {};
+ error?.details?.forEach((err) => {
+ newErrors[err.path[0]] = err.message;
+ });
+ setErrors((prev) => ({ ...prev, ...newErrors }));
+ };
+
+ return (
+
+
+
+
+ Create your{" "}
+
+
+ status page
+
+
+
+
+ Access
+
+ If your status page is ready, you can mark it as published.
+
+
+
+
+
+
+
+
+ Basic Information
+
+ Define company name and the subdomain that your status page points to.
+
+
+
+
+
+
+
+
+
+ Logo
+ Upload a logo for your status page
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default CreateStatus;
diff --git a/Client/src/Pages/DistributedUptime/Details/index.jsx b/Client/src/Pages/DistributedUptime/Details/index.jsx
index ab39ec2ec..a8fc0c309 100644
--- a/Client/src/Pages/DistributedUptime/Details/index.jsx
+++ b/Client/src/Pages/DistributedUptime/Details/index.jsx
@@ -10,21 +10,23 @@ import StatBoxes from "./Components/StatBoxes";
import MonitorHeader from "./Components/MonitorHeader";
import MonitorTimeFrameHeader from "../../../Components/MonitorTimeFrameHeader";
import GenericFallback from "../../../Components/GenericFallback";
+import MonitorCreateHeader from "../../../Components/MonitorCreateHeader";
//Utils
import { useTheme } from "@mui/material/styles";
import { useState } from "react";
import { useParams } from "react-router-dom";
-import useSubscribeToDetails from "./Hooks/useSubscribeToDetails";
+import { useIsAdmin } from "../../../Hooks/useIsAdmin";
+import { useSubscribeToDetails } from "./Hooks/useSubscribeToDetails";
const DistributedUptimeDetails = () => {
const { monitorId } = useParams();
-
// Local State
const [dateRange, setDateRange] = useState("day");
// Utils
const theme = useTheme();
+ const isAdmin = useIsAdmin();
const [isLoading, networkError, connectionStatus, monitor, lastUpdateTrigger] =
useSubscribeToDetails({ monitorId, dateRange });
// Constants
@@ -65,6 +67,11 @@ const DistributedUptimeDetails = () => {
gap={theme.spacing(10)}
>
+
{
},
},
onRowClick: (row) => {
- navigate(`/distributed-uptime/${row.id}`);
+ navigate(`/distributed-uptime/${row._id}`);
},
}}
/>
diff --git a/Client/src/Pages/DistributedUptime/Status/Hooks/useStatusPageFetchByUrl.jsx b/Client/src/Pages/DistributedUptime/Status/Hooks/useStatusPageFetchByUrl.jsx
new file mode 100644
index 000000000..973e157c7
--- /dev/null
+++ b/Client/src/Pages/DistributedUptime/Status/Hooks/useStatusPageFetchByUrl.jsx
@@ -0,0 +1,35 @@
+import { useState, useEffect } from "react";
+import { networkService } from "../../../../main";
+import { createToast } from "../../../../Utils/toastUtils";
+import { useSelector } from "react-redux";
+
+const useStatusPageFetchByUrl = ({ url }) => {
+ const [isLoading, setIsLoading] = useState(true);
+ const [networkError, setNetworkError] = useState(false);
+ const [statusPage, setStatusPage] = useState(undefined);
+ const [monitorId, setMonitorId] = useState(undefined);
+ const { authToken } = useSelector((state) => state.auth);
+ useEffect(() => {
+ const fetchStatusPageByUrl = async () => {
+ try {
+ const response = await networkService.getStatusPageByUrl({ authToken, url });
+ if (!response?.data?.data) return;
+ const statusPage = response.data.data;
+ setStatusPage(statusPage);
+ setMonitorId(statusPage?.monitors[0]);
+ } catch (error) {
+ setNetworkError(true);
+ createToast({
+ body: error.message,
+ });
+ } finally {
+ setIsLoading(false);
+ }
+ };
+ fetchStatusPageByUrl();
+ }, [authToken, url]);
+
+ return [isLoading, networkError, statusPage, monitorId];
+};
+
+export { useStatusPageFetchByUrl };
diff --git a/Client/src/Pages/DistributedUptime/Status/index.jsx b/Client/src/Pages/DistributedUptime/Status/index.jsx
new file mode 100644
index 000000000..c2cee4c12
--- /dev/null
+++ b/Client/src/Pages/DistributedUptime/Status/index.jsx
@@ -0,0 +1,135 @@
+//Components
+import DistributedUptimeMap from "../Details/Components/DistributedUptimeMap";
+import Breadcrumbs from "../../../Components/Breadcrumbs";
+import { Stack, Typography } from "@mui/material";
+import DeviceTicker from "../Details/Components/DeviceTicker";
+import DistributedUptimeResponseChart from "../Details/Components/DistributedUptimeResponseChart";
+import NextExpectedCheck from "../Details/Components/NextExpectedCheck";
+import Footer from "../Details/Components/Footer";
+import StatBoxes from "../Details/Components/StatBoxes";
+import ControlsHeader from "../../StatusPage/Status/Components/ControlsHeader";
+import MonitorTimeFrameHeader from "../../../Components/MonitorTimeFrameHeader";
+import GenericFallback from "../../../Components/GenericFallback";
+import Dialog from "../../../Components/Dialog";
+//Utils
+import { useTheme } from "@mui/material/styles";
+import { useState } from "react";
+import { useParams } from "react-router-dom";
+import { useIsAdmin } from "../../../Hooks/useIsAdmin";
+import { useSubscribeToDetails } from "../Details/Hooks/useSubscribeToDetails";
+import { useStatusPageFetchByUrl } from "./Hooks/useStatusPageFetchByUrl";
+import { useStatusPageDelete } from "../../StatusPage/Status/Hooks/useStatusPageDelete";
+import { useNavigate } from "react-router-dom";
+const DistributedUptimeStatus = () => {
+ const { url } = useParams();
+ // Local State
+ const [dateRange, setDateRange] = useState("day");
+ const [isDeleteOpen, setIsDeleteOpen] = useState(false);
+ // Utils
+ const theme = useTheme();
+ const navigate = useNavigate();
+ const [statusPageIsLoading, statusPageNetworkError, statusPage, monitorId] =
+ useStatusPageFetchByUrl({
+ url,
+ });
+
+ const [isLoading, networkError, connectionStatus, monitor, lastUpdateTrigger] =
+ useSubscribeToDetails({ monitorId, dateRange });
+
+ const [deleteStatusPage, isDeleting] = useStatusPageDelete(() => {
+ navigate("/distributed-uptime");
+ }, url);
+ // Constants
+ const BREADCRUMBS = [
+ { name: "Distributed Uptime", path: "/distributed-uptime" },
+ { name: "status", path: `` },
+ ];
+
+ if (networkError) {
+ return (
+
+
+ Network error
+
+ Please check your connection
+
+ );
+ }
+
+ if (typeof monitor === "undefined" || monitor.totalChecks === 0) {
+ return (
+
+
+
+ There is no check history for this monitor yet.
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default DistributedUptimeStatus;
diff --git a/Client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx b/Client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx
index 3f1832889..6f030b2e5 100644
--- a/Client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx
+++ b/Client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx
@@ -126,7 +126,7 @@ const MonitorsTable = ({ shouldRender, monitors, isAdmin, handleActionMenuDelete
transition: "background-color .3s ease",
},
},
- onRowClick: (row) => openDetails(row.id),
+ onRowClick: (row) => openDetails(row._id),
}}
/>
);