mirror of
https://github.com/Unleash/unleash.git
synced 2025-02-09 00:18:00 +01:00
Merge branch 'main' into fix-rollout-datatype
This commit is contained in:
commit
a724127970
@ -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",
|
||||
|
@ -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={
|
||||
<div className={styles.container}>
|
||||
<ToastRenderer />
|
||||
|
||||
<ConditionallyRender
|
||||
condition={showEnvSplash}
|
||||
show={
|
||||
<EnvironmentSplash onFinish={refetchSplash} />
|
||||
}
|
||||
elseShow={
|
||||
<LayoutPicker>
|
||||
<Switch>
|
||||
<ProtectedRoute
|
||||
exact
|
||||
path="/"
|
||||
unauthorized={isUnauthorized()}
|
||||
component={Redirect}
|
||||
renderProps={{ to: '/features' }}
|
||||
/>
|
||||
{renderMainLayoutRoutes()}
|
||||
{renderStandaloneRoutes()}
|
||||
<Route
|
||||
path="/404"
|
||||
component={NotFound}
|
||||
/>
|
||||
<Redirect to="/404" />
|
||||
</Switch>
|
||||
<Feedback openUrl="http://feedback.unleash.run" />
|
||||
</LayoutPicker>
|
||||
}
|
||||
/>
|
||||
<LayoutPicker>
|
||||
<Switch>
|
||||
<ProtectedRoute
|
||||
exact
|
||||
path="/"
|
||||
unauthorized={isUnauthorized()}
|
||||
component={Redirect}
|
||||
renderProps={{ to: '/features' }}
|
||||
/>
|
||||
{routes.map(renderRoute)}
|
||||
<Route path="/404" component={NotFound} />
|
||||
<Redirect to="/404" />
|
||||
</Switch>
|
||||
<Feedback openUrl="http://feedback.unleash.run" />
|
||||
<SplashPageRedirect />
|
||||
</LayoutPicker>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
|
@ -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<
|
||||
<>
|
||||
<ConditionallyRender
|
||||
condition={oneOf(newOperators, localConstraint.operator)}
|
||||
show={
|
||||
<Alert severity="warning">
|
||||
In order to use this constraint operator, you need to
|
||||
update your SDK to the latest version.
|
||||
</Alert>
|
||||
}
|
||||
show={<OperatorUpgradeAlert />}
|
||||
/>
|
||||
|
||||
<div className={styles.inputContainer}>
|
||||
|
@ -38,7 +38,7 @@ const FormTemplate: React.FC<ICreateProps> = ({
|
||||
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())) {
|
||||
|
@ -0,0 +1,23 @@
|
||||
import { Alert } from '@material-ui/lab';
|
||||
|
||||
export const OperatorUpgradeAlert = () => {
|
||||
return (
|
||||
<Alert severity="warning">
|
||||
Remember to update your Unleash client! New operators require new
|
||||
SDK versions. <OperatorDocsLink />.
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
const OperatorDocsLink = () => {
|
||||
return (
|
||||
<a
|
||||
href="https://docs.getunleash.io/advanced/strategy_constraints#strategy-constraint-operators"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
style={{ color: 'inherit' }}
|
||||
>
|
||||
Read more
|
||||
</a>
|
||||
);
|
||||
};
|
@ -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
|
||||
);
|
||||
};
|
||||
|
@ -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",
|
||||
|
@ -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: {},
|
||||
},
|
||||
];
|
||||
|
@ -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 = (
|
||||
<PermissionIconButton
|
||||
@ -70,9 +71,7 @@ const ProjectInfo = ({
|
||||
condition={Boolean(description)}
|
||||
show={
|
||||
<ConditionallyRender
|
||||
condition={
|
||||
description.length < LONG_DESCRIPTION
|
||||
}
|
||||
condition={isShortDescription}
|
||||
show={
|
||||
<p
|
||||
data-loading
|
||||
@ -113,7 +112,7 @@ const ProjectInfo = ({
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={description.length < LONG_DESCRIPTION}
|
||||
condition={isShortDescription}
|
||||
show={permissionButton}
|
||||
/>
|
||||
</div>
|
||||
|
72
frontend/src/component/splash/SplashPage/SplashPage.tsx
Normal file
72
frontend/src/component/splash/SplashPage/SplashPage.tsx
Normal file
@ -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 (
|
||||
<Switch>
|
||||
<Route path="/splash/environments">
|
||||
<SplashPageEnvironments />
|
||||
</Route>
|
||||
<Route path="/splash/operators">
|
||||
<SplashPageOperators />
|
||||
</Route>
|
||||
<Route>
|
||||
<Redirect to="/" />
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
|
||||
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<string[]>).includes(value);
|
||||
};
|
@ -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 (
|
||||
<>
|
||||
<Splash
|
||||
<SplashPageEnvironmentsContent
|
||||
onFinish={onFinish}
|
||||
components={[
|
||||
<EnvironmentSplashPage
|
||||
<SplashPageEnvironmentsContainer
|
||||
key={1}
|
||||
title={
|
||||
<h2 className={styles.title}>
|
||||
@ -61,7 +53,7 @@ const EnvironmentSplash = ({ onFinish }: IEnvironmentSplashProps) => {
|
||||
}
|
||||
image={<CloudCircle className={styles.icon} />}
|
||||
/>,
|
||||
<EnvironmentSplashPage
|
||||
<SplashPageEnvironmentsContainer
|
||||
key={2}
|
||||
title={
|
||||
<h2 className={styles.title}>
|
||||
@ -79,7 +71,7 @@ const EnvironmentSplash = ({ onFinish }: IEnvironmentSplashProps) => {
|
||||
}
|
||||
image={<Logo1 className={styles.logo} />}
|
||||
/>,
|
||||
<EnvironmentSplashPage
|
||||
<SplashPageEnvironmentsContainer
|
||||
key={3}
|
||||
title={
|
||||
<h2 className={styles.title}>
|
||||
@ -99,7 +91,7 @@ const EnvironmentSplash = ({ onFinish }: IEnvironmentSplashProps) => {
|
||||
}
|
||||
image={<Logo2 className={styles.logo} />}
|
||||
/>,
|
||||
<EnvironmentSplashPage
|
||||
<SplashPageEnvironmentsContainer
|
||||
key={4}
|
||||
title={
|
||||
<h2 className={styles.title}>
|
||||
@ -125,7 +117,7 @@ const EnvironmentSplash = ({ onFinish }: IEnvironmentSplashProps) => {
|
||||
}
|
||||
image={<VpnKey className={styles.icon} />}
|
||||
/>,
|
||||
<EnvironmentSplashPage
|
||||
<SplashPageEnvironmentsContainer
|
||||
key={5}
|
||||
title={
|
||||
<h2 className={styles.title}>Want to know more?</h2>
|
||||
@ -202,5 +194,3 @@ const EnvironmentSplash = ({ onFinish }: IEnvironmentSplashProps) => {
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EnvironmentSplash;
|
@ -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 (
|
||||
<div>
|
||||
{title}
|
||||
@ -22,5 +22,3 @@ const EnvironmentSplashPage = ({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default EnvironmentSplashPage;
|
@ -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',
|
@ -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<ISplashProps> = ({
|
||||
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<ISplashProps> = ({
|
||||
onClick={onClose}
|
||||
data-test={CLOSE_SPLASH}
|
||||
>
|
||||
<CloseOutlined />
|
||||
<CloseOutlined titleAccess="Close" />
|
||||
</IconButton>
|
||||
</div>
|
||||
{components[counter]}
|
||||
@ -109,5 +108,3 @@ const Splash: React.FC<ISplashProps> = ({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Splash;
|
@ -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,
|
||||
},
|
||||
}));
|
@ -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 (
|
||||
<section className={styles.container}>
|
||||
<div className={styles.content}>
|
||||
<header className={styles.header}>
|
||||
<h1 className={styles.title}>New strategy operators</h1>
|
||||
<IconButton
|
||||
className={styles.close}
|
||||
onClick={() => push('/')}
|
||||
>
|
||||
<CloseOutlined titleAccess="Close" />
|
||||
</IconButton>
|
||||
<p className={styles.ingress}>
|
||||
We've added some new feature strategy constraint
|
||||
operators. Fine-tune your feature targeting like never
|
||||
before.
|
||||
</p>
|
||||
</header>
|
||||
<div className={styles.body}>
|
||||
<p>For example:</p>
|
||||
<ul className={styles.list}>
|
||||
<li>
|
||||
<span>Toggle features at dates: </span>
|
||||
<span>
|
||||
<strong>DATE_BEFORE</strong>{' '}
|
||||
<strong>DATE_AFTER</strong>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span>Toggle features for versions: </span>
|
||||
<span>
|
||||
<strong>SEMVER_EQ</strong>{' '}
|
||||
<strong>SEMVER_GT</strong>{' '}
|
||||
<strong>SEMVER_LT</strong>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span>Toggle features for strings: </span>
|
||||
<span>
|
||||
<strong>STR_CONTAINS</strong>{' '}
|
||||
<strong>STR_ENDS_WITH</strong>{' '}
|
||||
<strong>STR_STARTS_WITH</strong>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<span>Toggle features for numbers: </span>
|
||||
<span>
|
||||
<strong>NUM_GT</strong> <strong>NUM_GTE</strong>{' '}
|
||||
<strong>NUM_LT</strong> <strong>NUM_LTE</strong>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<footer className={styles.footer}>
|
||||
<p>
|
||||
<a
|
||||
href="https://docs.getunleash.io/advanced/strategy_constraints#numeric-operators"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={styles.link}
|
||||
>
|
||||
Read all about operators in our in-depth{' '}
|
||||
<strong>docs</strong>
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
<p>
|
||||
<Button
|
||||
className={styles.button}
|
||||
variant="contained"
|
||||
component={Link}
|
||||
to="/"
|
||||
>
|
||||
Fine, whatever, I have work to do!
|
||||
</Button>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
<OperatorUpgradeAlert />
|
||||
</section>
|
||||
);
|
||||
};
|
@ -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 <Redirect to={`/splash/${showSplashId}`} />;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
@ -163,12 +163,14 @@ export const StrategiesList = () => {
|
||||
|
||||
const reactivateButton = (strategy: ICustomStrategy) => (
|
||||
<Tooltip title="Reactivate activation strategy">
|
||||
<PermissionIconButton
|
||||
onClick={() => onReactivateStrategy(strategy)}
|
||||
permission={UPDATE_STRATEGY}
|
||||
>
|
||||
<VisibilityOff />
|
||||
</PermissionIconButton>
|
||||
<div>
|
||||
<PermissionIconButton
|
||||
onClick={() => onReactivateStrategy(strategy)}
|
||||
permission={UPDATE_STRATEGY}
|
||||
>
|
||||
<VisibilityOff titleAccess="Reactivate" />
|
||||
</PermissionIconButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@ -179,7 +181,7 @@ export const StrategiesList = () => {
|
||||
<Tooltip title="You cannot deprecate the default strategy">
|
||||
<div>
|
||||
<IconButton disabled>
|
||||
<Visibility />
|
||||
<Visibility titleAccess="Deprecate strategy" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
@ -190,7 +192,7 @@ export const StrategiesList = () => {
|
||||
onClick={() => onDeprecateStrategy(strategy)}
|
||||
permission={UPDATE_STRATEGY}
|
||||
>
|
||||
<Visibility />
|
||||
<Visibility titleAccess="Deprecate strategy" />
|
||||
</PermissionIconButton>
|
||||
</div>
|
||||
}
|
||||
|
@ -159,14 +159,17 @@ exports[`renders correctly with one strategy 1`] = `
|
||||
className="MuiIconButton-label"
|
||||
>
|
||||
<svg
|
||||
aria-hidden={true}
|
||||
className="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"
|
||||
/>
|
||||
<title>
|
||||
Deprecate strategy
|
||||
</title>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
@ -431,14 +434,17 @@ exports[`renders correctly with one strategy without permissions 1`] = `
|
||||
className="MuiIconButton-label"
|
||||
>
|
||||
<svg
|
||||
aria-hidden={true}
|
||||
className="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"
|
||||
/>
|
||||
<title>
|
||||
Deprecate strategy
|
||||
</title>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
|
@ -15,7 +15,7 @@ export interface IProject {
|
||||
members: number;
|
||||
version: string;
|
||||
name: string;
|
||||
description: string;
|
||||
description?: string;
|
||||
environments: string[];
|
||||
health: number;
|
||||
features: IFeatureToggleListItem[];
|
||||
|
@ -6,7 +6,6 @@ interface IRoute {
|
||||
title?: string;
|
||||
component: React.ComponentType;
|
||||
type: string;
|
||||
layout: string;
|
||||
hidden?: boolean;
|
||||
flag?: string;
|
||||
parent?: string;
|
||||
|
@ -2049,7 +2049,14 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-dom@17.0.13", "@types/react-dom@>=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==
|
||||
|
Loading…
Reference in New Issue
Block a user