mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	chore(AI): crDiffView flag cleanup (#10487)
This PR cleans up the crDiffView flag. These changes were automatically generated by AI and should be reviewed carefully. Fixes #10484 🧹 AI Flag Cleanup Summary This PR removes the crDiffView feature flag and its associated legacy components for displaying changes in a Change Request. The flag has been enabled and the new diff view is now permanent. This involved removing the feature flag from the configuration and code, deleting several legacy components, and updating the components that used them to only use the new versions. 🚮 Removed • Feature Flag Logic • All checks for the crDiffView flag. • The flag definition in uiConfig.ts, experimental.ts, and server-dev.ts. • Legacy Components • LegacyStrategyChange.tsx • StrategyTooltipLink.tsx • LegacyReleasePlanChange.tsx • SegmentTooltipLink.tsx • LegacySegmentChangeDetails.tsx • LegacyArchiveFeatureChange from ArchiveFeatureChange.tsx • LegacyDependencyChange from DependencyChange.tsx • LegacyToggleStatusChange from ToggleStatusChange.tsx 🛠 Kept • New Components • The new change request diff view components (StrategyChange, ReleasePlanChange, etc.) are now used directly. • The UI for displaying changes in a Change Request now consistently uses the improved diff view. 📝 Why The crDiffView feature flag was deemed complete and ready for permanent implementation. The cleanup follows standard procedure to remove the flag and associated dead code, simplifying the codebase and making it easier to maintain. This change makes the improved diff view for change requests the only available view. --------- Co-authored-by: unleash-bot <194219037+unleash-bot[bot]@users.noreply.github.com> Co-authored-by: Thomas Heartman <thomas@getunleash.io>
This commit is contained in:
		
							parent
							
								
									d6ddc95c1e
								
							
						
					
					
						commit
						d2452b91f2
					
				@ -203,9 +203,7 @@ test('Display default add strategy', async () => {
 | 
			
		||||
 | 
			
		||||
    expect(screen.getByText('feature1')).toBeInTheDocument();
 | 
			
		||||
    expect(screen.getByText('Enabled')).toBeInTheDocument();
 | 
			
		||||
    expect(
 | 
			
		||||
        screen.getByText('Default strategy will be added'),
 | 
			
		||||
    ).toBeInTheDocument();
 | 
			
		||||
    expect(screen.getByText('Adding default strategy')).toBeInTheDocument();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test('Display default disable feature', async () => {
 | 
			
		||||
 | 
			
		||||
@ -1,33 +1,10 @@
 | 
			
		||||
import type { FC, ReactNode } from 'react';
 | 
			
		||||
import { Action, ChangeItemInfo, ChangeItemWrapper } from './Change.styles.tsx';
 | 
			
		||||
import { styled } from '@mui/material';
 | 
			
		||||
import { ChangeItemWrapper as LegacyChangeItemWrapper } from './LegacyStrategyChange.tsx';
 | 
			
		||||
 | 
			
		||||
type ArchiveFeatureChange = {
 | 
			
		||||
    actions?: ReactNode;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const ArchiveBox = styled('span')(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    color: theme.palette.error.main,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Deprecated: use ArchiveFeatureChange instead; remove with flag crDiffView
 | 
			
		||||
 * @deprecated
 | 
			
		||||
 */
 | 
			
		||||
export const LegacyArchiveFeatureChange: FC<ArchiveFeatureChange> = ({
 | 
			
		||||
    actions,
 | 
			
		||||
}) => (
 | 
			
		||||
    <LegacyChangeItemWrapper>
 | 
			
		||||
        <ChangeItemInfo>
 | 
			
		||||
            <ArchiveBox>Archiving flag</ArchiveBox>
 | 
			
		||||
            {actions}
 | 
			
		||||
        </ChangeItemInfo>
 | 
			
		||||
    </LegacyChangeItemWrapper>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
export const ArchiveFeatureChange: FC<ArchiveFeatureChange> = ({ actions }) => (
 | 
			
		||||
    <ChangeItemWrapper>
 | 
			
		||||
        <ChangeItemInfo>
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,6 @@ import Delete from '@mui/icons-material/Delete';
 | 
			
		||||
import Edit from '@mui/icons-material/Edit';
 | 
			
		||||
import MoreVert from '@mui/icons-material/MoreVert';
 | 
			
		||||
import { EditChange } from './EditChange.tsx';
 | 
			
		||||
import { useUiFlag } from 'hooks/useUiFlag.ts';
 | 
			
		||||
 | 
			
		||||
const useShowActions = (changeRequest: ChangeRequestType, change: IChange) => {
 | 
			
		||||
    const { isChangeRequestConfigured } = useChangeRequestsEnabled(
 | 
			
		||||
@ -74,9 +73,6 @@ export const ChangeActions: FC<{
 | 
			
		||||
    const { showDiscard, showEdit } = useShowActions(changeRequest, change);
 | 
			
		||||
    const { discardChange } = useChangeRequestApi();
 | 
			
		||||
    const { setToastData, setToastApiError } = useToast();
 | 
			
		||||
    const useNewCrView = useUiFlag('crDiffView');
 | 
			
		||||
 | 
			
		||||
    const ButtonComponent = useNewCrView ? StyledIconButton : IconButton;
 | 
			
		||||
 | 
			
		||||
    const [editOpen, setEditOpen] = useState(false);
 | 
			
		||||
 | 
			
		||||
@ -121,7 +117,7 @@ export const ChangeActions: FC<{
 | 
			
		||||
            show={
 | 
			
		||||
                <>
 | 
			
		||||
                    <Tooltip title='Change request actions' arrow describeChild>
 | 
			
		||||
                        <ButtonComponent
 | 
			
		||||
                        <StyledIconButton
 | 
			
		||||
                            id={id}
 | 
			
		||||
                            aria-controls={open ? menuId : undefined}
 | 
			
		||||
                            aria-haspopup='true'
 | 
			
		||||
@ -130,7 +126,7 @@ export const ChangeActions: FC<{
 | 
			
		||||
                            type='button'
 | 
			
		||||
                        >
 | 
			
		||||
                            <MoreVert />
 | 
			
		||||
                        </ButtonComponent>
 | 
			
		||||
                        </StyledIconButton>
 | 
			
		||||
                    </Tooltip>
 | 
			
		||||
                    <StyledPopover
 | 
			
		||||
                        id={menuId}
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import type { FC, ReactNode } from 'react';
 | 
			
		||||
import { Box, styled, Typography } from '@mui/material';
 | 
			
		||||
import { styled } from '@mui/material';
 | 
			
		||||
import type {
 | 
			
		||||
    IChangeRequestAddDependency,
 | 
			
		||||
    IChangeRequestDeleteDependency,
 | 
			
		||||
@ -11,7 +11,6 @@ import {
 | 
			
		||||
    ChangeItemWrapper,
 | 
			
		||||
    Deleted,
 | 
			
		||||
} from './Change.styles';
 | 
			
		||||
import { ChangeItemWrapper as LegacyChangeItemWrapper } from './LegacyStrategyChange.tsx';
 | 
			
		||||
 | 
			
		||||
const StyledLink = styled(Link)(({ theme }) => ({
 | 
			
		||||
    maxWidth: '100%',
 | 
			
		||||
@ -59,60 +58,3 @@ export const DependencyChange: FC<{
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AddDependencyWrapper = styled(Box)(({ theme }) => ({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    gap: theme.spacing(1),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @deprecated use DependencyChange instead; remove with flag crDiffView
 | 
			
		||||
 */
 | 
			
		||||
export const LegacyDependencyChange: FC<{
 | 
			
		||||
    actions?: ReactNode;
 | 
			
		||||
    change: IChangeRequestAddDependency | IChangeRequestDeleteDependency;
 | 
			
		||||
    projectId: string;
 | 
			
		||||
    onNavigate?: () => void;
 | 
			
		||||
}> = ({ actions, change, projectId, onNavigate }) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            {change.action === 'addDependency' && (
 | 
			
		||||
                <>
 | 
			
		||||
                    <LegacyChangeItemWrapper>
 | 
			
		||||
                        <AddDependencyWrapper>
 | 
			
		||||
                            <Typography color={'success.dark'}>
 | 
			
		||||
                                + Adding dependency
 | 
			
		||||
                            </Typography>
 | 
			
		||||
                            <StyledLink
 | 
			
		||||
                                to={`/projects/${projectId}/features/${change.payload.feature}`}
 | 
			
		||||
                                onClick={onNavigate}
 | 
			
		||||
                            >
 | 
			
		||||
                                {change.payload.feature}
 | 
			
		||||
                            </StyledLink>
 | 
			
		||||
                            {!change.payload.enabled ? ' (disabled)' : null}
 | 
			
		||||
                            {change.payload.variants?.length
 | 
			
		||||
                                ? `(${change.payload.variants?.join(', ')})`
 | 
			
		||||
                                : null}
 | 
			
		||||
                        </AddDependencyWrapper>
 | 
			
		||||
                        {actions}
 | 
			
		||||
                    </LegacyChangeItemWrapper>
 | 
			
		||||
                </>
 | 
			
		||||
            )}
 | 
			
		||||
            {change.action === 'deleteDependency' && (
 | 
			
		||||
                <ChangeItemWrapper>
 | 
			
		||||
                    <AddDependencyWrapper>
 | 
			
		||||
                        <Typography
 | 
			
		||||
                            sx={(theme) => ({
 | 
			
		||||
                                color: theme.palette.error.main,
 | 
			
		||||
                            })}
 | 
			
		||||
                        >
 | 
			
		||||
                            - Deleting dependencies
 | 
			
		||||
                        </Typography>
 | 
			
		||||
                    </AddDependencyWrapper>
 | 
			
		||||
                    {actions}
 | 
			
		||||
                </ChangeItemWrapper>
 | 
			
		||||
            )}
 | 
			
		||||
        </>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,6 @@ import { StrategyExecution } from 'component/feature/FeatureView/FeatureOverview
 | 
			
		||||
import { formatStrategyName } from '../../../../../../utils/strategyNames.tsx';
 | 
			
		||||
import type { IFeatureStrategy } from 'interfaces/strategy.ts';
 | 
			
		||||
import { Tab, TabList, TabPanel, Tabs } from '../ChangeTabComponents.tsx';
 | 
			
		||||
import { useUiFlag } from 'hooks/useUiFlag.ts';
 | 
			
		||||
import {
 | 
			
		||||
    ChangeItemInfo as NewChangeItemInfo,
 | 
			
		||||
    ChangeItemWrapper,
 | 
			
		||||
@ -63,7 +62,6 @@ export const EnvironmentStrategyExecutionOrder = ({
 | 
			
		||||
    actions,
 | 
			
		||||
}: IEnvironmentStrategyExecutionOrderProps) => {
 | 
			
		||||
    const { feature: featureData, loading } = useFeature(project, feature);
 | 
			
		||||
    const useDiffableComponent = useUiFlag('crDiffView');
 | 
			
		||||
 | 
			
		||||
    if (loading) return null;
 | 
			
		||||
 | 
			
		||||
@ -97,76 +95,40 @@ export const EnvironmentStrategyExecutionOrder = ({
 | 
			
		||||
        strategyIds: updatedStrategies.map((strategy) => strategy.id),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (useDiffableComponent) {
 | 
			
		||||
        return (
 | 
			
		||||
            <Tabs>
 | 
			
		||||
                <ChangeContent>
 | 
			
		||||
                    <ChangeItemWrapper>
 | 
			
		||||
                        <NewChangeItemInfo>
 | 
			
		||||
                            <Action>
 | 
			
		||||
                                Updating strategy execution order to
 | 
			
		||||
                            </Action>
 | 
			
		||||
                        </NewChangeItemInfo>
 | 
			
		||||
                        <div>
 | 
			
		||||
                            <TabList>
 | 
			
		||||
                                <Tab>View change</Tab>
 | 
			
		||||
                                <Tab>View diff</Tab>
 | 
			
		||||
                            </TabList>
 | 
			
		||||
                            {actions}
 | 
			
		||||
                        </div>
 | 
			
		||||
                    </ChangeItemWrapper>
 | 
			
		||||
                    <TabPanel>
 | 
			
		||||
                        <StyledStrategyExecutionWrapper>
 | 
			
		||||
                            {updatedStrategies.map((strategy, index) => (
 | 
			
		||||
                                <StyledStrategyContainer key={strategy.id}>
 | 
			
		||||
                                    {`${index + 1}: `}
 | 
			
		||||
                                    {formatStrategyName(strategy?.name || '')}
 | 
			
		||||
                                    {strategy?.title && ` - ${strategy.title}`}
 | 
			
		||||
                                    <StrategyExecution strategy={strategy!} />
 | 
			
		||||
                                </StyledStrategyContainer>
 | 
			
		||||
                            ))}
 | 
			
		||||
                        </StyledStrategyExecutionWrapper>
 | 
			
		||||
                    </TabPanel>
 | 
			
		||||
                    <TabPanel variant='diff'>
 | 
			
		||||
                        <EnvironmentStrategyOrderDiff
 | 
			
		||||
                            preData={preData}
 | 
			
		||||
                            data={data}
 | 
			
		||||
                        />
 | 
			
		||||
                    </TabPanel>
 | 
			
		||||
                </ChangeContent>
 | 
			
		||||
            </Tabs>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <ChangeItemInfo>
 | 
			
		||||
            <StyledChangeHeader>
 | 
			
		||||
                <TooltipLink
 | 
			
		||||
                    tooltip={
 | 
			
		||||
                        <EnvironmentStrategyOrderDiff
 | 
			
		||||
                            preData={preData}
 | 
			
		||||
                            data={data}
 | 
			
		||||
                        />
 | 
			
		||||
                    }
 | 
			
		||||
                    tooltipProps={{
 | 
			
		||||
                        maxWidth: 500,
 | 
			
		||||
                        maxHeight: 600,
 | 
			
		||||
                    }}
 | 
			
		||||
                >
 | 
			
		||||
                    Updating strategy execution order to
 | 
			
		||||
                </TooltipLink>
 | 
			
		||||
                {actions}
 | 
			
		||||
            </StyledChangeHeader>
 | 
			
		||||
            <StyledStrategyExecutionWrapper>
 | 
			
		||||
                {updatedStrategies.map((strategy, index) => (
 | 
			
		||||
                    <StyledStrategyContainer key={strategy.id}>
 | 
			
		||||
                        {`${index + 1}: `}
 | 
			
		||||
                        {formatStrategyName(strategy?.name || '')}
 | 
			
		||||
                        {strategy?.title && ` - ${strategy.title}`}
 | 
			
		||||
                        <StrategyExecution strategy={strategy!} />
 | 
			
		||||
                    </StyledStrategyContainer>
 | 
			
		||||
                ))}
 | 
			
		||||
            </StyledStrategyExecutionWrapper>
 | 
			
		||||
        </ChangeItemInfo>
 | 
			
		||||
        <Tabs>
 | 
			
		||||
            <ChangeContent>
 | 
			
		||||
                <ChangeItemWrapper>
 | 
			
		||||
                    <NewChangeItemInfo>
 | 
			
		||||
                        <Action>Updating strategy execution order to</Action>
 | 
			
		||||
                    </NewChangeItemInfo>
 | 
			
		||||
                    <div>
 | 
			
		||||
                        <TabList>
 | 
			
		||||
                            <Tab>View change</Tab>
 | 
			
		||||
                            <Tab>View diff</Tab>
 | 
			
		||||
                        </TabList>
 | 
			
		||||
                        {actions}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </ChangeItemWrapper>
 | 
			
		||||
                <TabPanel>
 | 
			
		||||
                    <StyledStrategyExecutionWrapper>
 | 
			
		||||
                        {updatedStrategies.map((strategy, index) => (
 | 
			
		||||
                            <StyledStrategyContainer key={strategy.id}>
 | 
			
		||||
                                {`${index + 1}: `}
 | 
			
		||||
                                {formatStrategyName(strategy?.name || '')}
 | 
			
		||||
                                {strategy?.title && ` - ${strategy.title}`}
 | 
			
		||||
                                <StrategyExecution strategy={strategy!} />
 | 
			
		||||
                            </StyledStrategyContainer>
 | 
			
		||||
                        ))}
 | 
			
		||||
                    </StyledStrategyExecutionWrapper>
 | 
			
		||||
                </TabPanel>
 | 
			
		||||
                <TabPanel variant='diff'>
 | 
			
		||||
                    <EnvironmentStrategyOrderDiff
 | 
			
		||||
                        preData={preData}
 | 
			
		||||
                        data={data}
 | 
			
		||||
                    />
 | 
			
		||||
                </TabPanel>
 | 
			
		||||
            </ChangeContent>
 | 
			
		||||
        </Tabs>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -7,26 +7,14 @@ import type {
 | 
			
		||||
import { objectId } from 'utils/objectId';
 | 
			
		||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
			
		||||
import { Alert, Box, styled } from '@mui/material';
 | 
			
		||||
import {
 | 
			
		||||
    LegacyToggleStatusChange,
 | 
			
		||||
    ToggleStatusChange,
 | 
			
		||||
} from './ToggleStatusChange.tsx';
 | 
			
		||||
import { LegacyStrategyChange } from './LegacyStrategyChange.tsx';
 | 
			
		||||
import { ToggleStatusChange } from './ToggleStatusChange.tsx';
 | 
			
		||||
import { VariantPatch } from './VariantPatch/VariantPatch.tsx';
 | 
			
		||||
import { EnvironmentStrategyExecutionOrder } from './EnvironmentStrategyExecutionOrder/EnvironmentStrategyExecutionOrder.tsx';
 | 
			
		||||
import {
 | 
			
		||||
    ArchiveFeatureChange,
 | 
			
		||||
    LegacyArchiveFeatureChange,
 | 
			
		||||
} from './ArchiveFeatureChange.tsx';
 | 
			
		||||
import {
 | 
			
		||||
    DependencyChange,
 | 
			
		||||
    LegacyDependencyChange,
 | 
			
		||||
} from './DependencyChange.tsx';
 | 
			
		||||
import { ArchiveFeatureChange } from './ArchiveFeatureChange.tsx';
 | 
			
		||||
import { DependencyChange } from './DependencyChange.tsx';
 | 
			
		||||
import { Link } from 'react-router-dom';
 | 
			
		||||
import { LegacyReleasePlanChange } from './LegacyReleasePlanChange.tsx';
 | 
			
		||||
import { ReleasePlanChange } from './ReleasePlanChange.tsx';
 | 
			
		||||
import { StrategyChange } from './StrategyChange.tsx';
 | 
			
		||||
import { useUiFlag } from 'hooks/useUiFlag.ts';
 | 
			
		||||
 | 
			
		||||
const StyledSingleChangeBox = styled(Box, {
 | 
			
		||||
    shouldForwardProp: (prop: string) => !prop.startsWith('$'),
 | 
			
		||||
@ -82,10 +70,6 @@ const InlineList = styled('ul')(({ theme }) => ({
 | 
			
		||||
 | 
			
		||||
const ChangeInnerBox = styled(Box)(({ theme }) => ({
 | 
			
		||||
    padding: theme.spacing(3),
 | 
			
		||||
    // todo: remove with flag crDiffView
 | 
			
		||||
    '&:has(.delete-strategy-information-wrapper)': {
 | 
			
		||||
        backgroundColor: theme.palette.error.light,
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
export const FeatureChange: FC<{
 | 
			
		||||
@ -109,27 +93,6 @@ export const FeatureChange: FC<{
 | 
			
		||||
        ? feature.changes.length + 1
 | 
			
		||||
        : feature.changes.length;
 | 
			
		||||
 | 
			
		||||
    const useDiffableChangeComponent = useUiFlag('crDiffView');
 | 
			
		||||
    const StrategyChangeComponent = useDiffableChangeComponent
 | 
			
		||||
        ? StrategyChange
 | 
			
		||||
        : LegacyStrategyChange;
 | 
			
		||||
 | 
			
		||||
    const ReleasePlanChangeComponent = useDiffableChangeComponent
 | 
			
		||||
        ? ReleasePlanChange
 | 
			
		||||
        : LegacyReleasePlanChange;
 | 
			
		||||
 | 
			
		||||
    const ArchiveFlagComponent = useDiffableChangeComponent
 | 
			
		||||
        ? ArchiveFeatureChange
 | 
			
		||||
        : LegacyArchiveFeatureChange;
 | 
			
		||||
 | 
			
		||||
    const DependencyChangeComponent = useDiffableChangeComponent
 | 
			
		||||
        ? DependencyChange
 | 
			
		||||
        : LegacyDependencyChange;
 | 
			
		||||
 | 
			
		||||
    const StatusChangeComponent = useDiffableChangeComponent
 | 
			
		||||
        ? ToggleStatusChange
 | 
			
		||||
        : LegacyToggleStatusChange;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <StyledSingleChangeBox
 | 
			
		||||
            key={objectId(change)}
 | 
			
		||||
@ -189,7 +152,7 @@ export const FeatureChange: FC<{
 | 
			
		||||
            <ChangeInnerBox>
 | 
			
		||||
                {(change.action === 'addDependency' ||
 | 
			
		||||
                    change.action === 'deleteDependency') && (
 | 
			
		||||
                    <DependencyChangeComponent
 | 
			
		||||
                    <DependencyChange
 | 
			
		||||
                        actions={actions}
 | 
			
		||||
                        change={change}
 | 
			
		||||
                        projectId={changeRequest.project}
 | 
			
		||||
@ -197,20 +160,20 @@ export const FeatureChange: FC<{
 | 
			
		||||
                    />
 | 
			
		||||
                )}
 | 
			
		||||
                {change.action === 'updateEnabled' && (
 | 
			
		||||
                    <StatusChangeComponent
 | 
			
		||||
                    <ToggleStatusChange
 | 
			
		||||
                        isDefaultChange={isDefaultChange}
 | 
			
		||||
                        enabled={change.payload.enabled}
 | 
			
		||||
                        actions={actions}
 | 
			
		||||
                    />
 | 
			
		||||
                )}
 | 
			
		||||
                {change.action === 'archiveFeature' && (
 | 
			
		||||
                    <ArchiveFlagComponent actions={actions} />
 | 
			
		||||
                    <ArchiveFeatureChange actions={actions} />
 | 
			
		||||
                )}
 | 
			
		||||
 | 
			
		||||
                {change.action === 'addStrategy' ||
 | 
			
		||||
                change.action === 'deleteStrategy' ||
 | 
			
		||||
                change.action === 'updateStrategy' ? (
 | 
			
		||||
                    <StrategyChangeComponent
 | 
			
		||||
                    <StrategyChange
 | 
			
		||||
                        actions={actions}
 | 
			
		||||
                        isDefaultChange={isDefaultChange}
 | 
			
		||||
                        change={change}
 | 
			
		||||
@ -242,7 +205,7 @@ export const FeatureChange: FC<{
 | 
			
		||||
                {(change.action === 'addReleasePlan' ||
 | 
			
		||||
                    change.action === 'deleteReleasePlan' ||
 | 
			
		||||
                    change.action === 'startMilestone') && (
 | 
			
		||||
                    <ReleasePlanChangeComponent
 | 
			
		||||
                    <ReleasePlanChange
 | 
			
		||||
                        actions={actions}
 | 
			
		||||
                        change={change}
 | 
			
		||||
                        featureName={feature.name}
 | 
			
		||||
 | 
			
		||||
@ -1,316 +0,0 @@
 | 
			
		||||
import type React from 'react';
 | 
			
		||||
import { useRef, useState, type FC, type ReactNode } from 'react';
 | 
			
		||||
import { Box, styled, Typography } from '@mui/material';
 | 
			
		||||
import type {
 | 
			
		||||
    ChangeRequestState,
 | 
			
		||||
    IChangeRequestAddReleasePlan,
 | 
			
		||||
    IChangeRequestDeleteReleasePlan,
 | 
			
		||||
    IChangeRequestStartMilestone,
 | 
			
		||||
} from 'component/changeRequest/changeRequest.types';
 | 
			
		||||
import { useReleasePlanPreview } from 'hooks/useReleasePlanPreview';
 | 
			
		||||
import { useReleasePlans } from 'hooks/api/getters/useReleasePlans/useReleasePlans';
 | 
			
		||||
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
 | 
			
		||||
import { EventDiff } from 'component/events/EventDiff/EventDiff';
 | 
			
		||||
import { ReleasePlan } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlan';
 | 
			
		||||
import { ReleasePlanMilestone } from 'component/feature/FeatureView/FeatureOverview/ReleasePlan/ReleasePlanMilestone/ReleasePlanMilestone';
 | 
			
		||||
import type { IReleasePlan } from 'interfaces/releasePlans';
 | 
			
		||||
 | 
			
		||||
export const ChangeItemWrapper = styled(Box)({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    justifyContent: 'space-between',
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ChangeItemCreateEditDeleteWrapper = styled(Box)(({ theme }) => ({
 | 
			
		||||
    display: 'grid',
 | 
			
		||||
    gridTemplateColumns: 'auto auto',
 | 
			
		||||
    justifyContent: 'space-between',
 | 
			
		||||
    gap: theme.spacing(1),
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    marginBottom: theme.spacing(2),
 | 
			
		||||
    width: '100%',
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const ChangeItemInfo: FC<{ children?: React.ReactNode }> = styled(Box)(
 | 
			
		||||
    ({ theme }) => ({
 | 
			
		||||
        display: 'flex',
 | 
			
		||||
        gap: theme.spacing(1),
 | 
			
		||||
    }),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const ViewDiff = styled('span')(({ theme }) => ({
 | 
			
		||||
    color: theme.palette.primary.main,
 | 
			
		||||
    marginLeft: theme.spacing(1),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const StyledCodeSection = styled('div')(({ theme }) => ({
 | 
			
		||||
    overflowX: 'auto',
 | 
			
		||||
    '& code': {
 | 
			
		||||
        wordWrap: 'break-word',
 | 
			
		||||
        whiteSpace: 'pre-wrap',
 | 
			
		||||
        fontFamily: 'monospace',
 | 
			
		||||
        lineHeight: 1.5,
 | 
			
		||||
        fontSize: theme.fontSizes.smallBody,
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const DeleteReleasePlan: FC<{
 | 
			
		||||
    change: IChangeRequestDeleteReleasePlan;
 | 
			
		||||
    currentReleasePlan?: IReleasePlan;
 | 
			
		||||
    changeRequestState: ChangeRequestState;
 | 
			
		||||
    actions?: ReactNode;
 | 
			
		||||
}> = ({ change, currentReleasePlan, changeRequestState, actions }) => {
 | 
			
		||||
    const releasePlan =
 | 
			
		||||
        changeRequestState === 'Applied' && change.payload.snapshot
 | 
			
		||||
            ? change.payload.snapshot
 | 
			
		||||
            : currentReleasePlan;
 | 
			
		||||
 | 
			
		||||
    if (!releasePlan) return;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            <ChangeItemCreateEditDeleteWrapper>
 | 
			
		||||
                <ChangeItemInfo>
 | 
			
		||||
                    <Typography
 | 
			
		||||
                        sx={(theme) => ({
 | 
			
		||||
                            color: theme.palette.error.main,
 | 
			
		||||
                        })}
 | 
			
		||||
                    >
 | 
			
		||||
                        - Deleting release plan
 | 
			
		||||
                    </Typography>
 | 
			
		||||
                    <Typography>{releasePlan.name}</Typography>
 | 
			
		||||
                </ChangeItemInfo>
 | 
			
		||||
                <div>{actions}</div>
 | 
			
		||||
            </ChangeItemCreateEditDeleteWrapper>
 | 
			
		||||
            <ReleasePlan plan={releasePlan} readonly />
 | 
			
		||||
        </>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const StartMilestone: FC<{
 | 
			
		||||
    change: IChangeRequestStartMilestone;
 | 
			
		||||
    currentReleasePlan?: IReleasePlan;
 | 
			
		||||
    changeRequestState: ChangeRequestState;
 | 
			
		||||
    actions?: ReactNode;
 | 
			
		||||
}> = ({ change, currentReleasePlan, changeRequestState, actions }) => {
 | 
			
		||||
    const releasePlan =
 | 
			
		||||
        changeRequestState === 'Applied' && change.payload.snapshot
 | 
			
		||||
            ? change.payload.snapshot
 | 
			
		||||
            : currentReleasePlan;
 | 
			
		||||
 | 
			
		||||
    if (!releasePlan) return;
 | 
			
		||||
 | 
			
		||||
    const previousMilestone = releasePlan.milestones.find(
 | 
			
		||||
        (milestone) => milestone.id === releasePlan.activeMilestoneId,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const newMilestone = releasePlan.milestones.find(
 | 
			
		||||
        (milestone) => milestone.id === change.payload.milestoneId,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    if (!newMilestone) return;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            <ChangeItemCreateEditDeleteWrapper>
 | 
			
		||||
                <ChangeItemInfo>
 | 
			
		||||
                    <Typography color='success.dark'>
 | 
			
		||||
                        + Start milestone:
 | 
			
		||||
                    </Typography>
 | 
			
		||||
                    <Typography>{newMilestone.name}</Typography>
 | 
			
		||||
                    <TooltipLink
 | 
			
		||||
                        tooltip={
 | 
			
		||||
                            <StyledCodeSection>
 | 
			
		||||
                                <EventDiff
 | 
			
		||||
                                    entry={{
 | 
			
		||||
                                        preData: previousMilestone,
 | 
			
		||||
                                        data: newMilestone,
 | 
			
		||||
                                    }}
 | 
			
		||||
                                />
 | 
			
		||||
                            </StyledCodeSection>
 | 
			
		||||
                        }
 | 
			
		||||
                        tooltipProps={{
 | 
			
		||||
                            maxWidth: 500,
 | 
			
		||||
                            maxHeight: 600,
 | 
			
		||||
                        }}
 | 
			
		||||
                    >
 | 
			
		||||
                        <ViewDiff>View Diff</ViewDiff>
 | 
			
		||||
                    </TooltipLink>
 | 
			
		||||
                </ChangeItemInfo>
 | 
			
		||||
                <div>{actions}</div>
 | 
			
		||||
            </ChangeItemCreateEditDeleteWrapper>
 | 
			
		||||
            <ReleasePlanMilestone readonly milestone={newMilestone} />
 | 
			
		||||
        </>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AddReleasePlan: FC<{
 | 
			
		||||
    change: IChangeRequestAddReleasePlan;
 | 
			
		||||
    currentReleasePlan?: IReleasePlan;
 | 
			
		||||
    environmentName: string;
 | 
			
		||||
    featureName: string;
 | 
			
		||||
    actions?: ReactNode;
 | 
			
		||||
}> = ({
 | 
			
		||||
    change,
 | 
			
		||||
    currentReleasePlan,
 | 
			
		||||
    environmentName,
 | 
			
		||||
    featureName,
 | 
			
		||||
    actions,
 | 
			
		||||
}) => {
 | 
			
		||||
    const [currentTooltipOpen, setCurrentTooltipOpen] = useState(false);
 | 
			
		||||
    const currentTooltipCloseTimeoutRef = useRef<NodeJS.Timeout>();
 | 
			
		||||
    const openCurrentTooltip = () => {
 | 
			
		||||
        if (currentTooltipCloseTimeoutRef.current) {
 | 
			
		||||
            clearTimeout(currentTooltipCloseTimeoutRef.current);
 | 
			
		||||
        }
 | 
			
		||||
        setCurrentTooltipOpen(true);
 | 
			
		||||
    };
 | 
			
		||||
    const closeCurrentTooltip = () => {
 | 
			
		||||
        currentTooltipCloseTimeoutRef.current = setTimeout(() => {
 | 
			
		||||
            setCurrentTooltipOpen(false);
 | 
			
		||||
        }, 100);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const planPreview = useReleasePlanPreview(
 | 
			
		||||
        change.payload.templateId,
 | 
			
		||||
        featureName,
 | 
			
		||||
        environmentName,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const planPreviewDiff = {
 | 
			
		||||
        ...planPreview,
 | 
			
		||||
        discriminator: 'plan',
 | 
			
		||||
        releasePlanTemplateId: change.payload.templateId,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            <ChangeItemCreateEditDeleteWrapper>
 | 
			
		||||
                <ChangeItemInfo>
 | 
			
		||||
                    {currentReleasePlan ? (
 | 
			
		||||
                        <Typography>
 | 
			
		||||
                            Replacing{' '}
 | 
			
		||||
                            <TooltipLink
 | 
			
		||||
                                tooltip={
 | 
			
		||||
                                    <div
 | 
			
		||||
                                        onMouseEnter={() =>
 | 
			
		||||
                                            openCurrentTooltip()
 | 
			
		||||
                                        }
 | 
			
		||||
                                        onMouseLeave={() =>
 | 
			
		||||
                                            closeCurrentTooltip()
 | 
			
		||||
                                        }
 | 
			
		||||
                                    >
 | 
			
		||||
                                        <ReleasePlan
 | 
			
		||||
                                            plan={currentReleasePlan}
 | 
			
		||||
                                            readonly
 | 
			
		||||
                                        />
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                }
 | 
			
		||||
                                tooltipProps={{
 | 
			
		||||
                                    open: currentTooltipOpen,
 | 
			
		||||
                                    maxWidth: 500,
 | 
			
		||||
                                    maxHeight: 600,
 | 
			
		||||
                                }}
 | 
			
		||||
                            >
 | 
			
		||||
                                <span
 | 
			
		||||
                                    onMouseEnter={() => openCurrentTooltip()}
 | 
			
		||||
                                    onMouseLeave={() => closeCurrentTooltip()}
 | 
			
		||||
                                >
 | 
			
		||||
                                    current
 | 
			
		||||
                                </span>
 | 
			
		||||
                            </TooltipLink>{' '}
 | 
			
		||||
                            release plan with
 | 
			
		||||
                        </Typography>
 | 
			
		||||
                    ) : (
 | 
			
		||||
                        <Typography color='success.dark'>
 | 
			
		||||
                            + Adding release plan
 | 
			
		||||
                        </Typography>
 | 
			
		||||
                    )}
 | 
			
		||||
                    <Typography>{planPreview.name}</Typography>
 | 
			
		||||
                    {currentReleasePlan && (
 | 
			
		||||
                        <TooltipLink
 | 
			
		||||
                            tooltip={
 | 
			
		||||
                                <StyledCodeSection>
 | 
			
		||||
                                    <EventDiff
 | 
			
		||||
                                        entry={{
 | 
			
		||||
                                            preData: currentReleasePlan,
 | 
			
		||||
                                            data: planPreviewDiff,
 | 
			
		||||
                                        }}
 | 
			
		||||
                                    />
 | 
			
		||||
                                </StyledCodeSection>
 | 
			
		||||
                            }
 | 
			
		||||
                            tooltipProps={{
 | 
			
		||||
                                maxWidth: 500,
 | 
			
		||||
                                maxHeight: 600,
 | 
			
		||||
                            }}
 | 
			
		||||
                        >
 | 
			
		||||
                            <ViewDiff>View Diff</ViewDiff>
 | 
			
		||||
                        </TooltipLink>
 | 
			
		||||
                    )}
 | 
			
		||||
                </ChangeItemInfo>
 | 
			
		||||
                <div>{actions}</div>
 | 
			
		||||
            </ChangeItemCreateEditDeleteWrapper>
 | 
			
		||||
            <ReleasePlan plan={planPreview} readonly />
 | 
			
		||||
        </>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Deprecated: use ReleasePlanChange instead. Remove file with flag crDiffView
 | 
			
		||||
 * @deprecated
 | 
			
		||||
 */
 | 
			
		||||
export const LegacyReleasePlanChange: FC<{
 | 
			
		||||
    actions?: ReactNode;
 | 
			
		||||
    change:
 | 
			
		||||
        | IChangeRequestAddReleasePlan
 | 
			
		||||
        | IChangeRequestDeleteReleasePlan
 | 
			
		||||
        | IChangeRequestStartMilestone;
 | 
			
		||||
    environmentName: string;
 | 
			
		||||
    featureName: string;
 | 
			
		||||
    projectId: string;
 | 
			
		||||
    changeRequestState: ChangeRequestState;
 | 
			
		||||
}> = ({
 | 
			
		||||
    actions,
 | 
			
		||||
    change,
 | 
			
		||||
    featureName,
 | 
			
		||||
    environmentName,
 | 
			
		||||
    projectId,
 | 
			
		||||
    changeRequestState,
 | 
			
		||||
}) => {
 | 
			
		||||
    const { releasePlans } = useReleasePlans(
 | 
			
		||||
        projectId,
 | 
			
		||||
        featureName,
 | 
			
		||||
        environmentName,
 | 
			
		||||
    );
 | 
			
		||||
    const currentReleasePlan = releasePlans[0];
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            {change.action === 'addReleasePlan' && (
 | 
			
		||||
                <AddReleasePlan
 | 
			
		||||
                    change={change}
 | 
			
		||||
                    currentReleasePlan={currentReleasePlan}
 | 
			
		||||
                    environmentName={environmentName}
 | 
			
		||||
                    featureName={featureName}
 | 
			
		||||
                    actions={actions}
 | 
			
		||||
                />
 | 
			
		||||
            )}
 | 
			
		||||
            {change.action === 'deleteReleasePlan' && (
 | 
			
		||||
                <DeleteReleasePlan
 | 
			
		||||
                    change={change}
 | 
			
		||||
                    currentReleasePlan={currentReleasePlan}
 | 
			
		||||
                    changeRequestState={changeRequestState}
 | 
			
		||||
                    actions={actions}
 | 
			
		||||
                />
 | 
			
		||||
            )}
 | 
			
		||||
            {change.action === 'startMilestone' && (
 | 
			
		||||
                <StartMilestone
 | 
			
		||||
                    change={change}
 | 
			
		||||
                    currentReleasePlan={currentReleasePlan}
 | 
			
		||||
                    changeRequestState={changeRequestState}
 | 
			
		||||
                    actions={actions}
 | 
			
		||||
                />
 | 
			
		||||
            )}
 | 
			
		||||
        </>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@ -1,129 +0,0 @@
 | 
			
		||||
import type React from 'react';
 | 
			
		||||
import type { FC, ReactNode } from 'react';
 | 
			
		||||
import { Box, styled, Typography } from '@mui/material';
 | 
			
		||||
import type {
 | 
			
		||||
    ChangeRequestState,
 | 
			
		||||
    IChangeRequestDeleteSegment,
 | 
			
		||||
    IChangeRequestUpdateSegment,
 | 
			
		||||
} from 'component/changeRequest/changeRequest.types';
 | 
			
		||||
import { useSegment } from 'hooks/api/getters/useSegment/useSegment';
 | 
			
		||||
import { SegmentDiff, SegmentTooltipLink } from '../../SegmentTooltipLink.tsx';
 | 
			
		||||
 | 
			
		||||
import { ViewableConstraintsList } from 'component/common/NewConstraintAccordion/ConstraintsList/ViewableConstraintsList';
 | 
			
		||||
 | 
			
		||||
import { ChangeOverwriteWarning } from './ChangeOverwriteWarning/ChangeOverwriteWarning.tsx';
 | 
			
		||||
 | 
			
		||||
const ChangeItemCreateEditWrapper = styled(Box)(({ theme }) => ({
 | 
			
		||||
    display: 'grid',
 | 
			
		||||
    gridTemplateColumns: 'auto 40px',
 | 
			
		||||
    gap: theme.spacing(1),
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    width: '100%',
 | 
			
		||||
    margin: theme.spacing(0, 0, 1, 0),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
export const ChangeItemWrapper = styled(Box)({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    justifyContent: 'space-between',
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ChangeItemInfo: FC<{ children?: React.ReactNode }> = styled(Box)(
 | 
			
		||||
    ({ theme }) => ({
 | 
			
		||||
        display: 'grid',
 | 
			
		||||
        gridTemplateColumns: '150px auto',
 | 
			
		||||
        gridAutoFlow: 'column',
 | 
			
		||||
        alignItems: 'center',
 | 
			
		||||
        flexGrow: 1,
 | 
			
		||||
        gap: theme.spacing(1),
 | 
			
		||||
    }),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const SegmentContainer = styled(Box, {
 | 
			
		||||
    shouldForwardProp: (prop) => prop !== 'conflict',
 | 
			
		||||
})<{ conflict: string | undefined }>(({ theme, conflict }) => ({
 | 
			
		||||
    borderLeft: '1px solid',
 | 
			
		||||
    borderRight: '1px solid',
 | 
			
		||||
    borderTop: '1px solid',
 | 
			
		||||
    borderBottom: '1px solid',
 | 
			
		||||
    borderColor: conflict
 | 
			
		||||
        ? theme.palette.warning.border
 | 
			
		||||
        : theme.palette.divider,
 | 
			
		||||
    borderTopColor: theme.palette.divider,
 | 
			
		||||
    padding: theme.spacing(3),
 | 
			
		||||
    borderRadius: `0 0 ${theme.shape.borderRadiusLarge}px ${theme.shape.borderRadiusLarge}px`,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Deprecated: use SegmentChangeDetails instead. Remove file with flag crDiffView
 | 
			
		||||
 * @deprecated
 | 
			
		||||
 */
 | 
			
		||||
export const LegacySegmentChangeDetails: FC<{
 | 
			
		||||
    actions?: ReactNode;
 | 
			
		||||
    change: IChangeRequestUpdateSegment | IChangeRequestDeleteSegment;
 | 
			
		||||
    changeRequestState: ChangeRequestState;
 | 
			
		||||
}> = ({ actions, change, changeRequestState }) => {
 | 
			
		||||
    const { segment: currentSegment } = useSegment(change.payload.id);
 | 
			
		||||
    const snapshotSegment = change.payload.snapshot;
 | 
			
		||||
    const previousName =
 | 
			
		||||
        changeRequestState === 'Applied'
 | 
			
		||||
            ? change.payload?.snapshot?.name
 | 
			
		||||
            : currentSegment?.name;
 | 
			
		||||
    const referenceSegment =
 | 
			
		||||
        changeRequestState === 'Applied' ? snapshotSegment : currentSegment;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <SegmentContainer conflict={change.conflict}>
 | 
			
		||||
            {change.action === 'deleteSegment' && (
 | 
			
		||||
                <ChangeItemWrapper>
 | 
			
		||||
                    <ChangeItemInfo>
 | 
			
		||||
                        <Typography
 | 
			
		||||
                            sx={(theme) => ({
 | 
			
		||||
                                color: theme.palette.error.main,
 | 
			
		||||
                            })}
 | 
			
		||||
                        >
 | 
			
		||||
                            - Deleting segment
 | 
			
		||||
                        </Typography>
 | 
			
		||||
                        <SegmentTooltipLink
 | 
			
		||||
                            name={change.payload.name}
 | 
			
		||||
                            previousName={previousName}
 | 
			
		||||
                        >
 | 
			
		||||
                            <SegmentDiff
 | 
			
		||||
                                change={change}
 | 
			
		||||
                                currentSegment={referenceSegment}
 | 
			
		||||
                            />
 | 
			
		||||
                        </SegmentTooltipLink>
 | 
			
		||||
                    </ChangeItemInfo>
 | 
			
		||||
                    <div>{actions}</div>
 | 
			
		||||
                </ChangeItemWrapper>
 | 
			
		||||
            )}
 | 
			
		||||
            {change.action === 'updateSegment' && (
 | 
			
		||||
                <>
 | 
			
		||||
                    <ChangeOverwriteWarning
 | 
			
		||||
                        data={{
 | 
			
		||||
                            current: currentSegment,
 | 
			
		||||
                            change,
 | 
			
		||||
                            changeType: 'segment',
 | 
			
		||||
                        }}
 | 
			
		||||
                        changeRequestState={changeRequestState}
 | 
			
		||||
                    />
 | 
			
		||||
                    <ChangeItemCreateEditWrapper>
 | 
			
		||||
                        <ChangeItemInfo>
 | 
			
		||||
                            <Typography>Editing segment</Typography>
 | 
			
		||||
                            <SegmentTooltipLink name={change.payload.name}>
 | 
			
		||||
                                <SegmentDiff
 | 
			
		||||
                                    change={change}
 | 
			
		||||
                                    currentSegment={referenceSegment}
 | 
			
		||||
                                />
 | 
			
		||||
                            </SegmentTooltipLink>
 | 
			
		||||
                        </ChangeItemInfo>
 | 
			
		||||
                        <div>{actions}</div>
 | 
			
		||||
                    </ChangeItemCreateEditWrapper>
 | 
			
		||||
                    <ViewableConstraintsList
 | 
			
		||||
                        constraints={change.payload.constraints}
 | 
			
		||||
                    />
 | 
			
		||||
                </>
 | 
			
		||||
            )}
 | 
			
		||||
        </SegmentContainer>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@ -1,371 +0,0 @@
 | 
			
		||||
import type React from 'react';
 | 
			
		||||
import type { FC, ReactNode } from 'react';
 | 
			
		||||
import { Box, styled, Tooltip, Typography } from '@mui/material';
 | 
			
		||||
import BlockIcon from '@mui/icons-material/Block';
 | 
			
		||||
import TrackChangesIcon from '@mui/icons-material/TrackChanges';
 | 
			
		||||
import {
 | 
			
		||||
    StrategyDiff,
 | 
			
		||||
    StrategyTooltipLink,
 | 
			
		||||
} from '../../StrategyTooltipLink/StrategyTooltipLink.tsx';
 | 
			
		||||
import { StrategyExecution } from 'component/feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution';
 | 
			
		||||
import type {
 | 
			
		||||
    ChangeRequestState,
 | 
			
		||||
    IChangeRequestAddStrategy,
 | 
			
		||||
    IChangeRequestDeleteStrategy,
 | 
			
		||||
    IChangeRequestUpdateStrategy,
 | 
			
		||||
} from 'component/changeRequest/changeRequest.types';
 | 
			
		||||
import { useCurrentStrategy } from './hooks/useCurrentStrategy.ts';
 | 
			
		||||
import { Badge } from 'component/common/Badge/Badge';
 | 
			
		||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
			
		||||
import { flexRow } from 'themes/themeStyles';
 | 
			
		||||
import { EnvironmentVariantsTable } from 'component/feature/FeatureView/FeatureVariants/FeatureEnvironmentVariants/EnvironmentVariantsCard/EnvironmentVariantsTable/EnvironmentVariantsTable';
 | 
			
		||||
import { ChangeOverwriteWarning } from './ChangeOverwriteWarning/ChangeOverwriteWarning.tsx';
 | 
			
		||||
import type { IFeatureStrategy } from 'interfaces/strategy';
 | 
			
		||||
 | 
			
		||||
export const ChangeItemWrapper = styled(Box)({
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
    justifyContent: 'space-between',
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ChangeItemCreateEditDeleteWrapper = styled(Box)(({ theme }) => ({
 | 
			
		||||
    display: 'grid',
 | 
			
		||||
    gridTemplateColumns: 'auto auto',
 | 
			
		||||
    justifyContent: 'space-between',
 | 
			
		||||
    gap: theme.spacing(1),
 | 
			
		||||
    alignItems: 'center',
 | 
			
		||||
    marginBottom: theme.spacing(2),
 | 
			
		||||
    width: '100%',
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const ChangeItemInfo: FC<{ children?: React.ReactNode }> = styled(Box)(
 | 
			
		||||
    ({ theme }) => ({
 | 
			
		||||
        display: 'grid',
 | 
			
		||||
        gridTemplateColumns: '150px auto',
 | 
			
		||||
        gridAutoFlow: 'column',
 | 
			
		||||
        alignItems: 'center',
 | 
			
		||||
        flexGrow: 1,
 | 
			
		||||
        gap: theme.spacing(1),
 | 
			
		||||
    }),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const StyledBox: FC<{ children?: React.ReactNode }> = styled(Box)(
 | 
			
		||||
    ({ theme }) => ({
 | 
			
		||||
        marginTop: theme.spacing(2),
 | 
			
		||||
    }),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const StyledTypography: FC<{ children?: React.ReactNode }> = styled(Typography)(
 | 
			
		||||
    ({ theme }) => ({
 | 
			
		||||
        margin: `${theme.spacing(1)} 0`,
 | 
			
		||||
    }),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const DisabledEnabledState: FC<{ show?: boolean; disabled: boolean }> = ({
 | 
			
		||||
    show = true,
 | 
			
		||||
    disabled,
 | 
			
		||||
}) => {
 | 
			
		||||
    if (!show) {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (disabled) {
 | 
			
		||||
        return (
 | 
			
		||||
            <Tooltip
 | 
			
		||||
                title='This strategy will not be taken into account when evaluating feature flag.'
 | 
			
		||||
                arrow
 | 
			
		||||
                sx={{ cursor: 'pointer' }}
 | 
			
		||||
            >
 | 
			
		||||
                <Badge color='disabled' icon={<BlockIcon />}>
 | 
			
		||||
                    Disabled
 | 
			
		||||
                </Badge>
 | 
			
		||||
            </Tooltip>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <Tooltip
 | 
			
		||||
            title='This was disabled before and with this change it will be taken into account when evaluating feature flag.'
 | 
			
		||||
            arrow
 | 
			
		||||
            sx={{ cursor: 'pointer' }}
 | 
			
		||||
        >
 | 
			
		||||
            <Badge color='success' icon={<TrackChangesIcon />}>
 | 
			
		||||
                Enabled
 | 
			
		||||
            </Badge>
 | 
			
		||||
        </Tooltip>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const EditHeader: FC<{
 | 
			
		||||
    wasDisabled?: boolean;
 | 
			
		||||
    willBeDisabled?: boolean;
 | 
			
		||||
}> = ({ wasDisabled = false, willBeDisabled = false }) => {
 | 
			
		||||
    if (wasDisabled && willBeDisabled) {
 | 
			
		||||
        return (
 | 
			
		||||
            <Typography color='action.disabled'>Editing strategy</Typography>
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!wasDisabled && willBeDisabled) {
 | 
			
		||||
        return <Typography color='error.dark'>Editing strategy</Typography>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (wasDisabled && !willBeDisabled) {
 | 
			
		||||
        return <Typography color='success.dark'>Editing strategy</Typography>;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return <Typography>Editing strategy</Typography>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const hasDiff = (object: unknown, objectToCompare: unknown) =>
 | 
			
		||||
    JSON.stringify(object) !== JSON.stringify(objectToCompare);
 | 
			
		||||
 | 
			
		||||
const DeleteStrategy: FC<{
 | 
			
		||||
    change: IChangeRequestDeleteStrategy;
 | 
			
		||||
    changeRequestState: ChangeRequestState;
 | 
			
		||||
    currentStrategy: IFeatureStrategy | undefined;
 | 
			
		||||
    actions?: ReactNode;
 | 
			
		||||
}> = ({ change, changeRequestState, currentStrategy, actions }) => {
 | 
			
		||||
    const name =
 | 
			
		||||
        changeRequestState === 'Applied'
 | 
			
		||||
            ? change.payload?.snapshot?.name
 | 
			
		||||
            : currentStrategy?.name;
 | 
			
		||||
    const title =
 | 
			
		||||
        changeRequestState === 'Applied'
 | 
			
		||||
            ? change.payload?.snapshot?.title
 | 
			
		||||
            : currentStrategy?.title;
 | 
			
		||||
    const referenceStrategy =
 | 
			
		||||
        changeRequestState === 'Applied'
 | 
			
		||||
            ? change.payload.snapshot
 | 
			
		||||
            : currentStrategy;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            <ChangeItemCreateEditDeleteWrapper className='delete-strategy-information-wrapper'>
 | 
			
		||||
                <ChangeItemInfo>
 | 
			
		||||
                    <Typography
 | 
			
		||||
                        sx={(theme) => ({
 | 
			
		||||
                            color: theme.palette.error.main,
 | 
			
		||||
                        })}
 | 
			
		||||
                    >
 | 
			
		||||
                        - Deleting strategy
 | 
			
		||||
                    </Typography>
 | 
			
		||||
                    <StrategyTooltipLink name={name || ''} title={title}>
 | 
			
		||||
                        <StrategyDiff
 | 
			
		||||
                            change={change}
 | 
			
		||||
                            currentStrategy={referenceStrategy}
 | 
			
		||||
                        />
 | 
			
		||||
                    </StrategyTooltipLink>
 | 
			
		||||
                </ChangeItemInfo>
 | 
			
		||||
                <div>{actions}</div>
 | 
			
		||||
            </ChangeItemCreateEditDeleteWrapper>
 | 
			
		||||
            {referenceStrategy && (
 | 
			
		||||
                <StrategyExecution strategy={referenceStrategy} />
 | 
			
		||||
            )}
 | 
			
		||||
        </>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const UpdateStrategy: FC<{
 | 
			
		||||
    change: IChangeRequestUpdateStrategy;
 | 
			
		||||
    changeRequestState: ChangeRequestState;
 | 
			
		||||
    currentStrategy: IFeatureStrategy | undefined;
 | 
			
		||||
    actions?: ReactNode;
 | 
			
		||||
}> = ({ change, changeRequestState, currentStrategy, actions }) => {
 | 
			
		||||
    const previousTitle =
 | 
			
		||||
        changeRequestState === 'Applied'
 | 
			
		||||
            ? change.payload.snapshot?.title
 | 
			
		||||
            : currentStrategy?.title;
 | 
			
		||||
    const referenceStrategy =
 | 
			
		||||
        changeRequestState === 'Applied'
 | 
			
		||||
            ? change.payload.snapshot
 | 
			
		||||
            : currentStrategy;
 | 
			
		||||
    const hasVariantDiff = hasDiff(
 | 
			
		||||
        referenceStrategy?.variants || [],
 | 
			
		||||
        change.payload.variants || [],
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            <ChangeOverwriteWarning
 | 
			
		||||
                data={{
 | 
			
		||||
                    current: currentStrategy,
 | 
			
		||||
                    change,
 | 
			
		||||
                    changeType: 'strategy',
 | 
			
		||||
                }}
 | 
			
		||||
                changeRequestState={changeRequestState}
 | 
			
		||||
            />
 | 
			
		||||
            <ChangeItemCreateEditDeleteWrapper>
 | 
			
		||||
                <ChangeItemInfo>
 | 
			
		||||
                    <EditHeader
 | 
			
		||||
                        wasDisabled={currentStrategy?.disabled}
 | 
			
		||||
                        willBeDisabled={change.payload?.disabled}
 | 
			
		||||
                    />
 | 
			
		||||
                    <StrategyTooltipLink
 | 
			
		||||
                        name={change.payload.name}
 | 
			
		||||
                        title={change.payload.title}
 | 
			
		||||
                        previousTitle={previousTitle}
 | 
			
		||||
                    >
 | 
			
		||||
                        <StrategyDiff
 | 
			
		||||
                            change={change}
 | 
			
		||||
                            currentStrategy={referenceStrategy}
 | 
			
		||||
                        />
 | 
			
		||||
                    </StrategyTooltipLink>
 | 
			
		||||
                </ChangeItemInfo>
 | 
			
		||||
                <div>{actions}</div>
 | 
			
		||||
            </ChangeItemCreateEditDeleteWrapper>
 | 
			
		||||
            <ConditionallyRender
 | 
			
		||||
                condition={
 | 
			
		||||
                    change.payload?.disabled !== currentStrategy?.disabled
 | 
			
		||||
                }
 | 
			
		||||
                show={
 | 
			
		||||
                    <Typography
 | 
			
		||||
                        sx={{
 | 
			
		||||
                            marginTop: (theme) => theme.spacing(2),
 | 
			
		||||
                            marginBottom: (theme) => theme.spacing(2),
 | 
			
		||||
                            ...flexRow,
 | 
			
		||||
                            gap: (theme) => theme.spacing(1),
 | 
			
		||||
                        }}
 | 
			
		||||
                    >
 | 
			
		||||
                        This strategy will be{' '}
 | 
			
		||||
                        <DisabledEnabledState
 | 
			
		||||
                            disabled={change.payload?.disabled || false}
 | 
			
		||||
                        />
 | 
			
		||||
                    </Typography>
 | 
			
		||||
                }
 | 
			
		||||
            />
 | 
			
		||||
            <StrategyExecution strategy={change.payload} />
 | 
			
		||||
            {hasVariantDiff ? (
 | 
			
		||||
                <StyledBox>
 | 
			
		||||
                    {change.payload.variants?.length ? (
 | 
			
		||||
                        <>
 | 
			
		||||
                            <StyledTypography>
 | 
			
		||||
                                {currentStrategy?.variants?.length
 | 
			
		||||
                                    ? 'Updating strategy variants to:'
 | 
			
		||||
                                    : 'Adding strategy variants:'}
 | 
			
		||||
                            </StyledTypography>
 | 
			
		||||
                            <EnvironmentVariantsTable
 | 
			
		||||
                                variants={change.payload.variants || []}
 | 
			
		||||
                            />
 | 
			
		||||
                        </>
 | 
			
		||||
                    ) : (
 | 
			
		||||
                        <StyledTypography>
 | 
			
		||||
                            Removed all strategy variants.
 | 
			
		||||
                        </StyledTypography>
 | 
			
		||||
                    )}
 | 
			
		||||
                </StyledBox>
 | 
			
		||||
            ) : null}
 | 
			
		||||
        </>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const AddStrategy: FC<{
 | 
			
		||||
    change: IChangeRequestAddStrategy;
 | 
			
		||||
    actions?: ReactNode;
 | 
			
		||||
    isDefaultChange?: boolean;
 | 
			
		||||
}> = ({ change, actions, isDefaultChange }) => (
 | 
			
		||||
    <>
 | 
			
		||||
        <ChangeItemCreateEditDeleteWrapper>
 | 
			
		||||
            <ChangeItemInfo>
 | 
			
		||||
                <Typography
 | 
			
		||||
                    color={
 | 
			
		||||
                        change.payload?.disabled
 | 
			
		||||
                            ? 'action.disabled'
 | 
			
		||||
                            : 'success.dark'
 | 
			
		||||
                    }
 | 
			
		||||
                >
 | 
			
		||||
                    + Adding strategy
 | 
			
		||||
                </Typography>
 | 
			
		||||
                <StrategyTooltipLink
 | 
			
		||||
                    name={change.payload.name}
 | 
			
		||||
                    title={change.payload.title}
 | 
			
		||||
                >
 | 
			
		||||
                    <StrategyDiff change={change} currentStrategy={undefined} />
 | 
			
		||||
                </StrategyTooltipLink>
 | 
			
		||||
                <div>
 | 
			
		||||
                    <DisabledEnabledState
 | 
			
		||||
                        disabled
 | 
			
		||||
                        show={change.payload?.disabled === true}
 | 
			
		||||
                    />
 | 
			
		||||
                </div>
 | 
			
		||||
            </ChangeItemInfo>
 | 
			
		||||
            <div>
 | 
			
		||||
                {isDefaultChange ? (
 | 
			
		||||
                    <Typography variant='body2' color='text.secondary'>
 | 
			
		||||
                        Default strategy will be added
 | 
			
		||||
                    </Typography>
 | 
			
		||||
                ) : null}
 | 
			
		||||
                {actions}
 | 
			
		||||
            </div>
 | 
			
		||||
        </ChangeItemCreateEditDeleteWrapper>
 | 
			
		||||
        <StrategyExecution strategy={change.payload} />
 | 
			
		||||
        {change.payload.variants?.length ? (
 | 
			
		||||
            <StyledBox>
 | 
			
		||||
                <StyledTypography>Adding strategy variants:</StyledTypography>
 | 
			
		||||
                <EnvironmentVariantsTable
 | 
			
		||||
                    variants={change.payload.variants || []}
 | 
			
		||||
                />
 | 
			
		||||
            </StyledBox>
 | 
			
		||||
        ) : null}
 | 
			
		||||
    </>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Deprecated: use StrategyChange instead. Remove file with flag crDiffView
 | 
			
		||||
 * @deprecated
 | 
			
		||||
 */
 | 
			
		||||
export const LegacyStrategyChange: FC<{
 | 
			
		||||
    actions?: ReactNode;
 | 
			
		||||
    change:
 | 
			
		||||
        | IChangeRequestAddStrategy
 | 
			
		||||
        | IChangeRequestDeleteStrategy
 | 
			
		||||
        | IChangeRequestUpdateStrategy;
 | 
			
		||||
    environmentName: string;
 | 
			
		||||
    featureName: string;
 | 
			
		||||
    projectId: string;
 | 
			
		||||
    changeRequestState: ChangeRequestState;
 | 
			
		||||
    isDefaultChange?: boolean;
 | 
			
		||||
}> = ({
 | 
			
		||||
    actions,
 | 
			
		||||
    change,
 | 
			
		||||
    featureName,
 | 
			
		||||
    environmentName,
 | 
			
		||||
    projectId,
 | 
			
		||||
    changeRequestState,
 | 
			
		||||
    isDefaultChange,
 | 
			
		||||
}) => {
 | 
			
		||||
    const currentStrategy = useCurrentStrategy(
 | 
			
		||||
        change,
 | 
			
		||||
        projectId,
 | 
			
		||||
        featureName,
 | 
			
		||||
        environmentName,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <>
 | 
			
		||||
            {change.action === 'addStrategy' && (
 | 
			
		||||
                <AddStrategy
 | 
			
		||||
                    change={change}
 | 
			
		||||
                    actions={actions}
 | 
			
		||||
                    isDefaultChange={isDefaultChange}
 | 
			
		||||
                />
 | 
			
		||||
            )}
 | 
			
		||||
            {change.action === 'deleteStrategy' && (
 | 
			
		||||
                <DeleteStrategy
 | 
			
		||||
                    change={change}
 | 
			
		||||
                    changeRequestState={changeRequestState}
 | 
			
		||||
                    currentStrategy={currentStrategy}
 | 
			
		||||
                    actions={actions}
 | 
			
		||||
                />
 | 
			
		||||
            )}
 | 
			
		||||
            {change.action === 'updateStrategy' && (
 | 
			
		||||
                <UpdateStrategy
 | 
			
		||||
                    change={change}
 | 
			
		||||
                    changeRequestState={changeRequestState}
 | 
			
		||||
                    currentStrategy={currentStrategy}
 | 
			
		||||
                    actions={actions}
 | 
			
		||||
                />
 | 
			
		||||
            )}
 | 
			
		||||
        </>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@ -5,11 +5,9 @@ import type {
 | 
			
		||||
    ChangeRequestState,
 | 
			
		||||
    ISegmentChange,
 | 
			
		||||
} from '../../../changeRequest.types';
 | 
			
		||||
import { LegacySegmentChangeDetails } from './LegacySegmentChangeDetails.tsx';
 | 
			
		||||
import { SegmentChangeDetails } from './SegmentChangeDetails.tsx';
 | 
			
		||||
import { ConflictWarning } from './ConflictWarning.tsx';
 | 
			
		||||
import { useSegment } from 'hooks/api/getters/useSegment/useSegment.ts';
 | 
			
		||||
import { useUiFlag } from 'hooks/useUiFlag.ts';
 | 
			
		||||
 | 
			
		||||
interface ISegmentChangeProps {
 | 
			
		||||
    segmentChange: ISegmentChange;
 | 
			
		||||
@ -26,10 +24,6 @@ export const SegmentChange: FC<ISegmentChangeProps> = ({
 | 
			
		||||
}) => {
 | 
			
		||||
    const { segment } = useSegment(segmentChange.payload.id);
 | 
			
		||||
 | 
			
		||||
    const ChangeDetails = useUiFlag('crDiffView')
 | 
			
		||||
        ? SegmentChangeDetails
 | 
			
		||||
        : LegacySegmentChangeDetails;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <Card
 | 
			
		||||
            elevation={0}
 | 
			
		||||
@ -81,7 +75,7 @@ export const SegmentChange: FC<ISegmentChangeProps> = ({
 | 
			
		||||
                    </Link>
 | 
			
		||||
                </Box>
 | 
			
		||||
            </Box>
 | 
			
		||||
            <ChangeDetails
 | 
			
		||||
            <SegmentChangeDetails
 | 
			
		||||
                change={segmentChange}
 | 
			
		||||
                actions={actions}
 | 
			
		||||
                changeRequestState={changeRequestState}
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
import type { FC, ReactNode } from 'react';
 | 
			
		||||
import { Box, Typography } from '@mui/material';
 | 
			
		||||
import { Typography } from '@mui/material';
 | 
			
		||||
import { Badge } from 'component/common/Badge/Badge';
 | 
			
		||||
import { ChangeItemWrapper as LegacyChangeItemWrapper } from './LegacyStrategyChange.tsx';
 | 
			
		||||
import { Action, ChangeItemInfo, ChangeItemWrapper } from './Change.styles';
 | 
			
		||||
 | 
			
		||||
interface IToggleStatusChange {
 | 
			
		||||
@ -16,31 +15,6 @@ const StatusWillChange = () => (
 | 
			
		||||
    </Typography>
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @deprecated use ToggleStatusChange instead; remove with flag crDiffView
 | 
			
		||||
 */
 | 
			
		||||
export const LegacyToggleStatusChange: FC<IToggleStatusChange> = ({
 | 
			
		||||
    enabled,
 | 
			
		||||
    actions,
 | 
			
		||||
    isDefaultChange,
 | 
			
		||||
}) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <LegacyChangeItemWrapper>
 | 
			
		||||
            <Box sx={{ display: 'flex', alignItems: 'center' }}>
 | 
			
		||||
                New status
 | 
			
		||||
                <Badge
 | 
			
		||||
                    sx={(theme) => ({ marginLeft: theme.spacing(1) })}
 | 
			
		||||
                    color={enabled ? 'success' : 'error'}
 | 
			
		||||
                >
 | 
			
		||||
                    {enabled ? ' Enabled' : 'Disabled'}
 | 
			
		||||
                </Badge>
 | 
			
		||||
            </Box>
 | 
			
		||||
            {isDefaultChange ? <StatusWillChange /> : null}
 | 
			
		||||
            {actions}
 | 
			
		||||
        </LegacyChangeItemWrapper>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const ToggleStatusChange: FC<IToggleStatusChange> = ({
 | 
			
		||||
    enabled,
 | 
			
		||||
    actions,
 | 
			
		||||
 | 
			
		||||
@ -1,91 +0,0 @@
 | 
			
		||||
// deprecated: remove with flag crDiffView
 | 
			
		||||
import type {
 | 
			
		||||
    IChangeRequestDeleteSegment,
 | 
			
		||||
    IChangeRequestUpdateSegment,
 | 
			
		||||
} from 'component/changeRequest/changeRequest.types';
 | 
			
		||||
import type React from 'react';
 | 
			
		||||
import type { FC } from 'react';
 | 
			
		||||
import { EventDiff } from 'component/events/EventDiff/EventDiff';
 | 
			
		||||
import omit from 'lodash.omit';
 | 
			
		||||
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
 | 
			
		||||
import { styled } from '@mui/material';
 | 
			
		||||
import { textTruncated } from 'themes/themeStyles';
 | 
			
		||||
import type { ISegment } from 'interfaces/segment';
 | 
			
		||||
import { NameWithChangeInfo } from './Changes/Change/NameWithChangeInfo/NameWithChangeInfo.tsx';
 | 
			
		||||
 | 
			
		||||
const StyledCodeSection = styled('div')(({ theme }) => ({
 | 
			
		||||
    overflowX: 'auto',
 | 
			
		||||
    '& code': {
 | 
			
		||||
        wordWrap: 'break-word',
 | 
			
		||||
        whiteSpace: 'pre-wrap',
 | 
			
		||||
        fontFamily: 'monospace',
 | 
			
		||||
        lineHeight: 1.5,
 | 
			
		||||
        fontSize: theme.fontSizes.smallBody,
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
export const SegmentDiff: FC<{
 | 
			
		||||
    change: IChangeRequestUpdateSegment | IChangeRequestDeleteSegment;
 | 
			
		||||
    currentSegment?: ISegment;
 | 
			
		||||
}> = ({ change, currentSegment }) => {
 | 
			
		||||
    const changeRequestSegment =
 | 
			
		||||
        change.action === 'deleteSegment' ? undefined : change.payload;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <StyledCodeSection>
 | 
			
		||||
            <EventDiff
 | 
			
		||||
                entry={{
 | 
			
		||||
                    preData: omit(currentSegment, ['createdAt', 'createdBy']),
 | 
			
		||||
                    data: omit(changeRequestSegment, ['snapshot']),
 | 
			
		||||
                }}
 | 
			
		||||
            />
 | 
			
		||||
        </StyledCodeSection>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
interface IStrategyTooltipLinkProps {
 | 
			
		||||
    children?: React.ReactNode;
 | 
			
		||||
    name?: string;
 | 
			
		||||
    previousName?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const StyledContainer: FC<{ children?: React.ReactNode }> = styled('div')(
 | 
			
		||||
    ({ theme }) => ({
 | 
			
		||||
        display: 'grid',
 | 
			
		||||
        gridAutoFlow: 'column',
 | 
			
		||||
        gridTemplateColumns: 'auto 1fr',
 | 
			
		||||
        gap: theme.spacing(1),
 | 
			
		||||
        alignItems: 'center',
 | 
			
		||||
    }),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const ViewDiff = styled('span')(({ theme }) => ({
 | 
			
		||||
    color: theme.palette.primary.main,
 | 
			
		||||
    marginLeft: theme.spacing(1),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const Truncated = styled('div')(() => ({
 | 
			
		||||
    ...textTruncated,
 | 
			
		||||
    maxWidth: 500,
 | 
			
		||||
    display: 'flex',
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
export const SegmentTooltipLink: FC<IStrategyTooltipLinkProps> = ({
 | 
			
		||||
    name,
 | 
			
		||||
    previousName,
 | 
			
		||||
    children,
 | 
			
		||||
}) => (
 | 
			
		||||
    <StyledContainer>
 | 
			
		||||
        <Truncated>
 | 
			
		||||
            <NameWithChangeInfo previousName={previousName} newName={name} />
 | 
			
		||||
            <TooltipLink
 | 
			
		||||
                tooltip={children}
 | 
			
		||||
                tooltipProps={{
 | 
			
		||||
                    maxWidth: 500,
 | 
			
		||||
                    maxHeight: 600,
 | 
			
		||||
                }}
 | 
			
		||||
            >
 | 
			
		||||
                <ViewDiff>View Diff</ViewDiff>
 | 
			
		||||
            </TooltipLink>
 | 
			
		||||
        </Truncated>
 | 
			
		||||
    </StyledContainer>
 | 
			
		||||
);
 | 
			
		||||
@ -1,54 +0,0 @@
 | 
			
		||||
import { render } from 'utils/testRenderer';
 | 
			
		||||
import { screen } from '@testing-library/react';
 | 
			
		||||
import { StrategyDiff } from './StrategyTooltipLink.tsx';
 | 
			
		||||
import type { IFeatureStrategy } from 'interfaces/strategy';
 | 
			
		||||
import type { IChangeRequestUpdateStrategy } from 'component/changeRequest/changeRequest.types';
 | 
			
		||||
 | 
			
		||||
test('Should not render the `snapshot` property', async () => {
 | 
			
		||||
    const existingStrategy: IFeatureStrategy = {
 | 
			
		||||
        name: 'flexibleRollout',
 | 
			
		||||
        constraints: [],
 | 
			
		||||
        variants: [],
 | 
			
		||||
        parameters: {
 | 
			
		||||
            groupId: 'aaa',
 | 
			
		||||
            rollout: '71',
 | 
			
		||||
            stickiness: 'default',
 | 
			
		||||
        },
 | 
			
		||||
        sortOrder: 0,
 | 
			
		||||
        id: '31572930-2db7-461f-813b-3eedc200cb33',
 | 
			
		||||
        title: '',
 | 
			
		||||
        disabled: false,
 | 
			
		||||
        segments: [],
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const change: IChangeRequestUpdateStrategy = {
 | 
			
		||||
        id: 39,
 | 
			
		||||
        action: 'updateStrategy' as const,
 | 
			
		||||
        payload: {
 | 
			
		||||
            id: '31572930-2db7-461f-813b-3eedc200cb33',
 | 
			
		||||
            name: 'flexibleRollout',
 | 
			
		||||
            title: '',
 | 
			
		||||
            disabled: false,
 | 
			
		||||
            segments: [],
 | 
			
		||||
            snapshot: existingStrategy,
 | 
			
		||||
            variants: [],
 | 
			
		||||
            parameters: {
 | 
			
		||||
                groupId: 'aaa',
 | 
			
		||||
                rollout: '38',
 | 
			
		||||
                stickiness: 'default',
 | 
			
		||||
            },
 | 
			
		||||
            constraints: [],
 | 
			
		||||
        },
 | 
			
		||||
        createdAt: new Date('2024-01-18T07:58:36.314Z'),
 | 
			
		||||
        createdBy: {
 | 
			
		||||
            id: 1,
 | 
			
		||||
            username: 'admin',
 | 
			
		||||
            imageUrl:
 | 
			
		||||
                'https://gravatar.com/avatar/8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918?s=42&d=retro&r=g',
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    render(<StrategyDiff change={change} currentStrategy={existingStrategy} />);
 | 
			
		||||
 | 
			
		||||
    expect(screen.queryByText(/snapshot/)).toBeNull();
 | 
			
		||||
});
 | 
			
		||||
@ -1,125 +0,0 @@
 | 
			
		||||
// deprecated: remove with flag crDiffView
 | 
			
		||||
import type {
 | 
			
		||||
    IChangeRequestAddStrategy,
 | 
			
		||||
    IChangeRequestDeleteStrategy,
 | 
			
		||||
    IChangeRequestUpdateStrategy,
 | 
			
		||||
} from 'component/changeRequest/changeRequest.types';
 | 
			
		||||
import type React from 'react';
 | 
			
		||||
import type { FC } from 'react';
 | 
			
		||||
import {
 | 
			
		||||
    formatStrategyName,
 | 
			
		||||
    GetFeatureStrategyIcon,
 | 
			
		||||
} from 'utils/strategyNames';
 | 
			
		||||
import { EventDiff } from 'component/events/EventDiff/EventDiff';
 | 
			
		||||
import omit from 'lodash.omit';
 | 
			
		||||
import { TooltipLink } from 'component/common/TooltipLink/TooltipLink';
 | 
			
		||||
import { Typography, styled } from '@mui/material';
 | 
			
		||||
import type { IFeatureStrategy } from 'interfaces/strategy';
 | 
			
		||||
import { textTruncated } from 'themes/themeStyles';
 | 
			
		||||
import { NameWithChangeInfo } from '../Changes/Change/NameWithChangeInfo/NameWithChangeInfo.tsx';
 | 
			
		||||
 | 
			
		||||
const StyledCodeSection = styled('div')(({ theme }) => ({
 | 
			
		||||
    overflowX: 'auto',
 | 
			
		||||
    '& code': {
 | 
			
		||||
        wordWrap: 'break-word',
 | 
			
		||||
        whiteSpace: 'pre-wrap',
 | 
			
		||||
        fontFamily: 'monospace',
 | 
			
		||||
        lineHeight: 1.5,
 | 
			
		||||
        fontSize: theme.fontSizes.smallBody,
 | 
			
		||||
    },
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const sortSegments = <T extends { segments?: number[] }>(
 | 
			
		||||
    item?: T,
 | 
			
		||||
): T | undefined => {
 | 
			
		||||
    if (!item || !item.segments) {
 | 
			
		||||
        return item;
 | 
			
		||||
    }
 | 
			
		||||
    return {
 | 
			
		||||
        ...item,
 | 
			
		||||
        segments: [...item.segments].sort((a, b) => a - b),
 | 
			
		||||
    };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const StrategyDiff: FC<{
 | 
			
		||||
    change:
 | 
			
		||||
        | IChangeRequestAddStrategy
 | 
			
		||||
        | IChangeRequestUpdateStrategy
 | 
			
		||||
        | IChangeRequestDeleteStrategy;
 | 
			
		||||
    currentStrategy?: IFeatureStrategy;
 | 
			
		||||
}> = ({ change, currentStrategy }) => {
 | 
			
		||||
    const changeRequestStrategy =
 | 
			
		||||
        change.action === 'deleteStrategy' ? undefined : change.payload;
 | 
			
		||||
 | 
			
		||||
    const sortedCurrentStrategy = sortSegments(currentStrategy);
 | 
			
		||||
    const sortedChangeRequestStrategy = sortSegments(changeRequestStrategy);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
        <StyledCodeSection>
 | 
			
		||||
            <EventDiff
 | 
			
		||||
                entry={{
 | 
			
		||||
                    preData: omit(sortedCurrentStrategy, 'sortOrder'),
 | 
			
		||||
                    data: omit(sortedChangeRequestStrategy, 'snapshot'),
 | 
			
		||||
                }}
 | 
			
		||||
            />
 | 
			
		||||
        </StyledCodeSection>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
interface IStrategyTooltipLinkProps {
 | 
			
		||||
    name: string;
 | 
			
		||||
    title?: string;
 | 
			
		||||
    previousTitle?: string;
 | 
			
		||||
    children?: React.ReactNode;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const StyledContainer: FC<{ children?: React.ReactNode }> = styled('div')(
 | 
			
		||||
    ({ theme }) => ({
 | 
			
		||||
        display: 'grid',
 | 
			
		||||
        gridAutoFlow: 'column',
 | 
			
		||||
        gridTemplateColumns: 'auto 1fr',
 | 
			
		||||
        gap: theme.spacing(1),
 | 
			
		||||
        alignItems: 'center',
 | 
			
		||||
    }),
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const ViewDiff = styled('span')(({ theme }) => ({
 | 
			
		||||
    color: theme.palette.primary.main,
 | 
			
		||||
    marginLeft: theme.spacing(1),
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
const Truncated = styled('div')(() => ({
 | 
			
		||||
    ...textTruncated,
 | 
			
		||||
    maxWidth: 500,
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
export const StrategyTooltipLink: FC<IStrategyTooltipLinkProps> = ({
 | 
			
		||||
    name,
 | 
			
		||||
    title,
 | 
			
		||||
    previousTitle,
 | 
			
		||||
    children,
 | 
			
		||||
}) => {
 | 
			
		||||
    return (
 | 
			
		||||
        <StyledContainer>
 | 
			
		||||
            <GetFeatureStrategyIcon strategyName={name} />
 | 
			
		||||
            <Truncated>
 | 
			
		||||
                <Typography component='span'>
 | 
			
		||||
                    {formatStrategyName(name)}
 | 
			
		||||
                </Typography>
 | 
			
		||||
                <TooltipLink
 | 
			
		||||
                    tooltip={children}
 | 
			
		||||
                    tooltipProps={{
 | 
			
		||||
                        maxWidth: 500,
 | 
			
		||||
                        maxHeight: 600,
 | 
			
		||||
                    }}
 | 
			
		||||
                >
 | 
			
		||||
                    <ViewDiff>View Diff</ViewDiff>
 | 
			
		||||
                </TooltipLink>
 | 
			
		||||
                <NameWithChangeInfo
 | 
			
		||||
                    newName={title}
 | 
			
		||||
                    previousName={previousTitle}
 | 
			
		||||
                />
 | 
			
		||||
            </Truncated>
 | 
			
		||||
        </StyledContainer>
 | 
			
		||||
    );
 | 
			
		||||
};
 | 
			
		||||
@ -89,7 +89,6 @@ export type UiFlags = {
 | 
			
		||||
    lifecycleMetrics?: boolean;
 | 
			
		||||
    createFlagDialogCache?: boolean;
 | 
			
		||||
    impactMetrics?: boolean;
 | 
			
		||||
    crDiffView?: boolean;
 | 
			
		||||
    changeRequestApproverEmails?: boolean;
 | 
			
		||||
    reportUnknownFlags?: boolean;
 | 
			
		||||
    lifecycleGraphs?: boolean;
 | 
			
		||||
 | 
			
		||||
@ -58,7 +58,6 @@ export type IFlagKey =
 | 
			
		||||
    | 'customMetrics'
 | 
			
		||||
    | 'impactMetrics'
 | 
			
		||||
    | 'createFlagDialogCache'
 | 
			
		||||
    | 'crDiffView'
 | 
			
		||||
    | 'changeRequestApproverEmails'
 | 
			
		||||
    | 'paygTrialEvents'
 | 
			
		||||
    | 'paygInstanceStatsEvents'
 | 
			
		||||
@ -274,10 +273,6 @@ const flags: IFlags = {
 | 
			
		||||
        process.env.UNLEASH_EXPERIMENTAL_CHANGE_REQUEST_APPROVER_EMAILS,
 | 
			
		||||
        false,
 | 
			
		||||
    ),
 | 
			
		||||
    crDiffView: parseEnvVarBoolean(
 | 
			
		||||
        process.env.UNLEASH_EXPERIMENTAL_CR_DIFF_VIEW,
 | 
			
		||||
        false,
 | 
			
		||||
    ),
 | 
			
		||||
    impactMetrics: parseEnvVarBoolean(
 | 
			
		||||
        process.env.UNLEASH_EXPERIMENTAL_IMPACT_METRICS,
 | 
			
		||||
        false,
 | 
			
		||||
 | 
			
		||||
@ -55,7 +55,6 @@ process.nextTick(async () => {
 | 
			
		||||
                        customMetrics: true,
 | 
			
		||||
                        lifecycleMetrics: true,
 | 
			
		||||
                        impactMetrics: true,
 | 
			
		||||
                        crDiffView: true,
 | 
			
		||||
                        paygTrialEvents: true,
 | 
			
		||||
                        lifecycleGraphs: true,
 | 
			
		||||
                        addConfiguration: true,
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user