mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-23 18:19:51 -06:00
Merge pull request #748 from bluewave-labs/fix/colorful-revamp-extravaganza
Theme file revamp
This commit is contained in:
@@ -1,31 +1,3 @@
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.react:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
a:nth-of-type(2) .logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
@@ -28,14 +28,22 @@ 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 lightTheme from "./Utils/Theme/lightTheme";
|
||||
import darkTheme from "./Utils/Theme/darkTheme";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
function App() {
|
||||
const AdminCheckedRegister = withAdminCheck(Register);
|
||||
const MonitorsWithAdminProp = withAdminProp(Monitors);
|
||||
const DetailsWithAdminProp = withAdminProp(Details);
|
||||
const PageSpeedWithAdminProp = withAdminProp(PageSpeed);
|
||||
const MaintenanceWithAdminProp = withAdminProp(Maintenance);
|
||||
|
||||
const mode = useSelector((state) => state.ui.mode);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ThemeProvider theme={mode === "light" ? lightTheme : darkTheme}>
|
||||
<Routes>
|
||||
<Route exact path="/" element={<HomeLayout />}>
|
||||
<Route
|
||||
@@ -74,7 +82,7 @@ function App() {
|
||||
/>
|
||||
<Route
|
||||
path="maintenance"
|
||||
element={<ProtectedRoute Component={Maintenance} />}
|
||||
element={<ProtectedRoute Component={MaintenanceWithAdminProp} />}
|
||||
/>
|
||||
<Route
|
||||
path="/maintenance/create"
|
||||
@@ -129,7 +137,7 @@ function App() {
|
||||
/>
|
||||
</Routes>
|
||||
<ToastContainer />
|
||||
</>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ const icons = {
|
||||
|
||||
const Alert = ({ variant, title, body, isToast, hasIcon = true, onClick }) => {
|
||||
const theme = useTheme();
|
||||
const { bg, border, color } = theme.alert[variant];
|
||||
const { text, light, border } = theme.palette[variant];
|
||||
const icon = icons[variant];
|
||||
|
||||
return (
|
||||
@@ -41,23 +41,25 @@ const Alert = ({ variant, title, body, isToast, hasIcon = true, onClick }) => {
|
||||
justifyContent="flex-start"
|
||||
alignItems={hasIcon ? "" : "center"}
|
||||
className="alert row-stack"
|
||||
gap={theme.gap.ml}
|
||||
gap={theme.spacing(8)}
|
||||
sx={{
|
||||
padding: hasIcon ? theme.gap.ml : `${theme.gap.small} ${theme.gap.ml}`,
|
||||
backgroundColor: bg,
|
||||
padding: hasIcon
|
||||
? theme.spacing(8)
|
||||
: `${theme.spacing(4)} ${theme.spacing(8)}`,
|
||||
backgroundColor: light,
|
||||
border: `solid 1px ${border}`,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
}}
|
||||
>
|
||||
{hasIcon && <Box sx={{ color: color }}>{icon}</Box>}
|
||||
{hasIcon && <Box sx={{ color: text }}>{icon}</Box>}
|
||||
<Stack direction="column" gap="2px" sx={{ flex: 1 }}>
|
||||
{title && (
|
||||
<Typography sx={{ fontWeight: "700", color: `${color} !important` }}>
|
||||
<Typography sx={{ fontWeight: "700", color: `${text}` }}>
|
||||
{title}
|
||||
</Typography>
|
||||
)}
|
||||
{body && (
|
||||
<Typography sx={{ fontWeight: "400", color: `${color} !important` }}>
|
||||
<Typography sx={{ fontWeight: "400", color: `${text}` }}>
|
||||
{body}
|
||||
</Typography>
|
||||
)}
|
||||
@@ -69,7 +71,7 @@ const Alert = ({ variant, title, body, isToast, hasIcon = true, onClick }) => {
|
||||
sx={{
|
||||
fontWeight: "600",
|
||||
width: "fit-content",
|
||||
mt: theme.gap.small,
|
||||
mt: theme.spacing(4),
|
||||
padding: 0,
|
||||
minWidth: 0,
|
||||
"&:hover": {
|
||||
@@ -95,10 +97,6 @@ const Alert = ({ variant, title, body, isToast, hasIcon = true, onClick }) => {
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
"& .MuiTouchRipple-root": {
|
||||
pointerEvents: "none",
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CloseIcon
|
||||
|
||||
@@ -57,6 +57,7 @@ const Avatar = ({ src, small, sx }) => {
|
||||
}
|
||||
sx={{
|
||||
fontSize: small ? "16px" : "22px",
|
||||
color: "white",
|
||||
fontWeight: 400,
|
||||
backgroundColor: stringToColor(`${user?.firstName} ${user?.lastName}`),
|
||||
display: "inline-flex",
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
|
||||
.MuiPaper-root:has(table.MuiTable-root) {
|
||||
box-shadow: none;
|
||||
border: solid 1px var(--env-var-color-16);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
.MuiTable-root
|
||||
.MuiTableBody-root
|
||||
@@ -26,10 +24,7 @@
|
||||
.MuiTableCell-root {
|
||||
border: none;
|
||||
}
|
||||
.MuiTable-root .MuiTableHead-root,
|
||||
.MuiTable-root .MuiTableRow-root:hover {
|
||||
background-color: var(--env-var-color-13);
|
||||
}
|
||||
|
||||
.MuiTable-root .MuiTableHead-root .MuiTableCell-root,
|
||||
.MuiTable-root .MuiTableBody-root .MuiTableCell-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
@@ -37,7 +32,6 @@
|
||||
.MuiTable-root .MuiTableHead-root .MuiTableCell-root {
|
||||
padding: var(--env-var-spacing-1) var(--env-var-spacing-2);
|
||||
font-weight: 500;
|
||||
color: var(--env-var-color-2);
|
||||
}
|
||||
.MuiTable-root .MuiTableHead-root span {
|
||||
display: inline-block;
|
||||
@@ -52,7 +46,6 @@
|
||||
height: 20px;
|
||||
}
|
||||
.MuiTable-root .MuiTableBody-root .MuiTableCell-root {
|
||||
color: var(--env-var-color-5);
|
||||
padding: 6px var(--env-var-spacing-2);
|
||||
}
|
||||
.MuiTable-root .MuiTableBody-root .MuiTableRow-root {
|
||||
@@ -60,21 +53,14 @@
|
||||
}
|
||||
|
||||
.MuiPaper-root + .MuiPagination-root {
|
||||
margin-top: 24px;
|
||||
border: solid 1px var(--env-var-color-16);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
padding: var(--env-var-spacing-1-plus) var(--env-var-spacing-2);
|
||||
}
|
||||
.MuiPaper-root + .MuiPagination-root ul {
|
||||
justify-content: center;
|
||||
}
|
||||
.MuiPaper-root
|
||||
+ .MuiPagination-root
|
||||
ul
|
||||
li:not(:first-child):not(:last-child)
|
||||
button {
|
||||
.MuiPaper-root + .MuiPagination-root button {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-5);
|
||||
font-weight: 500;
|
||||
}
|
||||
.MuiPaper-root + .MuiPagination-root ul li:first-child {
|
||||
@@ -83,11 +69,6 @@
|
||||
.MuiPaper-root + .MuiPagination-root ul li:last-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
.MuiPaper-root + .MuiPagination-root ul li:first-child button,
|
||||
.MuiPaper-root + .MuiPagination-root ul li:last-child button {
|
||||
border: solid 1px var(--env-var-color-16);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
.MuiPaper-root + .MuiPagination-root ul li:first-child button {
|
||||
padding: 0 var(--env-var-spacing-1) 0 var(--env-var-spacing-1-plus);
|
||||
}
|
||||
@@ -107,23 +88,14 @@
|
||||
content: "Next";
|
||||
margin-right: 15px;
|
||||
}
|
||||
.MuiPaper-root + .MuiPagination-root .MuiPaginationItem-root.Mui-selected {
|
||||
background-color: var(--env-var-color-15);
|
||||
}
|
||||
.MuiPaper-root + .MuiPagination-root div.MuiPaginationItem-root {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.MuiTablePagination-root p {
|
||||
color: var(--env-var-color-2);
|
||||
font-weight: 500;
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
}
|
||||
.MuiTablePagination-root button,
|
||||
.MuiTablePagination-root .MuiSelect-select {
|
||||
border: solid 1px #ebebeb;
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
.MuiTablePagination-root .MuiTablePagination-select.MuiSelect-select {
|
||||
text-align: left;
|
||||
text-align-last: left;
|
||||
@@ -137,10 +109,6 @@
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
.MuiTablePagination-root svg path {
|
||||
stroke: var(--env-var-color-2);
|
||||
stroke-width: 1.3;
|
||||
}
|
||||
.MuiTablePagination-root .MuiSelect-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
@@ -151,38 +119,9 @@
|
||||
.MuiTablePagination-root button.Mui-disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.MuiTablePagination-root button:not(.Mui-disabled):hover {
|
||||
background-color: #f4f4f4;
|
||||
border-color: #f4f4f4;
|
||||
}
|
||||
|
||||
.pagination-dropdown.MuiPaper-root {
|
||||
box-shadow: var(--env-var-shadow-1);
|
||||
border: solid 1px var(--env-var-color-6);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
.pagination-dropdown ul {
|
||||
padding: 4px;
|
||||
min-width: 100px;
|
||||
}
|
||||
.pagination-dropdown li,
|
||||
body:has(.pagination-dropdown) p:has(+ .MuiTablePagination-root) {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-2);
|
||||
padding: 4px;
|
||||
}
|
||||
.pagination-dropdown li.Mui-selected,
|
||||
.pagination-dropdown li.Mui-selected.Mui-focusVisible,
|
||||
.pagination-dropdown li.Mui-selected:hover {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
body:has(.pagination-dropdown) p:has(+ .MuiTablePagination-root) {
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
}
|
||||
|
||||
.monitors .MuiTable-root .MuiTableHead-root .MuiTableCell-root {
|
||||
text-transform: uppercase;
|
||||
color: var(--env-var-color-5);
|
||||
opacity: 0.8;
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
font-weight: 400;
|
||||
|
||||
@@ -200,12 +200,31 @@ const BasicTable = ({ data, paginated, reversed, table }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableContainer component={Paper}>
|
||||
<TableContainer
|
||||
component={Paper}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.background.main,
|
||||
border: `solid 1px ${theme.palette.border.light}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
}}
|
||||
>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableHead
|
||||
sx={{
|
||||
backgroundColor: theme.palette.background.accent,
|
||||
}}
|
||||
>
|
||||
<TableRow>
|
||||
{data.cols.map((col) => (
|
||||
<TableCell key={col.id}>{col.name}</TableCell>
|
||||
<TableCell
|
||||
key={col.id}
|
||||
sx={{
|
||||
color: theme.palette.text.secondary,
|
||||
borderBottomColor: theme.palette.border.light,
|
||||
}}
|
||||
>
|
||||
{col.name}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
@@ -215,12 +234,25 @@ const BasicTable = ({ data, paginated, reversed, table }) => {
|
||||
<TableRow
|
||||
sx={{
|
||||
cursor: row.handleClick ? "pointer" : "default",
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.background.accent,
|
||||
},
|
||||
}}
|
||||
key={row.id}
|
||||
onClick={row.handleClick ? row.handleClick : null}
|
||||
>
|
||||
{row.data.map((cell) => {
|
||||
return <TableCell key={cell.id}>{cell.data}</TableCell>;
|
||||
return (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
sx={{
|
||||
color: theme.palette.text.secondary,
|
||||
borderBottomColor: theme.palette.border.light,
|
||||
}}
|
||||
>
|
||||
{cell.data}
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
);
|
||||
@@ -233,9 +265,14 @@ const BasicTable = ({ data, paginated, reversed, table }) => {
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
px={theme.gap.small}
|
||||
px={theme.spacing(4)}
|
||||
sx={{
|
||||
"& p": {
|
||||
color: theme.palette.text.tertiary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ opacity: 0.7 }}>
|
||||
<Typography px={theme.spacing(2)} fontSize={12} sx={{ opacity: 0.7 }}>
|
||||
Showing {getRange()} of {data.rows.length} monitor(s)
|
||||
</Typography>
|
||||
<TablePagination
|
||||
@@ -260,27 +297,44 @@ const BasicTable = ({ data, paginated, reversed, table }) => {
|
||||
keepMounted: true,
|
||||
PaperProps: {
|
||||
className: "pagination-dropdown",
|
||||
sx: {
|
||||
mt: 0,
|
||||
mb: theme.spacing(2),
|
||||
},
|
||||
},
|
||||
transformOrigin: { vertical: "bottom", horizontal: "left" },
|
||||
anchorOrigin: { vertical: "top", horizontal: "left" },
|
||||
sx: { mt: "-4px" },
|
||||
sx: { mt: theme.spacing(-2) },
|
||||
},
|
||||
inputProps: { id: "pagination-dropdown" },
|
||||
IconComponent: SelectorVertical,
|
||||
sx: {
|
||||
ml: theme.gap.small,
|
||||
mr: theme.gap.large,
|
||||
minWidth: theme.gap.xl,
|
||||
ml: theme.spacing(4),
|
||||
mr: theme.spacing(12),
|
||||
minWidth: theme.spacing(20),
|
||||
textAlign: "left",
|
||||
"&.Mui-focused > div": {
|
||||
backgroundColor: theme.palette.otherColors.white,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
mt: theme.gap.medium,
|
||||
color: theme.palette.otherColors.bluishGray,
|
||||
mt: theme.spacing(6),
|
||||
color: theme.palette.text.secondary,
|
||||
"& button.MuiButtonBase-root, & .MuiSelect-select": {
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
},
|
||||
"& svg path": {
|
||||
stroke: theme.palette.text.tertiary,
|
||||
strokeWidth: 1.3,
|
||||
},
|
||||
"& button:not(.Mui-disabled):hover": {
|
||||
backgroundColor: theme.palette.background.fill,
|
||||
borderColor: theme.palette.background.fill,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.MuiBreadcrumbs-root {
|
||||
height: 36px;
|
||||
height: 34px;
|
||||
}
|
||||
.MuiBreadcrumbs-root svg {
|
||||
width: 16px;
|
||||
@@ -7,17 +7,8 @@
|
||||
}
|
||||
.MuiBreadcrumbs-root .MuiBreadcrumbs-li a {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-2);
|
||||
opacity: 0.8;
|
||||
font-weight: 400;
|
||||
}
|
||||
.MuiBreadcrumbs-root .MuiBreadcrumbs-li:hover a {
|
||||
color: var(--env-var-color-2);
|
||||
}
|
||||
.MuiBreadcrumbs-root .MuiBreadcrumbs-li:not(:last-child):hover a {
|
||||
opacity: 1;
|
||||
background-color: #e3e3e3;
|
||||
}
|
||||
.MuiBreadcrumbs-root .MuiBreadcrumbs-li:not(:last-child) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -25,23 +25,36 @@ const Breadcrumbs = ({ list }) => {
|
||||
<MUIBreadcrumbs
|
||||
separator={<ArrowRight />}
|
||||
aria-label="breadcrumb"
|
||||
p={theme.gap.small}
|
||||
px={theme.spacing(2)}
|
||||
py={theme.spacing(3.5)}
|
||||
width="fit-content"
|
||||
backgroundColor={theme.palette.otherColors.fillGray}
|
||||
borderRadius={`${theme.shape.borderRadius}px`}
|
||||
backgroundColor={theme.palette.background.fill}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
lineHeight="18px"
|
||||
sx={{
|
||||
"& .MuiBreadcrumbs-li:not(:last-of-type):hover a": {
|
||||
backgroundColor: theme.palette.other.fill,
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{list.map((item, index) => {
|
||||
return (
|
||||
<Box
|
||||
component="a"
|
||||
key={`${item.name}-${index}`}
|
||||
px={theme.gap.small}
|
||||
pt={theme.gap.xs}
|
||||
pb="6px"
|
||||
sx={{ textTransform: "capitalize" }}
|
||||
borderRadius={`${theme.shape.borderRadius}px`}
|
||||
px={theme.spacing(4)}
|
||||
pt={theme.spacing(2)}
|
||||
pb={theme.spacing(3)}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
onClick={() => navigate(item.path)}
|
||||
sx={{
|
||||
opacity: 0.8,
|
||||
textTransform: "capitalize",
|
||||
"&, &:hover": {
|
||||
color: theme.palette.text.tertiary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</Box>
|
||||
|
||||
@@ -77,6 +77,7 @@ const Button = ({
|
||||
fontWeight: 400,
|
||||
boxShadow: "none",
|
||||
textTransform: "none",
|
||||
borderRadius: "4px",
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
|
||||
@@ -74,12 +74,13 @@ const ButtonSpinner = ({
|
||||
sx={{
|
||||
boxShadow: "none",
|
||||
textTransform: "none",
|
||||
borderRadius: "4px",
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
"&:hover": {
|
||||
boxShadow: "none",
|
||||
transition: "none"
|
||||
transition: "none",
|
||||
},
|
||||
...sx,
|
||||
}}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
.bar-tooltip .MuiTooltip-tooltip {
|
||||
background-color: white;
|
||||
border: solid 1px black;
|
||||
box-shadow: var(--env-var-shadow-1);
|
||||
border: solid 1px var(--env-var-color-6);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.bar-tooltip .MuiTooltip-tooltip p {
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
color: var(--env-var-color-2);
|
||||
font-weight: 500;
|
||||
}
|
||||
.bar-tooltip .MuiTooltip-tooltip span {
|
||||
font-size: var(--env-var-font-size-small);
|
||||
color: var(--env-var-color-2);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ const BarChart = ({ checks = [] }) => {
|
||||
<Stack
|
||||
direction="row"
|
||||
flexWrap="nowrap"
|
||||
gap="3px"
|
||||
gap={theme.spacing(1.5)}
|
||||
height="50px"
|
||||
width="fit-content"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
@@ -39,11 +39,11 @@ const BarChart = ({ checks = [] }) => {
|
||||
<Box
|
||||
key={`${check}-${index}`}
|
||||
position="relative"
|
||||
width="9px"
|
||||
width={theme.spacing(4.5)}
|
||||
height="100%"
|
||||
backgroundColor={theme.palette.otherColors.fillGray}
|
||||
backgroundColor={theme.palette.background.fill}
|
||||
sx={{
|
||||
borderRadius: "3px",
|
||||
borderRadius: theme.spacing(1.5),
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
@@ -53,15 +53,15 @@ const BarChart = ({ checks = [] }) => {
|
||||
<Typography>
|
||||
{formatDate(new Date(check.createdAt), { year: undefined })}
|
||||
</Typography>
|
||||
<Box mt={theme.gap.xs}>
|
||||
<Box mt={theme.spacing(2)}>
|
||||
<Box
|
||||
display="inline-block"
|
||||
width={theme.gap.small}
|
||||
height={theme.gap.small}
|
||||
width={theme.spacing(4)}
|
||||
height={theme.spacing(4)}
|
||||
backgroundColor={
|
||||
check.status
|
||||
? theme.label.up.dotColor
|
||||
: theme.label.down.dotColor
|
||||
? theme.palette.success.main
|
||||
: theme.palette.error.text
|
||||
}
|
||||
sx={{ borderRadius: "50%" }}
|
||||
/>
|
||||
@@ -69,8 +69,8 @@ const BarChart = ({ checks = [] }) => {
|
||||
display="inline-flex"
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
ml={theme.gap.xs}
|
||||
gap={theme.gap.large}
|
||||
ml={theme.spacing(2)}
|
||||
gap={theme.spacing(12)}
|
||||
>
|
||||
<Typography component="span" sx={{ opacity: 0.8 }}>
|
||||
Response Time
|
||||
@@ -99,6 +99,27 @@ const BarChart = ({ checks = [] }) => {
|
||||
},
|
||||
},
|
||||
],
|
||||
sx: {
|
||||
"& .MuiTooltip-tooltip": {
|
||||
backgroundColor: theme.palette.background.main,
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.dark,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
boxShadow: theme.shape.boxShadow,
|
||||
px: theme.spacing(4),
|
||||
py: theme.spacing(2),
|
||||
},
|
||||
"& .MuiTooltip-tooltip p": {
|
||||
fontSize: 12,
|
||||
color: theme.palette.text.tertiary,
|
||||
fontWeight: 500,
|
||||
},
|
||||
"& .MuiTooltip-tooltip span": {
|
||||
fontSize: 11,
|
||||
color: theme.palette.text.tertiary,
|
||||
fontWeight: 600,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
@@ -107,10 +128,10 @@ const BarChart = ({ checks = [] }) => {
|
||||
width="9px"
|
||||
height="100%"
|
||||
backgroundColor={
|
||||
check.status ? theme.label.up.bgColor : theme.label.down.bgColor
|
||||
check.status ? theme.palette.success.bg : theme.palette.error.bg
|
||||
}
|
||||
sx={{
|
||||
borderRadius: "3px",
|
||||
borderRadius: theme.spacing(1.5),
|
||||
"&:hover > .MuiBox-root": {
|
||||
filter: "brightness(0.8)",
|
||||
},
|
||||
@@ -123,11 +144,11 @@ const BarChart = ({ checks = [] }) => {
|
||||
height={`${animate ? check.responseTime : 0}%`}
|
||||
backgroundColor={
|
||||
check.status
|
||||
? theme.label.up.dotColor
|
||||
: theme.label.down.dotColor
|
||||
? theme.palette.success.main
|
||||
: theme.palette.error.text
|
||||
}
|
||||
sx={{
|
||||
borderRadius: "3px",
|
||||
borderRadius: theme.spacing(1.5),
|
||||
transition: "height 600ms cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import { BarChart, Bar, Cell, ReferenceLine, Label } from "recharts";
|
||||
|
||||
import PropTypes from "prop-types";
|
||||
import { useTheme } from "@emotion/react";
|
||||
|
||||
const MonitorDetails60MinChart = ({ data }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const labelStyle = {
|
||||
fontSize: "10px",
|
||||
fill: "#475467",
|
||||
fill: theme.palette.text.tertiary,
|
||||
};
|
||||
|
||||
const color = {
|
||||
true: "var(--env-var-color-23)",
|
||||
false: "var(--env-var-color-24)",
|
||||
undefined: "var(--env-var-color-33)",
|
||||
true: theme.palette.success.main,
|
||||
false: theme.palette.error.text,
|
||||
undefined: theme.palette.unresolved.main,
|
||||
};
|
||||
return (
|
||||
<BarChart
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
.area-tooltip {
|
||||
background-color: white;
|
||||
border: solid 1px var(--env-var-color-4);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
padding: var(--env-var-spacing-1) var(--env-var-spacing-2);
|
||||
}
|
||||
.area-tooltip p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.area-tooltip p:first-of-type {
|
||||
color: var(--env-var-color-3);
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
.area-tooltip p:last-of-type {
|
||||
margin-top: 5px;
|
||||
color: var(--env-var-color-5);
|
||||
font-size: var(--env-var-font-size-small);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,31 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { AreaChart, Area, XAxis, Tooltip, ResponsiveContainer } from "recharts";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import "./index.css";
|
||||
|
||||
const CustomToolTip = ({ active, payload, label }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
if (active && payload && payload.length) {
|
||||
return (
|
||||
<div className="area-tooltip">
|
||||
<p>
|
||||
<Box
|
||||
className="area-tooltip"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.background.main,
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.dark,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
py: theme.spacing(6),
|
||||
px: theme.spacing(8),
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
sx={{
|
||||
color: theme.palette.common.main,
|
||||
fontSize: 13,
|
||||
}}
|
||||
>
|
||||
{new Date(label).toLocaleDateString("en-US", {
|
||||
weekday: "short", // Mon
|
||||
month: "long", // July
|
||||
@@ -18,10 +37,18 @@ const CustomToolTip = ({ active, payload, label }) => {
|
||||
minute: "2-digit", // 15
|
||||
hour12: true, // AM/PM format
|
||||
})}
|
||||
</p>
|
||||
<p>Response Time (ms): {payload[0].payload.originalResponseTime}</p>{" "}
|
||||
</Typography>
|
||||
<Typography
|
||||
mt={theme.spacing(2.5)}
|
||||
sx={{
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: 13,
|
||||
}}
|
||||
>
|
||||
Response Time (ms): {payload[0].payload.originalResponseTime}
|
||||
</Typography>{" "}
|
||||
{/* Display original value */}
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
.chart-container {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: flex-end;
|
||||
height: 50px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import "./index.css";
|
||||
import PropTypes from "prop-types";
|
||||
import { Stack } from "@mui/material";
|
||||
import { BarChart, Bar, ResponsiveContainer, Cell } from "recharts";
|
||||
import "./index.css";
|
||||
|
||||
const ResponseTimeChart = ({ checks = [] }) => {
|
||||
return (
|
||||
<div className="chart-container">
|
||||
<Stack
|
||||
flexDirection="row"
|
||||
justifyContent="space-around"
|
||||
alignItems="flex-end"
|
||||
height="50px"
|
||||
width="300px"
|
||||
>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart
|
||||
width={150}
|
||||
@@ -18,15 +25,15 @@ const ResponseTimeChart = ({ checks = [] }) => {
|
||||
key={`cell-${index}`}
|
||||
fill={
|
||||
check.status === true
|
||||
? "var(--env-var-color-23)"
|
||||
: "var(--env-var-color-24)"
|
||||
? "var(--success-color)"
|
||||
: "var(--error-color)"
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import React from "react";
|
||||
import "./statistic.css";
|
||||
import PropTypes from "prop-types";
|
||||
import { useTheme } from "@mui/material";
|
||||
|
||||
/**
|
||||
* @component
|
||||
* @param {Object} props
|
||||
* @param {string} props.title - The title text for the statistic (required)
|
||||
* @param {string} props.value - The numerical or textual value for the statistic (required)
|
||||
* @returns {JSX.Element} - Renders the statistic component
|
||||
*/
|
||||
const Statistic = ({ title, value }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const fontLookup = {
|
||||
default: theme.font.default.font,
|
||||
};
|
||||
|
||||
const fontFamily = fontLookup["default"];
|
||||
|
||||
return (
|
||||
<div className="statistic-tile" style={{ fontFamily: fontFamily }}>
|
||||
<div className="statistic-tile-title">{title}</div>
|
||||
<div className="statistic-tile-value">{value}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Statistic.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default Statistic;
|
||||
@@ -1,31 +0,0 @@
|
||||
:root {
|
||||
--stats-spacing-general-0: 5px 10px;
|
||||
--color-border-0: #eaecf0;
|
||||
--border-radius-0: 4px;
|
||||
--font-size-0: 1rem;
|
||||
--font-size-1: 13px;
|
||||
--font-color-0: #344054;
|
||||
--margin-bottom-0: 10px;
|
||||
}
|
||||
|
||||
.statistic-tile {
|
||||
margin: var(--stats-spacing-general-0);
|
||||
padding: var(--stats-spacing-general-0);
|
||||
border: 1px solid var(--color-border-0);
|
||||
border-radius: var(--border-radius-0);
|
||||
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.statistic-tile-title {
|
||||
font-size: var(--font-size-0);
|
||||
color: var(--font-color-0);
|
||||
margin-bottom: var(--margin-bottom-0);
|
||||
}
|
||||
|
||||
.statistic-tile-value {
|
||||
font-size: var(--font-size-1);
|
||||
color: var(--font-color-0);
|
||||
font-weight: 700;
|
||||
margin-bottom: var(--margin-bottom-0);
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import "./check.css";
|
||||
import PropTypes from "prop-types";
|
||||
import CheckGrey from "../../assets/icons/check.svg?react";
|
||||
import CheckOutlined from "../../assets/icons/check-outlined.svg?react";
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
|
||||
/**
|
||||
@@ -22,25 +22,41 @@ import { useTheme } from "@emotion/react";
|
||||
*/
|
||||
const Check = ({ text, variant = "info", outlined = false }) => {
|
||||
const theme = useTheme();
|
||||
const colors = {
|
||||
success: theme.palette.success.main,
|
||||
error: theme.palette.error.text,
|
||||
info: theme.palette.info.border,
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={outlined ? theme.gap.medium : theme.gap.small}
|
||||
className={`check${
|
||||
variant === "error"
|
||||
? " check-error"
|
||||
: variant === "success"
|
||||
? " check-success"
|
||||
: " check-info"
|
||||
}`}
|
||||
className="check"
|
||||
gap={outlined ? theme.spacing(6) : theme.spacing(4)}
|
||||
alignItems="center"
|
||||
>
|
||||
{outlined ? (
|
||||
<CheckOutlined alt="check" />
|
||||
) : (
|
||||
<CheckGrey alt="form checks" />
|
||||
<Box
|
||||
lineHeight={0}
|
||||
sx={{
|
||||
"& svg > path": { fill: colors[variant] },
|
||||
}}
|
||||
>
|
||||
<CheckGrey alt="form checks" />
|
||||
</Box>
|
||||
)}
|
||||
<Typography component="span">{text}</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{
|
||||
color:
|
||||
variant === "info" ? theme.palette.text.tertiary : colors[variant],
|
||||
opacity: 0.8,
|
||||
}}
|
||||
>
|
||||
{text}
|
||||
</Typography>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,17 +1 @@
|
||||
.check > span.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
color: var(--env-var-color-2);
|
||||
opacity: 0.8;
|
||||
}
|
||||
.check-error span.MuiTypography-root {
|
||||
color: var(--env-var-color-24);
|
||||
}
|
||||
.check-error svg > path {
|
||||
fill: var(--env-var-color-19);
|
||||
}
|
||||
.check-success span.MuiTypography-root {
|
||||
color: var(--env-var-color-17);
|
||||
}
|
||||
.check-success svg > path {
|
||||
fill: var(--env-var-color-23);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
font-size: var(--env-var-font-size-large);
|
||||
font-weight: 600;
|
||||
}
|
||||
[class*="fallback__"] .check span.MuiTypography-root,
|
||||
[class*="fallback__"] h1.MuiTypography-root {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
[class*="fallback__"] button.MuiButtonBase-root,
|
||||
[class*="fallback__"] .check {
|
||||
width: max-content;
|
||||
|
||||
@@ -2,7 +2,7 @@ import PropTypes from "prop-types";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
import Skeleton from "../../assets/Images/create-placeholder.svg?react";
|
||||
import background from "../../assets/Images/background_pattern_decorative.png";
|
||||
import Background from "../../assets/Images/background-grid.svg?react";
|
||||
import Button from "../Button";
|
||||
import Check from "../Check/Check";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
@@ -27,16 +27,26 @@ const Fallback = ({ title, checks, link = "/", isAdmin }) => {
|
||||
<Stack
|
||||
className={`fallback__${title.trim().split(" ")[0]}`}
|
||||
alignItems="center"
|
||||
gap={theme.gap.xl}
|
||||
gap={theme.spacing(20)}
|
||||
>
|
||||
<Skeleton style={{ zIndex: 1 }} />
|
||||
<Box
|
||||
className="background-pattern-svg"
|
||||
sx={{ backgroundImage: `url(${background})` }}
|
||||
/>
|
||||
<Stack gap={theme.gap.small} maxWidth={"275px"} zIndex={1}>
|
||||
<Typography component="h1" marginY={theme.gap.medium}>
|
||||
A {title} monitor is used to:
|
||||
sx={{
|
||||
"& svg g g:last-of-type path": {
|
||||
stroke: theme.palette.border.light,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Background style={{ width: "100%" }} />
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(4)} maxWidth={"275px"} zIndex={1}>
|
||||
<Typography
|
||||
component="h1"
|
||||
marginY={theme.spacing(4)}
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
A {title} is used to:
|
||||
</Typography>
|
||||
{checks.map((check, index) => (
|
||||
<Check
|
||||
@@ -46,6 +56,7 @@ const Fallback = ({ title, checks, link = "/", isAdmin }) => {
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
{/* TODO - display a different fallback if user is not an admin*/}
|
||||
{isAdmin && (
|
||||
<Button
|
||||
level="primary"
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
.checkbox-wrapper {
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
.checkbox-wrapper:not(:has(.Mui-disabled)):hover {
|
||||
background-color: var(--env-var-color-15);
|
||||
}
|
||||
.checkbox-wrapper span.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-2);
|
||||
}
|
||||
.checkbox-wrapper .MuiButtonBase-root {
|
||||
margin-right: var(--env-var-spacing-1);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { FormControlLabel, Checkbox as MuiCheckbox } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import CheckboxOutline from "../../../assets/icons/checkbox-outline.svg?react";
|
||||
import CheckboxFilled from "../../../assets/icons/checkbox-filled.svg?react";
|
||||
|
||||
@@ -16,7 +17,7 @@ import "./index.css";
|
||||
* @param {boolean} [props.isDisabled] - Whether the checkbox is disabled or not.
|
||||
*
|
||||
* @returns {JSX.Element}
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* <Checkbox
|
||||
* id="checkbox-id"
|
||||
@@ -37,6 +38,7 @@ const Checkbox = ({
|
||||
isDisabled,
|
||||
}) => {
|
||||
const sizes = { small: "14px", medium: "16px", large: "18px" };
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<FormControlLabel
|
||||
@@ -61,10 +63,20 @@ const Checkbox = ({
|
||||
label={label}
|
||||
disabled={isDisabled}
|
||||
sx={{
|
||||
p: "5px",
|
||||
m: "-5px",
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
p: theme.spacing(2.5),
|
||||
m: theme.spacing(-2.5),
|
||||
"& .MuiButtonBase-root": {
|
||||
width: "20px",
|
||||
width: theme.spacing(10),
|
||||
p: 0,
|
||||
mr: theme.spacing(6),
|
||||
},
|
||||
"&:not(:has(.Mui-disabled)):hover": {
|
||||
backgroundColor: theme.palette.background.accent,
|
||||
},
|
||||
"& span.MuiTypography-root": {
|
||||
fontSize: 13,
|
||||
color: theme.palette.text.tertiary,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -8,24 +8,6 @@
|
||||
.field .input-error {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
.field h3.MuiTypography-root,
|
||||
.field h5.MuiTypography-root,
|
||||
.field input,
|
||||
.field textarea {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.field h3.MuiTypography-root {
|
||||
font-weight: 500;
|
||||
}
|
||||
.field h3.MuiTypography-root span.field-required {
|
||||
color: var(--env-var-color-19);
|
||||
margin-left: 2px;
|
||||
}
|
||||
.field h3.MuiTypography-root span.field-optional {
|
||||
opacity: 0.6;
|
||||
margin-left: 4px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.field h5.MuiTypography-root {
|
||||
position: relative;
|
||||
opacity: 0.8;
|
||||
@@ -37,24 +19,7 @@
|
||||
.field .MuiInputBase-root:has(.MuiInputAdornment-root) {
|
||||
padding-right: var(--env-var-spacing-1-minus);
|
||||
}
|
||||
.field
|
||||
.MuiInputBase-root:has(.copy.MuiInputAdornment-root > .MuiButtonBase-root) {
|
||||
padding-right: 0;
|
||||
}
|
||||
.field .MuiInputBase-root .copy.MuiInputAdornment-root .MuiButtonBase-root {
|
||||
padding: 0 var(--env-var-spacing-1-minus);
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
border-radius: 0;
|
||||
height: 34px;
|
||||
}
|
||||
.field .MuiInputBase-root .copy.MuiInputAdornment-root .MuiButtonBase-root svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.field .MuiInputBase-root .MuiInputAdornment-root .MuiButtonBase-root:hover {
|
||||
border-color: var(--env-var-color-29);
|
||||
}
|
||||
|
||||
.field input {
|
||||
height: 100%;
|
||||
padding: 0 var(--env-var-spacing-1-minus);
|
||||
@@ -62,22 +27,6 @@
|
||||
.field .MuiInputBase-root:has(textarea) {
|
||||
padding: var(--env-var-spacing-1-minus);
|
||||
}
|
||||
.field .MuiOutlinedInput-root fieldset {
|
||||
border-color: var(--env-var-color-29);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
.field:not(:has(.Mui-disabled)):not(:has(.input-error))
|
||||
.MuiOutlinedInput-root:hover:not(:has(input:focus)):not(:has(textarea:focus))
|
||||
fieldset {
|
||||
border-color: var(--env-var-color-29);
|
||||
}
|
||||
.field .input-error {
|
||||
color: var(--env-var-color-24);
|
||||
opacity: 0.8;
|
||||
}
|
||||
.field:has(.input-error) .MuiOutlinedInput-root fieldset {
|
||||
border-color: var(--env-var-color-19);
|
||||
}
|
||||
|
||||
.register-page .field .MuiOutlinedInput-root fieldset,
|
||||
.register-page .field input,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { forwardRef, useState } from "react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import {
|
||||
IconButton,
|
||||
@@ -9,10 +10,7 @@ import {
|
||||
} from "@mui/material";
|
||||
import VisibilityOff from "@mui/icons-material/VisibilityOff";
|
||||
import Visibility from "@mui/icons-material/Visibility";
|
||||
import ContentCopyIcon from "@mui/icons-material/ContentCopy";
|
||||
import Button from "../../Button";
|
||||
import "./index.css";
|
||||
import { forwardRef, useState } from "react";
|
||||
|
||||
/**
|
||||
* @param {Object} props
|
||||
@@ -23,7 +21,6 @@ import { forwardRef, useState } from "react";
|
||||
* @param {boolean} [props.isRequired] - Indicates if the field is required, will display a red asterisk.
|
||||
* @param {boolean} [props.isOptional] - Indicates if the field is optional, will display optional text.
|
||||
* @param {string} [props.optionalLabel] - Optional label for the input field.
|
||||
* @param {boolean} [props.hasCopy] - Indicates if the field supports copying.
|
||||
* @param {string} [props.autoComplete] - Autocomplete value for the input field.
|
||||
* @param {string} [props.placeholder] - Placeholder text for the input field.
|
||||
* @param {string} props.value - Value of the input field.
|
||||
@@ -43,7 +40,6 @@ const Field = forwardRef(
|
||||
isRequired,
|
||||
isOptional,
|
||||
optionalLabel,
|
||||
hasCopy,
|
||||
autoComplete,
|
||||
placeholder,
|
||||
value,
|
||||
@@ -56,25 +52,54 @@ const Field = forwardRef(
|
||||
) => {
|
||||
const theme = useTheme();
|
||||
|
||||
// TODO - are we using this feature anywhere ?
|
||||
const [copy, setCopy] = useState(false);
|
||||
const handleCopy = () => {
|
||||
setCopy(true);
|
||||
setTimeout(() => setCopy(false), 1000);
|
||||
};
|
||||
|
||||
const [isVisible, setVisible] = useState(false);
|
||||
|
||||
return (
|
||||
<Stack gap={theme.gap.xs} className={`field field-${type}`}>
|
||||
<Stack
|
||||
gap={theme.spacing(2)}
|
||||
className={`field field-${type}`}
|
||||
sx={{
|
||||
"& fieldset": {
|
||||
borderColor: theme.palette.border.dark,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
},
|
||||
"&:not(:has(.Mui-disabled)):not(:has(.input-error)) .MuiOutlinedInput-root:hover:not(:has(input:focus)):not(:has(textarea:focus)) fieldset":
|
||||
{
|
||||
borderColor: theme.palette.border.dark,
|
||||
},
|
||||
"&:has(.input-error) .MuiOutlinedInput-root fieldset": {
|
||||
borderColor: theme.palette.error.text,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{label && (
|
||||
<Typography component="h3">
|
||||
<Typography
|
||||
component="h3"
|
||||
color={theme.palette.text.secondary}
|
||||
fontWeight={500}
|
||||
>
|
||||
{label}
|
||||
{isRequired ? <span className="field-required">*</span> : ""}
|
||||
{isRequired ? (
|
||||
<Typography
|
||||
component="span"
|
||||
ml={theme.spacing(1)}
|
||||
color={theme.palette.error.text}
|
||||
>
|
||||
*
|
||||
</Typography>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
{isOptional ? (
|
||||
<span className="field-optional">
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
fontWeight={400}
|
||||
ml={theme.spacing(2)}
|
||||
sx={{ opacity: 0.6 }}
|
||||
>
|
||||
{optionalLabel || "(optional)"}
|
||||
</span>
|
||||
</Typography>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
@@ -92,13 +117,18 @@ const Field = forwardRef(
|
||||
onChange={onChange}
|
||||
disabled={disabled}
|
||||
inputRef={ref}
|
||||
inputProps={{
|
||||
sx: {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}}
|
||||
sx={
|
||||
type === "url"
|
||||
? {
|
||||
"& .MuiInputBase-root": { padding: 0 },
|
||||
"& .MuiStack-root": {
|
||||
borderTopLeftRadius: `${theme.shape.borderRadius}px`,
|
||||
borderBottomLeftRadius: `${theme.shape.borderRadius}px`,
|
||||
borderTopLeftRadius: theme.shape.borderRadius,
|
||||
borderBottomLeftRadius: theme.shape.borderRadius,
|
||||
},
|
||||
}
|
||||
: {}
|
||||
@@ -110,65 +140,53 @@ const Field = forwardRef(
|
||||
alignItems="center"
|
||||
height="100%"
|
||||
sx={{
|
||||
borderRight: `solid 1px ${theme.palette.section.borderColor}`,
|
||||
backgroundColor: "#f9f9fa",
|
||||
pl: theme.gap.medium,
|
||||
borderRight: `solid 1px ${theme.palette.border.dark}`,
|
||||
backgroundColor: theme.palette.background.accent,
|
||||
pl: theme.spacing(6),
|
||||
}}
|
||||
>
|
||||
<Typography component="h5" sx={{ lineHeight: 1 }}>
|
||||
<Typography
|
||||
component="h5"
|
||||
color={theme.palette.text.secondary}
|
||||
sx={{ lineHeight: 1 }}
|
||||
>
|
||||
{https ? "https" : "http"}://
|
||||
</Typography>
|
||||
</Stack>
|
||||
),
|
||||
endAdornment:
|
||||
type === "password" ? (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
aria-label="toggle password visibility"
|
||||
onClick={() => setVisible((show) => !show)}
|
||||
tabIndex={-1}
|
||||
sx={{
|
||||
color: theme.palette.section.borderColor,
|
||||
padding: `calc(${theme.gap.xs} / 2)`,
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
"& .MuiTouchRipple-root": {
|
||||
pointerEvents: "none",
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{!isVisible ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
) : (
|
||||
hasCopy && (
|
||||
<InputAdornment className="copy" position="end">
|
||||
<Button
|
||||
level="tertiary"
|
||||
label={copy ? "Copied" : "Copy"}
|
||||
img={<ContentCopyIcon />}
|
||||
onClick={handleCopy}
|
||||
sx={{
|
||||
borderLeft: `solid 1px ${theme.palette.section.borderColor}`,
|
||||
lineHeight: 0,
|
||||
"& .MuiTouchRipple-root": {
|
||||
pointerEvents: "none",
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</InputAdornment>
|
||||
)
|
||||
),
|
||||
endAdornment: type === "password" && (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
aria-label="toggle password visibility"
|
||||
onClick={() => setVisible((show) => !show)}
|
||||
tabIndex={-1}
|
||||
sx={{
|
||||
color: theme.palette.border.dark,
|
||||
padding: theme.spacing(1),
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
"& .MuiTouchRipple-root": {
|
||||
pointerEvents: "none",
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{!isVisible ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
{error && (
|
||||
<Typography
|
||||
component="span"
|
||||
className="input-error"
|
||||
mt={theme.gap.xs}
|
||||
color={theme.palette.error.text}
|
||||
mt={theme.spacing(2)}
|
||||
sx={{
|
||||
opacity: 0.8,
|
||||
}}
|
||||
>
|
||||
{error}
|
||||
</Typography>
|
||||
@@ -195,7 +213,6 @@ Field.propTypes = {
|
||||
isRequired: PropTypes.bool,
|
||||
isOptional: PropTypes.bool,
|
||||
optionalLabel: PropTypes.string,
|
||||
hasCopy: PropTypes.bool,
|
||||
autoComplete: PropTypes.string,
|
||||
placeholder: PropTypes.string,
|
||||
value: PropTypes.string.isRequired,
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
.image-field-wrapper h2.MuiTypography-root,
|
||||
.image-field-wrapper p.MuiTypography-root,
|
||||
.image-field-wrapper + p.MuiTypography-root,
|
||||
.MuiStack-root:has(#modal-update-picture) h1.MuiTypography-root {
|
||||
color: var(--env-var-color-2);
|
||||
}
|
||||
.MuiStack-root:has(#modal-update-picture) h1.MuiTypography-root {
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -20,10 +14,5 @@
|
||||
}
|
||||
.image-field-wrapper + p.MuiTypography-root,
|
||||
.image-field-wrapper p.MuiTypography-root {
|
||||
opacity: 0.6;
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
}
|
||||
.image-field-wrapper h2.MuiTypography-root span {
|
||||
color: var(--env-var-color-3);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -31,19 +31,19 @@ const ImageField = ({ id, src, loading, onChange }) => {
|
||||
<>
|
||||
<Box
|
||||
className="image-field-wrapper"
|
||||
mt="20px"
|
||||
mt={theme.spacing(8)}
|
||||
sx={{
|
||||
position: "relative",
|
||||
height: "fit-content",
|
||||
border: "dashed",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderColor: isDragging
|
||||
? theme.palette.primary.main
|
||||
: theme.palette.otherColors.graishWhite,
|
||||
? theme.palette.common.main
|
||||
: theme.palette.border.light,
|
||||
borderWidth: "2px",
|
||||
transition: "0.2s",
|
||||
"&:hover": {
|
||||
borderColor: theme.palette.primary.main,
|
||||
borderColor: theme.palette.common.main,
|
||||
backgroundColor: "hsl(215, 87%, 51%, 0.05)",
|
||||
},
|
||||
}}
|
||||
@@ -85,20 +85,40 @@ const ImageField = ({ id, src, loading, onChange }) => {
|
||||
<IconButton
|
||||
sx={{
|
||||
pointerEvents: "none",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
border: `solid ${theme.shape.borderThick}px ${theme.palette.otherColors.graishWhite}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
border: `solid ${theme.shape.borderThick}px ${theme.palette.border.light}`,
|
||||
boxShadow: theme.shape.boxShadow,
|
||||
}}
|
||||
>
|
||||
<CloudUploadIcon />
|
||||
</IconButton>
|
||||
<Typography component="h2">
|
||||
<span>Click to upload</span> or drag and drop
|
||||
<Typography component="h2" color={theme.palette.text.tertiary}>
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
color={theme.palette.common.main}
|
||||
fontWeight={500}
|
||||
>
|
||||
Click to upload
|
||||
</Typography>{" "}
|
||||
or drag and drop
|
||||
</Typography>
|
||||
<Typography
|
||||
component="p"
|
||||
color={theme.palette.text.tertiary}
|
||||
sx={{ opacity: 0.6 }}
|
||||
>
|
||||
(maximum size: 3MB)
|
||||
</Typography>
|
||||
<Typography component="p">(maximum size: 3MB)</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
<Typography component="p">Supported formats: JPG, PNG</Typography>
|
||||
<Typography
|
||||
component="p"
|
||||
color={theme.palette.text.tertiary}
|
||||
sx={{ opacity: 0.6 }}
|
||||
>
|
||||
Supported formats: JPG, PNG
|
||||
</Typography>
|
||||
</>
|
||||
) : (
|
||||
<Stack direction="row" justifyContent="center">
|
||||
|
||||
1
Client/src/Components/Inputs/Radio/index.css
Normal file
1
Client/src/Components/Inputs/Radio/index.css
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import PropTypes from "prop-types";
|
||||
import { FormControlLabel, Radio as MUIRadio, Typography } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import RadioChecked from "../../../assets/icons/radio-checked.svg?react";
|
||||
import "./index.css";
|
||||
|
||||
/**
|
||||
* RadioButton component.
|
||||
* Radio component.
|
||||
*
|
||||
* @component
|
||||
* @example
|
||||
* // Usage:
|
||||
* <RadioButton
|
||||
* <Radio
|
||||
* title="Radio Button Title"
|
||||
* desc="Radio Button Description"
|
||||
* size="small"
|
||||
@@ -15,47 +21,66 @@
|
||||
* @param {string} props.title - The title of the radio button.
|
||||
* @param {string} [props.desc] - The description of the radio button.
|
||||
* @param {string} [props.size="small"] - The size of the radio button.
|
||||
* @returns {JSX.Element} - The rendered RadioButton component.
|
||||
* @returns {JSX.Element} - The rendered Radio component.
|
||||
*/
|
||||
|
||||
import PropTypes from "prop-types";
|
||||
import { FormControlLabel, Radio, Typography } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import "./index.css";
|
||||
|
||||
function RadioButton(props) {
|
||||
const Radio = (props) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<FormControlLabel
|
||||
className="custom-radio-button"
|
||||
checked={props.checked}
|
||||
value={props.value}
|
||||
control={
|
||||
<Radio
|
||||
<MUIRadio
|
||||
id={props.id}
|
||||
size={props.size}
|
||||
sx={{ color: theme.palette.secondary.main }}
|
||||
checkedIcon={<RadioChecked />}
|
||||
sx={{
|
||||
color: "transparent",
|
||||
width: 16,
|
||||
height: 16,
|
||||
boxShadow: "inset 0 0 0 1px #656a74",
|
||||
mt: theme.spacing(0.5),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
onChange={props.onChange}
|
||||
label={
|
||||
<>
|
||||
<Typography component="p">{props.title}</Typography>
|
||||
<Typography component="h6" mt="2px">
|
||||
<Typography
|
||||
component="h6"
|
||||
mt={theme.spacing(1)}
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
{props.desc}
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
labelPlacement="end"
|
||||
sx={{ margin: 0, alignItems: "flex-start" }}
|
||||
sx={{
|
||||
alignItems: "flex-start",
|
||||
p: theme.spacing(2.5),
|
||||
m: theme.spacing(-2.5),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
"&:hover": {
|
||||
backgroundColor: theme.palette.background.accent,
|
||||
},
|
||||
"& .MuiButtonBase-root": {
|
||||
p: 0,
|
||||
mr: theme.spacing(6),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
RadioButton.propTypes = {
|
||||
Radio.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
desc: PropTypes.string,
|
||||
size: PropTypes.string,
|
||||
};
|
||||
|
||||
export default RadioButton;
|
||||
export default Radio;
|
||||
@@ -1,46 +1,7 @@
|
||||
.select-wrapper .select-component > .MuiSelect-select {
|
||||
padding: 0 10px;
|
||||
height: 34px !important;
|
||||
display: flex !important;
|
||||
height: 34px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-transform: capitalize;
|
||||
line-height: 1;
|
||||
}
|
||||
.select-wrapper .MuiInputBase-root:has(> .MuiSelect-select) {
|
||||
min-width: 150px;
|
||||
}
|
||||
.select-wrapper .MuiInputBase-root:has(> .MuiSelect-select),
|
||||
.MuiMenu-List .MuiMenuItem-root,
|
||||
.select-wrapper h3.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.select-wrapper h3.MuiTypography-root {
|
||||
font-weight: 500;
|
||||
}
|
||||
.MuiInputBase-root:has(> .MuiSelect-select) fieldset {
|
||||
border-color: var(--env-var-color-29);
|
||||
}
|
||||
.MuiInputBase-root:not(.Mui-focused):has(> .MuiSelect-select):hover fieldset {
|
||||
border-color: var(--env-var-color-29);
|
||||
}
|
||||
|
||||
.select-dropdown.MuiPaper-root {
|
||||
box-shadow: var(--env-var-shadow-1);
|
||||
border: solid 1px var(--env-var-color-6);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
.select-dropdown .MuiList-root {
|
||||
min-width: 100px;
|
||||
}
|
||||
.select-dropdown li {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-5);
|
||||
padding: 5px 8px;
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
.select-dropdown li.Mui-selected,
|
||||
.select-dropdown li.Mui-selected.Mui-focusVisible,
|
||||
.select-dropdown li.Mui-selected:hover {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
@@ -55,34 +55,44 @@ const Select = ({
|
||||
const theme = useTheme();
|
||||
const itemStyles = {
|
||||
fontSize: "var(--env-var-font-size-medium)",
|
||||
color: theme.palette.otherColors.bluishGray,
|
||||
textTransform: "capitalize",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
margin: theme.gap.xs,
|
||||
color: theme.palette.text.tertiary,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
margin: theme.spacing(2),
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack gap={theme.gap.xs} className="select-wrapper">
|
||||
{label && <Typography component="h3">{label}</Typography>}
|
||||
<Stack gap={theme.spacing(2)} className="select-wrapper">
|
||||
{label && (
|
||||
<Typography
|
||||
component="h3"
|
||||
color={theme.palette.text.secondary}
|
||||
fontWeight={500}
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
)}
|
||||
<MuiSelect
|
||||
className="select-component"
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
displayEmpty
|
||||
inputProps={{ id: id }}
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
className: "select-dropdown",
|
||||
style: {
|
||||
marginTop: theme.gap.xs,
|
||||
},
|
||||
},
|
||||
MenuListProps: {
|
||||
style: { padding: 0 },
|
||||
},
|
||||
}}
|
||||
IconComponent={KeyboardArrowDownIcon}
|
||||
sx={{ ...sx }}
|
||||
sx={{
|
||||
fontSize: 13,
|
||||
minWidth: "125px",
|
||||
"& fieldset": {
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderColor: theme.palette.border.dark,
|
||||
},
|
||||
"&:not(.Mui-focused):hover fieldset": {
|
||||
borderColor: theme.palette.border.dark,
|
||||
},
|
||||
"& svg path": {
|
||||
fill: theme.palette.other.icon,
|
||||
},
|
||||
...sx,
|
||||
}}
|
||||
>
|
||||
{placeholder && (
|
||||
<MenuItem
|
||||
|
||||
@@ -30,9 +30,9 @@ const BaseLabel = ({ label, styles, children }) => {
|
||||
<Box
|
||||
className="label"
|
||||
sx={{
|
||||
borderRadius: `${borderRadius}px`,
|
||||
borderColor: theme.palette.tertiary.main,
|
||||
color: theme.palette.tertiary.main,
|
||||
borderRadius: borderRadius,
|
||||
borderColor: theme.palette.text.tertiary,
|
||||
color: theme.palette.text.tertiary,
|
||||
padding: padding,
|
||||
...styles,
|
||||
}}
|
||||
@@ -90,7 +90,7 @@ const ColoredLabel = ({ label, color }) => {
|
||||
typeof color !== "string" ||
|
||||
!/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(color)
|
||||
) {
|
||||
color = theme.palette.labelGray.color;
|
||||
color = theme.palette.border.light;
|
||||
}
|
||||
|
||||
// Calculate lighter shades for border and bg
|
||||
@@ -127,8 +127,26 @@ ColoredLabel.propTypes = {
|
||||
|
||||
const StatusLabel = ({ status, text, customStyles }) => {
|
||||
const theme = useTheme();
|
||||
const colors = {
|
||||
up: {
|
||||
dotColor: theme.palette.success.main,
|
||||
bgColor: theme.palette.success.bg,
|
||||
borderColor: theme.palette.success.light,
|
||||
},
|
||||
down: {
|
||||
dotColor: theme.palette.error.text,
|
||||
bgColor: theme.palette.error.bg,
|
||||
borderColor: theme.palette.error.light,
|
||||
},
|
||||
"cannot resolve": {
|
||||
dotColor: theme.palette.unresolved.main,
|
||||
bgColor: theme.palette.unresolved.bg,
|
||||
borderColor: theme.palette.unresolved.light,
|
||||
},
|
||||
};
|
||||
|
||||
// Look up the color for the status
|
||||
const { borderColor, bgColor, dotColor } = theme.label[status];
|
||||
const { borderColor, bgColor, dotColor } = colors[status];
|
||||
|
||||
return (
|
||||
<BaseLabel
|
||||
|
||||
@@ -16,24 +16,24 @@ const Link = ({ level, label, url }) => {
|
||||
const levelConfig = {
|
||||
primary: {},
|
||||
secondary: {
|
||||
color: theme.palette.otherColors.bluishGray,
|
||||
color: theme.palette.text.secondary,
|
||||
sx: {
|
||||
":hover": {
|
||||
color: theme.palette.otherColors.bluishGray,
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
},
|
||||
},
|
||||
tertiary: {
|
||||
color: theme.palette.tertiary.main,
|
||||
color: theme.palette.text.tertiary,
|
||||
sx: {
|
||||
textDecoration: "underline",
|
||||
textDecorationStyle: "dashed",
|
||||
textDecorationColor: theme.palette.primary.main,
|
||||
textDecorationColor: theme.palette.common.main,
|
||||
textUnderlineOffset: "1px",
|
||||
":hover": {
|
||||
color: theme.palette.tertiary.main,
|
||||
textDecorationColor: theme.palette.primary.main,
|
||||
backgroundColor: theme.palette.tertiary.linkHover,
|
||||
color: theme.palette.text.tertiary,
|
||||
textDecorationColor: theme.palette.common.main,
|
||||
backgroundColor: theme.palette.background.fill,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,32 +1,10 @@
|
||||
.progress-bar-container {
|
||||
background-color: #fafafa;
|
||||
padding: var(--env-var-spacing-1-plus);
|
||||
}
|
||||
.progress-bar-container h2.MuiTypography-root,
|
||||
.progress-bar-container p.MuiTypography-root {
|
||||
color: var(--env-var-color-2);
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
.progress-bar-container p.MuiTypography-root {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.progress-bar-container p.MuiTypography-root:has(span) {
|
||||
font-size: 12px;
|
||||
}
|
||||
.progress-bar-container p.MuiTypography-root span {
|
||||
padding-left: 2px;
|
||||
}
|
||||
.progress-bar-container:has(p.input-error){
|
||||
border-color: var(--env-var-color-19);
|
||||
background-color: var(--env-var-color-21);
|
||||
padding: 8px var(--env-var-spacing-1-plus);
|
||||
}
|
||||
.progress-bar-container p.input-error{
|
||||
color: var(--env-var-color-24);
|
||||
opacity: 0.8;
|
||||
}
|
||||
.progress-bar-container:has(p.input-error)>.MuiStack-root>svg{
|
||||
fill: var(--env-var-color-24);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
@@ -32,21 +32,36 @@ const ProgressUpload = ({
|
||||
error,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="progress-bar-container"
|
||||
mt="20px"
|
||||
mt={theme.spacing(10)}
|
||||
p={theme.spacing(8)}
|
||||
sx={{
|
||||
minWidth: "200px",
|
||||
height: "fit-content",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
border: `solid 1px ${theme.palette.otherColors.graishWhite}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
backgroundColor: "#fafafa",
|
||||
"&:has(.input-error)": {
|
||||
borderColor: theme.palette.error.main,
|
||||
backgroundColor: theme.palette.error.bg,
|
||||
py: theme.spacing(4),
|
||||
px: theme.spacing(8),
|
||||
"& > .MuiStack-root > svg": {
|
||||
fill: theme.palette.error.text,
|
||||
width: "20px",
|
||||
height: "20px",
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
mb={error ? "0" : "10px"}
|
||||
gap="10px"
|
||||
mb={error ? 0 : theme.spacing(5)}
|
||||
gap={theme.spacing(5)}
|
||||
alignItems={error ? "center" : "flex-end"}
|
||||
>
|
||||
{error ? (
|
||||
@@ -54,10 +69,11 @@ const ProgressUpload = ({
|
||||
) : icon ? (
|
||||
<IconButton
|
||||
sx={{
|
||||
backgroundColor: theme.palette.otherColors.white,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
pointerEvents: "none",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
border: `solid ${theme.shape.borderThick}px ${theme.palette.otherColors.graishWhite}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
border: 2,
|
||||
borderColor: theme.palette.border.light,
|
||||
boxShadow: theme.shape.boxShadow,
|
||||
}}
|
||||
>
|
||||
@@ -67,15 +83,21 @@ const ProgressUpload = ({
|
||||
""
|
||||
)}
|
||||
{error ? (
|
||||
<Typography component="p" className="input-error">
|
||||
<Typography
|
||||
component="p"
|
||||
className="input-error"
|
||||
color={theme.palette.error.text}
|
||||
>
|
||||
{error}
|
||||
</Typography>
|
||||
) : (
|
||||
<Box>
|
||||
<Typography component="h2" mb="5px">
|
||||
<Box color={theme.palette.text.tertiary}>
|
||||
<Typography component="h2" mb={theme.spacing(1.5)}>
|
||||
{error ? error : label}
|
||||
</Typography>
|
||||
<Typography component="p">{!error && size}</Typography>
|
||||
<Typography component="p" sx={{ opacity: 0.6 }}>
|
||||
{!error && size}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
<IconButton
|
||||
@@ -85,9 +107,9 @@ const ProgressUpload = ({
|
||||
? {
|
||||
alignSelf: "flex-start",
|
||||
ml: "auto",
|
||||
mr: "-5px",
|
||||
mt: "-5px",
|
||||
padding: "5px",
|
||||
mr: theme.spacing(-2.5),
|
||||
mt: theme.spacing(-2.5),
|
||||
padding: theme.spacing(2.5),
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
@@ -109,20 +131,23 @@ const ProgressUpload = ({
|
||||
</Stack>
|
||||
{!error ? (
|
||||
<Stack direction="row" alignItems="center">
|
||||
<Box sx={{ width: "100%", mr: "10px" }}>
|
||||
<Box sx={{ width: "100%", mr: theme.spacing(5) }}>
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
value={progress}
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "10px",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
maxWidth: "500px",
|
||||
backgroundColor: theme.palette.otherColors.graishWhite,
|
||||
backgroundColor: theme.palette.border.light,
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Typography component="p" sx={{ minWidth: "max-content" }}>
|
||||
<Typography
|
||||
component="p"
|
||||
sx={{ minWidth: "max-content", opacity: 0.6 }}
|
||||
>
|
||||
{progress}
|
||||
<span>%</span>
|
||||
</Typography>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
.create-monitor-form .custom-radio-button.MuiFormControlLabel-root,
|
||||
.configure-monitor .custom-radio-button.MuiFormControlLabel-root {
|
||||
padding: 5px;
|
||||
margin: -5px;
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
.create-monitor-form .custom-radio-button.MuiFormControlLabel-root:hover,
|
||||
.configure-monitor .custom-radio-button.MuiFormControlLabel-root:hover {
|
||||
background-color: var(--env-var-color-15);
|
||||
}
|
||||
|
||||
.custom-radio-button.MuiFormControlLabel-root .MuiButtonBase-root {
|
||||
margin-right: var(--env-var-spacing-1);
|
||||
padding: 0;
|
||||
}
|
||||
@@ -1,16 +1,8 @@
|
||||
aside .selected-path,
|
||||
.sidebar-popup .selected-path,
|
||||
aside .MuiListItemButton-root:hover {
|
||||
background-color: var(--env-var-color-13);
|
||||
}
|
||||
aside .MuiList-root svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
opacity: 0.9;
|
||||
}
|
||||
aside .MuiList-root svg path {
|
||||
stroke: var(--env-var-color-2);
|
||||
}
|
||||
aside span.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
line-height: 1;
|
||||
@@ -26,11 +18,6 @@ aside .MuiListSubheader-root {
|
||||
margin-bottom: 2px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
aside span.MuiTypography-root,
|
||||
aside p.MuiTypography-root,
|
||||
aside .MuiListSubheader-root {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
aside p.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-small);
|
||||
opacity: 0.8;
|
||||
@@ -47,82 +34,15 @@ aside .selected-path span.MuiTypography-root {
|
||||
aside .MuiCollapse-wrapperInner .MuiList-root > .MuiListItemButton-root {
|
||||
position: relative;
|
||||
}
|
||||
aside .MuiDivider-root {
|
||||
border-color: var(--env-var-color-6);
|
||||
}
|
||||
aside
|
||||
.MuiCollapse-wrapperInner
|
||||
.MuiList-root
|
||||
> .MuiListItemButton-root::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -7px;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-left: solid 1px var(--env-var-color-5);
|
||||
opacity: 0.2;
|
||||
}
|
||||
aside
|
||||
.MuiCollapse-wrapperInner
|
||||
.MuiList-root
|
||||
> .MuiListItemButton-root:last-child::before {
|
||||
height: 50%;
|
||||
}
|
||||
aside .MuiCollapse-wrapperInner .MuiList-root svg,
|
||||
aside .MuiList-root .MuiListItemText-root + svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
aside .MuiCollapse-wrapperInner .MuiList-root > .MuiListItemButton-root::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
top: 45%;
|
||||
height: 3px;
|
||||
width: 3px;
|
||||
border-radius: 50%;
|
||||
background-color: #d6d9dd;
|
||||
}
|
||||
|
||||
aside
|
||||
.MuiCollapse-wrapperInner
|
||||
.MuiList-root
|
||||
> .selected-path.MuiListItemButton-root::after {
|
||||
background-color: var(--env-var-color-2);
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.sidebar-popup .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-popup .MuiList-root {
|
||||
padding: 3px;
|
||||
min-width: 100px;
|
||||
}
|
||||
.sidebar-popup li.MuiButtonBase-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-5);
|
||||
padding: 5px 8px;
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
.sidebar-popup li.MuiButtonBase-root:hover {
|
||||
background-color: var(--env-var-color-13);
|
||||
}
|
||||
.sidebar-popup li.MuiButtonBase-root:has(.MuiBox-root):hover {
|
||||
background-color: white;
|
||||
}
|
||||
.sidebar-popup li.MuiButtonBase-root:has(.MuiBox-root) {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.sidebar-popup .MuiModal-root p.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-2);
|
||||
}
|
||||
.sidebar-popup svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -85,8 +86,7 @@ function Sidebar() {
|
||||
const location = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
const authState = useSelector((state) => state.auth);
|
||||
const { sidebar } = useSelector((state) => state.ui);
|
||||
let collapsed = sidebar.collapsed;
|
||||
const collapsed = useSelector((state) => state.ui.sidebar.collapsed);
|
||||
const [open, setOpen] = useState({ Dashboard: false, Account: false });
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [popup, setPopup] = useState();
|
||||
@@ -124,21 +124,38 @@ function Sidebar() {
|
||||
<Stack
|
||||
component="aside"
|
||||
className={collapsed ? "collapsed" : "expanded"}
|
||||
gap={theme.gap.medium}
|
||||
py={theme.spacing(6)}
|
||||
gap={theme.spacing(6)}
|
||||
sx={{
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
"& .selected-path, & .MuiListItemButton-root:hover": {
|
||||
backgroundColor: theme.palette.background.accent,
|
||||
},
|
||||
"& .MuiList-root svg path": {
|
||||
stroke: theme.palette.text.tertiary,
|
||||
},
|
||||
"& p, & span, & .MuiListSubheader-root": {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack pt={theme.gap.medium} pb={theme.gap.large} pl={theme.gap.ml}>
|
||||
<Stack direction="row" alignItems="center" gap={theme.gap.small}>
|
||||
<Stack pt={theme.spacing(6)} pb={theme.spacing(12)} pl={theme.spacing(8)}>
|
||||
<Stack direction="row" alignItems="center" gap={theme.spacing(4)}>
|
||||
<Stack
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
minWidth={theme.gap.lgplus}
|
||||
minHeight={theme.gap.lgplus}
|
||||
fontSize="18px"
|
||||
minWidth={theme.spacing(16)}
|
||||
minHeight={theme.spacing(16)}
|
||||
pl="1px"
|
||||
fontSize={18}
|
||||
color="white"
|
||||
sx={{
|
||||
position: "relative",
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
borderRadius: "4px",
|
||||
backgroundColor: theme.palette.common.main,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
userSelect: "none",
|
||||
}}
|
||||
>
|
||||
@@ -146,7 +163,7 @@ function Sidebar() {
|
||||
</Stack>
|
||||
<Typography
|
||||
component="span"
|
||||
mt={theme.gap.xs}
|
||||
mt={theme.spacing(2)}
|
||||
sx={{ opacity: 0.8, fontWeight: 500 }}
|
||||
>
|
||||
BlueWave Uptime
|
||||
@@ -158,20 +175,21 @@ function Sidebar() {
|
||||
top: 60,
|
||||
right: 0,
|
||||
transform: `translate(50%, 0)`,
|
||||
backgroundColor: theme.palette.otherColors.fillGray,
|
||||
border: `solid 1px ${theme.palette.otherColors.graishWhite}`,
|
||||
p: "5px",
|
||||
backgroundColor: theme.palette.background.fill,
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
p: theme.spacing(2.5),
|
||||
"& svg": {
|
||||
width: theme.gap.ml,
|
||||
height: theme.gap.ml,
|
||||
width: theme.spacing(8),
|
||||
height: theme.spacing(8),
|
||||
"& path": {
|
||||
stroke: theme.palette.otherColors.bluishGray,
|
||||
stroke: theme.palette.text.secondary,
|
||||
},
|
||||
},
|
||||
"&:focus": { outline: "none" },
|
||||
"&:hover": {
|
||||
backgroundColor: "#e3e3e3",
|
||||
borderColor: theme.palette.otherColors.graishWhite,
|
||||
backgroundColor: theme.palette.border.light,
|
||||
borderColor: theme.palette.border.light,
|
||||
},
|
||||
}}
|
||||
onClick={() => {
|
||||
@@ -192,15 +210,15 @@ function Sidebar() {
|
||||
component="div"
|
||||
id="nested-menu-subheader"
|
||||
sx={{
|
||||
pt: theme.gap.small,
|
||||
px: collapsed ? theme.gap.xs : theme.gap.small,
|
||||
backgroundColor: "transparent"
|
||||
pt: theme.spacing(4),
|
||||
px: collapsed ? theme.spacing(2) : theme.spacing(4),
|
||||
backgroundColor: "transparent",
|
||||
}}
|
||||
>
|
||||
Menu
|
||||
</ListSubheader>
|
||||
}
|
||||
sx={{ px: theme.gap.medium }}
|
||||
sx={{ px: theme.spacing(6) }}
|
||||
>
|
||||
{menu.map((item) =>
|
||||
item.path ? (
|
||||
@@ -229,9 +247,9 @@ function Sidebar() {
|
||||
onClick={() => navigate(`/${item.path}`)}
|
||||
sx={{
|
||||
height: "37px",
|
||||
gap: theme.gap.small,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
px: theme.gap.small,
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
px: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon}</ListItemIcon>
|
||||
@@ -266,9 +284,9 @@ function Sidebar() {
|
||||
onClick={(event) => openPopup(event, item.name)}
|
||||
sx={{
|
||||
position: "relative",
|
||||
gap: theme.gap.small,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
px: theme.gap.small,
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
px: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon}</ListItemIcon>
|
||||
@@ -284,7 +302,21 @@ function Sidebar() {
|
||||
vertical: "top",
|
||||
horizontal: "right",
|
||||
}}
|
||||
sx={{ ml: theme.gap.ml }}
|
||||
slotProps={{
|
||||
paper: {
|
||||
sx: {
|
||||
mt: theme.spacing(-2),
|
||||
ml: theme.spacing(1),
|
||||
},
|
||||
},
|
||||
}}
|
||||
MenuListProps={{ sx: { px: 1, py: 2 } }}
|
||||
sx={{
|
||||
ml: theme.spacing(8),
|
||||
"& .selected-path": {
|
||||
backgroundColor: theme.palette.background.accent,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{item.nested.map((child) => {
|
||||
if (
|
||||
@@ -308,10 +340,14 @@ function Sidebar() {
|
||||
closePopup();
|
||||
}}
|
||||
sx={{
|
||||
gap: theme.gap.small,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
pl: theme.gap.small,
|
||||
mb: "1px",
|
||||
gap: theme.spacing(4),
|
||||
opacity: 0.9,
|
||||
"& svg": {
|
||||
"& path": {
|
||||
stroke: theme.palette.other.icon,
|
||||
strokeWidth: 1.1,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{child.icon}
|
||||
@@ -331,9 +367,9 @@ function Sidebar() {
|
||||
}))
|
||||
}
|
||||
sx={{
|
||||
gap: theme.gap.small,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
px: theme.gap.small,
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
px: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon}</ListItemIcon>
|
||||
@@ -344,7 +380,7 @@ function Sidebar() {
|
||||
<List
|
||||
component="div"
|
||||
disablePadding
|
||||
sx={{ pl: theme.gap.large }}
|
||||
sx={{ pl: theme.spacing(12) }}
|
||||
>
|
||||
{item.nested.map((child) => {
|
||||
if (
|
||||
@@ -365,9 +401,35 @@ function Sidebar() {
|
||||
key={child.path}
|
||||
onClick={() => navigate(`/${child.path}`)}
|
||||
sx={{
|
||||
gap: theme.gap.small,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
pl: theme.gap.small,
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
pl: theme.spacing(4),
|
||||
"&::before": {
|
||||
content: `""`,
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: "-7px",
|
||||
height: "100%",
|
||||
borderLeft: 1,
|
||||
borderLeftColor: theme.palette.other.line,
|
||||
},
|
||||
"&:last-child::before": {
|
||||
height: "50%",
|
||||
},
|
||||
"&::after": {
|
||||
content: `""`,
|
||||
position: "absolute",
|
||||
top: "45%",
|
||||
left: "-8px",
|
||||
height: "3px",
|
||||
width: "3px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: theme.palette.other.line,
|
||||
},
|
||||
"&.selected-path::after": {
|
||||
backgroundColor: theme.palette.text.tertiary,
|
||||
transform: "scale(1.2)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 0 }}>
|
||||
@@ -383,7 +445,7 @@ function Sidebar() {
|
||||
)
|
||||
)}
|
||||
</List>
|
||||
<Divider sx={{ my: theme.gap.small }} />
|
||||
<Divider sx={{ my: theme.spacing(4) }} />
|
||||
{/* other */}
|
||||
<List
|
||||
component="nav"
|
||||
@@ -393,15 +455,15 @@ function Sidebar() {
|
||||
component="div"
|
||||
id="nested-other-subheader"
|
||||
sx={{
|
||||
pt: theme.gap.small,
|
||||
px: collapsed ? 0 : theme.gap.small,
|
||||
backgroundColor: "transparent"
|
||||
pt: theme.spacing(4),
|
||||
px: collapsed ? 0 : theme.spacing(4),
|
||||
backgroundColor: "transparent",
|
||||
}}
|
||||
>
|
||||
Other
|
||||
</ListSubheader>
|
||||
}
|
||||
sx={{ px: theme.gap.medium }}
|
||||
sx={{ px: theme.spacing(6) }}
|
||||
>
|
||||
{other.map((item) => (
|
||||
<Tooltip
|
||||
@@ -436,9 +498,9 @@ function Sidebar() {
|
||||
: navigate(`/${item.path}`)
|
||||
}
|
||||
sx={{
|
||||
gap: theme.gap.small,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
px: theme.gap.small,
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
px: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon}</ListItemIcon>
|
||||
@@ -453,10 +515,10 @@ function Sidebar() {
|
||||
direction="row"
|
||||
height="50px"
|
||||
alignItems="center"
|
||||
py={theme.gap.small}
|
||||
px={theme.gap.ml}
|
||||
gap={theme.gap.xs}
|
||||
borderRadius={`${theme.shape.borderRadius}px`}
|
||||
py={theme.spacing(4)}
|
||||
px={theme.spacing(8)}
|
||||
gap={theme.spacing(2)}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
>
|
||||
{collapsed ? (
|
||||
<>
|
||||
@@ -483,47 +545,11 @@ 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",
|
||||
}}
|
||||
sx={{ ml: theme.gap.ml }}
|
||||
>
|
||||
<MenuItem sx={{ cursor: "default", minWidth: "150px" }}>
|
||||
<Box>
|
||||
<Typography component="span" fontWeight={500} fontSize="13px">
|
||||
{authState.user?.firstName} {authState.user?.lastName}
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{ textTransform: "capitalize", fontSize: "12px" }}
|
||||
>
|
||||
{authState.user?.role}
|
||||
</Typography>
|
||||
</Box>
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem
|
||||
onClick={logout}
|
||||
sx={{
|
||||
gap: theme.gap.small,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
pl: theme.gap.small,
|
||||
}}
|
||||
>
|
||||
<LogoutSvg />
|
||||
Log out
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Avatar small={true} />
|
||||
<Box ml={theme.gap.xs}>
|
||||
<Box ml={theme.spacing(2)}>
|
||||
<Typography component="span" fontWeight={500}>
|
||||
{authState.user?.firstName} {authState.user?.lastName}
|
||||
</Typography>
|
||||
@@ -531,16 +557,84 @@ function Sidebar() {
|
||||
{authState.user?.role}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Tooltip title="Log Out">
|
||||
<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: {
|
||||
marginTop: theme.spacing(-4),
|
||||
marginLeft: collapsed ? theme.spacing(2) : 0,
|
||||
},
|
||||
},
|
||||
}}
|
||||
MenuListProps={{
|
||||
sx: {
|
||||
p: 2,
|
||||
"& 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 />}
|
||||
<MenuItem onClick={() => dispatch(setMode("light"))}>Light</MenuItem>
|
||||
<MenuItem onClick={() => dispatch(setMode("dark"))}>Dark</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem
|
||||
onClick={logout}
|
||||
sx={{
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
pl: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
<LogoutSvg />
|
||||
Log out
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import TabPanel from "@mui/lab/TabPanel";
|
||||
import React, { useState } from "react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { Box, Divider, Stack, Typography } from "@mui/material";
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
import ButtonSpinner from "../../ButtonSpinner";
|
||||
import Field from "../../Inputs/Field";
|
||||
import { credentials } from "../../../Validation/validation";
|
||||
@@ -97,13 +97,20 @@ const PasswordPanel = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<TabPanel value="password">
|
||||
<TabPanel
|
||||
value="password"
|
||||
sx={{
|
||||
"& h1, & input": {
|
||||
color: theme.palette.text.tertiary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
component="form"
|
||||
onSubmit={handleSubmit}
|
||||
noValidate
|
||||
spellCheck="false"
|
||||
gap={theme.gap.xl}
|
||||
gap={theme.spacing(20)}
|
||||
>
|
||||
<Stack direction="row">
|
||||
<Box flex={0.9}>
|
||||
@@ -165,9 +172,9 @@ const PasswordPanel = () => {
|
||||
loadingText="Saving..."
|
||||
disabled={Object.keys(errors).length !== 0 && true}
|
||||
sx={{
|
||||
paddingX: theme.gap.large,
|
||||
paddingX: theme.spacing(12),
|
||||
width: "fit-content",
|
||||
mt: theme.gap.xl,
|
||||
mt: theme.spacing(20),
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -216,13 +216,20 @@ const ProfilePanel = () => {
|
||||
const isModalOpen = (name) => isOpen === name;
|
||||
|
||||
return (
|
||||
<TabPanel value="profile">
|
||||
<TabPanel
|
||||
value="profile"
|
||||
sx={{
|
||||
"& h1, & p, & input": {
|
||||
color: theme.palette.text.tertiary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
component="form"
|
||||
className="edit-profile-form"
|
||||
noValidate
|
||||
spellCheck="false"
|
||||
gap={theme.gap.xl}
|
||||
gap={theme.spacing(20)}
|
||||
>
|
||||
<Stack direction="row">
|
||||
<Box flex={0.9}>
|
||||
@@ -295,7 +302,7 @@ const ProfilePanel = () => {
|
||||
label="Update"
|
||||
onClick={openPictureModal}
|
||||
sx={{
|
||||
color: theme.palette.primary.main,
|
||||
color: theme.palette.common.main,
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
@@ -303,7 +310,9 @@ const ProfilePanel = () => {
|
||||
<Divider
|
||||
aria-hidden="true"
|
||||
width="0"
|
||||
sx={{ marginY: theme.spacing(1) }}
|
||||
sx={{
|
||||
marginY: theme.spacing(1),
|
||||
}}
|
||||
/>
|
||||
<Stack direction="row" justifyContent="flex-end">
|
||||
<Box width="fit-content">
|
||||
@@ -317,18 +326,24 @@ const ProfilePanel = () => {
|
||||
Object.keys(errors).length !== 0 && !errors?.picture && true
|
||||
}
|
||||
sx={{
|
||||
paddingX: theme.gap.large,
|
||||
paddingX: theme.spacing(12),
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Divider aria-hidden="true" sx={{ marginY: theme.spacing(6) }} />
|
||||
<Divider
|
||||
aria-hidden="true"
|
||||
sx={{
|
||||
marginY: theme.spacing(20),
|
||||
borderColor: theme.palette.border.light,
|
||||
}}
|
||||
/>
|
||||
<Stack
|
||||
component="form"
|
||||
noValidate
|
||||
spellCheck="false"
|
||||
gap={theme.gap.small}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Box>
|
||||
<Typography component="h1">Delete account</Typography>
|
||||
@@ -341,7 +356,7 @@ const ProfilePanel = () => {
|
||||
level="error"
|
||||
label="Delete account"
|
||||
onClick={() => setIsOpen("delete")}
|
||||
sx={{ width: "fit-content", mt: theme.gap.small }}
|
||||
sx={{ width: "fit-content", mt: theme.spacing(4) }}
|
||||
/>
|
||||
</Stack>
|
||||
{/* TODO - Update ModalPopup Component with @mui for reusability */}
|
||||
@@ -353,18 +368,19 @@ const ProfilePanel = () => {
|
||||
disablePortal
|
||||
>
|
||||
<Stack
|
||||
gap="10px"
|
||||
gap={theme.spacing(5)}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 500,
|
||||
bgcolor: "white",
|
||||
border: "solid 1px #f2f2f2",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
bgcolor: theme.palette.background.main,
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
boxShadow: 24,
|
||||
p: "30px",
|
||||
p: theme.spacing(15),
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
@@ -378,7 +394,12 @@ const ProfilePanel = () => {
|
||||
and all of your data will be deleted. Deleting your account is
|
||||
permanent and non-recoverable action.
|
||||
</Typography>
|
||||
<Stack direction="row" gap="10px" mt="10px" justifyContent="flex-end">
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.spacing(5)}
|
||||
mt={theme.spacing(5)}
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<Button
|
||||
level="tertiary"
|
||||
label="Cancel"
|
||||
@@ -406,17 +427,22 @@ const ProfilePanel = () => {
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 450,
|
||||
bgcolor: "white",
|
||||
border: "solid 1px #f2f2f2",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
bgcolor: theme.palette.background.main,
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
boxShadow: 24,
|
||||
p: "30px",
|
||||
p: theme.spacing(15),
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography id="modal-update-picture" component="h1">
|
||||
<Typography
|
||||
id="modal-update-picture"
|
||||
component="h1"
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
Upload Image
|
||||
</Typography>
|
||||
<ImageField
|
||||
@@ -447,7 +473,12 @@ const ProfilePanel = () => {
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
<Stack direction="row" mt="20px" gap="10px" justifyContent="flex-end">
|
||||
<Stack
|
||||
direction="row"
|
||||
mt={theme.spacing(10)}
|
||||
gap={theme.spacing(5)}
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<Button
|
||||
level="secondary"
|
||||
label="Edit"
|
||||
|
||||
@@ -99,12 +99,10 @@ const TeamPanel = () => {
|
||||
id: idx,
|
||||
data: (
|
||||
<Stack>
|
||||
<Typography
|
||||
style={{ color: theme.palette.otherColors.blackish }}
|
||||
>
|
||||
<Typography style={{ color: theme.palette.text.secondary }}>
|
||||
{member.firstName + " " + member.lastName}
|
||||
</Typography>
|
||||
<Typography sx={{ opacity: 0.6 }}>
|
||||
<Typography>
|
||||
Created {new Date(member.createdAt).toLocaleDateString()}
|
||||
</Typography>
|
||||
</Stack>
|
||||
@@ -212,7 +210,18 @@ const TeamPanel = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<TabPanel value="team">
|
||||
<TabPanel
|
||||
value="team"
|
||||
sx={{
|
||||
"& h1": {
|
||||
color: theme.palette.text.tertiary,
|
||||
},
|
||||
"& .MuiTable-root .MuiTableBody-root .MuiTableCell-root, & .MuiTable-root p + p":
|
||||
{
|
||||
color: theme.palette.text.accent,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{/* FEATURE STILL TO BE IMPLEMENTED */}
|
||||
{/* <Stack component="form">
|
||||
<Box sx={{ alignSelf: "flex-start" }}>
|
||||
@@ -243,7 +252,7 @@ const TeamPanel = () => {
|
||||
},
|
||||
}}
|
||||
inputProps={{
|
||||
sx: { textAlign: "end", padding: theme.gap.small },
|
||||
sx: { textAlign: "end", padding: theme.spacing(4) },
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
@@ -253,8 +262,8 @@ const TeamPanel = () => {
|
||||
onClick={() => toggleEdit()}
|
||||
sx={{
|
||||
minWidth: 0,
|
||||
paddingX: theme.gap.small,
|
||||
ml: orgStates.isEdit ? theme.gap.small : 0,
|
||||
paddingX: theme.spacing(4),
|
||||
ml: orgStates.isEdit ? theme.spacing(4) : 0,
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
@@ -264,24 +273,30 @@ const TeamPanel = () => {
|
||||
component="form"
|
||||
noValidate
|
||||
spellCheck="false"
|
||||
gap={theme.gap.large}
|
||||
gap={theme.spacing(12)}
|
||||
>
|
||||
<Typography component="h1">Team members</Typography>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="flex-end"
|
||||
gap={theme.gap.medium}
|
||||
sx={{ fontSize: "14px" }}
|
||||
gap={theme.spacing(6)}
|
||||
sx={{ fontSize: 14 }}
|
||||
>
|
||||
<ButtonGroup>
|
||||
<ButtonGroup
|
||||
sx={{
|
||||
"& button, & button:hover": {
|
||||
borderColor: theme.palette.border.light,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
level="secondary"
|
||||
label="All"
|
||||
onClick={() => setFilter("all")}
|
||||
sx={{
|
||||
backgroundColor:
|
||||
filter === "all" && theme.palette.otherColors.fillGray,
|
||||
filter === "all" && theme.palette.background.fill,
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
@@ -290,7 +305,7 @@ const TeamPanel = () => {
|
||||
onClick={() => setFilter("admin")}
|
||||
sx={{
|
||||
backgroundColor:
|
||||
filter === "admin" && theme.palette.otherColors.fillGray,
|
||||
filter === "admin" && theme.palette.background.fill,
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
@@ -299,7 +314,7 @@ const TeamPanel = () => {
|
||||
onClick={() => setFilter("user")}
|
||||
sx={{
|
||||
backgroundColor:
|
||||
filter === "user" && theme.palette.otherColors.fillGray,
|
||||
filter === "user" && theme.palette.background.fill,
|
||||
}}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
@@ -307,7 +322,7 @@ const TeamPanel = () => {
|
||||
<Button
|
||||
level="primary"
|
||||
label="Invite a team member"
|
||||
sx={{ paddingX: "30px" }}
|
||||
sx={{ paddingX: theme.spacing(15) }}
|
||||
onClick={() => setIsOpen(true)}
|
||||
/>
|
||||
</Stack>
|
||||
@@ -326,18 +341,19 @@ const TeamPanel = () => {
|
||||
disablePortal
|
||||
>
|
||||
<Stack
|
||||
gap="10px"
|
||||
gap={theme.spacing(5)}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 400,
|
||||
bgcolor: "white",
|
||||
border: "solid 1px #f2f2f2",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
bgcolor: theme.palette.background.main,
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
boxShadow: 24,
|
||||
p: "30px",
|
||||
p: theme.spacing(15),
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
@@ -349,7 +365,7 @@ const TeamPanel = () => {
|
||||
<Typography
|
||||
id="invite-member-to-team"
|
||||
component="p"
|
||||
sx={{ mb: theme.gap.medium }}
|
||||
sx={{ mb: theme.spacing(6) }}
|
||||
>
|
||||
When you add a new team member, they will get access to all
|
||||
monitors.
|
||||
@@ -374,14 +390,14 @@ const TeamPanel = () => {
|
||||
}))
|
||||
}
|
||||
items={[
|
||||
{ _id: "admin", name: "admin" },
|
||||
{ _id: "user", name: "user" },
|
||||
{ _id: "admin", name: "Admin" },
|
||||
{ _id: "user", name: "User" },
|
||||
]}
|
||||
/>
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.gap.small}
|
||||
mt={theme.gap.ml}
|
||||
gap={theme.spacing(4)}
|
||||
mt={theme.spacing(8)}
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<Button
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import "./tooltipWithTail.css";
|
||||
import React from "react";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import Tooltip, { tooltipClasses } from "@mui/material/Tooltip";
|
||||
import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
|
||||
|
||||
const CustomizedTooltip = styled(
|
||||
({ className, placement, arrow, ...props }) => (
|
||||
<Tooltip
|
||||
{...props}
|
||||
placement={placement}
|
||||
arrow={arrow}
|
||||
classes={{ popper: className }}
|
||||
/>
|
||||
)
|
||||
)(({ theme }) => ({
|
||||
[`& .${tooltipClasses.tooltip}`]: {
|
||||
backgroundColor: "black",
|
||||
},
|
||||
[`& .${tooltipClasses.arrow}`]: {
|
||||
color: "black",
|
||||
},
|
||||
}));
|
||||
|
||||
function TooltipWithTail({ placement, arrow = false, title, text }) {
|
||||
return (
|
||||
<div>
|
||||
<CustomizedTooltip
|
||||
arrow={arrow}
|
||||
placement={placement}
|
||||
title={
|
||||
<React.Fragment>
|
||||
{title && <div className="tooltip-title">{title}</div>}
|
||||
<div className="tooltip-description">{text}</div>
|
||||
</React.Fragment>
|
||||
}
|
||||
className="tooltip-holder"
|
||||
>
|
||||
<HelpOutlineIcon style={{ fill: "#98A2B3" }} />
|
||||
</CustomizedTooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default TooltipWithTail;
|
||||
@@ -1,49 +0,0 @@
|
||||
:root {
|
||||
--env-var-color-1: #101828;
|
||||
--env-var-color-2: #475467;
|
||||
--env-var-color-3: #1570ef;
|
||||
|
||||
--env-var-color-4: #d0d5dd;
|
||||
--env-var-color-5: #344054;
|
||||
--env-var-color-6: #eaecf0;
|
||||
--env-var-color-7: #7f56d9;
|
||||
--env-var-color-8: #fff;
|
||||
--env-var-color-9: #f2f2f2;
|
||||
--env-var-color-10: #175cd3;
|
||||
|
||||
--env-var-radius-1: 4px;
|
||||
--env-var-radius-2: 8px;
|
||||
|
||||
--env-var-width-1: 100vw;
|
||||
--env-var-width-2: 360px;
|
||||
|
||||
--env-var-height-1: 100vh;
|
||||
|
||||
--env-var-spacing-1: 12px;
|
||||
--env-var-spacing-2: 24px;
|
||||
--env-var-spacing-3: 32px;
|
||||
--env-var-spacing-4: 40px;
|
||||
|
||||
--env-var-font-size-small: 11px;
|
||||
--env-var-font-size-medium: 13px;
|
||||
--env-var-font-size-medium-plus: 14px;
|
||||
--env-var-font-size-large: 16px;
|
||||
--env-var-font-size-xlarge: 30px;
|
||||
|
||||
--env-var-img-width-1: 20px;
|
||||
|
||||
--env-var-default-1: 24px;
|
||||
--env-var-default-2: 40px;
|
||||
}
|
||||
|
||||
.tooltip-title {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
font-weight: bold;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.tooltip-description {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-4);
|
||||
margin: 5px;
|
||||
}
|
||||
@@ -12,6 +12,7 @@ const initialState = {
|
||||
sidebar: {
|
||||
collapsed: false,
|
||||
},
|
||||
mode: "light",
|
||||
};
|
||||
|
||||
const uiSlice = createSlice({
|
||||
@@ -19,13 +20,19 @@ const uiSlice = createSlice({
|
||||
initialState,
|
||||
reducers: {
|
||||
setRowsPerPage: (state, action) => {
|
||||
state[action.payload.table].rowsPerPage = action.payload.value;
|
||||
const { table, value } = action.payload;
|
||||
if (state[table]) {
|
||||
state[table].rowsPerPage = value;
|
||||
}
|
||||
},
|
||||
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;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#root:has(.home-layout) {
|
||||
background-color: var(--env-var-color-30);
|
||||
background-color: var(--secondary-bg);
|
||||
}
|
||||
|
||||
.home-layout {
|
||||
@@ -19,12 +19,6 @@
|
||||
|
||||
height: calc(100vh - var(--env-var-spacing-2) * 2);
|
||||
max-width: var(--env-var-side-bar-width);
|
||||
|
||||
border: 1px solid var(--env-var-color-6);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
background-color: var(--env-var-color-8);
|
||||
|
||||
padding: var(--env-var-spacing-1) 0;
|
||||
}
|
||||
|
||||
.home-layout > div {
|
||||
@@ -32,19 +26,11 @@
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.home-layout > div:has(> [class*="fallback__"]) {
|
||||
border: solid 1px var(--env-var-color-6);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
border-style: dashed;
|
||||
background-color: var(--env-var-color-8);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
.home-layout > div:has(> [class*="fallback__"]) .background-pattern-svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -33%);
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 0;
|
||||
|
||||
width: 100%;
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -6,12 +6,6 @@
|
||||
.account .MuiSelect-select {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
.account h1.MuiTypography-root,
|
||||
.account p.MuiTypography-root,
|
||||
.account input,
|
||||
.account .MuiSelect-select {
|
||||
color: var(--env-var-color-2);
|
||||
}
|
||||
.account h1.MuiTypography-root {
|
||||
font-weight: 600;
|
||||
}
|
||||
@@ -19,10 +13,6 @@
|
||||
padding: 0;
|
||||
margin-top: 50px;
|
||||
}
|
||||
.account .MuiDivider-root,
|
||||
.account .MuiButtonGroup-root button {
|
||||
border-color: var(--env-var-color-16);
|
||||
}
|
||||
.account button:not(.MuiIconButton-root) {
|
||||
height: 34px;
|
||||
}
|
||||
@@ -43,9 +33,6 @@
|
||||
top: 100%;
|
||||
}
|
||||
|
||||
.account .MuiTable-root .MuiTableBody-root .MuiTableCell-root {
|
||||
color: var(--env-var-color-25);
|
||||
}
|
||||
.account .MuiTableBody-root .MuiTableCell-root {
|
||||
padding: var(--env-var-spacing-1-plus) var(--env-var-spacing-2);
|
||||
}
|
||||
|
||||
@@ -34,18 +34,18 @@ const Account = ({ open = "profile" }) => {
|
||||
return (
|
||||
<Box
|
||||
className="account"
|
||||
px={theme.gap.xl}
|
||||
py={theme.gap.large}
|
||||
px={theme.spacing(20)}
|
||||
py={theme.spacing(12)}
|
||||
border={1}
|
||||
borderColor={theme.palette.otherColors.graishWhite}
|
||||
borderRadius={`${theme.shape.borderRadius}px`}
|
||||
backgroundColor={theme.palette.otherColors.white}
|
||||
borderColor={theme.palette.border.light}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
backgroundColor={theme.palette.background.main}
|
||||
>
|
||||
<TabContext value={tab}>
|
||||
<Box
|
||||
sx={{
|
||||
borderBottom: 1,
|
||||
borderColor: "var(--env-var-color-16)",
|
||||
borderColor: theme.palette.border.light,
|
||||
"& .MuiTabs-root": { height: "fit-content", minHeight: "0" },
|
||||
}}
|
||||
>
|
||||
@@ -56,15 +56,15 @@ const Account = ({ open = "profile" }) => {
|
||||
key={index}
|
||||
value={label.toLowerCase()}
|
||||
sx={{
|
||||
fontSize: "13px",
|
||||
color: theme.palette.secondary.main,
|
||||
fontSize: 13,
|
||||
color: theme.palette.text.tertiary,
|
||||
textTransform: "none",
|
||||
minWidth: "fit-content",
|
||||
minHeight: 0,
|
||||
paddingLeft: "0",
|
||||
paddingY: theme.gap.small,
|
||||
paddingLeft: 0,
|
||||
paddingY: theme.spacing(4),
|
||||
fontWeight: 400,
|
||||
marginRight: theme.gap.ml,
|
||||
marginRight: theme.spacing(8),
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
|
||||
@@ -85,7 +85,21 @@ const CheckEmail = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack className="check-email-page auth" overflow="hidden">
|
||||
<Stack
|
||||
className="check-email-page auth"
|
||||
overflow="hidden"
|
||||
sx={{
|
||||
"& h1": {
|
||||
color: theme.palette.common.main,
|
||||
fontWeight: 600,
|
||||
fontSize: 26,
|
||||
},
|
||||
"& p": {
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.accent,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
className="background-pattern-svg"
|
||||
sx={{ backgroundImage: `url(${background})` }}
|
||||
@@ -93,8 +107,8 @@ const CheckEmail = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
px={theme.gap.large}
|
||||
gap={theme.gap.small}
|
||||
px={theme.spacing(12)}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Logo style={{ borderRadius: theme.shape.borderRadius }} />
|
||||
<Typography sx={{ userSelect: "none" }}>BlueWave Uptime</Typography>
|
||||
@@ -104,31 +118,31 @@ const CheckEmail = () => {
|
||||
maxWidth={600}
|
||||
flex={1}
|
||||
justifyContent="center"
|
||||
px={{ xs: theme.gap.large, lg: theme.gap.xl }}
|
||||
pb={theme.gap.xl}
|
||||
px={{ xs: theme.spacing(12), lg: theme.spacing(20) }}
|
||||
pb={theme.spacing(20)}
|
||||
mx="auto"
|
||||
sx={{
|
||||
"& > .MuiStack-root": {
|
||||
border: 1,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderColor: theme.palette.otherColors.graishWhite,
|
||||
backgroundColor: theme.palette.otherColors.white,
|
||||
borderRadius: theme.spacing(5),
|
||||
borderColor: theme.palette.border.light,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
padding: {
|
||||
xs: theme.gap.large,
|
||||
sm: theme.gap.xl,
|
||||
xs: theme.spacing(12),
|
||||
sm: theme.spacing(20),
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
gap={{ xs: theme.gap.ml, sm: theme.gap.large }}
|
||||
gap={{ xs: theme.spacing(8), sm: theme.spacing(12) }}
|
||||
alignItems="center"
|
||||
textAlign="center"
|
||||
>
|
||||
<Box>
|
||||
<EmailIcon alt="email icon" />
|
||||
<Typography component="h1">Check your email</Typography>
|
||||
<Typography mt={theme.gap.xs}>
|
||||
<Typography mt={theme.spacing(2)}>
|
||||
We sent a password reset link to{" "}
|
||||
<Typography className="email-sent-to" component="span">
|
||||
{email || "username@email.com"}
|
||||
@@ -144,13 +158,13 @@ const CheckEmail = () => {
|
||||
maxWidth: 400,
|
||||
}}
|
||||
/>
|
||||
<Typography sx={{ alignSelf: "center", mb: theme.gap.medium }}>
|
||||
<Typography sx={{ alignSelf: "center", mb: theme.spacing(6) }}>
|
||||
Didn't receive the email?{" "}
|
||||
<Typography
|
||||
component="span"
|
||||
onClick={resendToken}
|
||||
sx={{
|
||||
color: theme.palette.primary.main,
|
||||
color: theme.palette.common.main,
|
||||
userSelect: "none",
|
||||
pointerEvents: disabled ? "none" : "auto",
|
||||
cursor: disabled ? "default" : "pointer",
|
||||
@@ -162,11 +176,12 @@ const CheckEmail = () => {
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Box textAlign="center" p={theme.gap.large}>
|
||||
<Box textAlign="center" p={theme.spacing(12)}>
|
||||
<Typography display="inline-block">Go back to —</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
ml={theme.gap.xs}
|
||||
ml={theme.spacing(2)}
|
||||
color={theme.palette.common.main}
|
||||
onClick={handleNavigate}
|
||||
sx={{ userSelect: "none" }}
|
||||
>
|
||||
|
||||
@@ -87,7 +87,21 @@ const ForgotPassword = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack className="forgot-password-page auth" overflow="hidden">
|
||||
<Stack
|
||||
className="forgot-password-page auth"
|
||||
overflow="hidden"
|
||||
sx={{
|
||||
"& h1": {
|
||||
color: theme.palette.common.main,
|
||||
fontWeight: 600,
|
||||
fontSize: 24,
|
||||
},
|
||||
"& p": {
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.accent,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
className="background-pattern-svg"
|
||||
sx={{ backgroundImage: `url(${background})` }}
|
||||
@@ -95,8 +109,8 @@ const ForgotPassword = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
px={theme.gap.large}
|
||||
gap={theme.gap.small}
|
||||
px={theme.spacing(12)}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Logo style={{ borderRadius: theme.shape.borderRadius }} />
|
||||
<Typography sx={{ userSelect: "none" }}>BlueWave Uptime</Typography>
|
||||
@@ -106,24 +120,24 @@ const ForgotPassword = () => {
|
||||
maxWidth={600}
|
||||
flex={1}
|
||||
justifyContent="center"
|
||||
px={{ xs: theme.gap.large, lg: theme.gap.xl }}
|
||||
pb={theme.gap.xl}
|
||||
px={{ xs: theme.spacing(12), lg: theme.spacing(20) }}
|
||||
pb={theme.spacing(20)}
|
||||
mx="auto"
|
||||
sx={{
|
||||
"& > .MuiStack-root": {
|
||||
border: 1,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderColor: theme.palette.otherColors.graishWhite,
|
||||
backgroundColor: theme.palette.otherColors.white,
|
||||
borderRadius: theme.spacing(5),
|
||||
borderColor: theme.palette.border.light,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
padding: {
|
||||
xs: theme.gap.large,
|
||||
sm: theme.gap.xl,
|
||||
xs: theme.spacing(12),
|
||||
sm: theme.spacing(20),
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
gap={{ xs: theme.gap.ml, sm: theme.gap.large }}
|
||||
gap={{ xs: theme.spacing(8), sm: theme.spacing(12) }}
|
||||
alignItems="center"
|
||||
textAlign="center"
|
||||
>
|
||||
@@ -134,39 +148,45 @@ const ForgotPassword = () => {
|
||||
No worries, we'll send you reset instructions.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box width="100%" textAlign="left">
|
||||
<form noValidate spellCheck={false} onSubmit={handleSubmit}>
|
||||
<Field
|
||||
type="email"
|
||||
id="forgot-password-email-input"
|
||||
label="Email"
|
||||
isRequired={true}
|
||||
placeholder="Enter your email"
|
||||
value={form.email}
|
||||
onChange={handleChange}
|
||||
error={errors.email}
|
||||
/>
|
||||
<ButtonSpinner
|
||||
disabled={errors.email !== undefined}
|
||||
onClick={handleSubmit}
|
||||
isLoading={isLoading}
|
||||
level="primary"
|
||||
label="Send instructions"
|
||||
sx={{
|
||||
width: "100%",
|
||||
fontWeight: 400,
|
||||
mt: theme.gap.mlplus,
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
<Box
|
||||
component="form"
|
||||
width="95%"
|
||||
textAlign="left"
|
||||
noValidate
|
||||
spellCheck={false}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Field
|
||||
type="email"
|
||||
id="forgot-password-email-input"
|
||||
label="Email"
|
||||
isRequired={true}
|
||||
placeholder="Enter your email"
|
||||
value={form.email}
|
||||
onChange={handleChange}
|
||||
error={errors.email}
|
||||
/>
|
||||
<ButtonSpinner
|
||||
disabled={errors.email !== undefined}
|
||||
onClick={handleSubmit}
|
||||
isLoading={isLoading}
|
||||
level="primary"
|
||||
label="Send instructions"
|
||||
sx={{
|
||||
width: "100%",
|
||||
fontWeight: 400,
|
||||
mt: theme.spacing(15),
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Box textAlign="center" p={theme.gap.large}>
|
||||
<Box textAlign="center" p={theme.spacing(12)}>
|
||||
<Typography display="inline-block">Go back to —</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
ml={theme.gap.xs}
|
||||
color={theme.palette.common.main}
|
||||
ml={theme.spacing(2)}
|
||||
onClick={handleNavigate}
|
||||
sx={{ userSelect: "none" }}
|
||||
>
|
||||
|
||||
@@ -30,7 +30,7 @@ const LandingPage = ({ onContinue }) => {
|
||||
return (
|
||||
<>
|
||||
<Stack
|
||||
gap={{ xs: theme.gap.ml, sm: theme.gap.large }}
|
||||
gap={{ xs: theme.spacing(8), sm: theme.spacing(12) }}
|
||||
alignItems="center"
|
||||
textAlign="center"
|
||||
>
|
||||
@@ -47,7 +47,7 @@ const LandingPage = ({ onContinue }) => {
|
||||
sx={{
|
||||
width: "100%",
|
||||
"& svg": {
|
||||
mr: theme.gap.small,
|
||||
mr: theme.spacing(4),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
@@ -64,6 +64,11 @@ const LandingPage = ({ onContinue }) => {
|
||||
"noreferrer"
|
||||
);
|
||||
}}
|
||||
sx={{
|
||||
"&:hover": {
|
||||
color: theme.palette.text.tertiary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Terms of Service
|
||||
</Typography>{" "}
|
||||
@@ -77,6 +82,11 @@ const LandingPage = ({ onContinue }) => {
|
||||
"noreferrer"
|
||||
);
|
||||
}}
|
||||
sx={{
|
||||
"&:hover": {
|
||||
color: theme.palette.text.tertiary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Privacy Policy.
|
||||
</Typography>
|
||||
@@ -114,12 +124,15 @@ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack gap={{ xs: theme.gap.ml, sm: theme.gap.large }} textAlign="center">
|
||||
<Stack
|
||||
gap={{ xs: theme.spacing(8), sm: theme.spacing(12) }}
|
||||
textAlign="center"
|
||||
>
|
||||
<Box>
|
||||
<Typography component="h1">Log In</Typography>
|
||||
<Typography>Enter your email address</Typography>
|
||||
</Box>
|
||||
<Box textAlign="left">
|
||||
<Box textAlign="left" mb={theme.spacing(5)}>
|
||||
<form noValidate spellCheck={false} onSubmit={onSubmit}>
|
||||
<Field
|
||||
type="email"
|
||||
@@ -144,10 +157,10 @@ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
img={<ArrowBackRoundedIcon />}
|
||||
onClick={onBack}
|
||||
sx={{
|
||||
mb: theme.gap.medium,
|
||||
px: theme.gap.ml,
|
||||
mb: theme.spacing(6),
|
||||
px: theme.spacing(8),
|
||||
"& svg.MuiSvgIcon-root": {
|
||||
mr: theme.gap.xs,
|
||||
mr: theme.spacing(2),
|
||||
},
|
||||
}}
|
||||
props={{ tabIndex: -1 }}
|
||||
@@ -204,12 +217,15 @@ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack gap={{ xs: theme.gap.ml, sm: theme.gap.large }} textAlign="center">
|
||||
<Stack
|
||||
gap={{ xs: theme.spacing(8), sm: theme.spacing(12) }}
|
||||
textAlign="center"
|
||||
>
|
||||
<Box>
|
||||
<Typography component="h1">Log In</Typography>
|
||||
<Typography>Enter your password</Typography>
|
||||
</Box>
|
||||
<Box textAlign="left">
|
||||
<Box textAlign="left" mb={theme.spacing(5)}>
|
||||
<form noValidate spellCheck={false} onSubmit={onSubmit}>
|
||||
<Field
|
||||
type="password"
|
||||
@@ -233,10 +249,10 @@ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
img={<ArrowBackRoundedIcon />}
|
||||
onClick={onBack}
|
||||
sx={{
|
||||
mb: theme.gap.medium,
|
||||
px: theme.gap.ml,
|
||||
mb: theme.spacing(6),
|
||||
px: theme.spacing(8),
|
||||
"& svg.MuiSvgIcon-root": {
|
||||
mr: theme.gap.xs,
|
||||
mr: theme.spacing(2),
|
||||
},
|
||||
}}
|
||||
props={{ tabIndex: -1 }}
|
||||
@@ -250,12 +266,17 @@ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
/>
|
||||
</Stack>
|
||||
<Box textAlign="center">
|
||||
<Typography className="forgot-p" display="inline-block">
|
||||
<Typography
|
||||
className="forgot-p"
|
||||
display="inline-block"
|
||||
color={theme.palette.common.main}
|
||||
>
|
||||
Forgot password?
|
||||
</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
ml={theme.gap.xs}
|
||||
color={theme.palette.common.main}
|
||||
ml={theme.spacing(2)}
|
||||
sx={{ userSelect: "none" }}
|
||||
onClick={handleNavigate}
|
||||
>
|
||||
@@ -393,7 +414,21 @@ const Login = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack className="login-page auth" overflow="hidden">
|
||||
<Stack
|
||||
className="login-page auth"
|
||||
overflow="hidden"
|
||||
sx={{
|
||||
"& h1": {
|
||||
color: theme.palette.common.main,
|
||||
fontWeight: 600,
|
||||
fontSize: 30,
|
||||
},
|
||||
"& p": {
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.accent,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
className="background-pattern-svg"
|
||||
sx={{ backgroundImage: `url(${background})` }}
|
||||
@@ -401,8 +436,8 @@ const Login = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
px={theme.gap.large}
|
||||
gap={theme.gap.small}
|
||||
px={theme.spacing(12)}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Logo style={{ borderRadius: theme.shape.borderRadius }} />
|
||||
<Typography sx={{ userSelect: "none" }}>BlueWave Uptime</Typography>
|
||||
@@ -412,18 +447,18 @@ const Login = () => {
|
||||
maxWidth={600}
|
||||
flex={1}
|
||||
justifyContent="center"
|
||||
px={{ xs: theme.gap.large, lg: theme.gap.xl }}
|
||||
pb={theme.gap.xl}
|
||||
px={{ xs: theme.spacing(12), lg: theme.spacing(20) }}
|
||||
pb={theme.spacing(20)}
|
||||
mx="auto"
|
||||
sx={{
|
||||
"& > .MuiStack-root": {
|
||||
border: 1,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderColor: theme.palette.otherColors.graishWhite,
|
||||
backgroundColor: theme.palette.otherColors.white,
|
||||
borderRadius: theme.spacing(5),
|
||||
borderColor: theme.palette.border.light,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
padding: {
|
||||
xs: theme.gap.large,
|
||||
sm: theme.gap.xl,
|
||||
xs: theme.spacing(12),
|
||||
sm: theme.spacing(20),
|
||||
},
|
||||
},
|
||||
}}
|
||||
@@ -450,13 +485,14 @@ const Login = () => {
|
||||
)
|
||||
)}
|
||||
</Stack>
|
||||
<Box textAlign="center" p={theme.gap.large}>
|
||||
<Box textAlign="center" p={theme.spacing(12)}>
|
||||
<Typography display="inline-block">
|
||||
Don't have an account? —
|
||||
</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
ml={theme.gap.xs}
|
||||
color={theme.palette.common.main}
|
||||
ml={theme.spacing(2)}
|
||||
onClick={() => {
|
||||
navigate("/register");
|
||||
}}
|
||||
|
||||
@@ -22,7 +22,21 @@ const NewPasswordConfirmed = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack className="password-confirmed-page auth" overflow="hidden">
|
||||
<Stack
|
||||
className="password-confirmed-page auth"
|
||||
overflow="hidden"
|
||||
sx={{
|
||||
"& h1": {
|
||||
color: theme.palette.common.main,
|
||||
fontWeight: 600,
|
||||
fontSize: 22,
|
||||
},
|
||||
"& p": {
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.accent,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
className="background-pattern-svg"
|
||||
sx={{ backgroundImage: `url(${background})` }}
|
||||
@@ -30,8 +44,8 @@ const NewPasswordConfirmed = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
px={theme.gap.large}
|
||||
gap={theme.gap.small}
|
||||
px={theme.spacing(12)}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Logo style={{ borderRadius: theme.shape.borderRadius }} />
|
||||
<Typography sx={{ userSelect: "none" }}>BlueWave Uptime</Typography>
|
||||
@@ -41,31 +55,31 @@ const NewPasswordConfirmed = () => {
|
||||
maxWidth={600}
|
||||
flex={1}
|
||||
justifyContent="center"
|
||||
px={{ xs: theme.gap.large, lg: theme.gap.xl }}
|
||||
pb={theme.gap.xl}
|
||||
px={{ xs: theme.spacing(12), lg: theme.spacing(20) }}
|
||||
pb={theme.spacing(20)}
|
||||
mx="auto"
|
||||
sx={{
|
||||
"& > .MuiStack-root": {
|
||||
border: 1,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderColor: theme.palette.otherColors.graishWhite,
|
||||
backgroundColor: theme.palette.otherColors.white,
|
||||
borderRadius: theme.spacing(5),
|
||||
borderColor: theme.palette.border.light,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
padding: {
|
||||
xs: theme.gap.large,
|
||||
sm: theme.gap.xl,
|
||||
xs: theme.spacing(12),
|
||||
sm: theme.spacing(20),
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
gap={{ xs: theme.gap.ml, sm: theme.gap.large }}
|
||||
gap={{ xs: theme.spacing(8), sm: theme.spacing(12) }}
|
||||
alignItems="center"
|
||||
textAlign="center"
|
||||
>
|
||||
<Box>
|
||||
<ConfirmIcon alt="password confirm icon" />
|
||||
<Typography component="h1">Password reset</Typography>
|
||||
<Typography mt={theme.gap.xs}>
|
||||
<Typography mt={theme.spacing(2)}>
|
||||
Your password has been successfully reset. Click below to log in
|
||||
magically.
|
||||
</Typography>
|
||||
@@ -81,11 +95,12 @@ const NewPasswordConfirmed = () => {
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Box textAlign="center" p={theme.gap.large}>
|
||||
<Box textAlign="center" p={theme.spacing(12)}>
|
||||
<Typography display="inline-block">Go back to —</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
ml={theme.gap.xs}
|
||||
color={theme.palette.common.main}
|
||||
ml={theme.spacing(2)}
|
||||
onClick={handleNavigate}
|
||||
sx={{ userSelect: "none" }}
|
||||
>
|
||||
|
||||
@@ -33,7 +33,7 @@ const LandingPage = ({ isSuperAdmin, onSignup }) => {
|
||||
return (
|
||||
<>
|
||||
<Stack
|
||||
gap={{ xs: theme.gap.ml, sm: theme.gap.large }}
|
||||
gap={{ xs: theme.spacing(8), sm: theme.spacing(12) }}
|
||||
alignItems="center"
|
||||
textAlign="center"
|
||||
>
|
||||
@@ -53,7 +53,7 @@ const LandingPage = ({ isSuperAdmin, onSignup }) => {
|
||||
sx={{
|
||||
width: "100%",
|
||||
"& svg": {
|
||||
mr: theme.gap.small,
|
||||
mr: theme.spacing(4),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
@@ -70,6 +70,11 @@ const LandingPage = ({ isSuperAdmin, onSignup }) => {
|
||||
"noreferrer"
|
||||
);
|
||||
}}
|
||||
sx={{
|
||||
"&:hover": {
|
||||
color: theme.palette.text.tertiary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Terms of Service
|
||||
</Typography>{" "}
|
||||
@@ -83,6 +88,11 @@ const LandingPage = ({ isSuperAdmin, onSignup }) => {
|
||||
"noreferrer"
|
||||
);
|
||||
}}
|
||||
sx={{
|
||||
"&:hover": {
|
||||
color: theme.palette.text.tertiary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Privacy Policy.
|
||||
</Typography>
|
||||
@@ -121,13 +131,22 @@ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack gap={{ xs: theme.gap.ml, sm: theme.gap.large }} textAlign="center">
|
||||
<Stack
|
||||
gap={{ xs: theme.spacing(8), sm: theme.spacing(12) }}
|
||||
textAlign="center"
|
||||
>
|
||||
<Box>
|
||||
<Typography component="h1">Sign Up</Typography>
|
||||
<Typography>Enter your personal details</Typography>
|
||||
</Box>
|
||||
<Box textAlign="left">
|
||||
<form noValidate spellCheck={false} onSubmit={onSubmit}>
|
||||
<Box
|
||||
component="form"
|
||||
noValidate
|
||||
spellCheck={false}
|
||||
onSubmit={onSubmit}
|
||||
mb={theme.spacing(10)}
|
||||
>
|
||||
<Field
|
||||
id="register-firstname-input"
|
||||
label="Name"
|
||||
@@ -139,8 +158,14 @@ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
error={errors.firstName}
|
||||
ref={inputRef}
|
||||
/>
|
||||
</form>
|
||||
<form noValidate spellCheck={false} onSubmit={onSubmit}>
|
||||
</Box>
|
||||
<Box
|
||||
component="form"
|
||||
noValidate
|
||||
spellCheck={false}
|
||||
onSubmit={onSubmit}
|
||||
mb={theme.spacing(5)}
|
||||
>
|
||||
<Field
|
||||
id="register-lastname-input"
|
||||
label="Surname"
|
||||
@@ -151,7 +176,7 @@ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
onChange={onChange}
|
||||
error={errors.lastName}
|
||||
/>
|
||||
</form>
|
||||
</Box>
|
||||
</Box>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Button
|
||||
@@ -161,9 +186,9 @@ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
img={<ArrowBackRoundedIcon />}
|
||||
onClick={onBack}
|
||||
sx={{
|
||||
px: theme.gap.ml,
|
||||
px: theme.spacing(8),
|
||||
"& svg.MuiSvgIcon-root": {
|
||||
mr: theme.gap.xs,
|
||||
mr: theme.spacing(2),
|
||||
},
|
||||
}}
|
||||
props={{ tabIndex: -1 }}
|
||||
@@ -212,13 +237,22 @@ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack gap={{ xs: theme.gap.ml, sm: theme.gap.large }} textAlign="center">
|
||||
<Stack
|
||||
gap={{ xs: theme.spacing(8), sm: theme.spacing(12) }}
|
||||
textAlign="center"
|
||||
>
|
||||
<Box>
|
||||
<Typography component="h1">Sign Up</Typography>
|
||||
<Typography>Enter your email address</Typography>
|
||||
</Box>
|
||||
<Box textAlign="left">
|
||||
<form noValidate spellCheck={false} onSubmit={onSubmit}>
|
||||
<Box
|
||||
component="form"
|
||||
noValidate
|
||||
spellCheck={false}
|
||||
onSubmit={onSubmit}
|
||||
mb={theme.spacing(5)}
|
||||
>
|
||||
<Field
|
||||
type="email"
|
||||
id="register-email-input"
|
||||
@@ -232,7 +266,7 @@ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
error={errors.email}
|
||||
ref={inputRef}
|
||||
/>
|
||||
</form>
|
||||
</Box>
|
||||
</Box>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Button
|
||||
@@ -242,9 +276,9 @@ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
img={<ArrowBackRoundedIcon />}
|
||||
onClick={onBack}
|
||||
sx={{
|
||||
px: theme.gap.ml,
|
||||
px: theme.spacing(8),
|
||||
"& svg.MuiSvgIcon-root": {
|
||||
mr: theme.gap.xs,
|
||||
mr: theme.spacing(2),
|
||||
},
|
||||
}}
|
||||
props={{ tabIndex: -1 }}
|
||||
@@ -293,13 +327,28 @@ const StepThree = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack gap={{ xs: theme.gap.ml, sm: theme.gap.large }} textAlign="center">
|
||||
<Stack
|
||||
gap={{ xs: theme.spacing(8), sm: theme.spacing(12) }}
|
||||
textAlign="center"
|
||||
>
|
||||
<Box>
|
||||
<Typography component="h1">Sign Up</Typography>
|
||||
<Typography>Create your password</Typography>
|
||||
</Box>
|
||||
<Box textAlign="left">
|
||||
<form noValidate spellCheck={false} onSubmit={onSubmit}>
|
||||
<Box
|
||||
textAlign="left"
|
||||
sx={{
|
||||
"& .input-error": {
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="form"
|
||||
noValidate
|
||||
spellCheck={false}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<Field
|
||||
type="password"
|
||||
id="register-password-input"
|
||||
@@ -312,8 +361,13 @@ const StepThree = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
error={errors.password}
|
||||
ref={inputRef}
|
||||
/>
|
||||
</form>
|
||||
<form noValidate spellCheck={false} onSubmit={onSubmit}>
|
||||
</Box>
|
||||
<Box
|
||||
component="form"
|
||||
noValidate
|
||||
spellCheck={false}
|
||||
onSubmit={onSubmit}
|
||||
>
|
||||
<Field
|
||||
type="password"
|
||||
id="register-confirm-input"
|
||||
@@ -325,10 +379,10 @@ const StepThree = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
onChange={onChange}
|
||||
error={errors.confirm}
|
||||
/>
|
||||
</form>
|
||||
</Box>
|
||||
<Stack
|
||||
gap={theme.gap.small}
|
||||
mb={{ xs: theme.gap.ml, sm: theme.gap.large }}
|
||||
gap={theme.spacing(4)}
|
||||
mb={{ xs: theme.spacing(6), sm: theme.spacing(8) }}
|
||||
>
|
||||
<Check
|
||||
text={
|
||||
@@ -395,9 +449,9 @@ const StepThree = ({ form, errors, onSubmit, onChange, onBack }) => {
|
||||
img={<ArrowBackRoundedIcon />}
|
||||
onClick={onBack}
|
||||
sx={{
|
||||
px: theme.gap.ml,
|
||||
px: theme.spacing(8),
|
||||
"& svg.MuiSvgIcon-root": {
|
||||
mr: theme.gap.xs,
|
||||
mr: theme.spacing(2),
|
||||
},
|
||||
}}
|
||||
props={{ tabIndex: -1 }}
|
||||
@@ -585,7 +639,21 @@ const Register = ({ isSuperAdmin }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack className="register-page auth" overflow="hidden">
|
||||
<Stack
|
||||
className="register-page auth"
|
||||
overflow="hidden"
|
||||
sx={{
|
||||
"& h1": {
|
||||
color: theme.palette.common.main,
|
||||
fontWeight: 600,
|
||||
fontSize: 30,
|
||||
},
|
||||
"& p": {
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.accent,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
className="background-pattern-svg"
|
||||
sx={{ backgroundImage: `url(${background})` }}
|
||||
@@ -593,8 +661,8 @@ const Register = ({ isSuperAdmin }) => {
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
px={theme.gap.large}
|
||||
gap={theme.gap.small}
|
||||
px={theme.spacing(12)}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Logo style={{ borderRadius: theme.shape.borderRadius }} />
|
||||
<Typography sx={{ userSelect: "none" }}>BlueWave Uptime</Typography>
|
||||
@@ -604,18 +672,18 @@ const Register = ({ isSuperAdmin }) => {
|
||||
maxWidth={600}
|
||||
flex={1}
|
||||
justifyContent="center"
|
||||
px={{ xs: theme.gap.large, lg: theme.gap.xl }}
|
||||
pb={theme.gap.xl}
|
||||
px={{ xs: theme.spacing(12), lg: theme.spacing(20) }}
|
||||
pb={theme.spacing(20)}
|
||||
mx="auto"
|
||||
sx={{
|
||||
"& > .MuiStack-root": {
|
||||
border: 1,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderColor: theme.palette.otherColors.graishWhite,
|
||||
backgroundColor: theme.palette.otherColors.white,
|
||||
borderRadius: theme.spacing(5),
|
||||
borderColor: theme.palette.border.light,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
padding: {
|
||||
xs: theme.gap.large,
|
||||
sm: theme.gap.xl,
|
||||
xs: theme.spacing(12),
|
||||
sm: theme.spacing(20),
|
||||
},
|
||||
},
|
||||
}}
|
||||
@@ -653,17 +721,17 @@ const Register = ({ isSuperAdmin }) => {
|
||||
""
|
||||
)}
|
||||
</Stack>
|
||||
<Box textAlign="center" p={theme.gap.large}>
|
||||
<Box textAlign="center" p={theme.spacing(12)}>
|
||||
<Typography display="inline-block">
|
||||
Already have an account? —
|
||||
</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
ml={theme.gap.xs}
|
||||
ml={theme.spacing(2)}
|
||||
onClick={() => {
|
||||
navigate("/login");
|
||||
}}
|
||||
sx={{ userSelect: "none" }}
|
||||
sx={{ userSelect: "none", color: theme.palette.common.main }}
|
||||
>
|
||||
Log In
|
||||
</Typography>
|
||||
|
||||
@@ -104,7 +104,21 @@ const SetNewPassword = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack className="set-new-password-page auth" overflow="hidden">
|
||||
<Stack
|
||||
className="set-new-password-page auth"
|
||||
overflow="hidden"
|
||||
sx={{
|
||||
"& h1": {
|
||||
color: theme.palette.common.main,
|
||||
fontWeight: 600,
|
||||
fontSize: 24,
|
||||
},
|
||||
"& p": {
|
||||
fontSize: 14,
|
||||
color: theme.palette.text.accent,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
className="background-pattern-svg"
|
||||
sx={{ backgroundImage: `url(${background})` }}
|
||||
@@ -112,8 +126,8 @@ const SetNewPassword = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
px={theme.gap.large}
|
||||
gap={theme.gap.small}
|
||||
px={theme.spacing(12)}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
<Logo style={{ borderRadius: theme.shape.borderRadius }} />
|
||||
<Typography sx={{ userSelect: "none" }}>BlueWave Uptime</Typography>
|
||||
@@ -123,24 +137,24 @@ const SetNewPassword = () => {
|
||||
maxWidth={600}
|
||||
flex={1}
|
||||
justifyContent="center"
|
||||
px={{ xs: theme.gap.large, lg: theme.gap.xl }}
|
||||
pb={theme.gap.large}
|
||||
px={{ xs: theme.spacing(12), lg: theme.spacing(20) }}
|
||||
pb={theme.spacing(12)}
|
||||
mx="auto"
|
||||
sx={{
|
||||
"& > .MuiStack-root": {
|
||||
border: 1,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderColor: theme.palette.otherColors.graishWhite,
|
||||
backgroundColor: theme.palette.otherColors.white,
|
||||
borderRadius: theme.spacing(5),
|
||||
borderColor: theme.palette.border.light,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
padding: {
|
||||
xs: theme.gap.large,
|
||||
sm: theme.gap.xl,
|
||||
xs: theme.spacing(12),
|
||||
sm: theme.spacing(20),
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
gap={{ xs: theme.gap.ml, sm: theme.gap.large }}
|
||||
gap={{ xs: theme.spacing(8), sm: theme.spacing(12) }}
|
||||
alignItems="center"
|
||||
textAlign="center"
|
||||
>
|
||||
@@ -151,8 +165,21 @@ const SetNewPassword = () => {
|
||||
Your new password must be different to previously used passwords.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box width="100%" textAlign="left">
|
||||
<form noValidate spellCheck={false} onSubmit={handleSubmit}>
|
||||
<Box
|
||||
width="100%"
|
||||
textAlign="left"
|
||||
sx={{
|
||||
"& .input-error": {
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
component="form"
|
||||
noValidate
|
||||
spellCheck={false}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Field
|
||||
type="password"
|
||||
id="register-password-input"
|
||||
@@ -163,8 +190,13 @@ const SetNewPassword = () => {
|
||||
onChange={handleChange}
|
||||
error={errors.password}
|
||||
/>
|
||||
</form>
|
||||
<form noValidate spellCheck={false} onSubmit={handleSubmit}>
|
||||
</Box>
|
||||
<Box
|
||||
component="form"
|
||||
noValidate
|
||||
spellCheck={false}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
<Field
|
||||
type="password"
|
||||
id="confirm-password-input"
|
||||
@@ -175,8 +207,8 @@ const SetNewPassword = () => {
|
||||
onChange={handleChange}
|
||||
error={errors.confirm}
|
||||
/>
|
||||
</form>
|
||||
<Stack gap={theme.gap.small} mb={theme.gap.large}>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(4)} mb={theme.spacing(12)}>
|
||||
<Check
|
||||
text={
|
||||
<>
|
||||
@@ -244,11 +276,12 @@ const SetNewPassword = () => {
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Box textAlign="center" p={theme.gap.large}>
|
||||
<Box textAlign="center" p={theme.spacing(12)}>
|
||||
<Typography display="inline-block">Go back to —</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
ml={theme.gap.xs}
|
||||
color={theme.palette.common.main}
|
||||
ml={theme.spacing(2)}
|
||||
onClick={() => navigate("/login")}
|
||||
sx={{ userSelect: "none" }}
|
||||
>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
.auth h1 {
|
||||
font-size: var(--env-var-font-size-xlarge);
|
||||
font-weight: 600;
|
||||
color: var(--env-var-color-3);
|
||||
}
|
||||
.auth button:not(.MuiIconButton-root),
|
||||
.auth p,
|
||||
@@ -13,16 +12,8 @@
|
||||
.auth span {
|
||||
font-size: var(--env-var-font-size-medium-plus);
|
||||
}
|
||||
.auth p {
|
||||
color: var(--env-var-color-2-light);
|
||||
}
|
||||
.auth .field h3.MuiTypography-root,
|
||||
.auth .field input {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
|
||||
.auth p + span {
|
||||
color: var(--env-var-color-3);
|
||||
opacity: 0.8;
|
||||
cursor: pointer;
|
||||
transition: opacity 300ms ease-in;
|
||||
@@ -34,7 +25,6 @@
|
||||
transition: all 200ms;
|
||||
}
|
||||
.auth p > span:not(.email-sent-to):hover {
|
||||
color: var(--env-var-color-2);
|
||||
text-underline-offset: 4px;
|
||||
}
|
||||
.auth p + span:hover {
|
||||
@@ -86,11 +76,12 @@
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.forgot-password-page h1,
|
||||
.check-email-page h1,
|
||||
.password-confirmed-page h1,
|
||||
.set-new-password-page h1 {
|
||||
font-size: 22px;
|
||||
.auth .field {
|
||||
position: relative;
|
||||
}
|
||||
.auth .input-error {
|
||||
position: absolute;
|
||||
top: calc(100% - 2px);
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
|
||||
@@ -21,14 +21,16 @@ import { useSelector } from "react-redux";
|
||||
import { networkService } from "../../../main";
|
||||
import { StatusLabel } from "../../../Components/Label";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
import { useTheme } from "@emotion/react";
|
||||
|
||||
const IncidentTable = ({ monitors, selectedMonitor, filter }) => {
|
||||
const theme = useTheme();
|
||||
const { authToken, user } = useSelector((state) => state.auth);
|
||||
const [checks, setChecks] = useState([]);
|
||||
const [checksCount, setChecksCount] = useState(0);
|
||||
const [paginationController, setPaginationController] = useState({
|
||||
page: 0,
|
||||
rowsPerPage: 12,
|
||||
rowsPerPage: 14,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -107,26 +109,31 @@ const IncidentTable = ({ monitors, selectedMonitor, filter }) => {
|
||||
next: ArrowForwardRoundedIcon,
|
||||
}}
|
||||
{...item}
|
||||
sx={{
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
let sharedStyles = {
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
p: theme.spacing(30),
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{checks?.length === 0 && selectedMonitor === "0" ? (
|
||||
<Box>
|
||||
<Typography textAlign="center">No incidents recorded yet.</Typography>
|
||||
<Box sx={{ ...sharedStyles }}>
|
||||
<Typography textAlign="center" color={theme.palette.text.secondary}>
|
||||
No incidents recorded yet.
|
||||
</Typography>
|
||||
</Box>
|
||||
) : checks?.length === 0 ? (
|
||||
<Box>
|
||||
<Typography textAlign="center">
|
||||
<Box sx={{ ...sharedStyles }}>
|
||||
<Typography textAlign="center" color={theme.palette.text.secondary}>
|
||||
The monitor you have selected has no recorded incidents yet.
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
.incidents h1.MuiTypography-root,
|
||||
.incidents p.MuiTypography-root {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.incidents h1.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large);
|
||||
font-weight: 600;
|
||||
@@ -12,19 +8,4 @@
|
||||
}
|
||||
.incidents button.MuiButtonBase-root {
|
||||
height: 34px;
|
||||
border-color: var(--env-var-color-16);
|
||||
}
|
||||
.incidents .MuiPagination-root {
|
||||
background-color: var(--env-var-color-8);
|
||||
}
|
||||
.incidents > .MuiBox-root {
|
||||
background-color: var(--env-var-color-8);
|
||||
border: solid 1px var(--env-var-color-6);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
padding: 60px;
|
||||
}
|
||||
|
||||
/* remove later */
|
||||
.incidents .MuiSelect-select {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ const SkeletonLayout = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Stack direction="row" alignItems="center" gap={theme.gap.medium}>
|
||||
<Stack direction="row" alignItems="center" gap={theme.spacing(6)}>
|
||||
<Skeleton variant="rounded" width={150} height={34} />
|
||||
<Skeleton variant="rounded" width="15%" height={34} />
|
||||
<Skeleton
|
||||
@@ -78,13 +78,21 @@ const Incidents = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack className="incidents" pt={theme.gap.xl} gap={theme.gap.large}>
|
||||
<Stack
|
||||
className="incidents"
|
||||
pt={theme.spacing(21)}
|
||||
gap={theme.spacing(12)}
|
||||
>
|
||||
{loading ? (
|
||||
<SkeletonLayout />
|
||||
) : (
|
||||
<>
|
||||
<Stack direction="row" alignItems="center" gap={theme.gap.medium}>
|
||||
<Typography display="inline-block" component="h1">
|
||||
<Stack direction="row" alignItems="center" gap={theme.spacing(6)}>
|
||||
<Typography
|
||||
display="inline-block"
|
||||
component="h1"
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
Incidents for
|
||||
</Typography>
|
||||
<Select
|
||||
@@ -93,15 +101,27 @@ const Incidents = () => {
|
||||
value={selectedMonitor}
|
||||
onChange={handleSelect}
|
||||
items={Object.values(monitors)}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.background.main,
|
||||
}}
|
||||
/>
|
||||
<ButtonGroup sx={{ ml: "auto" }}>
|
||||
<ButtonGroup
|
||||
sx={{
|
||||
ml: "auto",
|
||||
"& .MuiButtonBase-root, & .MuiButtonBase-root:hover": {
|
||||
borderColor: theme.palette.border.light,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
level="secondary"
|
||||
label="All"
|
||||
onClick={() => setFilter("all")}
|
||||
sx={{
|
||||
backgroundColor:
|
||||
filter === "all" && theme.palette.otherColors.fillGray,
|
||||
filter === "all"
|
||||
? theme.palette.background.fill
|
||||
: theme.palette.background.main,
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
@@ -110,7 +130,9 @@ const Incidents = () => {
|
||||
onClick={() => setFilter("down")}
|
||||
sx={{
|
||||
backgroundColor:
|
||||
filter === "down" && theme.palette.otherColors.fillGray,
|
||||
filter === "down"
|
||||
? theme.palette.background.fill
|
||||
: theme.palette.background.main,
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
@@ -119,7 +141,9 @@ const Incidents = () => {
|
||||
onClick={() => setFilter("resolve")}
|
||||
sx={{
|
||||
backgroundColor:
|
||||
filter === "resolve" && theme.palette.otherColors.fillGray,
|
||||
filter === "resolve"
|
||||
? theme.palette.background.fill
|
||||
: theme.palette.background.main,
|
||||
}}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
.integrations h1.MuiTypography-root,
|
||||
.integrations p.MuiTypography-root {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.integrations h1.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large);
|
||||
font-weight: 600;
|
||||
@@ -12,9 +8,3 @@
|
||||
.integrations button {
|
||||
height: var(--env-var-height-2);
|
||||
}
|
||||
|
||||
.integrations .MuiGrid-item > .MuiStack-root {
|
||||
border: solid 1px var(--env-var-color-6);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
background-color: var(--env-var-color-8);
|
||||
}
|
||||
|
||||
@@ -25,13 +25,17 @@ const IntegrationsComponent = ({ icon, header, info, onClick }) => {
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
gap={theme.gap.large}
|
||||
p={theme.gap.ml}
|
||||
pl={theme.gap.large}
|
||||
gap={theme.spacing(12)}
|
||||
p={theme.spacing(8)}
|
||||
pl={theme.spacing(12)}
|
||||
height="100%"
|
||||
border={1}
|
||||
borderColor={theme.palette.border.light}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
backgroundColor={theme.palette.background.main}
|
||||
>
|
||||
{icon}
|
||||
<Stack gap={theme.gap.xs} flex={1}>
|
||||
<Stack gap={theme.spacing(2)} flex={1}>
|
||||
<Typography component="h1">{header}</Typography>
|
||||
<Typography
|
||||
sx={{
|
||||
@@ -108,12 +112,21 @@ const Integrations = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<Stack className="integrations" pt={theme.gap.xl} gap={theme.gap.xs}>
|
||||
<Stack
|
||||
className="integrations"
|
||||
pt={theme.spacing(20)}
|
||||
gap={theme.spacing(2)}
|
||||
sx={{
|
||||
"& h1, & p": {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography component="h1">Integrations</Typography>
|
||||
<Typography mb={theme.gap.large}>
|
||||
<Typography mb={theme.spacing(12)}>
|
||||
Connect BlueWave Uptime to your favorite service.
|
||||
</Typography>
|
||||
<Grid container spacing={theme.gap.large}>
|
||||
<Grid container spacing={theme.spacing(12)}>
|
||||
{integrations.map((integration, index) => (
|
||||
<IntegrationsComponent
|
||||
key={index}
|
||||
|
||||
@@ -13,6 +13,7 @@ import Field from "../../../Components/Inputs/Field";
|
||||
import { maintenanceWindowValidation } from "../../../Validation/validation";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { useTheme } from "@emotion/react";
|
||||
|
||||
const directory = {
|
||||
title: "Create a maintenance window",
|
||||
@@ -56,6 +57,8 @@ const durationOptions = [
|
||||
];
|
||||
|
||||
const CreateNewMaintenanceWindow = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
const [values, setValues] = useState({
|
||||
repeat: 1,
|
||||
date: dayjs(),
|
||||
@@ -159,7 +162,11 @@ const CreateNewMaintenanceWindow = () => {
|
||||
{
|
||||
title: "Duration",
|
||||
component: (
|
||||
<Stack className="duration-config" gap={2} direction="row">
|
||||
<Stack
|
||||
className="duration-config"
|
||||
gap={theme.spacing(5)}
|
||||
direction="row"
|
||||
>
|
||||
<Field
|
||||
id="duration-value"
|
||||
placeholder="60"
|
||||
@@ -195,7 +202,7 @@ const CreateNewMaintenanceWindow = () => {
|
||||
<Stack
|
||||
className="add-monitors-fields"
|
||||
sx={{ width: "60%", maxWidth: "380px" }}
|
||||
gap={2}
|
||||
gap={theme.spacing(5)}
|
||||
>
|
||||
<Field
|
||||
id="add-monitors"
|
||||
@@ -208,7 +215,7 @@ const CreateNewMaintenanceWindow = () => {
|
||||
sx={{
|
||||
width: "fit-content",
|
||||
fontSize: "var(--env-var-font-size-small)",
|
||||
borderBottom: "1px dashed var(--env-var-color-3)",
|
||||
borderBottom: `1px dashed ${theme.palette.common.main}`,
|
||||
paddingBottom: "4px",
|
||||
}}
|
||||
>
|
||||
@@ -221,15 +228,15 @@ const CreateNewMaintenanceWindow = () => {
|
||||
|
||||
return (
|
||||
<div className="create-maintenance-window">
|
||||
<Stack gap={3}>
|
||||
<Stack gap={theme.spacing(10)}>
|
||||
<Button
|
||||
id="btn-back"
|
||||
sx={{
|
||||
width: "100px",
|
||||
height: "30px",
|
||||
gap: "10px",
|
||||
backgroundColor: "var(--env-var-color-32)",
|
||||
color: "var(--env-var-color-5)",
|
||||
backgroundColor: theme.palette.background.fill,
|
||||
color: theme.palette.text.secondary,
|
||||
}}
|
||||
label="Back"
|
||||
level="tertiary"
|
||||
@@ -239,8 +246,8 @@ const CreateNewMaintenanceWindow = () => {
|
||||
<Typography
|
||||
sx={{
|
||||
fontSize: "var(--env-var-font-size-large)",
|
||||
fontWeight: "600",
|
||||
color: "var(--env-var-color-5)",
|
||||
fontWeight: 600,
|
||||
color: theme.palette.text.secondary,
|
||||
}}
|
||||
>
|
||||
{directory.title}
|
||||
@@ -252,14 +259,14 @@ const CreateNewMaintenanceWindow = () => {
|
||||
</Box>
|
||||
<Stack
|
||||
className="maintenance-options"
|
||||
gap={5}
|
||||
paddingY={4}
|
||||
paddingX={8}
|
||||
paddingBottom={10}
|
||||
gap={theme.spacing(20)}
|
||||
paddingY={theme.spacing(15)}
|
||||
paddingX={theme.spacing(20)}
|
||||
sx={{
|
||||
border: "1px solid var(--env-var-color-16)",
|
||||
borderRadius: "var(--env-var-radius-1)",
|
||||
backgroundColor: "var(--env-var-color-0)",
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
}}
|
||||
>
|
||||
{configOptions.map((item, index) => (
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
}
|
||||
|
||||
.maintenance-title {
|
||||
color: var(--env-var-color-2);
|
||||
color: var(--secondary-color-light);
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-top: 40px;
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
.checklist-item {
|
||||
font-size: 13px;
|
||||
color: var(--env-var-color-2);
|
||||
color: var(--secondary-color-light);
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
import { Box } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import Fallback from "../../Components/Fallback";
|
||||
import "./index.css";
|
||||
|
||||
const Maintenance = () => {
|
||||
const Maintenance = ({ isAdmin }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<div className="maintenance">
|
||||
<Box
|
||||
className="maintenance"
|
||||
sx={{
|
||||
':has(> [class*="fallback__"])': {
|
||||
position: "relative",
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderStyle: "dashed",
|
||||
backgroundColor: theme.palette.background.main,
|
||||
overflow: "hidden",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Fallback
|
||||
title="maintenance window"
|
||||
checks={[
|
||||
@@ -12,8 +29,9 @@ const Maintenance = () => {
|
||||
"Stop sending alerts in maintenance windows",
|
||||
]}
|
||||
link="/maintenance/create"
|
||||
isAdmin={isAdmin}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,52 +1,17 @@
|
||||
.configure-monitor h1.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large-plus);
|
||||
color: var(--env-var-color-1);
|
||||
font-weight: 600;
|
||||
}
|
||||
.configure-monitor h2.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large);
|
||||
font-weight: 600;
|
||||
}
|
||||
.configure-monitor h2.MuiTypography-root,
|
||||
.configure-monitor .config-box p.MuiTypography-root,
|
||||
.configure-monitor .MuiSelect-select {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.configure-monitor .MuiModal-root h2.MuiTypography-root {
|
||||
color: var(--env-var-color-1);
|
||||
}
|
||||
.configure-monitor p.MuiTypography-root,
|
||||
.configure-monitor .MuiSelect-select {
|
||||
.configure-monitor p.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
.configure-monitor h6.MuiTypography-root {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.configure-monitor h6.MuiTypography-root,
|
||||
.configure-monitor h1.MuiTypography-root + span.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
}
|
||||
.configure-monitor button.MuiButtonBase-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
.configure-monitor button.MuiButtonBase-root {
|
||||
height: var(--env-var-height-2);
|
||||
}
|
||||
.configure-monitor .config-box {
|
||||
border: 1px solid var(--env-var-color-16);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
background-color: var(--env-var-color-8);
|
||||
}
|
||||
.configure-monitor .config-box > .MuiBox-root,
|
||||
.configure-monitor .config-box > .MuiStack-root {
|
||||
flex: 1;
|
||||
padding: 24px 30px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
.configure-monitor .config-box > *:first-child {
|
||||
flex: 0.7;
|
||||
border-right: solid 1px var(--env-var-color-16);
|
||||
}
|
||||
|
||||
.configure-monitor .MuiStack-root:has(span.MuiTypography-root.input-error) {
|
||||
position: relative;
|
||||
}
|
||||
@@ -54,25 +19,3 @@
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
}
|
||||
.configure-monitor .MuiInputBase-root:has(> .Mui-disabled) {
|
||||
background-color: var(--env-var-color-13);
|
||||
}
|
||||
|
||||
.MuiInputBase-root:has(#monitor-interval) {
|
||||
height: 34px;
|
||||
width: 100%;
|
||||
}
|
||||
#monitor-interval {
|
||||
padding: 0 10px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.MuiInputBase-root:not(.Mui-focused):has(#monitor-interval):hover fieldset {
|
||||
border-color: var(--env-var-color-29);
|
||||
}
|
||||
|
||||
body:has(.configure-monitor) .select-dropdown .MuiMenuItem-root,
|
||||
.configure-monitor .select-wrapper .select-component > .MuiSelect-select {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Box, Modal, Stack, Typography } from "@mui/material";
|
||||
import { monitorValidation } from "../../../Validation/validation";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
import { ConfigBox } from "../styled";
|
||||
import {
|
||||
updateUptimeMonitor,
|
||||
pauseUptimeMonitor,
|
||||
@@ -189,7 +190,7 @@ const Configure = () => {
|
||||
const protocol = parsedUrl?.protocol?.replace(":", "") || "";
|
||||
|
||||
return (
|
||||
<Stack className="configure-monitor" gap={theme.gap.large}>
|
||||
<Stack className="configure-monitor" gap={theme.spacing(12)}>
|
||||
{Object.keys(monitor).length === 0 ? (
|
||||
<SkeletonLayout />
|
||||
) : (
|
||||
@@ -205,20 +206,25 @@ const Configure = () => {
|
||||
component="form"
|
||||
noValidate
|
||||
spellCheck="false"
|
||||
gap={theme.gap.large}
|
||||
gap={theme.spacing(12)}
|
||||
flex={1}
|
||||
>
|
||||
<Stack direction="row" gap={theme.gap.xs}>
|
||||
<Stack direction="row" gap={theme.spacing(2)}>
|
||||
<PulseDot
|
||||
color={
|
||||
monitor?.status
|
||||
? theme.label.up.dotColor
|
||||
: theme.label.down.dotColor
|
||||
? theme.palette.success.main
|
||||
: theme.palette.error.main
|
||||
}
|
||||
/>
|
||||
<Box>
|
||||
{parsedUrl?.host ? (
|
||||
<Typography component="h1" mb={theme.gap.xs} lineHeight={1}>
|
||||
<Typography
|
||||
component="h1"
|
||||
mb={theme.spacing(2)}
|
||||
lineHeight={1}
|
||||
color={theme.palette.text.primary}
|
||||
>
|
||||
{parsedUrl.host || "..."}
|
||||
</Typography>
|
||||
) : (
|
||||
@@ -226,11 +232,11 @@ const Configure = () => {
|
||||
)}
|
||||
<Typography
|
||||
component="span"
|
||||
lineHeight={theme.gap.large}
|
||||
lineHeight={theme.spacing(12)}
|
||||
sx={{
|
||||
color: monitor?.status
|
||||
? "var(--env-var-color-17)"
|
||||
: "var(--env-var-color-24)",
|
||||
? theme.palette.success.main
|
||||
: theme.palette.error.text,
|
||||
}}
|
||||
>
|
||||
Your site is {monitor?.status ? "up" : "down"}.
|
||||
@@ -249,12 +255,12 @@ const Configure = () => {
|
||||
animate="rotate180"
|
||||
img={<PauseCircleOutlineIcon />}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.otherColors.fillGray,
|
||||
pl: theme.gap.small,
|
||||
pr: theme.gap.medium,
|
||||
mr: theme.gap.medium,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
pl: theme.spacing(4),
|
||||
pr: theme.spacing(6),
|
||||
mr: theme.spacing(6),
|
||||
"& svg": {
|
||||
mr: theme.gap.xs,
|
||||
mr: theme.spacing(2),
|
||||
},
|
||||
}}
|
||||
onClick={handlePause}
|
||||
@@ -265,25 +271,21 @@ const Configure = () => {
|
||||
label="Remove"
|
||||
sx={{
|
||||
boxShadow: "none",
|
||||
px: theme.gap.ml,
|
||||
px: theme.spacing(8),
|
||||
}}
|
||||
onClick={() => setIsOpen(true)}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Stack
|
||||
className="config-box"
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">General settings</Typography>
|
||||
<Typography component="p" sx={{ mt: theme.gap.xs }}>
|
||||
<Typography component="p">
|
||||
Here you can select the URL of the host, together with the
|
||||
type of monitor.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.gap.xl}>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
<Field
|
||||
type={monitor?.type === "http" ? "url" : "text"}
|
||||
https={protocol === "https"}
|
||||
@@ -305,20 +307,16 @@ const Configure = () => {
|
||||
error={errors["name"]}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack
|
||||
className="config-box"
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">Incident notifications</Typography>
|
||||
<Typography component="p" mt={theme.gap.xs}>
|
||||
<Typography component="p">
|
||||
When there is an incident, notify users.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.gap.medium}>
|
||||
<Typography component="p" mt={theme.gap.small}>
|
||||
<Stack gap={theme.spacing(6)}>
|
||||
<Typography component="p">
|
||||
When there is a new incident,
|
||||
</Typography>
|
||||
<Checkbox
|
||||
@@ -351,7 +349,7 @@ const Configure = () => {
|
||||
{monitor?.notifications?.some(
|
||||
(notification) => notification.type === "emails"
|
||||
) ? (
|
||||
<Box mx={`calc(${theme.gap.ml} * 2)`}>
|
||||
<Box mx={theme.spacing(16)}>
|
||||
<Field
|
||||
id="notify-email-list"
|
||||
type="text"
|
||||
@@ -359,7 +357,7 @@ const Configure = () => {
|
||||
value=""
|
||||
onChange={() => logger.warn("disabled")}
|
||||
/>
|
||||
<Typography mt={theme.gap.small}>
|
||||
<Typography mt={theme.spacing(4)}>
|
||||
You can separate multiple emails with a comma
|
||||
</Typography>
|
||||
</Box>
|
||||
@@ -367,16 +365,12 @@ const Configure = () => {
|
||||
""
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack
|
||||
className="config-box"
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">Advanced settings</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.gap.xl}>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
<Select
|
||||
id="monitor-interval-configure"
|
||||
label="Check frequency"
|
||||
@@ -385,13 +379,13 @@ const Configure = () => {
|
||||
items={frequencies}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
<Stack direction="row" justifyContent="flex-end" mt="auto">
|
||||
<ButtonSpinner
|
||||
isLoading={isLoading}
|
||||
level="primary"
|
||||
label="Save"
|
||||
sx={{ px: theme.gap.large }}
|
||||
sx={{ px: theme.spacing(12) }}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
</Stack>
|
||||
@@ -406,33 +400,43 @@ const Configure = () => {
|
||||
disablePortal
|
||||
>
|
||||
<Stack
|
||||
gap={theme.gap.xs}
|
||||
gap={theme.spacing(2)}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 400,
|
||||
bgcolor: "white",
|
||||
border: "solid 1px #f2f2f2",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
bgcolor: theme.palette.background.main,
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
boxShadow: 24,
|
||||
p: "30px",
|
||||
p: theme.spacing(15),
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography id="modal-delete-monitor" component="h2">
|
||||
<Typography
|
||||
id="modal-delete-monitor"
|
||||
component="h2"
|
||||
fontSize={16}
|
||||
color={theme.palette.text.primary}
|
||||
fontWeight={600}
|
||||
>
|
||||
Do you really want to delete this monitor?
|
||||
</Typography>
|
||||
<Typography id="delete-monitor-confirmation">
|
||||
<Typography
|
||||
id="delete-monitor-confirmation"
|
||||
color={theme.palette.text.tertiary}
|
||||
>
|
||||
Once deleted, this monitor cannot be retrieved.
|
||||
</Typography>
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.gap.small}
|
||||
mt={theme.gap.large}
|
||||
gap={theme.spacing(4)}
|
||||
mt={theme.spacing(12)}
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<Button
|
||||
|
||||
@@ -1,23 +1,11 @@
|
||||
.create-monitor h1.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large-plus);
|
||||
color: var(--env-var-color-1);
|
||||
font-weight: 500;
|
||||
}
|
||||
.create-monitor h2.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large);
|
||||
font-weight: 600;
|
||||
}
|
||||
.create-monitor p.MuiTypography-root,
|
||||
.create-monitor button.MuiButtonBase-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
.create-monitor h2.MuiTypography-root {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.create-monitor h6.MuiTypography-root,
|
||||
.create-monitor p.MuiTypography-root {
|
||||
color: var(--env-var-color-2);
|
||||
}
|
||||
.create-monitor h6.MuiTypography-root,
|
||||
.create-monitor .MuiBox-root .field + p.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
@@ -26,11 +14,11 @@
|
||||
.create-monitor button.MuiButtonBase-root {
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.create-monitor .error-container {
|
||||
position: relative;
|
||||
}
|
||||
.create-monitor .error-container p.MuiTypography-root.input-error {
|
||||
color: var(--env-var-color-24);
|
||||
opacity: 0.8;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -42,30 +30,9 @@
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
}
|
||||
.create-monitor .config-box {
|
||||
flex-direction: row;
|
||||
border: 1px solid var(--env-var-color-16);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
background-color: var(--env-var-color-8);
|
||||
}
|
||||
.create-monitor .config-box > .MuiBox-root,
|
||||
.create-monitor .config-box > .MuiStack-root {
|
||||
flex: 1;
|
||||
padding: 24px 30px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
.create-monitor .config-box > *:first-child {
|
||||
flex: 0.7;
|
||||
border-right: solid 1px var(--env-var-color-16);
|
||||
}
|
||||
|
||||
.create-monitor .MuiStack-root .MuiButtonGroup-root button {
|
||||
font-size: var(--env-var-font-size-small);
|
||||
height: 28px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
body:has(.create-monitor) .select-dropdown .MuiMenuItem-root,
|
||||
.create-monitor .select-wrapper .select-component > .MuiSelect-select {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import "./index.css";
|
||||
import { useState } from "react";
|
||||
import RadioButton from "../../../Components/RadioButton";
|
||||
import Button from "../../../Components/Button";
|
||||
import { Box, ButtonGroup, Stack, Typography } from "@mui/material";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { monitorValidation } from "../../../Validation/validation";
|
||||
import { createUptimeMonitor } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
import { ConfigBox } from "../styled";
|
||||
import Radio from "../../../Components/Inputs/Radio";
|
||||
import Button from "../../../Components/Button";
|
||||
import Field from "../../../Components/Inputs/Field";
|
||||
import Select from "../../../Components/Inputs/Select";
|
||||
import Checkbox from "../../../Components/Inputs/Checkbox";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import { logger } from "../../../Utils/Logger";
|
||||
import "./index.css";
|
||||
|
||||
const CreateMonitor = () => {
|
||||
const MS_PER_MINUTE = 60000;
|
||||
@@ -33,7 +34,7 @@ const CreateMonitor = () => {
|
||||
const [monitor, setMonitor] = useState({
|
||||
url: "",
|
||||
name: "",
|
||||
type: "",
|
||||
type: "http",
|
||||
notifications: [],
|
||||
interval: 1,
|
||||
});
|
||||
@@ -144,7 +145,14 @@ const CreateMonitor = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<Box className="create-monitor">
|
||||
<Box
|
||||
className="create-monitor"
|
||||
sx={{
|
||||
"& h1": {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Breadcrumbs
|
||||
list={[
|
||||
{ name: "monitors", path: "/monitors" },
|
||||
@@ -157,14 +165,14 @@ const CreateMonitor = () => {
|
||||
onSubmit={handleCreateMonitor}
|
||||
noValidate
|
||||
spellCheck="false"
|
||||
gap={theme.gap.large}
|
||||
mt={theme.gap.medium}
|
||||
gap={theme.spacing(12)}
|
||||
mt={theme.spacing(6)}
|
||||
>
|
||||
<Typography component="h1">
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
color={theme.palette.otherColors.bluishGray}
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
Create your{" "}
|
||||
</Typography>
|
||||
@@ -172,15 +180,15 @@ const CreateMonitor = () => {
|
||||
monitor
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Stack className="config-box">
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">General settings</Typography>
|
||||
<Typography component="p" mt={theme.gap.xs}>
|
||||
<Typography component="p">
|
||||
Here you can select the URL of the host, together with the type of
|
||||
monitor.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.gap.xl}>
|
||||
<Stack gap={theme.spacing(15)}>
|
||||
<Field
|
||||
type={monitor.type === "http" ? "url" : "text"}
|
||||
id="monitor-url"
|
||||
@@ -202,17 +210,17 @@ const CreateMonitor = () => {
|
||||
error={errors["name"]}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack className="config-box">
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">Checks to perform</Typography>
|
||||
<Typography component="p" mt={theme.gap.xs}>
|
||||
<Typography component="p">
|
||||
You can always add or remove checks after adding your site.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.gap.large}>
|
||||
<Stack gap={theme.gap.medium}>
|
||||
<RadioButton
|
||||
<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."
|
||||
@@ -228,8 +236,11 @@ const CreateMonitor = () => {
|
||||
label="HTTPS"
|
||||
onClick={() => setHttps(true)}
|
||||
sx={{
|
||||
backgroundColor:
|
||||
https && theme.palette.otherColors.fillGray,
|
||||
backgroundColor: https && theme.palette.background.fill,
|
||||
borderColor: theme.palette.border.dark,
|
||||
"&:hover": {
|
||||
borderColor: theme.palette.border.dark,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
@@ -237,8 +248,11 @@ const CreateMonitor = () => {
|
||||
label="HTTP"
|
||||
onClick={() => setHttps(false)}
|
||||
sx={{
|
||||
backgroundColor:
|
||||
!https && theme.palette.otherColors.fillGray,
|
||||
backgroundColor: !https && theme.palette.background.fill,
|
||||
borderColor: theme.palette.border.dark,
|
||||
"&:hover": {
|
||||
borderColor: theme.palette.border.dark,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
@@ -246,7 +260,7 @@ const CreateMonitor = () => {
|
||||
""
|
||||
)}
|
||||
</Stack>
|
||||
<RadioButton
|
||||
<Radio
|
||||
id="monitor-checks-ping"
|
||||
title="Ping monitoring"
|
||||
desc="Check whether your server is available or not."
|
||||
@@ -257,7 +271,11 @@ const CreateMonitor = () => {
|
||||
/>
|
||||
{errors["type"] ? (
|
||||
<Box className="error-container">
|
||||
<Typography component="p" className="input-error">
|
||||
<Typography
|
||||
component="p"
|
||||
className="input-error"
|
||||
color={theme.palette.error.text}
|
||||
>
|
||||
{errors["type"]}
|
||||
</Typography>
|
||||
</Box>
|
||||
@@ -265,15 +283,15 @@ const CreateMonitor = () => {
|
||||
""
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack className="config-box">
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">Incident notifications</Typography>
|
||||
<Typography component="p" mt={theme.gap.xs}>
|
||||
<Typography component="p">
|
||||
When there is an incident, notify users.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.gap.medium}>
|
||||
<Stack gap={theme.spacing(6)}>
|
||||
<Typography component="p">When there is a new incident,</Typography>
|
||||
<Checkbox
|
||||
id="notify-sms"
|
||||
@@ -303,7 +321,7 @@ const CreateMonitor = () => {
|
||||
{monitor.notifications.some(
|
||||
(notification) => notification.type === "emails"
|
||||
) ? (
|
||||
<Box mx={`calc(${theme.gap.ml} * 2)`}>
|
||||
<Box mx={theme.spacing(16)}>
|
||||
<Field
|
||||
id="notify-email-list"
|
||||
type="text"
|
||||
@@ -311,7 +329,7 @@ const CreateMonitor = () => {
|
||||
value=""
|
||||
onChange={() => logger.warn("disabled")}
|
||||
/>
|
||||
<Typography mt={theme.gap.small}>
|
||||
<Typography mt={theme.spacing(4)}>
|
||||
You can separate multiple emails with a comma
|
||||
</Typography>
|
||||
</Box>
|
||||
@@ -319,12 +337,12 @@ const CreateMonitor = () => {
|
||||
""
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack className="config-box">
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h2">Advanced settings</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.gap.large}>
|
||||
<Stack gap={theme.spacing(12)}>
|
||||
<Select
|
||||
id="monitor-interval"
|
||||
label="Check frequency"
|
||||
@@ -332,73 +350,8 @@ const CreateMonitor = () => {
|
||||
onChange={(event) => handleChange(event, "interval")}
|
||||
items={frequencies}
|
||||
/>
|
||||
{/* TODO */}
|
||||
{/* <FlexibileTextField
|
||||
id="monitor-settings-retries"
|
||||
title="Maximum retries before the service is marked as down"
|
||||
type="number"
|
||||
value={advancedSettings.retries}
|
||||
onChange={(event) =>
|
||||
handleChange(event, "retries", setAdvancedSettings)
|
||||
}
|
||||
/>
|
||||
<FlexibileTextField
|
||||
id="monitor-settings-codes"
|
||||
title="Accepted status codes"
|
||||
type="number"
|
||||
value={advancedSettings.codes}
|
||||
onChange={(event) =>
|
||||
handleChange(event, "codes", setAdvancedSettings)
|
||||
}
|
||||
/>
|
||||
<FlexibileTextField
|
||||
id="monitor-settings-redirects"
|
||||
title="Maximum redirects"
|
||||
type="number"
|
||||
value={advancedSettings.redirects}
|
||||
onChange={(event) =>
|
||||
handleChange(event, "redirects", setAdvancedSettings)
|
||||
}
|
||||
/> */}
|
||||
</Stack>
|
||||
</Stack>
|
||||
{/* TODO */}
|
||||
{/*
|
||||
<ConfigBox
|
||||
leftLayout={
|
||||
<div className="config-box-desc">
|
||||
<div className="config-box-desc-title">Proxy settings</div>
|
||||
</div>
|
||||
}
|
||||
rightLayout={
|
||||
<div className="proxy-setting-config">
|
||||
<CustomizableCheckBox
|
||||
id="monitor-proxy-enable"
|
||||
title="Enable proxy"
|
||||
isChecked={proxy.enabled}
|
||||
handleChange={() => handleCheck("enabled", setProxy)}
|
||||
/>
|
||||
<FlexibileTextField
|
||||
id="monitor-proxy-protocol"
|
||||
title="Proxy protocol"
|
||||
value={proxy.protocol}
|
||||
onChange={(event) => handleChange(event, "protocol", setProxy)}
|
||||
/>
|
||||
<FlexibileTextField
|
||||
id="monitor-proxy-address"
|
||||
title="Proxy address"
|
||||
value={proxy.address}
|
||||
onChange={(event) => handleChange(event, "address", setProxy)}
|
||||
/>
|
||||
<FlexibileTextField
|
||||
id="monitor-proxy-port"
|
||||
title="Proxy port"
|
||||
value={proxy.proxy_port}
|
||||
onChange={(event) => handleChange(event, "proxy_port", setProxy)}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
/> */}
|
||||
</ConfigBox>
|
||||
<Stack direction="row" justifyContent="flex-end">
|
||||
<Button
|
||||
id="create-monitor-btn"
|
||||
|
||||
@@ -18,8 +18,10 @@ import { StatusLabel } from "../../../../Components/Label";
|
||||
import ArrowBackRoundedIcon from "@mui/icons-material/ArrowBackRounded";
|
||||
import ArrowForwardRoundedIcon from "@mui/icons-material/ArrowForwardRounded";
|
||||
import { logger } from "../../../../Utils/Logger";
|
||||
import { useTheme } from "@emotion/react";
|
||||
|
||||
const PaginationTable = ({ monitorId, dateRange }) => {
|
||||
const theme = useTheme();
|
||||
const { authToken } = useSelector((state) => state.auth);
|
||||
const [checks, setChecks] = useState([]);
|
||||
const [checksCount, setChecksCount] = useState(0);
|
||||
@@ -85,15 +87,6 @@ const PaginationTable = ({ monitorId, dateRange }) => {
|
||||
next: ArrowForwardRoundedIcon,
|
||||
}}
|
||||
{...item}
|
||||
sx={{
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
"& .MuiTouchRipple-root": {
|
||||
pointerEvents: "none",
|
||||
display: "none",
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -1,25 +1,13 @@
|
||||
.monitor-details h1.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large-plus);
|
||||
color: var(--env-var-color-1);
|
||||
font-weight: 600;
|
||||
}
|
||||
.monitor-details h2.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large);
|
||||
}
|
||||
.monitor-details h2.MuiTypography-root{
|
||||
.monitor-details h2.MuiTypography-root {
|
||||
font-weight: 600;
|
||||
}
|
||||
.monitor-details h2.MuiTypography-root,
|
||||
.monitor-details h4.MuiTypography-root {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.monitor-details h6.MuiTypography-root {
|
||||
color: var(--env-var-color-3);
|
||||
}
|
||||
.monitor-details button.MuiButtonBase-root,
|
||||
.monitor-details h4.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
.monitor-details button.MuiButtonBase-root {
|
||||
height: var(--env-var-height-2);
|
||||
line-height: 1;
|
||||
@@ -28,28 +16,3 @@
|
||||
.monitor-details p.MuiTypography-root span.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
}
|
||||
.monitor-details p.MuiTypography-root {
|
||||
color: var(--env-var-color-2);
|
||||
}
|
||||
|
||||
.monitor-details .stat-box {
|
||||
flex: 1 20%;
|
||||
min-width: 100px;
|
||||
border: solid 1px;
|
||||
border-radius: var(--env-var-radius-1);
|
||||
padding: var(--env-var-spacing-1) 16px;
|
||||
background-color: var(--env-var-color-8);
|
||||
}
|
||||
.monitor-details h6.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium-plus);
|
||||
}
|
||||
.monitor-details h6.MuiTypography-root span.MuiTypography-root {
|
||||
font-style: italic;
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-5);
|
||||
opacity: 0.7;
|
||||
}
|
||||
.monitor-details button.MuiButtonBase-root,
|
||||
.monitor-details .stat-box {
|
||||
border-color: var(--env-var-color-16);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import { Box, Skeleton, Stack, Typography, useTheme } from "@mui/material";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
@@ -19,12 +19,41 @@ import PulseDot from "../../../Components/Animated/PulseDot";
|
||||
import "./index.css";
|
||||
|
||||
const StatBox = ({ title, value }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Box className="stat-box">
|
||||
<Typography variant="h6" mb={1} fontWeight={500}>
|
||||
<Box
|
||||
className="stat-box"
|
||||
flex="20%"
|
||||
minWidth="100px"
|
||||
px={theme.spacing(8)}
|
||||
py={theme.spacing(4)}
|
||||
border={1}
|
||||
borderColor={theme.palette.border.light}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
backgroundColor={theme.palette.background.main}
|
||||
>
|
||||
<Typography
|
||||
variant="h6"
|
||||
mb={theme.spacing(2)}
|
||||
fontSize={14}
|
||||
fontWeight={500}
|
||||
color={theme.palette.common.main}
|
||||
sx={{
|
||||
"& span": {
|
||||
color: theme.palette.text.accent,
|
||||
fontSize: 13,
|
||||
fontStyle: "italic",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography variant="h4" fontWeight={500}>
|
||||
<Typography
|
||||
variant="h4"
|
||||
fontWeight={500}
|
||||
fontSize={13}
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
{value}
|
||||
</Typography>
|
||||
</Box>
|
||||
@@ -47,8 +76,8 @@ const SkeletonLayout = () => {
|
||||
return (
|
||||
<>
|
||||
<Skeleton variant="rounded" width="20%" height={34} />
|
||||
<Stack gap={theme.gap.xl} mt={theme.gap.medium}>
|
||||
<Stack direction="row" gap={theme.gap.small} mt={theme.gap.small}>
|
||||
<Stack gap={theme.spacing(20)} mt={theme.spacing(6)}>
|
||||
<Stack direction="row" gap={theme.spacing(4)} mt={theme.spacing(4)}>
|
||||
<Skeleton
|
||||
variant="circular"
|
||||
style={{ minWidth: 24, minHeight: 24 }}
|
||||
@@ -59,7 +88,7 @@ const SkeletonLayout = () => {
|
||||
variant="rounded"
|
||||
width="50%"
|
||||
height={18}
|
||||
sx={{ mt: theme.gap.small }}
|
||||
sx={{ mt: theme.spacing(4) }}
|
||||
/>
|
||||
</Box>
|
||||
<Skeleton
|
||||
@@ -72,7 +101,7 @@ const SkeletonLayout = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
gap={theme.gap.large}
|
||||
gap={theme.spacing(12)}
|
||||
>
|
||||
<Skeleton variant="rounded" width="100%" height={80} />
|
||||
<Skeleton variant="rounded" width="100%" height={80} />
|
||||
@@ -82,7 +111,7 @@ const SkeletonLayout = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
mb={theme.gap.ml}
|
||||
mb={theme.spacing(8)}
|
||||
>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
@@ -96,7 +125,7 @@ const SkeletonLayout = () => {
|
||||
<Skeleton variant="rounded" width="100%" height="100%" />
|
||||
</Box>
|
||||
</Box>
|
||||
<Stack gap={theme.gap.ml}>
|
||||
<Stack gap={theme.spacing(8)}>
|
||||
<Skeleton variant="rounded" width="20%" height={24} />
|
||||
<Skeleton variant="rounded" width="100%" height={200} />
|
||||
<Skeleton variant="rounded" width="100%" height={50} />
|
||||
@@ -111,6 +140,7 @@ const SkeletonLayout = () => {
|
||||
* @component
|
||||
*/
|
||||
const DetailsPage = ({ isAdmin }) => {
|
||||
const theme = useTheme();
|
||||
const [monitor, setMonitor] = useState({});
|
||||
const { monitorId } = useParams();
|
||||
const { authToken } = useSelector((state) => state.auth);
|
||||
@@ -158,7 +188,6 @@ const DetailsPage = ({ isAdmin }) => {
|
||||
fetchCertificate();
|
||||
}, [authToken, monitorId, monitor]);
|
||||
|
||||
const theme = useTheme();
|
||||
let loading = Object.keys(monitor).length === 0;
|
||||
return (
|
||||
<Box className="monitor-details">
|
||||
@@ -172,26 +201,33 @@ const DetailsPage = ({ isAdmin }) => {
|
||||
{ name: "details", path: `/monitors/${monitorId}` },
|
||||
]}
|
||||
/>
|
||||
<Stack gap={theme.gap.large} mt={theme.gap.large}>
|
||||
<Stack direction="row" gap={theme.gap.xs}>
|
||||
<Stack gap={theme.spacing(12)} mt={theme.spacing(12)}>
|
||||
<Stack direction="row" gap={theme.spacing(2)}>
|
||||
<PulseDot
|
||||
color={
|
||||
monitor?.status
|
||||
? theme.label.up.dotColor
|
||||
: theme.label.down.dotColor
|
||||
? theme.palette.success.main
|
||||
: theme.palette.error.main
|
||||
}
|
||||
/>
|
||||
<Box>
|
||||
<Typography component="h1" sx={{ lineHeight: 1 }}>
|
||||
<Typography
|
||||
component="h1"
|
||||
color={theme.palette.text.primary}
|
||||
lineHeight={1}
|
||||
>
|
||||
{monitor.url?.replace(/^https?:\/\//, "") || "..."}
|
||||
</Typography>
|
||||
<Typography mt={theme.gap.small}>
|
||||
<Typography
|
||||
mt={theme.spacing(4)}
|
||||
color={theme.palette.text.tertiary}
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{
|
||||
color: monitor?.status
|
||||
? "var(--env-var-color-17)"
|
||||
: "var(--env-var-color-24)",
|
||||
? theme.palette.success.main
|
||||
: theme.palette.success.text,
|
||||
}}
|
||||
>
|
||||
Your site is {monitor?.status ? "up" : "down"}.
|
||||
@@ -207,8 +243,8 @@ const DetailsPage = ({ isAdmin }) => {
|
||||
img={
|
||||
<SettingsIcon
|
||||
style={{
|
||||
minWidth: theme.gap.mlplus,
|
||||
minHeight: theme.gap.mlplus,
|
||||
minWidth: theme.spacing(10),
|
||||
minHeight: theme.spacing(10),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
@@ -216,10 +252,10 @@ const DetailsPage = ({ isAdmin }) => {
|
||||
sx={{
|
||||
ml: "auto",
|
||||
alignSelf: "flex-end",
|
||||
backgroundColor: "#f4f4f4",
|
||||
px: theme.gap.medium,
|
||||
backgroundColor: theme.palette.background.fill,
|
||||
px: theme.spacing(6),
|
||||
"& svg": {
|
||||
mr: "6px",
|
||||
mr: theme.spacing(3),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
@@ -228,7 +264,7 @@ const DetailsPage = ({ isAdmin }) => {
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
gap={theme.gap.large}
|
||||
gap={theme.spacing(12)}
|
||||
flexWrap="wrap"
|
||||
>
|
||||
<StatBox
|
||||
@@ -281,20 +317,29 @@ const DetailsPage = ({ isAdmin }) => {
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
mb={theme.gap.ml}
|
||||
mb={theme.spacing(8)}
|
||||
>
|
||||
<Typography component="h2" alignSelf="flex-end">
|
||||
<Typography
|
||||
component="h2"
|
||||
alignSelf="flex-end"
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
Response Times
|
||||
</Typography>
|
||||
<ButtonGroup>
|
||||
<ButtonGroup
|
||||
sx={{
|
||||
"& .MuiButtonBase-root": {
|
||||
borderColor: theme.palette.border.light,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
level="secondary"
|
||||
label="Day"
|
||||
onClick={() => setDateRange("day")}
|
||||
sx={{
|
||||
backgroundColor:
|
||||
dateRange === "day" &&
|
||||
theme.palette.otherColors.fillGray,
|
||||
dateRange === "day" && theme.palette.background.fill,
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
@@ -303,8 +348,7 @@ const DetailsPage = ({ isAdmin }) => {
|
||||
onClick={() => setDateRange("week")}
|
||||
sx={{
|
||||
backgroundColor:
|
||||
dateRange === "week" &&
|
||||
theme.palette.otherColors.fillGray,
|
||||
dateRange === "week" && theme.palette.background.fill,
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
@@ -313,19 +357,18 @@ const DetailsPage = ({ isAdmin }) => {
|
||||
onClick={() => setDateRange("month")}
|
||||
sx={{
|
||||
backgroundColor:
|
||||
dateRange === "month" &&
|
||||
theme.palette.otherColors.fillGray,
|
||||
dateRange === "month" && theme.palette.background.fill,
|
||||
}}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</Stack>
|
||||
<Box
|
||||
p={theme.gap.mlplus}
|
||||
pb={theme.gap.xs}
|
||||
backgroundColor={theme.palette.otherColors.white}
|
||||
p={theme.spacing(10)}
|
||||
pb={theme.spacing(2)}
|
||||
backgroundColor={theme.palette.background.main}
|
||||
border={1}
|
||||
borderColor={theme.palette.otherColors.graishWhite}
|
||||
borderRadius={`${theme.shape.borderRadius}px`}
|
||||
borderColor={theme.palette.border.light}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
sx={{ height: "250px" }}
|
||||
>
|
||||
<MonitorDetailsAreaChart
|
||||
@@ -333,8 +376,10 @@ const DetailsPage = ({ isAdmin }) => {
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Stack gap={theme.gap.ml}>
|
||||
<Typography component="h2">History</Typography>
|
||||
<Stack gap={theme.spacing(8)}>
|
||||
<Typography component="h2" color={theme.palette.text.secondary}>
|
||||
History
|
||||
</Typography>
|
||||
<PaginationTable monitorId={monitorId} dateRange={dateRange} />
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
@@ -8,26 +8,31 @@ 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;
|
||||
if (title === "up") {
|
||||
color = theme.pie.green.stroke;
|
||||
color = theme.palette.success.main;
|
||||
icon = (
|
||||
<Box sx={{ ...sharedStyles, top: 8 }}>
|
||||
<Arrow />
|
||||
</Box>
|
||||
);
|
||||
} else if (title === "down") {
|
||||
color = theme.pie.red.stroke;
|
||||
color = theme.palette.error.text;
|
||||
icon = (
|
||||
<Box sx={{ ...sharedStyles, transform: "rotate(180deg)", top: 5 }}>
|
||||
<Arrow />
|
||||
</Box>
|
||||
);
|
||||
} else if (title === "paused") {
|
||||
color = theme.pie.yellow.stroke;
|
||||
color = theme.palette.warning.main;
|
||||
icon = (
|
||||
<Box sx={{ ...sharedStyles, top: 12, right: 12 }}>
|
||||
<ClockSnooze />
|
||||
@@ -40,15 +45,15 @@ const StatusBox = ({ title, value }) => {
|
||||
position="relative"
|
||||
flex={1}
|
||||
border={1}
|
||||
borderColor={theme.palette.otherColors.graishWhite}
|
||||
borderRadius={`${theme.shape.borderRadius}px`}
|
||||
backgroundColor={theme.palette.otherColors.white}
|
||||
px={theme.gap.large}
|
||||
py={theme.gap.ml}
|
||||
borderColor={theme.palette.border.light}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
backgroundColor={theme.palette.background.main}
|
||||
px={theme.spacing(12)}
|
||||
py={theme.spacing(8)}
|
||||
overflow="hidden"
|
||||
sx={{
|
||||
"&:hover": {
|
||||
backgroundColor: "#f9fafb",
|
||||
backgroundColor: theme.palette.background.accent,
|
||||
},
|
||||
"&:after": {
|
||||
position: "absolute",
|
||||
@@ -67,8 +72,8 @@ const StatusBox = ({ title, value }) => {
|
||||
textTransform="uppercase"
|
||||
fontSize={15}
|
||||
letterSpacing={0.5}
|
||||
color={theme.palette.otherColors.bluishGray}
|
||||
mb={theme.gap.ml}
|
||||
color={theme.palette.text.secondary}
|
||||
mb={theme.spacing(8)}
|
||||
sx={{ opacity: 0.6 }}
|
||||
>
|
||||
{title}
|
||||
@@ -87,7 +92,7 @@ const StatusBox = ({ title, value }) => {
|
||||
component="span"
|
||||
fontSize={20}
|
||||
fontWeight={300}
|
||||
color={theme.palette.otherColors.bluishGray}
|
||||
color={theme.palette.text.secondary}
|
||||
sx={{ opacity: 0.3 }}
|
||||
>
|
||||
#
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useState } from "react";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import {
|
||||
IconButton,
|
||||
@@ -14,10 +16,8 @@ import {
|
||||
getUptimeMonitorsByTeamId,
|
||||
} from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
import Settings from "../../../assets/icons/settings-bold.svg?react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import Button from "../../../Components/Button";
|
||||
import PropTypes from "prop-types";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const ActionsMenu = ({ monitor, isAdmin }) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
@@ -77,6 +77,9 @@ const ActionsMenu = ({ monitor, isAdmin }) => {
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
"& svg path": {
|
||||
stroke: theme.palette.other.icon,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Settings />
|
||||
@@ -87,6 +90,26 @@ const ActionsMenu = ({ monitor, isAdmin }) => {
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={(e) => closeMenu(e)}
|
||||
slotProps={{
|
||||
paper: {
|
||||
sx: {
|
||||
"& ul": {
|
||||
p: theme.spacing(2.5),
|
||||
minWidth: "100px",
|
||||
},
|
||||
"& li": {
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: 13,
|
||||
px: theme.spacing(4),
|
||||
py: theme.spacing(2.5),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
},
|
||||
"& li:last-of-type": {
|
||||
color: theme.palette.error.text,
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{actions.url !== null ? (
|
||||
<MenuItem
|
||||
@@ -137,48 +160,58 @@ const ActionsMenu = ({ monitor, isAdmin }) => {
|
||||
aria-labelledby="modal-delete-monitor"
|
||||
aria-describedby="delete-monitor-confirmation"
|
||||
open={isOpen}
|
||||
onClose={() => setIsOpen(false)}
|
||||
disablePortal
|
||||
onClose={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsOpen(false);
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
gap={theme.gap.xs}
|
||||
gap={theme.spacing(2)}
|
||||
color={theme.palette.text.secondary}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 400,
|
||||
bgcolor: "white",
|
||||
border: "solid 1px #f2f2f2",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
bgcolor: theme.palette.background.main,
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
boxShadow: 24,
|
||||
p: "30px",
|
||||
p: theme.spacing(15),
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography id="modal-delete-monitor" component="h2">
|
||||
<Typography id="modal-delete-monitor" component="h2" fontWeight={600}>
|
||||
Do you really want to delete this monitor?
|
||||
</Typography>
|
||||
<Typography id="delete-monitor-confirmation">
|
||||
<Typography id="delete-monitor-confirmation" fontSize={13}>
|
||||
Once deleted, this monitor cannot be retrieved.
|
||||
</Typography>
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.gap.small}
|
||||
mt={theme.gap.large}
|
||||
gap={theme.spacing(4)}
|
||||
mt={theme.spacing(12)}
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<Button
|
||||
level="tertiary"
|
||||
label="Cancel"
|
||||
onClick={() => setIsOpen(false)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setIsOpen(false);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
level="error"
|
||||
label="Delete"
|
||||
onClick={(e) => handleRemove(e)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation(e);
|
||||
handleRemove(e);
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
@@ -11,12 +11,13 @@ const Fallback = ({ isAdmin }) => {
|
||||
return (
|
||||
<Stack
|
||||
alignItems="center"
|
||||
backgroundColor={theme.palette.otherColors.white}
|
||||
p={theme.gap.xxl}
|
||||
gap={theme.gap.xs}
|
||||
backgroundColor={theme.palette.background.main}
|
||||
p={theme.spacing(30)}
|
||||
gap={theme.spacing(2)}
|
||||
border={1}
|
||||
borderRadius={`${theme.shape.borderRadius}px`}
|
||||
borderColor={theme.palette.otherColors.graishWhite}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
borderColor={theme.palette.border.light}
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
<Typography component="h2">No monitors found</Typography>
|
||||
<Typography>
|
||||
@@ -29,7 +30,7 @@ const Fallback = ({ isAdmin }) => {
|
||||
onClick={() => {
|
||||
navigate("/monitors/create");
|
||||
}}
|
||||
sx={{ mt: theme.gap.large }}
|
||||
sx={{ mt: theme.spacing(12) }}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
import PropTypes from "prop-types";
|
||||
/**
|
||||
* Host component.
|
||||
@@ -13,12 +13,39 @@ import PropTypes from "prop-types";
|
||||
*/
|
||||
const Host = ({ params }) => {
|
||||
return (
|
||||
<Stack direction="row" alignItems="baseline" className="host">
|
||||
{params.title}
|
||||
<Typography component="span" sx={{ color: params.percentageColor }}>
|
||||
<Box className="host">
|
||||
<Box
|
||||
display="inline-block"
|
||||
position="relative"
|
||||
sx={{
|
||||
fontWeight: 500,
|
||||
"&:before": {
|
||||
position: "absolute",
|
||||
content: `""`,
|
||||
width: "4px",
|
||||
height: "4px",
|
||||
borderRadius: "50%",
|
||||
backgroundColor: "gray",
|
||||
opacity: 0.8,
|
||||
right: "-10px",
|
||||
top: "42%",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{params.title}
|
||||
</Box>
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{
|
||||
color: params.percentageColor,
|
||||
fontWeight: 500,
|
||||
ml: "15px",
|
||||
}}
|
||||
>
|
||||
{params.percentage}%
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Box sx={{ opacity: 0.6 }}>{params.url}</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
.monitors h1.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large-plus);
|
||||
color: var(--env-var-color-1);
|
||||
font-weight: 500;
|
||||
}
|
||||
.monitors h2.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large);
|
||||
color: var(--env-var-color-5);
|
||||
font-weight: 500;
|
||||
}
|
||||
.monitors p.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.monitors h1.MuiTypography-root + p.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium-plus);
|
||||
}
|
||||
@@ -28,36 +22,9 @@
|
||||
padding: 5px;
|
||||
min-width: 25px;
|
||||
min-height: 25px;
|
||||
background-color: var(--env-var-color-15);
|
||||
border: 1px solid var(--env-var-color-6);
|
||||
border-radius: 50%;
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
font-weight: 500;
|
||||
color: var(--env-var-color-1);
|
||||
margin-left: 10px;
|
||||
line-height: 0.8;
|
||||
}
|
||||
|
||||
body:has(.monitors) .actions-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;
|
||||
}
|
||||
body:has(.monitors) .actions-menu .MuiList-root {
|
||||
padding: 6px 8px;
|
||||
min-width: 100px;
|
||||
}
|
||||
body:has(.monitors) .actions-menu li.MuiButtonBase-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-5);
|
||||
padding: 5px 8px;
|
||||
border-radius: var(--env-var-radius-1);
|
||||
}
|
||||
body:has(.monitors) .actions-menu li.MuiButtonBase-root:last-of-type {
|
||||
color: var(--env-var-color-26);
|
||||
}
|
||||
body:has(.monitors) .actions-menu .MuiModal-root p.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
color: var(--env-var-color-2);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import SkeletonLayout from "./skeleton";
|
||||
import Fallback from "./fallback";
|
||||
import StatusBox from "./StatusBox";
|
||||
import { buildData } from "./monitorData";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
|
||||
const Monitors = ({ isAdmin }) => {
|
||||
const theme = useTheme();
|
||||
@@ -42,43 +43,86 @@ const Monitors = ({ isAdmin }) => {
|
||||
|
||||
let loading = monitorState.isLoading && monitorState.monitors.length === 0;
|
||||
|
||||
const now = new Date();
|
||||
const hour = now.getHours();
|
||||
|
||||
let greeting = "";
|
||||
let emoji = "";
|
||||
if (hour < 12) {
|
||||
greeting = "morning";
|
||||
emoji = "🌅";
|
||||
} else if (hour < 18) {
|
||||
greeting = "afternoon";
|
||||
emoji = "🌞";
|
||||
} else {
|
||||
greeting = "evening";
|
||||
emoji = "🌙";
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack className="monitors" gap={theme.gap.large}>
|
||||
<Stack className="monitors" gap={theme.spacing(12)}>
|
||||
{loading ? (
|
||||
<SkeletonLayout />
|
||||
) : (
|
||||
<>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<Typography
|
||||
component="h1"
|
||||
sx={{ lineHeight: 1, alignSelf: "flex-end" }}
|
||||
<Box>
|
||||
<Breadcrumbs list={[{ name: `monitors`, path: "/monitors" }]} />
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
mt={theme.spacing(5)}
|
||||
>
|
||||
Hello, {authState.user.firstName}
|
||||
</Typography>
|
||||
{isAdmin && monitorState.monitors?.length !== 0 && (
|
||||
<Button
|
||||
level="primary"
|
||||
label="Create monitor"
|
||||
onClick={() => {
|
||||
navigate("/monitors/create");
|
||||
}}
|
||||
sx={{ fontWeight: 500 }}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
{monitorState.monitors?.length === 0 && (
|
||||
<Box>
|
||||
<Typography
|
||||
component="h1"
|
||||
lineHeight={1}
|
||||
color={theme.palette.text.primary}
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
Good {greeting},{" "}
|
||||
</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
fontWeight="inherit"
|
||||
>
|
||||
{authState.user.firstName} {emoji}
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{ opacity: 0.8 }}
|
||||
lineHeight={1}
|
||||
fontWeight={300}
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
Here’s an overview of your uptime monitors.
|
||||
</Typography>
|
||||
</Box>
|
||||
{monitorState.monitors?.length !== 0 && (
|
||||
<Button
|
||||
level="primary"
|
||||
label="Create monitor"
|
||||
onClick={() => {
|
||||
navigate("/monitors/create");
|
||||
}}
|
||||
sx={{ fontWeight: 500 }}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Box>
|
||||
{isAdmin && monitorState.monitors?.length === 0 && (
|
||||
<Fallback isAdmin={isAdmin} />
|
||||
)}
|
||||
|
||||
{monitorState.monitors?.length !== 0 && (
|
||||
<>
|
||||
<Stack
|
||||
gap={theme.gap.large}
|
||||
gap={theme.spacing(12)}
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
@@ -88,17 +132,32 @@ const Monitors = ({ isAdmin }) => {
|
||||
</Stack>
|
||||
<Box
|
||||
flex={1}
|
||||
p={theme.gap.lgplus}
|
||||
px={theme.spacing(16)}
|
||||
py={theme.spacing(12)}
|
||||
border={1}
|
||||
borderColor={theme.palette.otherColors.graishWhite}
|
||||
backgroundColor={theme.palette.otherColors.white}
|
||||
sx={{
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
}}
|
||||
borderColor={theme.palette.border.light}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
backgroundColor={theme.palette.background.main}
|
||||
>
|
||||
<Stack direction="row" alignItems="center" mb={theme.gap.large}>
|
||||
<Typography component="h2">Current monitors</Typography>
|
||||
<Box className="current-monitors-counter">
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
mb={theme.spacing(12)}
|
||||
>
|
||||
<Typography
|
||||
component="h2"
|
||||
color={theme.palette.text.secondary}
|
||||
letterSpacing={-0.5}
|
||||
>
|
||||
Actively monitoring
|
||||
</Typography>
|
||||
<Box
|
||||
className="current-monitors-counter"
|
||||
color={theme.palette.text.primary}
|
||||
border={1}
|
||||
borderColor={theme.palette.border.light}
|
||||
backgroundColor={theme.palette.background.accent}
|
||||
>
|
||||
{monitorState.monitors.length}
|
||||
</Box>
|
||||
{/* TODO - add search bar */}
|
||||
|
||||
@@ -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,14 +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(--env-var-color-17)"
|
||||
: "var(--env-var-color-19)",
|
||||
? theme.palette.success.main
|
||||
: theme.palette.error.text,
|
||||
status: monitor.status === true ? "up" : "down",
|
||||
};
|
||||
|
||||
@@ -43,7 +47,6 @@ export const buildData = (monitors, isAdmin, navigate) => {
|
||||
|
||||
return {
|
||||
id: monitor._id,
|
||||
// disabled for now
|
||||
handleClick: () => {
|
||||
navigate(`/monitors/${monitor._id}`);
|
||||
},
|
||||
|
||||
@@ -16,7 +16,7 @@ const SkeletonLayout = () => {
|
||||
<Skeleton variant="rounded" width="15%" height={36} />
|
||||
</Stack>
|
||||
<Stack
|
||||
gap={theme.gap.large}
|
||||
gap={theme.spacing(12)}
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
@@ -24,7 +24,11 @@ const SkeletonLayout = () => {
|
||||
<Skeleton variant="rounded" width="100%" height={100} />
|
||||
<Skeleton variant="rounded" width="100%" height={100} />
|
||||
</Stack>
|
||||
<Stack gap={theme.gap.large} p={theme.gap.xl} backgroundColor="#f9fafb">
|
||||
<Stack
|
||||
gap={theme.spacing(12)}
|
||||
p={theme.spacing(20)}
|
||||
backgroundColor="#f9fafb"
|
||||
>
|
||||
<Skeleton variant="rounded" width="50%" height={25} />
|
||||
<Skeleton variant="rounded" width="100%" height={300} />
|
||||
<Skeleton variant="rounded" width="100%" height={100} />
|
||||
|
||||
35
Client/src/Pages/Monitors/styled.jsx
Normal file
35
Client/src/Pages/Monitors/styled.jsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Stack, styled } from "@mui/material";
|
||||
|
||||
export const ConfigBox = styled(Stack)(({ theme }) => ({
|
||||
flexDirection: "row",
|
||||
border: 1,
|
||||
borderStyle: "solid",
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.spacing(2),
|
||||
backgroundColor: theme.palette.background.main,
|
||||
"& > *": {
|
||||
paddingTop: theme.spacing(12),
|
||||
paddingBottom: theme.spacing(18),
|
||||
},
|
||||
"& > div:first-of-type": {
|
||||
flex: 0.7,
|
||||
borderRight: 1,
|
||||
borderRightStyle: "solid",
|
||||
borderRightColor: theme.palette.border.light,
|
||||
paddingRight: theme.spacing(15),
|
||||
paddingLeft: theme.spacing(15),
|
||||
},
|
||||
"& > div:last-of-type": {
|
||||
flex: 1,
|
||||
paddingRight: theme.spacing(20),
|
||||
paddingLeft: theme.spacing(18),
|
||||
},
|
||||
"& h2": {
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: 15,
|
||||
fontWeight: 600,
|
||||
},
|
||||
"& h3, & p": {
|
||||
color: theme.palette.text.tertiary,
|
||||
},
|
||||
}));
|
||||
@@ -1,15 +0,0 @@
|
||||
.not-found-page {
|
||||
height: 100vh;
|
||||
}
|
||||
.not-found-page button,
|
||||
.not-found-page p {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
.not-found-page h1 {
|
||||
font-size: var(--env-var-font-size-large);
|
||||
font-weight: 600;
|
||||
}
|
||||
.not-found-page h1,
|
||||
.not-found-page p {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
import Button from "../../Components/Button";
|
||||
import "./index.css";
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import NotFoundSvg from "../../../src/assets/Images/sushi_404.svg";
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
import { useNavigate } from "react-router";
|
||||
import NotFoundSvg from "../../../src/assets/Images/sushi_404.svg";
|
||||
import { useTheme } from "@emotion/react";
|
||||
|
||||
/**
|
||||
* Support for defaultProps will be removed from function components in a future major release
|
||||
@@ -33,16 +33,24 @@ const DefaultValue = {
|
||||
*/
|
||||
const NotFound = ({ title = DefaultValue.title, desc = DefaultValue.desc }) => {
|
||||
const navigate = useNavigate();
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Stack className="not-found-page" justifyContent="center">
|
||||
<Stack gap="20px" alignItems="center">
|
||||
<Stack height="100vh" justifyContent="center">
|
||||
<Stack
|
||||
gap={theme.spacing(2)}
|
||||
alignItems="center"
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
<img src={NotFoundSvg} alt="404" style={{ maxHeight: "25rem" }} />
|
||||
<Typography component="h1">{title}</Typography>
|
||||
<Typography>{desc}</Typography>
|
||||
<Typography component="h1" fontSize={16} fontWeight={600}>
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography fontSize={13}>{desc}</Typography>
|
||||
<Button
|
||||
label="Go to the main dashboard"
|
||||
level="primary"
|
||||
sx={{ mt: "24px" }}
|
||||
sx={{ mt: theme.spacing(10) }}
|
||||
onClick={() => navigate("/")}
|
||||
/>
|
||||
</Stack>
|
||||
|
||||
@@ -1,23 +1,9 @@
|
||||
.configure-pagespeed form > .MuiStack-root:nth-child(2) {
|
||||
background-color: var(--env-var-color-8);
|
||||
border: solid 1px var(--env-var-color-6);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
padding: var(--env-var-font-size-large-plus) var(--env-var-font-size-xlarge);
|
||||
}
|
||||
.configure-pagespeed h1.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large-plus);
|
||||
}
|
||||
.configure-pagespeed h1.MuiTypography-root,
|
||||
.configure-pagespeed h2.MuiTypography-root {
|
||||
color: var(--env-var-color-1);
|
||||
}
|
||||
.configure-pagespeed span.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
}
|
||||
.configure-pagespeed p.MuiTypography-root,
|
||||
.configure-pagespeed h3.MuiTypography-root {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.configure-pagespeed h1.MuiTypography-root,
|
||||
.configure-pagespeed h3.MuiTypography-root,
|
||||
.configure-pagespeed h2.MuiTypography-root {
|
||||
@@ -38,13 +24,10 @@
|
||||
.configure-pagespeed .checkbox-wrapper {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
.configure-pagespeed .MuiInputBase-root:has(> .Mui-disabled) {
|
||||
background-color: var(--env-var-color-13);
|
||||
}
|
||||
|
||||
/* to be removed */
|
||||
.configure-pagespeed .section-disabled {
|
||||
opacity: 0.4;
|
||||
background-color: #f1f1f2;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
user-select: none;
|
||||
@@ -55,11 +38,6 @@
|
||||
height: var(--env-var-height-2);
|
||||
}
|
||||
|
||||
body:has(.configure-pagespeed) .select-dropdown .MuiMenuItem-root,
|
||||
.configure-pagespeed .select-wrapper .select-component > .MuiSelect-select {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.configure-pagespeed h3.MuiTypography-root {
|
||||
flex: 0.7;
|
||||
}
|
||||
|
||||
@@ -32,8 +32,8 @@ const SkeletonLayout = () => {
|
||||
return (
|
||||
<>
|
||||
<Skeleton variant="rounded" width="15%" height={34} />
|
||||
<Stack gap={theme.gap.xl} mt={theme.gap.medium} maxWidth="1000px">
|
||||
<Stack direction="row" gap={theme.gap.small} mt={theme.gap.small}>
|
||||
<Stack gap={theme.spacing(20)} mt={theme.spacing(6)} maxWidth="1000px">
|
||||
<Stack direction="row" gap={theme.spacing(4)} mt={theme.spacing(4)}>
|
||||
<Skeleton
|
||||
variant="circular"
|
||||
style={{ minWidth: 24, minHeight: 24 }}
|
||||
@@ -43,13 +43,13 @@ const SkeletonLayout = () => {
|
||||
variant="rounded"
|
||||
width="50%"
|
||||
height={24}
|
||||
sx={{ mb: theme.gap.small }}
|
||||
sx={{ mb: theme.spacing(4) }}
|
||||
/>
|
||||
<Skeleton variant="rounded" width="50%" height={18} />
|
||||
</Box>
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.gap.medium}
|
||||
gap={theme.spacing(6)}
|
||||
sx={{
|
||||
ml: "auto",
|
||||
alignSelf: "flex-end",
|
||||
@@ -147,7 +147,7 @@ const PageSpeedConfigure = () => {
|
||||
let loading = Object.keys(monitor).length === 0;
|
||||
|
||||
return (
|
||||
<Stack className="configure-pagespeed" gap={theme.gap.large}>
|
||||
<Stack className="configure-pagespeed" gap={theme.spacing(12)}>
|
||||
{loading ? (
|
||||
<SkeletonLayout />
|
||||
) : (
|
||||
@@ -165,21 +165,22 @@ const PageSpeedConfigure = () => {
|
||||
spellCheck="false"
|
||||
onSubmit={handleSave}
|
||||
flex={1}
|
||||
gap={theme.gap.large}
|
||||
gap={theme.spacing(12)}
|
||||
>
|
||||
<Stack direction="row" gap={theme.gap.xs}>
|
||||
<Stack direction="row" gap={theme.spacing(2)}>
|
||||
<PulseDot
|
||||
color={
|
||||
monitor?.status
|
||||
? theme.label.up.dotColor
|
||||
: theme.label.down.dotColor
|
||||
? theme.palette.success.main
|
||||
: theme.palette.error.main
|
||||
}
|
||||
/>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h1"
|
||||
mb={theme.gap.xs}
|
||||
mb={theme.spacing(2)}
|
||||
sx={{ lineHeight: 1 }}
|
||||
color={theme.palette.text.primary}
|
||||
>
|
||||
{monitor?.url}
|
||||
</Typography>
|
||||
@@ -187,8 +188,8 @@ const PageSpeedConfigure = () => {
|
||||
component="span"
|
||||
sx={{
|
||||
color: monitor?.status
|
||||
? "var(--env-var-color-17)"
|
||||
: "var(--env-var-color-24)",
|
||||
? theme.palette.success.main
|
||||
: theme.palette.error.text,
|
||||
}}
|
||||
>
|
||||
Your pagespeed monitor is {monitor?.status ? "live" : "down"}.
|
||||
@@ -201,11 +202,11 @@ const PageSpeedConfigure = () => {
|
||||
animate="rotate180"
|
||||
img={<PauseCircleOutlineIcon />}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.otherColors.fillGray,
|
||||
pl: theme.gap.small,
|
||||
pr: theme.gap.medium,
|
||||
backgroundColor: theme.palette.background.fill,
|
||||
pl: theme.spacing(4),
|
||||
pr: theme.spacing(6),
|
||||
"& svg": {
|
||||
mr: theme.gap.xs,
|
||||
mr: theme.spacing(2),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
@@ -214,14 +215,27 @@ const PageSpeedConfigure = () => {
|
||||
label="Remove"
|
||||
sx={{
|
||||
boxShadow: "none",
|
||||
px: theme.gap.ml,
|
||||
ml: theme.gap.medium,
|
||||
px: theme.spacing(8),
|
||||
ml: theme.spacing(6),
|
||||
}}
|
||||
onClick={() => setIsOpen(true)}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
<Stack gap={theme.gap.xl}>
|
||||
<Stack
|
||||
border={1}
|
||||
borderColor={theme.palette.border.light}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
backgroundColor={theme.palette.background.main}
|
||||
p={theme.spacing(20)}
|
||||
pl={theme.spacing(15)}
|
||||
gap={theme.spacing(20)}
|
||||
sx={{
|
||||
"& h3, & p": {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack direction="row">
|
||||
<Typography component="h3">Monitor display name</Typography>
|
||||
<Field
|
||||
@@ -233,7 +247,14 @@ const PageSpeedConfigure = () => {
|
||||
error={errors.name}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack direction="row">
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={{
|
||||
".MuiInputBase-root:has(> .Mui-disabled)": {
|
||||
backgroundColor: theme.palette.background.accent,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography component="h3">URL</Typography>
|
||||
<Field
|
||||
type="url"
|
||||
@@ -259,8 +280,11 @@ const PageSpeedConfigure = () => {
|
||||
Incidents notifications{" "}
|
||||
<Typography component="span">(coming soon)</Typography>
|
||||
</Typography>
|
||||
<Stack className="section-disabled">
|
||||
<Typography mb={theme.gap.small}>
|
||||
<Stack
|
||||
className="section-disabled"
|
||||
backgroundColor={theme.palette.background.fill}
|
||||
>
|
||||
<Typography mb={theme.spacing(4)}>
|
||||
When there is a new incident,
|
||||
</Typography>
|
||||
<Checkbox
|
||||
@@ -279,7 +303,7 @@ const PageSpeedConfigure = () => {
|
||||
label="Notify via email to following emails"
|
||||
isChecked={false}
|
||||
/>
|
||||
<Box mx={`calc(${theme.gap.ml} * 2)`}>
|
||||
<Box mx={theme.spacing(16)}>
|
||||
<Field
|
||||
id="notify-emails-list"
|
||||
placeholder="notifications@gmail.com"
|
||||
@@ -287,7 +311,7 @@ const PageSpeedConfigure = () => {
|
||||
onChange={() => logger.warn("disabled")}
|
||||
error=""
|
||||
/>
|
||||
<Typography mt={theme.gap.small}>
|
||||
<Typography mt={theme.spacing(4)}>
|
||||
You can separate multiple emails with a comma
|
||||
</Typography>
|
||||
</Box>
|
||||
@@ -300,7 +324,7 @@ const PageSpeedConfigure = () => {
|
||||
level="primary"
|
||||
label="Save"
|
||||
onClick={handleSave}
|
||||
sx={{ px: theme.gap.large, mt: theme.gap.large }}
|
||||
sx={{ px: theme.spacing(12), mt: theme.spacing(12) }}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
@@ -314,33 +338,41 @@ const PageSpeedConfigure = () => {
|
||||
disablePortal
|
||||
>
|
||||
<Stack
|
||||
gap={theme.gap.xs}
|
||||
gap={theme.spacing(2)}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: 400,
|
||||
bgcolor: "white",
|
||||
border: "solid 1px #f2f2f2",
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
bgcolor: theme.palette.background.main,
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
boxShadow: 24,
|
||||
p: "30px",
|
||||
p: theme.spacing(15),
|
||||
"&:focus": {
|
||||
outline: "none",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography id="modal-delete-pagespeed-monitor" component="h2">
|
||||
<Typography
|
||||
id="modal-delete-pagespeed-monitor"
|
||||
component="h2"
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
Do you really want to delete this monitor?
|
||||
</Typography>
|
||||
<Typography id="delete-pagespeed-monitor-confirmation">
|
||||
<Typography
|
||||
id="delete-pagespeed-monitor-confirmation"
|
||||
color={theme.palette.text.tertiary}
|
||||
>
|
||||
Once deleted, this monitor cannot be retrieved.
|
||||
</Typography>
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.gap.small}
|
||||
mt={theme.gap.large}
|
||||
gap={theme.spacing(4)}
|
||||
mt={theme.spacing(12)}
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<Button
|
||||
|
||||
@@ -1,17 +1,3 @@
|
||||
.create-page-speed form > .MuiStack-root:not(:last-of-type) {
|
||||
background-color: var(--env-var-color-8);
|
||||
border: solid 1px var(--env-var-color-6);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
padding: var(--env-var-font-size-large-plus) var(--env-var-font-size-xlarge);
|
||||
}
|
||||
.create-page-speed h1.MuiTypography-root {
|
||||
font-size: 22px;
|
||||
color: var(--env-var-color-1);
|
||||
}
|
||||
.create-page-speed p.MuiTypography-root,
|
||||
.create-page-speed h3.MuiTypography-root {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.create-page-speed h1.MuiTypography-root,
|
||||
.create-page-speed h3.MuiTypography-root {
|
||||
font-weight: 600;
|
||||
@@ -31,10 +17,10 @@
|
||||
.create-page-speed .checkbox-wrapper {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
/* to be removed */
|
||||
.create-page-speed .section-disabled {
|
||||
opacity: 0.4;
|
||||
background-color: #f1f1f2;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
user-select: none;
|
||||
@@ -45,11 +31,6 @@
|
||||
height: var(--env-var-height-2);
|
||||
}
|
||||
|
||||
body:has(.create-page-speed) .select-dropdown .MuiMenuItem-root,
|
||||
.create-page-speed .select-wrapper .select-component > .MuiSelect-select {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
.create-page-speed h3.MuiTypography-root {
|
||||
flex: 0.7;
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ const CreatePageSpeed = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack className="create-page-speed" gap={theme.gap.mlplus}>
|
||||
<Stack className="create-page-speed" gap={theme.spacing(6)}>
|
||||
<Breadcrumbs
|
||||
list={[
|
||||
{ name: "pagespeed", path: "/pagespeed" },
|
||||
@@ -111,11 +111,47 @@ const CreatePageSpeed = () => {
|
||||
noValidate
|
||||
spellCheck="false"
|
||||
onSubmit={handleCreate}
|
||||
gap={theme.gap.large}
|
||||
gap={theme.spacing(12)}
|
||||
flex={1}
|
||||
>
|
||||
<Typography component="h1">Create pagespeed monitor</Typography>
|
||||
<Stack gap={theme.gap.xl}>
|
||||
<Typography
|
||||
component="h1"
|
||||
lineHeight={1}
|
||||
fontSize={21}
|
||||
color={theme.palette.text.primary}
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
Create your{" "}
|
||||
</Typography>
|
||||
<Typography component="span" fontSize="inherit" fontWeight="inherit">
|
||||
pagespeed{" "}
|
||||
</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
fontSize="inherit"
|
||||
color={theme.palette.text.secondary}
|
||||
>
|
||||
monitor
|
||||
</Typography>
|
||||
</Typography>
|
||||
<Stack
|
||||
border={1}
|
||||
borderColor={theme.palette.border.light}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
backgroundColor={theme.palette.background.main}
|
||||
p={theme.spacing(20)}
|
||||
pl={theme.spacing(15)}
|
||||
gap={theme.spacing(20)}
|
||||
sx={{
|
||||
"& h3, & p": {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack direction="row">
|
||||
<Typography component="h3">Monitor display name</Typography>
|
||||
<Field
|
||||
@@ -152,8 +188,11 @@ const CreatePageSpeed = () => {
|
||||
Incidents notifications{" "}
|
||||
<Typography component="span">(coming soon)</Typography>
|
||||
</Typography>
|
||||
<Stack className="section-disabled">
|
||||
<Typography mb={theme.gap.small}>
|
||||
<Stack
|
||||
className="section-disabled"
|
||||
backgroundColor={theme.palette.background.fill}
|
||||
>
|
||||
<Typography mb={theme.spacing(4)}>
|
||||
When there is a new incident,
|
||||
</Typography>
|
||||
<Checkbox
|
||||
@@ -172,7 +211,7 @@ const CreatePageSpeed = () => {
|
||||
label="Notify via email to following emails"
|
||||
isChecked={false}
|
||||
/>
|
||||
<Box mx={`calc(${theme.gap.ml} * 2)`}>
|
||||
<Box mx={theme.spacing(16)}>
|
||||
<Field
|
||||
id="notify-emails-list"
|
||||
placeholder="notifications@gmail.com"
|
||||
@@ -180,7 +219,7 @@ const CreatePageSpeed = () => {
|
||||
onChange={() => logger.warn("disabled")}
|
||||
error=""
|
||||
/>
|
||||
<Typography mt={theme.gap.small}>
|
||||
<Typography mt={theme.spacing(4)}>
|
||||
You can separate multiple emails with a comma
|
||||
</Typography>
|
||||
</Box>
|
||||
@@ -193,7 +232,7 @@ const CreatePageSpeed = () => {
|
||||
level="primary"
|
||||
label="Create"
|
||||
onClick={handleCreate}
|
||||
sx={{ px: theme.gap.large, mt: theme.gap.large }}
|
||||
sx={{ px: theme.spacing(12), mt: theme.spacing(12) }}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
.page-speed-details h1 {
|
||||
font-size: var(--env-var-font-size-large-plus);
|
||||
color: var(--env-var-color-1);
|
||||
}
|
||||
.page-speed-details h2,
|
||||
.page-speed-details h6 {
|
||||
@@ -15,13 +14,7 @@
|
||||
.page-speed-details h4 > span {
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
}
|
||||
.page-speed-details h2,
|
||||
.page-speed-details h4,
|
||||
.page-speed-details p {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.page-speed-details h6 {
|
||||
color: var(--env-var-color-3);
|
||||
line-height: 22px;
|
||||
}
|
||||
.page-speed-details h1,
|
||||
@@ -33,14 +26,6 @@
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
.page-speed-details .stat-box,
|
||||
.page-speed-details > .MuiStack-root:last-child,
|
||||
.page-speed-details > .MuiBox-root:nth-last-child(3) {
|
||||
border: solid 1px var(--env-var-color-6);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
background-color: var(--env-var-color-8);
|
||||
min-width: 200px;
|
||||
}
|
||||
.page-speed-details .stat-box {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@@ -29,17 +29,28 @@ const StatBox = ({ icon, title, value }) => {
|
||||
<Stack
|
||||
className="stat-box"
|
||||
direction="row"
|
||||
gap={theme.gap.small}
|
||||
pt={theme.gap.ml}
|
||||
px={theme.gap.ml}
|
||||
pb={theme.gap.mlplus}
|
||||
gap={theme.spacing(4)}
|
||||
pt={theme.spacing(8)}
|
||||
px={theme.spacing(8)}
|
||||
pb={theme.spacing(10)}
|
||||
border={1}
|
||||
borderColor={theme.palette.border.light}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
backgroundColor={theme.palette.background.main}
|
||||
minWidth="200px"
|
||||
>
|
||||
{icon}
|
||||
<Box>
|
||||
<Typography variant="h6" mb={theme.gap.medium}>
|
||||
<Typography
|
||||
variant="h6"
|
||||
color={theme.palette.common.main}
|
||||
mb={theme.spacing(6)}
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography variant="h4">{value}</Typography>
|
||||
<Typography variant="h4" color={theme.palette.text.secondary}>
|
||||
{value}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
@@ -151,7 +162,7 @@ const SkeletonLayout = () => {
|
||||
return (
|
||||
<>
|
||||
<Skeleton variant="rounded" width="15%" height={34} />
|
||||
<Stack direction="row" gap={theme.gap.small}>
|
||||
<Stack direction="row" gap={theme.spacing(4)}>
|
||||
<Skeleton variant="circular" style={{ minWidth: 24, minHeight: 24 }} />
|
||||
<Box width="85%">
|
||||
<Skeleton variant="rounded" width="50%" height={24} />
|
||||
@@ -159,7 +170,7 @@ const SkeletonLayout = () => {
|
||||
variant="rounded"
|
||||
width="50%"
|
||||
height={18}
|
||||
sx={{ mt: theme.gap.small }}
|
||||
sx={{ mt: theme.spacing(4) }}
|
||||
/>
|
||||
</Box>
|
||||
<Skeleton
|
||||
@@ -172,7 +183,7 @@ const SkeletonLayout = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
gap={theme.gap.xl}
|
||||
gap={theme.spacing(20)}
|
||||
flexWrap="wrap"
|
||||
>
|
||||
<Skeleton variant="rounded" width="30%" height={90} sx={{ flex: 1 }} />
|
||||
@@ -237,10 +248,37 @@ const PageSpeedDetails = () => {
|
||||
* @returns {{stroke: string, text: string, bg: string}} The color properties for the given performance value.
|
||||
*/
|
||||
const getColors = (value) => {
|
||||
if (value >= 90 && value <= 100) return theme.pie.green;
|
||||
else if (value >= 50 && value < 90) return theme.pie.yellow;
|
||||
else if (value >= 0 && value < 50) return theme.pie.red;
|
||||
return theme.pie.default;
|
||||
if (value >= 90 && value <= 100)
|
||||
return {
|
||||
stroke: theme.palette.success.main,
|
||||
strokeBg: theme.palette.success.light,
|
||||
text: theme.palette.success.text,
|
||||
bg: theme.palette.success.bg,
|
||||
shape: "circle",
|
||||
};
|
||||
else if (value >= 50 && value < 90)
|
||||
return {
|
||||
stroke: theme.palette.warning.main,
|
||||
strokeBg: theme.palette.warning.bg,
|
||||
text: theme.palette.warning.text,
|
||||
bg: theme.palette.warning.light,
|
||||
shape: "square",
|
||||
};
|
||||
else if (value >= 0 && value < 50)
|
||||
return {
|
||||
stroke: theme.palette.error.text,
|
||||
strokeBg: theme.palette.error.light,
|
||||
text: theme.palette.error.text,
|
||||
bg: theme.palette.error.bg,
|
||||
shape: "circle",
|
||||
};
|
||||
return {
|
||||
stroke: theme.palette.unresolved.main,
|
||||
strokeBg: theme.palette.unresolved.light,
|
||||
text: theme.palette.unresolved.main,
|
||||
bg: theme.palette.unresolved.bg,
|
||||
shape: "",
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -312,8 +350,15 @@ const PageSpeedDetails = () => {
|
||||
|
||||
let loading = Object.keys(monitor).length === 0;
|
||||
const data = monitor?.checks ? [...monitor.checks].reverse() : [];
|
||||
|
||||
let sharedStyles = {
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
backgroundColor: theme.palette.background.main,
|
||||
};
|
||||
return (
|
||||
<Stack className="page-speed-details" gap={theme.gap.large}>
|
||||
<Stack className="page-speed-details" gap={theme.spacing(12)}>
|
||||
{loading ? (
|
||||
<SkeletonLayout />
|
||||
) : (
|
||||
@@ -324,25 +369,30 @@ const PageSpeedDetails = () => {
|
||||
{ name: "details", path: `/pagespeed/${monitorId}` },
|
||||
]}
|
||||
/>
|
||||
<Stack direction="row" gap={theme.gap.xs}>
|
||||
<Stack direction="row" gap={theme.spacing(2)}>
|
||||
<PulseDot
|
||||
color={
|
||||
monitor?.status
|
||||
? theme.label.up.dotColor
|
||||
: theme.label.down.dotColor
|
||||
? theme.palette.success.main
|
||||
: theme.palette.error.main
|
||||
}
|
||||
/>
|
||||
<Box>
|
||||
<Typography
|
||||
component="h1"
|
||||
mb={theme.gap.xs}
|
||||
mb={theme.spacing(2)}
|
||||
color={theme.palette.text.primary}
|
||||
sx={{ lineHeight: 1 }}
|
||||
>
|
||||
{monitor?.url}
|
||||
</Typography>
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{ color: "var(--env-var-color-17)" }}
|
||||
color={
|
||||
monitor?.status
|
||||
? theme.palette.success.main
|
||||
: theme.palette.error.text
|
||||
}
|
||||
>
|
||||
Your pagespeed monitor is live.
|
||||
</Typography>
|
||||
@@ -353,15 +403,18 @@ const PageSpeedDetails = () => {
|
||||
animate="rotate90"
|
||||
img={
|
||||
<SettingsIcon
|
||||
style={{ width: theme.gap.mlplus, height: theme.gap.mlplus }}
|
||||
style={{
|
||||
width: theme.spacing(10),
|
||||
height: theme.spacing(10),
|
||||
}}
|
||||
/>
|
||||
}
|
||||
onClick={() => navigate(`/pagespeed/configure/${monitorId}`)}
|
||||
sx={{
|
||||
ml: "auto",
|
||||
alignSelf: "flex-end",
|
||||
backgroundColor: theme.palette.otherColors.fillGray,
|
||||
px: theme.gap.medium,
|
||||
backgroundColor: theme.palette.background.fill,
|
||||
px: theme.spacing(6),
|
||||
"& svg": {
|
||||
mr: "6px",
|
||||
},
|
||||
@@ -371,7 +424,7 @@ const PageSpeedDetails = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
gap={theme.gap.large}
|
||||
gap={theme.spacing(12)}
|
||||
flexWrap="wrap"
|
||||
>
|
||||
<StatBox
|
||||
@@ -412,19 +465,39 @@ const PageSpeedDetails = () => {
|
||||
value={formatDurationRounded(monitor?.interval)}
|
||||
></StatBox>
|
||||
</Stack>
|
||||
<Typography component="h2">Score history</Typography>
|
||||
<Box height="300px">
|
||||
<Typography component="h2" color={theme.palette.text.secondary}>
|
||||
Score history
|
||||
</Typography>
|
||||
<Box
|
||||
height="300px"
|
||||
sx={{
|
||||
...sharedStyles,
|
||||
}}
|
||||
>
|
||||
<PageSpeedLineChart pageSpeedChecks={data} />
|
||||
</Box>
|
||||
<Typography component="h2">Performance report</Typography>
|
||||
<Stack direction="row" alignItems="center" overflow="hidden" flex={1}>
|
||||
<Typography component="h2" color={theme.palette.text.secondary}>
|
||||
Performance report
|
||||
</Typography>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
overflow="hidden"
|
||||
flex={1}
|
||||
sx={{
|
||||
"& p": {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
...sharedStyles,
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
alignItems="center"
|
||||
textAlign="center"
|
||||
minWidth="300px"
|
||||
flex={1}
|
||||
px={theme.gap.xl}
|
||||
py={theme.gap.ml}
|
||||
px={theme.spacing(20)}
|
||||
py={theme.spacing(8)}
|
||||
>
|
||||
<Box onMouseLeave={() => setExpand(false)}>
|
||||
{expand ? (
|
||||
@@ -514,12 +587,12 @@ const PageSpeedDetails = () => {
|
||||
</PieChart>
|
||||
)}
|
||||
</Box>
|
||||
<Typography mt={theme.gap.medium}>
|
||||
<Typography mt={theme.spacing(6)}>
|
||||
Values are estimated and may vary.{" "}
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{
|
||||
color: theme.palette.primary.main,
|
||||
color: theme.palette.common.main,
|
||||
fontWeight: 500,
|
||||
textDecoration: "underline",
|
||||
cursor: "pointer",
|
||||
@@ -530,21 +603,23 @@ const PageSpeedDetails = () => {
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Box
|
||||
px={theme.gap.xl}
|
||||
py={theme.gap.ml}
|
||||
px={theme.spacing(20)}
|
||||
py={theme.spacing(8)}
|
||||
height="100%"
|
||||
flex={1}
|
||||
sx={{
|
||||
borderLeft: `solid 1px ${theme.palette.otherColors.graishWhite}`,
|
||||
borderLeft: 1,
|
||||
borderLeftColor: theme.palette.border.light,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
mb={theme.gap.medium}
|
||||
pb={theme.gap.ml}
|
||||
color={theme.palette.secondary.main}
|
||||
mb={theme.spacing(6)}
|
||||
pb={theme.spacing(8)}
|
||||
color={theme.palette.text.secondary}
|
||||
textAlign="center"
|
||||
sx={{
|
||||
borderBottom: `solid 1px ${theme.palette.otherColors.graishWhite}`,
|
||||
borderBottom: 1,
|
||||
borderBottomColor: theme.palette.border.light,
|
||||
borderBottomStyle: "dashed",
|
||||
}}
|
||||
>
|
||||
@@ -552,7 +627,7 @@ const PageSpeedDetails = () => {
|
||||
<Typography
|
||||
component="span"
|
||||
sx={{
|
||||
color: theme.palette.primary.main,
|
||||
color: theme.palette.common.main,
|
||||
fontWeight: 500,
|
||||
textDecoration: "underline",
|
||||
cursor: "pointer",
|
||||
@@ -569,8 +644,8 @@ const PageSpeedDetails = () => {
|
||||
<Stack
|
||||
direction="row"
|
||||
flexWrap="wrap"
|
||||
pt={theme.gap.ml}
|
||||
gap={theme.gap.ml}
|
||||
pt={theme.spacing(8)}
|
||||
gap={theme.spacing(8)}
|
||||
>
|
||||
{Object.keys(audits).map((key) => {
|
||||
if (key === "_id") return;
|
||||
@@ -581,8 +656,8 @@ const PageSpeedDetails = () => {
|
||||
let shape = (
|
||||
<Box
|
||||
sx={{
|
||||
width: theme.gap.medium,
|
||||
height: theme.gap.medium,
|
||||
width: theme.spacing(6),
|
||||
height: theme.spacing(6),
|
||||
borderRadius: "50%",
|
||||
backgroundColor: metricParams.stroke,
|
||||
}}
|
||||
@@ -592,8 +667,8 @@ const PageSpeedDetails = () => {
|
||||
shape = (
|
||||
<Box
|
||||
sx={{
|
||||
width: theme.gap.medium,
|
||||
height: theme.gap.medium,
|
||||
width: theme.spacing(6),
|
||||
height: theme.spacing(6),
|
||||
backgroundColor: metricParams.stroke,
|
||||
}}
|
||||
></Box>
|
||||
@@ -604,10 +679,14 @@ const PageSpeedDetails = () => {
|
||||
sx={{
|
||||
width: 0,
|
||||
height: 0,
|
||||
ml: `calc((${theme.gap.medium} - ${theme.gap.small}) / -2)`,
|
||||
borderLeft: `${theme.gap.small} solid transparent`,
|
||||
borderRight: `${theme.gap.small} solid transparent`,
|
||||
borderBottom: `${theme.gap.medium} solid ${metricParams.stroke}`,
|
||||
ml: `calc((${theme.spacing(6)} - ${theme.spacing(
|
||||
4
|
||||
)}) / -2)`,
|
||||
borderLeft: `${theme.spacing(4)} solid transparent`,
|
||||
borderRight: `${theme.spacing(4)} solid transparent`,
|
||||
borderBottom: `${theme.spacing(6)} solid ${
|
||||
metricParams.stroke
|
||||
}`,
|
||||
}}
|
||||
></Box>
|
||||
);
|
||||
@@ -630,7 +709,7 @@ const PageSpeedDetails = () => {
|
||||
className="metric"
|
||||
key={`${key}-box`}
|
||||
direction="row"
|
||||
gap={theme.gap.small}
|
||||
gap={theme.spacing(4)}
|
||||
>
|
||||
{shape}
|
||||
<Box>
|
||||
@@ -650,8 +729,8 @@ const PageSpeedDetails = () => {
|
||||
component="span"
|
||||
ml="2px"
|
||||
sx={{
|
||||
color: theme.palette.secondary.main,
|
||||
fontSize: "13px",
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: 13,
|
||||
}}
|
||||
>
|
||||
{unit}
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
.page-speed h1.MuiTypography-root,
|
||||
.page-speed span.MuiTypography-root,
|
||||
.page-speed p.MuiTypography-root {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.page-speed h2.MuiTypography-root {
|
||||
color: var(--env-var-color-3);
|
||||
line-height: 1.1;
|
||||
}
|
||||
.page-speed h1.MuiTypography-root,
|
||||
@@ -19,11 +13,6 @@
|
||||
.page-speed p.MuiTypography-root > span.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-small-plus);
|
||||
}
|
||||
.page-speed .MuiGrid-item > .MuiStack-root {
|
||||
border: solid 1px var(--env-var-color-6);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
background-color: var(--env-var-color-8);
|
||||
}
|
||||
.page-speed .label {
|
||||
padding: 7px;
|
||||
height: 22px;
|
||||
@@ -33,6 +22,3 @@
|
||||
height: 34px;
|
||||
align-self: flex-end;
|
||||
}
|
||||
.page-speed [class*="fallback__"] h1 {
|
||||
margin-left: var(--env-var-spacing-3);
|
||||
}
|
||||
|
||||
@@ -22,19 +22,33 @@ const Card = ({ data }) => {
|
||||
item
|
||||
lg={6}
|
||||
flexGrow={1}
|
||||
sx={{ "&:hover > .MuiStack-root": { backgroundColor: "#f9fafb" } }}
|
||||
sx={{
|
||||
"&:hover > .MuiStack-root": {
|
||||
backgroundColor: "var(--primary-bg-accent)",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.gap.medium}
|
||||
p={theme.gap.ml}
|
||||
gap={theme.spacing(6)}
|
||||
p={theme.spacing(8)}
|
||||
onClick={() => navigate(`/pagespeed/${data._id}`)}
|
||||
border={1}
|
||||
borderColor={theme.palette.border.light}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
backgroundColor={theme.palette.background.main}
|
||||
sx={{ cursor: "pointer" }}
|
||||
>
|
||||
<PageSpeedIcon style={{ width: theme.gap.ml, height: theme.gap.ml }} />
|
||||
<PageSpeedIcon
|
||||
style={{ width: theme.spacing(8), height: theme.spacing(8) }}
|
||||
/>
|
||||
<Box flex={1}>
|
||||
<Stack direction="row" justifyContent="space-between">
|
||||
<Typography component="h2" mb={theme.gap.xs}>
|
||||
<Typography
|
||||
component="h2"
|
||||
mb={theme.spacing(2)}
|
||||
color={theme.palette.common.main}
|
||||
>
|
||||
{data.name}
|
||||
</Typography>
|
||||
<StatusLabel
|
||||
@@ -43,7 +57,7 @@ const Card = ({ data }) => {
|
||||
/>
|
||||
</Stack>
|
||||
<Typography>{data.url.replace(/^https?:\/\//, "")}</Typography>
|
||||
<Typography mt={theme.gap.large}>
|
||||
<Typography mt={theme.spacing(12)}>
|
||||
<Typography component="span" fontWeight={600}>
|
||||
Last checked:{" "}
|
||||
</Typography>
|
||||
@@ -71,11 +85,11 @@ const SkeletonLayout = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Stack gap={theme.gap.xs}>
|
||||
<Stack gap={theme.spacing(2)}>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
mb={theme.gap.large}
|
||||
mb={theme.spacing(12)}
|
||||
>
|
||||
<Box width="80%">
|
||||
<Skeleton variant="rounded" width="25%" height={24} />
|
||||
@@ -83,7 +97,7 @@ const SkeletonLayout = () => {
|
||||
variant="rounded"
|
||||
width="50%"
|
||||
height={19.5}
|
||||
sx={{ mt: theme.gap.xs }}
|
||||
sx={{ mt: theme.spacing(2) }}
|
||||
/>
|
||||
</Box>
|
||||
<Skeleton
|
||||
@@ -93,7 +107,7 @@ const SkeletonLayout = () => {
|
||||
sx={{ alignSelf: "flex-end" }}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack direction="row" flexWrap="wrap" gap={theme.gap.large}>
|
||||
<Stack direction="row" flexWrap="wrap" gap={theme.spacing(12)}>
|
||||
<Skeleton
|
||||
variant="rounded"
|
||||
width="100%"
|
||||
@@ -141,19 +155,40 @@ const PageSpeed = ({ isAdmin }) => {
|
||||
let isActuallyLoading = isLoading && monitors.length === 0;
|
||||
|
||||
return (
|
||||
<Box className="page-speed" pt={theme.gap.xl}>
|
||||
<Box
|
||||
className="page-speed"
|
||||
pt={theme.spacing(20)}
|
||||
sx={{
|
||||
':has(> [class*="fallback__"])': {
|
||||
position: "relative",
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderStyle: "dashed",
|
||||
backgroundColor: theme.palette.background.main,
|
||||
overflow: "hidden",
|
||||
},
|
||||
}}
|
||||
>
|
||||
{isActuallyLoading ? (
|
||||
<SkeletonLayout />
|
||||
) : monitors?.length !== 0 ? (
|
||||
<Stack gap={theme.gap.xs}>
|
||||
<Stack
|
||||
gap={theme.spacing(2)}
|
||||
sx={{
|
||||
"& h1, & span.MuiTypography-root, & p": {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
mb={theme.gap.large}
|
||||
mb={theme.spacing(12)}
|
||||
>
|
||||
<Box>
|
||||
<Typography component="h1">All page speed monitors</Typography>
|
||||
<Typography mt={theme.gap.xs}>
|
||||
<Typography mt={theme.spacing(2)}>
|
||||
Click on one of the monitors to get more site speed information.
|
||||
</Typography>
|
||||
</Box>
|
||||
@@ -163,7 +198,7 @@ const PageSpeed = ({ isAdmin }) => {
|
||||
onClick={() => navigate("/pagespeed/create")}
|
||||
/>
|
||||
</Stack>
|
||||
<Grid container spacing={theme.gap.large}>
|
||||
<Grid container spacing={theme.spacing(12)}>
|
||||
{monitors?.map((monitor) => (
|
||||
<Card data={monitor} key={`monitor-${monitor._id}`} />
|
||||
))}
|
||||
@@ -171,7 +206,7 @@ const PageSpeed = ({ isAdmin }) => {
|
||||
</Stack>
|
||||
) : (
|
||||
<Fallback
|
||||
title="page speed"
|
||||
title="pagespeed monitor"
|
||||
checks={[
|
||||
"Report on the user experience of a page",
|
||||
"Help analyze webpage speed",
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
.settings p.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-medium);
|
||||
}
|
||||
.settings h1.MuiTypography-root,
|
||||
.settings h2.MuiTypography-root,
|
||||
.settings p.MuiTypography-root {
|
||||
color: var(--env-var-color-5);
|
||||
}
|
||||
.settings h1.MuiTypography-root {
|
||||
font-size: var(--env-var-font-size-large);
|
||||
}
|
||||
@@ -23,21 +18,4 @@
|
||||
.settings span.MuiTypography-root {
|
||||
opacity: 0.6;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.settings .config-box {
|
||||
padding: var(--env-var-spacing-4) 50px;
|
||||
padding-bottom: 60px;
|
||||
border: 1px solid var(--env-var-color-16);
|
||||
border-radius: var(--env-var-radius-1);
|
||||
background-color: var(--env-var-color-8);
|
||||
}
|
||||
.settings .config-box .MuiBox-root,
|
||||
.settings .config-box .MuiStack-root {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.settings-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--env-var-spacing-4);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { Box, Stack, Typography } from "@mui/material";
|
||||
import { Box, Stack, styled, Typography } from "@mui/material";
|
||||
import Button from "../../Components/Button";
|
||||
import Field from "../../Components/Inputs/Field";
|
||||
import Link from "../../Components/Link";
|
||||
@@ -10,6 +10,33 @@ import "./index.css";
|
||||
const Settings = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
const ConfigBox = styled("div")({
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
gap: theme.spacing(20),
|
||||
paddingTop: theme.spacing(12),
|
||||
paddingInline: theme.spacing(15),
|
||||
paddingBottom: theme.spacing(25),
|
||||
backgroundColor: theme.palette.background.main,
|
||||
border: 1,
|
||||
borderStyle: "solid",
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.spacing(2),
|
||||
"& > div:first-of-type": {
|
||||
flex: 0.7,
|
||||
},
|
||||
"& > div:last-of-type": {
|
||||
flex: 1,
|
||||
},
|
||||
"& h1, & h2": {
|
||||
color: theme.palette.text.secondary,
|
||||
},
|
||||
"& p": {
|
||||
color: theme.palette.text.tertiary,
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="settings"
|
||||
@@ -17,16 +44,16 @@ const Settings = () => {
|
||||
paddingBottom: 0,
|
||||
}}
|
||||
>
|
||||
<form className="settings-form" noValidate spellCheck="false">
|
||||
<Stack
|
||||
className="config-box"
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
gap={theme.gap.xxl}
|
||||
>
|
||||
<Stack
|
||||
component="form"
|
||||
gap={theme.spacing(12)}
|
||||
noValidate
|
||||
spellCheck="false"
|
||||
>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h1">General Settings</Typography>
|
||||
<Typography sx={{ mt: theme.gap.small, mb: theme.gap.xs }}>
|
||||
<Typography sx={{ mt: theme.spacing(2), mb: theme.spacing(2) }}>
|
||||
<Typography component="span">Display timezone</Typography>- The
|
||||
timezone of the dashboard you publicly display.
|
||||
</Typography>
|
||||
@@ -35,7 +62,7 @@ const Settings = () => {
|
||||
timezone of your server.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.gap.xl}>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
<Select
|
||||
id="display-timezone"
|
||||
label="Display timezone"
|
||||
@@ -51,21 +78,16 @@ const Settings = () => {
|
||||
items={[{ _id: "est", name: "America / Toronto" }]}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack
|
||||
className="config-box"
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
gap={theme.gap.xxl}
|
||||
>
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h1">History and monitoring</Typography>
|
||||
<Typography sx={{ mt: theme.gap.small }}>
|
||||
<Typography sx={{ mt: theme.spacing(2) }}>
|
||||
Define here for how long you want to keep the data. You can also
|
||||
remove all past data.
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.gap.xl}>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
<Field
|
||||
type="text"
|
||||
id="history-monitoring"
|
||||
@@ -81,24 +103,19 @@ const Settings = () => {
|
||||
<Button
|
||||
level="error"
|
||||
label="Clear all stats"
|
||||
sx={{ mt: theme.gap.small }}
|
||||
sx={{ mt: theme.spacing(4) }}
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
<Stack
|
||||
className="config-box"
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
gap={theme.gap.xxl}
|
||||
>
|
||||
</ConfigBox>
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h1">About</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography component="h2">BlueWave Uptime v1.0.0</Typography>
|
||||
<Typography
|
||||
sx={{ mt: theme.gap.xs, mb: theme.gap.medium, opacity: 0.6 }}
|
||||
sx={{ mt: theme.spacing(2), mb: theme.spacing(6), opacity: 0.6 }}
|
||||
>
|
||||
Developed by Bluewave Labs.
|
||||
</Typography>
|
||||
@@ -108,11 +125,15 @@ const Settings = () => {
|
||||
label="https://github.com/bluewave-labs"
|
||||
/>
|
||||
</Box>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
<Stack direction="row" justifyContent="flex-end">
|
||||
<Button level="primary" label="Save" sx={{ px: theme.gap.large }} />
|
||||
<Button
|
||||
level="primary"
|
||||
label="Save"
|
||||
sx={{ px: theme.spacing(12), mt: theme.spacing(20) }}
|
||||
/>
|
||||
</Stack>
|
||||
</form>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,25 @@
|
||||
import { Box } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import Fallback from "../../Components/Fallback";
|
||||
|
||||
const Status = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<div className="status">
|
||||
<Box
|
||||
className="status"
|
||||
sx={{
|
||||
':has(> [class*="fallback__"])': {
|
||||
position: "relative",
|
||||
border: 1,
|
||||
borderColor: theme.palette.border.light,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
borderStyle: "dashed",
|
||||
backgroundColor: theme.palette.background.main,
|
||||
overflow: "hidden",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Fallback
|
||||
title="status page"
|
||||
checks={[
|
||||
@@ -11,7 +28,7 @@ const Status = () => {
|
||||
"Build trust with your customers",
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
import { createTheme } from "@mui/material";
|
||||
|
||||
// Colors for MUI theme
|
||||
const primaryMain = "#1570EF";
|
||||
const secondaryMain = "#475467";
|
||||
const tertiaryMain = "#475467";
|
||||
const tertiaryLinkHover = "#eff8ff";
|
||||
|
||||
// Label Colors
|
||||
const labelOrange = "#F79009";
|
||||
const labelGray = "#475467";
|
||||
const labelPurple = "#6941C6";
|
||||
const labelGreen = "#067647";
|
||||
const labelRed = "#F04438";
|
||||
|
||||
// Section colors
|
||||
const sectionBorder = "#D0D5DD";
|
||||
const sectionBg = "#F8F9F8";
|
||||
|
||||
// Other Colors
|
||||
const otherColorsBlackish = "#101828";
|
||||
const otherColorsBluishGray = "#344054";
|
||||
const otherColorsGraishWhite = "#eaecf0";
|
||||
const otherColorsPurple = "#7f56d9";
|
||||
const otherColorsWhite = "#fff";
|
||||
const otherColorsGraishWhiteLight = "#f2f2f2";
|
||||
const otherColorsStrongBlue = "#f2f2f2";
|
||||
const otherColorsSlateGray = "#667085";
|
||||
const otherColorsFillGray = "#f4f4f4";
|
||||
|
||||
//box shadow
|
||||
const shadow =
|
||||
"0px 4px 24px -4px rgba(16, 24, 40, 0.08), 0px 3px 3px -3px rgba(16, 24, 40, 0.03)";
|
||||
|
||||
// Global Font Family
|
||||
const fontFamilyDefault =
|
||||
'"Inter","system-ui", "Avenir", "Helvetica", "Arial", sans-serif';
|
||||
|
||||
const theme = createTheme({
|
||||
typography: { fontFamily: fontFamilyDefault, fontSize: 13 },
|
||||
palette: {
|
||||
primary: {
|
||||
main: primaryMain,
|
||||
},
|
||||
secondary: {
|
||||
main: secondaryMain,
|
||||
},
|
||||
tertiary: {
|
||||
main: tertiaryMain,
|
||||
linkHover: tertiaryLinkHover,
|
||||
},
|
||||
labelOrange: {
|
||||
color: labelOrange,
|
||||
},
|
||||
labelGray: {
|
||||
color: labelGray,
|
||||
},
|
||||
labelPurple: {
|
||||
color: labelPurple,
|
||||
},
|
||||
labelGreen: {
|
||||
color: labelGreen,
|
||||
},
|
||||
labelRed: {
|
||||
color: labelRed,
|
||||
},
|
||||
section: {
|
||||
borderColor: sectionBorder,
|
||||
bgColor: sectionBg,
|
||||
},
|
||||
otherColors: {
|
||||
blackish: otherColorsBlackish,
|
||||
bluishGray: otherColorsBluishGray,
|
||||
graishWhite: otherColorsGraishWhite,
|
||||
purple: otherColorsPurple,
|
||||
white: otherColorsWhite,
|
||||
graishWhiteLight: otherColorsGraishWhiteLight,
|
||||
strongBlue: otherColorsStrongBlue,
|
||||
slateGray: otherColorsSlateGray,
|
||||
fillGray: otherColorsFillGray,
|
||||
},
|
||||
},
|
||||
shape: {
|
||||
borderRadius: 4,
|
||||
borderThick: 2,
|
||||
boxShadow: shadow,
|
||||
},
|
||||
content: {
|
||||
pX: "80px",
|
||||
pY: "40px",
|
||||
},
|
||||
gap: {
|
||||
xs: "4px",
|
||||
small: "8px",
|
||||
medium: "12px",
|
||||
ml: "16px",
|
||||
mlplus: "20px",
|
||||
large: "24px",
|
||||
lgplus: "32px",
|
||||
xl: "40px",
|
||||
xxl: "60px",
|
||||
triplexl: "120px",
|
||||
},
|
||||
alert: {
|
||||
info: {
|
||||
color: secondaryMain,
|
||||
bg: otherColorsWhite,
|
||||
border: sectionBorder,
|
||||
},
|
||||
error: {
|
||||
color: "#d92d20",
|
||||
bg: "hsla(0, 100%, 52%, 0.03)",
|
||||
border: "#f04438",
|
||||
},
|
||||
warning: {
|
||||
color: "#DC6803",
|
||||
bg: "#fffcf5",
|
||||
border: "#fec84b",
|
||||
},
|
||||
},
|
||||
//add more as needed
|
||||
label: {
|
||||
up: {
|
||||
borderColor: "#D4F4E1",
|
||||
bgColor: "#ecfdf3",
|
||||
dotColor: "#17B26A",
|
||||
},
|
||||
down: {
|
||||
borderColor: "#fbd1d1",
|
||||
bgColor: "#f9eced",
|
||||
dotColor: "#f04438",
|
||||
},
|
||||
"cannot resolve": {
|
||||
borderColor: "#e2eaf7",
|
||||
bgColor: "#f2f4f7",
|
||||
dotColor: "#4e5ba6",
|
||||
},
|
||||
},
|
||||
pie: {
|
||||
green: {
|
||||
stroke: "#17b26a",
|
||||
strokeBg: "#d4f4e1",
|
||||
text: "#079455",
|
||||
bg: "#ecfdf3",
|
||||
shape: "circle",
|
||||
},
|
||||
yellow: {
|
||||
stroke: "#fdb022",
|
||||
strokeBg: "rgba(255, 192, 34, 0.3)",
|
||||
text: "#dc6803",
|
||||
bg: "#fffcf5",
|
||||
shape: "square",
|
||||
},
|
||||
red: {
|
||||
stroke: "#f04438",
|
||||
strokeBg: "#ffecea",
|
||||
text: "#f04438",
|
||||
bg: "#ffeaea",
|
||||
shape: "triangle",
|
||||
},
|
||||
default: {
|
||||
stroke: "#4e5ba6",
|
||||
strokeBg: "#f2f4f7",
|
||||
text: "#4e5ba6",
|
||||
bg: "#f2f4f7",
|
||||
shape: "",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default theme;
|
||||
181
Client/src/Utils/Theme/darkTheme.js
Normal file
181
Client/src/Utils/Theme/darkTheme.js
Normal file
@@ -0,0 +1,181 @@
|
||||
import { createTheme } from "@mui/material";
|
||||
|
||||
const text = {
|
||||
primary: "#fafafa",
|
||||
secondary: "#e6e6e6",
|
||||
tertiary: "#a1a1aa",
|
||||
accent: "#e6e6e6",
|
||||
disabled: "rgba(172, 172, 172, 0.3)",
|
||||
};
|
||||
const background = {
|
||||
main: "#151518",
|
||||
alt: "#09090b",
|
||||
fill: "#2e2e2e",
|
||||
accent: "#18181a",
|
||||
};
|
||||
const border = { light: "#27272a", dark: "#2c2c2c" };
|
||||
|
||||
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)";
|
||||
|
||||
const darkTheme = createTheme({
|
||||
typography: { fontFamily: fontFamilyDefault, fontSize: 13 },
|
||||
palette: {
|
||||
common: { main: "#1570ef" },
|
||||
text: text,
|
||||
background: background,
|
||||
border: border,
|
||||
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" },
|
||||
divider: border.light,
|
||||
other: {
|
||||
icon: "#e6e6e6",
|
||||
line: "#27272a",
|
||||
fill: "#18181a",
|
||||
},
|
||||
// TO BE REMOVED //
|
||||
primary: {
|
||||
main: "#1570ef",
|
||||
},
|
||||
secondary: {
|
||||
main: "#e6e6e6",
|
||||
},
|
||||
tertiary: {
|
||||
main: "#e6e6e6",
|
||||
},
|
||||
// ----------------- //
|
||||
},
|
||||
spacing: 2,
|
||||
components: {
|
||||
MuiButtonBase: {
|
||||
defaultProps: {
|
||||
disableRipple: true,
|
||||
},
|
||||
},
|
||||
MuiIconButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
padding: 4,
|
||||
"&: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,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiList: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
padding: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
shape: {
|
||||
borderRadius: 2,
|
||||
borderThick: 2,
|
||||
boxShadow: shadow,
|
||||
},
|
||||
});
|
||||
|
||||
export default darkTheme;
|
||||
181
Client/src/Utils/Theme/lightTheme.js
Normal file
181
Client/src/Utils/Theme/lightTheme.js
Normal file
@@ -0,0 +1,181 @@
|
||||
import { createTheme } from "@mui/material";
|
||||
|
||||
const text = {
|
||||
primary: "#1c2130",
|
||||
secondary: "#344054",
|
||||
tertiary: "#475467",
|
||||
accent: "#838c99",
|
||||
};
|
||||
const background = {
|
||||
main: "#FFFFFF",
|
||||
alt: "#FCFCFD",
|
||||
fill: "#F4F4F4",
|
||||
accent: "#f9fafb",
|
||||
};
|
||||
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)";
|
||||
|
||||
const lightTheme = createTheme({
|
||||
typography: { fontFamily: fontFamilyDefault, fontSize: 13 },
|
||||
palette: {
|
||||
mode: "dark",
|
||||
common: { main: "#1570ef" },
|
||||
text: text,
|
||||
background: background,
|
||||
border: border,
|
||||
info: {
|
||||
text: "#475467",
|
||||
main: "#475467",
|
||||
bg: "#ffffff",
|
||||
light: "#ffffff",
|
||||
border: "#D0D5DD",
|
||||
},
|
||||
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: "#fffcf5",
|
||||
bg: "#ffecbc",
|
||||
border: "#fec84b",
|
||||
},
|
||||
unresolved: { main: "#4e5ba6", light: "#e2eaf7", bg: "#f2f4f7" },
|
||||
divider: border.light,
|
||||
other: {
|
||||
icon: "#667085",
|
||||
line: "#d6d9dd",
|
||||
fill: "#e3e3e3",
|
||||
},
|
||||
// TO BE REMOVED //
|
||||
primary: {
|
||||
main: "#1570EF",
|
||||
},
|
||||
secondary: {
|
||||
main: "#475467",
|
||||
},
|
||||
tertiary: {
|
||||
main: "#475467",
|
||||
},
|
||||
// ----------------- //
|
||||
},
|
||||
spacing: 2,
|
||||
components: {
|
||||
MuiButtonBase: {
|
||||
defaultProps: {
|
||||
disableRipple: true,
|
||||
},
|
||||
},
|
||||
MuiIconButton: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
padding: 4,
|
||||
"&:hover": {
|
||||
backgroundColor: background.fill,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiPaper: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
marginTop: 4,
|
||||
border: 1,
|
||||
borderStyle: "solid",
|
||||
borderColor: border.light,
|
||||
borderRadius: 4,
|
||||
boxShadow: shadow,
|
||||
backgroundColor: background.main,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiList: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
padding: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiMenuItem: {
|
||||
styleOverrides: {
|
||||
root: {
|
||||
borderRadius: 4,
|
||||
backgroundColor: "inherit",
|
||||
padding: "4px 6px",
|
||||
color: text.secondary,
|
||||
fontSize: 13,
|
||||
margin: 2,
|
||||
marginBottom: 0,
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
shape: {
|
||||
borderRadius: 2,
|
||||
borderThick: 2,
|
||||
boxShadow: shadow,
|
||||
},
|
||||
});
|
||||
|
||||
export default lightTheme;
|
||||
5
Client/src/assets/icons/dots-vertical.svg
Normal file
5
Client/src/assets/icons/dots-vertical.svg
Normal file
@@ -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 |
4
Client/src/assets/icons/radio-checked.svg
Normal file
4
Client/src/assets/icons/radio-checked.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8Z" fill="#1570EF"/>
|
||||
<path d="M5 8C5 6.34315 6.34315 5 8 5C9.65685 5 11 6.34315 11 8C11 9.65685 9.65685 11 8 11C6.34315 11 5 9.65685 5 8Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 371 B |
@@ -7,59 +7,12 @@
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-weight: 400;
|
||||
|
||||
/* color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424; */
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
/* Generalized Stylings */
|
||||
|
||||
--env-var-font-familt-1: "Roboto", "Helvetica", "Arial", sans-serif;
|
||||
|
||||
--env-var-color-0: #fff;
|
||||
--env-var-color-1: #1c2130;
|
||||
--env-var-color-2: #475467;
|
||||
--env-var-color-2-light: #838c99;
|
||||
--env-var-color-3: #1570ef;
|
||||
|
||||
--env-var-color-4: #d0d5dd;
|
||||
--env-var-color-5: #344054;
|
||||
--env-var-color-6: #eaecf0;
|
||||
--env-var-color-7: #7f56d9;
|
||||
--env-var-color-8: #fff;
|
||||
--env-var-color-9: #f2f2f2;
|
||||
--env-var-color-10: #175cd3;
|
||||
--env-var-color-11: #5d6b98;
|
||||
--env-var-color-12: #182230;
|
||||
--env-var-color-13: #f9fafb;
|
||||
--env-var-color-14: #98a2b3;
|
||||
--env-var-color-15: #f2f4f7;
|
||||
--env-var-color-16: #ebebeb;
|
||||
--env-var-color-17: #079455;
|
||||
--env-var-color-18: #ffc301;
|
||||
--env-var-color-19: #f04438;
|
||||
--env-var-color-20: #ecfdf3;
|
||||
--env-var-color-21: hsla(0, 100%, 52%, 0.06);
|
||||
--env-var-color-22: #4e5ba6;
|
||||
--env-var-color-23: #17b26a;
|
||||
--env-var-color-24: #d92d20;
|
||||
--env-var-color-25: #667085;
|
||||
--env-var-color-26: #ff1818;
|
||||
|
||||
--env-var-color-27: #fecf60;
|
||||
--lighter-env-var-color-27: rgba(254, 207, 96, 0.1);
|
||||
--env-var-color-28: #f79009;
|
||||
--env-var-color-29: #d0d5dd;
|
||||
--env-var-color-30: #fcfcfd;
|
||||
--env-var-color-31: #1a1919;
|
||||
--env-var-color-32: #f5f5f5;
|
||||
--env-var-color-33: rgba(83, 83, 83, 0.1);
|
||||
|
||||
--env-var-radius-1: 4px;
|
||||
--env-var-radius-2: 8px;
|
||||
|
||||
@@ -99,48 +52,12 @@
|
||||
0px 3px 3px -3px rgba(16, 24, 40, 0.03);
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-width: 320px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
.MuiInputBase-root.Mui-disabled input {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -165,7 +82,7 @@ button:focus-visible {
|
||||
}
|
||||
|
||||
body .MuiSkeleton-root {
|
||||
background-color: var(--env-var-color-15);
|
||||
background-color: #f2f4f7;
|
||||
}
|
||||
|
||||
@keyframes ripple {
|
||||
@@ -178,18 +95,3 @@ body .MuiSkeleton-root {
|
||||
transform: scale(2);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user