From 329b60fff17de2f8be779868259b50f0ca8364ec Mon Sep 17 00:00:00 2001 From: caiodasilva2005 Date: Sun, 22 Dec 2024 10:57:40 -0500 Subject: [PATCH] #3074-refactored notifications into pop-ups --- src/backend/index.ts | 2 + .../controllers/notifications.controllers.ts | 30 ------ .../src/controllers/popUps.controllers.ts | 27 +++++ .../notifications.query-args.ts | 11 -- .../prisma-query-args/pop-up.query-args.ts | 11 ++ .../migration.sql | 18 ++-- src/backend/src/prisma/schema.prisma | 16 +-- src/backend/src/prisma/seed.ts | 1 + .../src/routes/notifications.routes.ts | 2 - src/backend/src/routes/pop-up.routes.ts | 9 ++ .../src/services/announcement.service.ts | 2 + .../src/services/change-requests.services.ts | 6 +- .../src/services/design-reviews.services.ts | 4 +- .../src/services/notifications.services.ts | 98 +---------------- src/backend/src/services/pop-up.services.ts | 100 +++++++++++++++++ .../transformers/notifications.transformer.ts | 14 --- .../src/transformers/pop-up.transformer.ts | 12 +++ src/backend/src/utils/auth.utils.ts | 2 +- src/backend/src/utils/notifications.utils.ts | 75 +------------ src/backend/src/utils/pop-up.utils.ts | 69 ++++++++++++ .../tests/unmocked/announcements.test.ts | 2 + .../tests/unmocked/notifications.test.ts | 102 ------------------ src/backend/tests/unmocked/pop-up.test.ts | 90 ++++++++++++++++ src/frontend/src/apis/notifications.api.ts | 19 ---- src/frontend/src/apis/pop-ups.api.ts | 19 ++++ .../src/components/NotificationAlert.tsx | 53 --------- .../src/components/NotificationCard.tsx | 24 ++--- src/frontend/src/components/PopUpAlert.tsx | 49 +++++++++ src/frontend/src/hooks/notifications.hooks.ts | 36 ------- src/frontend/src/hooks/pop-ups.hooks.ts | 36 +++++++ src/frontend/src/pages/HomePage/Home.tsx | 4 +- src/frontend/src/utils/urls.ts | 14 +-- src/shared/index.ts | 2 +- src/shared/src/types/announcements.types.ts | 2 +- ...notifications.types.ts => pop-up-types.ts} | 4 +- 35 files changed, 479 insertions(+), 486 deletions(-) create mode 100644 src/backend/src/controllers/popUps.controllers.ts delete mode 100644 src/backend/src/prisma-query-args/notifications.query-args.ts create mode 100644 src/backend/src/prisma-query-args/pop-up.query-args.ts rename src/backend/src/prisma/migrations/{20241222123937_homepage_redesign => 20241222150147_homepage_redesign}/migration.sql (70%) create mode 100644 src/backend/src/routes/pop-up.routes.ts create mode 100644 src/backend/src/services/pop-up.services.ts delete mode 100644 src/backend/src/transformers/notifications.transformer.ts create mode 100644 src/backend/src/transformers/pop-up.transformer.ts create mode 100644 src/backend/src/utils/pop-up.utils.ts delete mode 100644 src/backend/tests/unmocked/notifications.test.ts create mode 100644 src/backend/tests/unmocked/pop-up.test.ts delete mode 100644 src/frontend/src/apis/notifications.api.ts create mode 100644 src/frontend/src/apis/pop-ups.api.ts delete mode 100644 src/frontend/src/components/NotificationAlert.tsx create mode 100644 src/frontend/src/components/PopUpAlert.tsx delete mode 100644 src/frontend/src/hooks/notifications.hooks.ts create mode 100644 src/frontend/src/hooks/pop-ups.hooks.ts rename src/shared/src/types/{notifications.types.ts => pop-up-types.ts} (50%) diff --git a/src/backend/index.ts b/src/backend/index.ts index 8a505bff76..86470eb0a0 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -19,6 +19,7 @@ import organizationRouter from './src/routes/organizations.routes'; import recruitmentRouter from './src/routes/recruitment.routes'; import announcementsRouter from './src/routes/announcements.routes'; import onboardingRouter from './src/routes/onboarding.routes'; +import popUpsRouter from './src/routes/pop-up.routes'; const app = express(); @@ -70,6 +71,7 @@ app.use('/templates', workPackageTemplatesRouter); app.use('/cars', carsRouter); app.use('/organizations', organizationRouter); app.use('/recruitment', recruitmentRouter); +app.use('/pop-ups', popUpsRouter); app.use('/announcements', announcementsRouter); app.use('/onboarding', onboardingRouter); app.use('/', (_req, res) => { diff --git a/src/backend/src/controllers/notifications.controllers.ts b/src/backend/src/controllers/notifications.controllers.ts index e85846405b..bdf9c44f13 100644 --- a/src/backend/src/controllers/notifications.controllers.ts +++ b/src/backend/src/controllers/notifications.controllers.ts @@ -11,34 +11,4 @@ export default class NotificationsController { next(error); } } - - static async getUserUnreadNotifications(req: Request, res: Response, next: NextFunction) { - try { - const { organization, currentUser } = req; - - const unreadNotifications = await NotificationsService.getUserUnreadNotifications( - currentUser.userId, - organization.organizationId - ); - res.status(200).json(unreadNotifications); - } catch (error: unknown) { - next(error); - } - } - - static async removeUserNotification(req: Request, res: Response, next: NextFunction) { - try { - const { notificationId } = req.params; - const { organization, currentUser } = req; - - const unreadNotifications = await NotificationsService.removeUserNotification( - currentUser.userId, - notificationId, - organization.organizationId - ); - res.status(200).json(unreadNotifications); - } catch (error: unknown) { - next(error); - } - } } diff --git a/src/backend/src/controllers/popUps.controllers.ts b/src/backend/src/controllers/popUps.controllers.ts new file mode 100644 index 0000000000..6247a03fbd --- /dev/null +++ b/src/backend/src/controllers/popUps.controllers.ts @@ -0,0 +1,27 @@ +import { NextFunction, Request, Response } from 'express'; +import { PopUpService } from '../services/pop-up.services'; + +export default class PopUpsController { + static async getUserUnreadPopUps(req: Request, res: Response, next: NextFunction) { + try { + const { organization, currentUser } = req; + + const unreadPopUps = await PopUpService.getUserUnreadPopUps(currentUser.userId, organization.organizationId); + res.status(200).json(unreadPopUps); + } catch (error: unknown) { + next(error); + } + } + + static async removeUserPopUps(req: Request, res: Response, next: NextFunction) { + try { + const { popUpId } = req.params; + const { organization, currentUser } = req; + + const unreadPopUps = await PopUpService.removeUserPopUp(currentUser.userId, popUpId, organization.organizationId); + res.status(200).json(unreadPopUps); + } catch (error: unknown) { + next(error); + } + } +} diff --git a/src/backend/src/prisma-query-args/notifications.query-args.ts b/src/backend/src/prisma-query-args/notifications.query-args.ts deleted file mode 100644 index 4cf877ac5c..0000000000 --- a/src/backend/src/prisma-query-args/notifications.query-args.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Prisma } from '@prisma/client'; -import { getUserQueryArgs } from './user.query-args'; - -export type NotificationQueryArgs = ReturnType; - -export const getNotificationQueryArgs = (organizationId: string) => - Prisma.validator()({ - include: { - users: getUserQueryArgs(organizationId) - } - }); diff --git a/src/backend/src/prisma-query-args/pop-up.query-args.ts b/src/backend/src/prisma-query-args/pop-up.query-args.ts new file mode 100644 index 0000000000..0862956d22 --- /dev/null +++ b/src/backend/src/prisma-query-args/pop-up.query-args.ts @@ -0,0 +1,11 @@ +import { Prisma } from '@prisma/client'; +import { getUserQueryArgs } from './user.query-args'; + +export type PopUpQueryArgs = ReturnType; + +export const getPopUpQueryArgs = (organizationId: string) => + Prisma.validator()({ + include: { + users: getUserQueryArgs(organizationId) + } + }); diff --git a/src/backend/src/prisma/migrations/20241222123937_homepage_redesign/migration.sql b/src/backend/src/prisma/migrations/20241222150147_homepage_redesign/migration.sql similarity index 70% rename from src/backend/src/prisma/migrations/20241222123937_homepage_redesign/migration.sql rename to src/backend/src/prisma/migrations/20241222150147_homepage_redesign/migration.sql index 1ff99b84a0..17a36eb2d3 100644 --- a/src/backend/src/prisma/migrations/20241222123937_homepage_redesign/migration.sql +++ b/src/backend/src/prisma/migrations/20241222150147_homepage_redesign/migration.sql @@ -9,7 +9,7 @@ ALTER TABLE "Project" ADD COLUMN "organizationId" TEXT; CREATE TABLE "Announcement" ( "announcementId" TEXT NOT NULL, "text" TEXT NOT NULL, - "dateCreated" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "dateMessageSent" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, "dateDeleted" TIMESTAMP(3), "senderName" TEXT NOT NULL, "slackEventId" TEXT NOT NULL, @@ -19,13 +19,13 @@ CREATE TABLE "Announcement" ( ); -- CreateTable -CREATE TABLE "Notification" ( - "notificationId" TEXT NOT NULL, +CREATE TABLE "PopUp" ( + "popUpId" TEXT NOT NULL, "text" TEXT NOT NULL, "iconName" TEXT NOT NULL, "eventLink" TEXT, - CONSTRAINT "Notification_pkey" PRIMARY KEY ("notificationId") + CONSTRAINT "PopUp_pkey" PRIMARY KEY ("popUpId") ); -- CreateTable @@ -35,7 +35,7 @@ CREATE TABLE "_receivedAnnouncements" ( ); -- CreateTable -CREATE TABLE "_userNotifications" ( +CREATE TABLE "_userPopUps" ( "A" TEXT NOT NULL, "B" TEXT NOT NULL ); @@ -50,10 +50,10 @@ CREATE UNIQUE INDEX "_receivedAnnouncements_AB_unique" ON "_receivedAnnouncement CREATE INDEX "_receivedAnnouncements_B_index" ON "_receivedAnnouncements"("B"); -- CreateIndex -CREATE UNIQUE INDEX "_userNotifications_AB_unique" ON "_userNotifications"("A", "B"); +CREATE UNIQUE INDEX "_userPopUps_AB_unique" ON "_userPopUps"("A", "B"); -- CreateIndex -CREATE INDEX "_userNotifications_B_index" ON "_userNotifications"("B"); +CREATE INDEX "_userPopUps_B_index" ON "_userPopUps"("B"); -- AddForeignKey ALTER TABLE "Project" ADD CONSTRAINT "Project_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("organizationId") ON DELETE SET NULL ON UPDATE CASCADE; @@ -65,7 +65,7 @@ ALTER TABLE "_receivedAnnouncements" ADD CONSTRAINT "_receivedAnnouncements_A_fk ALTER TABLE "_receivedAnnouncements" ADD CONSTRAINT "_receivedAnnouncements_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("userId") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE "_userNotifications" ADD CONSTRAINT "_userNotifications_A_fkey" FOREIGN KEY ("A") REFERENCES "Notification"("notificationId") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "_userPopUps" ADD CONSTRAINT "_userPopUps_A_fkey" FOREIGN KEY ("A") REFERENCES "PopUp"("popUpId") ON DELETE CASCADE ON UPDATE CASCADE; -- AddForeignKey -ALTER TABLE "_userNotifications" ADD CONSTRAINT "_userNotifications_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("userId") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "_userPopUps" ADD CONSTRAINT "_userPopUps_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("userId") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/src/backend/src/prisma/schema.prisma b/src/backend/src/prisma/schema.prisma index 9d9bcb1f7f..c62827da19 100644 --- a/src/backend/src/prisma/schema.prisma +++ b/src/backend/src/prisma/schema.prisma @@ -181,7 +181,7 @@ model User { createdMilestones Milestone[] @relation(name: "milestoneCreator") deletedMilestones Milestone[] @relation(name: "milestoneDeleter") unreadAnnouncements Announcement[] @relation(name: "receivedAnnouncements") - unreadNotifications Notification[] @relation(name: "userNotifications") + unreadPopUps PopUp[] @relation(name: "userPopUps") } model Role { @@ -935,17 +935,17 @@ model Announcement { announcementId String @id @default(uuid()) text String usersReceived User[] @relation("receivedAnnouncements") - dateCreated DateTime @default(now()) + dateMessageSent DateTime @default(now()) dateDeleted DateTime? senderName String slackEventId String @unique slackChannelName String } -model Notification { - notificationId String @id @default(uuid()) - text String - iconName String - users User[] @relation("userNotifications") - eventLink String? +model PopUp { + popUpId String @id @default(uuid()) + text String + iconName String + users User[] @relation("userPopUps") + eventLink String? } diff --git a/src/backend/src/prisma/seed.ts b/src/backend/src/prisma/seed.ts index e7b5aa4c84..ddd4e190cb 100644 --- a/src/backend/src/prisma/seed.ts +++ b/src/backend/src/prisma/seed.ts @@ -1898,6 +1898,7 @@ const performSeed: () => Promise = async () => { await AnnouncementService.createAnnouncement( 'Welcome to Finishline!', [regina.userId], + new Date(), 'Thomas Emrax', '1', 'software', diff --git a/src/backend/src/routes/notifications.routes.ts b/src/backend/src/routes/notifications.routes.ts index d5411bddde..4701b0f3ea 100644 --- a/src/backend/src/routes/notifications.routes.ts +++ b/src/backend/src/routes/notifications.routes.ts @@ -4,7 +4,5 @@ import NotificationsController from '../controllers/notifications.controllers'; const notificationsRouter = express.Router(); notificationsRouter.post('/task-deadlines', NotificationsController.sendDailySlackNotifications); -notificationsRouter.get('/current-user', NotificationsController.getUserUnreadNotifications); -notificationsRouter.post('/:notificationId/remove', NotificationsController.removeUserNotification); export default notificationsRouter; diff --git a/src/backend/src/routes/pop-up.routes.ts b/src/backend/src/routes/pop-up.routes.ts new file mode 100644 index 0000000000..5ecaeff01f --- /dev/null +++ b/src/backend/src/routes/pop-up.routes.ts @@ -0,0 +1,9 @@ +import express from 'express'; +import PopUpsController from '../controllers/popUps.controllers'; + +const popUpsRouter = express.Router(); + +popUpsRouter.get('/current-user', PopUpsController.getUserUnreadPopUps); +popUpsRouter.post('/:popUpId/remove', PopUpsController.removeUserPopUps); + +export default popUpsRouter; diff --git a/src/backend/src/services/announcement.service.ts b/src/backend/src/services/announcement.service.ts index 7562755f01..8d35cb879a 100644 --- a/src/backend/src/services/announcement.service.ts +++ b/src/backend/src/services/announcement.service.ts @@ -20,6 +20,7 @@ export default class AnnouncementService { static async createAnnouncement( text: string, usersReceivedIds: string[], + dateMessageSent: Date, senderName: string, slackEventId: string, slackChannelName: string, @@ -33,6 +34,7 @@ export default class AnnouncementService { userId: id })) }, + dateMessageSent, senderName, slackEventId, slackChannelName diff --git a/src/backend/src/services/change-requests.services.ts b/src/backend/src/services/change-requests.services.ts index 85e0dc5579..709f4b2e91 100644 --- a/src/backend/src/services/change-requests.services.ts +++ b/src/backend/src/services/change-requests.services.ts @@ -46,7 +46,7 @@ import { import { ChangeRequestQueryArgs, getChangeRequestQueryArgs } from '../prisma-query-args/change-requests.query-args'; import proposedSolutionTransformer from '../transformers/proposed-solutions.transformer'; import { getProposedSolutionQueryArgs } from '../prisma-query-args/proposed-solutions.query-args'; -import { sendHomeCrRequestReviewNotification, sendHomeCrReviewedNotification } from '../utils/notifications.utils'; +import { sendCrRequestReviewPopUp, sendCrReviewedPopUp } from '../utils/pop-up.utils'; export default class ChangeRequestsService { /** @@ -151,7 +151,7 @@ export default class ChangeRequestsService { // send a notification to the submitter that their change request has been reviewed await sendCRSubmitterReviewedNotification(updated); - await sendHomeCrReviewedNotification(foundCR, updated.submitter, accepted, organization.organizationId); + await sendCrReviewedPopUp(foundCR, updated.submitter, accepted, organization.organizationId); // send a reply to a CR's notifications of its updated status await sendSlackCRStatusToThread(updated.notificationSlackThreads, foundCR.crId, foundCR.identifier, accepted); @@ -1082,6 +1082,6 @@ export default class ChangeRequestsService { // send slack message to CR reviewers await sendSlackRequestedReviewNotification(newReviewers, changeRequestTransformer(foundCR)); - await sendHomeCrRequestReviewNotification(foundCR, newReviewers, organization.organizationId); + await sendCrRequestReviewPopUp(foundCR, newReviewers, organization.organizationId); } } diff --git a/src/backend/src/services/design-reviews.services.ts b/src/backend/src/services/design-reviews.services.ts index 644903fa43..64363b0c61 100644 --- a/src/backend/src/services/design-reviews.services.ts +++ b/src/backend/src/services/design-reviews.services.ts @@ -39,7 +39,7 @@ import { getWorkPackageQueryArgs } from '../prisma-query-args/work-packages.quer import { UserWithSettings } from '../utils/auth.utils'; import { getUserScheduleSettingsQueryArgs } from '../prisma-query-args/user.query-args'; import { createCalendarEvent, deleteCalendarEvent, updateCalendarEvent } from '../utils/google-integration.utils'; -import { sendHomeDrNotification } from '../utils/notifications.utils'; +import { sendDrPopUp } from '../utils/pop-up.utils'; export default class DesignReviewsService { /** @@ -206,7 +206,7 @@ export default class DesignReviewsService { } } - await sendHomeDrNotification(designReview, members, submitter, wbsElement.name, organization.organizationId); + await sendDrPopUp(designReview, members, submitter, wbsElement.name, organization.organizationId); const project = wbsElement.workPackage?.project; const teams = project?.teams; diff --git a/src/backend/src/services/notifications.services.ts b/src/backend/src/services/notifications.services.ts index 3da62b744f..a443d93588 100644 --- a/src/backend/src/services/notifications.services.ts +++ b/src/backend/src/services/notifications.services.ts @@ -11,10 +11,8 @@ import { daysBetween, startOfDay, wbsPipe } from 'shared'; import { buildDueString } from '../utils/slack.utils'; import WorkPackagesService from './work-packages.services'; import { addWeeksToDate } from 'shared'; -import { HttpException, NotFoundException } from '../utils/errors.utils'; +import { HttpException } from '../utils/errors.utils'; import { meetingStartTimePipe } from '../utils/design-reviews.utils'; -import { getNotificationQueryArgs } from '../prisma-query-args/notifications.query-args'; -import notificationTransformer from '../transformers/notifications.transformer'; export default class NotificationsService { static async sendDailySlackNotifications() { @@ -195,98 +193,4 @@ export default class NotificationsService { await Promise.all(promises); } - - /** - * Gets all of a user's unread notifications - * @param userId id of user to get unread notifications from - * @param organization the user's orgainzation - * @returns the unread notifications of the user - */ - static async getUserUnreadNotifications(userId: string, organizationId: string) { - const unreadNotifications = await prisma.notification.findMany({ - where: { - users: { - some: { userId } - } - }, - ...getNotificationQueryArgs(organizationId) - }); - - if (!unreadNotifications) throw new HttpException(404, 'User Unread Notifications Not Found'); - - return unreadNotifications.map(notificationTransformer); - } - - /** - * Removes a notification from the user's unread notifications - * @param userId id of the current user - * @param notificationId id of the notification to remove - * @param organization the user's organization - * @returns the user's updated unread notifications - */ - static async removeUserNotification(userId: string, notificationId: string, organizationId: string) { - const updatedUser = await prisma.user.update({ - where: { userId }, - data: { - unreadNotifications: { - disconnect: { - notificationId - } - } - }, - include: { unreadNotifications: getNotificationQueryArgs(organizationId) } - }); - - if (!updatedUser) throw new HttpException(404, `Failed to remove notication: ${notificationId}`); - - return updatedUser.unreadNotifications.map(notificationTransformer); - } - - /** - * Creates and sends a notification to all users with the given userIds - * @param text writing in the notification - * @param iconName icon that appears in the notification - * @param userIds ids of users to send the notification to - * @param organizationId - * @param eventLink link the notification will go to when clicked - * @returns the created notification - */ - static async sendNotifcationToUsers( - text: string, - iconName: string, - userIds: string[], - organizationId: string, - eventLink?: string - ) { - const createdNotification = await prisma.notification.create({ - data: { - text, - iconName, - eventLink - }, - ...getNotificationQueryArgs(organizationId) - }); - - if (!createdNotification) throw new HttpException(500, 'Failed to create notification'); - - const notificationsPromises = userIds.map(async (userId) => { - const requestedUser = await prisma.user.findUnique({ - where: { userId } - }); - - if (!requestedUser) throw new NotFoundException('User', userId); - - return await prisma.user.update({ - where: { userId: requestedUser.userId }, - data: { - unreadNotifications: { - connect: { notificationId: createdNotification.notificationId } - } - } - }); - }); - - await Promise.all(notificationsPromises); - return notificationTransformer(createdNotification); - } } diff --git a/src/backend/src/services/pop-up.services.ts b/src/backend/src/services/pop-up.services.ts new file mode 100644 index 0000000000..267c72761f --- /dev/null +++ b/src/backend/src/services/pop-up.services.ts @@ -0,0 +1,100 @@ +import { getPopUpQueryArgs } from '../prisma-query-args/pop-up.query-args'; +import prisma from '../prisma/prisma'; +import popUpTransformer from '../transformers/pop-up.transformer'; +import { HttpException, NotFoundException } from '../utils/errors.utils'; + +export class PopUpService { + /** + * Gets all of a user's unread pop up + * @param userId id of user to get unread pop up from + * @param organization the user's orgainzation + * @returns the unread pop up of the user + */ + static async getUserUnreadPopUps(userId: string, organizationId: string) { + const unreadPopUps = await prisma.popUp.findMany({ + where: { + users: { + some: { userId } + } + }, + ...getPopUpQueryArgs(organizationId) + }); + + if (!unreadPopUps) throw new HttpException(404, 'User Unread Notifications Not Found'); + + return unreadPopUps.map(popUpTransformer); + } + + /** + * Removes a pop up from the user's unread pop up + * @param userId id of the current user + * @param popUpId id of the pop up to remove + * @param organization the user's organization + * @returns the user's updated unread pop up + */ + static async removeUserPopUp(userId: string, popUpId: string, organizationId: string) { + const updatedUser = await prisma.user.update({ + where: { userId }, + data: { + unreadPopUps: { + disconnect: { + popUpId + } + } + }, + include: { unreadPopUps: getPopUpQueryArgs(organizationId) } + }); + + if (!updatedUser) throw new HttpException(404, `Failed to remove notication: ${popUpId}`); + + return updatedUser.unreadPopUps.map(popUpTransformer); + } + + /** + * Creates and sends a pop up to all users with the given userIds + * @param text writing in the pop up + * @param iconName icon that appears in the pop up + * @param userIds ids of users to send the pop up to + * @param organizationId + * @param eventLink link the pop up will go to when clicked + * @returns the created notification + */ + static async sendPopUpToUsers( + text: string, + iconName: string, + userIds: string[], + organizationId: string, + eventLink?: string + ) { + const createdPopUp = await prisma.popUp.create({ + data: { + text, + iconName, + eventLink + }, + ...getPopUpQueryArgs(organizationId) + }); + + if (!createdPopUp) throw new HttpException(500, 'Failed to create notification'); + + const popUpPromises = userIds.map(async (userId) => { + const requestedUser = await prisma.user.findUnique({ + where: { userId } + }); + + if (!requestedUser) throw new NotFoundException('User', userId); + + return await prisma.user.update({ + where: { userId: requestedUser.userId }, + data: { + unreadPopUps: { + connect: { popUpId: createdPopUp.popUpId } + } + } + }); + }); + + await Promise.all(popUpPromises); + return popUpTransformer(createdPopUp); + } +} diff --git a/src/backend/src/transformers/notifications.transformer.ts b/src/backend/src/transformers/notifications.transformer.ts deleted file mode 100644 index 45dd25dee9..0000000000 --- a/src/backend/src/transformers/notifications.transformer.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Prisma } from '@prisma/client'; -import { NotificationQueryArgs } from '../prisma-query-args/notifications.query-args'; -import { Notification } from 'shared'; - -const notificationTransformer = (notification: Prisma.NotificationGetPayload): Notification => { - return { - notificationId: notification.notificationId, - text: notification.text, - iconName: notification.iconName, - eventLink: notification.eventLink ?? undefined - }; -}; - -export default notificationTransformer; diff --git a/src/backend/src/transformers/pop-up.transformer.ts b/src/backend/src/transformers/pop-up.transformer.ts new file mode 100644 index 0000000000..1be7e4ec68 --- /dev/null +++ b/src/backend/src/transformers/pop-up.transformer.ts @@ -0,0 +1,12 @@ +import { Prisma } from '@prisma/client'; +import { PopUpQueryArgs } from '../prisma-query-args/pop-up.query-args'; +import { PopUp } from 'shared'; + +const popUpTransformer = (popUp: Prisma.PopUpGetPayload): PopUp => { + return { + ...popUp, + eventLink: popUp.eventLink ?? undefined + }; +}; + +export default popUpTransformer; diff --git a/src/backend/src/utils/auth.utils.ts b/src/backend/src/utils/auth.utils.ts index 7e7602eb6a..8520d881e4 100644 --- a/src/backend/src/utils/auth.utils.ts +++ b/src/backend/src/utils/auth.utils.ts @@ -66,7 +66,7 @@ export const requireJwtDev = (req: Request, res: Response, next: NextFunction) = ) { next(); } else if ( - req.path.startsWith('/notifications/taskdeadlines') // task deadline notification endpoint + req.path.startsWith('/notifications') // task deadline notification endpoint ) { notificationEndpointAuth(req, res, next); } else { diff --git a/src/backend/src/utils/notifications.utils.ts b/src/backend/src/utils/notifications.utils.ts index 445f36ed67..8fb6046318 100644 --- a/src/backend/src/utils/notifications.utils.ts +++ b/src/backend/src/utils/notifications.utils.ts @@ -1,7 +1,5 @@ -import { Task as Prisma_Task, WBS_Element, Design_Review, Change_Request } from '@prisma/client'; +import { Task as Prisma_Task, WBS_Element, Design_Review } from '@prisma/client'; import { UserWithSettings } from './auth.utils'; -import NotificationsService from '../services/notifications.services'; -import { User } from '@prisma/client'; export type TaskWithAssignees = Prisma_Task & { assignees: UserWithSettings[] | null; @@ -37,74 +35,3 @@ export const endOfDayTomorrow = () => { endOfDay.setDate(startOfDay.getDate() + 1); return endOfDay; }; - -/** - * Sends a finishline notification that a design review was scheduled - * @param designReview dr that was created - * @param members optional and required members of the dr - * @param submitter the user who created the dr - * @param workPackageName the name of the work package associated witht the dr - * @param organizationId id of the organization of the dr - */ -export const sendHomeDrNotification = async ( - designReview: Design_Review, - members: User[], - submitter: User, - workPackageName: string, - organizationId: string -) => { - const designReviewLink = `/settings/preferences?drId=${designReview.designReviewId}`; - - const msg = `Design Review for ${workPackageName} is being scheduled by ${submitter.firstName} ${submitter.lastName}`; - await NotificationsService.sendNotifcationToUsers( - msg, - 'calendar_month', - members.map((member) => member.userId), - organizationId, - designReviewLink - ); -}; - -/** - * Sends a finishline notification that a change request was reviewed - * @param changeRequest cr that was requested review - * @param submitter the user who submitted the cr - * @param accepted true if the cr changes were accepted, false if denied - * @param organizationId id of the organization of the cr - */ -export const sendHomeCrReviewedNotification = async ( - changeRequest: Change_Request, - submitter: User, - accepted: boolean, - organizationId: string -) => { - const changeRequestLink = `/change-requests/${changeRequest.crId}`; - await NotificationsService.sendNotifcationToUsers( - `CR #${changeRequest.identifier} has been ${accepted ? 'approved!' : 'denied.'}`, - accepted ? 'check_circle' : 'cancel', - [submitter.userId], - organizationId, - changeRequestLink - ); -}; - -/** - * Sends a finishline notification to all requested reviewers of a change request - * @param changeRequest cr that was requested review - * @param reviewers user's reviewing the cr - * @param organizationId id of the organization of the cr - */ -export const sendHomeCrRequestReviewNotification = async ( - changeRequest: Change_Request, - reviewers: User[], - organizationId: string -) => { - const changeRequestLink = `/change-requests/${changeRequest.crId}`; - await NotificationsService.sendNotifcationToUsers( - `Your review has been requested on CR #${changeRequest.identifier}`, - 'edit_note', - reviewers.map((reviewer) => reviewer.userId), - organizationId, - changeRequestLink - ); -}; diff --git a/src/backend/src/utils/pop-up.utils.ts b/src/backend/src/utils/pop-up.utils.ts new file mode 100644 index 0000000000..30c0fecdf0 --- /dev/null +++ b/src/backend/src/utils/pop-up.utils.ts @@ -0,0 +1,69 @@ +import { Change_Request, Design_Review, User } from '@prisma/client'; +import { PopUpService } from '../services/pop-up.services'; + +/** + * Sends a pop up that a design review was scheduled + * @param designReview dr that was created + * @param members optional and required members of the dr + * @param submitter the user who created the dr + * @param workPackageName the name of the work package associated witht the dr + * @param organizationId id of the organization of the dr + */ +export const sendDrPopUp = async ( + designReview: Design_Review, + members: User[], + submitter: User, + workPackageName: string, + organizationId: string +) => { + const designReviewLink = `/settings/preferences?drId=${designReview.designReviewId}`; + + const msg = `Design Review for ${workPackageName} is being scheduled by ${submitter.firstName} ${submitter.lastName}`; + await PopUpService.sendPopUpToUsers( + msg, + 'calendar_month', + members.map((member) => member.userId), + organizationId, + designReviewLink + ); +}; + +/** + * Sends a pop up that a change request was reviewed + * @param changeRequest cr that was requested review + * @param submitter the user who submitted the cr + * @param accepted true if the cr changes were accepted, false if denied + * @param organizationId id of the organization of the cr + */ +export const sendCrReviewedPopUp = async ( + changeRequest: Change_Request, + submitter: User, + accepted: boolean, + organizationId: string +) => { + const changeRequestLink = `/change-requests/${changeRequest.crId}`; + await PopUpService.sendPopUpToUsers( + `CR #${changeRequest.identifier} has been ${accepted ? 'approved!' : 'denied.'}`, + accepted ? 'check_circle' : 'cancel', + [submitter.userId], + organizationId, + changeRequestLink + ); +}; + +/** + * Sends a finishline pop up to all requested reviewers of a change request + * @param changeRequest cr that was requested review + * @param reviewers user's reviewing the cr + * @param organizationId id of the organization of the cr + */ +export const sendCrRequestReviewPopUp = async (changeRequest: Change_Request, reviewers: User[], organizationId: string) => { + const changeRequestLink = `/change-requests/${changeRequest.crId}`; + await PopUpService.sendPopUpToUsers( + `Your review has been requested on CR #${changeRequest.identifier}`, + 'edit_note', + reviewers.map((reviewer) => reviewer.userId), + organizationId, + changeRequestLink + ); +}; diff --git a/src/backend/tests/unmocked/announcements.test.ts b/src/backend/tests/unmocked/announcements.test.ts index 534b546edb..97ab4baeef 100644 --- a/src/backend/tests/unmocked/announcements.test.ts +++ b/src/backend/tests/unmocked/announcements.test.ts @@ -20,6 +20,7 @@ describe('Announcemnts Tests', () => { await AnnouncementService.createAnnouncement( 'test1', [testBatman.userId], + new Date(), 'Thomas Emrax', '1', 'software', @@ -28,6 +29,7 @@ describe('Announcemnts Tests', () => { await AnnouncementService.createAnnouncement( 'test2', [testBatman.userId], + new Date(), 'Superman', '50', 'mechanical', diff --git a/src/backend/tests/unmocked/notifications.test.ts b/src/backend/tests/unmocked/notifications.test.ts deleted file mode 100644 index 026099c1c3..0000000000 --- a/src/backend/tests/unmocked/notifications.test.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Organization } from '@prisma/client'; -import { createTestOrganization, createTestUser, resetUsers } from '../test-utils'; -import { batmanAppAdmin, supermanAdmin } from '../test-data/users.test-data'; -import { NotFoundException } from '../../src/utils/errors.utils'; -import prisma from '../../src/prisma/prisma'; -import NotificationService from '../../src/services/notifications.services'; - -describe('Notifications Tests', () => { - let orgId: string; - let organization: Organization; - beforeEach(async () => { - organization = await createTestOrganization(); - orgId = organization.organizationId; - }); - - afterEach(async () => { - await resetUsers(); - }); - - describe('Send Notification', () => { - it('fails on invalid user id', async () => { - await expect( - async () => - await NotificationService.sendNotifcationToUsers( - 'test notification', - 'star', - ['1', '2'], - organization.organizationId - ) - ).rejects.toThrow(new NotFoundException('User', '1')); - }); - - it('Succeeds and sends notification to user', async () => { - const testBatman = await createTestUser(batmanAppAdmin, orgId); - const testSuperman = await createTestUser(supermanAdmin, orgId); - await NotificationService.sendNotifcationToUsers( - 'test notification', - 'star', - [testBatman.userId, testSuperman.userId], - organization.organizationId - ); - - const batmanWithNotifications = await prisma.user.findUnique({ - where: { userId: testBatman.userId }, - include: { unreadNotifications: true } - }); - - const supermanWithNotifications = await prisma.user.findUnique({ - where: { userId: testBatman.userId }, - include: { unreadNotifications: true } - }); - - expect(batmanWithNotifications?.unreadNotifications).toHaveLength(1); - expect(batmanWithNotifications?.unreadNotifications[0].text).toBe('test notification'); - expect(supermanWithNotifications?.unreadNotifications).toHaveLength(1); - expect(supermanWithNotifications?.unreadNotifications[0].text).toBe('test notification'); - }); - }); - - describe('Get Notifications', () => { - it('Succeeds and gets user notifications', async () => { - const testBatman = await createTestUser(batmanAppAdmin, orgId); - await NotificationService.sendNotifcationToUsers('test1', 'test1', [testBatman.userId], orgId); - await NotificationService.sendNotifcationToUsers('test2', 'test2', [testBatman.userId], orgId); - - const notifications = await NotificationService.getUserUnreadNotifications( - testBatman.userId, - organization.organizationId - ); - - expect(notifications).toHaveLength(2); - expect(notifications[0].text).toBe('test1'); - expect(notifications[1].text).toBe('test2'); - }); - }); - - describe('Remove Notifications', () => { - it('Succeeds and removes user notification', async () => { - const testBatman = await createTestUser(batmanAppAdmin, orgId); - await NotificationService.sendNotifcationToUsers('test1', 'test1', [testBatman.userId], orgId); - await NotificationService.sendNotifcationToUsers('test2', 'test2', [testBatman.userId], orgId); - - const notifications = await NotificationService.getUserUnreadNotifications( - testBatman.userId, - organization.organizationId - ); - - expect(notifications).toHaveLength(2); - expect(notifications[0].text).toBe('test1'); - expect(notifications[1].text).toBe('test2'); - - const updatedNotifications = await NotificationService.removeUserNotification( - testBatman.userId, - notifications[0].notificationId, - organization.organizationId - ); - - expect(updatedNotifications).toHaveLength(1); - expect(updatedNotifications[0].text).toBe('test2'); - }); - }); -}); diff --git a/src/backend/tests/unmocked/pop-up.test.ts b/src/backend/tests/unmocked/pop-up.test.ts new file mode 100644 index 0000000000..4421aea4f8 --- /dev/null +++ b/src/backend/tests/unmocked/pop-up.test.ts @@ -0,0 +1,90 @@ +import { Organization } from '@prisma/client'; +import { createTestOrganization, createTestUser, resetUsers } from '../test-utils'; +import { batmanAppAdmin, supermanAdmin } from '../test-data/users.test-data'; +import { NotFoundException } from '../../src/utils/errors.utils'; +import prisma from '../../src/prisma/prisma'; +import { PopUpService } from '../../src/services/pop-up.services'; + +describe('Pop Ups Tests', () => { + let orgId: string; + let organization: Organization; + beforeEach(async () => { + organization = await createTestOrganization(); + orgId = organization.organizationId; + }); + + afterEach(async () => { + await resetUsers(); + }); + + describe('Send Pop Up', () => { + it('fails on invalid user id', async () => { + await expect( + async () => await PopUpService.sendPopUpToUsers('test pop up', 'star', ['1', '2'], organization.organizationId) + ).rejects.toThrow(new NotFoundException('User', '1')); + }); + + it('Succeeds and sends pop up to user', async () => { + const testBatman = await createTestUser(batmanAppAdmin, orgId); + const testSuperman = await createTestUser(supermanAdmin, orgId); + await PopUpService.sendPopUpToUsers( + 'test pop up', + 'star', + [testBatman.userId, testSuperman.userId], + organization.organizationId + ); + + const batmanWithPopUps = await prisma.user.findUnique({ + where: { userId: testBatman.userId }, + include: { unreadPopUps: true } + }); + + const supermanWithPopUps = await prisma.user.findUnique({ + where: { userId: testBatman.userId }, + include: { unreadPopUps: true } + }); + + expect(batmanWithPopUps?.unreadPopUps).toHaveLength(1); + expect(batmanWithPopUps?.unreadPopUps[0].text).toBe('test pop up'); + expect(supermanWithPopUps?.unreadPopUps).toHaveLength(1); + expect(supermanWithPopUps?.unreadPopUps[0].text).toBe('test pop up'); + }); + }); + + describe('Get Notifications', () => { + it('Succeeds and gets user pop ups', async () => { + const testBatman = await createTestUser(batmanAppAdmin, orgId); + await PopUpService.sendPopUpToUsers('test1', 'test1', [testBatman.userId], orgId); + await PopUpService.sendPopUpToUsers('test2', 'test2', [testBatman.userId], orgId); + + const popUps = await PopUpService.getUserUnreadPopUps(testBatman.userId, organization.organizationId); + + expect(popUps).toHaveLength(2); + expect(popUps[0].text).toBe('test1'); + expect(popUps[1].text).toBe('test2'); + }); + }); + + describe('Remove Pop Ups', () => { + it('Succeeds and removes user pop up', async () => { + const testBatman = await createTestUser(batmanAppAdmin, orgId); + await PopUpService.sendPopUpToUsers('test1', 'test1', [testBatman.userId], orgId); + await PopUpService.sendPopUpToUsers('test2', 'test2', [testBatman.userId], orgId); + + const popUps = await PopUpService.getUserUnreadPopUps(testBatman.userId, organization.organizationId); + + expect(popUps).toHaveLength(2); + expect(popUps[0].text).toBe('test1'); + expect(popUps[1].text).toBe('test2'); + + const updatedPopUps = await PopUpService.removeUserPopUp( + testBatman.userId, + popUps[0].popUpId, + organization.organizationId + ); + + expect(updatedPopUps).toHaveLength(1); + expect(updatedPopUps[0].text).toBe('test2'); + }); + }); +}); diff --git a/src/frontend/src/apis/notifications.api.ts b/src/frontend/src/apis/notifications.api.ts deleted file mode 100644 index 454e9d4170..0000000000 --- a/src/frontend/src/apis/notifications.api.ts +++ /dev/null @@ -1,19 +0,0 @@ -import axios from '../utils/axios'; -import { apiUrls } from '../utils/urls'; -import { Notification } from 'shared'; - -/* - * Gets all unread notifications of the user with the given id - */ -export const getNotifications = () => { - return axios.get(apiUrls.notificationsCurrentUser(), { - transformResponse: (data) => JSON.parse(data) - }); -}; - -/* - * Removes a notification from the user with the given id - */ -export const removeNotification = (notificationId: string) => { - return axios.post(apiUrls.notificationsRemove(notificationId)); -}; diff --git a/src/frontend/src/apis/pop-ups.api.ts b/src/frontend/src/apis/pop-ups.api.ts new file mode 100644 index 0000000000..a0674b9527 --- /dev/null +++ b/src/frontend/src/apis/pop-ups.api.ts @@ -0,0 +1,19 @@ +import axios from '../utils/axios'; +import { apiUrls } from '../utils/urls'; +import { PopUp } from 'shared'; + +/* + * Gets all unread notifications of the user with the given id + */ +export const getPopUps = () => { + return axios.get(apiUrls.popUpsCurrentUser(), { + transformResponse: (data) => JSON.parse(data) + }); +}; + +/* + * Removes a notification from the user with the given id + */ +export const removePopUps = (notificationId: string) => { + return axios.post(apiUrls.popUpsRemove(notificationId)); +}; diff --git a/src/frontend/src/components/NotificationAlert.tsx b/src/frontend/src/components/NotificationAlert.tsx deleted file mode 100644 index 53b01a5aaa..0000000000 --- a/src/frontend/src/components/NotificationAlert.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { Box } from '@mui/material'; -import React, { useEffect, useState } from 'react'; -import { Notification } from 'shared'; -import NotificationCard from './NotificationCard'; -import { useHistory } from 'react-router-dom'; -import { useCurrentUserNotifications, useRemoveUserNotification } from '../hooks/notifications.hooks'; - -const NotificationAlert: React.FC = () => { - const { data: notifications, isLoading: notificationsIsLoading } = useCurrentUserNotifications(); - const { mutateAsync: removeNotification, isLoading: removeIsLoading } = useRemoveUserNotification(); - const [currentNotification, setCurrentNotification] = useState(); - const history = useHistory(); - - useEffect(() => { - if (notifications && notifications.length > 0) { - setCurrentNotification(notifications[0]); - } - }, [notifications]); - - const removeNotificationWrapper = async (notification: Notification) => { - setCurrentNotification(undefined); - await removeNotification(notification); - }; - - const onClick = async (notification: Notification) => { - if (!!notification.eventLink) { - await removeNotificationWrapper(notification); - history.push(notification.eventLink); - } - }; - - return ( - - {!removeIsLoading && !notificationsIsLoading && currentNotification && ( - - )} - - ); -}; - -export default NotificationAlert; diff --git a/src/frontend/src/components/NotificationCard.tsx b/src/frontend/src/components/NotificationCard.tsx index 38dd994029..e905ca2ad6 100644 --- a/src/frontend/src/components/NotificationCard.tsx +++ b/src/frontend/src/components/NotificationCard.tsx @@ -1,15 +1,15 @@ import { Box, Card, Icon, IconButton, Typography, useTheme } from '@mui/material'; import React from 'react'; -import { Notification } from 'shared'; +import { PopUp } from 'shared'; import CloseIcon from '@mui/icons-material/Close'; -interface NotificationCardProps { - notification: Notification; - removeNotification: (notificationId: Notification) => Promise; - onClick: (notificationId: Notification) => Promise; +interface PopUpCardProps { + popUp: PopUp; + removePopUp: (popUp: PopUp) => Promise; + onClick: (popUp: PopUp) => Promise; } -const NotificationCard: React.FC = ({ notification, removeNotification, onClick }) => { +const PopUpCard: React.FC = ({ popUp, removePopUp, onClick }) => { const theme = useTheme(); return ( = ({ notification, remov }} > await onClick(notification)} + onClick={async () => await onClick(popUp)} sx={{ display: 'flex', alignItems: 'center', gap: 1, - cursor: !!notification.eventLink ? 'pointer' : 'default' + cursor: !!popUp.eventLink ? 'pointer' : 'default' }} > = ({ notification, remov fontSize: 36 }} > - {notification.iconName} + {popUp.iconName} - {notification.text} + {popUp.text} - removeNotification(notification)}> + removePopUp(popUp)}> @@ -71,4 +71,4 @@ const NotificationCard: React.FC = ({ notification, remov ); }; -export default NotificationCard; +export default PopUpCard; diff --git a/src/frontend/src/components/PopUpAlert.tsx b/src/frontend/src/components/PopUpAlert.tsx new file mode 100644 index 0000000000..452f28800d --- /dev/null +++ b/src/frontend/src/components/PopUpAlert.tsx @@ -0,0 +1,49 @@ +import { Box } from '@mui/material'; +import React, { useEffect, useState } from 'react'; +import { PopUp } from 'shared'; +import NotificationCard from './NotificationCard'; +import { useHistory } from 'react-router-dom'; +import { useCurrentUserPopUps, useRemoveUserPopUp } from '../hooks/pop-ups.hooks'; + +const PopUpAlert: React.FC = () => { + const { data: popUps, isLoading: popUpsIsLoading } = useCurrentUserPopUps(); + const { mutateAsync: removePopUp, isLoading: removeIsLoading } = useRemoveUserPopUp(); + const [currentPopUp, setCurrentPopUp] = useState(); + const history = useHistory(); + + useEffect(() => { + if (popUps && popUps.length > 0) { + setCurrentPopUp(popUps[0]); + } + }, [popUps]); + + const removePopUpWrapper = async (popUp: PopUp) => { + setCurrentPopUp(undefined); + await removePopUp(popUp); + }; + + const onClick = async (popUp: PopUp) => { + if (!!popUp.eventLink) { + await removePopUpWrapper(popUp); + history.push(popUp.eventLink); + } + }; + + return ( + + {!removeIsLoading && !popUpsIsLoading && currentPopUp && ( + + )} + + ); +}; + +export default PopUpAlert; diff --git a/src/frontend/src/hooks/notifications.hooks.ts b/src/frontend/src/hooks/notifications.hooks.ts deleted file mode 100644 index 5ca9ae621b..0000000000 --- a/src/frontend/src/hooks/notifications.hooks.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useMutation, useQuery, useQueryClient } from 'react-query'; -import { Notification } from 'shared'; -import { getNotifications, removeNotification } from '../apis/notifications.api'; - -/** - * Curstom react hook to get all unread notifications from a user - * @param userId id of user to get unread notifications from - * @returns - */ -export const useCurrentUserNotifications = () => { - return useQuery(['notifications', 'current-user'], async () => { - const { data } = await getNotifications(); - return data; - }); -}; - -/** - * Curstom react hook to remove a notification from a user's unread notifications - * @param userId id of user to get unread notifications from - * @returns - */ -export const useRemoveUserNotification = () => { - const queryClient = useQueryClient(); - return useMutation( - ['notifications', 'current-user', 'remove'], - async (notification: Notification) => { - const { data } = await removeNotification(notification.notificationId); - return data; - }, - { - onSuccess: () => { - queryClient.invalidateQueries(['notifications', 'current-user']); - } - } - ); -}; diff --git a/src/frontend/src/hooks/pop-ups.hooks.ts b/src/frontend/src/hooks/pop-ups.hooks.ts new file mode 100644 index 0000000000..7816102dd6 --- /dev/null +++ b/src/frontend/src/hooks/pop-ups.hooks.ts @@ -0,0 +1,36 @@ +import { useMutation, useQuery, useQueryClient } from 'react-query'; +import { PopUp } from 'shared'; +import { getPopUps, removePopUps } from '../apis/pop-ups.api'; + +/** + * Curstom react hook to get all unread notifications from a user + * @param userId id of user to get unread notifications from + * @returns + */ +export const useCurrentUserPopUps = () => { + return useQuery(['pop-ups', 'current-user'], async () => { + const { data } = await getPopUps(); + return data; + }); +}; + +/** + * Curstom react hook to remove a notification from a user's unread notifications + * @param userId id of user to get unread notifications from + * @returns + */ +export const useRemoveUserPopUp = () => { + const queryClient = useQueryClient(); + return useMutation( + ['pop-ups', 'current-user', 'remove'], + async (popUp: PopUp) => { + const { data } = await removePopUps(popUp.popUpId); + return data; + }, + { + onSuccess: () => { + queryClient.invalidateQueries(['pop-ups', 'current-user']); + } + } + ); +}; diff --git a/src/frontend/src/pages/HomePage/Home.tsx b/src/frontend/src/pages/HomePage/Home.tsx index f76ecc0e4c..1ce8f7c7f6 100644 --- a/src/frontend/src/pages/HomePage/Home.tsx +++ b/src/frontend/src/pages/HomePage/Home.tsx @@ -11,14 +11,14 @@ import { useState } from 'react'; import MemberHomePage from './MemberHomePage'; import LeadHomePage from './LeadHomePage'; import AdminHomePage from './AdminHomePage'; -import NotificationAlert from '../../components/NotificationAlert'; +import PopUpAlert from '../../components/PopUpAlert'; const Home = () => { const user = useCurrentUser(); const [onMemberHomePage, setOnMemberHomePage] = useState(false); return ( <> - {!onMemberHomePage && } + {!onMemberHomePage && } {isGuest(user.role) && !onMemberHomePage ? ( ) : isMember(user.role) ? ( diff --git a/src/frontend/src/utils/urls.ts b/src/frontend/src/utils/urls.ts index a924917909..15a25d3026 100644 --- a/src/frontend/src/utils/urls.ts +++ b/src/frontend/src/utils/urls.ts @@ -197,10 +197,10 @@ const faqCreate = () => `${recruitment()}/faq/create`; const faqEdit = (id: string) => `${recruitment()}/faq/${id}/edit`; const faqDelete = (id: string) => `${recruitment()}/faq/${id}/delete`; -/************** Notification Endpoints ***************/ -const notifications = () => `${API_URL}/notifications`; -const notificationsCurrentUser = () => `${notifications()}/current-user`; -const notificationsRemove = (id: string) => `${notifications()}/${id}/remove`; +/************** Pop Up Endpoints ***************/ +const popUps = () => `${API_URL}/pop-ups`; +const popUpsCurrentUser = () => `${popUps()}/current-user`; +const popUpsRemove = (id: string) => `${popUps()}/${id}/remove`; /************** Onboarding Endpoints ***************/ const onboarding = () => `${API_URL}/onboarding`; @@ -367,9 +367,9 @@ export const apiUrls = { faqDelete, imageById, - notifications, - notificationsCurrentUser, - notificationsRemove, + popUps, + popUpsCurrentUser, + popUpsRemove, version }; diff --git a/src/shared/index.ts b/src/shared/index.ts index 40246d0fe4..93e9c8899b 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -11,7 +11,7 @@ export * from './src/types/team-types'; 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/pop-up-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 index c0e2d615a7..ac31f72062 100644 --- a/src/shared/src/types/announcements.types.ts +++ b/src/shared/src/types/announcements.types.ts @@ -5,7 +5,7 @@ export interface Announcement { text: string; usersReceived: User[]; senderName: string; - dateCreated: Date; + dateMessageSent: Date; slackEventId: string; slackChannelName: string; dateDeleted?: Date; diff --git a/src/shared/src/types/notifications.types.ts b/src/shared/src/types/pop-up-types.ts similarity index 50% rename from src/shared/src/types/notifications.types.ts rename to src/shared/src/types/pop-up-types.ts index abd16fcd21..2b517a3900 100644 --- a/src/shared/src/types/notifications.types.ts +++ b/src/shared/src/types/pop-up-types.ts @@ -1,5 +1,5 @@ -export interface Notification { - notificationId: string; +export interface PopUp { + popUpId: string; text: string; iconName: string; eventLink?: string;