Skip to content

Commit a305fa9

Browse files
authored
Merge pull request #2208 from Northeastern-Electric-Racing/#2131-drc-create-modal
#2131 Create DRC create modal
2 parents fac7a7b + 558bb23 commit a305fa9

10 files changed

Lines changed: 501 additions & 89 deletions

File tree

src/backend/src/controllers/design-reviews.controllers.ts

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ export default class DesignReviewsController {
1313
}
1414
}
1515

16+
static async getAllTeamTypes(req: Request, res: Response, next: NextFunction) {
17+
try {
18+
const teamTypes = await DesignReviewsService.getAllTeamTypes();
19+
return res.status(200).json(teamTypes);
20+
} catch (error: unknown) {
21+
next(error);
22+
}
23+
}
24+
1625
static async deleteDesignReview(req: Request, res: Response, next: NextFunction) {
1726
try {
1827
const drId: string = req.params.designReviewId;
@@ -27,33 +36,16 @@ export default class DesignReviewsController {
2736
static async createDesignReview(req: Request, res: Response, next: NextFunction) {
2837
try {
2938
const submitter: User = await getCurrentUser(res);
30-
const {
31-
dateScheduled,
32-
teamTypeId,
33-
requiredMemberIds,
34-
optionalMemberIds,
35-
location,
36-
isOnline,
37-
isInPerson,
38-
zoomLink,
39-
docTemplateLink,
40-
wbsNum,
41-
meetingTimes
42-
} = req.body;
39+
const { dateScheduled, teamTypeId, requiredMemberIds, optionalMemberIds, wbsNum, meetingTimes } = req.body;
4340

4441
const createdDesignReview = await DesignReviewsService.createDesignReview(
4542
submitter,
4643
dateScheduled,
4744
teamTypeId,
4845
requiredMemberIds,
4946
optionalMemberIds,
50-
isOnline,
51-
isInPerson,
52-
docTemplateLink,
5347
wbsNum,
54-
meetingTimes,
55-
zoomLink,
56-
location
48+
meetingTimes
5749
);
5850
return res.status(200).json(createdDesignReview);
5951
} catch (error: unknown) {

src/backend/src/routes/design-reviews.routes.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const designReviewsRouter = express.Router();
77

88
designReviewsRouter.get('/', DesignReviewsController.getAllDesignReviews);
99

10+
designReviewsRouter.get('/teamType/all', DesignReviewsController.getAllTeamTypes);
11+
1012
designReviewsRouter.delete('/:designReviewId/delete', DesignReviewsController.deleteDesignReview);
1113
designReviewsRouter.get('/:designReviewId', DesignReviewsController.getSingleDesignReview);
1214

@@ -18,12 +20,9 @@ designReviewsRouter.post(
1820
intMinZero(body('requiredMemberIds.*')),
1921
body('optionalMemberIds').isArray(),
2022
intMinZero(body('optionalMemberIds.*')),
21-
nonEmptyString(body('location').optional()),
22-
body('isOnline').isBoolean(),
23-
body('isInPerson').isBoolean(),
24-
nonEmptyString(body('zoomLink').optional()),
25-
nonEmptyString(body('docTemplateLink')).optional(),
26-
body('wbsNum'),
23+
intMinZero(body('wbsNum.carNumber')),
24+
intMinZero(body('wbsNum.projectNumber')),
25+
intMinZero(body('wbsNum.workPackageNumber')),
2726
body('meetingTimes').isArray(),
2827
intMinZero(body('meetingTimes.*')),
2928
validateInputs,

src/backend/src/services/design-reviews.services.ts

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Design_Review_Status, User } from '@prisma/client';
2-
import { DesignReview, WbsNumber, isAdmin, isLeadership, isNotLeadership } from 'shared';
2+
import { DesignReview, TeamType, WbsNumber, isAdmin, isLeadership, isNotLeadership } from 'shared';
33
import prisma from '../prisma/prisma';
44
import {
55
NotFoundException,
@@ -27,6 +27,11 @@ export default class DesignReviewsService {
2727
return designReviews.map(designReviewTransformer);
2828
}
2929

30+
static async getAllTeamTypes(): Promise<TeamType[]> {
31+
const teamTypes = await prisma.teamType.findMany();
32+
return teamTypes;
33+
}
34+
3035
/**
3136
* Deletes a design review
3237
* @param submitter the user who deleted the design review
@@ -55,34 +60,24 @@ export default class DesignReviewsService {
5560
}
5661

5762
/**
58-
* Creates a design review
59-
* @param submitter user who submitted the design review
63+
* Create a design review
64+
* @param submitter User submitting the design review
6065
* @param dateScheduled when the design review is scheduled for
61-
* @param teamTypeId team type id of the design review
62-
* @param requiredMemberIds ids of the required members to attend the design review
63-
* @param optionalMemberIds ids of the optional members to attend the design reivew
64-
* @param isOnline if design review is online
65-
* @param isInPerson if design review is in person
66-
* @param docTemplateLink link to the doc template
67-
* @param wbsNum wbs number for the design review
68-
* @param meetingTimes the meeting times for the design review
69-
* @param zoomLink link for the zoom if design review is online
70-
* @param location location of the design review if in person
71-
* @returns a design review
66+
* @param teamTypeId team type id
67+
* @param requiredMemberIds ids of members who are required to go
68+
* @param optionalMemberIds ids of members who do not have to go
69+
* @param wbsNum wbs num related to the design review
70+
* @param meetingTimes meeting times of the design review
71+
* @returns a new design review
7272
*/
7373
static async createDesignReview(
7474
submitter: User,
7575
dateScheduled: Date,
7676
teamTypeId: string,
7777
requiredMemberIds: number[],
7878
optionalMemberIds: number[],
79-
isOnline: boolean,
80-
isInPerson: boolean,
81-
docTemplateLink: string,
8279
wbsNum: WbsNumber,
83-
meetingTimes: number[],
84-
zoomLink?: string,
85-
location?: string
80+
meetingTimes: number[]
8681
): Promise<DesignReview> {
8782
if (!isLeadership(submitter.role)) throw new AccessDeniedException('create design review');
8883

@@ -125,14 +120,6 @@ export default class DesignReviewsService {
125120
}
126121
}
127122

128-
if (isOnline && !zoomLink) {
129-
throw new HttpException(400, 'If the design review is online then there needs to be a zoom link');
130-
}
131-
132-
if (isInPerson && !location) {
133-
throw new HttpException(400, 'If the design review is in person then there needs to be a location');
134-
}
135-
136123
if (dateScheduled.valueOf() < new Date().valueOf()) {
137124
throw new HttpException(400, 'Design review cannot be scheduled for a past day');
138125
}
@@ -142,11 +129,8 @@ export default class DesignReviewsService {
142129
dateScheduled,
143130
dateCreated: new Date(),
144131
status: Design_Review_Status.UNCONFIRMED,
145-
location,
146-
isOnline,
147-
isInPerson,
148-
zoomLink,
149-
docTemplateLink,
132+
isOnline: false,
133+
isInPerson: false,
150134
userCreated: { connect: { userId: submitter.userId } },
151135
teamType: { connect: { teamTypeId: teamType.teamTypeId } },
152136
requiredMembers: { connect: requiredMemberIds.map((memberId) => ({ userId: memberId })) },

src/backend/tests/design-reviews.test.ts

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -426,16 +426,12 @@ describe('Design Reviews', () => {
426426
'1',
427427
[],
428428
[],
429-
true,
430-
false,
431-
'doc temp',
432429
{
433430
carNumber: 1,
434431
projectNumber: 2,
435432
workPackageNumber: 0
436433
},
437-
[0, 1, 2, 3],
438-
'zoooooom'
434+
[0, 1, 2, 3]
439435
);
440436

441437
expect(res.teamType).toBe(teamType1);
@@ -449,16 +445,12 @@ describe('Design Reviews', () => {
449445
'1',
450446
[],
451447
[],
452-
true,
453-
false,
454-
'doc temp',
455448
{
456449
carNumber: 1,
457450
projectNumber: 2,
458451
workPackageNumber: 0
459452
},
460-
[0, 1, 2, 3],
461-
'zoom'
453+
[0, 1, 2, 3]
462454
)
463455
).rejects.toThrow(new AccessDeniedException('create design review'));
464456
});
@@ -472,16 +464,12 @@ describe('Design Reviews', () => {
472464
'15',
473465
[],
474466
[],
475-
true,
476-
false,
477-
'doc temp',
478467
{
479468
carNumber: 1,
480469
projectNumber: 2,
481470
workPackageNumber: 0
482471
},
483-
[0, 1, 2, 3],
484-
'zoom'
472+
[0, 1, 2, 3]
485473
)
486474
).rejects.toThrow(new NotFoundException('Team Type', '15'));
487475
});
@@ -497,16 +485,12 @@ describe('Design Reviews', () => {
497485
'1',
498486
[],
499487
[],
500-
true,
501-
false,
502-
'doc temp',
503488
{
504489
carNumber: 15,
505490
projectNumber: 2,
506491
workPackageNumber: 0
507492
},
508-
[0, 1, 2, 3],
509-
'zoom'
493+
[0, 1, 2, 3]
510494
)
511495
).rejects.toThrow(new NotFoundException('WBS Element', 15));
512496
});

src/frontend/src/apis/design-reviews.api.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,19 @@
44
*/
55
import { EditDesignReviewPayload } from '../hooks/design-reviews.hooks';
66
import axios from '../utils/axios';
7+
import { DesignReview } from 'shared';
78
import { apiUrls } from '../utils/urls';
9+
import { CreateDesignReviewsPayload } from '../hooks/design-reviews.hooks';
810
import { designReviewTransformer } from './transformers/design-reviews.tranformers';
911

12+
/**
13+
* Create a design review
14+
* @param payload all info needed to create a design review
15+
*/
16+
export const createDesignReviews = async (payload: CreateDesignReviewsPayload) => {
17+
return axios.post<DesignReview>(apiUrls.designReviewsCreate(), payload);
18+
};
19+
1020
/**
1121
* Gets all the design reviews
1222
*/
@@ -16,6 +26,15 @@ export const getAllDesignReviews = () => {
1626
});
1727
};
1828

