Skip to content

Commit 546213b

Browse files
authored
Merge pull request #3476 from Northeastern-Electric-Racing/#3435-implement-action-button
#3435 implemented part page actions button
2 parents 30c21d2 + 812d583 commit 546213b

13 files changed

Lines changed: 658 additions & 143 deletions

File tree

src/backend/src/controllers/part-review.controllers.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export default class PartReviewController {
8383

8484
static async updatePart(req: Request, res: Response, next: NextFunction) {
8585
try {
86-
const { index, commonName, description, reviewStatus, tagIds, assigneeIds } = req.body;
86+
const { index, commonName, description, reviewStatus, tagIds, assigneeIds, reviewerIds } = req.body;
8787
const { partId } = req.params;
8888
const part = await PartReviewService.updatePart(
8989
req.organization.organizationId,
@@ -94,7 +94,8 @@ export default class PartReviewController {
9494
description,
9595
reviewStatus,
9696
tagIds,
97-
assigneeIds
97+
assigneeIds,
98+
reviewerIds
9899
);
99100
res.status(200).json(part);
100101
} catch (error: unknown) {

src/backend/src/routes/parts.routes.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ partsRouter.post(
6161
PartReviewController.createSubmission
6262
);
6363

64+
partsRouter.post(
65+
'/submission/:submissionId/update',
66+
nonEmptyString(body('name')),
67+
body('notes').optional().isString(),
68+
validateInputs,
69+
PartReviewController.updateSubmission
70+
);
71+
6472
partsRouter.get('/tags', PartReviewController.getAllPartTags);
6573
partsRouter.get('/faqs', PartReviewController.getAllPartReviewFAQS);
6674

@@ -173,6 +181,7 @@ partsRouter.post(
173181
body('reviewStatus').custom((value) => Object.values(Review_Status).includes(value)),
174182
body('tagIds').isArray(),
175183
body('assigneeIds').isArray(),
184+
body('reviewerIds').isArray(),
176185
validateInputs,
177186
PartReviewController.updatePart
178187
);

src/backend/src/services/part-review.services.ts

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ export default class PartReviewService {
213213
}
214214

215215
static async uploadFile(file: Express.Multer.File) {
216+
console.log('Service Function file', file);
216217
const data = await uploadFile(file);
217218

218219
if (!data?.id) {
@@ -231,7 +232,8 @@ export default class PartReviewService {
231232
description: string,
232233
reviewStatus: Review_Status,
233234
tagIds: string[],
234-
assigneeIds: string[]
235+
assigneeIds: string[],
236+
reviewerIds: string[]
235237
) {
236238
const part = await prisma.part.findUnique({
237239
where: { partId },
@@ -247,24 +249,63 @@ export default class PartReviewService {
247249

248250
if (!hasPermission) throw new AccessDeniedException('Only leadership and the part creator can update part data');
249251

250-
const updatedPart = await prisma.part.update({
251-
where: { partId },
252-
data: {
253-
index,
254-
commonName,
255-
description,
256-
status: reviewStatus,
257-
tags: {
258-
set: tagIds.map((partTagId) => ({ partTagId }))
252+
const editedPart = await prisma.$transaction(async (tx) => {
253+
const editedPart = await tx.part.update({
254+
where: { partId },
255+
data: {
256+
index,
257+
commonName,
258+
description,
259+
status: reviewStatus,
260+
tags: {
261+
set: tagIds.map((partTagId) => ({ partTagId }))
262+
},
263+
assignees: {
264+
set: assigneeIds.map((userId) => ({ userId }))
265+
}
259266
},
260-
assignees: {
261-
set: assigneeIds.map((userId) => ({ userId }))
262-
}
263-
},
264-
...getPartQueryArgs(organizationId)
267+
...getPartQueryArgs(organizationId)
268+
});
269+
270+
const reviewersToAdd = reviewerIds.filter(
271+
(id) => !editedPart.reviewRequests.some((reviewReq) => reviewReq.reviewerRequested.userId === id)
272+
);
273+
274+
await Promise.all(
275+
reviewersToAdd.map(async (id) => {
276+
return tx.partReviewRequest.create({
277+
data: {
278+
part: {
279+
connect: {
280+
partId: editedPart.partId
281+
}
282+
},
283+
requester: { connect: { userId: updater.userId } },
284+
reviewerRequested: { connect: { userId: id } }
285+
}
286+
});
287+
})
288+
);
289+
290+
const reviewRequestsToRemove = editedPart.reviewRequests.filter(
291+
(reviewReq) => !reviewerIds.includes(reviewReq.reviewerRequested.userId)
292+
);
293+
294+
await Promise.all(
295+
reviewRequestsToRemove.map(async (reviewReq) => {
296+
return tx.partReviewRequest.update({
297+
where: { partReviewRequestId: reviewReq.partReviewRequestId },
298+
data: {
299+
dateDeleted: new Date()
300+
}
301+
});
302+
})
303+
);
304+
305+
return editedPart;
265306
});
266307

267-
return partTransformer(updatedPart);
308+
return partTransformer(editedPart);
268309
}
269310

270311
static async deletePart(partId: string, deleter: User, organizationId: string) {

src/backend/tests/unit/part-review.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ describe('part review tests', () => {
215215
'new description',
216216
Review_Status.IN_REVIEW,
217217
[tag2.partTagId],
218-
[superman.userId, nonAdmin.userId]
218+
[superman.userId, nonAdmin.userId],
219+
[batman.userId]
219220
);
220221

221222
expect(updatedPart.commonName).toBe('part2');
@@ -250,7 +251,8 @@ describe('part review tests', () => {
250251
'new description',
251252
Review_Status.IN_REVIEW,
252253
[tag2.partTagId],
253-
[superman.userId, nonAdmin.userId]
254+
[superman.userId, nonAdmin.userId],
255+
[batman.userId]
254256
)
255257
).rejects.toThrow(new DeletedException('Part', part.partId));
256258
});
@@ -307,7 +309,8 @@ describe('part review tests', () => {
307309
'new description',
308310
Review_Status.IN_REVIEW,
309311
[],
310-
[superman.userId, nonAdmin.userId]
312+
[superman.userId, nonAdmin.userId],
313+
[batman.userId]
311314
)
312315
).rejects.toThrow(new AccessDeniedException('Only leadership and the part creator can update part data'));
313316
});

src/frontend/src/hooks/part-review.hooks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ export const useUploadPreviewImage = (partId: string) => {
190190
*/
191191
export const useDeletePart = (partId: string) => {
192192
const queryClient = useQueryClient();
193-
return useMutation<{ message: string }, Error, any>(
193+
return useMutation<{ message: string }, Error, void>(
194194
['parts', 'delete'],
195195
async () => {
196196
const { data } = await deletePart(partId);

src/frontend/src/pages/PartPage/PartPage.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import PartSubmissionDetails from './PartPageComponents/PartSubmissionDetails';
1414
import PartOverview from './PartPageComponents/PartOverview';
1515
import PartHistoryView from './PartPageComponents/PartHistoryView';
1616
import ReviewSidebar from './PartPageComponents/ReviewPage';
17+
import PartActionsMenu from './PartPageComponents/PartActionsMenu';
1718

1819
const PartPage: React.FC = () => {
1920
interface ParamTypes {
@@ -186,6 +187,14 @@ const PartPage: React.FC = () => {
186187
{ name: `${wbsPipe(project.wbsNum)} - ${project.name}`, route: `${routes.PROJECTS}/${wbsNum}` },
187188
{ name: 'Files', route: `${routes.PROJECTS}/${wbsNum}/parts-review` }
188189
]}
190+
headerRight={
191+
<PartActionsMenu
192+
part={partWithAllReviews}
193+
submissionIndex={subIndex}
194+
reviewIndex={reviewIndex}
195+
wbsNum={validateWBS(wbsNum)}
196+
/>
197+
}
189198
>
190199
<Breadcrumbs sx={{ mb: 2 }}></Breadcrumbs>
191200
<Grid container px={2} gap={5}>

0 commit comments

Comments
 (0)