mirror of
https://github.com/Unleash/unleash.git
synced 2025-08-27 13:49:10 +02: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