mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-18 13:48:58 +02:00
Chore(1-3825)/remove legacy strategy separator (#10137)
Deletes the legacy strategy separator and removes all references to it. Luckily, all references to the separator were in dangling files that could themselves be deleted directly.
This commit is contained in:
parent
74ae35298d
commit
b66cff9af5
@ -1,55 +0,0 @@
|
||||
// deprecated; remove with the `flagOverviewRedesign` flag
|
||||
import { Box, styled, useTheme } from '@mui/material';
|
||||
import { ConditionallyRender } from '../ConditionallyRender/ConditionallyRender.tsx';
|
||||
|
||||
interface IStrategySeparatorProps {
|
||||
text: 'AND' | 'OR';
|
||||
}
|
||||
|
||||
const StyledContent = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(0.75, 1),
|
||||
color: theme.palette.text.primary,
|
||||
fontSize: theme.fontSizes.smallerBody,
|
||||
backgroundColor: theme.palette.background.elevation2,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
position: 'absolute',
|
||||
zIndex: theme.zIndex.fab,
|
||||
top: '50%',
|
||||
left: theme.spacing(2),
|
||||
transform: 'translateY(-50%)',
|
||||
lineHeight: 1,
|
||||
}));
|
||||
|
||||
const StyledCenteredContent = styled(StyledContent)(({ theme }) => ({
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
backgroundColor: theme.palette.seen.primary,
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
padding: theme.spacing(0.75, 1.5),
|
||||
}));
|
||||
|
||||
/**
|
||||
* @deprecated remove with 'flagOverviewRedesign' flag. This pollutes a lot of places in the codebase 😞
|
||||
*/
|
||||
export const StrategySeparator = ({ text }: IStrategySeparatorProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
height: theme.spacing(text === 'AND' ? 1 : 1.5),
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={text === 'AND'}
|
||||
show={() => <StyledContent>{text}</StyledContent>}
|
||||
elseShow={() => (
|
||||
<StyledCenteredContent>{text}</StyledCenteredContent>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,33 +0,0 @@
|
||||
import { Fragment } from 'react';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
|
||||
import type { ISegment } from 'interfaces/segment';
|
||||
import { SegmentItem } from 'component/common/SegmentItem/SegmentItem';
|
||||
|
||||
interface IFeatureOverviewSegmentProps {
|
||||
segments?: ISegment[];
|
||||
disabled?: boolean | null;
|
||||
}
|
||||
|
||||
export const FeatureOverviewSegment = ({
|
||||
segments,
|
||||
disabled = false,
|
||||
}: IFeatureOverviewSegmentProps) => {
|
||||
if (!segments || segments.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{segments.map((segment, index) => (
|
||||
<Fragment key={segment.id}>
|
||||
<ConditionallyRender
|
||||
condition={index > 0}
|
||||
show={<StrategySeparator text='AND' />}
|
||||
/>
|
||||
<SegmentItem segment={segment} disabled={disabled} />
|
||||
</Fragment>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,97 +0,0 @@
|
||||
import { Box, styled, Typography, useTheme } from '@mui/material';
|
||||
import CancelOutlined from '@mui/icons-material/CancelOutlined';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import StringTruncator from 'component/common/StringTruncator/StringTruncator';
|
||||
|
||||
interface ICustomParameterItem {
|
||||
text: string;
|
||||
input?: string | null;
|
||||
isRequired?: boolean;
|
||||
}
|
||||
|
||||
const StyledWrapper = styled(Box)(({ theme }) => ({
|
||||
width: '100%',
|
||||
padding: theme.spacing(2, 3),
|
||||
borderRadius: theme.shape.borderRadiusMedium,
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 2,
|
||||
}));
|
||||
|
||||
export const CustomParameterItem = ({
|
||||
text,
|
||||
input = null,
|
||||
isRequired = false,
|
||||
}: ICustomParameterItem) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const color = input === null ? 'error' : 'neutral';
|
||||
const requiredError = isRequired && input === null;
|
||||
|
||||
return (
|
||||
<StyledWrapper>
|
||||
<Typography
|
||||
variant='subtitle1'
|
||||
color={theme.palette[color].main}
|
||||
sx={{ minWidth: 118 }}
|
||||
>
|
||||
{`${input === null ? 'no value' : input}`}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Box sx={{ flexGrow: 1 }}>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(requiredError)}
|
||||
show={
|
||||
<>
|
||||
<Typography
|
||||
component='span'
|
||||
color={theme.palette.error.main}
|
||||
>
|
||||
{' required parameter '}
|
||||
</Typography>
|
||||
<StringTruncator
|
||||
maxWidth='300'
|
||||
text={text}
|
||||
maxLength={50}
|
||||
/>
|
||||
<Typography
|
||||
component='span'
|
||||
color={theme.palette.error.main}
|
||||
>
|
||||
{' is not set '}
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
elseShow={
|
||||
<>
|
||||
<Typography
|
||||
component='span'
|
||||
color='text.disabled'
|
||||
>
|
||||
{' set on parameter '}
|
||||
</Typography>
|
||||
<StringTruncator
|
||||
maxWidth='300'
|
||||
text={text}
|
||||
maxLength={50}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(requiredError)}
|
||||
show={<CancelOutlined color={'error'} />}
|
||||
elseShow={<div />}
|
||||
/>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
@ -1,115 +0,0 @@
|
||||
import { Fragment, type VFC } from 'react';
|
||||
import {
|
||||
parseParameterNumber,
|
||||
parseParameterString,
|
||||
parseParameterStrings,
|
||||
} from 'utils/parseParameter';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
|
||||
import { useStrategies } from 'hooks/api/getters/useStrategies/useStrategies';
|
||||
import { CustomParameterItem } from './CustomParameterItem/CustomParameterItem.tsx';
|
||||
|
||||
interface ICustomStrategyProps {
|
||||
parameters: { [key: string]: string };
|
||||
strategyName: string;
|
||||
}
|
||||
|
||||
export const CustomStrategyParams: VFC<ICustomStrategyProps> = ({
|
||||
strategyName,
|
||||
parameters,
|
||||
}) => {
|
||||
const { strategies } = useStrategies();
|
||||
const definition = strategies.find((strategyDefinition) => {
|
||||
return strategyDefinition.name === strategyName;
|
||||
});
|
||||
|
||||
if (!definition?.editable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const items = definition?.parameters.map((param) => {
|
||||
const paramValue = parameters[param.name];
|
||||
const isRequired = param.required;
|
||||
|
||||
switch (param?.type) {
|
||||
case 'list': {
|
||||
const values = parseParameterStrings(paramValue);
|
||||
return (
|
||||
<CustomParameterItem
|
||||
isRequired={isRequired}
|
||||
text={param.name}
|
||||
input={values?.length > 0 ? values.join(', ') : null}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'percentage': {
|
||||
const percentage = parseParameterNumber(paramValue);
|
||||
const correctPercentage = !(
|
||||
paramValue === undefined ||
|
||||
paramValue === '' ||
|
||||
percentage < 0 ||
|
||||
percentage > 100
|
||||
);
|
||||
return (
|
||||
<CustomParameterItem
|
||||
text={param.name}
|
||||
isRequired={isRequired}
|
||||
input={correctPercentage ? `${percentage}%` : undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'boolean': {
|
||||
const bool = ['true', 'false'].includes(paramValue)
|
||||
? paramValue
|
||||
: undefined;
|
||||
return (
|
||||
<CustomParameterItem
|
||||
isRequired={isRequired}
|
||||
text={param.name}
|
||||
input={paramValue !== undefined ? bool : undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'string': {
|
||||
const value = parseParameterString(paramValue);
|
||||
return (
|
||||
<CustomParameterItem
|
||||
text={param.name}
|
||||
isRequired={isRequired}
|
||||
input={value !== undefined ? value : undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'number': {
|
||||
const isCorrect = !(
|
||||
paramValue === undefined || paramValue === ''
|
||||
);
|
||||
const number = parseParameterNumber(paramValue);
|
||||
return (
|
||||
<CustomParameterItem
|
||||
text={param.name}
|
||||
isRequired={isRequired}
|
||||
input={isCorrect ? `${number}` : undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'default':
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{items.map((item, index) => (
|
||||
<Fragment key={index}>
|
||||
<ConditionallyRender
|
||||
condition={index > 0}
|
||||
show={<StrategySeparator text='AND' />}
|
||||
/>
|
||||
{item}
|
||||
</Fragment>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,92 +0,0 @@
|
||||
import { Fragment, type VFC } from 'react';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
|
||||
import { styled } from '@mui/material';
|
||||
import type {
|
||||
PlaygroundRequestSchema,
|
||||
PlaygroundStrategySchema,
|
||||
} from 'openapi';
|
||||
import { PlaygroundResultStrategyExecutionParameters } from './StrategyExecutionParameters/StrategyExecutionParameters.tsx';
|
||||
import { CustomStrategyParams } from './CustomStrategyParams/CustomStrategyParams.tsx';
|
||||
import { formattedStrategyNames } from 'utils/strategyNames';
|
||||
import { StyledBoxSummary } from './StrategyExecution.styles';
|
||||
import { Badge } from 'component/common/Badge/Badge';
|
||||
import { ConstraintExecutionWithoutResults } from './ConstraintExecution/ConstraintExecutionWithoutResults.tsx';
|
||||
import { SegmentExecutionWithoutResult } from './SegmentExecution/SegmentExecutionWithoutResult.tsx';
|
||||
|
||||
interface IDisabledStrategyExecutionProps {
|
||||
strategyResult: PlaygroundStrategySchema;
|
||||
percentageFill?: string;
|
||||
input?: PlaygroundRequestSchema;
|
||||
}
|
||||
|
||||
const StyledStrategyExecutionWrapper = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(0),
|
||||
}));
|
||||
|
||||
export const DisabledStrategyExecution: VFC<
|
||||
IDisabledStrategyExecutionProps
|
||||
> = ({ strategyResult, input, percentageFill }) => {
|
||||
const { name, constraints, segments, parameters } = strategyResult;
|
||||
|
||||
const hasSegments = Boolean(segments && segments.length > 0);
|
||||
const hasConstraints = Boolean(constraints && constraints?.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) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const items = [
|
||||
hasSegments && <SegmentExecutionWithoutResult segments={segments} />,
|
||||
hasConstraints && (
|
||||
<ConstraintExecutionWithoutResults constraints={constraints} />
|
||||
),
|
||||
hasExecutionParameters && (
|
||||
<PlaygroundResultStrategyExecutionParameters
|
||||
parameters={parameters}
|
||||
constraints={constraints}
|
||||
input={input}
|
||||
disabled
|
||||
/>
|
||||
),
|
||||
hasCustomStrategyParameters && (
|
||||
<CustomStrategyParams strategyName={name} parameters={parameters} />
|
||||
),
|
||||
name === 'default' && (
|
||||
<StyledBoxSummary
|
||||
sx={(theme) => ({
|
||||
width: '100%',
|
||||
color: theme.palette.text.secondary,
|
||||
})}
|
||||
>
|
||||
The standard strategy is <Badge color={'disabled'}>ON</Badge>{' '}
|
||||
for all users.
|
||||
</StyledBoxSummary>
|
||||
),
|
||||
].filter(Boolean);
|
||||
|
||||
return (
|
||||
<StyledStrategyExecutionWrapper>
|
||||
{items.map((item, index) => (
|
||||
<Fragment key={index}>
|
||||
<ConditionallyRender
|
||||
condition={
|
||||
index > 0 &&
|
||||
(strategyResult.name === 'flexibleRollout'
|
||||
? index < items.length
|
||||
: index < items.length - 1)
|
||||
}
|
||||
show={<StrategySeparator text='AND' />}
|
||||
/>
|
||||
{item}
|
||||
</Fragment>
|
||||
))}
|
||||
</StyledStrategyExecutionWrapper>
|
||||
);
|
||||
};
|
@ -1,65 +0,0 @@
|
||||
import type { IReleasePlanMilestoneStrategy } from 'interfaces/releasePlans';
|
||||
import { type DragEventHandler, type RefObject, useRef } from 'react';
|
||||
import { Box, IconButton } from '@mui/material';
|
||||
import Edit from '@mui/icons-material/Edit';
|
||||
import Delete from '@mui/icons-material/DeleteOutlined';
|
||||
import { StrategySeparator } from 'component/common/StrategySeparator/LegacyStrategySeparator';
|
||||
import { MilestoneStrategyItem } from './MilestoneStrategyItem.tsx';
|
||||
|
||||
interface IMilestoneStrategyDraggableItemProps {
|
||||
strategy: Omit<IReleasePlanMilestoneStrategy, 'milestoneId'>;
|
||||
index: number;
|
||||
isDragging?: boolean;
|
||||
onDragStartRef: (
|
||||
ref: RefObject<HTMLDivElement>,
|
||||
index: number,
|
||||
) => DragEventHandler<HTMLButtonElement>;
|
||||
onDragOver: (
|
||||
ref: RefObject<HTMLDivElement>,
|
||||
index: number,
|
||||
) => DragEventHandler<HTMLDivElement>;
|
||||
onDragEnd: () => void;
|
||||
onDeleteClick: () => void;
|
||||
onEditClick: () => void;
|
||||
}
|
||||
|
||||
export const MilestoneStrategyDraggableItem = ({
|
||||
strategy,
|
||||
index,
|
||||
isDragging,
|
||||
onDragStartRef,
|
||||
onDragOver,
|
||||
onDragEnd,
|
||||
onDeleteClick,
|
||||
onEditClick,
|
||||
}: IMilestoneStrategyDraggableItemProps) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
return (
|
||||
<Box
|
||||
key={strategy.id}
|
||||
ref={ref}
|
||||
onDragOver={onDragOver(ref, index)}
|
||||
sx={{ opacity: isDragging ? '0.5' : '1' }}
|
||||
>
|
||||
{index > 0 && <StrategySeparator text='OR' />}
|
||||
<MilestoneStrategyItem
|
||||
strategy={strategy}
|
||||
onDragStart={onDragStartRef(ref, index)}
|
||||
onDragEnd={onDragEnd}
|
||||
actions={
|
||||
<>
|
||||
<IconButton title='Edit strategy' onClick={onEditClick}>
|
||||
<Edit />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
title='Remove release plan'
|
||||
onClick={onDeleteClick}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
@ -1,101 +0,0 @@
|
||||
import { Box, IconButton, styled } from '@mui/material';
|
||||
import { VariantsSplitPreview } from 'component/common/VariantsSplitPreview/VariantsSplitPreview';
|
||||
import {
|
||||
formatStrategyName,
|
||||
getFeatureStrategyIcon,
|
||||
} from 'utils/strategyNames';
|
||||
import type { IFeatureStrategy } from 'interfaces/strategy';
|
||||
import { StrategyExecution } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution';
|
||||
import type { DragEventHandler, ReactNode } from 'react';
|
||||
import DragIndicator from '@mui/icons-material/DragIndicator';
|
||||
|
||||
const StyledStrategy = styled('div')(({ theme }) => ({
|
||||
background: theme.palette.background.paper,
|
||||
}));
|
||||
|
||||
const DragIcon = styled(IconButton)({
|
||||
padding: 0,
|
||||
cursor: 'inherit',
|
||||
transition: 'color 0.2s ease-in-out',
|
||||
});
|
||||
|
||||
const StyledHeader = styled('div', {
|
||||
shouldForwardProp: (prop) => prop !== 'draggable',
|
||||
})<{ draggable: boolean }>(({ theme, draggable }) => ({
|
||||
display: 'flex',
|
||||
padding: theme.spacing(2),
|
||||
gap: theme.spacing(1),
|
||||
alignItems: 'center',
|
||||
color: theme.palette.text.primary,
|
||||
'& > svg': {
|
||||
fill: theme.palette.action.disabled,
|
||||
},
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
}));
|
||||
|
||||
const StyledStrategyExecution = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(2),
|
||||
}));
|
||||
|
||||
interface IReleasePlanMilestoneStrategyProps {
|
||||
strategy: IFeatureStrategy;
|
||||
onDragStart?: DragEventHandler<HTMLButtonElement>;
|
||||
onDragEnd?: DragEventHandler<HTMLButtonElement>;
|
||||
actions?: ReactNode;
|
||||
}
|
||||
|
||||
export const MilestoneStrategyItem = ({
|
||||
strategy,
|
||||
onDragStart,
|
||||
onDragEnd,
|
||||
actions,
|
||||
}: IReleasePlanMilestoneStrategyProps) => {
|
||||
const Icon = getFeatureStrategyIcon(strategy.strategyName);
|
||||
|
||||
return (
|
||||
<StyledStrategy>
|
||||
<StyledHeader draggable={Boolean(onDragStart)}>
|
||||
<DragIcon
|
||||
draggable
|
||||
disableRipple
|
||||
size='small'
|
||||
onDragStart={onDragStart}
|
||||
onDragEnd={onDragEnd}
|
||||
sx={{ cursor: 'move' }}
|
||||
>
|
||||
<DragIndicator
|
||||
titleAccess='Drag to reorder'
|
||||
cursor='grab'
|
||||
sx={{ color: 'action.active' }}
|
||||
/>
|
||||
</DragIcon>
|
||||
<Icon />
|
||||
{`${formatStrategyName(String(strategy.strategyName))}${strategy.title ? `: ${strategy.title}` : ''}`}
|
||||
<Box
|
||||
sx={{
|
||||
marginLeft: 'auto',
|
||||
display: 'flex',
|
||||
minHeight: (theme) => theme.spacing(6),
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{actions}
|
||||
</Box>
|
||||
</StyledHeader>
|
||||
<StyledStrategyExecution>
|
||||
<StrategyExecution strategy={strategy} />
|
||||
{strategy.variants &&
|
||||
strategy.variants.length > 0 &&
|
||||
(strategy.disabled ? (
|
||||
<Box sx={{ opacity: '0.5' }}>
|
||||
<VariantsSplitPreview
|
||||
variants={strategy.variants}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<VariantsSplitPreview variants={strategy.variants} />
|
||||
))}
|
||||
</StyledStrategyExecution>
|
||||
</StyledStrategy>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user