mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-18 07:28:31 -05:00
Merge pull request #625 from bluewave-labs/feat/app-layout
App layout redesign
This commit is contained in:
@@ -3,6 +3,27 @@ import PropTypes from "prop-types";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
/**
|
||||
* Generates a color based on the input string.
|
||||
* @param {string} string - The input string to generate the color from.
|
||||
* @returns {string}
|
||||
*/
|
||||
const stringToColor = (string) => {
|
||||
let hash = 0;
|
||||
let i;
|
||||
for (i = 0; i < string.length; i += 1) {
|
||||
hash = string.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
|
||||
let color = "#";
|
||||
for (i = 0; i < 3; i += 1) {
|
||||
const value = (hash >> (i * 8)) & 0xff;
|
||||
color += `00${value.toString(16)}`.slice(-2);
|
||||
}
|
||||
|
||||
return color;
|
||||
};
|
||||
|
||||
/**
|
||||
* @component
|
||||
* @param {Object} props
|
||||
@@ -18,7 +39,7 @@ import { useEffect, useState } from "react";
|
||||
const Avatar = ({ src, small, sx }) => {
|
||||
const { user } = useSelector((state) => state.auth);
|
||||
|
||||
const style = small ? { width: 25, height: 25 } : { width: 64, height: 64 };
|
||||
const style = small ? { width: 32, height: 32 } : { width: 64, height: 64 };
|
||||
const border = small ? 1 : 3;
|
||||
|
||||
const [image, setImage] = useState();
|
||||
@@ -35,8 +56,9 @@ const Avatar = ({ src, small, sx }) => {
|
||||
src ? src : user?.avatarImage ? image : "/static/images/avatar/2.jpg"
|
||||
}
|
||||
sx={{
|
||||
fontSize: small ? "13px" : "22px",
|
||||
fontSize: small ? "16px" : "22px",
|
||||
fontWeight: 400,
|
||||
backgroundColor: stringToColor(`${user?.firstName} ${user?.lastName}`),
|
||||
display: "inline-flex",
|
||||
"&::before": {
|
||||
content: `""`,
|
||||
@@ -52,7 +74,8 @@ const Avatar = ({ src, small, sx }) => {
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
{user.firstName?.charAt(0)}{user.lastName?.charAt(0)}
|
||||
{user.firstName?.charAt(0)}
|
||||
{user.lastName?.charAt(0)}
|
||||
</MuiAvatar>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/* NavBar Component Styles*/
|
||||
.MuiToolbar-root {
|
||||
min-height: var(--env-var-nav-bar-height) !important;
|
||||
}
|
||||
|
||||
.icon-button-toggle-title {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
|
||||
.icon-button-toggle-pic {
|
||||
width: 10px;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
#icon-button {
|
||||
-webkit-transition: none;
|
||||
transition: none;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#menu-appbar svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--env-var-color-25);
|
||||
}
|
||||
|
||||
#bw-uptime-logo-dashboard {
|
||||
width: fit-content;
|
||||
height: 16px;
|
||||
margin-left: 15px;
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
import "./index.css";
|
||||
import { cloneElement, useState } from "react";
|
||||
import AppBar from "@mui/material/AppBar";
|
||||
import Box from "@mui/material/Box";
|
||||
import Toolbar from "@mui/material/Toolbar";
|
||||
import IconButton from "@mui/material/IconButton";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Menu from "@mui/material/Menu";
|
||||
import Avatar from "../Avatar";
|
||||
import Tooltip from "@mui/material/Tooltip";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
|
||||
import { clearAuthState } from "../../Features/Auth/authSlice";
|
||||
import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import LockSvg from "../../assets/icons/lock.svg?react";
|
||||
import UserSvg from "../../assets/icons/user.svg?react";
|
||||
import TeamSvg from "../../assets/icons/user-two.svg?react";
|
||||
import LogoutSvg from "../../assets/icons/logout.svg?react";
|
||||
import { Stack, useScrollTrigger } from "@mui/material";
|
||||
import axiosIntance from "../../Utils/axiosConfig";
|
||||
import axios from "axios";
|
||||
|
||||
const icons = {
|
||||
Profile: <UserSvg />,
|
||||
Team: <TeamSvg />,
|
||||
Password: <LockSvg />,
|
||||
Logout: <LogoutSvg />,
|
||||
};
|
||||
|
||||
function AddBorderOnScroll(props) {
|
||||
const { children, window } = props;
|
||||
const trigger = useScrollTrigger({
|
||||
target: window ? window() : undefined,
|
||||
disableHysteresis: true,
|
||||
threshold: 0,
|
||||
});
|
||||
|
||||
return (
|
||||
<AppBar
|
||||
className={trigger ? "scrolled" : ""}
|
||||
position="sticky"
|
||||
sx={{
|
||||
boxShadow: "none",
|
||||
transition: "all 0.3s ease",
|
||||
borderBottom: "1px solid transparent",
|
||||
"&.scrolled": {
|
||||
borderBottom: "1px solid #eaecf0",
|
||||
backgroundColor: "white",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AppBar>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* NavBar component
|
||||
*
|
||||
* A responsive navigation bar component with a user menu.
|
||||
*
|
||||
* @component
|
||||
* @example
|
||||
* return (
|
||||
* <NavBar />
|
||||
* )
|
||||
*/
|
||||
function NavBar() {
|
||||
const theme = useTheme();
|
||||
const [anchorElUser, setAnchorElUser] = useState(null);
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const authState = useSelector((state) => state.auth);
|
||||
|
||||
// Initialize settings and update based on user role
|
||||
let settings = ["Profile", "Password", "Team", "Logout"];
|
||||
if (authState.user?.role && !authState.user.role.includes("admin")) {
|
||||
settings = ["Profile", "Password", "Logout"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles opening the user menu.
|
||||
*
|
||||
* @param {React.MouseEvent<HTMLElement>} event - The event triggered by clicking the user menu button.
|
||||
*/
|
||||
const handleOpenUserMenu = (event) => {
|
||||
setAnchorElUser(event.currentTarget);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles logging out the user
|
||||
*
|
||||
*/
|
||||
const logout = async () => {
|
||||
// Clear auth state
|
||||
dispatch(clearAuthState());
|
||||
dispatch(clearUptimeMonitorState());
|
||||
// Make request to BE to remove JWT from user
|
||||
await axiosIntance.post(
|
||||
"/auth/logout",
|
||||
{ email: authState.user.email },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authState.authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
navigate("/login");
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles closing the user menu.
|
||||
*/
|
||||
const handleCloseUserMenu = (setting) => {
|
||||
setAnchorElUser(null);
|
||||
switch (setting) {
|
||||
case "Profile":
|
||||
navigate("/account/profile");
|
||||
break;
|
||||
case "Team":
|
||||
navigate("/account/team");
|
||||
break;
|
||||
case "Password":
|
||||
navigate("/account/password");
|
||||
break;
|
||||
case "Logout":
|
||||
logout();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<AddBorderOnScroll>
|
||||
<Toolbar disableGutters sx={{ alignSelf: "flex-end", paddingX: "25px" }}>
|
||||
<Tooltip title="Open settings">
|
||||
<IconButton
|
||||
id="icon-button"
|
||||
onClick={handleOpenUserMenu}
|
||||
sx={{ p: 0 }}
|
||||
>
|
||||
<Stack direction="row" alignItems="center" gap="8px">
|
||||
<Avatar small={true} />
|
||||
<Box
|
||||
className="icon-button-toggle-title"
|
||||
sx={{ mr: "3px", lineHeight: 2 }}
|
||||
>
|
||||
{authState.user?.firstName} {authState.user?.lastName}
|
||||
</Box>
|
||||
<KeyboardArrowDownIcon sx={{ mt: "2px" }} />
|
||||
</Stack>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
sx={{
|
||||
mt: theme.spacing(5.5),
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
id="menu-appbar"
|
||||
anchorEl={anchorElUser}
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
open={Boolean(anchorElUser)}
|
||||
onClose={handleCloseUserMenu}
|
||||
>
|
||||
{settings.map((setting) => (
|
||||
<MenuItem
|
||||
id="menu-item"
|
||||
key={setting}
|
||||
onClick={() => handleCloseUserMenu(setting)}
|
||||
sx={{ width: "150px" }}
|
||||
>
|
||||
{icons[setting]}
|
||||
<Typography
|
||||
fontSize="var(--env-var-font-size-medium)"
|
||||
textAlign="center"
|
||||
marginLeft="8px"
|
||||
sx={{ fontWeight: 400, color: "#344054" }}
|
||||
>
|
||||
{setting}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</Toolbar>
|
||||
</AddBorderOnScroll>
|
||||
);
|
||||
}
|
||||
|
||||
export default NavBar;
|
||||
@@ -18,11 +18,14 @@ aside span.MuiTypography-root {
|
||||
color: var(--env-var-color-5);
|
||||
line-height: 1;
|
||||
}
|
||||
aside .MuiStack-root:nth-last-child(2) {
|
||||
aside .MuiStack-root:nth-last-child(3) {
|
||||
margin-top: auto;
|
||||
}
|
||||
aside .MuiStack-root:last-child,
|
||||
aside .MuiStack-root:nth-last-child(3) {
|
||||
position: relative;
|
||||
}
|
||||
aside .MuiStack-root:nth-last-child(2):before {
|
||||
aside .MuiStack-root:last-child:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
@@ -31,5 +34,35 @@ aside .MuiStack-root:nth-last-child(2):before {
|
||||
border-top: solid 1px var(--env-var-color-6);
|
||||
}
|
||||
aside .MuiStack-root:last-child {
|
||||
margin-bottom: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.sidebar-menu {
|
||||
margin-top: -20px;
|
||||
}
|
||||
.sidebar-menu .MuiPaper-root {
|
||||
box-shadow: var(--env-var-shadow-1);
|
||||
border: solid 1px var(--env-var-color-6);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
gap: 1px;
|
||||
}
|
||||
.sidebar-menu .MuiList-root {
|
||||
min-width: 100px;
|
||||
width: 150px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.sidebar-menu li.MuiButtonBase-root:last-child {
|
||||
border-top: solid 1px var(--env-var-color-6);
|
||||
padding: 12px 16px;
|
||||
}
|
||||
.sidebar-menu li.MuiButtonBase-root {
|
||||
min-height: fit-content;
|
||||
}
|
||||
.sidebar-menu .MuiList-root svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.sidebar-menu span {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-2);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
import { useState } from "react";
|
||||
import { Menu, MenuItem, Stack, Tooltip, Typography } from "@mui/material";
|
||||
import { useLocation, useNavigate } from "react-router";
|
||||
import { useTheme } from "@emotion/react";
|
||||
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { clearAuthState } from "../../Features/Auth/authSlice";
|
||||
import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
import { createToast } from "../../Utils/toastUtils";
|
||||
import axiosInstance from "../../Utils/axiosConfig";
|
||||
import Avatar from "../Avatar";
|
||||
import LockSvg from "../../assets/icons/lock.svg?react";
|
||||
import UserSvg from "../../assets/icons/user.svg?react";
|
||||
import TeamSvg from "../../assets/icons/user-two.svg?react";
|
||||
import LogoutSvg from "../../assets/icons/logout.svg?react";
|
||||
import BWULogo from "../../assets/Images/bwl-logo.svg?react";
|
||||
import Support from "../../assets/icons/support.svg?react";
|
||||
import StatusPages from "../../assets/icons/status-pages.svg?react";
|
||||
@@ -11,16 +21,10 @@ import Incidents from "../../assets/icons/incidents.svg?react";
|
||||
import Integrations from "../../assets/icons/integrations.svg?react";
|
||||
import PageSpeed from "../../assets/icons/page-speed.svg?react";
|
||||
import Settings from "../../assets/icons/settings.svg?react";
|
||||
import Arrow from "../../assets/icons/down-arrow.svg?react";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
/**
|
||||
* @component
|
||||
* Sidebar component serves as a sidebar containing a menu.
|
||||
*
|
||||
* @returns {JSX.Element} The JSX element representing the Sidebar component.
|
||||
*/
|
||||
|
||||
const menu = [
|
||||
{ name: "Monitors", path: "monitors", icon: <Monitors /> },
|
||||
{ name: "Incidents", path: "incidents", icon: <Incidents /> },
|
||||
@@ -32,10 +36,94 @@ const menu = [
|
||||
{ name: "Settings", path: "settings", icon: <Settings /> },
|
||||
];
|
||||
|
||||
const icons = {
|
||||
Profile: <UserSvg />,
|
||||
Team: <TeamSvg />,
|
||||
Password: <LockSvg />,
|
||||
Logout: <LogoutSvg />,
|
||||
};
|
||||
|
||||
/**
|
||||
* @component
|
||||
* Sidebar component serves as a sidebar containing a menu.
|
||||
*
|
||||
* @returns {JSX.Element} The JSX element representing the Sidebar component.
|
||||
*/
|
||||
|
||||
function Sidebar() {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const [anchorElUser, setAnchorElUser] = useState(null);
|
||||
const authState = useSelector((state) => state.auth);
|
||||
|
||||
// Initialize settings and update based on user role
|
||||
let settings = ["Profile", "Password", "Team", "Logout"];
|
||||
if (authState.user?.role && !authState.user.role.includes("admin")) {
|
||||
settings = ["Profile", "Password", "Logout"];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles opening the user menu.
|
||||
*
|
||||
* @param {React.MouseEvent<HTMLElement>} event - The event triggered by clicking the user menu button.
|
||||
*/
|
||||
const handleOpenUserMenu = (event) => {
|
||||
setAnchorElUser(event.currentTarget);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles logging out the user
|
||||
*
|
||||
*/
|
||||
const logout = async () => {
|
||||
try {
|
||||
// Make request to BE to remove JWT from user
|
||||
await axiosInstance.post(
|
||||
"/auth/logout",
|
||||
{ email: authState.user.email },
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${authState.authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// Clear auth state
|
||||
dispatch(clearAuthState());
|
||||
dispatch(clearUptimeMonitorState());
|
||||
navigate("/login");
|
||||
} catch (error) {
|
||||
createToast({
|
||||
body: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles closing the user menu.
|
||||
*/
|
||||
const handleCloseUserMenu = (setting) => {
|
||||
setAnchorElUser(null);
|
||||
switch (setting) {
|
||||
case "Profile":
|
||||
navigate("/account/profile");
|
||||
break;
|
||||
case "Team":
|
||||
navigate("/account/team");
|
||||
break;
|
||||
case "Password":
|
||||
navigate("/account/password");
|
||||
break;
|
||||
case "Logout":
|
||||
logout();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack component="aside" gap={theme.gap.xs}>
|
||||
@@ -48,6 +136,8 @@ function Sidebar() {
|
||||
key={item.path}
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
py={theme.gap.small}
|
||||
px={theme.gap.medium}
|
||||
gap={theme.gap.small}
|
||||
borderRadius={`${theme.shape.borderRadius}px`}
|
||||
onClick={() =>
|
||||
@@ -59,12 +149,66 @@ function Sidebar() {
|
||||
)
|
||||
: navigate(`/${item.path}`)
|
||||
}
|
||||
sx={{ p: `${theme.gap.small} ${theme.gap.medium}` }}
|
||||
>
|
||||
{item.icon}
|
||||
<Typography component="span">{item.name}</Typography>
|
||||
</Stack>
|
||||
))}
|
||||
<Tooltip
|
||||
title="Open settings"
|
||||
slotProps={{
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: [0, -14],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
py={theme.gap.small}
|
||||
px={theme.gap.medium}
|
||||
gap={theme.gap.xs}
|
||||
borderRadius={`${theme.shape.borderRadius}px`}
|
||||
onClick={handleOpenUserMenu}
|
||||
>
|
||||
<Avatar small={true} />
|
||||
<Typography component="span" ml={theme.gap.xs}>
|
||||
{authState.user?.firstName} {authState.user?.lastName}
|
||||
</Typography>
|
||||
<Arrow style={{ marginTop: "2px", marginLeft: "auto" }} />
|
||||
</Stack>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
className="sidebar-menu"
|
||||
anchorEl={anchorElUser}
|
||||
anchorOrigin={{
|
||||
vertical: "top",
|
||||
horizontal: "center",
|
||||
}}
|
||||
keepMounted
|
||||
open={Boolean(anchorElUser)}
|
||||
onClose={handleCloseUserMenu}
|
||||
autoFocus={false}
|
||||
sx={{
|
||||
ml: theme.gap.xxl,
|
||||
}}
|
||||
>
|
||||
{settings.map((setting) => (
|
||||
<MenuItem key={setting} onClick={() => handleCloseUserMenu(setting)}>
|
||||
{icons[setting]}
|
||||
<Typography component="span" ml={theme.gap.small}>
|
||||
{setting}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,55 +1,34 @@
|
||||
.home-layout {
|
||||
display: grid;
|
||||
position: relative;
|
||||
grid-template-areas:
|
||||
"aside header"
|
||||
"aside main";
|
||||
grid-template-columns: var(--env-var-side-bar-width) auto;
|
||||
grid-template-rows: var(--env-var-nav-bar-height) auto;
|
||||
#root:has(.home-layout) {
|
||||
background-color: var(--env-var-color-30);
|
||||
}
|
||||
|
||||
.home-layout {
|
||||
display: flex;
|
||||
position: relative;
|
||||
gap: var(--env-var-spacing-2);
|
||||
min-height: 100vh;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: var(--env-var-spacing-2);
|
||||
}
|
||||
|
||||
.home-layout aside {
|
||||
grid-area: aside;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: var(--env-var-side-bar-width);
|
||||
position: sticky;
|
||||
top: var(--env-var-spacing-2);
|
||||
left: 0;
|
||||
|
||||
height: calc(100vh - var(--env-var-spacing-2) * 2);
|
||||
max-width: var(--env-var-side-bar-width);
|
||||
flex: 1;
|
||||
|
||||
border: 1px solid var(--color-border-0);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
background-color: var(--env-var-color-8);
|
||||
border-right: 1px solid var(--color-border-0);
|
||||
|
||||
padding: var(--env-var-spacing-1) var(--env-var-spacing-1-plus);
|
||||
}
|
||||
.home-layout header {
|
||||
grid-area: header;
|
||||
background-color: var(--env-var-color-30);
|
||||
}
|
||||
|
||||
.home-layout > div {
|
||||
height: 100%;
|
||||
grid-area: main;
|
||||
padding: var(--env-var-spacing-2) calc(var(--env-var-spacing-4) * 2);
|
||||
padding-bottom: var(--env-var-spacing-4);
|
||||
max-width: 100%;
|
||||
}
|
||||
.home-layout > div:not(:has([class*="fallback"])) {
|
||||
max-width: 1500px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.home-layout {
|
||||
grid-template-areas:
|
||||
"header header"
|
||||
"main main";
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 64px auto;
|
||||
}
|
||||
|
||||
aside {
|
||||
display: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.home-layout > div {
|
||||
padding: var(--env-var-spacing-1);
|
||||
}
|
||||
min-height: calc(100vh - var(--env-var-spacing-2) * 2);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import NavBar from "../../Components/NavBar";
|
||||
import Sidebar from "../../Components/Sidebar";
|
||||
import { Outlet } from "react-router";
|
||||
import { Box } from "@mui/material";
|
||||
@@ -9,7 +8,6 @@ const HomeLayout = () => {
|
||||
return (
|
||||
<Box className="home-layout">
|
||||
<Sidebar />
|
||||
<NavBar />
|
||||
<Outlet />
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -29,7 +29,7 @@ const Account = ({ open = "profile" }) => {
|
||||
if (!user.role.includes("admin")) tabList = ["Profile", "Password"];
|
||||
|
||||
return (
|
||||
<Box className="account">
|
||||
<Box className="account" pt={theme.gap.xl}>
|
||||
<TabContext value={tab}>
|
||||
<Box
|
||||
sx={{
|
||||
|
||||
@@ -78,7 +78,7 @@ const Incidents = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack className="incidents" gap={theme.gap.large}>
|
||||
<Stack className="incidents" pt={theme.gap.xl} gap={theme.gap.large}>
|
||||
{loading ? (
|
||||
<SkeletonLayout />
|
||||
) : (
|
||||
|
||||
@@ -108,7 +108,7 @@ const Integrations = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack className="integrations" gap={theme.gap.xs}>
|
||||
<Stack className="integrations" pt={theme.gap.xl} gap={theme.gap.xs}>
|
||||
<Typography component="h1">Integrations</Typography>
|
||||
<Typography mb={theme.gap.large}>
|
||||
Connect BlueWave Uptime to your favorite service.
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
.configure-monitor-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--env-var-spacing-4);
|
||||
gap: var(--env-var-spacing-2);
|
||||
}
|
||||
|
||||
body:has(.configure-monitor) .select-dropdown .MuiMenuItem-root,
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
.create-monitor-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--env-var-spacing-4);
|
||||
gap: var(--env-var-spacing-2);
|
||||
}
|
||||
|
||||
.create-monitor .MuiStack-root .MuiButtonGroup-root button {
|
||||
|
||||
@@ -243,7 +243,7 @@ const Monitors = () => {
|
||||
let loading = monitorState.isLoading && monitorState.monitors.length === 0;
|
||||
|
||||
return (
|
||||
<Stack className="monitors" gap={theme.gap.large}>
|
||||
<Stack className="monitors" pt={theme.gap.xl} gap={theme.gap.large}>
|
||||
{loading ? (
|
||||
<SkeletonLayout />
|
||||
) : (
|
||||
@@ -253,7 +253,10 @@ const Monitors = () => {
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<Typography component="h1">
|
||||
<Typography
|
||||
component="h1"
|
||||
sx={{ lineHeight: 1, alignSelf: "flex-end" }}
|
||||
>
|
||||
Hello, {authState.user.firstName}
|
||||
</Typography>
|
||||
{monitorState.monitors?.length !== 0 && (
|
||||
@@ -302,7 +305,8 @@ const Monitors = () => {
|
||||
</Stack>
|
||||
<Stack
|
||||
gap={theme.gap.large}
|
||||
p={theme.gap.xl}
|
||||
p={theme.gap.lgplus}
|
||||
flex={1}
|
||||
border={1}
|
||||
borderColor={theme.palette.otherColors.graishWhite}
|
||||
backgroundColor={theme.palette.otherColors.white}
|
||||
|
||||
@@ -106,7 +106,7 @@ const CreatePageSpeed = () => {
|
||||
onClick={() => navigate("/pagespeed")}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.otherColors.fillGray,
|
||||
mb: theme.gap.large,
|
||||
mb: theme.gap.medium,
|
||||
px: theme.gap.ml,
|
||||
"& svg.MuiSvgIcon-root": {
|
||||
mr: theme.gap.small,
|
||||
@@ -122,8 +122,6 @@ const CreatePageSpeed = () => {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: theme.gap.large,
|
||||
// TODO
|
||||
maxWidth: "1000px",
|
||||
}}
|
||||
>
|
||||
<Typography component="h1">Create a page speed monitor</Typography>
|
||||
|
||||
@@ -363,7 +363,7 @@ const PageSpeedDetails = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
gap={theme.gap.xl}
|
||||
gap={theme.gap.large}
|
||||
flexWrap="wrap"
|
||||
>
|
||||
<StatBox
|
||||
@@ -409,7 +409,7 @@ const PageSpeedDetails = () => {
|
||||
<PageSpeedLineChart pageSpeedChecks={monitor?.checks?.reverse()} />
|
||||
</Box>
|
||||
<Typography component="h2">Performance report</Typography>
|
||||
<Stack direction="row" alignItems="center" overflow="hidden">
|
||||
<Stack direction="row" alignItems="center" overflow="hidden" flex={1}>
|
||||
<Stack
|
||||
alignItems="center"
|
||||
textAlign="center"
|
||||
|
||||
@@ -136,7 +136,7 @@ const PageSpeed = () => {
|
||||
let isActuallyLoading = isLoading && monitors.length === 0;
|
||||
|
||||
return (
|
||||
<Box className="page-speed">
|
||||
<Box className="page-speed" pt={theme.gap.xl}>
|
||||
{isActuallyLoading ? (
|
||||
<SkeletonLayout />
|
||||
) : monitors?.length !== 0 ? (
|
||||
|
||||
@@ -96,9 +96,10 @@ const theme = createTheme({
|
||||
ml: "16px",
|
||||
mlplus: "20px",
|
||||
large: "24px",
|
||||
lgplus: "32px",
|
||||
xl: "40px",
|
||||
xxl: "60px",
|
||||
triplexl: "120px"
|
||||
triplexl: "120px",
|
||||
},
|
||||
alert: {
|
||||
info: {
|
||||
|
||||
@@ -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="M6 9L12 15L18 9" stroke="#667085" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 212 B |
Reference in New Issue
Block a user