Added barebones theme toggle

This commit is contained in:
Daniel Cojocea committed 2024-08-28 22:14:55 -04:00
1 parent 9a62a42a09
commit aca350495a
21 files changed
+286 -105

No files matched your search

+8 -2
View File
@@ -28,6 +28,9 @@ import CreatePageSpeed from "./Pages/PageSpeed/CreatePageSpeed";
import CreateNewMaintenanceWindow from "./Pages/Maintenance/CreateMaintenanceWindow";
import PageSpeedDetails from "./Pages/PageSpeed/Details";
import PageSpeedConfigure from "./Pages/PageSpeed/Configure";
import { ThemeProvider } from "@emotion/react";
import theme, { darkTheme } from "./Utils/Theme";
import { useSelector } from "react-redux";
function App() {
const AdminCheckedRegister = withAdminCheck(Register);
@@ -35,8 +38,11 @@ function App() {
const DetailsWithAdminProp = withAdminProp(Details);
const PageSpeedWithAdminProp = withAdminProp(PageSpeed);
const MaintenanceWithAdminProp = withAdminProp(Maintenance);
const { mode } = useSelector((state) => state.ui);
return (
<>
<ThemeProvider theme={mode === "light" ? theme : darkTheme}>
<Routes>
<Route exact path="/" element={<HomeLayout />}>
<Route
@@ -130,7 +136,7 @@ function App() {
/>
</Routes>
<ToastContainer />
</>
</ThemeProvider>
);
}
@@ -53,7 +53,6 @@
}
.MuiPaper-root + .MuiPagination-root {
margin-top: 24px;
border-radius: var(--env-var-radius-1);
padding: var(--env-var-spacing-1-plus) var(--env-var-spacing-2);
}
+5 -1
View File
@@ -203,6 +203,7 @@ const BasicTable = ({ data, paginated, reversed, table }) => {
<TableContainer
component={Paper}
sx={{
backgroundColor: theme.palette.background.main,
border: `solid 1px ${theme.palette.border.light}`,
borderRadius: theme.shape.borderRadius,
}}
@@ -219,6 +220,7 @@ const BasicTable = ({ data, paginated, reversed, table }) => {
key={col.id}
sx={{
color: theme.palette.text.secondary,
borderBottomColor: theme.palette.border.light,
}}
>
{col.name}
@@ -245,6 +247,7 @@ const BasicTable = ({ data, paginated, reversed, table }) => {
key={cell.id}
sx={{
color: theme.palette.text.secondary,
borderBottomColor: theme.palette.border.light,
}}
>
{cell.data}
@@ -299,6 +302,7 @@ const BasicTable = ({ data, paginated, reversed, table }) => {
borderColor: theme.palette.border.light,
borderRadius: theme.shape.borderRadius,
boxShadow: theme.shape.boxShadow,
backgroundColor: theme.palette.background.main,
"& li": {
p: theme.spacing(2),
color: theme.palette.text.tertiary,
@@ -329,7 +333,7 @@ const BasicTable = ({ data, paginated, reversed, table }) => {
sx={{
mt: theme.spacing(6),
color: theme.palette.text.secondary,
"& button, & .MuiSelect-select": {
"& button.MuiButtonBase-root, & .MuiSelect-select": {
border: 1,
borderColor: theme.palette.border.light,
borderRadius: theme.shape.borderRadius,
@@ -72,7 +72,7 @@ const Checkbox = ({
mr: theme.spacing(6),
},
"&:not(:has(.Mui-disabled)):hover": {
backgroundColor: theme.palette.unresolved.bg,
backgroundColor: theme.palette.background.accent,
},
"& span.MuiTypography-root": {
fontSize: 13,
+1 -1
View File
@@ -141,7 +141,7 @@ const Field = forwardRef(
height="100%"
sx={{
borderRight: `solid 1px ${theme.palette.border.dark}`,
backgroundColor: "#f9f9fa",
backgroundColor: theme.palette.background.accent,
pl: theme.spacing(6),
}}
>
+7 -3
View File
@@ -41,7 +41,7 @@ const Radio = (props) => {
color: "transparent",
width: 16,
height: 16,
boxShadow: "inset 0 0 0 1px rgb(16 22 26 / 40%)",
boxShadow: "inset 0 0 0 1px #656a74",
mt: theme.spacing(0.5),
}}
/>
@@ -50,7 +50,11 @@ const Radio = (props) => {
label={
<>
<Typography component="p">{props.title}</Typography>
<Typography component="h6" mt={theme.spacing(1)}>
<Typography
component="h6"
mt={theme.spacing(1)}
color={theme.palette.text.secondary}
>
{props.desc}
</Typography>
</>
@@ -62,7 +66,7 @@ const Radio = (props) => {
m: theme.spacing(-2.5),
borderRadius: theme.shape.borderRadius,
"&:hover": {
backgroundColor: theme.palette.unresolved.bg,
backgroundColor: theme.palette.background.accent,
},
"& .MuiButtonBase-root": {
p: 0,
@@ -85,6 +85,7 @@ const Select = ({
borderColor: theme.palette.border.light,
borderRadius: theme.shape.borderRadius,
boxShadow: theme.shape.boxShadow,
backgroundColor: theme.palette.background.main,
},
},
MenuListProps: {
@@ -114,6 +115,9 @@ const Select = ({
"&:not(.Mui-focused):hover fieldset": {
borderColor: theme.palette.border.dark,
},
"& svg path": {
fill: theme.palette.other.icon,
},
...sx,
}}
>
+95 -72
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 { toggleSidebar } from "../../Features/UI/uiSlice";
import { setMode, toggleSidebar } from "../../Features/UI/uiSlice";
import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice";
import Avatar from "../Avatar";
import LockSvg from "../../assets/icons/lock.svg?react";
@@ -40,6 +40,7 @@ import ArrowDown from "../../assets/icons/down-arrow.svg?react";
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 "./index.css";
@@ -309,6 +310,8 @@ function Sidebar() {
borderColor: theme.palette.border.light,
borderRadius: theme.shape.borderRadius,
boxShadow: theme.shape.boxShadow,
backgroundColor: theme.palette.background.main,
ml: theme.spacing(1),
},
},
}}
@@ -424,7 +427,7 @@ function Sidebar() {
left: "-7px",
height: "100%",
borderLeft: 1,
borderLeftColor: "#d6d9dd",
borderLeftColor: theme.palette.other.line,
},
"&:last-child::before": {
height: "50%",
@@ -437,7 +440,7 @@ function Sidebar() {
height: "3px",
width: "3px",
borderRadius: "50%",
backgroundColor: "#d6d9dd",
backgroundColor: theme.palette.other.line,
},
"&.selected-path::after": {
backgroundColor: theme.palette.text.tertiary,
@@ -560,71 +563,6 @@ function Sidebar() {
<Avatar small={true} />
</IconButton>
</Tooltip>
<Menu
className="sidebar-popup"
anchorEl={anchorEl}
open={Boolean(anchorEl) && popup === "logout"}
onClose={closePopup}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
slotProps={{
paper: {
sx: {
border: 1,
borderColor: theme.palette.border.light,
borderRadius: theme.shape.borderRadius,
boxShadow: theme.shape.boxShadow,
},
},
}}
MenuListProps={{
sx: {
p: 3,
minWidth: "100px",
"& li": {
color: theme.palette.text.secondary,
fontSize: 13,
px: theme.spacing(4),
py: theme.spacing(2.5),
borderRadius: theme.shape.borderRadius,
},
"& li:hover": {
backgroundColor: theme.palette.background.accent,
},
"& li:has(.MuiBox-root):hover": {
backgroundColor: theme.palette.background.main,
},
},
}}
sx={{ ml: theme.spacing(8) }}
>
<MenuItem sx={{ cursor: "default", minWidth: "150px" }}>
<Box>
<Typography component="span" fontWeight={500} fontSize={13}>
{authState.user?.firstName} {authState.user?.lastName}
</Typography>
<Typography
sx={{ textTransform: "capitalize", fontSize: 12 }}
>
{authState.user?.role}
</Typography>
</Box>
</MenuItem>
<Divider sx={{ borderColor: theme.palette.border.light }} />
<MenuItem
onClick={logout}
sx={{
gap: theme.spacing(4),
borderRadius: theme.shape.borderRadius,
pl: theme.spacing(4),
}}
>
<LogoutSvg />
Log out
</MenuItem>
</Menu>
</>
) : (
<>
@@ -637,16 +575,101 @@ function Sidebar() {
{authState.user?.role}
</Typography>
</Box>
<Tooltip title="Log Out" disableInteractive>
<Tooltip title="Controls" disableInteractive>
<IconButton
sx={{ ml: "auto", "&:focus": { outline: "none" } }}
onClick={logout}
sx={{
ml: "auto",
mr: "-8px",
"&:focus": { outline: "none" },
"& svg": {
width: "20px",
height: "20px",
},
"& svg path": {
stroke: theme.palette.other.icon,
},
}}
onClick={(event) => openPopup(event, "logout")}
>
<LogoutSvg style={{ width: "20px", height: "20px" }} />
<DotsVertical />
</IconButton>
</Tooltip>
</>
)}
<Menu
className="sidebar-popup"
anchorEl={anchorEl}
open={Boolean(anchorEl) && popup === "logout"}
onClose={closePopup}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
slotProps={{
paper: {
sx: {
border: 1,
borderColor: theme.palette.border.light,
borderRadius: theme.shape.borderRadius,
boxShadow: theme.shape.boxShadow,
backgroundColor: theme.palette.background.main,
marginTop: theme.spacing(-4),
marginLeft: collapsed ? theme.spacing(2) : 0,
},
},
}}
MenuListProps={{
sx: {
p: 3,
minWidth: "100px",
"& li": {
color: theme.palette.text.secondary,
fontSize: 13,
px: theme.spacing(4),
py: theme.spacing(2.5),
borderRadius: theme.shape.borderRadius,
},
"& li:hover": {
backgroundColor: theme.palette.background.accent,
},
"& li:has(.MuiBox-root):hover": {
backgroundColor: theme.palette.background.main,
},
},
}}
sx={{
ml: theme.spacing(8),
}}
>
{collapsed && (
<MenuItem sx={{ cursor: "default", minWidth: "150px" }}>
<Box mb={theme.spacing(2)}>
<Typography component="span" fontWeight={500} fontSize={13}>
{authState.user?.firstName} {authState.user?.lastName}
</Typography>
<Typography sx={{ textTransform: "capitalize", fontSize: 12 }}>
{authState.user?.role}
</Typography>
</Box>
</MenuItem>
)}
{collapsed && (
<Divider sx={{ borderColor: theme.palette.border.light }} />
)}
<MenuItem onClick={() => dispatch(setMode("light"))}>Light</MenuItem>
<MenuItem onClick={() => dispatch(setMode("dark"))}>Dark</MenuItem>
<MenuItem
onClick={logout}
sx={{
gap: theme.spacing(4),
borderRadius: theme.shape.borderRadius,
pl: theme.spacing(4),
}}
>
<LogoutSvg />
Log out
</MenuItem>
</Menu>
</Stack>
</Stack>
);
+5 -1
View File
@@ -12,6 +12,7 @@ const initialState = {
sidebar: {
collapsed: false,
},
mode: "light",
};
const uiSlice = createSlice({
@@ -24,8 +25,11 @@ const uiSlice = createSlice({
toggleSidebar: (state) => {
state.sidebar.collapsed = !state.sidebar.collapsed;
},
setMode: (state, action) => {
state.mode = action.payload;
},
},
});
export default uiSlice.reducer;
export const { setRowsPerPage, toggleSidebar } = uiSlice.actions;
export const { setRowsPerPage, toggleSidebar, setMode } = uiSlice.actions;
+8 -3
View File
@@ -1,14 +1,19 @@
import Sidebar from "../../Components/Sidebar";
import { Outlet } from "react-router";
import { Box } from "@mui/material";
import { useTheme } from "@emotion/react";
import "./index.css";
const HomeLayout = () => {
const theme = useTheme();
return (
<Box className="home-layout">
<Sidebar />
<Outlet />
<Box backgroundColor={theme.palette.background.alt}>
<Box className="home-layout">
<Sidebar />
<Outlet />
</Box>
</Box>
);
};
@@ -30,7 +30,7 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => {
const [checksCount, setChecksCount] = useState(0);
const [paginationController, setPaginationController] = useState({
page: 0,
rowsPerPage: 12,
rowsPerPage: 14,
});
useEffect(() => {
@@ -169,6 +169,10 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => {
sx={{
border: `solid 1px ${theme.palette.border.light}`,
borderRadius: theme.shape.borderRadius,
backgroundColor: theme.palette.background.main,
"& .MuiTableCell-root": {
borderBottomColor: theme.palette.border.light,
},
}}
>
<Table>
+5 -1
View File
@@ -78,7 +78,11 @@ const Incidents = () => {
};
return (
<Stack className="incidents" pt={theme.spacing(20)} gap={theme.spacing(12)}>
<Stack
className="incidents"
pt={theme.spacing(21)}
gap={theme.spacing(12)}
>
{loading ? (
<SkeletonLayout />
) : (
@@ -237,6 +237,10 @@ const CreateMonitor = () => {
onClick={() => setHttps(true)}
sx={{
backgroundColor: https && theme.palette.background.fill,
borderColor: theme.palette.border.dark,
"&:hover": {
borderColor: theme.palette.border.dark,
},
}}
/>
<Button
@@ -245,6 +249,10 @@ const CreateMonitor = () => {
onClick={() => setHttps(false)}
sx={{
backgroundColor: !https && theme.palette.background.fill,
borderColor: theme.palette.border.dark,
"&:hover": {
borderColor: theme.palette.border.dark,
},
}}
/>
</ButtonGroup>
@@ -125,6 +125,7 @@ const PaginationTable = ({ monitorId, dateRange }) => {
sx={{
border: `solid 1px ${theme.palette.border.light}`,
borderRadius: theme.shape.borderRadius,
backgroundColor: theme.palette.background.main,
}}
>
<Table>
@@ -134,13 +135,28 @@ const PaginationTable = ({ monitorId, dateRange }) => {
}}
>
<TableRow>
<TableCell sx={{ color: theme.palette.text.secondary }}>
<TableCell
sx={{
color: theme.palette.text.secondary,
borderBottomColor: theme.palette.border.light,
}}
>
Status
</TableCell>
<TableCell sx={{ color: theme.palette.text.secondary }}>
<TableCell
sx={{
color: theme.palette.text.secondary,
borderBottomColor: theme.palette.border.light,
}}
>
Date & Time
</TableCell>
<TableCell sx={{ color: theme.palette.text.secondary }}>
<TableCell
sx={{
color: theme.palette.text.secondary,
borderBottomColor: theme.palette.border.light,
}}
>
Message
</TableCell>
</TableRow>
@@ -151,17 +167,31 @@ const PaginationTable = ({ monitorId, dateRange }) => {
return (
<TableRow key={check._id}>
<TableCell>
<TableCell
sx={{
borderBottomColor: theme.palette.border.light,
}}
>
<StatusLabel
status={status}
text={status}
customStyles={{ textTransform: "capitalize" }}
/>
</TableCell>
<TableCell>
<TableCell
sx={{
borderBottomColor: theme.palette.border.light,
}}
>
{new Date(check.createdAt).toLocaleString()}
</TableCell>
<TableCell>{check.statusCode}</TableCell>
<TableCell
sx={{
borderBottomColor: theme.palette.border.light,
}}
>
{check.statusCode}
</TableCell>
</TableRow>
);
})}
+7 -2
View File
@@ -8,7 +8,12 @@ import ClockSnooze from "../../../assets/icons/clock-snooze.svg?react";
const StatusBox = ({ title, value }) => {
const theme = useTheme();
let sharedStyles = { position: "absolute", right: 8, opacity: 0.5 };
let sharedStyles = {
position: "absolute",
right: 8,
opacity: 0.5,
"& svg path": { stroke: theme.palette.other.icon },
};
let color;
let icon;
@@ -48,7 +53,7 @@ const StatusBox = ({ title, value }) => {
overflow="hidden"
sx={{
"&:hover": {
backgroundColor: "#f9fafb",
backgroundColor: theme.palette.background.accent,
},
"&:after": {
position: "absolute",
@@ -77,6 +77,9 @@ const ActionsMenu = ({ monitor, isAdmin }) => {
"&:focus": {
outline: "none",
},
"& svg path": {
stroke: theme.palette.other.icon,
},
}}
>
<Settings />
@@ -94,6 +97,7 @@ const ActionsMenu = ({ monitor, isAdmin }) => {
borderColor: theme.palette.border.light,
borderRadius: theme.shape.borderRadius,
boxShadow: theme.shape.boxShadow,
backgroundColor: theme.palette.background.main,
"& ul": {
p: theme.spacing(2.5),
minWidth: "100px",
+1 -1
View File
@@ -145,7 +145,7 @@ const Monitors = ({ isAdmin }) => {
color={theme.palette.text.primary}
border={1}
borderColor={theme.palette.border.light}
backgroundColor={theme.palette.unresolved.bg}
backgroundColor={theme.palette.background.accent}
>
{monitorState.monitors.length}
</Box>
@@ -4,6 +4,7 @@ import BarChart from "../../../Components/Charts/BarChart";
import ArrowDownwardRoundedIcon from "@mui/icons-material/ArrowDownwardRounded";
import { StatusLabel } from "../../../Components/Label";
import { Box } from "@mui/material";
import { useTheme } from "@emotion/react";
const data = {
cols: [
@@ -27,13 +28,17 @@ const data = {
};
export const buildData = (monitors, isAdmin, navigate) => {
const theme = useTheme();
data.rows = monitors.map((monitor, idx) => {
const params = {
url: monitor.url,
title: monitor.name,
percentage: 100,
percentageColor:
monitor.status === true ? "var(--success-color)" : "var(--error-color)",
monitor.status === true
? theme.palette.success.main
: theme.palette.error.text,
status: monitor.status === true ? "up" : "down",
};
+72 -1
View File
@@ -53,7 +53,10 @@ const theme = createTheme({
border: "#fec84b",
},
unresolved: { main: "#4e5ba6", light: "#e2eaf7", bg: "#f2f4f7" },
other: {},
other: {
icon: "#667085",
line: "#d6d9dd"
},
primary: {
main: "#1570EF",
},
@@ -73,4 +76,72 @@ const theme = createTheme({
},
});
export const darkTheme = createTheme({
typography: { fontFamily: fontFamilyDefault, fontSize: 13 },
palette: {
common: { main: "#1570ef" },
text: {
primary: "#fafafa",
secondary: "#e6e6e6",
tertiary: "#a1a1aa",
accent: "#e6e6e6",
},
background: {
main: "#151518",
alt: "#09090b",
fill: "#2e2e2e",
accent: "#18181a",
},
border: { light: "#27272a", dark: "#2c2c2c" },
info: {
text: "#475467",
main: "#475467",
bg: "#ffffff",
light: "#ffffff",
border: "#D0D5DD",
},
success: {
text: "#079455",
main: "#45bb7a",
light: "#93d5aa",
bg: "#27272a",
},
error: {
text: "#f04438",
main: "#d32f2f",
light: "#f04438",
bg: "#27272a",
border: "#f04438",
},
warning: {
text: "#DC6803",
main: "#e88c30",
light: "#fffcf5",
bg: "#ffecbc",
border: "#fec84b",
},
unresolved: { main: "#4e5ba6", light: "#e2eaf7", bg: "#f2f4f7" },
other: {
icon: "#e6e6e6",
line: "#27272a"
},
primary: {
main: "#1570ef",
},
secondary: {
main: "#e6e6e6",
},
tertiary: {
main: "#e6e6e6",
},
},
spacing: 2,
components: {},
shape: {
borderRadius: 2,
borderThick: 2,
boxShadow: shadow,
},
});
export default theme;
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 6C12.5523 6 13 5.55228 13 5C13 4.44772 12.5523 4 12 4C11.4477 4 11 4.44772 11 5C11 5.55228 11.4477 6 12 6Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 20C12.5523 20 13 19.5523 13 19C13 18.4477 12.5523 18 12 18C11.4477 18 11 18.4477 11 19C11 19.5523 11.4477 20 12 20Z" stroke="black" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 733 B

+3 -7
View File
@@ -3,8 +3,6 @@ import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
import { BrowserRouter as Router, HashRouter } from "react-router-dom";
import theme from "./Utils/Theme.js";
import { ThemeProvider } from "@mui/material";
import { Provider } from "react-redux";
import { persistor, store } from "./store";
import { PersistGate } from "redux-persist/integration/react";
@@ -13,11 +11,9 @@ export const networkService = new NetworkService(store);
ReactDOM.createRoot(document.getElementById("root")).render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<ThemeProvider theme={theme}>
<Router>
<App />
</Router>
</ThemeProvider>
<Router>
<App />
</Router>
</PersistGate>
</Provider>
);