diff --git a/frontend/package.json b/frontend/package.json index 847b3a79a2..7fa4bbd3e8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -51,8 +51,8 @@ "@types/jest": "27.4.1", "@types/lodash.clonedeep": "4.5.6", "@types/node": "17.0.18", - "@types/react": "17.0.40", - "@types/react-dom": "17.0.13", + "@types/react": "17.0.41", + "@types/react-dom": "17.0.14", "@types/react-router-dom": "5.3.3", "@types/react-test-renderer": "17.0.1", "@types/react-timeago": "4.1.3", diff --git a/frontend/src/component/App.tsx b/frontend/src/component/App.tsx index 3b2094721d..64b200ea02 100644 --- a/frontend/src/component/App.tsx +++ b/frontend/src/component/App.tsx @@ -1,5 +1,4 @@ import ConditionallyRender from './common/ConditionallyRender'; -import EnvironmentSplash from './common/EnvironmentSplash/EnvironmentSplash'; import Feedback from './common/Feedback/Feedback'; import LayoutPicker from './layout/LayoutPicker/LayoutPicker'; import Loader from './common/Loader/Loader'; @@ -10,28 +9,15 @@ import ToastRenderer from './common/ToastRenderer/ToastRenderer'; import styles from './styles.module.scss'; import { Redirect, Route, Switch } from 'react-router-dom'; import { routes } from './menu/routes'; -import { useAuthDetails } from '../hooks/api/getters/useAuth/useAuthDetails'; -import { useAuthUser } from '../hooks/api/getters/useAuth/useAuthUser'; -import { useAuthSplash } from '../hooks/api/getters/useAuth/useAuthSplash'; +import { useAuthDetails } from 'hooks/api/getters/useAuth/useAuthDetails'; +import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser'; +import { SplashPageRedirect } from 'component/splash/SplashPageRedirect/SplashPageRedirect'; export const App = () => { - const { splash, refetchSplash } = useAuthSplash(); const { authDetails } = useAuthDetails(); const { user } = useAuthUser(); - const isLoggedIn = Boolean(user?.id); const hasFetchedAuth = Boolean(authDetails || user); - const showEnvSplash = isLoggedIn && splash?.environment === false; - - const renderMainLayoutRoutes = () => { - return routes.filter(route => route.layout === 'main').map(renderRoute); - }; - - const renderStandaloneRoutes = () => { - return routes - .filter(route => route.layout === 'standalone') - .map(renderRoute); - }; const isUnauthorized = (): boolean => { return !isLoggedIn; @@ -73,34 +59,22 @@ export const App = () => { elseShow={
- - - } - elseShow={ - - - - {renderMainLayoutRoutes()} - {renderStandaloneRoutes()} - - - - - - } - /> + + + + {routes.map(renderRoute)} + + + + + +
} /> diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ConstraintAccordionEditBody.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ConstraintAccordionEditBody.tsx index dc8612f926..5882a4aff7 100644 --- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ConstraintAccordionEditBody.tsx +++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/ConstraintAccordionEditBody/ConstraintAccordionEditBody.tsx @@ -5,10 +5,10 @@ import { CANCEL } from '../ConstraintAccordionEdit'; import { ConstraintFormHeader } from './ConstraintFormHeader/ConstraintFormHeader'; import { useStyles } from './ConstraintAccordionEditBody.styles'; import React from 'react'; -import { Alert } from '@material-ui/lab'; import { newOperators } from 'constants/operators'; import ConditionallyRender from 'component/common/ConditionallyRender/ConditionallyRender'; import { oneOf } from 'utils/one-of'; +import { OperatorUpgradeAlert } from 'component/common/OperatorUpgradeAlert/OperatorUpgradeAlert'; interface IConstraintAccordionBody { localConstraint: IConstraint; @@ -37,12 +37,7 @@ export const ConstraintAccordionEditBody: React.FC< <> - In order to use this constraint operator, you need to - update your SDK to the latest version. - - } + show={} />
diff --git a/frontend/src/component/common/FormTemplate/FormTemplate.tsx b/frontend/src/component/common/FormTemplate/FormTemplate.tsx index 08ff624225..dad1035223 100644 --- a/frontend/src/component/common/FormTemplate/FormTemplate.tsx +++ b/frontend/src/component/common/FormTemplate/FormTemplate.tsx @@ -38,7 +38,7 @@ const FormTemplate: React.FC = ({ const { setToastData } = useToast(); const styles = useStyles(); const commonStyles = useCommonStyles(); - const smallScreen = useMediaQuery(`(max-width:${900}px)`); + const smallScreen = useMediaQuery(`(max-width:${899}px)`); const copyCommand = () => { if (copy(formatApiCode())) { 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 ( + + + + + + + + + + + + ); +}; + +const useNavigationOnKeydown = (key: string, path: string) => { + const { push } = useHistory(); + + useEffect(() => { + const handler = (event: KeyboardEvent) => { + if (event.code === key) { + push(path); + } + }; + + window.addEventListener('keydown', handler); + return () => window.removeEventListener('keydown', handler); + }, [key, path, push]); +}; + +const isKnownSplashId = (value: string): value is SplashId => { + return (splashIds as Readonly).includes(value); +}; diff --git a/frontend/src/component/common/EnvironmentSplash/EnvironmentSplash.styles.ts b/frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironments.styles.ts similarity index 100% rename from frontend/src/component/common/EnvironmentSplash/EnvironmentSplash.styles.ts rename to frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironments.styles.ts diff --git a/frontend/src/component/common/EnvironmentSplash/EnvironmentSplash.tsx b/frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironments.tsx similarity index 89% rename from frontend/src/component/common/EnvironmentSplash/EnvironmentSplash.tsx rename to frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironments.tsx index 9490398d42..5bb5ae304e 100644 --- a/frontend/src/component/common/EnvironmentSplash/EnvironmentSplash.tsx +++ b/frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironments.tsx @@ -1,33 +1,25 @@ -import Splash from '../Splash/Splash'; -import EnvironmentSplashPage from './EnvironmentSplashPage/EnvironmentSplashPage'; +import { SplashPageEnvironmentsContent } from 'component/splash/SplashPageEnvironments/SplashPageEnvironmentsContent/SplashPageEnvironmentsContent'; +import { SplashPageEnvironmentsContainer } from 'component/splash/SplashPageEnvironments/SplashPageEnvironmentsContainer/SplashPageEnvironmentsContainer'; import { VpnKey, CloudCircle } from '@material-ui/icons'; -import { useStyles } from './EnvironmentSplash.styles'; -import { ReactComponent as Logo1 } from '../../../assets/img/splash_env1.svg'; -import { ReactComponent as Logo2 } from '../../../assets/img/splash_env2.svg'; -import { useEffect } from 'react'; -import useSplashApi from '../../../hooks/api/actions/useSplashApi/useSplashApi'; +import { useStyles } from 'component/splash/SplashPageEnvironments/SplashPageEnvironments.styles'; +import { ReactComponent as Logo1 } from 'assets/img/splash_env1.svg'; +import { ReactComponent as Logo2 } from 'assets/img/splash_env2.svg'; +import { useHistory } from 'react-router-dom'; -interface IEnvironmentSplashProps { - onFinish: (status: boolean) => void; -} - -const ENVIRONMENT_SPLASH_ID = 'environment'; - -const EnvironmentSplash = ({ onFinish }: IEnvironmentSplashProps) => { +export const SplashPageEnvironments = () => { const styles = useStyles(); - const { setSplashSeen } = useSplashApi(); + const { push } = useHistory(); - useEffect(() => { - setSplashSeen(ENVIRONMENT_SPLASH_ID); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + const onFinish = () => { + push('/'); + }; return ( <> - @@ -61,7 +53,7 @@ const EnvironmentSplash = ({ onFinish }: IEnvironmentSplashProps) => { } image={} />, - @@ -79,7 +71,7 @@ const EnvironmentSplash = ({ onFinish }: IEnvironmentSplashProps) => { } image={} />, - @@ -99,7 +91,7 @@ const EnvironmentSplash = ({ onFinish }: IEnvironmentSplashProps) => { } image={} />, - @@ -125,7 +117,7 @@ const EnvironmentSplash = ({ onFinish }: IEnvironmentSplashProps) => { } image={} />, - Want to know more? @@ -202,5 +194,3 @@ const EnvironmentSplash = ({ onFinish }: IEnvironmentSplashProps) => { ); }; - -export default EnvironmentSplash; diff --git a/frontend/src/component/common/EnvironmentSplash/EnvironmentSplashPage/EnvironmentSplashPage.tsx b/frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironmentsContainer/SplashPageEnvironmentsContainer.tsx similarity index 72% rename from frontend/src/component/common/EnvironmentSplash/EnvironmentSplashPage/EnvironmentSplashPage.tsx rename to frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironmentsContainer/SplashPageEnvironmentsContainer.tsx index 026ad2d32d..0641bd1a6e 100644 --- a/frontend/src/component/common/EnvironmentSplash/EnvironmentSplashPage/EnvironmentSplashPage.tsx +++ b/frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironmentsContainer/SplashPageEnvironmentsContainer.tsx @@ -1,18 +1,18 @@ import React from 'react'; -interface EnvironmentSplashPageProps { +interface ISplashPageEnvironmentsContainerProps { title: React.ReactNode; topDescription: React.ReactNode; image?: React.ReactNode; bottomDescription?: React.ReactNode; } -const EnvironmentSplashPage = ({ +export const SplashPageEnvironmentsContainer = ({ title, topDescription, image, bottomDescription, -}: EnvironmentSplashPageProps) => { +}: ISplashPageEnvironmentsContainerProps) => { return (
{title} @@ -22,5 +22,3 @@ const EnvironmentSplashPage = ({
); }; - -export default EnvironmentSplashPage; diff --git a/frontend/src/component/common/Splash/Splash.styles.ts b/frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironmentsContent/SplashPageEnvironmentsContent.styles.ts similarity index 98% rename from frontend/src/component/common/Splash/Splash.styles.ts rename to frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironmentsContent/SplashPageEnvironmentsContent.styles.ts index 2a64b56ccd..539536cc30 100644 --- a/frontend/src/component/common/Splash/Splash.styles.ts +++ b/frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironmentsContent/SplashPageEnvironmentsContent.styles.ts @@ -4,6 +4,7 @@ export const useStyles = makeStyles(theme => ({ splashMainContainer: { backgroundColor: theme.palette.primary.light, width: '100%', + minHeight: '100vh', display: 'flex', justifyContent: 'center', alignItems: 'center', diff --git a/frontend/src/component/common/Splash/Splash.tsx b/frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironmentsContent/SplashPageEnvironmentsContent.tsx similarity index 86% rename from frontend/src/component/common/Splash/Splash.tsx rename to frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironmentsContent/SplashPageEnvironmentsContent.tsx index 455270845f..f9f48f2484 100644 --- a/frontend/src/component/common/Splash/Splash.tsx +++ b/frontend/src/component/splash/SplashPageEnvironments/SplashPageEnvironmentsContent/SplashPageEnvironmentsContent.tsx @@ -1,23 +1,22 @@ import React, { Fragment, useState } from 'react'; import { Button, IconButton } from '@material-ui/core'; -import { useStyles } from './Splash.styles'; +import { useStyles } from 'component/splash/SplashPageEnvironments/SplashPageEnvironmentsContent/SplashPageEnvironmentsContent.styles'; import { CloseOutlined, FiberManualRecord, FiberManualRecordOutlined, } from '@material-ui/icons'; -import ConditionallyRender from '../ConditionallyRender'; -import { CLOSE_SPLASH } from '../../../testIds'; +import ConditionallyRender from 'component/common/ConditionallyRender'; +import { CLOSE_SPLASH } from 'testIds'; -interface ISplashProps { +interface ISplashPageEnvironmentsContentProps { components: React.ReactNode[]; onFinish: (status: boolean) => void; } -const Splash: React.FC = ({ - components, - onFinish, -}: ISplashProps) => { +export const SplashPageEnvironmentsContent: React.FC< + ISplashPageEnvironmentsContentProps +> = ({ components, onFinish }: ISplashPageEnvironmentsContentProps) => { const styles = useStyles(); const [counter, setCounter] = useState(0); @@ -76,7 +75,7 @@ const Splash: React.FC = ({ onClick={onClose} data-test={CLOSE_SPLASH} > - + {components[counter]} @@ -109,5 +108,3 @@ const Splash: React.FC = ({ ); }; - -export default Splash; diff --git a/frontend/src/component/splash/SplashPageOperators/SplashPageOperators.styles.tsx b/frontend/src/component/splash/SplashPageOperators/SplashPageOperators.styles.tsx new file mode 100644 index 0000000000..404b6a858d --- /dev/null +++ b/frontend/src/component/splash/SplashPageOperators/SplashPageOperators.styles.tsx @@ -0,0 +1,78 @@ +import { makeStyles } from '@material-ui/core/styles'; + +export const useStyles = makeStyles(theme => ({ + container: { + backgroundColor: theme.palette.primary.light, + minHeight: '100vh', + padding: '1rem', + display: 'grid', + gap: '1rem', + alignItems: 'center', + alignContent: 'center', + justifyContent: 'center', + gridTemplateColumns: 'minmax(0,auto)', + fontWeight: theme.fontWeight.thin, + }, + content: { + position: 'relative', + padding: '2rem', + borderRadius: '0.5rem', + backgroundColor: theme.palette.primary.main, + color: 'white', + [theme.breakpoints.up('md')]: { + padding: '4rem', + }, + }, + header: { + textAlign: 'center', + }, + footer: { + display: 'grid', + gap: '2rem', + textAlign: 'center', + justifyItems: 'center', + }, + body: { + margin: '2rem 0', + padding: '2rem 0', + borderTop: '1px solid', + borderBottom: '1px solid', + borderTopColor: theme.palette.primary.light, + borderBottomColor: theme.palette.primary.light, + }, + close: { + position: 'absolute', + top: 0, + right: 0, + color: 'inherit', + }, + title: { + fontWeight: 'inherit', + }, + ingress: { + maxWidth: '32rem', + margin: '1.5rem auto 0 auto', + }, + list: { + padding: '1rem 0', + [theme.breakpoints.up('md')]: { + padding: '1rem 2rem', + }, + '& li + li': { + marginTop: '0.25rem', + }, + '& strong': { + padding: '0 .2rem', + fontSize: theme.fontSizes.smallBody, + fontWeight: 'inherit', + backgroundColor: 'rgba(0, 0, 0, 0.2)', + }, + }, + link: { + color: 'inherit', + }, + button: { + background: 'white', + color: theme.palette.primary.main, + }, +})); diff --git a/frontend/src/component/splash/SplashPageOperators/SplashPageOperators.tsx b/frontend/src/component/splash/SplashPageOperators/SplashPageOperators.tsx new file mode 100644 index 0000000000..67492ce004 --- /dev/null +++ b/frontend/src/component/splash/SplashPageOperators/SplashPageOperators.tsx @@ -0,0 +1,91 @@ +import { useStyles } from 'component/splash/SplashPageOperators/SplashPageOperators.styles'; +import { Link, useHistory } from 'react-router-dom'; +import { Button, IconButton } from '@material-ui/core'; +import { CloseOutlined } from '@material-ui/icons'; +import { OperatorUpgradeAlert } from 'component/common/OperatorUpgradeAlert/OperatorUpgradeAlert'; + +export const SplashPageOperators = () => { + const { push } = useHistory(); + const styles = useStyles(); + + return ( +
+
+
+

New strategy operators

+ push('/')} + > + + +

+ We've added some new feature strategy constraint + operators. Fine-tune your feature targeting like never + before. +

+
+
+

For example:

+
    +
  • + Toggle features at dates: + + DATE_BEFORE{' '} + DATE_AFTER + +
  • +
  • + Toggle features for versions: + + SEMVER_EQ{' '} + SEMVER_GT{' '} + SEMVER_LT + +
  • +
  • + Toggle features for strings: + + STR_CONTAINS{' '} + STR_ENDS_WITH{' '} + STR_STARTS_WITH + +
  • +
  • + Toggle features for numbers: + + NUM_GT NUM_GTE{' '} + NUM_LT NUM_LTE + +
  • +
+
+ +
+ +
+ ); +}; diff --git a/frontend/src/component/splash/SplashPageRedirect/SplashPageRedirect.tsx b/frontend/src/component/splash/SplashPageRedirect/SplashPageRedirect.tsx new file mode 100644 index 0000000000..902876904f --- /dev/null +++ b/frontend/src/component/splash/SplashPageRedirect/SplashPageRedirect.tsx @@ -0,0 +1,52 @@ +import { useAuthSplash } from 'hooks/api/getters/useAuth/useAuthSplash'; +import { useLocation, Redirect } from 'react-router-dom'; +import { matchPath } from 'react-router'; +import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; +import { IFlags } from 'interfaces/uiConfig'; +import { IAuthSplash } from 'hooks/api/getters/useAuth/useAuthEndpoint'; +import { + activeSplashIds, + SplashId, +} from 'component/splash/SplashPage/SplashPage'; + +export const SplashPageRedirect = () => { + const { pathname } = useLocation(); + const { splash } = useAuthSplash(); + const { uiConfig, loading } = useUiConfig(); + + if (!splash || !uiConfig || loading) { + // Wait for everything to load. + return null; + } + + if (matchPath(pathname, { path: '/splash/:splashId' })) { + // We've already redirected to the splash page. + return null; + } + + // Find the splash page to show (if any). + const showSplashId = activeSplashIds.find(splashId => { + return ( + isSplashRelevant(splashId, uiConfig.flags) && + !hasSeenSplashId(splashId, splash) + ); + }); + + if (!showSplashId) { + return null; + } + + return ; +}; + +const hasSeenSplashId = (splashId: SplashId, splash: IAuthSplash): boolean => { + return Boolean(splash[splashId]); +}; + +const isSplashRelevant = (splashId: SplashId, flags: IFlags): boolean => { + if (splashId === 'operators') { + return Boolean(flags.C || flags.CO); + } + + return true; +}; diff --git a/frontend/src/component/strategies/StrategiesList/StrategiesList.tsx b/frontend/src/component/strategies/StrategiesList/StrategiesList.tsx index 9d7bbe51b8..21e8920f8c 100644 --- a/frontend/src/component/strategies/StrategiesList/StrategiesList.tsx +++ b/frontend/src/component/strategies/StrategiesList/StrategiesList.tsx @@ -163,12 +163,14 @@ export const StrategiesList = () => { const reactivateButton = (strategy: ICustomStrategy) => ( - onReactivateStrategy(strategy)} - permission={UPDATE_STRATEGY} - > - - +
+ onReactivateStrategy(strategy)} + permission={UPDATE_STRATEGY} + > + + +
); @@ -179,7 +181,7 @@ export const StrategiesList = () => {
- +
@@ -190,7 +192,7 @@ export const StrategiesList = () => { onClick={() => onDeprecateStrategy(strategy)} permission={UPDATE_STRATEGY} > - + } diff --git a/frontend/src/component/strategies/__tests__/__snapshots__/list-component-test.jsx.snap b/frontend/src/component/strategies/__tests__/__snapshots__/list-component-test.jsx.snap index d1f70d522a..3936770715 100644 --- a/frontend/src/component/strategies/__tests__/__snapshots__/list-component-test.jsx.snap +++ b/frontend/src/component/strategies/__tests__/__snapshots__/list-component-test.jsx.snap @@ -159,14 +159,17 @@ exports[`renders correctly with one strategy 1`] = ` className="MuiIconButton-label" > + + Deprecate strategy + + + Deprecate strategy + =16.9.0": +"@types/react-dom@17.0.14": + version "17.0.14" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.14.tgz#c8f917156b652ddf807711f5becbd2ab018dea9f" + integrity sha512-H03xwEP1oXmSfl3iobtmQ/2dHF5aBHr8aUMwyGZya6OW45G+xtdzmq6HkncefiBt5JU8DVyaWl/nWZbjZCnzAQ== + dependencies: + "@types/react" "*" + +"@types/react-dom@>=16.9.0": version "17.0.13" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.13.tgz#a3323b974ee4280070982b3112351bb1952a7809" integrity sha512-wEP+B8hzvy6ORDv1QBhcQia4j6ea4SFIBttHYpXKPFZRviBvknq0FRh3VrIxeXUmsPkwuXVZrVGG7KUVONmXCQ== @@ -2103,7 +2110,16 @@ "@types/scheduler" "*" csstype "^3.0.2" -"@types/react@17.0.40", "@types/react@>=16.9.0": +"@types/react@17.0.41": + version "17.0.41" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.41.tgz#6e179590d276394de1e357b3f89d05d7d3da8b85" + integrity sha512-chYZ9ogWUodyC7VUTRBfblysKLjnohhFY9bGLwvnUFFy48+vB9DikmB3lW0qTFmBcKSzmdglcvkHK71IioOlDA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/react@>=16.9.0": version "17.0.40" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.40.tgz#dc010cee6254d5239a138083f3799a16638e6bad" integrity sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==