diff --git a/backend/graphql/resolvers/notificationResolvers.ts b/backend/graphql/resolvers/notificationResolvers.ts index 4c243d67..03af7007 100644 --- a/backend/graphql/resolvers/notificationResolvers.ts +++ b/backend/graphql/resolvers/notificationResolvers.ts @@ -5,8 +5,11 @@ import INotificationService, { } from "../../services/interfaces/notificationService"; import IResidentService from "../../services/interfaces/residentService"; import ResidentService from "../../services/implementations/residentService"; +import IUserService from "../../services/interfaces/userService"; +import UserService from "../../services/implementations/userService"; -const residentService: IResidentService = new ResidentService(); +const userService: IUserService = new UserService(); +const residentService: IResidentService = new ResidentService(userService); const notificationService: INotificationService = new NotificationService( residentService, ); diff --git a/backend/graphql/resolvers/residentResolvers.ts b/backend/graphql/resolvers/residentResolvers.ts index cad94fc5..a6065b96 100644 --- a/backend/graphql/resolvers/residentResolvers.ts +++ b/backend/graphql/resolvers/residentResolvers.ts @@ -5,8 +5,11 @@ import IResidentService, { UpdateResidentDTO, RedeemCreditsResponse, } from "../../services/interfaces/residentService"; +import UserService from "../../services/implementations/userService"; +import IUserService from "../../services/interfaces/userService"; -const residentService: IResidentService = new ResidentService(); +const userService: IUserService = new UserService(); +const residentService: IResidentService = new ResidentService(userService); const residentResolvers = { Query: { @@ -67,6 +70,15 @@ const residentResolvers = { ): Promise => { return residentService.redeemCredits(parseInt(userId, 10), credits); }, + setResidentInactive: async ( + _parent: undefined, + { userId }: { userId: string }, + ): Promise => { + const updatedResident = await residentService.setResidentInactive( + parseInt(userId, 10), + ); + return updatedResident; + }, }, }; diff --git a/backend/graphql/resolvers/staffResolver.ts b/backend/graphql/resolvers/staffResolver.ts index 425da314..1ba0e722 100644 --- a/backend/graphql/resolvers/staffResolver.ts +++ b/backend/graphql/resolvers/staffResolver.ts @@ -4,8 +4,11 @@ import IStaffService, { CreateStaffDTO, UpdateStaffDTO, } from "../../services/interfaces/staffService"; +import UserService from "../../services/implementations/userService"; +import IUserService from "../../services/interfaces/userService"; -const staffService: IStaffService = new StaffService(); +const userService: IUserService = new UserService(); +const staffService: IStaffService = new StaffService(userService); const staffResolvers = { Query: { @@ -50,6 +53,15 @@ const staffResolvers = { const deletedStaff = await staffService.deleteStaff(parseInt(userId, 10)); return deletedStaff; }, + setStaffInactive: async ( + _parent: undefined, + { userId }: { userId: string }, + ): Promise => { + const updatedStaff = await staffService.setStaffInactive( + parseInt(userId, 10), + ); + return updatedStaff; + }, }, }; diff --git a/backend/graphql/types/residentType.ts b/backend/graphql/types/residentType.ts index 83b68f05..f4227d6e 100644 --- a/backend/graphql/types/residentType.ts +++ b/backend/graphql/types/residentType.ts @@ -72,6 +72,7 @@ const residentType = gql` updateResident(userId: ID!, resident: UpdateResidentDTO!): ResidentDTO! deleteResident(userId: ID!): ResidentDTO! redeemCredits(userId: ID!, credits: Float!): RedeemCreditResponse! + setResidentInactive(userId: ID!): ResidentDTO! } `; diff --git a/backend/graphql/types/staffType.ts b/backend/graphql/types/staffType.ts index 7ab7ca5b..8b43ddf0 100644 --- a/backend/graphql/types/staffType.ts +++ b/backend/graphql/types/staffType.ts @@ -45,6 +45,7 @@ const staffType = gql` addStaff(staff: CreateStaffDTO!): StaffDTO! updateStaff(userId: ID!, staff: UpdateStaffDTO!): StaffDTO! deleteStaff(userId: ID!): StaffDTO! + setStaffInactive(userId: ID!): StaffDTO! } `; diff --git a/backend/services/implementations/residentService.ts b/backend/services/implementations/residentService.ts index c74c33d4..392edd6e 100644 --- a/backend/services/implementations/residentService.ts +++ b/backend/services/implementations/residentService.ts @@ -7,12 +7,19 @@ import IResidentService, { UpdateResidentDTO, RedeemCreditsResponse, } from "../interfaces/residentService"; +import IUserService from "../interfaces/userService"; import logger from "../../utilities/logger"; import { getErrorMessage } from "../../utilities/errorUtils"; const Logger = logger(__filename); class ResidentService implements IResidentService { + userService: IUserService; + + constructor(userService: IUserService) { + this.userService = userService; + } + async addResident(resident: CreateResidentDTO): Promise { try { const firebaseUser = await firebaseAdmin.auth().createUser({ @@ -365,6 +372,44 @@ class ResidentService implements IResidentService { throw error; } } + + async setResidentInactive(userId: number): Promise { + try { + const resident = await prisma.resident.findUnique({ + where: { userId }, + include: { user: true }, + }); + + if (!resident) { + throw new Error(`Resident with ${userId} not found.`); + } + + this.userService.setUserInactive(userId); + + 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: false, + }; + } catch (error: unknown) { + Logger.error( + `Failed to set resident inactive. Reason = ${getErrorMessage(error)}`, + ); + throw error; + } + } } export default ResidentService; diff --git a/backend/services/implementations/staffService.ts b/backend/services/implementations/staffService.ts index 8934402a..c4c531e6 100644 --- a/backend/services/implementations/staffService.ts +++ b/backend/services/implementations/staffService.ts @@ -6,12 +6,19 @@ import IStaffService, { CreateStaffDTO, UpdateStaffDTO, } from "../interfaces/staffService"; +import IUserService from "../interfaces/userService"; import logger from "../../utilities/logger"; import { getErrorMessage } from "../../utilities/errorUtils"; const Logger = logger(__filename); class StaffService implements IStaffService { + userService: IUserService; + + constructor(userService: IUserService) { + this.userService = userService; + } + async addStaff(staff: CreateStaffDTO): Promise { try { const firebaseUser = await firebaseAdmin.auth().createUser({ @@ -234,6 +241,40 @@ class StaffService implements IStaffService { throw error; } } + + async setStaffInactive(userId: number): Promise { + try { + const staff = await prisma.staff.findUnique({ + where: { userId }, + include: { user: true }, + }); + + if (!staff) { + throw new Error(`Staff with userId ${userId} not found.`); + } + + this.userService.setUserInactive(userId); + + return { + userId: staff.userId, + isAdmin: staff.isAdmin, + email: staff.user.email, + phoneNumber: staff.user.phoneNumber, + firstName: staff.user.firstName, + lastName: staff.user.lastName, + displayName: staff.user.displayName, + profilePictureURL: staff.user.profilePictureURL, + isActive: false, + }; + } catch (error: unknown) { + Logger.error( + `Failed to set staff inactive. IDs = ${userId} because ${getErrorMessage( + error, + )}`, + ); + throw error; + } + } } export default StaffService; diff --git a/backend/services/implementations/userService.ts b/backend/services/implementations/userService.ts index 012d78c2..dcf61c2e 100644 --- a/backend/services/implementations/userService.ts +++ b/backend/services/implementations/userService.ts @@ -76,6 +76,28 @@ class UserService implements IUserService { throw error; } } + + async setUserInactive(userId: number): Promise { + try { + const user = await prisma.user.update({ + where: { + id: userId, + }, + data: { + isActive: false, + }, + }); + + if (!user) { + throw new Error(`User with userId ${userId} not found.`); + } + } catch (error: unknown) { + Logger.error( + `Failed to set user inactive. Reason = ${getErrorMessage(error)}`, + ); + throw error; + } + } } export default UserService; diff --git a/backend/services/interfaces/residentService.ts b/backend/services/interfaces/residentService.ts index bb33881a..ba972de2 100644 --- a/backend/services/interfaces/residentService.ts +++ b/backend/services/interfaces/residentService.ts @@ -88,6 +88,14 @@ interface IResidentService { */ getActiveResidents(): Promise>; + /** + * Update resident with userId to inactive + * @param userId resident's id + * @returns resident's type + * @throws Error if user type retrieval fails + */ + setResidentInactive(userId: number): Promise; + /** * Redeems certain resident's credits based on resident id * @param userId: resident id whose credits are to be redeemed diff --git a/backend/services/interfaces/staffService.ts b/backend/services/interfaces/staffService.ts index a11b95f1..d89e53fa 100644 --- a/backend/services/interfaces/staffService.ts +++ b/backend/services/interfaces/staffService.ts @@ -52,6 +52,14 @@ interface IStaffService { * @throws Error if staff retrieval fails */ getStaffByIds(staffIds: number[]): Promise>; + + /** + * Update staff with staffId to inactive + * @param staffId staff ids + * @returns a StaffDTO with staff's information + * @throws Error if staff retrieval fails + */ + setStaffInactive(userId: number): Promise; } export default IStaffService; diff --git a/backend/services/interfaces/userService.ts b/backend/services/interfaces/userService.ts index 606c9520..f4a2fb06 100644 --- a/backend/services/interfaces/userService.ts +++ b/backend/services/interfaces/userService.ts @@ -70,6 +70,14 @@ interface IUserService { * @throws Error if user type retrieval fails */ getUserTypeByAuthId(authId: string): Promise; + + /** + * Update user with userId to inactive + * @param userId user's id + * @returns user's type + * @throws Error if user type retrieval fails + */ + setUserInactive(userId: number): Promise; } export default IUserService;