Merged the conflict theme file.

This commit is contained in:
Owaise Imdad committed 2025-03-05 00:37:21 +05:30
commit b3bfab05e6
24 files changed
+388 -168

No files matched your search

+1 -1
View File
@@ -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.
+7 -7
View File
@@ -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
View File
@@ -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",
+2 -2
View File
@@ -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 (
+4 -3
View File
@@ -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)}%`}
+121 -36
View File
@@ -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={{
+3 -3
View File
@@ -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>
);
-4
View File
@@ -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>
</>
);
+1 -1
View File
@@ -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>
);
+35 -32
View File
@@ -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>
+25
View File
@@ -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,
+17 -1
View File
@@ -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 };
+12
View File
@@ -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
View File
@@ -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;
};
+8 -1
View File
@@ -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(),