mirror of
https://github.com/Unleash/unleash.git
synced 2025-06-14 01:16:17 +02:00
Feat/review page timeline (#2310)
* fix: styling * feat: overview timeline * fix: rename types * fix: pr comments
This commit is contained in:
parent
9fb431aab7
commit
9b10a8815b
@ -7,9 +7,9 @@ import { ChangeRequestReviewers } from './ChangeRequestReviewers/ChangeRequestRe
|
|||||||
import { ChangeRequest } from '../ChangeRequest/ChangeRequest';
|
import { ChangeRequest } from '../ChangeRequest/ChangeRequest';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
|
import { useChangeRequestApi } from 'hooks/api/actions/useChangeRequestApi/useChangeRequestApi';
|
||||||
|
import { ChangeRequestReviewStatus } from './ChangeRequestReviewStatus/ChangeRequestReviewStatus';
|
||||||
import useToast from 'hooks/useToast';
|
import useToast from 'hooks/useToast';
|
||||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||||
import { ChangeRequestReviewStatus } from './ChangeRequestReviewStatus/ChangeRequestReviewStatus';
|
|
||||||
|
|
||||||
export const ChangeRequestOverview: FC = () => {
|
export const ChangeRequestOverview: FC = () => {
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
@ -46,7 +46,7 @@ export const ChangeRequestOverview: FC = () => {
|
|||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ChangeRequestTimeline />
|
<ChangeRequestTimeline state={changeRequest.state} />
|
||||||
<ChangeRequestReviewers />
|
<ChangeRequestReviewers />
|
||||||
</Box>
|
</Box>
|
||||||
<Paper
|
<Paper
|
||||||
@ -66,7 +66,13 @@ export const ChangeRequestOverview: FC = () => {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<ChangeRequest changeRequest={changeRequest} />
|
<ChangeRequest changeRequest={changeRequest} />
|
||||||
<ChangeRequestReviewStatus approved={true} />
|
<ChangeRequestReviewStatus
|
||||||
|
approved={
|
||||||
|
changeRequest.state === 'Approved' ||
|
||||||
|
changeRequest.state === 'Applied'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
sx={{ marginTop: 2 }}
|
sx={{ marginTop: 2 }}
|
||||||
|
@ -3,7 +3,7 @@ import { Cancel, CheckCircle } from '@mui/icons-material';
|
|||||||
import { Box, Typography, Divider } from '@mui/material';
|
import { Box, Typography, Divider } from '@mui/material';
|
||||||
|
|
||||||
const styledComponentPropCheck = () => (prop: string) =>
|
const styledComponentPropCheck = () => (prop: string) =>
|
||||||
prop !== 'color' && prop !== 'sx';
|
prop !== 'color' && prop !== 'sx' && prop !== 'approved';
|
||||||
|
|
||||||
export const StyledFlexAlignCenterBox = styled(Box)(({ theme }) => ({
|
export const StyledFlexAlignCenterBox = styled(Box)(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
@ -12,7 +12,6 @@ import {
|
|||||||
StyledReviewTitle,
|
StyledReviewTitle,
|
||||||
StyledDivider,
|
StyledDivider,
|
||||||
} from './ChangeRequestReviewStatus.styles';
|
} from './ChangeRequestReviewStatus.styles';
|
||||||
|
|
||||||
interface ISuggestChangeReviewsStatusProps {
|
interface ISuggestChangeReviewsStatusProps {
|
||||||
approved: boolean;
|
approved: boolean;
|
||||||
}
|
}
|
||||||
@ -30,13 +29,11 @@ export const ChangeRequestReviewStatus: FC<
|
|||||||
/>
|
/>
|
||||||
</StyledButtonContainer>
|
</StyledButtonContainer>
|
||||||
<StyledReviewStatusContainer approved={approved}>
|
<StyledReviewStatusContainer approved={approved}>
|
||||||
<StyledFlexAlignCenterBox>
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={approved}
|
condition={approved}
|
||||||
show={<Approved approved={approved} />}
|
show={<Approved approved={approved} />}
|
||||||
elseShow={<ReviewRequired approved={approved} />}
|
elseShow={<ReviewRequired approved={approved} />}
|
||||||
/>
|
/>
|
||||||
</StyledFlexAlignCenterBox>
|
|
||||||
</StyledReviewStatusContainer>
|
</StyledReviewStatusContainer>
|
||||||
</StyledOuterContainer>
|
</StyledOuterContainer>
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
|
import { styled } from '@mui/material';
|
||||||
import { Box, Paper } from '@mui/material';
|
import { Box, Paper } from '@mui/material';
|
||||||
import Timeline from '@mui/lab/Timeline';
|
import Timeline from '@mui/lab/Timeline';
|
||||||
import TimelineItem, { timelineItemClasses } from '@mui/lab/TimelineItem';
|
import TimelineItem, { timelineItemClasses } from '@mui/lab/TimelineItem';
|
||||||
@ -6,47 +7,123 @@ import TimelineSeparator from '@mui/lab/TimelineSeparator';
|
|||||||
import TimelineDot from '@mui/lab/TimelineDot';
|
import TimelineDot from '@mui/lab/TimelineDot';
|
||||||
import TimelineConnector from '@mui/lab/TimelineConnector';
|
import TimelineConnector from '@mui/lab/TimelineConnector';
|
||||||
import TimelineContent from '@mui/lab/TimelineContent';
|
import TimelineContent from '@mui/lab/TimelineContent';
|
||||||
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
|
import { ChangeRequestState } from '../changeRequest.types';
|
||||||
|
interface ISuggestChangeTimelineProps {
|
||||||
|
state: ChangeRequestState;
|
||||||
|
}
|
||||||
|
interface ITimelineData {
|
||||||
|
title: string;
|
||||||
|
active: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export const ChangeRequestTimeline: FC = () => {
|
const StyledPaper = styled(Paper)(({ theme }) => ({
|
||||||
return (
|
|
||||||
<Paper
|
|
||||||
elevation={0}
|
|
||||||
sx={theme => ({
|
|
||||||
marginTop: theme.spacing(2),
|
marginTop: theme.spacing(2),
|
||||||
borderRadius: theme => `${theme.shape.borderRadiusLarge}px`,
|
borderRadius: `${theme.shape.borderRadiusLarge}px`,
|
||||||
})}
|
}));
|
||||||
>
|
|
||||||
<Box sx={theme => ({ padding: theme.spacing(2) })}>
|
const StyledBox = styled(Box)(({ theme }) => ({
|
||||||
<Timeline
|
padding: theme.spacing(2),
|
||||||
sx={{
|
marginBottom: `-${theme.spacing(4)}`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledTimeline = styled(Timeline)(() => ({
|
||||||
[`& .${timelineItemClasses.root}:before`]: {
|
[`& .${timelineItemClasses.root}:before`]: {
|
||||||
flex: 0,
|
flex: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
},
|
},
|
||||||
}}
|
}));
|
||||||
>
|
|
||||||
<TimelineItem>
|
export const ChangeRequestTimeline: FC<ISuggestChangeTimelineProps> = ({
|
||||||
<TimelineSeparator>
|
state,
|
||||||
<TimelineDot color="success" />
|
}) => {
|
||||||
<TimelineConnector color="success" />
|
const createTimeLineData = (state: ChangeRequestState): ITimelineData[] => {
|
||||||
</TimelineSeparator>
|
const steps: ChangeRequestState[] = [
|
||||||
<TimelineContent>Draft</TimelineContent>
|
'Draft',
|
||||||
</TimelineItem>
|
'In review',
|
||||||
<TimelineItem>
|
'Approved',
|
||||||
<TimelineSeparator>
|
'Applied',
|
||||||
<TimelineDot color="success" />
|
];
|
||||||
<TimelineConnector />
|
|
||||||
</TimelineSeparator>
|
return steps.map(step => ({
|
||||||
<TimelineContent>Approved</TimelineContent>
|
title: step,
|
||||||
</TimelineItem>
|
active: step === state,
|
||||||
<TimelineItem>
|
}));
|
||||||
<TimelineSeparator>
|
};
|
||||||
<TimelineDot />
|
|
||||||
</TimelineSeparator>
|
const renderTimeline = () => {
|
||||||
<TimelineContent>Applied</TimelineContent>
|
const data = createTimeLineData(state);
|
||||||
</TimelineItem>
|
const index = data.findIndex(item => item.active);
|
||||||
</Timeline>
|
const activeIndex: number | null = index !== -1 ? index : null;
|
||||||
</Box>
|
|
||||||
</Paper>
|
if (state === 'Cancelled') {
|
||||||
|
return createCancelledTimeline(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return createTimeline(data, activeIndex);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledPaper elevation={0}>
|
||||||
|
<StyledBox>
|
||||||
|
<StyledTimeline>{renderTimeline()}</StyledTimeline>
|
||||||
|
</StyledBox>
|
||||||
|
</StyledPaper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createTimeline = (data: ITimelineData[], activeIndex: number | null) => {
|
||||||
|
return data.map(({ title }, index) => {
|
||||||
|
const shouldConnectToNextItem = index < data.length - 1;
|
||||||
|
|
||||||
|
const connector = (
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={shouldConnectToNextItem}
|
||||||
|
show={<TimelineConnector />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (activeIndex !== null && activeIndex >= index) {
|
||||||
|
return createTimelineItem('success', title, connector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeIndex !== null && activeIndex + 1 === index) {
|
||||||
|
return createTimelineItem('primary', title, connector, {
|
||||||
|
variant: 'outlined',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return createTimelineItem('grey', title, connector);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createCancelledTimeline = (data: ITimelineData[]) => {
|
||||||
|
return data.map(({ title }, index) => {
|
||||||
|
const shouldConnectToNextItem = index < data.length - 1;
|
||||||
|
|
||||||
|
const connector = (
|
||||||
|
<ConditionallyRender
|
||||||
|
condition={shouldConnectToNextItem}
|
||||||
|
show={<TimelineConnector />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
return createTimelineItem('grey', title, connector);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const createTimelineItem = (
|
||||||
|
color: 'primary' | 'success' | 'grey',
|
||||||
|
title: string,
|
||||||
|
connector: JSX.Element,
|
||||||
|
timelineDotProps: { [key: string]: string } = {}
|
||||||
|
) => {
|
||||||
|
return (
|
||||||
|
<TimelineItem key={title}>
|
||||||
|
<TimelineSeparator>
|
||||||
|
<TimelineDot color={color} {...timelineDotProps} />
|
||||||
|
{connector}
|
||||||
|
</TimelineSeparator>
|
||||||
|
<TimelineContent>{title}</TimelineContent>
|
||||||
|
</TimelineItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
export type ChangeRequestState =
|
||||||
|
| 'Draft'
|
||||||
|
| 'Approved'
|
||||||
|
| 'In review'
|
||||||
|
| 'Applied'
|
||||||
|
| 'Cancelled';
|
@ -84,6 +84,8 @@ export const FeatureView = () => {
|
|||||||
return <FeatureNotFound />;
|
return <FeatureNotFound />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(uiConfig?.flags);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainLayout
|
<MainLayout
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
@ -124,7 +124,7 @@ const Project = () => {
|
|||||||
<MainLayout
|
<MainLayout
|
||||||
ref={ref}
|
ref={ref}
|
||||||
subheader={
|
subheader={
|
||||||
!uiConfig?.flags?.changeRequests ? (
|
uiConfig?.flags?.changeRequests ? (
|
||||||
<DraftBanner project={projectId} />
|
<DraftBanner project={projectId} />
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user