diff --git a/Client/src/Components/Inputs/Search/index.jsx b/Client/src/Components/Inputs/Search/index.jsx
index 9a1995726..a2f35cac7 100644
--- a/Client/src/Components/Inputs/Search/index.jsx
+++ b/Client/src/Components/Inputs/Search/index.jsx
@@ -54,6 +54,8 @@ const Search = ({
isAdorned = true,
error,
disabled,
+ startAdornment,
+ endAdornment
}) => {
const theme = useTheme();
@@ -66,15 +68,15 @@ const Search = ({
onInputChange={(_, newValue) => {
handleInputChange(newValue);
}}
- onChange={(_, newValue) => {
- handleChange && handleChange(newValue);
+ onChange={(e, newValue) => {
+ handleChange && handleChange(e, newValue);
}}
fullWidth
freeSolo
disabled={disabled}
disableClearable
options={options}
- getOptionLabel={(option) => option[filteredBy]}
+ getOptionLabel={(option) => option[filteredBy]??""}
renderInput={(params) => (
}),
+ slotProps={{
+ input: {
+ ...params.InputProps,
+ ...(isAdorned && { startAdornment: }),
+ ...(startAdornment && { startAdornment: startAdornment }),
+ ...(endAdornment && { endAdornment: endAdornment }),
+ },
}}
sx={{
"& fieldset": {
@@ -187,7 +193,7 @@ Search.propTypes = {
options: PropTypes.array.isRequired,
filteredBy: PropTypes.string.isRequired,
secondaryLabel: PropTypes.string,
- value: PropTypes.array,
+ value: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
inputValue: PropTypes.string.isRequired,
handleInputChange: PropTypes.func.isRequired,
handleChange: PropTypes.func,
@@ -195,6 +201,8 @@ Search.propTypes = {
sx: PropTypes.object,
error: PropTypes.string,
disabled: PropTypes.bool,
+ startAdornment: PropTypes.object,
+ endAdornment: PropTypes.object
};
export default Search;
diff --git a/Client/src/Components/Inputs/TextInput/Adornments/index.jsx b/Client/src/Components/Inputs/TextInput/Adornments/index.jsx
index 5c309b3a6..4d7b09d0f 100644
--- a/Client/src/Components/Inputs/TextInput/Adornments/index.jsx
+++ b/Client/src/Components/Inputs/TextInput/Adornments/index.jsx
@@ -3,8 +3,8 @@ import { useTheme } from "@mui/material/styles";
import PropTypes from "prop-types";
import VisibilityOff from "@mui/icons-material/VisibilityOff";
import Visibility from "@mui/icons-material/Visibility";
-import Docs from "../../../../assets/icons/docs.svg?react";
-import DeleteIcon from "../../../../assets/icons/trash-bin.svg?react";
+import ReorderRoundedIcon from '@mui/icons-material/ReorderRounded';
+import DeleteIcon from "../../../../assets/icons/trash-bin.svg?react";
export const HttpAdornment = ({ https, prefix }) => {
const theme = useTheme();
@@ -70,7 +70,7 @@ PasswordEndAdornment.propTypes = {
export const ServerStartAdornment = () => {
return (
-
+
);
};
diff --git a/Client/src/Components/TabPanels/Status/Card/Server/index.jsx b/Client/src/Components/TabPanels/Status/Card/Server/index.jsx
index ed874ec01..c21f6963f 100644
--- a/Client/src/Components/TabPanels/Status/Card/Server/index.jsx
+++ b/Client/src/Components/TabPanels/Status/Card/Server/index.jsx
@@ -1,11 +1,21 @@
-import TextInput from "../../../../Inputs/TextInput";
import { ServerStartAdornment, ServerEndAdornment } from "../../../../Inputs/TextInput/Adornments";
+import Search from "../../../../Inputs/Search";
+import {useState} from "react"
-
-const Server = ({ id, removeItem, value, onChange }) => {
+const Server = ({ id, removeItem, onChange, monitors, value, label }) => {
+ const [search, setSearch] = useState("");
+ const handleSearch = (val) => {
+ setSearch(val);
+ };
return (
- }
endAdornment={
{
removeItem={removeItem}
/>
}
- id={id}
- value= {value}
- onChange ={ onChange }
- >
+ handleInputChange={handleSearch}
+ handleChange={onChange}
+ />
);
};
diff --git a/Client/src/Components/TabPanels/Status/Card/index.jsx b/Client/src/Components/TabPanels/Status/Card/index.jsx
index a16c3d1ee..35c7cf2b0 100644
--- a/Client/src/Components/TabPanels/Status/Card/index.jsx
+++ b/Client/src/Components/TabPanels/Status/Card/index.jsx
@@ -13,12 +13,13 @@ const CustomCard = React.forwardRef((props, ref) => (
removeItem={props.removeCard}
value={props.value}
onChange={props.onChange}
+ monitors={props.monitors}
/>
));
-const MyCard = ({ id, index, moveCard, removeCard, value, onChange }) => {
+const MyCard = ({ id, index, moveCard, removeCard, value, onChange, monitors }) => {
const ref = useRef(null);
const [, drop] = useDrop({
accept: ItemTypes.CARD,
@@ -80,6 +81,7 @@ const MyCard = ({ id, index, moveCard, removeCard, value, onChange }) => {
removeCard={removeCard}
value={value}
onChange={onChange}
+ monitors={monitors}
/>
);
};
diff --git a/Client/src/Components/TabPanels/Status/ContentPanel.jsx b/Client/src/Components/TabPanels/Status/ContentPanel.jsx
index 3d5fef0e3..39c18c9ba 100644
--- a/Client/src/Components/TabPanels/Status/ContentPanel.jsx
+++ b/Client/src/Components/TabPanels/Status/ContentPanel.jsx
@@ -1,4 +1,4 @@
-import { useState, useCallback, useContext } from "react";
+import { useState, useCallback, useContext, useEffect } from "react";
import { Button, Box, Stack, Typography } from "@mui/material";
import ConfigBox from "../../../Components/ConfigBox";
import { useTheme } from "@emotion/react";
@@ -6,11 +6,38 @@ import TabPanel from "@mui/lab/TabPanel";
import Card from "./Card";
import update from "immutability-helper";
import { StatusFormContext } from "../../../Pages/Status/CreateStatusContext";
+import { useSelector } from "react-redux";
+import { logger } from "../../../Utils/Logger"
+import { createToast } from "../../../Utils/toastUtils";
+import { networkService } from "../../../main";
const ContentPanel = () => {
const theme = useTheme();
const { form, setForm, errors, setErrors } = useContext(StatusFormContext);
- const [cards, setCards] = useState(form.monitors);
+ const [cards, setCards] = useState(form.monitors.length>0? form.monitors.map((m, idx)=>({id: idx, val: m})): []);
+ const {user, authToken } = useSelector((state) => state.auth);
+ const [monitors, setMonitors] = useState([]);
+
+ useEffect(() => {
+ const fetchMonitors = async () => {
+ try {
+ const response = await networkService.getMonitorsByTeamId({
+ authToken: authToken,
+ teamId: user.teamId,
+ limit: -1,
+ types: ["http", "ping", "pagespeed", "hardware"],
+ });
+ if(response.data.data.monitors.length==0){
+ setErrors({monitors: "Please config monitors to setup status page"})
+ }
+ setMonitors(response.data.data.monitors);
+ } catch (error) {
+ createToast({ body: "Failed to fetch monitors data" });
+ logger.error("Failed to fetch monitors", error);
+ }
+ };
+ fetchMonitors();
+ }, [user, authToken]);
const moveCard = useCallback(
(dragIndex, hoverIndex) => {
@@ -27,31 +54,40 @@ const ContentPanel = () => {
[cards]
);
- const handleCardChange = (event) => {
+ const handleCardChange = (event, val) => {
event.preventDefault();
- const { value, id } = event.target;
- let idx = cards.findIndex((a) => a.id == id);
+ const { id } = event.target;
+ let idx = cards.findIndex((a) => {
+ let found = false;
+ let optionIdx = id.indexOf("-option");
+ if (optionIdx !== -1) found = a.id == id.substr(0, optionIdx);
+ else found = a.id == id;
+ return found;
+ });
let newCards;
if (idx >= 0) {
let x = JSON.parse(JSON.stringify(cards[idx]));
- x.url = value;
+ x.val = val;
newCards = update(cards, { $splice: [[idx, 1, x]] });
setCards(newCards);
} else {
- newCards = update(cards, { $push: [{ id: id, url: value }] });
+ newCards = update(cards, { $push: [{ id: id, val: val }] });
setCards(newCards);
}
- setForm({ ...form, monitors: newCards });
+ setForm({ ...form, monitors: newCards.map(c=>c.val) });
};
const handleAddNew = () => {
+ if (cards.length === monitors.length) return;
const newCards = [...cards, { id: "" + Math.random() }];
setCards(newCards);
- setForm({ ...form, monitors: newCards });
};
const removeCard = (id) => {
const newCards = cards.filter((c) => c?.id != id);
setCards(newCards);
- setForm({ ...form, monitors: newCards });
+ setForm({
+ ...form,
+ monitors: newCards.filter((c) => c.val !== undefined).map((c) => c.val),
+ });
};
return (
@@ -107,7 +143,12 @@ const ContentPanel = () => {
{" "}
Servers list{" "}
-
+
{cards.length > 0 && (
{
direction="column"
gap={theme.spacing(2)}
>
- {cards.map((card, idx) => (
-
- ))}
+ {cards.map((card, idx) => {
+ return (
+
+ );
+ })}
)}
{errors["monitors"] && (
diff --git a/Client/src/Pages/Maintenance/CreateMaintenance/index.jsx b/Client/src/Pages/Maintenance/CreateMaintenance/index.jsx
index c832d463a..e38c3c058 100644
--- a/Client/src/Pages/Maintenance/CreateMaintenance/index.jsx
+++ b/Client/src/Pages/Maintenance/CreateMaintenance/index.jsx
@@ -181,7 +181,7 @@ const CreateMaintenance = () => {
setSearch(value);
};
- const handleSelectMonitors = (monitors) => {
+ const handleSelectMonitors = (_, monitors) => {
setForm({ ...form, monitors });
const { error } = maintenanceWindowValidation.validate(
{ monitors },
diff --git a/Client/src/Pages/Status/CreateStatus/index.jsx b/Client/src/Pages/Status/CreateStatus/index.jsx
index 8f81a3c31..1f112c19d 100644
--- a/Client/src/Pages/Status/CreateStatus/index.jsx
+++ b/Client/src/Pages/Status/CreateStatus/index.jsx
@@ -22,7 +22,7 @@ import { networkService } from "../../../main";
const CreateStatus = ({ initForm }) => {
const theme = useTheme();
const mode = useSelector((state) => state.ui.mode);
- const {authToken} = useSelector((state) => state.auth);
+ const { authToken } = useSelector((state) => state.auth);
const {apiBaseUrl} = useSelector((state) => state.settings);
const [tabIdx, setTabIdx] = useState(0);
const [errors, setErrors] = useState({});
@@ -46,7 +46,6 @@ import { networkService } from "../../../main";
// showBarcode: false,
}
);
-
const setActiveTabOnErrors = () => {
let newIdx = -1;
Object.keys(errors).map((id) => {
@@ -72,7 +71,6 @@ import { networkService } from "../../../main";
delete form.logo;
//let localData = {...form, monitors: form.monitors.map(m=>m.url)}
let localData = {...form, monitors: form.monitors, theme: mode}
-
if (hasValidationErrors(localData, publicPageGeneralSettingsValidation, setErrors)) {
setActiveTabOnErrors();
return;
@@ -85,7 +83,8 @@ import { networkService } from "../../../main";
// if (error) return;
//form.logo = { ...logo, size: formatBytes(logo?.size) };
- let config = { authToken: authToken, url: apiBaseUrl, data: localData };
+ localData = {...form, monitors: form.monitors.map(m=>m.url),theme: mode}
+ let config = { authToken: authToken, url: "status-page", data: localData };
try{
const res = await networkService.createStatusPage(config)
if (!res.status === 200) {
diff --git a/Client/src/Pages/Status/index.jsx b/Client/src/Pages/Status/index.jsx
index 84ad6e7db..eefcf347d 100644
--- a/Client/src/Pages/Status/index.jsx
+++ b/Client/src/Pages/Status/index.jsx
@@ -6,6 +6,8 @@ import CreateStatus from "./CreateStatus";
import { useEffect, useState } from "react";
import { networkService } from "../../main";
import { useSelector } from "react-redux";
+import { createToast } from "../../Utils/toastUtils"
+import { logger } from "../../Utils/Logger";
const Status = () => {
const theme = useTheme();
@@ -13,13 +15,18 @@ const Status = () => {
const {authToken} = useSelector((state) => state.auth);
const {apiBaseUrl} = useSelector((state) => state.settings);
const [initForm, setInitForm] = useState({});
-
useEffect(() => {
const getStatusPage = async () => {
- let config = { authToken: authToken, url: apiBaseUrl };
- let res = await networkService.getStatusPageByUrl(config);
- if(res && res.data)
- setInitForm( res.data.data)
+ let config = { authToken: authToken, url: "status-page" };
+ try {
+ let res = await networkService.getStatusPageByUrl(config);
+ if(res && res.data)
+ setInitForm( res.data.data)
+ }catch (error) {
+ createToast({ body: "Failed to fetch status page data" });
+ logger.error("Failed to fetch status page", error);
+ }
+
};
getStatusPage();
}, []);