From 5eb31467b2f4290317f9322fd2e5ee29ab2e8aba Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Thu, 2 Apr 2026 17:43:55 -0400 Subject: [PATCH 01/21] #4090 moved slack notif field from event type to event --- src/backend/src/prisma/schema.prisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/src/prisma/schema.prisma b/src/backend/src/prisma/schema.prisma index 47adf8f79e..7111a5cc6c 100644 --- a/src/backend/src/prisma/schema.prisma +++ b/src/backend/src/prisma/schema.prisma @@ -1179,6 +1179,7 @@ model Event { description String? notificationSlackThreads Message_Info[] calendarEventIds String[] + sendSlackNotifications Boolean } model Calendar { @@ -1221,7 +1222,6 @@ model Event_Type { description Boolean onlyHeadsOrAboveForEventCreation Boolean requiresConfirmation Boolean - sendSlackNotifications Boolean events Event[] organizationId String organization Organization @relation(fields: [organizationId], references: [organizationId]) From 8c31acb15c9978bca4b31ab8b7f75a4a9f26bccd Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Thu, 2 Apr 2026 18:07:23 -0400 Subject: [PATCH 02/21] #4090 moved slack notifs field from event type to event in shared types --- src/shared/src/types/calendar-types.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/shared/src/types/calendar-types.ts b/src/shared/src/types/calendar-types.ts index de8c7d5229..df07646f8f 100644 --- a/src/shared/src/types/calendar-types.ts +++ b/src/shared/src/types/calendar-types.ts @@ -142,7 +142,6 @@ export interface EventType { description: boolean; onlyHeadsOrAboveForEventCreation: boolean; requiresConfirmation: boolean; - sendSlackNotifications: boolean; } export interface EventTypeCreateArgs { @@ -162,7 +161,6 @@ export interface EventTypeCreateArgs { description: boolean; onlyHeadsOrAbove: boolean; requiresConfirmation: boolean; - sendSlackNotifications: boolean; } export interface Shop { @@ -213,6 +211,7 @@ export interface Event { description?: string; status: EventStatus; initialDateScheduled?: Date; + sendSlackNotifications: boolean; } export type EventInstance = Omit & From dcdb053b7df7cba13d146200fca794f1d5ae8f30 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Sun, 5 Apr 2026 02:22:27 -0400 Subject: [PATCH 03/21] #4090 moved slack notifs field from event type to event in routes --- src/backend/src/routes/calendar.routes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/src/routes/calendar.routes.ts b/src/backend/src/routes/calendar.routes.ts index 6f7e60ce12..f2b4acd5e0 100644 --- a/src/backend/src/routes/calendar.routes.ts +++ b/src/backend/src/routes/calendar.routes.ts @@ -64,7 +64,6 @@ calendarRouter.post( body('description').isBoolean(), body('onlyHeadsOrAbove').isBoolean(), body('requiresConfirmation').isBoolean(), - body('sendSlackNotifications').isBoolean(), validateInputs, CalendarController.createEventType ); @@ -88,7 +87,6 @@ calendarRouter.post( body('description').isBoolean(), body('onlyHeadsOrAbove').isBoolean(), body('requiresConfirmation').isBoolean(), - body('sendSlackNotifications').isBoolean(), validateInputs, CalendarController.editEventType ); @@ -119,6 +117,7 @@ calendarRouter.post( isDate(body('scheduleSlots.*.startTime')), isDate(body('scheduleSlots.*.endTime')), body('scheduleSlots.*.allDay').isBoolean(), + body('sendSlackNotifications').isBoolean(), validateInputs, CalendarController.createEvent ); @@ -147,6 +146,7 @@ calendarRouter.post( nonEmptyString(body('documents.*.googleFileId')), body('questionDocumentLink').optional().isString(), body('description').optional().isString(), + body('sendSlackNotifications').isBoolean(), validateInputs, CalendarController.editEvent ); From b4f63626dcc611ec80b92b8cd7a8c09d3c9ea32e Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Sun, 5 Apr 2026 02:27:41 -0400 Subject: [PATCH 04/21] #4090 moved slack notifs field from event type to event controllers --- .../src/controllers/calendar.controllers.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/backend/src/controllers/calendar.controllers.ts b/src/backend/src/controllers/calendar.controllers.ts index 53377b9650..3c4396d7b7 100644 --- a/src/backend/src/controllers/calendar.controllers.ts +++ b/src/backend/src/controllers/calendar.controllers.ts @@ -21,8 +21,7 @@ export default class CalendarController { documents, description, onlyHeadsOrAbove, - requiresConfirmation, - sendSlackNotifications + requiresConfirmation } = req.body; const eventType = await CalendarService.createEventType( @@ -43,8 +42,7 @@ export default class CalendarController { documents, description, onlyHeadsOrAbove, - requiresConfirmation, - sendSlackNotifications + requiresConfirmation ); res.status(200).json(eventType); } catch (error: unknown) { @@ -205,8 +203,7 @@ export default class CalendarController { documents, description, onlyHeadsOrAbove, - requiresConfirmation, - sendSlackNotifications + requiresConfirmation } = req.body; const eventType = await CalendarService.editEventType( @@ -228,8 +225,7 @@ export default class CalendarController { documents, description, onlyHeadsOrAbove, - requiresConfirmation, - sendSlackNotifications + requiresConfirmation ); res.status(200).json(eventType); } catch (error: unknown) { @@ -278,7 +274,8 @@ export default class CalendarController { questionDocumentLink, location, zoomLink, - description + description, + sendSlackNotifications } = req.body; const parsedScheduleSlots = scheduleSlots.map((slot: any) => ({ @@ -306,7 +303,8 @@ export default class CalendarController { questionDocumentLink, location, zoomLink, - description + description, + sendSlackNotifications ); res.status(200).json(event); } catch (error: unknown) { @@ -332,7 +330,8 @@ export default class CalendarController { questionDocumentLink, location, zoomLink, - description + description, + sendSlackNotifications } = req.body; const event = await CalendarService.editEvent( @@ -352,7 +351,8 @@ export default class CalendarController { questionDocumentLink, location, zoomLink, - description + description, + sendSlackNotifications ); res.status(200).json(event); } catch (error: unknown) { From 7d6ec788bc170a92c04fd2bcd9f828916a81b082 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Sun, 5 Apr 2026 19:26:25 -0400 Subject: [PATCH 05/21] #4090 moved sendslacknotifs from event type to event services, and changed services so that they check event instead of eventtype for sendslacknotifs --- src/backend/src/services/calendar.services.ts | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/backend/src/services/calendar.services.ts b/src/backend/src/services/calendar.services.ts index 7aa1033af7..2efabebf7b 100644 --- a/src/backend/src/services/calendar.services.ts +++ b/src/backend/src/services/calendar.services.ts @@ -90,7 +90,6 @@ export default class CalendarService { * @param description Determines if a description is associated with this event type. * @param onlyHeadsOrAbove Determines if events under this event type can only be created by heads or above. * @param requiredConfirmation Determines if events under this event type need to be confirmed. - * @param sendSlackNotifications Determines if users will be notified via slack * * @returns The created event type. * @@ -116,8 +115,7 @@ export default class CalendarService { documents: boolean, description: boolean, onlyHeadsOrAbove: boolean, - requiresConfirmation: boolean, - sendSlackNotifications: boolean + requiresConfirmation: boolean ): Promise { if (!(await userHasPermission(submitter.userId, organization.organizationId, isAdmin))) { throw new AccessDeniedAdminOnlyException('create event type'); @@ -176,7 +174,6 @@ export default class CalendarService { description, onlyHeadsOrAboveForEventCreation: onlyHeadsOrAbove, requiresConfirmation, - sendSlackNotifications, organizationId: organization.organizationId }, ...getEventTypeQueryArgs(organization.organizationId) @@ -246,6 +243,7 @@ export default class CalendarService { * @param location Location of the event. * @param zoomLink Zoom Link if the event is online. * @param description Describes the event. + * @param sendSlackNotifications Determines if this event should receive slack notifications. * * @returns The created event. * @@ -265,6 +263,7 @@ export default class CalendarService { workPackageIds: string[], scheduleSlots: ScheduleSlotCreateArgs[], initialDateScheduled: Date | undefined, + sendSlackNotifications: boolean, teamTypeId?: string, questionDocumentLink?: string, location?: string, @@ -306,7 +305,8 @@ export default class CalendarService { location, zoomLink, questionDocumentLink, - description + description, + sendSlackNotifications }); // Validate required memberIds @@ -460,7 +460,8 @@ export default class CalendarService { location, zoomLink, questionDocumentLink, - description + description, + sendSlackNotifications }, ...getEventQueryArgs(organization.organizationId) }); @@ -493,7 +494,7 @@ export default class CalendarService { } } - if (foundEventType.sendSlackNotifications) { + if (sendSlackNotifications) { const members = await prisma.user.findMany({ where: { userId: { in: optionalMemberIds.concat(requiredMemberIds) } } }); @@ -567,6 +568,7 @@ export default class CalendarService { * @param location Location of the event. * @param zoomLink Zoom Link if the event is online. * @param description Describes the event. + * @param sendSlackNotifications Determines if this event should receive slack notifications. * * @returns The edited event. * @@ -586,6 +588,7 @@ export default class CalendarService { machineryIds: string[], workPackageIds: string[], documents: EventDocumentCreateArgs[], + sendSlackNotifications: boolean, teamTypeId?: string, questionDocumentLink?: string, location?: string, @@ -773,7 +776,8 @@ export default class CalendarService { location, zoomLink, questionDocumentLink, - description + description, + sendSlackNotifications }, ...getEventQueryArgs(organization.organizationId) }); @@ -783,11 +787,11 @@ export default class CalendarService { const edittedEvent = eventTransformer(updatedEvent); - if (status === Event_Status.SCHEDULED && foundEventType.sendSlackNotifications) { + if (status === Event_Status.SCHEDULED && sendSlackNotifications) { await sendEventScheduledSlackNotif(updatedEvent.notificationSlackThreads, edittedEvent); } - if (status === Event_Status.CONFIRMED && foundEventType.sendSlackNotifications) { + if (status === Event_Status.CONFIRMED && sendSlackNotifications) { await sendEventConfirmationToThread(updatedEvent.notificationSlackThreads, updatedEvent.userCreated); } @@ -1350,7 +1354,7 @@ export default class CalendarService { if (!foundEventType) throw new NotFoundException('Event Type', eventTypeId); if (foundEventType.dateDeleted) throw new DeletedException('Event Type', eventTypeId); - if (foundEventType.sendSlackNotifications) { + if (updatedEvent.sendSlackNotifications) { await sendEventUserConfirmationToThread(updatedEvent.notificationSlackThreads, submitter); } @@ -1366,7 +1370,7 @@ export default class CalendarService { status: Event_Status.CONFIRMED } }); - if (foundEventType.sendSlackNotifications) { + if (updatedEvent.sendSlackNotifications) { await sendEventConfirmationToThread(updatedEvent.notificationSlackThreads, updatedEvent.userCreated); } } @@ -1476,7 +1480,7 @@ export default class CalendarService { where: { eventTypeId } }); - if (foundEventType?.sendSlackNotifications) { + if (updatedEvent.sendSlackNotifications) { await sendEventScheduledSlackNotif(updatedEvent.notificationSlackThreads, eventTransformer(updatedEvent)); } @@ -2209,7 +2213,6 @@ export default class CalendarService { * @param description Determines if a description is associated with this event type. * @param onlyHeadsOrAbove Determines if events associated with this event type can only be made by heads or above. * @param requiredConfirmation Determines if events associated with this event type need to be confirmed. - * @param sendSlackNotifications Determines if events associated with this event type should receive slack notifications. * * @returns The created event type. * @@ -2236,8 +2239,7 @@ export default class CalendarService { documents: boolean, description: boolean, onlyHeadsOrAbove: boolean, - requiresConfirmation: boolean, - sendSlackNotifications: boolean + requiresConfirmation: boolean ): Promise { if (!(await userHasPermission(submitter.userId, organization.organizationId, isAdmin))) { throw new AccessDeniedAdminOnlyException('edit event type'); @@ -2295,8 +2297,7 @@ export default class CalendarService { documents, description, onlyHeadsOrAboveForEventCreation: onlyHeadsOrAbove, - requiresConfirmation, - sendSlackNotifications + requiresConfirmation }, ...getEventTypeQueryArgs(organization.organizationId) }); From 6f16f8469915c79df8f2f093fcffa0f79077acf4 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Sun, 5 Apr 2026 19:29:31 -0400 Subject: [PATCH 06/21] #4090 moved sendslacknotifs from event type to event transformers --- src/backend/src/transformers/calendar.transformer.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/src/transformers/calendar.transformer.ts b/src/backend/src/transformers/calendar.transformer.ts index d1317748a8..2b74501c8b 100644 --- a/src/backend/src/transformers/calendar.transformer.ts +++ b/src/backend/src/transformers/calendar.transformer.ts @@ -83,8 +83,7 @@ export const eventTypeTransformer = (eventType: Prisma.Event_TypeGetPayload): questionDocumentLink: event.questionDocumentLink ?? undefined, description: event.description ?? undefined, status: eventStatusTransformer(event.status), - initialDateScheduled: event.initialDateScheduled ?? undefined + initialDateScheduled: event.initialDateScheduled ?? undefined, + sendSlackNotifications: event.sendSlackNotifications }; }; @@ -178,7 +178,8 @@ export const eventWithMembersTransformer = (event: Prisma.EventGetPayload Date: Sun, 5 Apr 2026 20:52:33 -0400 Subject: [PATCH 07/21] #4090 send slack notifs service filters using event instead of eventtype --- src/backend/src/services/notifications.services.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/backend/src/services/notifications.services.ts b/src/backend/src/services/notifications.services.ts index 9cbd1d6d3c..8046b7e763 100644 --- a/src/backend/src/services/notifications.services.ts +++ b/src/backend/src/services/notifications.services.ts @@ -121,7 +121,7 @@ export default class NotificationsService { } /** - * Sends Slack notifications for all events scheduled for today whose event type has sendSlackNotifications enabled + * Sends Slack notifications for all events scheduled for today that have sendSlackNotifications enabled */ static async sendEventSlackNotifications() { const endOfToday = startOfTomorrowEST(); @@ -136,9 +136,7 @@ export default class NotificationsService { AND: [{ endTime: { gte: startOfToday } }, { startTime: { lte: endOfToday } }] } }, - eventType: { - sendSlackNotifications: true - } + sendSlackNotifications: true }, include: { requiredMembers: { include: { userSettings: true } }, From ce55512f22932466f14a5fc2cfb0ae7f23a5cb41 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Sun, 5 Apr 2026 20:53:13 -0400 Subject: [PATCH 08/21] #4090 fixed schema formatting --- src/backend/src/prisma/schema.prisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/src/prisma/schema.prisma b/src/backend/src/prisma/schema.prisma index 7111a5cc6c..b90850556b 100644 --- a/src/backend/src/prisma/schema.prisma +++ b/src/backend/src/prisma/schema.prisma @@ -1179,7 +1179,7 @@ model Event { description String? notificationSlackThreads Message_Info[] calendarEventIds String[] - sendSlackNotifications Boolean + sendSlackNotifications Boolean } model Calendar { From 776a4be22d89b5fcb6ab261e0d0111f73c280a90 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Sun, 5 Apr 2026 21:18:40 -0400 Subject: [PATCH 09/21] #4090 made migration for moving sendslacknotifs field --- .../migration.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/backend/src/prisma/migrations/20260406005526_move_send_slack_notifs_from_event_type_to_event/migration.sql diff --git a/src/backend/src/prisma/migrations/20260406005526_move_send_slack_notifs_from_event_type_to_event/migration.sql b/src/backend/src/prisma/migrations/20260406005526_move_send_slack_notifs_from_event_type_to_event/migration.sql new file mode 100644 index 0000000000..a1bf6dfd68 --- /dev/null +++ b/src/backend/src/prisma/migrations/20260406005526_move_send_slack_notifs_from_event_type_to_event/migration.sql @@ -0,0 +1,12 @@ +/* + Warnings: + + - You are about to drop the column `sendSlackNotifications` on the `Event_Type` table. All the data in the column will be lost. + - Added the required column `sendSlackNotifications` to the `Event` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Event" ADD COLUMN "sendSlackNotifications" BOOLEAN NOT NULL; + +-- AlterTable +ALTER TABLE "Event_Type" DROP COLUMN "sendSlackNotifications"; From 24126c220a333c504f8a3c9d74f7b0c2fd6f347f Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Sun, 5 Apr 2026 21:59:53 -0400 Subject: [PATCH 10/21] #4090 modified tests to match new sendslacknotifs field --- src/backend/tests/unit/calendar.test.ts | 57 +++++++++++++++++-------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/src/backend/tests/unit/calendar.test.ts b/src/backend/tests/unit/calendar.test.ts index 50a6b39623..83d7a9e867 100644 --- a/src/backend/tests/unit/calendar.test.ts +++ b/src/backend/tests/unit/calendar.test.ts @@ -79,8 +79,7 @@ describe('Calendar Tests', () => { true, // documents true, // description true, // onlyHeadsOrAbove - false, // requiresConfirmation - changed to false so tests don't need initialDateScheduled - true // sendSlackNotifications + false // requiresConfirmation - changed to false so tests don't need initialDateScheduled ); }); @@ -263,7 +262,6 @@ describe('Calendar Tests', () => { true, false, true, - false, false ) ).rejects.toThrow(new AccessDeniedAdminOnlyException('create event type')); @@ -288,7 +286,6 @@ describe('Calendar Tests', () => { false, false, true, - false, false ); @@ -307,7 +304,6 @@ describe('Calendar Tests', () => { expect(result.description).toBe(false); expect(result.onlyHeadsOrAboveForEventCreation).toBe(true); expect(result.requiresConfirmation).toBe(false); - expect(result.sendSlackNotifications).toBe(false); }); }); @@ -653,8 +649,7 @@ describe('Calendar Tests', () => { false, false, true, - true, - false + true ); }); @@ -680,7 +675,6 @@ describe('Calendar Tests', () => { false, false, false, - false, false ) ).rejects.toThrow(new AccessDeniedAdminOnlyException('edit event type')); @@ -708,7 +702,6 @@ describe('Calendar Tests', () => { true, true, true, - true, true ) ).rejects.toThrow(new NotFoundException('Calendar', invalidCalendarId)); @@ -754,7 +747,6 @@ describe('Calendar Tests', () => { false, false, false, - false, false ) ).rejects.toThrow(new InvalidOrganizationException('Calendar')); @@ -782,8 +774,7 @@ describe('Calendar Tests', () => { false, false, false, - true, - false + true ) ).rejects.toThrow(new NotFoundException('Event Type', nonExistentId)); }); @@ -808,7 +799,6 @@ describe('Calendar Tests', () => { true, false, false, - false, false ); @@ -828,7 +818,6 @@ describe('Calendar Tests', () => { expect(result.description).toBe(false); expect(result.onlyHeadsOrAboveForEventCreation).toBe(false); expect(result.requiresConfirmation).toBe(false); - expect(result.sendSlackNotifications).toBe(false); }); }); @@ -891,7 +880,8 @@ describe('Calendar Tests', () => { [machinery.machineryId], [], scheduleSlots, - undefined, // initialDateScheduled + undefined, // initialDateScheduled, + true, // sendSlackNotification undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -911,6 +901,7 @@ describe('Calendar Tests', () => { expect(result.machinery[0].machineryId).toBe(machinery.machineryId); expect(result.workPackages).toHaveLength(0); expect(result.scheduledTimes).toHaveLength(1); + expect(result.sendSlackNotifications).toBe(true); expect(result.teamType).toBe(undefined); expect(result.approved).toBe(Conflict_Status.NO_CONFLICT); expect(result.approvalRequiredFrom).toBe(undefined); @@ -943,6 +934,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined // teamTypeId ) ).rejects.toThrow(new NotFoundException('Event Type', 'non-existent-event-type-id')); @@ -971,6 +963,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined // teamTypeId ) ).rejects.toThrow(new InvalidOrganizationException('Event Type')); @@ -998,6 +991,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1015,6 +1009,7 @@ describe('Calendar Tests', () => { expect(result.machinery).toHaveLength(1); expect(result.workPackages).toHaveLength(0); expect(result.scheduledTimes).toHaveLength(1); + expect(result.sendSlackNotifications).toBe(true); expect(result.teamType).toBe(undefined); expect(result.approved).toBe(Conflict_Status.NO_CONFLICT); expect(result.approvalRequiredFrom).toBeUndefined(); @@ -1047,6 +1042,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1079,6 +1075,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1111,6 +1108,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1143,6 +1141,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1175,6 +1174,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1213,6 +1213,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1258,6 +1259,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1293,6 +1295,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1313,6 +1316,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1356,6 +1360,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1376,6 +1381,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1405,6 +1411,7 @@ describe('Calendar Tests', () => { [], scheduleSlots2, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1449,6 +1456,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1469,6 +1477,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1498,6 +1507,7 @@ describe('Calendar Tests', () => { [], scheduleSlots2, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1679,6 +1689,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotification undefined, // teamTypeId 'https://example.com/questions.pdf', 'Conference Room A', @@ -1701,7 +1712,8 @@ describe('Calendar Tests', () => { [], [], [], - [] + [], + true ) ).rejects.toThrow(new NotFoundException('Event', 'non-existent-id')); }); @@ -1725,7 +1737,7 @@ describe('Calendar Tests', () => { [], [], [], - [] + true ) ).rejects.toThrow(new DeletedException('Event', event.eventId)); }); @@ -1745,6 +1757,7 @@ describe('Calendar Tests', () => { [machinery.machineryId], [], [], + true, undefined, 'https://example.com/questions.pdf', 'Conference Room A', @@ -1769,6 +1782,7 @@ describe('Calendar Tests', () => { [machinery.machineryId], [], [], + true, undefined, 'https://example.com/questions.pdf', 'Conference Room A', @@ -1799,6 +1813,7 @@ describe('Calendar Tests', () => { [machinery.machineryId], [], [], + true, undefined, 'https://example.com/questions.pdf', 'Conference Room A', @@ -1823,6 +1838,7 @@ describe('Calendar Tests', () => { ['non-existent-machinery-id'], [], [], + true, undefined, 'https://example.com/questions.pdf', 'Conference Room A', @@ -1853,6 +1869,7 @@ describe('Calendar Tests', () => { [deletedMachinery.machineryId], [], [], + true, undefined, 'https://example.com/questions.pdf', 'Conference Room A', @@ -1878,6 +1895,7 @@ describe('Calendar Tests', () => { [machinery.machineryId], [], [], + true, undefined, 'https://updated.com/questions.pdf', 'Updated Location', @@ -1928,6 +1946,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, // initialDateScheduled + true, // sendSlackNotifications undefined, // teamTypeId 'https://updated.com/questions.pdf', 'Updated Location', @@ -2110,6 +2129,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, + true, // sendSlackNotifications undefined, 'https://example.com/questions.pdf', 'Conference Room A', @@ -2162,6 +2182,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, + true, // sendSlackNotifications undefined, 'https://example.com/questions.pdf', 'Conference Room A', @@ -2368,6 +2389,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, + true, // sendSlackNotifications undefined, 'https://example.com/questions.pdf', 'Conference Room A', @@ -2486,6 +2508,7 @@ describe('Calendar Tests', () => { [], scheduleSlots, undefined, + true, // sendSlackNotifications undefined, 'https://example.com/questions.pdf', 'Conference Room A', From 83d2250e8dd98589e410bfa5c4207cedaf6993f4 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Mon, 13 Apr 2026 22:18:20 -0400 Subject: [PATCH 11/21] #4090 moved sendSlackNotifs to correct position in editEvent and createEvent service calls --- src/backend/src/controllers/calendar.controllers.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/src/controllers/calendar.controllers.ts b/src/backend/src/controllers/calendar.controllers.ts index 3c4396d7b7..05e50db54a 100644 --- a/src/backend/src/controllers/calendar.controllers.ts +++ b/src/backend/src/controllers/calendar.controllers.ts @@ -299,12 +299,12 @@ export default class CalendarController { workPackageIds, parsedScheduleSlots, parsedInitialDateScheduled, + sendSlackNotifications, teamTypeId, questionDocumentLink, location, zoomLink, description, - sendSlackNotifications ); res.status(200).json(event); } catch (error: unknown) { @@ -347,12 +347,12 @@ export default class CalendarController { machineryIds, workPackageIds, documents, + sendSlackNotifications, teamTypeId, questionDocumentLink, location, zoomLink, - description, - sendSlackNotifications + description ); res.status(200).json(event); } catch (error: unknown) { From ae3726c8ec124980686a2e3eb8a2fafa1b4d3399 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Mon, 13 Apr 2026 22:20:17 -0400 Subject: [PATCH 12/21] #4090 removed unnecessary sendSlackNotifs parameter in validation call --- src/backend/src/services/calendar.services.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/backend/src/services/calendar.services.ts b/src/backend/src/services/calendar.services.ts index 2efabebf7b..cabcab7d7f 100644 --- a/src/backend/src/services/calendar.services.ts +++ b/src/backend/src/services/calendar.services.ts @@ -305,8 +305,7 @@ export default class CalendarService { location, zoomLink, questionDocumentLink, - description, - sendSlackNotifications + description }); // Validate required memberIds From b955b7cce4a4fd978f2d9c7a0fa587e91dd7c3f8 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Mon, 13 Apr 2026 22:58:40 -0400 Subject: [PATCH 13/21] #4090 removed foundEventType from scheduleEvent as it is not necessary anymore (it directly checks event for sendSlackNotifs) --- src/backend/src/services/calendar.services.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/backend/src/services/calendar.services.ts b/src/backend/src/services/calendar.services.ts index cabcab7d7f..cf3d3c8b5b 100644 --- a/src/backend/src/services/calendar.services.ts +++ b/src/backend/src/services/calendar.services.ts @@ -1474,11 +1474,6 @@ export default class CalendarService { }); } - const { eventTypeId } = updatedEvent; - const foundEventType = await prisma.event_Type.findUnique({ - where: { eventTypeId } - }); - if (updatedEvent.sendSlackNotifications) { await sendEventScheduledSlackNotif(updatedEvent.notificationSlackThreads, eventTransformer(updatedEvent)); } From 517c5a72d41ad7c0577c2f7d47b9325e942551b7 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Mon, 13 Apr 2026 23:25:04 -0400 Subject: [PATCH 14/21] #4090 modified seeded events/eventtypes so that sendslacknotifs is in the correct places --- src/backend/src/prisma/seed.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/backend/src/prisma/seed.ts b/src/backend/src/prisma/seed.ts index 8161ea418e..4992d7ad4e 100644 --- a/src/backend/src/prisma/seed.ts +++ b/src/backend/src/prisma/seed.ts @@ -3576,7 +3576,6 @@ const performSeed: () => Promise = async () => { false, true, false, - false, false ); @@ -3599,7 +3598,6 @@ const performSeed: () => Promise = async () => { true, true, false, - true, true ); @@ -3622,7 +3620,6 @@ const performSeed: () => Promise = async () => { true, false, false, - false, false ); @@ -3645,7 +3642,6 @@ const performSeed: () => Promise = async () => { false, false, false, - false, false ); @@ -3668,6 +3664,7 @@ const performSeed: () => Promise = async () => { } ], undefined, + true, mechanical.teamTypeId, undefined, 'Conference Room A', @@ -3709,6 +3706,7 @@ const performSeed: () => Promise = async () => { } ], undefined, + false, mechanical.teamTypeId, undefined, 'Conference Room A', @@ -3735,6 +3733,7 @@ const performSeed: () => Promise = async () => { } ], undefined, + false, mechanical.teamTypeId, undefined, 'Conference Room A', @@ -3761,6 +3760,7 @@ const performSeed: () => Promise = async () => { } ], undefined, + true, mechanical.teamTypeId, undefined, 'Conference Room A', @@ -3787,6 +3787,7 @@ const performSeed: () => Promise = async () => { } ], undefined, + true, mechanical.teamTypeId, undefined, 'Conference Room A', @@ -3813,6 +3814,7 @@ const performSeed: () => Promise = async () => { } ], undefined, + false, mechanical.teamTypeId, undefined, 'Conference Room A', @@ -3833,6 +3835,7 @@ const performSeed: () => Promise = async () => { [workPackage1.id], [], weeksFromNow(1), + true, software.teamTypeId, 'https://docs.google.com/document/d/2_example', 'Conference Room B', @@ -3859,6 +3862,7 @@ const performSeed: () => Promise = async () => { } ], undefined, + true, electrical.teamTypeId, 'https://docs.google.com/document/d/3_example', undefined, @@ -3885,6 +3889,7 @@ const performSeed: () => Promise = async () => { } ], undefined, + false, mechanical.teamTypeId, undefined, undefined, From a48d26634cf61d6d17fb794785bae8a12e1c4949 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Mon, 13 Apr 2026 23:32:17 -0400 Subject: [PATCH 15/21] #4090 fixed tests involving create/edit events and event types --- src/backend/tests/test-utils.ts | 1 + src/backend/tests/unit/calendar.test.ts | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backend/tests/test-utils.ts b/src/backend/tests/test-utils.ts index 0ae348f923..514b04b4a1 100644 --- a/src/backend/tests/test-utils.ts +++ b/src/backend/tests/test-utils.ts @@ -622,6 +622,7 @@ export const createTestDesignReviewEvent = async () => { [testWorkPackage.workPackageId], // workPackageIds [], // scheduleSlots - empty for confirmation events new Date('2027-03-25T10:00:00'), // initialDateScheduled - required for requiresConfirmation events + false, // sendSlackNotifications teamType.teamTypeId, // team type id 'https://docs.google.com/document/d/test-design-review-questions', // questionDocument 'Campus Center Room 101', // location diff --git a/src/backend/tests/unit/calendar.test.ts b/src/backend/tests/unit/calendar.test.ts index 83d7a9e867..18a01bcb49 100644 --- a/src/backend/tests/unit/calendar.test.ts +++ b/src/backend/tests/unit/calendar.test.ts @@ -1737,6 +1737,7 @@ describe('Calendar Tests', () => { [], [], [], + [], true ) ).rejects.toThrow(new DeletedException('Event', event.eventId)); @@ -2014,8 +2015,7 @@ describe('Calendar Tests', () => { false, false, false, - true, - false + true ); }); @@ -2072,8 +2072,7 @@ describe('Calendar Tests', () => { false, false, false, - true, - false + true ); await expect(CalendarService.deleteEventType(adminUser, otherOrgEventType.eventTypeId, organization)).rejects.toThrow( From 38cac06ec2fec4619f0df61bfb14458cd5fee79b Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Mon, 13 Apr 2026 23:33:05 -0400 Subject: [PATCH 16/21] #4090 fixed prettier formatting --- src/backend/src/controllers/calendar.controllers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/src/controllers/calendar.controllers.ts b/src/backend/src/controllers/calendar.controllers.ts index 05e50db54a..c744809f5a 100644 --- a/src/backend/src/controllers/calendar.controllers.ts +++ b/src/backend/src/controllers/calendar.controllers.ts @@ -304,7 +304,7 @@ export default class CalendarController { questionDocumentLink, location, zoomLink, - description, + description ); res.status(200).json(event); } catch (error: unknown) { From b156d651842e32bd7171b4430559cf58233d0f39 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Mon, 13 Apr 2026 23:47:12 -0400 Subject: [PATCH 17/21] #4090 added sendslacknotifs to event with members shared type --- src/shared/src/types/calendar-types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shared/src/types/calendar-types.ts b/src/shared/src/types/calendar-types.ts index df07646f8f..1512a787d9 100644 --- a/src/shared/src/types/calendar-types.ts +++ b/src/shared/src/types/calendar-types.ts @@ -254,6 +254,7 @@ export interface EventWithMembers { description?: string; status: EventStatus; initialDateScheduled?: Date; + sendSlackNotifications: boolean; } export interface TeamType { From 8c645f71bbc664eb534aa16ba2237c6d62380050 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Tue, 14 Apr 2026 00:43:47 -0400 Subject: [PATCH 18/21] #4090 fixed createEventType call (it had a schedule parameter for some reason) --- src/backend/tests/test-utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend/tests/test-utils.ts b/src/backend/tests/test-utils.ts index 514b04b4a1..cf5b7ac437 100644 --- a/src/backend/tests/test-utils.ts +++ b/src/backend/tests/test-utils.ts @@ -577,7 +577,6 @@ export const createTestDesignReviewEvent = async () => { 'Design Review', [], // No calendar IDs for now organization, - true, // schedule true, // requiredMembers true, // optionalMembers false, // teams From b708ab68c5890637577219d6529b92f6e1de551d Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Thu, 16 Apr 2026 19:30:29 -0400 Subject: [PATCH 19/21] #4090 sendslacknotifs has default value of false --- .../migration.sql | 12 ------------ .../migration.sql | 5 +++++ src/backend/src/prisma/schema.prisma | 2 +- 3 files changed, 6 insertions(+), 13 deletions(-) delete mode 100644 src/backend/src/prisma/migrations/20260406005526_move_send_slack_notifs_from_event_type_to_event/migration.sql create mode 100644 src/backend/src/prisma/migrations/20260416232819_move_send_slack_notifs_to_event/migration.sql diff --git a/src/backend/src/prisma/migrations/20260406005526_move_send_slack_notifs_from_event_type_to_event/migration.sql b/src/backend/src/prisma/migrations/20260406005526_move_send_slack_notifs_from_event_type_to_event/migration.sql deleted file mode 100644 index a1bf6dfd68..0000000000 --- a/src/backend/src/prisma/migrations/20260406005526_move_send_slack_notifs_from_event_type_to_event/migration.sql +++ /dev/null @@ -1,12 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `sendSlackNotifications` on the `Event_Type` table. All the data in the column will be lost. - - Added the required column `sendSlackNotifications` to the `Event` table without a default value. This is not possible if the table is not empty. - -*/ --- AlterTable -ALTER TABLE "Event" ADD COLUMN "sendSlackNotifications" BOOLEAN NOT NULL; - --- AlterTable -ALTER TABLE "Event_Type" DROP COLUMN "sendSlackNotifications"; diff --git a/src/backend/src/prisma/migrations/20260416232819_move_send_slack_notifs_to_event/migration.sql b/src/backend/src/prisma/migrations/20260416232819_move_send_slack_notifs_to_event/migration.sql new file mode 100644 index 0000000000..d93e251d95 --- /dev/null +++ b/src/backend/src/prisma/migrations/20260416232819_move_send_slack_notifs_to_event/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "Event" ADD COLUMN "sendSlackNotifications" BOOLEAN NOT NULL DEFAULT false; + +-- AlterTable +ALTER TABLE "Event_Type" DROP COLUMN "sendSlackNotifications"; diff --git a/src/backend/src/prisma/schema.prisma b/src/backend/src/prisma/schema.prisma index b90850556b..9e8ab62ee5 100644 --- a/src/backend/src/prisma/schema.prisma +++ b/src/backend/src/prisma/schema.prisma @@ -1179,7 +1179,7 @@ model Event { description String? notificationSlackThreads Message_Info[] calendarEventIds String[] - sendSlackNotifications Boolean + sendSlackNotifications Boolean @default(false) } model Calendar { From 58d48779bdfca34434f11f633c311dd0d5d6052c Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Fri, 17 Apr 2026 01:23:14 -0400 Subject: [PATCH 20/21] #4090 schema formatting fixed --- src/backend/src/prisma/schema.prisma | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/src/prisma/schema.prisma b/src/backend/src/prisma/schema.prisma index 9e8ab62ee5..c4a7fac098 100644 --- a/src/backend/src/prisma/schema.prisma +++ b/src/backend/src/prisma/schema.prisma @@ -1179,7 +1179,7 @@ model Event { description String? notificationSlackThreads Message_Info[] calendarEventIds String[] - sendSlackNotifications Boolean @default(false) + sendSlackNotifications Boolean @default(false) } model Calendar { From 036865fbc4da419f1f15352c9c7f91f66558e070 Mon Sep 17 00:00:00 2001 From: Tony Feng Date: Sat, 18 Apr 2026 20:00:57 -0400 Subject: [PATCH 21/21] #4090 changed migration to backfill sendslacknotifs for events based on eventtypes --- .../migration.sql | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/backend/src/prisma/migrations/20260416232819_move_send_slack_notifs_to_event/migration.sql b/src/backend/src/prisma/migrations/20260416232819_move_send_slack_notifs_to_event/migration.sql index d93e251d95..c66ef90189 100644 --- a/src/backend/src/prisma/migrations/20260416232819_move_send_slack_notifs_to_event/migration.sql +++ b/src/backend/src/prisma/migrations/20260416232819_move_send_slack_notifs_to_event/migration.sql @@ -1,5 +1,12 @@ -- AlterTable ALTER TABLE "Event" ADD COLUMN "sendSlackNotifications" BOOLEAN NOT NULL DEFAULT false; +-- Backfill Event.sendSlackNotifications from Event_Type before dropping the old column +UPDATE "Event" e +SET "sendSlackNotifications" = true +FROM "Event_Type" et +WHERE e."eventTypeId" = et."eventTypeId" +AND et."sendSlackNotifications" = true; + -- AlterTable ALTER TABLE "Event_Type" DROP COLUMN "sendSlackNotifications";