diff --git a/Client/package-lock.json b/Client/package-lock.json index 25f2601ef..658f28287 100644 --- a/Client/package-lock.json +++ b/Client/package-lock.json @@ -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", diff --git a/Client/package.json b/Client/package.json index 7f3d9cfe7..af0536d3f 100644 --- a/Client/package.json +++ b/Client/package.json @@ -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", diff --git a/Client/src/Components/Inputs/Checkbox/index.jsx b/Client/src/Components/Inputs/Checkbox/index.jsx index b5b5b8981..08bb5b9c4 100644 --- a/Client/src/Components/Inputs/Checkbox/index.jsx +++ b/Client/src/Components/Inputs/Checkbox/index.jsx @@ -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, diff --git a/Client/src/Components/Inputs/ColorPicker/index.jsx b/Client/src/Components/Inputs/ColorPicker/index.jsx index 52068e2c6..de63400bc 100644 --- a/Client/src/Components/Inputs/ColorPicker/index.jsx +++ b/Client/src/Components/Inputs/ColorPicker/index.jsx @@ -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: * * */ -const ColorPicker = ({ id, label, value, error, onChange, onBlur }) => { +const ColorPicker = ({ id, value, error, onChange, onBlur }) => { const theme = useTheme(); return ( @@ -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, diff --git a/Client/src/Components/TabPanels/Status/ContentPanel.jsx b/Client/src/Components/TabPanels/Status/ContentPanel.jsx index 2b78039bf..cbc4cad1c 100644 --- a/Client/src/Components/TabPanels/Status/ContentPanel.jsx +++ b/Client/src/Components/TabPanels/Status/ContentPanel.jsx @@ -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 = () => { - { Add new - {cards.length > 0 && ( - - - - )} + + + {errors["monitors"] && ( { {errors["monitors"]} )} - + @@ -167,23 +168,22 @@ const ContentPanel = () => { Show more details on the status page - {/* + - */} + /> + diff --git a/Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx b/Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx index 737b75500..98b5dbf5e 100644 --- a/Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx +++ b/Client/src/Components/TabPanels/Status/GeneralSettingsPanel.jsx @@ -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 = () => { - {/* */} + /> @@ -176,13 +150,7 @@ const GeneralSettingsPanel = () => { type="url" label="Your status page address" disabled - value={form.url ?? STATUS_PAGE} - startAdornment={ - - } + value={form.url ?? "/" + STATUS_PAGE} onChange={handleChange} onBlur={handleBlur} helperText={errors["url"]} @@ -210,46 +178,67 @@ const GeneralSettingsPanel = () => { items={timezones} error={errors["display-timezone"]} /> - - {progress.isLoading || progress.value !== 0 || errors["logo"] ? ( - } - label={logo?.name} - size={formatBytes(logo?.size)} - progress={progress.value} - onClick={removeLogo} - error={errors["logo"]} - /> - ) : logo && logo.type ? ( - + - Remove Logo - - ) : ( - "" - )} - + Logo + + + {progress.isLoading || progress.value !== 0 || errors["logo"] ? ( + } + label={logo?.name} + size={formatBytes(logo?.size)} + progress={progress.value} + onClick={removeLogo} + error={errors["logo"]} + /> + ) : logo && logo.type ? ( + + Remove Logo + + ) : ( + "" + )} + + + + Color + + + + diff --git a/Client/src/Components/TabPanels/Status/ServersList/Server/index.jsx b/Client/src/Components/TabPanels/Status/ServersList/Server/index.jsx index 8e6c0361f..574a0873f 100644 --- a/Client/src/Components/TabPanels/Status/ServersList/Server/index.jsx +++ b/Client/src/Components/TabPanels/Status/ServersList/Server/index.jsx @@ -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, }; diff --git a/Client/src/Components/TabPanels/Status/ServersList/index.jsx b/Client/src/Components/TabPanels/Status/ServersList/index.jsx index 18f021592..ca05ffa06 100644 --- a/Client/src/Components/TabPanels/Status/ServersList/index.jsx +++ b/Client/src/Components/TabPanels/Status/ServersList/index.jsx @@ -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 ( {(provided, snapshot) => ( - {cards.map((item, index) => ( {(provided, snapshot) => ( - - + )} ))} {provided.placeholder} - + )} @@ -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; diff --git a/Client/src/Pages/Status/CreateStatus/index.jsx b/Client/src/Pages/Status/CreateStatus/index.jsx index 9d2ed6bc3..aadef20bd 100644 --- a/Client/src/Pages/Status/CreateStatus/index.jsx +++ b/Client/src/Pages/Status/CreateStatus/index.jsx @@ -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 ( {tabIdx == 0 ? : } diff --git a/Client/src/Pages/Status/CreateStatusContext/index.jsx b/Client/src/Pages/Status/CreateStatusContext/index.jsx index 1fff224fc..cd7127025 100644 --- a/Client/src/Pages/Status/CreateStatusContext/index.jsx +++ b/Client/src/Pages/Status/CreateStatusContext/index.jsx @@ -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 ( - + {children} ); diff --git a/Client/src/Validation/validation.js b/Client/src/Validation/validation.js index 5e9a1bec2..6b18ef632 100644 --- a/Client/src/Validation/validation.js +++ b/Client/src/Validation/validation.js @@ -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 };