29+
/**
30+
* Gets all the team types
31+
*/
32+
export const getAllTeamTypes = () => {
33+
return axios.get(apiUrls.teamTypes(), {
34+
transformResponse: (data) => JSON.parse(data)
35+
});
36+
};
37+
1938
/**
2039
* Edit a design review
2140
*

src/frontend/src/hooks/design-reviews.hooks.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,39 @@
33
* See the LICENSE file in the repository root folder for details.
44
*/
55
import { useMutation, useQuery, useQueryClient } from 'react-query';
6-
import { editDesignReview, getAllDesignReviews, getSingleDesignReview } from '../apis/design-reviews.api';
7-
import { DesignReview, DesignReviewStatus } from 'shared';
6+
import { DesignReview, TeamType, WbsNumber, DesignReviewStatus } from 'shared';
7+
import {
8+
editDesignReview,
9+
createDesignReviews,
10+
getAllDesignReviews,
11+
getAllTeamTypes,
12+
getSingleDesignReview
13+
} from '../apis/design-reviews.api';
14+
15+
export interface CreateDesignReviewsPayload {
16+
dateScheduled: Date;
17+
teamTypeId: string;
18+
requiredMemberIds: number[];
19+
optionalMemberIds: number[];
20+
wbsNum: WbsNumber;
21+
meetingTimes: number[];
22+
}
23+
24+
export const useCreateDesignReviews = () => {
25+
const queryClient = useQueryClient();
26+
return useMutation<DesignReview, Error, CreateDesignReviewsPayload>(
27+
['design reviews', 'create'],
28+
async (formData: CreateDesignReviewsPayload) => {
29+
const { data } = await createDesignReviews(formData);
30+
return data;
31+
},
32+
{
33+
onSuccess: () => {
34+
queryClient.invalidateQueries(['design-reviews']);
35+
}
36+
}
37+
);
38+
};
839

