diff --git a/src/backend/src/controllers/users.controllers.ts b/src/backend/src/controllers/users.controllers.ts index bf965c8d8e..cc084c714e 100644 --- a/src/backend/src/controllers/users.controllers.ts +++ b/src/backend/src/controllers/users.controllers.ts @@ -216,4 +216,16 @@ export default class UsersController { next(error); } } + + static async getUserUnreadAnnouncements(req: Request, res: Response, next: NextFunction) { + try { + const { userId } = req.params; + const { organization } = req; + + const unreadAnnouncements = await UsersService.getUserUnreadAnnouncements(userId, organization); + res.status(200).json(unreadAnnouncements); + } catch (error: unknown) { + next(error); + } + } } diff --git a/src/backend/src/prisma-query-args/announcements.query.args.ts b/src/backend/src/prisma-query-args/announcements.query.args.ts new file mode 100644 index 0000000000..2f0e1ba294 --- /dev/null +++ b/src/backend/src/prisma-query-args/announcements.query.args.ts @@ -0,0 +1,12 @@ +import { Prisma } from '@prisma/client'; +import { getUserQueryArgs } from './user.query-args'; + +export type AnnouncementQueryArgs = ReturnType; + +export const getAnnouncementQueryArgs = (organizationId: string) => + Prisma.validator()({ + include: { + usersReceived: getUserQueryArgs(organizationId), + userCreated: getUserQueryArgs(organizationId) + } + }); diff --git a/src/backend/src/prisma/migrations/20241218031222_home_page_updates/migration.sql b/src/backend/src/prisma/migrations/20241218044032_homepage_updates/migration.sql similarity index 95% rename from src/backend/src/prisma/migrations/20241218031222_home_page_updates/migration.sql rename to src/backend/src/prisma/migrations/20241218044032_homepage_updates/migration.sql index c7975f2e21..f35a709d3f 100644 --- a/src/backend/src/prisma/migrations/20241218031222_home_page_updates/migration.sql +++ b/src/backend/src/prisma/migrations/20241218044032_homepage_updates/migration.sql @@ -8,8 +8,10 @@ ALTER TABLE "Project" ADD COLUMN "organizationId" TEXT; CREATE TABLE "Announcement" ( "announcementId" TEXT NOT NULL, "text" TEXT NOT NULL, - "dateCrated" TIMESTAMP(3) NOT NULL, + "dateCreated" TIMESTAMP(3) NOT NULL, "userCreatedId" TEXT NOT NULL, + "slackEventId" TEXT NOT NULL, + "slackChannelName" TEXT NOT NULL, CONSTRAINT "Announcement_pkey" PRIMARY KEY ("announcementId") ); diff --git a/src/backend/src/prisma/schema.prisma b/src/backend/src/prisma/schema.prisma index e0157c3353..f70d812b2a 100644 --- a/src/backend/src/prisma/schema.prisma +++ b/src/backend/src/prisma/schema.prisma @@ -180,7 +180,7 @@ model User { deletedFrequentlyAskedQuestions FrequentlyAskedQuestion[] @relation(name: "frequentlyAskedQuestionDeleter") createdMilestones Milestone[] @relation(name: "milestoneCreator") deletedMilestones Milestone[] @relation(name: "milestoneDeleter") - receivedAnnouncements Announcement[] @relation(name: "receivedAnnouncements") + unreadAnnouncements Announcement[] @relation(name: "receivedAnnouncements") createdAnnouncements Announcement[] @relation(name: "createdAnnouncements") unreadNotifications Notification[] @relation(name: "userNotifications") } @@ -932,12 +932,14 @@ model Milestone { } model Announcement { - announcementId String @id @default(uuid()) - text String - usersReceived User[] @relation("receivedAnnouncements") - dateCrated DateTime - userCreatedId String - userCreated User @relation("createdAnnouncements", fields: [userCreatedId], references: [userId]) + announcementId String @id @default(uuid()) + text String + usersReceived User[] @relation("receivedAnnouncements") + dateCreated DateTime + userCreatedId String + userCreated User @relation("createdAnnouncements", fields: [userCreatedId], references: [userId]) + slackEventId String + slackChannelName String } model Notification { diff --git a/src/backend/src/routes/users.routes.ts b/src/backend/src/routes/users.routes.ts index 622a6fb01c..802d586500 100644 --- a/src/backend/src/routes/users.routes.ts +++ b/src/backend/src/routes/users.routes.ts @@ -55,6 +55,7 @@ userRouter.post( UsersController.getManyUserTasks ); userRouter.get('/:userId/notifications', UsersController.getUserUnreadNotifications); +userRouter.get('/:userId/announcements', UsersController.getUserUnreadAnnouncements); userRouter.post( '/:userId/notifications/remove', nonEmptyString(body('notificationId')), diff --git a/src/backend/src/services/users.services.ts b/src/backend/src/services/users.services.ts index 1358ca5f6c..fff25ef56b 100644 --- a/src/backend/src/services/users.services.ts +++ b/src/backend/src/services/users.services.ts @@ -40,6 +40,8 @@ import { getTaskQueryArgs } from '../prisma-query-args/tasks.query-args'; import taskTransformer from '../transformers/tasks.transformer'; import { getNotificationQueryArgs } from '../prisma-query-args/notifications.query-args'; import notificationTransformer from '../transformers/notifications.transformer'; +import { getAnnouncementQueryArgs } from '../prisma-query-args/announcements.query.args'; +import announcementTransformer from '../transformers/announcements.transformer'; export default class UsersService { /** @@ -585,6 +587,22 @@ export default class UsersService { return requestedUser.unreadNotifications.map(notificationTransformer); } + /** + * Gets all of a user's unread announcements + * @param userId id of user to get unread announcements from + * @param organization the user's orgainzation + * @returns the unread announcements of the user + */ + static async getUserUnreadAnnouncements(userId: string, organization: Organization) { + const requestedUser = await prisma.user.findUnique({ + where: { userId }, + include: { unreadAnnouncements: getAnnouncementQueryArgs(organization.organizationId) } + }); + if (!requestedUser) throw new NotFoundException('User', userId); + + return requestedUser.unreadAnnouncements.map(announcementTransformer); + } + /** * Removes a notification from the user's unread notifications * @param userId id of the user to remove notification from diff --git a/src/backend/src/transformers/announcements.transformer.ts b/src/backend/src/transformers/announcements.transformer.ts new file mode 100644 index 0000000000..2a8f77e6b0 --- /dev/null +++ b/src/backend/src/transformers/announcements.transformer.ts @@ -0,0 +1,17 @@ +import { Prisma } from '@prisma/client'; +import { AnnouncementQueryArgs } from '../prisma-query-args/announcements.query.args'; +import { Announcement } from 'shared'; +import { userTransformer } from './user.transformer'; + +const announcementTransformer = (announcement: Prisma.AnnouncementGetPayload): Announcement => { + return { + announcementId: announcement.announcementId, + text: announcement.text, + dateCreated: announcement.dateCreated, + userCreated: userTransformer(announcement.userCreated), + slackEventId: announcement.slackEventId, + slackChannelName: announcement.slackChannelName + }; +}; + +export default announcementTransformer; diff --git a/src/shared/index.ts b/src/shared/index.ts index 409dae2e65..40246d0fe4 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -12,6 +12,7 @@ export * from './src/types/task-types'; export * from './src/types/reimbursement-requests-types'; export * from './src/types/design-review-types'; export * from './src/types/notifications.types'; +export * from './src/types/announcements.types'; export * from './src/validate-wbs'; export * from './src/date-utils'; diff --git a/src/shared/src/types/announcements.types.ts b/src/shared/src/types/announcements.types.ts new file mode 100644 index 0000000000..9315535c86 --- /dev/null +++ b/src/shared/src/types/announcements.types.ts @@ -0,0 +1,10 @@ +import { User } from './user-types'; + +export interface Announcement { + announcementId: string; + text: string; + userCreated: User; + dateCreated: Date; + slackEventId: string; + slackChannelName: string; +}