Skip to content

Commit 0eceac1

Browse files
authored
Merge pull request #1736 from bluewave-labs/feat/fe/list-du-status-pages
feat: fe/list du status pages
2 parents deca3b9 + 5f86824 commit 0eceac1

File tree

27 files changed

+403
-101
lines changed

27 files changed

+403
-101
lines changed

Client/src/Components/Fallback/index.jsx

-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ const Fallback = ({ title, checks, link = "/", isAdmin, vowelStart = false }) =>
2727

2828
return (
2929
<Box
30-
className="page-speed"
3130
position="relative"
3231
border={1}
3332
borderColor={theme.palette.primary.lowContrast}

Client/src/Pages/DistributedUptime/Create/index.jsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { useNavigate } from "react-router-dom";
1818
import { useSelector, useDispatch } from "react-redux";
1919
import { monitorValidation } from "../../../Validation/validation";
2020
import { createUptimeMonitor } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
21+
import { useLocation } from "react-router-dom";
2122

2223
// Constants
2324
const BREADCRUMBS = [
@@ -34,6 +35,9 @@ const SELECT_VALUES = [
3435
];
3536

3637
const CreateDistributedUptime = () => {
38+
const location = useLocation();
39+
const isCreate = location.pathname.startsWith("/distributed-uptime/create");
40+
3741
// Redux state
3842
const { user, authToken } = useSelector((state) => state.auth);
3943
const isLoading = useSelector((state) => state.uptimeMonitors.isLoading);
@@ -103,7 +107,6 @@ const CreateDistributedUptime = () => {
103107
{ [name]: value },
104108
{ abortEarly: false }
105109
);
106-
console.log(name);
107110
setErrors((prev) => ({
108111
...prev,
109112
...(error ? { [name]: error.details[0].message } : { [name]: undefined }),

Client/src/Pages/DistributedUptime/Details/index.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ const DistributedUptimeDetails = () => {
7474
<MonitorCreateHeader
7575
label="Create status page"
7676
isAdmin={isAdmin}
77-
path={`/distributed-uptime/status/create/${monitorId}`}
77+
path={`/status/distributed/create/${monitorId}`}
7878
/>
7979
<MonitorHeader monitor={monitor} />
8080
<StatBoxes

Client/src/Pages/DistributedUptime/CreateStatus/index.jsx Client/src/Pages/DistributedUptimeStatus/Create/index.jsx

+45-7
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import LogoPlaceholder from "../../../assets/Images/logo_placeholder.svg";
99
import Breadcrumbs from "../../../Components/Breadcrumbs";
1010
// Utils
1111
import { useTheme } from "@emotion/react";
12-
import { useState } from "react";
12+
import { useState, useEffect } from "react";
1313
import { useParams } from "react-router-dom";
14+
import { useStatusPageFetch } from "../../StatusPage/Status/Hooks/useStatusPageFetch";
1415
import { useCreateStatusPage } from "../../StatusPage/Create/Hooks/useCreateStatusPage";
1516
import { useLocation } from "react-router-dom";
1617
import { statusPageValidation } from "../../../Validation/validation";
@@ -20,20 +21,25 @@ import { useNavigate } from "react-router-dom";
2021

2122
const CreateStatus = () => {
2223
const theme = useTheme();
23-
const { monitorId } = useParams();
24+
const { monitorId, url } = useParams();
2425
const navigate = useNavigate();
2526
const location = useLocation();
26-
const isCreate = location.pathname.startsWith("/distributed-uptime/status/create");
27+
const isCreate = location.pathname.startsWith("/status/distributed/create");
2728
const [createStatusPage, isLoading, networkError] = useCreateStatusPage(isCreate);
29+
30+
const [statusPage, statusPageMonitors, statusPageIsLoading, statusPageNetworkError] =
31+
useStatusPageFetch(isCreate, url);
32+
2833
const BREADCRUMBS = [
2934
{ name: "distributed uptime", path: "/distributed-uptime" },
3035
{ name: "details", path: `/distributed-uptime/${monitorId}` },
31-
{ name: "create status page", path: `` },
36+
{ name: isCreate ? "create status page" : "edit status page", path: `` },
3237
];
3338
// Local state
3439
const [form, setForm] = useState({
40+
type: "distributed",
3541
isPublished: false,
36-
url: Math.floor(Math.random() * 1000000).toFixed(0),
42+
url: url ?? Math.floor(Math.random() * 1000000).toFixed(0),
3743
logo: undefined,
3844
companyName: "",
3945
monitors: [monitorId],
@@ -87,8 +93,9 @@ const CreateStatus = () => {
8793
if (typeof error === "undefined") {
8894
const success = await createStatusPage({ form: formToSubmit });
8995
if (success) {
90-
createToast({ body: "Status page created successfully" });
91-
navigate(`/distributed-uptime/status/${form.url}`);
96+
const verb = isCreate ? "created" : "updated";
97+
createToast({ body: `Status page ${verb} successfully` });
98+
navigate(`/status/distributed/${form.url}`);
9299
}
93100
return;
94101
}
@@ -99,6 +106,36 @@ const CreateStatus = () => {
99106
setErrors((prev) => ({ ...prev, ...newErrors }));
100107
};
101108

109+
// If we are configuring, populate fields
110+
useEffect(() => {
111+
if (isCreate) return;
112+
if (typeof statusPage === "undefined") {
113+
return;
114+
}
115+
116+
let newLogo = undefined;
117+
if (statusPage.logo) {
118+
newLogo = {
119+
src: `data:${statusPage.logo.contentType};base64,${statusPage.logo.data}`,
120+
name: "logo",
121+
type: statusPage.logo.contentType,
122+
size: null,
123+
};
124+
}
125+
126+
setForm((prev) => {
127+
return {
128+
...prev,
129+
companyName: statusPage?.companyName,
130+
isPublished: statusPage?.isPublished,
131+
timezone: statusPage?.timezone,
132+
monitors: statusPageMonitors.map((monitor) => monitor._id),
133+
color: statusPage?.color,
134+
logo: newLogo,
135+
};
136+
});
137+
}, [isCreate, statusPage, statusPageMonitors]);
138+
102139
return (
103140
<Stack gap={theme.spacing(10)}>
104141
<Breadcrumbs list={BREADCRUMBS} />
@@ -159,6 +196,7 @@ const CreateStatus = () => {
159196
name="url"
160197
type="url"
161198
label="Your status page address"
199+
disabled={!isCreate}
162200
value={form.url}
163201
onChange={handleFormChange}
164202
helperText={errors["url"]}

Client/src/Pages/DistributedUptime/Status/Hooks/useStatusPageFetchByUrl.jsx Client/src/Pages/DistributedUptimeStatus/Status/Hooks/useStatusPageFetchByUrl.jsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ const useStatusPageFetchByUrl = ({ url }) => {
1313
useEffect(() => {
1414
const fetchStatusPageByUrl = async () => {
1515
try {
16-
const response = await networkService.getStatusPageByUrl({ authToken, url });
16+
const response = await networkService.getStatusPageByUrl({
17+
authToken,
18+
url,
19+
type: "distributed",
20+
});
1721
if (!response?.data?.data) return;
1822
const statusPage = response.data.data;
1923
setStatusPage(statusPage);

Client/src/Pages/DistributedUptime/Status/index.jsx Client/src/Pages/DistributedUptimeStatus/Status/index.jsx

+11-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
//Components
2-
import DistributedUptimeMap from "../Details/Components/DistributedUptimeMap";
2+
import DistributedUptimeMap from "../../DistributedUptime/Details/Components/DistributedUptimeMap";
33
import Breadcrumbs from "../../../Components/Breadcrumbs";
44
import { Stack, Typography } from "@mui/material";
5-
import DeviceTicker from "../Details/Components/DeviceTicker";
6-
import DistributedUptimeResponseChart from "../Details/Components/DistributedUptimeResponseChart";
7-
import NextExpectedCheck from "../Details/Components/NextExpectedCheck";
8-
import Footer from "../Details/Components/Footer";
9-
import StatBoxes from "../Details/Components/StatBoxes";
5+
import DeviceTicker from "../../DistributedUptime/Details/Components/DeviceTicker";
6+
import DistributedUptimeResponseChart from "../../DistributedUptime/Details/Components/DistributedUptimeResponseChart";
7+
import NextExpectedCheck from "../../DistributedUptime/Details/Components/NextExpectedCheck";
8+
import Footer from "../../DistributedUptime/Details/Components/Footer";
9+
import StatBoxes from "../../DistributedUptime/Details/Components/StatBoxes";
1010
import ControlsHeader from "../../StatusPage/Status/Components/ControlsHeader";
1111
import MonitorTimeFrameHeader from "../../../Components/MonitorTimeFrameHeader";
1212
import GenericFallback from "../../../Components/GenericFallback";
@@ -16,15 +16,15 @@ import SkeletonLayout from "./Components/Skeleton";
1616
import { useTheme } from "@mui/material/styles";
1717
import { useState } from "react";
1818
import { useParams } from "react-router-dom";
19-
import { useSubscribeToDetails } from "../Details/Hooks/useSubscribeToDetails";
19+
import { useSubscribeToDetails } from "../../DistributedUptime/Details/Hooks/useSubscribeToDetails";
2020
import { useStatusPageFetchByUrl } from "./Hooks/useStatusPageFetchByUrl";
2121
import { useStatusPageDelete } from "../../StatusPage/Status/Hooks/useStatusPageDelete";
2222
import { useNavigate } from "react-router-dom";
2323
import { useLocation } from "react-router-dom";
2424
const DistributedUptimeStatus = () => {
2525
const { url } = useParams();
2626
const location = useLocation();
27-
const isPublic = location.pathname.startsWith("/distributed-uptime/status/public");
27+
const isPublic = location.pathname.startsWith("/status/distributed/public");
2828

2929
// Local State
3030
const [dateRange, setDateRange] = useState("day");
@@ -84,7 +84,7 @@ const DistributedUptimeStatus = () => {
8484
}
8585

8686
// Done loading, a status page exists but is not public
87-
if (!statusPageIsLoading && statusPage.isPublished === false) {
87+
if (!statusPageIsLoading && isPublic && statusPage.isPublished === false) {
8888
return (
8989
<Stack sx={sx}>
9090
<GenericFallback>
@@ -141,6 +141,8 @@ const DistributedUptimeStatus = () => {
141141
isDeleting={isDeleting}
142142
isDeleteOpen={isDeleteOpen}
143143
setIsDeleteOpen={setIsDeleteOpen}
144+
url={url}
145+
type="distributed"
144146
/>
145147
<StatBoxes
146148
monitor={monitor}

Client/src/Pages/StatusPage/Create/Components/Tabs/Settings.jsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import timezones from "../../../../../Utils/timezones.json";
1515
import PropTypes from "prop-types";
1616

1717
const TabSettings = ({
18+
isCreate,
1819
tabValue,
1920
form,
2021
handleFormChange,
@@ -68,8 +69,8 @@ const TabSettings = ({
6869
id="url"
6970
name="url"
7071
type="url"
72+
disabled={!isCreate}
7173
label="Your status page address"
72-
disabled
7374
value={form.url}
7475
onChange={handleFormChange}
7576
/>
@@ -128,6 +129,7 @@ const TabSettings = ({
128129
};
129130

130131
TabSettings.propTypes = {
132+
isCreate: PropTypes.bool,
131133
tabValue: PropTypes.string,
132134
form: PropTypes.object,
133135
handleFormChange: PropTypes.func,

Client/src/Pages/StatusPage/Create/Components/Tabs/index.jsx

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Content from "./Content";
88
import PropTypes from "prop-types";
99

1010
const Tabs = ({
11+
isCreate,
1112
form,
1213
errors,
1314
monitors,
@@ -47,6 +48,7 @@ const Tabs = ({
4748
progress={progress}
4849
removeLogo={removeLogo}
4950
errors={errors}
51+
isCreate={isCreate}
5052
/>
5153
) : (
5254
<Content

Client/src/Pages/StatusPage/Create/Hooks/useCreateStatusPage.jsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { networkService } from "../../../../main";
33
import { useSelector } from "react-redux";
44
import { createToast } from "../../../../Utils/toastUtils";
55

6-
const useCreateStatusPage = (isCreate) => {
6+
const useCreateStatusPage = (isCreate, url) => {
77
const { authToken, user } = useSelector((state) => state.auth);
88

99
const [isLoading, setIsLoading] = useState(false);
1010
const [networkError, setNetworkError] = useState(false);
1111
const createStatusPage = async ({ form }) => {
1212
setIsLoading(true);
1313
try {
14-
await networkService.createStatusPage({ authToken, user, form, isCreate });
14+
await networkService.createStatusPage({ authToken, user, form, isCreate, url });
1515
return true;
1616
} catch (error) {
1717
setNetworkError(true);

Client/src/Pages/StatusPage/Create/index.jsx

+16-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { createToast } from "../../../Utils/toastUtils";
1414
import { useNavigate } from "react-router-dom";
1515
import { useLocation } from "react-router-dom";
1616
import { useStatusPageFetch } from "../Status/Hooks/useStatusPageFetch";
17+
import { useParams } from "react-router-dom";
1718

1819
//Constants
1920
const TAB_LIST = ["General settings", "Contents"];
@@ -24,16 +25,18 @@ const ERROR_TAB_MAPPING = [
2425
];
2526

2627
const CreateStatusPage = () => {
28+
const { url } = useParams();
2729
//Local state
2830
const [tab, setTab] = useState(0);
2931
const [progress, setProgress] = useState({ value: 0, isLoading: false });
3032
const [form, setForm] = useState({
3133
isPublished: false,
3234
companyName: "",
33-
url: "/status/public",
35+
url: url ?? Math.floor(Math.random() * 1000000).toFixed(0),
3436
logo: undefined,
3537
timezone: "America/Toronto",
3638
color: "#4169E1",
39+
type: "uptime",
3740
monitors: [],
3841
showCharts: true,
3942
showUptimePercentage: true,
@@ -45,16 +48,17 @@ const CreateStatusPage = () => {
4548

4649
// Setup
4750
const location = useLocation();
48-
const isCreate = location.pathname === "/status/create";
51+
const isCreate = location.pathname === "/status/uptime/create";
4952

5053
//Utils
5154
const theme = useTheme();
5255
const [monitors, isLoading, networkError] = useMonitorsFetch();
5356
const [createStatusPage, createStatusIsLoading, createStatusPageNetworkError] =
5457
useCreateStatusPage(isCreate);
5558
const navigate = useNavigate();
59+
5660
const [statusPage, statusPageMonitors, statusPageIsLoading, statusPageNetworkError] =
57-
useStatusPageFetch(isCreate);
61+
useStatusPageFetch(isCreate, url);
5862

5963
// Handlers
6064
const handleFormChange = (e) => {
@@ -131,7 +135,7 @@ const CreateStatusPage = () => {
131135
const success = await createStatusPage({ form });
132136
if (success) {
133137
createToast({ body: "Status page created successfully" });
134-
navigate("/status");
138+
navigate(`/status/uptime/${form.url}`);
135139
}
136140
return;
137141
}
@@ -141,7 +145,6 @@ const CreateStatusPage = () => {
141145
newErrors[err.path[0]] = err.message;
142146
});
143147
setErrors((prev) => ({ ...prev, ...newErrors }));
144-
145148
const errorTabs = Object.keys(newErrors).map((err) => {
146149
return ERROR_TAB_MAPPING.findIndex((tab) => tab.includes(err));
147150
});
@@ -150,6 +153,13 @@ const CreateStatusPage = () => {
150153
if (errorTabs.some((errorTab) => errorTab === tab)) {
151154
return;
152155
}
156+
157+
// If we get -1, there's an unknown error
158+
if (errorTabs[0] === -1) {
159+
createToast({ body: "Unknown error" });
160+
return;
161+
}
162+
153163
// Otherwise go to tab with error
154164
setTab(errorTabs[0]);
155165
};
@@ -218,6 +228,7 @@ const CreateStatusPage = () => {
218228
tab={tab}
219229
setTab={setTab}
220230
TAB_LIST={TAB_LIST}
231+
isCreate={isCreate}
221232
/>
222233
<Stack
223234
direction="row"

0 commit comments

Comments
 (0)