Skip to content

Commit 7c9dacd

Browse files
authored
Merge pull request #1751 from Northeastern-Electric-Racing/feature/Change-Request-Redesign
Feature/change request redesign
2 parents 5a79d94 + 6291cc0 commit 7c9dacd

29 files changed

Lines changed: 840 additions & 579 deletions

src/frontend/src/components/ChangeRequestDetailCard.tsx

Lines changed: 69 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -3,96 +3,62 @@
33
* See the LICENSE file in the repository root folder for details.
44
*/
55

6-
import { Card, CardContent, Grid, Typography } from '@mui/material';
7-
import Chip from '@mui/material/Chip';
8-
import { green, blue, red, grey, orange } from '@mui/material/colors';
6+
import { Card, CardContent, Grid, Typography, useTheme } from '@mui/material';
97
import { Box, Stack } from '@mui/system';
108
import { Link } from '@mui/material';
119
import {
1210
ActivationChangeRequest,
1311
ChangeRequest,
12+
ChangeRequestStatus,
1413
ChangeRequestType,
15-
StageGateChangeRequest,
1614
StandardChangeRequest,
1715
wbsPipe
1816
} from 'shared';
1917
import { routes } from '../utils/routes';
2018
import { Link as RouterLink } from 'react-router-dom';
21-
import { datePipe, fullNamePipe } from '../utils/pipes';
22-
import { Cancel, Construction, DateRange, Description, DoneAll, Person, Start, Work } from '@mui/icons-material';
23-
import { ChangeRequestTypeTextPipe } from '../utils/enum-pipes';
19+
import { fullNamePipe } from '../utils/pipes';
20+
import ChangeRequestTypePill from './ChangeRequestTypePill';
21+
import ChangeRequestStatusPill from './ChangeRequestStatusPill';
2422

