From c70b38a62aa93ab6c35dea0496e322e3fb28e7dd Mon Sep 17 00:00:00 2001
From: Tymoteusz Czech <2625371+Tymek@users.noreply.github.com>
Date: Wed, 27 Jul 2022 12:00:15 +0200
Subject: [PATCH] Feature toggle page update (#1140)
* feat: add icon to custom strategies
* feat: update feature toggle screen layout
* strategy and constraints separators
* style disabled envirnments
* strategy constraint style
* strategy drag and drop
* feature env emtpy state
* quick add strategy api
* reorder strategies api integration
* feature strategy header title
* openapi update
* style small chip component
* fix comments after review
* fix issues with strategy constraint operators
* Revert "openapi update"
This reverts commit 27e7651ebae26f61ca76ec910e1f209bae7f2955.
* fix tooltip ref
---
.../ConstraintAccordion.styles.ts | 33 ++---
.../CaseSensitiveButton.tsx | 37 +++---
.../InvertedOperatorButton.tsx | 44 ++++---
.../ConstraintAccordionList.styles.ts | 4 +-
.../ConstraintAccordionList.tsx | 33 +++--
.../ConstraintAccordionView.tsx | 7 +-
.../ConstraintAccordionViewBody.style.ts | 27 ++++
.../ConstraintAccordionViewBody.tsx | 83 ++----------
.../MultipleValues/MultipleValues.tsx | 47 +++++++
.../SingleValue/SingleValue.tsx | 29 ++++
.../ConstraintAccordionViewHeader.tsx | 4 +-
.../ConstraintAccordionViewHeaderInfo.tsx | 2 +
.../ConstraintViewHeaderOperator.tsx | 16 ++-
...raintAccordionViewHeaderMultipleValues.tsx | 4 +-
...ontraintAccordionViewHeaderSingleValue.tsx | 2 +-
.../StyledIconWrapper/StyledIconWrapper.tsx | 36 +++--
.../ConstraintOperator.styles.ts | 2 +-
.../ConstraintOperator/ConstraintOperator.tsx | 10 +-
.../StrategySeparator/StrategySeparator.tsx | 59 ++++++---
.../TablePlaceholder.styles.ts | 2 +-
.../FeatureStrategyEmpty.styles.ts | 22 +++-
.../FeatureStrategyEmpty.tsx | 109 +++++++++++----
.../PresetCard/PresetCard.tsx | 35 +++++
.../FeatureStrategyIcon.tsx | 4 +-
.../FeatureStrategyMenu.tsx | 2 +-
.../FeatureMetricsStats.styles.ts | 2 +-
.../EnvironmentAccordionBody.styles.ts | 14 ++
.../EnvironmentAccordionBody.tsx | 113 ++++++++++++++++
.../StrategyDraggableItem.tsx | 35 +++++
.../ConstraintItem/ConstraintItem.styles.ts} | 7 +-
.../ConstraintItem/ConstraintItem.tsx} | 11 +-
.../StrategyExecution.styles.ts} | 6 +
.../StrategyExecution/StrategyExecution.tsx} | 110 ++++++++--------
.../StrategyItem/StrategyItem.styles.ts} | 7 +-
.../StrategyItem/StrategyItem.tsx} | 41 ++++--
.../EnvironmentFooter.tsx} | 18 +--
.../FeatureOverviewEnvironment.styles.ts | 56 +++-----
.../FeatureOverviewEnvironment.tsx | 124 +++++++++++-------
.../FeatureOverviewEnvironmentBody.tsx | 57 --------
...eatureOverviewEnvironmentMetrics.styles.ts | 2 +-
.../FeatureOverviewEnvironmentStrategies.tsx | 26 ----
.../SectionSeparator/SectionSeparator.tsx | 35 +++++
.../FeatureOverviewEnvironments.tsx | 14 +-
.../history/EventLog/EventLog.styles.ts | 2 +-
.../PlaygroundEditor/PlaygroundEditor.tsx | 2 +-
.../project/ProjectCard/ProjectCard.styles.ts | 2 +-
.../PasswordChecker/PasswordChecker.styles.ts | 2 +-
.../useFeatureStrategyApi.ts | 22 +++-
frontend/src/interfaces/strategy.ts | 5 +
frontend/src/themes/theme.ts | 26 ++++
frontend/src/utils/strategyNames.ts | 5 +-
51 files changed, 897 insertions(+), 500 deletions(-)
create mode 100644 frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/ConstraintAccordionViewBody.style.ts
create mode 100644 frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/MultipleValues/MultipleValues.tsx
create mode 100644 frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/SingleValue/SingleValue.tsx
create mode 100644 frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/PresetCard/PresetCard.tsx
create mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.styles.ts
create mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx
create mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem.tsx
rename frontend/src/component/feature/FeatureView/FeatureOverview/{FeatureOverviewExecution/FeatureOverviewExecutionChips/FeatureOverviewExecutionChips.styles.ts => FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/ConstraintItem/ConstraintItem.styles.ts} (62%)
rename frontend/src/component/feature/FeatureView/FeatureOverview/{FeatureOverviewExecution/FeatureOverviewExecutionChips/FeatureOverviewExecutionChips.tsx => FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/ConstraintItem/ConstraintItem.tsx} (84%)
rename frontend/src/component/feature/FeatureView/FeatureOverview/{FeatureOverviewExecution/FeatureOverviewExecution.styles.ts => FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.styles.ts} (59%)
rename frontend/src/component/feature/FeatureView/FeatureOverview/{FeatureOverviewExecution/FeatureOverviewExecution.tsx => FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.tsx} (79%)
rename frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/{FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy.styles.ts => EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.styles.ts} (75%)
rename frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/{FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy.tsx => EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx} (70%)
rename frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/{FeatureOverviewEnvironmentFooter/FeatureOverviewEnvironmentFooter.tsx => EnvironmentFooter/EnvironmentFooter.tsx} (52%)
delete mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentBody/FeatureOverviewEnvironmentBody.tsx
delete mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategies.tsx
create mode 100644 frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/SectionSeparator/SectionSeparator.tsx
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.styles.ts b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.styles.ts
index 45fc481d0b..ab801c3e3d 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.styles.ts
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordion.styles.ts
@@ -33,7 +33,7 @@ export const useStyles = makeStyles()(theme => ({
},
headerMetaInfo: {
display: 'flex',
- alignItems: 'center',
+ alignItems: 'stretch',
[theme.breakpoints.down(710)]: { flexDirection: 'column' },
},
headerContainer: {
@@ -48,12 +48,11 @@ export const useStyles = makeStyles()(theme => ({
},
headerValuesContainerWrapper: {
display: 'flex',
- flexDirection: 'row',
- justifyContent: 'center',
+ alignItems: 'stretch',
},
headerValuesContainer: {
display: 'flex',
- flexDirection: 'column',
+ alignItems: 'stretch',
},
headerValues: {
fontSize: theme.fontSizes.smallBody,
@@ -106,7 +105,10 @@ export const useStyles = makeStyles()(theme => ({
},
display: 'inline-flex',
},
- headerSelect: { marginRight: '1rem', width: '200px' },
+ headerSelect: {
+ marginRight: '1rem',
+ width: '200px',
+ },
chip: {
margin: '0 0.5rem 0.5rem 0',
},
@@ -121,7 +123,7 @@ export const useStyles = makeStyles()(theme => ({
},
},
accordionDetails: {
- borderTop: `1px solid ${theme.palette.grey[300]}`,
+ borderTop: `1px dashed ${theme.palette.grey[300]}`,
display: 'flex',
flexDirection: 'column',
},
@@ -132,33 +134,16 @@ export const useStyles = makeStyles()(theme => ({
},
summary: {
border: 'none',
- padding: '0.25rem 1rem',
+ padding: theme.spacing(0.5, 3),
'&:hover .valuesExpandLabel': {
textDecoration: 'underline',
},
},
- settingsParagraph: {
- display: 'flex',
- alignItems: 'center',
- padding: '0.5rem 0',
- },
settingsIcon: {
height: '32.5px',
width: '32.5px',
marginRight: '0.5rem',
fill: theme.palette.inactiveIcon,
},
- singleValueView: {
- display: 'flex',
- alignItems: 'center',
- [theme.breakpoints.down(600)]: { flexDirection: 'column' },
- },
- singleValueText: {
- marginRight: '0.75rem',
- [theme.breakpoints.down(600)]: {
- marginBottom: '0.75rem',
- marginRight: 0,
- },
- },
form: { padding: 0, margin: 0, width: '100%' },
}));
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/StyledToggleButton/CaseSensitiveButton/CaseSensitiveButton.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/StyledToggleButton/CaseSensitiveButton/CaseSensitiveButton.tsx
index 16edba2e5a..3663818960 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/StyledToggleButton/CaseSensitiveButton/CaseSensitiveButton.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/StyledToggleButton/CaseSensitiveButton/CaseSensitiveButton.tsx
@@ -1,4 +1,4 @@
-import { Tooltip } from '@mui/material';
+import { Tooltip, Box } from '@mui/material';
import { ReactComponent as CaseSensitive } from 'assets/icons/24_Text format.svg';
import { ReactComponent as CaseSensitiveOff } from 'assets/icons/24_Text format off.svg';
import React from 'react';
@@ -17,30 +17,35 @@ interface CaseSensitiveButtonProps {
export const CaseSensitiveButton = ({
localConstraint,
setCaseInsensitive,
-}: CaseSensitiveButtonProps) => {
- return (
-
+}: CaseSensitiveButtonProps) => (
+
+
+
-
- }
- elseShow={
-
+ }
+ elseShow={
-
- }
- />
- );
-};
+ }
+ />
+
+
+);
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/StyledToggleButton/InvertedOperatorButton/InvertedOperatorButton.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/StyledToggleButton/InvertedOperatorButton/InvertedOperatorButton.tsx
index b05667b4d0..df30a1e712 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/StyledToggleButton/InvertedOperatorButton/InvertedOperatorButton.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionEdit/StyledToggleButton/InvertedOperatorButton/InvertedOperatorButton.tsx
@@ -1,8 +1,7 @@
-import { Tooltip } from '@mui/material';
-import { ReactComponent as NegatedIcon } from '../../../../../../assets/icons/24_Negator.svg';
-import { ReactComponent as NegatedIconOff } from '../../../../../../assets/icons/24_Negator off.svg';
-import React from 'react';
-import { IConstraint } from '../../../../../../interfaces/strategy';
+import { Box, Tooltip } from '@mui/material';
+import { ReactComponent as NegatedIcon } from 'assets/icons/24_Negator.svg';
+import { ReactComponent as NegatedIconOff } from 'assets/icons/24_Negator off.svg';
+import { IConstraint } from 'interfaces/strategy';
import {
StyledToggleButtonOff,
StyledToggleButtonOn,
@@ -17,30 +16,35 @@ interface InvertedOperatorButtonProps {
export const InvertedOperatorButton = ({
localConstraint,
setInvertedOperator,
-}: InvertedOperatorButtonProps) => {
- return (
-
+}: InvertedOperatorButtonProps) => (
+
+
+
-
- }
- elseShow={
-
+ }
+ elseShow={
-
- }
- />
- );
-};
+ }
+ />
+
+
+);
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.styles.ts b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.styles.ts
index 830852c474..8d9e3f4602 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.styles.ts
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.styles.ts
@@ -3,8 +3,8 @@ import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
width: '100%',
- display: 'grid',
- gap: '1rem',
+ display: 'flex',
+ flexDirection: 'column',
},
help: {
fill: theme.palette.grey[600],
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx
index 03672fb694..88e4ba3300 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList.tsx
@@ -1,5 +1,7 @@
+import React, { forwardRef, Fragment, useImperativeHandle } from 'react';
+import { Button, Tooltip } from '@mui/material';
+import { Help } from '@mui/icons-material';
import { IConstraint } from 'interfaces/strategy';
-import React, { forwardRef, useImperativeHandle } from 'react';
import { ConstraintAccordion } from 'component/common/ConstraintAccordion/ConstraintAccordion';
import produce from 'immer';
import useUnleashContext from 'hooks/api/getters/useUnleashContext/useUnleashContext';
@@ -8,8 +10,7 @@ import { objectId } from 'utils/objectId';
import { useStyles } from './ConstraintAccordionList.styles';
import { createEmptyConstraint } from 'component/common/ConstraintAccordion/ConstraintAccordionList/createEmptyConstraint';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import { Button, Tooltip } from '@mui/material';
-import { Help } from '@mui/icons-material';
+import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
interface IConstraintAccordionListProps {
constraints: IConstraint[];
@@ -129,16 +130,22 @@ export const ConstraintAccordionList = forwardRef<
}
/>
{constraints.map((constraint, index) => (
-
+
+ 0}
+ show={ }
+ />
+
+
))}
);
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx
index 033f5b6c18..44aa810358 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView.tsx
@@ -1,6 +1,6 @@
+import { useState } from 'react';
import { Accordion, AccordionSummary, AccordionDetails } from '@mui/material';
import { IConstraint } from 'interfaces/strategy';
-
import { ConstraintAccordionViewBody } from './ConstraintAccordionViewBody/ConstraintAccordionViewBody';
import { ConstraintAccordionViewHeader } from './ConstraintAccordionViewHeader/ConstraintAccordionViewHeader';
import { oneOf } from 'utils/oneOf';
@@ -9,9 +9,8 @@ import {
numOperators,
semVerOperators,
} from 'constants/operators';
-
import { useStyles } from '../ConstraintAccordion.styles';
-import { useState } from 'react';
+
interface IConstraintAccordionViewProps {
constraint: IConstraint;
onDelete?: () => void;
@@ -46,7 +45,7 @@ export const ConstraintAccordionView = ({
sx={{ cursor: expandable ? 'pointer' : 'default' }}
>
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/ConstraintAccordionViewBody.style.ts b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/ConstraintAccordionViewBody.style.ts
new file mode 100644
index 0000000000..7d0cc2d8b8
--- /dev/null
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/ConstraintAccordionViewBody.style.ts
@@ -0,0 +1,27 @@
+import { makeStyles } from 'tss-react/mui';
+
+export const useStyles = makeStyles()(theme => ({
+ chip: {
+ margin: '0 0.5rem 0.5rem 0',
+ },
+ chipValue: {
+ whiteSpace: 'pre',
+ },
+ singleValueView: {
+ display: 'flex',
+ alignItems: 'center',
+ [theme.breakpoints.down(600)]: { flexDirection: 'column' },
+ },
+ singleValueText: {
+ marginRight: '0.75rem',
+ [theme.breakpoints.down(600)]: {
+ marginBottom: '0.75rem',
+ marginRight: 0,
+ },
+ },
+ settingsParagraph: {
+ display: 'flex',
+ alignItems: 'center',
+ padding: '0.5rem 0',
+ },
+}));
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/ConstraintAccordionViewBody.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/ConstraintAccordionViewBody.tsx
index 16a47142ef..026b87c511 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/ConstraintAccordionViewBody.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/ConstraintAccordionViewBody.tsx
@@ -1,15 +1,14 @@
-import { Chip } from '@mui/material';
import { ImportExportOutlined, TextFormatOutlined } from '@mui/icons-material';
-import StringTruncator from 'component/common/StringTruncator/StringTruncator';
-import { useState } from 'react';
import { stringOperators } from 'constants/operators';
import { IConstraint } from 'interfaces/strategy';
import { oneOf } from 'utils/oneOf';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import { useStyles } from 'component/common/ConstraintAccordion/ConstraintAccordion.styles';
-import { ConstraintValueSearch } from 'component/common/ConstraintAccordion/ConstraintValueSearch/ConstraintValueSearch';
import { formatConstraintValue } from 'utils/formatConstraintValue';
import { useLocationSettings } from 'hooks/useLocationSettings';
+import { useStyles as useAccordionStyles } from 'component/common/ConstraintAccordion/ConstraintAccordion.styles';
+import { useStyles } from './ConstraintAccordionViewBody.style';
+import { MultipleValues } from './MultipleValues/MultipleValues';
+import { SingleValue } from './SingleValue/SingleValue';
interface IConstraintAccordionViewBodyProps {
constraint: IConstraint;
@@ -18,7 +17,8 @@ interface IConstraintAccordionViewBodyProps {
export const ConstraintAccordionViewBody = ({
constraint,
}: IConstraintAccordionViewBodyProps) => {
- const { classes: styles } = useStyles();
+ const { classes: styles } = useAccordionStyles();
+ const { classes } = useStyles();
const { locationSettings } = useLocationSettings();
return (
@@ -29,7 +29,7 @@ export const ConstraintAccordionViewBody = ({
Boolean(constraint.caseInsensitive)
}
show={
-
+
{' '}
Case insensitive setting is active
@@ -39,7 +39,7 @@ export const ConstraintAccordionViewBody = ({
+
{' '}
Operator is negated
@@ -56,70 +56,3 @@ export const ConstraintAccordionViewBody = ({
);
};
-
-interface ISingleValueProps {
- value: string | undefined;
- operator: string;
-}
-
-const SingleValue = ({ value, operator }: ISingleValueProps) => {
- const { classes: styles } = useStyles();
- if (!value) return null;
-
- return (
-
-
Value must be {operator}
{' '}
-
- }
- className={styles.chip}
- />
-
- );
-};
-
-interface IMultipleValuesProps {
- values: string[] | undefined;
-}
-
-const MultipleValues = ({ values }: IMultipleValuesProps) => {
- const [filter, setFilter] = useState('');
- const { classes: styles } = useStyles();
-
- if (!values || values.length === 0) return null;
-
- return (
- <>
- 20}
- show={
-
- }
- />
- {values
- .filter(value => value.includes(filter))
- .map((value, index) => (
-
- }
- className={styles.chip}
- />
- ))}
- >
- );
-};
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/MultipleValues/MultipleValues.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/MultipleValues/MultipleValues.tsx
new file mode 100644
index 0000000000..cdcb925720
--- /dev/null
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/MultipleValues/MultipleValues.tsx
@@ -0,0 +1,47 @@
+import { useState } from 'react';
+import { Chip } from '@mui/material';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import StringTruncator from 'component/common/StringTruncator/StringTruncator';
+import { ConstraintValueSearch } from '../../../ConstraintValueSearch/ConstraintValueSearch';
+import { useStyles } from '../ConstraintAccordionViewBody.style';
+
+interface IMultipleValuesProps {
+ values: string[] | undefined;
+}
+
+export const MultipleValues = ({ values }: IMultipleValuesProps) => {
+ const [filter, setFilter] = useState('');
+ const { classes: styles } = useStyles();
+
+ if (!values || values.length === 0) return null;
+
+ return (
+ <>
+ 20}
+ show={
+
+ }
+ />
+ {values
+ .filter(value => value.includes(filter))
+ .map((value, index) => (
+
+ }
+ className={styles.chip}
+ />
+ ))}
+ >
+ );
+};
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/SingleValue/SingleValue.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/SingleValue/SingleValue.tsx
new file mode 100644
index 0000000000..f34ef16d46
--- /dev/null
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/SingleValue/SingleValue.tsx
@@ -0,0 +1,29 @@
+import { Chip } from '@mui/material';
+import StringTruncator from 'component/common/StringTruncator/StringTruncator';
+import { useStyles } from '../ConstraintAccordionViewBody.style';
+
+interface ISingleValueProps {
+ value: string | undefined;
+ operator: string;
+}
+
+export const SingleValue = ({ value, operator }: ISingleValueProps) => {
+ const { classes: styles } = useStyles();
+ if (!value) return null;
+
+ return (
+
+
Value must be {operator}
{' '}
+
+ }
+ className={styles.chip}
+ />
+
+ );
+};
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx
index e9eddc7f85..897304438d 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeader.tsx
@@ -1,10 +1,8 @@
import { ConstraintIcon } from 'component/common/ConstraintAccordion/ConstraintIcon';
import { IConstraint } from 'interfaces/strategy';
-
-import { useStyles } from 'component/common/ConstraintAccordion/ConstraintAccordion.styles';
-import React from 'react';
import { ConstraintAccordionViewHeaderInfo } from './ConstraintAccordionViewHeaderInfo/ConstraintAccordionViewHeaderInfo';
import { ConstraintAccordionHeaderActions } from '../../ConstraintAccordionHeaderActions/ConstraintAccordionHeaderActions';
+import { useStyles } from 'component/common/ConstraintAccordion/ConstraintAccordion.styles';
interface IConstraintAccordionViewHeaderProps {
constraint: IConstraint;
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeaderInfo/ConstraintAccordionViewHeaderInfo.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeaderInfo/ConstraintAccordionViewHeaderInfo.tsx
index 3e2fc4e282..90f5d9ad5f 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeaderInfo/ConstraintAccordionViewHeaderInfo.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintAccordionViewHeaderInfo/ConstraintAccordionViewHeaderInfo.tsx
@@ -15,6 +15,8 @@ const StyledHeaderText = styled('span')(({ theme }) => ({
maxWidth: '100px',
minWidth: '100px',
marginRight: '10px',
+ marginTop: 'auto',
+ marginBottom: 'auto',
wordBreak: 'break-word',
fontSize: theme.fontSizes.smallBody,
[theme.breakpoints.down(710)]: {
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintViewHeaderOperator/ConstraintViewHeaderOperator.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintViewHeaderOperator/ConstraintViewHeaderOperator.tsx
index 9cd51b9269..6dcbab9655 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintViewHeaderOperator/ConstraintViewHeaderOperator.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintViewHeaderOperator/ConstraintViewHeaderOperator.tsx
@@ -1,9 +1,8 @@
import { IConstraint } from '../../../../../../interfaces/strategy';
import { ConditionallyRender } from '../../../../ConditionallyRender/ConditionallyRender';
-import { Tooltip } from '@mui/material';
+import { Tooltip, Box } from '@mui/material';
import { ReactComponent as NegatedIcon } from '../../../../../../assets/icons/24_Negator.svg';
import { ConstraintOperator } from '../../../ConstraintOperator/ConstraintOperator';
-import React from 'react';
import { useStyles } from '../../../ConstraintAccordion.styles';
import { StyledIconWrapper } from '../StyledIconWrapper/StyledIconWrapper';
@@ -22,14 +21,19 @@ export const ConstraintViewHeaderOperator = ({
condition={Boolean(constraint.inverted)}
show={
-
-
-
+
+
+
+
+
}
/>
-
+
);
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderMultipleValues/ConstraintAccordionViewHeaderMultipleValues.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderMultipleValues/ConstraintAccordionViewHeaderMultipleValues.tsx
index 2ddb041d2b..5367f42b53 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderMultipleValues/ConstraintAccordionViewHeaderMultipleValues.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderMultipleValues/ConstraintAccordionViewHeaderMultipleValues.tsx
@@ -17,6 +17,7 @@ const StyledValuesSpan = styled('span')(({ theme }) => ({
overflow: 'hidden',
wordBreak: 'break-word',
fontSize: theme.fontSizes.smallBody,
+ margin: 'auto 0',
[theme.breakpoints.down(710)]: {
margin: theme.spacing(1, 0),
textAlign: 'center',
@@ -90,8 +91,7 @@ export const ConstraintAccordionViewHeaderMultipleValues = ({
)}
>
{!expanded
- ? `View all (
- ${constraint?.values?.length})`
+ ? `View all (${constraint?.values?.length})`
: 'View less'}
}
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderSingleValue/ContraintAccordionViewHeaderSingleValue.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderSingleValue/ContraintAccordionViewHeaderSingleValue.tsx
index 00818af583..c23bb02388 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderSingleValue/ContraintAccordionViewHeaderSingleValue.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ContraintAccordionViewHeaderSingleValue/ContraintAccordionViewHeaderSingleValue.tsx
@@ -4,13 +4,13 @@ import { stringOperators } from '../../../../../../constants/operators';
import { Chip, styled, Tooltip } from '@mui/material';
import { ReactComponent as CaseSensitive } from '../../../../../../assets/icons/24_Text format.svg';
import { formatConstraintValue } from '../../../../../../utils/formatConstraintValue';
-import React from 'react';
import { useStyles } from '../../../ConstraintAccordion.styles';
import { StyledIconWrapper } from '../StyledIconWrapper/StyledIconWrapper';
import { IConstraint } from '../../../../../../interfaces/strategy';
import { useLocationSettings } from '../../../../../../hooks/useLocationSettings';
const StyledSingleValueChip = styled(Chip)(({ theme }) => ({
+ margin: 'auto 0',
[theme.breakpoints.down(710)]: {
margin: theme.spacing(1, 0),
},
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/StyledIconWrapper/StyledIconWrapper.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/StyledIconWrapper/StyledIconWrapper.tsx
index dd6568c427..0e2ac4e654 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/StyledIconWrapper/StyledIconWrapper.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/StyledIconWrapper/StyledIconWrapper.tsx
@@ -1,16 +1,34 @@
import { styled } from '@mui/material';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { FC, forwardRef } from 'react';
-export const StyledIconWrapper = styled('div')<{
- marginright?: string;
-}>(({ theme, marginright }) => ({
+export const StyledIconWrapperBase = styled('div')<{
+ prefix?: boolean;
+}>(({ theme }) => ({
backgroundColor: theme.palette.grey[200],
width: 28,
- height: 48,
- display: 'inline-flex',
+ display: 'flex',
+ alignItems: 'center',
justifyContent: 'center',
- padding: '10px 0',
+ alignSelf: 'stretch',
color: theme.palette.primary.main,
- marginRight: marginright ? marginright : '1rem',
- marginTop: 'auto',
- marginBottom: 'auto',
+ marginRight: '1rem',
+ borderRadius: theme.shape.borderRadius,
}));
+
+const StyledPrefixIconWrapper = styled(StyledIconWrapperBase)(() => ({
+ marginRight: 0,
+ borderTopRightRadius: 0,
+ borderBottomRightRadius: 0,
+}));
+
+export const StyledIconWrapper = forwardRef<
+ HTMLDivElement,
+ { isPrefix?: boolean; children?: React.ReactNode }
+>(({ isPrefix, ...props }, ref) => (
+ }
+ elseShow={() => }
+ />
+));
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.styles.ts b/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.styles.ts
index ff2bac017d..618eb43670 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.styles.ts
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.styles.ts
@@ -2,7 +2,7 @@ import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
- padding: '0.5rem 0.75rem',
+ padding: theme.spacing(0.5, 1.5),
borderRadius: theme.shape.borderRadius,
backgroundColor: theme.palette.grey[200],
lineHeight: 1.25,
diff --git a/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx b/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx
index b21c6159a1..991c97b0a9 100644
--- a/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx
+++ b/frontend/src/component/common/ConstraintAccordion/ConstraintOperator/ConstraintOperator.tsx
@@ -5,10 +5,12 @@ import React from 'react';
interface IConstraintOperatorProps {
constraint: IConstraint;
+ hasPrefix?: boolean;
}
export const ConstraintOperator = ({
constraint,
+ hasPrefix,
}: IConstraintOperatorProps) => {
const { classes: styles } = useStyles();
@@ -16,7 +18,13 @@ export const ConstraintOperator = ({
const operatorText = formatOperatorDescription(constraint.operator);
return (
-
+
{operatorName}
{operatorText}
diff --git a/frontend/src/component/common/StrategySeparator/StrategySeparator.tsx b/frontend/src/component/common/StrategySeparator/StrategySeparator.tsx
index 1a56ea9be0..4867a5c2a7 100644
--- a/frontend/src/component/common/StrategySeparator/StrategySeparator.tsx
+++ b/frontend/src/component/common/StrategySeparator/StrategySeparator.tsx
@@ -1,24 +1,45 @@
-import { useTheme } from '@mui/material';
+import { styled } from '@mui/material';
+import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender';
interface IStrategySeparatorProps {
- text: string;
+ text: 'AND' | 'OR';
}
-export const StrategySeparator = ({ text }: IStrategySeparatorProps) => {
- const theme = useTheme();
+const StyledContainer = styled('div')(({ theme }) => ({
+ height: theme.spacing(2),
+ position: 'relative',
+ width: '100%',
+}));
- return (
-
- {text}
-
- );
-};
+const StyledContent = styled('div')(({ theme }) => ({
+ padding: theme.spacing(0.75, 1.5),
+ color: theme.palette.text.primary,
+ fontSize: theme.fontSizes.smallerBody,
+ backgroundColor: theme.palette.secondaryContainer,
+ borderRadius: theme.shape.borderRadius,
+ position: 'absolute',
+ zIndex: theme.zIndex.fab,
+ top: '50%',
+ left: theme.spacing(3),
+ transform: 'translateY(-50%)',
+}));
+
+const StyledCenteredContent = styled(StyledContent)(({ theme }) => ({
+ top: '50%',
+ left: '50%',
+ transform: 'translate(-50%, -50%)',
+ backgroundColor: theme.palette.secondary.light,
+ border: `1px solid ${theme.palette.primary.border}`,
+}));
+
+export const StrategySeparator = ({ text }: IStrategySeparatorProps) => (
+
+ {text} }
+ elseShow={() => (
+ {text}
+ )}
+ />
+
+);
diff --git a/frontend/src/component/common/Table/TablePlaceholder/TablePlaceholder.styles.ts b/frontend/src/component/common/Table/TablePlaceholder/TablePlaceholder.styles.ts
index 65c3de6384..4df809bbce 100644
--- a/frontend/src/component/common/Table/TablePlaceholder/TablePlaceholder.styles.ts
+++ b/frontend/src/component/common/Table/TablePlaceholder/TablePlaceholder.styles.ts
@@ -2,7 +2,7 @@ import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
emptyStateListItem: {
- border: `2px dashed ${theme.palette.grey[100]}`,
+ border: `2px dashed ${theme.palette.neutral.light}`,
padding: '0.8rem',
textAlign: 'center',
display: 'flex',
diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty.styles.ts b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty.styles.ts
index 9575516df7..fd2b71c21f 100644
--- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty.styles.ts
+++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty.styles.ts
@@ -1,14 +1,22 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
- noItemsParagraph: {
- margin: '1rem 0',
+ container: {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ justifyContent: 'center',
},
- link: {
- display: 'block',
- margin: '1rem 0 0 0',
+ title: {
+ fontSize: theme.fontSizes.bodySize,
+ textAlign: 'center',
+ color: theme.palette.text.primary,
+ marginBottom: theme.spacing(1),
},
- envName: {
- fontWeight: 'bold',
+ description: {
+ color: theme.palette.text.secondary,
+ fontSize: theme.fontSizes.smallBody,
+ textAlign: 'center',
+ marginBottom: theme.spacing(3),
},
}));
diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty.tsx
index e943aefd83..707599909e 100644
--- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty.tsx
+++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty.tsx
@@ -1,7 +1,13 @@
-import NoItems from 'component/common/NoItems/NoItems';
-import StringTruncator from 'component/common/StringTruncator/StringTruncator';
-import { useStyles } from './FeatureStrategyEmpty.styles';
+import { Link } from 'react-router-dom';
+import { Box } from '@mui/material';
+import { SectionSeparator } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/SectionSeparator/SectionSeparator';
+import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi';
+import useToast from 'hooks/useToast';
+import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import { FeatureStrategyMenu } from '../FeatureStrategyMenu/FeatureStrategyMenu';
+import { PresetCard } from './PresetCard/PresetCard';
+import { useStyles } from './FeatureStrategyEmpty.styles';
+import { formatUnknownError } from 'utils/formatUnknownError';
interface IFeatureStrategyEmptyProps {
projectId: string;
@@ -15,30 +21,58 @@ export const FeatureStrategyEmpty = ({
environmentId,
}: IFeatureStrategyEmptyProps) => {
const { classes: styles } = useStyles();
+ const { addStrategyToFeature } = useFeatureStrategyApi();
+ const { setToastData, setToastApiError } = useToast();
+ const { refetchFeature } = useFeature(projectId, featureId);
+
+ const onAfterAddStrategy = () => {
+ refetchFeature();
+ setToastData({
+ title: 'Strategy created',
+ text: 'Successfully created strategy',
+ type: 'success',
+ });
+ };
+
+ const onAddSimpleStrategy = async () => {
+ try {
+ await addStrategyToFeature(projectId, featureId, environmentId, {
+ name: 'default',
+ parameters: {},
+ constraints: [],
+ });
+ onAfterAddStrategy();
+ } catch (error) {
+ setToastApiError(formatUnknownError(error));
+ }
+ };
+
+ const onAddGradualRolloutStrategy = async () => {
+ try {
+ await addStrategyToFeature(projectId, featureId, environmentId, {
+ name: 'flexibleRollout',
+ parameters: {
+ rollout: '50',
+ stickiness: 'default',
+ },
+ constraints: [],
+ });
+ onAfterAddStrategy();
+ } catch (error) {
+ setToastApiError(formatUnknownError(error));
+ }
+ };
return (
-
-
- No strategies added in the{' '}
- {' '}
- environment
-
-
+
+
+ You have not defined any strategies yet.
+
+
Strategies added in this environment will only be executed if
- the SDK is using an API key configured for this environment.
-
- Read more here
-
+ the SDK is using an{' '}
+ API key configured for this
+ environment.
-
+
+ Or use a strategy template
+
+
+
+ The standard strategy is strictly on/off for your entire
+ userbase.
+
+
+ Roll out to a percentage of your userbase.
+
+
+
);
};
diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/PresetCard/PresetCard.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/PresetCard/PresetCard.tsx
new file mode 100644
index 0000000000..f4e5cfea3f
--- /dev/null
+++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyEmpty/PresetCard/PresetCard.tsx
@@ -0,0 +1,35 @@
+import { Button, Card, CardContent, Typography } from '@mui/material';
+import { FC } from 'react';
+
+interface IPresetCardProps {
+ title: string;
+ onClick: () => void;
+}
+
+export const PresetCard: FC = ({
+ title,
+ children,
+ onClick,
+}) => (
+
+
+
+ {title}
+
+
+ {children}
+
+
+
+ Use template
+
+
+
+);
diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyIcon/FeatureStrategyIcon.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyIcon/FeatureStrategyIcon.tsx
index 1665665354..a90721befa 100644
--- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyIcon/FeatureStrategyIcon.tsx
+++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyIcon/FeatureStrategyIcon.tsx
@@ -16,7 +16,9 @@ export const FeatureStrategyIcon = ({
return (
-
+ <>
+
+ >
);
diff --git a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx
index bb8aa10114..481925b40b 100644
--- a/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx
+++ b/frontend/src/component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu.tsx
@@ -1,7 +1,7 @@
+import React, { useState } from 'react';
import PermissionButton, {
IPermissionButtonProps,
} from 'component/common/PermissionButton/PermissionButton';
-import React, { useState } from 'react';
import { CREATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
import { Popover } from '@mui/material';
import { FeatureStrategyMenuCards } from './FeatureStrategyMenuCards/FeatureStrategyMenuCards';
diff --git a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsStats/FeatureMetricsStats.styles.ts b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsStats/FeatureMetricsStats.styles.ts
index c6cc8001fb..80c05eb61e 100644
--- a/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsStats/FeatureMetricsStats.styles.ts
+++ b/frontend/src/component/feature/FeatureView/FeatureMetrics/FeatureMetricsStats/FeatureMetricsStats.styles.ts
@@ -3,7 +3,7 @@ import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
item: {
padding: theme.spacing(2),
- background: theme.palette.grey[100],
+ background: theme.palette.secondaryContainer,
borderRadius: theme.spacing(2),
textAlign: 'center',
[theme.breakpoints.up('md')]: {
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.styles.ts b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.styles.ts
new file mode 100644
index 0000000000..01e1765bca
--- /dev/null
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.styles.ts
@@ -0,0 +1,14 @@
+import { makeStyles } from 'tss-react/mui';
+
+export const useStyles = makeStyles()(theme => ({
+ accordionBodyInnerContainer: {
+ [theme.breakpoints.down(400)]: {
+ padding: '0.5rem',
+ },
+ },
+ accordionBody: {
+ width: '100%',
+ position: 'relative',
+ paddingBottom: '1rem',
+ },
+}));
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx
new file mode 100644
index 0000000000..4cca8ddd9f
--- /dev/null
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/EnvironmentAccordionBody.tsx
@@ -0,0 +1,113 @@
+import { useEffect, useState } from 'react';
+import { Alert } from '@mui/material';
+import useFeatureStrategyApi from 'hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi';
+import { formatUnknownError } from 'utils/formatUnknownError';
+import useToast from 'hooks/useToast';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { StrategyDraggableItem } from './StrategyDraggableItem/StrategyDraggableItem';
+import { IFeatureEnvironment } from 'interfaces/featureToggle';
+import { FeatureStrategyEmpty } from 'component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty';
+import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
+import { useStyles } from './EnvironmentAccordionBody.styles';
+import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
+
+interface IEnvironmentAccordionBodyProps {
+ isDisabled: boolean;
+ featureEnvironment?: IFeatureEnvironment;
+}
+
+const EnvironmentAccordionBody = ({
+ featureEnvironment,
+ isDisabled,
+}: IEnvironmentAccordionBodyProps) => {
+ const projectId = useRequiredPathParam('projectId');
+ const featureId = useRequiredPathParam('featureId');
+ const { setStrategiesSortOrder } = useFeatureStrategyApi();
+ const { setToastData, setToastApiError } = useToast();
+ const { refetchFeature } = useFeature(projectId, featureId);
+
+ const [strategies, setStrategies] = useState(
+ featureEnvironment?.strategies || []
+ );
+ const { classes: styles } = useStyles();
+ useEffect(() => {
+ // Use state to enable drag and drop, but switch to API output when it arrives
+ setStrategies(featureEnvironment?.strategies || []);
+ }, [featureEnvironment?.strategies]);
+
+ if (!featureEnvironment) {
+ return null;
+ }
+
+ const onDragAndDrop = async (
+ from: number,
+ to: number,
+ dropped?: boolean
+ ) => {
+ if (from !== to && dropped) {
+ const newStrategies = [...strategies];
+ const movedStrategy = newStrategies.splice(from, 1)[0];
+ newStrategies.splice(to, 0, movedStrategy);
+ setStrategies(newStrategies);
+ try {
+ await setStrategiesSortOrder(
+ projectId,
+ featureId,
+ featureEnvironment.name,
+ [...newStrategies].map((strategy, sortOrder) => ({
+ id: strategy.id,
+ sortOrder,
+ }))
+ );
+ refetchFeature();
+ setToastData({
+ title: 'Order of strategies updated',
+ type: 'success',
+ });
+ } catch (error: unknown) {
+ setToastApiError(formatUnknownError(error));
+ }
+ }
+ };
+
+ return (
+
+
+
0 && isDisabled}
+ show={() => (
+
+ This environment is disabled, which means that none
+ of your strategies are executing.
+
+ )}
+ />
+ 0}
+ show={
+ <>
+ {strategies.map((strategy, index) => (
+
+ ))}
+ >
+ }
+ elseShow={
+
+ }
+ />
+
+
+ );
+};
+
+export default EnvironmentAccordionBody;
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem.tsx
new file mode 100644
index 0000000000..7465b6b3f6
--- /dev/null
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem.tsx
@@ -0,0 +1,35 @@
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
+import { MoveListItem, useDragItem } from 'hooks/useDragItem';
+import { IFeatureStrategy } from 'interfaces/strategy';
+import { StrategyItem } from './StrategyItem/StrategyItem';
+
+interface IStrategyDraggableItemProps {
+ strategy: IFeatureStrategy;
+ environmentName: string;
+ index: number;
+ onDragAndDrop: MoveListItem;
+}
+
+export const StrategyDraggableItem = ({
+ strategy,
+ index,
+ environmentName,
+ onDragAndDrop,
+}: IStrategyDraggableItemProps) => {
+ const ref = useDragItem(index, onDragAndDrop);
+
+ return (
+
+ 0}
+ show={ }
+ />
+
+
+ );
+};
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecutionChips/FeatureOverviewExecutionChips.styles.ts b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/ConstraintItem/ConstraintItem.styles.ts
similarity index 62%
rename from frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecutionChips/FeatureOverviewExecutionChips.styles.ts
rename to frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/ConstraintItem/ConstraintItem.styles.ts
index 45ebf5aec8..fb8f37bb11 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecutionChips/FeatureOverviewExecutionChips.styles.ts
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/ConstraintItem/ConstraintItem.styles.ts
@@ -1,7 +1,12 @@
import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
- container: { textAlign: 'center' },
+ container: {
+ width: '100%',
+ padding: theme.spacing(2, 3),
+ borderRadius: theme.shape.borderRadius,
+ border: `1px solid ${theme.palette.divider}`,
+ },
chip: {
margin: '0.25rem',
},
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecutionChips/FeatureOverviewExecutionChips.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/ConstraintItem/ConstraintItem.tsx
similarity index 84%
rename from frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecutionChips/FeatureOverviewExecutionChips.tsx
rename to frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/ConstraintItem/ConstraintItem.tsx
index 42168f0688..727d9ce42d 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecutionChips/FeatureOverviewExecutionChips.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/ConstraintItem/ConstraintItem.tsx
@@ -1,17 +1,14 @@
import { Chip } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import { useStyles } from './FeatureOverviewExecutionChips.styles';
+import { useStyles } from './ConstraintItem.styles';
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
-interface IFeatureOverviewExecutionChipsProps {
+interface IConstraintItemProps {
value: string[];
text: string;
}
-const FeatureOverviewExecutionChips = ({
- value,
- text,
-}: IFeatureOverviewExecutionChipsProps) => {
+export const ConstraintItem = ({ value, text }: IConstraintItemProps) => {
const { classes: styles } = useStyles();
return (
@@ -44,5 +41,3 @@ const FeatureOverviewExecutionChips = ({
);
};
-
-export default FeatureOverviewExecutionChips;
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecution.styles.ts b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.styles.ts
similarity index 59%
rename from frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecution.styles.ts
rename to frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.styles.ts
index 779b243dbd..e2d49389e1 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecution.styles.ts
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.styles.ts
@@ -9,4 +9,10 @@ export const useStyles = makeStyles()(theme => ({
valueSeparator: {
color: theme.palette.grey[700],
},
+ summary: {
+ width: '100%',
+ padding: theme.spacing(2, 3),
+ borderRadius: theme.shape.borderRadius,
+ border: `1px solid ${theme.palette.divider}`,
+ },
}));
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecution.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.tsx
similarity index 79%
rename from frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecution.tsx
rename to frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.tsx
index f832e0e468..e1214863a9 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecution.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution.tsx
@@ -1,37 +1,29 @@
import { Fragment } from 'react';
-import {
- IFeatureStrategy,
- IFeatureStrategyParameters,
- IConstraint,
-} from 'interfaces/strategy';
+import { Box, Chip } from '@mui/material';
+import { IFeatureStrategy } from 'interfaces/strategy';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import PercentageCircle from 'component/common/PercentageCircle/PercentageCircle';
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
-import FeatureOverviewExecutionChips from './FeatureOverviewExecutionChips/FeatureOverviewExecutionChips';
+import { ConstraintItem } from './ConstraintItem/ConstraintItem';
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { FeatureOverviewSegment } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewSegment/FeatureOverviewSegment';
import { ConstraintAccordionList } from 'component/common/ConstraintAccordion/ConstraintAccordionList/ConstraintAccordionList';
-import { useStyles } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecution.styles';
+import { useStyles } from './StrategyExecution.styles';
import {
parseParameterString,
parseParameterNumber,
parseParameterStrings,
} from 'utils/parseParameter';
-interface IFeatureOverviewExecutionProps {
- parameters: IFeatureStrategyParameters;
- constraints?: IConstraint[];
+interface IStrategyExecutionProps {
strategy: IFeatureStrategy;
percentageFill?: string;
}
-const FeatureOverviewExecution = ({
- parameters,
- constraints = [],
- strategy,
-}: IFeatureOverviewExecutionProps) => {
+export const StrategyExecution = ({ strategy }: IStrategyExecutionProps) => {
+ const { parameters, constraints = [] } = strategy;
const { classes: styles } = useStyles();
const { strategies } = useStrategies();
const { uiConfig } = useUiConfig();
@@ -52,51 +44,51 @@ const FeatureOverviewExecution = ({
case 'rollout':
case 'Rollout':
return (
-
-
- {parameters[key]}% of your base{' '}
- {constraints.length > 0
- ? 'who match constraints'
- : ''}{' '}
- is included.
-
-
+
-
+
+ {' '}
+ of your base{' '}
+ {constraints.length > 0
+ ? 'who match constraints'
+ : ''}{' '}
+ is included.
+
+
);
case 'userIds':
case 'UserIds':
const users = parseParameterStrings(parameters[key]);
return (
-
+
);
case 'hostNames':
case 'HostNames':
const hosts = parseParameterStrings(parameters[key]);
return (
-
+
);
case 'IPs':
const IPs = parseParameterStrings(parameters[key]);
- return (
-
- );
+ return ;
case 'stickiness':
case 'groupId':
return null;
@@ -117,10 +109,7 @@ const FeatureOverviewExecution = ({
);
return (
-
+
}
@@ -130,13 +119,21 @@ const FeatureOverviewExecution = ({
case 'percentage':
return (
-
- {strategy?.parameters[param.name]}% of your base{' '}
+
+ {' '}
+ of your base{' '}
{constraints?.length > 0
? 'who match constraints'
: ''}{' '}
is included.
-
+
The standard strategy is on for all users.}
+ show={
+
+ The standard strategy is{' '}
+ {' '}
+ for all users.
+
+ }
/>
{renderParameters()}
{renderCustomStrategy()}
>
);
};
-
-export default FeatureOverviewExecution;
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy.styles.ts b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.styles.ts
similarity index 75%
rename from frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy.styles.ts
rename to frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.styles.ts
index 4bfd97e8ff..2972a4ee62 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy.styles.ts
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.styles.ts
@@ -2,18 +2,20 @@ import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
container: {
- borderRadius: theme.shape.borderRadius,
+ borderRadius: theme.shape.borderRadiusMedium,
border: `1px solid ${theme.palette.grey[300]}`,
'& + &': {
marginTop: '1rem',
},
+ background: theme.palette.background.default,
},
header: {
- padding: '0.5rem',
+ padding: theme.spacing(0.5, 2),
display: 'flex',
gap: '0.5rem',
alignItems: 'center',
borderBottom: `1px solid ${theme.palette.grey[300]}`,
+ fontWeight: theme.typography.fontWeightMedium,
},
icon: {
fill: theme.palette.inactiveIcon,
@@ -25,7 +27,6 @@ export const useStyles = makeStyles()(theme => ({
body: {
padding: '1rem',
display: 'grid',
- gap: '1rem',
justifyItems: 'center',
},
}));
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx
similarity index 70%
rename from frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy.tsx
rename to frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx
index 7844f5706d..f44f82ce6a 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx
@@ -1,5 +1,5 @@
-import { Edit } from '@mui/icons-material';
-import { useTheme } from '@mui/material/styles';
+import { DragIndicator, Edit } from '@mui/icons-material';
+import { styled, useTheme, IconButton } from '@mui/material';
import { Link } from 'react-router-dom';
import { IFeatureStrategy } from 'interfaces/strategy';
import {
@@ -8,28 +8,36 @@ import {
} from 'utils/strategyNames';
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
import { UPDATE_FEATURE_STRATEGY } from 'component/providers/AccessProvider/permissions';
-import FeatureOverviewExecution from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewExecution/FeatureOverviewExecution';
-import { useStyles } from './FeatureOverviewEnvironmentStrategy.styles';
import { formatEditStrategyPath } from 'component/feature/FeatureStrategy/FeatureStrategyEdit/FeatureStrategyEdit';
import { FeatureStrategyRemove } from 'component/feature/FeatureStrategy/FeatureStrategyRemove/FeatureStrategyRemove';
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
+import { StrategyExecution } from './StrategyExecution/StrategyExecution';
+import { useStyles } from './StrategyItem.styles';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-interface IFeatureOverviewEnvironmentStrategyProps {
+interface IStrategyItemProps {
environmentId: string;
strategy: IFeatureStrategy;
+ isDraggable?: boolean;
}
-const FeatureOverviewEnvironmentStrategy = ({
+const DragIcon = styled(IconButton)(({ theme }) => ({
+ padding: 0,
+ cursor: 'inherit',
+ transition: 'color 0.2s ease-in-out',
+}));
+
+export const StrategyItem = ({
environmentId,
strategy,
-}: IFeatureOverviewEnvironmentStrategyProps) => {
+ isDraggable,
+}: IStrategyItemProps) => {
const projectId = useRequiredPathParam('projectId');
const featureId = useRequiredPathParam('featureId');
const theme = useTheme();
const { classes: styles } = useStyles();
const Icon = getFeatureStrategyIcon(strategy.name);
- const { parameters, constraints } = strategy;
const editStrategyPath = formatEditStrategyPath(
projectId,
@@ -41,6 +49,17 @@ const FeatureOverviewEnvironmentStrategy = ({
return (
);
};
-
-export default FeatureOverviewEnvironmentStrategy;
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentFooter/FeatureOverviewEnvironmentFooter.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentFooter/EnvironmentFooter.tsx
similarity index 52%
rename from frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentFooter/FeatureOverviewEnvironmentFooter.tsx
rename to frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentFooter/EnvironmentFooter.tsx
index 56ef5df658..7c2ea3285e 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentFooter/FeatureOverviewEnvironmentFooter.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentFooter/EnvironmentFooter.tsx
@@ -1,27 +1,22 @@
import { IFeatureEnvironmentMetrics } from 'interfaces/featureToggle';
-import { useStyles } from '../FeatureOverviewEnvironment.styles';
import { FeatureMetricsStats } from 'component/feature/FeatureView/FeatureMetrics/FeatureMetricsStats/FeatureMetricsStats';
+import { SectionSeparator } from '../SectionSeparator/SectionSeparator';
-interface IFeatureOverviewEnvironmentFooterProps {
+interface IEnvironmentFooterProps {
environmentMetric?: IFeatureEnvironmentMetrics;
}
-const FeatureOverviewEnvironmentFooter = ({
+export const EnvironmentFooter = ({
environmentMetric,
-}: IFeatureOverviewEnvironmentFooterProps) => {
- const { classes: styles } = useStyles();
-
+}: IEnvironmentFooterProps) => {
if (!environmentMetric) {
return null;
}
return (
<>
-
+ Feature toggle exposure
+
);
};
-export default FeatureOverviewEnvironmentFooter;
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.styles.ts b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.styles.ts
index c5858bb049..3e82138c84 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.styles.ts
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.styles.ts
@@ -2,13 +2,13 @@ import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
featureOverviewEnvironment: {
- borderRadius: '12.5px',
- padding: '0.2rem',
- marginBottom: '1rem',
- backgroundColor: '#fff',
+ borderRadius: theme.shape.borderRadiusLarge,
+ marginBottom: theme.spacing(2),
+ background: theme.palette.background.default,
},
- accordionContainer: {
- width: '100%',
+ accordion: {
+ boxShadow: 'none',
+ background: 'none',
},
accordionHeader: {
boxShadow: 'none',
@@ -22,16 +22,26 @@ export const useStyles = makeStyles()(theme => ({
padding: '0.5rem',
},
},
+ accordionDetails: {
+ padding: theme.spacing(3),
+ background: theme.palette.secondaryContainer,
+ borderBottomLeftRadius: theme.shape.borderRadiusLarge,
+ borderBottomRightRadius: theme.shape.borderRadiusLarge,
+ borderBottom: `4px solid ${theme.palette.primary.light}`,
+ },
+ accordionDetailsDisabled: {
+ borderBottom: `4px solid ${theme.palette.dividerAlternative}`,
+ },
accordionBody: {
width: '100%',
position: 'relative',
- paddingBottom: '1rem',
+ paddingBottom: theme.spacing(2),
},
header: {
display: 'flex',
justifyContent: 'center',
flexDirection: 'column',
- paddingTop: '1.5rem',
+ // paddingTop: '1.5rem',
},
headerTitle: {
display: 'flex',
@@ -46,14 +56,6 @@ export const useStyles = makeStyles()(theme => ({
marginBottom: '0.5rem',
},
},
- disabledIndicatorPos: {
- position: 'absolute',
- top: '15px',
- left: '20px',
- [theme.breakpoints.down(560)]: {
- top: '13px',
- },
- },
iconContainer: {
backgroundColor: theme.palette.primary.light,
borderRadius: '50%',
@@ -69,32 +71,14 @@ export const useStyles = makeStyles()(theme => ({
width: '17px',
height: '17px',
},
- resultInfo: {
- display: 'flex',
- alignItems: 'center',
- margin: '1rem 0',
- },
- leftWing: {
- height: '2px',
- backgroundColor: theme.palette.grey[300],
- width: '90%',
- },
- separatorText: {
- fontSize: theme.fontSizes.smallBody,
- textAlign: 'center',
- padding: '0 1rem',
- },
- rightWing: {
- height: '2px',
- backgroundColor: theme.palette.grey[300],
- width: '90%',
- },
linkContainer: {
display: 'flex',
justifyContent: 'flex-end',
marginBottom: '1rem',
},
truncator: {
+ fontSize: theme.fontSizes.bodySize,
+ fontWeight: theme.typography.fontWeightMedium,
[theme.breakpoints.down(560)]: {
textAlign: 'center',
},
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.tsx
index 3a16ca9372..fee4f75c1c 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironment.tsx
@@ -1,4 +1,12 @@
-import { Accordion, AccordionDetails, AccordionSummary } from '@mui/material';
+import {
+ Accordion,
+ AccordionDetails,
+ AccordionSummary,
+ Box,
+ Chip,
+ useTheme,
+} from '@mui/material';
+import classNames from 'classnames';
import { ExpandMore } from '@mui/icons-material';
import { useFeature } from 'hooks/api/getters/useFeature/useFeature';
import useFeatureMetrics from 'hooks/api/getters/useFeatureMetrics/useFeatureMetrics';
@@ -8,14 +16,14 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
import EnvironmentIcon from 'component/common/EnvironmentIcon/EnvironmentIcon';
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
import { useStyles } from './FeatureOverviewEnvironment.styles';
-import FeatureOverviewEnvironmentBody from './FeatureOverviewEnvironmentBody/FeatureOverviewEnvironmentBody';
-import FeatureOverviewEnvironmentFooter from './FeatureOverviewEnvironmentFooter/FeatureOverviewEnvironmentFooter';
+import EnvironmentAccordionBody from './EnvironmentAccordionBody/EnvironmentAccordionBody';
+import { EnvironmentFooter } from './EnvironmentFooter/EnvironmentFooter';
import FeatureOverviewEnvironmentMetrics from './FeatureOverviewEnvironmentMetrics/FeatureOverviewEnvironmentMetrics';
import { FeatureStrategyMenu } from 'component/feature/FeatureStrategy/FeatureStrategyMenu/FeatureStrategyMenu';
import { FEATURE_ENVIRONMENT_ACCORDION } from 'utils/testIds';
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
import { FeatureStrategyIcons } from 'component/feature/FeatureStrategy/FeatureStrategyIcons/FeatureStrategyIcons';
-import { Badge } from 'component/common/Badge/Badge';
+// import { Badge } from 'component/common/Badge/Badge';
interface IFeatureOverviewEnvironmentProps {
env: IFeatureEnvironment;
@@ -25,6 +33,7 @@ const FeatureOverviewEnvironment = ({
env,
}: IFeatureOverviewEnvironmentProps) => {
const { classes: styles } = useStyles();
+ const theme = useTheme();
const projectId = useRequiredPathParam('projectId');
const featureId = useRequiredPathParam('featureId');
const { metrics } = useFeatureMetrics(projectId, featureId);
@@ -38,38 +47,57 @@ const FeatureOverviewEnvironment = ({
featureEnvironment => featureEnvironment.name === env.name
);
- const getOverviewText = () => {
- if (env.enabled) {
- return `${environmentMetric?.yes} received this feature because the following strategies are executing`;
- }
- return `This environment is disabled, which means that none of your strategies are executing`;
- };
-
return (
-
+
}
>
-
+
-
- Feature toggle execution for
+
-
+
+ }
+ />
+
-
- Disabled
-
- }
- />
-
-
-
- 0
- }
- show={
-
+
+ 0
+ }
+ show={
+ <>
+
+
+
+
- }
- />
-
+ >
+ }
+ />
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentBody/FeatureOverviewEnvironmentBody.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentBody/FeatureOverviewEnvironmentBody.tsx
deleted file mode 100644
index 33f8ebfc5a..0000000000
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentBody/FeatureOverviewEnvironmentBody.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
-import FeatureOverviewEnvironmentStrategies from '../FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategies';
-import { useStyles } from '../FeatureOverviewEnvironment.styles';
-import { IFeatureEnvironment } from 'interfaces/featureToggle';
-import { FeatureStrategyEmpty } from 'component/feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty';
-import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
-
-interface IFeatureOverviewEnvironmentBodyProps {
- getOverviewText: () => string;
- featureEnvironment?: IFeatureEnvironment;
-}
-
-const FeatureOverviewEnvironmentBody = ({
- featureEnvironment,
- getOverviewText,
-}: IFeatureOverviewEnvironmentBodyProps) => {
- const projectId = useRequiredPathParam('projectId');
- const featureId = useRequiredPathParam('featureId');
- const { classes: styles } = useStyles();
-
- if (!featureEnvironment) {
- return null;
- }
-
- return (
-
-
-
-
-
- {getOverviewText()}
-
-
-
-
0}
- show={
- <>
-
- >
- }
- elseShow={
-
- }
- />
-
-
- );
-};
-export default FeatureOverviewEnvironmentBody;
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentMetrics/FeatureOverviewEnvironmentMetrics.styles.ts b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentMetrics/FeatureOverviewEnvironmentMetrics.styles.ts
index 2d6af9211d..c44e909ede 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentMetrics/FeatureOverviewEnvironmentMetrics.styles.ts
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentMetrics/FeatureOverviewEnvironmentMetrics.styles.ts
@@ -20,7 +20,7 @@ export const useStyles = makeStyles()(theme => ({
},
},
infoParagraph: {
- maxWidth: '215px',
+ maxWidth: '270px',
marginTop: '0.25rem',
fontSize: theme.fontSizes.smallBody,
[theme.breakpoints.down(700)]: {
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategies.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategies.tsx
deleted file mode 100644
index 782e33fff1..0000000000
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/FeatureOverviewEnvironmentStrategies/FeatureOverviewEnvironmentStrategies.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { IFeatureStrategy } from 'interfaces/strategy';
-import FeatureOverviewEnvironmentStrategy from './FeatureOverviewEnvironmentStrategy/FeatureOverviewEnvironmentStrategy';
-
-interface IFeatureOverviewEnvironmentStrategiesProps {
- strategies: IFeatureStrategy[];
- environmentName: string;
-}
-
-const FeatureOverviewEnvironmentStrategies = ({
- strategies,
- environmentName,
-}: IFeatureOverviewEnvironmentStrategiesProps) => {
- return (
- <>
- {strategies.map(strategy => (
-
- ))}
- >
- );
-};
-
-export default FeatureOverviewEnvironmentStrategies;
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/SectionSeparator/SectionSeparator.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/SectionSeparator/SectionSeparator.tsx
new file mode 100644
index 0000000000..d3c39ae7a1
--- /dev/null
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/SectionSeparator/SectionSeparator.tsx
@@ -0,0 +1,35 @@
+import { FC } from 'react';
+import { styled } from '@mui/material';
+
+const SeparatorContainer = styled('div')(({ theme }) => ({
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ margin: '1rem 0',
+ position: 'relative',
+ '&:before': {
+ content: '""',
+ position: 'absolute',
+ top: '50%',
+ transform: 'translateY(-50%)',
+ height: 2,
+ width: '100%',
+ backgroundColor: theme.palette.divider,
+ },
+}));
+
+const SeparatorContent = styled('span')(({ theme }) => ({
+ fontSize: theme.fontSizes.subHeader,
+ textAlign: 'center',
+ padding: '0 1rem',
+ background: theme.palette.secondaryContainer,
+ position: 'relative',
+ maxWidth: '80%',
+ color: theme.palette.text.secondary,
+}));
+
+export const SectionSeparator: FC = ({ children }) => (
+
+ {children}
+
+);
diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironments.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironments.tsx
index 95e27e9b81..1742a78cbc 100644
--- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironments.tsx
+++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironments.tsx
@@ -11,13 +11,13 @@ const FeatureOverviewEnvironments = () => {
const { environments } = feature;
- const renderEnvironments = () => {
- return environments?.map(env => {
- return ;
- });
- };
-
- return <>{renderEnvironments()}>;
+ return (
+ <>
+ {environments?.map(env => (
+
+ ))}
+ >
+ );
};
export default FeatureOverviewEnvironments;
diff --git a/frontend/src/component/history/EventLog/EventLog.styles.ts b/frontend/src/component/history/EventLog/EventLog.styles.ts
index 29b650ec73..cff9878cf4 100644
--- a/frontend/src/component/history/EventLog/EventLog.styles.ts
+++ b/frontend/src/component/history/EventLog/EventLog.styles.ts
@@ -2,7 +2,7 @@ import { makeStyles } from 'tss-react/mui';
export const useStyles = makeStyles()(theme => ({
eventEntry: {
- border: `1px solid ${theme.palette.grey[100]}`,
+ border: `1px solid ${theme.palette.neutral.light}`,
padding: '1rem',
margin: '1rem 0',
borderRadius: theme.shape.borderRadius,
diff --git a/frontend/src/component/playground/Playground/PlaygroundForm/PlaygroundCodeFieldset/PlaygroundEditor/PlaygroundEditor.tsx b/frontend/src/component/playground/Playground/PlaygroundForm/PlaygroundCodeFieldset/PlaygroundEditor/PlaygroundEditor.tsx
index 9fae5b6bf7..d57b527dd1 100644
--- a/frontend/src/component/playground/Playground/PlaygroundForm/PlaygroundCodeFieldset/PlaygroundEditor/PlaygroundEditor.tsx
+++ b/frontend/src/component/playground/Playground/PlaygroundForm/PlaygroundCodeFieldset/PlaygroundEditor/PlaygroundEditor.tsx
@@ -14,7 +14,7 @@ interface IPlaygroundEditorProps {
const StyledEditorHeader = styled('aside')(({ theme }) => ({
height: '50px',
- backgroundColor: theme.palette.grey[100],
+ backgroundColor: theme.palette.neutral.light,
borderTopRightRadius: theme.shape.borderRadiusMedium,
borderTopLeftRadius: theme.shape.borderRadiusMedium,
padding: theme.spacing(1, 2),
diff --git a/frontend/src/component/project/ProjectCard/ProjectCard.styles.ts b/frontend/src/component/project/ProjectCard/ProjectCard.styles.ts
index 2fdecd2dda..1f20c987bf 100644
--- a/frontend/src/component/project/ProjectCard/ProjectCard.styles.ts
+++ b/frontend/src/component/project/ProjectCard/ProjectCard.styles.ts
@@ -16,7 +16,7 @@ export const useStyles = makeStyles()(theme => ({
},
'&:hover': {
transition: 'background-color 0.2s ease-in-out',
- backgroundColor: theme.palette.grey[100],
+ backgroundColor: theme.palette.neutral.light,
},
},
header: {
diff --git a/frontend/src/component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker.styles.ts b/frontend/src/component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker.styles.ts
index 4cfcabeb91..34a62cc5c3 100644
--- a/frontend/src/component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker.styles.ts
+++ b/frontend/src/component/user/common/ResetPasswordForm/PasswordChecker/PasswordChecker.styles.ts
@@ -10,7 +10,7 @@ export const useStyles = makeStyles()(theme => ({
},
headerContainer: { display: 'flex', padding: '0.5rem' },
divider: {
- backgroundColor: theme.palette.grey[100],
+ backgroundColor: theme.palette.neutral.light,
height: '1px',
width: '100%',
},
diff --git a/frontend/src/hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi.ts b/frontend/src/hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi.ts
index bf695c844a..f22a3a3220 100644
--- a/frontend/src/hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi.ts
+++ b/frontend/src/hooks/api/actions/useFeatureStrategyApi/useFeatureStrategyApi.ts
@@ -1,4 +1,8 @@
-import { IFeatureStrategyPayload, IFeatureStrategy } from 'interfaces/strategy';
+import {
+ IFeatureStrategyPayload,
+ IFeatureStrategy,
+ IFeatureStrategySortOrder,
+} from 'interfaces/strategy';
import useAPI from '../useApi/useApi';
const useFeatureStrategyApi = () => {
@@ -52,10 +56,26 @@ const useFeatureStrategyApi = () => {
await makeRequest(req.caller, req.id);
};
+ const setStrategiesSortOrder = async (
+ projectId: string,
+ featureId: string,
+ environmentId: string,
+ payload: IFeatureStrategySortOrder[]
+ ): Promise => {
+ const path = `api/admin/projects/${projectId}/features/${featureId}/environments/${environmentId}/strategies/set-sort-order`;
+ const req = createRequest(
+ path,
+ { method: 'POST', body: JSON.stringify(payload) },
+ 'setStrategiesSortOrderOnFeature'
+ );
+ await makeRequest(req.caller, req.id);
+ };
+
return {
addStrategyToFeature,
updateStrategyOnFeature,
deleteStrategyFromFeature,
+ setStrategiesSortOrder,
loading,
errors,
};
diff --git a/frontend/src/interfaces/strategy.ts b/frontend/src/interfaces/strategy.ts
index f81df26ad6..a9564f1aca 100644
--- a/frontend/src/interfaces/strategy.ts
+++ b/frontend/src/interfaces/strategy.ts
@@ -51,3 +51,8 @@ export interface IConstraint {
operator: Operator;
contextName: string;
}
+
+export interface IFeatureStrategySortOrder {
+ id: string;
+ sortOrder: number;
+}
diff --git a/frontend/src/themes/theme.ts b/frontend/src/themes/theme.ts
index 8f23875c5e..14558c5061 100644
--- a/frontend/src/themes/theme.ts
+++ b/frontend/src/themes/theme.ts
@@ -26,6 +26,9 @@ export default createTheme({
fontSize: '1.5rem',
lineHeight: 1.875,
},
+ caption: {
+ fontSize: `${12 / 16}rem`,
+ },
},
fontSizes: {
mainHeader: '1.25rem',
@@ -287,5 +290,28 @@ export default createTheme({
},
},
},
+ MuiChip: {
+ styleOverrides: {
+ root: ({ ownerState, theme }) => ({
+ ...(ownerState.variant === 'outlined' &&
+ ownerState.size === 'small' && {
+ borderRadius: theme.shape.borderRadius,
+ margin: 0,
+ borderWidth: 1,
+ borderStyle: 'solid',
+ fontWeight: theme.typography.fontWeightBold,
+ fontSize: theme.typography.caption.fontSize,
+ ...(ownerState.color === 'success' && {
+ backgroundColor: colors.green[50],
+ borderColor: theme.palette.success.border,
+ color: theme.palette.success.dark,
+ }),
+ ...(ownerState.color === 'default' && {
+ color: theme.palette.text.secondary,
+ }),
+ }),
+ }),
+ },
+ },
},
});
diff --git a/frontend/src/utils/strategyNames.ts b/frontend/src/utils/strategyNames.ts
index dfc6b163bb..da608aa082 100644
--- a/frontend/src/utils/strategyNames.ts
+++ b/frontend/src/utils/strategyNames.ts
@@ -2,6 +2,7 @@ import LocationOnIcon from '@mui/icons-material/LocationOn';
import PeopleIcon from '@mui/icons-material/People';
import LanguageIcon from '@mui/icons-material/Language';
import PowerSettingsNewIcon from '@mui/icons-material/PowerSettingsNew';
+import CodeIcon from '@mui/icons-material/Code';
import { ReactComponent as RolloutIcon } from 'assets/icons/rollout.svg';
import { ElementType } from 'react';
@@ -11,6 +12,8 @@ export const formatStrategyName = (strategyName: string): string => {
export const getFeatureStrategyIcon = (strategyName: string): ElementType => {
switch (strategyName) {
+ case 'default':
+ return PowerSettingsNewIcon;
case 'remoteAddress':
return LanguageIcon;
case 'flexibleRollout':
@@ -20,7 +23,7 @@ export const getFeatureStrategyIcon = (strategyName: string): ElementType => {
case 'applicationHostname':
return LocationOnIcon;
default:
- return PowerSettingsNewIcon;
+ return CodeIcon;
}
};