diff --git a/client/src/Components/design-elements/BasePage.tsx b/client/src/Components/design-elements/BasePage.tsx
index 691e770dc..38274f2d4 100644
--- a/client/src/Components/design-elements/BasePage.tsx
+++ b/client/src/Components/design-elements/BasePage.tsx
@@ -1,15 +1,14 @@
import Logo from "@/assets/icons/checkmate-icon.svg?react";
import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
-import Alert from "@mui/material/Alert";
import Link from "@mui/material/Link";
import { LAYOUT, SPACING } from "@/Utils/Theme/constants";
import {
ErrorFallback,
EmptyFallback,
EmptyMonitorFallback,
- BaseFallback,
} from "@/Components/design-elements/Fallback";
+import { EmptyState } from "@/Components/design-elements/EmptyState";
import { Breadcrumb } from "@/Components/design-elements/Breadcrumb";
import CircularProgress from "@mui/material/CircularProgress";
import { HeaderAuthControls } from "@/Pages/Auth/components/HeaderAuthControls";
@@ -21,32 +20,58 @@ import { Link as RouterLink } from "react-router-dom";
import { Typography } from "@mui/material";
export const PageSpeedKeyPriorityFallback = () => {
+ const theme = useTheme();
return (
-
-
-
-
- ),
+
+
-
-
-
+ >
+ ⚠
+
+
+
+ ),
+ }}
+ />
+
+
+ }
+ />
);
};
diff --git a/client/src/Components/design-elements/EmptyState.tsx b/client/src/Components/design-elements/EmptyState.tsx
new file mode 100644
index 000000000..d6795e1e9
--- /dev/null
+++ b/client/src/Components/design-elements/EmptyState.tsx
@@ -0,0 +1,155 @@
+import Stack from "@mui/material/Stack";
+import Typography from "@mui/material/Typography";
+import { useTheme } from "@mui/material/styles";
+import { Link as RouterLink } from "react-router-dom";
+import { Button } from "@/Components/inputs";
+import { LAYOUT } from "@/Utils/Theme/constants";
+import { EmptyStateIllustration } from "./EmptyStateIllustration";
+
+import type { ReactNode } from "react";
+
+interface EmptyStateProps {
+ title: string;
+ description?: ReactNode;
+ actionText?: string;
+ actionTo?: string;
+ onAction?: () => void;
+ secondaryText?: string;
+ secondaryHref?: string;
+ compact?: boolean;
+ alert?: ReactNode;
+ children?: ReactNode;
+}
+
+export const EmptyState = ({
+ title,
+ description,
+ actionText,
+ actionTo,
+ onAction,
+ secondaryText,
+ secondaryHref,
+ compact = false,
+ alert,
+ children,
+}: EmptyStateProps) => {
+ const theme = useTheme();
+
+ if (compact) {
+ return (
+
+
+
+
+ {title}
+
+ {description && (
+
+ {description}
+
+ )}
+
+
+ );
+ }
+
+ return (
+
+
+
+ {title}
+
+ {description && (
+
+ {description}
+
+ )}
+ {alert && (
+
+ {alert}
+
+ )}
+ {(actionText || secondaryText) && (
+
+ {actionText && (actionTo || onAction) && (
+
+ )}
+ {secondaryText && secondaryHref && (
+
+ {secondaryText} →
+
+ )}
+
+ )}
+ {children}
+
+ );
+};
diff --git a/client/src/Components/design-elements/EmptyStateIllustration.tsx b/client/src/Components/design-elements/EmptyStateIllustration.tsx
new file mode 100644
index 000000000..e8f74ed88
--- /dev/null
+++ b/client/src/Components/design-elements/EmptyStateIllustration.tsx
@@ -0,0 +1,214 @@
+interface EmptyStateIllustrationProps {
+ width?: number | string;
+ height?: number | string;
+}
+
+export const EmptyStateIllustration = ({
+ width = 160,
+ height = 120,
+}: EmptyStateIllustrationProps) => {
+ return (
+
+ );
+};
diff --git a/client/src/Components/design-elements/Fallback.tsx b/client/src/Components/design-elements/Fallback.tsx
index 003bda647..aa30c75ad 100644
--- a/client/src/Components/design-elements/Fallback.tsx
+++ b/client/src/Components/design-elements/Fallback.tsx
@@ -1,12 +1,8 @@
import Stack from "@mui/material/Stack";
-import Typography from "@mui/material/Typography";
-import { BulletPointCheck, SkeletonCard } from "@/Components/design-elements";
-import { Button } from "@/Components/inputs";
-import { SPACING, LAYOUT } from "@/Utils/Theme/constants";
-
-import { useNavigate } from "react-router";
-import useMediaQuery from "@mui/material/useMediaQuery";
import { useTheme } from "@mui/material/styles";
+import { LAYOUT } from "@/Utils/Theme/constants";
+import { EmptyState } from "./EmptyState";
+
import type { StackProps } from "@mui/material/Stack";
interface BaseFallbackProps extends StackProps {
@@ -15,30 +11,13 @@ interface BaseFallbackProps extends StackProps {
export const BaseFallback = ({ children, ...props }: BaseFallbackProps) => {
const theme = useTheme();
- const isSmall = useMediaQuery(theme.breakpoints.down("md"));
-
return (
-
{children}
);
@@ -51,21 +30,19 @@ export const ErrorFallback = ({
title: string;
subtitle: string;
}) => {
- const theme = useTheme();
return (
-
-
- {title}
-
- {subtitle}
-
+
);
};
+const bulletsToDescription = (bullets: unknown): string | undefined => {
+ if (!Array.isArray(bullets) || bullets.length === 0) return undefined;
+ return bullets.join(" • ");
+};
+
export const EmptyFallback = ({
title,
bullets,
@@ -73,52 +50,21 @@ export const EmptyFallback = ({
actionLink,
}: {
title: string;
- bullets: any;
+ bullets: unknown;
actionButtonText: string;
actionLink: string;
}) => {
- const theme = useTheme();
- const navigate = useNavigate();
return (
-
-
- {title}
-
- {Array.isArray(bullets) &&
- bullets?.map((bullet: string) => (
-
- ))}
-
-
-
-
-
-
+
);
};
export const EmptyMonitorFallback = ({
- page,
title,
bullets,
actionButtonText,
@@ -126,51 +72,16 @@ export const EmptyMonitorFallback = ({
}: {
page: string;
title: string;
- bullets: any;
+ bullets: unknown;
actionButtonText: string;
actionLink: string;
}) => {
- const theme = useTheme();
- const navigate = useNavigate();
return (
-
-
-
- {title}
-
-
- {Array.isArray(bullets) &&
- bullets?.map((bullet: string, index: number) => (
-
- ))}
-
-
-
-
-
-
+
);
};
diff --git a/client/src/Components/design-elements/index.tsx b/client/src/Components/design-elements/index.tsx
index ed6a885f6..12fad3f62 100644
--- a/client/src/Components/design-elements/index.tsx
+++ b/client/src/Components/design-elements/index.tsx
@@ -1,5 +1,7 @@
export * from "./Breadcrumb";
export * from "./BasePage";
+export * from "./EmptyState";
+export * from "./EmptyStateIllustration";
export * from "./Fallback";
export * from "./SkeletonCard";
export * from "./BulletPointCheck";