diff --git a/frontend/src/assets/img/ossSegments.png b/frontend/src/assets/img/ossSegments.png
new file mode 100644
index 0000000000..02593ef0fe
Binary files /dev/null and b/frontend/src/assets/img/ossSegments.png differ
diff --git a/frontend/src/assets/img/ossSegmentsConfetti.svg b/frontend/src/assets/img/ossSegmentsConfetti.svg
new file mode 100644
index 0000000000..9bdf15af75
--- /dev/null
+++ b/frontend/src/assets/img/ossSegmentsConfetti.svg
@@ -0,0 +1 @@
+
diff --git a/frontend/src/component/layout/MainLayout/MainLayout.tsx b/frontend/src/component/layout/MainLayout/MainLayout.tsx
index b5a30304be..55b0f38ae0 100644
--- a/frontend/src/component/layout/MainLayout/MainLayout.tsx
+++ b/frontend/src/component/layout/MainLayout/MainLayout.tsx
@@ -1,4 +1,4 @@
-import React, { forwardRef, ReactNode } from 'react';
+import { forwardRef, ReactNode } from 'react';
import { Grid, styled } from '@mui/material';
import Header from 'component/menu/Header/Header';
import Footer from 'component/menu/Footer/Footer';
@@ -15,6 +15,7 @@ import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
import { DraftBanner } from './DraftBanner/DraftBanner';
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
import { Demo } from 'component/demo/Demo';
+import { SegmentsSplashScreen } from 'component/splash/SegmentsSplashScreen/SegmentsSplashScreen';
interface IMainLayoutProps {
children: ReactNode;
@@ -76,12 +77,17 @@ const MainLayoutContentContainer = styled('div')(({ theme }) => ({
export const MainLayout = forwardRef(
({ children }, ref) => {
- const { uiConfig } = useUiConfig();
+ const { uiConfig, isOss, loading } = useUiConfig();
const projectId = useOptionalPathParam('projectId');
const { isChangeRequestConfiguredInAnyEnv } = useChangeRequestsEnabled(
projectId || '',
);
+ // only show segment splash if we're really certain it's OSS.
+ // Otherwise it might lead to flashing the splash to
+ // pro/enterprise users before data has loaded.
+ const showSegmentSplash = !loading && isOss();
+
return (
<>
@@ -127,6 +133,10 @@ export const MainLayout = forwardRef(
+ }
+ />
>
>
diff --git a/frontend/src/component/splash/SegmentsSplashScreen/SegmentsDialog.tsx b/frontend/src/component/splash/SegmentsSplashScreen/SegmentsDialog.tsx
new file mode 100644
index 0000000000..0c7e491192
--- /dev/null
+++ b/frontend/src/component/splash/SegmentsSplashScreen/SegmentsDialog.tsx
@@ -0,0 +1,69 @@
+import {
+ Dialog,
+ DialogProps,
+ IconButton,
+ Typography,
+ styled,
+} from '@mui/material';
+import CloseIcon from '@mui/icons-material/Close';
+import confetti from 'assets/img/ossSegmentsConfetti.svg';
+import { formatAssetPath } from 'utils/formatPath';
+
+const StyledDialog = styled(Dialog)(({ theme }) => ({
+ '& .MuiDialog-paper': {
+ borderRadius: theme.shape.borderRadiusExtraLarge,
+ maxWidth: theme.spacing(90),
+ padding: theme.spacing(7.5),
+ textAlign: 'center',
+ backgroundImage: `url('${formatAssetPath(confetti)}')`,
+ },
+ zIndex: theme.zIndex.snackbar,
+ '& .MuiModal-backdrop': {
+ background:
+ 'linear-gradient(-54deg, rgba(61, 57, 128, 0.80) 0%, rgba(97, 91, 194, 0.80) 26.77%, rgba(106, 99, 224, 0.80) 48.44%, rgba(106, 99, 224, 0.80) 72.48%, rgba(129, 84, 191, 0.80) 99.99%)',
+ backdropFilter: 'blur(2px)',
+ },
+}));
+
+const StyledCloseButton = styled(IconButton)(({ theme }) => ({
+ position: 'absolute',
+ right: theme.spacing(2),
+ top: theme.spacing(2),
+ color: theme.palette.neutral.main,
+}));
+
+const StyledHeader = styled(Typography)(({ theme }) => ({
+ fontSize: theme.fontSizes.largeHeader,
+ fontWeight: theme.fontWeight.bold,
+}));
+
+interface ISegmentsDialogProps extends DialogProps {
+ open: boolean;
+ onClose: () => void;
+ preventCloseOnBackdropClick?: boolean;
+ children: React.ReactNode;
+}
+
+export const SegmentsDialog = ({
+ open,
+ onClose,
+ preventCloseOnBackdropClick,
+ children,
+ ...props
+}: ISegmentsDialogProps) => (
+ {
+ if (preventCloseOnBackdropClick && r === 'backdropClick') return;
+ onClose();
+ }}
+ {...props}
+ >
+
+
+
+ {children}
+
+);
+
+SegmentsDialog.Header = StyledHeader;
diff --git a/frontend/src/component/splash/SegmentsSplashScreen/SegmentsSplashScreen.tsx b/frontend/src/component/splash/SegmentsSplashScreen/SegmentsSplashScreen.tsx
new file mode 100644
index 0000000000..695b9e7964
--- /dev/null
+++ b/frontend/src/component/splash/SegmentsSplashScreen/SegmentsSplashScreen.tsx
@@ -0,0 +1,129 @@
+import { Button, Typography, styled } from '@mui/material';
+import { SegmentsDialog } from './SegmentsDialog';
+import ossSegmentsImage from 'assets/img/ossSegments.png';
+import { formatAssetPath } from 'utils/formatPath';
+import { Launch } from '@mui/icons-material';
+import { createLocalStorage } from 'utils/createLocalStorage';
+import React from 'react';
+import { useNavigate } from 'react-router-dom';
+import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
+
+const StyledActions = styled('div')(({ theme }) => ({
+ display: 'grid',
+ gridTemplateColumns: '1fr 1fr',
+ gap: theme.spacing(3),
+ marginBlockStart: theme.spacing(5),
+}));
+
+const StyledButton = styled(Button)(({ theme }) => ({
+ height: theme.spacing(7),
+}));
+
+interface SegmentsSplashScreenProps {
+ open: boolean;
+ onClose: () => void;
+ showSegments: () => void;
+}
+
+const StyledHeader = styled('h2')(({ theme }) => ({
+ fontSize: theme.fontSizes.mainHeader,
+ fontWeight: theme.fontWeight.bold,
+}));
+
+const StyledImage = styled('img')(({ theme }) => ({
+ width: '100%',
+ marginBlockStart: theme.spacing(3),
+}));
+
+const StyledLink = styled('a')(({ theme }) => ({
+ marginBlockStart: theme.spacing(3),
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ textDecoration: 'underline',
+ gap: theme.spacing(0.5),
+ '& > svg': {
+ fontSize: theme.fontSizes.bodySize,
+ },
+}));
+
+const SegmentsSplashScreenContent = ({
+ open,
+ onClose,
+ showSegments,
+}: SegmentsSplashScreenProps) => (
+ <>
+
+
+ Segments are now available in Open Source!
+
+
+ We are excited to announce that we are releasing an enterprise
+ feature for our open source community.
+
+
+
+ Read all about segments in the documentation
+
+
+
+
+
+ Show me segments
+
+
+ Close
+
+
+
+ >
+);
+
+export const SegmentsSplashScreen: React.FC = () => {
+ const { value: localStorageState, setValue: setLocalStorageState } =
+ createLocalStorage('OssSegmentsSplashScreen:v1', { shown: false });
+
+ const [showSegmentSplash, setShowSegmentSplash] = React.useState(true);
+
+ const navigate = useNavigate();
+ const closeSegmentsSplash = () => {
+ setShowSegmentSplash(false);
+ setLocalStorageState({ shown: true });
+ };
+
+ const { trackEvent } = usePlausibleTracker();
+
+ return (
+ {
+ closeSegmentsSplash();
+ trackEvent('oss-segments-splash-screen', {
+ props: {
+ eventType: 'close splash',
+ },
+ });
+ }}
+ showSegments={() => {
+ closeSegmentsSplash();
+ navigate(`/segments`);
+ trackEvent('oss-segments-splash-screen', {
+ props: {
+ eventType: 'navigate to segments',
+ },
+ });
+ }}
+ />
+ );
+};
diff --git a/frontend/src/hooks/usePlausibleTracker.ts b/frontend/src/hooks/usePlausibleTracker.ts
index 41453deed4..97a80cd18f 100644
--- a/frontend/src/hooks/usePlausibleTracker.ts
+++ b/frontend/src/hooks/usePlausibleTracker.ts
@@ -49,7 +49,8 @@ export type CustomEvents =
| 'open-integration'
| 'feature-naming-pattern'
| 'project-mode'
- | 'dependent_features';
+ | 'dependent_features'
+ | 'oss-segments-splash-screen';
export const usePlausibleTracker = () => {
const plausible = useContext(PlausibleContext);