Merge branch 'bluewave-labs:develop' into FIX_1848_smallUIFixes

This commit is contained in:
Owaise Imdad authored and GitHub committed 2025-03-02 12:57:51 +05:30
commit 9edfd7185b
19 files changed
+283 -48

No files matched your search

+1 -1
View File
@@ -107,7 +107,7 @@ If you have any questions, suggestions or comments, please use our [Discord chan
## 🤝 Contributing
We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 3700+ stars and attracted 45+ contributors from around the globe.
We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 4.4K+ stars and attracted 50+ contributors from around the globe.
Our repo is starred by employees from **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes and NEC**, so don’t hold back — jump in, contribute and learn with us!
+16 -16
View File
@@ -16,7 +16,7 @@
"@mui/lab": "6.0.0-beta.29",
"@mui/material": "6.4.6",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.27.1",
"@mui/x-data-grid": "7.27.2",
"@mui/x-date-pickers": "7.27.1",
"@reduxjs/toolkit": "2.6.0",
"@solana/wallet-adapter-base": "0.9.23",
@@ -3858,9 +3858,9 @@
}
},
"node_modules/@mui/x-data-grid": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.27.1.tgz",
"integrity": "sha512-dDgrCIiIcb3XPxUVxXQsk9yjXFSAXBunqW1m1jjcr0D3jj3qHibBtGslSKnBmmcoT6XLAXdbZOu1fFC+xYhyGA==",
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.27.2.tgz",
"integrity": "sha512-Nj6PWp6WN0gkps676j1Jai3mP1fQotJbqMTvllU7LwQp09S/RE/uJicYdqfeWQLxtQipWm5dnAY63gzsTiAHGA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.25.7",
@@ -4713,9 +4713,9 @@
}
},
"node_modules/@remix-run/router": {
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.22.0.tgz",
"integrity": "sha512-MBOl8MeOzpK0HQQQshKB7pABXbmyHizdTpqnrIseTbsv0nAepwC2ENZa1aaBExNQcpLoXmWthhak8SABLzvGPw==",
"version": "1.23.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz",
"integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
@@ -16305,12 +16305,12 @@
}
},
"node_modules/react-router": {
"version": "6.29.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.29.0.tgz",
"integrity": "sha512-DXZJoE0q+KyeVw75Ck6GkPxFak63C4fGqZGNijnWgzB/HzSP1ZfTlBj5COaGWwhrMQ/R8bXiq5Ooy4KG+ReyjQ==",
"version": "6.30.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz",
"integrity": "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.22.0"
"@remix-run/router": "1.23.0"
},
"engines": {
"node": ">=14.0.0"
@@ -16320,13 +16320,13 @@
}
},
"node_modules/react-router-dom": {
"version": "6.29.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.29.0.tgz",
"integrity": "sha512-pkEbJPATRJ2iotK+wUwHfy0xs2T59YPEN8BQxVCPeBZvK7kfPESRc/nyxzdcxR17hXgUPYx2whMwl+eo9cUdnQ==",
"version": "6.30.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.0.tgz",
"integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==",
"license": "MIT",
"dependencies": {
"@remix-run/router": "1.22.0",
"react-router": "6.29.0"
"@remix-run/router": "1.23.0",
"react-router": "6.30.0"
},
"engines": {
"node": ">=14.0.0"
+1 -1
View File
@@ -16,7 +16,7 @@
"@fontsource/roboto": "^5.0.13",
"@hello-pangea/dnd": "^18.0.0",
"@mui/x-charts": "^7.5.1",
"@mui/x-data-grid": "7.27.1",
"@mui/x-data-grid": "7.27.2",
"@mui/x-date-pickers": "7.27.1",
"@mui/icons-material": "6.4.6",
"@mui/lab": "6.0.0-beta.29",
@@ -0,0 +1,108 @@
// Components
import { Typography, Stack } from "@mui/material";
import PropTypes from "prop-types";
import { useTheme } from "@emotion/react";
import IconBox from "../../IconBox";
/**
* `EmptyView` is a functional React component that displays an empty state view with an optional icon, header, and message.
*
* @component
* @param {Object} props - The properties that define the `EmptyView` component.
* @param {React.ReactNode} [props.icon] - An optional icon to display at the top of the empty view.
* @param {string} [props.header] - An optional header text displayed next to the icon.
* @param {string} [props.message="No Data"] - The message to be displayed in the empty view.
* @param {'h1' | 'h2' | 'h3'} [props.headingLevel="h2"] - The heading level for the message text.
* @param {string} [props.justifyContent="flex-start"] - The CSS `justify-content` value to align elements vertically.
* @param {string} [props.height="100%"] - The height of the empty view container.
*
* @example
* // Example usage of EmptyView component:
* <EmptyView
* icon={<SomeIcon />}
* header="Average Response Time"
* message="No Response Time Available"
* headingLevel="h2"
* justifyContent="center"
* height="50%"
* />
*
* @returns {React.Element} The `EmptyView` component with customizable icon, header, and message.
*/
const EmptyView = ({
icon,
header,
message = "No Data",
headingLevel = "h2",
justifyContent = "flex-start",
height = "100%"
}) => {
const theme = useTheme();
return (
<Stack
flex={1}
direction="row"
sx={{
backgroundColor: theme.palette.primary.main,
border: 1,
borderStyle: "solid",
borderColor: theme.palette.primary.lowContrast,
borderRadius: 2,
borderTopRightRadius: 4,
borderBottomRightRadius: 4,
}}
>
<Stack
flex={1}
alignItems="center"
sx={{
padding: theme.spacing(8),
justifyContent,
gap: theme.spacing(8),
height,
"& h2": {
color: theme.palette.primary.contrastTextSecondary,
fontSize: 15,
fontWeight: 500,
},
"& tspan, & text": {
fill: theme.palette.primary.contrastTextTertiary,
},
}}
>
<Stack
alignSelf="flex-start"
direction="row"
alignItems="center"
gap={theme.spacing(6)}
>
{icon && <IconBox>{icon}</IconBox>}
{header && <Typography component="h2">{header}</Typography>}
</Stack>
<Stack
flex={1}
justifyContent="center"
alignItems="center"
>
<Typography component={headingLevel}>
{message}
</Typography>
</Stack>
</Stack>
</Stack>
);
};
EmptyView.propTypes = {
message: PropTypes.string,
icon: PropTypes.node,
header: PropTypes.string,
headingLevel: PropTypes.oneOf(['h1', 'h2', 'h3']),
justifyContent: PropTypes.string,
height: PropTypes.string
};
export default EmptyView;
+8
View File
@@ -1,6 +1,7 @@
import { Stack, Typography } from "@mui/material";
import { useTheme } from "@emotion/react";
import IconBox from "../../IconBox";
import EmptyView from "./EmptyView";
import PropTypes from "prop-types";
const ChartBox = ({
@@ -12,8 +13,13 @@ const ChartBox = ({
Legend,
borderRadiusRight = 4,
sx,
noDataMessage,
isEmpty = false,
}) => {
const theme = useTheme();
if (isEmpty) {
return <EmptyView icon={icon} header={header} message={noDataMessage} />;
}
return (
<Stack
flex={1}
@@ -88,4 +94,6 @@ ChartBox.propTypes = {
icon: PropTypes.node,
header: PropTypes.string,
height: PropTypes.string,
noDataMessage: PropTypes.string,
isEmpty: PropTypes.bool
};
+21 -3
View File
@@ -628,10 +628,19 @@ function Sidebar() {
) : (
<>
<Avatar small={true} />
<Box ml={theme.spacing(2)}>
<Box
ml={theme.spacing(2)}
sx={{ maxWidth: "50%", overflow: "hidden" }}
>
<Typography
component="span"
fontWeight={500}
sx={{
display: "block",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
}}
>
{authState.user?.firstName} {authState.user?.lastName}
</Typography>
@@ -706,12 +715,21 @@ function Sidebar() {
}}
>
{collapsed && (
<MenuItem sx={{ cursor: "default", minWidth: "150px" }}>
<Box mb={theme.spacing(2)}>
<MenuItem sx={{ cursor: "default", minWidth: "50%" }}>
<Box
mb={theme.spacing(2)}
sx={{ maxWidth: "50%", overflow: "hidden" }}
>
<Typography
component="span"
fontWeight={500}
fontSize={13}
sx={{
display: "block",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
}}
>
{authState.user?.firstName} {authState.user?.lastName}
</Typography>
+4
View File
@@ -15,6 +15,8 @@ import { useTheme } from "@emotion/react";
* @typedef {Object} Header
* @property {number|string} id - The unique identifier for the header.
* @property {React.ReactNode} content - The content to display in the header cell.
* @property {Function} onClick - A function to be called when this cell is clicked, receiving the event and row data as an argument.
* @property {Object} getCellSx - Function that takes a row and returns a style object for the table cell.
* @property {Function} render - A function to render the cell content for a given row.
*/
@@ -107,6 +109,8 @@ const DataTable = ({
<TableCell
align={index === 0 ? "left" : "center"}
key={header.id}
onClick={header.onClick ? (e) => header.onClick(e, row) : null}
sx={header.getCellSx ? header.getCellSx(row) : {}}
>
{header.render(row)}
</TableCell>
+2 -2
View File
@@ -53,8 +53,8 @@ const uiSlice = createSlice({
state.timezone = action.payload.timezone;
},
setLanguage(state, action) {
state.language = action.payload;
},
state.language = action.payload;
},
},
});
@@ -5,7 +5,7 @@ import { useTheme } from "@emotion/react";
import { useMonitorUtils } from "../../../../Hooks/useMonitorUtils";
import { createToast } from "../../../../Utils/toastUtils";
const useSubscribeToMonitors = () => {
const useSubscribeToMonitors = (page, rowsPerPage) => {
// Redux
const { user } = useSelector((state) => state.auth);
@@ -25,8 +25,8 @@ const useSubscribeToMonitors = () => {
teamId: user.teamId,
limit: 25,
types: ["distributed_http"],
page: 0,
rowsPerPage: 10,
page,
rowsPerPage,
filter: null,
field: null,
order: null,
@@ -56,7 +56,7 @@ const useSubscribeToMonitors = () => {
});
setNetworkError(true);
}
}, [user, getMonitorWithPercentage, theme, isLoading]);
}, [user, getMonitorWithPercentage, theme, isLoading, page, rowsPerPage]);
return [isLoading, networkError, monitors, monitorsSummary, filteredMonitors];
};
export { useSubscribeToMonitors };
+23 -1
View File
@@ -3,10 +3,12 @@ import { Stack, Typography } from "@mui/material";
import Breadcrumbs from "../../../Components/Breadcrumbs";
import CreateMonitorHeader from "../../../Components/MonitorCreateHeader";
import MonitorTable from "./Components/MonitorTable";
import Pagination from "../../../Components/Table/TablePagination";
import Fallback from "../../../Components/Fallback";
import GenericFallback from "../../../Components/GenericFallback";
// Utils
import { useState } from "react";
import { useTheme } from "@mui/material/styles";
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
import { useSubscribeToMonitors } from "./Hooks/useSubscribeToMonitors";
@@ -16,11 +18,23 @@ const BREADCRUMBS = [{ name: `Distributed Uptime`, path: "/distributed-uptime" }
const DistributedUptimeMonitors = () => {
// Local state
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
// Utils
const theme = useTheme();
const isAdmin = useIsAdmin();
const [isLoading, networkError, monitors, monitorsSummary, filteredMonitors] =
useSubscribeToMonitors();
useSubscribeToMonitors(page, rowsPerPage);
// Handlers
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(event.target.value);
setPage(0);
};
if (isLoading) {
return <SkeletonLayout />;
@@ -69,6 +83,14 @@ const DistributedUptimeMonitors = () => {
isLoading={isLoading}
monitors={filteredMonitors}
/>
<Pagination
itemCount={monitorsSummary?.totalMonitors ?? 0}
paginationLabel="monitors"
page={page}
rowsPerPage={rowsPerPage}
handleChangePage={handleChangePage}
handleChangeRowsPerPage={handleChangeRowsPerPage}
/>
</Stack>
);
};
@@ -18,10 +18,11 @@ import InfoBox from "../../../Components/InfoBox";
import StatusHeader from "../../DistributedUptime/Details/Components/StatusHeader";
import MonitorsList from "./Components/MonitorsList";
import { RowContainer } from "../../../Components/StandardContainer";
import ThemeSwitch from "../../../Components/ThemeSwitch";
//Utils
import { useTheme } from "@mui/material/styles";
import { useState, useRef } from "react";
import { useState, useEffect, useRef } from "react";
import { useParams } from "react-router-dom";
import { useSubscribeToDetails } from "../../DistributedUptime/Details/Hooks/useSubscribeToDetails";
import { useDUStatusPageFetchByUrl } from "./Hooks/useDUStatusPageFetchByUrl";
@@ -31,6 +32,8 @@ import SubHeader from "../../../Components/Subheader";
import { safelyParseFloat } from "../../../Utils/utils";
import { useNavigate, useLocation } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { useSelector, useDispatch } from "react-redux";
import { setMode } from "../../../Features/UI/uiSlice";
// Responsive
import { useMediaQuery } from "@mui/material";
@@ -40,8 +43,12 @@ const DistributedUptimeStatus = () => {
const location = useLocation();
const { t } = useTranslation();
const isPublic = location.pathname.startsWith("/status/distributed/public");
const elementToCapture = useRef(null);
// Redux state
const mode = useSelector((state) => state.ui.mode);
const originalModeRef = useRef(null);
// Local State
const [dateRange, setDateRange] = useState("day");
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
@@ -50,6 +57,7 @@ const DistributedUptimeStatus = () => {
const theme = useTheme();
const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm"));
const navigate = useNavigate();
const dispatch = useDispatch();
const [
statusPageIsLoading,
@@ -85,6 +93,28 @@ const DistributedUptimeStatus = () => {
};
}
// Default to dark mode
useEffect(() => {
const cleanup = () => {
if (originalModeRef.current === null) {
originalModeRef.current = mode;
}
if (isPublic) {
dispatch(setMode(originalModeRef.current));
}
};
if (isPublic) {
dispatch(setMode("dark"));
}
window.addEventListener("beforeunload", cleanup);
return () => {
window.removeEventListener("beforeunload", cleanup);
};
}, [dispatch, isPublic]);
// Done loading, a status page doesn't exist
if (!statusPageIsLoading && typeof statusPage === "undefined") {
return (
@@ -158,9 +188,10 @@ const DistributedUptimeStatus = () => {
}}
>
{!isPublic && <Breadcrumbs list={BREADCRUMBS} />}
<ControlsHeader
shouldShow={!isPublic}
statusPage={statusPage}
isPublic={isPublic}
isDeleting={isDeleting}
isDeleteOpen={isDeleteOpen}
setIsDeleteOpen={setIsDeleteOpen}
@@ -183,11 +214,9 @@ const DistributedUptimeStatus = () => {
<RowContainer>
<Stack>
<Typography variant={`body2`}>
{t("distributedRightCatagoryTitle")}
</Typography>
<Typography variant={`h2`}>
{t("distributedRightCatagoryDescription")}
{t("distributedRightCategoryTitle")}
</Typography>
<Typography variant={`h2`}>{statusPage.companyName}</Typography>
</Stack>
</RowContainer>
</SubHeader>
@@ -2,7 +2,7 @@
import { Box, Stack, Typography, Button } from "@mui/material";
import Image from "../../../../../Components/Image";
import SettingsIcon from "../../../../../assets/icons/settings-bold.svg?react";
import ThemeSwitch from "../../../../../Components/ThemeSwitch";
//Utils
import { useTheme } from "@mui/material/styles";
import { useNavigate } from "react-router-dom";
@@ -76,6 +76,7 @@ Controls.propTypes = {
const ControlsHeader = ({
statusPage,
isPublic,
isDeleting,
isDeleteOpen,
setIsDeleteOpen,
@@ -105,7 +106,16 @@ const ControlsHeader = ({
maxHeight={"50px"}
base64={statusPage?.logo?.data}
/>
<Typography variant="h1">{statusPage?.companyName}</Typography>
<Typography
variant="h1"
overflow="hidden"
textOverflow="ellipsis"
sx={{
maxWidth: { xs: "200px", sm: "100%" },
}}
>
{statusPage?.companyName}
</Typography>
</Stack>
<Controls
isDeleting={isDeleting}
@@ -114,6 +124,7 @@ const ControlsHeader = ({
url={url}
type={type}
/>
{isPublic && <ThemeSwitch />}
</Stack>
);
};
@@ -121,6 +132,7 @@ const ControlsHeader = ({
ControlsHeader.propTypes = {
url: PropTypes.string,
statusPage: PropTypes.object,
isPublic: PropTypes.bool,
isDeleting: PropTypes.bool,
isDeleteOpen: PropTypes.bool.isRequired,
setIsDeleteOpen: PropTypes.func.isRequired,
@@ -17,16 +17,40 @@ const StatusPagesTable = ({ data }) => {
},
{
id: "url",
content: "URL",
content: "Public URL",
onClick: (e, row) => {
if (row.isPublished) {
e.stopPropagation();
const url =
row.type === "distributed"
? `/status/distributed/public/${row.url}`
: `/status/uptime/public/${row.url}`;
navigate(url);
}
},
render: (row) => {
const content = row.isPublished ? `/${row.url}` : "Unpublished";
return (
<Stack
direction="row"
alignItems="center"
justifyContent="center"
gap={theme.spacing(2)}
paddingLeft={theme.spacing(2)}
paddingRight={theme.spacing(2)}
borderRadius={theme.spacing(4)}
sx={{
...(row.isPublished && {
":hover": {
backgroundColor: `${theme.palette.primary.light}`,
cursor: "pointer",
borderRadius: 1,
},
}),
}}
>
<Typography>{`/${row.url}`}</Typography>
<ArrowOutwardIcon />
<Typography>{content}</Typography>
{row.isPublished && <ArrowOutwardIcon />}
</Stack>
);
},
@@ -51,6 +51,7 @@ const StatusPages = () => {
/>
);
}
return (
<Stack gap={theme.spacing(10)}>
<Breadcrumbs list={BREADCRUMBS} />
@@ -35,6 +35,7 @@ const ChartBoxes = ({
const denominator =
totalUpChecks + totalDownChecks > 0 ? totalUpChecks + totalDownChecks : 1;
const groupedUptimePercentage = (totalUpChecks / denominator) * 100;
const noIncidentsMessage = "Great. No Incidents, yet!";
return (
<Stack
@@ -45,6 +46,7 @@ const ChartBoxes = ({
<ChartBox
icon={<UptimeIcon />}
header="Uptime"
isEmpty={monitor?.uptimePercentage === 0 && !monitor?.upChecks?.length}
>
<Stack
width={"100%"}
@@ -95,6 +97,8 @@ const ChartBoxes = ({
<ChartBox
icon={<IncidentsIcon />}
header="Incidents"
noDataMessage={noIncidentsMessage}
isEmpty={monitor?.groupedDownChecks?.length === 0}
>
<Stack width={"100%"}>
<Box position="relative">
+1 -1
View File
@@ -282,7 +282,7 @@ const newSemanticColors = {
},
chatbot: {
background: {
light: "#112B2B",
light: newColors.gray200,
dark: "#112B2B",
},
textAccent: {
+10 -3
View File
@@ -7,11 +7,18 @@ const nameSchema = joi
.string()
.max(50)
.trim()
.pattern(/^[A-Za-z]+$/)
.pattern(/^(?=.*[\p{L}\p{Sc}])[\p{L}\p{Sc}\s']+$/u, {
name: "name.containsLetterOrSymbol",
})
.pattern(/^[\p{L}\p{Sc}\s']+$/u, {
name: "name.validCharacters",
})
.messages({
"string.empty": "Name is required",
"string.max": "Name must be less than 50 characters long",
"string.pattern.base": "Name must contain only letters",
"string.max": "Name must be less than 50 characters",
"string.pattern.name": "Name must contain at least 1 letter or currency symbol",
"string.pattern.base.validCharacters":
"Can only contain letters, spaces, apostrophes, and currency symbols",
});
const passwordSchema = joi
+1 -2
View File
@@ -63,8 +63,7 @@
"webhookUnsupportedPlatform": "Unsupported platform: {platform}",
"distributedStatusHeaderText": "Real-time, real-device coverage",
"distributedStatusSubHeaderText": "Powered by millions devices worldwide, view a system performance by global region, country or city",
"distributedRightCatagoryTitle": "Monitor",
"distributedRightCatagoryDescription": "Mainnet Beta Cluster",
"distributedRightCategoryTitle": "Monitor",
"distributedStatusServerMonitors": "Server Monitors",
"distributedStatusServerMonitorsDescription": "Monitor status of related servers"
}
+1 -2
View File
@@ -58,6 +58,5 @@
"authRegisterBySigningUp": "Kayıt olarak, aşağıdaki şartları kabul ediyorsunuz:",
"distributedStatusHeaderText": "Gerçek zamanlı, Gerçek cihazlar kapsamı",
"distributedStatusSubHeaderText": "Dünya çapında milyonlarca cihaz tarafından desteklenen sistem performansını küresel bölgeye, ülkeye veya şehre göre görüntüleyin",
"distributedRightCatagoryTitle": "Monitör",
"distributedRightCatagoryDescription": "Mainnet Beta Kümesi"
"distributedRightCategoryTitle": "Monitör"
}