mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	Playground result segments, constraints initial
This commit is contained in:
		
							parent
							
								
									352a4cca13
								
							
						
					
					
						commit
						9e38cf3ff9
					
				| @ -1,5 +1,11 @@ | ||||
| import { useState } from 'react'; | ||||
| import { Accordion, AccordionSummary, AccordionDetails } from '@mui/material'; | ||||
| import { | ||||
|     Accordion, | ||||
|     AccordionSummary, | ||||
|     AccordionDetails, | ||||
|     SxProps, | ||||
|     Theme, | ||||
| } from '@mui/material'; | ||||
| import { IConstraint } from 'interfaces/strategy'; | ||||
| import { ConstraintAccordionViewBody } from './ConstraintAccordionViewBody/ConstraintAccordionViewBody'; | ||||
| import { ConstraintAccordionViewHeader } from './ConstraintAccordionViewHeader/ConstraintAccordionViewHeader'; | ||||
| @ -15,12 +21,14 @@ interface IConstraintAccordionViewProps { | ||||
|     constraint: IConstraint; | ||||
|     onDelete?: () => void; | ||||
|     onEdit?: () => void; | ||||
|     sx?: SxProps<Theme>; | ||||
| } | ||||
| 
 | ||||
| export const ConstraintAccordionView = ({ | ||||
|     constraint, | ||||
|     onEdit, | ||||
|     onDelete, | ||||
|     sx = undefined, | ||||
| }: IConstraintAccordionViewProps) => { | ||||
|     const { classes: styles } = useStyles(); | ||||
|     const [expandable, setExpandable] = useState(true); | ||||
| @ -42,6 +50,7 @@ export const ConstraintAccordionView = ({ | ||||
|             className={styles.accordion} | ||||
|             classes={{ root: styles.accordionRoot }} | ||||
|             expanded={expanded} | ||||
|             sx={sx} | ||||
|         > | ||||
|             <AccordionSummary | ||||
|                 classes={{ root: styles.summary }} | ||||
|  | ||||
| @ -1,8 +1,9 @@ | ||||
| import { styled } from '@mui/material'; | ||||
| import { styled, SxProps, Theme } from '@mui/material'; | ||||
| import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender'; | ||||
| 
 | ||||
| interface IStrategySeparatorProps { | ||||
|     text: 'AND' | 'OR'; | ||||
|     sx?: SxProps<Theme>; | ||||
| } | ||||
| 
 | ||||
| const StyledContainer = styled('div')(({ theme }) => ({ | ||||
| @ -32,8 +33,8 @@ const StyledCenteredContent = styled(StyledContent)(({ theme }) => ({ | ||||
|     border: `1px solid ${theme.palette.primary.border}`, | ||||
| })); | ||||
| 
 | ||||
| export const StrategySeparator = ({ text }: IStrategySeparatorProps) => ( | ||||
|     <StyledContainer> | ||||
| export const StrategySeparator = ({ text, sx }: IStrategySeparatorProps) => ( | ||||
|     <StyledContainer sx={sx}> | ||||
|         <ConditionallyRender | ||||
|             condition={text === 'AND'} | ||||
|             show={() => <StyledContent>{text}</StyledContent>} | ||||
|  | ||||
| @ -9,6 +9,7 @@ export const useStyles = makeStyles()(theme => ({ | ||||
|         gap: '24px', | ||||
|         width: '728px', | ||||
|         height: 'auto', | ||||
|         // overflowY: 'scroll',
 | ||||
|         overflowY: 'scroll', | ||||
|         backgroundColor: theme.palette.tertiary.light, | ||||
|     }, | ||||
| })); | ||||
|  | ||||
| @ -2,13 +2,10 @@ import { | ||||
|     PlaygroundFeatureSchema, | ||||
|     PlaygroundFeatureStrategyResult, | ||||
| } from '../../../../../hooks/api/actions/usePlayground/playground.model'; | ||||
| import { Box, IconButton, Popover, Typography } from '@mui/material'; | ||||
| import { IconButton, Popover, styled, Typography } from '@mui/material'; | ||||
| import { InfoOutlined } from '@mui/icons-material'; | ||||
| import { IconCell } from '../../../../common/Table/cells/IconCell/IconCell'; | ||||
| import React, { useRef, useState } from 'react'; | ||||
| import { ConditionallyRender } from '../../../../common/ConditionallyRender/ConditionallyRender'; | ||||
| import { StrategyDraggableItem } from '../../../../feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyDraggableItem'; | ||||
| import { FeatureStrategyEmpty } from '../../../../feature/FeatureStrategy/FeatureStrategyEmpty/FeatureStrategyEmpty'; | ||||
| import { PlaygroundResultFeatureStrategyItem } from './PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem'; | ||||
| import { useStyles } from './FeatureResultInfoPopoverCell.styles'; | ||||
| import { PlaygroundResultFeatureDetails } from './PlaygroundResultFeatureDetails/PlaygroundResultFeatureDetails'; | ||||
| @ -17,12 +14,14 @@ interface FeatureResultInfoPopoverCellProps { | ||||
|     feature?: PlaygroundFeatureSchema; | ||||
| } | ||||
| 
 | ||||
| const FeatureResultPopoverWrapper = styled('div')(({ theme }) => ({ | ||||
|     alignItems: 'flex-end', | ||||
|     color: theme.palette.tertiary.main, | ||||
| })); | ||||
| 
 | ||||
| export const FeatureResultInfoPopoverCell = ({ | ||||
|     feature, | ||||
| }: FeatureResultInfoPopoverCellProps) => { | ||||
|     if (!feature) { | ||||
|         return null; | ||||
|     } | ||||
|     const [open, setOpen] = useState(false); | ||||
|     const { classes: styles } = useStyles(); | ||||
|     const ref = useRef(null); | ||||
| @ -36,7 +35,7 @@ export const FeatureResultInfoPopoverCell = ({ | ||||
|             name: 'default', | ||||
|             id: 'strategy-id', | ||||
|             parameters: {}, | ||||
|             result: false, | ||||
|             result: true, | ||||
|             constraints: [ | ||||
|                 { | ||||
|                     result: false, | ||||
| @ -44,7 +43,12 @@ export const FeatureResultInfoPopoverCell = ({ | ||||
|                     operator: 'IN', | ||||
|                     caseInsensitive: true, | ||||
|                     inverted: false, | ||||
|                     values: ['a', 'b'], | ||||
|                     values: [ | ||||
|                         'a', | ||||
|                         'b', | ||||
|                         'sdlghigoiahr;g', | ||||
|                         'WOGIHwegoihwlwEGHLwgklWEGK;L', | ||||
|                     ], | ||||
|                 }, | ||||
|             ], | ||||
|             segments: [ | ||||
| @ -55,9 +59,9 @@ export const FeatureResultInfoPopoverCell = ({ | ||||
|                     constraints: [ | ||||
|                         { | ||||
|                             result: false, | ||||
|                             contextName: 'appName', | ||||
|                             contextName: 'environment', | ||||
|                             operator: 'IN', | ||||
|                             caseInsensitive: true, | ||||
|                             caseInsensitive: false, | ||||
|                             inverted: false, | ||||
|                             values: ['a', 'b'], | ||||
|                         }, | ||||
| @ -67,8 +71,12 @@ export const FeatureResultInfoPopoverCell = ({ | ||||
|         }, | ||||
|     ]; | ||||
| 
 | ||||
|     if (!feature) { | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|         <> | ||||
|         <FeatureResultPopoverWrapper> | ||||
|             <IconButton onClick={togglePopover}> | ||||
|                 <InfoOutlined ref={ref} /> | ||||
|             </IconButton> | ||||
| @ -92,7 +100,7 @@ export const FeatureResultInfoPopoverCell = ({ | ||||
|                     show={ | ||||
|                         <> | ||||
|                             <Typography | ||||
|                                 variant={'subtitle2'} | ||||
|                                 variant={'subtitle1'} | ||||
|                             >{`Strategies (${strategies.length})`}</Typography> | ||||
|                             {strategies.map((strategy, index) => ( | ||||
|                                 <PlaygroundResultFeatureStrategyItem | ||||
| @ -105,6 +113,6 @@ export const FeatureResultInfoPopoverCell = ({ | ||||
|                     } | ||||
|                 /> | ||||
|             </Popover> | ||||
|         </> | ||||
|         </FeatureResultPopoverWrapper> | ||||
|     ); | ||||
| }; | ||||
|  | ||||
| @ -0,0 +1,35 @@ | ||||
| import { PlaygroundFeatureStrategyConstraintResult } 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'; | ||||
| 
 | ||||
| interface PlaygroundResultConstraintExecutionProps { | ||||
|     constraints?: PlaygroundFeatureStrategyConstraintResult[]; | ||||
| } | ||||
| 
 | ||||
| export const PlaygroundResultConstraintExecution = ({ | ||||
|     constraints, | ||||
| }: PlaygroundResultConstraintExecutionProps) => { | ||||
|     if (!constraints) return null; | ||||
| 
 | ||||
|     return ( | ||||
|         <> | ||||
|             {constraints?.map((constraint, index) => ( | ||||
|                 <Fragment key={objectId(constraint)}> | ||||
|                     <ConditionallyRender | ||||
|                         condition={index > 0} | ||||
|                         show={<StrategySeparator text="AND" />} | ||||
|                     /> | ||||
|                     <ConstraintAccordionView | ||||
|                         constraint={constraint} | ||||
|                         sx={{ | ||||
|                             backgroundColor: 'transparent!important', | ||||
|                         }} | ||||
|                     /> | ||||
|                 </Fragment> | ||||
|             ))} | ||||
|         </> | ||||
|     ); | ||||
| }; | ||||
| @ -8,8 +8,8 @@ import { | ||||
| } from '../../../../../../utils/strategyNames'; | ||||
| import StringTruncator from '../../../../../common/StringTruncator/StringTruncator'; | ||||
| import { PlaygroundResultChip } from '../../PlaygroundResultChip/PlaygroundResultChip'; | ||||
| import { StrategyExecution } from '../../../../../feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution'; | ||||
| import { PlaygroundFeatureStrategyResult } from 'hooks/api/actions/usePlayground/playground.model'; | ||||
| import { PlaygroundResultStrategyExecution } from '../PlaygroundResultStrategyExecution/PlaygroundResultStrategyExecution'; | ||||
| 
 | ||||
| interface IPlaygroundResultFeatureStrategyItemProps { | ||||
|     strategy: PlaygroundFeatureStrategyResult; | ||||
| @ -31,14 +31,14 @@ export const PlaygroundResultFeatureStrategyItem = ({ | ||||
|     strategy, | ||||
|     index, | ||||
| }: IPlaygroundResultFeatureStrategyItemProps) => { | ||||
|     const { result } = strategy; | ||||
|     const { result, name } = strategy; | ||||
|     const { classes: styles } = useStyles(); | ||||
|     const theme = useTheme(); | ||||
|     const Icon = getFeatureStrategyIcon(strategy.name); | ||||
|     const label = | ||||
|         result === undefined ? 'Not found' : result ? 'True' : 'False'; | ||||
|     const border = Boolean(result) | ||||
|         ? `2px solid ${theme.palette.success.main}` | ||||
|         ? `1px solid ${theme.palette.success.main}` | ||||
|         : `1px solid ${theme.palette.divider}`; | ||||
| 
 | ||||
|     return ( | ||||
| @ -54,7 +54,7 @@ export const PlaygroundResultFeatureStrategyItem = ({ | ||||
|                         <StringTruncator | ||||
|                             maxWidth="150" | ||||
|                             maxLength={15} | ||||
|                             text={formatStrategyName(strategy.name)} | ||||
|                             text={formatStrategyName(name)} | ||||
|                         /> | ||||
|                     </div> | ||||
|                     <PlaygroundResultChip | ||||
| @ -64,9 +64,9 @@ export const PlaygroundResultFeatureStrategyItem = ({ | ||||
|                     /> | ||||
|                 </div> | ||||
|                 <div className={styles.body}> | ||||
|                     <StrategyExecution | ||||
|                         strategy={strategy} | ||||
|                         percentageFill={theme.palette.grey[200]} | ||||
|                     <PlaygroundResultStrategyExecution | ||||
|                         strategyResult={strategy} | ||||
|                         percentageFill={theme.palette.tertiary.light} | ||||
|                     /> | ||||
|                 </div> | ||||
|             </div> | ||||
|  | ||||
| @ -0,0 +1,12 @@ | ||||
| import { makeStyles } from 'tss-react/mui'; | ||||
| 
 | ||||
| export const useStyles = makeStyles()(theme => ({ | ||||
|     container: {}, | ||||
|     link: { | ||||
|         textDecoration: 'none', | ||||
|         marginLeft: theme.spacing(1), | ||||
|         '&:hover': { | ||||
|             textDecoration: 'underline', | ||||
|         }, | ||||
|     }, | ||||
| })); | ||||
| @ -0,0 +1,103 @@ | ||||
| import { PlaygroundFeatureStrategySegmentResult } 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 { useStyles } from './PlaygroundResultSegmentExecution.styles'; | ||||
| import { styled, Typography } from '@mui/material'; | ||||
| import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender'; | ||||
| 
 | ||||
| interface PlaygroundResultSegmentExecutionProps { | ||||
|     segments?: PlaygroundFeatureStrategySegmentResult[]; | ||||
| } | ||||
| 
 | ||||
| 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: '8px', | ||||
| })); | ||||
| 
 | ||||
| const SegmentExecutionConstraintWrapper = styled('div')(({ theme }) => ({ | ||||
|     padding: '12px', | ||||
| })); | ||||
| 
 | ||||
| const SegmentResultTextWrapper = styled('div')(({ theme }) => ({ | ||||
|     color: theme.palette.error.main, | ||||
|     display: 'inline-flex', | ||||
|     justifyContent: 'center', | ||||
|     marginRight: '12px', | ||||
|     gap: '8px', | ||||
| })); | ||||
| 
 | ||||
| export const PlaygroundResultSegmentExecution = ({ | ||||
|     segments, | ||||
| }: PlaygroundResultSegmentExecutionProps) => { | ||||
|     const { classes: styles } = useStyles(); | ||||
|     if (!segments) return null; | ||||
|     return ( | ||||
|         <> | ||||
|             {segments.map(segment => ( | ||||
|                 <SegmentExecutionWrapper key={segment.id}> | ||||
|                     <SegmentExecutionHeader> | ||||
|                         <SegmentExecutionLinkWrapper> | ||||
|                             <DonutLarge color="secondary" sx={{ mr: 1 }} />{' '} | ||||
|                             Segment:{' '} | ||||
|                             <Link | ||||
|                                 to={`/segments/edit/${segment.id}`} | ||||
|                                 className={styles.link} | ||||
|                             > | ||||
|                                 {segment.name} | ||||
|                             </Link> | ||||
|                         </SegmentExecutionLinkWrapper> | ||||
|                         <ConditionallyRender | ||||
|                             condition={!Boolean(segment.result)} | ||||
|                             show={ | ||||
|                                 <SegmentResultTextWrapper> | ||||
|                                     <Typography | ||||
|                                         variant={'subtitle2'} | ||||
|                                         sx={{ pt: 0.25 }} | ||||
|                                     > | ||||
|                                         segment is false | ||||
|                                     </Typography> | ||||
|                                     <span> | ||||
|                                         <CancelOutlined /> | ||||
|                                     </span> | ||||
|                                 </SegmentResultTextWrapper> | ||||
|                             } | ||||
|                         /> | ||||
|                     </SegmentExecutionHeader> | ||||
|                     <SegmentExecutionConstraintWrapper> | ||||
|                         <PlaygroundResultConstraintExecution | ||||
|                             constraints={segment.constraints} | ||||
|                         /> | ||||
|                     </SegmentExecutionConstraintWrapper> | ||||
|                     <StrategySeparator text="AND" sx={{ pt: 1 }} /> | ||||
|                 </SegmentExecutionWrapper> | ||||
|             ))} | ||||
|         </> | ||||
|     ); | ||||
| }; | ||||
| @ -0,0 +1,18 @@ | ||||
| import { makeStyles } from 'tss-react/mui'; | ||||
| 
 | ||||
| export const useStyles = makeStyles()(theme => ({ | ||||
|     valueContainer: { | ||||
|         display: 'flex', | ||||
|         alignItems: 'center', | ||||
|         gap: '1ch', | ||||
|     }, | ||||
|     valueSeparator: { | ||||
|         color: theme.palette.grey[700], | ||||
|     }, | ||||
|     summary: { | ||||
|         width: '100%', | ||||
|         padding: theme.spacing(2, 3), | ||||
|         borderRadius: theme.shape.borderRadius, | ||||
|         border: `1px solid ${theme.palette.divider}`, | ||||
|     }, | ||||
| })); | ||||
| @ -0,0 +1,61 @@ | ||||
| import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender'; | ||||
| import { StrategySeparator } from '../../../../../common/StrategySeparator/StrategySeparator'; | ||||
| import { Box, Chip } 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 React from 'react'; | ||||
| import { PlaygroundResultConstraintExecution } from '../PlaygroundResultConstraintExecution/PlaygroundResultConstraintExecution'; | ||||
| import { PlaygroundResultSegmentExecution } from '../PlaygroundResultSegmentExecution/PlaygroundResultSegmentExecution'; | ||||
| 
 | ||||
| interface PlaygroundResultStrategyExecutionProps { | ||||
|     strategyResult: PlaygroundFeatureStrategyResult; | ||||
|     percentageFill?: string; | ||||
| } | ||||
| 
 | ||||
| export const PlaygroundResultStrategyExecution = ({ | ||||
|     strategyResult, | ||||
| }: PlaygroundResultStrategyExecutionProps) => { | ||||
|     const { name, constraints, segments } = strategyResult; | ||||
| 
 | ||||
|     const { uiConfig } = useUiConfig(); | ||||
|     const { classes: styles } = useStyles(); | ||||
| 
 | ||||
|     return ( | ||||
|         <> | ||||
|             <ConditionallyRender | ||||
|                 condition={ | ||||
|                     Boolean(uiConfig.flags.SE) && | ||||
|                     Boolean(segments && segments.length > 0) | ||||
|                 } | ||||
|                 show={<PlaygroundResultSegmentExecution segments={segments} />} | ||||
|             /> | ||||
|             <ConditionallyRender | ||||
|                 condition={Boolean(constraints && constraints.length > 0)} | ||||
|                 show={ | ||||
|                     <> | ||||
|                         <PlaygroundResultConstraintExecution | ||||
|                             constraints={constraints} | ||||
|                         /> | ||||
|                         <StrategySeparator text="AND" /> | ||||
|                     </> | ||||
|                 } | ||||
|             /> | ||||
|             <ConditionallyRender | ||||
|                 condition={name === 'default'} | ||||
|                 show={ | ||||
|                     <Box sx={{ width: '100%' }} className={styles.summary}> | ||||
|                         The standard strategy is{' '} | ||||
|                         <Chip | ||||
|                             variant="outlined" | ||||
|                             size="small" | ||||
|                             color="success" | ||||
|                             label="ON" | ||||
|                         />{' '} | ||||
|                         for all users. | ||||
|                     </Box> | ||||
|                 } | ||||
|             /> | ||||
|         </> | ||||
|     ); | ||||
| }; | ||||
| @ -1,9 +1,5 @@ | ||||
| import React from 'react'; | ||||
| import { colors } from 'themes/colors'; | ||||
| import { ReactComponent as FeatureEnabledIcon } from 'assets/icons/isenabled-true.svg'; | ||||
| import { ReactComponent as FeatureDisabledIcon } from 'assets/icons/isenabled-false.svg'; | ||||
| import { Box, Chip, styled, useTheme } from '@mui/material'; | ||||
| import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; | ||||
| import { Box, styled } from '@mui/material'; | ||||
| import { PlaygroundResultChip } from '../PlaygroundResultChip/PlaygroundResultChip'; | ||||
| 
 | ||||
| interface IFeatureStatusCellProps { | ||||
|  | ||||
| @ -23,8 +23,6 @@ import { PlaygroundFeatureSchema } from 'hooks/api/actions/usePlayground/playgro | ||||
| import { Box, Typography, useMediaQuery, useTheme } from '@mui/material'; | ||||
| import useLoading from 'hooks/useLoading'; | ||||
| import { VariantCell } from './VariantCell/VariantCell'; | ||||
| import { IconCell } from '../../../common/Table/cells/IconCell/IconCell'; | ||||
| import { InfoOutlined } from '@mui/icons-material'; | ||||
| import { FeatureResultInfoPopoverCell } from './FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell'; | ||||
| 
 | ||||
| const defaultSort: SortingRule<string> = { id: 'name' }; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user