diff --git a/frontend/src/component/common/OperatorUpgradeAlert/OperatorUpgradeAlert.tsx b/frontend/src/component/common/OperatorUpgradeAlert/OperatorUpgradeAlert.tsx
new file mode 100644
index 0000000000..8be54f080b
--- /dev/null
+++ b/frontend/src/component/common/OperatorUpgradeAlert/OperatorUpgradeAlert.tsx
@@ -0,0 +1,23 @@
+import { Alert } from '@material-ui/lab';
+
+export const OperatorUpgradeAlert = () => {
+ return (
+
+ Remember to update your Unleash client! New operators require new
+ SDK versions. .
+
+ );
+};
+
+const OperatorDocsLink = () => {
+ return (
+
+ Read more
+
+ );
+};
diff --git a/frontend/src/component/layout/LayoutPicker/LayoutPicker.jsx b/frontend/src/component/layout/LayoutPicker/LayoutPicker.jsx
index 68b6172b91..ebf4b8754d 100644
--- a/frontend/src/component/layout/LayoutPicker/LayoutPicker.jsx
+++ b/frontend/src/component/layout/LayoutPicker/LayoutPicker.jsx
@@ -17,10 +17,12 @@ const LayoutPicker = ({ children }) => {
const isResetPasswordSuccessPage = matchPath(pathname, {
path: '/reset-password-success',
});
-
const isForgottenPasswordPage = matchPath(pathname, {
path: '/forgotten-password',
});
+ const isSplashPage = matchPath(pathname, {
+ path: '/splash/:id',
+ });
const is404 = matchPath(pathname, { path: '/404' });
@@ -30,6 +32,7 @@ const LayoutPicker = ({ children }) => {
isChangePasswordPage ||
isResetPasswordSuccessPage ||
isForgottenPasswordPage ||
+ isSplashPage ||
is404
);
};
diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap
index 2e2f0bce48..c0b21e9ab4 100644
--- a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap
+++ b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap
@@ -4,7 +4,13 @@ exports[`returns all baseRoutes 1`] = `
Array [
Object {
"component": [Function],
- "layout": "main",
+ "menu": Object {},
+ "path": "/splash/:splashId",
+ "title": "Unleash",
+ "type": "protected",
+ },
+ Object {
+ "component": [Function],
"menu": Object {},
"parent": "/projects",
"path": "/projects/create",
@@ -13,7 +19,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/projects",
"path": "/projects/:id/edit",
@@ -22,7 +27,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/archive",
"path": "/projects/:id/archived",
@@ -31,7 +35,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/projects/:id/features/:name/:activeTab",
"path": "/projects/:id/features/:name/:activeTab/copy",
@@ -40,7 +43,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/projects",
"path": "/projects/:projectId/features/:featureId/edit",
@@ -50,7 +52,6 @@ Array [
Object {
"component": [Function],
"flags": "E",
- "layout": "main",
"menu": Object {},
"parent": "/projects",
"path": "/projects/:projectId/features/:featureId",
@@ -59,7 +60,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/projects",
"path": "/projects/:id/features/:name/:activeTab",
@@ -68,7 +68,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/projects/:id/features",
"path": "/projects/:projectId/create-toggle",
@@ -77,7 +76,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/features",
"path": "/projects/:projectId/features2/:name",
@@ -87,7 +85,6 @@ Array [
Object {
"component": [Function],
"flag": "P",
- "layout": "main",
"menu": Object {},
"parent": "/projects",
"path": "/projects/:id/:activeTab",
@@ -97,7 +94,6 @@ Array [
Object {
"component": [Function],
"flag": "P",
- "layout": "main",
"menu": Object {},
"parent": "/projects",
"path": "/projects/:id",
@@ -106,7 +102,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {
"mobile": true,
},
@@ -116,7 +111,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/features",
"path": "/features/:activeTab/:name",
@@ -125,7 +119,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {
"mobile": true,
},
@@ -135,7 +128,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/applications",
"path": "/applications/:name",
@@ -144,7 +136,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {
"advanced": true,
"mobile": true,
@@ -156,7 +147,6 @@ Array [
Object {
"component": [Function],
"flag": "C",
- "layout": "main",
"menu": Object {},
"parent": "/context",
"path": "/context/create",
@@ -166,7 +156,6 @@ Array [
Object {
"component": [Function],
"flag": "C",
- "layout": "main",
"menu": Object {},
"parent": "/context",
"path": "/context/edit/:name",
@@ -176,7 +165,6 @@ Array [
Object {
"component": [Function],
"flag": "C",
- "layout": "main",
"menu": Object {
"advanced": true,
"mobile": true,
@@ -187,7 +175,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/strategies",
"path": "/strategies/create",
@@ -196,7 +183,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/strategies",
"path": "/strategies/:name/edit",
@@ -205,7 +191,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/strategies",
"path": "/strategies/:name",
@@ -214,7 +199,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {
"advanced": true,
"mobile": true,
@@ -225,7 +209,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/environments",
"path": "/environments/create",
@@ -234,7 +217,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"path": "/environments/:id",
"title": "Edit",
@@ -243,7 +225,6 @@ Array [
Object {
"component": [Function],
"flag": "EEA",
- "layout": "main",
"menu": Object {
"advanced": true,
"mobile": true,
@@ -254,7 +235,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/tag-types",
"path": "/tag-types/create",
@@ -263,7 +243,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/tag-types",
"path": "/tag-types/edit/:name",
@@ -272,7 +251,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {
"advanced": true,
"mobile": true,
@@ -283,7 +261,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/addons",
"path": "/addons/create/:providerId",
@@ -292,7 +269,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/addons",
"path": "/addons/edit/:addonId",
@@ -302,7 +278,6 @@ Array [
Object {
"component": [Function],
"hidden": false,
- "layout": "main",
"menu": Object {
"advanced": true,
"mobile": true,
@@ -315,7 +290,6 @@ Array [
"component": [Function],
"flag": "SE",
"hidden": false,
- "layout": "main",
"menu": Object {
"advanced": true,
"mobile": true,
@@ -326,7 +300,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/history",
"path": "/history/:toggleName",
@@ -335,7 +308,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {
"adminSettings": true,
},
@@ -345,7 +317,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"path": "/archive",
"title": "Archived Toggles",
@@ -353,7 +324,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/admin",
"path": "/admin/api/create-token",
@@ -363,7 +333,6 @@ Array [
Object {
"component": [Function],
"flag": "RE",
- "layout": "main",
"menu": Object {},
"path": "/admin/create-project-role",
"title": "Create",
@@ -372,7 +341,6 @@ Array [
Object {
"component": [Function],
"flag": "RE",
- "layout": "main",
"menu": Object {},
"path": "/admin/roles/:id/edit",
"title": "Edit",
@@ -380,7 +348,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {
"advanced": true,
"mobile": true,
@@ -392,7 +359,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {
"adminSettings": true,
},
@@ -403,7 +369,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {},
"parent": "/admin",
"path": "/admin/create-user",
@@ -412,7 +377,6 @@ Array [
},
Object {
"component": [Function],
- "layout": "main",
"menu": Object {
"adminSettings": true,
},
@@ -424,7 +388,6 @@ Array [
Object {
"component": [Function],
"flag": "RE",
- "layout": "main",
"menu": Object {
"adminSettings": true,
},
@@ -436,7 +399,6 @@ Array [
Object {
"component": [Function],
"hidden": false,
- "layout": "main",
"menu": Object {},
"path": "/admin",
"title": "Admin",
diff --git a/frontend/src/component/menu/routes.js b/frontend/src/component/menu/routes.js
index a352a18d82..bd96b0533c 100644
--- a/frontend/src/component/menu/routes.js
+++ b/frontend/src/component/menu/routes.js
@@ -46,18 +46,26 @@ import { EventHistoryPage } from '../history/EventHistoryPage/EventHistoryPage';
import { FeatureEventHistoryPage } from '../history/FeatureEventHistoryPage/FeatureEventHistoryPage';
import { CreateStrategy } from '../strategies/CreateStrategy/CreateStrategy';
import { EditStrategy } from '../strategies/EditStrategy/EditStrategy';
-import { SegmentsList } from 'component/segments/SegmentList/SegmentList';
+import { SegmentsList } from '../segments/SegmentList/SegmentList';
+import { SplashPage } from '../splash/SplashPage/SplashPage';
export const routes = [
- // Project
+ // Splash
+ {
+ path: '/splash/:splashId',
+ title: 'Unleash',
+ component: SplashPage,
+ type: 'protected',
+ menu: {},
+ },
+ // Project
{
path: '/projects/create',
parent: '/projects',
title: 'Create',
component: CreateProject,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -66,7 +74,6 @@ export const routes = [
title: ':id',
component: EditProject,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -75,7 +82,6 @@ export const routes = [
parent: '/archive',
component: RedirectArchive,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -84,7 +90,6 @@ export const routes = [
title: 'Copy',
component: CopyFeatureToggle,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -93,7 +98,6 @@ export const routes = [
title: 'Edit Feature',
component: EditFeature,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -102,7 +106,6 @@ export const routes = [
title: 'FeatureView',
component: FeatureView,
type: 'protected',
- layout: 'main',
flags: E,
menu: {},
},
@@ -112,7 +115,6 @@ export const routes = [
title: ':name',
component: FeatureView,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -121,7 +123,6 @@ export const routes = [
title: 'Create feature toggle',
component: CreateFeature,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -130,7 +131,6 @@ export const routes = [
title: ':name',
component: RedirectFeatureView,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -140,7 +140,6 @@ export const routes = [
component: Project,
flag: P,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -150,7 +149,6 @@ export const routes = [
component: Project,
flag: P,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -158,7 +156,6 @@ export const routes = [
title: 'Projects',
component: ProjectListNew,
type: 'protected',
- layout: 'main',
menu: { mobile: true },
},
@@ -169,7 +166,6 @@ export const routes = [
title: ':name',
component: RedirectFeatureView,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -177,7 +173,6 @@ export const routes = [
title: 'Feature Toggles',
component: FeatureToggleListContainer,
type: 'protected',
- layout: 'main',
menu: { mobile: true },
},
@@ -188,7 +183,6 @@ export const routes = [
parent: '/applications',
component: ApplicationEdit,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -196,7 +190,6 @@ export const routes = [
title: 'Applications',
component: ApplicationList,
type: 'protected',
- layout: 'main',
menu: { mobile: true, advanced: true },
},
@@ -207,7 +200,6 @@ export const routes = [
title: 'Create',
component: CreateContext,
type: 'protected',
- layout: 'main',
flag: C,
menu: {},
},
@@ -217,7 +209,6 @@ export const routes = [
title: ':name',
component: EditContext,
type: 'protected',
- layout: 'main',
flag: C,
menu: {},
},
@@ -227,7 +218,6 @@ export const routes = [
component: ContextList,
type: 'protected',
flag: C,
- layout: 'main',
menu: { mobile: true, advanced: true },
},
@@ -238,7 +228,6 @@ export const routes = [
parent: '/strategies',
component: CreateStrategy,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -247,7 +236,6 @@ export const routes = [
parent: '/strategies',
component: EditStrategy,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -256,7 +244,6 @@ export const routes = [
parent: '/strategies',
component: StrategyView,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -264,7 +251,6 @@ export const routes = [
title: 'Strategies',
component: StrategiesList,
type: 'protected',
- layout: 'main',
menu: { mobile: true, advanced: true },
},
{
@@ -273,7 +259,6 @@ export const routes = [
component: CreateEnvironment,
parent: '/environments',
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -281,7 +266,6 @@ export const routes = [
title: 'Edit',
component: EditEnvironment,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -289,7 +273,6 @@ export const routes = [
title: 'Environments',
component: EnvironmentList,
type: 'protected',
- layout: 'main',
flag: EEA,
menu: { mobile: true, advanced: true },
},
@@ -301,7 +284,6 @@ export const routes = [
title: 'Create',
component: CreateTagType,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -310,7 +292,6 @@ export const routes = [
title: ':name',
component: EditTagType,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -318,7 +299,6 @@ export const routes = [
title: 'Tag types',
component: TagTypeList,
type: 'protected',
- layout: 'main',
menu: { mobile: true, advanced: true },
},
@@ -329,7 +309,6 @@ export const routes = [
title: 'Create',
component: CreateAddon,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -338,7 +317,6 @@ export const routes = [
title: 'Edit',
component: EditAddon,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -347,7 +325,6 @@ export const routes = [
component: AddonList,
hidden: false,
type: 'protected',
- layout: 'main',
menu: { mobile: true, advanced: true },
},
@@ -359,7 +336,6 @@ export const routes = [
component: SegmentsList,
hidden: false,
type: 'protected',
- layout: 'main',
menu: { mobile: true, advanced: true },
flag: SE,
},
@@ -371,7 +347,6 @@ export const routes = [
parent: '/history',
component: FeatureEventHistoryPage,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -379,7 +354,6 @@ export const routes = [
title: 'Event History',
component: EventHistoryPage,
type: 'protected',
- layout: 'main',
menu: { adminSettings: true },
},
@@ -389,7 +363,6 @@ export const routes = [
title: 'Archived Toggles',
component: ArchiveListContainer,
type: 'protected',
- layout: 'main',
menu: {},
},
@@ -400,7 +373,6 @@ export const routes = [
title: 'API access',
component: CreateApiToken,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -408,7 +380,6 @@ export const routes = [
title: 'Create',
component: CreateProjectRole,
type: 'protected',
- layout: 'main',
menu: {},
flag: RE,
},
@@ -417,7 +388,6 @@ export const routes = [
title: 'Edit',
component: EditProjectRole,
type: 'protected',
- layout: 'main',
menu: {},
flag: RE,
},
@@ -426,7 +396,6 @@ export const routes = [
title: 'Edit',
component: EditUser,
type: 'protected',
- layout: 'main',
menu: {},
hidden: true,
},
@@ -436,7 +405,6 @@ export const routes = [
title: 'API access',
component: AdminApi,
type: 'protected',
- layout: 'main',
menu: { mobile: true, advanced: true },
},
{
@@ -445,7 +413,6 @@ export const routes = [
title: 'Users',
component: AdminUsers,
type: 'protected',
- layout: 'main',
menu: { adminSettings: true },
},
{
@@ -454,7 +421,6 @@ export const routes = [
title: 'Users',
component: CreateUser,
type: 'protected',
- layout: 'main',
menu: {},
},
{
@@ -463,7 +429,6 @@ export const routes = [
title: 'Single Sign-On',
component: AuthSettings,
type: 'protected',
- layout: 'main',
menu: { adminSettings: true },
},
{
@@ -472,7 +437,6 @@ export const routes = [
component: AdminInvoice,
hidden: true,
type: 'protected',
- layout: 'main',
menu: { adminSettings: true },
},
{
@@ -481,7 +445,6 @@ export const routes = [
title: 'Project Roles',
component: ProjectRoles,
type: 'protected',
- layout: 'main',
flag: RE,
menu: { adminSettings: true },
},
@@ -491,7 +454,6 @@ export const routes = [
component: Admin,
hidden: false,
type: 'protected',
- layout: 'main',
menu: {},
},
/* If you update this route path, make sure you update the path in SWRProvider.tsx */
@@ -502,7 +464,6 @@ export const routes = [
component: Login,
type: 'unprotected',
hidden: true,
- layout: 'standalone',
menu: {},
},
/* If you update this route path, make sure you update the path in SWRProvider.tsx */
@@ -512,7 +473,6 @@ export const routes = [
hidden: true,
component: NewUser,
type: 'unprotected',
- layout: 'standalone',
menu: {},
},
/* If you update this route path, make sure you update the path in SWRProvider.tsx */
@@ -522,7 +482,6 @@ export const routes = [
hidden: true,
component: ResetPassword,
type: 'unprotected',
- layout: 'standalone',
menu: {},
},
{
@@ -531,7 +490,6 @@ export const routes = [
hidden: true,
component: ForgottenPassword,
type: 'unprotected',
- layout: 'standalone',
menu: {},
},
];
diff --git a/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.tsx b/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.tsx
index 5482d29b24..8a94309fe8 100644
--- a/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.tsx
+++ b/frontend/src/component/project/Project/ProjectInfo/ProjectInfo.tsx
@@ -22,7 +22,7 @@ interface IProjectInfoProps {
memberCount: number;
featureCount: number;
health: number;
- description: string;
+ description?: string;
}
const ProjectInfo = ({
@@ -42,10 +42,11 @@ const ProjectInfo = ({
}
const LONG_DESCRIPTION = 100;
+ const isShortDescription =
+ !description || description.length < LONG_DESCRIPTION;
const permissionButtonClass = classnames({
- [styles.permissionButtonShortDesc]:
- description.length < LONG_DESCRIPTION,
+ [styles.permissionButtonShortDesc]: isShortDescription,
});
const permissionButton = (
diff --git a/frontend/src/component/splash/SplashPage/SplashPage.tsx b/frontend/src/component/splash/SplashPage/SplashPage.tsx
new file mode 100644
index 0000000000..96c20f6ed3
--- /dev/null
+++ b/frontend/src/component/splash/SplashPage/SplashPage.tsx
@@ -0,0 +1,72 @@
+import { Switch, Route, useHistory, Redirect } from 'react-router-dom';
+import { SplashPageEnvironments } from '../SplashPageEnvironments/SplashPageEnvironments';
+import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
+import useSplashApi from 'hooks/api/actions/useSplashApi/useSplashApi';
+import { SplashPageOperators } from 'component/splash/SplashPageOperators/SplashPageOperators';
+import { useEffect } from 'react';
+import { useAuthSplash } from 'hooks/api/getters/useAuth/useAuthSplash';
+
+// All known splash IDs.
+export const splashIds = ['environments', 'operators'] as const;
+
+// Active splash IDs that may be shown to the user.
+export const activeSplashIds: SplashId[] = ['operators'];
+
+export type SplashId = typeof splashIds[number];
+
+export const SplashPage = () => {
+ const splashId = useRequiredPathParam('splashId');
+ const isKnownId = isKnownSplashId(splashId);
+ const { refetchSplash } = useAuthSplash();
+ const { setSplashSeen } = useSplashApi();
+
+ // Close the splash "modal" on escape.
+ useNavigationOnKeydown('Escape', '/');
+
+ // Mark the splash ID as seen.
+ useEffect(() => {
+ if (splashId && isKnownId) {
+ setSplashSeen(splashId)
+ .then(() => refetchSplash())
+ .catch(console.warn);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [splashId, isKnownId]);
+
+ if (!isKnownId) {
+ return null;
+ }
+
+ return (
+