Merged the conflict theme file.
This commit is contained in:
commit
b3bfab05e6
24 files changed
+388
-168
No files matched your search
@@ -16,7 +16,7 @@ Mention the issue number(s) this PR addresses (e.g., #123).
|
||||
- [ ] I have labelled the PR correctly.
|
||||
- [ ] The issue I am working on is assigned to me.
|
||||
- [ ] I didn't use any hardcoded values (otherwise it will not scale, and will make it difficult to maintain consistency across the application).
|
||||
- [ ] I made sure font sizes, color choices etc are all referenced from the theme.
|
||||
- [ ] I made sure font sizes, color choices etc are all referenced from the theme. I have no hardcoded dimensions.
|
||||
- [ ] My PR is granular and targeted to one specific feature.
|
||||
- [ ] I took a screenshot or a video and attached to this PR if there is a UI change.
|
||||
|
||||
Generated
+7
-7
@@ -33,7 +33,7 @@
|
||||
"immutability-helper": "^3.1.1",
|
||||
"joi": "17.13.3",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"maplibre-gl": "5.1.1",
|
||||
"maplibre-gl": "5.2.0",
|
||||
"mui-color-input": "^5.0.1",
|
||||
"react": "18.3.1",
|
||||
"react-dnd": "^16.0.1",
|
||||
@@ -14113,9 +14113,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/maplibre-gl": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.1.1.tgz",
|
||||
"integrity": "sha512-0Z6ODzyFu/grwT6K1eIBpv6MZE4xnJD1AV+Yq1hPzOh/YCY36r9BlSaU7d7n2/HJOaoKOy0b2YF8cS4dD+iEVQ==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.2.0.tgz",
|
||||
"integrity": "sha512-9zZKD0M80qtDsqBet+EDuAhoCeA/cnAuZAA0p3hcGKGbyjM/SH+R6wQvnBEgvJz9UhDynnkoKdUwhI+fUkHoXQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@mapbox/geojson-rewind": "^0.5.2",
|
||||
@@ -15642,9 +15642,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz",
|
||||
"integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==",
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz",
|
||||
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@
|
||||
"i18next": "^24.2.2",
|
||||
"immutability-helper": "^3.1.1",
|
||||
"joi": "17.13.3",
|
||||
"maplibre-gl": "5.1.1",
|
||||
"maplibre-gl": "5.2.0",
|
||||
"mui-color-input": "^5.0.1",
|
||||
"react": "18.3.1",
|
||||
"react-dnd": "^16.0.1",
|
||||
|
||||
@@ -36,8 +36,8 @@ const Alert = ({ variant, title, body, isToast, hasIcon = true, onClick }) => {
|
||||
*/
|
||||
|
||||
const text = theme.palette.secondary.contrastText;
|
||||
const bg = theme.palette.secondary.main;
|
||||
const border = theme.palette.secondary.contrastText;
|
||||
const border = theme.palette.alert.contrastText;
|
||||
const bg = theme.palette.alert.main;
|
||||
const icon = icons[variant];
|
||||
|
||||
return (
|
||||
|
||||
@@ -62,6 +62,7 @@ const CustomGauge = ({ progress = 0, radius = 70, strokeWidth = 15, threshold =
|
||||
className="radial-chart"
|
||||
width={radius}
|
||||
height={radius}
|
||||
sx={{ backgroundColor: theme.palette.primary.main, borderRadius: "50%" }}
|
||||
>
|
||||
<svg
|
||||
viewBox={`0 0 ${totalSize} ${totalSize}`}
|
||||
@@ -70,7 +71,7 @@ const CustomGauge = ({ progress = 0, radius = 70, strokeWidth = 15, threshold =
|
||||
>
|
||||
<circle
|
||||
className="radial-chart-base"
|
||||
stroke={theme.palette.primary.lowContrast} // CAIO_REVIEW
|
||||
stroke={theme.palette.secondary.light} // CAIO_REVIEW
|
||||
strokeWidth={strokeWidth}
|
||||
fill="none"
|
||||
cx={totalSize / 2} // Center the circle
|
||||
@@ -98,8 +99,8 @@ const CustomGauge = ({ progress = 0, radius = 70, strokeWidth = 15, threshold =
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
...theme.typography.body2,
|
||||
fill: theme.typography.body2.color,
|
||||
...theme.typography.h2,
|
||||
fill: theme.typography.h2.color,
|
||||
}}
|
||||
>
|
||||
{`${progressWithinRange.toFixed(1)}%`}
|
||||
|
||||
@@ -63,35 +63,29 @@ const menu = [
|
||||
|
||||
{ name: "Status pages", path: "status", icon: <StatusPages /> },
|
||||
{ name: "Maintenance", path: "maintenance", icon: <Maintenance /> },
|
||||
// { name: "Integrations", path: "integrations", icon: <Integrations /> },
|
||||
{
|
||||
name: "Account",
|
||||
icon: <Account />,
|
||||
nested: [
|
||||
{ name: "Profile", path: "account/profile", icon: <UserSvg /> },
|
||||
{ name: "Password", path: "account/password", icon: <LockSvg /> },
|
||||
{ name: "Team", path: "account/team", icon: <TeamSvg /> },
|
||||
],
|
||||
},
|
||||
{ name: "Integrations", path: "integrations", icon: <Integrations /> },
|
||||
{
|
||||
name: "Settings",
|
||||
icon: <Settings />,
|
||||
path: "settings",
|
||||
},
|
||||
];
|
||||
|
||||
const otherMenuItems = [
|
||||
{ name: "Support", path: "support", icon: <Support /> },
|
||||
{
|
||||
name: "Other",
|
||||
icon: <Folder />,
|
||||
nested: [
|
||||
{ name: "Support", path: "support", icon: <Support /> },
|
||||
{
|
||||
name: "Discussions",
|
||||
path: "discussions",
|
||||
icon: <Discussions />,
|
||||
},
|
||||
{ name: "Docs", path: "docs", icon: <Docs /> },
|
||||
{ name: "Changelog", path: "changelog", icon: <ChangeLog /> },
|
||||
],
|
||||
name: "Discussions",
|
||||
path: "discussions",
|
||||
icon: <Discussions />,
|
||||
},
|
||||
{ name: "Docs", path: "docs", icon: <Docs /> },
|
||||
{ name: "Changelog", path: "changelog", icon: <ChangeLog /> },
|
||||
];
|
||||
|
||||
const accountMenuItems = [
|
||||
{ name: "Profile", path: "account/profile", icon: <UserSvg /> },
|
||||
{ name: "Password", path: "account/password", icon: <LockSvg /> },
|
||||
{ name: "Team", path: "account/team", icon: <TeamSvg /> },
|
||||
];
|
||||
|
||||
/* TODO this could be a key in nested Path would be the link */
|
||||
@@ -133,12 +127,38 @@ function Sidebar() {
|
||||
(state) => state.ui.distributedUptimeEnabled
|
||||
);
|
||||
|
||||
const accountMenuItem = menu.find((item) => item.name === "Account");
|
||||
if (user.role?.includes("demo") && accountMenuItem) {
|
||||
accountMenuItem.nested = accountMenuItem.nested.filter((item) => {
|
||||
return item.name !== "Password";
|
||||
});
|
||||
}
|
||||
const renderAccountMenuItems = () => {
|
||||
let filteredAccountMenuItems = [...accountMenuItems];
|
||||
|
||||
// If the user is in demo mode, remove the "Password" option
|
||||
if (user.role?.includes("demo")) {
|
||||
filteredAccountMenuItems = filteredAccountMenuItems.filter(
|
||||
(item) => item.name !== "Password"
|
||||
);
|
||||
}
|
||||
|
||||
// If the user is NOT a superadmin, remove the "Team" option
|
||||
if (user.role && !user.role.includes("superadmin")) {
|
||||
filteredAccountMenuItems = filteredAccountMenuItems.filter(
|
||||
(item) => item.name !== "Team"
|
||||
);
|
||||
}
|
||||
|
||||
return filteredAccountMenuItems.map((item) => (
|
||||
<MenuItem
|
||||
key={item.name}
|
||||
onClick={() => navigate(item.path)}
|
||||
sx={{
|
||||
gap: theme.spacing(2),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
pl: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
{item.icon}
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
));
|
||||
};
|
||||
|
||||
const openPopup = (event, id) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
@@ -260,7 +280,6 @@ function Sidebar() {
|
||||
>
|
||||
{collapsed ? <ArrowRight /> : <ArrowLeft />}
|
||||
</IconButton>
|
||||
|
||||
{/* TODO Alignment done using padding. Use single source of truth to that*/}
|
||||
<Stack
|
||||
pt={theme.spacing(6)}
|
||||
@@ -588,8 +607,59 @@ function Sidebar() {
|
||||
})}
|
||||
</List>
|
||||
</Box>
|
||||
<Divider sx={{ mt: "auto", borderColor: theme.palette.primary.lowContrast }} />
|
||||
|
||||
<List
|
||||
component="nav"
|
||||
disablePadding
|
||||
sx={{ px: theme.spacing(6) }}
|
||||
>
|
||||
{otherMenuItems.map((item) => {
|
||||
return item.path ? (
|
||||
<Tooltip
|
||||
key={item.path}
|
||||
placement="right"
|
||||
title={collapsed ? item.name : ""}
|
||||
slotProps={{
|
||||
popper: {
|
||||
modifiers: [
|
||||
{
|
||||
name: "offset",
|
||||
options: {
|
||||
offset: [0, -16],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}}
|
||||
disableInteractive
|
||||
>
|
||||
<ListItemButton
|
||||
className={
|
||||
location.pathname.startsWith(`/${item.path}`) ? "selected-path" : ""
|
||||
}
|
||||
onClick={() => {
|
||||
const url = URL_MAP[item.path];
|
||||
if (url) {
|
||||
window.open(url, "_blank", "noreferrer");
|
||||
} else {
|
||||
navigate(`/${item.path}`);
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
height: "37px",
|
||||
gap: theme.spacing(4),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
px: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 0 }}>{item.icon} </ListItemIcon>
|
||||
<ListItemText>{item.name} </ListItemText>{" "}
|
||||
</ListItemButton>
|
||||
</Tooltip>
|
||||
) : null;
|
||||
})}
|
||||
</List>
|
||||
<Divider sx={{ mt: "auto", borderColor: theme.palette.primary.lowContrast }} />
|
||||
<Stack
|
||||
direction="row"
|
||||
height="50px"
|
||||
@@ -711,14 +781,19 @@ function Sidebar() {
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
ml: theme.spacing(8),
|
||||
ml: theme.spacing(4),
|
||||
}}
|
||||
>
|
||||
{collapsed && (
|
||||
<MenuItem sx={{ cursor: "default", minWidth: "50%" }}>
|
||||
<Box
|
||||
mb={theme.spacing(2)}
|
||||
sx={{ maxWidth: "50%", overflow: "hidden" }}
|
||||
sx={{
|
||||
minWidth: "50%",
|
||||
maxWidth: "max-content",
|
||||
overflow: "visible",
|
||||
whiteSpace: "nowrap",
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
@@ -727,13 +802,22 @@ function Sidebar() {
|
||||
sx={{
|
||||
display: "block",
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "hidden",
|
||||
textOverflow: "ellipsis",
|
||||
overflow: "visible",
|
||||
// wordBreak: "break-word",
|
||||
textOverflow: "clip",
|
||||
}}
|
||||
>
|
||||
{authState.user?.firstName} {authState.user?.lastName}
|
||||
</Typography>
|
||||
<Typography sx={{ textTransform: "capitalize", fontSize: 12 }}>
|
||||
<Typography
|
||||
sx={{
|
||||
textTransform: "capitalize",
|
||||
fontSize: 12,
|
||||
whiteSpace: "nowrap",
|
||||
overflow: "visible",
|
||||
// wordBreak: "break-word",
|
||||
}}
|
||||
>
|
||||
{authState.user?.role}
|
||||
</Typography>
|
||||
</Box>
|
||||
@@ -741,7 +825,8 @@ function Sidebar() {
|
||||
)}
|
||||
{/* TODO Do we need two dividers? */}
|
||||
{collapsed && <Divider />}
|
||||
<Divider />
|
||||
{/* <Divider /> */}
|
||||
{renderAccountMenuItems()}
|
||||
<MenuItem
|
||||
onClick={logout}
|
||||
sx={{
|
||||
|
||||
@@ -50,7 +50,7 @@ const StatBox = ({
|
||||
borderColor: theme.palette[themeColor].lowContrast,
|
||||
}
|
||||
: {
|
||||
background: `linear-gradient(340deg, ${theme.palette.tertiary.main} 20%, ${theme.palette.primary.main} 45%)`,
|
||||
background: `linear-gradient(340deg, ${theme.palette.tertiary.main} 10%, ${theme.palette.primary.main} 45%)`,
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
};
|
||||
|
||||
@@ -62,7 +62,7 @@ const StatBox = ({
|
||||
color: theme.palette.primary.contrastTextSecondary,
|
||||
};
|
||||
|
||||
const spanFixedStyles = { marginLeft: theme.spacing(2), fontSize: 15 };
|
||||
const spanFixedStyles = { marginLeft: theme.spacing(2), fontSize: 15, fontWeight: 600};
|
||||
const detailTextStyles = gradient
|
||||
? {
|
||||
color: theme.palette[themeColor].contrastText,
|
||||
@@ -120,7 +120,7 @@ const StatBox = ({
|
||||
)}
|
||||
<Stack>
|
||||
<Typography component="h2">{heading}</Typography>
|
||||
<Typography>{subHeading}</Typography>
|
||||
<Typography sx={{fontWeight: 500}}>{subHeading}</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -57,10 +57,6 @@ const Account = ({ open = "profile" }) => {
|
||||
className="account"
|
||||
px={theme.spacing(20)}
|
||||
py={theme.spacing(12)}
|
||||
backgroundColor={theme.palette.primary.main}
|
||||
border={1}
|
||||
borderColor={theme.palette.primary.lowContrast}
|
||||
borderRadius={theme.shape.borderRadius}
|
||||
>
|
||||
<TabContext value={tab}>
|
||||
<Box
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { networkService } from "../../../../main";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const getRandomDevice = () => {
|
||||
const manufacturers = {
|
||||
|
||||
@@ -20,7 +20,6 @@ const MonitorTable = ({ isLoading, monitors }) => {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const { determineState } = useMonitorUtils();
|
||||
|
||||
const headers = [
|
||||
{
|
||||
id: "name",
|
||||
|
||||
@@ -20,11 +20,52 @@ const useSubscribeToMonitors = (page, rowsPerPage) => {
|
||||
const { getMonitorWithPercentage } = useMonitorUtils();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchInitialData = async () => {
|
||||
try {
|
||||
const initialDataRes = await networkService.getDistributedUptimeMonitors({
|
||||
teamId: user.teamId,
|
||||
limit: 25,
|
||||
types: [
|
||||
typeof import.meta.env.VITE_DEPIN_TESTING === "undefined"
|
||||
? "distributed_http"
|
||||
: "distributed_test",
|
||||
],
|
||||
page,
|
||||
rowsPerPage,
|
||||
});
|
||||
const responseData = initialDataRes?.data?.data;
|
||||
if (typeof responseData === "undefined") throw new Error("No data");
|
||||
|
||||
const { monitors, filteredMonitors, summary } = responseData;
|
||||
|
||||
const mappedMonitors = filteredMonitors?.map((monitor) =>
|
||||
getMonitorWithPercentage(monitor, theme)
|
||||
);
|
||||
|
||||
setMonitors(monitors);
|
||||
setMonitorsSummary(summary);
|
||||
setFilteredMonitors(mappedMonitors);
|
||||
} catch (error) {
|
||||
setNetworkError(true);
|
||||
createToast({
|
||||
body: error.message,
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchInitialData();
|
||||
|
||||
try {
|
||||
const cleanup = networkService.subscribeToDistributedUptimeMonitors({
|
||||
teamId: user.teamId,
|
||||
limit: 25,
|
||||
types: ["distributed_http"],
|
||||
types: [
|
||||
typeof import.meta.env.VITE_DEPIN_TESTING === "undefined"
|
||||
? "distributed_http"
|
||||
: "distributed_test",
|
||||
],
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter: null,
|
||||
|
||||
@@ -20,6 +20,13 @@ const OptionsHeader = ({
|
||||
const theme = useTheme();
|
||||
const monitorNames = typeof monitors !== "undefined" ? Object.values(monitors) : [];
|
||||
|
||||
// The stacks below which are three in number have the same style so
|
||||
const stackStyles = {
|
||||
direction: "row",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing(6),
|
||||
};
|
||||
|
||||
if (!shouldRender) return <SkeletonLayout />;
|
||||
|
||||
return (
|
||||
@@ -27,17 +34,13 @@ const OptionsHeader = ({
|
||||
direction="row"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(6)}
|
||||
>
|
||||
<Stack {...stackStyles}>
|
||||
<Typography
|
||||
display="inline-block"
|
||||
component="h1"
|
||||
color={theme.palette.primary.contrastTextSecondary}
|
||||
>
|
||||
Incidents for
|
||||
Incidents for:
|
||||
</Typography>
|
||||
<Select
|
||||
id="incidents-select-monitor"
|
||||
@@ -49,13 +52,10 @@ const OptionsHeader = ({
|
||||
backgroundColor: theme.palette.primary.main,
|
||||
color: theme.palette.primary.contrastTextSecondary,
|
||||
}}
|
||||
truncate={true}
|
||||
/>
|
||||
</Stack>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(6)}
|
||||
>
|
||||
<Stack {...stackStyles}>
|
||||
<Typography
|
||||
display="inline-block"
|
||||
component="h1"
|
||||
@@ -63,14 +63,7 @@ const OptionsHeader = ({
|
||||
>
|
||||
Filter by:
|
||||
</Typography>
|
||||
<ButtonGroup
|
||||
sx={{
|
||||
ml: "auto",
|
||||
"& .MuiButtonBase-root, & .MuiButtonBase-root:hover": {
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={(filter === "all").toString()}
|
||||
@@ -94,11 +87,7 @@ const OptionsHeader = ({
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Stack>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(6)}
|
||||
>
|
||||
<Stack {...stackStyles}>
|
||||
<Typography
|
||||
display="inline-block"
|
||||
component="h1"
|
||||
@@ -106,14 +95,7 @@ const OptionsHeader = ({
|
||||
>
|
||||
Show:
|
||||
</Typography>
|
||||
<ButtonGroup
|
||||
sx={{
|
||||
ml: "auto",
|
||||
"& .MuiButtonBase-root, & .MuiButtonBase-root:hover": {
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ButtonGroup>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={(dateRange === "hour").toString()}
|
||||
|
||||
@@ -9,6 +9,16 @@ import PropTypes from "prop-types";
|
||||
const Gauge = ({ value, heading, metricOne, valueOne, metricTwo, valueTwo }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const valueStyle = {
|
||||
borderRadius: theme.spacing(2),
|
||||
backgroundColor: theme.palette.tertiary.main,
|
||||
width: "40%",
|
||||
mb: theme.spacing(2),
|
||||
mt: theme.spacing(2),
|
||||
pr: theme.spacing(2),
|
||||
textAlign: 'right'
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseContainer>
|
||||
<Stack
|
||||
@@ -16,12 +26,21 @@ const Gauge = ({ value, heading, metricOne, valueOne, metricTwo, valueTwo }) =>
|
||||
gap={theme.spacing(2)}
|
||||
alignItems="center"
|
||||
>
|
||||
<CustomGauge
|
||||
progress={value}
|
||||
radius={100}
|
||||
color={theme.palette.primary.main}
|
||||
/>
|
||||
<Typography component="h2">{heading}</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
width: "100%",
|
||||
backgroundColor: theme.palette.gradient.color1
|
||||
}}
|
||||
>
|
||||
<CustomGauge
|
||||
progress={value}
|
||||
radius={100}
|
||||
/>
|
||||
<Typography component="h2" sx={{ fontWeight: 600 }}>{heading}</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
@@ -31,18 +50,20 @@ const Gauge = ({ value, heading, metricOne, valueOne, metricTwo, valueTwo }) =>
|
||||
<Stack
|
||||
justifyContent={"space-between"}
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Typography>{metricOne}</Typography>
|
||||
<Typography>{valueOne}</Typography>
|
||||
<Typography sx={valueStyle}>{valueOne}</Typography>
|
||||
</Stack>
|
||||
<Stack
|
||||
justifyContent={"space-between"}
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Typography>{metricTwo}</Typography>
|
||||
<Typography>{valueTwo}</Typography>
|
||||
<Typography sx={valueStyle}>{valueTwo}</Typography>
|
||||
</Stack>
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
@@ -64,6 +64,7 @@ const useHardwareUtils = () => {
|
||||
return (
|
||||
<>
|
||||
{Number(MB.toFixed(0))}
|
||||
{space ? " " : ""}
|
||||
<Typography component="span">MB</Typography>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -62,7 +62,7 @@ const InfrastructureDetails = () => {
|
||||
monitor={monitor}
|
||||
/>
|
||||
<GenericFallback>
|
||||
<Typography>No check history for htis monitor yet.</Typography>
|
||||
<Typography>No check history for this monitor yet.</Typography>
|
||||
</GenericFallback>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -219,7 +219,8 @@ const Settings = () => {
|
||||
<Box>
|
||||
<Typography component="h1">{t("settingsGeneralSettings")}</Typography>
|
||||
<Typography sx={{ mt: theme.spacing(2), mb: theme.spacing(2) }}>
|
||||
<Typography component="span">{t("settingsDisplayTimezone")}</Typography>- {t("settingsDisplayTimezoneDescription")}
|
||||
<Typography component="span">{t("settingsDisplayTimezone")}</Typography>-{" "}
|
||||
{t("settingsDisplayTimezoneDescription")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
@@ -242,17 +243,17 @@ const Settings = () => {
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack gap={theme.spacing(20)}>
|
||||
<Select
|
||||
id="theme-mode"
|
||||
label={t("settingsThemeMode")}
|
||||
value={mode}
|
||||
onChange={(e) => {
|
||||
dispatch(setMode(e.target.value));
|
||||
}}
|
||||
items={[
|
||||
{ _id: "light", name: "Light" },
|
||||
{ _id: "dark", name: "Dark" },
|
||||
]}
|
||||
<Select
|
||||
id="theme-mode"
|
||||
label={t("settingsThemeMode")}
|
||||
value={mode}
|
||||
onChange={(e) => {
|
||||
dispatch(setMode(e.target.value));
|
||||
}}
|
||||
items={[
|
||||
{ _id: "light", name: "Light" },
|
||||
{ _id: "dark", name: "Dark" },
|
||||
]}
|
||||
></Select>
|
||||
<Select
|
||||
id="language"
|
||||
@@ -283,7 +284,28 @@ const Settings = () => {
|
||||
dispatch(setDistributedUptimeEnabled(e.target.checked));
|
||||
}}
|
||||
/>
|
||||
{distributedUptimeEnabled === true ? t("settingsEnabled") : t("settingsDisabled")}
|
||||
{distributedUptimeEnabled === true
|
||||
? t("settingsEnabled")
|
||||
: t("settingsDisabled")}
|
||||
</Box>
|
||||
</ConfigBox>
|
||||
)}
|
||||
{isAdmin && (
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h1">{t("settingsWallet")}</Typography>
|
||||
<Typography sx={{ mt: theme.spacing(2) }}>
|
||||
{t("settingsWalletDescription")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
>
|
||||
<WalletMultiButton />
|
||||
<WalletDisconnectButton />
|
||||
</Stack>
|
||||
</Box>
|
||||
</ConfigBox>
|
||||
)}
|
||||
@@ -378,25 +400,6 @@ const Settings = () => {
|
||||
/>
|
||||
</ConfigBox>
|
||||
)}
|
||||
{isAdmin && (
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
<Typography component="h1">{t("settingsWallet")}</Typography>
|
||||
<Typography sx={{ mt: theme.spacing(2) }}>
|
||||
{t("settingsWalletDescription")}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box>
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
>
|
||||
<WalletMultiButton />
|
||||
<WalletDisconnectButton />
|
||||
</Stack>
|
||||
</Box>
|
||||
</ConfigBox>
|
||||
)}
|
||||
|
||||
<ConfigBox>
|
||||
<Box>
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
import ConfigBox from "../../../../../Components/ConfigBox";
|
||||
import PropTypes from "prop-types";
|
||||
import { useTheme } from "@emotion/react";
|
||||
|
||||
// This can be used to add any extra/additional section/stacks on top of existing sections on the tab
|
||||
const ConfigStack = ({ title, description, children }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<ConfigBox>
|
||||
<Stack gap={theme.spacing(6)}>
|
||||
<Typography component="h2">{title}</Typography>
|
||||
<Typography component="p">{description}</Typography>
|
||||
</Stack>
|
||||
{children}
|
||||
</ConfigBox>
|
||||
);
|
||||
};
|
||||
|
||||
ConfigStack.propTypes = {
|
||||
title: PropTypes.string.isRequired, // Title must be a string and is required
|
||||
description: PropTypes.string.isRequired, // Description must be a string and is required
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default ConfigStack;
|
||||
@@ -1,13 +1,13 @@
|
||||
// Components
|
||||
import { Stack, Typography, Button } from "@mui/material";
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
import { TabPanel } from "@mui/lab";
|
||||
import ConfigBox from "../../../../../Components/ConfigBox";
|
||||
import MonitorList from "../MonitorList";
|
||||
import Search from "../../../../../Components/Inputs/Search";
|
||||
import Checkbox from "../../../../../Components/Inputs/Checkbox";
|
||||
// Utils
|
||||
import { useState } from "react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import ConfigStack from "./ConfigStack";
|
||||
const Content = ({
|
||||
tabValue,
|
||||
form,
|
||||
@@ -34,14 +34,10 @@ const Content = ({
|
||||
return (
|
||||
<TabPanel value={tabValue}>
|
||||
<Stack gap={theme.spacing(10)}>
|
||||
<ConfigBox>
|
||||
<Stack gap={theme.spacing(6)}>
|
||||
<Typography component="h2">Status page servers</Typography>
|
||||
<Typography component="p">
|
||||
You can add any number of servers that you monitor to your status page. You
|
||||
can also reorder them for the best viewing experience.
|
||||
</Typography>
|
||||
</Stack>
|
||||
<ConfigStack
|
||||
title="Status page servers"
|
||||
description="You can add any number of servers that you monitor to your status page. You can also reorder them for the best viewing experience."
|
||||
>
|
||||
<Stack>
|
||||
<Stack
|
||||
direction="row"
|
||||
@@ -72,13 +68,12 @@ const Content = ({
|
||||
setSelectedMonitors={handleMonitorsChange}
|
||||
/>
|
||||
</Stack>
|
||||
</ConfigBox>{" "}
|
||||
<ConfigBox>
|
||||
<Stack gap={theme.spacing(6)}>
|
||||
<Typography component="h2">Features</Typography>
|
||||
<Typography component="p">Show more details on the status page</Typography>
|
||||
</Stack>
|
||||
<Stack sx={{ margin: theme.spacing(6) }}>
|
||||
</ConfigStack>
|
||||
<ConfigStack
|
||||
title="Features"
|
||||
description="Show more details on the status page"
|
||||
>
|
||||
<Stack>
|
||||
<Checkbox
|
||||
id="showCharts"
|
||||
name="showCharts"
|
||||
@@ -94,9 +89,8 @@ const Content = ({
|
||||
onChange={handleFormChange}
|
||||
/>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
</ConfigStack>
|
||||
</Stack>
|
||||
<Stack gap={theme.spacing(6)}></Stack>
|
||||
</TabPanel>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -73,6 +73,8 @@ const TabSettings = ({
|
||||
label="Your status page address"
|
||||
value={form.url}
|
||||
onChange={handleFormChange}
|
||||
helperText={errors["url"]}
|
||||
error={errors["url"] ? true : false}
|
||||
/>
|
||||
</Stack>
|
||||
</ConfigBox>
|
||||
|
||||
@@ -807,6 +807,31 @@ class NetworkService {
|
||||
);
|
||||
}
|
||||
|
||||
getDistributedUptimeMonitors(config) {
|
||||
const { teamId, limit, types, page, rowsPerPage, filter, field, order } = config;
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (limit) params.append("limit", limit);
|
||||
if (types) {
|
||||
types.forEach((type) => {
|
||||
params.append("type", type);
|
||||
});
|
||||
}
|
||||
if (page) params.append("page", page);
|
||||
if (rowsPerPage) params.append("rowsPerPage", rowsPerPage);
|
||||
if (filter) params.append("filter", filter);
|
||||
if (field) params.append("field", field);
|
||||
if (order) params.append("order", order);
|
||||
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
}
|
||||
|
||||
const url = `${this.axiosInstance.defaults.baseURL}/distributed-uptime/monitors/${teamId}/initial?${params.toString()}`;
|
||||
return this.axiosInstance.get(url);
|
||||
}
|
||||
|
||||
subscribeToDistributedUptimeMonitors(config) {
|
||||
const {
|
||||
teamId,
|
||||
|
||||
@@ -40,7 +40,6 @@ const paletteColors = {
|
||||
blueGray150: "#667085",
|
||||
blueGray200: "#475467",
|
||||
blueGray400: "#344054",
|
||||
blueGray900: "#1c2130",
|
||||
blueBlueWave: "#1570EF",
|
||||
blue700: "#4E5BA6",
|
||||
purple300: "#664EFF",
|
||||
@@ -89,11 +88,14 @@ const newColors = {
|
||||
gray100: "#F3F3F3",
|
||||
gray200: "#EFEFEF",
|
||||
gray500: "#A2A3A3",
|
||||
gray900: "#1c1c1c",
|
||||
blueGray50: "#E8F0FE",
|
||||
blueGray500: "#475467",
|
||||
blueGray600: "#344054",
|
||||
blueGray800: "#1C2130",
|
||||
blueGray900: "#515151",
|
||||
blueBlueWave: "#1570EF",
|
||||
lightBlueWave: "#CDE2FF",
|
||||
/* I changed green 100 and green 700. Need to change red and warning as well, and refactor the object following the structure */
|
||||
green100: "#67cd78",
|
||||
green200: "#4B9B77",
|
||||
@@ -171,6 +173,10 @@ const newSemanticColors = {
|
||||
light: newColors.gray200,
|
||||
dark: "#313131" /* newColors.blueGray600 */,
|
||||
},
|
||||
light: {
|
||||
light: newColors.lightBlueWave,
|
||||
dark: newColors.lightBlueWave,
|
||||
},
|
||||
contrastText: {
|
||||
light: newColors.blueGray600,
|
||||
dark: newColors.gray200,
|
||||
@@ -290,6 +296,16 @@ const newSemanticColors = {
|
||||
dark: "#C084FC",
|
||||
},
|
||||
},
|
||||
alert: {
|
||||
main: {
|
||||
light: newColors.gray200,
|
||||
dark: newColors.gray900,
|
||||
},
|
||||
contrastText: {
|
||||
light: newColors.blueGray600,
|
||||
dark: newColors.blueGray900,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export { typographyLevels, semanticColors as colors, newSemanticColors };
|
||||
@@ -382,6 +382,18 @@ const baseTheme = (palette) => ({
|
||||
}),
|
||||
},
|
||||
},
|
||||
MuiButtonGroup: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
ml: "auto",
|
||||
"& .MuiButtonBase-root, & .MuiButtonBase-root:hover": {
|
||||
borderColor: theme.palette.primary.lowContrast,
|
||||
width: "auto",
|
||||
whiteSpace: "nowrap",
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
|
||||
// This code is added for clock in maintenance page
|
||||
// code starts from here.
|
||||
|
||||
+24
-14
@@ -3,8 +3,8 @@
|
||||
* @param {string} str String whose first letter is to be capitalized
|
||||
* @returns A string with first letter capitalized
|
||||
*/
|
||||
export const capitalizeFirstLetter = (str) => {
|
||||
if (str === null || str === undefined) {
|
||||
export const capitalizeFirstLetter = (str) => {
|
||||
if (str === null || str === undefined) {
|
||||
return "";
|
||||
}
|
||||
if (typeof str !== "string") {
|
||||
@@ -12,7 +12,7 @@ export const capitalizeFirstLetter = (str) => {
|
||||
}
|
||||
if (str.length === 0) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
};
|
||||
|
||||
@@ -22,15 +22,25 @@ export const capitalizeFirstLetter = (str) => {
|
||||
* @returns A string with first letter lower cased
|
||||
*/
|
||||
|
||||
export const toLowerCaseFirstLetter = (str) => {
|
||||
export const toLowerCaseFirstLetter = (str) => {
|
||||
if (str === null || str === undefined) {
|
||||
return "";
|
||||
}
|
||||
if (typeof str !== "string") {
|
||||
throw new TypeError("Input must be a string");
|
||||
}
|
||||
if (str.length === 0) {
|
||||
return "";
|
||||
}
|
||||
return str.charAt(0).toLowerCase() + str.slice(1);
|
||||
};
|
||||
return "";
|
||||
}
|
||||
if (typeof str !== "string") {
|
||||
throw new TypeError("Input must be a string");
|
||||
}
|
||||
if (str.length === 0) {
|
||||
return "";
|
||||
}
|
||||
return str.charAt(0).toLowerCase() + str.slice(1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a string is null, undefined, or empty (including strings with only whitespace).
|
||||
* @param {string} str - The string to check.
|
||||
* @returns {boolean} - Returns true if the string is null, undefined, or empty.
|
||||
*/
|
||||
export const isEmpty = (str) => {
|
||||
// Check if string is null, undefined, or empty (including whitespace only)
|
||||
return str === null || typeof str === "undefined" || str.trim().length === 0;
|
||||
};
|
||||
@@ -222,7 +222,14 @@ const statusPageValidation = joi.object({
|
||||
.string()
|
||||
.trim()
|
||||
.messages({ "string.empty": "Company name is required." }),
|
||||
url: joi.string().trim().messages({ "string.empty": "URL is required." }),
|
||||
url: joi
|
||||
.string()
|
||||
.pattern(/^[a-zA-Z0-9_-]+$/) // Only allow alphanumeric, underscore, and hyphen
|
||||
.required()
|
||||
.messages({
|
||||
"string.pattern.base":
|
||||
"URL can only contain letters, numbers, underscores, and hyphens",
|
||||
}),
|
||||
timezone: joi.string().trim().messages({ "string.empty": "Timezone is required." }),
|
||||
color: joi.string().trim().messages({ "string.empty": "Color is required." }),
|
||||
theme: joi.string(),
|
||||
|
||||
Reference in new issue
Block a user