Skip to content

Commit ac5ca76

Browse files
authored
Merge pull request #1743 from Northeastern-Electric-Racing/#1644-proposed-solution-redesign
#1644 proposed solution redesign
2 parents 29d5942 + ec668e5 commit ac5ca76

8 files changed

Lines changed: 161 additions & 122 deletions

File tree

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/layouts/PageTitle/PageTitle.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const PageTitle: React.FC<PageTitleProps> = ({ title, previousPages, headerRight
3939
zIndex={1}
4040
bgcolor={theme.palette.background.default}
4141
>
42-
<Grid container>
42+
<Grid container rowSpacing={1}>
4343
<Grid item xs={6} md={4}>
4444
<Typography variant="h4" fontSize={30}>
4545
{title}

src/frontend/src/pages/ChangeRequestDetailPage/ProposedSolutionView.tsx

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
*/
55

66
import { ProposedSolution } from 'shared';
7-
import PageBlock from '../../layouts/PageBlock';
87
import Chip from '@mui/material/Chip';
98
import Button from '@mui/material/Button';
109
import Grid from '@mui/material/Grid';
11-
import { dollarsPipe, weeksPipe } from '../../utils/pipes';
10+
import { weeksPipe } from '../../utils/pipes';
1211
import DeleteIcon from '@mui/icons-material/Delete';
1312
import DetailDisplay from '../../components/DetailDisplay';
13+
import { Typography, useTheme } from '@mui/material';
14+
import AttachMoneyIcon from '@mui/icons-material/AttachMoney';
15+
import ScheduleIcon from '@mui/icons-material/Schedule';
1416

1517
interface ProposedSolutionViewProps {
1618
proposedSolution: ProposedSolution;
@@ -26,39 +28,55 @@ const ProposedSolutionView: React.FC<ProposedSolutionViewProps> = ({
2628
crReviewed
2729
}) => {
2830
const faded = crReviewed != null && proposedSolution.approved === false;
31+
const theme = useTheme();
32+
const isDark = theme.palette.mode === 'dark';
2933

3034
return (
31-
<PageBlock title="" style={{ opacity: faded ? 0.5 : 1 }}>
32-
{showDeleteButton && onDelete !== undefined ? (
33-
<Button
34-
color="error"
35-
variant="outlined"
36-
onClick={() => {
37-
onDelete(proposedSolution);
38-
}}
39-
sx={{ position: 'absolute', right: 75 }}
40-
>
41-
<DeleteIcon />
42-
</Button>
43-
) : null}
44-
<Grid container rowSpacing={1} columnSpacing={{ xs: 1, sm: 2, md: 3 }}>
45-
<Grid item xs={7}>
35+
<Grid
36+
style={{ opacity: faded ? 0.5 : 1 }}
37+
sx={{
38+
minHeight: 300,
39+
p: 2,
40+
mb: 5,
41+
bgcolor: theme.palette.background.paper,
42+
width: '100%',
43+
borderRadius: '8px 8px 8px 8px',
44+
boxShadow: 1
45+
}}
46+
>
47+
<Grid container rowSpacing={1}>
48+
<Grid item xs={12} sx={{ p: 2, mt: 1, minHeight: 100, borderRadius: 1, bgcolor: isDark ? '#474848' : 'white' }}>
4649
<DetailDisplay label="Description" content={proposedSolution.description} />
4750
</Grid>
48-
<Grid item xs={3}>
49-
<DetailDisplay label="Budget Impact" content={dollarsPipe(proposedSolution.budgetImpact)} />
50-
</Grid>
51-
<Grid item xs={2}>
52-
{proposedSolution.approved ? <Chip label="Approved" color="success" /> : null}
53-
</Grid>
54-
<Grid item xs={7}>
51+
<Grid item xs={12} sx={{ p: 2, mt: 2, minHeight: 100, borderRadius: 1, bgcolor: isDark ? '#474848' : 'white' }}>
5552
<DetailDisplay label="Scope Impact" content={proposedSolution.scopeImpact} />
5653
</Grid>
57-
<Grid item xs={5}>
58-
<DetailDisplay label="Timeline Impact" content={weeksPipe(proposedSolution.timelineImpact)} />
54+
<Grid item xs={7} display="flex" sx={{ marginTop: 0.5 }}>
55+
<AttachMoneyIcon sx={{ mr: 1 }} />
56+
<Typography>{proposedSolution.budgetImpact}</Typography>
57+
</Grid>
58+
<Grid item xs={12} display="flex" justifyContent="space-between">
59+
<Grid display="flex" sx={{ marginTop: 0.5 }}>
60+
<ScheduleIcon sx={{ mr: 1 }} />
61+
<Typography>{weeksPipe(proposedSolution.timelineImpact)}</Typography>
62+
</Grid>
63+
<Grid item>{proposedSolution.approved && <Chip label="Approved" color="success" />}</Grid>
64+
{showDeleteButton && onDelete !== undefined && (
65+
<Grid item>
66+
<Button
67+
color="error"
68+
variant="outlined"
69+
onClick={() => {
70+
onDelete(proposedSolution);
71+
}}
72+
>
73+
<DeleteIcon />
74+
</Button>
75+
</Grid>
76+
)}
5977
</Grid>
6078
</Grid>
61-
</PageBlock>
79+
</Grid>
6280
);
6381
};
6482

src/frontend/src/pages/CreateChangeRequestPage/CreateChangeRequestView.tsx

Lines changed: 82 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { Controller, useFieldArray, useForm } from 'react-hook-form';
88
import { yupResolver } from '@hookform/resolvers/yup';
99
import { ChangeRequestReason, ChangeRequestType, Project, ProposedSolution, wbsPipe, WorkPackage } from 'shared';
1010
import { routes } from '../../utils/routes';
11-
import PageBlock from '../../layouts/PageBlock';
1211
import TextField from '@mui/material/TextField';
1312
import FormHelperText from '@mui/material/FormHelperText';
1413
import Box from '@mui/material/Box';
@@ -24,6 +23,7 @@ import {
2423
FormLabel,
2524
IconButton,
2625
MenuItem,
26+
RadioGroup,
2727
Select
2828
} from '@mui/material';
2929
import { FormInput } from './CreateChangeRequest';
@@ -37,6 +37,8 @@ import NERSuccessButton from '../../components/NERSuccessButton';
3737
import { wbsNamePipe } from '../../utils/pipes';
3838
import PageLayout from '../../components/PageLayout';
3939
import { wbsNumComparator } from 'shared/src/validate-wbs';
40+
import { ChangeEvent } from 'react';
41+
import { NERButton } from '../../components/NERButton';
4042
import { useQuery } from '../../hooks/utils.hooks';
4143

4244
interface CreateChangeRequestViewProps {
@@ -169,31 +171,46 @@ const CreateChangeRequestsView: React.FC<CreateChangeRequestViewProps> = ({
169171
<ReactHookTextField
170172
required
171173
multiline
174+
rows={4}
172175
control={control}
173176
label="Explain"
174-
sx={{ flexGrow: 1, mx: 1, borderRadius: 2 }}
177+
sx={{ flexGrow: 1, borderRadius: 2, width: '100%' }}
175178
{...register(`why.${index}.explain`)}
176179
errorMessage={errors.why?.[index]?.explain}
177180
/>
178181
);
179182
};
180183

181184
return (
182-
<PageLayout title="New Change Request" previousPages={[{ name: 'Change Requests', route: routes.CHANGE_REQUESTS }]}>
183-
<form
184-
id={'create-standard-change-request-form'}
185-
onSubmit={(e) => {
186-
e.preventDefault();
187-
e.stopPropagation();
188-
handleSubmit(onSubmit)(e);
189-
}}
190-
onKeyPress={(e) => {
191-
e.key === 'Enter' && e.preventDefault();
192-
}}
185+
<form
186+
id={'create-standard-change-request-form'}
187+
onSubmit={(e) => {
188+
e.preventDefault();
189+
e.stopPropagation();
190+
handleSubmit(onSubmit)(e);
191+
}}
192+
onKeyPress={(e) => {
193+
e.key === 'Enter' && e.preventDefault();
194+
}}
195+
>
196+
<PageLayout
197+
stickyHeader
198+
title="New Change Request"
199+
previousPages={[{ name: 'Change Requests', route: routes.CHANGE_REQUESTS }]}
200+
headerRight={
201+
<Box textAlign="right" sx={{ mb: 2 }}>
202+
<NERFailButton variant="contained" onClick={handleCancel} sx={{ mx: 1, width: 90 }}>
203+
Cancel
204+
</NERFailButton>
205+
<NERSuccessButton variant="contained" type="submit" sx={{ mx: 1, width: 90, mt: { xs: 1, md: 0 } }}>
206+
Submit
207+
</NERSuccessButton>
208+
</Box>
209+
}
193210
>
194-
<PageBlock title="Details">
195-
<Grid container spacing={2}>
196-
<Grid item xs={6}>
211+
<Grid container spacing={2} display="flex" justifyContent="space-between">
212+
<Grid container item spacing={2} xs={12} md={6}>
213+
<Grid item xs={12}>
197214
<FormLabel>WBS</FormLabel>
198215
<NERAutocomplete
199216
id="wbs-autocomplete"
@@ -204,61 +221,75 @@ const CreateChangeRequestsView: React.FC<CreateChangeRequestViewProps> = ({
204221
value={wbsDropdownOptions.find((element) => element.id === wbsNum) || null}
205222
/>
206223
</Grid>
207-
<Grid item xs={6}>
224+
<Grid item xs={10}>
208225
<FormControl fullWidth>
209226
<FormLabel>Type</FormLabel>
210227
<Controller
211228
name="type"
212229
control={control}
213230
rules={{ required: true }}
214231
render={({ field: { onChange, value } }) => (
215-
<TextField select onChange={onChange} value={value}>
216-
{permittedTypes.map((t) => (
217-
<MenuItem key={t} value={t}>
218-
{t}
219-
</MenuItem>
220-
))}
221-
</TextField>
232+
<RadioGroup onChange={onChange} value={value}>
233+
<Box display="flex" flexDirection={{ xs: 'column', md: 'row' }} sx={{ gap: 2 }}>
234+
{permittedTypes.map((t) => (
235+
<NERButton
236+
sx={{
237+
fontSize: { xs: '0.8rem', md: '1rem' },
238+
width: { xs: '83%', md: 'auto' }
239+
}}
240+
key={t}
241+
variant={value === t ? 'contained' : 'outlined'}
242+
onClick={() => onChange(t as 'ISSUE' | ChangeEvent<Element>)}
243+
>
244+
{t}
245+
</NERButton>
246+
))}
247+
</Box>
248+
</RadioGroup>
222249
)}
223250
/>
224251
</FormControl>
225252
</Grid>
226253
<Grid item xs={12}>
227254
<FormControl fullWidth>
228-
<FormLabel>What</FormLabel>
255+
<FormLabel>What needs to be changed?</FormLabel>
229256
<ReactHookTextField
230257
name="what"
231258
control={control}
232259
multiline
233260
rows={4}
234261
errorMessage={errors.what}
235-
placeholder="What is the situation?"
262+
placeholder="Explain *"
236263
/>
237264
</FormControl>
238265
</Grid>
239266
<Grid item xs={12}>
240267
<FormControl fullWidth>
241-
<FormLabel>Why</FormLabel>
268+
<FormLabel>Why does this need to be changed?</FormLabel>
242269
<Box>
243270
{whys.map((element, index) => (
244-
<Box display="flex" flexDirection="row" sx={{ mb: 1 }}>
245-
<Select
246-
{...register(`why.${index}.type`)}
247-
sx={{ width: 200 }}
248-
defaultValue={element.type}
249-
key={element.id}
250-
>
251-
{Object.values(ChangeRequestReason).map((type) => (
252-
<MenuItem key={type} value={type}>
253-
{type}
254-
</MenuItem>
255-
))}
256-
</Select>
257-
{renderReasonInput(index)}
258-
<IconButton type="button" onClick={() => removeWhy(index)}>
259-
<DeleteIcon />
260-
</IconButton>
261-
</Box>
271+
<Grid container xs={12} display="flex" flexDirection="row" sx={{ mb: 1 }}>
272+
<Grid item xs={12}>
273+
<Select
274+
{...register(`why.${index}.type`)}
275+
sx={{ width: { sx: 'auto', md: 200 }, mr: 2 }}
276+
defaultValue={element.type}
277+
key={element.id}
278+
>
279+
{Object.values(ChangeRequestReason).map((type) => (
280+
<MenuItem key={type} value={type}>
281+
{type}
282+
</MenuItem>
283+
))}
284+
</Select>
285+
<IconButton color="error" type="button" onClick={() => removeWhy(index)}>
286+
<DeleteIcon />
287+
</IconButton>
288+
</Grid>
289+
<Grid item xs={12} sx={{ mt: 1, mb: 1 }}>
290+
{renderReasonInput(index)}
291+
</Grid>
292+
</Grid>
262293
))}
263294
</Box>
264295
<FormHelperText>{errors.why?.message}</FormHelperText>
@@ -276,20 +307,12 @@ const CreateChangeRequestsView: React.FC<CreateChangeRequestViewProps> = ({
276307
</Button>
277308
</Grid>
278309
</Grid>
279-
</PageBlock>
280-
<PageBlock title="Proposed Solutions">
281-
<CreateProposedSolutionsList proposedSolutions={proposedSolutions} setProposedSolutions={setProposedSolutions} />
282-
</PageBlock>
283-
<Box textAlign="right">
284-
<NERFailButton variant="contained" onClick={handleCancel} sx={{ mx: 1 }}>
285-
Cancel
286-
</NERFailButton>
287-
<NERSuccessButton variant="contained" type="submit" sx={{ mx: 1 }}>
288-
Submit
289-
</NERSuccessButton>
290-
</Box>
291-
</form>
292-
</PageLayout>
310+
<Grid item xs={12} md={5} sx={{ mt: -2 }}>
311+
<CreateProposedSolutionsList proposedSolutions={proposedSolutions} setProposedSolutions={setProposedSolutions} />
312+
</Grid>
313+
</Grid>
314+
</PageLayout>
315+
</form>
293316
);
294317
};
295318

src/frontend/src/pages/CreateChangeRequestPage/CreateProposedSolutionsList.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@ import { isGuest, ProposedSolution } from 'shared';
77
import ProposedSolutionForm from '../ChangeRequestDetailPage/ProposedSolutionForm';
88
import { useState } from 'react';
99
import ProposedSolutionView from '../ChangeRequestDetailPage/ProposedSolutionView';
10-
import styles from '../../stylesheets/pages/change-request-detail-page/proposed-solutions-list.module.css';
11-
import { useAuth } from '../../hooks/auth.hooks';
12-
import { Button } from '@mui/material';
13-
import LoadingIndicator from '../../components/LoadingIndicator';
10+
import { Button, Typography } from '@mui/material';
11+
import { useCurrentUser } from '../../hooks/users.hooks';
12+
import { Box } from '@mui/system';
1413

1514
interface CreateProposedSolutionsListProps {
1615
proposedSolutions: ProposedSolution[];
@@ -21,11 +20,9 @@ const CreateProposedSolutionsList: React.FC<CreateProposedSolutionsListProps> =
2120
proposedSolutions,
2221
setProposedSolutions
2322
}) => {
24-
const auth = useAuth();
23+
const user = useCurrentUser();
2524
const [showEditableForm, setShowEditableForm] = useState<boolean>(false);
2625

27-
if (!auth.user) return <LoadingIndicator />;
28-
2926
const addProposedSolution = async (data: ProposedSolution) => {
3027
setProposedSolutions([...proposedSolutions, data]);
3128
setShowEditableForm(false);
@@ -37,7 +34,17 @@ const CreateProposedSolutionsList: React.FC<CreateProposedSolutionsListProps> =
3734

3835
return (
3936
<>
40-
<div className={styles.proposedSolutionsList}>
37+
{!isGuest(user.role) && (
38+
<Box display="flex" justifyContent="space-between">
39+
<Typography variant="h5" sx={{ mt: 2 }}>
40+
Proposed Solutions
41+
</Typography>
42+
<Button onClick={() => setShowEditableForm(true)} variant="contained" color="success" sx={{ mt: 2 }}>
43+
+ Add Solution
44+
</Button>
45+
</Box>
46+
)}
47+
<div style={{ marginTop: '30px' }}>
4148
{proposedSolutions.map((proposedSolution, i) => (
4249
<ProposedSolutionView
4350
key={i}
@@ -47,13 +54,7 @@ const CreateProposedSolutionsList: React.FC<CreateProposedSolutionsListProps> =
4754
/>
4855
))}
4956
</div>
50-
{!isGuest(auth.user.role) ? (
51-
<Button onClick={() => setShowEditableForm(true)} variant="contained" color="success" sx={{ marginTop: 2 }}>
52-
+ Add Proposed Solution
53-
</Button>
54-
) : (
55-
''
56-
)}
57+
5758
<ProposedSolutionForm onAdd={addProposedSolution} open={showEditableForm} onClose={() => setShowEditableForm(false)} />
5859
</>
5960
);

0 commit comments

Comments
 (0)