mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-31 06:10:07 -06:00
- Remove Color picker OOTB label, add custom label
- Add label for logo - Replace react beautiful dnd with hello-pangea to fix the "defaultProps will not be supported " console warning due to react beatuful dnd deprecation - Add all the checkboxes and respective validation - Remove subdomain from UI display - Pass Handlers as context for both tabs to ease code scalability - All Status page fields are now ready for submission
This commit is contained in:
110
Client/package-lock.json
generated
110
Client/package-lock.json
generated
@@ -11,6 +11,7 @@
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@fontsource/roboto": "^5.0.13",
|
||||
"@hello-pangea/dnd": "^17.0.0",
|
||||
"@mui/icons-material": "6.3.1",
|
||||
"@mui/lab": "6.0.0-beta.22",
|
||||
"@mui/material": "6.3.1",
|
||||
@@ -25,7 +26,6 @@
|
||||
"jwt-decode": "^4.0.0",
|
||||
"mui-color-input": "^5.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "9.2.0",
|
||||
"react-router": "^6.23.0",
|
||||
@@ -995,6 +995,24 @@
|
||||
"@hapi/hoek": "^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@hello-pangea/dnd": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-17.0.0.tgz",
|
||||
"integrity": "sha512-LDDPOix/5N0j5QZxubiW9T0M0+1PR0rTDWeZF5pu1Tz91UQnuVK4qQ/EjY83Qm2QeX0eM8qDXANfDh3VVqtR4Q==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.25.6",
|
||||
"css-box-model": "^1.2.1",
|
||||
"memoize-one": "^6.0.0",
|
||||
"raf-schd": "^4.0.3",
|
||||
"react-redux": "^9.1.2",
|
||||
"redux": "^5.0.1",
|
||||
"use-memo-one": "^1.1.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
|
||||
@@ -2370,15 +2388,6 @@
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/hoist-non-react-statics": {
|
||||
"version": "3.3.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz",
|
||||
"integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==",
|
||||
"dependencies": {
|
||||
"@types/react": "*",
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
|
||||
@@ -2411,25 +2420,6 @@
|
||||
"@types/react": "^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-redux": {
|
||||
"version": "7.1.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.34.tgz",
|
||||
"integrity": "sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==",
|
||||
"dependencies": {
|
||||
"@types/hoist-non-react-statics": "^3.3.0",
|
||||
"@types/react": "*",
|
||||
"hoist-non-react-statics": "^3.3.0",
|
||||
"redux": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-redux/node_modules/redux": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-transition-group": {
|
||||
"version": "4.4.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
|
||||
@@ -4978,9 +4968,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
@@ -5477,62 +5467,6 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-beautiful-dnd": {
|
||||
"version": "13.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz",
|
||||
"integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==",
|
||||
"deprecated": "react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2",
|
||||
"css-box-model": "^1.2.0",
|
||||
"memoize-one": "^5.1.1",
|
||||
"raf-schd": "^4.0.2",
|
||||
"react-redux": "^7.2.0",
|
||||
"redux": "^4.0.4",
|
||||
"use-memo-one": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.5 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-beautiful-dnd/node_modules/react-is": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
|
||||
},
|
||||
"node_modules/react-beautiful-dnd/node_modules/react-redux": {
|
||||
"version": "7.2.9",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
|
||||
"integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.15.4",
|
||||
"@types/react-redux": "^7.1.20",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^17.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.3 || ^17 || ^18"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-beautiful-dnd/node_modules/redux": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@fontsource/roboto": "^5.0.13",
|
||||
"@hello-pangea/dnd": "^17.0.0",
|
||||
"@mui/icons-material": "6.3.1",
|
||||
"@mui/lab": "6.0.0-beta.22",
|
||||
"@mui/material": "6.3.1",
|
||||
@@ -28,7 +29,6 @@
|
||||
"jwt-decode": "^4.0.0",
|
||||
"mui-color-input": "^5.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "9.2.0",
|
||||
"react-router": "^6.23.0",
|
||||
|
||||
@@ -115,7 +115,7 @@ Checkbox.propTypes = {
|
||||
label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
|
||||
size: PropTypes.oneOf(["small", "medium", "large"]),
|
||||
isChecked: PropTypes.bool.isRequired,
|
||||
value: PropTypes.string,
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
|
||||
onChange: PropTypes.func,
|
||||
isDisabled: PropTypes.bool,
|
||||
alignSelf: PropTypes.bool,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { MuiColorInput } from "mui-color-input";
|
||||
/**
|
||||
*
|
||||
* @param {*} id The ID of the component
|
||||
* @param {*} label The Label of the component
|
||||
* @param {*} value The color value of the component
|
||||
* @param {*} error The error of the component
|
||||
* @param {*} onChange The Change handler function
|
||||
@@ -15,7 +14,6 @@ import { MuiColorInput } from "mui-color-input";
|
||||
* Example usage:
|
||||
* <ColorPicker
|
||||
* id="color"
|
||||
* label="Color"
|
||||
* value={form.color}
|
||||
* error={errors["color"]}
|
||||
* onChange={handleColorChange}
|
||||
@@ -23,7 +21,7 @@ import { MuiColorInput } from "mui-color-input";
|
||||
* >
|
||||
* </ColorPicker>
|
||||
*/
|
||||
const ColorPicker = ({ id, label, value, error, onChange, onBlur }) => {
|
||||
const ColorPicker = ({ id, value, error, onChange, onBlur }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Stack gap={theme.spacing(4)}>
|
||||
@@ -31,7 +29,6 @@ const ColorPicker = ({ id, label, value, error, onChange, onBlur }) => {
|
||||
format="hex"
|
||||
value={value}
|
||||
id={id}
|
||||
label={label}
|
||||
onChange={onChange}
|
||||
onBlur={onBlur}
|
||||
/>
|
||||
@@ -54,7 +51,6 @@ const ColorPicker = ({ id, label, value, error, onChange, onBlur }) => {
|
||||
|
||||
ColorPicker.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
value: PropTypes.string,
|
||||
error: PropTypes.string,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
|
||||
@@ -1,30 +1,37 @@
|
||||
import { useState, useContext, useEffect } from "react";
|
||||
import { Button, Box, Stack, Typography } from "@mui/material";
|
||||
import ConfigBox from "../../../Components/ConfigBox";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import TabPanel from "@mui/lab/TabPanel";
|
||||
|
||||
import ConfigBox from "../../../Components/ConfigBox";
|
||||
import { StatusFormContext } from "../../../Pages/Status/CreateStatusContext";
|
||||
import { useSelector } from "react-redux";
|
||||
import { logger } from "../../../Utils/Logger"
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { networkService } from "../../../main";
|
||||
import ServersList from "./ServersList";
|
||||
|
||||
import Checkbox from "../../Inputs/Checkbox";
|
||||
|
||||
/**
|
||||
* Content Panel is used to compose the second part of the status page
|
||||
* for the servers/monitors to watch for in its public page presence and some
|
||||
* for the servers/monitors to watch for in its public page presence and some
|
||||
* other server related configurations etc
|
||||
*
|
||||
*
|
||||
*/
|
||||
const ContentPanel = () => {
|
||||
const theme = useTheme();
|
||||
const { form, setForm, errors, setErrors } = useContext(StatusFormContext);
|
||||
const {
|
||||
form,
|
||||
setForm,
|
||||
errors,
|
||||
setErrors,
|
||||
handleBlur,
|
||||
handelCheckboxChange,
|
||||
} = useContext(StatusFormContext);
|
||||
const [cards, setCards] = useState([]);
|
||||
const {user, authToken } = useSelector((state) => state.auth);
|
||||
const { user, authToken } = useSelector((state) => state.auth);
|
||||
const [monitors, setMonitors] = useState([]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const fetchMonitors = async () => {
|
||||
try {
|
||||
@@ -34,10 +41,10 @@ const ContentPanel = () => {
|
||||
limit: null, // donot return any checks for the monitors
|
||||
types: ["http"], // status page is available only for the uptime type
|
||||
});
|
||||
if(response.data.data.monitors.length==0){
|
||||
setErrors({monitors: "Please config monitors to setup status page"})
|
||||
if (response.data.data.monitors.length == 0) {
|
||||
setErrors({ monitors: "Please config monitors to setup status page" });
|
||||
}
|
||||
const fullMonitors = response.data.data.monitors ;
|
||||
const fullMonitors = response.data.data.monitors;
|
||||
setMonitors(fullMonitors);
|
||||
if (form.monitors.length > 0) {
|
||||
const initiCards = form.monitors.map((mid, idx) => ({
|
||||
@@ -52,12 +59,12 @@ const ContentPanel = () => {
|
||||
createToast({ body: "Failed to fetch monitors data" });
|
||||
logger.error("Failed to fetch monitors", error);
|
||||
}
|
||||
};
|
||||
};
|
||||
fetchMonitors();
|
||||
}, [user, authToken]);
|
||||
const handleAddNew = () => {
|
||||
if (cards.length === monitors.length) return;
|
||||
const newCards = [...cards, { id: "" + Math.random(),val:{} }];
|
||||
const newCards = [...cards, { id: "" + Math.random(), val: {} }];
|
||||
setCards(newCards);
|
||||
};
|
||||
const removeCard = (id) => {
|
||||
@@ -96,10 +103,10 @@ const ContentPanel = () => {
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Box
|
||||
<Stack
|
||||
className="status-contents-server-list"
|
||||
sx={{
|
||||
margin: theme.spacing(20),
|
||||
margin: theme.spacing(6),
|
||||
border: "solid",
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderColor: theme.palette.border.light,
|
||||
@@ -129,22 +136,16 @@ const ContentPanel = () => {
|
||||
Add new
|
||||
</Button>
|
||||
</Stack>
|
||||
{cards.length > 0 && (
|
||||
<Stack
|
||||
id="monitors"
|
||||
direction="column"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<ServersList
|
||||
monitors={monitors}
|
||||
cards={cards}
|
||||
setCards={setCards}
|
||||
form={form}
|
||||
setForm={setForm}
|
||||
removeItem={removeCard}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<ServersList
|
||||
monitors={monitors}
|
||||
cards={cards}
|
||||
setCards={setCards}
|
||||
form={form}
|
||||
setForm={setForm}
|
||||
removeItem={removeCard}
|
||||
/>
|
||||
|
||||
{errors["monitors"] && (
|
||||
<Typography
|
||||
component="span"
|
||||
@@ -157,7 +158,7 @@ const ContentPanel = () => {
|
||||
{errors["monitors"]}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
|
||||
<ConfigBox>
|
||||
@@ -167,23 +168,22 @@ const ContentPanel = () => {
|
||||
<Typography component="p">Show more details on the status page</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
{/* <Stack sx={{margin: theme.spacing(20)}}
|
||||
>
|
||||
<Stack sx={{ margin: theme.spacing(6) }}>
|
||||
<Checkbox
|
||||
id="show-barcode"
|
||||
id="showBarcode"
|
||||
label={`Show Barcode`}
|
||||
isChecked={form.showBarcode}
|
||||
onChange={handleChange}
|
||||
onChange={handelCheckboxChange}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
<Checkbox
|
||||
id="show-uptime-percentage"
|
||||
id="showUptimePercentage"
|
||||
label={`Show Uptime Percentage`}
|
||||
isChecked={form.showUptimePercentage}
|
||||
onChange={handleChange}
|
||||
onChange={handelCheckboxChange}
|
||||
onBlur={handleBlur}
|
||||
/>
|
||||
</Stack> */}
|
||||
/>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
|
||||
@@ -1,38 +1,34 @@
|
||||
import { useState, useRef, useContext } from "react";
|
||||
import { Box, Button, Stack, Typography } from "@mui/material";
|
||||
import ConfigBox from "../../../Components/ConfigBox";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import TabPanel from "@mui/lab/TabPanel";
|
||||
import ImageIcon from "@mui/icons-material/Image";
|
||||
|
||||
import ConfigBox from "../../../Components/ConfigBox";
|
||||
import TextInput from "../../Inputs/TextInput";
|
||||
import ImageField from "../../Inputs/Image";
|
||||
import timezones from "../../../Utils/timezones.json";
|
||||
import Select from "../../Inputs/Select";
|
||||
import {
|
||||
logoImageValidation,
|
||||
publicPageGeneralSettingsValidation,
|
||||
} from "../../../Validation/validation";
|
||||
import { buildErrors } from "../../../Validation/error";
|
||||
import { logoImageValidation } from "../../../Validation/validation";
|
||||
import { formatBytes } from "../../../Utils/fileUtils";
|
||||
import ProgressUpload from "../../ProgressBars";
|
||||
import ImageIcon from "@mui/icons-material/Image";
|
||||
import { HttpAdornment } from "../../Inputs/TextInput/Adornments";
|
||||
import { StatusFormContext } from "../../../Pages/Status/CreateStatusContext";
|
||||
import ColorPicker from "../../Inputs/ColorPicker";
|
||||
import Checkbox from "../../Inputs/Checkbox";
|
||||
|
||||
/**
|
||||
* General settings panel is ued to compose part of the public static page
|
||||
* for general informations like company name, subdomain url, logo and color etc
|
||||
*/
|
||||
const GeneralSettingsPanel = () => {
|
||||
const theme = useTheme();
|
||||
const { form, setForm, errors, setErrors } = useContext(StatusFormContext);
|
||||
const theme = useTheme();
|
||||
const { form, setForm, errors, setErrors, handleBlur, handleChange, handelCheckboxChange } =
|
||||
useContext(StatusFormContext);
|
||||
const [logo, setLogo] = useState(form.logo);
|
||||
|
||||
const [progress, setProgress] = useState({ value: 0, isLoading: false });
|
||||
const intervalRef = useRef(null);
|
||||
const SUBDOAMIN_PREFIX =
|
||||
import.meta.env.VITE_STATUS_PAGE_SUBDOMAIN_PREFIX ?? "http://localhost/";
|
||||
const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";
|
||||
const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL ?? "status-page";
|
||||
|
||||
// Clears specific error from errors state
|
||||
const clearError = (err) => {
|
||||
@@ -59,29 +55,6 @@ const GeneralSettingsPanel = () => {
|
||||
...prev,
|
||||
color: newValue,
|
||||
}));
|
||||
}
|
||||
|
||||
const handleChange = (event) => {
|
||||
event.preventDefault();
|
||||
const { value, id, name } = event.target;
|
||||
setForm((prev) => ({
|
||||
...prev,
|
||||
[id ?? name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleBlur = (event) => {
|
||||
event.preventDefault();
|
||||
const { value, id } = event.target;
|
||||
const { error } = publicPageGeneralSettingsValidation.validate(
|
||||
{ [id]: value },
|
||||
{
|
||||
abortEarly: false,
|
||||
}
|
||||
);
|
||||
setErrors((prev) => {
|
||||
return buildErrors(prev, id, error);
|
||||
});
|
||||
};
|
||||
|
||||
const validateField = (toValidate, schema, name = "logo") => {
|
||||
@@ -105,7 +78,7 @@ const GeneralSettingsPanel = () => {
|
||||
name: pic.name,
|
||||
type: pic.type,
|
||||
size: pic.size,
|
||||
}
|
||||
};
|
||||
setProgress((prev) => ({ ...prev, isLoading: true }));
|
||||
setLogo(newLogo);
|
||||
setForm({ ...form, logo: newLogo });
|
||||
@@ -141,13 +114,14 @@ const GeneralSettingsPanel = () => {
|
||||
</Stack>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(6)}>
|
||||
{/* <Checkbox
|
||||
id="published-to-public"
|
||||
<Checkbox
|
||||
id="publish"
|
||||
label={`Published and visible to the public`}
|
||||
isChecked={form.publish}
|
||||
onChange={handleChange}
|
||||
value={form.publish}
|
||||
onChange={handelCheckboxChange}
|
||||
onBlur={handleBlur}
|
||||
/> */}
|
||||
/>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
|
||||
@@ -176,13 +150,7 @@ const GeneralSettingsPanel = () => {
|
||||
type="url"
|
||||
label="Your status page address"
|
||||
disabled
|
||||
value={form.url ?? STATUS_PAGE}
|
||||
startAdornment={
|
||||
<HttpAdornment
|
||||
prefix={SUBDOAMIN_PREFIX}
|
||||
https={false}
|
||||
/>
|
||||
}
|
||||
value={form.url ?? "/" + STATUS_PAGE}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
helperText={errors["url"]}
|
||||
@@ -210,46 +178,67 @@ const GeneralSettingsPanel = () => {
|
||||
items={timezones}
|
||||
error={errors["display-timezone"]}
|
||||
/>
|
||||
<ImageField
|
||||
id="logo"
|
||||
src={form.logo?.src ?? logo?.src}
|
||||
loading={progress.isLoading && progress.value !== 100}
|
||||
onChange={handleLogo}
|
||||
isRound={false}
|
||||
/>
|
||||
{progress.isLoading || progress.value !== 0 || errors["logo"] ? (
|
||||
<ProgressUpload
|
||||
icon={<ImageIcon />}
|
||||
label={logo?.name}
|
||||
size={formatBytes(logo?.size)}
|
||||
progress={progress.value}
|
||||
onClick={removeLogo}
|
||||
error={errors["logo"]}
|
||||
/>
|
||||
) : logo && logo.type ? (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
onClick={removeLogo}
|
||||
sx={{
|
||||
width: "100%",
|
||||
maxWidth: "200px",
|
||||
alignSelf: "center",
|
||||
}}
|
||||
<Stack direction={"column"}>
|
||||
<Typography
|
||||
component="h3"
|
||||
color={theme.palette.text.secondary}
|
||||
fontWeight={500}
|
||||
fontSize={13}
|
||||
sx={{ mb: theme.spacing(-2) }}
|
||||
>
|
||||
Remove Logo
|
||||
</Button>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<ColorPicker
|
||||
id="color"
|
||||
label="Color"
|
||||
value={form.color}
|
||||
error={errors["color"]}
|
||||
onChange={handleColorChange}
|
||||
onBlur={handleBlur}
|
||||
></ColorPicker>
|
||||
Logo
|
||||
</Typography>
|
||||
<ImageField
|
||||
id="logo"
|
||||
src={form.logo?.src ?? logo?.src}
|
||||
loading={progress.isLoading && progress.value !== 100}
|
||||
onChange={handleLogo}
|
||||
isRound={false}
|
||||
/>
|
||||
{progress.isLoading || progress.value !== 0 || errors["logo"] ? (
|
||||
<ProgressUpload
|
||||
icon={<ImageIcon />}
|
||||
label={logo?.name}
|
||||
size={formatBytes(logo?.size)}
|
||||
progress={progress.value}
|
||||
onClick={removeLogo}
|
||||
error={errors["logo"]}
|
||||
/>
|
||||
) : logo && logo.type ? (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
onClick={removeLogo}
|
||||
sx={{
|
||||
width: "100%",
|
||||
maxWidth: "200px",
|
||||
alignSelf: "center",
|
||||
}}
|
||||
>
|
||||
Remove Logo
|
||||
</Button>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</Stack>
|
||||
<Stack direction={"column"}>
|
||||
<Typography
|
||||
component="h3"
|
||||
color={theme.palette.text.secondary}
|
||||
fontWeight={500}
|
||||
fontSize={13}
|
||||
>
|
||||
Color
|
||||
</Typography>
|
||||
|
||||
<ColorPicker
|
||||
id="color"
|
||||
value={form.color}
|
||||
error={errors["color"]}
|
||||
onChange={handleColorChange}
|
||||
onBlur={handleBlur}
|
||||
></ColorPicker>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
</Stack>
|
||||
|
||||
@@ -3,12 +3,13 @@ import Search from "../../../../Inputs/Search";
|
||||
import {useState} from "react"
|
||||
import React from "react";
|
||||
import { Stack } from "@mui/material";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} id The Id of the Server component
|
||||
* @param {*} monitors The server monitors options
|
||||
* @param {*} value the option label of the server/monitor, namely the monitor name field
|
||||
* @param {*} value - Current input value for the Autocomplete
|
||||
* @param {*} removeItem The function used to remove a single server
|
||||
* @param {*} onChange The Change handler function to handle when the server value is changed
|
||||
* used to update the server(monitor) lists*
|
||||
@@ -49,9 +50,9 @@ const Server = ({ id, value, monitors, onChange, removeItem, dragHandleProps })
|
||||
Server.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
monitors: PropTypes.array.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
removeItem: PropTypes.function.isRequired,
|
||||
onChange: PropTypes.function.isRequired,
|
||||
value: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
||||
removeItem: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
dragHandleProps: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
|
||||
import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
|
||||
import Server from "./Server";
|
||||
import update from "immutability-helper";
|
||||
import PropTypes from "prop-types";
|
||||
import { Stack, useTheme } from "@mui/material";
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -16,7 +18,8 @@ import update from "immutability-helper";
|
||||
|
||||
|
||||
const ServersList = ({ monitors, cards, setCards, form, setForm, removeItem }) => {
|
||||
const grid = 8;
|
||||
const theme = useTheme()
|
||||
const grid = parseInt(theme.spacing(4));
|
||||
|
||||
const handleCardChange = (event, val) => {
|
||||
let newCards;
|
||||
@@ -63,11 +66,11 @@ const ServersList = ({ monitors, cards, setCards, form, setForm, removeItem }) =
|
||||
const getItemStyle = (isDragging, draggableStyle) => ({
|
||||
// some basic styles to make the items look a bit nicer
|
||||
userSelect: "none",
|
||||
padding: grid * 2,
|
||||
padding: grid,
|
||||
margin: `0 0 ${grid}px 0`,
|
||||
|
||||
// change background colour if dragging
|
||||
background: isDragging ? "lightgreen" : "lightgrey",
|
||||
background: isDragging ? "#D0D5DD" : "#F8F9F8",
|
||||
|
||||
// styles we need to apply on draggables
|
||||
...draggableStyle,
|
||||
@@ -76,17 +79,17 @@ const ServersList = ({ monitors, cards, setCards, form, setForm, removeItem }) =
|
||||
const getListStyle = (isDraggingOver) => ({
|
||||
background: isDraggingOver ? "lightblue" : "white",
|
||||
padding: grid,
|
||||
width: 550,
|
||||
});
|
||||
|
||||
return (
|
||||
<DragDropContext onDragEnd={handleDragEnd}>
|
||||
<Droppable droppableId="droppable">
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
<Stack
|
||||
{...provided.droppableProps}
|
||||
ref={provided.innerRef}
|
||||
style={getListStyle(snapshot.isDraggingOver)}
|
||||
sx={{...getListStyle(snapshot.isDraggingOver)}}
|
||||
|
||||
>
|
||||
{cards.map((item, index) => (
|
||||
<Draggable
|
||||
@@ -95,13 +98,13 @@ const ServersList = ({ monitors, cards, setCards, form, setForm, removeItem }) =
|
||||
index={index}
|
||||
>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
<Stack
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
style={getItemStyle(
|
||||
sx={{...getItemStyle(
|
||||
snapshot.isDragging,
|
||||
provided.draggableProps.style
|
||||
)}
|
||||
)}}
|
||||
>
|
||||
<Server
|
||||
key={index}
|
||||
@@ -112,12 +115,12 @@ const ServersList = ({ monitors, cards, setCards, form, setForm, removeItem }) =
|
||||
removeItem={removeItem}
|
||||
dragHandleProps={provided.dragHandleProps}
|
||||
/>
|
||||
</div>
|
||||
</Stack>
|
||||
)}
|
||||
</Draggable>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
</Stack>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
@@ -127,10 +130,10 @@ const ServersList = ({ monitors, cards, setCards, form, setForm, removeItem }) =
|
||||
ServersList.propTypes = {
|
||||
monitors: PropTypes.array.isRequired,
|
||||
cards: PropTypes.array.isRequired,
|
||||
setCards: PropTypes.function.isRequired,
|
||||
setCards: PropTypes.func.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
setForm: PropTypes.function.isRequired,
|
||||
removeItem: PropTypes.function.isRequired
|
||||
setForm: PropTypes.func.isRequired,
|
||||
removeItem: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default ServersList;
|
||||
|
||||
@@ -3,15 +3,17 @@ import { useState } from "react";
|
||||
import { Box, Tab, useTheme, Stack, Button } from "@mui/material";
|
||||
import TabContext from "@mui/lab/TabContext";
|
||||
import TabList from "@mui/lab/TabList";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
import GeneralSettingsPanel from "../../../Components/TabPanels/Status/GeneralSettingsPanel";
|
||||
import ContentPanel from "../../../Components/TabPanels/Status/ContentPanel";
|
||||
import { publicPageGeneralSettingsValidation } from "../../../Validation/validation";
|
||||
import { publicPageSettingsValidation } from "../../../Validation/validation";
|
||||
import { hasValidationErrors } from "../../../Validation/error";
|
||||
import { StatusFormProvider } from "../CreateStatusContext";
|
||||
import { formatBytes } from "../../../Utils/fileUtils";
|
||||
import { useSelector } from "react-redux";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { networkService } from "../../../main";
|
||||
import { buildErrors } from "../../../Validation/error";
|
||||
|
||||
/**
|
||||
* CreateStatus page renders a page with tabs for general settings and contents.
|
||||
@@ -31,21 +33,21 @@ import { networkService } from "../../../main";
|
||||
];
|
||||
const tabList = ["General settings", "Contents"];
|
||||
const hasInitForm = initForm && Object.keys(initForm).length > 0;
|
||||
const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";
|
||||
const STATUS_PAGE = import.meta.env.VITE_STATU_PAGE_URL?? "status-page";
|
||||
const [form, setForm] = useState(
|
||||
hasInitForm
|
||||
? initForm
|
||||
: {
|
||||
companyName: "",
|
||||
url: STATUS_PAGE,
|
||||
url: "/"+STATUS_PAGE,
|
||||
timezone: "America/Toronto",
|
||||
color: "#4169E1",
|
||||
//which fields matching below?
|
||||
//publish: false,
|
||||
publish: false,
|
||||
logo: null,
|
||||
monitors: [],
|
||||
// showUptimePercentage: false,
|
||||
// showBarcode: false,
|
||||
showUptimePercentage: false,
|
||||
showBarcode: false,
|
||||
}
|
||||
);
|
||||
const setActiveTabOnErrors = () => {
|
||||
@@ -77,16 +79,15 @@ import { networkService } from "../../../main";
|
||||
: form.monitors,
|
||||
theme: mode,
|
||||
logo: { type: form.logo?.type ?? "", size: form.logo?.size ?? "" },
|
||||
};
|
||||
delete localData.logo
|
||||
};
|
||||
if (
|
||||
hasValidationErrors(localData, publicPageGeneralSettingsValidation, setErrors)
|
||||
hasValidationErrors(localData, publicPageSettingsValidation, setErrors)
|
||||
) {
|
||||
setActiveTabOnErrors();
|
||||
return;
|
||||
}
|
||||
|
||||
//localData.logo = form.logo
|
||||
localData.logo = form.logo
|
||||
localData.url = STATUS_PAGE
|
||||
let config = { authToken: authToken, url: STATUS_PAGE, data: localData };
|
||||
try {
|
||||
@@ -100,6 +101,39 @@ import { networkService } from "../../../main";
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (event) => {
|
||||
event.preventDefault();
|
||||
const { value, id, name } = event.target;
|
||||
setForm((prev) => ({
|
||||
...prev,
|
||||
[id ?? name]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handelCheckboxChange = (e) => {
|
||||
const { id } = e.target;
|
||||
|
||||
setForm((prev) => {
|
||||
return ({
|
||||
...prev,
|
||||
[id ]: !prev[id]
|
||||
})});
|
||||
}
|
||||
|
||||
const handleBlur = (event) => {
|
||||
event.preventDefault();
|
||||
const { value, id } = event.target;
|
||||
const { error } = publicPageSettingsValidation.validate(
|
||||
{ [id]: value },
|
||||
{
|
||||
abortEarly: false,
|
||||
}
|
||||
);
|
||||
setErrors((prev) => {
|
||||
return buildErrors(prev, id, error);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack
|
||||
className="status"
|
||||
@@ -151,6 +185,9 @@ import { networkService } from "../../../main";
|
||||
setForm={setForm}
|
||||
errors={errors}
|
||||
setErrors={setErrors}
|
||||
handleBlur ={handleBlur}
|
||||
handleChange = {handleChange}
|
||||
handelCheckboxChange = {handelCheckboxChange}
|
||||
>
|
||||
{tabIdx == 0 ? <GeneralSettingsPanel /> : <ContentPanel />}
|
||||
</StatusFormProvider>
|
||||
|
||||
@@ -5,11 +5,33 @@ const StatusFormContext = createContext({
|
||||
setForm: () => {},
|
||||
errors: {},
|
||||
setErrors: () => {},
|
||||
handleBlur: () => {},
|
||||
handleChange: () => {},
|
||||
handelCheckboxChange: () =>{}
|
||||
});
|
||||
|
||||
const StatusFormProvider = ({ form, setForm, errors, setErrors, children }) => {
|
||||
const StatusFormProvider = ({
|
||||
form,
|
||||
setForm,
|
||||
errors,
|
||||
setErrors,
|
||||
handleBlur,
|
||||
handleChange,
|
||||
handelCheckboxChange,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<StatusFormContext.Provider value={{ form, setForm, errors, setErrors }}>
|
||||
<StatusFormContext.Provider
|
||||
value={{
|
||||
form,
|
||||
setForm,
|
||||
errors,
|
||||
setErrors,
|
||||
handleBlur,
|
||||
handleChange,
|
||||
handelCheckboxChange,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</StatusFormContext.Provider>
|
||||
);
|
||||
|
||||
@@ -189,7 +189,7 @@ const logoImageValidation = joi.object({
|
||||
}),
|
||||
});
|
||||
|
||||
const publicPageGeneralSettingsValidation = joi.object({
|
||||
const publicPageSettingsValidation = joi.object({
|
||||
publish: joi.bool(),
|
||||
companyName: joi
|
||||
.string()
|
||||
@@ -205,7 +205,11 @@ const publicPageGeneralSettingsValidation = joi.object({
|
||||
"array.empty": "At least one monitor is required",
|
||||
"any.required": "Monitors are required",
|
||||
}),
|
||||
logo: logoImageValidation
|
||||
logo: logoImageValidation,
|
||||
showUptimePercentage: joi.boolean(),
|
||||
showBarcode: joi.boolean(),
|
||||
showBarcode: joi.boolean()
|
||||
|
||||
});
|
||||
const settingsValidation = joi.object({
|
||||
ttl: joi.number().required().messages({
|
||||
@@ -321,6 +325,6 @@ export {
|
||||
maintenanceWindowValidation,
|
||||
advancedSettingsValidation,
|
||||
infrastructureMonitorValidation,
|
||||
publicPageGeneralSettingsValidation,
|
||||
publicPageSettingsValidation,
|
||||
logoImageValidation
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user