- Add GetStatusPageByUrl to networkservice
- Make static status page and actual status config page toggleable - Remove the sidebar nested menu item entry for status page tabs - Pass context to status config page which contains general settings and contents tabs - Status config page is now one submission and one validation
This commit is contained in:
1 parent
5045eafb8e
commit
219bcd4361
7 files changed
+120
-108
No files matched your search
+8
-1
@@ -151,7 +151,14 @@ function App() {
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="status/create"
|
||||
element={
|
||||
<ProtectedRoute
|
||||
Component={CreateStatus}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="integrations"
|
||||
element={<ProtectedRoute Component={Integrations} />}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { useState, useCallback, useContext } from "react";
|
||||
import { Button, Box, Stack, Typography } from "@mui/material";
|
||||
import { ConfigBox } from "../../../Pages/Settings/styled";
|
||||
import { useTheme } from "@emotion/react";
|
||||
@@ -8,20 +8,13 @@ import { buildErrors } from "../../../Validation/error";
|
||||
import Card from "./Card";
|
||||
import update from "immutability-helper";
|
||||
import Checkbox from "../../Inputs/Checkbox";
|
||||
import { StatusFormContext } from "../../../Pages/Status/TabContext";
|
||||
|
||||
const ContentPanel = ({errors, setErrors}) => {
|
||||
const ContentPanel = () => {
|
||||
const theme = useTheme();
|
||||
const { form, setForm, errors, setErrors } = useContext(StatusFormContext);
|
||||
const [cards, setCards] = useState(form.monitors);
|
||||
|
||||
// Clears specific error from errors state
|
||||
const clearError = (err) => {
|
||||
setErrors((prev) => {
|
||||
const updatedErrors = { ...prev };
|
||||
if (updatedErrors[err]) delete updatedErrors[err];
|
||||
return updatedErrors;
|
||||
});
|
||||
};
|
||||
|
||||
const moveCard = useCallback(
|
||||
(dragIndex, hoverIndex) => {
|
||||
const dragCard = cards[dragIndex];
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useRef, useContext } from "react";
|
||||
import { Button, Box, Stack, Typography } from "@mui/material";
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
import { ConfigBox } from "../../../Pages/Settings/styled";
|
||||
import Checkbox from "../../Inputs/Checkbox";
|
||||
import { useTheme } from "@emotion/react";
|
||||
@@ -17,13 +17,12 @@ import { formatBytes } from "../../../Utils/fileUtils";
|
||||
import ProgressUpload from "../../ProgressBars";
|
||||
import ImageIcon from "@mui/icons-material/Image";
|
||||
import { HttpAdornment } from "../../Inputs/TextInput/Adornments";
|
||||
import { hasValidationErrors } from "../../../Validation/error";
|
||||
import { StatusFormContext } from "../../../Pages/Status/TabContext";
|
||||
|
||||
const GeneralSettingsPanel = ({errors, setErrors}) => {
|
||||
const GeneralSettingsPanel = () => {
|
||||
const theme = useTheme();
|
||||
const [logo, setLogo] = useState();
|
||||
const { form, setForm } = useContext(StatusFormContext);
|
||||
const { form, setForm, errors, setErrors } = useContext(StatusFormContext);
|
||||
const [progress, setProgress] = useState({ value: 0, isLoading: false });
|
||||
const intervalRef = useRef(null);
|
||||
|
||||
|
||||
@@ -4,37 +4,21 @@ import { Box, Tab, useTheme, Stack, Button } from "@mui/material";
|
||||
import TabContext from "@mui/lab/TabContext";
|
||||
import TabList from "@mui/lab/TabList";
|
||||
import GeneralSettingsPanel from "../../../Components/TabPanels/Status/GeneralSettingsPanel";
|
||||
import {
|
||||
capitalizeFirstLetter,
|
||||
} from "../../../Utils/stringUtils";
|
||||
import { capitalizeFirstLetter } from "../../../Utils/stringUtils";
|
||||
import ContentPanel from "../../../Components/TabPanels/Status/ContentPanel";
|
||||
import {
|
||||
publicPageGeneralSettingsValidation,
|
||||
} from "../../../Validation/validation";
|
||||
import { publicPageGeneralSettingsValidation } from "../../../Validation/validation";
|
||||
import { hasValidationErrors } from "../../../Validation/error";
|
||||
import { StatusFormProvider } from "../TabContext"
|
||||
|
||||
import { StatusFormProvider } from "../TabContext";
|
||||
/**
|
||||
* CreateStatus page renders a page with tabs for general settings and contents.
|
||||
* @param {string} [props.open] - Specifies the initially open tab: 'general settings' or 'content'.
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
|
||||
const CreateStatus = ({ open = "general-settings" }) => {
|
||||
const CreateStatus = ({ open = "general-settings", initForm }) => {
|
||||
const theme = useTheme();
|
||||
let tabList = ["General Settings", "Contents"];
|
||||
const [errors, setErrors] = useState({});
|
||||
const tab = open
|
||||
.split("-")
|
||||
.map((a) => capitalizeFirstLetter(a))
|
||||
.join(" ");
|
||||
|
||||
const [tabIdx, setTabIdx] = useState(tabList.indexOf(tab));
|
||||
|
||||
const handleTabChange = (event, newTab) => {
|
||||
setTabIdx(tabList.indexOf(newTab));
|
||||
};
|
||||
|
||||
const [form, setForm] = useState({
|
||||
companyName: "",
|
||||
url: "",
|
||||
@@ -45,13 +29,23 @@ const CreateStatus = ({ open = "general-settings" }) => {
|
||||
logo: null,
|
||||
monitors: [],
|
||||
showUptimePercentage: false,
|
||||
showBarcode: false
|
||||
showBarcode: false,
|
||||
});
|
||||
const tab = open
|
||||
.split("-")
|
||||
.map((a) => capitalizeFirstLetter(a))
|
||||
.join(" ");
|
||||
|
||||
const [tabIdx, setTabIdx] = useState(tabList.indexOf(tab));
|
||||
|
||||
const handleTabChange = (event, newTab) => {
|
||||
setTabIdx(tabList.indexOf(newTab));
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
//validate rest of the form
|
||||
delete form.logo;
|
||||
if (hasValidationErrors(form, publicPageGeneralSettingsValidation, setErrors)) {
|
||||
if (hasValidationErrors(form, publicPageGeneralSettingsValidation, setErrors)) {
|
||||
return;
|
||||
}
|
||||
// //validate image field, double check if it is required
|
||||
@@ -59,9 +53,9 @@ const CreateStatus = ({ open = "general-settings" }) => {
|
||||
// { type: logo?.type ?? null, size: logo?.size ?? null },
|
||||
// logoImageValidation
|
||||
// );
|
||||
if (error) return;
|
||||
// if (error) return;
|
||||
form.logo = { ...logo, size: formatBytes(logo?.size) };
|
||||
};
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setTabIdx(tabList.indexOf(tab));
|
||||
@@ -112,17 +106,13 @@ const CreateStatus = ({ open = "general-settings" }) => {
|
||||
))}
|
||||
</TabList>
|
||||
</Box>
|
||||
<StatusFormProvider>
|
||||
{tabIdx == 0 ? (
|
||||
<GeneralSettingsPanel
|
||||
errors={errors}
|
||||
setErrors={setErrors}/>
|
||||
) : (
|
||||
<ContentPanel
|
||||
ss errors={errors}
|
||||
setErrors={setErrors}
|
||||
/>
|
||||
)}
|
||||
<StatusFormProvider
|
||||
form={initForm && Object.keys(initForm) > 0 ? initForm : form}
|
||||
setForm={setForm}
|
||||
errors={errors}
|
||||
setErrors={setErrors}
|
||||
>
|
||||
{tabIdx == 0 ? <GeneralSettingsPanel /> : <ContentPanel />}
|
||||
</StatusFormProvider>
|
||||
</TabContext>
|
||||
<Stack
|
||||
|
||||
@@ -1,29 +1,18 @@
|
||||
import { createContext, useState, React } from 'react';
|
||||
import { createContext, useState, React } from "react";
|
||||
|
||||
const StatusFormContext = createContext({
|
||||
form: {},
|
||||
setForm: ()=>{},
|
||||
form: {},
|
||||
setForm: () => {},
|
||||
errors: {},
|
||||
setErrors: () => {},
|
||||
});
|
||||
|
||||
const StatusFormProvider = ({ children }) => {
|
||||
const [form, setForm] = useState({
|
||||
companyName: "",
|
||||
url: "",
|
||||
timezone: "America/Toronto",
|
||||
color: "#4169E1",
|
||||
//which fields matching below?
|
||||
publish: false,
|
||||
logo: null,
|
||||
monitors: [],
|
||||
showUptimePercentage: false,
|
||||
showBarcode: false,
|
||||
});
|
||||
|
||||
const StatusFormProvider = ({ form, setForm, errors, setErrors, children }) => {
|
||||
return (
|
||||
<StatusFormContext.Provider value={{ form, setForm}}>
|
||||
<StatusFormContext.Provider value={{ form, setForm, errors, setErrors }}>
|
||||
{children}
|
||||
</StatusFormContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export { StatusFormContext, StatusFormProvider };
|
||||
export { StatusFormContext, StatusFormProvider };
|
||||
@@ -2,50 +2,72 @@ import { useTheme } from "@emotion/react";
|
||||
import Fallback from "../../Components/Fallback";
|
||||
import { Box, Button, Stack } from "@mui/material";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import CreateStatus from "./CreateStatus";
|
||||
import { useEffect } from "react";
|
||||
import { networkService } from "../../main";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const Status = () => {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const navigate = useNavigate();
|
||||
const {authToken} = useSelector((state) => state.auth);
|
||||
const {apiBaseUrl} = useSelector((state) => state.settings);
|
||||
let initForm = {}
|
||||
|
||||
useEffect(() => {
|
||||
const getStatusPage = async () => {
|
||||
let config = { authToken: authToken, url: apiBaseUrl };
|
||||
initForm = await networkService.getStatusPageByUrl(config);
|
||||
};
|
||||
getStatusPage();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="status"
|
||||
sx={{
|
||||
':has(> [class*="fallback__"])': {
|
||||
position: "relative",
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderStyle: "dashed",
|
||||
backgroundColor: theme.palette.background.main,
|
||||
overflow: "hidden",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Fallback
|
||||
title="status page"
|
||||
checks={[
|
||||
"Share your uptime publicly",
|
||||
"Keep your users informed about incidents",
|
||||
"Build trust with your customers",
|
||||
]}
|
||||
/>
|
||||
<Stack
|
||||
alignItems="center"
|
||||
mt={theme.spacing(10)}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
navigate("/status/create");
|
||||
<>
|
||||
{Object.keys(initForm).length===0? (
|
||||
<Box
|
||||
className="status"
|
||||
sx={{
|
||||
':has(> [class*="fallback__"])': {
|
||||
position: "relative",
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderStyle: "dashed",
|
||||
backgroundColor: theme.palette.background.main,
|
||||
overflow: "hidden",
|
||||
},
|
||||
}}
|
||||
sx={{ fontWeight: 500 }}
|
||||
>
|
||||
Let's create your status page
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
<Fallback
|
||||
title="status page"
|
||||
checks={[
|
||||
"Share your uptime publicly",
|
||||
"Keep your users informed about incidents",
|
||||
"Build trust with your customers",
|
||||
]}
|
||||
/>
|
||||
<Stack
|
||||
alignItems="center"
|
||||
mt={theme.spacing(10)}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
navigate("/status/create");
|
||||
}}
|
||||
sx={{ fontWeight: 500 }}
|
||||
>
|
||||
Let's create your status page
|
||||
</Button>
|
||||
</Stack>
|
||||
</Box>
|
||||
) : (
|
||||
<CreateStatus initForm={initForm}/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Status;
|
||||
@@ -846,6 +846,18 @@ class NetworkService {
|
||||
"https://api.github.com/repos/bluewave-labs/bluewave-uptime/releases/latest"
|
||||
);
|
||||
}
|
||||
|
||||
async getStatusPageByUrl(config){
|
||||
const params = new URLSearchParams();
|
||||
params.append("url", encodeURIComponent(config.url));
|
||||
|
||||
return this.axiosInstance.get(`/status-page?${params.toString()}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default NetworkService;
|
||||
|
||||
Reference in new issue
Block a user