mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-14 05:28:39 -05:00
Addressed all the comments in pr.
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
import * as React from "react";
|
||||
import Button from "@mui/material/Button";
|
||||
import ButtonGroup from "@mui/material/ButtonGroup";
|
||||
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
|
||||
import ClickAwayListener from "@mui/material/ClickAwayListener";
|
||||
import Grow from "@mui/material/Grow";
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Popper from "@mui/material/Popper";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import MenuList from "@mui/material/MenuList";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { createToast } from "../../Utils/toastUtils";
|
||||
import { useExportMonitors } from "../../Hooks/monitorHooks";
|
||||
|
||||
const options = ["Import Monitors", "Export Monitors"];
|
||||
|
||||
const MonitorActions = ({ isLoading }) => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const anchorRef = React.useRef(null);
|
||||
const [selectedIndex, setSelectedIndex] = React.useState(0);
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const [exportMonitors, isExporting] = useExportMonitors();
|
||||
|
||||
const handleClick = async () => {
|
||||
if (selectedIndex === 0) {
|
||||
// Import
|
||||
navigate("/uptime/bulk-import");
|
||||
} else {
|
||||
// Export
|
||||
const [success, error] = await exportMonitors();
|
||||
if (!success) {
|
||||
createToast({ body: error || t("export.failed") });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleMenuItemClick = (event, index) => {
|
||||
setSelectedIndex(index);
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
const handleToggle = () => {
|
||||
setOpen((prevOpen) => !prevOpen);
|
||||
};
|
||||
|
||||
const handleClose = (event) => {
|
||||
if (anchorRef.current && anchorRef.current.contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<ButtonGroup
|
||||
variant="contained"
|
||||
color="accent"
|
||||
ref={anchorRef}
|
||||
aria-label="Monitor actions"
|
||||
disabled={isLoading || isExporting}
|
||||
>
|
||||
<Button onClick={handleClick}>{options[selectedIndex]}</Button>
|
||||
<Button
|
||||
size="small"
|
||||
aria-controls={open ? "split-button-menu" : undefined}
|
||||
aria-expanded={open ? "true" : undefined}
|
||||
aria-label="select monitor action"
|
||||
aria-haspopup="menu"
|
||||
onClick={handleToggle}
|
||||
>
|
||||
<ArrowDropDownIcon />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<Popper
|
||||
sx={{ zIndex: 1 }}
|
||||
open={open}
|
||||
anchorEl={anchorRef.current}
|
||||
role={undefined}
|
||||
transition
|
||||
disablePortal
|
||||
>
|
||||
{({ TransitionProps, placement }) => (
|
||||
<Grow
|
||||
{...TransitionProps}
|
||||
style={{
|
||||
transformOrigin: placement === "bottom" ? "center top" : "center bottom",
|
||||
}}
|
||||
>
|
||||
<Paper>
|
||||
<ClickAwayListener onClickAway={handleClose}>
|
||||
<MenuList
|
||||
id="split-button-menu"
|
||||
autoFocusItem
|
||||
>
|
||||
{options.map((option, index) => (
|
||||
<MenuItem
|
||||
key={option}
|
||||
selected={index === selectedIndex}
|
||||
onClick={(event) => handleMenuItemClick(event, index)}
|
||||
>
|
||||
{option}
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuList>
|
||||
</ClickAwayListener>
|
||||
</Paper>
|
||||
</Grow>
|
||||
)}
|
||||
</Popper>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default MonitorActions;
|
||||
@@ -3,13 +3,12 @@ import { useNavigate } from "react-router-dom";
|
||||
import PropTypes from "prop-types";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useExportMonitors } from "../../Hooks/monitorHooks";
|
||||
import MonitorActions from "../MonitorActions";
|
||||
|
||||
const CreateMonitorHeader = ({ isAdmin, label, isLoading = true, path, bulkPath }) => {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const [exportMonitors, isExporting] = useExportMonitors();
|
||||
|
||||
// Use the provided label or fall back to the translated default
|
||||
|
||||
@@ -30,29 +29,7 @@ const CreateMonitorHeader = ({ isAdmin, label, isLoading = true, path, bulkPath
|
||||
>
|
||||
{label || t("createNew")}
|
||||
</Button>
|
||||
{bulkPath && (
|
||||
<>
|
||||
<Button
|
||||
loading={isLoading}
|
||||
variant="contained"
|
||||
color="accent"
|
||||
onClick={() => {
|
||||
navigate(`${bulkPath}`);
|
||||
}}
|
||||
>
|
||||
{t("bulkImport.title")}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
loading={isExporting}
|
||||
variant="contained"
|
||||
color="accent"
|
||||
onClick={exportMonitors}
|
||||
>
|
||||
{t("export.title")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{bulkPath && <MonitorActions isLoading={isLoading} />}
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -460,10 +460,10 @@ const useExportMonitors = () => {
|
||||
const exportMonitors = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const blob = await networkService.exportMonitors();
|
||||
const response = await networkService.exportMonitors();
|
||||
|
||||
// Create a download link
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const url = window.URL.createObjectURL(response);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.setAttribute("download", "monitors.csv");
|
||||
|
||||
@@ -1027,7 +1027,7 @@ class NetworkService {
|
||||
const response = await this.axiosInstance.get("/monitors/export", {
|
||||
responseType: "blob",
|
||||
});
|
||||
return response.data;
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -744,5 +744,10 @@
|
||||
"title": "Export Monitors",
|
||||
"success": "Monitors exported successfully!",
|
||||
"failed": "Failed to export monitors"
|
||||
},
|
||||
"monitorActions": {
|
||||
"title": "Export/Import",
|
||||
"import": "Import Monitors",
|
||||
"export": "Export Monitors"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,7 +685,7 @@ class MonitorController {
|
||||
explain,
|
||||
});
|
||||
return res.success({
|
||||
msg: "OK", // TODO
|
||||
msg: "OK",
|
||||
data: result,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -708,6 +708,12 @@ class MonitorController {
|
||||
const { teamId } = req.user;
|
||||
|
||||
const monitors = await this.db.getMonitorsByTeamId({ teamId });
|
||||
if (!monitors || monitors.length === 0) {
|
||||
return res.success({
|
||||
msg: this.stringService.noMonitorsFound,
|
||||
data: null,
|
||||
});
|
||||
}
|
||||
const csvData = monitors?.filteredMonitors?.map((monitor) => ({
|
||||
name: monitor.name,
|
||||
description: monitor.description,
|
||||
@@ -721,10 +727,14 @@ class MonitorController {
|
||||
|
||||
const csv = pkg.unparse(csvData);
|
||||
|
||||
res.setHeader("Content-Type", "text/csv");
|
||||
res.setHeader("Content-Disposition", "attachment; filename=monitors.csv");
|
||||
|
||||
res.send(csv);
|
||||
return res.success({
|
||||
msg: this.stringService.monitorsExported,
|
||||
data: csv,
|
||||
headers: {
|
||||
"Content-Type": "text/csv",
|
||||
"Content-Disposition": "attachment; filename=monitors.csv",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "exportMonitorsToCSV"));
|
||||
}
|
||||
|
||||
@@ -16,7 +16,12 @@ const responseHandler = (req, res, next) => {
|
||||
* @param {*} [options.data=null] - Response data payload
|
||||
* @returns {Object} Express response object
|
||||
*/
|
||||
res.success = ({ status = 200, msg = "OK", data = null }) => {
|
||||
res.success = ({ status = 200, msg = "OK", data = null, headers = {} }) => {
|
||||
// Set custom headers if provided
|
||||
Object.entries(headers).forEach(([key, value]) => {
|
||||
res.set(key, value);
|
||||
});
|
||||
|
||||
return res.status(status).json({
|
||||
success: true,
|
||||
msg: msg,
|
||||
|
||||
Reference in New Issue
Block a user