1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-06 00:07:44 +01:00
unleash.unleash/frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx

239 lines
9.2 KiB
TypeScript
Raw Normal View History

2022-11-29 14:59:04 +01:00
import React, { FC, VFC } from 'react';
2022-12-01 14:44:33 +01:00
import { Alert, Box, styled } from '@mui/material';
import { ChangeRequestFeatureToggleChange } from '../ChangeRequestOverview/ChangeRequestFeatureToggleChange/ChangeRequestFeatureToggleChange';
import { objectId } from 'utils/objectId';
import { ToggleStatusChange } from '../ChangeRequestOverview/ChangeRequestFeatureToggleChange/ToggleStatusChange';
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
import { formatUnknownError } from 'utils/formatUnknownError';
import useToast from 'hooks/useToast';
2022-11-29 14:59:04 +01:00
import type {
2022-12-01 14:44:33 +01:00
IChange,
2022-11-29 14:59:04 +01:00
IChangeRequest,
2022-12-01 14:44:33 +01:00
IChangeRequestFeature,
2022-11-29 14:59:04 +01:00
} from '../changeRequest.types';
2022-12-01 14:44:33 +01:00
import { hasNameField } from '../changeRequest.types';
import {
2022-11-23 12:16:20 +01:00
Discard,
StrategyAddedChange,
StrategyDeletedChange,
StrategyEditedChange,
} from '../ChangeRequestOverview/ChangeRequestFeatureToggleChange/StrategyChange';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
2022-12-01 14:44:33 +01:00
import { StrategyExecution } from '../../feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution';
import {
CodeSnippetPopover,
PopoverDiff,
} from './CodeSnippetPopover/CodeSnippetPopover';
interface IChangeRequestProps {
changeRequest: IChangeRequest;
onRefetch?: () => void;
onNavigate?: () => void;
}
const StyledSingleChangeBox = styled(Box, {
shouldForwardProp: (prop: string) => !prop.startsWith('$'),
})<{
$hasConflict: boolean;
$isAfterWarning: boolean;
$isLast: boolean;
$isInConflictFeature: boolean;
}>(
({
theme,
$hasConflict,
$isInConflictFeature,
$isAfterWarning,
$isLast,
}) => ({
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.dividerAlternative,
borderTopColor:
($hasConflict || $isAfterWarning) && !$isInConflictFeature
? theme.palette.warning.border
: theme.palette.dividerAlternative,
})
);
const StyledAlert = styled(Alert)(({ theme }) => ({
borderRadius: 0,
padding: theme.spacing(0, 2),
'&.MuiAlert-standardWarning': {
borderStyle: 'none none solid none',
},
}));
2022-12-01 14:44:33 +01:00
const Change: FC<{
onDiscard: () => Promise<void>;
index: number;
changeRequest: IChangeRequest;
change: IChange;
feature: IChangeRequestFeature;
}> = ({ index, change, feature, changeRequest, onDiscard }) => {
const { isChangeRequestConfigured } = useChangeRequestsEnabled(
changeRequest.project
);
const allowChangeRequestActions = isChangeRequestConfigured(
changeRequest.environment
);
2022-11-29 14:59:04 +01:00
2022-12-01 14:44:33 +01:00
const showDiscard =
allowChangeRequestActions &&
!['Cancelled', 'Applied'].includes(changeRequest.state) &&
changeRequest.features.flatMap(feature => feature.changes).length > 1;
2022-11-29 14:59:04 +01:00
return (
2022-12-01 14:44:33 +01:00
<StyledSingleChangeBox
key={objectId(change)}
$hasConflict={Boolean(change.conflict)}
$isInConflictFeature={Boolean(feature.conflict)}
$isAfterWarning={Boolean(feature.changes[index - 1]?.conflict)}
$isLast={index + 1 === feature.changes.length}
>
<ConditionallyRender
condition={Boolean(change.conflict) && !feature.conflict}
show={
<StyledAlert severity="warning">
<strong>Conflict!</strong> This change cant be applied.{' '}
{change.conflict}.
</StyledAlert>
}
/>
<Box sx={{ p: 2 }}>
{change.action === 'updateEnabled' && (
<ToggleStatusChange
enabled={change.payload.enabled}
discard={
<ConditionallyRender
condition={showDiscard}
show={<Discard onDiscard={onDiscard} />}
/>
}
2022-11-29 14:59:04 +01:00
/>
2022-12-01 14:44:33 +01:00
)}
{change.action === 'addStrategy' && (
<>
<StrategyAddedChange
discard={
<ConditionallyRender
condition={showDiscard}
show={<Discard onDiscard={onDiscard} />}
/>
}
>
<CodeSnippetPopover change={change}>
<PopoverDiff
change={change}
feature={feature.name}
environmentName={changeRequest.environment}
project={changeRequest.project}
/>
</CodeSnippetPopover>
</StrategyAddedChange>
<StrategyExecution strategy={change.payload} />
</>
)}
{change.action === 'deleteStrategy' && (
<StrategyDeletedChange
discard={
<ConditionallyRender
condition={showDiscard}
show={<Discard onDiscard={onDiscard} />}
/>
}
>
{hasNameField(change.payload) && (
<CodeSnippetPopover change={change}>
<PopoverDiff
change={change}
feature={feature.name}
environmentName={changeRequest.environment}
project={changeRequest.project}
/>
</CodeSnippetPopover>
)}
</StrategyDeletedChange>
)}
{change.action === 'updateStrategy' && (
<>
<StrategyEditedChange
discard={
<ConditionallyRender
condition={showDiscard}
show={<Discard onDiscard={onDiscard} />}
/>
}
>
<CodeSnippetPopover change={change}>
<PopoverDiff
change={change}
feature={feature.name}
environmentName={changeRequest.environment}
project={changeRequest.project}
/>
</CodeSnippetPopover>
</StrategyEditedChange>
<StrategyExecution strategy={change.payload} />
</>
)}
</Box>
</StyledSingleChangeBox>
2022-11-29 14:59:04 +01:00
);
};
export const ChangeRequest: VFC<IChangeRequestProps> = ({
changeRequest,
onRefetch,
onNavigate,
}) => {
2022-11-30 08:55:40 +01:00
const { discardChange } = useChangeRequestApi();
const { setToastData, setToastApiError } = useToast();
const onDiscard = (id: number) => async () => {
try {
2022-11-30 08:55:40 +01:00
await discardChange(changeRequest.project, changeRequest.id, id);
setToastData({
title: 'Change discarded from change request draft.',
type: 'success',
});
onRefetch?.();
} catch (error: unknown) {
setToastApiError(formatUnknownError(error));
}
};
2022-11-23 12:16:20 +01:00
return (
<Box>
2022-12-01 14:44:33 +01:00
{changeRequest.features?.map(feature => (
<ChangeRequestFeatureToggleChange
2022-12-01 14:44:33 +01:00
key={feature.name}
featureName={feature.name}
projectId={changeRequest.project}
onNavigate={onNavigate}
2022-12-01 14:44:33 +01:00
conflict={feature.conflict}
>
2022-12-01 14:44:33 +01:00
{feature.changes.map((change, index) => (
<Change
onDiscard={onDiscard(change.id)}
index={index}
changeRequest={changeRequest}
change={change}
feature={feature}
/>
))}
</ChangeRequestFeatureToggleChange>
))}
</Box>
);
};