mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +01:00 
			
		
		
		
	hide discard for nonauthors (#2634)
This commit is contained in:
		
							parent
							
								
									1be2483e6a
								
							
						
					
					
						commit
						2107834768
					
				@ -1,30 +1,9 @@
 | 
				
			|||||||
import React, { FC, VFC } from 'react';
 | 
					import React, { VFC } from 'react';
 | 
				
			||||||
import { Alert, Box, styled } from '@mui/material';
 | 
					import { Box } from '@mui/material';
 | 
				
			||||||
import { ChangeRequestFeatureToggleChange } from '../ChangeRequestOverview/ChangeRequestFeatureToggleChange/ChangeRequestFeatureToggleChange';
 | 
					import type { IChangeRequest } from '../changeRequest.types';
 | 
				
			||||||
import { objectId } from 'utils/objectId';
 | 
					import { FeatureToggleChanges } from './Changes/FeatureToggleChanges';
 | 
				
			||||||
import { ToggleStatusChange } from '../ChangeRequestOverview/ChangeRequestFeatureToggleChange/ToggleStatusChange';
 | 
					import { Change } from './Changes/Change/Change';
 | 
				
			||||||
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
 | 
					import { DiscardContainer } from './Changes/Change/Discard';
 | 
				
			||||||
import { formatUnknownError } from 'utils/formatUnknownError';
 | 
					 | 
				
			||||||
import useToast from 'hooks/useToast';
 | 
					 | 
				
			||||||
import type {
 | 
					 | 
				
			||||||
    IChange,
 | 
					 | 
				
			||||||
    IChangeRequest,
 | 
					 | 
				
			||||||
    IChangeRequestFeature,
 | 
					 | 
				
			||||||
} from '../changeRequest.types';
 | 
					 | 
				
			||||||
import { hasNameField } from '../changeRequest.types';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
    Discard,
 | 
					 | 
				
			||||||
    StrategyAddedChange,
 | 
					 | 
				
			||||||
    StrategyDeletedChange,
 | 
					 | 
				
			||||||
    StrategyEditedChange,
 | 
					 | 
				
			||||||
} from '../ChangeRequestOverview/ChangeRequestFeatureToggleChange/StrategyChange';
 | 
					 | 
				
			||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
					 | 
				
			||||||
import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
 | 
					 | 
				
			||||||