25-
const determineChangeRequestTypeView = (cr: ChangeRequest) => {
26-
switch (cr.type) {
27-
case 'STAGE_GATE':
28-
return <StageGateCardDetails cr={cr as StageGateChangeRequest} />;
29-
case 'ACTIVATION':
30-
return <ActivationCardDetails cr={cr as ActivationChangeRequest} />;
31-
default:
32-
return <StandardCardDetails cr={cr as StandardChangeRequest} />;
33-
}
34-
};
35-
36-
const determineChangeRequestPillColor = (type: ChangeRequestType) => {
37-
switch (type) {
38-
case 'STAGE_GATE':
39-
return orange[900];
40-
case 'ACTIVATION':
41-
return green[600];
42-
case 'DEFINITION_CHANGE':
43-
return blue[600];
44-
case 'ISSUE':
45-
return red[400];
46-
default:
47-
return grey[500];
48-
}
49-
};
50-
51-
const StandardCardDetails = ({ cr }: { cr: StandardChangeRequest }) => {
52-
return (
53-
<Grid container mt={1} ml={'2px'}>
54-
<Grid item>
55-
<Description sx={{ ml: '-4px' }} display={'inline'} />
56-
</Grid>
57-
<Grid item xs>
58-
<Typography ml={'4px'} display={'inline'}>
59-
{cr.what}
60-
</Typography>
61-
</Grid>
62-
</Grid>
63-
);
64-
};
65-
66-
const StageGateCardDetails = ({ cr }: { cr: StageGateChangeRequest }) => {
23+
const CRCardDescription = ({ cr }: { cr: ChangeRequest }) => {
24+
const theme = useTheme();
25+
const isAccepted = cr.status === ChangeRequestStatus.Implemented || cr.status === ChangeRequestStatus.Accepted;
26+
const isStageGate = cr.type === ChangeRequestType.StageGate;
27+
const isActivation = cr.type === ChangeRequestType.Activation;
6728
return (
68-
<Box ml={-1}>
69-
{cr.confirmDone ? (
70-
<Chip icon={<DoneAll />} label={'Done'} sx={{ backgroundColor: 'transparent' }} />
71-
) : (
72-
<Chip icon={<Cancel />} label={'Not Done'} sx={{ backgroundColor: 'transparent' }} />
73-
)}
74-
</Box>
75-
);
76-
};
77-
78-
const ActivationCardDetails = ({ cr }: { cr: ActivationChangeRequest }) => {
79-
return (
80-
<Box>
81-
<Stack direction="row">
82-
<Chip
83-
icon={<Construction />}
84-
label={fullNamePipe(cr.projectLead)}
85-
sx={{ backgroundColor: 'transparent', mr: 2, ml: -1, maxWidth: '150' }}
86-
/>
87-
<Chip
88-
icon={<Work />}
89-
label={fullNamePipe(cr.projectManager)}
90-
sx={{ backgroundColor: 'transparent', maxWidth: '150' }}
91-
/>
92-
</Stack>
93-
<Stack direction="row" justifyContent={'space-between'}>
94-
<Chip icon={<Start />} label={datePipe(cr.startDate)} sx={{ backgroundColor: 'transparent', ml: -1 }} />
95-
</Stack>
29+
<Box
30+
sx={{
31+
backgroundColor: theme.palette.divider,
32+
width: '100%',
33+
height: 75,
34+
borderRadius: 1,
35+
padding: 1,
36+
overflow: 'hidden',
37+
textOverflow: 'ellipsis'
38+
}}
39+
>
40+
<Typography variant="body1" fontSize={14}>
41+
{isAccepted ? (
42+
cr.reviewNotes ? (
43+
'Review Notes: ' + cr.reviewNotes
44+
) : (
45+
'No review notes'
46+
)
47+
) : isActivation ? (
48+
<div>
49+
<Typography variant="body1" fontSize={14}>
50+
Lead: {fullNamePipe((cr as ActivationChangeRequest).projectLead)}
51+
</Typography>
52+
<Typography variant="body1" fontSize={14}>
53+
Manager: {fullNamePipe((cr as ActivationChangeRequest).projectManager)}
54+
</Typography>
55+
</div>
56+
) : isStageGate ? (
57+
'Stage Gate ' + wbsPipe(cr.wbsNum) + ' - ' + cr.wbsName
58+
) : (
59+
(cr as StandardChangeRequest).what
60+
)}
61+
</Typography>
9662
</Box>
9763
);
9864
};
@@ -101,49 +67,41 @@ interface ChangeRequestDetailCardProps {
10167
changeRequest: ChangeRequest;
10268
}
10369

104-
// Convert work package stage into badge for display
10570
const ChangeRequestDetailCard: React.FC<ChangeRequestDetailCardProps> = ({ changeRequest }) => {
106-
const ChangeRequestTypeView = () => determineChangeRequestTypeView(changeRequest);
107-
const pillColor = determineChangeRequestPillColor(changeRequest.type);
10871
return (
109-
<Card sx={{ width: 300, mr: 1, mb: 1, borderRadius: 5 }}>
72+
<Card sx={{ minWidth: 325, maxWidth: 325, mr: 2, borderRadius: 3, mb: 2 }}>
11073
<CardContent>
111-
<Grid container justifyContent="space-between" alignItems="center">
112-
<Grid item>
113-
<Link component={RouterLink} to={`${routes.CHANGE_REQUESTS}/${changeRequest.crId}`} noWrap>
74+
<Grid container justifyContent="space-between" alignItems="flex-start">
75+
<Grid item xs mb={1} mt={-1.5}>
76+
<Link
77+
underline={'none'}
78+
color={'inherit'}
79+
component={RouterLink}
80+
to={`${routes.CHANGE_REQUESTS}/${changeRequest.crId}`}
81+
>
11482
<Typography variant="h6" sx={{ mb: 0.5 }}>
115-
{'CR #' + changeRequest.crId}
83+
{'Change Request #' + changeRequest.crId}
11684
</Typography>
11785
</Link>
86+
<Stack direction={'column'} maxWidth={'195px'}>
87+
<Typography variant="body1" sx={{ mr: 2, fontWeight: 'bold', fontSize: 13 }}>
88+
From: {fullNamePipe(changeRequest.submitter)}
89+
</Typography>
90+
<Typography fontWeight={'bold'} fontSize={12} noWrap>
91+
<Link component={RouterLink} to={`${routes.PROJECTS}/${wbsPipe(changeRequest.wbsNum)}`}>
92+
{wbsPipe(changeRequest.wbsNum)} {changeRequest.wbsName}
93+
</Link>
94+
</Typography>
95+
</Stack>
11896
</Grid>
119-
<Grid item display="flex" justifyContent="flex-end">
120-
<Chip
121-
size="small"
122-
label={ChangeRequestTypeTextPipe(changeRequest.type)}
123-
variant="outlined"
124-
sx={{
125-
fontSize: 12,
126-
color: pillColor,
127-
borderColor: pillColor,
128-
mb: 0.5
129-
}}
130-
/>
97+
<Grid item xs="auto" mb={1}>
98+
<Stack direction={'column'} spacing={1}>
99+
<ChangeRequestTypePill type={changeRequest.type} />
100+
<ChangeRequestStatusPill status={changeRequest.status} />
101+
</Stack>
131102
</Grid>
132103
</Grid>
133-
<Stack direction="row">
134-
<Chip
135-
icon={<Person />}
136-
label={fullNamePipe(changeRequest.submitter)}
137-
sx={{ mr: 2, ml: -1, backgroundColor: 'transparent', maxWidth: '150' }}
138-
/>
139-
<Chip icon={<DateRange />} label={datePipe(changeRequest.dateSubmitted)} sx={{ backgroundColor: 'transparent' }} />
140-
</Stack>
141-
<Typography fontWeight={'regular'} variant="h6" fontSize={16} noWrap>
142-
<Link component={RouterLink} to={`${routes.PROJECTS}/${wbsPipe(changeRequest.wbsNum)}`}>
143-
{wbsPipe(changeRequest.wbsNum)} - {changeRequest.wbsName}
144-
</Link>
145-
</Typography>
146-
<ChangeRequestTypeView />
104+
<CRCardDescription cr={changeRequest} />
147105
</CardContent>
148106
</Card>
149107
);
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* This file is part of NER's FinishLine and licensed under GNU AGPLv3.
3+
* See the LICENSE file in the repository root folder for details.
4+
*/
5+
6+
import Chip from '@mui/material/Chip';
7+
import { green, blue, red, grey, purple } from '@mui/material/colors';
8+
import { ChangeRequestStatus } from 'shared';
9+
import { ChangeRequestStatusTextPipe } from '../utils/enum-pipes';
10+
11+
const determineChangeRequestStatusPillColor = (status: ChangeRequestStatus) => {
12+
switch (status) {
13+
case ChangeRequestStatus.Implemented:
14+
return blue[600];
15+
case ChangeRequestStatus.Accepted:
16+
return green[600];
17+
case ChangeRequestStatus.Denied:
18+
return red[400];
19+
case ChangeRequestStatus.Open:
20+
return purple[400];
21+
default:
22+
return grey[500];
23+
}
24+
};
25+
26+
const ChangeRequestStatusPill = ({ status }: { status: ChangeRequestStatus }) => {
27+
const statusPillColor = determineChangeRequestStatusPillColor(status);
28+
return (
29+
<Chip
30+
size="small"
31+
label={ChangeRequestStatusTextPipe(status)}
32+
variant="filled"
33+
sx={{
34+
fontSize: 12,
35+
color: 'white',
36+
backgroundColor: statusPillColor,
37+
width: 100
38+
}}
39+
/>
40+
);
41+
};
42+
43+
export default ChangeRequestStatusPill;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Chip } from '@mui/material';
2+
import { ChangeRequestType } from 'shared';
3+
import { ChangeRequestTypeTextPipe } from '../utils/enum-pipes';
4+
import { red } from '@mui/material/colors';
5+
6+
const ChangeRequestTypePill = ({ type }: { type: ChangeRequestType }) => {
7+
return (
8+
<Chip
9+
size="small"
10+
label={ChangeRequestTypeTextPipe(type)}
11+
variant="filled"
12+
sx={{
13+
fontSize: 12,
14+
color: 'white',
15+
backgroundColor: red[600],
16+
width: 100
17+
}}
18+
/>
19+
);
20+
};
21+
22+
export default ChangeRequestTypePill;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* This file is part of NER's FinishLine and licensed under GNU AGPLv3.
3+
* See the LICENSE file in the repository root folder for details.
4+
*/
5+
6+
import Typography from '@mui/material/Typography';
7+
import { Grid } from '@mui/material';
8+
import { ReactNode } from 'react';
9+
10+
interface InfoBlockProps {
11+
title: string;
12+
icon?: ReactNode;
13+
}
14+
15+
/**
16+
* Custom component for a consistent page-building block.
17+
* @param title The title of the block on the page
18+
* @param children The children of the block
19+
*/
20+
const InfoBlock: React.FC<InfoBlockProps> = ({ title, icon, children }) => {
21+
return (
22+
<Grid container spacing={1}>
23+
<Grid item xs={12} display="flex" gap="5px" alignItems="center">
24+
<Typography sx={{ fontWeight: 'bold', fontSize: '19px' }}>{title}</Typography>
25+
{icon}
26+
</Grid>
27+
<Grid item xs={12}>
28+
{children}
29+
</Grid>
30+
</Grid>
31+
);
32+
};
33+
34+
export default InfoBlock;

src/frontend/src/components/NERButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Button, styled } from '@mui/material';
33
export const NERButton = styled(Button)({
44
textTransform: 'none',
55
fontSize: 16,
6-
borderColor: '#0062cc',
6+
borderColor: '#ef4345',
77
boxShadow: 'none',
88
'&:hover': {
99
backgroundColor: '#ff0000'

src/frontend/src/components/PageLayout.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Box } from '@mui/system';
1111

1212
interface PageLayoutProps {
1313
title?: string;
14+
chips?: ReactNode;
1415
hidePageTitle?: boolean;
1516
previousPages?: LinkItem[];
1617
headerRight?: ReactNode;
@@ -21,6 +22,7 @@ interface PageLayoutProps {
2122
const PageLayout: React.FC<PageLayoutProps> = ({
2223
children,
2324
title,
25+
chips,
2426
hidePageTitle = false,
2527
previousPages = [],
2628
headerRight,
@@ -33,7 +35,9 @@ const PageLayout: React.FC<PageLayoutProps> = ({
3335
<title>{`FinishLine ${title && `| ${title}`}`}</title>
3436
<meta name="description" content="FinishLine Project Management Dashboard" />
3537
</Helmet>
36-
{!hidePageTitle && title && <PageTitle sticky={stickyHeader} {...{ title, previousPages, headerRight, tabs }} />}
38+
{!hidePageTitle && title && (
39+
<PageTitle sticky={stickyHeader} {...{ title, chips, previousPages, headerRight, tabs }} />
40+
)}
3741
{children}
3842
</Box>
3943
);

src/frontend/src/components/Tabs.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,10 @@ const NERTabs = ({ setTab, tabsLabels, baseUrl, defaultTab, id }: TabProps) => {
3636
};
3737

3838
return (
39-
<Tabs
40-
sx={{ borderBottom: 1, borderColor: 'divider' }}
41-
value={tabValue}
42-
onChange={handleTabChange}
43-
aria-label={`${id}-tabs`}
44-
>
39+
<Tabs value={tabValue} onChange={handleTabChange} aria-label={`${id}-tabs`}>
4540
{tabsLabels.map((tab, idx) => (
4641
<Tab
42+
sx={{ borderBottom: 1, borderColor: 'divider' }}
4743
label={tab.tabName}
4844
aria-label={tab.tabUrlValue}
4945
value={idx}

0 commit comments

Comments
 (0)