From 2107834768d369f0a91e8cd10b0a3fb2fe82a31f Mon Sep 17 00:00:00 2001 From: Mateusz Kwasniewski Date: Thu, 8 Dec 2022 13:24:15 +0100 Subject: [PATCH] hide discard for nonauthors (#2634) --- .../ChangeRequest/ChangeRequest.tsx | 220 ++---------------- .../ChangeRequest/Changes/Change/Change.tsx | 144 ++++++++++++ .../ChangeRequest/Changes/Change/Discard.tsx | 74 ++++++ .../Changes/Change}/StrategyChange.tsx | 17 -- .../Changes/Change}/ToggleStatusChange.tsx | 0 .../Changes/FeatureToggleChanges.tsx} | 12 +- 6 files changed, 241 insertions(+), 226 deletions(-) create mode 100644 frontend/src/component/changeRequest/ChangeRequest/Changes/Change/Change.tsx create mode 100644 frontend/src/component/changeRequest/ChangeRequest/Changes/Change/Discard.tsx rename frontend/src/component/changeRequest/{ChangeRequestOverview/ChangeRequestFeatureToggleChange => ChangeRequest/Changes/Change}/StrategyChange.tsx (81%) rename frontend/src/component/changeRequest/{ChangeRequestOverview/ChangeRequestFeatureToggleChange => ChangeRequest/Changes/Change}/ToggleStatusChange.tsx (100%) rename frontend/src/component/changeRequest/{ChangeRequestOverview/ChangeRequestFeatureToggleChange/ChangeRequestFeatureToggleChange.tsx => ChangeRequest/Changes/FeatureToggleChanges.tsx} (92%) diff --git a/frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx b/frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx index 778ba31460..f2c2e3793a 100644 --- a/frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx +++ b/frontend/src/component/changeRequest/ChangeRequest/ChangeRequest.tsx @@ -1,30 +1,9 @@ -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; @@ -32,190 +11,15 @@ interface IChangeRequestProps { 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; - 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 ( - - - Conflict! This change can’t be applied.{' '} - {change.conflict}. - - } - /> - - {change.action === 'updateEnabled' && ( - } - /> - } - /> - )} - {change.action === 'addStrategy' && ( - <> - } - /> - } - > - - - - - - - )} - {change.action === 'deleteStrategy' && ( - } - /> - } - > - {hasNameField(change.payload) && ( - - - - )} - - )} - {change.action === 'updateStrategy' && ( - <> - } - /> - } - > - - - - - - - )} - - - ); -}; - export const ChangeRequest: VFC = ({ 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 ( {changeRequest.features?.map(feature => ( - = ({ {feature.changes.map((change, index) => ( + } index={index} changeRequest={changeRequest} change={change} feature={feature} /> ))} - + ))} ); diff --git a/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/Change.tsx b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/Change.tsx new file mode 100644 index 0000000000..5f82baddb6 --- /dev/null +++ b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/Change.tsx @@ -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 ( + + + Conflict! This change can’t be applied.{' '} + {change.conflict}. + + } + /> + + {change.action === 'updateEnabled' && ( + + )} + {change.action === 'addStrategy' && ( + <> + + + + + + + + )} + {change.action === 'deleteStrategy' && ( + + {hasNameField(change.payload) && ( + + + + )} + + )} + {change.action === 'updateStrategy' && ( + <> + + + + + + + + )} + + + ); +}; diff --git a/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/Discard.tsx b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/Discard.tsx new file mode 100644 index 0000000000..e30177946c --- /dev/null +++ b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/Discard.tsx @@ -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 }) => ( + + Discard + +); + +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 ( + } + /> + ); +}; diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange/StrategyChange.tsx b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/StrategyChange.tsx similarity index 81% rename from frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange/StrategyChange.tsx rename to frontend/src/component/changeRequest/ChangeRequest/Changes/Change/StrategyChange.tsx index 8d7e987ae5..c353f1b4ab 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange/StrategyChange.tsx +++ b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/StrategyChange.tsx @@ -1,10 +1,6 @@ import { Box, Link, styled, Typography } from '@mui/material'; import { FC, ReactNode } from 'react'; -interface IStrategyChangeProps { - onDiscard: () => void; -} - export const ChangeItemWrapper = styled(Box)(({ theme }) => ({ display: 'flex', justifyContent: 'space-between', @@ -16,19 +12,6 @@ const ChangeItemInfo: FC = styled(Box)(({ theme }) => ({ gap: theme.spacing(1), })); -const StyledLink = styled(Link)(() => ({ - textDecoration: 'none', - '&:hover, &:focus': { - textDecoration: 'underline', - }, -})); - -export const Discard: FC = ({ onDiscard }) => ( - - Discard - -); - export const StrategyAddedChange: FC<{ discard?: ReactNode }> = ({ children, discard, diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange/ToggleStatusChange.tsx b/frontend/src/component/changeRequest/ChangeRequest/Changes/Change/ToggleStatusChange.tsx similarity index 100% rename from frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange/ToggleStatusChange.tsx rename to frontend/src/component/changeRequest/ChangeRequest/Changes/Change/ToggleStatusChange.tsx diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange/ChangeRequestFeatureToggleChange.tsx b/frontend/src/component/changeRequest/ChangeRequest/Changes/FeatureToggleChanges.tsx similarity index 92% rename from frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange/ChangeRequestFeatureToggleChange.tsx rename to frontend/src/component/changeRequest/ChangeRequest/Changes/FeatureToggleChanges.tsx index 8569511cad..f5351e32a1 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestFeatureToggleChange/ChangeRequestFeatureToggleChange.tsx +++ b/frontend/src/component/changeRequest/ChangeRequest/Changes/FeatureToggleChanges.tsx @@ -3,16 +3,20 @@ import { Link } from 'react-router-dom'; import { Alert, Box, Card, Typography } from '@mui/material'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; -interface IChangeRequestToggleChange { +interface IFeatureToggleChanges { featureName: string; projectId: string; conflict?: string; onNavigate?: () => void; } -export const ChangeRequestFeatureToggleChange: FC< - IChangeRequestToggleChange -> = ({ featureName, projectId, conflict, onNavigate, children }) => ( +export const FeatureToggleChanges: FC = ({ + featureName, + projectId, + conflict, + onNavigate, + children, +}) => ( ({