import { StrategyExecution } from '../../feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution';
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
    CodeSnippetPopover,
 | 
					 | 
				
			||||||
    PopoverDiff,
 | 
					 | 
				
			||||||
} from './CodeSnippetPopover/CodeSnippetPopover';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IChangeRequestProps {
 | 
					interface IChangeRequestProps {
 | 
				
			||||||
    changeRequest: IChangeRequest;
 | 
					    changeRequest: IChangeRequest;
 | 
				
			||||||
@ -32,190 +11,15 @@ interface IChangeRequestProps {
 | 
				
			|||||||
    onNavigate?: () => 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',
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
}));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const showDiscard =
 | 
					 | 
				
			||||||
        allowChangeRequestActions &&
 | 
					 | 
				
			||||||
        !['Cancelled', 'Applied'].includes(changeRequest.state) &&
 | 
					 | 
				
			||||||
        changeRequest.features.flatMap(feature => feature.changes).length > 1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					 | 
				
			||||||
        <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 can’t 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} />}
 | 
					 | 
				
			||||||
                            />
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    />
 | 
					 | 
				
			||||||
                )}
 | 
					 | 
				
			||||||
                {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>
 | 
					 | 
				
			||||||
    );
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const ChangeRequest: VFC<IChangeRequestProps> = ({
 | 
					export const ChangeRequest: VFC<IChangeRequestProps> = ({
 | 
				
			||||||
    changeRequest,
 | 
					    changeRequest,
 | 
				
			||||||
    onRefetch,
 | 
					    onRefetch,
 | 
				
			||||||
    onNavigate,
 | 
					    onNavigate,
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
    const { discardChange } = useChangeRequestApi();
 | 
					 | 
				
			||||||
    const { setToastData, setToastApiError } = useToast();
 | 
					 | 
				
			||||||
    const onDiscard = (id: number) => async () => {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            await discardChange(changeRequest.project, changeRequest.id, id);
 | 
					 | 
				
			||||||
            setToastData({
 | 
					 | 
				
			||||||
                title: 'Change discarded from change request draft.',
 | 
					 | 
				
			||||||
                type: 'success',
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            onRefetch?.();
 | 
					 | 
				
			||||||
        } catch (error: unknown) {
 | 
					 | 
				
			||||||
            setToastApiError(formatUnknownError(error));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Box>
 | 
					        <Box>
 | 
				
			||||||
            {changeRequest.features?.map(feature => (
 | 
					            {changeRequest.features?.map(feature => (
 | 
				
			||||||
                <ChangeRequestFeatureToggleChange
 | 
					                <FeatureToggleChanges
 | 
				
			||||||
                    key={feature.name}
 | 
					                    key={feature.name}
 | 
				
			||||||
                    featureName={feature.name}
 | 
					                    featureName={feature.name}
 | 
				
			||||||
                    projectId={changeRequest.project}
 | 
					                    projectId={changeRequest.project}
 | 
				
			||||||
@ -225,14 +29,20 @@ export const ChangeRequest: VFC<IChangeRequestProps> = ({
 | 
				
			|||||||
                    {feature.changes.map((change, index) => (
 | 
					                    {feature.changes.map((change, index) => (
 | 
				
			||||||
                        <Change
 | 
					                        <Change
 | 
				
			||||||
                            key={index}
 | 
					                            key={index}
 | 
				
			||||||
                            onDiscard={onDiscard(change.id)}
 | 
					                            discard={
 | 
				
			||||||
 | 
					                                <DiscardContainer
 | 
				
			||||||
 | 
					                                    changeRequest={changeRequest}
 | 
				
			||||||
 | 
					                                    changeId={change.id}
 | 
				
			||||||
 | 
					                                    onPostDiscard={onRefetch}
 | 
				
			||||||
 | 
					                                />
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
                            index={index}
 | 
					                            index={index}
 | 
				
			||||||
                            changeRequest={changeRequest}
 | 
					                            changeRequest={changeRequest}
 | 
				
			||||||
                            change={change}
 | 
					                            change={change}
 | 
				
			||||||
                            feature={feature}
 | 
					                            feature={feature}
 | 
				
			||||||
                        />
 | 
					                        />
 | 
				
			||||||
                    ))}
 | 
					                    ))}
 | 
				
			||||||
                </ChangeRequestFeatureToggleChange>
 | 
					                </FeatureToggleChanges>
 | 
				
			||||||
            ))}
 | 
					            ))}
 | 
				
			||||||
        </Box>
 | 
					        </Box>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,144 @@
 | 
				
			|||||||
 | 
					import React, { FC, ReactNode } from 'react';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    hasNameField,
 | 
				
			||||||
 | 
					    IChange,
 | 
				
			||||||
 | 
					    IChangeRequest,
 | 
				
			||||||
 | 
					    IChangeRequestFeature,
 | 
				
			||||||
 | 
					} from '../../../changeRequest.types';
 | 
				
			||||||
 | 
					import { objectId } from 'utils/objectId';
 | 
				
			||||||
 | 
					import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
				
			||||||
 | 
					import { Alert, Box, styled } from '@mui/material';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    CodeSnippetPopover,
 | 
				
			||||||
 | 
					    PopoverDiff,
 | 
				
			||||||
 | 
					} from '../../CodeSnippetPopover/CodeSnippetPopover';
 | 
				
			||||||
 | 
					import { StrategyExecution } from '../../../../feature/FeatureView/FeatureOverview/FeatureOverviewEnvironments/FeatureOverviewEnvironment/EnvironmentAccordionBody/StrategyDraggableItem/StrategyItem/StrategyExecution/StrategyExecution';
 | 
				
			||||||
 | 
					import { ToggleStatusChange } from './ToggleStatusChange';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					    StrategyAddedChange,
 | 
				
			||||||
 | 
					    StrategyDeletedChange,
 | 
				
			||||||
 | 
					    StrategyEditedChange,
 | 
				
			||||||
 | 
					} from './StrategyChange';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const Change: FC<{
 | 
				
			||||||
 | 
					    discard: ReactNode;
 | 
				
			||||||
 | 
					    index: number;
 | 
				
			||||||
 | 
					    changeRequest: IChangeRequest;
 | 
				
			||||||
 | 
					    change: IChange;
 | 
				
			||||||
 | 
					    feature: IChangeRequestFeature;
 | 
				
			||||||
 | 
					}> = ({ index, change, feature, changeRequest, discard }) => {
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <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 can’t be applied.{' '}
 | 
				
			||||||
 | 
					                        {change.conflict}.
 | 
				
			||||||
 | 
					                    </StyledAlert>
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					            <Box sx={{ p: 2 }}>
 | 
				
			||||||
 | 
					                {change.action === 'updateEnabled' && (
 | 
				
			||||||
 | 
					                    <ToggleStatusChange
 | 
				
			||||||
 | 
					                        enabled={change.payload.enabled}
 | 
				
			||||||
 | 
					                        discard={discard}
 | 
				
			||||||
 | 
					                    />
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					                {change.action === 'addStrategy' && (
 | 
				
			||||||
 | 
					                    <>
 | 
				
			||||||
 | 
					                        <StrategyAddedChange discard={discard}>
 | 
				
			||||||
 | 
					                            <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={discard}>
 | 
				
			||||||
 | 
					                        {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={discard}>
 | 
				
			||||||
 | 
					                            <CodeSnippetPopover change={change}>
 | 
				
			||||||
 | 
					                                <PopoverDiff
 | 
				
			||||||
 | 
					                                    change={change}
 | 
				
			||||||
 | 
					                                    feature={feature.name}
 | 
				
			||||||
 | 
					                                    environmentName={changeRequest.environment}
 | 
				
			||||||
 | 
					                                    project={changeRequest.project}
 | 
				
			||||||
 | 
					                                />
 | 
				
			||||||
 | 
					                            </CodeSnippetPopover>
 | 
				
			||||||
 | 
					                        </StrategyEditedChange>
 | 
				
			||||||
 | 
					                        <StrategyExecution strategy={change.payload} />
 | 
				
			||||||
 | 
					                    </>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					            </Box>
 | 
				
			||||||
 | 
					        </StyledSingleChangeBox>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					import React, { FC } from 'react';
 | 
				
			||||||
 | 
					import { IChangeRequest } from '../../../changeRequest.types';
 | 
				
			||||||
 | 
					import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
 | 
				
			||||||
 | 
					import useToast from 'hooks/useToast';
 | 
				
			||||||
 | 
					import { formatUnknownError } from 'utils/formatUnknownError';
 | 
				
			||||||
 | 
					import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
				
			||||||
 | 
					import { useChangeRequestsEnabled } from 'hooks/useChangeRequestsEnabled';
 | 
				
			||||||
 | 
					import { useAuthUser } from 'hooks/api/getters/useAuth/useAuthUser';
 | 
				
			||||||
 | 
					import { changesCount } from '../../../changesCount';
 | 
				
			||||||
 | 
					import { Box, Link, styled } from '@mui/material';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const useShowDiscard = (changeRequest: IChangeRequest) => {
 | 
				
			||||||
 | 
					    const { isChangeRequestConfigured } = useChangeRequestsEnabled(
 | 
				
			||||||
 | 
					        changeRequest.project
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    const allowChangeRequestActions = isChangeRequestConfigured(
 | 
				
			||||||
 | 
					        changeRequest.environment
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    const isPending = !['Cancelled', 'Applied'].includes(changeRequest.state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const { user } = useAuthUser();
 | 
				
			||||||
 | 
					    const isAuthor = user?.id === changeRequest.createdBy.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const showDiscard =
 | 
				
			||||||
 | 
					        allowChangeRequestActions &&
 | 
				
			||||||
 | 
					        isPending &&
 | 
				
			||||||
 | 
					        isAuthor &&
 | 
				
			||||||
 | 
					        changesCount(changeRequest) > 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return showDiscard;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledLink = styled(Link)(() => ({
 | 
				
			||||||
 | 
					    textDecoration: 'none',
 | 
				
			||||||
 | 
					    '&:hover, &:focus': {
 | 
				
			||||||
 | 
					        textDecoration: 'underline',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const Discard: FC<{ onDiscard: () => void }> = ({ onDiscard }) => (
 | 
				
			||||||
 | 
					    <Box>
 | 
				
			||||||
 | 
					        <StyledLink onClick={onDiscard}>Discard</StyledLink>
 | 
				
			||||||
 | 
					    </Box>
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const DiscardContainer: FC<{
 | 
				
			||||||
 | 
					    changeRequest: IChangeRequest;
 | 
				
			||||||
 | 
					    changeId: number;
 | 
				
			||||||
 | 
					    onPostDiscard?: () => void;
 | 
				
			||||||
 | 
					}> = ({ changeRequest, changeId, onPostDiscard }) => {
 | 
				
			||||||
 | 
					    const showDiscard = useShowDiscard(changeRequest);
 | 
				
			||||||
 | 
					    const { discardChange } = useChangeRequestApi();
 | 
				
			||||||
 | 
					    const { setToastData, setToastApiError } = useToast();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const onDiscard = (id: number) => async () => {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            await discardChange(changeRequest.project, changeRequest.id, id);
 | 
				
			||||||
 | 
					            setToastData({
 | 
				
			||||||
 | 
					                title: 'Change discarded from change request draft.',
 | 
				
			||||||
 | 
					                type: 'success',
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            onPostDiscard?.();
 | 
				
			||||||
 | 
					        } catch (error: unknown) {
 | 
				
			||||||
 | 
					            setToastApiError(formatUnknownError(error));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					        <ConditionallyRender
 | 
				
			||||||
 | 
					            condition={showDiscard}
 | 
				
			||||||
 | 
					            show={<Discard onDiscard={onDiscard(changeId)} />}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -1,10 +1,6 @@
 | 
				
			|||||||
import { Box, Link, styled, Typography } from '@mui/material';
 | 
					import { Box, Link, styled, Typography } from '@mui/material';
 | 
				
			||||||
import { FC, ReactNode } from 'react';
 | 
					import { FC, ReactNode } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IStrategyChangeProps {
 | 
					 | 
				
			||||||
    onDiscard: () => void;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const ChangeItemWrapper = styled(Box)(({ theme }) => ({
 | 
					export const ChangeItemWrapper = styled(Box)(({ theme }) => ({
 | 
				
			||||||
    display: 'flex',
 | 
					    display: 'flex',
 | 
				
			||||||
    justifyContent: 'space-between',
 | 
					    justifyContent: 'space-between',
 | 
				
			||||||
@ -16,19 +12,6 @@ const ChangeItemInfo: FC = styled(Box)(({ theme }) => ({
 | 
				
			|||||||
    gap: theme.spacing(1),
 | 
					    gap: theme.spacing(1),
 | 
				
			||||||
}));
 | 
					}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledLink = styled(Link)(() => ({
 | 
					 | 
				
			||||||
    textDecoration: 'none',
 | 
					 | 
				
			||||||
    '&:hover, &:focus': {
 | 
					 | 
				
			||||||
        textDecoration: 'underline',
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
}));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const Discard: FC<IStrategyChangeProps> = ({ onDiscard }) => (
 | 
					 | 
				
			||||||
    <Box>
 | 
					 | 
				
			||||||
        <StyledLink onClick={onDiscard}>Discard</StyledLink>
 | 
					 | 
				
			||||||
    </Box>
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export const StrategyAddedChange: FC<{ discard?: ReactNode }> = ({
 | 
					export const StrategyAddedChange: FC<{ discard?: ReactNode }> = ({
 | 
				
			||||||
    children,
 | 
					    children,
 | 
				
			||||||
    discard,
 | 
					    discard,
 | 
				
			||||||
@ -3,16 +3,20 @@ import { Link } from 'react-router-dom';
 | 
				
			|||||||
import { Alert, Box, Card, Typography } from '@mui/material';
 | 
					import { Alert, Box, Card, Typography } from '@mui/material';
 | 
				
			||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
					import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface IChangeRequestToggleChange {
 | 
					interface IFeatureToggleChanges {
 | 
				
			||||||
    featureName: string;
 | 
					    featureName: string;
 | 
				
			||||||
    projectId: string;
 | 
					    projectId: string;
 | 
				
			||||||
    conflict?: string;
 | 
					    conflict?: string;
 | 
				
			||||||
    onNavigate?: () => void;
 | 
					    onNavigate?: () => void;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ChangeRequestFeatureToggleChange: FC<
 | 
					export const FeatureToggleChanges: FC<IFeatureToggleChanges> = ({
 | 
				
			||||||
    IChangeRequestToggleChange
 | 
					    featureName,
 | 
				
			||||||
> = ({ featureName, projectId, conflict, onNavigate, children }) => (
 | 
					    projectId,
 | 
				
			||||||
 | 
					    conflict,
 | 
				
			||||||
 | 
					    onNavigate,
 | 
				
			||||||
 | 
					    children,
 | 
				
			||||||
 | 
					}) => (
 | 
				
			||||||
    <Card
 | 
					    <Card
 | 
				
			||||||
        elevation={0}
 | 
					        elevation={0}
 | 
				
			||||||
        sx={theme => ({
 | 
					        sx={theme => ({
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user