Populate table

This commit is contained in:
Alex Holliday committed 2024-10-04 15:25:41 +08:00
1 parent b0182cff6d
commit 7b65debed6
9 files changed
+324 -27

No files matched your search

+3
View File
@@ -9,6 +9,9 @@ const initialState = {
team: {
rowsPerPage: 5,
},
maintenance: {
rowsPerPage: 5,
},
sidebar: {
collapsed: false,
},
@@ -190,6 +190,7 @@ const CreateMaintenance = () => {
repeat,
};
console.log(submit);
if (repeat === 0) {
submit.expiry = end;
}
@@ -0,0 +1,182 @@
import { useState } from "react";
import { useTheme } from "@emotion/react";
import { useNavigate } from "react-router-dom";
import {
Button,
IconButton,
Menu,
MenuItem,
Modal,
Stack,
Typography,
} from "@mui/material";
import Settings from "../../../../assets/icons/settings-bold.svg?react";
import PropTypes from "prop-types";
const ActionsMenu = ({ isAdmin, maintenanceWindow }) => {
const [anchorEl, setAnchorEl] = useState(null);
const [isOpen, setIsOpen] = useState(false);
const theme = useTheme();
const handleRemove = async (event) => {
event.preventDefault();
event.stopPropagation();
console.log("remove");
setIsOpen(false); // close modal
};
const openMenu = (event) => {
event.preventDefault();
event.stopPropagation();
setAnchorEl(event.currentTarget);
};
const openRemove = (e) => {
closeMenu(e);
setIsOpen(true);
};
const closeMenu = (e) => {
e.stopPropagation();
setAnchorEl(null);
};
const navigate = useNavigate();
return (
<>
<IconButton
aria-label="monitor actions"
onClick={(event) => {
event.stopPropagation();
openMenu(event);
}}
sx={{
"&:focus": {
outline: "none",
},
"& svg path": {
stroke: theme.palette.other.icon,
},
}}
>
<Settings />
</IconButton>
<Menu
className="actions-menu"
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={(e) => closeMenu(e)}
disableScrollLock
slotProps={{
paper: {
sx: {
"& ul": { p: theme.spacing(2.5) },
"& li": { m: 0 },
"& li:last-of-type": {
color: theme.palette.error.text,
},
},
},
}}
>
<MenuItem
onClick={(e) => {
closeMenu(e);
e.stopPropagation();
console.log("Edit");
}}
>
Edit
</MenuItem>
<MenuItem
onClick={(e) => {
closeMenu(e);
e.stopPropagation();
console.log("Pause");
}}
>
Pause
</MenuItem>
<MenuItem
onClick={(e) => {
e.stopPropagation();
openRemove(e);
}}
>
Remove
</MenuItem>
</Menu>
<Modal
aria-labelledby="modal-delete-monitor"
aria-describedby="delete-monitor-confirmation"
open={isOpen}
onClose={(e) => {
e.stopPropagation();
setIsOpen(false);
}}
>
<Stack
gap={theme.spacing(2)}
sx={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
width: 400,
bgcolor: theme.palette.background.main,
border: 1,
borderColor: theme.palette.border.light,
borderRadius: theme.shape.borderRadius,
boxShadow: 24,
p: theme.spacing(15),
"&:focus": {
outline: "none",
},
}}
>
<Typography id="modal-delete-maintenance" component="h2" variant="h2">
Do you really want to remove this maintenance window?
</Typography>
<Stack
direction="row"
gap={theme.spacing(4)}
mt={theme.spacing(12)}
justifyContent="flex-end"
>
<Button
variant="text"
color="info"
onClick={(e) => {
e.stopPropagation();
setIsOpen(false);
}}
>
Cancel
</Button>
<Button
variant="contained"
color="error"
onClick={(e) => {
e.stopPropagation(e);
handleRemove(e);
}}
>
Delete
</Button>
</Stack>
</Stack>
</Modal>
</>
);
};
ActionsMenu.propTypes = {
maintenanceWindow: PropTypes.object,
isAdmin: PropTypes.bool,
updateCallback: PropTypes.func,
};
export default ActionsMenu;
@@ -15,16 +15,18 @@ import {
} from "@mui/material";
import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
import ArrowUpwardRoundedIcon from "@mui/icons-material/ArrowUpwardRounded";
import ActionsMenu from "./ActionsMenu";
import { useState, useEffect, memo, useCallback, useRef } from "react";
import { useDispatch } from "react-redux";
import { useDispatch, useSelector } from "react-redux";
import { useTheme } from "@emotion/react";
import LeftArrowDouble from "../../../assets/icons/left-arrow-double.svg?react";
import RightArrowDouble from "../../../assets/icons/right-arrow-double.svg?react";
import LeftArrow from "../../../assets/icons/left-arrow.svg?react";
import RightArrow from "../../../assets/icons/right-arrow.svg?react";
import SelectorVertical from "../../../assets/icons/selector-vertical.svg?react";
// import ActionsMenu from "../actionsMenu";
import { formatDurationRounded } from "../../../Utils/timeUtils";
import { StatusLabel } from "../../../Components/Label";
import { setRowsPerPage } from "../../../Features/UI/uiSlice";
/**
* Component for pagination actions (first, previous, next, last).
@@ -101,16 +103,15 @@ TablePaginationActions.propTypes = {
const MaintenanceTable = ({
page,
setPage,
rowsPerPage,
setRowsPerPage,
sort,
setSort,
maintenanceWindows,
maintenanceWindowCount,
}) => {
const { rowsPerPage } = useSelector((state) => state.ui.maintenance);
const theme = useTheme();
const dispatch = useDispatch();
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
@@ -119,7 +120,7 @@ const MaintenanceTable = ({
dispatch(
setRowsPerPage({
value: parseInt(event.target.value, 10),
table: "monitors",
table: "maintenance",
})
);
setPage(0);
@@ -159,7 +160,7 @@ const MaintenanceTable = ({
onClick={() => handleSort("name")}
>
<Box>
Host
Maintenance Window Name
<span
style={{
visibility: sort.field === "name" ? "visible" : "hidden",
@@ -184,7 +185,7 @@ const MaintenanceTable = ({
<span
style={{
visibility:
sort.field === "status" ? "visible" : "hidden",
sort.field === "active" ? "visible" : "hidden",
}}
>
{sort.order === "asc" ? (
@@ -195,12 +196,38 @@ const MaintenanceTable = ({
</span>
</Box>
</TableCell>
<TableCell>Response Time</TableCell>
<TableCell>Type</TableCell>
<TableCell>Next Window</TableCell>
<TableCell>Repeat</TableCell>
<TableCell>Actions</TableCell>
</TableRow>
</TableHead>
<TableBody></TableBody>
<TableBody>
{maintenanceWindows.map((maintenanceWindow) => {
const text = maintenanceWindow.active ? "active" : "paused";
const status = maintenanceWindow.active ? "up" : "paused";
return (
<TableRow key={maintenanceWindow._id}>
<TableCell>{maintenanceWindow.name}</TableCell>
<TableCell>
<StatusLabel
status={status}
text={text}
customStyles={{ textTransform: "capitalize" }}
/>
</TableCell>
<TableCell>{maintenanceWindow.start}</TableCell>
<TableCell>
{maintenanceWindow.repeat === 0
? "N/A"
: formatDurationRounded(maintenanceWindow.repeat)}
</TableCell>
<TableCell>
<ActionsMenu maintenanceWindow={maintenanceWindow} />
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
<Stack
+56 -14
View File
@@ -1,22 +1,43 @@
import { Box } from "@mui/material";
import { Box, Stack, Button } from "@mui/material";
import { useTheme } from "@emotion/react";
import Fallback from "../../Components/Fallback";
import { useState, useEffect } from "react";
import "./index.css";
import MaintenanceTable from "./MaintenanceTable";
import { useSelector } from "react-redux";
import { networkService } from "../../main";
import Breadcrumbs from "../../Components/Breadcrumbs";
import { useNavigate } from "react-router-dom";
const Maintenance = ({ isAdmin }) => {
const theme = useTheme();
const authState = useSelector((state) => state.auth);
const navigate = useNavigate();
const { authToken } = useSelector((state) => state.auth);
const { rowsPerPage } = useSelector((state) => state.ui.maintenance);
const [maintenanceWindows, setMaintenanceWindows] = useState([]);
const [maintenanceWindowCount, setMaintenanceWindowCount] = useState(0);
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(5);
const [sort, setSort] = useState({});
useEffect(() => {});
useEffect(() => {
const fetchMaintenanceWindows = async () => {
try {
const response = await networkService.getMaintenanceWindowsByTeamId({
authToken: authToken,
page: page,
rowsPerPage: rowsPerPage,
});
const { maintenanceWindows, maintenanceWindowCount } =
response.data.data;
setMaintenanceWindows(maintenanceWindows);
setMaintenanceWindowCount(maintenanceWindowCount);
} catch (error) {
console.log(error);
}
};
fetchMaintenanceWindows();
}, [authToken, page, rowsPerPage]);
return (
<Box
@@ -34,16 +55,37 @@ const Maintenance = ({ isAdmin }) => {
}}
>
{maintenanceWindows.length > 0 && (
<MaintenanceTable
page={page}
setPage={setPage}
rowsPerPage={rowsPerPage}
setRowsPerPage={setRowsPerPage}
sort={sort}
setSort={setSort}
maintenanceWindows={maintenanceWindows}
maintenanceWindowCount={maintenanceWindowCount}
/>
<Stack gap={theme.spacing(8)}>
<Stack
direction="row"
justifyContent="space-between"
alignItems="center"
mt={theme.spacing(5)}
>
<Breadcrumbs
list={[{ name: "maintenance", path: "/maintenance" }]}
/>
<Button
variant="contained"
color="primary"
onClick={() => {
navigate("/maintenance/create");
}}
sx={{ fontWeight: 500 }}
>
Create maintenance window
</Button>
</Stack>
<MaintenanceTable
page={page}
setPage={setPage}
rowsPerPage={rowsPerPage}
sort={sort}
setSort={setSort}
maintenanceWindows={maintenanceWindows}
maintenanceWindowCount={maintenanceWindowCount}
/>
</Stack>
)}
{isAdmin && maintenanceWindows.length === 0 && (
<Fallback
+38
View File
@@ -709,6 +709,44 @@ class NetworkService {
}
);
}
/**
* ************************************
* Get maintenance windows by teamId
* ************************************
*
* @async
* @param {Object} config - The configuration object.
* @param {string} config.authToken - The authorization token to be used in the request header.
* @param {string} [config.active] - The status of the maintenance windows to retrieve.
* @param {number} [config.page] - The page number for pagination.
* @param {number} [config.rowsPerPage] - The number of rows per page for pagination.
* @param {string} [config.field] - The field to sort by.
* @param {string} [config.order] - The order in which to sort the field.
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
*
*/
async getMaintenanceWindowsByTeamId(config) {
const { authToken, active, page, rowsPerPage, field, order } = config;
const params = new URLSearchParams();
if (active) params.append("status", active);
if (page) params.append("page", page);
if (rowsPerPage) params.append("rowsPerPage", rowsPerPage);
if (field) params.append("field", field);
if (order) params.append("order", order);
return this.axiosInstance.get(
`/maintenance-window/team?${params.toString()}`,
{
headers: {
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
},
}
);
}
}
export default NetworkService;
@@ -28,6 +28,7 @@ const createMaintenanceWindows = async (req, res, next) => {
return req.db.createMaintenanceWindow({
teamId,
monitorId,
name: req.body.name,
active: req.body.active ? req.body.active : true,
repeat: req.body.repeat,
start: req.body.start,
+3
View File
@@ -43,6 +43,9 @@ const MaintenanceWindow = mongoose.Schema(
type: Boolean,
default: true,
},
name: {
type: String,
},
repeat: {
type: Number,
},
@@ -36,7 +36,7 @@ const createMaintenanceWindow = async (maintenanceWindowData) => {
if (maintenanceWindowData.oneTime) {
maintenanceWindow.expiry = maintenanceWindowData.end;
}
const result = maintenanceWindow.save();
const result = await maintenanceWindow.save();
return result;
} catch (error) {
error.service = SERVICE_NAME;