- Replace Doc icon with Reorder icon

- Replace Text input with Search component
- Extend Search component to accept start and end adorment
This commit is contained in:
Shemy Gan committed 2024-12-27 20:59:03 -05:00
1 parent d844078ed7
commit 23d97f8e56
8 files changed
+122 -53

No files matched your search

+15 -7
View File
@@ -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) => (
<Stack>
<Typography
@@ -89,9 +91,13 @@ const Search = ({
{...params}
error={Boolean(error)}
placeholder="Type to search"
InputProps={{
...params.InputProps,
...(isAdorned && { startAdornment: <SearchAdornment /> }),
slotProps={{
input: {
...params.InputProps,
...(isAdorned && { startAdornment: <SearchAdornment /> }),
...(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;
@@ -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 (
<InputAdornment position="start">
<Docs />
<ReorderRoundedIcon />
</InputAdornment>
);
};
@@ -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 (
<TextInput
type="text"
<Search
id={id}
multiple={false}
isAdorned={false}
options={monitors ? monitors : []}
filteredBy="name"
inputValue={search}
value={value}
startAdornment={<ServerStartAdornment />}
endAdornment={
<ServerEndAdornment
@@ -13,10 +23,9 @@ const Server = ({ id, removeItem, value, onChange }) => {
removeItem={removeItem}
/>
}
id={id}
value= {value}
onChange ={ onChange }
></TextInput>
handleInputChange={handleSearch}
handleChange={onChange}
/>
);
};
@@ -13,12 +13,13 @@ const CustomCard = React.forwardRef((props, ref) => (
removeItem={props.removeCard}
value={props.value}
onChange={props.onChange}
monitors={props.monitors}
/>
</CardContent>
</Card>
));
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}
/>
);
};
@@ -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{" "}
</Typography>
<Button onClick={handleAddNew}>Add New</Button>
<Button
onClick={handleAddNew}
disabled={monitors.length === 0 || monitors.length <= cards.length}
>
Add New
</Button>
</Stack>
{cards.length > 0 && (
<Stack
@@ -115,18 +156,21 @@ const ContentPanel = () => {
direction="column"
gap={theme.spacing(2)}
>
{cards.map((card, idx) => (
<Card
key={idx}
index={idx}
id={card?.id ?? "" + Math.random()}
text={"" + idx}
moveCard={moveCard}
removeCard={removeCard}
value={card?.url ?? ""}
onChange={handleCardChange}
/>
))}
{cards.map((card, idx) => {
return (
<Card
key={idx}
index={idx}
id={card?.id ?? "" + Math.random()}
text={"" + idx}
moveCard={moveCard}
removeCard={removeCard}
value={card.val ?? {}}
onChange={handleCardChange}
monitors={monitors}
/>
);
})}
</Stack>
)}
{errors["monitors"] && (
@@ -181,7 +181,7 @@ const CreateMaintenance = () => {
setSearch(value);
};
const handleSelectMonitors = (monitors) => {
const handleSelectMonitors = (_, monitors) => {
setForm({ ...form, monitors });
const { error } = maintenanceWindowValidation.validate(
{ monitors },
@@ -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) {
+12 -5
View File
@@ -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();
}, []);