diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestHeader/ChangeRequestHeader.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestHeader/ChangeRequestHeader.tsx index 90073d82f3..dad381f18f 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestHeader/ChangeRequestHeader.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestHeader/ChangeRequestHeader.tsx @@ -22,7 +22,7 @@ export const ChangeRequestHeader: FC<{ changeRequest: IChangeRequest }> = ({ Change request #{changeRequest.id} - ; + diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx index cea58f9821..4dbbfc9a0e 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestOverview.tsx @@ -1,5 +1,6 @@ +import { styled } from '@mui/material'; import { FC } from 'react'; -import { Box, Button, Paper } from '@mui/material'; +import { Box } from '@mui/material'; import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest'; import { ChangeRequestHeader } from './ChangeRequestHeader/ChangeRequestHeader'; import { ChangeRequestTimeline } from './ChangeRequestTimeline/ChangeRequestTimeline'; @@ -10,11 +11,42 @@ import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useCh import { ChangeRequestReviewStatus } from './ChangeRequestReviewStatus/ChangeRequestReviewStatus'; import useToast from 'hooks/useToast'; import { formatUnknownError } from 'utils/formatUnknownError'; +import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +import Button from '@mui/material/Button'; +import Paper from '@mui/material/Paper'; +import { ReviewButton } from './ReviewButton/ReviewButton'; + +const StyledAsideBox = styled(Box)(({ theme }) => ({ + width: '30%', + display: 'flex', + flexDirection: 'column', +})); + +const StyledPaper = styled(Paper)(({ theme }) => ({ + marginTop: theme.spacing(2), + marginLeft: theme.spacing(2), + width: '70%', + padding: theme.spacing(1, 2), + borderRadius: theme.shape.borderRadiusLarge, +})); + +const StyledButtonBox = styled(Box)(({ theme }) => ({ + marginTop: theme.spacing(2), + display: 'flex', + justifyContent: 'flex-end', +})); + +const StyledInnerContainer = styled(Box)(({ theme }) => ({ + padding: theme.spacing(2), +})); export const ChangeRequestOverview: FC = () => { const projectId = useRequiredPathParam('projectId'); const id = useRequiredPathParam('id'); - const { data: changeRequest } = useChangeRequest(projectId, id); + const { data: changeRequest, refetchChangeRequest } = useChangeRequest( + projectId, + id + ); const { applyChanges } = useChangeRequestApi(); const { setToastData, setToastApiError } = useToast(); @@ -25,10 +57,11 @@ export const ChangeRequestOverview: FC = () => { const onApplyChanges = async () => { try { await applyChanges(projectId, id); + refetchChangeRequest(); setToastData({ type: 'success', title: 'Success', - text: 'Changes appplied', + text: 'Changes applied', }); } catch (error: unknown) { setToastApiError(formatUnknownError(error)); @@ -39,49 +72,36 @@ export const ChangeRequestOverview: FC = () => { <> - + - - - ({ - marginTop: theme.spacing(2), - marginLeft: theme.spacing(2), - width: '70%', - padding: 2, - borderRadius: theme => - `${theme.shape.borderRadiusLarge}px`, - })} - > - ({ - padding: theme.spacing(2), - })} - > + {/* */} + + + Changes - - - + + } + /> + + Apply changes + + } + /> + + + ); diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.styles.ts b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.styles.ts index 55081e7e59..31c0fcc432 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.styles.ts +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.styles.ts @@ -3,7 +3,12 @@ import { Cancel, CheckCircle } from '@mui/icons-material'; import { Box, Typography, Divider } from '@mui/material'; const styledComponentPropCheck = () => (prop: string) => - prop !== 'color' && prop !== 'sx' && prop !== 'approved'; + prop !== 'color' && + prop !== 'sx' && + prop !== 'approved' && + prop !== 'border' && + prop !== 'bgColor' && + prop !== 'svgColor'; export const StyledFlexAlignCenterBox = styled(Box)(({ theme }) => ({ display: 'flex', @@ -31,11 +36,9 @@ export const StyledOuterContainer = styled(Box)(({ theme }) => ({ export const StyledButtonContainer = styled(Box, { shouldForwardProp: styledComponentPropCheck(), -})<{ approved: boolean }>(({ theme, approved }) => ({ +})<{ bgColor: string; svgColor: string }>(({ theme, bgColor, svgColor }) => ({ borderRadius: `${theme.shape.borderRadiusMedium}px`, - backgroundColor: approved - ? theme.palette.success.main - : theme.palette.tableHeaderBackground, + backgroundColor: bgColor, padding: theme.spacing(1, 2), marginRight: theme.spacing(2), height: '45px', @@ -44,9 +47,7 @@ export const StyledButtonContainer = styled(Box, { alignItems: 'center', justifyContent: 'center', ['svg']: { - color: approved - ? theme.palette.tertiary.background - : theme.palette.neutral.main, + color: svgColor, }, })); @@ -56,18 +57,16 @@ export const StyledDivider = styled(Divider)(({ theme }) => ({ export const StyledReviewStatusContainer = styled(Box, { shouldForwardProp: styledComponentPropCheck(), -})<{ approved: boolean }>(({ theme, approved }) => ({ +})<{ border: string }>(({ theme, border }) => ({ borderRadius: `${theme.shape.borderRadiusLarge}px`, - border: approved - ? `2px solid ${theme.palette.success.main}` - : `1px solid ${theme.palette.tertiary.main}`, + border: border, padding: theme.spacing(3), width: '100%', })); export const StyledReviewTitle = styled(Typography, { shouldForwardProp: styledComponentPropCheck(), -})<{ approved: boolean }>(({ theme, approved }) => ({ +})<{ color: string }>(({ theme, color }) => ({ fontWeight: 'bold', - color: approved ? theme.palette.success.main : theme.palette.error.main, + color, })); diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.tsx index b8e87ad0a1..533759ba32 100644 --- a/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ChangeRequestReviewStatus/ChangeRequestReviewStatus.tsx @@ -1,7 +1,6 @@ import { FC } from 'react'; -import { Box, Typography } from '@mui/material'; +import { Box, Theme, Typography, useTheme } from '@mui/material'; import { ReactComponent as ChangesAppliedIcon } from 'assets/icons/merge.svg'; -import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { StyledOuterContainer, StyledButtonContainer, @@ -12,40 +11,98 @@ import { StyledReviewTitle, StyledDivider, } from './ChangeRequestReviewStatus.styles'; +import { ChangeRequestState } from 'component/changeRequest/changeRequest.types'; interface ISuggestChangeReviewsStatusProps { - approved: boolean; + state: ChangeRequestState; } +const resolveBorder = (state: ChangeRequestState, theme: Theme) => { + if (state === 'Approved') { + return `2px solid ${theme.palette.success.main}`; + } + + if (state === 'Applied') { + return `2px solid ${theme.palette.primary.main}`; + } + + return `1px solid ${theme.palette.tertiary.main}`; +}; + +const resolveIconColors = (state: ChangeRequestState, theme: Theme) => { + if (state === 'Approved') { + return { + bgColor: theme.palette.success.main!, + svgColor: theme.palette.tertiary.background, + }; + } + + if (state === 'Applied') { + return { + bgColor: theme.palette.primary.main!, + svgColor: theme.palette.tertiary.background, + }; + } + + return { + bgColor: theme.palette.tableHeaderBackground, + svgColor: theme.palette.neutral.main!, + }; +}; + export const ChangeRequestReviewStatus: FC< ISuggestChangeReviewsStatusProps -> = ({ approved }) => { +> = ({ state }) => { + const theme = useTheme(); + return ( - + - - } - elseShow={} - /> + + ); }; -const Approved = ({ approved }: ISuggestChangeReviewsStatusProps) => { +interface IResolveComponentProps { + state: ChangeRequestState; +} + +const ResolveComponent = ({ state }: IResolveComponentProps) => { + if (!state) { + return null; + } + + if (state === 'Approved') { + return ; + } + + if (state === 'Applied') { + return ; + } + + if (state === 'Cancelled') { + return ; + } + + return ; +}; + +const Approved = () => { + const theme = useTheme(); + return ( <> - + Changed approved @@ -59,7 +116,7 @@ const Approved = ({ approved }: ISuggestChangeReviewsStatusProps) => { - + Changes are ready to be applied @@ -68,13 +125,15 @@ const Approved = ({ approved }: ISuggestChangeReviewsStatusProps) => { ); }; -const ReviewRequired = ({ approved }: ISuggestChangeReviewsStatusProps) => { +const ReviewRequired = () => { + const theme = useTheme(); + return ( <> - + Review required @@ -88,10 +147,44 @@ const ReviewRequired = ({ approved }: ISuggestChangeReviewsStatusProps) => { - + Apply changes is blocked ); }; + +const Applied = () => { + const theme = useTheme(); + + return ( + <> + + + + + Changes applied + + + + + ); +}; + +const Cancelled = () => { + const theme = useTheme(); + + return ( + <> + + + + + Changes cancelled + + + + + ); +}; diff --git a/frontend/src/component/changeRequest/ChangeRequestOverview/ReviewButton/ReviewButton.tsx b/frontend/src/component/changeRequest/ChangeRequestOverview/ReviewButton/ReviewButton.tsx new file mode 100644 index 0000000000..3c4af76c49 --- /dev/null +++ b/frontend/src/component/changeRequest/ChangeRequestOverview/ReviewButton/ReviewButton.tsx @@ -0,0 +1,132 @@ +import React from 'react'; +import { useRequiredPathParam } from 'hooks/useRequiredPathParam'; +import { useChangeRequest } from 'hooks/api/getters/useChangeRequest/useChangeRequest'; +import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi'; +import { formatUnknownError } from 'utils/formatUnknownError'; +import useToast from 'hooks/useToast'; + +import { + Button, + Grow, + Paper, + Popper, + MenuItem, + MenuList, + ClickAwayListener, +} from '@mui/material'; + +import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; + +export const ReviewButton = () => { + const projectId = useRequiredPathParam('projectId'); + const id = useRequiredPathParam('id'); + const { refetchChangeRequest } = useChangeRequest(projectId, id); + const { setToastApiError, setToastData } = useToast(); + + const { changeState } = useChangeRequestApi(); + + const [open, setOpen] = React.useState(false); + const anchorRef = React.useRef(null); + + const onApprove = async () => { + try { + await changeState(projectId, Number(id), { + state: 'Approved', + }); + refetchChangeRequest(); + setToastData({ + type: 'success', + title: 'Success', + text: 'Changes approved', + }); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); + } + }; + + const onReject = async () => { + try { + await changeState(projectId, Number(id), { + state: 'Cancelled', + }); + refetchChangeRequest(); + setToastData({ + type: 'success', + title: 'Success', + text: 'Changes rejected', + }); + } catch (error: unknown) { + setToastApiError(formatUnknownError(error)); + } + }; + + const onToggle = () => { + setOpen(prevOpen => !prevOpen); + }; + + const onClose = (event: Event) => { + if ( + anchorRef.current && + anchorRef.current.contains(event.target as HTMLElement) + ) { + return; + } + + setOpen(false); + }; + + return ( + + + + {({ TransitionProps, placement }) => ( + + + + + + Approve changes + + + Reject changes + + + + + + )} + + + ); +}; diff --git a/frontend/src/component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge.tsx b/frontend/src/component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge.tsx index 26ddd79145..13e7d7ec08 100644 --- a/frontend/src/component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge.tsx +++ b/frontend/src/component/changeRequest/ChangeRequestStatusBadge/ChangeRequestStatusBadge.tsx @@ -35,10 +35,7 @@ export const ChangeRequestStatusBadge: VFC = ({ ); case 'Cancelled': return ( - } - > + }> Cancelled );