Merge pull request #1152 from bluewave-labs/refactor/theme

Refactor - Theme
This commit is contained in:
Alexander Holliday authored and GitHub committed 2024-11-13 18:31:37 -08:00
commit e22476ee4a
36 files changed
+1128 -905

No files matched your search

+13 -13
View File
@@ -8,12 +8,12 @@
"name": "client",
"version": "0.0.0",
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@fontsource/roboto": "^5.0.13",
"@mui/icons-material": "^5.15.17",
"@mui/lab": "^5.0.0-alpha.170",
"@mui/material": "^5.15.16",
"@mui/material": "^5.16.7",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.22.2",
"@mui/x-date-pickers": "7.22.2",
@@ -304,9 +304,9 @@
}
},
"node_modules/@babel/runtime": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.9.tgz",
"integrity": "sha512-4zpTHZ9Cm6L9L+uIqghQX8ZXg8HKFcjYO3qHoO8zTmRm6HQUJ8SSJ+KRvbMBZn0EGVlT4DRYeQ/6hjlyXBh+Kg==",
"version": "7.26.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
"license": "MIT",
"dependencies": {
"regenerator-runtime": "^0.14.0"
@@ -1356,12 +1356,12 @@
}
},
"node_modules/@mui/types": {
"version": "7.2.15",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.15.tgz",
"integrity": "sha512-nbo7yPhtKJkdf9kcVOF8JZHPZTmqXjJ/tI0bdWgHg5tp9AnIN4Y7f7wm9T+0SyGYJk76+GYZ8Q5XaTYAsUHN0Q==",
"version": "7.2.19",
"resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.19.tgz",
"integrity": "sha512-6XpZEM/Q3epK9RN8ENoXuygnqUQxE+siN/6rGRi2iwJPgBUR25mphYQ9ZI87plGh58YoZ5pp40bFvKYOCDJ3tA==",
"license": "MIT",
"peerDependencies": {
"@types/react": "^17.0.0 || ^18.0.0"
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
@@ -2350,9 +2350,9 @@
"license": "MIT"
},
"node_modules/@types/prop-types": {
"version": "15.7.12",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
"version": "15.7.13",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
"integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==",
"license": "MIT"
},
"node_modules/@types/react": {
+3 -3
View File
@@ -11,12 +11,12 @@
"preview": "vite preview"
},
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@fontsource/roboto": "^5.0.13",
"@mui/icons-material": "^5.15.17",
"@mui/lab": "^5.0.0-alpha.170",
"@mui/material": "^5.15.16",
"@mui/material": "^5.16.7",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.22.2",
"@mui/x-date-pickers": "7.22.2",
-7
View File
@@ -1,7 +0,0 @@
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
+39 -23
View File
@@ -1,14 +1,12 @@
import { useEffect } from "react";
import { Routes, Route } from "react-router-dom";
import { Routes, Route, Navigate } from "react-router-dom";
import { useSelector } from "react-redux";
import { useDispatch } from "react-redux";
// import "./App.css";
import "react-toastify/dist/ReactToastify.css";
import { ToastContainer } from "react-toastify";
import NotFound from "./Pages/NotFound";
import Login from "./Pages/Auth/Login";
import Register from "./Pages/Auth/Register/Register";
import HomeLayout from "./Layouts/HomeLayout";
import Account from "./Pages/Account";
import Monitors from "./Pages/Monitors/Home";
import CreateMonitor from "./Pages/Monitors/CreateMonitor";
@@ -30,15 +28,17 @@ import CreatePageSpeed from "./Pages/PageSpeed/CreatePageSpeed";
import CreateNewMaintenanceWindow from "./Pages/Maintenance/CreateMaintenance";
import PageSpeedDetails from "./Pages/PageSpeed/Details";
import PageSpeedConfigure from "./Pages/PageSpeed/Configure";
import withAdminCheck from "./HOC/withAdminCheck";
import withAdminProp from "./HOC/withAdminProp";
import HomeLayout from "./Components/Layouts/HomeLayout";
import withAdminCheck from "./Components/HOC/withAdminCheck";
import withAdminProp from "./Components/HOC/withAdminProp";
import { ThemeProvider } from "@emotion/react";
import lightTheme from "./Utils/Theme/lightTheme";
import darkTheme from "./Utils/Theme/darkTheme";
import { CssBaseline } from "@mui/material";
import { CssBaseline, GlobalStyles } from "@mui/material";
import { getAppSettings } from "./Features/Settings/settingsSlice";
import { logger } from "./Utils/Logger"; // Import the logger
import { networkService } from "./main";
import { Infrastructure } from "./Pages/Infrastructure";
function App() {
const AdminCheckedRegister = withAdminCheck(Register);
const MonitorsWithAdminProp = withAdminProp(Monitors);
@@ -67,8 +67,19 @@ function App() {
}, []);
return (
/* Extract Themeprovider, baseline and global styles to Styles */
<ThemeProvider theme={mode === "light" ? lightTheme : darkTheme}>
<CssBaseline />
<GlobalStyles
styles={({ palette }) => {
return {
body: {
backgroundImage: `radial-gradient(circle, ${palette.gradient.color1}, ${palette.gradient.color2}, ${palette.gradient.color3}, ${palette.gradient.color4}, ${palette.gradient.color5})`,
},
};
}}
/>
{/* Extract Routes to Routes */}
<Routes>
<Route
exact
@@ -78,7 +89,7 @@ function App() {
<Route
exact
path="/"
element={<ProtectedRoute Component={MonitorsWithAdminProp} />}
element={<Navigate to="/monitors" />}
/>
<Route
path="/monitors"
@@ -96,6 +107,27 @@ function App() {
path="/monitors/configure/:monitorId/"
element={<ProtectedRoute Component={Configure} />}
/>
<Route
path="pagespeed"
element={<ProtectedRoute Component={PageSpeedWithAdminProp} />}
/>
<Route
path="pagespeed/create"
element={<ProtectedRoute Component={CreatePageSpeed} />}
/>
<Route
path="pagespeed/:monitorId"
element={<ProtectedRoute Component={PageSpeedDetailsWithAdminProp} />}
/>
<Route
path="pagespeed/configure/:monitorId"
element={<ProtectedRoute Component={PageSpeedConfigure} />}
/>
<Route
path="infrastructure"
element={<ProtectedRoute Component={Infrastructure} />}
/>
<Route
path="incidents/:monitorId?"
element={<ProtectedRoute Component={Incidents} />}
@@ -152,22 +184,6 @@ function App() {
/>
}
/>
<Route
path="pagespeed"
element={<ProtectedRoute Component={PageSpeedWithAdminProp} />}
/>
<Route
path="pagespeed/create"
element={<ProtectedRoute Component={CreatePageSpeed} />}
/>
<Route
path="pagespeed/:monitorId"
element={<ProtectedRoute Component={PageSpeedDetailsWithAdminProp} />}
/>
<Route
path="pagespeed/configure/:monitorId"
element={<ProtectedRoute Component={PageSpeedConfigure} />}
/>
</Route>
<Route
+3
View File
@@ -31,6 +31,9 @@ const icons = {
const Alert = ({ variant, title, body, isToast, hasIcon = true, onClick }) => {
const theme = useTheme();
/* TODO
This needs fixing. text bg and border not necessarilly exist. Probably text becomes contrastText. border becomes contrastText. bg becomes dark.
Check possible variants, see implementation in light and dark theme, and adjust */
const { text, bg, border } = theme.palette[variant];
const icon = icons[variant];
@@ -5,6 +5,7 @@ import { useEffect, useState } from "react";
import "./index.css";
import { useSelector } from "react-redux";
/* TODO add prop validation and jsdocs */
const BarChart = ({ checks = [] }) => {
const theme = useTheme();
const [animate, setAnimate] = useState(false);
@@ -65,7 +66,9 @@ const BarChart = ({ checks = [] }) => {
width={theme.spacing(4)}
height={theme.spacing(4)}
backgroundColor={
check.status ? theme.palette.success.main : theme.palette.error.text
check.status
? theme.palette.success.main
: theme.palette.error.contrastText
}
sx={{ borderRadius: "50%" }}
/>
@@ -111,7 +114,7 @@ const BarChart = ({ checks = [] }) => {
],
sx: {
"& .MuiTooltip-tooltip": {
backgroundColor: theme.palette.background.main,
backgroundColor: theme.palette.secondary.main,
border: 1,
borderColor: theme.palette.border.dark,
borderRadius: theme.shape.borderRadius,
@@ -138,7 +141,9 @@ const BarChart = ({ checks = [] }) => {
width="9px"
height="100%"
backgroundColor={
check.status ? theme.palette.success.bg : theme.palette.error.bg
check.status
? theme.palette.success.contrastText
: theme.palette.error.dark
}
sx={{
borderRadius: theme.spacing(1.5),
@@ -153,7 +158,9 @@ const BarChart = ({ checks = [] }) => {
width="100%"
height={`${animate ? check.responseTime : 0}%`}
backgroundColor={
check.status ? theme.palette.success.main : theme.palette.error.text
check.status
? theme.palette.success.main
: theme.palette.error.contrastText
}
sx={{
borderRadius: theme.spacing(1.5),
+1 -1
View File
@@ -24,7 +24,7 @@ const Check = ({ text, noHighlightText, variant = "info", outlined = false }) =>
const theme = useTheme();
const colors = {
success: theme.palette.success.main,
error: theme.palette.error.text,
error: theme.palette.error.contrastText,
info: theme.palette.info.border,
};
@@ -1,8 +1,8 @@
import React, { useEffect } from "react";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { logger } from "../Utils/Logger";
import { networkService } from "../main";
import { logger } from "../../Utils/Logger";
import { networkService } from "../../main";
const withAdminCheck = (WrappedComponent) => {
const WithAdminCheck = (props) => {
File renamed without changes.
+29
View File
@@ -0,0 +1,29 @@
import { Typography } from "@mui/material";
import PropTypes from "prop-types";
/**
* A heading component that renders text with a specific heading level.
*
* @param {Object} props - The properties passed to the Heading component.
* @param {('h1'|'h2'|'h3')} props.component - The heading level for the component.
* @param {string} props.children - The content to display inside the heading.
* @returns {JSX.Element} The Typography component with specified heading properties.
*/
function Heading({ component, children }) {
return (
<Typography
component={component}
variant="h2"
fontWeight={600}
>
{children}
</Typography>
);
}
Heading.propTypes = {
component: PropTypes.oneOf(["h1", "h2", "h3"]).isRequired,
children: PropTypes.string.isRequired,
};
export { Heading };
+3 -3
View File
@@ -67,7 +67,7 @@ const Field = forwardRef(
borderColor: theme.palette.border.dark,
},
"&:has(.input-error) .MuiOutlinedInput-root fieldset": {
borderColor: theme.palette.error.text,
borderColor: theme.palette.error.contrastText,
},
display: hidden ? "none" : "",
}}
@@ -83,7 +83,7 @@ const Field = forwardRef(
<Typography
component="span"
ml={theme.spacing(1)}
color={theme.palette.error.text}
color={theme.palette.error.contrastText}
>
*
</Typography>
@@ -189,7 +189,7 @@ const Field = forwardRef(
<Typography
component="span"
className="input-error"
color={theme.palette.error.text}
color={theme.palette.error.contrastText}
mt={theme.spacing(2)}
sx={{
opacity: 0.8,
+1 -1
View File
@@ -41,7 +41,7 @@ const Radio = (props) => {
color: "transparent",
width: 16,
height: 16,
boxShadow: "inset 0 0 0 1px #656a74",
boxShadow: `inset 0 0 0 1px ${theme.palette.secondary.main}`,
mt: theme.spacing(0.5),
}}
/>
@@ -99,7 +99,7 @@ const Search = ({
<Typography
component="span"
className="input-error"
color={theme.palette.error.text}
color={theme.palette.error.contrastText}
mt={theme.spacing(2)}
sx={{
opacity: 0.8,
-1
View File
@@ -1,5 +1,4 @@
.label {
border: 1px solid #000;
display: inline-flex;
justify-content: center;
align-items: center;
+7 -7
View File
@@ -31,7 +31,7 @@ const BaseLabel = ({ label, styles, children }) => {
className="label"
sx={{
borderRadius: borderRadius,
borderColor: theme.palette.text.tertiary,
border: `1px solid ${theme.palette.text.tertiary}`,
color: theme.palette.text.tertiary,
padding: padding,
...styles,
@@ -127,22 +127,22 @@ const StatusLabel = ({ status, text, customStyles }) => {
const colors = {
up: {
dotColor: theme.palette.success.main,
bgColor: theme.palette.success.bg,
borderColor: theme.palette.success.light,
bgColor: theme.palette.success./* dark */ contrastText,
borderColor: theme.palette.success.main /* light */,
},
down: {
dotColor: theme.palette.error.text,
bgColor: theme.palette.error.bg,
dotColor: theme.palette.error.contrastText,
bgColor: theme.palette.error.dark,
borderColor: theme.palette.error.light,
},
paused: {
dotColor: theme.palette.warning.main,
bgColor: theme.palette.warning.bg,
bgColor: theme.palette.warning.dark,
borderColor: theme.palette.warning.light,
},
pending: {
dotColor: theme.palette.warning.main,
bgColor: theme.palette.warning.bg,
bgColor: theme.palette.warning.dark,
borderColor: theme.palette.warning.light,
},
"cannot resolve": {
@@ -1,4 +1,4 @@
import Sidebar from "../../Components/Sidebar";
import Sidebar from "../../Sidebar";
import { Outlet } from "react-router";
import { Stack } from "@mui/material";
+3 -3
View File
@@ -34,11 +34,11 @@ const ProgressUpload = ({ icon, label, size, progress = 0, onClick, error }) =>
backgroundColor: theme.palette.background.fill,
"&:has(.input-error)": {
borderColor: theme.palette.error.main,
backgroundColor: theme.palette.error.bg,
backgroundColor: theme.palette.error.dark,
py: theme.spacing(4),
px: theme.spacing(8),
"& > .MuiStack-root > svg": {
fill: theme.palette.error.text,
fill: theme.palette.error.contrastText,
width: "20px",
height: "20px",
},
@@ -85,7 +85,7 @@ const ProgressUpload = ({ icon, label, size, progress = 0, onClick, error }) =>
<Typography
component="p"
className="input-error"
color={theme.palette.error.text}
color={theme.palette.error.contrastText}
>
{error}
</Typography>
+13 -21
View File
@@ -19,7 +19,7 @@ import { useLocation, useNavigate } from "react-router";
import { useTheme } from "@emotion/react";
import { useDispatch, useSelector } from "react-redux";
import { clearAuthState } from "../../Features/Auth/authSlice";
import { setMode, toggleSidebar } from "../../Features/UI/uiSlice";
import { toggleSidebar } from "../../Features/UI/uiSlice";
import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice";
import Avatar from "../Avatar";
import LockSvg from "../../assets/icons/lock.svg?react";
@@ -29,7 +29,6 @@ import LogoutSvg from "../../assets/icons/logout.svg?react";
import Support from "../../assets/icons/support.svg?react";
import Dashboard from "../../assets/icons/dashboard.svg?react";
import Account from "../../assets/icons/user-edit.svg?react";
import StatusPages from "../../assets/icons/status-pages.svg?react";
import Maintenance from "../../assets/icons/maintenance.svg?react";
import Monitors from "../../assets/icons/monitors.svg?react";
import Incidents from "../../assets/icons/incidents.svg?react";
@@ -54,6 +53,7 @@ const menu = [
nested: [
{ name: "Monitors", path: "monitors", icon: <Monitors /> },
{ name: "Pagespeed", path: "pagespeed", icon: <PageSpeed /> },
{ name: "Infrastructure", path: "infrastructure", icon: <Integrations /> },
],
},
{ name: "Incidents", path: "incidents", icon: <Incidents /> },
@@ -113,7 +113,6 @@ function Sidebar() {
const [popup, setPopup] = useState();
const { user } = useSelector((state) => state.auth);
// Remove demo password if demo
const accountMenuItem = menu.find((item) => item.name === "Account");
if (user.role?.includes("demo") && accountMenuItem) {
accountMenuItem.nested = accountMenuItem.nested.filter((item) => {
@@ -148,7 +147,9 @@ function Sidebar() {
if (matchedKey) {
setOpen((prev) => ({ ...prev, [PATH_MAP[matchedKey]]: true }));
}
}, []);
}, [location]);
/* TODO refactor this, there are a some ternaries and comments in the return */
return (
<Stack
@@ -181,7 +182,13 @@ function Sidebar() {
direction="row"
alignItems="center"
gap={theme.spacing(4)}
onClick={() => window.open("https://github.com/bluewave-labs/bluewave-uptime", "_blank", "noreferrer")}
onClick={() =>
window.open(
"https://github.com/bluewave-labs/bluewave-uptime",
"_blank",
"noreferrer"
)
}
sx={{ cursor: "pointer" }}
>
<Stack
@@ -297,6 +304,7 @@ function Sidebar() {
</ListItemButton>
</Tooltip>
) : collapsed ? (
/* TODO Do we ever get here? */
<React.Fragment key={item.name}>
<Tooltip
placement="right"
@@ -616,22 +624,6 @@ function Sidebar() {
</MenuItem>
)}
{collapsed && <Divider />}
{/* <MenuItem
onClick={() => {
dispatch(setMode("light"));
closePopup();
}}
>
Light
</MenuItem>
<MenuItem
onClick={() => {
dispatch(setMode("dark"));
closePopup();
}}
>
Dark
</MenuItem> */}
<Divider />
<MenuItem
onClick={logout}
+29
View File
@@ -0,0 +1,29 @@
import { Box, Button } from "@mui/material";
import { Heading } from "../../Components/Heading";
/**
* This is the Infrastructure monitoring page. This is a work in progress
*
* @param - Define params.
* @returns {JSX.Element} The infrastructure monitoring page.
*/
function Infrastructure() {
return (
<Box component="main">
<Button
variant="contained"
color="primary"
onClick={() => {
// navigate("/monitors/create");
}}
sx={{ fontWeight: 500 }}
>
Create monitor
</Button>
<Heading component="h2">Infrastructure monitors</Heading>
</Box>
);
}
export { Infrastructure };
@@ -114,7 +114,7 @@ const ActionsMenu = ({ /* isAdmin, */ maintenanceWindow, updateCallback }) => {
"& ul": { p: theme.spacing(2.5) },
"& li": { m: 0 },
"& li:last-of-type": {
color: theme.palette.error.text,
color: theme.palette.error.contrastText,
},
},
},
@@ -333,7 +333,7 @@ const CreateMonitor = () => {
<Typography
component="p"
className="input-error"
color={theme.palette.error.text}
color={theme.palette.error.contrastText}
>
{errors["type"]}
</Typography>
@@ -61,7 +61,7 @@ const UpBarChart = memo(({ data, type, onBarHover }) => {
? { main: theme.palette.success.main, light: theme.palette.success.light }
: uptime > 50
? { main: theme.palette.warning.main, light: theme.palette.warning.light }
: { main: theme.palette.error.text, light: theme.palette.error.light };
: { main: theme.palette.error.contrastText, light: theme.palette.error.light };
};
// TODO - REMOVE THIS LATER
@@ -201,10 +201,10 @@ const DownBarChart = memo(({ data, type, onBarHover }) => {
key={`cell-${entry.time}`}
fill={
hoveredBarIndex === index
? theme.palette.error.text
? theme.palette.error.contrastText
: chartHovered
? theme.palette.error.light
: theme.palette.error.text
: theme.palette.error.contrastText
}
onMouseEnter={() => {
setHoveredBarIndex(index);
@@ -246,24 +246,24 @@ const ResponseGaugeChart = ({ data }) => {
? {
category: "Excellent",
main: theme.palette.success.main,
bg: theme.palette.success.bg,
bg: theme.palette.success.dark,
}
: responseTime <= 500
? {
category: "Fair",
main: theme.palette.success.main,
bg: theme.palette.success.bg,
bg: theme.palette.success.dark,
}
: responseTime <= 600
? {
category: "Acceptable",
main: theme.palette.warning.main,
bg: theme.palette.warning.bg,
bg: theme.palette.warning.dark,
}
: {
category: "Poor",
main: theme.palette.error.text,
bg: theme.palette.error.bg,
main: theme.palette.error.contrastText,
bg: theme.palette.error.dark,
};
return (
@@ -1,10 +1,11 @@
import { useTheme } from "@emotion/react";
import { Box, Stack, Typography } from "@mui/material";
import { Box, Stack } from "@mui/material";
import Search from "../../../../Components/Inputs/Search";
import MemoizedMonitorTable from "../MonitorTable";
import { useState } from "react";
import useDebounce from "../../../../Utils/debounce";
import PropTypes from "prop-types";
import { Heading } from "../../../../Components/Heading";
const CurrentMonitoring = ({ totalMonitors, monitors, isAdmin }) => {
const theme = useTheme();
@@ -18,26 +19,15 @@ const CurrentMonitoring = ({ totalMonitors, monitors, isAdmin }) => {
return (
<Box
flex={1}
px={theme.spacing(10)}
py={theme.spacing(8)}
border={1}
borderColor={theme.palette.border.light}
borderRadius={theme.shape.borderRadius}
backgroundColor={theme.palette.background.main}
>
<Stack
direction="row"
alignItems="center"
mb={theme.spacing(8)}
>
<Typography
component="h2"
variant="h2"
fontWeight={500}
letterSpacing={-0.2}
>
Actively monitoring
</Typography>
<Heading component="h2">Actively monitoring</Heading>
<Box
className="current-monitors-counter"
color={theme.palette.text.primary}
+1 -1
View File
@@ -25,7 +25,7 @@ const StatusBox = ({ title, value }) => {
</Box>
);
} else if (title === "down") {
color = theme.palette.error.text;
color = theme.palette.error.main;
icon = (
<Box sx={{ ...sharedStyles, transform: "rotate(180deg)", top: 5 }}>
<Arrow />
@@ -109,7 +109,7 @@ const ActionsMenu = ({ monitor, isAdmin, updateCallback }) => {
"& ul": { p: theme.spacing(2.5) },
"& li": { m: 0 },
"& li:last-of-type": {
color: theme.palette.error.text,
color: theme.palette.error.contrastText,
},
},
},
+1
View File
@@ -1,3 +1,4 @@
/* TODO take these from here and declare using emotion. Plus, the values should live in the theme */
.monitors .MuiStack-root > button:not(.MuiIconButton-root) {
font-size: var(--env-var-font-size-medium);
height: var(--env-var-height-2);
+10 -10
View File
@@ -28,20 +28,20 @@ const useUtils = () => {
};
const statusStyles = {
up: {
backgroundColor: theme.palette.success.bg,
background: `linear-gradient(340deg, ${theme.palette.success.light} -60%, ${theme.palette.success.bg} 35%)`,
backgroundColor: theme.palette.success.dark,
background: `linear-gradient(340deg, ${theme.palette.success.light} -60%, ${theme.palette.success.dark} 35%)`,
borderColor: theme.palette.success.light,
"& h2": { color: theme.palette.success.main },
},
down: {
backgroundColor: theme.palette.error.bg,
background: `linear-gradient(340deg, ${theme.palette.error.light} -60%, ${theme.palette.error.bg} 35%)`,
backgroundColor: theme.palette.error.dark,
background: `linear-gradient(340deg, ${theme.palette.error.light} -60%, ${theme.palette.error.dark} 35%)`,
borderColor: theme.palette.error.light,
"& h2": { color: theme.palette.error.main },
},
paused: {
backgroundColor: theme.palette.warning.bg,
background: `linear-gradient(340deg, ${theme.palette.warning.light} -60%, ${theme.palette.warning.bg} 35%)`,
backgroundColor: theme.palette.warning.dark,
background: `linear-gradient(340deg, ${theme.palette.warning.light} -60%, ${theme.palette.warning.dark} 35%)`,
borderColor: theme.palette.warning.light,
"& h2": { color: theme.palette.warning.main },
},
@@ -54,22 +54,22 @@ const useUtils = () => {
};
const pagespeedStyles = {
up: {
bg: theme.palette.success.bg,
bg: theme.palette.success.dark,
light: theme.palette.success.light,
stroke: theme.palette.success.main,
},
down: {
bg: theme.palette.error.bg,
bg: theme.palette.error.dark,
light: theme.palette.error.light,
stroke: theme.palette.error.main,
},
paused: {
bg: theme.palette.warning.bg,
bg: theme.palette.warning.dark,
light: theme.palette.warning.light,
stroke: theme.palette.warning.main,
},
pending: {
bg: theme.palette.warning.bg,
bg: theme.palette.warning.dark,
light: theme.palette.warning.light,
stroke: theme.palette.warning.main,
},
@@ -1,11 +1,14 @@
import { useState } from "react";
import { Box, Button, ButtonGroup, Stack, Typography } from "@mui/material";
import LoadingButton from '@mui/lab/LoadingButton';
import LoadingButton from "@mui/lab/LoadingButton";
import { useSelector, useDispatch } from "react-redux";
import { monitorValidation } from "../../../Validation/validation";
import { useNavigate } from "react-router-dom";
import { useTheme } from "@emotion/react";
import { createPageSpeed, checkEndpointResolution } from "../../../Features/PageSpeedMonitor/pageSpeedMonitorSlice";
import {
createPageSpeed,
checkEndpointResolution,
} from "../../../Features/PageSpeedMonitor/pageSpeedMonitorSlice";
import { createToast } from "../../../Utils/toastUtils";
import { logger } from "../../../Utils/Logger";
import { ConfigBox } from "../../Monitors/styled";
@@ -17,12 +20,12 @@ import Breadcrumbs from "../../../Components/Breadcrumbs";
import "./index.css";
const CreatePageSpeed = () => {
const MS_PER_MINUTE = 60000;
const { user, authToken } = useSelector((state) => state.auth);
const { isLoading } = useSelector((state) => state.pageSpeedMonitors);
const dispatch = useDispatch();
const navigate = useNavigate();
const theme = useTheme();
const MS_PER_MINUTE = 60000;
const { user, authToken } = useSelector((state) => state.auth);
const { isLoading } = useSelector((state) => state.pageSpeedMonitors);
const dispatch = useDispatch();
const navigate = useNavigate();
const theme = useTheme();
const idMap = {
"monitor-url": "url",
@@ -105,252 +108,260 @@ const CreatePageSpeed = () => {
abortEarly: false,
});
if (error) {
const newErrors = {};
error.details.forEach((err) => {
newErrors[err.path[0]] = err.message;
});
setErrors(newErrors);
createToast({ body: "Error validation data." });
} else {
const checkEndpointAction = await dispatch(
checkEndpointResolution({ authToken, monitorURL: form.url })
)
if (checkEndpointAction.meta.requestStatus === "rejected") {
createToast({ body: "The endpoint you entered doesn't resolve. Check the URL again." });
setErrors({ url: "The entered URL is not reachable." });
return;
}
if (error) {
const newErrors = {};
error.details.forEach((err) => {
newErrors[err.path[0]] = err.message;
});
setErrors(newErrors);
createToast({ body: "Error validation data." });
} else {
const checkEndpointAction = await dispatch(
checkEndpointResolution({ authToken, monitorURL: form.url })
);
if (checkEndpointAction.meta.requestStatus === "rejected") {
createToast({
body: "The endpoint you entered doesn't resolve. Check the URL again.",
});
setErrors({ url: "The entered URL is not reachable." });
return;
}
form = {
...form,
description: form.name,
teamId: user.teamId,
userId: user._id,
notifications: monitor.notifications,
};
const action = await dispatch(
createPageSpeed({ authToken, monitor: form })
);
if (action.meta.requestStatus === "fulfilled") {
createToast({ body: "Monitor created successfully!" });
navigate("/pagespeed");
} else {
createToast({ body: "Failed to create monitor." });
}
}
};
form = {
...form,
description: form.name,
teamId: user.teamId,
userId: user._id,
notifications: monitor.notifications,
};
const action = await dispatch(createPageSpeed({ authToken, monitor: form }));
if (action.meta.requestStatus === "fulfilled") {
createToast({ body: "Monitor created successfully!" });
navigate("/pagespeed");
} else {
createToast({ body: "Failed to create monitor." });
}
}
};
//select values
const frequencies = [
{ _id: 3, name: "3 minutes" },
{ _id: 5, name: "5 minutes" },
{ _id: 10, name: "10 minutes" },
{ _id: 20, name: "20 minutes" },
{ _id: 60, name: "1 hour" },
{ _id: 1440, name: "1 day" },
{ _id: 10080, name: "1 week" },
];
return (
<Box
className="create-monitor"
sx={{
"& h1": {
color: theme.palette.text.primary,
},
}}
>
<Breadcrumbs
list={[
{ name: "pagespeed", path: "/pagespeed" },
{ name: "create", path: `/pagespeed/create` },
]}
/>
<Stack
component="form"
className="create-monitor-form"
onSubmit={handleCreateMonitor}
noValidate
spellCheck="false"
gap={theme.spacing(12)}
mt={theme.spacing(6)}
>
<Typography component="h1" variant="h1">
<Typography component="span" fontSize="inherit">
Create your{" "}
</Typography>
<Typography
component="span"
fontSize="inherit"
fontWeight="inherit"
color={theme.palette.text.secondary}
>
pagespeed monitor
</Typography>
</Typography>
<ConfigBox>
<Box>
<Typography component="h2">General settings</Typography>
<Typography component="p">
Here you can select the URL of the host, together with the type of
monitor.
</Typography>
</Box>
<Stack gap={theme.spacing(15)}>
<Field
type={"url"}
id="monitor-url"
label="URL to monitor"
https={https}
placeholder="google.com"
value={monitor.url}
onChange={handleChange}
error={errors["url"]}
/>
<Field
type="text"
id="monitor-name"
label="Display name"
isOptional={true}
placeholder="Google"
value={monitor.name}
onChange={handleChange}
error={errors["name"]}
/>
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">Checks to perform</Typography>
<Typography component="p">
You can always add or remove checks after adding your site.
</Typography>
</Box>
<Stack gap={theme.spacing(12)}>
<Stack gap={theme.spacing(6)}>
<Radio
id="monitor-checks-http"
title="Website monitoring"
desc="Use HTTP(s) to monitor your website or API endpoint."
size="small"
value="http"
checked={monitor.type === "pagespeed"}
onChange={(event) => handleChange(event)}
/>
<ButtonGroup sx={{ ml: "32px" }}>
<Button
variant="group"
filled={https.toString()}
onClick={() => setHttps(true)}
>
HTTPS
</Button>
<Button
variant="group"
filled={(!https).toString()}
onClick={() => setHttps(false)}
>
HTTP
</Button>
</ButtonGroup>
</Stack>
{errors["type"] ? (
<Box className="error-container">
<Typography
component="p"
className="input-error"
color={theme.palette.error.text}
>
{errors["type"]}
</Typography>
</Box>
) : (
""
)}
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">Incident notifications</Typography>
<Typography component="p">
When there is an incident, notify users.
</Typography>
</Box>
<Stack gap={theme.spacing(6)}>
<Typography component="p">When there is a new incident,</Typography>
<Checkbox
id="notify-sms"
label="Notify via SMS (coming soon)"
isChecked={false}
value=""
onChange={() => logger.warn("disabled")}
isDisabled={true}
/>
<Checkbox
id="notify-email-default"
label={`Notify via email (to ${user.email})`}
isChecked={monitor.notifications.some(
(notification) => notification.type === "email"
)}
value={user?.email}
onChange={(event) => handleChange(event)}
/>
<Checkbox
id="notify-email"
label="Also notify via email to multiple addresses (coming soon)"
isChecked={false}
value=""
onChange={() => logger.warn("disabled")}
isDisabled={true}
/>
{monitor.notifications.some(
(notification) => notification.type === "emails"
) ? (
<Box mx={theme.spacing(16)}>
<Field
id="notify-email-list"
type="text"
placeholder="name@gmail.com"
value=""
onChange={() => logger.warn("disabled")}
/>
<Typography mt={theme.spacing(4)}>
You can separate multiple emails with a comma
</Typography>
</Box>
) : (
""
)}
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">Advanced settings</Typography>
</Box>
<Stack gap={theme.spacing(12)}>
<Select
id="monitor-interval"
label="Check frequency"
value={monitor.interval || 3}
onChange={(event) => handleChange(event, "interval")}
items={frequencies}
/>
</Stack>
</ConfigBox>
<Stack direction="row" justifyContent="flex-end">
<LoadingButton
variant="contained"
color="primary"
onClick={handleCreateMonitor}
disabled={Object.keys(errors).length !== 0 && true}
loading={isLoading}
>
Create monitor
</LoadingButton>
</Stack>
</Stack>
</Box>
);
//select values
const frequencies = [
{ _id: 3, name: "3 minutes" },
{ _id: 5, name: "5 minutes" },
{ _id: 10, name: "10 minutes" },
{ _id: 20, name: "20 minutes" },
{ _id: 60, name: "1 hour" },
{ _id: 1440, name: "1 day" },
{ _id: 10080, name: "1 week" },
];
return (
<Box
className="create-monitor"
sx={{
"& h1": {
color: theme.palette.text.primary,
},
}}
>
<Breadcrumbs
list={[
{ name: "pagespeed", path: "/pagespeed" },
{ name: "create", path: `/pagespeed/create` },
]}
/>
<Stack
component="form"
className="create-monitor-form"
onSubmit={handleCreateMonitor}
noValidate
spellCheck="false"
gap={theme.spacing(12)}
mt={theme.spacing(6)}
>
<Typography
component="h1"
variant="h1"
>
<Typography
component="span"
fontSize="inherit"
>
Create your{" "}
</Typography>
<Typography
component="span"
fontSize="inherit"
fontWeight="inherit"
color={theme.palette.text.secondary}
>
pagespeed monitor
</Typography>
</Typography>
<ConfigBox>
<Box>
<Typography component="h2">General settings</Typography>
<Typography component="p">
Here you can select the URL of the host, together with the type of monitor.
</Typography>
</Box>
<Stack gap={theme.spacing(15)}>
<Field
type={"url"}
id="monitor-url"
label="URL to monitor"
https={https}
placeholder="google.com"
value={monitor.url}
onChange={handleChange}
error={errors["url"]}
/>
<Field
type="text"
id="monitor-name"
label="Display name"
isOptional={true}
placeholder="Google"
value={monitor.name}
onChange={handleChange}
error={errors["name"]}
/>
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">Checks to perform</Typography>
<Typography component="p">
You can always add or remove checks after adding your site.
</Typography>
</Box>
<Stack gap={theme.spacing(12)}>
<Stack gap={theme.spacing(6)}>
<Radio
id="monitor-checks-http"
title="Website monitoring"
desc="Use HTTP(s) to monitor your website or API endpoint."
size="small"
value="http"
checked={monitor.type === "pagespeed"}
onChange={(event) => handleChange(event)}
/>
<ButtonGroup sx={{ ml: "32px" }}>
<Button
variant="group"
filled={https.toString()}
onClick={() => setHttps(true)}
>
HTTPS
</Button>
<Button
variant="group"
filled={(!https).toString()}
onClick={() => setHttps(false)}
>
HTTP
</Button>
</ButtonGroup>
</Stack>
{errors["type"] ? (
<Box className="error-container">
<Typography
component="p"
className="input-error"
color={theme.palette.error.contrastText}
>
{errors["type"]}
</Typography>
</Box>
) : (
""
)}
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">Incident notifications</Typography>
<Typography component="p">
When there is an incident, notify users.
</Typography>
</Box>
<Stack gap={theme.spacing(6)}>
<Typography component="p">When there is a new incident,</Typography>
<Checkbox
id="notify-sms"
label="Notify via SMS (coming soon)"
isChecked={false}
value=""
onChange={() => logger.warn("disabled")}
isDisabled={true}
/>
<Checkbox
id="notify-email-default"
label={`Notify via email (to ${user.email})`}
isChecked={monitor.notifications.some(
(notification) => notification.type === "email"
)}
value={user?.email}
onChange={(event) => handleChange(event)}
/>
<Checkbox
id="notify-email"
label="Also notify via email to multiple addresses (coming soon)"
isChecked={false}
value=""
onChange={() => logger.warn("disabled")}
isDisabled={true}
/>
{monitor.notifications.some(
(notification) => notification.type === "emails"
) ? (
<Box mx={theme.spacing(16)}>
<Field
id="notify-email-list"
type="text"
placeholder="name@gmail.com"
value=""
onChange={() => logger.warn("disabled")}
/>
<Typography mt={theme.spacing(4)}>
You can separate multiple emails with a comma
</Typography>
</Box>
) : (
""
)}
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography component="h2">Advanced settings</Typography>
</Box>
<Stack gap={theme.spacing(12)}>
<Select
id="monitor-interval"
label="Check frequency"
value={monitor.interval || 3}
onChange={(event) => handleChange(event, "interval")}
items={frequencies}
/>
</Stack>
</ConfigBox>
<Stack
direction="row"
justifyContent="flex-end"
>
<LoadingButton
variant="contained"
color="primary"
onClick={handleCreateMonitor}
disabled={Object.keys(errors).length !== 0 && true}
loading={isLoading}
>
Create monitor
</LoadingButton>
</Stack>
</Stack>
</Box>
);
};
export default CreatePageSpeed;
@@ -127,22 +127,22 @@ const PieChart = ({ audits }) => {
return {
stroke: theme.palette.success.main,
strokeBg: theme.palette.success.light,
text: theme.palette.success.text,
bg: theme.palette.success.bg,
text: theme.palette.success.contrastText,
bg: theme.palette.success.dark,
};
else if (value >= 50 && value < 90)
return {
stroke: theme.palette.warning.main,
strokeBg: theme.palette.warning.light,
text: theme.palette.warning.text,
bg: theme.palette.warning.bg,
text: theme.palette.warning.contrastText,
bg: theme.palette.warning.dark,
};
else if (value >= 0 && value < 50)
return {
stroke: theme.palette.error.text,
stroke: theme.palette.error.contrastText,
strokeBg: theme.palette.error.light,
text: theme.palette.error.text,
bg: theme.palette.error.bg,
text: theme.palette.error.contrastText,
bg: theme.palette.error.dark,
};
return {
stroke: theme.palette.unresolved.main,
+1 -1
View File
@@ -362,7 +362,7 @@ const PageSpeedDetails = ({ isAdmin }) => {
: score >= 50
? theme.palette.warning.main
: score >= 0
? theme.palette.error.text
? theme.palette.error.contrastText
: theme.palette.unresolved.main;
// Find the position where the number ends and the unit begins
-1
View File
@@ -1 +0,0 @@
#Pages Folder
+244
View File
@@ -0,0 +1,244 @@
const typographyBase = 14;
/* TODO
Check for px in codebase. All font sizes should be in REM and should live here.
Rest should be checked on each case
*/
const typographyLevels = {
base: typographyBase,
xs: `${(typographyBase - 4) / 16}rem`,
s: `${(typographyBase - 2) / 16}rem`,
m: `${typographyBase / 16}rem`,
l: `${(typographyBase + 2) / 16}rem`,
xl: `${(typographyBase + 10) / 16}rem`,
};
/* TODO Review color palette and semantic colors */
const paletteColors = {
white: "#FFFFFF",
gray50: "#FEFEFE",
gray60: "#FEFDFE",
gray70: "#FDFDFD",
gray80: "#FDFCFD",
gray90: "#FCFCFD",
gray100: "#F4F4F4",
gray200: "#E3E3E3",
gray300: "#A2A3A3",
gray500: "#838C99",
gray600: "#454546",
gray750: "#36363E",
gray800: "#2D2D33",
gray850: "#131315",
gray860: "#111113",
gray870: "#0F0F11",
gray880: "#0C0C0E",
gray890: "#09090B",
blueGray20: "#E8F0FE",
blueGray150: "#667085",
blueGray200: "#475467",
blueGray400: "#344054",
blueGray900: "#1c2130",
blueBlueWave: "#1570EF",
blue700: "#4E5BA6",
purple300: "#664EFF",
purple400: "#3A1BFF",
green50: "#D4F4E1",
green150: "#45BB7A",
green400: "#079455",
green800: "#1C4428",
green900: "#12261E",
red50: "#F9ECED",
red100: "#FBD1D1",
red200: "#F04438",
red300: "#D32F2F",
red700: "#542426",
red800: "#301A1F",
orange50: "#FEF8EA",
orange100: "#FFECBC",
orange300: "#FDB022",
orange400: "#FF9F00",
orange500: "#E88C30",
orange600: "#DC6803",
orange800: "#624711",
};
const semanticColors = {
primary: {
main: {
light: paletteColors.blueBlueWave,
dark: paletteColors.blueBlueWave,
},
// TODO we dont have primary light, dark or contrast text
},
secondary: {
main: {
light: paletteColors.gray100,
dark: paletteColors.gray800,
},
contrastText: {
light: paletteColors.blueGray200,
dark: paletteColors.blueGray200,
},
light: {
light: paletteColors.gray200,
dark: paletteColors.gray200,
},
dark: {
light: paletteColors.gray200,
dark: paletteColors.gray200,
},
},
success: {
main: {
light: paletteColors.green400,
dark: paletteColors.green400,
},
contrastText: {
light: paletteColors.green50,
dark: paletteColors.green900,
},
light: {
light: paletteColors.green50,
dark: paletteColors.green150,
},
dark: {
light: paletteColors.green800,
dark: paletteColors.green900,
},
},
error: {
main: {
light: paletteColors.red300,
dark: paletteColors.red300,
},
contrastText: {
light: paletteColors.gray50,
dark: paletteColors.gray50,
},
light: {
light: paletteColors.red100,
dark: paletteColors.red700,
},
dark: {
light: paletteColors.red50,
dark: paletteColors.red800,
},
},
warning: {
main: {
light: paletteColors.orange300,
dark: paletteColors.orange400,
},
contrastText: {
light: paletteColors.orange600,
dark: paletteColors.orange500,
},
light: {
light: paletteColors.orange100,
dark: paletteColors.orange800,
},
dark: {
light: paletteColors.orange50,
dark: paletteColors.gray800,
},
},
/* From this part on, try to create semantic structure, not feature based structure */
gradient: {
color1: {
light: paletteColors.gray90,
dark: paletteColors.gray890,
},
color2: {
light: paletteColors.gray80,
dark: paletteColors.gray880,
},
color3: {
light: paletteColors.gray70,
dark: paletteColors.gray870,
},
color4: {
light: paletteColors.gray60,
dark: paletteColors.gray860,
},
color5: {
light: paletteColors.gray50,
dark: paletteColors.gray850,
},
},
background: {
main: {
light: paletteColors.white,
dark: paletteColors.gray850,
},
alt: {
light: paletteColors.gray90,
dark: paletteColors.gray890,
},
fill: {
light: paletteColors.gray100,
dark: paletteColors.gray800,
},
accent: {
light: paletteColors.gray100,
dark: paletteColors.gray850,
},
},
text: {
primary: {
light: paletteColors.blueGray900,
dark: paletteColors.gray100,
},
secondary: {
light: paletteColors.blueGray400,
dark: paletteColors.gray200,
},
tertiary: {
light: paletteColors.blueGray200,
dark: paletteColors.gray300,
},
accent: {
light: paletteColors.gray500,
dark: paletteColors.gray500,
},
},
border: {
light: {
light: paletteColors.gray200,
dark: paletteColors.gray800,
},
dark: {
light: paletteColors.gray200,
dark: paletteColors.gray750,
},
},
unresolved: {
main: {
light: paletteColors.blue700,
dark: paletteColors.purple300,
},
light: {
light: paletteColors.blueGray20,
dark: paletteColors.purple400,
},
bg: {
light: paletteColors.gray100,
dark: paletteColors.gray100,
},
},
other: {
icon: {
light: paletteColors.blueGray150,
},
line: {
light: paletteColors.gray200,
},
grid: {
light: paletteColors.gray300,
dark: paletteColors.gray600,
},
autofill: {
light: paletteColors.blueGray20,
},
},
};
export { typographyLevels, semanticColors as colors };
+100 -259
View File
@@ -1,267 +1,108 @@
import { createTheme } from "@mui/material";
import { baseTheme } from "./globalTheme";
import { colors } from "./constants";
const text = {
primary: "#fafafa",
secondary: "#e6e6e6",
tertiary: "#a1a1aa",
accent: "#8e8e8f",
disabled: "rgba(172, 172, 172, 0.3)",
};
const background = {
main: "#151518",
alt: "#09090b",
fill: "#2D2D33",
accent: "#18181a",
};
const border = { light: "#27272a", dark: "#36363e" };
const {
primary,
secondary,
success,
error,
warning,
gradient: {
color1: { dark: color1 },
color2: { dark: color2 },
color3: { dark: color3 },
color4: { dark: color4 },
color5: { dark: color5 },
},
background,
text,
border,
unresolved,
other,
} = colors;
const fontFamilyDefault =
'"Inter","system-ui", "Avenir", "Helvetica", "Arial", sans-serif';
const shadow =
"0px 4px 24px -4px rgba(255, 255, 255, 0.03), 0px 3px 3px -3px rgba(255, 255, 255, 0.01)";
const palette = {
primary: { main: primary.main.dark },
secondary: {
main: secondary.main.dark,
contrastText: secondary.contrastText.dark,
light: secondary.dark.dark,
dark: secondary.dark.dark,
},
success: {
main: success.main.dark,
contrastText: success.contrastText.dark,
light: success.light.dark,
dark: success.dark.dark,
},
error: {
main: error.main.dark,
contrastText: error.contrastText.dark,
light: error.light.dark,
dark: error.dark.dark,
},
warning: {
main: warning.main.dark,
light: warning.light.dark,
contrastText: warning.contrastText.dark,
dark: warning.dark.dark,
},
/* From this part on, try to create semantic structure, not feature based structure */
percentage: {
uptimePoor: error.main.dark,
uptimeFair: warning.contrastText.dark,
uptimeGood: warning.main.dark /* Change for a success color? ?*/,
uptimeExcellent: success.main.dark,
},
unresolved: {
main: unresolved.main.dark,
light: unresolved.light.dark,
bg: unresolved.bg.dark,
},
divider: border.light.dark,
other: {
icon: text.secondary.dark,
line: border.light.dark,
fill: background.accent.dark,
grid: other.grid.dark,
autofill: secondary.main.dark,
},
gradient: {
color1,
color2,
color3,
color4,
color5,
},
text: {
primary: text.primary.dark,
secondary: text.secondary.dark,
tertiary: text.tertiary.dark,
accent: text.accent.dark,
},
background: {
main: background.main.dark,
alt: background.alt.dark,
fill: background.fill.dark,
accent: background.accent.dark,
},
border: {
light: border.light.dark,
dark: border.dark.dark,
},
info: {
text: text.primary.dark,
main: text.secondary.dark,
bg: background.main.dark,
light: background.main.dark,
border: border.light.dark,
},
};
const darkTheme = createTheme({
typography: {
fontFamily: fontFamilyDefault,
fontSize: 13,
h1: { fontSize: 22, color: text.primary, fontWeight: 500 },
h2: { fontSize: 14.5, color: text.secondary, fontWeight: 400 },
body1: { fontSize: 13, color: text.tertiary, fontWeight: 400 },
body2: { fontSize: 12, color: text.tertiary, fontWeight: 400 },
},
chart: {
header: {
fontWeight: 400,
fill: text.tertiary,
fontSize: 11,
},
subheader: {
fontWeight: 400,
fill: text.tertiary,
fontSize: 9,
},
},
palette: {
mode: "dark",
primary: { main: "#1570ef" },
secondary: { main: "#2D2D33" },
text: text,
background: background,
border: border,
info: {
text: text.primary,
main: text.secondary,
bg: background.main,
light: background.main,
border: border.light,
},
success: {
text: "#079455",
main: "#45bb7a",
light: "#1c4428",
bg: "#12261e",
},
error: {
text: "#f04438",
main: "#d32f2f",
light: "#542426",
bg: "#301a1f",
dark: "#932020",
border: "#f04438",
},
warning: {
text: "#e88c30",
main: "#FF9F00",
light: "#624711",
bg: "#262115",
border: "#e88c30",
},
percentage: {
uptimePoor: "#d32f2f",
uptimeFair: "#e88c30",
uptimeGood: "#ffd600",
uptimeExcellent: "#079455",
},
unresolved: { main: "#664eff", light: "#3a1bff", bg: "#f2f4f7" },
divider: border.light,
other: {
icon: "#e6e6e6",
line: "#27272a",
fill: "#18181a",
grid: "#454546",
autofill: "#2d2d33",
},
},
spacing: 2,
components: {
MuiCssBaseline: {
styleOverrides: {
body: {
backgroundImage:
"radial-gradient(circle, #09090b, #0c0c0e, #0f0f11, #111113, #131315, #131315, #131315, #131315, #111113, #0f0f11, #0c0c0e, #09090b)",
lineHeight: "inherit",
paddingLeft: "calc(100vw - 100%)",
},
},
},
MuiButton: {
defaultProps: {
disableRipple: true,
},
styleOverrides: {
root: ({ theme }) => ({
variants: [
{
props: (props) => props.variant === "group",
style: {
color: theme.palette.secondary.contrastText,
backgroundColor: theme.palette.background.main,
border: 1,
borderStyle: "solid",
borderColor: theme.palette.border.light,
},
},
{
props: (props) => props.variant === "group" && props.filled === "true",
style: {
backgroundColor: theme.palette.secondary.main,
},
},
{
props: (props) =>
props.variant === "contained" && props.color === "secondary",
style: {
border: 1,
borderStyle: "solid",
borderColor: theme.palette.border.dark,
},
},
],
fontWeight: 400,
borderRadius: 4,
boxShadow: "none",
textTransform: "none",
"&:focus": {
outline: "none",
},
"&:hover": {
boxShadow: "none",
},
}),
},
},
MuiIconButton: {
styleOverrides: {
root: {
padding: 4,
transition: "none",
"&:hover": {
backgroundColor: border.light,
},
},
},
},
MuiPaper: {
styleOverrides: {
root: {
marginTop: 4,
padding: 0,
border: 1,
borderStyle: "solid",
borderColor: border.light,
borderRadius: 4,
boxShadow: shadow,
backgroundColor: background.main,
backgroundImage: "none",
},
},
},
MuiList: {
styleOverrides: {
root: {
padding: 0,
},
},
},
MuiListItemButton: {
styleOverrides: {
root: {
transition: "none",
},
},
},
MuiMenuItem: {
styleOverrides: {
root: {
borderRadius: 4,
backgroundColor: "inherit",
padding: "4px 6px",
color: text.secondary,
fontSize: 13,
margin: 2,
minWidth: 100,
"&:hover, &.Mui-selected, &.Mui-selected:hover, &.Mui-selected.Mui-focusVisible":
{
backgroundColor: background.fill,
},
},
},
},
MuiTableCell: {
styleOverrides: {
root: {
borderBottomColor: border.light,
},
},
},
MuiTableHead: {
styleOverrides: {
root: {
backgroundColor: background.accent,
},
},
},
MuiPagination: {
styleOverrides: {
root: {
backgroundColor: background.main,
border: 1,
borderStyle: "solid",
borderColor: border.light,
"& button": {
color: text.tertiary,
borderRadius: 4,
},
"& li:first-of-type button, & li:last-of-type button": {
border: 1,
borderStyle: "solid",
borderColor: border.light,
},
},
},
},
MuiPaginationItem: {
styleOverrides: {
root: {
"&:not(.MuiPaginationItem-ellipsis):hover, &.Mui-selected": {
backgroundColor: background.fill,
},
},
},
},
MuiSkeleton: {
styleOverrides: {
root: {
backgroundColor: "#151518",
},
},
},
},
shape: {
borderRadius: 2,
borderThick: 2,
boxShadow: shadow,
},
palette,
...baseTheme(palette),
});
export default darkTheme;
+220
View File
@@ -0,0 +1,220 @@
import { typographyLevels } from "./constants";
const fontFamilyPrimary = '"Inter" , sans-serif';
// const fontFamilySecondary = '"Avenir", sans-serif';
/* TODO take the color out from here */
const shadow =
"0px 4px 24px -4px rgba(16, 24, 40, 0.08), 0px 3px 3px -3px rgba(16, 24, 40, 0.03)";
const baseTheme = (palette) => ({
typography: {
fontFamily: fontFamilyPrimary,
fontSize: 14,
h1: {
fontSize: typographyLevels.xl,
color: palette.text.primary,
fontWeight: 500,
},
h2: {
fontSize: typographyLevels.l,
color: palette.text.secondary,
fontWeight: 400,
},
body1: {
fontSize: typographyLevels.m,
color: palette.text.tertiary,
fontWeight: 400,
},
body2: {
fontSize: typographyLevels.s,
color: palette.text.tertiary,
fontWeight: 400,
},
},
/* TODO take chart from here, it should live inside of the gauge, and get info from the theme */
chart: {
header: {
fontWeight: 400,
fill: palette.text.tertiary,
fontSize: typographyLevels.m,
},
subheader: {
fontWeight: 400,
fill: palette.text.tertiary,
fontSize: typographyLevels.xs,
},
},
spacing: 2,
/* TODO All these should live inside of a component*/
components: {
MuiButton: {
defaultProps: {
disableRipple: true,
},
styleOverrides: {
root: ({ theme }) => ({
variants: [
{
props: (props) => props.variant === "group",
style: {
color: theme.palette.secondary.contrastText,
backgroundColor: theme.palette.background.main,
border: 1,
borderStyle: "solid",
borderColor: theme.palette.border.light,
},
},
{
props: (props) => props.variant === "group" && props.filled === "true",
style: {
backgroundColor: theme.palette.secondary.main,
},
},
{
props: (props) =>
props.variant === "contained" && props.color === "secondary",
style: {
border: 1,
borderStyle: "solid",
borderColor: theme.palette.border.light,
},
},
{
props: (props) => props.variant === "contained" && props.disabled,
style: {
backgroundColor: `${theme.palette.secondary.main} !important`,
color: `${theme.palette.secondary.contrastText} !important`,
},
},
],
fontWeight: 400,
borderRadius: 4,
boxShadow: "none",
textTransform: "none",
"&:focus": {
outline: "none",
},
"&:hover": {
boxShadow: "none",
},
}),
},
},
MuiIconButton: {
styleOverrides: {
root: ({ theme }) => ({
padding: 4,
transition: "none",
"&:hover": {
backgroundColor: theme.palette.background.fill,
},
}),
},
},
MuiPaper: {
styleOverrides: {
root: ({ theme }) => {
return {
marginTop: 4,
padding: 0,
border: 1,
borderStyle: "solid",
borderColor: theme.palette.border.light,
borderRadius: 4,
boxShadow: shadow,
backgroundColor: theme.palette.background.main,
backgroundImage: "none",
};
},
},
},
MuiList: {
styleOverrides: {
root: {
padding: 0,
},
},
},
MuiListItemButton: {
styleOverrides: {
root: {
transition: "none",
},
},
},
MuiMenuItem: {
styleOverrides: {
root: ({ theme }) => ({
borderRadius: 4,
backgroundColor: "inherit",
padding: "4px 6px",
color: theme.palette.text.secondary,
fontSize: 13,
margin: 2,
minWidth: 100,
"&:hover, &.Mui-selected, &.Mui-selected:hover, &.Mui-selected.Mui-focusVisible":
{
backgroundColor: theme.palette.background.fill,
},
}),
},
},
MuiTableCell: {
styleOverrides: {
root: ({ theme }) => ({
borderBottomColor: theme.palette.border.light,
}),
},
},
MuiTableHead: {
styleOverrides: {
root: ({ theme }) => ({
backgroundColor: theme.palette.background.accent,
}),
},
},
MuiPagination: {
styleOverrides: {
root: ({ theme }) => ({
backgroundColor: theme.palette.background.main,
border: 1,
borderStyle: "solid",
borderColor: theme.palette.border.light,
"& button": {
color: theme.palette.text.tertiary,
borderRadius: 4,
},
"& li:first-of-type button, & li:last-of-type button": {
border: 1,
borderStyle: "solid",
borderColor: theme.palette.border.light,
},
}),
},
},
MuiPaginationItem: {
styleOverrides: {
root: ({ theme }) => ({
"&:not(.MuiPaginationItem-ellipsis):hover, &.Mui-selected": {
backgroundColor: theme.palette.background.fill,
},
}),
},
},
MuiSkeleton: {
styleOverrides: {
root: ({ theme }) => ({
backgroundColor: theme.palette.unresolved.bg,
}),
},
},
},
shape: {
borderRadius: 2,
borderThick: 2,
boxShadow: shadow,
},
});
export { baseTheme };
+106 -257
View File
@@ -1,265 +1,114 @@
import { createTheme } from "@mui/material";
import { baseTheme } from "./globalTheme";
import { colors } from "./constants";
const text = {
primary: "#1c2130",
secondary: "#344054",
tertiary: "#475467",
accent: "#838c99",
};
const background = {
main: "#FFFFFF",
alt: "#FCFCFD",
fill: "#F4F4F4",
accent: "#f9fafb",
/*
TODO
Next step: check if all keys here are being used in the codebase. e.g.: Search codebase for palette.primary; also check for destructuring palette ('= theme.palette')
*/
const {
primary,
secondary,
success,
error,
warning,
gradient: {
color1: { light: color1 },
color2: { light: color2 },
color3: { light: color3 },
color4: { light: color4 },
color5: { light: color5 },
},
background,
text,
border,
unresolved,
other,
} = colors;
const palette = {
primary: { main: primary.main.light },
secondary: {
main: secondary.main.light,
contrastText: secondary.contrastText.light,
light: secondary.dark.light,
dark: secondary.dark.light,
},
success: {
main: success.main.light,
contrastText: success.contrastText.light,
light: success.light.light,
dark: success.dark.light,
},
error: {
main: error.main.light,
contrastText: error.contrastText.light,
light: error.light.light,
dark: error.dark.light,
},
warning: {
main: warning.main.light,
light: warning.light.light,
contrastText: warning.contrastText.light,
dark: warning.dark.light,
},
/* From this part on, try to create semantic structure, not feature based structure */
percentage: {
uptimePoor: error.main.light,
uptimeFair: warning.contrastText.light,
uptimeGood: warning.main.light /* Change for a success color? */,
uptimeExcellent: success.main.light,
},
unresolved: {
main: unresolved.main.light,
light: unresolved.light.light,
bg: unresolved.bg.light,
},
divider: border.light.light,
other: {
icon: other.icon.light,
line: other.line.light,
fill: secondary.dark.light,
grid: other.grid.light,
autofill: other.autofill.light,
},
gradient: {
color1,
color2,
color3,
color4,
color5,
},
text: {
primary: text.primary.light,
secondary: text.secondary.light,
tertiary: text.tertiary.light,
accent: text.accent.light,
},
background: {
main: background.main.light,
alt: background.alt.light,
fill: background.fill.light,
accent: background.accent.light,
},
border: {
light: border.light.light,
dark: border.dark.light,
},
info: {
text: text.primary.light,
main: text.tertiary.light,
bg: background.main.light,
light: background.main.light,
border: border.dark.light,
},
};
const border = { light: "#eaecf0", dark: "#d0d5dd" };
const fontFamilyDefault =
'"Inter","system-ui", "Avenir", "Helvetica", "Arial", sans-serif';
const shadow =
"0px 4px 24px -4px rgba(16, 24, 40, 0.08), 0px 3px 3px -3px rgba(16, 24, 40, 0.03)";
/* TODO I figured out we could have just one theme by passing mode as parameter for theme function. Implement later */
const lightTheme = createTheme({
typography: {
fontFamily: fontFamilyDefault,
fontSize: 13,
h1: { fontSize: 22, color: text.primary, fontWeight: 500 },
h2: { fontSize: 14.5, color: text.secondary, fontWeight: 400 },
body1: { fontSize: 13, color: text.tertiary, fontWeight: 400 },
body2: { fontSize: 12, color: text.tertiary, fontWeight: 400 },
},
chart: {
header: {
fontWeight: 400,
fill: text.tertiary,
fontSize: 11,
},
subheader: {
fontWeight: 400,
fill: text.tertiary,
fontSize: 9,
},
},
palette: {
primary: { main: "#1570EF" },
secondary: { main: "#F4F4F4", dark: "#e3e3e3", contrastText: "#475467" },
text: text,
background: background,
border: border,
info: {
text: text.primary,
main: text.tertiary,
bg: background.main,
light: background.main,
border: border.dark,
},
success: {
text: "#079455",
main: "#17b26a",
light: "#d4f4e1",
bg: "#ecfdf3",
},
error: {
text: "#f04438",
main: "#d32f2f",
light: "#fbd1d1",
bg: "#f9eced",
border: "#f04438",
},
warning: {
text: "#DC6803",
main: "#fdb022",
light: "#ffecbc",
bg: "#fef8ea",
border: "#fec84b",
},
percentage: {
uptimePoor: "#d32f2f",
uptimeFair: "#ec8013",
uptimeGood: "#ffb800",
uptimeExcellent: "#079455",
},
unresolved: { main: "#4e5ba6", light: "#e2eaf7", bg: "#f2f4f7" },
divider: border.light,
other: {
icon: "#667085",
line: "#d6d9dd",
fill: "#e3e3e3",
grid: "#a2a3a3",
autofill: "#e8f0fe",
},
},
spacing: 2,
components: {
MuiCssBaseline: {
styleOverrides: {
body: {
backgroundImage:
"radial-gradient(circle, #fcfcfd, #fdfcfd, #fdfdfd, #fefdfe, #fefefe, #fefefe, #fefefe, #fefefe, #fefdfe, #fdfdfd, #fdfcfd, #fcfcfd)",
lineHeight: "inherit",
paddingLeft: "calc(100vw - 100%)",
},
},
},
MuiButton: {
defaultProps: {
disableRipple: true,
},
styleOverrides: {
root: ({ theme }) => ({
variants: [
{
props: (props) => props.variant === "group",
style: {
color: theme.palette.secondary.contrastText,
backgroundColor: theme.palette.background.main,
border: 1,
borderStyle: "solid",
borderColor: theme.palette.border.light,
},
},
{
props: (props) => props.variant === "group" && props.filled === "true",
style: {
backgroundColor: theme.palette.secondary.main,
},
},
{
props: (props) =>
props.variant === "contained" && props.color === "secondary",
style: {
border: 1,
borderStyle: "solid",
borderColor: theme.palette.border.light,
},
},
],
fontWeight: 400,
borderRadius: 4,
boxShadow: "none",
textTransform: "none",
"&:focus": {
outline: "none",
},
"&:hover": {
boxShadow: "none",
},
}),
},
},
MuiIconButton: {
styleOverrides: {
root: {
padding: 4,
transition: "none",
"&:hover": {
backgroundColor: background.fill,
},
},
},
},
MuiPaper: {
styleOverrides: {
root: {
marginTop: 4,
padding: 0,
border: 1,
borderStyle: "solid",
borderColor: border.light,
borderRadius: 4,
boxShadow: shadow,
backgroundColor: background.main,
backgroundImage: "none",
},
},
},
MuiList: {
styleOverrides: {
root: {
padding: 0,
},
},
},
MuiListItemButton: {
styleOverrides: {
root: {
transition: "none",
},
},
},
MuiMenuItem: {
styleOverrides: {
root: {
borderRadius: 4,
backgroundColor: "inherit",
padding: "4px 6px",
color: text.secondary,
fontSize: 13,
margin: 2,
minWidth: 100,
"&:hover, &.Mui-selected, &.Mui-selected:hover, &.Mui-selected.Mui-focusVisible":
{
backgroundColor: background.fill,
},
},
},
},
MuiTableCell: {
styleOverrides: {
root: {
borderBottomColor: border.light,
},
},
},
MuiTableHead: {
styleOverrides: {
root: {
backgroundColor: background.accent,
},
},
},
MuiPagination: {
styleOverrides: {
root: {
backgroundColor: background.main,
border: 1,
borderStyle: "solid",
borderColor: border.light,
"& button": {
color: text.tertiary,
borderRadius: 4,
},
"& li:first-of-type button, & li:last-of-type button": {
border: 1,
borderStyle: "solid",
borderColor: border.light,
},
},
},
},
MuiPaginationItem: {
styleOverrides: {
root: {
"&:not(.MuiPaginationItem-ellipsis):hover, &.Mui-selected": {
backgroundColor: background.fill,
},
},
},
},
MuiSkeleton: {
styleOverrides: {
root: {
backgroundColor: "#f2f4f7",
},
},
},
},
shape: {
borderRadius: 2,
borderThick: 2,
boxShadow: shadow,
},
palette,
...baseTheme(palette),
});
export default lightTheme;