Skip to content

Commit daf94b3

Browse files
committed
Merge branch 'develop' into #4107-maintenance-bom-inline-editing
2 parents 1592ff5 + 58b1740 commit daf94b3

27 files changed

Lines changed: 622 additions & 28 deletions

File tree

src/backend/src/controllers/finance.controllers.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ export default class FinanceController {
4343
contactPhone,
4444
contactPosition,
4545
stockDescription,
46-
discountDescription
46+
discountDescription,
47+
req.file
4748
);
4849
res.status(200).json(sponsor);
4950
} catch (error: unknown) {
@@ -383,7 +384,8 @@ export default class FinanceController {
383384
contactPhone,
384385
contactPosition,
385386
stockDescription,
386-
discountDescription
387+
discountDescription,
388+
req.file
387389
);
388390

389391
res.status(200).json(updatedSponsor);

src/backend/src/controllers/recruitment.controllers.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,48 @@ export default class RecruitmentController {
9999

100100
static async createGuestDefinition(req: Request, res: Response, next: NextFunction) {
101101
try {
102-
const { term, description, order, icon, buttonText, buttonLink } = req.body;
102+
const { term, description, order, icon, type, buttonText, buttonLink } = req.body;
103103
const definition = await RecruitmentServices.createGuestDefinition(
104104
req.currentUser,
105105
req.organization,
106106
term,
107107
description,
108108
order,
109+
type,
110+
icon,
111+
buttonText,
112+
buttonLink
113+
);
114+
res.status(200).json(definition);
115+
} catch (error: unknown) {
116+
next(error);
117+
}
118+
}
119+
120+
static async getSingleGuestDefinition(req: Request, res: Response, next: NextFunction) {
121+
try {
122+
const { definitionId } = req.params as Record<string, string>;
123+
124+
const definition = await RecruitmentServices.getSingleGuestDefinition(req.organization, definitionId);
125+
res.status(200).json(definition);
126+
} catch (error: unknown) {
127+
next(error);
128+
}
129+
}
130+
131+
static async editGuestDefinition(req: Request, res: Response, next: NextFunction) {
132+
try {
133+
const { definitionId } = req.params as Record<string, string>;
134+
const { term, description, order, type, icon, buttonText, buttonLink } = req.body;
135+
136+
const definition = await RecruitmentServices.editGuestDefinition(
137+
req.currentUser,
138+
req.organization,
139+
term,
140+
description,
141+
definitionId,
142+
order,
143+
type,
109144
icon,
110145
buttonText,
111146
buttonLink
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- CreateEnum
2+
CREATE TYPE "Guest_Definition_Type" AS ENUM ('PROJECT_MANAGEMENT', 'INFO_PAGE');
3+
4+
-- AlterTable
5+
ALTER TABLE "Guest_Definition" ADD COLUMN "type" "Guest_Definition_Type" NOT NULL DEFAULT 'INFO_PAGE';

src/backend/src/prisma/schema.prisma

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1787,21 +1787,27 @@ model Reimbursement_Request_Comment {
17871787
@@index([reimbursementRequestId])
17881788
}
17891789

1790+
enum Guest_Definition_Type {
1791+
PROJECT_MANAGEMENT
1792+
INFO_PAGE
1793+
}
1794+
17901795
model Guest_Definition {
1791-
definitionId String @id @default(uuid())
1796+
definitionId String @id @default(uuid())
17921797
term String
17931798
description String
17941799
order Int
1800+
type Guest_Definition_Type @default(INFO_PAGE)
17951801
buttonText String?
17961802
buttonLink String?
17971803
icon String?
1798-
dateCreated DateTime @default(now())
1804+
dateCreated DateTime @default(now())
17991805
dateDeleted DateTime?
1800-
userDeleted User? @relation(fields: [userDeletedId], references: [userId], name: "guestDefinitionDeleter")
1806+
userDeleted User? @relation(fields: [userDeletedId], references: [userId], name: "guestDefinitionDeleter")
18011807
userDeletedId String?
1802-
userCreated User @relation(fields: [userCreatedId], references: [userId], name: "guestDefinitionCreator")
1808+
userCreated User @relation(fields: [userCreatedId], references: [userId], name: "guestDefinitionCreator")
18031809
userCreatedId String
1804-
organization Organization @relation(fields: [organizationId], references: [organizationId])
1810+
organization Organization @relation(fields: [organizationId], references: [organizationId])
18051811
organizationId String
18061812
18071813
@@index([organizationId])

src/backend/src/prisma/seed.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,15 @@ import { dbSeedAllTeams } from './seed-data/teams.seed.js';
2222
import { seedReimbursementRequests } from './seed-data/reimbursement-requests.seed.js';
2323
import ChangeRequestsService from '../services/change-requests.services.js';
2424
import TeamsService from '../services/teams.services.js';
25-
import { DayOfWeek, MaterialStatus, RoleEnum, StandardChangeRequest, WbsElementStatus, WorkPackageStage } from 'shared';
25+
import {
26+
DayOfWeek,
27+
GuestDefinitionType,
28+
MaterialStatus,
29+
RoleEnum,
30+
StandardChangeRequest,
31+
WbsElementStatus,
32+
WorkPackageStage
33+
} from 'shared';
2634
import TasksService from '../services/tasks.services.js';
2735
import { seedProject } from './seed-data/projects.seed.js';
2836
import { seedWorkPackage } from './seed-data/work-packages.seed.js';
@@ -5117,6 +5125,7 @@ const performSeed: () => Promise<void> = async () => {
51175125
term: 'NER',
51185126
description: 'A really awesome organization!',
51195127
order: 0,
5128+
type: 'INFO_PAGE',
51205129
organizationId,
51215130
userCreatedId: batman.userId
51225131
}
@@ -5128,6 +5137,7 @@ const performSeed: () => Promise<void> = async () => {
51285137
'Projects',
51295138
'This is the definition of a project. Projects are blah blah blah',
51305139
0,
5140+
GuestDefinitionType.PROJECT_MANAGEMENT,
51315141
'bar_chart',
51325142
'Click here to view all our projects!',
51335143
'/projects'
@@ -5139,6 +5149,7 @@ const performSeed: () => Promise<void> = async () => {
51395149
'Change Requests',
51405150
'This is the definiton for a change request. Changes requests are blah blah blah',
51415151
0,
5152+
GuestDefinitionType.PROJECT_MANAGEMENT,
51425153
'bar_chart',
51435154
'Click here to view all our change requests!',
51445155
'/change-requests'
@@ -5150,6 +5161,7 @@ const performSeed: () => Promise<void> = async () => {
51505161
'Gantt Chart',
51515162
'This is the definiton for a change request. Changes requests are blah blah blah',
51525163
0,
5164+
GuestDefinitionType.PROJECT_MANAGEMENT,
51535165
'bar_chart',
51545166
'Click here to view all our projects!',
51555167
'/gantt'
@@ -5161,6 +5173,7 @@ const performSeed: () => Promise<void> = async () => {
51615173
'Design Reviews',
51625174
'This is the definiton for a design review. Design reviews are blah blah blah',
51635175
0,
5176+
GuestDefinitionType.PROJECT_MANAGEMENT,
51645177
'bar_chart',
51655178
'Click here to view all our design reviews!',
51665179
'/design-reviews'

src/backend/src/routes/finance.routes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@ import {
99
} from '../utils/validation.utils.js';
1010
import { body } from 'express-validator';
1111
import FinanceController from '../controllers/finance.controllers.js';
12+
import multer, { memoryStorage } from 'multer';
13+
import { MAX_FILE_SIZE } from 'shared';
1214

1315
const financeRouter = express.Router();
16+
const upload = multer({ limits: { fileSize: MAX_FILE_SIZE }, storage: memoryStorage() });
1417

1518
financeRouter.post(
1619
'/sponsor/create',
20+
upload.single('logoImage'),
1721
nonEmptyString(body('name')),
1822
body('activeStatus').isBoolean(),
1923
body('valueTypes').isArray(),
@@ -142,6 +146,7 @@ financeRouter.get('/sponsorTiers', FinanceController.getAllSponsorTiers);
142146

143147
financeRouter.post(
144148
'/sponsor/:sponsorId/edit',
149+
upload.single('logoImage'),
145150
nonEmptyString(body('name')),
146151
body('activeStatus').isBoolean(),
147152
body('valueTypes').isArray(),

src/backend/src/routes/recruitment.routes.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,29 @@ recruitmentRouter.post(
5757
nonEmptyString(body('term')),
5858
nonEmptyString(body('description')),
5959
body('order').isInt(),
60+
nonEmptyString(body('type')),
6061
nonEmptyString(body('icon')).optional(),
6162
nonEmptyString(body('buttonText')).optional(),
6263
nonEmptyString(body('buttonLink')).optional(),
6364
validateInputs,
6465
RecruitmentController.createGuestDefinition
6566
);
6667

68+
recruitmentRouter.get('/guestdefinition/:definitionId', RecruitmentController.getSingleGuestDefinition);
69+
70+
recruitmentRouter.post(
71+
'/guestdefinition/:definitionId/edit',
72+
nonEmptyString(body('term')),
73+
nonEmptyString(body('description')),
74+
body('order').isInt(),
75+
nonEmptyString(body('type')),
76+
nonEmptyString(body('icon')).optional(),
77+
nonEmptyString(body('buttonText')).optional(),
78+
nonEmptyString(body('buttonLink')).optional(),
79+
validateInputs,
80+
RecruitmentController.editGuestDefinition
81+
);
82+
6783
recruitmentRouter.delete('/guestdefinition/:definitionId/delete', RecruitmentController.deleteGuestDefinition);
6884
recruitmentRouter.get('/guestdefinitions', RecruitmentController.getAllGuestDefintions);
6985

src/backend/src/services/finance.services.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
getReimbursementRequestWhereInput
3535
} from '../utils/finance.utils.js';
3636
import { notifySponsorTaskAssignee } from '../utils/slack.utils.js';
37+
import { uploadFile } from '../utils/google-integration.utils.js';
3738
import { isUserFinanceTeamOrHead } from '../utils/reimbursement-requests.utils.js';
3839

3940
export default class FinanceServices {
@@ -56,7 +57,7 @@ export default class FinanceServices {
5657
* @param contactPosition The position of the sponsor contact.
5758
* @param sponsorTasks An array of sponsor tasks associated with the sponsor.
5859
* @param organization The organization for which the sponsor is being created.
59-
*
60+
* @param logoImage An optional logo image file for the sponsor.
6061
* @returns The created sponsor object, including associated tasks.
6162
*
6263
* @throws AccessDeniedAdminOnlyException If the submitter does not have permission to create a sponsor.
@@ -80,7 +81,8 @@ export default class FinanceServices {
8081
contactPhone?: string,
8182
contactPosition?: string,
8283
stockDescription?: string,
83-
discountDescription?: string
84+
discountDescription?: string,
85+
logoImage?: Express.Multer.File
8486
) {
8587
if (!(await userHasPermission(submitter.userId, organization.organizationId, isHead)))
8688
throw new AccessDeniedException('Only heads can create a sponsor');
@@ -104,6 +106,8 @@ export default class FinanceServices {
104106
data: { name: contactName, email: contactEmail, phone: contactPhone, position: contactPosition }
105107
});
106108

109+
const { id: logoImageId } = logoImage ? await uploadFile(logoImage) : { id: undefined };
110+
107111
const sponsor = await prisma.sponsor.create({
108112
data: {
109113
name,
@@ -118,6 +122,7 @@ export default class FinanceServices {
118122
taxExempt,
119123
discountCode,
120124
sponsorNotes,
125+
logoImageId,
121126
contactId: contact.sponsorContactId,
122127
sponsorTasks: {
123128
create: sponsorTasks.map((task) => ({
@@ -1200,6 +1205,8 @@ export default class FinanceServices {
12001205
* @param contactPosition The position of the sponsor contact.
12011206
* @param sponsorTasks An array of sponsor tasks associated with the sponsor.
12021207
* @param organization The organization for which the sponsor is being edited.
1208+
* @param logoImage An optional logo image file for the sponsor.
1209+
*
12031210
* @returns the edited sponsor.
12041211
*/
12051212

@@ -1223,7 +1230,8 @@ export default class FinanceServices {
12231230
contactPhone?: string,
12241231
contactPosition?: string,
12251232
stockDescription?: string,
1226-
discountDescription?: string
1233+
discountDescription?: string,
1234+
logoImage?: Express.Multer.File
12271235
): Promise<Sponsor> {
12281236
if (!(await userHasPermission(submitter.userId, organization.organizationId, isHead)))
12291237
throw new AccessDeniedException('Only heads can edit sponsors.');
@@ -1321,6 +1329,8 @@ export default class FinanceServices {
13211329
data: { name: contactName, email: contactEmail, phone: contactPhone, position: contactPosition }
13221330
});
13231331

1332+
const { id: logoImageId } = logoImage ? await uploadFile(logoImage) : { id: undefined };
1333+
13241334
const updatedSponsor = await prisma.sponsor.update({
13251335
where: { sponsorId: oldSponsor.sponsorId },
13261336
data: {
@@ -1335,7 +1345,8 @@ export default class FinanceServices {
13351345
tier: sponsorTierId ? { connect: { sponsorTierId } } : { disconnect: true },
13361346
taxExempt,
13371347
discountCode,
1338-
sponsorNotes
1348+
sponsorNotes,
1349+
...(logoImageId && { logoImageId })
13391350
},
13401351
...getSponsorQueryArgs(organization.organizationId)
13411352
});

0 commit comments

Comments
 (0)