Merge branch 'develop' into 901-create-confirmation-dialogs-for-clear-all-stats-and-remove-all-monitors

This commit is contained in:
Shemy Gan committed 2024-10-15 09:01:56 -04:00
commit fef74266c8
29 files changed
+2930 -207

No files matched your search

+2 -1
View File
@@ -74,6 +74,7 @@ const Field = forwardRef(
"&:has(.input-error) .MuiOutlinedInput-root fieldset": {
borderColor: theme.palette.error.text,
},
display: hidden ? "none" : "",
}}
>
{label && (
@@ -142,7 +143,7 @@ const Field = forwardRef(
},
}
: {
display: hidden ? "none" : "",
}
}
InputProps={{
+54 -83
View File
@@ -41,6 +41,9 @@ import ArrowUp from "../../assets/icons/up-arrow.svg?react";
import ArrowRight from "../../assets/icons/right-arrow.svg?react";
import ArrowLeft from "../../assets/icons/left-arrow.svg?react";
import DotsVertical from "../../assets/icons/dots-vertical.svg?react";
import ChangeLog from "../../assets/icons/changeLog.svg?react";
import Docs from "../../assets/icons/docs.svg?react";
import Folder from "../../assets/icons/folder.svg?react";
import "./index.css";
@@ -66,12 +69,30 @@ const menu = [
{ name: "Team", path: "account/team", icon: <TeamSvg /> },
],
},
{
name: "Other",
icon: <Folder />,
nested: [
{ name: "Settings", path: "settings", icon: <Settings /> },
{ name: "Support", path: "support", icon: <Support /> },
{ name: "Docs", path: "docs", icon: <Docs /> },
{ name: "Changelog", path: "changelog", icon: <ChangeLog /> },
],
},
];
const other = [
{ name: "Support", path: "support", icon: <Support /> },
{ name: "Settings", path: "settings", icon: <Settings /> },
];
const URL_MAP = {
support: "https://github.com/bluewave-labs/bluewave-uptime/issues",
docs: "https://bluewavelabs.gitbook.io/uptime-manager",
changelog: "https://github.com/bluewave-labs/bluewave-uptime/releases",
};
const PATH_MAP = {
monitors: "Dashboard",
pagespeed: "Dashboard",
account: "Account",
settings: "Other",
};
/**
* @component
@@ -87,7 +108,7 @@ function Sidebar() {
const dispatch = useDispatch();
const authState = useSelector((state) => state.auth);
const collapsed = useSelector((state) => state.ui.sidebar.collapsed);
const [open, setOpen] = useState({ Dashboard: false, Account: false });
const [open, setOpen] = useState({ Dashboard: false, Account: false, Other: false });
const [anchorEl, setAnchorEl] = useState(null);
const [popup, setPopup] = useState();
const { user } = useSelector((state) => state.auth);
@@ -120,13 +141,13 @@ function Sidebar() {
};
useEffect(() => {
if (
location.pathname.includes("monitors") ||
location.pathname.includes("pagespeed")
)
setOpen((prev) => ({ ...prev, Dashboard: true }));
else if (location.pathname.includes("/account"))
setOpen((prev) => ({ ...prev, Account: true }));
const matchedKey = Object.keys(PATH_MAP).find((key) =>
location.pathname.includes(key)
);
if (matchedKey) {
setOpen((prev) => ({ ...prev, [PATH_MAP[matchedKey]]: true }));
}
}, []);
return (
@@ -202,7 +223,9 @@ function Sidebar() {
},
}}
onClick={() => {
setOpen({ Dashboard: false, Account: false });
setOpen(prev =>
Object.fromEntries(Object.keys(prev).map(key => [key, false]))
)
dispatch(toggleSidebar());
}}
>
@@ -346,7 +369,12 @@ function Sidebar() {
}
key={child.path}
onClick={() => {
navigate(`/${child.path}`);
const url = URL_MAP[child.path];
if (url) {
window.open(url, "_blank", "noreferrer");
} else {
navigate(`/${child.path}`);
}
closePopup();
}}
sx={{
@@ -372,8 +400,8 @@ function Sidebar() {
<ListItemButton
onClick={() =>
setOpen((prev) => ({
...prev,
[`${item.name}`]: !prev[`${item.name}`],
...Object.fromEntries(Object.keys(prev).map(key => [key, false])),
[item.name]: !prev[item.name]
}))
}
sx={{
@@ -409,7 +437,14 @@ function Sidebar() {
: ""
}
key={child.path}
onClick={() => navigate(`/${child.path}`)}
onClick={() => {
const url = URL_MAP[child.path];
if (url) {
window.open(url, "_blank", "noreferrer");
} else {
navigate(`/${child.path}`);
}
}}
sx={{
gap: theme.spacing(4),
borderRadius: theme.shape.borderRadius,
@@ -455,70 +490,6 @@ function Sidebar() {
)
)}
</List>
<Divider sx={{ my: theme.spacing(4) }} />
{/* other */}
<List
component="nav"
aria-labelledby="nested-other-subheader"
subheader={
<ListSubheader
component="div"
id="nested-other-subheader"
sx={{
pt: theme.spacing(4),
px: collapsed ? 0 : theme.spacing(4),
backgroundColor: "transparent",
}}
>
Other
</ListSubheader>
}
sx={{ px: theme.spacing(6) }}
>
{other.map((item) => (
<Tooltip
key={item.path}
placement="right"
title={collapsed ? item.name : ""}
slotProps={{
popper: {
modifiers: [
{
name: "offset",
options: {
offset: [0, -16],
},
},
],
},
}}
disableInteractive
>
<ListItemButton
className={
location.pathname.includes(item.path) ? "selected-path" : ""
}
onClick={() =>
item.path === "support"
? window.open(
"https://github.com/bluewave-labs/bluewave-uptime/issues",
"_blank",
"noreferrer"
)
: navigate(`/${item.path}`)
}
sx={{
gap: theme.spacing(4),
borderRadius: theme.shape.borderRadius,
px: theme.spacing(4),
}}
>
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon}</ListItemIcon>
<ListItemText>{item.name}</ListItemText>
</ListItemButton>
</Tooltip>
))}
</List>
<Divider sx={{ mt: "auto" }} />
<Stack
@@ -632,7 +603,7 @@ function Sidebar() {
</MenuItem>
)}
{collapsed && <Divider />}
<MenuItem
{/* <MenuItem
onClick={() => {
dispatch(setMode("light"));
closePopup();
@@ -647,7 +618,7 @@ function Sidebar() {
}}
>
Dark
</MenuItem>
</MenuItem> */}
<Divider />
<MenuItem
onClick={logout}
@@ -469,14 +469,6 @@ const ProfilePanel = () => {
gap={theme.spacing(5)}
justifyContent="flex-end"
>
<Button
variant="outlined"
color="secondary"
disabled
sx={{ mr: "auto" }}
>
Edit
</Button>
<Button variant="text" color="info" onClick={removePicture}>
Remove
</Button>
@@ -49,6 +49,7 @@ const TeamPanel = () => {
const [tableData, setTableData] = useState({});
const [members, setMembers] = useState([]);
const [filter, setFilter] = useState("all");
const [isDisabled, setIsDisabled] = useState(true);
const [errors, setErrors] = useState({});
const [isSendingInvite, setIsSendingInvite] = useState(false);
@@ -137,6 +138,9 @@ const TeamPanel = () => {
setTableData(data);
}, [members, filter]);
useEffect(() => {
setIsDisabled(Object.keys(errors).length !== 0 || toInvite.email === "");
}, [errors, toInvite.email]);
// RENAME ORGANIZATION
const toggleEdit = () => {
@@ -172,6 +176,10 @@ const TeamPanel = () => {
};
const handleInviteMember = async () => {
if (!toInvite.email) {
setErrors((prev) => ({ ...prev, email: "Email is required." }));
return;
}
setIsSendingInvite(true);
if (!toInvite.role.includes("user") || !toInvite.role.includes("admin"))
setToInvite((prev) => ({ ...prev, role: ["user"] }));
@@ -415,7 +423,7 @@ const TeamPanel = () => {
color="primary"
onClick={handleInviteMember}
loading={isSendingInvite}
disabled={Object.keys(errors).length !== 0}
disabled={isDisabled}
>
Send invite
</LoadingButton>
+35 -1
View File
@@ -3,6 +3,7 @@ import { useSelector, useDispatch } from "react-redux";
import { useTheme } from "@emotion/react";
import { useNavigate } from "react-router-dom";
import { createToast } from "../../../Utils/toastUtils";
import { logger } from "../../../Utils/Logger";
import {
Button,
IconButton,
@@ -14,6 +15,7 @@ import {
} from "@mui/material";
import {
deleteUptimeMonitor,
pauseUptimeMonitor,
getUptimeMonitorsByTeamId,
} from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
import Settings from "../../../assets/icons/settings-bold.svg?react";
@@ -26,6 +28,8 @@ const ActionsMenu = ({ monitor, isAdmin, updateCallback }) => {
const dispatch = useDispatch();
const theme = useTheme();
const authState = useSelector((state) => state.auth);
const authToken = authState.authToken;
const handleRemove = async (event) => {
event.preventDefault();
@@ -44,6 +48,24 @@ const ActionsMenu = ({ monitor, isAdmin, updateCallback }) => {
}
};
const handlePause = async () => {
try {
const action = await dispatch(
pauseUptimeMonitor({ authToken, monitorId: monitor._id })
);
if (pauseUptimeMonitor.fulfilled.match(action)) {
updateCallback();
const state = action?.payload?.data.isActive === false ? "paused" : "resumed";
createToast({ body: `Monitor ${state} successfully.` });
} else {
throw new Error(action?.error?.message ?? "Failed to pause monitor.");
}
} catch (error) {
logger.error("Error pausing monitor:", monitor._id, error);
createToast({ body: "Failed to pause monitor." });
}
};
const openMenu = (event, id, url) => {
event.preventDefault();
event.stopPropagation();
@@ -155,6 +177,17 @@ const ActionsMenu = ({ monitor, isAdmin, updateCallback }) => {
Clone
</MenuItem>
)}
{isAdmin && (
<MenuItem
onClick={(e) => {
e.stopPropagation();
handlePause(e);
}}
>
{monitor?.isActive === true ? "Pause" : "Resume"}
</MenuItem>
)}
{isAdmin && (
<MenuItem
onClick={(e) => {
@@ -164,7 +197,7 @@ const ActionsMenu = ({ monitor, isAdmin, updateCallback }) => {
>
Remove
</MenuItem>
)}
)}
</Menu>
<Modal
aria-labelledby="modal-delete-monitor"
@@ -238,6 +271,7 @@ ActionsMenu.propTypes = {
_id: PropTypes.string,
url: PropTypes.string,
type: PropTypes.string,
isActive: PropTypes.bool,
}).isRequired,
isAdmin: PropTypes.bool,
updateCallback: PropTypes.func,
+22 -1
View File
@@ -15,7 +15,7 @@ import {
import { update } from "../../Features/Auth/authSlice";
import PropTypes from "prop-types";
import LoadingButton from "@mui/lab/LoadingButton";
import { setTimezone } from "../../Features/UI/uiSlice";
import { setTimezone, setMode } from "../../Features/UI/uiSlice";
import timezones from "../../Utils/timezones.json";
import { useState } from "react";
import { ConfigBox } from "./styled";
@@ -33,6 +33,7 @@ const Settings = ({ isAdmin }) => {
const { isLoading } = useSelector((state) => state.uptimeMonitors);
const { isLoading: authIsLoading } = useSelector((state) => state.auth);
const { timezone } = useSelector((state) => state.ui);
const {mode} = useSelector((state) => state.ui);
const [checksIsLoading, setChecksIsLoading] = useState(false);
const [form, setForm] = useState({
ttl: checkTTL ? (checkTTL / SECONDS_PER_DAY).toString() : 0,
@@ -188,6 +189,26 @@ const Settings = ({ isAdmin }) => {
/>
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h1">Appearance</Typography>
<Typography sx={{ mt: theme.spacing(2), mb: theme.spacing(2) }}>
Switch between light and dark mode.
</Typography>
</Box>
<Stack gap={theme.spacing(20)}>
<Select
id="theme-mode"
label="Theme Mode"
value={mode}
onChange={(e) => {
dispatch(setMode(e.target.value));
}}
items={[{_id:'light', name:'Light'},{_id:'dark', name:'Dark'}]}
>
</Select>
</Stack>
</ConfigBox>
{isAdmin && (
<ConfigBox>
<Box>
+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17 17L22 12L17 7M7 7L2 12L7 17M14 3L10 21" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 237 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 11H8M10 15H8M16 7H8M20 6.8V17.2C20 18.8802 20 19.7202 19.673 20.362C19.3854 20.9265 18.9265 21.3854 18.362 21.673C17.7202 22 16.8802 22 15.2 22H8.8C7.11984 22 6.27976 22 5.63803 21.673C5.07354 21.3854 4.6146 20.9265 4.32698 20.362C4 19.7202 4 18.8802 4 17.2V6.8C4 5.11984 4 4.27976 4.32698 3.63803C4.6146 3.07354 5.07354 2.6146 5.63803 2.32698C6.27976 2 7.11984 2 8.8 2H15.2C16.8802 2 17.7202 2 18.362 2.32698C18.9265 2.6146 19.3854 3.07354 19.673 3.63803C20 4.27976 20 5.11984 20 6.8Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 684 B

+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 8.2C3 7.07989 3 6.51984 3.21799 6.09202C3.40973 5.71569 3.71569 5.40973 4.09202 5.21799C4.51984 5 5.0799 5 6.2 5H9.67452C10.1637 5 10.4083 5 10.6385 5.05526C10.8425 5.10425 11.0376 5.18506 11.2166 5.29472C11.4184 5.4184 11.5914 5.59135 11.9373 5.93726L12.0627 6.06274C12.4086 6.40865 12.5816 6.5816 12.7834 6.70528C12.9624 6.81494 13.1575 6.89575 13.3615 6.94474C13.5917 7 13.8363 7 14.3255 7H17.8C18.9201 7 19.4802 7 19.908 7.21799C20.2843 7.40973 20.5903 7.71569 20.782 8.09202C21 8.51984 21 9.0799 21 10.2V15.8C21 16.9201 21 17.4802 20.782 17.908C20.5903 18.2843 20.2843 18.5903 19.908 18.782C19.4802 19 18.9201 19 17.8 19H6.2C5.07989 19 4.51984 19 4.09202 18.782C3.71569 18.5903 3.40973 18.2843 3.21799 17.908C3 17.4802 3 16.9201 3 15.8V8.2Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB