From ada3bf49ac2df195a1bc85a841c222e5d40c139b Mon Sep 17 00:00:00 2001 From: Jesse Huang <87463074+jeessh@users.noreply.github.com> Date: Thu, 31 Oct 2024 21:40:44 -0400 Subject: [PATCH 01/10] moved user fields to staff (and deleted some resident fields) --- backend/prisma/schema.prisma | 58 ++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index f1c85c7e..0bd6e03c 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -15,15 +15,11 @@ enum UserType { } model User { - id Int @id @default(autoincrement()) - type UserType - staff Staff? - resident Resident? - authId String @unique @map("auth_id") - email String @unique - phoneNumber String? @unique @map("phone_number") - firstName String @map("first_name") - lastName String @map("last_name") + id Int @id @default(autoincrement()) + type UserType + staff Staff? + resident Resident? + authId String @unique @map("auth_id") displayName String? @map("display_name") profilePictureURL String? @map("profile_picture_url") isActive Boolean @default(true) @map("is_active") @@ -36,6 +32,10 @@ model User { model Staff { user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) userId Int @id @map("user_id") + email String @unique + phoneNumber String? @unique @map("phone_number") + firstName String @map("first_name") + lastName String @map("last_name") isAdmin Boolean @default(false) @map("is_admin") tasksAssigned TaskAssigned[] warningsAssigned Warning[] @@ -47,12 +47,12 @@ model Resident { user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) userId Int @id @map("user_id") residentId Int @unique @map("resident_id") // Differs from user id, this id is assigned by the staff - birthDate DateTime @map("birth_date") @db.Date + // birthDate DateTime @map("birth_date") @db.Date roomNumber Int @map("room_number") credits Float @default(0) dateJoined DateTime @default(now()) @map("date_joined") @db.Date dateLeft DateTime? @map("date_left") @db.Date - notes String? + // notes String? tasks TaskAssigned[] warnings Warning[] @@ -83,19 +83,19 @@ enum RecurrenceFrequency { } model Task { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) type TaskType title String description String - creditValue Float @map("credit_value") - location TaskLocation @relation(fields: [locationId], references: [id], onDelete: Cascade, onUpdate: Cascade) - locationId Int @map("location_id") + creditValue Float @map("credit_value") + location TaskLocation @relation(fields: [locationId], references: [id], onDelete: Cascade, onUpdate: Cascade) + locationId Int @map("location_id") tasksAssigned TaskAssigned[] relatedWarnings Warning[] - endDate DateTime? @map("end_date") - recurrenceFrequency RecurrenceFrequency @map("recurrence_frequency") - specificDay DaysOfWeek? @map("specific_day") // used for one time tasks - repeatDays DaysOfWeek[] @map("repeat_days") // used for repeating tasks + endDate DateTime? @map("end_date") + recurrenceFrequency RecurrenceFrequency @map("recurrence_frequency") + specificDay DaysOfWeek? @map("specific_day") // used for one time tasks + repeatDays DaysOfWeek[] @map("repeat_days") // used for repeating tasks @@map("tasks") } @@ -110,16 +110,16 @@ model TaskLocation { } model TaskAssigned { - id Int @id @default(autoincrement()) - task Task @relation(fields: [taskId], references: [id]) - taskId Int @map("task_id") - assigner Staff? @relation(fields: [assignerId], references: [userId], onDelete: SetNull, onUpdate: Cascade) - assignerId Int? @map("assigner_id") - assignee Resident @relation(fields: [assigneeId], references: [userId], onDelete: Cascade, onUpdate: Cascade) - assigneeId Int @map("assignee_id") - status Status - startDate DateTime @map("start_date") - comments String? + id Int @id @default(autoincrement()) + task Task @relation(fields: [taskId], references: [id]) + taskId Int @map("task_id") + assigner Staff? @relation(fields: [assignerId], references: [userId], onDelete: SetNull, onUpdate: Cascade) + assignerId Int? @map("assigner_id") + assignee Resident @relation(fields: [assigneeId], references: [userId], onDelete: Cascade, onUpdate: Cascade) + assigneeId Int @map("assignee_id") + status Status + startDate DateTime @map("start_date") + comments String? @@map("tasks_assigned") } From b00502199352b50fa27598e67c5155200594a1b8 Mon Sep 17 00:00:00 2001 From: Jesse Huang <87463074+jeessh@users.noreply.github.com> Date: Thu, 7 Nov 2024 21:46:00 -0500 Subject: [PATCH 02/10] updated staff, resident and user schema --- backend/graphql/types/residentType.ts | 18 ------ backend/prisma/schema.prisma | 2 +- .../implementations/residentService.ts | 55 +----------------- .../services/implementations/staffService.ts | 58 +++++++++---------- .../services/implementations/userService.ts | 3 - .../services/interfaces/residentService.ts | 6 -- backend/services/interfaces/staffService.ts | 12 ++++ backend/services/interfaces/userService.ts | 17 +----- .../Mutations/ResidentsMutations.ts | 18 ------ .../src/APIClients/Queries/TaskQueries.ts | 2 +- 10 files changed, 47 insertions(+), 144 deletions(-) diff --git a/backend/graphql/types/residentType.ts b/backend/graphql/types/residentType.ts index 83b68f05..bd19d6db 100644 --- a/backend/graphql/types/residentType.ts +++ b/backend/graphql/types/residentType.ts @@ -6,53 +6,35 @@ const residentType = gql` type ResidentDTO { userId: Int! residentId: Int! - email: String! - phoneNumber: String - firstName: String! - lastName: String! displayName: String profilePictureURL: String isActive: Boolean! - birthDate: Date! roomNumber: Int! credits: Float! dateJoined: Date! dateLeft: Date - notes: String } input CreateResidentDTO { - email: String! password: String! - phoneNumber: String - firstName: String! - lastName: String! displayName: String profilePictureURL: String residentId: Int! - birthDate: Date! roomNumber: Int! credits: Float dateJoined: Date dateLeft: Date - notes: String } input UpdateResidentDTO { - email: String password: String - phoneNumber: String - firstName: String - lastName: String displayName: String profilePictureURL: String residentId: Int - birthDate: Date roomNumber: Int credits: Float dateJoined: Date dateLeft: Date - notes: String } enum RedeemCreditResponse { diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 0bd6e03c..51f1ba63 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -32,7 +32,7 @@ model User { model Staff { user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) userId Int @id @map("user_id") - email String @unique + email String @unique @map("email") phoneNumber String? @unique @map("phone_number") firstName String @map("first_name") lastName String @map("last_name") diff --git a/backend/services/implementations/residentService.ts b/backend/services/implementations/residentService.ts index c74c33d4..2af2c081 100644 --- a/backend/services/implementations/residentService.ts +++ b/backend/services/implementations/residentService.ts @@ -16,7 +16,6 @@ class ResidentService implements IResidentService { async addResident(resident: CreateResidentDTO): Promise { try { const firebaseUser = await firebaseAdmin.auth().createUser({ - email: resident.email, password: resident.password, }); @@ -24,20 +23,14 @@ class ResidentService implements IResidentService { const newResident = await prisma.resident.create({ data: { residentId: resident.residentId, - birthDate: resident.birthDate, roomNumber: resident.roomNumber, credits: resident.credits, dateJoined: resident.dateJoined, dateLeft: resident.dateLeft, - notes: resident.notes, user: { create: { authId: firebaseUser.uid, type: UserType.RESIDENT, - email: resident.email, - phoneNumber: resident.phoneNumber, - firstName: resident.firstName, - lastName: resident.lastName, displayName: resident.displayName, profilePictureURL: resident.profilePictureURL, isActive: true, @@ -50,16 +43,10 @@ class ResidentService implements IResidentService { return { userId: newResident.userId, residentId: newResident.residentId, - birthDate: newResident.birthDate, roomNumber: newResident.roomNumber, credits: newResident.credits, dateJoined: newResident.dateJoined, dateLeft: newResident.dateLeft, - notes: newResident.notes, - email: newResident.user.email, - phoneNumber: newResident.user.phoneNumber, - firstName: newResident.user.firstName, - lastName: newResident.user.lastName, displayName: newResident.user.displayName, profilePictureURL: newResident.user.profilePictureURL, isActive: newResident.user.isActive, @@ -105,34 +92,28 @@ class ResidentService implements IResidentService { } const { authId } = oldUser; - const email = "email" in resident ? resident.email : oldUser.email; + // const email = "email" in resident ? resident.email : oldUser.email; if ("password" in resident) { await firebaseAdmin.auth().updateUser(authId, { - email, + // email, password: resident.password, }); } else { - await firebaseAdmin.auth().updateUser(authId, { email }); + await firebaseAdmin.auth().updateUser(authId, {}); } const updatedResident = await prisma.resident.update({ where: { userId }, data: { residentId: resident.residentId || undefined, - birthDate: resident.birthDate || undefined, roomNumber: resident.roomNumber || undefined, credits: resident.credits || undefined, dateJoined: resident.dateJoined || undefined, dateLeft: resident.dateLeft || undefined, - notes: resident.notes || undefined, user: { update: { data: { - email: resident.email || undefined, - phoneNumber: resident.phoneNumber || undefined, - firstName: resident.firstName || undefined, - lastName: resident.lastName || undefined, displayName: resident.displayName || undefined, profilePictureURL: resident.profilePictureURL || undefined, isActive: resident.isActive || undefined, @@ -146,16 +127,10 @@ class ResidentService implements IResidentService { return { userId: updatedResident.userId, residentId: updatedResident.residentId, - birthDate: updatedResident.birthDate, roomNumber: updatedResident.roomNumber, credits: updatedResident.credits, dateJoined: updatedResident.dateJoined, dateLeft: updatedResident.dateLeft, - notes: updatedResident.notes, - email: updatedResident.user.email, - phoneNumber: updatedResident.user.phoneNumber, - firstName: updatedResident.user.firstName, - lastName: updatedResident.user.lastName, displayName: updatedResident.user.displayName, profilePictureURL: updatedResident.user.profilePictureURL, isActive: updatedResident.user.isActive, @@ -198,16 +173,10 @@ class ResidentService implements IResidentService { return { userId: deletedResident.userId, residentId: deletedResident.residentId, - birthDate: deletedResident.birthDate, roomNumber: deletedResident.roomNumber, credits: deletedResident.credits, dateJoined: deletedResident.dateJoined, dateLeft: deletedResident.dateLeft, - notes: deletedResident.notes, - email: deletedResident.user.email, - phoneNumber: deletedResident.user.phoneNumber, - firstName: deletedResident.user.firstName, - lastName: deletedResident.user.lastName, displayName: deletedResident.user.displayName, profilePictureURL: deletedResident.user.profilePictureURL, isActive: deletedResident.user.isActive, @@ -231,16 +200,10 @@ class ResidentService implements IResidentService { return { userId: resident.userId, residentId: resident.residentId, - birthDate: resident.birthDate, roomNumber: resident.roomNumber, credits: resident.credits, dateJoined: resident.dateJoined, dateLeft: resident.dateLeft, - notes: resident.notes, - email: resident.user.email, - phoneNumber: resident.user.phoneNumber, - firstName: resident.user.firstName, - lastName: resident.user.lastName, displayName: resident.user.displayName, profilePictureURL: resident.user.profilePictureURL, isActive: resident.user.isActive, @@ -264,16 +227,10 @@ class ResidentService implements IResidentService { return { userId: resident.userId, residentId: resident.residentId, - birthDate: resident.birthDate, roomNumber: resident.roomNumber, credits: resident.credits, dateJoined: resident.dateJoined, dateLeft: resident.dateLeft, - notes: resident.notes, - email: resident.user.email, - phoneNumber: resident.user.phoneNumber, - firstName: resident.user.firstName, - lastName: resident.user.lastName, displayName: resident.user.displayName, profilePictureURL: resident.user.profilePictureURL, isActive: resident.user.isActive, @@ -304,16 +261,10 @@ class ResidentService implements IResidentService { return { userId: resident.userId, residentId: resident.residentId, - birthDate: resident.birthDate, roomNumber: resident.roomNumber, credits: resident.credits, dateJoined: resident.dateJoined, dateLeft: resident.dateLeft, - notes: resident.notes, - email: resident.user.email, - phoneNumber: resident.user.phoneNumber, - firstName: resident.user.firstName, - lastName: resident.user.lastName, displayName: resident.user.displayName, profilePictureURL: resident.user.profilePictureURL, isActive: resident.user.isActive, diff --git a/backend/services/implementations/staffService.ts b/backend/services/implementations/staffService.ts index 8934402a..9bec479a 100644 --- a/backend/services/implementations/staffService.ts +++ b/backend/services/implementations/staffService.ts @@ -23,14 +23,14 @@ class StaffService implements IStaffService { const newStaff = await prisma.staff.create({ data: { isAdmin: staff.isAdmin, + email: staff.email, + firstName: staff.firstName, + lastName: staff.lastName, + phoneNumber: staff.phoneNumber, user: { create: { authId: firebaseUser.uid, type: UserType.STAFF, - email: staff.email, - phoneNumber: staff.phoneNumber, - firstName: staff.firstName, - lastName: staff.lastName, displayName: staff.displayName, profilePictureURL: staff.profilePictureURL, }, @@ -43,9 +43,9 @@ class StaffService implements IStaffService { userId: newStaff.userId, isAdmin: newStaff.isAdmin, email: firebaseUser.email ?? "", - phoneNumber: newStaff.user.phoneNumber, - firstName: newStaff.user.firstName, - lastName: newStaff.user.lastName, + phoneNumber: newStaff.phoneNumber, + firstName: newStaff.firstName, + lastName: newStaff.lastName, displayName: newStaff.user.displayName, profilePictureURL: newStaff.user.profilePictureURL, isActive: newStaff.user.isActive, @@ -83,8 +83,8 @@ class StaffService implements IStaffService { } const { authId } = originalUser; - const email = "email" in staff ? staff.email : originalUser.email; - + // const email = "email" in staff ? staff.email : originalUser.email; + const { email } = staff; if ("password" in staff) { await firebaseAdmin.auth().updateUser(authId, { email, @@ -98,13 +98,13 @@ class StaffService implements IStaffService { where: { userId }, data: { isAdmin: staff.isAdmin, + email: staff.email, + phoneNumber: staff.phoneNumber, + firstName: staff.firstName, + lastName: staff.lastName, user: { update: { data: { - email: staff.email, - phoneNumber: staff.phoneNumber, - firstName: staff.firstName, - lastName: staff.lastName, displayName: staff.displayName, profilePictureURL: staff.profilePictureURL, isActive: staff.isActive, @@ -120,10 +120,10 @@ class StaffService implements IStaffService { return { userId: updatedStaff.userId, isAdmin: updatedStaff.isAdmin, - email: updatedStaff.user.email, - phoneNumber: updatedStaff.user.phoneNumber, - firstName: updatedStaff.user.firstName, - lastName: updatedStaff.user.lastName, + email: updatedStaff.email, + phoneNumber: updatedStaff.phoneNumber, + firstName: updatedStaff.firstName, + lastName: updatedStaff.lastName, displayName: updatedStaff.user.displayName, profilePictureURL: updatedStaff.user.profilePictureURL, isActive: updatedStaff.user.isActive, @@ -162,10 +162,10 @@ class StaffService implements IStaffService { return { userId: deletedStaff.userId, isAdmin: deletedStaff.isAdmin, - email: deletedUser.email, - phoneNumber: deletedUser.phoneNumber, - firstName: deletedUser.firstName, - lastName: deletedUser.lastName, + email: deletedStaff.email, + phoneNumber: deletedStaff.phoneNumber, + firstName: deletedStaff.firstName, + lastName: deletedStaff.lastName, displayName: deletedUser.displayName, profilePictureURL: deletedUser.profilePictureURL, isActive: deletedUser.isActive, @@ -190,10 +190,10 @@ class StaffService implements IStaffService { return { userId: staff.userId, isAdmin: staff.isAdmin, - email: staff.user.email, - phoneNumber: staff.user.phoneNumber, - firstName: staff.user.firstName, - lastName: staff.user.lastName, + email: staff.email, + phoneNumber: staff.phoneNumber, + firstName: staff.firstName, + lastName: staff.lastName, displayName: staff.user.displayName, profilePictureURL: staff.user.profilePictureURL, isActive: staff.user.isActive, @@ -216,10 +216,10 @@ class StaffService implements IStaffService { return { userId: staff.userId, isAdmin: staff.isAdmin, - email: staff.user.email, - phoneNumber: staff.user.phoneNumber, - firstName: staff.user.firstName, - lastName: staff.user.lastName, + email: staff.email, + phoneNumber: staff.phoneNumber, + firstName: staff.firstName, + lastName: staff.lastName, displayName: staff.user.displayName, profilePictureURL: staff.user.profilePictureURL, isActive: staff.user.isActive, diff --git a/backend/services/implementations/userService.ts b/backend/services/implementations/userService.ts index 012d78c2..fe4a6cb9 100644 --- a/backend/services/implementations/userService.ts +++ b/backend/services/implementations/userService.ts @@ -20,9 +20,6 @@ class UserService implements IUserService { return { id: user.id, type: user.type, - email: firebaseUser.email ?? "", - firstName: user.firstName, - lastName: user.lastName, }; } catch (error: unknown) { Logger.error(`Failed to get user. Reason = ${getErrorMessage(error)}`); diff --git a/backend/services/interfaces/residentService.ts b/backend/services/interfaces/residentService.ts index bb33881a..4b22841e 100644 --- a/backend/services/interfaces/residentService.ts +++ b/backend/services/interfaces/residentService.ts @@ -3,32 +3,26 @@ import { UserDTO, CreateUserDTO, UpdateUserDTO } from "./userService"; export interface ResidentDTO extends Omit { userId: number; residentId: number; - birthDate: Date; roomNumber: number; credits: number; dateJoined: Date; dateLeft: Date | null; - notes: string | null; } export interface CreateResidentDTO extends CreateUserDTO { residentId: number; - birthDate: Date; roomNumber: number; credits?: number; dateJoined?: Date; dateLeft?: Date; - notes?: string; } export interface UpdateResidentDTO extends UpdateUserDTO { residentId?: number; - birthDate?: Date; roomNumber?: number; credits?: number; dateJoined?: Date; dateLeft?: Date; - notes?: string; } // Have to manually map enums as ts treats enums as numbers diff --git a/backend/services/interfaces/staffService.ts b/backend/services/interfaces/staffService.ts index a11b95f1..1624cd7e 100644 --- a/backend/services/interfaces/staffService.ts +++ b/backend/services/interfaces/staffService.ts @@ -2,15 +2,27 @@ import { UserDTO, CreateUserDTO, UpdateUserDTO } from "./userService"; export interface StaffDTO extends Omit { userId: number; + email: string; + phoneNumber: string | null; + firstName: string; + lastName: string; isAdmin: boolean; } export interface CreateStaffDTO extends CreateUserDTO { isAdmin: boolean; + email: string; + phoneNumber: string | null; + firstName: string; + lastName: string; } export interface UpdateStaffDTO extends UpdateUserDTO { isAdmin?: boolean; + email: string; + phoneNumber: string | null; + firstName: string; + lastName: string; } interface IStaffService { diff --git a/backend/services/interfaces/userService.ts b/backend/services/interfaces/userService.ts index 606c9520..9c6157bb 100644 --- a/backend/services/interfaces/userService.ts +++ b/backend/services/interfaces/userService.ts @@ -3,36 +3,21 @@ import { UserType } from "../../prisma"; export type UserDTO = { id: number; type: UserType; - email: string; - phoneNumber: string | null; - firstName: string; - lastName: string; displayName: string | null; profilePictureURL: string | null; isActive: boolean; }; -export type SimplifiedUserDTO = Pick< - UserDTO, - "id" | "type" | "email" | "firstName" | "lastName" ->; +export type SimplifiedUserDTO = Pick; export interface CreateUserDTO { - email: string; password: string; - phoneNumber?: string; - firstName: string; - lastName: string; displayName?: string; profilePictureURL?: string; } export interface UpdateUserDTO { - email?: string; password?: string; - phoneNumber?: string; - firstName?: string; - lastName?: string; displayName?: string; profilePictureURL?: string; isActive?: boolean; diff --git a/frontend/src/APIClients/Mutations/ResidentsMutations.ts b/frontend/src/APIClients/Mutations/ResidentsMutations.ts index 330cc81b..7a99695e 100644 --- a/frontend/src/APIClients/Mutations/ResidentsMutations.ts +++ b/frontend/src/APIClients/Mutations/ResidentsMutations.ts @@ -5,19 +5,13 @@ export const ADD_RESIDENT = gql` addResident(resident: $resident) { userId residentId - email - phoneNumber - firstName - lastName displayName profilePictureURL isActive - birthDate roomNumber credits dateJoined dateLeft - notes } } `; @@ -27,19 +21,13 @@ export const UPDATE_RESIDENT = gql` updateResident(userId: $userId, resident: $resident) { userId residentId - email - phoneNumber - firstName - lastName displayName profilePictureURL isActive - birthDate roomNumber credits dateJoined dateLeft - notes } } `; @@ -49,19 +37,13 @@ export const DELETE_RESIDENT = gql` deleteResident(userId: $userId) { userId residentId - email - phoneNumber - firstName - lastName displayName profilePictureURL isActive - birthDate roomNumber credits dateJoined dateLeft - notes } } `; diff --git a/frontend/src/APIClients/Queries/TaskQueries.ts b/frontend/src/APIClients/Queries/TaskQueries.ts index 7607d5c1..d2057359 100644 --- a/frontend/src/APIClients/Queries/TaskQueries.ts +++ b/frontend/src/APIClients/Queries/TaskQueries.ts @@ -17,7 +17,7 @@ export const GET_TASK_BY_ID = gql` `; export const GET_TASKS_BY_TYPE = gql` - query getTaskById($type: TaskType!) { + query getTasksByType($type: TaskType!) { getTasksByType(type: $type) { id type From d49c10428d045d8f296cc7c7a30ea6ccd470b5a6 Mon Sep 17 00:00:00 2001 From: Jesse Huang <87463074+jeessh@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:06:48 -0500 Subject: [PATCH 03/10] Updated schema and initial notifications --- .../resolvers/notificationResolvers.ts | 26 ++++-- backend/graphql/types/notificationType.ts | 15 +++- backend/prisma/schema.prisma | 82 ++++++++++--------- .../implementations/notificationService.ts | 63 ++++++++++---- .../interfaces/notificationService.ts | 27 +++++- 5 files changed, 149 insertions(+), 64 deletions(-) diff --git a/backend/graphql/resolvers/notificationResolvers.ts b/backend/graphql/resolvers/notificationResolvers.ts index 4c243d67..71835098 100644 --- a/backend/graphql/resolvers/notificationResolvers.ts +++ b/backend/graphql/resolvers/notificationResolvers.ts @@ -2,6 +2,7 @@ import NotificationService from "../../services/implementations/notificationServ import INotificationService, { NotificationDTO, NotificationReceivedDTO, + UpdateNotificationDTO, } from "../../services/interfaces/notificationService"; import IResidentService from "../../services/interfaces/residentService"; import ResidentService from "../../services/implementations/residentService"; @@ -13,11 +14,11 @@ const notificationService: INotificationService = new NotificationService( const notificationResolvers = { Query: { - getNotificationsByUserId: async ( + getNotificationsByRoomIds: async ( _parent: undefined, - { userId }: { userId: string }, + { roomIds }: { roomIds: string[] }, ): Promise => { - return notificationService.getNotificationsByUserId(Number(userId)); + return notificationService.getNotificationsByRoomIds(roomIds.map(Number)); }, getNotificationById: async ( _parent: undefined, @@ -33,15 +34,15 @@ const notificationResolvers = { authorId, title, message, - recipientIds, + roomIds, }: { authorId: number; title: string; message: string; - recipientIds: number[]; + roomIds: number[]; }, ): Promise => { - const ids = recipientIds.map((id) => Number(id)); + const ids = roomIds.map((id) => Number(id)); const newNotification = await notificationService.sendNotification( Number(authorId), title, @@ -68,6 +69,19 @@ const notificationResolvers = { ); return updatedNotification; }, + updateNotification: async ( + _parent: undefined, + { + notificationId, + notification, + }: { notificationId: number; notification: UpdateNotificationDTO }, + ): Promise => { + const updatedNotification = await notificationService.updateNotificationById( + Number(notificationId), + notification, + ); + return updatedNotification; + }, sendAnnouncement: async ( _parent: undefined, { diff --git a/backend/graphql/types/notificationType.ts b/backend/graphql/types/notificationType.ts index 212cc70a..5efba3fe 100644 --- a/backend/graphql/types/notificationType.ts +++ b/backend/graphql/types/notificationType.ts @@ -17,8 +17,15 @@ const notificationType = gql` seen: Boolean! } + input UpdateNotificationDTO { + authorId: ID + title: String + message: String + createdAt: DateTime + } + extend type Query { - getNotificationsByUserId(userId: ID!): [NotificationReceivedDTO!] + getNotificationsByRoomIds(roomIds: [Int!]): [NotificationReceivedDTO!] getNotificationById(id: ID!): NotificationReceivedDTO! } @@ -27,10 +34,14 @@ const notificationType = gql` authorId: ID! title: String! message: String! - recipientIds: [ID!] + roomIds: [Int!] ): NotificationDTO! deleteUserNotification(notificationId: ID!): NotificationDTO! updateSeenNotification(notificationId: ID!): NotificationReceivedDTO! + updateNotification( + notificationId: ID! + notification: UpdateNotificationDTO! + ): NotificationDTO! sendAnnouncement( title: String message: String diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 51f1ba63..d6bc12dc 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -15,46 +15,45 @@ enum UserType { } model User { - id Int @id @default(autoincrement()) - type UserType - staff Staff? - resident Resident? - authId String @unique @map("auth_id") - displayName String? @map("display_name") - profilePictureURL String? @map("profile_picture_url") - isActive Boolean @default(true) @map("is_active") - notificationsSent Notification[] - notificationsReceived NotificationReceived[] + id Int @id @default(autoincrement()) + type UserType + staff Staff? + resident Resident? + authId String @unique @map("auth_id") + displayName String? @map("display_name") + profilePictureURL String? @map("profile_picture_url") + isActive Boolean @default(true) @map("is_active") @@map("users") } model Staff { - user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) - userId Int @id @map("user_id") - email String @unique @map("email") - phoneNumber String? @unique @map("phone_number") - firstName String @map("first_name") - lastName String @map("last_name") - isAdmin Boolean @default(false) @map("is_admin") - tasksAssigned TaskAssigned[] - warningsAssigned Warning[] + user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) + userId Int @id @map("user_id") + email String @unique @map("email") + phoneNumber String? @unique @map("phone_number") + firstName String @map("first_name") + lastName String @map("last_name") + isAdmin Boolean @default(false) @map("is_admin") + tasksAssigned TaskAssigned[] + warningsAssigned Warning[] + NotificationsSent Notification[] @@map("staff") } model Resident { - user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) - userId Int @id @map("user_id") - residentId Int @unique @map("resident_id") // Differs from user id, this id is assigned by the staff - // birthDate DateTime @map("birth_date") @db.Date - roomNumber Int @map("room_number") - credits Float @default(0) - dateJoined DateTime @default(now()) @map("date_joined") @db.Date - dateLeft DateTime? @map("date_left") @db.Date - // notes String? - tasks TaskAssigned[] - warnings Warning[] + user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) + userId Int @id @map("user_id") + residentId Int @unique @map("resident_id") // Differs from user id, this id is assigned by the staff + roomNumber Int @map("room_number") + credits Float @default(0) + dateJoined DateTime @default(now()) @map("date_joined") @db.Date + dateLeft DateTime? @map("date_left") @db.Date + tasks TaskAssigned[] + warnings Warning[] + notificationGroup NotificationGroup[] + notificationRecieved NotificationReceived[] @@map("residents") } @@ -147,14 +146,21 @@ model Warning { @@map("warnings") } +model NotificationGroup { + id Int @id @default(autoincrement()) + recipients Resident[] + notifications Notification[] +} + model Notification { - id Int @id @default(autoincrement()) - author User? @relation(fields: [authorId], references: [id], onDelete: SetNull, onUpdate: Cascade) - authorId Int? @map("author_id") - title String - message String - createdAt DateTime @default(now()) @map("created_at") @db.Date - recipients NotificationReceived[] + id Int @id @default(autoincrement()) + message String + createdAt DateTime @default(now()) @map("created_at") @db.Date + author Staff? @relation(fields: [authorId], references: [userId], onDelete: SetNull, onUpdate: Cascade) + authorId Int? @map("author_id") + group NotificationGroup @relation(fields: [groupId], references: [id]) + groupId Int @map("group_id") + notificationReceived NotificationReceived[] @@map("notifications") } @@ -163,7 +169,7 @@ model NotificationReceived { id Int @id @default(autoincrement()) notification Notification @relation(fields: [notificationId], references: [id], onDelete: Cascade, onUpdate: Cascade) notificationId Int @map("notification_id") - recipient User @relation(fields: [recipientId], references: [id], onDelete: Cascade, onUpdate: Cascade) + recipient Resident @relation(fields: [recipientId], references: [userId], onDelete: Cascade, onUpdate: Cascade) recipientId Int @map("recipient_id") seen Boolean @default(false) diff --git a/backend/services/implementations/notificationService.ts b/backend/services/implementations/notificationService.ts index b9d6ce77..53326088 100644 --- a/backend/services/implementations/notificationService.ts +++ b/backend/services/implementations/notificationService.ts @@ -2,6 +2,7 @@ import prisma from "../../prisma"; import INotificationService, { NotificationDTO, NotificationReceivedDTO, + UpdateNotificationDTO, } from "../interfaces/notificationService"; import IResidentService from "../interfaces/residentService"; import logger from "../../utilities/logger"; @@ -16,20 +17,20 @@ class NotificationService implements INotificationService { this.residentService = residentService; } - async getNotificationsByUserId( - id: number, + async getNotificationsByRoomIds( + roomIds: number[], ): Promise { try { - const user = await prisma.user.findUnique({ - where: { - id, - }, - include: { - notificationsReceived: true, - }, + const residents = await prisma.resident.findMany({ + where: { roomNumber: { in: roomIds } }, }); - if (!user) throw new Error(`No User found.`); - return user.notificationsReceived; + const residentIds = residents.map((resident) => resident.userId); + + const notificationReceived = await prisma.notificationReceived.findMany({ + where: { recipientId: { in: residentIds } }, + }); + if (!notificationReceived) throw new Error(`No User found.`); + return notificationReceived; } catch (error) { Logger.error( `Failed to get Notification. Reason = ${getErrorMessage(error)}`, @@ -60,9 +61,14 @@ class NotificationService implements INotificationService { authorId: number, title: string, message: string, - recipientIds: number[], + roomIds: number[], ): Promise { try { + const residents = await prisma.resident.findMany({ + where: { roomNumber: { in: roomIds } }, + }); + const residentIds = residents.map((resident) => resident.userId); + const newNotification = await prisma.notification.create({ data: { title, @@ -71,10 +77,10 @@ class NotificationService implements INotificationService { connect: { id: authorId }, }, recipients: { - create: recipientIds.map((recipient) => ({ + create: residentIds.map((resident) => ({ recipient: { connect: { - id: recipient, + id: resident, }, }, })), @@ -148,6 +154,35 @@ class NotificationService implements INotificationService { } } + async updateNotificationById( + notificationId: number, + notification: UpdateNotificationDTO, + ): Promise { + try { + const updatedNotification = await prisma.notification.update({ + where: { + id: notificationId, + }, + data: { + ...notification, + }, + include: { + recipients: true, + }, + }); + + if (!updatedNotification) + throw new Error(`notification id ${notificationId} not found`); + + return updatedNotification; + } catch (error) { + Logger.error( + `Failed to set seen flag. Reason = ${getErrorMessage(error)}`, + ); + throw error; + } + } + async sendAnnouncement( title: string, message: string, diff --git a/backend/services/interfaces/notificationService.ts b/backend/services/interfaces/notificationService.ts index a8588e14..c55f1313 100644 --- a/backend/services/interfaces/notificationService.ts +++ b/backend/services/interfaces/notificationService.ts @@ -7,6 +7,13 @@ export interface NotificationDTO { recipients: NotificationReceivedDTO[]; } +export interface UpdateNotificationDTO { + authorId?: number; + title?: string; + message?: string; + createdAt?: Date; +} + export interface NotificationReceivedDTO { id: number; notificationId: number; @@ -21,7 +28,9 @@ interface INotificationService { * @returns a NotificationDTO[] associated with that users notifications * @throws Error if retrieval fails */ - getNotificationsByUserId(id: number): Promise; + getNotificationsByRoomIds( + roomIds: number[], + ): Promise; /** * Get a notification by a defined id @@ -36,7 +45,7 @@ interface INotificationService { * @param authorId user id of author of notification * @param title title of notification * @param message message of notification - * @param recipientIds user ids of recipients of notification + * @param roomIds room ids of recipients of notification * @returns a NotificationDTO associated with the posted notifications * @throws Error if creation fails */ @@ -44,7 +53,7 @@ interface INotificationService { authorId: number, title: string, message: string, - recipientIds: number[], + roomIds: number[], ): Promise; /** @@ -58,7 +67,6 @@ interface INotificationService { /** * Update a user notification to be seen - * @param userId user id * @param notificationId notification id * @returns a NotificationDTO associated with the now seen Notification * @throws Error if retrieval fails @@ -67,6 +75,17 @@ interface INotificationService { notificationId: number, ): Promise; + /** + * Update a user notification to be seen + * @param notificationId notification id + * @returns a NotificationDTO associated with the updated Notification + * @throws Error if retrieval fails + */ + updateNotificationById( + notificationId: number, + notification: UpdateNotificationDTO, + ): Promise; + /** * Post an announcement notification to all active residents * @param title title of announcement From 8fa28bb684a147d57b50bf43a39be355ed8072e7 Mon Sep 17 00:00:00 2001 From: Jesse Huang <87463074+jeessh@users.noreply.github.com> Date: Thu, 31 Oct 2024 21:40:44 -0400 Subject: [PATCH 04/10] moved user fields to staff (and deleted some resident fields) --- backend/prisma/schema.prisma | 58 ++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index f1c85c7e..0bd6e03c 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -15,15 +15,11 @@ enum UserType { } model User { - id Int @id @default(autoincrement()) - type UserType - staff Staff? - resident Resident? - authId String @unique @map("auth_id") - email String @unique - phoneNumber String? @unique @map("phone_number") - firstName String @map("first_name") - lastName String @map("last_name") + id Int @id @default(autoincrement()) + type UserType + staff Staff? + resident Resident? + authId String @unique @map("auth_id") displayName String? @map("display_name") profilePictureURL String? @map("profile_picture_url") isActive Boolean @default(true) @map("is_active") @@ -36,6 +32,10 @@ model User { model Staff { user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) userId Int @id @map("user_id") + email String @unique + phoneNumber String? @unique @map("phone_number") + firstName String @map("first_name") + lastName String @map("last_name") isAdmin Boolean @default(false) @map("is_admin") tasksAssigned TaskAssigned[] warningsAssigned Warning[] @@ -47,12 +47,12 @@ model Resident { user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) userId Int @id @map("user_id") residentId Int @unique @map("resident_id") // Differs from user id, this id is assigned by the staff - birthDate DateTime @map("birth_date") @db.Date + // birthDate DateTime @map("birth_date") @db.Date roomNumber Int @map("room_number") credits Float @default(0) dateJoined DateTime @default(now()) @map("date_joined") @db.Date dateLeft DateTime? @map("date_left") @db.Date - notes String? + // notes String? tasks TaskAssigned[] warnings Warning[] @@ -83,19 +83,19 @@ enum RecurrenceFrequency { } model Task { - id Int @id @default(autoincrement()) + id Int @id @default(autoincrement()) type TaskType title String description String - creditValue Float @map("credit_value") - location TaskLocation @relation(fields: [locationId], references: [id], onDelete: Cascade, onUpdate: Cascade) - locationId Int @map("location_id") + creditValue Float @map("credit_value") + location TaskLocation @relation(fields: [locationId], references: [id], onDelete: Cascade, onUpdate: Cascade) + locationId Int @map("location_id") tasksAssigned TaskAssigned[] relatedWarnings Warning[] - endDate DateTime? @map("end_date") - recurrenceFrequency RecurrenceFrequency @map("recurrence_frequency") - specificDay DaysOfWeek? @map("specific_day") // used for one time tasks - repeatDays DaysOfWeek[] @map("repeat_days") // used for repeating tasks + endDate DateTime? @map("end_date") + recurrenceFrequency RecurrenceFrequency @map("recurrence_frequency") + specificDay DaysOfWeek? @map("specific_day") // used for one time tasks + repeatDays DaysOfWeek[] @map("repeat_days") // used for repeating tasks @@map("tasks") } @@ -110,16 +110,16 @@ model TaskLocation { } model TaskAssigned { - id Int @id @default(autoincrement()) - task Task @relation(fields: [taskId], references: [id]) - taskId Int @map("task_id") - assigner Staff? @relation(fields: [assignerId], references: [userId], onDelete: SetNull, onUpdate: Cascade) - assignerId Int? @map("assigner_id") - assignee Resident @relation(fields: [assigneeId], references: [userId], onDelete: Cascade, onUpdate: Cascade) - assigneeId Int @map("assignee_id") - status Status - startDate DateTime @map("start_date") - comments String? + id Int @id @default(autoincrement()) + task Task @relation(fields: [taskId], references: [id]) + taskId Int @map("task_id") + assigner Staff? @relation(fields: [assignerId], references: [userId], onDelete: SetNull, onUpdate: Cascade) + assignerId Int? @map("assigner_id") + assignee Resident @relation(fields: [assigneeId], references: [userId], onDelete: Cascade, onUpdate: Cascade) + assigneeId Int @map("assignee_id") + status Status + startDate DateTime @map("start_date") + comments String? @@map("tasks_assigned") } From 307efdf1efe308121cf4dfcf0e9306e74ee71384 Mon Sep 17 00:00:00 2001 From: Jesse Huang <87463074+jeessh@users.noreply.github.com> Date: Thu, 7 Nov 2024 21:46:00 -0500 Subject: [PATCH 05/10] updated staff, resident and user schema --- backend/graphql/types/residentType.ts | 18 ------ backend/prisma/schema.prisma | 2 +- .../implementations/residentService.ts | 55 +----------------- .../services/implementations/staffService.ts | 58 +++++++++---------- .../services/implementations/userService.ts | 3 - .../services/interfaces/residentService.ts | 6 -- backend/services/interfaces/staffService.ts | 12 ++++ backend/services/interfaces/userService.ts | 17 +----- .../Mutations/ResidentsMutations.ts | 18 ------ 9 files changed, 46 insertions(+), 143 deletions(-) diff --git a/backend/graphql/types/residentType.ts b/backend/graphql/types/residentType.ts index 83b68f05..bd19d6db 100644 --- a/backend/graphql/types/residentType.ts +++ b/backend/graphql/types/residentType.ts @@ -6,53 +6,35 @@ const residentType = gql` type ResidentDTO { userId: Int! residentId: Int! - email: String! - phoneNumber: String - firstName: String! - lastName: String! displayName: String profilePictureURL: String isActive: Boolean! - birthDate: Date! roomNumber: Int! credits: Float! dateJoined: Date! dateLeft: Date - notes: String } input CreateResidentDTO { - email: String! password: String! - phoneNumber: String - firstName: String! - lastName: String! displayName: String profilePictureURL: String residentId: Int! - birthDate: Date! roomNumber: Int! credits: Float dateJoined: Date dateLeft: Date - notes: String } input UpdateResidentDTO { - email: String password: String - phoneNumber: String - firstName: String - lastName: String displayName: String profilePictureURL: String residentId: Int - birthDate: Date roomNumber: Int credits: Float dateJoined: Date dateLeft: Date - notes: String } enum RedeemCreditResponse { diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 0bd6e03c..51f1ba63 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -32,7 +32,7 @@ model User { model Staff { user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) userId Int @id @map("user_id") - email String @unique + email String @unique @map("email") phoneNumber String? @unique @map("phone_number") firstName String @map("first_name") lastName String @map("last_name") diff --git a/backend/services/implementations/residentService.ts b/backend/services/implementations/residentService.ts index c74c33d4..2af2c081 100644 --- a/backend/services/implementations/residentService.ts +++ b/backend/services/implementations/residentService.ts @@ -16,7 +16,6 @@ class ResidentService implements IResidentService { async addResident(resident: CreateResidentDTO): Promise { try { const firebaseUser = await firebaseAdmin.auth().createUser({ - email: resident.email, password: resident.password, }); @@ -24,20 +23,14 @@ class ResidentService implements IResidentService { const newResident = await prisma.resident.create({ data: { residentId: resident.residentId, - birthDate: resident.birthDate, roomNumber: resident.roomNumber, credits: resident.credits, dateJoined: resident.dateJoined, dateLeft: resident.dateLeft, - notes: resident.notes, user: { create: { authId: firebaseUser.uid, type: UserType.RESIDENT, - email: resident.email, - phoneNumber: resident.phoneNumber, - firstName: resident.firstName, - lastName: resident.lastName, displayName: resident.displayName, profilePictureURL: resident.profilePictureURL, isActive: true, @@ -50,16 +43,10 @@ class ResidentService implements IResidentService { return { userId: newResident.userId, residentId: newResident.residentId, - birthDate: newResident.birthDate, roomNumber: newResident.roomNumber, credits: newResident.credits, dateJoined: newResident.dateJoined, dateLeft: newResident.dateLeft, - notes: newResident.notes, - email: newResident.user.email, - phoneNumber: newResident.user.phoneNumber, - firstName: newResident.user.firstName, - lastName: newResident.user.lastName, displayName: newResident.user.displayName, profilePictureURL: newResident.user.profilePictureURL, isActive: newResident.user.isActive, @@ -105,34 +92,28 @@ class ResidentService implements IResidentService { } const { authId } = oldUser; - const email = "email" in resident ? resident.email : oldUser.email; + // const email = "email" in resident ? resident.email : oldUser.email; if ("password" in resident) { await firebaseAdmin.auth().updateUser(authId, { - email, + // email, password: resident.password, }); } else { - await firebaseAdmin.auth().updateUser(authId, { email }); + await firebaseAdmin.auth().updateUser(authId, {}); } const updatedResident = await prisma.resident.update({ where: { userId }, data: { residentId: resident.residentId || undefined, - birthDate: resident.birthDate || undefined, roomNumber: resident.roomNumber || undefined, credits: resident.credits || undefined, dateJoined: resident.dateJoined || undefined, dateLeft: resident.dateLeft || undefined, - notes: resident.notes || undefined, user: { update: { data: { - email: resident.email || undefined, - phoneNumber: resident.phoneNumber || undefined, - firstName: resident.firstName || undefined, - lastName: resident.lastName || undefined, displayName: resident.displayName || undefined, profilePictureURL: resident.profilePictureURL || undefined, isActive: resident.isActive || undefined, @@ -146,16 +127,10 @@ class ResidentService implements IResidentService { return { userId: updatedResident.userId, residentId: updatedResident.residentId, - birthDate: updatedResident.birthDate, roomNumber: updatedResident.roomNumber, credits: updatedResident.credits, dateJoined: updatedResident.dateJoined, dateLeft: updatedResident.dateLeft, - notes: updatedResident.notes, - email: updatedResident.user.email, - phoneNumber: updatedResident.user.phoneNumber, - firstName: updatedResident.user.firstName, - lastName: updatedResident.user.lastName, displayName: updatedResident.user.displayName, profilePictureURL: updatedResident.user.profilePictureURL, isActive: updatedResident.user.isActive, @@ -198,16 +173,10 @@ class ResidentService implements IResidentService { return { userId: deletedResident.userId, residentId: deletedResident.residentId, - birthDate: deletedResident.birthDate, roomNumber: deletedResident.roomNumber, credits: deletedResident.credits, dateJoined: deletedResident.dateJoined, dateLeft: deletedResident.dateLeft, - notes: deletedResident.notes, - email: deletedResident.user.email, - phoneNumber: deletedResident.user.phoneNumber, - firstName: deletedResident.user.firstName, - lastName: deletedResident.user.lastName, displayName: deletedResident.user.displayName, profilePictureURL: deletedResident.user.profilePictureURL, isActive: deletedResident.user.isActive, @@ -231,16 +200,10 @@ class ResidentService implements IResidentService { return { userId: resident.userId, residentId: resident.residentId, - birthDate: resident.birthDate, roomNumber: resident.roomNumber, credits: resident.credits, dateJoined: resident.dateJoined, dateLeft: resident.dateLeft, - notes: resident.notes, - email: resident.user.email, - phoneNumber: resident.user.phoneNumber, - firstName: resident.user.firstName, - lastName: resident.user.lastName, displayName: resident.user.displayName, profilePictureURL: resident.user.profilePictureURL, isActive: resident.user.isActive, @@ -264,16 +227,10 @@ class ResidentService implements IResidentService { return { userId: resident.userId, residentId: resident.residentId, - birthDate: resident.birthDate, roomNumber: resident.roomNumber, credits: resident.credits, dateJoined: resident.dateJoined, dateLeft: resident.dateLeft, - notes: resident.notes, - email: resident.user.email, - phoneNumber: resident.user.phoneNumber, - firstName: resident.user.firstName, - lastName: resident.user.lastName, displayName: resident.user.displayName, profilePictureURL: resident.user.profilePictureURL, isActive: resident.user.isActive, @@ -304,16 +261,10 @@ class ResidentService implements IResidentService { return { userId: resident.userId, residentId: resident.residentId, - birthDate: resident.birthDate, roomNumber: resident.roomNumber, credits: resident.credits, dateJoined: resident.dateJoined, dateLeft: resident.dateLeft, - notes: resident.notes, - email: resident.user.email, - phoneNumber: resident.user.phoneNumber, - firstName: resident.user.firstName, - lastName: resident.user.lastName, displayName: resident.user.displayName, profilePictureURL: resident.user.profilePictureURL, isActive: resident.user.isActive, diff --git a/backend/services/implementations/staffService.ts b/backend/services/implementations/staffService.ts index 8934402a..9bec479a 100644 --- a/backend/services/implementations/staffService.ts +++ b/backend/services/implementations/staffService.ts @@ -23,14 +23,14 @@ class StaffService implements IStaffService { const newStaff = await prisma.staff.create({ data: { isAdmin: staff.isAdmin, + email: staff.email, + firstName: staff.firstName, + lastName: staff.lastName, + phoneNumber: staff.phoneNumber, user: { create: { authId: firebaseUser.uid, type: UserType.STAFF, - email: staff.email, - phoneNumber: staff.phoneNumber, - firstName: staff.firstName, - lastName: staff.lastName, displayName: staff.displayName, profilePictureURL: staff.profilePictureURL, }, @@ -43,9 +43,9 @@ class StaffService implements IStaffService { userId: newStaff.userId, isAdmin: newStaff.isAdmin, email: firebaseUser.email ?? "", - phoneNumber: newStaff.user.phoneNumber, - firstName: newStaff.user.firstName, - lastName: newStaff.user.lastName, + phoneNumber: newStaff.phoneNumber, + firstName: newStaff.firstName, + lastName: newStaff.lastName, displayName: newStaff.user.displayName, profilePictureURL: newStaff.user.profilePictureURL, isActive: newStaff.user.isActive, @@ -83,8 +83,8 @@ class StaffService implements IStaffService { } const { authId } = originalUser; - const email = "email" in staff ? staff.email : originalUser.email; - + // const email = "email" in staff ? staff.email : originalUser.email; + const { email } = staff; if ("password" in staff) { await firebaseAdmin.auth().updateUser(authId, { email, @@ -98,13 +98,13 @@ class StaffService implements IStaffService { where: { userId }, data: { isAdmin: staff.isAdmin, + email: staff.email, + phoneNumber: staff.phoneNumber, + firstName: staff.firstName, + lastName: staff.lastName, user: { update: { data: { - email: staff.email, - phoneNumber: staff.phoneNumber, - firstName: staff.firstName, - lastName: staff.lastName, displayName: staff.displayName, profilePictureURL: staff.profilePictureURL, isActive: staff.isActive, @@ -120,10 +120,10 @@ class StaffService implements IStaffService { return { userId: updatedStaff.userId, isAdmin: updatedStaff.isAdmin, - email: updatedStaff.user.email, - phoneNumber: updatedStaff.user.phoneNumber, - firstName: updatedStaff.user.firstName, - lastName: updatedStaff.user.lastName, + email: updatedStaff.email, + phoneNumber: updatedStaff.phoneNumber, + firstName: updatedStaff.firstName, + lastName: updatedStaff.lastName, displayName: updatedStaff.user.displayName, profilePictureURL: updatedStaff.user.profilePictureURL, isActive: updatedStaff.user.isActive, @@ -162,10 +162,10 @@ class StaffService implements IStaffService { return { userId: deletedStaff.userId, isAdmin: deletedStaff.isAdmin, - email: deletedUser.email, - phoneNumber: deletedUser.phoneNumber, - firstName: deletedUser.firstName, - lastName: deletedUser.lastName, + email: deletedStaff.email, + phoneNumber: deletedStaff.phoneNumber, + firstName: deletedStaff.firstName, + lastName: deletedStaff.lastName, displayName: deletedUser.displayName, profilePictureURL: deletedUser.profilePictureURL, isActive: deletedUser.isActive, @@ -190,10 +190,10 @@ class StaffService implements IStaffService { return { userId: staff.userId, isAdmin: staff.isAdmin, - email: staff.user.email, - phoneNumber: staff.user.phoneNumber, - firstName: staff.user.firstName, - lastName: staff.user.lastName, + email: staff.email, + phoneNumber: staff.phoneNumber, + firstName: staff.firstName, + lastName: staff.lastName, displayName: staff.user.displayName, profilePictureURL: staff.user.profilePictureURL, isActive: staff.user.isActive, @@ -216,10 +216,10 @@ class StaffService implements IStaffService { return { userId: staff.userId, isAdmin: staff.isAdmin, - email: staff.user.email, - phoneNumber: staff.user.phoneNumber, - firstName: staff.user.firstName, - lastName: staff.user.lastName, + email: staff.email, + phoneNumber: staff.phoneNumber, + firstName: staff.firstName, + lastName: staff.lastName, displayName: staff.user.displayName, profilePictureURL: staff.user.profilePictureURL, isActive: staff.user.isActive, diff --git a/backend/services/implementations/userService.ts b/backend/services/implementations/userService.ts index 012d78c2..fe4a6cb9 100644 --- a/backend/services/implementations/userService.ts +++ b/backend/services/implementations/userService.ts @@ -20,9 +20,6 @@ class UserService implements IUserService { return { id: user.id, type: user.type, - email: firebaseUser.email ?? "", - firstName: user.firstName, - lastName: user.lastName, }; } catch (error: unknown) { Logger.error(`Failed to get user. Reason = ${getErrorMessage(error)}`); diff --git a/backend/services/interfaces/residentService.ts b/backend/services/interfaces/residentService.ts index bb33881a..4b22841e 100644 --- a/backend/services/interfaces/residentService.ts +++ b/backend/services/interfaces/residentService.ts @@ -3,32 +3,26 @@ import { UserDTO, CreateUserDTO, UpdateUserDTO } from "./userService"; export interface ResidentDTO extends Omit { userId: number; residentId: number; - birthDate: Date; roomNumber: number; credits: number; dateJoined: Date; dateLeft: Date | null; - notes: string | null; } export interface CreateResidentDTO extends CreateUserDTO { residentId: number; - birthDate: Date; roomNumber: number; credits?: number; dateJoined?: Date; dateLeft?: Date; - notes?: string; } export interface UpdateResidentDTO extends UpdateUserDTO { residentId?: number; - birthDate?: Date; roomNumber?: number; credits?: number; dateJoined?: Date; dateLeft?: Date; - notes?: string; } // Have to manually map enums as ts treats enums as numbers diff --git a/backend/services/interfaces/staffService.ts b/backend/services/interfaces/staffService.ts index a11b95f1..1624cd7e 100644 --- a/backend/services/interfaces/staffService.ts +++ b/backend/services/interfaces/staffService.ts @@ -2,15 +2,27 @@ import { UserDTO, CreateUserDTO, UpdateUserDTO } from "./userService"; export interface StaffDTO extends Omit { userId: number; + email: string; + phoneNumber: string | null; + firstName: string; + lastName: string; isAdmin: boolean; } export interface CreateStaffDTO extends CreateUserDTO { isAdmin: boolean; + email: string; + phoneNumber: string | null; + firstName: string; + lastName: string; } export interface UpdateStaffDTO extends UpdateUserDTO { isAdmin?: boolean; + email: string; + phoneNumber: string | null; + firstName: string; + lastName: string; } interface IStaffService { diff --git a/backend/services/interfaces/userService.ts b/backend/services/interfaces/userService.ts index 606c9520..9c6157bb 100644 --- a/backend/services/interfaces/userService.ts +++ b/backend/services/interfaces/userService.ts @@ -3,36 +3,21 @@ import { UserType } from "../../prisma"; export type UserDTO = { id: number; type: UserType; - email: string; - phoneNumber: string | null; - firstName: string; - lastName: string; displayName: string | null; profilePictureURL: string | null; isActive: boolean; }; -export type SimplifiedUserDTO = Pick< - UserDTO, - "id" | "type" | "email" | "firstName" | "lastName" ->; +export type SimplifiedUserDTO = Pick; export interface CreateUserDTO { - email: string; password: string; - phoneNumber?: string; - firstName: string; - lastName: string; displayName?: string; profilePictureURL?: string; } export interface UpdateUserDTO { - email?: string; password?: string; - phoneNumber?: string; - firstName?: string; - lastName?: string; displayName?: string; profilePictureURL?: string; isActive?: boolean; diff --git a/frontend/src/APIClients/Mutations/ResidentsMutations.ts b/frontend/src/APIClients/Mutations/ResidentsMutations.ts index 330cc81b..7a99695e 100644 --- a/frontend/src/APIClients/Mutations/ResidentsMutations.ts +++ b/frontend/src/APIClients/Mutations/ResidentsMutations.ts @@ -5,19 +5,13 @@ export const ADD_RESIDENT = gql` addResident(resident: $resident) { userId residentId - email - phoneNumber - firstName - lastName displayName profilePictureURL isActive - birthDate roomNumber credits dateJoined dateLeft - notes } } `; @@ -27,19 +21,13 @@ export const UPDATE_RESIDENT = gql` updateResident(userId: $userId, resident: $resident) { userId residentId - email - phoneNumber - firstName - lastName displayName profilePictureURL isActive - birthDate roomNumber credits dateJoined dateLeft - notes } } `; @@ -49,19 +37,13 @@ export const DELETE_RESIDENT = gql` deleteResident(userId: $userId) { userId residentId - email - phoneNumber - firstName - lastName displayName profilePictureURL isActive - birthDate roomNumber credits dateJoined dateLeft - notes } } `; From f5f61f7c55c1efeb78271e9e8512d17f15dab33d Mon Sep 17 00:00:00 2001 From: Jesse Huang <87463074+jeessh@users.noreply.github.com> Date: Thu, 21 Nov 2024 19:06:48 -0500 Subject: [PATCH 06/10] Updated schema and initial notifications --- .../resolvers/notificationResolvers.ts | 26 ++++-- backend/graphql/types/notificationType.ts | 15 +++- backend/prisma/schema.prisma | 82 ++++++++++--------- .../implementations/notificationService.ts | 63 ++++++++++---- .../interfaces/notificationService.ts | 27 +++++- 5 files changed, 149 insertions(+), 64 deletions(-) diff --git a/backend/graphql/resolvers/notificationResolvers.ts b/backend/graphql/resolvers/notificationResolvers.ts index 4c243d67..71835098 100644 --- a/backend/graphql/resolvers/notificationResolvers.ts +++ b/backend/graphql/resolvers/notificationResolvers.ts @@ -2,6 +2,7 @@ import NotificationService from "../../services/implementations/notificationServ import INotificationService, { NotificationDTO, NotificationReceivedDTO, + UpdateNotificationDTO, } from "../../services/interfaces/notificationService"; import IResidentService from "../../services/interfaces/residentService"; import ResidentService from "../../services/implementations/residentService"; @@ -13,11 +14,11 @@ const notificationService: INotificationService = new NotificationService( const notificationResolvers = { Query: { - getNotificationsByUserId: async ( + getNotificationsByRoomIds: async ( _parent: undefined, - { userId }: { userId: string }, + { roomIds }: { roomIds: string[] }, ): Promise => { - return notificationService.getNotificationsByUserId(Number(userId)); + return notificationService.getNotificationsByRoomIds(roomIds.map(Number)); }, getNotificationById: async ( _parent: undefined, @@ -33,15 +34,15 @@ const notificationResolvers = { authorId, title, message, - recipientIds, + roomIds, }: { authorId: number; title: string; message: string; - recipientIds: number[]; + roomIds: number[]; }, ): Promise => { - const ids = recipientIds.map((id) => Number(id)); + const ids = roomIds.map((id) => Number(id)); const newNotification = await notificationService.sendNotification( Number(authorId), title, @@ -68,6 +69,19 @@ const notificationResolvers = { ); return updatedNotification; }, + updateNotification: async ( + _parent: undefined, + { + notificationId, + notification, + }: { notificationId: number; notification: UpdateNotificationDTO }, + ): Promise => { + const updatedNotification = await notificationService.updateNotificationById( + Number(notificationId), + notification, + ); + return updatedNotification; + }, sendAnnouncement: async ( _parent: undefined, { diff --git a/backend/graphql/types/notificationType.ts b/backend/graphql/types/notificationType.ts index 212cc70a..5efba3fe 100644 --- a/backend/graphql/types/notificationType.ts +++ b/backend/graphql/types/notificationType.ts @@ -17,8 +17,15 @@ const notificationType = gql` seen: Boolean! } + input UpdateNotificationDTO { + authorId: ID + title: String + message: String + createdAt: DateTime + } + extend type Query { - getNotificationsByUserId(userId: ID!): [NotificationReceivedDTO!] + getNotificationsByRoomIds(roomIds: [Int!]): [NotificationReceivedDTO!] getNotificationById(id: ID!): NotificationReceivedDTO! } @@ -27,10 +34,14 @@ const notificationType = gql` authorId: ID! title: String! message: String! - recipientIds: [ID!] + roomIds: [Int!] ): NotificationDTO! deleteUserNotification(notificationId: ID!): NotificationDTO! updateSeenNotification(notificationId: ID!): NotificationReceivedDTO! + updateNotification( + notificationId: ID! + notification: UpdateNotificationDTO! + ): NotificationDTO! sendAnnouncement( title: String message: String diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 51f1ba63..d6bc12dc 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -15,46 +15,45 @@ enum UserType { } model User { - id Int @id @default(autoincrement()) - type UserType - staff Staff? - resident Resident? - authId String @unique @map("auth_id") - displayName String? @map("display_name") - profilePictureURL String? @map("profile_picture_url") - isActive Boolean @default(true) @map("is_active") - notificationsSent Notification[] - notificationsReceived NotificationReceived[] + id Int @id @default(autoincrement()) + type UserType + staff Staff? + resident Resident? + authId String @unique @map("auth_id") + displayName String? @map("display_name") + profilePictureURL String? @map("profile_picture_url") + isActive Boolean @default(true) @map("is_active") @@map("users") } model Staff { - user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) - userId Int @id @map("user_id") - email String @unique @map("email") - phoneNumber String? @unique @map("phone_number") - firstName String @map("first_name") - lastName String @map("last_name") - isAdmin Boolean @default(false) @map("is_admin") - tasksAssigned TaskAssigned[] - warningsAssigned Warning[] + user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) + userId Int @id @map("user_id") + email String @unique @map("email") + phoneNumber String? @unique @map("phone_number") + firstName String @map("first_name") + lastName String @map("last_name") + isAdmin Boolean @default(false) @map("is_admin") + tasksAssigned TaskAssigned[] + warningsAssigned Warning[] + NotificationsSent Notification[] @@map("staff") } model Resident { - user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) - userId Int @id @map("user_id") - residentId Int @unique @map("resident_id") // Differs from user id, this id is assigned by the staff - // birthDate DateTime @map("birth_date") @db.Date - roomNumber Int @map("room_number") - credits Float @default(0) - dateJoined DateTime @default(now()) @map("date_joined") @db.Date - dateLeft DateTime? @map("date_left") @db.Date - // notes String? - tasks TaskAssigned[] - warnings Warning[] + user User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade) + userId Int @id @map("user_id") + residentId Int @unique @map("resident_id") // Differs from user id, this id is assigned by the staff + roomNumber Int @map("room_number") + credits Float @default(0) + dateJoined DateTime @default(now()) @map("date_joined") @db.Date + dateLeft DateTime? @map("date_left") @db.Date + tasks TaskAssigned[] + warnings Warning[] + notificationGroup NotificationGroup[] + notificationRecieved NotificationReceived[] @@map("residents") } @@ -147,14 +146,21 @@ model Warning { @@map("warnings") } +model NotificationGroup { + id Int @id @default(autoincrement()) + recipients Resident[] + notifications Notification[] +} + model Notification { - id Int @id @default(autoincrement()) - author User? @relation(fields: [authorId], references: [id], onDelete: SetNull, onUpdate: Cascade) - authorId Int? @map("author_id") - title String - message String - createdAt DateTime @default(now()) @map("created_at") @db.Date - recipients NotificationReceived[] + id Int @id @default(autoincrement()) + message String + createdAt DateTime @default(now()) @map("created_at") @db.Date + author Staff? @relation(fields: [authorId], references: [userId], onDelete: SetNull, onUpdate: Cascade) + authorId Int? @map("author_id") + group NotificationGroup @relation(fields: [groupId], references: [id]) + groupId Int @map("group_id") + notificationReceived NotificationReceived[] @@map("notifications") } @@ -163,7 +169,7 @@ model NotificationReceived { id Int @id @default(autoincrement()) notification Notification @relation(fields: [notificationId], references: [id], onDelete: Cascade, onUpdate: Cascade) notificationId Int @map("notification_id") - recipient User @relation(fields: [recipientId], references: [id], onDelete: Cascade, onUpdate: Cascade) + recipient Resident @relation(fields: [recipientId], references: [userId], onDelete: Cascade, onUpdate: Cascade) recipientId Int @map("recipient_id") seen Boolean @default(false) diff --git a/backend/services/implementations/notificationService.ts b/backend/services/implementations/notificationService.ts index b9d6ce77..53326088 100644 --- a/backend/services/implementations/notificationService.ts +++ b/backend/services/implementations/notificationService.ts @@ -2,6 +2,7 @@ import prisma from "../../prisma"; import INotificationService, { NotificationDTO, NotificationReceivedDTO, + UpdateNotificationDTO, } from "../interfaces/notificationService"; import IResidentService from "../interfaces/residentService"; import logger from "../../utilities/logger"; @@ -16,20 +17,20 @@ class NotificationService implements INotificationService { this.residentService = residentService; } - async getNotificationsByUserId( - id: number, + async getNotificationsByRoomIds( + roomIds: number[], ): Promise { try { - const user = await prisma.user.findUnique({ - where: { - id, - }, - include: { - notificationsReceived: true, - }, + const residents = await prisma.resident.findMany({ + where: { roomNumber: { in: roomIds } }, }); - if (!user) throw new Error(`No User found.`); - return user.notificationsReceived; + const residentIds = residents.map((resident) => resident.userId); + + const notificationReceived = await prisma.notificationReceived.findMany({ + where: { recipientId: { in: residentIds } }, + }); + if (!notificationReceived) throw new Error(`No User found.`); + return notificationReceived; } catch (error) { Logger.error( `Failed to get Notification. Reason = ${getErrorMessage(error)}`, @@ -60,9 +61,14 @@ class NotificationService implements INotificationService { authorId: number, title: string, message: string, - recipientIds: number[], + roomIds: number[], ): Promise { try { + const residents = await prisma.resident.findMany({ + where: { roomNumber: { in: roomIds } }, + }); + const residentIds = residents.map((resident) => resident.userId); + const newNotification = await prisma.notification.create({ data: { title, @@ -71,10 +77,10 @@ class NotificationService implements INotificationService { connect: { id: authorId }, }, recipients: { - create: recipientIds.map((recipient) => ({ + create: residentIds.map((resident) => ({ recipient: { connect: { - id: recipient, + id: resident, }, }, })), @@ -148,6 +154,35 @@ class NotificationService implements INotificationService { } } + async updateNotificationById( + notificationId: number, + notification: UpdateNotificationDTO, + ): Promise { + try { + const updatedNotification = await prisma.notification.update({ + where: { + id: notificationId, + }, + data: { + ...notification, + }, + include: { + recipients: true, + }, + }); + + if (!updatedNotification) + throw new Error(`notification id ${notificationId} not found`); + + return updatedNotification; + } catch (error) { + Logger.error( + `Failed to set seen flag. Reason = ${getErrorMessage(error)}`, + ); + throw error; + } + } + async sendAnnouncement( title: string, message: string, diff --git a/backend/services/interfaces/notificationService.ts b/backend/services/interfaces/notificationService.ts index a8588e14..c55f1313 100644 --- a/backend/services/interfaces/notificationService.ts +++ b/backend/services/interfaces/notificationService.ts @@ -7,6 +7,13 @@ export interface NotificationDTO { recipients: NotificationReceivedDTO[]; } +export interface UpdateNotificationDTO { + authorId?: number; + title?: string; + message?: string; + createdAt?: Date; +} + export interface NotificationReceivedDTO { id: number; notificationId: number; @@ -21,7 +28,9 @@ interface INotificationService { * @returns a NotificationDTO[] associated with that users notifications * @throws Error if retrieval fails */ - getNotificationsByUserId(id: number): Promise; + getNotificationsByRoomIds( + roomIds: number[], + ): Promise; /** * Get a notification by a defined id @@ -36,7 +45,7 @@ interface INotificationService { * @param authorId user id of author of notification * @param title title of notification * @param message message of notification - * @param recipientIds user ids of recipients of notification + * @param roomIds room ids of recipients of notification * @returns a NotificationDTO associated with the posted notifications * @throws Error if creation fails */ @@ -44,7 +53,7 @@ interface INotificationService { authorId: number, title: string, message: string, - recipientIds: number[], + roomIds: number[], ): Promise; /** @@ -58,7 +67,6 @@ interface INotificationService { /** * Update a user notification to be seen - * @param userId user id * @param notificationId notification id * @returns a NotificationDTO associated with the now seen Notification * @throws Error if retrieval fails @@ -67,6 +75,17 @@ interface INotificationService { notificationId: number, ): Promise; + /** + * Update a user notification to be seen + * @param notificationId notification id + * @returns a NotificationDTO associated with the updated Notification + * @throws Error if retrieval fails + */ + updateNotificationById( + notificationId: number, + notification: UpdateNotificationDTO, + ): Promise; + /** * Post an announcement notification to all active residents * @param title title of announcement From a5428eb6daccf98e1fcee18e05deacf77cd27b48 Mon Sep 17 00:00:00 2001 From: Jesse Huang <87463074+jeessh@users.noreply.github.com> Date: Thu, 21 Nov 2024 22:09:26 -0500 Subject: [PATCH 07/10] fixed taskAssignedDTO required fields --- backend/graphql/types/taskType.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/graphql/types/taskType.ts b/backend/graphql/types/taskType.ts index e0b47e63..3433c5d1 100644 --- a/backend/graphql/types/taskType.ts +++ b/backend/graphql/types/taskType.ts @@ -65,11 +65,11 @@ const taskType = gql` } input InputTaskAssignedDTO { - taskId: Int - assigneeId: Int - assignerId: Int - status: Status - startDate: DateTime + taskId: Int! + assigneeId: Int! + assignerId: Int! + status: Status! + startDate: DateTime! comments: String } From 79f26a3c98ebfd24d057ed5648ea1c23792e9465 Mon Sep 17 00:00:00 2001 From: KathleenX7 Date: Thu, 28 Nov 2024 20:30:35 -0500 Subject: [PATCH 08/10] notification apis --- .../resolvers/notificationResolvers.ts | 119 +++-- backend/graphql/types/notificationType.ts | 49 +- backend/graphql/types/residentType.ts | 2 + backend/prisma/schema.prisma | 9 +- .../implementations/notificationService.ts | 442 ++++++++++++++---- .../implementations/residentService.ts | 19 + .../interfaces/notificationService.ts | 158 +++++-- .../services/interfaces/residentService.ts | 7 + 8 files changed, 612 insertions(+), 193 deletions(-) diff --git a/backend/graphql/resolvers/notificationResolvers.ts b/backend/graphql/resolvers/notificationResolvers.ts index 71835098..351b304f 100644 --- a/backend/graphql/resolvers/notificationResolvers.ts +++ b/backend/graphql/resolvers/notificationResolvers.ts @@ -1,8 +1,10 @@ import NotificationService from "../../services/implementations/notificationService"; import INotificationService, { NotificationDTO, + NotificationGroupDTO, NotificationReceivedDTO, UpdateNotificationDTO, + CreateNotificationDTO, } from "../../services/interfaces/notificationService"; import IResidentService from "../../services/interfaces/residentService"; import ResidentService from "../../services/implementations/residentService"; @@ -14,67 +16,86 @@ const notificationService: INotificationService = new NotificationService( const notificationResolvers = { Query: { - getNotificationsByRoomIds: async ( + getNotificationsByIds: async ( _parent: undefined, - { roomIds }: { roomIds: string[] }, + { notificationIds }: { notificationIds: string[] }, ): Promise => { - return notificationService.getNotificationsByRoomIds(roomIds.map(Number)); + const notificationReceived = await notificationService.getNotificationsByIds( + notificationIds.map(Number), + ); + return notificationReceived; }, - getNotificationById: async ( + getNotificationByResident: async ( _parent: undefined, - { id }: { id: string }, - ): Promise => { - return notificationService.getNotificationById(Number(id)); + { residentId }: { residentId: string }, + ): Promise => { + const notificationReceived = await notificationService.getNotificationByResident( + Number(residentId), + ); + return notificationReceived; + }, + getAllGroupsAndNotifications: async (): Promise => { + const notificationGroups = await notificationService.getAllGroupsAndNotifications(); + return notificationGroups; }, }, Mutation: { - sendNotification: async ( + createNotificationGroup: async ( _parent: undefined, { - authorId, - title, - message, roomIds, }: { - authorId: number; - title: string; - message: string; roomIds: number[]; }, - ): Promise => { + ): Promise => { const ids = roomIds.map((id) => Number(id)); - const newNotification = await notificationService.sendNotification( - Number(authorId), - title, - message, + const newNotificationGroup = await notificationService.createNotificationGroup( ids, ); - return newNotification; + return newNotificationGroup; }, - deleteUserNotification: async ( + createAnnouncementGroup: async (): Promise => { + const newNotificationGroup = await notificationService.createAnnouncementGroup(); + return newNotificationGroup; + }, + sendNotificationToGroup: async ( _parent: undefined, - { notificationId }: { notificationId: number }, + { + groupId, + notification, + }: { + groupId: number; + notification: CreateNotificationDTO; + }, ): Promise => { - const deletedNotification = await notificationService.deleteUserNotification( - Number(notificationId), + const newNotification = await notificationService.sendNotificationToGroup( + Number(groupId), + notification, ); - return deletedNotification; + return newNotification; }, - updateSeenNotification: async ( + deleteNotificationGroup: async ( _parent: undefined, - { notificationId }: { notificationId: number }, - ): Promise => { - const updatedNotification = await notificationService.updateSeenNotification( - Number(notificationId), + { + groupId, + }: { + groupId: number; + }, + ): Promise => { + const deletedGroup = await notificationService.deleteNotificationGroup( + Number(groupId), ); - return updatedNotification; + return deletedGroup; }, - updateNotification: async ( + updateNotificationById: async ( _parent: undefined, { notificationId, notification, - }: { notificationId: number; notification: UpdateNotificationDTO }, + }: { + notificationId: number; + notification: UpdateNotificationDTO; + }, ): Promise => { const updatedNotification = await notificationService.updateNotificationById( Number(notificationId), @@ -82,20 +103,30 @@ const notificationResolvers = { ); return updatedNotification; }, - sendAnnouncement: async ( + deleteNotificationByIds: async ( _parent: undefined, { - title, - message, - userId, - }: { title: string; message: string; userId: number }, - ): Promise => { - const newAnnouncement = await notificationService.sendAnnouncement( - title, - message, - Number(userId), + notificationIds, + }: { + notificationIds: number[]; + }, + ): Promise => { + const ids = notificationIds.map((id) => Number(id)); + await notificationService.deleteNotificationByIds(ids); + return true; + }, + updateSeenNotification: async ( + _parent: undefined, + { + notificationSeenId, + }: { + notificationSeenId: number; + }, + ): Promise => { + const updatedNotificationReceived = await notificationService.updateSeenNotification( + Number(notificationSeenId), ); - return newAnnouncement; + return updatedNotificationReceived; }, }, }; diff --git a/backend/graphql/types/notificationType.ts b/backend/graphql/types/notificationType.ts index 5efba3fe..b8007d3e 100644 --- a/backend/graphql/types/notificationType.ts +++ b/backend/graphql/types/notificationType.ts @@ -3,50 +3,59 @@ import { gql } from "apollo-server-express"; const notificationType = gql` type NotificationDTO { id: ID! - authorId: ID - title: String! message: String! - createdAt: DateTime! - recipients: [NotificationReceivedDTO!] + createdAt: DateTime + authorId: ID + recipients: [NotificationReceivedDTO] + } + + type NotificationGroupDTO { + id: ID! + recipients: [ResidentDTO!] + notifications: [NotificationDTO!] + announcementGroup: Boolean! } type NotificationReceivedDTO { id: ID! notificationId: ID! + notification: NotificationDTO recipientId: ID! seen: Boolean! } input UpdateNotificationDTO { authorId: ID - title: String message: String createdAt: DateTime } + input CreateNotificationDTO { + authorId: ID + message: String! + createdAt: DateTime + } + extend type Query { - getNotificationsByRoomIds(roomIds: [Int!]): [NotificationReceivedDTO!] - getNotificationById(id: ID!): NotificationReceivedDTO! + getNotificationsByIds(notificationIds: [ID!]): [NotificationReceivedDTO!] + getNotificationByResident(residentId: ID!): [NotificationReceivedDTO!] + getAllGroupsAndNotifications: [NotificationGroupDTO!] } extend type Mutation { - sendNotification( - authorId: ID! - title: String! - message: String! - roomIds: [Int!] + createNotificationGroup(roomIds: [Int!]): NotificationGroupDTO! + createAnnouncementGroup: NotificationGroupDTO! + sendNotificationToGroup( + groupId: ID! + notification: CreateNotificationDTO! ): NotificationDTO! - deleteUserNotification(notificationId: ID!): NotificationDTO! - updateSeenNotification(notificationId: ID!): NotificationReceivedDTO! - updateNotification( + deleteNotificationGroup(groupId: ID!): NotificationGroupDTO! + updateNotificationById( notificationId: ID! notification: UpdateNotificationDTO! ): NotificationDTO! - sendAnnouncement( - title: String - message: String - userId: ID - ): NotificationDTO! + deleteNotificationByIds(notificationIds: [ID!]): Boolean! + updateSeenNotification(notificationSeenId: ID!): NotificationReceivedDTO! } `; diff --git a/backend/graphql/types/residentType.ts b/backend/graphql/types/residentType.ts index bd19d6db..bf5fa145 100644 --- a/backend/graphql/types/residentType.ts +++ b/backend/graphql/types/residentType.ts @@ -13,6 +13,8 @@ const residentType = gql` credits: Float! dateJoined: Date! dateLeft: Date + notificationGroup: [NotificationGroupDTO!]! + notificationRecieved: [NotificationReceivedDTO]! } input CreateResidentDTO { diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index d6bc12dc..db995cca 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -147,9 +147,10 @@ model Warning { } model NotificationGroup { - id Int @id @default(autoincrement()) - recipients Resident[] - notifications Notification[] + id Int @id @default(autoincrement()) + recipients Resident[] + notifications Notification[] + announcementGroup Boolean } model Notification { @@ -158,7 +159,7 @@ model Notification { createdAt DateTime @default(now()) @map("created_at") @db.Date author Staff? @relation(fields: [authorId], references: [userId], onDelete: SetNull, onUpdate: Cascade) authorId Int? @map("author_id") - group NotificationGroup @relation(fields: [groupId], references: [id]) + group NotificationGroup @relation(fields: [groupId], references: [id], onDelete: Cascade) groupId Int @map("group_id") notificationReceived NotificationReceived[] diff --git a/backend/services/implementations/notificationService.ts b/backend/services/implementations/notificationService.ts index 53326088..bb8828da 100644 --- a/backend/services/implementations/notificationService.ts +++ b/backend/services/implementations/notificationService.ts @@ -2,6 +2,8 @@ import prisma from "../../prisma"; import INotificationService, { NotificationDTO, NotificationReceivedDTO, + NotificationGroupDTO, + CreateNotificationDTO, UpdateNotificationDTO, } from "../interfaces/notificationService"; import IResidentService from "../interfaces/residentService"; @@ -17,77 +19,90 @@ class NotificationService implements INotificationService { this.residentService = residentService; } - async getNotificationsByRoomIds( + async createNotificationGroup( roomIds: number[], - ): Promise { + ): Promise { try { const residents = await prisma.resident.findMany({ where: { roomNumber: { in: roomIds } }, }); const residentIds = residents.map((resident) => resident.userId); - const notificationReceived = await prisma.notificationReceived.findMany({ - where: { recipientId: { in: residentIds } }, + const newNotificationGroup = await prisma.notificationGroup.create({ + data: { + recipients: { + connect: residentIds.map((id) => ({ userId: id })), + }, + announcementGroup: false, + }, }); - if (!notificationReceived) throw new Error(`No User found.`); - return notificationReceived; + + return newNotificationGroup; } catch (error) { Logger.error( - `Failed to get Notification. Reason = ${getErrorMessage(error)}`, + `Failed to create Notification Group. Reason = ${getErrorMessage( + error, + )}`, ); throw error; } } - async getNotificationById(id: number): Promise { + async createAnnouncementGroup(): Promise { try { - const notification = await prisma.notificationReceived.findUnique({ - where: { - id, + const residents = await prisma.resident.findMany(); + const residentIds = residents.map((resident) => resident.userId); + + const newNotificationGroup = await prisma.notificationGroup.create({ + data: { + recipients: { + connect: residentIds.map((id) => ({ userId: id })), + }, + announcementGroup: true, }, }); - if (!notification) throw new Error(`notification id ${id} not found`); - return notification; - } catch (error: unknown) { + return newNotificationGroup; + } catch (error) { Logger.error( - `Failed to get Notification. Reason = ${getErrorMessage(error)}`, + `Failed to create Notification Group. Reason = ${getErrorMessage( + error, + )}`, ); throw error; } } - async sendNotification( - authorId: number, - title: string, - message: string, - roomIds: number[], + async sendNotificationToGroup( + groupId: number, + notification: CreateNotificationDTO, ): Promise { try { - const residents = await prisma.resident.findMany({ - where: { roomNumber: { in: roomIds } }, + const notificationGroup = await prisma.notificationGroup.findUnique({ + where: { id: groupId }, + include: { recipients: true }, }); - const residentIds = residents.map((resident) => resident.userId); - const newNotification = await prisma.notification.create({ data: { - title, - message, + message: notification.message, + createdAt: notification.createdAt, author: { - connect: { id: authorId }, + connect: notification.authorId + ? { userId: notification.authorId } + : undefined, }, - recipients: { - create: residentIds.map((resident) => ({ - recipient: { - connect: { - id: resident, - }, - }, - })), + group: { + connect: { id: groupId }, + }, + notificationReceived: { + create: notificationGroup + ? notificationGroup.recipients.map((resident) => ({ + recipient: { + connect: { userId: resident.userId }, + }, + })) + : undefined, }, - }, - include: { - recipients: true, }, }); @@ -100,21 +115,20 @@ class NotificationService implements INotificationService { } } - async deleteUserNotification( - notificationId: number, - ): Promise { + async deleteNotificationGroup( + groupId: number, + ): Promise { try { - const deletedNotification = await prisma.notification.delete({ + const deletedNotificationGroup = await prisma.notificationGroup.delete({ where: { - id: notificationId, + id: groupId, }, - include: { recipients: true }, }); - if (!deletedNotification) - throw new Error(`notification id ${notificationId} not found`); + if (!deletedNotificationGroup) + throw new Error(`notification id ${groupId} not found`); - return deletedNotification; + return deletedNotificationGroup; } catch (error) { Logger.error( `Failed to set isDelete flag. Reason = ${getErrorMessage(error)}`, @@ -123,32 +137,37 @@ class NotificationService implements INotificationService { } } - async updateSeenNotification( - notificationRecievedId: number, - ): Promise { + async getAllGroupsAndNotifications(): Promise { try { - await prisma.notificationReceived.update({ - where: { - id: notificationRecievedId, - }, - data: { - seen: true, + const notificationGroups = await prisma.notificationGroup.findMany({ + include: { + // recipients: true, // TODO: resident type is incompatiable at time of writing + notifications: true, }, }); + return notificationGroups; + } catch (error) { + Logger.error( + `Failed to create Notification. Reason = ${getErrorMessage(error)}`, + ); + throw error; + } + } - const updatedNotification = await prisma.notificationReceived.findUnique({ - where: { - id: notificationRecievedId, - }, + async getNotificationsByIds( + notificationIds: number[], + ): Promise { + try { + const notificationReceived = await prisma.notificationReceived.findMany({ + where: { notificationId: { in: notificationIds } }, + include: { notification: true }, }); - if (!updatedNotification) - throw new Error(`notification id ${notificationRecievedId} not found`); - - return updatedNotification; + if (!notificationReceived) throw new Error(`No User found.`); + return notificationReceived; } catch (error) { Logger.error( - `Failed to set seen flag. Reason = ${getErrorMessage(error)}`, + `Failed to get Notification. Reason = ${getErrorMessage(error)}`, ); throw error; } @@ -166,9 +185,6 @@ class NotificationService implements INotificationService { data: { ...notification, }, - include: { - recipients: true, - }, }); if (!updatedNotification) @@ -183,44 +199,280 @@ class NotificationService implements INotificationService { } } - async sendAnnouncement( - title: string, - message: string, - userId: number, - ): Promise { + async deleteNotificationByIds(notficationId: number[]): Promise { try { - const activeResidents = await this.residentService.getActiveResidents(); - const newNotification = await prisma.notification.create({ + const deletedNotification = await prisma.notification.deleteMany({ + where: { id: { in: notficationId } }, + }); + + if (!deletedNotification) + throw new Error(`notification id ${notficationId} not found`); + return true; + } catch (error) { + Logger.error( + `Failed to set isDelete flag. Reason = ${getErrorMessage(error)}`, + ); + throw error; + } + } + + async updateSeenNotification( + notificationSeenId: number, + ): Promise { + try { + await prisma.notificationReceived.update({ + where: { + id: notificationSeenId, + }, data: { - title, - message, - author: { - connect: { id: userId }, - }, - recipients: { - create: activeResidents.map((recipient) => ({ - recipient: { - connect: { - id: recipient.userId, - }, - }, - })), - }, + seen: true, }, - include: { - recipients: true, + }); + + const updatedNotification = await prisma.notificationReceived.findUnique({ + where: { + id: notificationSeenId, }, }); - return newNotification; + + if (!updatedNotification) + throw new Error(`notification id ${notificationSeenId} not found`); + + return updatedNotification; } catch (error) { Logger.error( - `Failed to create Notification for Announcement. Reason = ${getErrorMessage( - error, - )}`, + `Failed to set seen flag. Reason = ${getErrorMessage(error)}`, + ); + throw error; + } + } + + async getNotificationByResident( + residentId: number, + ): Promise { + try { + const notification = await prisma.notificationReceived.findMany({ + where: { + recipientId: residentId, + }, + include: { notification: true }, + }); + if (!notification) + throw new Error(`notification id ${residentId} not found`); + + return notification; + } catch (error: unknown) { + Logger.error( + `Failed to get Notification. Reason = ${getErrorMessage(error)}`, ); throw error; } } + + // async getNotificationsByRoomIds( + // roomIds: number[], + // ): Promise { + // try { + // const residents = await prisma.resident.findMany({ + // where: { roomNumber: { in: roomIds } }, + // }); + // const residentIds = residents.map((resident) => resident.userId); + + // const notificationReceived = await prisma.notificationReceived.findMany({ + // where: { recipientId: { in: residentIds } }, + // }); + // if (!notificationReceived) throw new Error(`No User found.`); + // return notificationReceived; + // } catch (error) { + // Logger.error( + // `Failed to get Notification. Reason = ${getErrorMessage(error)}`, + // ); + // throw error; + // } + // } + + // async getNotificationById(id: number): Promise { + // try { + // const notification = await prisma.notificationReceived.findUnique({ + // where: { + // id, + // }, + // }); + // if (!notification) throw new Error(`notification id ${id} not found`); + + // return notification; + // } catch (error: unknown) { + // Logger.error( + // `Failed to get Notification. Reason = ${getErrorMessage(error)}`, + // ); + // throw error; + // } + // } + + // async sendNotification( + // authorId: number, + // title: string, + // message: string, + // roomIds: number[], + // ): Promise { + // try { + // const residents = await prisma.resident.findMany({ + // where: { roomNumber: { in: roomIds } }, + // }); + // const residentIds = residents.map((resident) => resident.userId); + + // const newNotification = await prisma.notification.create({ + // data: { + // title, + // message, + // author: { + // connect: { id: authorId }, + // }, + // recipients: { + // create: residentIds.map((resident) => ({ + // recipient: { + // connect: { + // id: resident, + // }, + // }, + // })), + // }, + // }, + // include: { + // recipients: true, + // }, + // }); + + // return newNotification; + // } catch (error) { + // Logger.error( + // `Failed to create Notification. Reason = ${getErrorMessage(error)}`, + // ); + // throw error; + // } + // } + + // async deleteUserNotification( + // notificationId: number, + // ): Promise { + // try { + // const deletedNotification = await prisma.notification.delete({ + // where: { + // id: notificationId, + // }, + // include: { recipients: true }, + // }); + + // if (!deletedNotification) + // throw new Error(`notification id ${notificationId} not found`); + + // return deletedNotification; + // } catch (error) { + // Logger.error( + // `Failed to set isDelete flag. Reason = ${getErrorMessage(error)}`, + // ); + // throw error; + // } + // } + + // async updateSeenNotification( + // notificationRecievedId: number, + // ): Promise { + // try { + // await prisma.notificationReceived.update({ + // where: { + // id: notificationRecievedId, + // }, + // data: { + // seen: true, + // }, + // }); + + // const updatedNotification = await prisma.notificationReceived.findUnique({ + // where: { + // id: notificationRecievedId, + // }, + // }); + + // if (!updatedNotification) + // throw new Error(`notification id ${notificationRecievedId} not found`); + + // return updatedNotification; + // } catch (error) { + // Logger.error( + // `Failed to set seen flag. Reason = ${getErrorMessage(error)}`, + // ); + // throw error; + // } + // } + + // async updateNotificationById( + // notificationId: number, + // notification: UpdateNotificationDTO, + // ): Promise { + // try { + // const updatedNotification = await prisma.notification.update({ + // where: { + // id: notificationId, + // }, + // data: { + // ...notification, + // }, + // include: { + // recipients: true, + // }, + // }); + + // if (!updatedNotification) + // throw new Error(`notification id ${notificationId} not found`); + + // return updatedNotification; + // } catch (error) { + // Logger.error( + // `Failed to set seen flag. Reason = ${getErrorMessage(error)}`, + // ); + // throw error; + // } + // } + + // async sendAnnouncement( + // title: string, + // message: string, + // userId: number, + // ): Promise { + // try { + // const activeResidents = await this.residentService.getActiveResidents(); + // const newNotification = await prisma.notification.create({ + // data: { + // title, + // message, + // author: { + // connect: { id: userId }, + // }, + // recipients: { + // create: activeResidents.map((recipient) => ({ + // recipient: { + // connect: { + // id: recipient.userId, + // }, + // }, + // })), + // }, + // }, + // include: { + // recipients: true, + // }, + // }); + // return newNotification; + // } catch (error) { + // Logger.error( + // `Failed to create Notification for Announcement. Reason = ${getErrorMessage( + // error, + // )}`, + // ); + // throw error; + // } + // } } export default NotificationService; diff --git a/backend/services/implementations/residentService.ts b/backend/services/implementations/residentService.ts index 2af2c081..305c9e34 100644 --- a/backend/services/implementations/residentService.ts +++ b/backend/services/implementations/residentService.ts @@ -20,6 +20,13 @@ class ResidentService implements IResidentService { }); try { + const announcementGroups = await prisma.notificationGroup.findMany({ + where: { + announcementGroup: true, + }, + include: { notifications: true }, + }); + const newResident = await prisma.resident.create({ data: { residentId: resident.residentId, @@ -36,6 +43,18 @@ class ResidentService implements IResidentService { isActive: true, }, }, + notificationGroup: { + connect: announcementGroups.map((group) => ({ id: group.id })), + }, + notificationRecieved: { + create: announcementGroups.flatMap((group) => + group.notifications.map((notif) => ({ + notification: { + connect: { id: notif.id }, + }, + })), + ), + }, }, include: { user: true }, }); diff --git a/backend/services/interfaces/notificationService.ts b/backend/services/interfaces/notificationService.ts index c55f1313..9054c3fb 100644 --- a/backend/services/interfaces/notificationService.ts +++ b/backend/services/interfaces/notificationService.ts @@ -1,44 +1,142 @@ +import { ResidentDTO } from "./residentService"; + export interface NotificationDTO { id: number; + message: string; + createdAt?: Date; authorId: number | null; - title: string; + groupId: number; + recipients?: NotificationReceivedDTO[]; +} + +export interface CreateNotificationDTO { message: string; - createdAt: Date; - recipients: NotificationReceivedDTO[]; + createdAt?: Date; + authorId: number | null; } export interface UpdateNotificationDTO { authorId?: number; - title?: string; message?: string; createdAt?: Date; } +export interface NotificationGroupDTO { + id: number; + recipients?: ResidentDTO[]; + notifications?: NotificationDTO[]; + announcementGroup: boolean; +} + export interface NotificationReceivedDTO { id: number; notificationId: number; + notification?: NotificationDTO; recipientId: number; seen: boolean; } interface INotificationService { /** - * Get all notifications for a given user id - * @param id user id + * create a new notification group + * @param roomIds list of room ids that correspond to resideents + * @returns a NotificationGroupDTO that was just created + * @throws Error if cration fails + */ + createNotificationGroup(roomIds: number[]): Promise; + + /** + * Create a new notification group + * @returns a NotificationGroupDTO that was deleted + * @throws Error if creation fails + */ + createAnnouncementGroup(): Promise; + + /** + * send a notification to a group + * @param groupId notification group to send to + * @param notification information related to the notification to be created + * @returns a NotificationDTO that was just created + * @throws Error if creation fails + */ + sendNotificationToGroup( + groupId: number, + notification: CreateNotificationDTO, + ): Promise; + + /** + * Delete a notification group + * @param groupId notification group to delete + * @returns a NotificationGroupDTO that was deleted + * @throws Error if deletion fails + */ + deleteNotificationGroup(groupId: number): Promise; + + /** + * Get all groups and their associated notifications + * @returns a list of NotificationGroupDTOs with their notifications + * @throws Error if retrieval fails + */ + getAllGroupsAndNotifications(): Promise; + + /** + * Get all notifications for given notification id + * @param id notification id * @returns a NotificationDTO[] associated with that users notifications * @throws Error if retrieval fails */ - getNotificationsByRoomIds( - roomIds: number[], + getNotificationsByIds( + notificationIds: number[], ): Promise; /** - * Get a notification by a defined id + * Updates notification for a given notification id * @param id notification id - * @returns a NotificationDTO associated with the notification id + * @returns a NotificationDTO associated with the updated Notification * @throws Error if retrieval fails */ - getNotificationById(id: number): Promise; + updateNotificationById( + notificationId: number, + notification: UpdateNotificationDTO, + ): Promise; + + /** + * Deletes notifications for given notification ids + * @param id notification id + * @returns a NotificationDTO associated with the updated Notification + * @throws Error if retrieval fails + */ + deleteNotificationByIds(notficationId: number[]): Promise; + + /** + * Update a user notification to be seen + * @param notificationId notification id + * @returns a NotificationDTO associated with the now seen Notification + * @throws Error if retrieval fails + */ + updateSeenNotification( + notificationSeenId: number, + ): Promise; + + /** + * Gets the notifications associated with a resident + * @param residentId resident id + * @returns a list of NotificationDTOs that the resident has + * @throws Error if retrieval fails + */ + getNotificationByResident( + residentId: number, + ): Promise; + + /** + * Get all notifications for a given user id + * @param id user id + * @returns a NotificationDTO[] associated with that users notifications + * @throws Error if retrieval fails + */ + // getNotificationsByRoomIds( + // roomIds: number[], + // ): Promise; /** * Post a notification to a specified resident or residents @@ -49,12 +147,12 @@ interface INotificationService { * @returns a NotificationDTO associated with the posted notifications * @throws Error if creation fails */ - sendNotification( - authorId: number, - title: string, - message: string, - roomIds: number[], - ): Promise; + // sendNotification( + // authorId: number, + // title: string, + // message: string, + // roomIds: number[], + // ): Promise; /** * Delete a user notification based on a notification id and user id @@ -63,7 +161,7 @@ interface INotificationService { * @returns a NotificationDTO associated with the deleted Notification * @throws Error if retrieval fails */ - deleteUserNotification(notificationId: number): Promise; + // deleteUserNotification(notificationId: number): Promise; /** * Update a user notification to be seen @@ -71,9 +169,9 @@ interface INotificationService { * @returns a NotificationDTO associated with the now seen Notification * @throws Error if retrieval fails */ - updateSeenNotification( - notificationId: number, - ): Promise; + // updateSeenNotification( + // notificationId: number, + // ): Promise; /** * Update a user notification to be seen @@ -81,10 +179,10 @@ interface INotificationService { * @returns a NotificationDTO associated with the updated Notification * @throws Error if retrieval fails */ - updateNotificationById( - notificationId: number, - notification: UpdateNotificationDTO, - ): Promise; + // updateNotificationById( + // notificationId: number, + // notification: UpdateNotificationDTO, + // ): Promise; /** * Post an announcement notification to all active residents @@ -94,11 +192,11 @@ interface INotificationService { * @returns the new updated NotificationDTO * @throws Error if creation fails */ - sendAnnouncement( - title: string, - message: string, - userId: number, - ): Promise; + // sendAnnouncement( + // title: string, + // message: string, + // userId: number, + // ): Promise; } export default INotificationService; diff --git a/backend/services/interfaces/residentService.ts b/backend/services/interfaces/residentService.ts index 4b22841e..427b7dda 100644 --- a/backend/services/interfaces/residentService.ts +++ b/backend/services/interfaces/residentService.ts @@ -1,3 +1,8 @@ +// eslint-disable-next-line import/no-cycle +import { + NotificationGroupDTO, + NotificationReceivedDTO, +} from "./notificationService"; import { UserDTO, CreateUserDTO, UpdateUserDTO } from "./userService"; export interface ResidentDTO extends Omit { @@ -7,6 +12,8 @@ export interface ResidentDTO extends Omit { credits: number; dateJoined: Date; dateLeft: Date | null; + notificationGroup?: NotificationGroupDTO[]; + notificationRecieved?: NotificationReceivedDTO[]; } export interface CreateResidentDTO extends CreateUserDTO { From 0b2f09b2d16fa15e03c90cea25a5b9f75a2ee344 Mon Sep 17 00:00:00 2001 From: Jesse Huang <87463074+jeessh@users.noreply.github.com> Date: Thu, 28 Nov 2024 20:45:50 -0500 Subject: [PATCH 09/10] ran linter --- .../pages/announcements/AnnouncementsPage.tsx | 2 +- frontend/src/types/TaskTypes.ts | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/pages/announcements/AnnouncementsPage.tsx b/frontend/src/components/pages/announcements/AnnouncementsPage.tsx index ccfd47fc..6f973bc5 100644 --- a/frontend/src/components/pages/announcements/AnnouncementsPage.tsx +++ b/frontend/src/components/pages/announcements/AnnouncementsPage.tsx @@ -17,7 +17,7 @@ import { } from "../../../APIClients/Mutations/NotificationMutations"; import { - GET_NOTIFCATION_BY_ID, + GET_NOTIFCATION_BY_ID, GET_NOTIFICATIONS_BY_USER_ID, } from "../../../APIClients/Queries/NotificationQueries"; diff --git a/frontend/src/types/TaskTypes.ts b/frontend/src/types/TaskTypes.ts index 99712dac..527415f2 100644 --- a/frontend/src/types/TaskTypes.ts +++ b/frontend/src/types/TaskTypes.ts @@ -1,6 +1,16 @@ export type TaskType = "REQUIRED" | "OPTIONAL" | "CUSTOM" | "CHORE"; -export type RecurrenceFrequency = "ONE_TIME" | "REPEATS_PER_WEEK_SELECTED" | "REPEATS_PER_WEEK_ONCE"; -export type DaysOfWeek = "MONDAY" | "TUESDAY" | "WEDNESDAY" | "THURSDAY" | "FRIDAY" | "SATURDAY" | "SUNDAY"; +export type RecurrenceFrequency = + | "ONE_TIME" + | "REPEATS_PER_WEEK_SELECTED" + | "REPEATS_PER_WEEK_ONCE"; +export type DaysOfWeek = + | "MONDAY" + | "TUESDAY" + | "WEDNESDAY" + | "THURSDAY" + | "FRIDAY" + | "SATURDAY" + | "SUNDAY"; export interface Task { id: string; From 1c9b1c4e91c922150e25e9899cd593150501805b Mon Sep 17 00:00:00 2001 From: Jesse Huang <87463074+jeessh@users.noreply.github.com> Date: Thu, 28 Nov 2024 20:49:41 -0500 Subject: [PATCH 10/10] ran linter in backend --- backend/services/interfaces/notificationService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/services/interfaces/notificationService.ts b/backend/services/interfaces/notificationService.ts index 9054c3fb..437f53ab 100644 --- a/backend/services/interfaces/notificationService.ts +++ b/backend/services/interfaces/notificationService.ts @@ -1,4 +1,4 @@ -import { ResidentDTO } from "./residentService"; +import type { ResidentDTO } from "./residentService"; export interface NotificationDTO { id: number;