1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-25 00:07:47 +01:00

feat: change request header (#2317)

* feat: change request header

* fix: dom nesting paragraphs

* fix: change path
This commit is contained in:
Fredrik Strand Oseberg 2022-11-02 14:23:44 +01:00 committed by GitHub
parent 6622346286
commit 2f1f9cecc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 169 additions and 163 deletions

View File

@ -0,0 +1,37 @@
import { styled } from '@mui/material';
import { Avatar, Box, Card, Paper, Typography } from '@mui/material';
export const StyledPaper = styled(Paper)(({ theme }) => ({
padding: theme.spacing(2, 4),
borderRadius: `${theme.shape.borderRadiusLarge}px`,
}));
export const StyledContainer = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: 2,
marginBottom: theme.spacing(2),
}));
export const StyledInnerContainer = styled(Box)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(1.5),
}));
export const StyledHeader = styled(Typography)(({ theme }) => ({
display: 'flex',
alignItems: 'center',
marginRight: theme.spacing(1),
fontSize: theme.fontSizes.mainHeader,
}));
export const StyledCard = styled(Card)(({ theme }) => ({
padding: theme.spacing(0.5, 1),
backgroundColor: theme.palette.tertiary.light,
}));
export const StyledAvatar = styled(Avatar)(() => ({
height: '30px',
width: '30px',
}));

View File

@ -1,69 +1,60 @@
import { Box } from '@mui/material';
import { FC } from 'react'; import { FC } from 'react';
import { Avatar, Box, Card, Paper, Typography } from '@mui/material'; import { Typography } from '@mui/material';
import { PlaygroundResultChip } from 'component/playground/Playground/PlaygroundResultsTable/PlaygroundResultChip/PlaygroundResultChip';
import { ReactComponent as ChangesAppliedIcon } from 'assets/icons/merge.svg';
import TimeAgo from 'react-timeago'; import TimeAgo from 'react-timeago';
import { resolveChangeRequestStatusIcon } from 'component/changeRequest/changeRequest.utils';
import { IChangeRequest } from 'component/changeRequest/changeRequest.types';
import {
StyledPaper,
StyledContainer,
StyledHeader,
StyledInnerContainer,
StyledAvatar,
StyledCard,
} from './ChangeRequestHeader.styles';
export const ChangeRequestHeader: FC<{ changeRequest: any }> = ({ export const ChangeRequestHeader: FC<{ changeRequest: IChangeRequest }> = ({
changeRequest, changeRequest,
}) => { }) => {
return ( return (
<Paper <StyledPaper elevation={0}>
elevation={0} <StyledContainer>
sx={theme => ({ <StyledHeader variant="h1">
p: theme.spacing(2, 4), Change request #{changeRequest.id}
borderRadius: theme => `${theme.shape.borderRadiusLarge}px`, </StyledHeader>
})} {resolveChangeRequestStatusIcon(changeRequest.state)}
> </StyledContainer>
<Box <StyledInnerContainer>
sx={theme => ({ <Typography variant="body2" sx={{ margin: 'auto 0' }}>
display: 'flex',
alignItems: 'center',
gap: 2,
marginBottom: theme.spacing(2),
})}
>
<Typography
sx={{
display: 'flex',
alignItems: 'center',
}}
variant="h1"
>
Change request
<Typography variant="h1" component="p">
#{changeRequest.id}
</Typography>
</Typography>
<PlaygroundResultChip
// icon={<ChangesAppliedIcon strokeWidth="0.25" />}
label="Changes approved"
enabled
/>
</Box>
<Box sx={{ display: 'flex', verticalAlign: 'center', gap: 2 }}>
<Typography sx={{ margin: 'auto 0' }}>
Created <TimeAgo date={new Date(changeRequest.createdAt)} />{' '} Created <TimeAgo date={new Date(changeRequest.createdAt)} />{' '}
by by
</Typography> </Typography>
<Avatar src={changeRequest?.createdBy?.avatar} /> <StyledAvatar src={changeRequest?.createdBy?.imageUrl} />
<Card <Box>
variant="outlined" <StyledCard variant="outlined">
sx={theme => ({ <Typography variant="body2">
padding: 1, Environment:{' '}
backgroundColor: theme.palette.tertiary.light, <Typography
})} display="inline"
> fontWeight="bold"
Environment:{' '} variant="body2"
<Typography display="inline" fontWeight="bold"> component="span"
{changeRequest?.environment} >
</Typography>{' '} {changeRequest?.environment}
| Updates:{' '} </Typography>{' '}
<Typography display="inline" fontWeight="bold"> | Updates:{' '}
{changeRequest?.features.length} feature toggles <Typography
</Typography> variant="body2"
</Card> display="inline"
</Box> fontWeight="bold"
</Paper> component="span"
>
{changeRequest?.features.length} feature toggles
</Typography>
</Typography>
</StyledCard>
</Box>
</StyledInnerContainer>
</StyledPaper>
); );
}; };

View File

@ -8,7 +8,7 @@ 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 { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ChangeRequestState } from '../changeRequest.types'; import { ChangeRequestState } from '../../changeRequest.types';
interface ISuggestChangeTimelineProps { interface ISuggestChangeTimelineProps {
state: ChangeRequestState; state: ChangeRequestState;
} }

View File

@ -1,6 +0,0 @@
export type ChangeRequestState =
| 'Draft'
| 'Approved'
| 'In review'
| 'Applied'
| 'Cancelled';

View File

