Skip to content

Commit

Permalink
hide discard for nonauthors (#2634)
Browse files Browse the repository at this point in the history
  • Loading branch information
kwasniew committed Dec 8, 2022
1 parent 1be2483 commit 2107834
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 226 deletions.
220 changes: 15 additions & 205 deletions frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx
@@ -1,221 +1,25 @@
import React, { FC, VFC } from 'react';
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';
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';
import React, { VFC } from 'react';
import { Box } from '@mui/material';
import type { IChangeRequest } from '../changeRequest.types';
import { FeatureToggleChanges } from './Changes/FeatureToggleChanges';
import { Change } from './Changes/Change/Change';
import { DiscardContainer } from './Changes/Change/Discard';

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',
},
}));

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> = ({
changeRequest,
onRefetch,
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 (
<Box>
{changeRequest.features?.map(feature => (
<ChangeRequestFeatureToggleChange
<FeatureToggleChanges
key={feature.name}
featureName={feature.name}
projectId={changeRequest.project}
Expand All @@ -225,14 +29,20 @@ export const ChangeRequest: VFC<IChangeRequestProps> = ({
{feature.changes.map((change, index) => (
<Change
key={index}
onDiscard={onDiscard(change.id)}
discard={
<DiscardContainer
changeRequest={changeRequest}
changeId={change.id}
onPostDiscard={onRefetch}
/>
}
index={index}
changeRequest={changeRequest}
change={change}
feature={feature}
/>
))}
</ChangeRequestFeatureToggleChange>
</FeatureToggleChanges>
))}
</Box>
);
Expand Down
@@ -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 comments on commit 2107834

Please sign in to comment.