mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-20 00:18:47 -05:00
remove monitorHooks
This commit is contained in:
@@ -1,228 +0,0 @@
|
||||
// Components
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import Icon from "../Icon";
|
||||
import Dialog from "../Dialog/index.jsx";
|
||||
|
||||
// Utils
|
||||
import { useState } from "react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { createToast } from "../../../Utils/toastUtils.jsx";
|
||||
|
||||
import PropTypes from "prop-types";
|
||||
import { usePauseMonitor, useDeleteMonitor } from "../../../Hooks/monitorHooks.js";
|
||||
|
||||
const ActionsMenu = ({
|
||||
monitor,
|
||||
isAdmin,
|
||||
updateRowCallback,
|
||||
pauseCallback,
|
||||
setIsLoading = () => {},
|
||||
}) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [actions, setActions] = useState({});
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const theme = useTheme();
|
||||
const [pauseMonitor, isPausing, error] = usePauseMonitor();
|
||||
const [deleteMonitor, isDeleting] = useDeleteMonitor();
|
||||
|
||||
const handleRemove = async (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
let monitor = { id: actions.id };
|
||||
await deleteMonitor({ monitor });
|
||||
updateRowCallback();
|
||||
};
|
||||
|
||||
const handlePause = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await pauseMonitor({ monitorId: monitor.id });
|
||||
pauseCallback();
|
||||
} catch (error) {
|
||||
createToast({ body: "Failed to pause monitor." });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const openMenu = (event, id, url) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setAnchorEl(event.currentTarget);
|
||||
setActions({ id: id, url: url });
|
||||
};
|
||||
|
||||
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, monitor.id, monitor.type === "ping" ? null : monitor.url);
|
||||
}}
|
||||
sx={{
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
"& svg path": {
|
||||
stroke: theme.palette.primary.contrastTextTertiary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
name="Settings"
|
||||
size={20}
|
||||
/>
|
||||
</IconButton>
|
||||
|
||||
<Menu
|
||||
className="actions-menu"
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={(e) => closeMenu(e)}
|
||||
disableScrollLock
|
||||
slotProps={{
|
||||
paper: {
|
||||
sx: {
|
||||
"& ul": {
|
||||
p: theme.spacing(2.5),
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
},
|
||||
"& li": { m: 0, color: theme.palette.primary.contrastTextSecondary },
|
||||
/*
|
||||
This should not be set automatically on the last of type
|
||||
"& li:last-of-type": {
|
||||
color: theme.palette.error.main,
|
||||
}, */
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{actions.url !== null ? (
|
||||
<MenuItem
|
||||
onClick={(e) => {
|
||||
closeMenu(e);
|
||||
e.stopPropagation();
|
||||
window.open(actions.url, "_blank", "noreferrer");
|
||||
}}
|
||||
>
|
||||
Open site
|
||||
</MenuItem>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/uptime/${actions.id}`);
|
||||
}}
|
||||
>
|
||||
Details
|
||||
</MenuItem>
|
||||
{/* TODO - pass monitor id to Incidents page */}
|
||||
<MenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/incidents/${actions.id}`);
|
||||
}}
|
||||
>
|
||||
Incidents
|
||||
</MenuItem>
|
||||
{isAdmin && (
|
||||
<MenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
navigate(`/uptime/configure/${actions.id}`);
|
||||
}}
|
||||
>
|
||||
Configure
|
||||
</MenuItem>
|
||||
)}
|
||||
{isAdmin && (
|
||||
<MenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate(`/uptime/create/${actions.id}`);
|
||||
}}
|
||||
>
|
||||
Clone
|
||||
</MenuItem>
|
||||
)}
|
||||
{isAdmin && (
|
||||
<MenuItem
|
||||
onClick={(e) => {
|
||||
closeMenu(e);
|
||||
|
||||
e.stopPropagation();
|
||||
handlePause(e);
|
||||
}}
|
||||
>
|
||||
{monitor?.isActive === true ? "Pause" : "Resume"}
|
||||
</MenuItem>
|
||||
)}
|
||||
{isAdmin && (
|
||||
<MenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
openRemove(e);
|
||||
}}
|
||||
sx={{ "&.MuiButtonBase-root": { color: theme.palette.error.main } }}
|
||||
>
|
||||
Remove
|
||||
</MenuItem>
|
||||
)}
|
||||
</Menu>
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
theme={theme}
|
||||
title="Do you really want to delete this monitor?"
|
||||
description="Once deleted, this monitor cannot be retrieved."
|
||||
/* Do we need stop propagation? */
|
||||
onCancel={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsOpen(false);
|
||||
}}
|
||||
confirmationButtonLabel="Delete"
|
||||
/* Do we need stop propagation? */
|
||||
onConfirm={(e) => {
|
||||
console.log(e);
|
||||
e.stopPropagation();
|
||||
handleRemove(e);
|
||||
}}
|
||||
isLoading={isDeleting}
|
||||
modelTitle="modal-delete-monitor"
|
||||
modelDescription="delete-monitor-confirmation"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ActionsMenu.propTypes = {
|
||||
monitor: PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
url: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
isActive: PropTypes.bool,
|
||||
}).isRequired,
|
||||
isAdmin: PropTypes.bool,
|
||||
updateRowCallback: PropTypes.func,
|
||||
pauseCallback: PropTypes.func,
|
||||
setIsLoading: PropTypes.func,
|
||||
};
|
||||
|
||||
export default ActionsMenu;
|
||||
@@ -179,3 +179,37 @@ export const useDelete = <R = any>() => {
|
||||
|
||||
return { deleteFn, loading, error };
|
||||
};
|
||||
|
||||
export const useLazyGet = <R = any>() => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { toastError } = useToast();
|
||||
|
||||
const getFn = async (
|
||||
endpoint: string,
|
||||
config?: AxiosRequestConfig
|
||||
): Promise<ApiResponse<R> | null> => {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const res = await get<ApiResponse<R>>(endpoint, {
|
||||
...config,
|
||||
headers: {
|
||||
...config?.headers,
|
||||
},
|
||||
});
|
||||
return res.data;
|
||||
} catch (err: any) {
|
||||
console.error(err);
|
||||
const errMsg = err?.response?.data?.msg || err.message || "An error occurred";
|
||||
toastError(errMsg);
|
||||
setError(errMsg);
|
||||
return null;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return { get: getFn, loading, error };
|
||||
};
|
||||
|
||||
@@ -1,170 +0,0 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { networkService } from "../main.jsx";
|
||||
import { createToast } from "../Utils/toastUtils.jsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export const useFetchMonitorsByTeamId = ({ types, filter, updateTrigger }) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [monitors, setMonitors] = useState(undefined);
|
||||
const [networkError, setNetworkError] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchMonitors = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const res = await networkService.getMonitorsByTeamId({
|
||||
types,
|
||||
filter,
|
||||
});
|
||||
if (res?.data?.data) {
|
||||
setMonitors(res.data.data);
|
||||
}
|
||||
} catch (error) {
|
||||
setNetworkError(true);
|
||||
createToast({
|
||||
body: error.message,
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
fetchMonitors();
|
||||
}, [types, filter, updateTrigger]);
|
||||
return [monitors, isLoading, networkError];
|
||||
};
|
||||
|
||||
export const useDeleteMonitor = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const deleteMonitor = async (monitorId, successCallback = () => {}) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await networkService.deleteMonitorById({ monitorId });
|
||||
successCallback();
|
||||
createToast({ body: t("monitorDeleted") });
|
||||
} catch (error) {
|
||||
createToast({ body: t("failedDeleteMonitor") });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
return [deleteMonitor, isLoading];
|
||||
};
|
||||
|
||||
export const usePauseMonitor = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const pauseMonitor = async (monitorId, successCallback = () => {}) => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const res = await networkService.pauseMonitorById({ monitorId });
|
||||
successCallback();
|
||||
if (res.data.data.isActive === false) {
|
||||
createToast({ body: t("monitorPaused") });
|
||||
} else {
|
||||
createToast({ body: t("monitorResumed") });
|
||||
}
|
||||
} catch (error) {
|
||||
setError(error.message);
|
||||
createToast({ body: t("failedPauseMonitor") });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return [pauseMonitor, isLoading, error];
|
||||
};
|
||||
|
||||
export const useAddDemoMonitors = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const addDemoMonitors = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await networkService.addDemoMonitors();
|
||||
createToast({ body: t("monitorHooks.successAddDemoMonitors") });
|
||||
} catch (error) {
|
||||
createToast({ body: t("monitorHooks.failureAddDemoMonitors") });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
return [addDemoMonitors, isLoading];
|
||||
};
|
||||
|
||||
export const useDeleteAllMonitors = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const deleteAllMonitors = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await networkService.deleteAllMonitors();
|
||||
createToast({ body: t("settingsMonitorsDeleted") });
|
||||
} catch (error) {
|
||||
createToast({ body: t("settingsFailedToDeleteMonitors") });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
return [deleteAllMonitors, isLoading];
|
||||
};
|
||||
|
||||
export const useDeleteMonitorStats = () => {
|
||||
const { t } = useTranslation();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const deleteMonitorStats = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await networkService.deleteChecksByTeamId();
|
||||
createToast({ body: t("settingsStatsCleared") });
|
||||
} catch (error) {
|
||||
createToast({ body: t("settingsFailedToClearStats") });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return [deleteMonitorStats, isLoading];
|
||||
};
|
||||
|
||||
export const useCreateBulkMonitors = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const createBulkMonitors = async (file, user) => {
|
||||
setIsLoading(true);
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("csvFile", file);
|
||||
|
||||
try {
|
||||
const response = await networkService.createBulkMonitors(formData);
|
||||
return [true, response.data, null];
|
||||
} catch (err) {
|
||||
const errorMessage = err?.response?.data?.msg || err.message;
|
||||
return [false, null, errorMessage];
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return [createBulkMonitors, isLoading];
|
||||
};
|
||||
|
||||
export const useFetchJson = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const fetchJson = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const res = await networkService.fetchJson();
|
||||
createToast({ body: "JSON fetched successfully" });
|
||||
return res?.data?.data ?? [];
|
||||
} catch (error) {
|
||||
createToast({ body: "Failed to create monitor." });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
return [fetchJson, isLoading];
|
||||
};
|
||||
@@ -26,14 +26,9 @@ import {
|
||||
} from "@/Features/UI/uiSlice.js";
|
||||
import SettingsStats from "./SettingsStats.jsx";
|
||||
|
||||
import { useFetchSettings, useSaveSettings } from "@/Hooks/settingsHooks.js";
|
||||
import { useFetchSettings, useSaveSettings } from "@/Hooks/settingsHooks";
|
||||
import { useIsAdmin } from "@/Hooks/useIsAdmin.js";
|
||||
import {
|
||||
useAddDemoMonitors,
|
||||
useDeleteAllMonitors,
|
||||
useDeleteMonitorStats,
|
||||
useFetchJson,
|
||||
} from "@/Hooks/monitorHooks.js";
|
||||
import { usePost, useDelete, useLazyGet } from "@/Hooks/UseApi";
|
||||
// Constants
|
||||
const BREADCRUMBS = [{ name: `Settings`, path: "/settings" }];
|
||||
|
||||
@@ -64,8 +59,6 @@ const Settings = () => {
|
||||
setIsEmailPasswordSet,
|
||||
});
|
||||
|
||||
const [addDemoMonitors, isAddingDemoMonitors] = useAddDemoMonitors();
|
||||
|
||||
const [isSaving, saveError, saveSettings] = useSaveSettings({
|
||||
setSettingsData,
|
||||
setIsApiKeySet,
|
||||
@@ -73,9 +66,12 @@ const Settings = () => {
|
||||
setIsEmailPasswordSet,
|
||||
setEmailPasswordHasBeenReset,
|
||||
});
|
||||
const [deleteAllMonitors, isDeletingMonitors] = useDeleteAllMonitors();
|
||||
const [deleteMonitorStats, isDeletingMonitorStats] = useDeleteMonitorStats();
|
||||
const [fetchJson, isFetchingJson] = useFetchJson();
|
||||
|
||||
// New API hooks to replace monitorHooks
|
||||
const { post: postDemoMonitors, loading: isAddingDemoMonitors } = usePost();
|
||||
const { deleteFn: deleteAllMonitorsFn, loading: isDeletingMonitors } = useDelete();
|
||||
const { deleteFn: deleteMonitorStatsFn, loading: isDeletingMonitorStats } = useDelete();
|
||||
const { get: fetchJson, loading: isFetchingJson } = useLazyGet();
|
||||
|
||||
// Setup
|
||||
const isAdmin = useIsAdmin();
|
||||
@@ -130,22 +126,23 @@ const Settings = () => {
|
||||
}
|
||||
|
||||
if (name === "deleteStats") {
|
||||
await deleteMonitorStats();
|
||||
await deleteMonitorStatsFn("/checks/team");
|
||||
return;
|
||||
}
|
||||
|
||||
if (name === "demo") {
|
||||
await addDemoMonitors();
|
||||
await postDemoMonitors("/monitors/demo", {});
|
||||
return;
|
||||
}
|
||||
|
||||
if (name === "deleteMonitors") {
|
||||
await deleteAllMonitors();
|
||||
await deleteAllMonitorsFn("/monitors/");
|
||||
return;
|
||||
}
|
||||
|
||||
if (name === "export") {
|
||||
const json = await fetchJson();
|
||||
const res = await fetchJson("/monitors/export/json");
|
||||
const json = res?.data ?? [];
|
||||
if (!json || json.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useState, useRef } from "react";
|
||||
import { Button, Typography } from "@mui/material";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const UploadFile = ({ onFileSelect }) => {
|
||||
// Changed prop to onFileSelect
|
||||
const theme = useTheme();
|
||||
const [file, setFile] = useState();
|
||||
const [error, setError] = useState("");
|
||||
const inputRef = useRef();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleSelectFile = () => {
|
||||
inputRef.current.click();
|
||||
};
|
||||
|
||||
const handleFileChange = (e) => {
|
||||
setError("");
|
||||
const selectedFile = e.target.files[0];
|
||||
|
||||
// Basic file validation
|
||||
if (!selectedFile) return;
|
||||
|
||||
if (!selectedFile.name.toLowerCase().endsWith(".csv")) {
|
||||
setError(t("bulkImport.invalidFileType"));
|
||||
return;
|
||||
}
|
||||
|
||||
setFile(selectedFile);
|
||||
onFileSelect(selectedFile); // Pass the file directly to parent
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="file"
|
||||
accept=".csv"
|
||||
style={{ display: "none" }}
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
<Typography
|
||||
component="h2"
|
||||
mb={theme.spacing(1.5)}
|
||||
sx={{ wordBreak: "break-all" }}
|
||||
>
|
||||
{file?.name || t("bulkImport.noFileSelected")}
|
||||
</Typography>
|
||||
<Typography
|
||||
component="div"
|
||||
mb={theme.spacing(1.5)}
|
||||
color={theme.palette.error.main}
|
||||
>
|
||||
{error}
|
||||
</Typography>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="accent"
|
||||
onClick={handleSelectFile}
|
||||
>
|
||||
{t("bulkImport.selectFile")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
UploadFile.prototype = {
|
||||
onFileSelect: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default UploadFile;
|
||||
@@ -1,117 +0,0 @@
|
||||
// React, Redux, Router
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useState } from "react";
|
||||
// MUI
|
||||
import { Box, Stack, Typography, Button, Link } from "@mui/material";
|
||||
|
||||
//Components
|
||||
import { createToast } from "../../../Utils/toastUtils.jsx";
|
||||
import Breadcrumbs from "@/Components/v1/Breadcrumbs/index.jsx";
|
||||
import ConfigBox from "@/Components/v1/ConfigBox/index.jsx";
|
||||
import UploadFile from "./Upload.jsx";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { useCreateBulkMonitors } from "../../../Hooks/monitorHooks.js";
|
||||
|
||||
const BulkImport = () => {
|
||||
const theme = useTheme();
|
||||
const { user } = useSelector((state) => state.auth);
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const [selectedFile, setSelectedFile] = useState(null);
|
||||
|
||||
const crumbs = [
|
||||
{ name: t("uptime"), path: "/uptime" },
|
||||
{ name: t("bulkImport.title"), path: `/uptime/bulk-import` },
|
||||
];
|
||||
|
||||
const [createBulkMonitors, hookLoading] = useCreateBulkMonitors();
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!selectedFile) {
|
||||
createToast({ body: t("bulkImport.noFileSelected") });
|
||||
return;
|
||||
}
|
||||
|
||||
const [success, data, error] = await createBulkMonitors(selectedFile, user);
|
||||
|
||||
if (success) {
|
||||
// You can use `data` here if needed
|
||||
createToast({ body: t("bulkImport.uploadSuccess") });
|
||||
navigate("/uptime");
|
||||
} else {
|
||||
createToast({ body: error || t("bulkImport.uploadFailed") });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box className="bulk-import-monitor">
|
||||
<Breadcrumbs list={crumbs} />
|
||||
<Stack
|
||||
component="form"
|
||||
gap={theme.spacing(12)}
|
||||
mt={theme.spacing(6)}
|
||||
>
|
||||
<Typography
|
||||
component="h1"
|
||||
variant="h1"
|
||||
>
|
||||
{t("bulkImport.title")}
|
||||
</Typography>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h2"
|
||||
variant="h2"
|
||||
>
|
||||
{t("bulkImport.selectFileTips")}
|
||||
</Typography>
|
||||
<Typography component="p">
|
||||
<Trans
|
||||
i18nKey="bulkImport.selectFileDescription"
|
||||
components={{
|
||||
template: (
|
||||
<Link
|
||||
color="info"
|
||||
download
|
||||
href="/bulk_import_monitors_template.csv"
|
||||
/>
|
||||
),
|
||||
sample: (
|
||||
<Link
|
||||
color="info"
|
||||
download
|
||||
href="/bulk_import_monitors_sample.csv"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(12)}>
|
||||
<Stack gap={theme.spacing(6)}>
|
||||
<UploadFile onFileSelect={(file) => setSelectedFile(file)} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="accent"
|
||||
onClick={handleSubmit}
|
||||
disabled={hookLoading}
|
||||
loading={hookLoading}
|
||||
>
|
||||
{t("submit")}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default BulkImport;
|
||||
@@ -53,7 +53,6 @@ import CreateNewMaintenanceWindow from "@/Pages/Maintenance/create";
|
||||
import ProtectedRoute from "../Components/v1/ProtectedRoute";
|
||||
import RoleProtectedRoute from "../Components/v1/RoleProtectedRoute";
|
||||
import withAdminCheck from "@/Components/v1/HOC/withAdminCheck";
|
||||
import BulkImport from "../Pages/Uptime/BulkImport/index.jsx";
|
||||
import Logs from "../Pages/Logs";
|
||||
|
||||
import CreateMonitor from "@/Pages/CreateMonitor";
|
||||
@@ -87,10 +86,7 @@ const Routes = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="/uptime/bulk-import"
|
||||
element={<BulkImport />}
|
||||
/>
|
||||
<Route path="/uptime/bulk-import" />
|
||||
|
||||
<Route
|
||||
path="/uptime/create"
|
||||
|
||||
Reference in New Issue
Block a user