mirror of
https://github.com/Unleash/unleash.git
synced 2025-10-13 11:17:26 +02:00
bug fixes, improvements and env handling
This commit is contained in:
parent
9fee273cff
commit
42fbc27148
@ -18,14 +18,14 @@ import {
|
||||
import { useStyles } from '../ConstraintAccordion.styles';
|
||||
import {
|
||||
PlaygroundFeatureStrategyConstraintResult,
|
||||
SdkContextSchema,
|
||||
PlaygroundRequestSchema,
|
||||
} from '../../../../hooks/api/actions/usePlayground/playground.model';
|
||||
|
||||
interface IConstraintAccordionViewProps {
|
||||
constraint: IConstraint | PlaygroundFeatureStrategyConstraintResult;
|
||||
onDelete?: () => void;
|
||||
onEdit?: () => void;
|
||||
playgroundContext?: SdkContextSchema;
|
||||
playgroundInput?: PlaygroundRequestSchema;
|
||||
maxLength?: number;
|
||||
sx?: SxProps<Theme>;
|
||||
}
|
||||
@ -36,7 +36,7 @@ export const ConstraintAccordionView = ({
|
||||
onDelete,
|
||||
sx = undefined,
|
||||
maxLength,
|
||||
playgroundContext,
|
||||
playgroundInput,
|
||||
}: IConstraintAccordionViewProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const [expandable, setExpandable] = useState(true);
|
||||
@ -46,7 +46,6 @@ export const ConstraintAccordionView = ({
|
||||
[...semVerOperators, ...numOperators, ...dateOperators],
|
||||
constraint.operator
|
||||
);
|
||||
|
||||
const handleClick = () => {
|
||||
if (expandable) {
|
||||
setExpanded(!expanded);
|
||||
@ -79,7 +78,7 @@ export const ConstraintAccordionView = ({
|
||||
allowExpand={setExpandable}
|
||||
expanded={expanded}
|
||||
maxLength={maxLength ?? 112}
|
||||
playgroundContext={playgroundContext}
|
||||
playgroundInput={playgroundInput}
|
||||
/>
|
||||
</AccordionSummary>
|
||||
|
||||
|
@ -5,7 +5,7 @@ import { ConstraintAccordionHeaderActions } from '../../ConstraintAccordionHeade
|
||||
import { useStyles } from 'component/common/ConstraintAccordion/ConstraintAccordion.styles';
|
||||
import {
|
||||
PlaygroundFeatureStrategyConstraintResult,
|
||||
SdkContextSchema,
|
||||
PlaygroundRequestSchema,
|
||||
} from '../../../../../hooks/api/actions/usePlayground/playground.model';
|
||||
|
||||
interface IConstraintAccordionViewHeaderProps {
|
||||
@ -15,7 +15,7 @@ interface IConstraintAccordionViewHeaderProps {
|
||||
singleValue: boolean;
|
||||
expanded: boolean;
|
||||
allowExpand: (shouldExpand: boolean) => void;
|
||||
playgroundContext?: SdkContextSchema;
|
||||
playgroundInput?: PlaygroundRequestSchema;
|
||||
maxLength?: number;
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ export const ConstraintAccordionViewHeader = ({
|
||||
allowExpand,
|
||||
expanded,
|
||||
maxLength,
|
||||
playgroundContext,
|
||||
playgroundInput,
|
||||
}: IConstraintAccordionViewHeaderProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
@ -41,7 +41,7 @@ export const ConstraintAccordionViewHeader = ({
|
||||
expanded={expanded}
|
||||
result={'result' in constraint ? constraint.result : undefined}
|
||||
maxLength={maxLength}
|
||||
playgroundContext={playgroundContext}
|
||||
playgroundInput={playgroundInput}
|
||||
/>
|
||||
<ConstraintAccordionHeaderActions
|
||||
onEdit={onEdit}
|
||||
|
@ -7,7 +7,7 @@ import React from 'react';
|
||||
import { IConstraint } from '../../../../../../interfaces/strategy';
|
||||
import { useStyles } from '../../../ConstraintAccordion.styles';
|
||||
import { CancelOutlined } from '@mui/icons-material';
|
||||
import { SdkContextSchema } from '../../../../../../hooks/api/actions/usePlayground/playground.model';
|
||||
import { PlaygroundRequestSchema } from '../../../../../../hooks/api/actions/usePlayground/playground.model';
|
||||
|
||||
const StyledHeaderText = styled('span')(({ theme }) => ({
|
||||
display: '-webkit-box',
|
||||
@ -43,7 +43,7 @@ interface ConstraintAccordionViewHeaderMetaInfoProps {
|
||||
allowExpand: (shouldExpand: boolean) => void;
|
||||
result?: boolean;
|
||||
maxLength?: number;
|
||||
playgroundContext?: SdkContextSchema;
|
||||
playgroundInput?: PlaygroundRequestSchema;
|
||||
}
|
||||
|
||||
export const ConstraintAccordionViewHeaderInfo = ({
|
||||
@ -52,12 +52,17 @@ export const ConstraintAccordionViewHeaderInfo = ({
|
||||
allowExpand,
|
||||
expanded,
|
||||
result,
|
||||
playgroundInput,
|
||||
maxLength = 112,
|
||||
playgroundContext,
|
||||
}: ConstraintAccordionViewHeaderMetaInfoProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const theme = useTheme();
|
||||
|
||||
const isPlayground = Boolean(playgroundInput);
|
||||
const constrainExistsInContext =
|
||||
isPlayground &&
|
||||
Boolean(playgroundInput?.context[constraint.contextName]);
|
||||
|
||||
return (
|
||||
<StyledHeaderWrapper>
|
||||
<div className={styles.headerMetaInfo}>
|
||||
@ -65,24 +70,17 @@ export const ConstraintAccordionViewHeaderInfo = ({
|
||||
<StyledHeaderText>
|
||||
{constraint.contextName}
|
||||
<ConditionallyRender
|
||||
condition={
|
||||
result !== undefined &&
|
||||
Boolean(playgroundContext)
|
||||
}
|
||||
condition={isPlayground}
|
||||
show={
|
||||
<Typography
|
||||
variant={'body1'}
|
||||
color={
|
||||
Boolean(
|
||||
playgroundContext![
|
||||
constraint.contextName
|
||||
]
|
||||
)
|
||||
constrainExistsInContext
|
||||
? theme.palette.neutral.dark
|
||||
: theme.palette.error.main
|
||||
}
|
||||
>
|
||||
{playgroundContext![
|
||||
{playgroundInput?.context[
|
||||
constraint.contextName
|
||||
] || 'no value'}
|
||||
</Typography>
|
||||
|
@ -203,6 +203,7 @@ export const Playground: VFC<{}> = () => {
|
||||
<PlaygroundResultsTable
|
||||
loading={loading}
|
||||
features={results?.features}
|
||||
input={results?.input}
|
||||
/>
|
||||
}
|
||||
elseShow={<PlaygroundGuidance />}
|
||||
|
@ -1,17 +1,21 @@
|
||||
import {
|
||||
PlaygroundFeatureSchema,
|
||||
PlaygroundFeatureStrategyResult,
|
||||
PlaygroundRequestSchema,
|
||||
} from 'hooks/api/actions/usePlayground/playground.model';
|
||||
import { IconButton, Popover, styled, Typography } from '@mui/material';
|
||||
import { IconButton, Popover, styled } from '@mui/material';
|
||||
import { InfoOutlined } from '@mui/icons-material';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { ConditionallyRender } from '../../../../common/ConditionallyRender/ConditionallyRender';
|
||||
import { PlaygroundResultFeatureStrategyItem } from './PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem';
|
||||
import { useStyles } from './FeatureResultInfoPopoverCell.styles';
|
||||
import { PlaygroundResultFeatureDetails } from './PlaygroundResultFeatureDetails/PlaygroundResultFeatureDetails';
|
||||
import {
|
||||
PlaygroundResultStrategyList,
|
||||
WrappedPlaygroundResultStrategyList,
|
||||
} from './PlaygroundResultStrategyList/PlaygroundResultStrategyList';
|
||||
|
||||
interface FeatureResultInfoPopoverCellProps {
|
||||
feature?: PlaygroundFeatureSchema;
|
||||
feature: PlaygroundFeatureSchema;
|
||||
input?: PlaygroundRequestSchema;
|
||||
}
|
||||
|
||||
const FeatureResultPopoverWrapper = styled('div')(({ theme }) => ({
|
||||
@ -21,6 +25,7 @@ const FeatureResultPopoverWrapper = styled('div')(({ theme }) => ({
|
||||
|
||||
export const FeatureResultInfoPopoverCell = ({
|
||||
feature,
|
||||
input,
|
||||
}: FeatureResultInfoPopoverCellProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const { classes: styles } = useStyles();
|
||||
@ -30,77 +35,6 @@ export const FeatureResultInfoPopoverCell = ({
|
||||
setOpen(!open);
|
||||
};
|
||||
|
||||
const dummyPlaygroundFeatureTrue: PlaygroundFeatureSchema = {
|
||||
name: feature?.name as any,
|
||||
projectId: 'default',
|
||||
isEnabled: true,
|
||||
} as any;
|
||||
|
||||
const dummyPlaygroundFeatureFalse: PlaygroundFeatureSchema = {
|
||||
name: feature?.name as any,
|
||||
projectId: 'default',
|
||||
isEnabled: false,
|
||||
} as any;
|
||||
|
||||
const strategies: PlaygroundFeatureStrategyResult[] = [
|
||||
{
|
||||
name: 'default',
|
||||
id: 'strategy-id',
|
||||
parameters: {},
|
||||
result: feature?.isEnabled as any,
|
||||
constraints: [
|
||||
{
|
||||
result: true,
|
||||
contextName: 'appName',
|
||||
operator: 'IN',
|
||||
caseInsensitive: false,
|
||||
inverted: false,
|
||||
values: ['MyApp', 'MyOtherApp', 'Unleash'],
|
||||
},
|
||||
],
|
||||
segments: [
|
||||
{
|
||||
result: feature?.isEnabled as any,
|
||||
id: 5,
|
||||
name: 'my-segment',
|
||||
constraints: [
|
||||
{
|
||||
result: feature?.isEnabled as any,
|
||||
contextName: 'environment',
|
||||
operator: 'IN',
|
||||
caseInsensitive: false,
|
||||
inverted: false,
|
||||
values: ['development'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'flexibleRollout',
|
||||
id: 'strategy-id',
|
||||
parameters: {},
|
||||
result: false,
|
||||
segments: [
|
||||
{
|
||||
result: false,
|
||||
id: 6,
|
||||
name: 'my-segment',
|
||||
constraints: [
|
||||
{
|
||||
result: false,
|
||||
contextName: 'appName',
|
||||
operator: 'IN',
|
||||
caseInsensitive: false,
|
||||
inverted: false,
|
||||
values: ['MyApp2'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
if (!feature) {
|
||||
return null;
|
||||
}
|
||||
@ -125,29 +59,24 @@ export const FeatureResultInfoPopoverCell = ({
|
||||
classes={{ paper: styles.popoverPaper }}
|
||||
>
|
||||
<PlaygroundResultFeatureDetails
|
||||
feature={
|
||||
feature.isEnabled
|
||||
? dummyPlaygroundFeatureTrue
|
||||
: dummyPlaygroundFeatureFalse
|
||||
}
|
||||
feature={feature}
|
||||
input={input}
|
||||
onClose={() => setOpen(false)}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={strategies.length > 0}
|
||||
condition={!feature.isEnabledInCurrentEnvironment}
|
||||
show={
|
||||
<>
|
||||
<Typography
|
||||
variant={'subtitle1'}
|
||||
sx={{ mb: 2, color: 'text.secondary' }}
|
||||
>{`Strategies (${strategies.length})`}</Typography>
|
||||
{strategies.map((strategy, index) => (
|
||||
<PlaygroundResultFeatureStrategyItem
|
||||
key={strategy.id}
|
||||
strategy={strategy}
|
||||
index={index}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
<PlaygroundResultStrategyList
|
||||
strategies={feature?.strategies}
|
||||
input={input}
|
||||
/>
|
||||
}
|
||||
elseShow={
|
||||
<WrappedPlaygroundResultStrategyList
|
||||
strategies={feature?.strategies}
|
||||
feature={feature}
|
||||
input={input}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</Popover>
|
||||
|
@ -9,9 +9,11 @@ export const useStyles = makeStyles()(theme => ({
|
||||
titleRow: {
|
||||
display: 'inline-flex',
|
||||
alignItems: 'flex-start',
|
||||
justifyItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '12px',
|
||||
marginTop: '12px',
|
||||
},
|
||||
alertRow: {},
|
||||
descriptionRow: {
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
|
@ -1,30 +1,45 @@
|
||||
import { PlaygroundFeatureSchema } from '../../../../../../hooks/api/actions/usePlayground/playground.model';
|
||||
import { IconButton, Typography, useTheme } from '@mui/material';
|
||||
import {
|
||||
PlaygroundFeatureSchema,
|
||||
PlaygroundRequestSchema,
|
||||
} from '../../../../../../hooks/api/actions/usePlayground/playground.model';
|
||||
import { Alert, IconButton, Typography, useTheme } from '@mui/material';
|
||||
import { PlaygroundResultChip } from '../../PlaygroundResultChip/PlaygroundResultChip';
|
||||
import { useStyles } from './PlaygroundResultFeatureDetails.styles';
|
||||
import { CloseOutlined } from '@mui/icons-material';
|
||||
import React from 'react';
|
||||
import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender';
|
||||
import { checkForEmptyValues } from './helpers';
|
||||
interface PlaygroundFeatureResultDetailsProps {
|
||||
feature: PlaygroundFeatureSchema;
|
||||
input?: PlaygroundRequestSchema;
|
||||
onClose: () => void;
|
||||
}
|
||||
export const PlaygroundResultFeatureDetails = ({
|
||||
feature,
|
||||
input,
|
||||
onClose,
|
||||
}: PlaygroundFeatureResultDetailsProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
const theme = useTheme();
|
||||
|
||||
const description = feature.isEnabled
|
||||
? 'This feature toggle is True in production because '
|
||||
: 'This feature toggle is False in production because ';
|
||||
const reason = feature.isEnabled
|
||||
const description = Boolean(feature.isEnabled)
|
||||
? `This feature toggle is True in ${input?.environment} because `
|
||||
: `This feature toggle is False in ${input?.environment} because `;
|
||||
|
||||
const reason = Boolean(feature.isEnabled)
|
||||
? 'at least one strategy is True'
|
||||
: feature?.isEnabledInCurrentEnvironment
|
||||
? 'the environment is disabled'
|
||||
: 'all strategies are False';
|
||||
const color = feature.isEnabled
|
||||
|
||||
const color = Boolean(feature.isEnabled)
|
||||
? theme.palette.success.main
|
||||
: theme.palette.error.main;
|
||||
|
||||
const noValueTxt = checkForEmptyValues(input?.context)
|
||||
? 'You did not provide a value for your context filed in step 2 of the configuration'
|
||||
: undefined;
|
||||
|
||||
const onCloseClick =
|
||||
onClose &&
|
||||
((event: React.SyntheticEvent) => {
|
||||
@ -55,6 +70,14 @@ export const PlaygroundResultFeatureDetails = ({
|
||||
{reason}
|
||||
</Typography>
|
||||
</div>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(noValueTxt)}
|
||||
show={
|
||||
<div className={styles.alertRow}>
|
||||
<Alert color={'info'}>{noValueTxt}</Alert>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,8 @@
|
||||
export function checkForEmptyValues(object?: Object): boolean {
|
||||
if (object === undefined) {
|
||||
return true;
|
||||
}
|
||||
return Object.values(object).every(v =>
|
||||
v && typeof v === 'object' ? checkForEmptyValues(v) : v === null
|
||||
);
|
||||
}
|
@ -1,16 +1,18 @@
|
||||
import {
|
||||
PlaygroundFeatureStrategyConstraintResult,
|
||||
SdkContextSchema,
|
||||
PlaygroundRequestSchema,
|
||||
} from 'hooks/api/actions/usePlayground/playground.model';
|
||||
import React, { Fragment } from 'react';
|
||||
import { objectId } from '../../../../../../utils/objectId';
|
||||
import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender';
|
||||
import { StrategySeparator } from '../../../../../common/StrategySeparator/StrategySeparator';
|
||||
import { ConstraintAccordionView } from '../../../../../common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView';
|
||||
import { objectId } from '../../../../../../../utils/objectId';
|
||||
import { ConditionallyRender } from '../../../../../../common/ConditionallyRender/ConditionallyRender';
|
||||
import { StrategySeparator } from '../../../../../../common/StrategySeparator/StrategySeparator';
|
||||
import { ConstraintAccordionView } from '../../../../../../common/ConstraintAccordion/ConstraintAccordionView/ConstraintAccordionView';
|
||||
import { styled } from '@mui/material';
|
||||
|
||||
interface PlaygroundResultConstraintExecutionProps {
|
||||
constraints?: PlaygroundFeatureStrategyConstraintResult[];
|
||||
compact: boolean;
|
||||
input?: PlaygroundRequestSchema;
|
||||
}
|
||||
|
||||
export const PlaygroundResultConstraintExecutionWrapper = styled('div')(
|
||||
@ -23,18 +25,9 @@ export const PlaygroundResultConstraintExecutionWrapper = styled('div')(
|
||||
|
||||
export const PlaygroundResultConstraintExecution = ({
|
||||
constraints,
|
||||
compact,
|
||||
input,
|
||||
}: PlaygroundResultConstraintExecutionProps) => {
|
||||
// const context = usePlaygroundContext();
|
||||
const contextFalse: SdkContextSchema = {
|
||||
appName: 'MyApp',
|
||||
environment: '',
|
||||
};
|
||||
|
||||
const contextTrue: SdkContextSchema = {
|
||||
appName: 'MyApp',
|
||||
environment: 'development',
|
||||
};
|
||||
|
||||
if (!constraints) return null;
|
||||
|
||||
return (
|
||||
@ -47,12 +40,8 @@ export const PlaygroundResultConstraintExecution = ({
|
||||
/>
|
||||
<ConstraintAccordionView
|
||||
constraint={constraint}
|
||||
playgroundContext={
|
||||
Boolean(constraint.result)
|
||||
? contextTrue
|
||||
: contextFalse
|
||||
}
|
||||
maxLength={80}
|
||||
playgroundInput={input}
|
||||
maxLength={compact ? 25 : 50}
|
||||
sx={{
|
||||
backgroundColor: 'transparent!important',
|
||||
}}
|
@ -28,6 +28,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
[theme.breakpoints.down(400)]: {
|
||||
padding: '0.5rem',
|
||||
},
|
||||
width: '100%',
|
||||
paddingBottom: '1rem',
|
||||
borderRadius: theme.shape.borderRadiusMedium,
|
||||
background: theme.palette.background.default,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Box, useTheme } from '@mui/material';
|
||||
import { Box, styled, Typography, useTheme } from '@mui/material';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { StrategySeparator } from 'component/common/StrategySeparator/StrategySeparator';
|
||||
import {
|
||||
@ -7,18 +7,33 @@ import {
|
||||
} from 'utils/strategyNames';
|
||||
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
|
||||
import { PlaygroundResultChip } from '../../PlaygroundResultChip/PlaygroundResultChip';
|
||||
import { PlaygroundFeatureStrategyResult } from 'hooks/api/actions/usePlayground/playground.model';
|
||||
import { PlaygroundResultStrategyExecution } from '../PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution';
|
||||
import {
|
||||
PlaygroundFeatureStrategyResult,
|
||||
PlaygroundRequestSchema,
|
||||
} from 'hooks/api/actions/usePlayground/playground.model';
|
||||
import { PlaygroundResultStrategyExecution } from './PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution';
|
||||
import { useStyles } from './PlaygroundResultFeatureStrategyItem.styles';
|
||||
|
||||
interface IPlaygroundResultFeatureStrategyItemProps {
|
||||
strategy: PlaygroundFeatureStrategyResult;
|
||||
index: number;
|
||||
input?: PlaygroundRequestSchema;
|
||||
compact: boolean;
|
||||
}
|
||||
|
||||
const StyledItemWrapper = styled('div')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginTop: '4px',
|
||||
gap: '4px',
|
||||
}));
|
||||
|
||||
export const PlaygroundResultFeatureStrategyItem = ({
|
||||
strategy,
|
||||
input,
|
||||
index,
|
||||
compact,
|
||||
}: IPlaygroundResultFeatureStrategyItemProps) => {
|
||||
const { result, name } = strategy;
|
||||
const { classes: styles } = useStyles();
|
||||
@ -31,34 +46,44 @@ export const PlaygroundResultFeatureStrategyItem = ({
|
||||
: `1px solid ${theme.palette.divider}`;
|
||||
|
||||
return (
|
||||
<Box key={strategy.id} sx={{ width: '100%', position: 'relative' }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: '100%',
|
||||
position: 'relative',
|
||||
paddingRight: compact ? '12px' : 0,
|
||||
}}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={index > 0}
|
||||
show={<StrategySeparator text="OR" />}
|
||||
/>
|
||||
<Box className={styles.innerContainer} sx={{ border }}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.headerName}>
|
||||
<Icon className={styles.icon} />
|
||||
<StringTruncator
|
||||
maxWidth="150"
|
||||
maxLength={15}
|
||||
text={formatStrategyName(name)}
|
||||
<StyledItemWrapper>
|
||||
<Typography variant={'subtitle1'}>{index + 1}</Typography>
|
||||
<Box className={styles.innerContainer} sx={{ border }}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.headerName}>
|
||||
<Icon className={styles.icon} />
|
||||
<StringTruncator
|
||||
maxWidth="150"
|
||||
maxLength={15}
|
||||
text={formatStrategyName(name)}
|
||||
/>
|
||||
</div>
|
||||
<PlaygroundResultChip
|
||||
showIcon={false}
|
||||
enabled={Boolean(result)}
|
||||
label={label}
|
||||
/>
|
||||
</div>
|
||||
<PlaygroundResultChip
|
||||
showIcon={false}
|
||||
enabled={Boolean(result)}
|
||||
label={label}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.body}>
|
||||
<PlaygroundResultStrategyExecution
|
||||
strategyResult={strategy}
|
||||
percentageFill={theme.palette.tertiary.light}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
<div className={styles.body}>
|
||||
<PlaygroundResultStrategyExecution
|
||||
strategyResult={strategy}
|
||||
input={input}
|
||||
percentageFill={theme.palette.tertiary.light}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
</StyledItemWrapper>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@ -1,14 +1,19 @@
|
||||
import { PlaygroundFeatureStrategySegmentResult } from '../../../../../../hooks/api/actions/usePlayground/playground.model';
|
||||
import {
|
||||
PlaygroundFeatureStrategySegmentResult,
|
||||
PlaygroundRequestSchema,
|
||||
} from '../../../../../../../hooks/api/actions/usePlayground/playground.model';
|
||||
import { PlaygroundResultConstraintExecution } from '../PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution';
|
||||
import { CancelOutlined, DonutLarge } from '@mui/icons-material';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { StrategySeparator } from '../../../../../common/StrategySeparator/StrategySeparator';
|
||||
import { StrategySeparator } from '../../../../../../common/StrategySeparator/StrategySeparator';
|
||||
import { useStyles } from './PlaygroundResultSegmentExecution.styles';
|
||||
import { styled, Typography } from '@mui/material';
|
||||
import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender';
|
||||
import { ConditionallyRender } from '../../../../../../common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
interface PlaygroundResultSegmentExecutionProps {
|
||||
segments?: PlaygroundFeatureStrategySegmentResult[];
|
||||
input?: PlaygroundRequestSchema;
|
||||
hasConstraints: boolean;
|
||||
}
|
||||
|
||||
const SegmentExecutionLinkWrapper = styled('div')(({ theme }) => ({
|
||||
@ -55,8 +60,11 @@ const SegmentResultTextWrapper = styled('div')(({ theme }) => ({
|
||||
|
||||
export const PlaygroundResultSegmentExecution = ({
|
||||
segments,
|
||||
input,
|
||||
hasConstraints,
|
||||
}: PlaygroundResultSegmentExecutionProps) => {
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
if (!segments) return null;
|
||||
return (
|
||||
<>
|
||||
@ -93,10 +101,14 @@ export const PlaygroundResultSegmentExecution = ({
|
||||
<SegmentExecutionConstraintWrapper>
|
||||
<PlaygroundResultConstraintExecution
|
||||
constraints={segment.constraints}
|
||||
compact={true}
|
||||
input={input}
|
||||
/>
|
||||
</SegmentExecutionConstraintWrapper>
|
||||
<ConditionallyRender
|
||||
condition={index < segments?.length - 1}
|
||||
condition={
|
||||
index === segments?.length - 1 && hasConstraints
|
||||
}
|
||||
show={<StrategySeparator text="AND" sx={{ pt: 1 }} />}
|
||||
/>
|
||||
</SegmentExecutionWrapper>
|
@ -10,7 +10,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
color: theme.palette.grey[700],
|
||||
},
|
||||
summary: {
|
||||
width: '100%',
|
||||
width: 'auto',
|
||||
padding: theme.spacing(2, 3),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
@ -1,9 +1,12 @@
|
||||
import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender';
|
||||
import { StrategySeparator } from '../../../../../common/StrategySeparator/StrategySeparator';
|
||||
import { Box, Chip } from '@mui/material';
|
||||
import { ConditionallyRender } from '../../../../../../common/ConditionallyRender/ConditionallyRender';
|
||||
import { StrategySeparator } from '../../../../../../common/StrategySeparator/StrategySeparator';
|
||||
import { Box, Chip, styled } from '@mui/material';
|
||||
import { useStyles } from './PlaygroundResultStrategyExecution.styles';
|
||||
import { PlaygroundFeatureStrategyResult } from '../../../../../../hooks/api/actions/usePlayground/playground.model';
|
||||
import useUiConfig from '../../../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import {
|
||||
PlaygroundFeatureStrategyResult,
|
||||
PlaygroundRequestSchema,
|
||||
} from '../../../../../../../hooks/api/actions/usePlayground/playground.model';
|
||||
import useUiConfig from '../../../../../../../hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import React from 'react';
|
||||
import { PlaygroundResultConstraintExecution } from '../PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution';
|
||||
import { PlaygroundResultSegmentExecution } from '../PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution';
|
||||
@ -11,24 +14,38 @@ import { PlaygroundResultSegmentExecution } from '../PlaygroundResultSegmentExec
|
||||
interface PlaygroundResultStrategyExecutionProps {
|
||||
strategyResult: PlaygroundFeatureStrategyResult;
|
||||
percentageFill?: string;
|
||||
input?: PlaygroundRequestSchema;
|
||||
}
|
||||
|
||||
const StyledStrategyExecutionWrapper = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(1),
|
||||
}));
|
||||
|
||||
export const PlaygroundResultStrategyExecution = ({
|
||||
strategyResult,
|
||||
input,
|
||||
}: PlaygroundResultStrategyExecutionProps) => {
|
||||
const { name, constraints, segments } = strategyResult;
|
||||
|
||||
const { uiConfig } = useUiConfig();
|
||||
const { classes: styles } = useStyles();
|
||||
|
||||
const hasConstraints = Boolean(constraints && constraints.length > 0);
|
||||
|
||||
return (
|
||||
<>
|
||||
<StyledStrategyExecutionWrapper>
|
||||
<ConditionallyRender
|
||||
condition={
|
||||
Boolean(uiConfig.flags.SE) &&
|
||||
Boolean(segments && segments.length > 0)
|
||||
}
|
||||
show={<PlaygroundResultSegmentExecution segments={segments} />}
|
||||
show={
|
||||
<PlaygroundResultSegmentExecution
|
||||
segments={segments}
|
||||
hasConstraints={hasConstraints}
|
||||
input={input}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(constraints && constraints.length > 0)}
|
||||
@ -36,6 +53,8 @@ export const PlaygroundResultStrategyExecution = ({
|
||||
<>
|
||||
<PlaygroundResultConstraintExecution
|
||||
constraints={constraints}
|
||||
compact={true}
|
||||
input={input}
|
||||
/>
|
||||
<StrategySeparator text="AND" />
|
||||
</>
|
||||
@ -56,6 +75,6 @@ export const PlaygroundResultStrategyExecution = ({
|
||||
</Box>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
</StyledStrategyExecutionWrapper>
|
||||
);
|
||||
};
|
@ -0,0 +1,88 @@
|
||||
import {
|
||||
PlaygroundFeatureSchema,
|
||||
PlaygroundFeatureStrategyResult,
|
||||
PlaygroundRequestSchema,
|
||||
} from '../../../../../../hooks/api/actions/usePlayground/playground.model';
|
||||
import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender';
|
||||
import { Alert, styled, Typography } from '@mui/material';
|
||||
import { PlaygroundResultFeatureStrategyItem } from '../PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem';
|
||||
|
||||
interface PlaygroundResultStrategyListProps {
|
||||
strategies: PlaygroundFeatureStrategyResult[];
|
||||
input?: PlaygroundRequestSchema;
|
||||
compact?: boolean;
|
||||
}
|
||||
|
||||
export const PlaygroundResultStrategyList = ({
|
||||
strategies,
|
||||
input,
|
||||
compact = false,
|
||||
}: PlaygroundResultStrategyListProps) => {
|
||||
return (
|
||||
<ConditionallyRender
|
||||
condition={strategies.length > 0}
|
||||
show={
|
||||
<>
|
||||
<Typography
|
||||
variant={'subtitle1'}
|
||||
sx={{ mt: 2, ml: 1, mb: 2, color: 'text.secondary' }}
|
||||
>{`Strategies (${strategies.length})`}</Typography>
|
||||
{strategies.map((strategy, index) => (
|
||||
<PlaygroundResultFeatureStrategyItem
|
||||
key={strategy.id}
|
||||
strategy={strategy}
|
||||
index={index}
|
||||
compact={compact}
|
||||
input={input}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const StyledAlertWrapper = styled('div')(({ theme }) => ({
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
borderRadius: theme.shape.borderRadiusMedium,
|
||||
border: `1px solid ${theme.palette.info.main}`,
|
||||
}));
|
||||
|
||||
const StyledListWrapper = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(1, 0.5),
|
||||
}));
|
||||
|
||||
const StyledAlert = styled(Alert)(({ theme }) => ({
|
||||
borderBottomLeftRadius: 0,
|
||||
borderBottomRightRadius: 0,
|
||||
}));
|
||||
|
||||
interface WrappedPlaygroundResultStrategyListProps
|
||||
extends PlaygroundResultStrategyListProps {
|
||||
feature: PlaygroundFeatureSchema;
|
||||
}
|
||||
|
||||
export const WrappedPlaygroundResultStrategyList = ({
|
||||
strategies,
|
||||
feature,
|
||||
input,
|
||||
}: WrappedPlaygroundResultStrategyListProps) => {
|
||||
return (
|
||||
<StyledAlertWrapper>
|
||||
<StyledAlert severity={'info'} color={'info'}>
|
||||
If environment would be enabled then this feature would be{' '}
|
||||
{feature.isEnabled ? 'TRUE' : 'FALSE'} and the strategies would
|
||||
evaluate like this:{' '}
|
||||
</StyledAlert>
|
||||
<StyledListWrapper>
|
||||
<PlaygroundResultStrategyList
|
||||
strategies={strategies}
|
||||
input={input}
|
||||
compact
|
||||
/>
|
||||
</StyledListWrapper>
|
||||
</StyledAlertWrapper>
|
||||
);
|
||||
};
|
@ -6,7 +6,7 @@ import { ReactComponent as FeatureEnabledIcon } from '../../../../../assets/icon
|
||||
import { ReactComponent as FeatureDisabledIcon } from '../../../../../assets/icons/isenabled-false.svg';
|
||||
|
||||
interface IResultChipProps {
|
||||
enabled: boolean;
|
||||
enabled: boolean | 'unevaluated';
|
||||
// Result icon - defaults to true
|
||||
showIcon?: boolean;
|
||||
label?: string;
|
||||
@ -53,7 +53,7 @@ export const PlaygroundResultChip = ({
|
||||
const theme = useTheme();
|
||||
const icon = (
|
||||
<ConditionallyRender
|
||||
condition={enabled}
|
||||
condition={Boolean(enabled)}
|
||||
show={
|
||||
<FeatureEnabledIcon
|
||||
color={theme.palette.success.main}
|
||||
@ -73,7 +73,7 @@ export const PlaygroundResultChip = ({
|
||||
|
||||
return (
|
||||
<ConditionallyRender
|
||||
condition={enabled}
|
||||
condition={Boolean(enabled)}
|
||||
show={
|
||||
<StyledTrueChip
|
||||
icon={showIcon ? icon : undefined}
|
||||
|
@ -19,7 +19,10 @@ import { LinkCell } from 'component/common/Table/cells/LinkCell/LinkCell';
|
||||
import { useSearch } from 'hooks/useSearch';
|
||||
import { createLocalStorage } from 'utils/createLocalStorage';
|
||||
import { FeatureStatusCell } from './FeatureStatusCell/FeatureStatusCell';
|
||||
import { PlaygroundFeatureSchema } from 'hooks/api/actions/usePlayground/playground.model';
|
||||
import {
|
||||
PlaygroundFeatureSchema,
|
||||
PlaygroundRequestSchema,
|
||||
} from 'hooks/api/actions/usePlayground/playground.model';
|
||||
import { Box, Typography, useMediaQuery, useTheme } from '@mui/material';
|
||||
import useLoading from 'hooks/useLoading';
|
||||
import { VariantCell } from './VariantCell/VariantCell';
|
||||
@ -33,11 +36,13 @@ const { value, setValue } = createLocalStorage(
|
||||
|
||||
interface IPlaygroundResultsTableProps {
|
||||
features?: PlaygroundFeatureSchema[];
|
||||
input?: PlaygroundRequestSchema;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export const PlaygroundResultsTable = ({
|
||||
features,
|
||||
input,
|
||||
loading,
|
||||
}: IPlaygroundResultsTableProps) => {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
@ -49,6 +54,75 @@ export const PlaygroundResultsTable = ({
|
||||
const isExtraSmallScreen = useMediaQuery(theme.breakpoints.down('sm'));
|
||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||
|
||||
const COLUMNS = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
searchable: true,
|
||||
minWidth: 160,
|
||||
Cell: ({ value, row: { original } }: any) => (
|
||||
<LinkCell
|
||||
title={value}
|
||||
to={`/projects/${original?.projectId}/features/${value}`}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: 'Project ID',
|
||||
accessor: 'projectId',
|
||||
sortType: 'alphanumeric',
|
||||
filterName: 'projectId',
|
||||
searchable: true,
|
||||
maxWidth: 170,
|
||||
Cell: ({ value }: any) => (
|
||||
<LinkCell title={value} to={`/projects/${value}`} />
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: 'Variant',
|
||||
id: 'variant',
|
||||
accessor: 'variant.name',
|
||||
sortType: 'alphanumeric',
|
||||
filterName: 'variant',
|
||||
searchable: true,
|
||||
width: 200,
|
||||
Cell: ({
|
||||
value,
|
||||
row: {
|
||||
original: { variant, feature, variants, isEnabled },
|
||||
},
|
||||
}: any) => (
|
||||
<VariantCell
|
||||
variant={variant?.enabled ? value : ''}
|
||||
variants={variants}
|
||||
feature={feature}
|
||||
isEnabled={isEnabled}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: 'isEnabled',
|
||||
accessor: 'isEnabled',
|
||||
filterName: 'isEnabled',
|
||||
filterParsing: (value: boolean) => (value ? 'true' : 'false'),
|
||||
Cell: ({ value }: any) => <FeatureStatusCell enabled={value} />,
|
||||
sortType: 'boolean',
|
||||
sortInverted: true,
|
||||
},
|
||||
{
|
||||
Header: '',
|
||||
id: 'info',
|
||||
Cell: ({ row }: any) => (
|
||||
<FeatureResultInfoPopoverCell
|
||||
feature={row.original}
|
||||
input={input}
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
}, [input]);
|
||||
|
||||
const {
|
||||
data: searchedData,
|
||||
getSearchText,
|
||||
@ -236,67 +310,3 @@ export const PlaygroundResultsTable = ({
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const COLUMNS = [
|
||||
{
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
searchable: true,
|
||||
minWidth: 160,
|
||||
Cell: ({ value, row: { original } }: any) => (
|
||||
<LinkCell
|
||||
title={value}
|
||||
to={`/projects/${original?.projectId}/features/${value}`}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: 'Project ID',
|
||||
accessor: 'projectId',
|
||||
sortType: 'alphanumeric',
|
||||
filterName: 'projectId',
|
||||
searchable: true,
|
||||
maxWidth: 170,
|
||||
Cell: ({ value }: any) => (
|
||||
<LinkCell title={value} to={`/projects/${value}`} />
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: 'Variant',
|
||||
id: 'variant',
|
||||
accessor: 'variant.name',
|
||||
sortType: 'alphanumeric',
|
||||
filterName: 'variant',
|
||||
searchable: true,
|
||||
width: 200,
|
||||
Cell: ({
|
||||
value,
|
||||
row: {
|
||||
original: { variant, feature, variants, isEnabled },
|
||||
},
|
||||
}: any) => (
|
||||
<VariantCell
|
||||
variant={variant?.enabled ? value : ''}
|
||||
variants={variants}
|
||||
feature={feature}
|
||||
isEnabled={isEnabled}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: 'isEnabled',
|
||||
accessor: 'isEnabled',
|
||||
filterName: 'isEnabled',
|
||||
filterParsing: (value: boolean) => (value ? 'true' : 'false'),
|
||||
Cell: ({ value }: any) => <FeatureStatusCell enabled={value} />,
|
||||
sortType: 'boolean',
|
||||
sortInverted: true,
|
||||
},
|
||||
{
|
||||
Header: '',
|
||||
id: 'info',
|
||||
Cell: ({ row }: any) => (
|
||||
<FeatureResultInfoPopoverCell feature={row.original} />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
@ -65,13 +65,17 @@ export interface PlaygroundFeatureSchema {
|
||||
* @type {boolean}
|
||||
* @memberof PlaygroundFeatureSchema
|
||||
*/
|
||||
isEnabled: boolean;
|
||||
isEnabled: boolean | 'unevaluated';
|
||||
|
||||
isEnabledInCurrentEnvironment: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {PlaygroundFeatureSchemaVariant}
|
||||
* @memberof PlaygroundFeatureSchema
|
||||
*/
|
||||
variant: PlaygroundFeatureSchemaVariant | null;
|
||||
|
||||
strategies: PlaygroundFeatureStrategyResult[];
|
||||
}
|
||||
export interface PlaygroundResponseSchema {
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user