mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-26 01:17:00 +02:00
Refine Playground UI (#1217)
* fix playground border radius consistency * improve playground alerts * fix: playground segments constraint type logic * fix: refactor segment execution * fix: comments * fix: add summary width * align playground spacing and borders * fix build - ts segment type in playground * fix status cell logic * update playground disabled env info * fix playground filter by status and sort Co-authored-by: Nuno Góis <github@nunogois.com> Co-authored-by: Fredrik Oseberg <fredrik.no@gmail.com> Co-authored-by: andreas-unleash <104830839+andreas-unleash@users.noreply.github.com> Co-authored-by: Nuno Góis <github@nunogois.com>
This commit is contained in:
parent
5ffb63e342
commit
859aa435e0
@ -1,20 +1,6 @@
|
|||||||
import { makeStyles } from 'tss-react/mui';
|
import { makeStyles } from 'tss-react/mui';
|
||||||
|
|
||||||
export const useStyles = makeStyles()(theme => ({
|
export const useStyles = makeStyles()(theme => ({
|
||||||
constraintIconContainer: {
|
|
||||||
backgroundColor: theme.palette.primary.main,
|
|
||||||
borderRadius: '50%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
marginRight: theme.spacing(1),
|
|
||||||
[theme.breakpoints.down(710)]: {
|
|
||||||
marginRight: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constraintIcon: {
|
|
||||||
fill: '#fff',
|
|
||||||
},
|
|
||||||
accordion: {
|
accordion: {
|
||||||
border: `1px solid ${theme.palette.dividerAlternative}`,
|
border: `1px solid ${theme.palette.dividerAlternative}`,
|
||||||
borderRadius: theme.shape.borderRadiusMedium,
|
borderRadius: theme.shape.borderRadiusMedium,
|
||||||
@ -33,9 +19,12 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
headerMetaInfo: {
|
headerMetaInfo: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'stretch',
|
alignItems: 'stretch',
|
||||||
|
marginLeft: theme.spacing(1),
|
||||||
[theme.breakpoints.down(710)]: {
|
[theme.breakpoints.down(710)]: {
|
||||||
|
marginLeft: 0,
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
headerContainer: {
|
headerContainer: {
|
||||||
@ -58,6 +47,10 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
justifyContent: 'stretch',
|
justifyContent: 'stretch',
|
||||||
margin: 'auto 0',
|
margin: 'auto 0',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
marginLeft: theme.spacing(1),
|
||||||
|
[theme.breakpoints.down(710)]: {
|
||||||
|
marginLeft: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
headerValues: {
|
headerValues: {
|
||||||
fontSize: theme.fontSizes.smallBody,
|
fontSize: theme.fontSizes.smallBody,
|
||||||
@ -71,13 +64,8 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
headerConstraintContainer: {
|
headerConstraintContainer: {
|
||||||
minWidth: '220px',
|
minWidth: '152px',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
paddingRight: '1rem',
|
|
||||||
[theme.breakpoints.between(1101, 1365)]: {
|
|
||||||
minWidth: '152px',
|
|
||||||
paddingRight: '0.5rem',
|
|
||||||
},
|
|
||||||
[theme.breakpoints.down(710)]: {
|
[theme.breakpoints.down(710)]: {
|
||||||
paddingRight: 0,
|
paddingRight: 0,
|
||||||
},
|
},
|
||||||
|
@ -22,6 +22,8 @@ interface IConstraintAccordionViewProps {
|
|||||||
onDelete?: () => void;
|
onDelete?: () => void;
|
||||||
onEdit?: () => void;
|
onEdit?: () => void;
|
||||||
sx?: SxProps<Theme>;
|
sx?: SxProps<Theme>;
|
||||||
|
compact?: boolean;
|
||||||
|
renderAfter?: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ConstraintAccordionView = ({
|
export const ConstraintAccordionView = ({
|
||||||
@ -29,6 +31,8 @@ export const ConstraintAccordionView = ({
|
|||||||
onEdit,
|
onEdit,
|
||||||
onDelete,
|
onDelete,
|
||||||
sx = undefined,
|
sx = undefined,
|
||||||
|
compact = false,
|
||||||
|
renderAfter,
|
||||||
}: IConstraintAccordionViewProps) => {
|
}: IConstraintAccordionViewProps) => {
|
||||||
const { classes: styles } = useStyles();
|
const { classes: styles } = useStyles();
|
||||||
const [expandable, setExpandable] = useState(true);
|
const [expandable, setExpandable] = useState(true);
|
||||||
@ -62,14 +66,24 @@ export const ConstraintAccordionView = ({
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ConstraintAccordionViewHeader
|
<div
|
||||||
constraint={constraint}
|
style={{
|
||||||
onEdit={onEdit}
|
display: 'flex',
|
||||||
onDelete={onDelete}
|
flexDirection: 'column',
|
||||||
singleValue={singleValue}
|
width: '100%',
|
||||||
allowExpand={setExpandable}
|
}}
|
||||||
expanded={expanded}
|
>
|
||||||
/>
|
<ConstraintAccordionViewHeader
|
||||||
|
constraint={constraint}
|
||||||
|
onEdit={onEdit}
|
||||||
|
onDelete={onDelete}
|
||||||
|
singleValue={singleValue}
|
||||||
|
allowExpand={setExpandable}
|
||||||
|
expanded={expanded}
|
||||||
|
compact={compact}
|
||||||
|
/>
|
||||||
|
{renderAfter}
|
||||||
|
</div>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
|
|
||||||
<AccordionDetails className={styles.accordionDetails}>
|
<AccordionDetails className={styles.accordionDetails}>
|
||||||
|
@ -11,6 +11,7 @@ interface IConstraintAccordionViewHeaderProps {
|
|||||||
singleValue: boolean;
|
singleValue: boolean;
|
||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
allowExpand: (shouldExpand: boolean) => void;
|
allowExpand: (shouldExpand: boolean) => void;
|
||||||
|
compact?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ConstraintAccordionViewHeader = ({
|
export const ConstraintAccordionViewHeader = ({
|
||||||
@ -20,12 +21,13 @@ export const ConstraintAccordionViewHeader = ({
|
|||||||
singleValue,
|
singleValue,
|
||||||
allowExpand,
|
allowExpand,
|
||||||
expanded,
|
expanded,
|
||||||
|
compact,
|
||||||
}: IConstraintAccordionViewHeaderProps) => {
|
}: IConstraintAccordionViewHeaderProps) => {
|
||||||
const { classes: styles } = useStyles();
|
const { classes: styles } = useStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.headerContainer}>
|
<div className={styles.headerContainer}>
|
||||||
<ConstraintIcon />
|
<ConstraintIcon compact={compact} />
|
||||||
<ConstraintAccordionViewHeaderInfo
|
<ConstraintAccordionViewHeaderInfo
|
||||||
constraint={constraint}
|
constraint={constraint}
|
||||||
singleValue={singleValue}
|
singleValue={singleValue}
|
||||||
|
@ -4,7 +4,7 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
|||||||
import { ConstraintAccordionViewHeaderSingleValue } from '../ContraintAccordionViewHeaderSingleValue/ConstraintAccordionViewHeaderSingleValue';
|
import { ConstraintAccordionViewHeaderSingleValue } from '../ContraintAccordionViewHeaderSingleValue/ConstraintAccordionViewHeaderSingleValue';
|
||||||
import { ConstraintAccordionViewHeaderMultipleValues } from '../ContraintAccordionViewHeaderMultipleValues/ConstraintAccordionViewHeaderMultipleValues';
|
import { ConstraintAccordionViewHeaderMultipleValues } from '../ContraintAccordionViewHeaderMultipleValues/ConstraintAccordionViewHeaderMultipleValues';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IConstraint } from '../../../../../../interfaces/strategy';
|
import { IConstraint } from 'interfaces/strategy';
|
||||||
import { useStyles } from '../../../ConstraintAccordion.styles';
|
import { useStyles } from '../../../ConstraintAccordion.styles';
|
||||||
|
|
||||||
const StyledHeaderText = styled('span')(({ theme }) => ({
|
const StyledHeaderText = styled('span')(({ theme }) => ({
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { ConditionallyRender } from '../../../../ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { IConstraint } from '../../../../../../interfaces/strategy';
|
import { IConstraint } from 'interfaces/strategy';
|
||||||
import { useStyles } from '../../../ConstraintAccordion.styles';
|
import { useStyles } from '../../../ConstraintAccordion.styles';
|
||||||
|
|
||||||
const StyledValuesSpan = styled('span')(({ theme }) => ({
|
const StyledValuesSpan = styled('span')(({ theme }) => ({
|
||||||
|
@ -2,11 +2,12 @@ import React, { useEffect } from 'react';
|
|||||||
import { Chip, styled } from '@mui/material';
|
import { Chip, styled } from '@mui/material';
|
||||||
import { formatConstraintValue } from 'utils/formatConstraintValue';
|
import { formatConstraintValue } from 'utils/formatConstraintValue';
|
||||||
import { useStyles } from '../../../ConstraintAccordion.styles';
|
import { useStyles } from '../../../ConstraintAccordion.styles';
|
||||||
import { IConstraint } from '../../../../../../interfaces/strategy';
|
import { IConstraint } from 'interfaces/strategy';
|
||||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
import { useLocationSettings } from 'hooks/useLocationSettings';
|
||||||
|
|
||||||
const StyledSingleValueChip = styled(Chip)(({ theme }) => ({
|
const StyledSingleValueChip = styled(Chip)(({ theme }) => ({
|
||||||
margin: 'auto 0',
|
margin: 'auto 0',
|
||||||
|
marginLeft: theme.spacing(1),
|
||||||
[theme.breakpoints.down(710)]: {
|
[theme.breakpoints.down(710)]: {
|
||||||
margin: theme.spacing(1, 0),
|
margin: theme.spacing(1, 0),
|
||||||
},
|
},
|
||||||
|
@ -12,12 +12,12 @@ export const StyledIconWrapperBase = styled('div')<{
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignSelf: 'stretch',
|
alignSelf: 'stretch',
|
||||||
color: theme.palette.primary.main,
|
color: theme.palette.primary.main,
|
||||||
marginRight: theme.spacing(1),
|
marginLeft: theme.spacing(1),
|
||||||
borderRadius: theme.shape.borderRadius,
|
borderRadius: theme.shape.borderRadius,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledPrefixIconWrapper = styled(StyledIconWrapperBase)(() => ({
|
const StyledPrefixIconWrapper = styled(StyledIconWrapperBase)(() => ({
|
||||||
marginRight: 0,
|
marginLeft: 0,
|
||||||
borderTopRightRadius: 0,
|
borderTopRightRadius: 0,
|
||||||
borderBottomRightRadius: 0,
|
borderBottomRightRadius: 0,
|
||||||
}));
|
}));
|
||||||
|
@ -1,12 +1,28 @@
|
|||||||
import { useStyles } from './ConstraintAccordion.styles';
|
import { VFC } from 'react';
|
||||||
|
import { Box } from '@mui/material';
|
||||||
import { TrackChanges } from '@mui/icons-material';
|
import { TrackChanges } from '@mui/icons-material';
|
||||||
|
|
||||||
export const ConstraintIcon = () => {
|
interface IConstraintIconProps {
|
||||||
const { classes: styles } = useStyles();
|
compact?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
export const ConstraintIcon: VFC<IConstraintIconProps> = ({ compact }) => (
|
||||||
<div className={styles.constraintIconContainer}>
|
<Box
|
||||||
<TrackChanges className={styles.constraintIcon} />
|
sx={{
|
||||||
</div>
|
backgroundColor: 'primary.light',
|
||||||
);
|
p: compact ? '1px' : '2px',
|
||||||
};
|
borderRadius: '50%',
|
||||||
|
width: compact ? '18px' : '24px',
|
||||||
|
height: compact ? '18px' : '24px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TrackChanges
|
||||||
|
sx={{
|
||||||
|
fill: 'white',
|
||||||
|
display: 'block',
|
||||||
|
width: compact ? '16px' : '20px',
|
||||||
|
height: compact ? '16px' : '20px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
@ -14,13 +14,17 @@ import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender'
|
|||||||
import { useStyles } from './SegmentItem.styles';
|
import { useStyles } from './SegmentItem.styles';
|
||||||
|
|
||||||
interface ISegmentItemProps {
|
interface ISegmentItemProps {
|
||||||
segment: ISegment;
|
segment: Partial<ISegment>;
|
||||||
isExpanded?: boolean;
|
isExpanded?: boolean;
|
||||||
|
constraintList?: JSX.Element;
|
||||||
|
headerContent?: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SegmentItem: VFC<ISegmentItemProps> = ({
|
export const SegmentItem: VFC<ISegmentItemProps> = ({
|
||||||
segment,
|
segment,
|
||||||
isExpanded,
|
isExpanded,
|
||||||
|
headerContent,
|
||||||
|
constraintList,
|
||||||
}) => {
|
}) => {
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
const [isOpen, setIsOpen] = useState(isExpanded || false);
|
const [isOpen, setIsOpen] = useState(isExpanded || false);
|
||||||
@ -46,6 +50,10 @@ export const SegmentItem: VFC<ISegmentItemProps> = ({
|
|||||||
>
|
>
|
||||||
{segment.name}
|
{segment.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={Boolean(headerContent)}
|
||||||
|
show={headerContent}
|
||||||
|
/>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={!isExpanded}
|
condition={!isExpanded}
|
||||||
show={
|
show={
|
||||||
@ -62,17 +70,23 @@ export const SegmentItem: VFC<ISegmentItemProps> = ({
|
|||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails sx={{ pt: 0 }}>
|
<AccordionDetails sx={{ pt: 0 }}>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={segment!.constraints?.length > 0}
|
condition={Boolean(constraintList)}
|
||||||
show={
|
show={constraintList}
|
||||||
<ConstraintAccordionList
|
|
||||||
constraints={segment.constraints}
|
|
||||||
showLabel={false}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
elseShow={
|
elseShow={
|
||||||
<Typography>
|
<ConditionallyRender
|
||||||
This segment has no constraints.
|
condition={(segment?.constraints?.length || 0) > 0}
|
||||||
</Typography>
|
show={
|
||||||
|
<ConstraintAccordionList
|
||||||
|
constraints={segment!.constraints!}
|
||||||
|
showLabel={false}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
elseShow={
|
||||||
|
<Typography>
|
||||||
|
This segment has no constraints.
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</AccordionDetails>
|
</AccordionDetails>
|
||||||
|
@ -17,11 +17,11 @@ const StyledSlider = withStyles(Slider, theme => ({
|
|||||||
valueLabel: {},
|
valueLabel: {},
|
||||||
track: {
|
track: {
|
||||||
height: 8,
|
height: 8,
|
||||||
borderRadius: 4,
|
borderRadius: theme.shape.borderRadius,
|
||||||
},
|
},
|
||||||
rail: {
|
rail: {
|
||||||
height: 8,
|
height: 8,
|
||||||
borderRadius: 4,
|
borderRadius: theme.shape.borderRadius,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ export const PlaygroundGuidancePopper = () => {
|
|||||||
sx={theme => ({
|
sx={theme => ({
|
||||||
padding: theme.spacing(8, 4),
|
padding: theme.spacing(8, 4),
|
||||||
maxWidth: '500px',
|
maxWidth: '500px',
|
||||||
borderRadius: theme.shape.borderRadiusExtraLarge,
|
borderRadius: `${theme.shape.borderRadiusExtraLarge}px`,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
|
@ -52,7 +52,7 @@ export const FeatureDetails = ({
|
|||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const customStrategiesTxt = hasCustomStrategies(feature)
|
const customStrategiesTxt = hasCustomStrategies(feature)
|
||||||
? `This feature uses custom strategies. Custom strategies can't be evaluated, so they will be marked as Unevaluated`
|
? `This feature uses custom strategies. Custom strategies can't be evaluated, so they will be marked accordingly.`
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const onCloseClick =
|
const onCloseClick =
|
||||||
@ -97,6 +97,9 @@ export const FeatureDetails = ({
|
|||||||
<Typography variant="subtitle1" color={color} component="span">
|
<Typography variant="subtitle1" color={color} component="span">
|
||||||
{reason}
|
{reason}
|
||||||
</Typography>
|
</Typography>
|
||||||
|
<Typography variant="body1" component="span">
|
||||||
|
.
|
||||||
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={Boolean(noValueTxt)}
|
condition={Boolean(noValueTxt)}
|
||||||
@ -110,7 +113,9 @@ export const FeatureDetails = ({
|
|||||||
condition={Boolean(customStrategiesTxt)}
|
condition={Boolean(customStrategiesTxt)}
|
||||||
show={
|
show={
|
||||||
<div className={styles.alertRow}>
|
<div className={styles.alertRow}>
|
||||||
<Alert color={'info'}>{customStrategiesTxt}</Alert>
|
<Alert severity="warning" color="info">
|
||||||
|
{customStrategiesTxt}
|
||||||
|
</Alert>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -10,5 +10,6 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
height: 'auto',
|
height: 'auto',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
backgroundColor: theme.palette.tertiary.light,
|
backgroundColor: theme.palette.tertiary.light,
|
||||||
|
borderRadius: theme.shape.borderRadiusLarge,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -17,16 +17,25 @@ export const PlaygroundResultFeatureStrategyList = ({
|
|||||||
}: PlaygroundResultFeatureStrategyListProps) => {
|
}: PlaygroundResultFeatureStrategyListProps) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={feature?.strategies?.data?.length === 0}
|
||||||
|
show={
|
||||||
|
<Alert severity="warning" sx={{ mt: 2 }}>
|
||||||
|
There are no strategies added to this feature toggle in
|
||||||
|
selected environment.
|
||||||
|
</Alert>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={
|
condition={
|
||||||
!feature.isEnabledInCurrentEnvironment &&
|
!feature.isEnabledInCurrentEnvironment &&
|
||||||
Boolean(feature?.strategies?.data)
|
(feature?.strategies?.data?.length || 0) > 0
|
||||||
}
|
}
|
||||||
show={
|
show={
|
||||||
<Alert severity={'info'} color={'info'}>
|
<Alert severity="info" color="warning">
|
||||||
If environment would be enabled then this feature would
|
If environment was enabled, then this feature toggle
|
||||||
be {feature.strategies?.result ? 'TRUE' : 'FALSE'} and
|
would be {feature.strategies?.result ? 'TRUE' : 'FALSE'}{' '}
|
||||||
the strategies would evaluate like this:{' '}
|
with strategies evaluated like so:{' '}
|
||||||
</Alert>
|
</Alert>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -35,6 +35,6 @@ export const useStyles = makeStyles()(theme => ({
|
|||||||
background: theme.palette.background.default,
|
background: theme.palette.background.default,
|
||||||
},
|
},
|
||||||
successBorder: {
|
successBorder: {
|
||||||
border: `1px solid ${theme.palette.success.main}`,
|
borderColor: theme.palette.success.main,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
import { makeStyles } from 'tss-react/mui';
|
|
||||||
|
|
||||||
export const useStyles = makeStyles()(theme => ({
|
|
||||||
constraintIconContainer: {
|
|
||||||
backgroundColor: theme.palette.primary.main,
|
|
||||||
borderRadius: '50%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
marginRight: theme.spacing(1),
|
|
||||||
[theme.breakpoints.down(650)]: {
|
|
||||||
marginBottom: '1rem',
|
|
||||||
marginRight: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
constraintIcon: {
|
|
||||||
fill: '#fff',
|
|
||||||
},
|
|
||||||
accordion: {
|
|
||||||
border: `1px solid ${theme.palette.dividerAlternative}`,
|
|
||||||
borderRadius: theme.spacing(1),
|
|
||||||
backgroundColor: '#fff',
|
|
||||||
boxShadow: 'none',
|
|
||||||
margin: 0,
|
|
||||||
},
|
|
||||||
accordionRoot: {
|
|
||||||
'&:before': {
|
|
||||||
opacity: '0 !important',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
headerMetaInfo: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'stretch',
|
|
||||||
[theme.breakpoints.down(710)]: { flexDirection: 'column' },
|
|
||||||
},
|
|
||||||
headerContainer: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
[theme.breakpoints.down(710)]: {
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
position: 'relative',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
headerValuesContainerWrapper: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'stretch',
|
|
||||||
margin: 'auto 0',
|
|
||||||
},
|
|
||||||
headerValuesContainer: {
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'stretch',
|
|
||||||
margin: 'auto 0',
|
|
||||||
flexDirection: 'column',
|
|
||||||
},
|
|
||||||
headerValues: {
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
},
|
|
||||||
headerValuesExpand: {
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
marginTop: '4px',
|
|
||||||
color: theme.palette.primary.dark,
|
|
||||||
[theme.breakpoints.down(710)]: {
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
headerConstraintContainer: {
|
|
||||||
minWidth: '220px',
|
|
||||||
position: 'relative',
|
|
||||||
paddingRight: '1rem',
|
|
||||||
[theme.breakpoints.between(1101, 1365)]: {
|
|
||||||
minWidth: '152px',
|
|
||||||
paddingRight: '0.5rem',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
headerText: {
|
|
||||||
maxWidth: '400px',
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
[theme.breakpoints.down('xl')]: {
|
|
||||||
display: 'none',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
chip: {
|
|
||||||
margin: '0 0.5rem 0.5rem 0',
|
|
||||||
},
|
|
||||||
chipValue: {
|
|
||||||
whiteSpace: 'pre',
|
|
||||||
},
|
|
||||||
accordionDetails: {
|
|
||||||
borderTop: `1px dashed ${theme.palette.grey[300]}`,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
},
|
|
||||||
valuesContainer: {
|
|
||||||
padding: '1rem 0rem',
|
|
||||||
maxHeight: '400px',
|
|
||||||
overflowY: 'auto',
|
|
||||||
},
|
|
||||||
summary: {
|
|
||||||
border: 'none',
|
|
||||||
padding: theme.spacing(0.5, 3),
|
|
||||||
'&:hover .valuesExpandLabel': {
|
|
||||||
textDecoration: 'underline',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
@ -1,91 +0,0 @@
|
|||||||
import { useState, VFC } from 'react';
|
|
||||||
import {
|
|
||||||
Accordion,
|
|
||||||
AccordionSummary,
|
|
||||||
AccordionDetails,
|
|
||||||
SxProps,
|
|
||||||
Theme,
|
|
||||||
useTheme,
|
|
||||||
} from '@mui/material';
|
|
||||||
import { ConstraintAccordionViewHeader } from './ConstraintAccordionViewHeader/ConstraintAccordionViewHeader';
|
|
||||||
import { oneOf } from 'utils/oneOf';
|
|
||||||
import {
|
|
||||||
dateOperators,
|
|
||||||
numOperators,
|
|
||||||
semVerOperators,
|
|
||||||
} from 'constants/operators';
|
|
||||||
import { useStyles } from './ConstraintAccordion.styles';
|
|
||||||
import {
|
|
||||||
PlaygroundConstraintSchema,
|
|
||||||
PlaygroundRequestSchema,
|
|
||||||
} from 'component/playground/Playground/interfaces/playground.model';
|
|
||||||
import { ConstraintAccordionViewBody } from 'component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewBody/ConstraintAccordionViewBody';
|
|
||||||
|
|
||||||
interface IConstraintAccordionViewProps {
|
|
||||||
constraint: PlaygroundConstraintSchema;
|
|
||||||
playgroundInput?: PlaygroundRequestSchema;
|
|
||||||
maxLength?: number;
|
|
||||||
sx?: SxProps<Theme>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ConstraintAccordionView: VFC<IConstraintAccordionViewProps> = ({
|
|
||||||
constraint,
|
|
||||||
sx = undefined,
|
|
||||||
maxLength,
|
|
||||||
playgroundInput,
|
|
||||||
}) => {
|
|
||||||
const { classes: styles } = useStyles();
|
|
||||||
const [expandable, setExpandable] = useState(true);
|
|
||||||
const [expanded, setExpanded] = useState(false);
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const singleValue = oneOf(
|
|
||||||
[...semVerOperators, ...numOperators, ...dateOperators],
|
|
||||||
constraint.operator
|
|
||||||
);
|
|
||||||
const handleClick = () => {
|
|
||||||
if (expandable) {
|
|
||||||
setExpanded(!expanded);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const backgroundColor = Boolean(playgroundInput)
|
|
||||||
? !Boolean((constraint as PlaygroundConstraintSchema).result)
|
|
||||||
? theme.palette.neutral.light
|
|
||||||
: 'inherit'
|
|
||||||
: 'inherit';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Accordion
|
|
||||||
className={styles.accordion}
|
|
||||||
classes={{ root: styles.accordionRoot }}
|
|
||||||
expanded={expanded}
|
|
||||||
sx={sx}
|
|
||||||
>
|
|
||||||
<AccordionSummary
|
|
||||||
classes={{ root: styles.summary }}
|
|
||||||
expandIcon={null}
|
|
||||||
onClick={handleClick}
|
|
||||||
sx={{
|
|
||||||
cursor: expandable ? 'pointer' : 'default!important',
|
|
||||||
'&:hover': {
|
|
||||||
cursor: expandable ? 'pointer' : 'default!important',
|
|
||||||
},
|
|
||||||
backgroundColor: backgroundColor,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ConstraintAccordionViewHeader
|
|
||||||
constraint={constraint}
|
|
||||||
singleValue={singleValue}
|
|
||||||
allowExpand={setExpandable}
|
|
||||||
expanded={expanded}
|
|
||||||
maxLength={maxLength ?? 112}
|
|
||||||
playgroundInput={playgroundInput}
|
|
||||||
/>
|
|
||||||
</AccordionSummary>
|
|
||||||
|
|
||||||
<AccordionDetails className={styles.accordionDetails}>
|
|
||||||
<ConstraintAccordionViewBody constraint={constraint} />
|
|
||||||
</AccordionDetails>
|
|
||||||
</Accordion>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,42 +0,0 @@
|
|||||||
import { ConstraintIcon } from 'component/common/ConstraintAccordion/ConstraintIcon';
|
|
||||||
import { ConstraintAccordionViewHeaderInfo } from './ConstraintAccordionViewHeaderInfo/ConstraintAccordionViewHeaderInfo';
|
|
||||||
import { useStyles } from 'component/common/ConstraintAccordion/ConstraintAccordion.styles';
|
|
||||||
import {
|
|
||||||
PlaygroundConstraintSchema,
|
|
||||||
PlaygroundRequestSchema,
|
|
||||||
} from 'component/playground/Playground/interfaces/playground.model';
|
|
||||||
|
|
||||||
interface PlaygroundConstraintAccordionViewHeaderProps {
|
|
||||||
constraint: PlaygroundConstraintSchema;
|
|
||||||
singleValue: boolean;
|
|
||||||
expanded: boolean;
|
|
||||||
allowExpand: (shouldExpand: boolean) => void;
|
|
||||||
playgroundInput?: PlaygroundRequestSchema;
|
|
||||||
maxLength?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ConstraintAccordionViewHeader = ({
|
|
||||||
constraint,
|
|
||||||
singleValue,
|
|
||||||
allowExpand,
|
|
||||||
expanded,
|
|
||||||
maxLength,
|
|
||||||
playgroundInput,
|
|
||||||
}: PlaygroundConstraintAccordionViewHeaderProps) => {
|
|
||||||
const { classes: styles } = useStyles();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.headerContainer}>
|
|
||||||
<ConstraintIcon />
|
|
||||||
<ConstraintAccordionViewHeaderInfo
|
|
||||||
constraint={constraint}
|
|
||||||
singleValue={singleValue}
|
|
||||||
allowExpand={allowExpand}
|
|
||||||
expanded={expanded}
|
|
||||||
result={constraint.result}
|
|
||||||
maxLength={maxLength}
|
|
||||||
playgroundInput={playgroundInput}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,111 +0,0 @@
|
|||||||
import { styled, Tooltip, Typography, useTheme } from '@mui/material';
|
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
||||||
import { PlaygroundSingleValue } from './PlaygroundSingleValue/PlaygroundSingleValue';
|
|
||||||
import { PLaygroundMultipleValues } from './PlaygroundMultipleValues/PlaygroundMultipleValues';
|
|
||||||
import React from 'react';
|
|
||||||
import { useStyles } from '../../ConstraintAccordion.styles';
|
|
||||||
import { CancelOutlined } from '@mui/icons-material';
|
|
||||||
import {
|
|
||||||
PlaygroundConstraintSchema,
|
|
||||||
PlaygroundRequestSchema,
|
|
||||||
} from 'component/playground/Playground/interfaces/playground.model';
|
|
||||||
import { ConstraintViewHeaderOperator } from 'component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionViewHeader/ConstraintViewHeaderOperator/ConstraintViewHeaderOperator';
|
|
||||||
|
|
||||||
const StyledHeaderText = styled('span')(({ theme }) => ({
|
|
||||||
display: '-webkit-box',
|
|
||||||
WebkitLineClamp: 3,
|
|
||||||
WebkitBoxOrient: 'vertical',
|
|
||||||
overflow: 'hidden',
|
|
||||||
maxWidth: '100px',
|
|
||||||
minWidth: '100px',
|
|
||||||
marginRight: '10px',
|
|
||||||
marginTop: 'auto',
|
|
||||||
marginBottom: 'auto',
|
|
||||||
wordBreak: 'break-word',
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
[theme.breakpoints.down(710)]: {
|
|
||||||
textAlign: 'center',
|
|
||||||
padding: theme.spacing(1, 0),
|
|
||||||
marginRight: 'inherit',
|
|
||||||
maxWidth: 'inherit',
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const StyledHeaderWrapper = styled('div')(({ theme }) => ({
|
|
||||||
display: 'flex',
|
|
||||||
width: '100%',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
borderRadius: theme.spacing(1),
|
|
||||||
}));
|
|
||||||
|
|
||||||
interface PlaygroundConstraintAccordionViewHeaderInfoProps {
|
|
||||||
constraint: PlaygroundConstraintSchema;
|
|
||||||
singleValue: boolean;
|
|
||||||
expanded: boolean;
|
|
||||||
allowExpand: (shouldExpand: boolean) => void;
|
|
||||||
result?: boolean;
|
|
||||||
maxLength?: number;
|
|
||||||
playgroundInput?: PlaygroundRequestSchema;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ConstraintAccordionViewHeaderInfo = ({
|
|
||||||
constraint,
|
|
||||||
singleValue,
|
|
||||||
allowExpand,
|
|
||||||
expanded,
|
|
||||||
result,
|
|
||||||
playgroundInput,
|
|
||||||
maxLength = 112,
|
|
||||||
}: PlaygroundConstraintAccordionViewHeaderInfoProps) => {
|
|
||||||
const { classes: styles } = useStyles();
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const constraintExistsInContext = Boolean(
|
|
||||||
playgroundInput?.context[constraint.contextName]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledHeaderWrapper>
|
|
||||||
<div className={styles.headerMetaInfo}>
|
|
||||||
<Tooltip title={constraint.contextName} arrow>
|
|
||||||
<StyledHeaderText>
|
|
||||||
{constraint.contextName}
|
|
||||||
<Typography
|
|
||||||
variant={'body1'}
|
|
||||||
color={
|
|
||||||
constraintExistsInContext
|
|
||||||
? theme.palette.neutral.dark
|
|
||||||
: theme.palette.error.main
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{playgroundInput?.context[constraint.contextName] ||
|
|
||||||
'no value'}
|
|
||||||
</Typography>
|
|
||||||
</StyledHeaderText>
|
|
||||||
</Tooltip>
|
|
||||||
<ConstraintViewHeaderOperator constraint={constraint} />
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={singleValue}
|
|
||||||
show={
|
|
||||||
<PlaygroundSingleValue
|
|
||||||
constraint={constraint}
|
|
||||||
allowExpand={allowExpand}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
elseShow={
|
|
||||||
<PLaygroundMultipleValues
|
|
||||||
constraint={constraint}
|
|
||||||
expanded={expanded}
|
|
||||||
allowExpand={allowExpand}
|
|
||||||
maxLength={maxLength}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={result !== undefined && !Boolean(result)}
|
|
||||||
show={<CancelOutlined color="error" sx={{ mt: 1 }} />}
|
|
||||||
/>
|
|
||||||
</StyledHeaderWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,85 +0,0 @@
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
||||||
import { styled, Typography } from '@mui/material';
|
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import classnames from 'classnames';
|
|
||||||
import { useStyles } from '../../../ConstraintAccordion.styles';
|
|
||||||
import { PlaygroundConstraintSchema } from 'component/playground/Playground/interfaces/playground.model';
|
|
||||||
|
|
||||||
const StyledValuesSpan = styled('span')(({ theme }) => ({
|
|
||||||
display: '-webkit-box',
|
|
||||||
WebkitLineClamp: 2,
|
|
||||||
WebkitBoxOrient: 'vertical',
|
|
||||||
overflow: 'hidden',
|
|
||||||
wordBreak: 'break-word',
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
margin: 'auto 0',
|
|
||||||
[theme.breakpoints.down(710)]: {
|
|
||||||
margin: theme.spacing(1, 0),
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
interface PLaygroundConstraintAccordionViewHeaderMultipleValueProps {
|
|
||||||
constraint: PlaygroundConstraintSchema;
|
|
||||||
expanded: boolean;
|
|
||||||
maxLength: number;
|
|
||||||
allowExpand: (shouldExpand: boolean) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PLaygroundMultipleValues = ({
|
|
||||||
constraint,
|
|
||||||
expanded,
|
|
||||||
allowExpand,
|
|
||||||
maxLength,
|
|
||||||
}: PLaygroundConstraintAccordionViewHeaderMultipleValueProps) => {
|
|
||||||
const { classes: styles } = useStyles();
|
|
||||||
|
|
||||||
const [expandable, setExpandable] = useState(false);
|
|
||||||
|
|
||||||
const text = useMemo(() => {
|
|
||||||
return constraint?.values?.map(value => value).join(', ');
|
|
||||||
}, [constraint]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (text) {
|
|
||||||
allowExpand((text?.length ?? 0) > maxLength);
|
|
||||||
setExpandable((text?.length ?? 0) > maxLength);
|
|
||||||
}
|
|
||||||
}, [text, maxLength, allowExpand, setExpandable]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.headerValuesContainerWrapper}>
|
|
||||||
<div className={styles.headerValuesContainer}>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={!Boolean(constraint.result)}
|
|
||||||
show={
|
|
||||||
<Typography
|
|
||||||
variant={'body2'}
|
|
||||||
color={'error'}
|
|
||||||
noWrap={true}
|
|
||||||
sx={{ mr: 1 }}
|
|
||||||
>
|
|
||||||
does not match values{' '}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<StyledValuesSpan>{text}</StyledValuesSpan>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={expandable}
|
|
||||||
show={
|
|
||||||
<p
|
|
||||||
className={classnames(
|
|
||||||
styles.headerValuesExpand,
|
|
||||||
'valuesExpandLabel'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{!expanded
|
|
||||||
? `View all (${constraint?.values?.length})`
|
|
||||||
: 'View less'}
|
|
||||||
</p>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,47 +0,0 @@
|
|||||||
import React, { useEffect } from 'react';
|
|
||||||
import { Chip, styled, Typography } from '@mui/material';
|
|
||||||
import { formatConstraintValue } from 'utils/formatConstraintValue';
|
|
||||||
import { useStyles } from '../../../ConstraintAccordion.styles';
|
|
||||||
import { useLocationSettings } from 'hooks/useLocationSettings';
|
|
||||||
import { PlaygroundConstraintSchema } from 'component/playground/Playground/interfaces/playground.model';
|
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
|
||||||
|
|
||||||
const StyledSingleValueChip = styled(Chip)(({ theme }) => ({
|
|
||||||
margin: 'auto 0',
|
|
||||||
[theme.breakpoints.down(710)]: {
|
|
||||||
margin: theme.spacing(1, 0),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
interface PlaygroundConstraintAccordionViewHeaderSingleValueProps {
|
|
||||||
constraint: PlaygroundConstraintSchema;
|
|
||||||
allowExpand: (shouldExpand: boolean) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const PlaygroundSingleValue = ({
|
|
||||||
constraint,
|
|
||||||
allowExpand,
|
|
||||||
}: PlaygroundConstraintAccordionViewHeaderSingleValueProps) => {
|
|
||||||
const { locationSettings } = useLocationSettings();
|
|
||||||
const { classes: styles } = useStyles();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
allowExpand(false);
|
|
||||||
}, [allowExpand]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.headerValuesContainerWrapper}>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={!Boolean(constraint.result)}
|
|
||||||
show={
|
|
||||||
<Typography variant={'body1'} color={'error'}>
|
|
||||||
does not match values{' '}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<StyledSingleValueChip
|
|
||||||
label={formatConstraintValue(constraint, locationSettings)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -0,0 +1,40 @@
|
|||||||
|
import { styled, Typography } from '@mui/material';
|
||||||
|
import { CancelOutlined } from '@mui/icons-material';
|
||||||
|
import {
|
||||||
|
PlaygroundConstraintSchema,
|
||||||
|
PlaygroundRequestSchema,
|
||||||
|
} from 'component/playground/Playground/interfaces/playground.model';
|
||||||
|
|
||||||
|
const StyledConstraintErrorDiv = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
color: theme.palette.error.main,
|
||||||
|
}));
|
||||||
|
|
||||||
|
interface IConstraintErrorProps {
|
||||||
|
constraint: PlaygroundConstraintSchema;
|
||||||
|
input?: PlaygroundRequestSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ConstraintError = ({
|
||||||
|
constraint,
|
||||||
|
input,
|
||||||
|
}: IConstraintErrorProps) => {
|
||||||
|
const formatText = () => {
|
||||||
|
const value = input?.context[constraint.contextName];
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
return `Constraint not met – the value in the context: { ${value} } is not ${constraint.operator} ${constraint.contextName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Constraint not met – no value was specified for ${constraint.contextName}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledConstraintErrorDiv>
|
||||||
|
<CancelOutlined style={{ marginRight: '0.25rem' }} />
|
||||||
|
<Typography variant="body2">{formatText()}</Typography>
|
||||||
|
</StyledConstraintErrorDiv>
|
||||||
|
);
|
||||||
|
};
|
@ -7,11 +7,12 @@ import { objectId } from 'utils/objectId';
|
|||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
|
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
|
||||||
import { styled } from '@mui/material';
|
import { styled } from '@mui/material';
|
||||||
import { ConstraintAccordionView } from './ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView';
|
import { ConstraintAccordionView } from 'component/common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView';
|
||||||
|
import { ConstraintError } from './ConstraintError/ConstraintError';
|
||||||
|
import { ConstraintOk } from './ConstraintOk/ConstraintOk';
|
||||||
|
|
||||||
interface IConstraintExecutionProps {
|
interface IConstraintExecutionProps {
|
||||||
constraints?: PlaygroundConstraintSchema[];
|
constraints?: PlaygroundConstraintSchema[];
|
||||||
compact: boolean;
|
|
||||||
input?: PlaygroundRequestSchema;
|
input?: PlaygroundRequestSchema;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,7 +24,6 @@ export const ConstraintExecutionWrapper = styled('div')(() => ({
|
|||||||
|
|
||||||
export const ConstraintExecution: VFC<IConstraintExecutionProps> = ({
|
export const ConstraintExecution: VFC<IConstraintExecutionProps> = ({
|
||||||
constraints,
|
constraints,
|
||||||
compact,
|
|
||||||
input,
|
input,
|
||||||
}) => {
|
}) => {
|
||||||
if (!constraints) return null;
|
if (!constraints) return null;
|
||||||
@ -33,16 +33,24 @@ export const ConstraintExecution: VFC<IConstraintExecutionProps> = ({
|
|||||||
{constraints?.map((constraint, index) => (
|
{constraints?.map((constraint, index) => (
|
||||||
<Fragment key={objectId(constraint)}>
|
<Fragment key={objectId(constraint)}>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={index > 0 && constraints?.length > 1}
|
condition={index > 0}
|
||||||
show={<StrategySeparator text="AND" />}
|
show={<StrategySeparator text="AND" />}
|
||||||
/>
|
/>
|
||||||
<ConstraintAccordionView
|
<ConstraintAccordionView
|
||||||
constraint={constraint}
|
constraint={constraint}
|
||||||
playgroundInput={input}
|
compact
|
||||||
maxLength={compact ? 25 : 50}
|
renderAfter={
|
||||||
sx={{
|
<ConditionallyRender
|
||||||
backgroundColor: 'transparent!important',
|
condition={constraint.result}
|
||||||
}}
|
show={<ConstraintOk />}
|
||||||
|
elseShow={
|
||||||
|
<ConstraintError
|
||||||
|
input={input}
|
||||||
|
constraint={constraint}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
import { CheckCircleOutline } from '@mui/icons-material';
|
||||||
|
import { styled, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
const StyledCheckOutline = styled(CheckCircleOutline)(({ theme }) => ({
|
||||||
|
color: theme.palette.success.main,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledConstraintOKDiv = styled('div')(({ theme }) => ({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: theme.spacing(1),
|
||||||
|
color: theme.palette.success.main,
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const ConstraintOk = () => {
|
||||||
|
return (
|
||||||
|
<StyledConstraintOKDiv>
|
||||||
|
<StyledCheckOutline style={{ marginRight: '0.25rem' }} />
|
||||||
|
<Typography variant="body2">
|
||||||
|
Constraint met by value in context
|
||||||
|
</Typography>
|
||||||
|
</StyledConstraintOKDiv>
|
||||||
|
);
|
||||||
|
};
|
@ -1,118 +1,77 @@
|
|||||||
import { VFC } from 'react';
|
import { Fragment, VFC } from 'react';
|
||||||
import {
|
import {
|
||||||
PlaygroundSegmentSchema,
|
PlaygroundSegmentSchema,
|
||||||
PlaygroundRequestSchema,
|
PlaygroundRequestSchema,
|
||||||
} from 'component/playground/Playground/interfaces/playground.model';
|
} from 'component/playground/Playground/interfaces/playground.model';
|
||||||
import { ConstraintExecution } from '../ConstraintExecution/ConstraintExecution';
|
import { ConstraintExecution } from '../ConstraintExecution/ConstraintExecution';
|
||||||
import { CancelOutlined, DonutLarge } from '@mui/icons-material';
|
import { CancelOutlined } from '@mui/icons-material';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
|
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
|
||||||
import { useStyles } from './SegmentExecution.styles';
|
|
||||||
import { styled, Typography } from '@mui/material';
|
import { styled, Typography } from '@mui/material';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import { SegmentItem } from 'component/common/SegmentItem/SegmentItem';
|
||||||
|
|
||||||
interface ISegmentExecutionProps {
|
interface ISegmentExecutionProps {
|
||||||
segments?: PlaygroundSegmentSchema[];
|
segments?: PlaygroundSegmentSchema[];
|
||||||
input?: PlaygroundRequestSchema;
|
input?: PlaygroundRequestSchema;
|
||||||
hasConstraints: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const SegmentExecutionLinkWrapper = styled('div')(({ theme }) => ({
|
|
||||||
padding: theme.spacing(2, 3),
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
fontSize: theme.fontSizes.smallBody,
|
|
||||||
position: 'relative',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const SegmentExecutionHeader = styled('div')(({ theme }) => ({
|
|
||||||
width: '100%',
|
|
||||||
display: 'inline-flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
'& + &': {
|
|
||||||
margin: theme.spacing(2),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const SegmentExecutionWrapper = styled('div')(({ theme }) => ({
|
|
||||||
flexDirection: 'column',
|
|
||||||
borderRadius: theme.shape.borderRadiusMedium,
|
|
||||||
border: `1px solid ${theme.palette.dividerAlternative}`,
|
|
||||||
'& + &': {
|
|
||||||
marginTop: theme.spacing(2),
|
|
||||||
},
|
|
||||||
background: theme.palette.neutral.light,
|
|
||||||
marginBottom: theme.spacing(1),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const SegmentExecutionConstraintWrapper = styled('div')(() => ({
|
|
||||||
padding: '12px',
|
|
||||||
}));
|
|
||||||
|
|
||||||
const SegmentResultTextWrapper = styled('div')(({ theme }) => ({
|
const SegmentResultTextWrapper = styled('div')(({ theme }) => ({
|
||||||
color: theme.palette.error.main,
|
color: theme.palette.error.main,
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
marginRight: '12px',
|
marginLeft: 'auto',
|
||||||
gap: theme.spacing(1),
|
gap: theme.spacing(1),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const SegmentExecution: VFC<ISegmentExecutionProps> = ({
|
export const SegmentExecution: VFC<ISegmentExecutionProps> = ({
|
||||||
segments,
|
segments,
|
||||||
input,
|
input,
|
||||||
hasConstraints,
|
|
||||||
}) => {
|
}) => {
|
||||||
const { classes: styles } = useStyles();
|
|
||||||
|
|
||||||
if (!segments) return null;
|
if (!segments) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{segments.map((segment, index) => (
|
{segments.map((segment, index) => (
|
||||||
<SegmentExecutionWrapper key={segment.id}>
|
<Fragment key={segment.id}>
|
||||||
<SegmentExecutionHeader>
|
<SegmentItem
|
||||||
<SegmentExecutionLinkWrapper>
|
segment={segment}
|
||||||
<DonutLarge color="secondary" sx={{ mr: 1 }} />{' '}
|
constraintList={
|
||||||
Segment:{' '}
|
<ConstraintExecution
|
||||||
<Link
|
constraints={segment.constraints}
|
||||||
to={`/segments/edit/${segment.id}`}
|
input={input}
|
||||||
className={styles.link}
|
/>
|
||||||
>
|
}
|
||||||
{segment.name}
|
headerContent={
|
||||||
</Link>
|
<ConditionallyRender
|
||||||
</SegmentExecutionLinkWrapper>
|
condition={!Boolean(segment.result)}
|
||||||
<ConditionallyRender
|
show={
|
||||||
condition={!Boolean(segment.result)}
|
<SegmentResultTextWrapper>
|
||||||
show={
|
<Typography
|
||||||
<SegmentResultTextWrapper>
|
variant={'subtitle2'}
|
||||||
<Typography
|
sx={{ pt: 0.25 }}
|
||||||
variant={'subtitle2'}
|
>
|
||||||
sx={{ pt: 0.25 }}
|
segment is false
|
||||||
>
|
</Typography>
|
||||||
segment is false
|
<span>
|
||||||
</Typography>
|
<CancelOutlined />
|
||||||
<span>
|
</span>
|
||||||
<CancelOutlined />
|
</SegmentResultTextWrapper>
|
||||||
</span>
|
}
|
||||||
</SegmentResultTextWrapper>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
isExpanded
|
||||||
</SegmentExecutionHeader>
|
/>
|
||||||
<SegmentExecutionConstraintWrapper>
|
|
||||||
<ConstraintExecution
|
|
||||||
constraints={segment.constraints}
|
|
||||||
input={input}
|
|
||||||
compact
|
|
||||||
/>
|
|
||||||
</SegmentExecutionConstraintWrapper>
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={
|
condition={
|
||||||
index === segments?.length - 1 && hasConstraints
|
// Add IF there is a next segment
|
||||||
|
index >= 0 &&
|
||||||
|
segments.length > 1 &&
|
||||||
|
// Don't add if it's the last segment item
|
||||||
|
index !== segments.length - 1
|
||||||
}
|
}
|
||||||
show={<StrategySeparator text="AND" />}
|
show={<StrategySeparator text="AND" />}
|
||||||
/>
|
/>
|
||||||
</SegmentExecutionWrapper>
|
</Fragment>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { VFC } from 'react';
|
import { Fragment, VFC } from 'react';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
|
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
|
||||||
import { Box, Chip, styled } from '@mui/material';
|
import { Box, Chip, styled } from '@mui/material';
|
||||||
@ -12,6 +12,7 @@ import { ConstraintExecution } from './ConstraintExecution/ConstraintExecution';
|
|||||||
import { SegmentExecution } from './SegmentExecution/SegmentExecution';
|
import { SegmentExecution } from './SegmentExecution/SegmentExecution';
|
||||||
import { PlaygroundResultStrategyExecutionParameters } from './StrategyExecutionParameters/StrategyExecutionParameters';
|
import { PlaygroundResultStrategyExecutionParameters } from './StrategyExecutionParameters/StrategyExecutionParameters';
|
||||||
import { CustomStrategyParams } from './CustomStrategyParams/CustomStrategyParams';
|
import { CustomStrategyParams } from './CustomStrategyParams/CustomStrategyParams';
|
||||||
|
import { formattedStrategyNames } from 'utils/strategyNames';
|
||||||
|
|
||||||
interface IStrategyExecutionProps {
|
interface IStrategyExecutionProps {
|
||||||
strategyResult: PlaygroundStrategySchema;
|
strategyResult: PlaygroundStrategySchema;
|
||||||
@ -32,72 +33,60 @@ export const StrategyExecution: VFC<IStrategyExecutionProps> = ({
|
|||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
const { classes: styles } = useStyles();
|
const { classes: styles } = useStyles();
|
||||||
|
|
||||||
|
const hasSegments =
|
||||||
|
Boolean(uiConfig.flags.SE) && Boolean(segments && segments.length > 0);
|
||||||
const hasConstraints = Boolean(constraints && constraints?.length > 0);
|
const hasConstraints = Boolean(constraints && constraints?.length > 0);
|
||||||
const hasParameters = Object.keys(parameters).length === 0;
|
const hasExecutionParameters =
|
||||||
|
name !== 'default' &&
|
||||||
|
Object.keys(formattedStrategyNames).includes(name);
|
||||||
|
const hasCustomStrategyParameters =
|
||||||
|
Object.keys(parameters).length > 0 &&
|
||||||
|
strategyResult.result.evaluationStatus === 'incomplete'; // Use of custom strategy can be more explicit from the API
|
||||||
|
|
||||||
if (!parameters) {
|
if (!parameters) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const items = [
|
||||||
<StyledStrategyExecutionWrapper>
|
hasSegments && <SegmentExecution segments={segments} input={input} />,
|
||||||
<ConditionallyRender
|
hasConstraints && (
|
||||||
condition={
|
<ConstraintExecution constraints={constraints} input={input} />
|
||||||
Boolean(uiConfig.flags.SE) &&
|
),
|
||||||
Boolean(segments && segments.length > 0)
|
hasExecutionParameters && (
|
||||||
}
|
|
||||||
show={
|
|
||||||
<SegmentExecution
|
|
||||||
segments={segments}
|
|
||||||
hasConstraints={hasConstraints}
|
|
||||||
input={input}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={Boolean(constraints && constraints.length > 0)}
|
|
||||||
show={
|
|
||||||
<>
|
|
||||||
<ConstraintExecution
|
|
||||||
constraints={constraints}
|
|
||||||
compact={true}
|
|
||||||
input={input}
|
|
||||||
/>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={Boolean(
|
|
||||||
constraints &&
|
|
||||||
constraints.length > 0 &&
|
|
||||||
!hasParameters
|
|
||||||
)}
|
|
||||||
show={<StrategySeparator text="AND" />}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<ConditionallyRender
|
|
||||||
condition={name === 'default'}
|
|
||||||
show={
|
|
||||||
<Box sx={{ width: '100%' }} className={styles.summary}>
|
|
||||||
The standard strategyResult is{' '}
|
|
||||||
<Chip
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
color="success"
|
|
||||||
label="ON"
|
|
||||||
/>{' '}
|
|
||||||
for all users.
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<PlaygroundResultStrategyExecutionParameters
|
<PlaygroundResultStrategyExecutionParameters
|
||||||
parameters={parameters}
|
parameters={parameters}
|
||||||
constraints={constraints}
|
constraints={constraints}
|
||||||
input={input}
|
input={input}
|
||||||
/>
|
/>
|
||||||
<CustomStrategyParams
|
),
|
||||||
strategyName={strategyResult.name}
|
hasCustomStrategyParameters && (
|
||||||
parameters={parameters}
|
<CustomStrategyParams strategyName={name} parameters={parameters} />
|
||||||
/>
|
),
|
||||||
|
name === 'default' && (
|
||||||
|
<Box sx={{ width: '100%' }} className={styles.summary}>
|
||||||
|
The standard strategy is{' '}
|
||||||
|
<Chip
|
||||||
|
variant="outlined"
|
||||||
|
size="small"
|
||||||
|
color="success"
|
||||||
|
label="ON"
|
||||||
|
/>{' '}
|
||||||
|
for all users.
|
||||||
|
</Box>
|
||||||
|
),
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledStrategyExecutionWrapper>
|
||||||
|
{items.map((item, index) => (
|
||||||
|
<Fragment key={index}>
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={index > 0}
|
||||||
|
show={<StrategySeparator text="AND" />}
|
||||||
|
/>
|
||||||
|
{item}
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
</StyledStrategyExecutionWrapper>
|
</StyledStrategyExecutionWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -25,102 +25,117 @@ export const PlaygroundResultStrategyExecutionParameters = ({
|
|||||||
input,
|
input,
|
||||||
}: PlaygroundResultStrategyExecutionParametersProps) => {
|
}: PlaygroundResultStrategyExecutionParametersProps) => {
|
||||||
const { classes: styles } = useStyles();
|
const { classes: styles } = useStyles();
|
||||||
const renderParameters = () => {
|
|
||||||
return Object.keys(parameters).map(key => {
|
|
||||||
switch (key) {
|
|
||||||
case 'rollout':
|
|
||||||
case 'Rollout':
|
|
||||||
const percentage = parseParameterNumber(parameters[key]);
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
className={styles.summary}
|
|
||||||
key={key}
|
|
||||||
sx={{ display: 'flex', alignItems: 'center' }}
|
|
||||||
>
|
|
||||||
<Box sx={{ mr: '1rem' }}>
|
|
||||||
<PercentageCircle
|
|
||||||
percentage={percentage}
|
|
||||||
size="2rem"
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<div>
|
|
||||||
<Chip
|
|
||||||
color="success"
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
label={`${percentage}%`}
|
|
||||||
/>{' '}
|
|
||||||
of your base{' '}
|
|
||||||
{constraints.length > 0
|
|
||||||
? 'who match constraints'
|
|
||||||
: ''}{' '}
|
|
||||||
is included.
|
|
||||||
</div>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
case 'userIds':
|
|
||||||
case 'UserIds':
|
|
||||||
const users = parseParameterStrings(parameters[key]);
|
|
||||||
return (
|
|
||||||
<PlaygroundParameterItem
|
|
||||||
key={key}
|
|
||||||
value={users}
|
|
||||||
text="user"
|
|
||||||
input={
|
|
||||||
Boolean(input?.context?.[getMappedParam(key)])
|
|
||||||
? input?.context?.[getMappedParam(key)]
|
|
||||||
: 'no value'
|
|
||||||
}
|
|
||||||
showReason={
|
|
||||||
Boolean(input?.context?.[getMappedParam(key)])
|
|
||||||
? !users.includes(
|
|
||||||
input?.context?.[getMappedParam(key)]
|
|
||||||
)
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case 'hostNames':
|
|
||||||
case 'HostNames':
|
|
||||||
const hosts = parseParameterStrings(parameters[key]);
|
|
||||||
return (
|
|
||||||
<PlaygroundParameterItem
|
|
||||||
key={key}
|
|
||||||
value={hosts}
|
|
||||||
text={'host'}
|
|
||||||
input={'no value'}
|
|
||||||
showReason={undefined}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case 'IPs':
|
|
||||||
const IPs = parseParameterStrings(parameters[key]);
|
|
||||||
return (
|
|
||||||
<PlaygroundParameterItem
|
|
||||||
key={key}
|
|
||||||
value={IPs}
|
|
||||||
text={'IP'}
|
|
||||||
input={
|
|
||||||
Boolean(input?.context?.[getMappedParam(key)])
|
|
||||||
? input?.context?.[getMappedParam(key)]
|
|
||||||
: 'no value'
|
|
||||||
}
|
|
||||||
showReason={
|
|
||||||
Boolean(input?.context?.[getMappedParam(key)])
|
|
||||||
? !IPs.includes(
|
|
||||||
input?.context?.[getMappedParam(key)]
|
|
||||||
)
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case 'stickiness':
|
|
||||||
case 'groupId':
|
|
||||||
return null;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return <>{renderParameters()}</>;
|
return (
|
||||||
|
<>
|
||||||
|
{Object.keys(parameters).map(key => {
|
||||||
|
switch (key) {
|
||||||
|
case 'rollout':
|
||||||
|
case 'Rollout':
|
||||||
|
const percentage = parseParameterNumber(
|
||||||
|
parameters[key]
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
className={styles.summary}
|
||||||
|
key={key}
|
||||||
|
sx={{ display: 'flex', alignItems: 'center' }}
|
||||||
|
>
|
||||||
|
<Box sx={{ mr: '1rem' }}>
|
||||||
|
<PercentageCircle
|
||||||
|
percentage={percentage}
|
||||||
|
size="2rem"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
<div>
|
||||||
|
<Chip
|
||||||
|
color="success"
|
||||||
|
variant="outlined"
|
||||||
|
size="small"
|
||||||
|
label={`${percentage}%`}
|
||||||
|
/>{' '}
|
||||||
|
of your base{' '}
|
||||||
|
{constraints.length > 0
|
||||||
|
? 'who match constraints'
|
||||||
|
: ''}{' '}
|
||||||
|
is included.
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
case 'userIds':
|
||||||
|
case 'UserIds':
|
||||||
|
const users = parseParameterStrings(parameters[key]);
|
||||||
|
return (
|
||||||
|
<PlaygroundParameterItem
|
||||||
|
key={key}
|
||||||
|
value={users}
|
||||||
|
text="user"
|
||||||
|
input={
|
||||||
|
Boolean(
|
||||||
|
input?.context?.[getMappedParam(key)]
|
||||||
|
)
|
||||||
|
? input?.context?.[getMappedParam(key)]
|
||||||
|
: 'no value'
|
||||||
|
}
|
||||||
|
showReason={
|
||||||
|
Boolean(
|
||||||
|
input?.context?.[getMappedParam(key)]
|
||||||
|
)
|
||||||
|
? !users.includes(
|
||||||
|
input?.context?.[
|
||||||
|
getMappedParam(key)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'hostNames':
|
||||||
|
case 'HostNames':
|
||||||
|
const hosts = parseParameterStrings(parameters[key]);
|
||||||
|
return (
|
||||||
|
<PlaygroundParameterItem
|
||||||
|
key={key}
|
||||||
|
value={hosts}
|
||||||
|
text={'host'}
|
||||||
|
input={'no value'}
|
||||||
|
showReason={undefined}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'IPs':
|
||||||
|
const IPs = parseParameterStrings(parameters[key]);
|
||||||
|
return (
|
||||||
|
<PlaygroundParameterItem
|
||||||
|
key={key}
|
||||||
|
value={IPs}
|
||||||
|
text={'IP'}
|
||||||
|
input={
|
||||||
|
Boolean(
|
||||||
|
input?.context?.[getMappedParam(key)]
|
||||||
|
)
|
||||||
|
? input?.context?.[getMappedParam(key)]
|
||||||
|
: 'no value'
|
||||||
|
}
|
||||||
|
showReason={
|
||||||
|
Boolean(
|
||||||
|
input?.context?.[getMappedParam(key)]
|
||||||
|
)
|
||||||
|
? !IPs.includes(
|
||||||
|
input?.context?.[
|
||||||
|
getMappedParam(key)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'stickiness':
|
||||||
|
case 'groupId':
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@ -22,10 +22,10 @@ export const FeatureStatusCell = ({ feature }: IFeatureStatusCellProps) => {
|
|||||||
if (feature?.isEnabled) {
|
if (feature?.isEnabled) {
|
||||||
return [true, 'True'];
|
return [true, 'True'];
|
||||||
}
|
}
|
||||||
if (feature?.strategies?.result === false) {
|
if (feature?.strategies?.result === 'unknown') {
|
||||||
return [false, 'False'];
|
return ['unknown', 'Unknown'];
|
||||||
}
|
}
|
||||||
return ['unknown', 'Unknown'];
|
return [false, 'False'];
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -101,14 +101,19 @@ export const PlaygroundResultsTable = ({
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: 'isEnabled',
|
||||||
Header: 'isEnabled',
|
Header: 'isEnabled',
|
||||||
accessor: 'isEnabled',
|
|
||||||
filterName: 'isEnabled',
|
filterName: 'isEnabled',
|
||||||
filterParsing: (value: boolean) => (value ? 'true' : 'false'),
|
accessor: (row: PlaygroundFeatureSchema) =>
|
||||||
|
row?.isEnabled
|
||||||
|
? 'true'
|
||||||
|
: row?.strategies?.result === 'unknown'
|
||||||
|
? 'unknown'
|
||||||
|
: 'false',
|
||||||
Cell: ({ row }: any) => (
|
Cell: ({ row }: any) => (
|
||||||
<FeatureStatusCell feature={row.original} />
|
<FeatureStatusCell feature={row.original} />
|
||||||
),
|
),
|
||||||
sortType: 'boolean',
|
sortType: 'playgroundResultState',
|
||||||
maxWidth: 120,
|
maxWidth: 120,
|
||||||
sortInverted: true,
|
sortInverted: true,
|
||||||
},
|
},
|
||||||
|
@ -53,8 +53,7 @@ export const VariantCell: VFC<IVariantCellProps> = ({
|
|||||||
id={`${feature}-result-variants`}
|
id={`${feature}-result-variants`}
|
||||||
PaperProps={{
|
PaperProps={{
|
||||||
sx: {
|
sx: {
|
||||||
borderRadius:
|
borderRadius: `${theme.shape.borderRadiusLarge}px`,
|
||||||
theme.shape.borderRadiusExtraLarge,
|
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
|
@ -18,4 +18,14 @@ export const sortTypes = {
|
|||||||
(a?.values?.[id] || '')
|
(a?.values?.[id] || '')
|
||||||
?.toLowerCase()
|
?.toLowerCase()
|
||||||
.localeCompare(b?.values?.[id]?.toLowerCase() || ''),
|
.localeCompare(b?.values?.[id]?.toLowerCase() || ''),
|
||||||
|
playgroundResultState: (v1: any, v2: any, id: string) => {
|
||||||
|
const a = v1?.values?.[id];
|
||||||
|
const b = v2?.values?.[id];
|
||||||
|
if (a === b) return 0;
|
||||||
|
if (a === 'true') return 1;
|
||||||
|
if (b === 'true') return -1;
|
||||||
|
if (a === 'false') return -1;
|
||||||
|
if (b === 'false') return 1;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -36,7 +36,7 @@ export const getFeatureStrategyIcon = (strategyName: string): ElementType => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const formattedStrategyNames: Record<string, string> = {
|
export const formattedStrategyNames: Record<string, string> = {
|
||||||
applicationHostname: 'Hosts',
|
applicationHostname: 'Hosts',
|
||||||
default: 'Standard',
|
default: 'Standard',
|
||||||
flexibleRollout: 'Gradual rollout',
|
flexibleRollout: 'Gradual rollout',
|
||||||
|
Loading…
Reference in New Issue
Block a user