940
/**
1041
* Custom react hook to get all design reviews
@@ -53,6 +84,18 @@ export const useEditDesignReview = (designReviewId: string) => {
5384
);
5485
};
5586

87+
/**
88+
* Custom react hook to get all team types
89+
*
90+
* @returns all the team types
91+
*/
92+
export const useAllTeamTypes = () => {
93+
return useQuery<TeamType[], Error>(['teamTypes'], async () => {
94+
const { data } = await getAllTeamTypes();
95+
return data;
96+
});
97+
};
98+
5699
/**
57100
* Custom react hook to get a single design review
58101
*

src/frontend/src/pages/CalendarPage/CalendarComponents/CalendarDayCard.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import { Box, Card, CardContent, Grid, IconButton, Link, Stack, Tooltip, Typography } from '@mui/material';
22
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';
3-
import { DesignReview } from 'shared';
3+
import { DesignReview, TeamType } from 'shared';
44
import { meetingStartTimePipe } from '../../../utils/pipes';
55
import ConstructionIcon from '@mui/icons-material/Construction';
66
import WorkOutlineIcon from '@mui/icons-material/WorkOutline';
77
import ElectricalServicesIcon from '@mui/icons-material/ElectricalServices';
88
import TerminalIcon from '@mui/icons-material/Terminal';
99
import { useState } from 'react';
1010
import DRCSummaryModal from '../DesignReviewSummaryModal';
11+
import { DesignReviewCreateModal } from '../DesignReviewCreateModal';
1112
import DynamicTooltip from '../../../components/DynamicTooltip';
1213

1314
export const getTeamTypeIcon = (teamTypeName: string, isLarge?: boolean) => {
@@ -23,13 +24,16 @@ export const getTeamTypeIcon = (teamTypeName: string, isLarge?: boolean) => {
2324
interface CalendarDayCardProps {
2425
cardDate: Date;
2526
events: DesignReview[];
27+
teamTypes: TeamType[];
2628
}
2729

28-
const CalendarDayCard: React.FC<CalendarDayCardProps> = ({ cardDate, events }) => {
30+
const CalendarDayCard: React.FC<CalendarDayCardProps> = ({ cardDate, events, teamTypes }) => {
31+
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
32+
2933
const DayCardTitle = () => (
3034
<Grid container alignItems="center" margin={0} padding={0}>
3135
<Grid item>
32-
<IconButton>
36+
<IconButton onClick={() => setIsCreateModalOpen(true)}>
3337
<AddCircleOutlineIcon fontSize="small" />
3438
</IconButton>
3539
</Grid>
@@ -145,6 +149,14 @@ const CalendarDayCard: React.FC<CalendarDayCardProps> = ({ cardDate, events }) =
145149

146150
return (
147151
<Card sx={{ borderRadius: 2, minWidth: 150, maxWidth: 150, minHeight: 90, maxHeight: 90 }}>
152+
<DesignReviewCreateModal
153+
showModal={isCreateModalOpen}
154+
handleClose={() => {
155+
setIsCreateModalOpen(false);
156+
}}
157+
teamTypes={teamTypes}
158+
defaultDate={cardDate}
159+
/>
148160
<CardContent sx={{ padding: 0 }}>
149161
<DayCardTitle />
150162
{events.length < 3 ? (

0 commit comments

Comments
 (0)