@ -1,115 +1,23 @@
import { VFC } from 'react'; import { VFC } from 'react';
import { Chip, styled } from '@mui/material';
import { colors } from 'themes/colors';
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell'; import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
import { Check, CircleOutlined, Close } from '@mui/icons-material'; import { resolveChangeRequestStatusIcon } from 'component/changeRequest/changeRequest.utils';
import { ChangeRequestState } from 'component/changeRequest/changeRequest.types';
interface IChangeRequestStatusCellProps { interface IChangeRequestStatusCellProps {
value?: string | null; value?: string | null;
} }
export enum ChangeRequestState {
DRAFT = 'Draft',
APPROVED = 'Approved',
IN_REVIEW = 'In review',
APPLIED = 'Applied',
CANCELLED = 'Cancelled',
REJECTED = 'Rejected',
}
export const StyledChip = styled(Chip)(({ theme, icon }) => ({
padding: theme.spacing(0, 1),
height: 30,
borderRadius: theme.shape.borderRadius,
fontWeight: theme.typography.fontWeightMedium,
gap: theme.spacing(1, 1),
['& .MuiChip-label']: {
padding: 0,
paddingLeft: Boolean(icon) ? theme.spacing(0.5) : 0,
},
}));
export const StyledRejectedChip = styled(StyledChip)(({ theme }) => ({
border: `1px solid ${theme.palette.error.main}`,
backgroundColor: colors.red['100'],
['& .MuiChip-label']: {
color: theme.palette.error.main,
},
['& .MuiChip-icon']: {
color: theme.palette.error.main,
},
}));
export const StyledApprovedChip = styled(StyledChip)(({ theme }) => ({
border: `1px solid ${theme.palette.success.main}`,
backgroundColor: colors.green['100'],
['& .MuiChip-label']: {
color: theme.palette.success.main,
},
['& .MuiChip-icon']: {
color: theme.palette.success.main,
},
}));
export const StyledReviewChip = styled(StyledChip)(({ theme }) => ({
border: `1px solid ${theme.palette.primary.main}`,
backgroundColor: colors.purple['100'],
['& .MuiChip-label']: {
color: theme.palette.primary.main,
},
['& .MuiChip-icon']: {
color: theme.palette.primary.main,
},
}));
export const ChangeRequestStatusCell: VFC<IChangeRequestStatusCellProps> = ({ export const ChangeRequestStatusCell: VFC<IChangeRequestStatusCellProps> = ({
value, value,
}) => { }) => {
const renderState = (state: string) => { const renderState = () => {
switch (state) { if (!value) return null;
case ChangeRequestState.IN_REVIEW: return resolveChangeRequestStatusIcon(value as ChangeRequestState);
return (
<StyledReviewChip
label={'Review required'}
icon={<CircleOutlined fontSize={'small'} />}
/>
);
case ChangeRequestState.APPROVED:
return (
<StyledApprovedChip
label={'Approved'}
icon={<Check fontSize={'small'} />}
/>
);
case ChangeRequestState.APPLIED:
return (
<StyledApprovedChip
label={'Applied'}
icon={<Check fontSize={'small'} />}
/>
);
case ChangeRequestState.CANCELLED:
return (
<StyledRejectedChip
label={'Cancelled'}
icon={<Close fontSize={'small'} sx={{ mr: 8 }} />}
/>
);
case ChangeRequestState.REJECTED:
return (
<StyledRejectedChip
label={'Rejected'}
icon={<Close fontSize={'small'} sx={{ mr: 8 }} />}
/>
);
default:
return null;
}
}; };
if (!value) { if (!value) {
return <TextCell />; return <TextCell />;
} }
return <TextCell>{renderState(value)}</TextCell>; return <TextCell>{renderState()}</TextCell>;
}; };

View File

@ -0,0 +1,36 @@
export type ChangeRequestState =
| 'Draft'
| 'Approved'
| 'In review'
| 'Applied'
| 'Cancelled';
export interface IChangeRequest {
id: number;
environment: string;
state: ChangeRequestState;
project: string;
createdBy: ICreatedBy;
createdAt: string;
features: IChangeRequestFeatures[];
}
interface ICreatedBy {
id: number;
username: string;
imageUrl: string;
}
interface IChangeRequestFeatures {
name: string;
changes: IChangeRequestFeatureChanges[];
}
interface IChangeRequestFeatureChanges {
id: number;
action: string;
payload: unknown;
createdAt: string;
createdBy: ICreatedBy;
warning?: string;
}

View File

@ -0,0 +1,40 @@
import { ChangeRequestState } from './changeRequest.types';
import { Badge } from 'component/common/Badge/Badge';
import { Check, CircleOutlined, Close } from '@mui/icons-material';
export const resolveChangeRequestStatusIcon = (state: ChangeRequestState) => {
const reviewRequired = (
<Badge color="secondary" icon={<CircleOutlined fontSize={'small'} />}>
Review required
</Badge>
);
switch (state) {
case 'Draft':
return reviewRequired;
case 'In review':
return reviewRequired;
case 'Approved':
return (
<Badge color="success" icon={<Check fontSize={'small'} />}>
Approved
</Badge>
);
case 'Applied':
return (
<Badge color="success" icon={<Check fontSize={'small'} />}>
Applied
</Badge>
);
case 'Cancelled':
return (
<Badge
color="error"
icon={<Close fontSize={'small'} sx={{ mr: 8 }} />}
>
Cancelled
</Badge>
);
default:
return reviewRequired;
}
};