1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-11-10 01:19:53 +01:00
unleash.unleash/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/FeatureChange.tsx
Gastón Fournier abe160eb7d
feat: Unleash v7 ESM migration (#9877)
We're migrating to ESM, which will allow us to import the latest
versions of our dependencies.

Co-Authored-By: Christopher Kolstad <chriswk@getunleash.io>
2025-05-14 09:47:12 +02:00

213 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { FC, ReactNode } from 'react';
import type {
IFeatureChange,
ChangeRequestType,
IChangeRequestFeature,
} from '../../../changeRequest.types';
import { objectId } from 'utils/objectId';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { Alert, Box, styled } from '@mui/material';
import { ToggleStatusChange } from './ToggleStatusChange.tsx';
import { StrategyChange } from './StrategyChange.tsx';
import { VariantPatch } from './VariantPatch/VariantPatch.tsx';
import { EnvironmentStrategyExecutionOrder } from './EnvironmentStrategyExecutionOrder/EnvironmentStrategyExecutionOrder.tsx';
import { ArchiveFeatureChange } from './ArchiveFeatureChange.tsx';
import { DependencyChange } from './DependencyChange.tsx';
import { Link } from 'react-router-dom';
import { ReleasePlanChange } from './ReleasePlanChange.tsx';
const StyledSingleChangeBox = styled(Box, {
shouldForwardProp: (prop: string) => !prop.startsWith('$'),
})<{
$hasConflict: boolean;
$isAfterWarning: boolean;
$isLast: boolean;
$isInConflictFeature: boolean;
}>(
({
theme,
$hasConflict,
$isInConflictFeature,
$isAfterWarning,
$isLast,
}) => ({
overflow: 'hidden',
borderLeft: '1px solid',
borderRight: '1px solid',
borderTop: '1px solid',
borderBottom: $isLast ? '1px solid' : 'none',
borderRadius: $isLast
? `0 0
${theme.shape.borderRadiusLarge}px ${theme.shape.borderRadiusLarge}px`
: 0,
borderColor:
$hasConflict || $isInConflictFeature
? theme.palette.warning.border
: theme.palette.divider,
borderTopColor:
($hasConflict || $isAfterWarning) && !$isInConflictFeature
? theme.palette.warning.border
: theme.palette.divider,
}),
);
const StyledAlert = styled(Alert)(({ theme }) => ({
borderRadius: 0,
padding: theme.spacing(1, 2),
'&.MuiAlert-standardWarning': {
borderStyle: 'none none solid none',
},
}));
const InlineList = styled('ul')(({ theme }) => ({
display: 'inline',
padding: 0,
li: { display: 'inline' },
'li + li::before': {
content: '", "',
},
}));
const ChangeInnerBox = styled(Box)(({ theme }) => ({
padding: theme.spacing(3),
'&:has(.delete-strategy-information-wrapper)': {
backgroundColor: theme.palette.error.light,
},
}));
export const FeatureChange: FC<{
actions: ReactNode;
index: number;
changeRequest: ChangeRequestType;
change: IFeatureChange;
feature: IChangeRequestFeature;
onNavigate?: () => void;
}> = ({ index, change, feature, changeRequest, actions, onNavigate }) => {
const lastIndex = feature.defaultChange
? feature.changes.length + 1
: feature.changes.length;
return (
<StyledSingleChangeBox
key={objectId(change)}
$hasConflict={Boolean(change.conflict || change.scheduleConflicts)}
$isInConflictFeature={Boolean(feature.conflict)}
$isAfterWarning={Boolean(
feature.changes[index - 1]?.conflict ||
feature.changes[index - 1]?.scheduleConflicts,
)}
$isLast={index + 1 === lastIndex}
>
<ConditionallyRender
condition={Boolean(change.conflict) && !feature.conflict}
show={
<StyledAlert severity='warning'>
<strong>Conflict!</strong> This change cant be applied.{' '}
{change.conflict}.
</StyledAlert>
}
/>
<ConditionallyRender
condition={Boolean(change.scheduleConflicts)}
show={
<StyledAlert severity='warning'>
<strong>Potential conflict!</strong> This change would
create conflicts with the following scheduled change
request(s):{' '}
<InlineList>
{(
change.scheduleConflicts ?? {
changeRequests: [],
}
).changeRequests.map(({ id, title }) => {
const text = title
? `#${id} (${title})`
: `#${id}`;
return (
<li key={id}>
<Link
to={`/projects/${changeRequest.project}/change-requests/${id}`}
target='_blank'
rel='noopener noreferrer'
title={`Change request ${id}`}
>
{text}
</Link>
</li>
);
})}
.
</InlineList>
</StyledAlert>
}
/>
<ChangeInnerBox>
{(change.action === 'addDependency' ||
change.action === 'deleteDependency') && (
<DependencyChange
actions={actions}
change={change}
projectId={changeRequest.project}
onNavigate={onNavigate}
/>
)}
{change.action === 'updateEnabled' && (
<ToggleStatusChange
enabled={change.payload.enabled}
actions={actions}
/>
)}
{change.action === 'archiveFeature' && (
<ArchiveFeatureChange actions={actions} />
)}
{change.action === 'addStrategy' ||
change.action === 'deleteStrategy' ||
change.action === 'updateStrategy' ? (
<StrategyChange
actions={actions}
change={change}
featureName={feature.name}
environmentName={changeRequest.environment}
projectId={changeRequest.project}
changeRequestState={changeRequest.state}
/>
) : null}
{change.action === 'patchVariant' && (
<VariantPatch
feature={feature.name}
project={changeRequest.project}
changeRequestState={changeRequest.state}
environment={changeRequest.environment}
change={change}
actions={actions}
/>
)}
{change.action === 'reorderStrategy' && (
<EnvironmentStrategyExecutionOrder
feature={feature.name}
project={changeRequest.project}
environment={changeRequest.environment}
change={change}
actions={actions}
/>
)}
{(change.action === 'addReleasePlan' ||
change.action === 'deleteReleasePlan' ||
change.action === 'startMilestone') && (
<ReleasePlanChange
actions={actions}
change={change}
featureName={feature.name}
environmentName={changeRequest.environment}
projectId={changeRequest.project}
changeRequestState={changeRequest.state}
/>
)}
</ChangeInnerBox>
</StyledSingleChangeBox>
);
};