diff --git a/frontend/src/component/common/Table/cells/IconCell/IconCell.tsx b/frontend/src/component/common/Table/cells/IconCell/IconCell.tsx index 16527d50e0..2ad4f6a8db 100644 --- a/frontend/src/component/common/Table/cells/IconCell/IconCell.tsx +++ b/frontend/src/component/common/Table/cells/IconCell/IconCell.tsx @@ -3,19 +3,11 @@ import React, { ReactNode } from 'react'; interface IIconCellProps { icon: ReactNode; - onClick?: () => void; } -export const IconCell = ({ icon, onClick }: IIconCellProps) => { - const handleClick = - onClick && - ((event: React.SyntheticEvent) => { - event.stopPropagation(); - onClick(); - }); +export const IconCell = ({ icon }: IIconCellProps) => { return ( ({ marginLeft: 'auto', display: 'flex', }, + resultChip: { + marginLeft: 'auto', + }, body: { padding: theme.spacing(2), justifyItems: 'center', diff --git a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx index f44f82ce6a..30bc4d251a 100644 --- a/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx +++ b/frontend/src/component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem.tsx @@ -1,7 +1,7 @@ import { DragIndicator, Edit } from '@mui/icons-material'; -import { styled, useTheme, IconButton } from '@mui/material'; +import { styled, useTheme, IconButton, Chip } from '@mui/material'; import { Link } from 'react-router-dom'; -import { IFeatureStrategy } from 'interfaces/strategy'; +import {IFeatureStrategy, IPlaygroundFeatureStrategyResult} from 'interfaces/strategy'; import { getFeatureStrategyIcon, formatStrategyName, @@ -18,8 +18,10 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit interface IStrategyItemProps { environmentId: string; - strategy: IFeatureStrategy; + strategy: IFeatureStrategy | IPlaygroundFeatureStrategyResult; isDraggable?: boolean; + showActions?: boolean; + result?: boolean; } const DragIcon = styled(IconButton)(({ theme }) => ({ @@ -32,6 +34,8 @@ export const StrategyItem = ({ environmentId, strategy, isDraggable, + showActions = true, + result, }: IStrategyItemProps) => { const projectId = useRequiredPathParam('projectId'); const featureId = useRequiredPathParam('featureId'); @@ -46,6 +50,8 @@ export const StrategyItem = ({ strategy.id ); + const showShouldShowResultChip = result !== undefined; + return (
@@ -66,25 +72,36 @@ export const StrategyItem = ({ maxLength={15} text={formatStrategyName(strategy.name)} /> -
- - - - -
+ + + + + +
+ } + /> + + } + />
void; -} - -export const PlaygroundFeatureResultInfoModal = ({ - feature, - open, - setOpen, -}: PlaygroundFeatureResultInfoModalProps) => { - if (!feature) { - return null; - } - - return ( - setOpen(false)}> -

Test

-
- ); -}; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell.tsx new file mode 100644 index 0000000000..eb10bf808d --- /dev/null +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell.tsx @@ -0,0 +1,85 @@ +import { PlaygroundFeatureSchema } from '../../../../../hooks/api/actions/usePlayground/playground.model'; +import { Box, IconButton, Popover } from '@mui/material'; +import { InfoOutlined } from '@mui/icons-material'; +import { IconCell } from '../../../../common/Table/cells/IconCell/IconCell'; +import React, { useRef, useState } from 'react'; + +interface FeatureResultInfoPopoverCellProps { + feature?: PlaygroundFeatureSchema; +} + +export const FeatureResultInfoPopoverCell = ({ + feature, +}: FeatureResultInfoPopoverCellProps) => { + if (!feature) { + return null; + } + const [open, setOpen] = useState(false); + const ref = useRef(null); + + const togglePopover = (event: React.SyntheticEvent) => { + setOpen(!open); + }; + + const strategies = [ + { + type: 'standard', + id: 'strategy-id', + result: false, + constraints: [ + { + result: false, + contextName: 'appName', + operator: 'IN', + caseInsensitive: true, + inverted: false, + values: ['a', 'b'], + }, + ], + segments: [ + { + result: true, + id: 5, + name: 'my-segment', + constraints: [ + { + result: false, + contextName: 'appName', + operator: 'IN', + caseInsensitive: true, + inverted: false, + values: ['a', 'b'], + }, + ], + }, + ], + }, + { + type: 'default', + result: true, + }, + ]; + + return ( + <> + + + + setOpen(false)} + anchorEl={ref.current} + anchorOrigin={{ + vertical: 'top', + horizontal: 'right', + }} + transformOrigin={{ + vertical: 'center', + horizontal: 'left', + }} + > + {feature.name} + + + ); +}; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem.tsx new file mode 100644 index 0000000000..f890d4da16 --- /dev/null +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureResultInfoPopoverCell/PlaygroundResultFeatureStrategyItem/PlaygroundResultFeatureStrategyItem.tsx @@ -0,0 +1,38 @@ +import { ConditionallyRender } from '../../../../../common/ConditionallyRender/ConditionallyRender'; +import { StrategySeparator } from '../../../../../common/StrategySeparator/StrategySeparator'; +import { StrategyItem } from '../../../../../feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyItem'; +import { + IConstraint, + IFeatureStrategy, IPlaygroundFeatureStrategyResult, +} from '../../../../../../interfaces/strategy'; +import { ISegment } from '../../../../../../interfaces/segment'; + + +interface IPlaygroundResultFeatureStrategyItemProps { + strategy: IPlaygroundFeatureStrategyResult; + environmentName: string; + index: number; +} + +export const PlaygroundResultFeatureStrategyItem = ({ + strategy, + environmentName, + index, +}: IPlaygroundResultFeatureStrategyItemProps) => { + const { result } = strategy; + + return ( +
+ 0} + show={} + /> + +
+ ); +}; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureStatusCell/FeatureStatusCell.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureStatusCell/FeatureStatusCell.tsx index 9d7f99462c..7ead5073d1 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureStatusCell/FeatureStatusCell.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/FeatureStatusCell/FeatureStatusCell.tsx @@ -4,37 +4,12 @@ import { ReactComponent as FeatureEnabledIcon } from 'assets/icons/isenabled-tru 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 {ResultChip} from "../ResultChip/ResultChip"; interface IFeatureStatusCellProps { enabled: boolean; } -const StyledFalseChip = styled(Chip)(({ theme }) => ({ - width: 80, - borderRadius: '5px', - border: `1px solid ${theme.palette.error.main}`, - backgroundColor: colors.red['200'], - ['& .MuiChip-label']: { - color: theme.palette.error.main, - }, - ['& .MuiChip-icon']: { - color: theme.palette.error.main, - }, -})); - -const StyledTrueChip = styled(Chip)(({ theme }) => ({ - width: 80, - borderRadius: '5px', - border: `1px solid ${theme.palette.success.main}`, - backgroundColor: colors.green['100'], - ['& .MuiChip-label']: { - color: theme.palette.success.main, - }, - ['& .MuiChip-icon']: { - color: theme.palette.success.main, - }, -})); - const StyledCellBox = styled(Box)(({ theme }) => ({ display: 'flex', alignItems: 'center', @@ -70,11 +45,7 @@ export const FeatureStatusCell = ({ enabled }: IFeatureStatusCellProps) => { return ( - } - elseShow={} - /> + ); diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultsTable.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultsTable.tsx index f7dafea0a3..5337251325 100644 --- a/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultsTable.tsx +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/PlaygroundResultsTable.tsx @@ -25,7 +25,7 @@ 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 { PlaygroundFeatureResultInfoModal } from '../PlaygroundFeatureResultInfoModal/PlaygroundFeatureResultInfoModal'; +import { FeatureResultInfoPopoverCell } from './FeatureResultInfoPopoverCell/FeatureResultInfoPopoverCell'; const defaultSort: SortingRule = { id: 'name' }; const { value, setValue } = createLocalStorage( @@ -51,87 +51,11 @@ export const PlaygroundResultsTable = ({ const isExtraSmallScreen = useMediaQuery(theme.breakpoints.down('sm')); const isSmallScreen = useMediaQuery(theme.breakpoints.down('md')); - const [selectedFeature, setSelectedFeature] = useState< - PlaygroundFeatureSchema | undefined - >(); - const [showResultInfoModal, setShowResultInfoModal] = useState(false); - - const columns = useMemo( - () => [ - { - Header: 'Name', - accessor: 'name', - searchable: true, - minWidth: 160, - Cell: ({ value, row: { original } }: any) => ( - - ), - }, - { - Header: 'Project ID', - accessor: 'projectId', - sortType: 'alphanumeric', - filterName: 'projectId', - searchable: true, - maxWidth: 170, - Cell: ({ value }: any) => ( - - ), - }, - { - Header: 'Variant', - id: 'variant', - accessor: 'variant.name', - sortType: 'alphanumeric', - filterName: 'variant', - searchable: true, - width: 200, - Cell: ({ - value, - row: { - original: { variant, feature, variants, isEnabled }, - }, - }: any) => ( - - ), - }, - { - Header: 'isEnabled', - accessor: 'isEnabled', - filterName: 'isEnabled', - filterParsing: (value: boolean) => (value ? 'true' : 'false'), - Cell: ({ value }: any) => , - sortType: 'boolean', - sortInverted: true, - }, - { - Header: '', - id: 'info', - Cell: ({ row }: any) => ( - } - onClick={() => onFeatureResultInfoClick(row.original)} - /> - ), - }, - ], - //eslint-disable-next-line - [] - ); - const { data: searchedData, getSearchText, getSearchContext, - } = useSearch(columns, searchValue, features || []); + } = useSearch(COLUMNS, searchValue, features || []); const data = useMemo(() => { return loading @@ -166,7 +90,7 @@ export const PlaygroundResultsTable = ({ } = useTable( { initialState, - columns: columns as any, + columns: COLUMNS as any, data: data as any, sortTypes, autoResetGlobalFilter: false, @@ -217,18 +141,8 @@ export const PlaygroundResultsTable = ({ // eslint-disable-next-line react-hooks/exhaustive-deps -- don't re-render after search params change }, [loading, sortBy, searchValue]); - const onFeatureResultInfoClick = (feature: PlaygroundFeatureSchema) => { - setSelectedFeature(feature); - setShowResultInfoModal(true); - }; - return ( <> - ); }; + +const COLUMNS = [ + { + Header: 'Name', + accessor: 'name', + searchable: true, + minWidth: 160, + Cell: ({ value, row: { original } }: any) => ( + + ), + }, + { + Header: 'Project ID', + accessor: 'projectId', + sortType: 'alphanumeric', + filterName: 'projectId', + searchable: true, + maxWidth: 170, + Cell: ({ value }: any) => ( + + ), + }, + { + Header: 'Variant', + id: 'variant', + accessor: 'variant.name', + sortType: 'alphanumeric', + filterName: 'variant', + searchable: true, + width: 200, + Cell: ({ + value, + row: { + original: { variant, feature, variants, isEnabled }, + }, + }: any) => ( + + ), + }, + { + Header: 'isEnabled', + accessor: 'isEnabled', + filterName: 'isEnabled', + filterParsing: (value: boolean) => (value ? 'true' : 'false'), + Cell: ({ value }: any) => , + sortType: 'boolean', + sortInverted: true, + }, + { + Header: '', + id: 'info', + Cell: ({ row }: any) => ( + + ), + }, +]; diff --git a/frontend/src/component/playground/Playground/PlaygroundResultsTable/ResultChip/ResultChip.tsx b/frontend/src/component/playground/Playground/PlaygroundResultsTable/ResultChip/ResultChip.tsx new file mode 100644 index 0000000000..6ad427a2af --- /dev/null +++ b/frontend/src/component/playground/Playground/PlaygroundResultsTable/ResultChip/ResultChip.tsx @@ -0,0 +1,46 @@ +import {Box, Chip, styled} from "@mui/material"; +import {colors} from "../../../../../themes/colors"; +import {ConditionallyRender} from "../../../../common/ConditionallyRender/ConditionallyRender"; +import React, {ReactElement} from "react"; + +interface IResultChipProps { + enabled: boolean; + icon?: ReactElement; + label?: string; +} + +export const StyledFalseChip = styled(Chip)(({ theme }) => ({ + width: 80, + borderRadius: '5px', + border: `1px solid ${theme.palette.error.main}`, + backgroundColor: colors.red['200'], + ['& .MuiChip-label']: { + color: theme.palette.error.main, + }, + ['& .MuiChip-icon']: { + color: theme.palette.error.main, + }, +})); + +export const StyledTrueChip = styled(Chip)(({ theme }) => ({ + width: 80, + borderRadius: '5px', + border: `1px solid ${theme.palette.success.main}`, + backgroundColor: colors.green['100'], + ['& .MuiChip-label']: { + color: theme.palette.success.main, + }, + ['& .MuiChip-icon']: { + color: theme.palette.success.main, + }, +})); + +export const ResultChip = ({ enabled, icon, label}: IResultChipProps) => { + return ( + } + elseShow={} + /> + ); +} diff --git a/frontend/src/interfaces/strategy.ts b/frontend/src/interfaces/strategy.ts index a9564f1aca..89e170272b 100644 --- a/frontend/src/interfaces/strategy.ts +++ b/frontend/src/interfaces/strategy.ts @@ -1,4 +1,5 @@ import { Operator } from 'constants/operators'; +import {ISegment} from "./segment"; export interface IFeatureStrategy { id: string; @@ -56,3 +57,20 @@ export interface IFeatureStrategySortOrder { id: string; sortOrder: number; } + + +export interface IPlaygroundFeatureStrategyConstraintResult extends IConstraint { + result: boolean; +} + +export interface IPlaygroundFeatureStrategySegmentResult extends ISegment { + result: boolean; +} + +export interface IPlaygroundFeatureStrategyResult { + type: string; + result: boolean; + id?: string; + constraints?: IPlaygroundFeatureStrategyConstraintResult[]; + segments?: IPlaygroundFeatureStrategySegmentResult[]; +}