diff --git a/.github/workflows/firebase-hosting-merge.yml b/.github/workflows/firebase-hosting-merge.yml deleted file mode 100644 index 7234ff81..00000000 --- a/.github/workflows/firebase-hosting-merge.yml +++ /dev/null @@ -1,33 +0,0 @@ -# This file was auto-generated by the Firebase CLI -# https://github.com/firebase/firebase-tools - -name: Deploy frontend to Firebase Hosting - -on: - push: - branches: - - main - paths: - - "frontend/**" - -defaults: - run: - working-directory: frontend - -jobs: - build_and_deploy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: echo "REACT_APP_BACKEND_URL=${{ secrets.DEV_BACKEND_URL }}" > .env - auth { - - run: echo "REACT_APP_OAUTH_CLIENT_ID=${{ secrets.DEV_OAUTH_CLIENT_ID }}" >> .env - } auth - - run: rm -rf node_modules && yarn install --frozen-lockfile && yarn build - - uses: FirebaseExtended/action-hosting-deploy@v0 - with: - repoToken: "${{ secrets.GITHUB_TOKEN }}" - firebaseServiceAccount: "${{ secrets.FIREBASE_SERVICE_ACCOUNT_UW_BLUEPRINT_STARTER_CODE }}" - channelId: live - projectId: "${{ secrets.DEV_PROJECT_ID }}" - entryPoint: ./frontend diff --git a/.github/workflows/firebase-hosting-pull-request.yml b/.github/workflows/firebase-hosting-pull-request.yml deleted file mode 100644 index 8b1705af..00000000 --- a/.github/workflows/firebase-hosting-pull-request.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This file was auto-generated by the Firebase CLI -# https://github.com/firebase/firebase-tools - -name: Deploy frontend to Firebase Hosting preview - -on: - pull_request: - paths: - - "frontend/**" - -defaults: - run: - working-directory: frontend - -jobs: - build_and_preview: - if: "${{ github.event.pull_request.head.repo.full_name == github.repository && github.base_ref == 'dev' }}" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: echo "REACT_APP_BACKEND_URL=${{ secrets.PREVIEW_BACKEND_URL }}" > .env - auth { - - run: echo "REACT_APP_OAUTH_CLIENT_ID=${{ secrets.DEV_OAUTH_CLIENT_ID }}" >> .env - } auth - - run: rm -rf node_modules && yarn install --frozen-lockfile && yarn build - - uses: FirebaseExtended/action-hosting-deploy@v0 - with: - repoToken: "${{ secrets.GITHUB_TOKEN }}" - firebaseServiceAccount: "${{ secrets.FIREBASE_SERVICE_ACCOUNT_UW_BLUEPRINT_STARTER_CODE }}" - projectId: "${{ secrets.DEV_PROJECT_ID }}" - entryPoint: ./frontend diff --git a/.github/workflows/heroku-deploy-dev-ts.yml b/.github/workflows/heroku-deploy-dev-ts.yml deleted file mode 100644 index b83735e2..00000000 --- a/.github/workflows/heroku-deploy-dev-ts.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Deploy backend to Heroku dev environment - -on: - push: - branches: - - main - paths: - - "backend/**" - -jobs: - deploy: - if: ${{ false }} - runs-on: ubuntu-latest - steps: - - name: Check out repository - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - name: Set Heroku login credentials - run: | - cat > ~/.netrc < // isAuthorizedByUserType(new Set([UserType.STAFF, UserType.RESIDENT])); // const authorizedByStaff = () => // isAuthorizedByUserType(new Set([UserType.STAFF])); -const graphQLMiddlewares = { - Query: { - // getNotificationsByUserId: authorizedByAllUserTypes(), - // getNotificationById: authorizedByAllUserTypes(), - // getStaffByIds: authorizedByStaff(), - // getAllStaff: authorizedByStaff(), - // getResidentsByIds: authorizedByStaff(), - // getAllResidents: authorizedByStaff(), - // getActiveResidents: authorizedByStaff(), - // getTaskById: authorizedByAllUserTypes(), - // getTasksByType: authorizedByAllUserTypes(), - // getTasksByAssigneeId: authorizedByAllUserTypes(), - // getTasksByAssignerId: authorizedByStaff(), - // getTasksByStartDate: authorizedByAllUserTypes(), - // getTasksByEndDate: authorizedByAllUserTypes(), - // getTasksByStatus: authorizedByAllUserTypes(), - }, - Mutation: { - // login: isAuthorizedByEmail("email"), - // refresh: isAuthorizedByEmail("email"), - // logout: isAuthorizedByUserId("userId"), - // resetPassword: isAuthorizedByEmail("email"), - // sendNotification: authorizedByAllUserTypes(), - // deleteUserNotification: authorizedByStaff(), - // updateSeenNotification: authorizedByAllUserTypes(), - // sendAnnouncement: authorizedByStaff(), - // addStaff: authorizedByStaff(), - // updateStaff: authorizedByStaff(), - // deleteStaff: authorizedByStaff(), - // addResident: authorizedByStaff(), - // updateResident: authorizedByStaff(), - // deleteResident: authorizedByStaff(), - // redeemCredits: authorizedByStaff(), - // createTask: authorizedByAllUserTypes(), - // updateTask: authorizedByAllUserTypes(), - // deleteTask: authorizedByAllUserTypes(), - // assignTask: authorizedByStaff(), - // changeTaskStatus: authorizedByStaff(), - // addWarning: authorizedByStaff(), - // deleteWarning: authorizedByStaff(), - }, -}; +// const graphQLMiddlewares = { +// Query: { +// // getNotificationsByUserId: authorizedByAllUserTypes(), +// // getNotificationById: authorizedByAllUserTypes(), +// // getStaffByIds: authorizedByStaff(), +// // getAllStaff: authorizedByStaff(), +// // getResidentsByIds: authorizedByStaff(), +// // getAllResidents: authorizedByStaff(), +// // getActiveResidents: authorizedByStaff(), +// // getTaskById: authorizedByAllUserTypes(), +// // getTasksByType: authorizedByAllUserTypes(), +// // getTasksByAssigneeId: authorizedByAllUserTypes(), +// // getTasksByAssignerId: authorizedByStaff(), +// // getTasksByStartDate: authorizedByAllUserTypes(), +// // getTasksByEndDate: authorizedByAllUserTypes(), +// // getTasksByStatus: authorizedByAllUserTypes(), +// }, +// Mutation: { +// // sendNotification: authorizedByAllUserTypes(), +// // deleteUserNotification: authorizedByStaff(), +// // updateSeenNotification: authorizedByAllUserTypes(), +// // sendAnnouncement: authorizedByStaff(), +// // addStaff: authorizedByStaff(), +// // updateStaff: authorizedByStaff(), +// // deleteStaff: authorizedByStaff(), +// // addResident: authorizedByStaff(), +// // updateResident: authorizedByStaff(), +// // deleteResident: authorizedByStaff(), +// // redeemCredits: authorizedByStaff(), +// // createTask: authorizedByAllUserTypes(), +// // updateTask: authorizedByAllUserTypes(), +// // deleteTask: authorizedByAllUserTypes(), +// // assignTask: authorizedByStaff(), +// // changeTaskStatus: authorizedByStaff(), +// }, +// }; -export default applyMiddleware(executableSchema, graphQLMiddlewares); +// export default applyMiddleware(executableSchema, graphQLMiddlewares); diff --git a/backend/graphql/resolvers/announcementResolver.ts b/backend/graphql/resolvers/announcementResolver.ts new file mode 100644 index 00000000..b1d63a47 --- /dev/null +++ b/backend/graphql/resolvers/announcementResolver.ts @@ -0,0 +1,134 @@ +// 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"; + +// const residentService: IResidentService = new ResidentService(); +// const notificationService: INotificationService = new NotificationService( +// residentService, +// ); + +// const notificationResolvers = { +// Query: { +// getNotificationsByIds: async ( +// _parent: undefined, +// { notificationIds }: { notificationIds: string[] }, +// ): Promise => { +// const notificationReceived = await notificationService.getNotificationsByIds( +// notificationIds.map(Number), +// ); +// return notificationReceived; +// }, +// getNotificationByResident: async ( +// _parent: undefined, +// { residentId }: { residentId: string }, +// ): Promise => { +// const notificationReceived = await notificationService.getNotificationByResident( +// Number(residentId), +// ); +// return notificationReceived; +// }, +// getAllGroupsAndNotifications: async (): Promise => { +// const notificationGroups = await notificationService.getAllGroupsAndNotifications(); +// return notificationGroups; +// }, +// }, +// Mutation: { +// createNotificationGroup: async ( +// _parent: undefined, +// { +// roomIds, +// }: { +// roomIds: number[]; +// }, +// ): Promise => { +// const ids = roomIds.map((id) => Number(id)); +// const newNotificationGroup = await notificationService.createNotificationGroup( +// ids, +// ); +// return newNotificationGroup; +// }, +// createAnnouncementGroup: async (): Promise => { +// const newNotificationGroup = await notificationService.createAnnouncementGroup(); +// return newNotificationGroup; +// }, +// sendNotificationToGroup: async ( +// _parent: undefined, +// { +// groupId, +// notification, +// }: { +// groupId: number; +// notification: CreateNotificationDTO; +// }, +// ): Promise => { +// const newNotification = await notificationService.sendNotificationToGroup( +// Number(groupId), +// notification, +// ); +// return newNotification; +// }, +// deleteNotificationGroup: async ( +// _parent: undefined, +// { +// groupId, +// }: { +// groupId: number; +// }, +// ): Promise => { +// const deletedGroup = await notificationService.deleteNotificationGroup( +// Number(groupId), +// ); +// return deletedGroup; +// }, +// updateNotificationById: async ( +// _parent: undefined, +// { +// notificationId, +// notification, +// }: { +// notificationId: number; +// notification: UpdateNotificationDTO; +// }, +// ): Promise => { +// const updatedNotification = await notificationService.updateNotificationById( +// Number(notificationId), +// notification, +// ); +// return updatedNotification; +// }, +// deleteNotificationByIds: async ( +// _parent: undefined, +// { +// 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 updatedNotificationReceived; +// }, +// }, +// }; + +// export default notificationResolvers; diff --git a/backend/graphql/resolvers/authResolvers.ts b/backend/graphql/resolvers/authResolvers.ts deleted file mode 100644 index 4d1b1034..00000000 --- a/backend/graphql/resolvers/authResolvers.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { CookieOptions, Request, Response } from "express"; - -import { UserType } from "../../prisma"; -import nodemailerConfig from "../../nodemailer.config"; -import AuthService from "../../services/implementations/authService"; -import EmailService from "../../services/implementations/emailService"; -import UserService from "../../services/implementations/userService"; -import IAuthService, { AuthDTO } from "../../services/interfaces/authService"; -import IEmailService from "../../services/interfaces/emailService"; -import IUserService from "../../services/interfaces/userService"; - -const userService: IUserService = new UserService(); -const emailService: IEmailService = new EmailService(nodemailerConfig); -const authService: IAuthService = new AuthService(userService, emailService); - -const cookieOptions: CookieOptions = { - httpOnly: true, - sameSite: process.env.PREVIEW_DEPLOY ? "none" : "strict", - secure: process.env.NODE_ENV === "production", -}; - -const authResolvers = { - Mutation: { - login: async ( - _parent: undefined, - { - email, - password, - userType, - }: { email: string; password: string; userType: UserType }, - { res }: { res: Response }, - ): Promise> => { - const authDTO = await authService.generateToken( - email, - password, - userType, - ); - const { refreshToken, ...user } = authDTO; - res.cookie("refreshToken", refreshToken, cookieOptions); - return user; - }, - refresh: async ( - _parent: undefined, - _args: Record, - { req, res }: { req: Request; res: Response }, - ): Promise => { - const token = await authService.renewToken(req.cookies.refreshToken); - res.cookie("refreshToken", token.refreshToken, cookieOptions); - return token.accessToken; - }, - logout: async ( - _parent: undefined, - { userId }: { userId: string }, - ): Promise => { - await authService.revokeTokens(userId); - }, - resetPassword: async ( - _parent: undefined, - { email }: { email: string }, - ): Promise => { - await authService.resetPassword(email); - return true; - }, - }, -}; - -export default authResolvers; diff --git a/backend/graphql/resolvers/miscResolver.ts b/backend/graphql/resolvers/miscResolver.ts new file mode 100644 index 00000000..cbaf7c87 --- /dev/null +++ b/backend/graphql/resolvers/miscResolver.ts @@ -0,0 +1,13 @@ +import MiscService from "../../services/implementation/miscImplementation"; +import IMiscService from "../../services/interface/miscInterface"; + +const miscService: IMiscService = new MiscService(); +const miscResolvers = { + Query: { + getAvailableRooms: async (): Promise => { + return miscService.getAvailableRooms(); + }, + }, +}; + +export default miscResolvers; diff --git a/backend/graphql/resolvers/notificationResolvers.ts b/backend/graphql/resolvers/notificationResolvers.ts deleted file mode 100644 index 351b304f..00000000 --- a/backend/graphql/resolvers/notificationResolvers.ts +++ /dev/null @@ -1,134 +0,0 @@ -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"; - -const residentService: IResidentService = new ResidentService(); -const notificationService: INotificationService = new NotificationService( - residentService, -); - -const notificationResolvers = { - Query: { - getNotificationsByIds: async ( - _parent: undefined, - { notificationIds }: { notificationIds: string[] }, - ): Promise => { - const notificationReceived = await notificationService.getNotificationsByIds( - notificationIds.map(Number), - ); - return notificationReceived; - }, - getNotificationByResident: async ( - _parent: undefined, - { residentId }: { residentId: string }, - ): Promise => { - const notificationReceived = await notificationService.getNotificationByResident( - Number(residentId), - ); - return notificationReceived; - }, - getAllGroupsAndNotifications: async (): Promise => { - const notificationGroups = await notificationService.getAllGroupsAndNotifications(); - return notificationGroups; - }, - }, - Mutation: { - createNotificationGroup: async ( - _parent: undefined, - { - roomIds, - }: { - roomIds: number[]; - }, - ): Promise => { - const ids = roomIds.map((id) => Number(id)); - const newNotificationGroup = await notificationService.createNotificationGroup( - ids, - ); - return newNotificationGroup; - }, - createAnnouncementGroup: async (): Promise => { - const newNotificationGroup = await notificationService.createAnnouncementGroup(); - return newNotificationGroup; - }, - sendNotificationToGroup: async ( - _parent: undefined, - { - groupId, - notification, - }: { - groupId: number; - notification: CreateNotificationDTO; - }, - ): Promise => { - const newNotification = await notificationService.sendNotificationToGroup( - Number(groupId), - notification, - ); - return newNotification; - }, - deleteNotificationGroup: async ( - _parent: undefined, - { - groupId, - }: { - groupId: number; - }, - ): Promise => { - const deletedGroup = await notificationService.deleteNotificationGroup( - Number(groupId), - ); - return deletedGroup; - }, - updateNotificationById: async ( - _parent: undefined, - { - notificationId, - notification, - }: { - notificationId: number; - notification: UpdateNotificationDTO; - }, - ): Promise => { - const updatedNotification = await notificationService.updateNotificationById( - Number(notificationId), - notification, - ); - return updatedNotification; - }, - deleteNotificationByIds: async ( - _parent: undefined, - { - 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 updatedNotificationReceived; - }, - }, -}; - -export default notificationResolvers; diff --git a/backend/graphql/resolvers/participantResolver.ts b/backend/graphql/resolvers/participantResolver.ts new file mode 100644 index 00000000..c3697a6e --- /dev/null +++ b/backend/graphql/resolvers/participantResolver.ts @@ -0,0 +1,112 @@ +import { Participant } from "@prisma/client"; +import ParticipantService from "../../services/implementation/participantImplementation"; +import IParticipantService from "../../services/interface/participantInterface"; + +const participantService: IParticipantService = new ParticipantService(); +const participantResolvers = { + Query: { + getAllParticipants: async (): Promise => { + return participantService.getAllParticipants(); + }, + getParticipantById: async ( + _parent: undefined, + { participantId }: { participantId: string }, + ): Promise => { + return participantService.getParticipantById(participantId); + }, + }, + Mutation: { + createParticipant: async ( + _parent: undefined, + { + participantId, + roomNumber, + arrival, + password, + }: { + participantId: string; + roomNumber: number; + arrival: string; + password: string; + }, + ): Promise => { + return participantService.createParticipant( + participantId, + roomNumber, + arrival, + password, + ); + }, + }, +}; + +export default participantResolvers; + +// import IResidentService, { +// ResidentDTO, +// CreateResidentDTO, +// UpdateResidentDTO, +// RedeemCreditsResponse, +// } from "../../services/interface/residentService"; + +// const residentResolvers = { +// Query: { +// getResidentsByIds: async ( +// _parent: undefined, +// { userIds }: { userIds: string[] }, +// ): Promise> => { +// return residentService.getResidentsByIds(userIds.map(Number)); +// }, +// getAllResidents: async (): Promise> => { +// return residentService.getAllResidents(); +// }, +// getActiveResidents: async (): Promise => { +// const activeResidents = await residentService.getActiveResidents(); +// return activeResidents; +// }, +// }, +// Mutation: { +// addResident: async ( +// _parent: undefined, +// { +// resident, +// }: { +// resident: CreateResidentDTO; +// }, +// ): Promise => { +// const newResident = await residentService.addResident(resident); +// return newResident; +// }, +// updateResident: async ( +// _parent: undefined, +// { +// userId, +// resident, +// }: { +// userId: string; +// resident: UpdateResidentDTO; +// }, +// ): Promise => { +// const newResident = await residentService.updateResident( +// parseInt(userId, 10), +// resident, +// ); +// return newResident; +// }, +// deleteResident: async ( +// _parent: undefined, +// { userId }: { userId: string }, +// ): Promise => { +// const deletedResident = await residentService.deleteResident( +// parseInt(userId, 10), +// ); +// return deletedResident; +// }, +// redeemCredits: async ( +// _parent: undefined, +// { userId, credits }: { userId: string; credits: number }, +// ): Promise => { +// return residentService.redeemCredits(parseInt(userId, 10), credits); +// }, +// }, +// }; diff --git a/backend/graphql/resolvers/residentResolvers.ts b/backend/graphql/resolvers/residentResolvers.ts deleted file mode 100644 index cad94fc5..00000000 --- a/backend/graphql/resolvers/residentResolvers.ts +++ /dev/null @@ -1,73 +0,0 @@ -import ResidentService from "../../services/implementations/residentService"; -import IResidentService, { - ResidentDTO, - CreateResidentDTO, - UpdateResidentDTO, - RedeemCreditsResponse, -} from "../../services/interfaces/residentService"; - -const residentService: IResidentService = new ResidentService(); - -const residentResolvers = { - Query: { - getResidentsByIds: async ( - _parent: undefined, - { userIds }: { userIds: string[] }, - ): Promise> => { - return residentService.getResidentsByIds(userIds.map(Number)); - }, - getAllResidents: async (): Promise> => { - return residentService.getAllResidents(); - }, - getActiveResidents: async (): Promise => { - const activeResidents = await residentService.getActiveResidents(); - return activeResidents; - }, - }, - Mutation: { - addResident: async ( - _parent: undefined, - { - resident, - }: { - resident: CreateResidentDTO; - }, - ): Promise => { - const newResident = await residentService.addResident(resident); - return newResident; - }, - updateResident: async ( - _parent: undefined, - { - userId, - resident, - }: { - userId: string; - resident: UpdateResidentDTO; - }, - ): Promise => { - const newResident = await residentService.updateResident( - parseInt(userId, 10), - resident, - ); - return newResident; - }, - deleteResident: async ( - _parent: undefined, - { userId }: { userId: string }, - ): Promise => { - const deletedResident = await residentService.deleteResident( - parseInt(userId, 10), - ); - return deletedResident; - }, - redeemCredits: async ( - _parent: undefined, - { userId, credits }: { userId: string; credits: number }, - ): Promise => { - return residentService.redeemCredits(parseInt(userId, 10), credits); - }, - }, -}; - -export default residentResolvers; diff --git a/backend/graphql/resolvers/staffResolver.ts b/backend/graphql/resolvers/staffResolver.ts deleted file mode 100644 index 425da314..00000000 --- a/backend/graphql/resolvers/staffResolver.ts +++ /dev/null @@ -1,56 +0,0 @@ -import StaffService from "../../services/implementations/staffService"; -import IStaffService, { - StaffDTO, - CreateStaffDTO, - UpdateStaffDTO, -} from "../../services/interfaces/staffService"; - -const staffService: IStaffService = new StaffService(); - -const staffResolvers = { - Query: { - getStaffByIds: async ( - _parent: undefined, - { userIds }: { userIds: string[] }, - ): Promise> => { - return staffService.getStaffByIds(userIds.map(Number)); - }, - getAllStaff: async (): Promise> => { - return staffService.getAllStaff(); - }, - }, - Mutation: { - addStaff: async ( - _parent: undefined, - { staff }: { staff: CreateStaffDTO }, - ): Promise => { - const newStaff = await staffService.addStaff(staff); - return newStaff; - }, - updateStaff: async ( - _parent: undefined, - { - userId, - staff, - }: { - userId: string; - staff: UpdateStaffDTO; - }, - ): Promise => { - const newStaff = await staffService.updateStaff( - parseInt(userId, 10), - staff, - ); - return newStaff; - }, - deleteStaff: async ( - _parent: undefined, - { userId }: { userId: string }, - ): Promise => { - const deletedStaff = await staffService.deleteStaff(parseInt(userId, 10)); - return deletedStaff; - }, - }, -}; - -export default staffResolvers; diff --git a/backend/graphql/resolvers/taskResolver.ts b/backend/graphql/resolvers/taskResolver.ts new file mode 100644 index 00000000..8c7c3f94 --- /dev/null +++ b/backend/graphql/resolvers/taskResolver.ts @@ -0,0 +1,106 @@ +// import { Status, TaskType } from "../../prisma"; +// import TaskService from "../../services/implementations/taskService"; +// import ITaskService, { +// TaskDTO, +// InputTaskDTO, +// InputTaskAssignedDTO, +// TaskAssignedDTO, +// } from "../../services/interfaces/taskService"; + +// const taskService: ITaskService = new TaskService(); + +// const taskResolvers = { +// Query: { +// getTaskById: async ( +// _parent: undefined, +// { taskId }: { taskId: number }, +// ): Promise => { +// const task = await taskService.getTaskById(taskId); +// return task; +// }, +// getTasksByType: async ( +// _parent: undefined, +// { type }: { type: TaskType }, +// ): Promise> => { +// const tasks = await taskService.getTasksByType(type); +// return tasks; +// }, +// getTasksByAssigneeId: async ( +// _parent: undefined, +// { assigneeId }: { assigneeId: number }, +// ): Promise => { +// const tasks = await taskService.getTasksByAssigneeId(assigneeId); +// return tasks; +// }, +// getTasksByAssignerId: async ( +// _parent: undefined, +// { assignerId }: { assignerId: number }, +// ): Promise => { +// const tasks = await taskService.getTasksByAssignerId(assignerId); +// return tasks; +// }, +// getTasksByStartDate: async ( +// _parent: undefined, +// { startDate }: { startDate: Date }, +// ): Promise => { +// const tasks = await taskService.getTasksByStartDate(startDate); +// return tasks; +// }, +// // getTasksByEndDate: async ( +// // _parent: undefined, +// // { endDate }: { endDate: Date }, +// // ): Promise => { +// // const tasks = await taskService.getTasksByEndDate(endDate); +// // return tasks; +// // }, +// getTasksByStatus: async ( +// _parent: undefined, +// { status }: { status: Status }, +// ): Promise => { +// const tasks = await taskService.getTasksByStatus(status); +// return tasks; +// }, +// }, +// Mutation: { +// createTask: async ( +// _parent: undefined, +// { task }: { task: InputTaskDTO }, +// ): Promise => { +// const newTask = await taskService.createTask(task); +// return newTask; +// }, +// updateTask: async ( +// _parent: undefined, +// { taskId, task }: { taskId: number; task: InputTaskDTO }, +// ): Promise => { +// const updatedTask = await taskService.updateTaskById(taskId, task); +// return updatedTask; +// }, +// deleteTask: async ( +// _parent: undefined, +// { taskId }: { taskId: number }, +// ): Promise => { +// const deletedTask = await taskService.deleteTaskById(taskId); +// return deletedTask; +// }, +// assignTask: async ( +// _parent: undefined, +// { taskAssigned }: { taskAssigned: InputTaskAssignedDTO }, +// ): Promise => { +// const newTask = await taskService.assignTask(taskAssigned); +// return newTask; +// }, +// changeTaskStatus: async ( +// _parent: undefined, +// { taskAssignedId, status }: { taskAssignedId: number; status: Status }, +// ): Promise => { +// const updatedTask = await taskService.changeTaskStatus( +// taskAssignedId, +// status, +// ); +// return updatedTask; +// }, +// }, +// }; + +// export default taskResolvers; diff --git a/backend/graphql/resolvers/taskResolvers.ts b/backend/graphql/resolvers/taskResolvers.ts deleted file mode 100644 index f80c5874..00000000 --- a/backend/graphql/resolvers/taskResolvers.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { Status, TaskType } from "../../prisma"; -import TaskService from "../../services/implementations/taskService"; -import ITaskService, { - TaskDTO, - InputTaskDTO, - InputTaskAssignedDTO, - TaskAssignedDTO, -} from "../../services/interfaces/taskService"; - -const taskService: ITaskService = new TaskService(); - -const taskResolvers = { - Query: { - getTaskById: async ( - _parent: undefined, - { taskId }: { taskId: number }, - ): Promise => { - const task = await taskService.getTaskById(taskId); - return task; - }, - getTasksByType: async ( - _parent: undefined, - { type }: { type: TaskType }, - ): Promise> => { - const tasks = await taskService.getTasksByType(type); - return tasks; - }, - getTasksByAssigneeId: async ( - _parent: undefined, - { assigneeId }: { assigneeId: number }, - ): Promise => { - const tasks = await taskService.getTasksByAssigneeId(assigneeId); - return tasks; - }, - getTasksByAssignerId: async ( - _parent: undefined, - { assignerId }: { assignerId: number }, - ): Promise => { - const tasks = await taskService.getTasksByAssignerId(assignerId); - return tasks; - }, - getTasksByStartDate: async ( - _parent: undefined, - { startDate }: { startDate: Date }, - ): Promise => { - const tasks = await taskService.getTasksByStartDate(startDate); - return tasks; - }, - // getTasksByEndDate: async ( - // _parent: undefined, - // { endDate }: { endDate: Date }, - // ): Promise => { - // const tasks = await taskService.getTasksByEndDate(endDate); - // return tasks; - // }, - getTasksByStatus: async ( - _parent: undefined, - { status }: { status: Status }, - ): Promise => { - const tasks = await taskService.getTasksByStatus(status); - return tasks; - }, - }, - Mutation: { - createTask: async ( - _parent: undefined, - { task }: { task: InputTaskDTO }, - ): Promise => { - const newTask = await taskService.createTask(task); - return newTask; - }, - updateTask: async ( - _parent: undefined, - { taskId, task }: { taskId: number; task: InputTaskDTO }, - ): Promise => { - const updatedTask = await taskService.updateTaskById(taskId, task); - return updatedTask; - }, - deleteTask: async ( - _parent: undefined, - { taskId }: { taskId: number }, - ): Promise => { - const deletedTask = await taskService.deleteTaskById(taskId); - return deletedTask; - }, - assignTask: async ( - _parent: undefined, - { taskAssigned }: { taskAssigned: InputTaskAssignedDTO }, - ): Promise => { - const newTask = await taskService.assignTask(taskAssigned); - return newTask; - }, - changeTaskStatus: async ( - _parent: undefined, - { taskAssignedId, status }: { taskAssignedId: number; status: Status }, - ): Promise => { - const updatedTask = await taskService.changeTaskStatus( - taskAssignedId, - status, - ); - return updatedTask; - }, - }, -}; - -export default taskResolvers; diff --git a/backend/graphql/resolvers/warningResolvers.ts b/backend/graphql/resolvers/warningResolvers.ts deleted file mode 100644 index a0376d4d..00000000 --- a/backend/graphql/resolvers/warningResolvers.ts +++ /dev/null @@ -1,28 +0,0 @@ -import WarningService from "../../services/implementations/warningService"; -import IWarningService, { - WarningDTO, - CreateWarningDTO, -} from "../../services/interfaces/warningService"; - -const warningService: IWarningService = new WarningService(); - -const warningResolvers = { - Mutation: { - addWarning: async ( - _parent: undefined, - { warning }: { warning: CreateWarningDTO }, - ): Promise => { - const newWarning = await warningService.addWarning(warning); - return newWarning; - }, - deleteWarning: async ( - _parent: undefined, - { id }: { id: number }, - ): Promise => { - const deletedWarning = await warningService.deleteWarning(id); - return deletedWarning; - }, - }, -}; - -export default warningResolvers; diff --git a/backend/graphql/types/authType.ts b/backend/graphql/types/authType.ts deleted file mode 100644 index a2f8f029..00000000 --- a/backend/graphql/types/authType.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { gql } from "apollo-server-express"; - -const authType = gql` - enum UserType { - STAFF - RESIDENT - } - - type AuthDTO { - id: ID! - type: UserType! - email: String! - firstName: String! - lastName: String! - accessToken: String! - } - - extend type Mutation { - login(email: String!, password: String!, userType: UserType!): AuthDTO! - refresh: String! - logout(userId: ID!): ID - resetPassword(email: String!): Boolean! - } -`; - -export default authType; diff --git a/backend/graphql/types/enums.ts b/backend/graphql/types/enums.ts new file mode 100644 index 00000000..3fe4d954 --- /dev/null +++ b/backend/graphql/types/enums.ts @@ -0,0 +1,24 @@ +import { gql } from "apollo-server-express"; + +const customTypes = gql` + enum StaffType { + ADMIN + RELIEF + } + + enum TaskType { + REQUIRED + OPTIONAL + } + + enum TaskStatus { + UNASSIGNED + ASSIGNED + INCOMPLETE + PENDING + COMPLETE + EXCUSED + } +`; + +export default customTypes; diff --git a/backend/graphql/types/models.ts b/backend/graphql/types/models.ts new file mode 100644 index 00000000..dd19be05 --- /dev/null +++ b/backend/graphql/types/models.ts @@ -0,0 +1,35 @@ +import { gql } from "apollo-server-express"; + +const dataModels = gql` + type Participant { + participantId: String! + roomNumber: Int! + arrival: String! + departure: String + password: String! + credit: Int! + } + + type Announcement { + announcementId: Int! + from: StaffType! + to: [Int!]! + createdAt: DateTime! + message: String! + } + + type Task { + taskId: Int! + roomNumber: Int + type: TaskType! + status: TaskStatus! + name: String! + isRecurring: Boolean! + start: DateTime! + end: DateTime! + credit: Int! + comment: String + } +`; + +export default dataModels; diff --git a/backend/graphql/types/notificationType.ts b/backend/graphql/types/notificationType.ts deleted file mode 100644 index 39190c77..00000000 --- a/backend/graphql/types/notificationType.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { gql } from "apollo-server-express"; - -const notificationType = gql` - type NotificationDTO { - id: ID! - message: String! - createdAt: DateTime - authorId: Int - recipients: [NotificationReceivedDTO] - } - - type NotificationGroupDTO { - id: ID! - recipients: [ResidentDTO!] - notifications: [NotificationDTO!] - announcementGroup: Boolean! - } - - type NotificationReceivedDTO { - id: ID! - notificationId: Int! - notification: NotificationDTO - recipientId: Int! - seen: Boolean! - } - - input UpdateNotificationDTO { - authorId: Int - message: String - createdAt: DateTime - } - - input CreateNotificationDTO { - authorId: Int - message: String! - createdAt: DateTime - } - - extend type Query { - getNotificationsByIds(notificationIds: [ID!]): [NotificationReceivedDTO!] - getNotificationByResident(residentId: Int!): [NotificationReceivedDTO!] - getAllGroupsAndNotifications: [NotificationGroupDTO!] - } - - extend type Mutation { - createNotificationGroup(roomIds: [Int!]): NotificationGroupDTO! - createAnnouncementGroup: NotificationGroupDTO! - sendNotificationToGroup( - groupId: ID! - notification: CreateNotificationDTO! - ): NotificationDTO! - deleteNotificationGroup(groupId: ID!): NotificationGroupDTO! - updateNotificationById( - notificationId: ID! - notification: UpdateNotificationDTO! - ): NotificationDTO! - deleteNotificationByIds(notificationIds: [ID!]): Boolean! - updateSeenNotification(notificationSeenId: ID!): NotificationReceivedDTO! - } -`; - -export default notificationType; diff --git a/backend/graphql/types/residentType.ts b/backend/graphql/types/residentType.ts deleted file mode 100644 index 39d62be9..00000000 --- a/backend/graphql/types/residentType.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { gql } from "apollo-server-express"; - -// TODO: Look into custom types for dates and date time types - -const residentType = gql` - type ResidentDTO { - userId: Int! - residentId: Int! - roomNumber: Int! - credits: Float! - dateJoined: Date! - dateLeft: Date - notificationGroup: [NotificationGroupDTO!]! - notificationRecieved: [NotificationReceivedDTO]! - } - - input CreateResidentDTO { - password: String! - residentId: Int! - roomNumber: Int! - credits: Float - dateJoined: Date - dateLeft: Date - } - - input UpdateResidentDTO { - residentId: Int - roomNumber: Int - credits: Float - dateJoined: Date - dateLeft: Date - } - - enum RedeemCreditResponse { - SUCCESS - NOT_ENOUGH_CREDITS - INVALID_ID - } - - extend type Query { - getResidentsByIds(userIds: [ID!]): [ResidentDTO!] - getAllResidents: [ResidentDTO!] - getActiveResidents: [ResidentDTO!] - } - - extend type Mutation { - addResident(resident: CreateResidentDTO!): ResidentDTO! - updateResident(userId: ID!, resident: UpdateResidentDTO!): ResidentDTO! - deleteResident(userId: ID!): ResidentDTO! - redeemCredits(userId: ID!, credits: Float!): RedeemCreditResponse! - } -`; - -export default residentType; diff --git a/backend/graphql/types/resolvers.ts b/backend/graphql/types/resolvers.ts new file mode 100644 index 00000000..b252c2d5 --- /dev/null +++ b/backend/graphql/types/resolvers.ts @@ -0,0 +1,20 @@ +import { gql } from "apollo-server-express"; + +const resolverTypes = gql` + type Query { + getAllParticipants: [Participant] + getParticipantById(participantId: String): Participant + getAvailableRooms: [Int] + } + + type Mutation { + createParticipant( + participantId: String + roomNumber: Int + arrival: String + password: String + ): Boolean + } +`; + +export default resolverTypes; diff --git a/backend/graphql/types/staffType.ts b/backend/graphql/types/staffType.ts deleted file mode 100644 index 3c204717..00000000 --- a/backend/graphql/types/staffType.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { gql } from "apollo-server-express"; - -const staffType = gql` - type StaffDTO { - userId: ID! - email: String! - phoneNumber: String - firstName: String! - lastName: String! - isAdmin: Boolean! - } - - input CreateStaffDTO { - email: String! - password: String! - phoneNumber: String - firstName: String! - lastName: String! - isAdmin: Boolean! - } - - input UpdateStaffDTO { - email: String - password: String - phoneNumber: String - firstName: String - lastName: String - isAdmin: Boolean - } - - extend type Query { - getStaffByIds(userIds: [ID!]): [StaffDTO!] - getAllStaff: [StaffDTO!] - } - - extend type Mutation { - addStaff(staff: CreateStaffDTO!): StaffDTO! - updateStaff(userId: ID!, staff: UpdateStaffDTO!): StaffDTO! - deleteStaff(userId: ID!): StaffDTO! - } -`; - -export default staffType; diff --git a/backend/graphql/types/taskType.ts b/backend/graphql/types/taskType.ts deleted file mode 100644 index 4a84242d..00000000 --- a/backend/graphql/types/taskType.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { gql } from "apollo-server-express"; - -const taskType = gql` - enum Status { - PENDING_APPROVAL - ASSIGNED - INCOMPLETE - COMPLETE - EXCUSED - } - - enum Recurrence_Frequency { - ONE_TIME - REPEATS_PER_WEEK_SELECTED - REPEATS_PER_WEEK_ONCE - } - - enum DaysOfWeek { - MONDAY - TUESDAY - WEDNESDAY - THURSDAY - FRIDAY - SATURDAY - SUNDAY - } - - enum TaskType { - REQUIRED - OPTIONAL - CHORE - ACHIEVEMENT - } - - type TaskDTO { - id: Int! - type: TaskType! - title: String! - description: String - creditValue: Int! - tasksAssigned: [TaskAssignedDTO!] - endDate: DateTime - recurrenceFrequency: Recurrence_Frequency! - specificDay: DaysOfWeek - repeatDays: [DaysOfWeek!] - } - - input InputTaskDTO { - type: TaskType! - title: String! - description: String - creditValue: Int! - endDate: DateTime - recurrenceFrequency: Recurrence_Frequency! - specificDay: DaysOfWeek - repeatDays: [DaysOfWeek!] - } - - input InputTaskAssignedDTO { - taskId: Int! - assigneeId: Int! - assignerId: Int! - status: Status! - startDate: DateTime! - comments: String - } - - type TaskAssignedDTO { - id: Int! - taskId: Int! - assigneeId: Int! - assignerId: Int! - status: Status! - startDate: DateTime! - comments: String - } - - extend type Query { - getTaskById(taskId: Int!): TaskDTO! - getTasksByType(type: TaskType!): [TaskDTO!] - getTasksByAssigneeId(assigneeId: Int!): [TaskAssignedDTO] - getTasksByAssignerId(assignerId: Int!): [TaskAssignedDTO] - getTasksByStartDate(startDate: DateTime!): [TaskAssignedDTO] - # getTasksByEndDate(endDate: DateTime!): [TaskAssignedDTO] - getTasksByStatus(status: Status!): [TaskAssignedDTO] - } - - extend type Mutation { - createTask(task: InputTaskDTO!): TaskDTO! - updateTask(taskId: Int, task: InputTaskDTO!): TaskDTO! - deleteTask(taskId: Int): TaskDTO! - assignTask(taskAssigned: InputTaskAssignedDTO): TaskAssignedDTO - changeTaskStatus(taskAssignedId: Int!, status: Status!): TaskAssignedDTO - } -`; - -export default taskType; diff --git a/backend/graphql/types/tempTaskType.ts b/backend/graphql/types/tempTaskType.ts deleted file mode 100644 index 3433c5d1..00000000 --- a/backend/graphql/types/tempTaskType.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { gql } from "apollo-server-express"; - -const taskType = gql` - enum Status { - PENDING_APPROVAL - ASSIGNED - INCOMPLETE - COMPLETE - EXCUSED - } - - enum Recurrence_Frequency { - ONE_TIME - REPEATS_PER_WEEK_SELECTED - REPEATS_PER_WEEK_ONCE - } - - enum DaysOfWeek { - MONDAY - TUESDAY - WEDNESDAY - THURSDAY - FRIDAY - SATURDAY - SUNDAY - } - - enum TaskType { - REQUIRED - OPTIONAL - CHORE - ACHIEVEMENT - } - - type TaskDTO { - id: Int! - type: TaskType! - title: String! - description: String! - creditValue: Int! - location: TaskLocationDTO! - tasksAssigned: [TaskAssignedDTO!] - endDate: DateTime - recurrenceFrequency: Recurrence_Frequency! - specificDay: DaysOfWeek - repeatDays: [DaysOfWeek!] - } - - type TaskLocationDTO { - id: Int! - title: String! - description: String! - } - - input InputTaskDTO { - type: TaskType! - title: String! - description: String! - creditValue: Int! - locationId: Int! - endDate: DateTime - recurrenceFrequency: Recurrence_Frequency! - specificDay: DaysOfWeek - repeatDays: [DaysOfWeek!] - } - - input InputTaskAssignedDTO { - taskId: Int! - assigneeId: Int! - assignerId: Int! - status: Status! - startDate: DateTime! - comments: String - } - - type TaskAssignedDTO { - id: Int! - taskId: Int! - assigneeId: Int! - assignerId: Int! - status: Status! - startDate: DateTime! - comments: String - } - - extend type Query { - getTaskById(taskId: Int!): TaskDTO! - getTasksByType(type: TaskType!): [TaskDTO!] - getTasksByAssigneeId(assigneeId: Int!): [TaskAssignedDTO] - getTasksByAssignerId(assignerId: Int!): [TaskAssignedDTO] - getTasksByStartDate(startDate: DateTime!): [TaskAssignedDTO] - # getTasksByEndDate(endDate: DateTime!): [TaskAssignedDTO] - getTasksByStatus(status: Status!): [TaskAssignedDTO] - } - - extend type Mutation { - createTask(task: InputTaskDTO!): TaskDTO! - updateTask(taskId: Int, task: InputTaskDTO!): TaskDTO! - deleteTask(taskId: Int): TaskDTO! - assignTask(taskAssigned: InputTaskAssignedDTO): TaskAssignedDTO - changeTaskStatus(taskAssignedId: Int!, status: Status!): TaskAssignedDTO - } -`; - -export default taskType; diff --git a/backend/graphql/types/warningType.ts b/backend/graphql/types/warningType.ts deleted file mode 100644 index f02dc78b..00000000 --- a/backend/graphql/types/warningType.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { gql } from "apollo-server-express"; - -const warningType = gql` - type WarningDTO { - id: Int! - title: String! - description: String! - dateIssued: Date - assigneeId: Int! - assignerId: Int - relatedTaskId: Int - } - - input CreateWarningDTO { - title: String! - description: String! - dateIssued: Date - assigneeId: Int! - assignerId: Int - relatedTaskId: Int - } - - extend type Mutation { - addWarning(warning: CreateWarningDTO!): WarningDTO! - deleteWarning(id: Int!): WarningDTO! - } -`; - -export default warningType; diff --git a/backend/jest.config.ts b/backend/jest.config.ts deleted file mode 100644 index 0dbcd906..00000000 --- a/backend/jest.config.ts +++ /dev/null @@ -1,189 +0,0 @@ -/* - * For a detailed explanation regarding each configuration property and type check, visit: - * https://jestjs.io/docs/configuration - */ - -export default { - // All imported modules in your tests should be mocked automatically - // automock: false, - - // Stop running tests after `n` failures - // bail: 0, - - // The directory where Jest should store its cached dependency information - // cacheDirectory: "/private/var/folders/k2/ck3w088577zd4cwb6ctnwh8w0000gn/T/jest_dx", - - // Automatically clear mock calls and instances between every test - clearMocks: true, - - // Indicates whether the coverage information should be collected while executing the test - collectCoverage: false, - - // An array of glob patterns indicating a set of files for which coverage information should be collected - // collectCoverageFrom: undefined, - - // The directory where Jest should output its coverage files - // coverageDirectory: "coverage", - - // An array of regexp pattern strings used to skip coverage collection - // coveragePathIgnorePatterns: [ - // "/node_modules/" - // ], - - // Indicates which provider should be used to instrument code for coverage - // coverageProvider: "babel", - - // A list of reporter names that Jest uses when writing coverage reports - // coverageReporters: [ - // "json", - // "text", - // "lcov", - // "clover" - // ], - - // An object that configures minimum threshold enforcement for coverage results - // coverageThreshold: undefined, - - // A path to a custom dependency extractor - // dependencyExtractor: undefined, - - // Make calling deprecated APIs throw helpful error messages - // errorOnDeprecated: false, - - // Force coverage collection from ignored files using an array of glob patterns - // forceCoverageMatch: [], - - // A path to a module which exports an async function that is triggered once before all test suites - // globalSetup: undefined, - - // A path to a module which exports an async function that is triggered once after all test suites - // globalTeardown: undefined, - - // A set of global variables that need to be available in all test environments - // globals: {}, - - // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers. - // maxWorkers: "50%", - - // An array of directory names to be searched recursively up from the requiring module's location - // moduleDirectories: [ - // "node_modules" - // ], - - // An array of file extensions your modules use - // moduleFileExtensions: [ - // "js", - // "jsx", - // "ts", - // "tsx", - // "json", - // "node" - // ], - - // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module - // moduleNameMapper: {}, - - // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader - // modulePathIgnorePatterns: [], - - // Activates notifications for test results - // notify: false, - - // An enum that specifies notification mode. Requires { notify: true } - // notifyMode: "failure-change", - - // A preset that is used as a base for Jest's configuration - // preset: undefined, - - // Run tests from one or more projects - // projects: undefined, - - // Use this configuration option to add custom reporters to Jest - // reporters: undefined, - - // Automatically reset mock state between every test - // resetMocks: false, - - // Reset the module registry before running each individual test - // resetModules: false, - - // A path to a custom resolver - // resolver: undefined, - - // Automatically restore mock state between every test - // restoreMocks: false, - - // The root directory that Jest should scan for tests and modules within - rootDir: "./", - - // A list of paths to directories that Jest should use to search for files in - // roots: ["services"], - - // Allows you to use a custom runner instead of Jest's default test runner - // runner: "jest-runner", - - // The paths to modules that run some code to configure or set up the testing environment before each test - // setupFiles: ["./testUtils/setup.ts"], - - // A list of paths to modules that run some code to configure or set up the testing framework before each test - // setupFilesAfterEnv: [], - - // The number of seconds after which a test is considered as slow and reported as such in the results. - // slowTestThreshold: 5, - - // A list of paths to snapshot serializer modules Jest should use for snapshot testing - // snapshotSerializers: [], - - // The test environment that will be used for testing - testEnvironment: "node", - - // Options that will be passed to the testEnvironment - // testEnvironmentOptions: {}, - - // Adds a location field to test results - // testLocationInResults: false, - - // The glob patterns Jest uses to detect test files - testMatch: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[tj]s?(x)"], - - // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - testPathIgnorePatterns: ["/build/", "/node_modules/"], - - // The regexp pattern or array of patterns that Jest uses to detect test files - // testRegex: [], - - // This option allows the use of a custom results processor - // testResultsProcessor: undefined, - - // This option allows use of a custom test runner - // testRunner: "jest-circus/runner", - - // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href - // testURL: "http://localhost", - - // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout" - // timers: "real", - - // A map from regular expressions to paths to transformers - transform: { - "^.+\\.(ts|tsx)$": "ts-jest", - }, - - // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: [ - // "/node_modules/", - // "\\.pnp\\.[^\\/]+$" - // ], - - // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them - // unmockedModulePathPatterns: undefined, - - // Indicates whether each individual test should be reported during the run - // verbose: undefined, - - // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode - // watchPathIgnorePatterns: [], - - // Whether to use watchman for file crawling - // watchman: true, -}; diff --git a/backend/middlewares/auth.ts b/backend/middlewares/auth.ts deleted file mode 100644 index ebfd16f4..00000000 --- a/backend/middlewares/auth.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { Request } from "express"; -import { AuthenticationError, ExpressContext } from "apollo-server-express"; -import { GraphQLResolveInfo } from "graphql"; - -import { UserType } from "../prisma"; -import AuthService from "../services/implementations/authService"; -import UserService from "../services/implementations/userService"; -import IAuthService from "../services/interfaces/authService"; - -const authService: IAuthService = new AuthService(new UserService()); - -export const getAccessToken = (req: Request): string | null => { - const authHeaderParts = req.headers.authorization?.split(" "); - if ( - authHeaderParts && - authHeaderParts.length >= 2 && - authHeaderParts[0].toLowerCase() === "bearer" - ) { - return authHeaderParts[1]; - } - return null; -}; - -/* Determine if request is authorized based on accessToken validity and role of client */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -export const isAuthorizedByUserType = (types: Set) => { - return async ( - resolve: ( - parent: any, - args: { [key: string]: any }, - context: ExpressContext, - info: GraphQLResolveInfo, - ) => any, - parent: any, - args: { [key: string]: any }, - context: ExpressContext, - info: GraphQLResolveInfo, - ) => { - const accessToken = getAccessToken(context.req); - const authorized = - accessToken && - (await authService.isAuthorizedByUserType(accessToken, types)); - - if (!authorized) { - throw new AuthenticationError( - "Failed authentication and/or authorization by user type", - ); - } - - return resolve(parent, args, context, info); - }; -}; - -/* Determine if request for a user-specific resource is authorized based on accessToken - * validity and if the userId that the token was issued to matches the requested userId - * Note: userIdField is the name of the request parameter containing the requested userId */ -export const isAuthorizedByUserId = (userIdField: string) => { - return async ( - resolve: ( - parent: any, - args: { [key: string]: any }, - context: ExpressContext, - info: GraphQLResolveInfo, - ) => any, - parent: any, - args: { [key: string]: any }, - context: ExpressContext, - info: GraphQLResolveInfo, - ) => { - const accessToken = getAccessToken(context.req); - const authorized = - accessToken && - (await authService.isAuthorizedByUserId(accessToken, args[userIdField])); - - if (!authorized) { - throw new AuthenticationError( - "Failed authentication and/or authorization by userId", - ); - } - - return resolve(parent, args, context, info); - }; -}; - -/* Determine if request for a user-specific resource is authorized based on accessToken - * validity and if the email that the token was issued to matches the requested email - * Note: emailField is the name of the request parameter containing the requested email */ -export const isAuthorizedByEmail = (emailField: string) => { - return async ( - resolve: ( - parent: any, - args: { [key: string]: any }, - context: ExpressContext, - info: GraphQLResolveInfo, - ) => any, - parent: any, - args: { [key: string]: any }, - context: ExpressContext, - info: GraphQLResolveInfo, - ) => { - const accessToken = getAccessToken(context.req); - const authorized = - accessToken && - (await authService.isAuthorizedByEmail(accessToken, args[emailField])); - - if (!authorized) { - throw new AuthenticationError( - "Failed authentication and/or authorization by email", - ); - } - - return resolve(parent, args, context, info); - }; -}; diff --git a/backend/middlewares/validators/authValidators.ts b/backend/middlewares/validators/authValidators.ts deleted file mode 100644 index db911a67..00000000 --- a/backend/middlewares/validators/authValidators.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { getApiValidationError, validatePrimitive } from "./util"; - -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable-next-line import/prefer-default-export */ -export const loginRequestValidator = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - if (req.body.idToken) { - if (!validatePrimitive(req.body.idToken, "string")) { - return res.status(400).json(getApiValidationError("idToken", "string")); - } - } else { - if (!validatePrimitive(req.body.email, "string")) { - return res.status(400).send(getApiValidationError("email", "string")); - } - if (!validatePrimitive(req.body.password, "string")) { - return res.status(400).send(getApiValidationError("password", "string")); - } - } - return next(); -}; - -export const registerRequestValidator = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - if (!validatePrimitive(req.body.firstName, "string")) { - return res.status(400).send(getApiValidationError("firstName", "string")); - } - if (!validatePrimitive(req.body.lastName, "string")) { - return res.status(400).send(getApiValidationError("lastName", "string")); - } - if (!validatePrimitive(req.body.email, "string")) { - return res.status(400).send(getApiValidationError("email", "string")); - } - if (!validatePrimitive(req.body.password, "string")) { - return res.status(400).send(getApiValidationError("password", "string")); - } - - return next(); -}; diff --git a/backend/middlewares/validators/entityValidators.ts b/backend/middlewares/validators/entityValidators.ts deleted file mode 100644 index c3ae641f..00000000 --- a/backend/middlewares/validators/entityValidators.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { - getApiValidationError, - getFileTypeValidationError, - validateArray, - validateFileType, - validatePrimitive, -} from "./util"; -import { getErrorMessage } from "../../utilities/errorUtils"; - -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable-next-line import/prefer-default-export */ -export const entityRequestDtoValidator = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - let body; - try { - body = JSON.parse(req.body.body); - } catch (e: unknown) { - return res.status(400).send(getErrorMessage(e)); - } - if (!validatePrimitive(body.stringField, "string")) { - return res.status(400).send(getApiValidationError("stringField", "string")); - } - if (!validatePrimitive(body.intField, "integer")) { - return res.status(400).send(getApiValidationError("intField", "integer")); - } - if (!validatePrimitive(body.enumField, "string")) { - return res.status(400).send(getApiValidationError("enumField", "string")); - } - if (!validateArray(body.stringArrayField, "string")) { - return res - .status(400) - .send(getApiValidationError("stringArrayField", "string", true)); - } - if (!validatePrimitive(body.boolField, "boolean")) { - return res.status(400).send(getApiValidationError("boolField", "boolean")); - } - if (req.file && !validateFileType(req.file.mimetype)) { - return res.status(400).send(getFileTypeValidationError(req.file.mimetype)); - } - return next(); -}; diff --git a/backend/middlewares/validators/simpleEntityValidators.ts b/backend/middlewares/validators/simpleEntityValidators.ts deleted file mode 100644 index f89c4bc8..00000000 --- a/backend/middlewares/validators/simpleEntityValidators.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { - getApiValidationError, - validateArray, - validatePrimitive, -} from "./util"; - -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable-next-line import/prefer-default-export */ -export const simpleEntityRequestDtoValidator = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - const { body } = req; - if (!validatePrimitive(body.stringField, "string")) { - return res.status(400).send(getApiValidationError("stringField", "string")); - } - if (!validatePrimitive(body.intField, "integer")) { - return res.status(400).send(getApiValidationError("intField", "integer")); - } - if (!validatePrimitive(body.enumField, "string")) { - return res.status(400).send(getApiValidationError("enumField", "string")); - } - if (!validateArray(body.stringArrayField, "string")) { - return res - .status(400) - .send(getApiValidationError("stringArrayField", "string", true)); - } - if (!validatePrimitive(body.boolField, "boolean")) { - return res.status(400).send(getApiValidationError("boolField", "boolean")); - } - return next(); -}; diff --git a/backend/middlewares/validators/userValidators.ts b/backend/middlewares/validators/userValidators.ts deleted file mode 100644 index f703e996..00000000 --- a/backend/middlewares/validators/userValidators.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Request, Response, NextFunction } from "express"; -import { getApiValidationError, validatePrimitive } from "./util"; - -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -export const createUserDtoValidator = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - if (!validatePrimitive(req.body.firstName, "string")) { - return res.status(400).send(getApiValidationError("firstName", "string")); - } - if (!validatePrimitive(req.body.lastName, "string")) { - return res.status(400).send(getApiValidationError("lastName", "string")); - } - if (!validatePrimitive(req.body.email, "string")) { - return res.status(400).send(getApiValidationError("email", "string")); - } - if (!validatePrimitive(req.body.role, "string")) { - return res.status(400).send(getApiValidationError("role", "string")); - } - if (!validatePrimitive(req.body.password, "string")) { - return res.status(400).send(getApiValidationError("password", "string")); - } - - return next(); -}; - -export const updateUserDtoValidator = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - if (!validatePrimitive(req.body.firstName, "string")) { - return res.status(400).send(getApiValidationError("firstName", "string")); - } - if (!validatePrimitive(req.body.lastName, "string")) { - return res.status(400).send(getApiValidationError("lastName", "string")); - } - if (!validatePrimitive(req.body.email, "string")) { - return res.status(400).send(getApiValidationError("email", "string")); - } - if (!validatePrimitive(req.body.role, "string")) { - return res.status(400).send(getApiValidationError("role", "string")); - } - return next(); -}; diff --git a/backend/middlewares/validators/util.ts b/backend/middlewares/validators/util.ts deleted file mode 100644 index 1007161c..00000000 --- a/backend/middlewares/validators/util.ts +++ /dev/null @@ -1,57 +0,0 @@ -type Type = "string" | "integer" | "boolean"; - -const allowableContentTypes = new Set([ - "text/plain", - "application/pdf", - "image/png", - "image/jpeg", - "image/gif", -]); - -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -export const validatePrimitive = (value: any, type: Type): boolean => { - if (value === undefined || value === null) return false; - - switch (type) { - case "string": { - return typeof value === "string"; - } - case "boolean": { - return typeof value === "boolean"; - } - case "integer": { - return typeof value === "number" && Number.isInteger(value); - } - default: { - return false; - } - } -}; - -export const validateArray = (value: any, type: Type): boolean => { - return ( - value !== undefined && - value !== null && - typeof value === "object" && - Array.isArray(value) && - value.every((item) => validatePrimitive(item, type)) - ); -}; - -export const validateFileType = (mimetype: string): boolean => { - return allowableContentTypes.has(mimetype); -}; - -export const getApiValidationError = ( - fieldName: string, - type: Type, - isArray = false, -): string => { - return `The ${fieldName} is not a ${type}${isArray ? " Array" : ""}`; -}; - -export const getFileTypeValidationError = (mimetype: string): string => { - const allowableContentTypesString = [...allowableContentTypes].join(", "); - return `The file type ${mimetype} is not one of ${allowableContentTypesString}`; -}; diff --git a/backend/migrate.js b/backend/migrate.js deleted file mode 100644 index 02a1f8a7..00000000 --- a/backend/migrate.js +++ /dev/null @@ -1,4 +0,0 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -require("ts-node/register"); - -require("./umzug").migrator.runAsCLI(); diff --git a/backend/migrations/20241010235436_task_refactor/migration.sql b/backend/migrations/20241010235436_task_refactor/migration.sql deleted file mode 100644 index b72a267f..00000000 --- a/backend/migrations/20241010235436_task_refactor/migration.sql +++ /dev/null @@ -1,172 +0,0 @@ --- CreateEnum -CREATE TYPE "UserType" AS ENUM ('STAFF', 'RESIDENT'); - --- CreateEnum -CREATE TYPE "TaskType" AS ENUM ('REQUIRED', 'OPTIONAL', 'CHORE', 'ACHIEVEMENT'); - --- CreateEnum -CREATE TYPE "DaysOfWeek" AS ENUM ('MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY'); - --- CreateEnum -CREATE TYPE "RecurrenceFrequency" AS ENUM ('ONE_TIME', 'REPEATS_PER_WEEK_SELECTED', 'REPEATS_PER_WEEK_ONCE'); - --- CreateEnum -CREATE TYPE "Status" AS ENUM ('PENDING_APPROVAL', 'ASSIGNED', 'INCOMPLETE', 'COMPLETE', 'EXCUSED'); - --- CreateTable -CREATE TABLE "users" ( - "id" SERIAL NOT NULL, - "type" "UserType" NOT NULL, - "auth_id" TEXT NOT NULL, - "email" TEXT NOT NULL, - "phone_number" TEXT, - "first_name" TEXT NOT NULL, - "last_name" TEXT NOT NULL, - "display_name" TEXT, - "profile_picture_url" TEXT, - "is_active" BOOLEAN NOT NULL DEFAULT true, - - CONSTRAINT "users_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "staff" ( - "user_id" INTEGER NOT NULL, - "is_admin" BOOLEAN NOT NULL DEFAULT false, - - CONSTRAINT "staff_pkey" PRIMARY KEY ("user_id") -); - --- CreateTable -CREATE TABLE "residents" ( - "user_id" INTEGER NOT NULL, - "resident_id" INTEGER NOT NULL, - "birth_date" DATE NOT NULL, - "room_number" INTEGER NOT NULL, - "credits" DOUBLE PRECISION NOT NULL DEFAULT 0, - "date_joined" DATE NOT NULL DEFAULT CURRENT_TIMESTAMP, - "date_left" DATE, - "notes" TEXT, - - CONSTRAINT "residents_pkey" PRIMARY KEY ("user_id") -); - --- CreateTable -CREATE TABLE "tasks" ( - "id" SERIAL NOT NULL, - "type" "TaskType" NOT NULL, - "title" TEXT NOT NULL, - "description" TEXT NOT NULL, - "credit_value" DOUBLE PRECISION NOT NULL, - "location_id" INTEGER NOT NULL, - "end_date" TIMESTAMP(3), - "recurrence_frequency" "RecurrenceFrequency" NOT NULL, - "specific_day" "DaysOfWeek", - "repeat_days" "DaysOfWeek"[], - - CONSTRAINT "tasks_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "task_locations" ( - "id" SERIAL NOT NULL, - "title" TEXT NOT NULL, - "description" TEXT NOT NULL, - - CONSTRAINT "task_locations_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "tasks_assigned" ( - "id" SERIAL NOT NULL, - "task_id" INTEGER NOT NULL, - "assigner_id" INTEGER, - "assignee_id" INTEGER NOT NULL, - "status" "Status" NOT NULL, - "start_date" TIMESTAMP(3) NOT NULL, - "comments" TEXT, - - CONSTRAINT "tasks_assigned_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "warnings" ( - "id" SERIAL NOT NULL, - "assigner_id" INTEGER, - "resident_id" INTEGER NOT NULL, - "related_task_id" INTEGER, - "title" TEXT NOT NULL, - "description" TEXT NOT NULL, - "date_issued" DATE NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "warnings_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "notifications" ( - "id" SERIAL NOT NULL, - "author_id" INTEGER, - "title" TEXT NOT NULL, - "message" TEXT NOT NULL, - "created_at" DATE NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "notifications_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "notifications_received" ( - "id" SERIAL NOT NULL, - "notification_id" INTEGER NOT NULL, - "recipient_id" INTEGER NOT NULL, - "seen" BOOLEAN NOT NULL DEFAULT false, - - CONSTRAINT "notifications_received_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "users_auth_id_key" ON "users"("auth_id"); - --- CreateIndex -CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); - --- CreateIndex -CREATE UNIQUE INDEX "users_phone_number_key" ON "users"("phone_number"); - --- CreateIndex -CREATE UNIQUE INDEX "residents_resident_id_key" ON "residents"("resident_id"); - --- AddForeignKey -ALTER TABLE "staff" ADD CONSTRAINT "staff_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "residents" ADD CONSTRAINT "residents_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "tasks" ADD CONSTRAINT "tasks_location_id_fkey" FOREIGN KEY ("location_id") REFERENCES "task_locations"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "tasks_assigned" ADD CONSTRAINT "tasks_assigned_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "tasks"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "tasks_assigned" ADD CONSTRAINT "tasks_assigned_assigner_id_fkey" FOREIGN KEY ("assigner_id") REFERENCES "staff"("user_id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "tasks_assigned" ADD CONSTRAINT "tasks_assigned_assignee_id_fkey" FOREIGN KEY ("assignee_id") REFERENCES "residents"("user_id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "warnings" ADD CONSTRAINT "warnings_assigner_id_fkey" FOREIGN KEY ("assigner_id") REFERENCES "staff"("user_id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "warnings" ADD CONSTRAINT "warnings_resident_id_fkey" FOREIGN KEY ("resident_id") REFERENCES "residents"("user_id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "warnings" ADD CONSTRAINT "warnings_related_task_id_fkey" FOREIGN KEY ("related_task_id") REFERENCES "tasks"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "notifications" ADD CONSTRAINT "notifications_author_id_fkey" FOREIGN KEY ("author_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "notifications_received" ADD CONSTRAINT "notifications_received_notification_id_fkey" FOREIGN KEY ("notification_id") REFERENCES "notifications"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "notifications_received" ADD CONSTRAINT "notifications_received_recipient_id_fkey" FOREIGN KEY ("recipient_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/backend/migrations/migration_lock.toml b/backend/migrations/migration_lock.toml deleted file mode 100644 index fbffa92c..00000000 --- a/backend/migrations/migration_lock.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) -provider = "postgresql" \ No newline at end of file diff --git a/backend/nodemailer.config.ts b/backend/nodemailer.config.ts deleted file mode 100644 index 35ddd37c..00000000 --- a/backend/nodemailer.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NodemailerConfig } from "./types"; - -const config: NodemailerConfig = { - service: "gmail", - auth: { - type: "OAuth2", - user: process.env.MAILER_USER ?? "", - clientId: process.env.MAILER_CLIENT_ID ?? "", - clientSecret: process.env.MAILER_CLIENT_SECRET ?? "", - refreshToken: process.env.MAILER_REFRESH_TOKEN ?? "", - }, -}; - -export default config; diff --git a/backend/package.json b/backend/package.json index efd97f3e..b2ea7807 100644 --- a/backend/package.json +++ b/backend/package.json @@ -4,7 +4,6 @@ "description": "", "main": "server.ts", "scripts": { - "test": "jest --runInBand --forceExit --detectOpenHandles", "dev": "nodemon -L", "lint": "eslint . --ext .ts,.js", "fix": "eslint . --ext .ts,.js --fix", diff --git a/backend/prisma/index.ts b/backend/prisma/index.ts index 55a64069..b5bf6ce8 100644 --- a/backend/prisma/index.ts +++ b/backend/prisma/index.ts @@ -1,13 +1,4 @@ -import { - PrismaClient, - UserType, - TaskType, - Status, - RecurrenceFrequency, - DaysOfWeek, -} from "@prisma/client"; - -export { UserType, TaskType, Status, RecurrenceFrequency, DaysOfWeek }; +import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index 9416b077..50d0545d 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -5,173 +5,59 @@ generator client { datasource db { provider = "postgresql" url = env("DATABASE_URL") - // uncomment this for neon db - // directUrl = env("DIRECT_URL") } -enum UserType { - STAFF - RESIDENT -} - -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") - - @@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[] - 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 - 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") +enum StaffType { + ADMIN + RELIEF } enum TaskType { REQUIRED OPTIONAL - CHORE - ACHIEVEMENT -} - -enum DaysOfWeek { - MONDAY - TUESDAY - WEDNESDAY - THURSDAY - FRIDAY - SATURDAY - SUNDAY } -enum RecurrenceFrequency { - ONE_TIME - REPEATS_PER_WEEK_SELECTED - REPEATS_PER_WEEK_ONCE -} - -model Task { - 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") - 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 - - @@map("tasks") -} - -// model TaskLocation { -// id Int @id @default(autoincrement()) -// title String -// description String -// tasks Task[] - -// @@map("task_locations") -// } - -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? - - @@map("tasks_assigned") -} - -enum Status { - PENDING_APPROVAL +enum TaskStatus { + UNASSIGNED ASSIGNED INCOMPLETE + PENDING COMPLETE EXCUSED } -model Warning { - id Int @id @default(autoincrement()) - 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("resident_id") - relatedTask Task? @relation(fields: [relatedTaskId], references: [id], onDelete: SetNull, onUpdate: Cascade) - relatedTaskId Int? @map("related_task_id") - title String - description String - dateIssued DateTime @default(now()) @map("date_issued") @db.Date - - @@map("warnings") -} +model Participant { + participantId String @id + roomNumber Int + arrival String + departure String? + password String + credit Int @default(0) -model NotificationGroup { - id Int @id @default(autoincrement()) - recipients Resident[] - notifications Notification[] - announcementGroup Boolean + @@map("participants") } -model Notification { - 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], onDelete: Cascade) - groupId Int @map("group_id") - notificationReceived NotificationReceived[] +model Announcement { + announcementId Int @id @default(autoincrement()) + from StaffType + to Int[] + createdAt DateTime + message String - @@map("notifications") + @@map("announcements") } -model NotificationReceived { - id Int @id @default(autoincrement()) - notification Notification @relation(fields: [notificationId], references: [id], onDelete: Cascade, onUpdate: Cascade) - notificationId Int @map("notification_id") - recipient Resident @relation(fields: [recipientId], references: [userId], onDelete: Cascade, onUpdate: Cascade) - recipientId Int @map("recipient_id") - seen Boolean @default(false) +model Task { + taskId Int @id @default(autoincrement()) + roomNumber Int? + type TaskType + status TaskStatus + name String + isRecurring Boolean + start DateTime + end DateTime + credit Int + comment String? - @@map("notifications_received") + @@map("tasks") } diff --git a/backend/server.ts b/backend/server.ts index 2a7df24f..9d73c6f8 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -1,7 +1,6 @@ import cookieParser from "cookie-parser"; import cors from "cors"; import express from "express"; -import * as firebaseAdmin from "firebase-admin"; import { ApolloServer } from "apollo-server-express"; import schema from "./graphql"; @@ -40,17 +39,6 @@ server.applyMiddleware({ cors: { origin: CORS_ALLOW_LIST, credentials: true }, }); -firebaseAdmin.initializeApp({ - credential: firebaseAdmin.credential.cert({ - projectId: process.env.FIREBASE_PROJECT_ID, - privateKey: process.env.FIREBASE_SVC_ACCOUNT_PRIVATE_KEY?.replace( - /\\n/g, - "\n", - ), - clientEmail: process.env.FIREBASE_SVC_ACCOUNT_CLIENT_EMAIL, - }), -}); - app.listen({ port: process.env.PORT || 5000 }, () => { /* eslint-disable-next-line no-console */ console.info(`Server is listening on port ${process.env.PORT || 5000}!`); diff --git a/backend/services/implementation/announcementImplementation.ts b/backend/services/implementation/announcementImplementation.ts new file mode 100644 index 00000000..a2e2577c --- /dev/null +++ b/backend/services/implementation/announcementImplementation.ts @@ -0,0 +1,541 @@ +// import prisma from "../../prisma"; +// import INotificationService, { +// NotificationDTO, +// NotificationReceivedDTO, +// NotificationGroupDTO, +// CreateNotificationDTO, +// UpdateNotificationDTO, +// } from "../interfaces/notificationService"; +// import IResidentService from "../interfaces/residentService"; +// import logger from "../../utilities/logger"; +// import { getErrorMessage } from "../../utilities/errorUtils"; + +// const Logger = logger(__filename); + +// class NotificationService implements INotificationService { +// residentService: IResidentService; + +// constructor(residentService: IResidentService) { +// this.residentService = residentService; +// } + +// async createNotificationGroup( +// roomIds: number[], +// ): Promise { +// try { +// if (roomIds.length === 0) { +// throw Object.assign(new Error("No rooms specified."), { code: 400 }); +// } +// if (roomIds.length > 1) { +// // enforces that a group can only have one member +// // remove in the future if the requirements change +// throw Object.assign( +// new Error("Notification Group can only have one room."), +// { code: 400 }, +// ); +// } +// const residents = await prisma.resident.findMany({ +// where: { roomNumber: { in: roomIds } }, +// }); +// if (residents.length !== roomIds.length) { +// throw Object.assign(new Error("Room id does not exist."), { +// code: 400, +// }); +// } +// const residentIds = residents.map((resident) => resident.userId); + +// const existingGroup = await prisma.notificationGroup.findMany({ +// where: { +// announcementGroup: false, +// recipients: { +// every: { +// userId: { in: residentIds }, +// }, +// }, +// }, +// include: { +// recipients: true, +// }, +// }); + +// if (existingGroup && existingGroup.length > 0) { +// // throw error if residents match +// existingGroup.forEach((group) => { +// if (group.recipients.length === residentIds.length) { +// throw Object.assign( +// new Error( +// "Notification Group already exists with specified roomIds.", +// ), +// { code: 400 }, +// ); +// } +// }); +// } + +// const newNotificationGroup = await prisma.notificationGroup.create({ +// data: { +// recipients: { +// connect: residentIds.map((id) => ({ userId: id })), +// }, +// announcementGroup: false, +// }, +// include: { +// recipients: true, +// }, +// }); + +// return newNotificationGroup; +// } catch (error) { +// Logger.error( +// `Failed to create Notification Group. Reason = ${getErrorMessage( +// error, +// )}`, +// ); +// throw error; +// } +// } + +// async createAnnouncementGroup(): Promise { +// try { +// const existingGroup = await prisma.notificationGroup.findMany({ +// where: { +// announcementGroup: true, +// }, +// }); +// if (existingGroup && existingGroup.length > 0) { +// throw Object.assign(new Error("Announcement Group already exists."), { +// code: 400, +// }); +// } + +// const residents = await prisma.resident.findMany({ +// where: { dateLeft: null }, +// }); +// const residentIds = residents.map((resident) => resident.userId); + +// const newNotificationGroup = await prisma.notificationGroup.create({ +// data: { +// recipients: { +// connect: residentIds.map((id) => ({ userId: id })), +// }, +// announcementGroup: true, +// }, +// include: { +// recipients: true, +// }, +// }); + +// return newNotificationGroup; +// } catch (error) { +// Logger.error( +// `Failed to create Notification Group. Reason = ${getErrorMessage( +// error, +// )}`, +// ); +// throw error; +// } +// } + +// async sendNotificationToGroup( +// groupId: number, +// notification: CreateNotificationDTO, +// ): Promise { +// try { +// const notificationGroup = await prisma.notificationGroup.findUnique({ +// where: { id: groupId }, +// include: { recipients: true }, +// }); +// const newNotification = await prisma.notification.create({ +// data: { +// message: notification.message, +// createdAt: notification.createdAt, +// author: { +// connect: notification.authorId +// ? { userId: notification.authorId } +// : undefined, +// }, +// group: { +// connect: { id: groupId }, +// }, +// notificationReceived: { +// create: notificationGroup +// ? notificationGroup.recipients.map((resident) => ({ +// recipient: { +// connect: { userId: resident.userId }, +// }, +// })) +// : undefined, +// }, +// }, +// }); + +// return newNotification; +// } catch (error) { +// Logger.error( +// `Failed to create Notification. Reason = ${getErrorMessage(error)}`, +// ); +// throw error; +// } +// } + +// async deleteNotificationGroup( +// groupId: number, +// ): Promise { +// try { +// const deletedNotificationGroup = await prisma.notificationGroup.delete({ +// where: { +// id: groupId, +// }, +// }); + +// if (!deletedNotificationGroup) +// throw new Error(`notification id ${groupId} not found`); + +// return deletedNotificationGroup; +// } catch (error) { +// Logger.error( +// `Failed to set isDelete flag. Reason = ${getErrorMessage(error)}`, +// ); +// throw error; +// } +// } + +// async getAllGroupsAndNotifications(): Promise { +// try { +// const notificationGroups = await prisma.notificationGroup.findMany({ +// include: { +// recipients: true, +// notifications: true, +// }, +// }); +// return notificationGroups; +// } catch (error) { +// Logger.error( +// `Failed to create Notification. Reason = ${getErrorMessage(error)}`, +// ); +// throw error; +// } +// } + +// async getNotificationsByIds( +// notificationIds: number[], +// ): Promise { +// try { +// const notificationReceived = await prisma.notificationReceived.findMany({ +// where: { notificationId: { in: notificationIds } }, +// include: { notification: true }, +// }); + +// if (!notificationReceived) throw new Error(`No User found.`); +// return notificationReceived; +// } catch (error) { +// Logger.error( +// `Failed to get Notification. Reason = ${getErrorMessage(error)}`, +// ); +// throw error; +// } +// } + +// async updateNotificationById( +// notificationId: number, +// notification: UpdateNotificationDTO, +// ): Promise { +// try { +// const updatedNotification = await prisma.notification.update({ +// where: { +// id: notificationId, +// }, +// data: { +// ...notification, +// }, +// }); + +// 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 deleteNotificationByIds(notficationId: number[]): Promise { +// try { +// 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: { +// seen: true, +// }, +// }); + +// const updatedNotification = await prisma.notificationReceived.findUnique({ +// where: { +// id: notificationSeenId, +// }, +// }); + +// if (!updatedNotification) +// throw new Error(`notification id ${notificationSeenId} not found`); + +// return updatedNotification; +// } catch (error) { +// Logger.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/implementation/miscImplementation.ts b/backend/services/implementation/miscImplementation.ts new file mode 100644 index 00000000..59ee83ef --- /dev/null +++ b/backend/services/implementation/miscImplementation.ts @@ -0,0 +1,34 @@ +import prisma from "../../prisma"; +import IMiscService from "../interface/miscInterface"; + +class MiscService implements IMiscService { + async getAvailableRooms(): Promise { + try { + const today = new Date(); + const rooms = await prisma.participant.findMany({ + where: { + OR: [ + { departure: null }, + { departure: { gte: today.toISOString() } }, + ], + }, + select: { + roomNumber: true, + }, + }); + const occupiedRooms = new Set(rooms.map((room) => room.roomNumber)); + const availableRooms: number[] = []; + for (let room = 1; room <= 10; room += 1) { + if (!occupiedRooms.has(room)) { + availableRooms.push(room); + } + } + return availableRooms; + } catch (err) { + console.log(err); + throw err; + } + } +} + +export default MiscService; diff --git a/backend/services/implementation/participantImplementation.ts b/backend/services/implementation/participantImplementation.ts new file mode 100644 index 00000000..68ee0670 --- /dev/null +++ b/backend/services/implementation/participantImplementation.ts @@ -0,0 +1,57 @@ +import { Participant } from "@prisma/client"; +import prisma from "../../prisma"; +import IParticipantService from "../interface/participantInterface"; + +class ParticipantService implements IParticipantService { + async getAllParticipants(): Promise { + try { + const participants = await prisma.participant.findMany({ + orderBy: [{ departure: "asc" }, { arrival: "desc" }], + }); + return participants; + } catch (err) { + console.log(err); + throw err; + } + } + + async getParticipantById(participantId: string): Promise { + try { + const participant: Participant | null = await prisma.participant.findUnique( + { + where: { + participantId, + }, + }, + ); + return participant; + } catch (err) { + console.log(err); + throw err; + } + } + + async createParticipant( + participantId: string, + roomNumber: number, + arrival: string, + password: string, + ): Promise { + try { + await prisma.participant.create({ + data: { + participantId, + roomNumber, + arrival, + password, + }, + }); + return true; + } catch (err) { + console.log(err); + throw err; + } + } +} + +export default ParticipantService; diff --git a/backend/services/implementation/taskImplementation.ts b/backend/services/implementation/taskImplementation.ts new file mode 100644 index 00000000..4b4ed5a8 --- /dev/null +++ b/backend/services/implementation/taskImplementation.ts @@ -0,0 +1,249 @@ +// import prisma, { TaskType, Status } from "../../prisma"; +// import ITaskService, { +// InputTaskDTO, +// TaskDTO, +// InputTaskAssignedDTO, +// TaskAssignedDTO, +// } from "../interfaces/taskService"; +// import logger from "../../utilities/logger"; +// import { getErrorMessage } from "../../utilities/errorUtils"; + +// const Logger = logger(__filename); + +// class TaskService implements ITaskService { +// async getTaskById(taskId: number): Promise { +// try { +// const task = await prisma.task.findUnique({ +// where: { +// id: taskId, +// }, +// // include: { +// // location: true, +// // }, +// }); +// if (!task) throw new Error(`task id ${taskId} not found`); +// return task; +// } catch (error: unknown) { +// Logger.error(`Failed to get task. Reason = ${getErrorMessage(error)}`); +// throw error; +// } +// } + +// async getTasksByType(type: TaskType): Promise { +// try { +// const tasks = await prisma.task.findMany({ +// where: { type }, +// // include: { +// // location: true, +// // }, +// }); +// if (!tasks) throw new Error(`task type ${type} not found`); + +// return tasks; +// } catch (error: unknown) { +// Logger.error(`Failed to get tasks. Reason = ${getErrorMessage(error)}`); +// throw error; +// } +// } + +// async getTasksByAssigneeId(assigneeId: number): Promise { +// try { +// const tasks = await prisma.taskAssigned.findMany({ +// where: { +// assigneeId, +// }, +// }); + +// return tasks; +// } catch (error: unknown) { +// Logger.error(`Failed to get tasks. Reason = ${getErrorMessage(error)}`); +// throw error; +// } +// } + +// async getTasksByAssignerId(assignerId: number): Promise { +// try { +// const tasks = await prisma.taskAssigned.findMany({ +// where: { +// assignerId, +// }, +// }); + +// return tasks; +// } catch (error: unknown) { +// Logger.error(`Failed to get tasks. Reason = ${getErrorMessage(error)}`); +// throw error; +// } +// } + +// async getTasksByStartDate(startDate: Date): Promise { +// try { +// const tasks = await prisma.taskAssigned.findMany({ +// where: { +// startDate, +// }, +// }); + +// return tasks; +// } catch (error: unknown) { +// Logger.error(`Failed to get tasks. Reason = ${getErrorMessage(error)}`); +// throw error; +// } +// } + +// // async getTasksByEndDate(endDate: Date): Promise { +// // try { +// // const tasks = await prisma.taskAssigned.findMany({ +// // where: { +// // endDate, +// // }, +// // }); + +// // return tasks; +// // } catch (error: unknown) { +// // Logger.error(`Failed to get tasks. Reason = ${getErrorMessage(error)}`); +// // throw error; +// // } +// // } + +// async getTasksByStatus(status: Status): Promise { +// try { +// const tasks = await prisma.taskAssigned.findMany({ +// where: { +// status, +// }, +// }); + +// return tasks; +// } catch (error: unknown) { +// Logger.error(`Failed to get tasks. Reason = ${getErrorMessage(error)}`); +// throw error; +// } +// } + +// async createTask(task: InputTaskDTO): Promise { +// try { +// const newTask = await prisma.task.create({ +// data: { +// title: task.title, +// type: task.type, +// description: task.description ?? "", +// creditValue: task.creditValue, +// // location: { +// // connect: { id: task.locationId }, +// // }, +// endDate: task.endDate, +// recurrenceFrequency: task.recurrenceFrequency, +// specificDay: task.specificDay, +// repeatDays: task.repeatDays, +// }, +// // include: { +// // location: true, +// // }, +// }); + +// return newTask; +// } catch (error: unknown) { +// Logger.error(`Failed to create task. Reason = ${getErrorMessage(error)}`); +// throw error; +// } +// } + +// async updateTaskById( +// taskId: number, +// updateTask: InputTaskDTO, +// ): Promise { +// try { +// const updatedTask = await prisma.task.update({ +// where: { +// id: taskId, +// }, +// data: updateTask, +// // include: { +// // location: true, +// // }, +// }); +// return updatedTask; +// } catch (error: unknown) { +// Logger.error(`Failed to update task. Reason = ${getErrorMessage(error)}`); +// throw error; +// } +// } + +// async deleteTaskById(taskId: number): Promise { +// try { +// await prisma.taskAssigned.deleteMany({ +// where: { +// taskId, +// }, +// }); + +// const deletedTask = await prisma.task.delete({ +// where: { +// id: taskId, +// }, +// // include: { +// // location: true, +// // }, +// }); + +// return deletedTask; +// } catch (error: unknown) { +// Logger.error(`Failed to update task. Reason = ${getErrorMessage(error)}`); +// throw error; +// } +// } + +// async assignTask( +// taskAssigned: InputTaskAssignedDTO, +// ): Promise { +// try { +// const newTaskAssigned = await prisma.taskAssigned.create({ +// data: { +// task: { +// connect: { id: taskAssigned.taskId }, +// }, +// assigner: { +// connect: { userId: taskAssigned.assignerId }, +// }, +// assignee: { +// connect: { userId: taskAssigned.assigneeId }, +// }, +// status: taskAssigned.status, +// startDate: taskAssigned.startDate, +// comments: taskAssigned.comments, +// }, +// }); + +// return newTaskAssigned; +// } catch (error: unknown) { +// Logger.error(`Failed to assign task. Reason = ${getErrorMessage(error)}`); +// throw error; +// } +// } + +// async changeTaskStatus( +// taskAssignedId: number, +// status: Status, +// ): Promise { +// try { +// const updatedTask = await prisma.taskAssigned.update({ +// where: { +// id: taskAssignedId, +// }, +// data: { +// status, +// }, +// }); + +// return updatedTask; +// } catch (error: unknown) { +// Logger.error( +// `Failed to update task status. Reason = ${getErrorMessage(error)}`, +// ); +// throw error; +// } +// } +// } + +// export default TaskService; diff --git a/backend/services/implementations/__tests__/userService.test.ts b/backend/services/implementations/__tests__/userService.test.ts deleted file mode 100644 index 33a5a2dd..00000000 --- a/backend/services/implementations/__tests__/userService.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -// import { snakeCase } from "lodash"; - -// import UserModel from "../../../models/user.model"; -// import UserService from "../userService"; - -// import { UserDTO } from "../../../types"; - -// import { testSql } from "../../../testUtils/testDb"; - -// const testUsers = [ -// { -// firstName: "Peter", -// lastName: "Pan", -// authId: "123", -// role: "Admin", -// }, -// { -// firstName: "Wendy", -// lastName: "Darling", -// authId: "321", -// role: "User", -// }, -// ]; - -// jest.mock("firebase-admin", () => { -// const auth = jest.fn().mockReturnValue({ -// getUser: jest.fn().mockReturnValue({ email: "test@test.com" }), -// }); -// return { auth }; -// }); - -// describe("pg userService", () => { -// let userService: UserService; - -// beforeEach(async () => { -// await testSql.sync({ force: true }); -// userService = new UserService(); -// }); - -// afterAll(async () => { -// await testSql.sync({ force: true }); -// await testSql.close(); -// }); - -// it("getUsers", async () => { -// const users = testUsers.map((user) => { -// const userSnakeCase: Record = {}; -// Object.entries(user).forEach(([key, value]) => { -// userSnakeCase[snakeCase(key)] = value; -// }); -// return userSnakeCase; -// }); - -// await UserModel.bulkCreate(users); - -// const res = await userService.getUsers(); - -// res.forEach((user: UserDTO, i) => { -// expect(user.firstName).toEqual(testUsers[i].firstName); -// expect(user.lastName).toEqual(testUsers[i].lastName); -// expect(user.role).toEqual(testUsers[i].role); -// }); -// }); -// }); diff --git a/backend/services/implementations/authService.ts b/backend/services/implementations/authService.ts deleted file mode 100644 index f5acac24..00000000 --- a/backend/services/implementations/authService.ts +++ /dev/null @@ -1,203 +0,0 @@ -import * as firebaseAdmin from "firebase-admin"; - -import { UserType } from "../../prisma"; -import { Token } from "../../types"; -import IAuthService, { AuthDTO } from "../interfaces/authService"; -import IEmailService from "../interfaces/emailService"; -import IUserService from "../interfaces/userService"; -import { getErrorMessage } from "../../utilities/errorUtils"; -import FirebaseRestClient from "../../utilities/firebaseRestClient"; -import logger from "../../utilities/logger"; - -const Logger = logger(__filename); - -class AuthService implements IAuthService { - userService: IUserService; - - emailService: IEmailService | null; - - constructor( - userService: IUserService, - emailService: IEmailService | null = null, - ) { - this.userService = userService; - this.emailService = emailService; - } - - /* eslint-disable class-methods-use-this */ - async generateToken( - email: string, - password: string, - userType: UserType, - ): Promise { - try { - const token = await FirebaseRestClient.signInWithPassword( - email, - password, - ); - const user = await this.userService.getUserByEmail(email); - if (user.type !== userType) { - throw new Error("User type does not match"); - } - return { ...token, ...user }; - } catch (error) { - Logger.error(`Failed to generate token for user with email ${email}`); - throw error; - } - } - - async revokeTokens(userId: string): Promise { - try { - const authId = await this.userService.getAuthIdById(userId); - - await firebaseAdmin.auth().revokeRefreshTokens(authId); - } catch (error: unknown) { - const errorMessage = [ - "Failed to revoke refresh tokens of user with id", - `${userId}.`, - "Reason =", - getErrorMessage(error), - ]; - Logger.error(errorMessage.join(" ")); - - throw error; - } - } - - async renewToken(refreshToken: string): Promise { - try { - return await FirebaseRestClient.refreshToken(refreshToken); - } catch (error) { - Logger.error("Failed to refresh token"); - throw error; - } - } - - async resetPassword(email: string): Promise { - if (!this.emailService) { - const errorMessage = - "Attempted to call resetPassword but this instance of AuthService does not have an EmailService instance"; - Logger.error(errorMessage); - throw new Error(errorMessage); - } - - try { - const resetLink = await firebaseAdmin - .auth() - .generatePasswordResetLink(email); - const emailBody = ` - Hello, -

- We have received a password reset request for your account. - Please click the following link to reset it. - This link is only valid for 1 hour. -

- Reset Password`; - - this.emailService.sendEmail(email, "Your Password Reset Link", emailBody); - } catch (error) { - Logger.error( - `Failed to generate password reset link for user with email ${email}`, - ); - throw error; - } - } - - async sendEmailVerificationLink(email: string): Promise { - if (!this.emailService) { - const errorMessage = - "Attempted to call sendEmailVerificationLink but this instance of AuthService does not have an EmailService instance"; - Logger.error(errorMessage); - throw new Error(errorMessage); - } - - try { - const emailVerificationLink = await firebaseAdmin - .auth() - .generateEmailVerificationLink(email); - const emailBody = ` - Hello, -

- Please click the following link to verify your email and activate your account. - This link is only valid for 1 hour. -

- Verify email`; - - this.emailService.sendEmail(email, "Verify your email", emailBody); - } catch (error) { - Logger.error( - `Failed to generate email verification link for user with email ${email}`, - ); - throw error; - } - } - - async isAuthorizedByUserType( - accessToken: string, - types: Set, - ): Promise { - try { - const decodedIdToken: firebaseAdmin.auth.DecodedIdToken = await firebaseAdmin - .auth() - .verifyIdToken(accessToken, true); - const userRole = await this.userService.getUserTypeByAuthId( - decodedIdToken.uid, - ); - - const firebaseUser = await firebaseAdmin - .auth() - .getUser(decodedIdToken.uid); - - return firebaseUser.emailVerified && types.has(userRole); - } catch (error) { - return false; - } - } - - async isAuthorizedByUserId( - accessToken: string, - requestedUserId: string, - ): Promise { - try { - const decodedIdToken: firebaseAdmin.auth.DecodedIdToken = await firebaseAdmin - .auth() - .verifyIdToken(accessToken, true); - const tokenUserId = await this.userService.getUserIdByAuthId( - decodedIdToken.uid, - ); - - const firebaseUser = await firebaseAdmin - .auth() - .getUser(decodedIdToken.uid); - - return ( - firebaseUser.emailVerified && String(tokenUserId) === requestedUserId - ); - } catch (error) { - return false; - } - } - - async isAuthorizedByEmail( - accessToken: string, - requestedEmail: string, - ): Promise { - try { - const decodedIdToken: firebaseAdmin.auth.DecodedIdToken = await firebaseAdmin - .auth() - .verifyIdToken(accessToken, true); - - const firebaseUser = await firebaseAdmin - .auth() - .getUser(decodedIdToken.uid); - - return ( - firebaseUser.emailVerified && decodedIdToken.email === requestedEmail - ); - } catch (error) { - return false; - } - } -} - -export default AuthService; diff --git a/backend/services/implementations/emailService.ts b/backend/services/implementations/emailService.ts deleted file mode 100644 index d7eeaf4a..00000000 --- a/backend/services/implementations/emailService.ts +++ /dev/null @@ -1,44 +0,0 @@ -import nodemailer, { Transporter } from "nodemailer"; -import IEmailService from "../interfaces/emailService"; -import { NodemailerConfig } from "../../types"; -import { getErrorMessage } from "../../utilities/errorUtils"; -import logger from "../../utilities/logger"; - -const Logger = logger(__filename); - -class EmailService implements IEmailService { - transporter: Transporter; - - sender: string; - - constructor(nodemailerConfig: NodemailerConfig, displayName?: string) { - this.transporter = nodemailer.createTransport(nodemailerConfig); - if (displayName) { - this.sender = `${displayName} <${nodemailerConfig.auth.user}>`; - } else { - this.sender = nodemailerConfig.auth.user; - } - } - - async sendEmail( - to: string, - subject: string, - htmlBody: string, - ): Promise { - const mailOptions = { - from: this.sender, - to, - subject, - html: htmlBody, - }; - - try { - return await this.transporter.sendMail(mailOptions); - } catch (error: unknown) { - Logger.error(`Failed to send email. Reason = ${getErrorMessage(error)}`); - throw error; - } - } -} - -export default EmailService; diff --git a/backend/services/implementations/fileStorageService.ts b/backend/services/implementations/fileStorageService.ts deleted file mode 100644 index 296980e5..00000000 --- a/backend/services/implementations/fileStorageService.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { storage } from "firebase-admin"; - -import IFileStorageService from "../interfaces/fileStorageService"; -import { getErrorMessage } from "../../utilities/errorUtils"; -import logger from "../../utilities/logger"; - -const Logger = logger(__filename); - -class FileStorageService implements IFileStorageService { - bucketName: string; - - constructor(bucketName: string) { - this.bucketName = bucketName; - } - - async getFile(fileName: string, expirationTimeMinutes = 60): Promise { - const bucket = storage().bucket(this.bucketName); - const expirationDate = new Date(); - expirationDate.setMinutes( - expirationDate.getMinutes() + expirationTimeMinutes, - ); - try { - const currentBlob = await bucket.file(fileName); - if (!(await currentBlob.exists())[0]) { - throw new Error(`File name ${fileName} does not exist`); - } - const res = await currentBlob.getSignedUrl({ - action: "read", - expires: expirationDate, - }); - return res[0]; - } catch (error: unknown) { - Logger.error( - `Failed to retrieve file. Reason = ${getErrorMessage(error)}`, - ); - throw error; - } - } - - async createFile( - fileName: string, - filePath: string, - contentType: string | null = null, - ): Promise { - try { - const bucket = storage().bucket(this.bucketName); - const currentBlob = await bucket.file(fileName); - if ((await currentBlob.exists())[0]) { - throw new Error(`File name ${fileName} already exists`); - } - await bucket.upload(filePath, { - destination: fileName, - metadata: { contentType }, - }); - } catch (error: unknown) { - Logger.error(`Failed to upload file. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async updateFile( - fileName: string, - filePath: string, - contentType: string | null = null, - ): Promise { - try { - const bucket = storage().bucket(this.bucketName); - const currentBlob = await bucket.file(fileName); - if (!(await currentBlob.exists())[0]) { - throw new Error(`File name ${fileName} does not exist`); - } - await bucket.upload(filePath, { - destination: fileName, - metadata: { contentType }, - }); - } catch (error: unknown) { - Logger.error(`Failed to update file. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async deleteFile(fileName: string): Promise { - try { - const bucket = storage().bucket(this.bucketName); - const currentBlob = await bucket.file(fileName); - if (!currentBlob) { - throw new Error(`File name ${fileName} does not exist`); - } - await currentBlob.delete(); - } catch (error: unknown) { - Logger.error(`Failed to delete file. Reason = ${getErrorMessage(error)}`); - throw error; - } - } -} - -export default FileStorageService; diff --git a/backend/services/implementations/notificationService.ts b/backend/services/implementations/notificationService.ts deleted file mode 100644 index 1517b439..00000000 --- a/backend/services/implementations/notificationService.ts +++ /dev/null @@ -1,541 +0,0 @@ -import prisma from "../../prisma"; -import INotificationService, { - NotificationDTO, - NotificationReceivedDTO, - NotificationGroupDTO, - CreateNotificationDTO, - UpdateNotificationDTO, -} from "../interfaces/notificationService"; -import IResidentService from "../interfaces/residentService"; -import logger from "../../utilities/logger"; -import { getErrorMessage } from "../../utilities/errorUtils"; - -const Logger = logger(__filename); - -class NotificationService implements INotificationService { - residentService: IResidentService; - - constructor(residentService: IResidentService) { - this.residentService = residentService; - } - - async createNotificationGroup( - roomIds: number[], - ): Promise { - try { - if (roomIds.length === 0) { - throw Object.assign(new Error("No rooms specified."), { code: 400 }); - } - if (roomIds.length > 1) { - // enforces that a group can only have one member - // remove in the future if the requirements change - throw Object.assign( - new Error("Notification Group can only have one room."), - { code: 400 }, - ); - } - const residents = await prisma.resident.findMany({ - where: { roomNumber: { in: roomIds } }, - }); - if (residents.length !== roomIds.length) { - throw Object.assign(new Error("Room id does not exist."), { - code: 400, - }); - } - const residentIds = residents.map((resident) => resident.userId); - - const existingGroup = await prisma.notificationGroup.findMany({ - where: { - announcementGroup: false, - recipients: { - every: { - userId: { in: residentIds }, - }, - }, - }, - include: { - recipients: true, - }, - }); - - if (existingGroup && existingGroup.length > 0) { - // throw error if residents match - existingGroup.forEach((group) => { - if (group.recipients.length === residentIds.length) { - throw Object.assign( - new Error( - "Notification Group already exists with specified roomIds.", - ), - { code: 400 }, - ); - } - }); - } - - const newNotificationGroup = await prisma.notificationGroup.create({ - data: { - recipients: { - connect: residentIds.map((id) => ({ userId: id })), - }, - announcementGroup: false, - }, - include: { - recipients: true, - }, - }); - - return newNotificationGroup; - } catch (error) { - Logger.error( - `Failed to create Notification Group. Reason = ${getErrorMessage( - error, - )}`, - ); - throw error; - } - } - - async createAnnouncementGroup(): Promise { - try { - const existingGroup = await prisma.notificationGroup.findMany({ - where: { - announcementGroup: true, - }, - }); - if (existingGroup && existingGroup.length > 0) { - throw Object.assign(new Error("Announcement Group already exists."), { - code: 400, - }); - } - - const residents = await prisma.resident.findMany({ - where: { dateLeft: null }, - }); - const residentIds = residents.map((resident) => resident.userId); - - const newNotificationGroup = await prisma.notificationGroup.create({ - data: { - recipients: { - connect: residentIds.map((id) => ({ userId: id })), - }, - announcementGroup: true, - }, - include: { - recipients: true, - }, - }); - - return newNotificationGroup; - } catch (error) { - Logger.error( - `Failed to create Notification Group. Reason = ${getErrorMessage( - error, - )}`, - ); - throw error; - } - } - - async sendNotificationToGroup( - groupId: number, - notification: CreateNotificationDTO, - ): Promise { - try { - const notificationGroup = await prisma.notificationGroup.findUnique({ - where: { id: groupId }, - include: { recipients: true }, - }); - const newNotification = await prisma.notification.create({ - data: { - message: notification.message, - createdAt: notification.createdAt, - author: { - connect: notification.authorId - ? { userId: notification.authorId } - : undefined, - }, - group: { - connect: { id: groupId }, - }, - notificationReceived: { - create: notificationGroup - ? notificationGroup.recipients.map((resident) => ({ - recipient: { - connect: { userId: resident.userId }, - }, - })) - : undefined, - }, - }, - }); - - return newNotification; - } catch (error) { - Logger.error( - `Failed to create Notification. Reason = ${getErrorMessage(error)}`, - ); - throw error; - } - } - - async deleteNotificationGroup( - groupId: number, - ): Promise { - try { - const deletedNotificationGroup = await prisma.notificationGroup.delete({ - where: { - id: groupId, - }, - }); - - if (!deletedNotificationGroup) - throw new Error(`notification id ${groupId} not found`); - - return deletedNotificationGroup; - } catch (error) { - Logger.error( - `Failed to set isDelete flag. Reason = ${getErrorMessage(error)}`, - ); - throw error; - } - } - - async getAllGroupsAndNotifications(): Promise { - try { - const notificationGroups = await prisma.notificationGroup.findMany({ - include: { - recipients: true, - notifications: true, - }, - }); - return notificationGroups; - } catch (error) { - Logger.error( - `Failed to create Notification. Reason = ${getErrorMessage(error)}`, - ); - throw error; - } - } - - async getNotificationsByIds( - notificationIds: number[], - ): Promise { - try { - const notificationReceived = await prisma.notificationReceived.findMany({ - where: { notificationId: { in: notificationIds } }, - include: { notification: true }, - }); - - if (!notificationReceived) throw new Error(`No User found.`); - return notificationReceived; - } catch (error) { - Logger.error( - `Failed to get Notification. Reason = ${getErrorMessage(error)}`, - ); - throw error; - } - } - - async updateNotificationById( - notificationId: number, - notification: UpdateNotificationDTO, - ): Promise { - try { - const updatedNotification = await prisma.notification.update({ - where: { - id: notificationId, - }, - data: { - ...notification, - }, - }); - - 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 deleteNotificationByIds(notficationId: number[]): Promise { - try { - 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: { - seen: true, - }, - }); - - const updatedNotification = await prisma.notificationReceived.findUnique({ - where: { - id: notificationSeenId, - }, - }); - - if (!updatedNotification) - throw new Error(`notification id ${notificationSeenId} not found`); - - return updatedNotification; - } catch (error) { - Logger.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 deleted file mode 100644 index b7cfc37a..00000000 --- a/backend/services/implementations/residentService.ts +++ /dev/null @@ -1,310 +0,0 @@ -import * as firebaseAdmin from "firebase-admin"; - -import prisma, { UserType } from "../../prisma"; -import IResidentService, { - ResidentDTO, - CreateResidentDTO, - UpdateResidentDTO, - RedeemCreditsResponse, -} from "../interfaces/residentService"; -import logger from "../../utilities/logger"; -import { getErrorMessage } from "../../utilities/errorUtils"; - -const Logger = logger(__filename); - -class ResidentService implements IResidentService { - async addResident(resident: CreateResidentDTO): Promise { - try { - const firebaseUser = await firebaseAdmin.auth().createUser({ - password: resident.password, - }); - - try { - const announcementGroups = await prisma.notificationGroup.findMany({ - where: { - announcementGroup: true, - }, - include: { notifications: true }, - }); - - const newResident = await prisma.resident.create({ - data: { - residentId: resident.residentId, - roomNumber: resident.roomNumber, - credits: resident.credits, - dateJoined: resident.dateJoined, - dateLeft: resident.dateLeft, - user: { - create: { - authId: firebaseUser.uid, - type: UserType.RESIDENT, - }, - }, - 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 }, - }); - - return { - userId: newResident.userId, - residentId: newResident.residentId, - roomNumber: newResident.roomNumber, - credits: newResident.credits, - dateJoined: newResident.dateJoined, - dateLeft: newResident.dateLeft, - }; - } catch (postgresError) { - try { - await firebaseAdmin.auth().deleteUser(firebaseUser.uid); - } catch (firebaseError: unknown) { - const errorMessage = [ - "Failed to rollback Firebase user creation after Postgres user creation failure. Reason =", - getErrorMessage(firebaseError), - "Orphaned authId (Firebase uid) =", - firebaseUser.uid, - ]; - Logger.error(errorMessage.join(" ")); - } - - throw postgresError; - } - } catch (error) { - Logger.error( - `Failed to create resident because ${getErrorMessage(error)}`, - ); - throw error; - } - } - - async updateResident( - userId: number, - resident: UpdateResidentDTO, - ): Promise { - try { - const oldUser = await prisma.user.findUnique({ - where: { id: userId }, - }); - - if (!oldUser) { - throw new Error(`resident ${userId} not found.`); - } - - if (oldUser.type !== UserType.RESIDENT) { - throw new Error(`${userId} is not a resident.`); - } - - const { authId } = oldUser; - // const email = "email" in resident ? resident.email : oldUser.email; - - if ("password" in resident) { - await firebaseAdmin.auth().updateUser(authId, { - // email, - password: resident.password, - }); - } else { - await firebaseAdmin.auth().updateUser(authId, {}); - } - - const updatedResident = await prisma.resident.update({ - where: { userId }, - data: { - residentId: resident.residentId || undefined, - roomNumber: resident.roomNumber || undefined, - credits: resident.credits || undefined, - dateJoined: resident.dateJoined || undefined, - dateLeft: resident.dateLeft || undefined, - }, - include: { user: true }, - }); - - return { - userId: updatedResident.userId, - residentId: updatedResident.residentId, - roomNumber: updatedResident.roomNumber, - credits: updatedResident.credits, - dateJoined: updatedResident.dateJoined, - dateLeft: updatedResident.dateLeft, - }; - } catch (error) { - Logger.error( - `Failed to update resident #${userId} because ${getErrorMessage( - error, - )}`, - ); - throw error; - } - } - - async deleteResident(userId: number): Promise { - try { - const deletedUser = await prisma.user.findUnique({ - where: { id: userId }, - }); - - if (!deletedUser) { - throw new Error(`resident ${userId} not found.`); - } - - if (deletedUser.type !== UserType.RESIDENT) { - throw new Error(`${userId} is not a resident.`); - } - - await firebaseAdmin.auth().deleteUser(deletedUser.authId); - - const deletedResident = await prisma.resident.delete({ - where: { userId }, - include: { user: true }, - }); - - await prisma.user.delete({ - where: { id: userId }, - }); - - return { - userId: deletedResident.userId, - residentId: deletedResident.residentId, - roomNumber: deletedResident.roomNumber, - credits: deletedResident.credits, - dateJoined: deletedResident.dateJoined, - dateLeft: deletedResident.dateLeft, - }; - } catch (error) { - Logger.error( - `Failed to delete resident #${userId} because ${getErrorMessage( - error, - )}`, - ); - throw error; - } - } - - async getAllResidents(): Promise { - try { - const allResidents = await prisma.resident.findMany({ - include: { user: true }, - }); - return allResidents.map((resident) => { - return { - userId: resident.userId, - residentId: resident.residentId, - roomNumber: resident.roomNumber, - credits: resident.credits, - dateJoined: resident.dateJoined, - dateLeft: resident.dateLeft, - }; - }); - } catch (error: unknown) { - Logger.error( - `Failed to get all residents. Reason = ${getErrorMessage(error)}`, - ); - throw error; - } - } - - async getResidentsByIds(userId: number[]): Promise { - try { - const getResidentById = await prisma.resident.findMany({ - where: { userId: { in: userId } }, - include: { user: true }, - }); - return getResidentById.map((resident) => { - return { - userId: resident.userId, - residentId: resident.residentId, - roomNumber: resident.roomNumber, - credits: resident.credits, - dateJoined: resident.dateJoined, - dateLeft: resident.dateLeft, - }; - }); - } catch (error: unknown) { - Logger.error( - `Failed to get residents by IDs. IDs = ${userId}. Reason = ${getErrorMessage( - error, - )}`, - ); - throw error; - } - } - - async getActiveResidents(): Promise { - try { - const residents = await prisma.resident.findMany({ - where: { - dateLeft: null, - }, - include: { user: true }, - }); - - if (!residents) throw new Error(`No residents found.`); - - return residents.map((resident) => { - return { - userId: resident.userId, - residentId: resident.residentId, - roomNumber: resident.roomNumber, - credits: resident.credits, - dateJoined: resident.dateJoined, - dateLeft: resident.dateLeft, - }; - }); - } catch (error: unknown) { - Logger.error( - `Failed to get all active residents. Reason = ${getErrorMessage( - error, - )}`, - ); - throw error; - } - } - - async redeemCredits( - userId: number, - credits: number, - ): Promise { - try { - const currentCredits = await prisma.resident.findUnique({ - where: { userId }, - select: { - credits: true, - }, - }); - - if (!currentCredits) { - return RedeemCreditsResponse.INVALID_ID; - } - if (currentCredits.credits >= credits) { - await prisma.resident.update({ - where: { userId }, - data: { - credits: { - decrement: credits, - }, - }, - }); - return RedeemCreditsResponse.SUCCESS; - } - return RedeemCreditsResponse.NOT_ENOUGH_CREDITS; - } catch (error: unknown) { - Logger.error( - `Failed to redeem resident's credits by IDs. IDs = ${userId}. Reason = ${getErrorMessage( - error, - )}`, - ); - throw error; - } - } -} - -export default ResidentService; diff --git a/backend/services/implementations/staffService.ts b/backend/services/implementations/staffService.ts deleted file mode 100644 index 2129108f..00000000 --- a/backend/services/implementations/staffService.ts +++ /dev/null @@ -1,213 +0,0 @@ -import * as firebaseAdmin from "firebase-admin"; - -import prisma, { UserType } from "../../prisma"; -import IStaffService, { - StaffDTO, - CreateStaffDTO, - UpdateStaffDTO, -} from "../interfaces/staffService"; -import logger from "../../utilities/logger"; -import { getErrorMessage } from "../../utilities/errorUtils"; - -const Logger = logger(__filename); - -class StaffService implements IStaffService { - async addStaff(staff: CreateStaffDTO): Promise { - try { - const firebaseUser = await firebaseAdmin.auth().createUser({ - email: staff.email, - password: staff.password, - }); - - try { - 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, - }, - }, - }, - include: { user: true }, - }); - - return { - userId: newStaff.userId, - isAdmin: newStaff.isAdmin, - email: firebaseUser.email ?? "", - phoneNumber: newStaff.phoneNumber, - firstName: newStaff.firstName, - lastName: newStaff.lastName, - }; - } catch (postgresError) { - try { - await firebaseAdmin.auth().deleteUser(firebaseUser.uid); - } catch (firebaseError: unknown) { - const errorMessage = [ - "Failed to rollback Firebase user creation after Postgres user creation failure. Reason =", - getErrorMessage(firebaseError), - "Orphaned authId (Firebase uid) =", - firebaseUser.uid, - ]; - Logger.error(errorMessage.join(" ")); - } - throw postgresError; - } - } catch (error) { - Logger.error(`Failed to create staff because ${getErrorMessage(error)}`); - throw error; - } - } - - async updateStaff(userId: number, staff: UpdateStaffDTO): Promise { - try { - const originalUser = await prisma.user.findUnique({ - where: { id: userId }, - }); - - if (!originalUser) { - throw new Error(`staff ${userId} not found.`); - } else if (originalUser.type !== UserType.STAFF) { - throw new Error(`id ${userId} is not a staff.`); - } - - const { authId } = originalUser; - // const email = "email" in staff ? staff.email : originalUser.email; - const { email } = staff; - if ("password" in staff) { - await firebaseAdmin.auth().updateUser(authId, { - email, - password: staff.password, - }); - } else { - await firebaseAdmin.auth().updateUser(authId, { email }); - } - - const updatedStaff = await prisma.staff.update({ - where: { userId }, - data: { - isAdmin: staff.isAdmin, - email: staff.email, - phoneNumber: staff.phoneNumber, - firstName: staff.firstName, - lastName: staff.lastName, - }, - include: { - user: true, - }, - }); - - return { - userId: updatedStaff.userId, - isAdmin: updatedStaff.isAdmin, - email: updatedStaff.email, - phoneNumber: updatedStaff.phoneNumber, - firstName: updatedStaff.firstName, - lastName: updatedStaff.lastName, - }; - } catch (error) { - Logger.error( - `Failed to update staff #${userId} because ${getErrorMessage(error)}`, - ); - throw error; - } - } - - async deleteStaff(userId: number): Promise { - try { - const deletedUser = await prisma.user.findUnique({ - where: { id: userId }, - }); - - if (!deletedUser) { - throw new Error(`staff ${userId} not found.`); - } else if (deletedUser.type !== UserType.STAFF) { - throw new Error(`id ${userId} is not a staff.`); - } - - await firebaseAdmin.auth().deleteUser(deletedUser.authId); - - const deletedStaff = await prisma.staff.delete({ - where: { userId }, - include: { user: true }, - }); - - await prisma.user.delete({ - where: { id: userId }, - }); - - return { - userId: deletedStaff.userId, - isAdmin: deletedStaff.isAdmin, - email: deletedStaff.email, - phoneNumber: deletedStaff.phoneNumber, - firstName: deletedStaff.firstName, - lastName: deletedStaff.lastName, - }; - } catch (error) { - Logger.error( - `Failed to delete staff #${userId} because ${getErrorMessage(error)}`, - ); - throw error; - } - } - - async getAllStaff(): Promise> { - try { - const allStaff = await prisma.staff.findMany({ - include: { - user: true, - }, - }); - - return allStaff.map((staff) => { - return { - userId: staff.userId, - isAdmin: staff.isAdmin, - email: staff.email, - phoneNumber: staff.phoneNumber, - firstName: staff.firstName, - lastName: staff.lastName, - }; - }); - } catch (error: unknown) { - Logger.error(`Failed to get all Staff because ${getErrorMessage(error)}`); - throw error; - } - } - - async getStaffByIds(userIds: number[]): Promise> { - try { - const getStaffById = await prisma.staff.findMany({ - where: { userId: { in: userIds } }, - include: { user: true }, - }); - - return getStaffById.map((staff) => { - return { - userId: staff.userId, - isAdmin: staff.isAdmin, - email: staff.email, - phoneNumber: staff.phoneNumber, - firstName: staff.firstName, - lastName: staff.lastName, - }; - }); - } catch (error: unknown) { - Logger.error( - `Failed to get staff by IDs. IDs = ${userIds} because ${getErrorMessage( - error, - )}`, - ); - throw error; - } - } -} - -export default StaffService; diff --git a/backend/services/implementations/taskService.ts b/backend/services/implementations/taskService.ts deleted file mode 100644 index ef876b43..00000000 --- a/backend/services/implementations/taskService.ts +++ /dev/null @@ -1,249 +0,0 @@ -import prisma, { TaskType, Status } from "../../prisma"; -import ITaskService, { - InputTaskDTO, - TaskDTO, - InputTaskAssignedDTO, - TaskAssignedDTO, -} from "../interfaces/taskService"; -import logger from "../../utilities/logger"; -import { getErrorMessage } from "../../utilities/errorUtils"; - -const Logger = logger(__filename); - -class TaskService implements ITaskService { - async getTaskById(taskId: number): Promise { - try { - const task = await prisma.task.findUnique({ - where: { - id: taskId, - }, - // include: { - // location: true, - // }, - }); - if (!task) throw new Error(`task id ${taskId} not found`); - return task; - } catch (error: unknown) { - Logger.error(`Failed to get task. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async getTasksByType(type: TaskType): Promise { - try { - const tasks = await prisma.task.findMany({ - where: { type }, - // include: { - // location: true, - // }, - }); - if (!tasks) throw new Error(`task type ${type} not found`); - - return tasks; - } catch (error: unknown) { - Logger.error(`Failed to get tasks. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async getTasksByAssigneeId(assigneeId: number): Promise { - try { - const tasks = await prisma.taskAssigned.findMany({ - where: { - assigneeId, - }, - }); - - return tasks; - } catch (error: unknown) { - Logger.error(`Failed to get tasks. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async getTasksByAssignerId(assignerId: number): Promise { - try { - const tasks = await prisma.taskAssigned.findMany({ - where: { - assignerId, - }, - }); - - return tasks; - } catch (error: unknown) { - Logger.error(`Failed to get tasks. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async getTasksByStartDate(startDate: Date): Promise { - try { - const tasks = await prisma.taskAssigned.findMany({ - where: { - startDate, - }, - }); - - return tasks; - } catch (error: unknown) { - Logger.error(`Failed to get tasks. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - // async getTasksByEndDate(endDate: Date): Promise { - // try { - // const tasks = await prisma.taskAssigned.findMany({ - // where: { - // endDate, - // }, - // }); - - // return tasks; - // } catch (error: unknown) { - // Logger.error(`Failed to get tasks. Reason = ${getErrorMessage(error)}`); - // throw error; - // } - // } - - async getTasksByStatus(status: Status): Promise { - try { - const tasks = await prisma.taskAssigned.findMany({ - where: { - status, - }, - }); - - return tasks; - } catch (error: unknown) { - Logger.error(`Failed to get tasks. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async createTask(task: InputTaskDTO): Promise { - try { - const newTask = await prisma.task.create({ - data: { - title: task.title, - type: task.type, - description: task.description ?? "", - creditValue: task.creditValue, - // location: { - // connect: { id: task.locationId }, - // }, - endDate: task.endDate, - recurrenceFrequency: task.recurrenceFrequency, - specificDay: task.specificDay, - repeatDays: task.repeatDays, - }, - // include: { - // location: true, - // }, - }); - - return newTask; - } catch (error: unknown) { - Logger.error(`Failed to create task. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async updateTaskById( - taskId: number, - updateTask: InputTaskDTO, - ): Promise { - try { - const updatedTask = await prisma.task.update({ - where: { - id: taskId, - }, - data: updateTask, - // include: { - // location: true, - // }, - }); - return updatedTask; - } catch (error: unknown) { - Logger.error(`Failed to update task. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async deleteTaskById(taskId: number): Promise { - try { - await prisma.taskAssigned.deleteMany({ - where: { - taskId, - }, - }); - - const deletedTask = await prisma.task.delete({ - where: { - id: taskId, - }, - // include: { - // location: true, - // }, - }); - - return deletedTask; - } catch (error: unknown) { - Logger.error(`Failed to update task. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async assignTask( - taskAssigned: InputTaskAssignedDTO, - ): Promise { - try { - const newTaskAssigned = await prisma.taskAssigned.create({ - data: { - task: { - connect: { id: taskAssigned.taskId }, - }, - assigner: { - connect: { userId: taskAssigned.assignerId }, - }, - assignee: { - connect: { userId: taskAssigned.assigneeId }, - }, - status: taskAssigned.status, - startDate: taskAssigned.startDate, - comments: taskAssigned.comments, - }, - }); - - return newTaskAssigned; - } catch (error: unknown) { - Logger.error(`Failed to assign task. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async changeTaskStatus( - taskAssignedId: number, - status: Status, - ): Promise { - try { - const updatedTask = await prisma.taskAssigned.update({ - where: { - id: taskAssignedId, - }, - data: { - status, - }, - }); - - return updatedTask; - } catch (error: unknown) { - Logger.error( - `Failed to update task status. Reason = ${getErrorMessage(error)}`, - ); - throw error; - } - } -} - -export default TaskService; diff --git a/backend/services/implementations/userService.ts b/backend/services/implementations/userService.ts deleted file mode 100644 index fe4a6cb9..00000000 --- a/backend/services/implementations/userService.ts +++ /dev/null @@ -1,78 +0,0 @@ -import * as firebaseAdmin from "firebase-admin"; - -import prisma, { UserType } from "../../prisma"; -import IUserService, { SimplifiedUserDTO } from "../interfaces/userService"; -import { getErrorMessage } from "../../utilities/errorUtils"; -import logger from "../../utilities/logger"; - -const Logger = logger(__filename); - -class UserService implements IUserService { - async getUserByEmail(email: string): Promise { - try { - const firebaseUser = await firebaseAdmin.auth().getUserByEmail(email); - const user = await prisma.user.findUnique({ - where: { authId: firebaseUser.uid }, - }); - if (!user) { - throw new Error(`userId with authID ${firebaseUser.uid} not found.`); - } - return { - id: user.id, - type: user.type, - }; - } catch (error: unknown) { - Logger.error(`Failed to get user. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async getUserIdByAuthId(authId: string): Promise { - try { - const user = await prisma.user.findUnique({ - where: { authId }, - }); - if (!user) { - throw new Error(`user with authId ${authId} not found.`); - } - return String(user.id); - } catch (error: unknown) { - Logger.error(`Failed to get user id. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async getAuthIdById(userId: string): Promise { - try { - const user = await prisma.user.findUnique({ - where: { id: Number(userId) }, - }); - if (!user) { - throw new Error(`userId ${userId} not found.`); - } - return user.authId; - } catch (error: unknown) { - Logger.error(`Failed to get authId. Reason = ${getErrorMessage(error)}`); - throw error; - } - } - - async getUserTypeByAuthId(authId: string): Promise { - try { - const user = await prisma.user.findUnique({ - where: { authId }, - }); - if (!user) { - throw new Error(`userId with authId ${authId} not found.`); - } - return user.type; - } catch (error: unknown) { - Logger.error( - `Failed to get user role. Reason = ${getErrorMessage(error)}`, - ); - throw error; - } - } -} - -export default UserService; diff --git a/backend/services/implementations/warningService.ts b/backend/services/implementations/warningService.ts deleted file mode 100644 index 45708718..00000000 --- a/backend/services/implementations/warningService.ts +++ /dev/null @@ -1,54 +0,0 @@ -import prisma from "../../prisma"; -import IWarningService, { - WarningDTO, - CreateWarningDTO, -} from "../interfaces/warningService"; -import logger from "../../utilities/logger"; -import { getErrorMessage } from "../../utilities/errorUtils"; - -const Logger = logger(__filename); - -class WarningService implements IWarningService { - async addWarning(warning: CreateWarningDTO): Promise { - try { - const newWarning = await prisma.warning.create({ - data: { ...warning }, - include: { - assignee: true, - assigner: true, - relatedTask: true, - }, - }); - - return newWarning; - } catch (error: unknown) { - Logger.error( - `Failed to create warning. Reason = ${getErrorMessage(error)}`, - ); - throw error; - } - } - - async deleteWarning(id: number): Promise { - try { - const deletedWarning = await prisma.warning.delete({ - where: { - id, - }, - include: { - assignee: true, - assigner: true, - relatedTask: true, - }, - }); - return deletedWarning; - } catch (error: unknown) { - Logger.error( - `Failed to delete warning #${id}. Reason = ${getErrorMessage(error)}`, - ); - throw error; - } - } -} - -export default WarningService; diff --git a/backend/services/interface/announcementInterface.ts b/backend/services/interface/announcementInterface.ts new file mode 100644 index 00000000..90d96fd8 --- /dev/null +++ b/backend/services/interface/announcementInterface.ts @@ -0,0 +1,202 @@ +// import type { ResidentDTO } from "./residentService"; + +// export interface NotificationDTO { +// id: number; +// message: string; +// createdAt?: Date; +// authorId: number | null; +// groupId: number; +// recipients?: NotificationReceivedDTO[]; +// } + +// export interface CreateNotificationDTO { +// message: string; +// createdAt?: Date; +// authorId: number | null; +// } + +// export interface UpdateNotificationDTO { +// authorId?: number; +// 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 { +// /** +// * 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 +// */ +// getNotificationsByIds( +// notificationIds: number[], +// ): Promise; + +// /** +// * Updates notification for a given notification id +// * @param id notification id +// * @returns a NotificationDTO associated with the updated Notification +// * @throws Error if retrieval fails +// */ +// 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 +// * @param authorId user id of author of notification +// * @param title title of notification +// * @param message message of notification +// * @param roomIds room ids of recipients of notification +// * @returns a NotificationDTO associated with the posted notifications +// * @throws Error if creation fails +// */ +// // sendNotification( +// // authorId: number, +// // title: string, +// // message: string, +// // roomIds: number[], +// // ): Promise; + +// /** +// * Delete a user notification based on a notification id and user id +// * @param userId user id for deleted notification +// * @param notificationId notification id for deleted notification +// * @returns a NotificationDTO associated with the deleted Notification +// * @throws Error if retrieval fails +// */ +// // deleteUserNotification(notificationId: 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( +// // 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 +// * @param message message content of announcement +// * @param userId id of annoucement sender +// * @returns the new updated NotificationDTO +// * @throws Error if creation fails +// */ +// // sendAnnouncement( +// // title: string, +// // message: string, +// // userId: number, +// // ): Promise; +// } + +// export default INotificationService; diff --git a/backend/services/interface/miscInterface.ts b/backend/services/interface/miscInterface.ts new file mode 100644 index 00000000..1e858c79 --- /dev/null +++ b/backend/services/interface/miscInterface.ts @@ -0,0 +1,5 @@ +interface IMiscService { + getAvailableRooms(): Promise; +} + +export default IMiscService; diff --git a/backend/services/interface/participantInterface.ts b/backend/services/interface/participantInterface.ts new file mode 100644 index 00000000..b6167a6c --- /dev/null +++ b/backend/services/interface/participantInterface.ts @@ -0,0 +1,14 @@ +import { Participant } from "@prisma/client"; + +interface IParticipantService { + getAllParticipants(): Promise; + getParticipantById(participantId: string): Promise; + createParticipant( + participantId: string, + roomNumber: number, + arrival: string, + password: string, + ): Promise; +} + +export default IParticipantService; diff --git a/backend/services/interface/taskInterface.ts b/backend/services/interface/taskInterface.ts new file mode 100644 index 00000000..46512c50 --- /dev/null +++ b/backend/services/interface/taskInterface.ts @@ -0,0 +1,160 @@ +// import { +// TaskType, +// Status, +// RecurrenceFrequency, +// DaysOfWeek, +// } from "../../prisma"; + +// export interface TaskDTO { +// id: number; +// type: TaskType; +// title: string; +// description: string | null; +// creditValue: number; +// // location: TaskLocationDTO; +// endDate: Date | null; +// recurrenceFrequency: RecurrenceFrequency; +// specificDay: DaysOfWeek | null; +// repeatDays: DaysOfWeek[]; +// } + +// // export interface TaskLocationDTO { +// // id: number; +// // title: string; +// // description: string; +// // } + +// export interface InputTaskDTO { +// type: TaskType; +// title: string; +// description: string | null; +// creditValue: number; +// // locationId: number; +// endDate: Date | null; +// recurrenceFrequency: RecurrenceFrequency; +// specificDay: DaysOfWeek | null; +// repeatDays: DaysOfWeek[]; +// } + +// export interface InputTaskAssignedDTO { +// taskId: number; +// assigneeId: number; +// assignerId?: number; +// status: Status; +// startDate: Date; +// comments?: string; +// } + +// export interface TaskAssignedDTO { +// id: number; +// taskId: number; +// assignerId: number | null; +// assigneeId: number; +// status: Status; +// startDate: Date; +// comments: string | null; +// } + +// interface ITaskService { +// /** +// * Get the task corresponding to the taskId +// * @param id task id +// * @returns a TaskDTO associated with the task id +// * @throws Error if task retrieval fails +// */ +// getTaskById(taskId: number): Promise; + +// /** +// * Get all tasks belonging to a category +// * @param type task type +// * @returns a list of TaskDTOs with a given type +// * @throws Error if task retrieval fails +// */ +// getTasksByType(type: TaskType): Promise; + +// /** +// * Get all tasks assigned to a resident +// * @param assigneeId assignee's id +// * @returns a list of TaskDTOs with a given assignee +// * @throws Error if task retrieval fails +// */ +// getTasksByAssigneeId(assigneeId: number): Promise; + +// /** +// * Get all tasks assigned by a staff member +// * @param assignerId assigner's id +// * @returns a list of TaskDTOs with a given assigner +// * @throws Error if task retrieval fails +// */ +// getTasksByAssignerId(assignerId: number): Promise; + +// /** +// * Get all tasks by a start date +// * @param startDate start date +// * @returns a list of TaskDTOs starting on the provided date +// * @throws Error if task retrieval fails +// */ +// getTasksByStartDate(startDate: Date): Promise; + +// /** +// * Get all tasks by an end date +// * @param endDate end date +// * @returns a list of TaskDTOs ending on the provided date +// * @throws Error if task retrieval fails +// */ +// // getTasksByEndDate(endDate: Date): Promise; + +// /** +// * Get all tasks by a status +// * @param status status +// * @returns a list of TaskDTO with a given status +// * @throws Error if task retrieval fails +// */ +// getTasksByStatus(status: Status): Promise; + +// /** +// * Create a task +// * @param task the user to be created +// * @returns a TaskDTO with the created task's information +// * @throws Error if task creation fails +// */ +// createTask(task: InputTaskDTO): Promise; + +// /** +// * Update a task. +// * @param taskId task's id +// * @param task the task to be updated +// * @returns a TaskDTO with the updated task's information +// * @throws Error if task update fails +// */ +// updateTaskById(taskId: number, task: InputTaskDTO): Promise; + +// /** +// * Delete a task by id +// * @param taskId task's taskId +// * @returns a TaskDTO with the deleted task's information +// * @throws Error if task deletion fails +// */ +// deleteTaskById(taskId: number): Promise; + +// /** +// * Assign a task to a resident +// * @param taskAssigned the task to be assigned +// * @returns a TaskAssignedDTO with the TaskAssigned's information +// * @throws Error if task assignment fails +// */ +// assignTask(taskAssigned: InputTaskAssignedDTO): Promise; + +// /** +// * Changes the status of an assigned task +// * @param taskAssigned the task to have its status changed +// * @returns a TaskAssignedDTO with the TaskAssigned's information +// * @throws Error if task status change fails +// */ +// changeTaskStatus( +// taskAssignedId: number, +// status: Status, +// ): Promise; +// } + +// export default ITaskService; diff --git a/backend/services/interfaces/authService.ts b/backend/services/interfaces/authService.ts deleted file mode 100644 index 9e9f061a..00000000 --- a/backend/services/interfaces/authService.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { UserType } from "../../prisma"; -import { Token } from "../../types"; -import { SimplifiedUserDTO } from "./userService"; - -export type AuthDTO = Token & SimplifiedUserDTO; - -interface IAuthService { - /** - * Generate a short-lived JWT access token and a long-lived refresh token - * when supplied user's email and password - * @param email user's email - * @param password user's password - * @returns AuthDTO object containing the access token, refresh token, and user info - * @throws Error if token generation fails - */ - generateToken( - email: string, - password: string, - userType: UserType, - ): Promise; - - /** - * Revoke all refresh tokens of a user - * @param userId userId of user whose refresh tokens are to be revoked - * @throws Error if token revocation fails - */ - revokeTokens(userId: string): Promise; - - /** - * Generate new access and refresh token pair using the provided refresh token - * @param refreshToken refresh token - * @returns Token object containing new access and refresh tokens - * @throws Error if token renewal fails - */ - renewToken(refreshToken: string): Promise; - - /** - * Generate a password reset link for the user with the given email and send - * the link to that email address - * @param email email of user requesting password reset - * @throws Error if unable to generate link or send email - */ - resetPassword(email: string): Promise; - - /** - * Generate an email verification link for the user with the given email and send - * the link to that email address - * @param email email of user that needs to be verified - * @throws Error if unable to generate link or send email - */ - sendEmailVerificationLink(email: string): Promise; - - /** - * Determine if the provided access token is valid and authorized for at least - * one of the specified roles - * @param accessToken user's access token - * @param roles roles to check for - * @returns true if token valid and authorized, false otherwise - */ - isAuthorizedByUserType( - accessToken: string, - types: Set, - ): Promise; - - /** - * Determine if the provided access token is valid and issued to the requested user - * @param accessToken user's access token - * @param requestedUserId userId of requested user - * @returns true if token valid and authorized, false otherwise - */ - isAuthorizedByUserId( - accessToken: string, - requestedUserId: string, - ): Promise; - - /** - * Determine if the provided access token is valid and issued to the requested user - * with the specified email address - * @param accessToken user's access token - * @param requestedEmail email address of requested user - * @returns true if token valid and authorized, false otherwise - */ - isAuthorizedByEmail( - accessToken: string, - requestedEmail: string, - ): Promise; -} - -export default IAuthService; diff --git a/backend/services/interfaces/emailService.ts b/backend/services/interfaces/emailService.ts deleted file mode 100644 index c56dca4d..00000000 --- a/backend/services/interfaces/emailService.ts +++ /dev/null @@ -1,12 +0,0 @@ -interface IEmailService { - /** - * Send email - * @param to recipient's email - * @param subject email subject - * @param htmlBody email body as html - * @throws Error if email was not sent successfully - */ - sendEmail(to: string, subject: string, htmlBody: string): Promise; -} - -export default IEmailService; diff --git a/backend/services/interfaces/fileStorageService.ts b/backend/services/interfaces/fileStorageService.ts deleted file mode 100644 index f9d56bc0..00000000 --- a/backend/services/interfaces/fileStorageService.ts +++ /dev/null @@ -1,48 +0,0 @@ -interface IFileStorageService { - /** - * Retrieves file - * @param fileName name of file - * @param expirationTimeMinutes expiration time in minutes for generated URL - * @returns Signed URL to file - * @throws Error if file is not retrieved - */ - getFile(fileName: string, expirationTimeMinutes?: number): Promise; - - /** - * Creates file - * @param fileName name of file - * @param filePath path of file - * @param contentType MIME type of file - * @throws Error if name of file already exists - * @throws Error if file is not uploaded - */ - createFile( - fileName: string, - filePath: string, - contentType?: string | null, - ): Promise; - - /** - * Updates file - * @param fileName name of file - * @param filePath path to file - * @param contentType MIME type of file - * @throws Error if name of file does not exist - * @throws Error if file is not updated - */ - updateFile( - fileName: string, - filePath: string, - contentType?: string | null, - ): Promise; - - /** - * Deletes file - * @param fileName name of file - * @throws Error if name of file does not exist - * @throws Error if file is not deleted - */ - deleteFile(fileName: string): Promise; -} - -export default IFileStorageService; diff --git a/backend/services/interfaces/notificationService.ts b/backend/services/interfaces/notificationService.ts deleted file mode 100644 index 437f53ab..00000000 --- a/backend/services/interfaces/notificationService.ts +++ /dev/null @@ -1,202 +0,0 @@ -import type { ResidentDTO } from "./residentService"; - -export interface NotificationDTO { - id: number; - message: string; - createdAt?: Date; - authorId: number | null; - groupId: number; - recipients?: NotificationReceivedDTO[]; -} - -export interface CreateNotificationDTO { - message: string; - createdAt?: Date; - authorId: number | null; -} - -export interface UpdateNotificationDTO { - authorId?: number; - 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 { - /** - * 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 - */ - getNotificationsByIds( - notificationIds: number[], - ): Promise; - - /** - * Updates notification for a given notification id - * @param id notification id - * @returns a NotificationDTO associated with the updated Notification - * @throws Error if retrieval fails - */ - 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 - * @param authorId user id of author of notification - * @param title title of notification - * @param message message of notification - * @param roomIds room ids of recipients of notification - * @returns a NotificationDTO associated with the posted notifications - * @throws Error if creation fails - */ - // sendNotification( - // authorId: number, - // title: string, - // message: string, - // roomIds: number[], - // ): Promise; - - /** - * Delete a user notification based on a notification id and user id - * @param userId user id for deleted notification - * @param notificationId notification id for deleted notification - * @returns a NotificationDTO associated with the deleted Notification - * @throws Error if retrieval fails - */ - // deleteUserNotification(notificationId: 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( - // 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 - * @param message message content of announcement - * @param userId id of annoucement sender - * @returns the new updated NotificationDTO - * @throws Error if creation fails - */ - // 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 deleted file mode 100644 index 427b7dda..00000000 --- a/backend/services/interfaces/residentService.ts +++ /dev/null @@ -1,105 +0,0 @@ -// eslint-disable-next-line import/no-cycle -import { - NotificationGroupDTO, - NotificationReceivedDTO, -} from "./notificationService"; -import { UserDTO, CreateUserDTO, UpdateUserDTO } from "./userService"; - -export interface ResidentDTO extends Omit { - userId: number; - residentId: number; - roomNumber: number; - credits: number; - dateJoined: Date; - dateLeft: Date | null; - notificationGroup?: NotificationGroupDTO[]; - notificationRecieved?: NotificationReceivedDTO[]; -} - -export interface CreateResidentDTO extends CreateUserDTO { - residentId: number; - roomNumber: number; - credits?: number; - dateJoined?: Date; - dateLeft?: Date; -} - -export interface UpdateResidentDTO extends UpdateUserDTO { - residentId?: number; - roomNumber?: number; - credits?: number; - dateJoined?: Date; - dateLeft?: Date; -} - -// Have to manually map enums as ts treats enums as numbers -export enum RedeemCreditsResponse { - SUCCESS = "SUCCESS", - NOT_ENOUGH_CREDITS = "NOT_ENOUGH_CREDITS", - INVALID_ID = "INVALID_ID", -} - -interface IResidentService { - /** - * Adds a resident - * @param resident: a CreateResidentDTO with the new resident's information - * @returns a ResidentDTO with created resident info - * @throws Error if resident reation fails - */ - addResident(resident: CreateResidentDTO): Promise; - - /** - * Update a resident's details - * @param userId: The ID of the resident information that needs to be updated - * @param resident: Update ResidentDTO of the resident with this information - * @returns: a ResidentDTO with resident's updated info - */ - updateResident( - userId: number, - resident: UpdateResidentDTO, - ): Promise; - - /** - * Deletes a resident by id - * @param userId: resident id of resident to be deleted - * @returns: a ResidentDTO with deleted resident's info - * @throws Error if resident deletion fails - */ - deleteResident(userId: number): Promise; - - /** - * Gets all residents - * @returns: array of ResidentDTO's with all residents - * @throws Error if retrieval fails - */ - getAllResidents(): Promise>; - - /** - * Gets certain residents based on resident id - * @param userId: array of resident id's to be retrieved - * @returns: array of ResidentDTO's with residents information - * @throws Error if retrieval fails - */ - getResidentsByIds(userId: number[]): Promise>; - - /** - * Gets all residents that are currently active - * @returns: array of ResidentDTO's with all active residents - * @throws Error if retrieval fails - */ - getActiveResidents(): Promise>; - - /** - * Redeems certain resident's credits based on resident id - * @param userId: resident id whose credits are to be redeemed - * and number of credits to be redeemed - * @returns: Enum of success or not enough credits - * @throws Error if retrieval fails - */ - redeemCredits( - userId: number, - credits: number, - ): Promise; -} - -export default IResidentService; diff --git a/backend/services/interfaces/staffService.ts b/backend/services/interfaces/staffService.ts deleted file mode 100644 index 1624cd7e..00000000 --- a/backend/services/interfaces/staffService.ts +++ /dev/null @@ -1,69 +0,0 @@ -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 { - /** - * Create a staff - * @param staff the staff to be created - * @returns a StaffDTO with the created user's information - * @throws Error if user creation fails - */ - addStaff(staff: CreateStaffDTO): Promise; - - /** - * Update a staff. - * @param staffId staff id - * @param staff the staff to be updated - * @returns a StaffDTO with the updated staff's information - * @throws Error if staff update fails - */ - updateStaff(staffId: number, staff: UpdateStaffDTO): Promise; - - /** - * Delete a staff by id - * @param staffId staff's id - * @throws Error if staff deletion fails - */ - deleteStaff(staffId: number): Promise; - - /** - * Get all staff information - * @returns array of StaffDTOs - * @throws Error if staff retrieval fails - */ - getAllStaff(): Promise>; - - /** - * Get staff associated with id - * @param staffIds list of staff ids - * @returns a StaffDTO with staff's information - * @throws Error if staff retrieval fails - */ - getStaffByIds(staffIds: number[]): Promise>; -} - -export default IStaffService; diff --git a/backend/services/interfaces/taskService.ts b/backend/services/interfaces/taskService.ts deleted file mode 100644 index b4066867..00000000 --- a/backend/services/interfaces/taskService.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { - TaskType, - Status, - RecurrenceFrequency, - DaysOfWeek, -} from "../../prisma"; - -export interface TaskDTO { - id: number; - type: TaskType; - title: string; - description: string | null; - creditValue: number; - // location: TaskLocationDTO; - endDate: Date | null; - recurrenceFrequency: RecurrenceFrequency; - specificDay: DaysOfWeek | null; - repeatDays: DaysOfWeek[]; -} - -// export interface TaskLocationDTO { -// id: number; -// title: string; -// description: string; -// } - -export interface InputTaskDTO { - type: TaskType; - title: string; - description: string | null; - creditValue: number; - // locationId: number; - endDate: Date | null; - recurrenceFrequency: RecurrenceFrequency; - specificDay: DaysOfWeek | null; - repeatDays: DaysOfWeek[]; -} - -export interface InputTaskAssignedDTO { - taskId: number; - assigneeId: number; - assignerId?: number; - status: Status; - startDate: Date; - comments?: string; -} - -export interface TaskAssignedDTO { - id: number; - taskId: number; - assignerId: number | null; - assigneeId: number; - status: Status; - startDate: Date; - comments: string | null; -} - -interface ITaskService { - /** - * Get the task corresponding to the taskId - * @param id task id - * @returns a TaskDTO associated with the task id - * @throws Error if task retrieval fails - */ - getTaskById(taskId: number): Promise; - - /** - * Get all tasks belonging to a category - * @param type task type - * @returns a list of TaskDTOs with a given type - * @throws Error if task retrieval fails - */ - getTasksByType(type: TaskType): Promise; - - /** - * Get all tasks assigned to a resident - * @param assigneeId assignee's id - * @returns a list of TaskDTOs with a given assignee - * @throws Error if task retrieval fails - */ - getTasksByAssigneeId(assigneeId: number): Promise; - - /** - * Get all tasks assigned by a staff member - * @param assignerId assigner's id - * @returns a list of TaskDTOs with a given assigner - * @throws Error if task retrieval fails - */ - getTasksByAssignerId(assignerId: number): Promise; - - /** - * Get all tasks by a start date - * @param startDate start date - * @returns a list of TaskDTOs starting on the provided date - * @throws Error if task retrieval fails - */ - getTasksByStartDate(startDate: Date): Promise; - - /** - * Get all tasks by an end date - * @param endDate end date - * @returns a list of TaskDTOs ending on the provided date - * @throws Error if task retrieval fails - */ - // getTasksByEndDate(endDate: Date): Promise; - - /** - * Get all tasks by a status - * @param status status - * @returns a list of TaskDTO with a given status - * @throws Error if task retrieval fails - */ - getTasksByStatus(status: Status): Promise; - - /** - * Create a task - * @param task the user to be created - * @returns a TaskDTO with the created task's information - * @throws Error if task creation fails - */ - createTask(task: InputTaskDTO): Promise; - - /** - * Update a task. - * @param taskId task's id - * @param task the task to be updated - * @returns a TaskDTO with the updated task's information - * @throws Error if task update fails - */ - updateTaskById(taskId: number, task: InputTaskDTO): Promise; - - /** - * Delete a task by id - * @param taskId task's taskId - * @returns a TaskDTO with the deleted task's information - * @throws Error if task deletion fails - */ - deleteTaskById(taskId: number): Promise; - - /** - * Assign a task to a resident - * @param taskAssigned the task to be assigned - * @returns a TaskAssignedDTO with the TaskAssigned's information - * @throws Error if task assignment fails - */ - assignTask(taskAssigned: InputTaskAssignedDTO): Promise; - - /** - * Changes the status of an assigned task - * @param taskAssigned the task to have its status changed - * @returns a TaskAssignedDTO with the TaskAssigned's information - * @throws Error if task status change fails - */ - changeTaskStatus( - taskAssignedId: number, - status: Status, - ): Promise; -} - -export default ITaskService; diff --git a/backend/services/interfaces/userService.ts b/backend/services/interfaces/userService.ts deleted file mode 100644 index 09a11d26..00000000 --- a/backend/services/interfaces/userService.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { UserType } from "../../prisma"; - -export type UserDTO = { - id: number; - type: UserType; - displayName?: string | null; - profilePictureURL?: string | null; -}; - -export type SimplifiedUserDTO = Pick; - -export interface CreateUserDTO { - password: string; - displayName?: string; - profilePictureURL?: string; -} - -export interface UpdateUserDTO { - password?: string; - displayName?: string; - profilePictureURL?: string; -} - -interface IUserService { - /** - * Get user associated with email - * @param email user's email - * @returns a UserDTO with user's information - * @throws Error if user retrieval fails - */ - getUserByEmail(email: string): Promise; - - /** - * Get id of user associated with authId - * @param authId user's authId - * @returns id of the user - * @throws Error if user id retrieval fails - */ - getUserIdByAuthId(authId: string): Promise; - - /** - * Get authId of user associated with id - * @param userId user's id - * @returns user's authId - * @throws Error if user authId retrieval fails - */ - getAuthIdById(userId: string): Promise; - - /** - * Get user type associated with authId - * @param authId user's authId - * @returns user's type - * @throws Error if user type retrieval fails - */ - getUserTypeByAuthId(authId: string): Promise; -} - -export default IUserService; diff --git a/backend/services/interfaces/warningService.ts b/backend/services/interfaces/warningService.ts deleted file mode 100644 index e1233c2d..00000000 --- a/backend/services/interfaces/warningService.ts +++ /dev/null @@ -1,38 +0,0 @@ -export interface WarningDTO { - id: number; - title: string; - description: string; - dateIssued?: Date; - assigneeId: number; - assignerId: number | null; - relatedTaskId?: number | null; -} - -export interface CreateWarningDTO { - title: string; - description: string; - dateIssued?: Date; - assigneeId: number; - assignerId: number | null; - relatedTaskId?: number | null; -} - -interface IWarningService { - /** - * Adds a warning - * @param warningInfo: a CreateWarningDTO with the new warning's information - * @returns a WarningDTO with created warning info - * @throws Error if warning creation fails - */ - addWarning(warningInfo: CreateWarningDTO): Promise; - - /** - * Deletes a warning by id - * @param id: warning id of warning to be deleted - * @returns: a WarningDTO with deleted warning's info - * @throws Error if warning deletion fails - */ - deleteWarning(id: number): Promise; -} - -export default IWarningService; diff --git a/backend/testUtils/testDb.ts b/backend/testUtils/testDb.ts deleted file mode 100644 index 82111a47..00000000 --- a/backend/testUtils/testDb.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { resolve } from "path"; - -import { Sequelize } from "sequelize-typescript"; - -const DATABASE_URL = - process.env.NODE_ENV === "production" - ? /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ - process.env.DATABASE_URL! - : `postgres://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PASSWORD}@${process.env.DB_HOST}:5432/${process.env.POSTGRES_DB_TEST}`; - -/* eslint-disable-next-line import/prefer-default-export */ -export const testSql = new Sequelize(DATABASE_URL, { - models: [resolve(__dirname, "../models/*.model.ts")], - logging: false, -}); diff --git a/backend/types.ts b/backend/types.ts deleted file mode 100644 index 43769d6b..00000000 --- a/backend/types.ts +++ /dev/null @@ -1,17 +0,0 @@ -export type Token = { - accessToken: string; - refreshToken: string; -}; - -export type NodemailerConfig = { - service: "gmail"; - auth: { - type: "OAuth2"; - user: string; - clientId: string; - clientSecret: string; - refreshToken: string; - }; -}; - -export type SignUpMethod = "PASSWORD" | "GOOGLE"; diff --git a/backend/umzug.ts b/backend/umzug.ts deleted file mode 100644 index 077322de..00000000 --- a/backend/umzug.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as path from "path"; - -import { Umzug, SequelizeStorage } from "umzug"; -import { Sequelize } from "sequelize-typescript"; - -const DATABASE_URL = - process.env.NODE_ENV === "production" - ? /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion */ - process.env.DATABASE_URL! - : `postgres://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PASSWORD}@${process.env.DB_HOST}:5432/${process.env.POSTGRES_DB_DEV}`; - -const sequelize = new Sequelize(DATABASE_URL, { - models: [path.join(__dirname, "/*.model.ts")], -}); - -export const migrator = new Umzug({ - migrations: { - glob: ["migrations/*.ts", { cwd: __dirname }], - }, - context: sequelize, - storage: new SequelizeStorage({ - sequelize, - }), - logger: console, -}); - -export type Migration = typeof migrator._types.migration; diff --git a/backend/utilities/CSVUtils.ts b/backend/utilities/CSVUtils.ts deleted file mode 100644 index c88173e7..00000000 --- a/backend/utilities/CSVUtils.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { parseAsync, transforms } from "json2csv"; -import { Readable, TransformOptions } from "stream"; - -type GenerateCSVParams = { - data: Readonly | ReadonlyArray | Readable; - fields?: string[]; - transformFunction?: (item: T) => Record; - flattenObjects?: boolean; - flattenArrays?: boolean; - pathsToUnwind?: string[]; - opts?: json2csv.Options; - transformOpts?: TransformOptions; -}; - -/** - * Generate a CSV from a JSON array/object or readable input stream. - * fields, transformFunction, flattenObjects, flattenArrays, and pathsToUnwind belong to json2csv.options but - * they are also provided as parameters here for convenience. - * For examples using each of these params, see the CSVUtils.test.ts file. - * @param data JSON array/object or stream to convert to a CSV string - * @param fields columns to include in the csv - * @param transformFunction function to transform fields of the object before converting to csv - * @param flattenObjects indicates whether each property of object fields should be split into a different column - * @param flattenArrays indicates whether each element of array fields should be split into a different column - * @param pathsToUnwind array fields that should be split into different rows - * @param opts options from json2csv to override or add additional options (https://mircozeiss.com/json2csv/#available-options) - * @param transformOpts transform options from stream module (https://nodejs.org/api/stream.html#stream_new_stream_transform_options) - * @returns CSV string - * @throws Error if JSON is not parsed properly - */ -/* eslint-disable-next-line import/prefer-default-export */ -export const generateCSV = async ({ - data, - fields, - transformFunction, - flattenObjects = false, - flattenArrays = false, - pathsToUnwind, - opts, - transformOpts, -}: GenerateCSVParams): Promise => { - const transformations = [ - transforms.flatten({ - objects: flattenObjects, - arrays: flattenArrays, - }), - ]; - if (transformFunction) { - transformations.push(transformFunction); - } - if (pathsToUnwind) { - transformations.push(transforms.unwind({ paths: pathsToUnwind })); - } - - const options = { - fields, - transforms: transformations, - ...opts, - }; - return parseAsync(data, options, transformOpts); -}; diff --git a/backend/utilities/errorUtils.ts b/backend/utilities/errorUtils.ts deleted file mode 100644 index a5e59f0f..00000000 --- a/backend/utilities/errorUtils.ts +++ /dev/null @@ -1,4 +0,0 @@ -/* eslint-disable-next-line import/prefer-default-export */ -export const getErrorMessage = (error: unknown): string => { - return error instanceof Error ? error.message : "Unknown error occurred."; -}; diff --git a/backend/utilities/firebaseRestClient.ts b/backend/utilities/firebaseRestClient.ts deleted file mode 100644 index 8afa69fc..00000000 --- a/backend/utilities/firebaseRestClient.ts +++ /dev/null @@ -1,181 +0,0 @@ -import fetch, { Response } from "node-fetch"; - -import { Token } from "../types"; -import logger from "./logger"; - -const Logger = logger(__filename); - -const FIREBASE_SIGN_IN_URL = - "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword"; -const FIREBASE_REFRESH_TOKEN_URL = - "https://securetoken.googleapis.com/v1/token"; -const FIREBASE_OAUTH_SIGN_IN_URL = - "https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp"; - -type PasswordSignInResponse = { - idToken: string; - email: string; - refreshToken: string; - expiresIn: string; - localId: string; - registered: boolean; -}; - -type OAuthSignInResponse = { - federatedId: string; - providerId: string; - localId: string; - emailVerified: boolean; - email: string; - oauthIdToken: string; - oauthAccessToken: string; - oauthTokenSecret: string; - rawUserInfo: string; - firstName: string; - lastName: string; - fullName: string; - displayName: string; - photoUrl: string; - idToken: string; - refreshToken: string; - expiresIn: string; - needConfirmation: boolean; -}; - -type RefreshTokenResponse = { - expires_in: string; - token_type: string; - refresh_token: string; - id_token: string; - user_id: string; - project_id: string; -}; - -type RequestError = { - error: { - code: number; - message: string; - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - errors: any; - }; -}; - -const FirebaseRestClient = { - // Docs: https://firebase.google.com/docs/reference/rest/auth/#section-sign-in-email-password - signInWithPassword: async ( - email: string, - password: string, - ): Promise => { - const response: Response = await fetch( - `${FIREBASE_SIGN_IN_URL}?key=${process.env.FIREBASE_WEB_API_KEY}`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - email, - password, - returnSecureToken: true, - }), - }, - ); - - const responseJson: - | PasswordSignInResponse - | RequestError = await response.json(); - - if (!response.ok) { - const errorMessage = [ - "Failed to sign-in via Firebase REST API, status code =", - `${response.status},`, - "error message =", - (responseJson as RequestError).error.message, - ]; - Logger.error(errorMessage.join(" ")); - - throw new Error("Failed to sign-in via Firebase REST API"); - } - - return { - accessToken: (responseJson as PasswordSignInResponse).idToken, - refreshToken: (responseJson as PasswordSignInResponse).refreshToken, - }; - }, - - // Docs: https://firebase.google.com/docs/reference/rest/auth/#section-sign-in-with-oauth-credential - signInWithGoogleOAuth: async ( - idToken: string, - ): Promise => { - const response: Response = await fetch( - `${FIREBASE_OAUTH_SIGN_IN_URL}?key=${process.env.FIREBASE_WEB_API_KEY}`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - postBody: `id_token=${idToken}&providerId=google.com`, - requestUri: process.env.FIREBASE_REQUEST_URI, - returnIdpCredential: true, - returnSecureToken: true, - }), - }, - ); - - const responseJson: - | OAuthSignInResponse - | RequestError = await response.json(); - - if (!response.ok) { - const errorMessage = [ - "Failed to sign-in via Firebase REST API with OAuth, status code =", - `${response.status},`, - "error message =", - (responseJson as RequestError).error.message, - ]; - Logger.error(errorMessage.join(" ")); - - throw new Error("Failed to sign-in via Firebase REST API"); - } - - return responseJson as OAuthSignInResponse; - }, - - // Docs: https://firebase.google.com/docs/reference/rest/auth/#section-refresh-token - refreshToken: async (refreshToken: string): Promise => { - const response: Response = await fetch( - `${FIREBASE_REFRESH_TOKEN_URL}?key=${process.env.FIREBASE_WEB_API_KEY}`, - { - method: "POST", - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, - body: `grant_type=refresh_token&refresh_token=${refreshToken}`, - }, - ); - - const responseJson: - | RefreshTokenResponse - | RequestError = await response.json(); - - if (!response.ok) { - const errorMessage = [ - "Failed to refresh token via Firebase REST API, status code =", - `${response.status},`, - "error message =", - (responseJson as RequestError).error.message, - ]; - Logger.error(errorMessage.join(" ")); - - throw new Error("Failed to refresh token via Firebase REST API"); - } - - return { - accessToken: (responseJson as RefreshTokenResponse).id_token, - refreshToken: (responseJson as RefreshTokenResponse).refresh_token, - }; - }, -}; - -export default FirebaseRestClient; diff --git a/backend/utilities/logger.ts b/backend/utilities/logger.ts deleted file mode 100644 index b8172b59..00000000 --- a/backend/utilities/logger.ts +++ /dev/null @@ -1,51 +0,0 @@ -import * as winston from "winston"; - -type Logger = { - error: (message: string) => void; - warn: (message: string) => void; - info: (message: string) => void; - http: (message: string) => void; - verbose: (message: string) => void; - debug: (message: string) => void; -}; - -const WinstonLogger: winston.Logger = winston.createLogger({ - level: "info", - format: winston.format.combine( - winston.format.timestamp(), - winston.format.json(), - ), - transports: [ - new winston.transports.File({ filename: "error.log", level: "error" }), - new winston.transports.File({ filename: "combined.log" }), - ], -}); - -if (process.env.NODE_ENV !== "production") { - WinstonLogger.add(new winston.transports.Console()); -} - -const logger = (fileName: string): Logger => { - return { - error: (message: string) => { - WinstonLogger.error(`[${fileName}] ${message}`); - }, - warn: (message: string) => { - WinstonLogger.warn(`[${fileName}] ${message}`); - }, - info: (message: string) => { - WinstonLogger.info(`[${fileName}] ${message}`); - }, - http: (message: string) => { - WinstonLogger.http(`[${fileName}] ${message}`); - }, - verbose: (message: string) => { - WinstonLogger.verbose(`[${fileName}] ${message}`); - }, - debug: (message: string) => { - WinstonLogger.debug(`[${fileName}] ${message}`); - }, - }; -}; - -export default logger; diff --git a/db-init/create-multiple-dbs.sh b/db-init/create-multiple-dbs.sh deleted file mode 100755 index aa665fa4..00000000 --- a/db-init/create-multiple-dbs.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -set -e -set -u - -function create_user_and_database() { - local database=$1 - echo " Creating user and database '$database'" - psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL - CREATE USER $database; - CREATE DATABASE $database; - GRANT ALL PRIVILEGES ON DATABASE $database TO $database; -EOSQL -} - -if [ -n "$POSTGRES_MULTIPLE_DATABASES" ]; then - echo "Multiple database creation requested: $POSTGRES_MULTIPLE_DATABASES" - for db in $(echo $POSTGRES_MULTIPLE_DATABASES | tr ',' ' '); do - create_user_and_database $db - done - echo "Multiple databases created" -fi diff --git a/e2e-tests/cat.png b/e2e-tests/cat.png deleted file mode 100644 index ad118fa8..00000000 Binary files a/e2e-tests/cat.png and /dev/null differ diff --git a/e2e-tests/conftest.py b/e2e-tests/conftest.py deleted file mode 100644 index 984e54f6..00000000 --- a/e2e-tests/conftest.py +++ /dev/null @@ -1,95 +0,0 @@ -import inflection -import os -import pytest -import requests -from dotenv import load_dotenv - -load_dotenv() - - -def pytest_addoption(parser): - parser.addoption("--lang", action="store", default="ts", choices=["ts", "python"]) - parser.addoption( - "--api", action="store", default="rest", choices=["rest", "graphql"] - ) - parser.addoption("--auth", action="store_true") - parser.addoption("--fs", action="store_true") - - -@pytest.fixture(scope="session") -def lang(request): - return request.config.getoption("--lang") - - -@pytest.fixture(scope="session") -def api(request): - return request.config.getoption("--api") - - -@pytest.fixture(scope="session") -def auth(request): - return request.config.getoption("--auth") - - -@pytest.fixture(scope="session") -def fs(request): - return request.config.getoption("--fs") - - -@pytest.fixture(scope="session") -def backend_url(): - return os.getenv("TEST_SCRIPT_BACKEND_URL") - - -@pytest.fixture(scope="session") -def new_user_email(): - return os.getenv("TEST_SCRIPT_NEW_USER_EMAIL") - - -@pytest.fixture(scope="module") -def auth_user(backend_url, api, auth): - if not auth: - return {} - if api == "rest": - response = requests.post( - f"{backend_url}/auth/login", - json={ - "email": os.getenv("TEST_SCRIPT_EMAIL"), - "password": os.getenv("TEST_SCRIPT_PASSWORD"), - }, - ) - return response.json() - else: - query = """ - mutation($email: String!, $password: String!) { - login(email: $email, password: $password) { - id - firstName - lastName - email - role - accessToken - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={ - "query": query, - "variables": { - "email": os.getenv("TEST_SCRIPT_EMAIL"), - "password": os.getenv("TEST_SCRIPT_PASSWORD"), - }, - }, - ) - return response.json()["data"]["login"] - - -@pytest.fixture(scope="module") -def auth_header(auth_user, lang): - if not auth_user: - return {} - accessTokenField = "accessToken" - if lang != "ts": - accessTokenField = inflection.underscore(accessTokenField) - return {"Authorization": "Bearer " + auth_user[accessTokenField]} diff --git a/e2e-tests/dog.jpg b/e2e-tests/dog.jpg deleted file mode 100644 index 172fdd61..00000000 Binary files a/e2e-tests/dog.jpg and /dev/null differ diff --git a/e2e-tests/test_auth.py b/e2e-tests/test_auth.py deleted file mode 100644 index 9f3b6445..00000000 --- a/e2e-tests/test_auth.py +++ /dev/null @@ -1,55 +0,0 @@ -import inflection -import requests - -from test_user import delete_user - - -def register_user(backend_url, body, access_token_field): - response = requests.post(f"{backend_url}/auth/register", json=body) - assert response.status_code == 200 - data = response.json() - assert "role" in data - assert data["role"] == "User" - assert "id" in data - assert access_token_field in data - expected = {k: v for k, v in body.items() if k != "password"} - actual = {k: v for k, v in data.items() if k in body} - assert actual == expected - return response.json() - - -def reset_password(backend_url, auth_header, email): - response = requests.post( - f"{backend_url}/auth/resetPassword/{email}", - headers=auth_header, - ) - assert response.status_code == 204 - - -def logout(backend_url, auth_header, id): - response = requests.post( - f"{backend_url}/auth/logout/{id}", - headers=auth_header, - ) - assert response.status_code == 204 - - -def test_auth(backend_url, auth_header, auth_user, lang, api, new_user_email): - if not auth_header or api != "rest": - return - - body = { - "firstName": "Test", - "lastName": "Script", - "email": new_user_email, - "password": "password123", - } - access_token_field = "accessToken" - if lang != "ts": - body = {inflection.underscore(k): v for k, v in body.items()} - access_token_field = inflection.underscore(access_token_field) - user = register_user(backend_url, body, access_token_field) - delete_user(backend_url, auth_header, user["id"], lang) - # Call the following with the auth user since we cannot verify emails on new users in the script - reset_password(backend_url, auth_header, auth_user["email"]) - logout(backend_url, auth_header, auth_user["id"]) diff --git a/e2e-tests/test_auth_gql.py b/e2e-tests/test_auth_gql.py deleted file mode 100644 index 8e21ae6e..00000000 --- a/e2e-tests/test_auth_gql.py +++ /dev/null @@ -1,89 +0,0 @@ -import inflection -import requests - -from test_user_gql import delete_user - - -def register_user(backend_url, body, access_token_field): - query = """ - mutation($user: RegisterUserDTO!) { - register(user: $user) { - id - firstName - lastName - email - role - accessToken - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"user": body}}, - ) - assert "data" in response.json() - assert "register" in response.json()["data"] - data = response.json()["data"]["register"] - assert "role" in data - assert data["role"] == "User" - assert "id" in data - assert access_token_field in data - expected = {k: v for k, v in body.items() if k != "password"} - actual = {k: v for k, v in data.items() if k in body} - assert actual == expected - return data - - -def reset_password(backend_url, auth_header, email): - query = """ - mutation($email: String!) { - resetPassword(email: $email) - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"email": email}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "resetPassword" in response.json()["data"] - data = response.json()["data"]["resetPassword"] - assert data - return data - - -def logout(backend_url, auth_header, id): - query = """ - mutation($userId: ID!) { - logout(userId: $userId) - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"userId": id}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "logout" in response.json()["data"] - return response.json()["data"]["logout"] - - -def test_auth_gql(backend_url, auth_header, auth_user, lang, api, new_user_email): - if not auth_header or api == "rest": - return - - body = { - "firstName": "Test", - "lastName": "Script", - "email": new_user_email, - "password": "password123", - } - access_token_field = "accessToken" - if lang != "ts": - body = {inflection.underscore(k): v for k, v in body.items()} - access_token_field = inflection.underscore(access_token_field) - user = register_user(backend_url, body, access_token_field) - delete_user(backend_url, auth_header, user["id"]) - # Call the following with the auth user since we cannot verify emails on new users in the script - reset_password(backend_url, auth_header, auth_user["email"]) - logout(backend_url, auth_header, auth_user["id"]) diff --git a/e2e-tests/test_entity.py b/e2e-tests/test_entity.py deleted file mode 100644 index da8a0008..00000000 --- a/e2e-tests/test_entity.py +++ /dev/null @@ -1,121 +0,0 @@ -import inflection -import json -import requests - - -def get_entities(backend_url, auth_header): - response = requests.get(f"{backend_url}/entities", headers=auth_header) - assert response.status_code == 200 - return response.json() - - -def get_entity_by_id(backend_url, auth_header, id): - response = requests.get( - f"{backend_url}/entities/{id}", - headers=auth_header, - ) - assert response.status_code == 200 - return response.json() - - -def get_file(backend_url, auth_header, filename): - response = requests.get( - f"{backend_url}/entities/files/{filename}", - headers=auth_header, - ) - assert response.status_code == 200 - return response.json() - - -def create_entity(backend_url, auth_header, body, fs, file, filename_field): - if fs: - response = requests.post( - f"{backend_url}/entities/", - headers=auth_header, - files={"file": file}, - data={"body": json.dumps(body)}, - ) - else: - response = requests.post( - f"{backend_url}/entities/", - json=body, - headers=auth_header, - ) - assert response.status_code == 201 - data = response.json() - if fs: - assert filename_field in data - actual = {k: v for k, v in data.items() if k in body} - assert actual == body - return data - - -def update_entity(backend_url, auth_header, id, body, fs, file, filename_field): - if fs: - response = requests.put( - f"{backend_url}/entities/{id}", - headers=auth_header, - files={"file": file}, - data={"body": json.dumps(body)}, - ) - else: - response = requests.put( - f"{backend_url}/entities/{id}", - json=body, - headers=auth_header, - ) - assert response.status_code == 200 - data = response.json() - if fs: - assert filename_field in data - actual = {k: v for k, v in data.items() if k in body} - assert actual == body - return data - - -def delete_entity(backend_url, auth_header, id): - response = requests.delete( - f"{backend_url}/entities/{id}", - headers=auth_header, - ) - assert response.status_code == 200 - - -def test_entities(backend_url, auth_header, lang, api, fs): - if api != "rest": - return - - body1 = { - "stringField": "TestScript1", - "intField": 1, - "enumField": "A", - "stringArrayField": ["test1", "test2"], - "boolField": True, - } - body2 = { - "stringField": "TestScript2", - "intField": 2, - "enumField": "B", - "stringArrayField": ["test2"], - "boolField": False, - } - filename_field = "fileName" - if lang != "ts": - body1 = {inflection.underscore(k): v for k, v in body1.items()} - body2 = {inflection.underscore(k): v for k, v in body2.items()} - filename_field = inflection.underscore(filename_field) - file1 = ("dog.jpg", open("dog.jpg", "rb"), "image/jpeg") - file2 = ("cat.png", open("cat.png", "rb"), "image/png") - - entity = create_entity(backend_url, auth_header, body1, fs, file1, filename_field) - if fs: - get_file(backend_url, auth_header, entity[filename_field]) - updated_entity = update_entity( - backend_url, auth_header, entity["id"], body2, fs, file2, filename_field - ) - if fs: - get_file(backend_url, auth_header, updated_entity[filename_field]) - retrieved_entity = get_entity_by_id(backend_url, auth_header, entity["id"]) - assert updated_entity == retrieved_entity - assert get_entities(backend_url, auth_header) - delete_entity(backend_url, auth_header, entity["id"]) diff --git a/e2e-tests/test_entity_gql.py b/e2e-tests/test_entity_gql.py deleted file mode 100644 index d8bb055b..00000000 --- a/e2e-tests/test_entity_gql.py +++ /dev/null @@ -1,273 +0,0 @@ -import inflection -import json -import requests - - -def get_entities(backend_url, auth_header, fs): - if fs: - query = """ - query { - entities { - id - stringField - intField - enumField - stringArrayField - boolField - fileName - } - } - """ - else: - query = """ - query { - entities { - id - stringField - intField - enumField - stringArrayField - boolField - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query}, - headers=auth_header, - ) - assert "data" in response.json() - assert "entities" in response.json()["data"] - return response.json()["data"]["entities"] - - -def get_entity_by_id(backend_url, auth_header, id, fs): - if fs: - query = """ - query($id: ID!) { - entity(id: $id) { - id - stringField - intField - enumField - stringArrayField - boolField - fileName - } - } - """ - else: - query = """ - query($id: ID!) { - entity(id: $id) { - id - stringField - intField - enumField - stringArrayField - boolField - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"id": id}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "entity" in response.json()["data"] - return response.json()["data"]["entity"] - - -def get_file(backend_url, auth_header, filename): - query = """ - query($fileUUID: ID!) { - file(fileUUID: $fileUUID) - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"fileUUID": filename}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "file" in response.json()["data"] - return response.json()["data"]["file"] - - -def create_entity(backend_url, auth_header, body, fs, file, filename_field): - if fs: - query = """ - mutation($entity: EntityRequestDTO!, $file: Upload) { - createEntity( - entity: $entity, file: $file - ) { - id - stringField - intField - stringArrayField - boolField - enumField - fileName - } - } - """ - # https://github.com/jaydenseric/graphql-multipart-request-spec - operations = json.dumps( - { - "query": query, - "variables": {"entity": body, "file": None}, - } - ) - map = json.dumps({"0": ["variables.file"]}) - response = requests.post( - f"{backend_url}/graphql", - data={"operations": operations, "map": map}, - files={"0": file}, - headers=auth_header, - ) - else: - query = """ - mutation($entity: EntityRequestDTO!) { - createEntity(entity: $entity) { - id - stringField - intField - stringArrayField - boolField - enumField - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"entity": body}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "createEntity" in response.json()["data"] - data = response.json()["data"]["createEntity"] - if fs: - assert filename_field in data - actual = {k: v for k, v in data.items() if k in body} - assert actual == body - return data - - -def update_entity(backend_url, auth_header, id, body, fs, file, filename_field): - if fs: - query = """ - mutation($id: ID!, $entity: EntityRequestDTO!, $file: Upload) { - updateEntity( - id: $id, entity: $entity, file: $file - ) { - id - stringField - intField - stringArrayField - boolField - enumField - fileName - } - } - """ - # https://github.com/jaydenseric/graphql-multipart-request-spec - operations = json.dumps( - { - "query": query, - "variables": {"id": id, "entity": body, "file": None}, - } - ) - map = json.dumps({"0": ["variables.file"]}) - response = requests.post( - f"{backend_url}/graphql", - data={"operations": operations, "map": map}, - files={"0": file}, - headers=auth_header, - ) - else: - query = """ - mutation($id: ID!, $entity: EntityRequestDTO!) { - updateEntity( - id: $id, entity: $entity - ) { - id - stringField - intField - stringArrayField - boolField - enumField - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"id": id, "entity": body}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "updateEntity" in response.json()["data"] - data = response.json()["data"]["updateEntity"] - if fs: - assert filename_field in data - actual = {k: v for k, v in data.items() if k in body} - assert actual == body - return data - - -def delete_entity(backend_url, auth_header, id): - query = """ - mutation($id: ID!) { - deleteEntity(id: $id) - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"id": id}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "deleteEntity" in response.json()["data"] - data = response.json()["data"]["deleteEntity"] - assert data == id - return data - - -def test_entities_gql(backend_url, auth_header, lang, api, fs): - if api == "rest": - return - - body1 = { - "stringField": "TestScript1", - "intField": 1, - "enumField": "A", - "stringArrayField": ["test1", "test2"], - "boolField": True, - } - body2 = { - "stringField": "TestScript2", - "intField": 2, - "enumField": "B", - "stringArrayField": ["test2"], - "boolField": False, - } - filename_field = "fileName" - if lang != "ts": - body1 = {inflection.underscore(k): v for k, v in body1.items()} - body2 = {inflection.underscore(k): v for k, v in body2.items()} - filename_field = inflection.underscore(filename_field) - file1 = ("dog.jpg", open("dog.jpg", "rb"), "image/jpeg") - file2 = ("cat.png", open("cat.png", "rb"), "image/png") - - entity = create_entity(backend_url, auth_header, body1, fs, file1, filename_field) - if fs: - get_file(backend_url, auth_header, entity[filename_field]) - updated_entity = update_entity( - backend_url, auth_header, entity["id"], body2, fs, file2, filename_field - ) - if fs: - get_file(backend_url, auth_header, updated_entity[filename_field]) - retrieved_entity = get_entity_by_id(backend_url, auth_header, entity["id"], fs) - assert updated_entity == retrieved_entity - assert get_entities(backend_url, auth_header, fs) - delete_entity(backend_url, auth_header, entity["id"]) diff --git a/e2e-tests/test_simple_entity.py b/e2e-tests/test_simple_entity.py deleted file mode 100644 index 20f31828..00000000 --- a/e2e-tests/test_simple_entity.py +++ /dev/null @@ -1,81 +0,0 @@ -import inflection -import requests - - -def get_entities(backend_url, auth_header): - response = requests.get(f"{backend_url}/simple-entities", headers=auth_header) - assert response.status_code == 200 - return response.json() - - -def get_entity_by_id(backend_url, auth_header, id): - response = requests.get( - f"{backend_url}/simple-entities/{id}", - headers=auth_header, - ) - assert response.status_code == 200 - return response.json() - - -def create_entity(backend_url, auth_header, body): - response = requests.post( - f"{backend_url}/simple-entities/", - json=body, - headers=auth_header, - ) - assert response.status_code == 201 - data = response.json() - actual = {k: v for k, v in data.items() if k in body} - assert actual == body - return data - - -def update_entity(backend_url, auth_header, id, body): - response = requests.put( - f"{backend_url}/simple-entities/{id}", - json=body, - headers=auth_header, - ) - assert response.status_code == 200 - data = response.json() - actual = {k: v for k, v in data.items() if k in body} - assert actual == body - return data - - -def delete_entity(backend_url, auth_header, id): - response = requests.delete( - f"{backend_url}/simple-entities/{id}", - headers=auth_header, - ) - assert response.status_code == 200 - - -def test_simple_entities(backend_url, auth_header, lang, api): - if api != "rest": - return - - body1 = { - "stringField": "TestScript1", - "intField": 1, - "enumField": "A", - "stringArrayField": ["test1", "test2"], - "boolField": True, - } - body2 = { - "stringField": "TestScript2", - "intField": 2, - "enumField": "B", - "stringArrayField": ["test2"], - "boolField": False, - } - if lang != "ts": - body1 = {inflection.underscore(k): v for k, v in body1.items()} - body2 = {inflection.underscore(k): v for k, v in body2.items()} - - entity = create_entity(backend_url, auth_header, body1) - updated_entity = update_entity(backend_url, auth_header, entity["id"], body2) - retrieved_entity = get_entity_by_id(backend_url, auth_header, entity["id"]) - assert updated_entity == retrieved_entity - assert get_entities(backend_url, auth_header) - delete_entity(backend_url, auth_header, entity["id"]) diff --git a/e2e-tests/test_simple_entity_gql.py b/e2e-tests/test_simple_entity_gql.py deleted file mode 100644 index 5951263e..00000000 --- a/e2e-tests/test_simple_entity_gql.py +++ /dev/null @@ -1,150 +0,0 @@ -import inflection -import requests - - -def get_entities(backend_url, auth_header): - query = """ - query { - simpleEntities { - id - stringField - intField - enumField - stringArrayField - boolField - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query}, - headers=auth_header, - ) - assert "data" in response.json() - assert "simpleEntities" in response.json()["data"] - return response.json()["data"]["simpleEntities"] - - -def get_entity_by_id(backend_url, auth_header, id): - query = """ - query($id: ID!) { - simpleEntity(id: $id) { - id - stringField - intField - enumField - stringArrayField - boolField - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"id": id}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "simpleEntity" in response.json()["data"] - return response.json()["data"]["simpleEntity"] - - -def create_entity(backend_url, auth_header, body): - query = """ - mutation($entity: SimpleEntityRequestDTO!) { - createSimpleEntity(entity: $entity) { - id - stringField - intField - stringArrayField - boolField - enumField - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"entity": body}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "createSimpleEntity" in response.json()["data"] - data = response.json()["data"]["createSimpleEntity"] - actual = {k: v for k, v in data.items() if k in body} - assert actual == body - return data - - -def update_entity(backend_url, auth_header, id, body): - query = """ - mutation($id: ID!, $entity: SimpleEntityRequestDTO!) { - updateSimpleEntity( - id: $id, entity: $entity - ) { - id - stringField - intField - stringArrayField - boolField - enumField - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"id": id, "entity": body}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "updateSimpleEntity" in response.json()["data"] - data = response.json()["data"]["updateSimpleEntity"] - actual = {k: v for k, v in data.items() if k in body} - assert actual == body - return data - - -def delete_entity(backend_url, auth_header, id): - query = """ - mutation($id: ID!) { - deleteSimpleEntity(id: $id) - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"id": id}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "deleteSimpleEntity" in response.json()["data"] - data = response.json()["data"]["deleteSimpleEntity"] - assert data == id - return data - - -def test_entities_gql(backend_url, auth_header, lang, api): - if api == "rest": - return - - body1 = { - "stringField": "TestScript1", - "intField": 1, - "enumField": "A", - "stringArrayField": ["test1", "test2"], - "boolField": True, - } - body2 = { - "stringField": "TestScript2", - "intField": 2, - "enumField": "B", - "stringArrayField": ["test2"], - "boolField": False, - } - if lang != "ts": - body1 = {inflection.underscore(k): v for k, v in body1.items()} - body2 = {inflection.underscore(k): v for k, v in body2.items()} - - entity = create_entity(backend_url, auth_header, body1) - updated_entity = update_entity(backend_url, auth_header, entity["id"], body2) - retrieved_entity = get_entity_by_id(backend_url, auth_header, entity["id"]) - assert updated_entity == retrieved_entity - assert get_entities(backend_url, auth_header) - delete_entity(backend_url, auth_header, entity["id"]) diff --git a/e2e-tests/test_user.py b/e2e-tests/test_user.py deleted file mode 100644 index add34854..00000000 --- a/e2e-tests/test_user.py +++ /dev/null @@ -1,106 +0,0 @@ -import inflection -import requests - - -def get_users(backend_url, auth_header): - response = requests.get(f"{backend_url}/users", headers=auth_header) - assert response.status_code == 200 - return response.json() - - -def get_user_by_id(backend_url, auth_header, id, lang): - if lang == "ts": - response = requests.get( - f"{backend_url}/users?userId={id}", - headers=auth_header, - ) - else: - response = requests.get( - f"{backend_url}/users?user_id={id}", - headers=auth_header, - ) - assert response.status_code == 200 - return response.json() - - -def get_user_by_email(backend_url, auth_header, email): - response = requests.get( - f"{backend_url}/users?email={email}", - headers=auth_header, - ) - assert response.status_code == 200 - return response.json() - - -def create_user(backend_url, auth_header, body): - response = requests.post( - f"{backend_url}/users/", - json=body, - headers=auth_header, - ) - assert response.status_code == 201 - data = response.json() - expected = {k: v for k, v in body.items() if k != "password"} - actual = {k: v for k, v in data.items() if k in body} - assert actual == expected - return data - - -def update_user(backend_url, auth_header, id, body): - response = requests.put( - f"{backend_url}/users/{id}", - json=body, - headers=auth_header, - ) - assert response.status_code == 200 - data = response.json() - actual = {k: v for k, v in data.items() if k in body} - assert actual == body - return data - - -def delete_user(backend_url, auth_header, id, lang): - if lang == "ts": - response = requests.delete( - f"{backend_url}/users?userId={id}", - headers=auth_header, - ) - else: - response = requests.delete( - f"{backend_url}/users?user_id={id}", - headers=auth_header, - ) - assert response.status_code == 204 - - -def test_users(backend_url, auth_header, lang, api, new_user_email): - if not auth_header or api != "rest": - return - - body1 = { - "firstName": "Test", - "lastName": "Script", - "role": "User", - "email": new_user_email, - "password": "password", - } - body2 = { - "firstName": "Test2", - "lastName": "Script2", - "role": "User", - "email": new_user_email, - } - if lang != "ts": - body1 = {inflection.underscore(k): v for k, v in body1.items()} - body2 = {inflection.underscore(k): v for k, v in body2.items()} - - user = create_user(backend_url, auth_header, body1) - updated_user = update_user(backend_url, auth_header, user["id"], body2) - retrieved_user_by_id = get_user_by_id(backend_url, auth_header, user["id"], lang) - assert updated_user == retrieved_user_by_id - retrieved_user_by_email = get_user_by_email( - backend_url, auth_header, updated_user["email"] - ) - assert updated_user == retrieved_user_by_email - assert get_users(backend_url, auth_header) - delete_user(backend_url, auth_header, user["id"], lang) diff --git a/e2e-tests/test_user_gql.py b/e2e-tests/test_user_gql.py deleted file mode 100644 index b75a796d..00000000 --- a/e2e-tests/test_user_gql.py +++ /dev/null @@ -1,168 +0,0 @@ -import inflection -import requests - - -def get_users(backend_url, auth_header): - query = """ - query { - users { - id - firstName - lastName - email - role - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query}, - headers=auth_header, - ) - assert "data" in response.json() - assert "users" in response.json()["data"] - return response.json()["data"]["users"] - - -def get_user_by_id(backend_url, auth_header, id): - query = """ - query($id: ID!) { - userById(id: $id) { - id - firstName - lastName - email - role - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"id": id}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "userById" in response.json()["data"] - return response.json()["data"]["userById"] - - -def get_user_by_email(backend_url, auth_header, email): - query = """ - query($email: String!) { - userByEmail(email: $email) { - id - firstName - lastName - email - role - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"email": email}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "userByEmail" in response.json()["data"] - return response.json()["data"]["userByEmail"] - - -def create_user(backend_url, auth_header, body): - query = """ - mutation($user: CreateUserDTO!) { - createUser(user: $user) { - id - firstName - lastName - email - role - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"user": body}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "createUser" in response.json()["data"] - data = response.json()["data"]["createUser"] - expected = {k: v for k, v in body.items() if k != "password"} - actual = {k: v for k, v in data.items() if k in body} - assert actual == expected - return data - - -def update_user(backend_url, auth_header, id, body): - query = """ - mutation($id: ID!, $user: UpdateUserDTO!) { - updateUser(id: $id, user: $user) { - id - firstName - lastName - email - role - } - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"id": id, "user": body}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "updateUser" in response.json()["data"] - data = response.json()["data"]["updateUser"] - actual = {k: v for k, v in data.items() if k in body} - assert actual == body - return data - - -def delete_user(backend_url, auth_header, id): - query = """ - mutation($id: ID!) { - deleteUserById(id: $id) - } - """ - response = requests.post( - f"{backend_url}/graphql", - json={"query": query, "variables": {"id": id}}, - headers=auth_header, - ) - assert "data" in response.json() - assert "deleteUserById" in response.json()["data"] - return response.json()["data"]["deleteUserById"] - - -def test_users_gql(backend_url, auth_header, lang, api, new_user_email): - if not auth_header or api == "rest": - return - - body1 = { - "firstName": "Test", - "lastName": "Script", - "role": "User", - "email": new_user_email, - "password": "password", - } - body2 = { - "firstName": "Test2", - "lastName": "Script2", - "role": "User", - "email": new_user_email, - } - if lang != "ts": - body1 = {inflection.underscore(k): v for k, v in body1.items()} - body2 = {inflection.underscore(k): v for k, v in body2.items()} - - user = create_user(backend_url, auth_header, body1) - updated_user = update_user(backend_url, auth_header, user["id"], body2) - retrieved_user_by_id = get_user_by_id(backend_url, auth_header, user["id"]) - assert updated_user == retrieved_user_by_id - retrieved_user_by_email = get_user_by_email( - backend_url, auth_header, updated_user["email"] - ) - assert updated_user == retrieved_user_by_email - assert get_users(backend_url, auth_header) - delete_user(backend_url, auth_header, user["id"]) diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 88316ae4..87a6bc48 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -31,6 +31,8 @@ module.exports = { "react/no-array-index-key": "off", "jsx-a11y/click-events-have-key-events": "off", "jsx-a11y/no-static-element-interactions": "off", + "no-nested-ternary": "off", + "import/prefer-default-export": "off", }, ignorePatterns: ["build/*"], }; diff --git a/frontend/.firebaserc b/frontend/.firebaserc deleted file mode 100644 index 32c94782..00000000 --- a/frontend/.firebaserc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "projects": { - "default": "uw-blueprint-starter-code" - } -} diff --git a/frontend/firebase.json b/frontend/firebase.json deleted file mode 100644 index 340ed5b7..00000000 --- a/frontend/firebase.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "hosting": { - "public": "build", - "ignore": [ - "firebase.json", - "**/.*", - "**/node_modules/**" - ], - "rewrites": [ - { - "source": "**", - "destination": "/index.html" - } - ] - } -} diff --git a/frontend/public/robots.txt b/frontend/public/robots.txt deleted file mode 100644 index e9e57dc4..00000000 --- a/frontend/public/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -# https://www.robotstxt.org/robotstxt.html -User-agent: * -Disallow: diff --git a/frontend/src/APIClients/AuthAPIClient.ts b/frontend/src/APIClients/AuthAPIClient.ts deleted file mode 100644 index 522b07a1..00000000 --- a/frontend/src/APIClients/AuthAPIClient.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { - FetchResult, - MutationFunctionOptions, - OperationVariables, -} from "@apollo/client"; - -import AUTHENTICATED_USER_KEY from "../constants/AuthConstants"; -import { AuthenticatedUser } from "../types/AuthTypes"; -import { UserType } from "../types/UserTypes"; -import { setLocalStorageObjProperty } from "../utils/LocalStorageUtils"; - -type LoginFunction = ( - options?: - | MutationFunctionOptions<{ login: AuthenticatedUser }, OperationVariables> - | undefined, -) => Promise< - FetchResult< - { login: AuthenticatedUser }, - Record, - Record - > ->; - -const login = async ( - email: string, - password: string, - userType: UserType, - loginFunction: LoginFunction, -): Promise => { - let user: AuthenticatedUser = null; - const result = await loginFunction({ - variables: { email, password, userType }, - }); - user = result.data?.login ?? null; - if (user) { - localStorage.setItem(AUTHENTICATED_USER_KEY, JSON.stringify(user)); - } - return user; -}; - -type RegisterFunction = ( - options?: - | MutationFunctionOptions< - { register: AuthenticatedUser }, - OperationVariables - > - | undefined, -) => Promise< - FetchResult< - { register: AuthenticatedUser }, - Record, - Record - > ->; - -const register = async ( - firstName: string, - lastName: string, - email: string, - password: string, - registerFunction: RegisterFunction, -): Promise => { - let user: AuthenticatedUser = null; - try { - const result = await registerFunction({ - variables: { firstName, lastName, email, password }, - }); - user = result.data?.register ?? null; - if (user) { - localStorage.setItem(AUTHENTICATED_USER_KEY, JSON.stringify(user)); - } - } catch (e: unknown) { - // eslint-disable-next-line no-alert - window.alert("Failed to sign up"); - } - return user; -}; - -type LogoutFunction = ( - options?: - | MutationFunctionOptions< - { - logout: null; - }, - OperationVariables - > - | undefined, -) => Promise< - FetchResult< - { - logout: null; - }, - Record, - Record - > ->; - -const logout = async ( - authenticatedUserId: string, - logoutFunction: LogoutFunction, -): Promise => { - const result = await logoutFunction({ - variables: { userId: authenticatedUserId }, - }); - if (result.data?.logout === null) { - localStorage.removeItem(AUTHENTICATED_USER_KEY); - return true; - } - return false; -}; - -type RefreshFunction = ( - options?: - | MutationFunctionOptions< - { - refresh: string; - }, - OperationVariables - > - | undefined, -) => Promise< - FetchResult< - { - refresh: string; - }, - Record, - Record - > ->; - -const refresh = async (refreshFunction: RefreshFunction): Promise => { - const result = await refreshFunction(); - const token = result.data?.refresh; - if (token) { - setLocalStorageObjProperty(AUTHENTICATED_USER_KEY, "accessToken", token); - return true; - } - return false; -}; - -export default { login, logout, register, refresh }; diff --git a/frontend/src/APIClients/BaseAPIClient.ts b/frontend/src/APIClients/BaseAPIClient.ts deleted file mode 100644 index 44122b34..00000000 --- a/frontend/src/APIClients/BaseAPIClient.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { jwtDecode } from "jwt-decode"; -import axios, { AxiosRequestConfig, AxiosRequestHeaders } from "axios"; - -import AUTHENTICATED_USER_KEY from "../constants/AuthConstants"; -import { DecodedJWT } from "../types/AuthTypes"; -import { setLocalStorageObjProperty } from "../utils/LocalStorageUtils"; - -interface AdaptAxiosRequestConfig extends AxiosRequestConfig { - headers: AxiosRequestHeaders; -} - -const baseAPIClient = axios.create({ - baseURL: process.env.REACT_APP_BACKEND_URL, -}); - -baseAPIClient.interceptors.request.use( - async (config: AdaptAxiosRequestConfig) => { - const newConfig = { ...config }; - - let authHeaderParts = [""]; - // if access token in header has expired, do a refresh - if (typeof config.headers.Authorization === "string") { - authHeaderParts = config.headers.Authorization?.split(" "); - } - - // config.headers.Authorization?.split(" "); - if ( - authHeaderParts && - authHeaderParts.length >= 2 && - authHeaderParts[0].toLowerCase() === "bearer" - ) { - const decodedToken = jwtDecode(authHeaderParts[1]) as DecodedJWT; - - if ( - decodedToken && - (typeof decodedToken === "string" || - decodedToken.exp <= Math.round(new Date().getTime() / 1000)) - ) { - const { data } = await axios.post( - `${process.env.REACT_APP_BACKEND_URL}/auth/refresh`, - {}, - { withCredentials: true }, - ); - - const accessToken = data.accessToken || data.access_token; - setLocalStorageObjProperty( - AUTHENTICATED_USER_KEY, - "accessToken", - accessToken, - ); - - newConfig.headers.Authorization = accessToken; - } - } - - return newConfig; - }, -); - -export default baseAPIClient; diff --git a/frontend/src/APIClients/Mutations/NotificationMutations.ts b/frontend/src/APIClients/Mutations/NotificationMutations.ts deleted file mode 100644 index c2d6ffc5..00000000 --- a/frontend/src/APIClients/Mutations/NotificationMutations.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { gql } from "@apollo/client"; - -export const CREATE_NOTIFICATION_GROUP = gql` - mutation CreateNotificationGroup($roomIds: [Int!]) { - createNotificationGroup(roomIds: $roomIds) { - id - announcementGroup - recipients { - userId - residentId - roomNumber - credits - dateJoined - dateLeft - } - } - } -`; - -export const CREATE_ANNOUNCEMENT_GROUP = gql` - mutation CreateAnnouncementGroup { - createAnnouncementGroup { - id - announcementGroup - recipients { - userId - residentId - roomNumber - credits - dateJoined - dateLeft - } - } - } -`; - -export const SEND_NOTIFICATION_TO_GROUP = gql` - mutation SendNotificationToGroup( - $groupId: ID! - $notification: CreateNotificationDTO! - ) { - sendNotificationToGroup(groupId: $groupId, notification: $notification) { - id - message - createdAt - authorId - } - } -`; - -export const DELETE_NOTIFICATION_GROUP = gql` - mutation DeleteNotificationGroup($groupId: ID!) { - deleteNotificationGroup(groupId: $groupId) { - id - announcementGroup - } - } -`; - -export const UPDATE_NOTIFICATION_BY_ID = gql` - mutation UpdateNotificationById( - $notificationId: ID! - $notification: UpdateNotificationDTO! - ) { - updateNotificationById( - notificationId: $notificationId - notification: $notification - ) { - id - message - createdAt - authorId - } - } -`; - -export const DELETE_NOTIFICATION_BY_IDS = gql` - mutation DeleteNotificationByIds($notificationIds: [ID!]) { - deleteNotificationByIds(notificationIds: $notificationIds) - } -`; - -export const UPDATE_SEEN_NOTIFICATION = gql` - mutation UpdateSeenNotification($notificationSeenId: ID!) { - updateSeenNotification(notificationSeenId: $notificationSeenId) { - id - notificationId - recipientId - seen - } - } -`; diff --git a/frontend/src/APIClients/Mutations/ResidentsMutations.ts b/frontend/src/APIClients/Mutations/ResidentsMutations.ts deleted file mode 100644 index c0f52d89..00000000 --- a/frontend/src/APIClients/Mutations/ResidentsMutations.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { gql } from "@apollo/client"; - -export const ADD_RESIDENT = gql` - mutation AddResident($resident: CreateResidentDTO!) { - addResident(resident: $resident) { - userId - residentId - roomNumber - credits - dateJoined - dateLeft - } - } -`; - -export const UPDATE_RESIDENT = gql` - mutation UpdateResident($userId: ID!, $resident: UpdateResidentDTO!) { - updateResident(userId: $userId, resident: $resident) { - userId - residentId - roomNumber - credits - dateJoined - dateLeft - } - } -`; - -export const DELETE_RESIDENT = gql` - mutation DeleteResident($userId: ID!) { - deleteResident(userId: $userId) { - userId - residentId - roomNumber - credits - dateJoined - dateLeft - } - } -`; diff --git a/frontend/src/APIClients/Mutations/StaffMutations.ts b/frontend/src/APIClients/Mutations/StaffMutations.ts deleted file mode 100644 index 779baed9..00000000 --- a/frontend/src/APIClients/Mutations/StaffMutations.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { gql } from "@apollo/client"; - -export const ADD_STAFF = gql` - mutation AddStaff($staff: CreateStaffDTO!) { - addStaff(staff: $staff) { - userId - email - phoneNumber - firstName - lastName - isAdmin - } - } -`; - -export const UPDATE_STAFF = gql` - mutation UpdateStaff($userId: userID!, $staff: UpdateStaffDTO!) { - updateStaff(userId: $userId, staff: $staff) { - userId - email - phoneNumber - firstName - lastName - isAdmin - } - } -`; - -export const DELETE_STAFF = gql` - mutation DeleteStaff($userId: ID!) { - deleteStaff(userId: $userId) { - userId - email - phoneNumber - firstName - lastName - isAdmin - } - } -`; diff --git a/frontend/src/APIClients/Mutations/TaskMutations.ts b/frontend/src/APIClients/Mutations/TaskMutations.ts deleted file mode 100644 index 7f8c96ec..00000000 --- a/frontend/src/APIClients/Mutations/TaskMutations.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { gql } from "@apollo/client"; - -export const CREATE_TASK = gql` - mutation CreateTask($task: InputTaskDTO!) { - createTask(task: $task) { - id - type - title - description - creditValue - endDate - recurrenceFrequency - specificDay - repeatDays - } - } -`; - -export const UPDATE_TASK = gql` - mutation UpdateTASK($taskId: Int!, $task: InputTaskDTO!) { - updateTask(taskId: $taskId, task: $task) { - id - type - title - description - creditValue - endDate - recurrenceFrequency - specificDay - repeatDays - } - } -`; - -export const DELETE_TASK = gql` - mutation DeleteStaff($taskId: Int!) { - deleteTask(taskId: $taskId) { - id - type - title - description - creditValue - endDate - recurrenceFrequency - specificDay - repeatDays - } - } -`; - -export const ASSIGN_TASK = gql` - mutation AssignTask($taskAssigned: InputTaskAssignedDTO!) { - assignTask(taskAssigned: $taskAssigned) { - id - taskId - assigneeId - assignerId - status - startDate - comments - } - } -`; - -export const CHANGE_TASK_STATUS = gql` - mutation ChangeTaskStatus($taskAssignedId: Int!, $status: Status!) { - changeTaskStatus(taskAssignedId: $taskAssignedId, status: $status) { - id - taskId - assigneeId - assignerId - status - startDate - comments - } - } -`; diff --git a/frontend/src/APIClients/Mutations/WarningsMutations.ts b/frontend/src/APIClients/Mutations/WarningsMutations.ts deleted file mode 100644 index 771420cf..00000000 --- a/frontend/src/APIClients/Mutations/WarningsMutations.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { gql } from "@apollo/client"; - -export const ADD_WARNING = gql` - mutation AddWarning($warning: CreateWarningDTO!) { - addWarning(warning: $warning) { - id - title - description - dateIssued - assigneeId - assignerId - relatedTaskId - } - } -`; - -export const DELETE_WARNING = gql` - mutation DeleteWarning($warningId: ID!) { - deleteWarning(id: $warningId) { - id - title - description - dateIssued - assigneeId - assignerId - relatedTaskId - } - } -`; diff --git a/frontend/src/APIClients/Queries/NotificationQueries.ts b/frontend/src/APIClients/Queries/NotificationQueries.ts deleted file mode 100644 index cd9afb61..00000000 --- a/frontend/src/APIClients/Queries/NotificationQueries.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { gql } from "@apollo/client"; - -export const GET_NOTIFICATIONS_BY_IDS = gql` - query getNotificationsByIds($notificationIds: [ID!]) { - getNotificationsByIds(notificationIds: $notificationIds) { - id - notificationId - recipientId - seen - notification { - id - message - createdAt - authorId - } - } - } -`; - -export const GET_NOTIFCATION_BY_RESIDENT = gql` - query getNotificationByResident($residentId: ID!) { - getNotificationByResident(residentId: $id) { - id - notificationId - recipientId - seen - notification { - id - message - createdAt - authorId - } - } - } -`; - -export const GET_ALL_GROUPS_AND_NOTIFICATIONS = gql` - query getAllGroupsAndNotifications { - getAllGroupsAndNotifications { - id - announcementGroup - notifications { - id - message - createdAt - authorId - } - recipients { - userId - residentId - roomNumber - credits - dateJoined - dateLeft - } - } - } -`; diff --git a/frontend/src/APIClients/Queries/ResidentsQueries.ts b/frontend/src/APIClients/Queries/ResidentsQueries.ts deleted file mode 100644 index a8b2cc92..00000000 --- a/frontend/src/APIClients/Queries/ResidentsQueries.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { gql } from "@apollo/client"; - -export const GET_RESIDENTS_BY_ID = gql` - query GetResidentsByIds($userIds: [ID!]) { - getResidentsByIds(userIds: $userIds) { - userId - residentId - roomNumber - credits - dateJoined - dateLeft - } - } -`; - -export const GET_ALL_RESIDENTS = gql` - query GetAllResidents { - getAllResidents { - userId - residentId - roomNumber - credits - dateJoined - dateLeft - } - } -`; - -export const GET_ACTIVE_RESIDENTS = gql` - query GetActiveResidents { - getActiveResidents { - userId - residentId - roomNumber - credits - dateJoined - dateLeft - } - } -`; diff --git a/frontend/src/APIClients/Queries/StaffQueries.ts b/frontend/src/APIClients/Queries/StaffQueries.ts deleted file mode 100644 index 18beaa99..00000000 --- a/frontend/src/APIClients/Queries/StaffQueries.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { gql } from "@apollo/client"; - -export const GET_ALL_STAFF = gql` - query GetAllStaff { - getAllStaff { - userId - email - phoneNumber - firstName - lastName - isAdmin - } - } -`; - -export const GET_STAFF_BY_IDS = gql` - query GetStaffByIds { - getStaffByIds { - userId - email - phoneNumber - firstName - lastName - isAdmin - } - } -`; diff --git a/frontend/src/APIClients/Queries/TaskQueries.ts b/frontend/src/APIClients/Queries/TaskQueries.ts deleted file mode 100644 index d2057359..00000000 --- a/frontend/src/APIClients/Queries/TaskQueries.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { gql } from "@apollo/client"; - -export const GET_TASK_BY_ID = gql` - query getTaskById($taskId: Int!) { - getTaskById(taskId: $taskId) { - id - type - title - description - creditValue - endDate - recurrenceFrequency - specificDay - repeatDays - } - } -`; - -export const GET_TASKS_BY_TYPE = gql` - query getTasksByType($type: TaskType!) { - getTasksByType(type: $type) { - id - type - title - description - creditValue - endDate - recurrenceFrequency - specificDay - repeatDays - } - } -`; - -export const GET_TASKS_BY_ASSIGNEE_ID = gql` - query getTasksByAssigneeId($assigneeId: Int!) { - getTasksByAssigneeId(assigneeId: $assigneeId) { - id - taskId - assigneeId - assignerId - status - startDate - comments - } - } -`; - -export const GET_TASKS_BY_ASSIGNER_ID = gql` - query GetTasksByAssignerId($assignerId: Int!) { - getTasksByAssignerId(assignerId: $assignerId) { - id - taskId - assigneeId - assignerId - status - startDate - comments - } - } -`; - -export const GET_TASKS_BY_START_DATE = gql` - query GetTasksByStartDate($startDate: DateTime!) { - getTasksByStartDate(startDate: $startDate) { - id - taskId - assigneeId - assignerId - status - startDate - comments - } - } -`; - -export const GET_TASKS_BY_STATUS = gql` - query GetTasksByStatus($status: Status!) { - getTasksByStatus(status: $status) { - id - taskId - assigneeId - assignerId - status - startDate - comments - } - } -`; diff --git a/frontend/src/APIClients/Types/NotificationType.ts b/frontend/src/APIClients/Types/NotificationType.ts deleted file mode 100644 index 244fb3c6..00000000 --- a/frontend/src/APIClients/Types/NotificationType.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { ResidentResponse } from "./ResidentsType"; - -export type NotificationResponse = { - id: string; - message: string; - createdAt?: Date; - authorId?: string; - recipients?: NotificationReceivedResponse[]; -}; - -export type NotificationCreateRequest = { - message: string; - createdAt?: Date; - authorId?: string; -}; - -export type NotificationUpdateRequest = { - message?: string; - createdAt?: Date; - authorId?: string; -}; - -export type NotificationGroupResponse = { - id: string; - recipients?: ResidentResponse[]; - notifications?: NotificationResponse[]; - announcementGroup: boolean; -}; - -export type NotificationReceivedResponse = { - id: string; - notificationId: string; - notification?: NotificationResponse; - recipientId: number; - seen: boolean; -}; diff --git a/frontend/src/APIClients/Types/ResidentsType.ts b/frontend/src/APIClients/Types/ResidentsType.ts deleted file mode 100644 index fa4e053c..00000000 --- a/frontend/src/APIClients/Types/ResidentsType.ts +++ /dev/null @@ -1,56 +0,0 @@ -export type UserResponse = { - userId: number; - residentId: number; - email: string; - phoneNumber?: string; - firstName: string; - lastName: string; - profilePictureURL?: string; - birthDate: string; - roomNumber: number; - credits: number; - dateJoined: string; - dateLeft?: Date; - notes?: string; -}; - -export type UserRequest = { - email: string; - password: string; - phoneNumber?: string; - firstName: string; - lastName: string; - profilePictureURL?: string; - residentId: number; - birthDate: string; - roomNumber: number; - credits: number; - dateJoined: string; - dateLeft?: Date; - notes?: string; -}; - -export type UserRequestUpdate = { - email?: string; - password?: string; - phoneNumber?: string; - firstName?: string; - lastName?: string; - profilePictureURL?: string; - residentId?: number; - birthDate?: string; - roomNumber?: number; - credits?: number; - dateJoined?: string; - dateLeft?: Date; - notes?: string; -}; - -export type ResidentResponse = { - userId: string; - residentId: string; - roomNumber: number; - credits: number; - dateJoined: Date; - dateLeft: Date; -}; diff --git a/frontend/src/APIClients/Types/StaffTypes.ts b/frontend/src/APIClients/Types/StaffTypes.ts deleted file mode 100644 index 6ab5f649..00000000 --- a/frontend/src/APIClients/Types/StaffTypes.ts +++ /dev/null @@ -1,56 +0,0 @@ -export type UserRequest = { - userId?: string; - email: string; - password: string; - phoneNumber?: string; - firstName: string; - lastName: string; - isAdmin: boolean; - displayName?: string; - profilePictureURL?: string; -}; - -export type UserRequestID = { - userId: string; - email: string; - password: string; - phoneNumber?: string; - firstName: string; - lastName: string; - isAdmin: boolean; - displayName?: string; - profilePictureURL?: string; -}; - -export type UserRequestAdd = { - userId?: number; - email: string; - phoneNumber: string; - firstName: string; - lastName: string; - displayName: string; - profilePictureURL?: string; - isAdmin: boolean; -}; - -export type UserRequestUpdate = { - userId: number; - email?: string; - phoneNumber?: string; - firstName?: string; - lastName?: string; - displayName?: string; - profilePictureURL?: string; - isAdmin?: boolean; -}; - -export type UserRequestDelete = { - userId: number; - email?: string; - phoneNumber?: string; - firstName?: string; - lastName?: string; - displayName?: string; - profilePictureURL?: string; - isAdmin?: boolean; -}; diff --git a/frontend/src/APIClients/Types/TaskType.ts b/frontend/src/APIClients/Types/TaskType.ts deleted file mode 100644 index 1ba62bae..00000000 --- a/frontend/src/APIClients/Types/TaskType.ts +++ /dev/null @@ -1,73 +0,0 @@ -export enum Status { - PENDING_APPROVAL = "PENDING_APPROVAL", - ASSIGNED = "ASSIGNED", - INCOMPLETE = "INCOMPLETE", - COMPLETE = "COMPLETE", - EXCUSED = "EXCUSED", -} - -export enum RecurrenceFrequency { - ONE_TIME = "ONE_TIME", - REPEATS_PER_WEEK_SELECTED = "REPEATS_PER_WEEK_SELECTED", - REPEATS_PER_WEEK_ONCE = "REPEATS_PER_WEEK_ONCE", -} - -export enum DaysOfWeek { - MONDAY = "MONDAY", - TUESDAY = "TUESDAY", - WEDNESDAY = "WEDNESDAY", - THURSDAY = "THURSDAY", - FRIDAY = "FRIDAY", - SATURDAY = "SATURDAY", - SUNDAY = "SUNDAY", -} - -export enum TaskTypeEnum { - REQUIRED = "REQUIRED", - OPTIONAL = "OPTIONAL", - CHORE = "CHORE", - ACHIEVEMENT = "ACHIEVEMENT", -} - -export type TaskResponse = { - id: number; - type: TaskTypeEnum; - title: string; - description?: string; - creditValue: number; - tasksAssigned: TaskResponse[]; - endDate?: Date; - recurrenceFrequency: RecurrenceFrequency; - specificDay?: DaysOfWeek; - repeatDays?: DaysOfWeek[]; -}; - -export type TaskRequest = { - type: TaskTypeEnum; - title: string; - description?: string; - creditValue: number; - endDate?: Date; - recurrenceFrequency: RecurrenceFrequency; - specificDay?: DaysOfWeek; - repeatDays?: DaysOfWeek[]; -}; - -export type TaskAssignedResponse = { - id: number; - taskId: number; - assigneeId: number; - assignerId: number; - status: Status; - startDate: Date; - comments?: string; -}; - -export type TaskAssignedRequest = { - taskId?: number; - assigneeId?: number; - assignerId?: number; - status?: Status; - startDate?: Date; - comments?: string; -}; diff --git a/frontend/src/APIClients/Types/WarningsType.ts b/frontend/src/APIClients/Types/WarningsType.ts deleted file mode 100644 index eb9a32b1..00000000 --- a/frontend/src/APIClients/Types/WarningsType.ts +++ /dev/null @@ -1,18 +0,0 @@ -export type WarningDTO = { - id: number; - title: string; - description: string; - dateIssued: Date; - assigneeId: number; - assignerId: number; - relatedTaskId: number; -}; - -export type CreateWarningDTO = { - title: string; - description: string; - dateIssued: Date; - assigneeId: number; - assignerId: number; - relatedTaskId: number; -}; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a77fe7fc..0d8d063f 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,57 +1,31 @@ -import "bootstrap/dist/css/bootstrap.min.css"; -import React, { useState, useReducer } from "react"; -import { ChakraProvider, extendTheme } from "@chakra-ui/react"; +import React from "react"; import { BrowserRouter as Router, Route, Routes as Switch, } from "react-router-dom"; -import * as Routes from "./constants/Routes"; -import AUTHENTICATED_USER_KEY from "./constants/AuthConstants"; -import { AuthenticatedUser } from "./types/AuthTypes"; -import { getLocalStorageObj } from "./utils/LocalStorageUtils"; -import AuthContext from "./contexts/AuthContext"; -import SampleContext, { - DEFAULT_SAMPLE_CONTEXT, -} from "./contexts/SampleContext"; -import SampleContextDispatcherContext from "./contexts/SampleContextDispatcherContext"; -import sampleContextReducer from "./reducers/SampleContextReducer"; +import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client"; +import { createUploadLink } from "apollo-upload-client"; +import { setContext } from "@apollo/client/link/context"; +import "bootstrap/dist/css/bootstrap.min.css"; +import { ChakraProvider, extendTheme } from "@chakra-ui/react"; import colors from "./theme/colors"; import modalTheme from "./theme/modals"; import buttonTheme from "./theme/buttons"; import tabsTheme from "./theme/tabs"; import { inputTheme, textareaTheme } from "./theme/inputs"; -import PrivateRoute from "./components/auth/PrivateRoute"; -import LoginPage from "./components/auth/LoginPage"; -import SignupPage from "./components/auth/SignupPage"; -import ResetPasswordPage from "./components/auth/ResetPasswordPage"; -import HomePage from "./components/pages/home/HomePage"; -import AnnouncementsPage from "./components/pages/announcements/AnnouncementsPage"; -import TasksPage from "./components/pages/tasks/TasksPage"; -import SchedulePage from "./components/pages/schedule/SchedulePage"; -import ResidentsPage from "./components/pages/residents/ResidentsPage"; -import InsightsPage from "./components/pages/insights/InsightsPage"; -import NotFoundPage from "./components/pages/NotFoundPage"; +import * as ROUTES from "./constants/routes"; +// import HomePage from "./components/pages/home/HomePage"; +// import SchedulePage from "./components/pages/schedule/SchedulePage"; +// import AnnouncementsPage from "./components/pages/announcements/AnnouncementsPage"; +import ParticipantsPage from "./components/pages/participants/ParticipantsPage"; +// import TasksPage from "./components/pages/tasks/TasksPage"; +import NotFoundPage from "./components/common/NotFoundPage"; const App = (): React.ReactElement => { - const currentUser: AuthenticatedUser = getLocalStorageObj( - AUTHENTICATED_USER_KEY, - ); - - const [authenticatedUser, setAuthenticatedUser] = - useState(currentUser); - - // Some sort of global state. Context API replaces redux. - // Split related states into different contexts as necessary. - // Split dispatcher and state into separate contexts as necessary. - const [sampleContext, dispatchSampleContextUpdate] = useReducer( - sampleContextReducer, - DEFAULT_SAMPLE_CONTEXT, - ); - const theme = extendTheme({ colors, components: { @@ -63,78 +37,44 @@ const App = (): React.ReactElement => { }, }); + const endpoint = createUploadLink({ + uri: `${process.env.REACT_APP_BACKEND_URL}/graphql`, + credentials: "include", + }); + + const header = setContext(async (_, { headers }) => { + const token = null; + return { + headers: { + ...headers, + authorization: token ? `Bearer ${token}` : "", + }, + }; + }); + + const apolloClient = new ApolloClient({ + link: header.concat(endpoint as any), + cache: new InMemoryCache(), + }); + return ( - - - - - - - } /> - } /> - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - } /> - - - - - - + + + + + {/* } /> */} + {/* } /> */} + {/* } /> */} + } + /> + {/* } /> */} + } /> + + + + ); }; diff --git a/frontend/src/components/auth/LoginPage.tsx b/frontend/src/components/auth/LoginPage.tsx deleted file mode 100644 index c4483410..00000000 --- a/frontend/src/components/auth/LoginPage.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import React, { useContext, useState } from "react"; -import { Navigate, useNavigate } from "react-router-dom"; -import { gql, useMutation } from "@apollo/client"; - -import { - Button, - Flex, - Text, - Input, - FormControl, - FormErrorMessage, -} from "@chakra-ui/react"; - -import authAPIClient from "../../APIClients/AuthAPIClient"; -import { HOME_PAGE, RESET_PASSWORD_PAGE } from "../../constants/Routes"; -import AuthContext from "../../contexts/AuthContext"; -import { AuthenticatedUser } from "../../types/AuthTypes"; -import { ReactComponent as Logo } from "../../assets/marillacPlaceLogo.svg"; - -const LOGIN = gql` - mutation Login($email: String!, $password: String!, $userType: UserType!) { - login(email: $email, password: $password, userType: $userType) { - id - type - email - firstName - lastName - accessToken - } - } -`; - -const LoginPage = (): React.ReactElement => { - const navigate = useNavigate(); - const { authenticatedUser, setAuthenticatedUser } = useContext(AuthContext); - - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - const [error, setError] = useState(""); - - const [login] = useMutation<{ login: AuthenticatedUser }>(LOGIN); - - const onLogInClick = async () => { - try { - const user: AuthenticatedUser = await authAPIClient.login( - email, - password, - "STAFF", - login, - ); - setAuthenticatedUser(user); - setError(""); - } catch (e: unknown) { - setError("Wrong email or password. Try again."); - } - }; - - if (authenticatedUser) { - return ; - } - - return ( - - - - - - - - Sign In - - - Please enter your login information - - - - setEmail(e.target.value)} - placeholder="Email" - h="49px" - borderColor="black" - mb="24px" - fontSize="18px" - /> - setPassword(e.target.value)} - placeholder="Password" - h="49px" - borderColor="black" - fontSize="18px" - /> - - {error} - navigate(RESET_PASSWORD_PAGE)} - cursor="pointer" - fontWeight="bold" - fontSize="18px" - textDecoration="underline" - float="right" - mt="8px" - > - Forgot Password? - - - - - - ); -}; - -export default LoginPage; diff --git a/frontend/src/components/auth/PrivateRoute.tsx b/frontend/src/components/auth/PrivateRoute.tsx deleted file mode 100644 index ae3ff3f4..00000000 --- a/frontend/src/components/auth/PrivateRoute.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, { useContext } from "react"; -import { Navigate } from "react-router-dom"; -import { Flex } from "@chakra-ui/react"; - -import AuthContext from "../../contexts/AuthContext"; -import { LOGIN_PAGE } from "../../constants/Routes"; -import SideBar from "../common/SideBar"; - -const PrivateRoute: React.FC<{ children: React.ReactElement }> = ({ - children, -}) => { - const { authenticatedUser } = useContext(AuthContext); - return ( - // after return, add "authenticatedUser ?"" - - - {children} - - // ) : ( - // - // ); - ); -}; - -export default PrivateRoute; diff --git a/frontend/src/components/auth/ResetPassword.tsx b/frontend/src/components/auth/ResetPassword.tsx deleted file mode 100644 index ab5e33b6..00000000 --- a/frontend/src/components/auth/ResetPassword.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { useContext } from "react"; -import { gql, useMutation } from "@apollo/client"; -import AuthContext from "../../contexts/AuthContext"; - -const RESET_PASSWORD = gql` - mutation ResetPassword($email: String!) { - resetPassword(email: $email) - } -`; - -const ResetPassword = (): React.ReactElement => { - const { authenticatedUser } = useContext(AuthContext); - - const [resetPassword] = useMutation<{ resetPassword: boolean }>( - RESET_PASSWORD, - ); - - const onResetPasswordClick = async () => { - await resetPassword({ variables: { email: authenticatedUser?.email } }); - }; - - return ( - - ); -}; - -export default ResetPassword; diff --git a/frontend/src/components/auth/ResetPasswordPage.tsx b/frontend/src/components/auth/ResetPasswordPage.tsx deleted file mode 100644 index 936d6de3..00000000 --- a/frontend/src/components/auth/ResetPasswordPage.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; - -const ResetPasswordPage = (): React.ReactElement => { - return ( -
-

Reset Password Page

-
- ); -}; - -export default ResetPasswordPage; diff --git a/frontend/src/components/auth/SignupPage.tsx b/frontend/src/components/auth/SignupPage.tsx deleted file mode 100644 index d2425eed..00000000 --- a/frontend/src/components/auth/SignupPage.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import React, { useContext, useState } from "react"; -import { Navigate } from "react-router-dom"; -import { gql, useMutation } from "@apollo/client"; - -import authAPIClient from "../../APIClients/AuthAPIClient"; -import { HOME_PAGE } from "../../constants/Routes"; -import AuthContext from "../../contexts/AuthContext"; -import { AuthenticatedUser } from "../../types/AuthTypes"; - -const REGISTER = gql` - mutation Signup_Register( - $firstName: String! - $lastName: String! - $email: String! - $password: String! - ) { - register( - user: { - firstName: $firstName - lastName: $lastName - email: $email - password: $password - } - ) { - id - firstName - lastName - email - role - accessToken - } - } -`; - -const Signup = (): React.ReactElement => { - const { authenticatedUser, setAuthenticatedUser } = useContext(AuthContext); - const [firstName, setFirstName] = useState(""); - const [lastName, setLastName] = useState(""); - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - - const [register] = useMutation<{ register: AuthenticatedUser }>(REGISTER); - - const onSignupClick = async () => { - const user: AuthenticatedUser = await authAPIClient.register( - firstName, - lastName, - email, - password, - register, - ); - setAuthenticatedUser(user); - }; - - if (authenticatedUser) { - return ; - } - - return ( -
-

Signup

-
-
- setFirstName(event.target.value)} - placeholder="first name" - /> -
-
- setLastName(event.target.value)} - placeholder="last name" - /> -
-
- setEmail(event.target.value)} - placeholder="username@domain.com" - /> -
-
- setPassword(event.target.value)} - placeholder="password" - /> -
-
- -
-
-
- ); -}; - -export default Signup; diff --git a/frontend/src/components/common/CommonTable.tsx b/frontend/src/components/common/CommonTable.tsx index c6647604..758b52a0 100644 --- a/frontend/src/components/common/CommonTable.tsx +++ b/frontend/src/components/common/CommonTable.tsx @@ -24,12 +24,12 @@ import ModalContainer from "./ModalContainer"; type TableTypes = string | number | boolean | Date | string[]; -export type ColumnInfoTypes = { header: string; key: string }; - export interface TableData { [key: string]: TableTypes; } +export type ColumnInfoTypes = { header: string; key: string }; + type Props = { data: TableData[]; columnInfo: ColumnInfoTypes[]; diff --git a/frontend/src/components/pages/NotFoundPage.tsx b/frontend/src/components/common/NotFoundPage.tsx similarity index 100% rename from frontend/src/components/pages/NotFoundPage.tsx rename to frontend/src/components/common/NotFoundPage.tsx diff --git a/frontend/src/components/common/SideBar.tsx b/frontend/src/components/common/SideBar.tsx index 812ad8f6..ea93dbbf 100644 --- a/frontend/src/components/common/SideBar.tsx +++ b/frontend/src/components/common/SideBar.tsx @@ -14,10 +14,8 @@ import { } from "@chakra-ui/react"; import { useNavigate } from "react-router-dom"; -import * as Routes from "../../constants/Routes"; +import * as Routes from "../../constants/routes"; import { ReactComponent as Logo } from "../../assets/marillacPlaceLogo.svg"; -import authAPIClient from "../../APIClients/AuthAPIClient"; -import AuthContext from "../../contexts/AuthContext"; const mockAuthenticatedUser = { id: "1", @@ -27,12 +25,6 @@ const mockAuthenticatedUser = { lastName: "Doe", }; -const LOGOUT = gql` - mutation Logout($userId: ID!) { - logout(userId: $userId) - } -`; - const SideBarTab: React.FC<{ label: string; handleClick: () => void }> = ({ label, handleClick, @@ -58,20 +50,6 @@ const SideBarTab: React.FC<{ label: string; handleClick: () => void }> = ({ const SideBar: React.FC = () => { const navigate = useNavigate(); - // const { authenticatedUser, setAuthenticatedUser } = useContext(AuthContext); - const { setAuthenticatedUser } = useContext(AuthContext); // Temp - const authenticatedUser = mockAuthenticatedUser; // Temp - const [logout] = useMutation<{ logout: null }>(LOGOUT); - - const onLogOutClick = async () => { - const success = await authAPIClient.logout( - String(authenticatedUser?.id), - logout, - ); - if (success) { - setAuthenticatedUser(null); - } - }; const pages = [ { label: "Home", route: Routes.HOME_PAGE }, @@ -148,7 +126,6 @@ const SideBar: React.FC = () => { - - - ); -}; +// return ( +//
+// +// setMessage(e.target.value)} +// w="100%" +// mr={4} +// /> +// +// +//
+// ); +// }; -type Props = { - announcements: NotificationGroupResponse[]; - selectedGroup: string; - addingNewRoom: boolean; - setAddingNewRoom: React.Dispatch>; - selectedRooms: number[]; - setSelectedRooms: React.Dispatch>; - sendNotification: (message: string, groupId: string) => Promise; - createNotificationGroupAndSendNotification: ( - selectedRooms: number[], - message: string, - ) => Promise; -}; +// type Props = { +// announcements: NotificationGroupResponse[]; +// selectedGroup: string; +// addingNewRoom: boolean; +// setAddingNewRoom: React.Dispatch>; +// selectedRooms: number[]; +// setSelectedRooms: React.Dispatch>; +// sendNotification: (message: string, groupId: string) => Promise; +// createNotificationGroupAndSendNotification: ( +// selectedRooms: number[], +// message: string, +// ) => Promise; +// }; -type PropsList = { - announcements: NotificationGroupResponse[]; - selectedGroup: string; -}; +// type PropsList = { +// announcements: NotificationGroupResponse[]; +// selectedGroup: string; +// }; -const AnnouncementsList = ({ announcements, selectedGroup }: PropsList) => { - console.log(announcements, selectedGroup); - if (selectedGroup.length === 0) { - return <>; - } +// const AnnouncementsList = ({ announcements, selectedGroup }: PropsList) => { +// console.log(announcements, selectedGroup); +// if (selectedGroup.length === 0) { +// return <>; +// } - return ( - - {announcements - .filter((group) => group.id === selectedGroup)[0] - .notifications?.map((notification, index) => ( - - - - - - {notification.authorId || ""} - - - {moment(notification.createdAt).fromNow()} - - - - - {notification.message} - - - ))} - - ); -}; +// return ( +// +// {announcements +// .filter((group) => group.id === selectedGroup)[0] +// .notifications?.map((notification, index) => ( +// +// +// +// +// +// {notification.authorId || ""} +// +// +// {moment(notification.createdAt).fromNow()} +// +// +// +// +// {notification.message} +// +// +// ))} +// +// ); +// }; -const AnnouncementsView = ({ - announcements, - selectedGroup, - addingNewRoom, - setAddingNewRoom, - selectedRooms, - setSelectedRooms, - sendNotification, - createNotificationGroupAndSendNotification, -}: Props): React.ReactElement => { - const groupInfo = announcements.find((group) => group.id === selectedGroup); - const rooms = - groupInfo && groupInfo.recipients - ? groupInfo.recipients.map((recipient) => recipient.roomNumber) - : []; - const [allRooms, setAllRooms] = useState([1, 2, 3, 4, 5, 6]); +// const AnnouncementsView = ({ +// announcements, +// selectedGroup, +// addingNewRoom, +// setAddingNewRoom, +// selectedRooms, +// setSelectedRooms, +// sendNotification, +// createNotificationGroupAndSendNotification, +// }: Props): React.ReactElement => { +// const groupInfo = announcements.find((group) => group.id === selectedGroup); +// const rooms = +// groupInfo && groupInfo.recipients +// ? groupInfo.recipients.map((recipient) => recipient.roomNumber) +// : []; +// const [allRooms, setAllRooms] = useState([1, 2, 3, 4, 5, 6]); - const addRoomToNewRoom = (roomId: number) => { - if (!selectedRooms.includes(roomId)) { - setSelectedRooms([...selectedRooms, roomId]); - } - }; +// const addRoomToNewRoom = (roomId: number) => { +// if (!selectedRooms.includes(roomId)) { +// setSelectedRooms([...selectedRooms, roomId]); +// } +// }; - const deleteRoomSelected = (roomId: number) => { - if (selectedRooms.includes(roomId)) { - setSelectedRooms(selectedRooms.filter((room) => room !== roomId)); - } - }; +// const deleteRoomSelected = (roomId: number) => { +// if (selectedRooms.includes(roomId)) { +// setSelectedRooms(selectedRooms.filter((room) => room !== roomId)); +// } +// }; - const handlePost = async (message: string) => { - if (addingNewRoom && selectedRooms.length > 0) { - await createNotificationGroupAndSendNotification(selectedRooms, message); - setSelectedRooms([]); - setAddingNewRoom(false); - return; - } - await sendNotification(message, selectedGroup); - }; +// const handlePost = async (message: string) => { +// if (addingNewRoom && selectedRooms.length > 0) { +// await createNotificationGroupAndSendNotification(selectedRooms, message); +// setSelectedRooms([]); +// setAddingNewRoom(false); +// return; +// } +// await sendNotification(message, selectedGroup); +// }; - const formatHeader = (roomIDs: number[]) => { - if (addingNewRoom && selectedGroup === "0") { - return ( - - - {selectedRooms.map((room) => ( - - - {" "} - {room === -1 ? "All Rooms" : `Room ${room}`} - - deleteRoomSelected(room)} - color="#57469D" - /> - - ))} - {selectedRooms.length === 0 && ( - - - - - - {[ - addRoomToNewRoom(-1)} - key="all-rooms" - > - All Rooms - , - ...allRooms - .filter((room) => !selectedRooms.includes(room)) - .map((room) => ( - addRoomToNewRoom(room)} - key={room} - > - Room {room} - - )), - ]} - - - )} - - - ); - } - return "All Rooms"; - }; +// const formatHeader = (roomIDs: number[]) => { +// if (addingNewRoom && selectedGroup === "0") { +// return ( +// +// +// {selectedRooms.map((room) => ( +// +// +// {" "} +// {room === -1 ? "All Rooms" : `Room ${room}`} +// +// deleteRoomSelected(room)} +// color="#57469D" +// /> +// +// ))} +// {selectedRooms.length === 0 && ( +// +// +// +// +// +// {[ +// addRoomToNewRoom(-1)} +// key="all-rooms" +// > +// All Rooms +// , +// ...allRooms +// .filter((room) => !selectedRooms.includes(room)) +// .map((room) => ( +// addRoomToNewRoom(room)} +// key={room} +// > +// Room {room} +// +// )), +// ]} +// +// +// )} +// +// +// ); +// } +// return "All Rooms"; +// }; - const getHeader = () => { - if (selectedGroup === "" || selectedGroup === "0") { - return formatHeader(rooms); - } - if (groupInfo?.announcementGroup) { - return "All Rooms"; - } - return formatRooms(rooms); - }; +// const getHeader = () => { +// if (selectedGroup === "" || selectedGroup === "0") { +// return formatHeader(rooms); +// } +// if (groupInfo?.announcementGroup) { +// return "All Rooms"; +// } +// return formatRooms(rooms); +// }; - return ( - - - -

{getHeader()}

- - - -
- - {selectedGroup !== "0" && ( - - )} - - - - -
-
- ); -}; +// return ( +// +// +// +//

{getHeader()}

+// +// +// +//
+// +// {selectedGroup !== "0" && ( +// +// )} +// +// +// +// +//
+//
+// ); +// }; -export default AnnouncementsView; +// export default AnnouncementsView; diff --git a/frontend/src/components/pages/home/AnnouncementNotification.tsx b/frontend/src/components/pages/home/AnnouncementNotification.tsx index 5b228f1f..77c7c4bb 100644 --- a/frontend/src/components/pages/home/AnnouncementNotification.tsx +++ b/frontend/src/components/pages/home/AnnouncementNotification.tsx @@ -1,78 +1,79 @@ -import React, { useState } from "react"; -import moment from "moment"; -import { Box, Text, Flex, Icon, IconButton } from "@chakra-ui/react"; -import PersonOutlineOutlinedIcon from "@mui/icons-material/PersonOutlineOutlined"; -import ChevronRightIcon from "@mui/icons-material/ChevronRight"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import ExpandLessIcon from "@mui/icons-material/ExpandLess"; - -import { Announcement } from "../../../types/NotificationTypes"; - -const AnnouncementNotification = ({ - room, - message, - createdAt, -}: Announcement): React.ReactElement => { - const [showFullMessage, setShowFullMessage] = useState(false); - - return ( - - - - - - - - - - {"Admin to Room ".concat(room)} - - - posted at {moment(createdAt).format("h:mm a")} - - - - {showFullMessage ? ( - - {message} - - ) : ( - - {message} - - )} - - - - - setShowFullMessage(!showFullMessage)} - icon={showFullMessage ? : } - size="md" - /> - - - - ); -}; - -export default AnnouncementNotification; +export {}; +// import React, { useState } from "react"; +// import moment from "moment"; +// import { Box, Text, Flex, Icon, IconButton } from "@chakra-ui/react"; +// import PersonOutlineOutlinedIcon from "@mui/icons-material/PersonOutlineOutlined"; +// import ChevronRightIcon from "@mui/icons-material/ChevronRight"; +// import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +// import ExpandLessIcon from "@mui/icons-material/ExpandLess"; + +// import { Announcement } from "../../../types/NotificationType"; + +// const AnnouncementNotification = ({ +// room, +// message, +// createdAt, +// }: Announcement): React.ReactElement => { +// const [showFullMessage, setShowFullMessage] = useState(false); + +// return ( +// +// +// +// +// +// +// +// +// +// {"Admin to Room ".concat(room)} +// +// +// posted at {moment(createdAt).format("h:mm a")} +// +// +// +// {showFullMessage ? ( +// +// {message} +// +// ) : ( +// +// {message} +// +// )} +// +// +// +// +// setShowFullMessage(!showFullMessage)} +// icon={showFullMessage ? : } +// size="md" +// /> +// +// +// +// ); +// }; + +// export default AnnouncementNotification; diff --git a/frontend/src/components/pages/home/HomePage.tsx b/frontend/src/components/pages/home/HomePage.tsx index 90fc3eee..ae41c618 100644 --- a/frontend/src/components/pages/home/HomePage.tsx +++ b/frontend/src/components/pages/home/HomePage.tsx @@ -1,153 +1,154 @@ -import React, { useState, useEffect } from "react"; -import { Flex, Box, Text } from "@chakra-ui/react"; -import { announcementsMockData } from "../../../mocks/notifications"; -import AnnouncementNotification from "./AnnouncementNotification"; -import { Announcement } from "../../../types/NotificationTypes"; -import RoomGrid from "./RoomGrid"; - -const HomePage = (): React.ReactElement => { - const [numberPosts, setNumberPosts] = useState(0); - const [viewAll, setViewAll] = useState(false); - const [announcements, setAnnouncements] = useState([]); - const [recentAnnouncements, setRecentAnnouncements] = useState< - Announcement[] - >([]); - - useEffect(() => { - // Combine all announcements into a single array - const combinedAnnouncements: Announcement[] = []; - Object.entries(announcementsMockData).forEach(([key, value]) => { - for (let i = 0; i < value.length; i += 1) { - const newAnnouncement: Announcement = { - room: key, - author: value[i].author, - message: value[i].message, - createdAt: value[i].createdAt, - }; - combinedAnnouncements.push(newAnnouncement); - } - }); - - const sortedAnnouncements = combinedAnnouncements.sort( - (a, b) => - new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), - ); - // Filter announcements from the current week - const oneWeekAgo = new Date(); - oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); - const thisWeeksAnnouncements = sortedAnnouncements.filter( - (announcement) => new Date(announcement.createdAt) >= oneWeekAgo, - ); - setAnnouncements(combinedAnnouncements); - setRecentAnnouncements(thisWeeksAnnouncements.slice(0, 3)); - setNumberPosts(thisWeeksAnnouncements.length); - }, []); - - const getDate = () => { - const options: Intl.DateTimeFormatOptions = { - year: "numeric", - month: "long", - day: "numeric", - }; - return new Date().toLocaleDateString("en-US", options); - }; - - return ( - - - - - - Marillac Place Overview - - - - {getDate()} - - - - - - - - Announcements - - - {numberPosts === 0 - ? "You're all caught up!" - : `${numberPosts} new posts today`} - - - - setViewAll(!viewAll)} - cursor="pointer" - textDecoration="underline" - > - {viewAll ? "Collapse All" : "View all"} - - - {(viewAll ? announcements : recentAnnouncements).map( - (announcement, index) => ( - - ), - )} - - - - ); -}; - -export default HomePage; +export {}; +// import React, { useState, useEffect } from "react"; +// import { Flex, Box, Text } from "@chakra-ui/react"; +// import { announcementsMockData } from "../../../mocks/notifications"; +// import AnnouncementNotification from "./AnnouncementNotification"; +// import { Announcement } from "../../../types/NotificationType"; +// import RoomGrid from "./RoomGrid"; + +// const HomePage = (): React.ReactElement => { +// const [numberPosts, setNumberPosts] = useState(0); +// const [viewAll, setViewAll] = useState(false); +// const [announcements, setAnnouncements] = useState([]); +// const [recentAnnouncements, setRecentAnnouncements] = useState< +// Announcement[] +// >([]); + +// useEffect(() => { +// // Combine all announcements into a single array +// const combinedAnnouncements: Announcement[] = []; +// Object.entries(announcementsMockData).forEach(([key, value]) => { +// for (let i = 0; i < value.length; i += 1) { +// const newAnnouncement: Announcement = { +// room: key, +// author: value[i].author, +// message: value[i].message, +// createdAt: value[i].createdAt, +// }; +// combinedAnnouncements.push(newAnnouncement); +// } +// }); + +// const sortedAnnouncements = combinedAnnouncements.sort( +// (a, b) => +// new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), +// ); +// // Filter announcements from the current week +// const oneWeekAgo = new Date(); +// oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); +// const thisWeeksAnnouncements = sortedAnnouncements.filter( +// (announcement) => new Date(announcement.createdAt) >= oneWeekAgo, +// ); +// setAnnouncements(combinedAnnouncements); +// setRecentAnnouncements(thisWeeksAnnouncements.slice(0, 3)); +// setNumberPosts(thisWeeksAnnouncements.length); +// }, []); + +// const getDate = () => { +// const options: Intl.DateTimeFormatOptions = { +// year: "numeric", +// month: "long", +// day: "numeric", +// }; +// return new Date().toLocaleDateString("en-US", options); +// }; + +// return ( +// +// +// +// +// +// Marillac Place Overview +// + +// +// {getDate()} +// +// +// +// +// +// +// +// Announcements +// +// +// {numberPosts === 0 +// ? "You're all caught up!" +// : `${numberPosts} new posts today`} +// +// + +// setViewAll(!viewAll)} +// cursor="pointer" +// textDecoration="underline" +// > +// {viewAll ? "Collapse All" : "View all"} +// +// +// {(viewAll ? announcements : recentAnnouncements).map( +// (announcement, index) => ( +// +// ), +// )} +// +// +// +// ); +// }; + +// export default HomePage; diff --git a/frontend/src/components/pages/home/HomeRoomCard.tsx b/frontend/src/components/pages/home/HomeRoomCard.tsx index 19cd933e..0eb4e0da 100644 --- a/frontend/src/components/pages/home/HomeRoomCard.tsx +++ b/frontend/src/components/pages/home/HomeRoomCard.tsx @@ -1,81 +1,82 @@ -import React from "react"; -import { Flex, Heading, Text, Box, Circle } from "@chakra-ui/react"; +export {}; +// import React from "react"; +// import { Flex, Heading, Text, Box, Circle } from "@chakra-ui/react"; -// TODO: Change name to id when schedule is done +// // TODO: Change name to id when schedule is done -type Props = { - room: string | number; - residentId: number; - pendingTasks: number; - assignedTasks: number; -}; +// type Props = { +// room: string | number; +// residentId: number; +// pendingTasks: number; +// assignedTasks: number; +// }; -const RoomCard = ({ - room, - residentId, - pendingTasks, - assignedTasks, -}: Props): React.ReactElement => { - return ( - - - - Room {room} — ID{residentId} - - - - - - {pendingTasks} - - - - Pending Tasks - - - - - - {assignedTasks} - - - - Tasks Assigned - - - - - - ); -}; +// const RoomCard = ({ +// room, +// residentId, +// pendingTasks, +// assignedTasks, +// }: Props): React.ReactElement => { +// return ( +// +// +// +// Room {room} — ID{residentId} +// +// +// +// +// +// {pendingTasks} +// +// +// +// Pending Tasks +// +// +// +// +// +// {assignedTasks} +// +// +// +// Tasks Assigned +// +// +// +// +// +// ); +// }; -export default RoomCard; +// export default RoomCard; diff --git a/frontend/src/components/pages/home/RoomGrid.tsx b/frontend/src/components/pages/home/RoomGrid.tsx index a3a6ce1a..c6d5012c 100644 --- a/frontend/src/components/pages/home/RoomGrid.tsx +++ b/frontend/src/components/pages/home/RoomGrid.tsx @@ -1,73 +1,74 @@ -import React, { useEffect, useState } from "react"; -import { Flex, Grid } from "@chakra-ui/react"; -import { useQuery } from "@apollo/client"; -import RoomCard from "./HomeRoomCard"; -import { GET_ACTIVE_RESIDENTS } from "../../../APIClients/Queries/ResidentsQueries"; -import { GET_TASKS_BY_STATUS } from "../../../APIClients/Queries/TaskQueries"; +export {}; +// import React, { useEffect, useState } from "react"; +// import { Flex, Grid } from "@chakra-ui/react"; +// import { useQuery } from "@apollo/client"; +// import RoomCard from "./HomeRoomCard"; +// import { GET_ACTIVE_RESIDENTS } from "../../../gql/queries/participants"; +// import { GET_TASKS_BY_STATUS } from "../../../gql/queries/TaskQueries"; -const RoomGrid = () => { - const [residents, setResidents] = useState([]); - const { data: residentData } = useQuery(GET_ACTIVE_RESIDENTS); +// const RoomGrid = () => { +// const [residents, setResidents] = useState([]); +// const { data: residentData } = useQuery(GET_ACTIVE_RESIDENTS); - const { data: pending } = useQuery(GET_TASKS_BY_STATUS, { - variables: { status: "PENDING_APPROVAL" }, - }); +// const { data: pending } = useQuery(GET_TASKS_BY_STATUS, { +// variables: { status: "PENDING_APPROVAL" }, +// }); - const { data: assigned } = useQuery(GET_TASKS_BY_STATUS, { - variables: { status: "ASSIGNED" }, - }); +// const { data: assigned } = useQuery(GET_TASKS_BY_STATUS, { +// variables: { status: "ASSIGNED" }, +// }); - const fetchTasksForResident = (assigneeId: number) => { - const assignedTasks = - assigned?.getTasksByStatus?.filter( - (task: { assigneeId: number }) => task.assigneeId === assigneeId, - ).length || 0; - const pendingTasks = - pending?.getTasksByStatus?.filter( - (task: { assigneeId: number }) => task.assigneeId === assigneeId, - ).length || 0; - return { assignedTasks, pendingTasks, loading: false, error: null }; - }; +// const fetchTasksForResident = (assigneeId: number) => { +// const assignedTasks = +// assigned?.getTasksByStatus?.filter( +// (task: { assigneeId: number }) => task.assigneeId === assigneeId, +// ).length || 0; +// const pendingTasks = +// pending?.getTasksByStatus?.filter( +// (task: { assigneeId: number }) => task.assigneeId === assigneeId, +// ).length || 0; +// return { assignedTasks, pendingTasks, loading: false, error: null }; +// }; - useEffect(() => { - setResidents(residentData?.getActiveResidents || []); - }, [pending, assigned, residentData]); +// useEffect(() => { +// setResidents(residentData?.getActiveResidents || []); +// }, [pending, assigned, residentData]); - return ( - - - {residents.map( - (room: { - roomNumber: string; - residentId: number; - userId: number; - }) => { - const { assignedTasks, pendingTasks } = fetchTasksForResident( - room.userId, - ); +// return ( +// +// +// {residents.map( +// (room: { +// roomNumber: string; +// residentId: number; +// userId: number; +// }) => { +// const { assignedTasks, pendingTasks } = fetchTasksForResident( +// room.userId, +// ); - return ( - - ); - }, - )} - - - ); -}; +// return ( +// +// ); +// }, +// )} +// +// +// ); +// }; -export default RoomGrid; +// export default RoomGrid; diff --git a/frontend/src/components/pages/insights/InsightsPage.tsx b/frontend/src/components/pages/insights/InsightsPage.tsx deleted file mode 100644 index 35ea2765..00000000 --- a/frontend/src/components/pages/insights/InsightsPage.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; - -const InsightsPage = (): React.ReactElement => { - return ( -
-

Insights Page

-
- ); -}; - -export default InsightsPage; diff --git a/frontend/src/components/pages/participants/AddParticipantCard.tsx b/frontend/src/components/pages/participants/AddParticipantCard.tsx new file mode 100644 index 00000000..2966c8bc --- /dev/null +++ b/frontend/src/components/pages/participants/AddParticipantCard.tsx @@ -0,0 +1,215 @@ +import React, { useState, useEffect } from "react"; +import { + Button, + Text, + Flex, + Select, + FormLabel, + Spinner, +} from "@chakra-ui/react"; + +import { useMutation, useQuery, useLazyQuery } from "@apollo/client"; +import { + GET_AVAILABLE_ROOMS, + GET_PARTICIPANT_BY_ID, +} from "../../../gql/queries"; +import { CREATE_PARTICIPANT } from "../../../gql/mutations"; + +import ModalContainer from "../../common/ModalContainer"; +import FormInputField from "../../common/form/FormInputField"; +import FormSelectField from "../../common/form/FormSelectField"; + +type AddParticipantCardProps = { + isOpen: boolean; + setIsOpen: React.Dispatch>; +}; + +const AddParticipantCard = ({ + isOpen, + setIsOpen, +}: AddParticipantCardProps): React.ReactElement => { + const [participantId, setParticipantId] = useState(""); + const [roomNumber, setRoomNumber] = useState(""); + const [arrivalDate, setArrivalDate] = useState(""); + const [password, setPassword] = useState(""); + + const [participantIdError, setParticipantIdError] = useState(""); + const [roomNumberError, setRoomNumberError] = useState(""); + const [arrivalDateError, setArrivalDateError] = useState(""); + const [passwordError, setPasswordError] = useState(""); + + const { + loading: getAvailableRoomsLoading, + error: getAvailableRoomsError, + data: getAvailableRoomsData, + } = useQuery(GET_AVAILABLE_ROOMS); + + const [ + getParticipantById, + { + loading: getParticipantByIdLoading, + error: getParticipantByIdError, + data: getParticipantByIdData, + }, + ] = useLazyQuery(GET_PARTICIPANT_BY_ID, { + variables: { participantId }, + }); + + const [createParticipant] = useMutation(CREATE_PARTICIPANT); + + const validate = async () => { + const errors = { + participantId: "", + roomNumber: "", + arrivalDate: "", + password: "", + }; + + if (participantId) { + await getParticipantById({ variables: { participantId } }); + if (getParticipantByIdError) { + errors.participantId = "Unknown error has occurred."; + } else if ( + getParticipantByIdData && + getParticipantByIdData.getParticipantById != null + ) { + errors.participantId = "ID already exists"; + } else { + errors.participantId = ""; + } + } else { + errors.participantId = "ID Number is missing"; + } + + errors.roomNumber = roomNumber ? "" : "Room Number is missing"; + errors.arrivalDate = arrivalDate ? "" : "Arrival Date is missing"; + errors.password = password ? "" : "Password is missing"; + + return errors; + }; + + const handleSubmit = async () => { + const errors = await validate(); + if ( + !errors.participantId && + !errors.roomNumber && + !errors.arrivalDate && + !errors.password + ) { + try { + const room = parseInt(roomNumber, 10); + await createParticipant({ + variables: { + participantId, + roomNumber: room, + arrival: arrivalDate, + password, + }, + }); + setIsOpen(false); + window.location.reload(); + } catch (err) { + console.error(err); + } + } else { + setParticipantIdError(errors.participantId); + setRoomNumberError(errors.roomNumber); + setArrivalDateError(errors.arrivalDate); + setPasswordError(errors.password); + } + }; + + const reset = () => { + setParticipantId(""); + setRoomNumber(""); + setArrivalDate(""); + setPassword(""); + setParticipantIdError(""); + setRoomNumberError(""); + setArrivalDateError(""); + setPasswordError(""); + }; + + return ( + + + { + setParticipantId(e.target.value); + }} + required + error={participantIdError} + /> + + {getAvailableRoomsLoading ? ( + + ) : getAvailableRoomsError ? ( + Error getting rooms. + ) : getAvailableRoomsData && getAvailableRoomsData.getAvailableRooms ? ( + ({ + key: room, + value: room, + display: `Room ${room}`, + }), + )} + onChange={(e) => setRoomNumber(e.target.value)} + required + error={roomNumberError} + /> + ) : ( + No available rooms. + )} + + { + setArrivalDate(e.target.value); + }} + required + error={arrivalDateError} + /> + + setPassword(e.target.value)} + required + error={passwordError} + /> + + + + + + + + ); +}; + +export default AddParticipantCard; diff --git a/frontend/src/components/pages/participants/EditParticipantCard.tsx b/frontend/src/components/pages/participants/EditParticipantCard.tsx new file mode 100644 index 00000000..66600417 --- /dev/null +++ b/frontend/src/components/pages/participants/EditParticipantCard.tsx @@ -0,0 +1,173 @@ +export {}; +// import React, { useState } from "react"; +// import { Button, Text, Flex } from "@chakra-ui/react"; +// import ModalContainer from "../../common/ModalContainer"; +// import FormField from "../../common/FormField"; + +// export type ResidentEditInfo = { +// residentId: number; +// roomNumber: number; +// arrivalDate: string; +// departureDate: string; +// password: string; +// }; + +// type Props = { +// isOpen: boolean; +// setIsOpen: React.Dispatch>; +// residentInfo: ResidentEditInfo; +// onCloseEditModal: any; +// }; + +// const ResidentEditModal = ({ +// isOpen, +// setIsOpen, +// residentInfo, +// onCloseEditModal, +// }: Props): React.ReactElement => { +// const [residentId, setResidentId] = useState(residentInfo.residentId); +// const [password, setPassword] = useState(residentInfo.password); +// const [arrivalDate, setArrivalDate] = useState(residentInfo.arrivalDate); +// const [departureDate, setDepartureDate] = useState( +// residentInfo.departureDate, +// ); + +// const [roomNumber, setRoomNumber] = useState(residentInfo.roomNumber); + +// const [showPassword, setShowPassword] = useState(false); +// const [submitPressed, setSubmitPressed] = useState(false); + +// const [roomList, setRoomList] = useState([1, 2, 3, 4, 5, 6, 7, 8]); +// const [residentIdList, setresidentIdList] = useState>([ +// 12345, 67890, 23456, 78901, 34567, 89012, 45678, 56789, 12346, 67891, 23457, +// 78902, 34568, 89013, 45679, 90124, 56790, 12347, 67892, +// ]); + +// const [invalidRoomNumber, setInvalidRoomNumber] = useState(""); +// const [invalidResidentId, setInvalidResidentId] = useState(""); +// // useEffect(() => { +// // // TODO: get roomList, residentIdList +// // }, []); + +// const handleSubmit = () => { +// setSubmitPressed(true); +// if (roomNumber !== residentInfo.roomNumber && roomNumber in roomList) { +// setInvalidRoomNumber( +// "This room number is already taken. Note: To set this field, please remove the room number from the Resident who occupies the room.", +// ); +// } else { +// setInvalidRoomNumber(""); +// } + +// if ( +// residentId !== residentInfo.residentId && +// residentIdList.includes(residentId) +// ) { +// setInvalidResidentId("This ID number is already taken."); +// } else { +// setInvalidResidentId(""); +// } + +// if (invalidRoomNumber || invalidResidentId) { +// return; +// } + +// // TODO: API POST to Residents/Participants + +// if (!residentId || !password || !arrivalDate) { +// // TODO: Add error handling +// } +// // TODO: API call to add resident +// }; + +// const resetFormState = () => { +// onCloseEditModal(); +// }; + +// return ( +// +// +// +// setResidentId(parseInt(e.target.value, 10))} +// submitPressed={submitPressed} +// required +// error={invalidResidentId !== ""} +// /> +// {invalidResidentId && ( +// +// {invalidResidentId} +// +// )} +// + +// +// setRoomNumber(parseInt(e.target.value, 10))} +// submitPressed={submitPressed} +// error={invalidRoomNumber !== ""} +// /> +// {invalidRoomNumber && ( +// +// {invalidRoomNumber} +// +// )} +// + +// +// {}} +// submitPressed={submitPressed} +// required +// /> +// setDepartureDate(e.target.value)} +// submitPressed={submitPressed} +// /> +// +// +// setPassword(e.target.value)} +// submitPressed={submitPressed} +// required +// isPassword +// showPassword={showPassword} +// setShowPassword={setShowPassword} +// /> +// + +// +// +// +// +// +// +// ); +// }; + +// export default ResidentEditModal; diff --git a/frontend/src/components/pages/participants/ParticipantsPage.tsx b/frontend/src/components/pages/participants/ParticipantsPage.tsx new file mode 100644 index 00000000..8b63d0c0 --- /dev/null +++ b/frontend/src/components/pages/participants/ParticipantsPage.tsx @@ -0,0 +1,203 @@ +import React, { useEffect, useState } from "react"; +import { + Flex, + Input, + Button, + Icon, + InputGroup, + InputLeftElement, + Spinner, +} from "@chakra-ui/react"; +import { Add, Search } from "@mui/icons-material"; + +import { useQuery } from "@apollo/client"; +import { GET_ALL_PARTICIPANTS } from "../../../gql/queries"; + +import CommonTable, { + ColumnInfoTypes, + TableData, +} from "../../common/CommonTable"; +import SideBar from "../../common/SideBar"; +import AddParticipantCard from "./AddParticipantCard"; + +const columnTypes: ColumnInfoTypes[] = [ + { + header: "ID Number", + key: "participantId", + }, + { + header: "Room #", + key: "roomNumber", + }, + { + header: "Arrival Date", + key: "arrival", + }, + { + header: "Departure Date", + key: "departure", + }, +]; + +const ParticipantsPage = (): React.ReactElement => { + const [addParticipantCardOpened, setAddParticipantCardOpened] = + useState(false); + // const [isModalOpen, setIsModalOpen] = useState("none"); + + // const [addResident] = useMutation<{ addResident: UserResponse }>( + // ADD_RESIDENT, + // ); + + // const [updateResident] = useMutation<{ + // userId: number; + // resident: UserResponse; + // }>(UPDATE_RESIDENT); + + // const [deleteResident] = useMutation<{ userId: number }>(DELETE_RESIDENT); + + // const handleAddResident = async () => { + // try { + // const date = new Date(); + // const formattedDate = date.toISOString().split("T")[0]; + + // const resident: UserRequest = { + // email: "dasfhsahfsoad@gmail.com", + // password: "qe8e9r789ewr", + // firstName: "Bob", + // lastName: "Bob", + // residentId: 1248120, + // birthDate: formattedDate, + // roomNumber: 3, + // credits: 500, + // dateJoined: formattedDate, + // }; + // await addResident({ variables: { resident } }); + // } catch (e) { + // console.log(e); + // } + // }; + + // const handleUpdateResident = async () => { + // try { + // const userId = 5; + // const resident: UserRequestUpdate = { + // lastName: "NEW NAME", + // roomNumber: 3, + // credits: 10, + // }; + // await updateResident({ variables: { userId, resident } }); + // } catch (e) { + // console.log(e); + // } + // }; + + // const handleDeleteResident = async () => { + // try { + // const userId = 1; + // await deleteResident({ variables: { userId } }); + // } catch (e) { + // console.log(e); + // } + // }; + + // const ids = [4]; + // const { + // loading: residentIdLoading, + // error: residentIdError, + // data: residentIdData, + // } = useQuery<{ userIds: [number] }>(GET_RESIDENTS_BY_ID, { + // variables: { userIds: ids }, + // }); + + // const { + // loading: residentActiveLoading, + // error: residentActiveError, + // data: residentActiveData, + // } = useQuery(GET_ACTIVE_RESIDENTS); + + const { + loading: getAllParticipantsLoading, + error: getAllParticipantsError, + data: getAllParticipantsData, + } = useQuery(GET_ALL_PARTICIPANTS); + + // const handleResidentEdit = (row: any) => { + // setIsModalOpen("edit"); + // // console.log(row); + // setEditInfo(row); + // }; + + // // CHANGE + // const handleRowClick = (row: any) => { + // setIsModalOpen("edit"); + // // console.log(row); + // setEditInfo(row); + // }; + + // const handleResidentSubmitEdit = () => { + // setEditInfo(undefined); + + // // TODO: modify data + // }; + + return ( + + + + + + + + + + + + + {getAllParticipantsLoading ? ( + + ) : getAllParticipantsError ? ( + {getAllParticipantsError.message} + ) : getAllParticipantsData.getAllParticipants ? ( + ({ + participantId: participant.participantId, + roomNumber: participant.roomNumber, + arrival: participant.arrival, + departure: participant.departure || "", + }), + )} + columnInfo={columnTypes} + onEdit={() => {}} + /> + ) : ( + No participants found. + )} + + + + {/* + {residentEditInfo && ( + setIsModalOpen("none")} + onCloseEditModal={handleResidentSubmitEdit} + /> + )} */} + + + ); +}; + +export default ParticipantsPage; diff --git a/frontend/src/components/pages/participants/ViewParticipantCard.tsx b/frontend/src/components/pages/participants/ViewParticipantCard.tsx new file mode 100644 index 00000000..21e2fe65 --- /dev/null +++ b/frontend/src/components/pages/participants/ViewParticipantCard.tsx @@ -0,0 +1,138 @@ +export {}; +// import React, { useState } from "react"; +// import { Button, Flex, FormControl, FormLabel, Select } from "@chakra-ui/react"; + +// import { useMutation } from "@apollo/client"; +// import ModalContainer from "../../common/ModalContainer"; +// import FormField from "../../common/FormField"; +// import { +// ADD_RESIDENT, +// UPDATE_RESIDENT, +// } from "../../../gql/mutations/ResidentsMutations"; + +// type Props = { +// isOpen: boolean; +// setIsOpen: React.Dispatch>; +// }; + +// const ResidentModal = ({ isOpen, setIsOpen }: Props): React.ReactElement => { +// const [residentId, setResidentId] = useState(null); +// const [password, setPassword] = useState(""); +// const [arrivalDate, setArrivalDate] = useState(""); +// const [rooms, setRooms] = useState([1, 2, 3]); +// const [roomNumber, setRoomNumber] = useState(null); + +// const [showPassword, setShowPassword] = useState(false); +// const [submitPressed, setSubmitPressed] = useState(false); + +// const [addResident] = useMutation(ADD_RESIDENT); + +// const resetFormState = () => { +// setResidentId(null); +// setPassword(""); +// setArrivalDate(""); +// setRoomNumber(null); +// setShowPassword(false); +// setSubmitPressed(false); +// }; + +// const handleAddResident = async () => { +// const newResident = { +// residentId, +// roomNumber, +// password, +// dateJoined: arrivalDate, +// }; + +// try { +// const response = await addResident({ +// variables: { resident: newResident }, +// }); +// console.log("Added resident:", response); +// setIsOpen(false); +// resetFormState(); +// window.location.reload(); +// } catch (error) { +// console.error("Error adding resident:", error); +// } +// }; + +// const handleSubmit = () => { +// console.log(roomNumber); +// setSubmitPressed(true); +// if (!residentId || !password || !arrivalDate || !roomNumber) { +// console.error("Missing field"); +// } +// handleAddResident(); +// }; + +// return ( +// +// +// +// setResidentId(e.target.value ? Number(e.target.value) : null) +// } +// submitPressed={submitPressed} +// required +// /> +// +// +// Room Number +// +// +// +// setArrivalDate(e.target.value)} +// submitPressed={submitPressed} +// required +// /> +// setPassword(e.target.value)} +// submitPressed={submitPressed} +// required +// isPassword +// showPassword={showPassword} +// setShowPassword={setShowPassword} +// /> +// +// +// +// +// +// +// ); +// }; + +// export default ResidentModal; diff --git a/frontend/src/components/pages/residents/ResidentEditModal.tsx b/frontend/src/components/pages/residents/ResidentEditModal.tsx deleted file mode 100644 index e15003a0..00000000 --- a/frontend/src/components/pages/residents/ResidentEditModal.tsx +++ /dev/null @@ -1,172 +0,0 @@ -import React, { useState } from "react"; -import { Button, Text, Flex } from "@chakra-ui/react"; -import ModalContainer from "../../common/ModalContainer"; -import FormField from "../../common/FormField"; - -export type ResidentEditInfo = { - residentId: number; - roomNumber: number; - arrivalDate: string; - departureDate: string; - password: string; -}; - -type Props = { - isOpen: boolean; - setIsOpen: React.Dispatch>; - residentInfo: ResidentEditInfo; - onCloseEditModal: any; -}; - -const ResidentEditModal = ({ - isOpen, - setIsOpen, - residentInfo, - onCloseEditModal, -}: Props): React.ReactElement => { - const [residentId, setResidentId] = useState(residentInfo.residentId); - const [password, setPassword] = useState(residentInfo.password); - const [arrivalDate, setArrivalDate] = useState(residentInfo.arrivalDate); - const [departureDate, setDepartureDate] = useState( - residentInfo.departureDate, - ); - - const [roomNumber, setRoomNumber] = useState(residentInfo.roomNumber); - - const [showPassword, setShowPassword] = useState(false); - const [submitPressed, setSubmitPressed] = useState(false); - - const [roomList, setRoomList] = useState([1, 2, 3, 4, 5, 6, 7, 8]); - const [residentIdList, setresidentIdList] = useState>([ - 12345, 67890, 23456, 78901, 34567, 89012, 45678, 56789, 12346, 67891, 23457, - 78902, 34568, 89013, 45679, 90124, 56790, 12347, 67892, - ]); - - const [invalidRoomNumber, setInvalidRoomNumber] = useState(""); - const [invalidResidentId, setInvalidResidentId] = useState(""); - // useEffect(() => { - // // TODO: get roomList, residentIdList - // }, []); - - const handleSubmit = () => { - setSubmitPressed(true); - if (roomNumber !== residentInfo.roomNumber && roomNumber in roomList) { - setInvalidRoomNumber( - "This room number is already taken. Note: To set this field, please remove the room number from the Resident who occupies the room.", - ); - } else { - setInvalidRoomNumber(""); - } - - if ( - residentId !== residentInfo.residentId && - residentIdList.includes(residentId) - ) { - setInvalidResidentId("This ID number is already taken."); - } else { - setInvalidResidentId(""); - } - - if (invalidRoomNumber || invalidResidentId) { - return; - } - - // TODO: API POST to Residents/Participants - - if (!residentId || !password || !arrivalDate) { - // TODO: Add error handling - } - // TODO: API call to add resident - }; - - const resetFormState = () => { - onCloseEditModal(); - }; - - return ( - - - - setResidentId(parseInt(e.target.value, 10))} - submitPressed={submitPressed} - required - error={invalidResidentId !== ""} - /> - {invalidResidentId && ( - - {invalidResidentId} - - )} - - - - setRoomNumber(parseInt(e.target.value, 10))} - submitPressed={submitPressed} - error={invalidRoomNumber !== ""} - /> - {invalidRoomNumber && ( - - {invalidRoomNumber} - - )} - - - - {}} - submitPressed={submitPressed} - required - /> - setDepartureDate(e.target.value)} - submitPressed={submitPressed} - /> - - - setPassword(e.target.value)} - submitPressed={submitPressed} - required - isPassword - showPassword={showPassword} - setShowPassword={setShowPassword} - /> - - - - - - - - - ); -}; - -export default ResidentEditModal; diff --git a/frontend/src/components/pages/residents/ResidentModal.tsx b/frontend/src/components/pages/residents/ResidentModal.tsx deleted file mode 100644 index 5f8a83fa..00000000 --- a/frontend/src/components/pages/residents/ResidentModal.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import React, { useState } from "react"; -import { Button, Flex, FormControl, FormLabel, Select } from "@chakra-ui/react"; - -import { useMutation } from "@apollo/client"; -import ModalContainer from "../../common/ModalContainer"; -import FormField from "../../common/FormField"; -import { - ADD_RESIDENT, - UPDATE_RESIDENT, -} from "../../../APIClients/Mutations/ResidentsMutations"; - -type Props = { - isOpen: boolean; - setIsOpen: React.Dispatch>; -}; - -const ResidentModal = ({ isOpen, setIsOpen }: Props): React.ReactElement => { - const [residentId, setResidentId] = useState(null); - const [password, setPassword] = useState(""); - const [arrivalDate, setArrivalDate] = useState(""); - const [rooms, setRooms] = useState([1, 2, 3]); - const [roomNumber, setRoomNumber] = useState(null); - - const [showPassword, setShowPassword] = useState(false); - const [submitPressed, setSubmitPressed] = useState(false); - - const [addResident] = useMutation(ADD_RESIDENT); - - const resetFormState = () => { - setResidentId(null); - setPassword(""); - setArrivalDate(""); - setRoomNumber(null); - setShowPassword(false); - setSubmitPressed(false); - }; - - const handleAddResident = async () => { - const newResident = { - residentId, - roomNumber, - password, - dateJoined: arrivalDate, - }; - - try { - const response = await addResident({ - variables: { resident: newResident }, - }); - console.log("Added resident:", response); - setIsOpen(false); - resetFormState(); - window.location.reload(); - } catch (error) { - console.error("Error adding resident:", error); - } - }; - - const handleSubmit = () => { - console.log(roomNumber); - setSubmitPressed(true); - if (!residentId || !password || !arrivalDate || !roomNumber) { - console.error("Missing field"); - } - handleAddResident(); - }; - - return ( - - - - setResidentId(e.target.value ? Number(e.target.value) : null) - } - submitPressed={submitPressed} - required - /> - - - Room Number - - - - setArrivalDate(e.target.value)} - submitPressed={submitPressed} - required - /> - setPassword(e.target.value)} - submitPressed={submitPressed} - required - isPassword - showPassword={showPassword} - setShowPassword={setShowPassword} - /> - - - - - - - ); -}; - -export default ResidentModal; diff --git a/frontend/src/components/pages/residents/ResidentsPage.tsx b/frontend/src/components/pages/residents/ResidentsPage.tsx deleted file mode 100644 index 9239b1f0..00000000 --- a/frontend/src/components/pages/residents/ResidentsPage.tsx +++ /dev/null @@ -1,211 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { - Flex, - Input, - Button, - Icon, - InputGroup, - InputLeftElement, -} from "@chakra-ui/react"; -import { Add, Search } from "@mui/icons-material"; -import { useQuery } from "@apollo/client"; -// import { -// ADD_RESIDENT, -// UPDATE_RESIDENT, -// DELETE_RESIDENT, -// } from "../../../APIClients/Mutations/ResidentsMutations"; - -import { - // GET_RESIDENTS_BY_ID, - GET_ALL_RESIDENTS, - // GET_ACTIVE_RESIDENTS, -} from "../../../APIClients/Queries/ResidentsQueries"; - -// import { -// UserResponse, -// UserRequest, -// UserRequestUpdate, -// } from "../../../APIClients/Types/ResidentsType"; - -import CommonTable, { - ColumnInfoTypes, - TableData, -} from "../../common/CommonTable"; -import ResidentModal from "./ResidentModal"; -import ResidentEditModal, { ResidentEditInfo } from "./ResidentEditModal"; -// import { residentsMockData } from "../../../mocks/residents"; - -const columnTypes: ColumnInfoTypes[] = [ - { - header: "ID Number", - key: "residentId", - }, - { - header: "Room #", - key: "roomNumber", - }, - { - header: "Arrival Date", - key: "arrivalDate", - }, - { - header: "Departure Date", - key: "departureDate", - }, -]; - -const ResidentsPage = (): React.ReactElement => { - const [residents, setResidents] = useState([]); - const [isModalOpen, setIsModalOpen] = useState("none"); - const [residentEditInfo, setEditInfo] = useState(); - - // const [addResident] = useMutation<{ addResident: UserResponse }>( - // ADD_RESIDENT, - // ); - - // const [updateResident] = useMutation<{ - // userId: number; - // resident: UserResponse; - // }>(UPDATE_RESIDENT); - - // const [deleteResident] = useMutation<{ userId: number }>(DELETE_RESIDENT); - - // const handleAddResident = async () => { - // try { - // const date = new Date(); - // const formattedDate = date.toISOString().split("T")[0]; - - // const resident: UserRequest = { - // email: "dasfhsahfsoad@gmail.com", - // password: "qe8e9r789ewr", - // firstName: "Bob", - // lastName: "Bob", - // residentId: 1248120, - // birthDate: formattedDate, - // roomNumber: 3, - // credits: 500, - // dateJoined: formattedDate, - // }; - // await addResident({ variables: { resident } }); - // } catch (e) { - // console.log(e); - // } - // }; - - // const handleUpdateResident = async () => { - // try { - // const userId = 5; - // const resident: UserRequestUpdate = { - // lastName: "NEW NAME", - // roomNumber: 3, - // credits: 10, - // }; - // await updateResident({ variables: { userId, resident } }); - // } catch (e) { - // console.log(e); - // } - // }; - - // const handleDeleteResident = async () => { - // try { - // const userId = 1; - // await deleteResident({ variables: { userId } }); - // } catch (e) { - // console.log(e); - // } - // }; - - // const ids = [4]; - // const { - // loading: residentIdLoading, - // error: residentIdError, - // data: residentIdData, - // } = useQuery<{ userIds: [number] }>(GET_RESIDENTS_BY_ID, { - // variables: { userIds: ids }, - // }); - - // const { - // loading: residentActiveLoading, - // error: residentActiveError, - // data: residentActiveData, - // } = useQuery(GET_ACTIVE_RESIDENTS); - - const { - // loading: residentAllLoading, MAY NEED TO ADD LOADING ICON/STATE - // error: residentAllError, - data: residentAllData, - } = useQuery(GET_ALL_RESIDENTS); - - useEffect(() => { - if (residentAllData?.getAllResidents) { - setResidents(residentAllData.getAllResidents); - } - }, [residentAllData]); - - const handleResidentEdit = (row: any) => { - setIsModalOpen("edit"); - // console.log(row); - setEditInfo(row); - }; - - // CHANGE - const handleRowClick = (row: any) => { - setIsModalOpen("edit"); - // console.log(row); - setEditInfo(row); - }; - - const handleResidentSubmitEdit = () => { - setEditInfo(undefined); - - // TODO: modify data - }; - - return ( - - - - - - - - - - - { - return { - roomNumber: item.roomNumber, - arrivalDate: item.dateJoined, - departureDate: item.dateLeft ? item.dateLeft : "", - residentId: item.residentId, - }; - })} - columnInfo={columnTypes} - onEdit={handleResidentEdit} - /> - setIsModalOpen("none")} - /> - - {residentEditInfo && ( - setIsModalOpen("none")} - onCloseEditModal={handleResidentSubmitEdit} - /> - )} - - ); -}; - -export default ResidentsPage; diff --git a/frontend/src/components/pages/schedule/SchedulePage.tsx b/frontend/src/components/pages/schedule/SchedulePage.tsx index 491cc696..6914bd79 100644 --- a/frontend/src/components/pages/schedule/SchedulePage.tsx +++ b/frontend/src/components/pages/schedule/SchedulePage.tsx @@ -1,196 +1,197 @@ -import React, { useEffect, useImperativeHandle, useRef, useState } from "react"; -import { - Flex, - Tabs, - TabList, - Tab, - Box, - Heading, - Button, - IconButton, - Icon, -} from "@chakra-ui/react"; - -import { - ArrowBackIosNew, - ArrowForwardIos, - Edit, - FormatListBulleted, - CalendarMonth, -} from "@mui/icons-material"; - -import FullCalendar from "@fullcalendar/react"; -import { CalendarApi } from "@fullcalendar/core"; -import { ScheduleType } from "../../../types/ScheduleTypes"; -import ScheduleListView from "./listView/ScheduleListView"; -import ScheduleCalendar from "./calendarView/ScheduleCalendar"; - -const SchedulePage = (): React.ReactElement => { - const [rooms, setRooms] = useState([]); - const [scheduleType, setScheduleType] = useState("LIST"); - const [scheduleData, setScheduleData] = useState(""); - const [active, setActive] = useState("List"); - const [dateRange, setDateRange] = useState("Jan 1 - 7"); - - const calendarRef = useRef(null); - - const handleNext = () => { - console.log(scheduleType); - if (scheduleType === "CALENDAR") { - calendarRef.current?.next(); - } - }; - - const handlePrev = () => { - if (scheduleType === "CALENDAR") { - calendarRef.current?.prev(); - } - }; - - useEffect(() => { - // TODO: Fetch occupied rooms from API? - setRooms([1, 2, 3, 4, 5, 6]); - }, []); - - useEffect(() => { - if (scheduleType === "LIST") { - setScheduleData("List"); - } else if (scheduleType === "CALENDAR") { - setScheduleData("Calendar"); - } - }, [scheduleType]); - - const selectOption = (e: React.MouseEvent) => { - setActive(e.currentTarget.innerText); - }; - - const formatTabs = (roomNums: number[]) => { - return ( - - - {roomNums.map((room) => ( - - Room {room} - - ))} - - - ); - }; - - return ( - - - {formatTabs(rooms)} - - - - - - January 2025 - {/* see announcements page for how to determine what text shows */} - - - - } - /> - - } - /> - - - - - - - - - - - - - - - - - - - {scheduleType === "CALENDAR" ? ( - setDateRange(range)} - /> - ) : ( - - )} - - - ); -}; - -export default SchedulePage; +export {}; +// import React, { useEffect, useImperativeHandle, useRef, useState } from "react"; +// import { +// Flex, +// Tabs, +// TabList, +// Tab, +// Box, +// Heading, +// Button, +// IconButton, +// Icon, +// } from "@chakra-ui/react"; + +// import { +// ArrowBackIosNew, +// ArrowForwardIos, +// Edit, +// FormatListBulleted, +// CalendarMonth, +// } from "@mui/icons-material"; + +// import FullCalendar from "@fullcalendar/react"; +// import { CalendarApi } from "@fullcalendar/core"; +// import { ScheduleType } from "../../../types/ScheduleTypes"; +// import ScheduleListView from "./listView/ScheduleListView"; +// import ScheduleCalendar from "./calendarView/ScheduleCalendar"; + +// const SchedulePage = (): React.ReactElement => { +// const [rooms, setRooms] = useState([]); +// const [scheduleType, setScheduleType] = useState("LIST"); +// const [scheduleData, setScheduleData] = useState(""); +// const [active, setActive] = useState("List"); +// const [dateRange, setDateRange] = useState("Jan 1 - 7"); + +// const calendarRef = useRef(null); + +// const handleNext = () => { +// console.log(scheduleType); +// if (scheduleType === "CALENDAR") { +// calendarRef.current?.next(); +// } +// }; + +// const handlePrev = () => { +// if (scheduleType === "CALENDAR") { +// calendarRef.current?.prev(); +// } +// }; + +// useEffect(() => { +// // TODO: Fetch occupied rooms from API? +// setRooms([1, 2, 3, 4, 5, 6]); +// }, []); + +// useEffect(() => { +// if (scheduleType === "LIST") { +// setScheduleData("List"); +// } else if (scheduleType === "CALENDAR") { +// setScheduleData("Calendar"); +// } +// }, [scheduleType]); + +// const selectOption = (e: React.MouseEvent) => { +// setActive(e.currentTarget.innerText); +// }; + +// const formatTabs = (roomNums: number[]) => { +// return ( +// +// +// {roomNums.map((room) => ( +// +// Room {room} +// +// ))} +// +// +// ); +// }; + +// return ( +// +// +// {formatTabs(rooms)} +// + +// +// +// +// January 2025 +// {/* see announcements page for how to determine what text shows */} +// + +// +// } +// /> +// +// } +// /> +// +// + +// +// +// +// + +// +// +// + +// +// + +// +// +// +// {scheduleType === "CALENDAR" ? ( +// setDateRange(range)} +// /> +// ) : ( +// +// )} +// +// +// ); +// }; + +// export default SchedulePage; diff --git a/frontend/src/components/pages/schedule/calendarView/ScheduleCalendar.css b/frontend/src/components/pages/schedule/calendarView/ScheduleCalendar.css index 48214f0f..2b9c16d7 100644 --- a/frontend/src/components/pages/schedule/calendarView/ScheduleCalendar.css +++ b/frontend/src/components/pages/schedule/calendarView/ScheduleCalendar.css @@ -1,4 +1,4 @@ -.fc .fc-toolbar-title { +/* .fc .fc-toolbar-title { font-size: 2em; font-weight: 700; } @@ -33,4 +33,4 @@ col { --fc-button-hover-border-color: rgb(71, 53, 147); --fc-button-active-bg-color: rgb(62, 42, 153); --fc-button-active-border-color: rgb(62, 42, 153); -} \ No newline at end of file +} */ \ No newline at end of file diff --git a/frontend/src/components/pages/schedule/calendarView/ScheduleCalendar.tsx b/frontend/src/components/pages/schedule/calendarView/ScheduleCalendar.tsx index 23f78968..8e058f64 100644 --- a/frontend/src/components/pages/schedule/calendarView/ScheduleCalendar.tsx +++ b/frontend/src/components/pages/schedule/calendarView/ScheduleCalendar.tsx @@ -1,148 +1,149 @@ -import FullCalendar from "@fullcalendar/react"; -import timeGridPlugin from "@fullcalendar/timegrid"; -import { DayHeaderContentArg, EventContentArg } from "@fullcalendar/core"; -import React, { - forwardRef, - useEffect, - useImperativeHandle, - useRef, -} from "react"; -import ModeCommentOutlinedIcon from "@mui/icons-material/ModeCommentOutlined"; -import "./ScheduleCalendar.css"; -import { ConnectingAirportsOutlined } from "@mui/icons-material"; +export {}; +// import FullCalendar from "@fullcalendar/react"; +// import timeGridPlugin from "@fullcalendar/timegrid"; +// import { DayHeaderContentArg, EventContentArg } from "@fullcalendar/core"; +// import React, { +// forwardRef, +// useEffect, +// useImperativeHandle, +// useRef, +// } from "react"; +// import ModeCommentOutlinedIcon from "@mui/icons-material/ModeCommentOutlined"; +// import "./ScheduleCalendar.css"; +// import { ConnectingAirportsOutlined } from "@mui/icons-material"; -const events = [ - { - title: "test", - start: new Date("2024-09-29T09:00:00"), - end: new Date("2024-09-29T10:00:00"), - allDay: true, - }, -]; +// const events = [ +// { +// title: "test", +// start: new Date("2024-09-29T09:00:00"), +// end: new Date("2024-09-29T10:00:00"), +// allDay: true, +// }, +// ]; -function renderEventContent(eventInfo: EventContentArg) { - const isAllDay = eventInfo.event.allDay; - return ( -
-
- {eventInfo.event.title} -
- {isAllDay - ? "" - : `${new Date(eventInfo.event.start!).toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - })} - ${new Date(eventInfo.event.end!).toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - })}`} - -
- ); -} +// function renderEventContent(eventInfo: EventContentArg) { +// const isAllDay = eventInfo.event.allDay; +// return ( +//
+//
+// {eventInfo.event.title} +//
+// {isAllDay +// ? "" +// : `${new Date(eventInfo.event.start!).toLocaleTimeString([], { +// hour: "2-digit", +// minute: "2-digit", +// })} - ${new Date(eventInfo.event.end!).toLocaleTimeString([], { +// hour: "2-digit", +// minute: "2-digit", +// })}`} +// +//
+// ); +// } -function renderHeaderContent(date: DayHeaderContentArg) { - const dayOfWeek = date.date.toLocaleString("en-US", { weekday: "long" }); - const dayOfMonth = date.date.getDate(); +// function renderHeaderContent(date: DayHeaderContentArg) { +// const dayOfWeek = date.date.toLocaleString("en-US", { weekday: "long" }); +// const dayOfMonth = date.date.getDate(); - return ( -
- {dayOfWeek.substring(0, 3).toUpperCase()} -
- {dayOfMonth} -
- ); -} +// return ( +//
+// {dayOfWeek.substring(0, 3).toUpperCase()} +//
+// {dayOfMonth} +//
+// ); +// } -interface ScheduleCalendarProps { - setDateRange: (range: string) => void; -} +// interface ScheduleCalendarProps { +// setDateRange: (range: string) => void; +// } -interface ScheduleCalendarHandle { - next: () => void; - prev: () => void; -} +// interface ScheduleCalendarHandle { +// next: () => void; +// prev: () => void; +// } -const ScheduleCalendar = forwardRef< - ScheduleCalendarHandle, - ScheduleCalendarProps ->((props, ref) => { - const calendarRef = useRef(null); +// const ScheduleCalendar = forwardRef< +// ScheduleCalendarHandle, +// ScheduleCalendarProps +// >((props, ref) => { +// const calendarRef = useRef(null); - const handleAllDayContent = (arg: any) => { - return {arg.text ? "" : ""}; - }; +// const handleAllDayContent = (arg: any) => { +// return {arg.text ? "" : ""}; +// }; - const formatDateRange = (startDate: Date, endDate: Date) => { - const startFormat = startDate.toLocaleDateString("en-US", { - month: "short", - day: "numeric", - }); - const endFormat = endDate.toLocaleDateString("en-US", { - month: "short", - day: "numeric", - }); +// const formatDateRange = (startDate: Date, endDate: Date) => { +// const startFormat = startDate.toLocaleDateString("en-US", { +// month: "short", +// day: "numeric", +// }); +// const endFormat = endDate.toLocaleDateString("en-US", { +// month: "short", +// day: "numeric", +// }); - if (startDate.getMonth() === endDate.getMonth()) { - return `${startFormat} - ${endFormat.split(" ")[1]}`; - } - return `${startFormat} - ${endFormat}`; - }; +// if (startDate.getMonth() === endDate.getMonth()) { +// return `${startFormat} - ${endFormat.split(" ")[1]}`; +// } +// return `${startFormat} - ${endFormat}`; +// }; - useImperativeHandle(ref, () => ({ - next: () => { - if (calendarRef.current) calendarRef.current.getApi().next(); - }, - prev: () => { - if (calendarRef.current) calendarRef.current.getApi().prev(); - }, - })); +// useImperativeHandle(ref, () => ({ +// next: () => { +// if (calendarRef.current) calendarRef.current.getApi().next(); +// }, +// prev: () => { +// if (calendarRef.current) calendarRef.current.getApi().prev(); +// }, +// })); - return ( -
- { - props.setDateRange(formatDateRange(dateInfo.start, dateInfo.end)); - }} - headerToolbar={false} - /> -
- ); -}); +// return ( +//
+// { +// props.setDateRange(formatDateRange(dateInfo.start, dateInfo.end)); +// }} +// headerToolbar={false} +// /> +//
+// ); +// }); -ScheduleCalendar.displayName = "ScheduleCalendar"; +// ScheduleCalendar.displayName = "ScheduleCalendar"; -export default ScheduleCalendar; +// export default ScheduleCalendar; diff --git a/frontend/src/components/pages/schedule/listView/ScheduleListView.tsx b/frontend/src/components/pages/schedule/listView/ScheduleListView.tsx index c3e43e57..0b038847 100644 --- a/frontend/src/components/pages/schedule/listView/ScheduleListView.tsx +++ b/frontend/src/components/pages/schedule/listView/ScheduleListView.tsx @@ -1,154 +1,155 @@ -import React, { useEffect, useState } from "react"; -import { Flex, Tabs, TabList, Tab, Text } from "@chakra-ui/react"; +export {}; +// import React, { useEffect, useState } from "react"; +// import { Flex, Tabs, TabList, Tab, Text } from "@chakra-ui/react"; -import { tasksColumnTypes } from "./columnKeys"; +// import { tasksColumnTypes } from "./columnKeys"; -import { - scheduleTasksMockData, - sundayScheduleTasksMockData, - mondayScheduleTasksMockData, - tuesdayScheduleTasksMockData, - wednesdayScheduleTasksMockData, - thursdayScheduleTasksMockData, - fridayScheduleTasksMockData, - saturdayScheduleTasksMockData, -} from "../../../../mocks/scheduletasks"; +// import { +// scheduleTasksMockData, +// sundayScheduleTasksMockData, +// mondayScheduleTasksMockData, +// tuesdayScheduleTasksMockData, +// wednesdayScheduleTasksMockData, +// thursdayScheduleTasksMockData, +// fridayScheduleTasksMockData, +// saturdayScheduleTasksMockData, +// } from "../../../../mocks/scheduletasks"; -import ScheduleTable, { ColumnInfoTypes, TableData } from "./ScheduleTable"; +// import ScheduleTable, { ColumnInfoTypes, TableData } from "./ScheduleTable"; -const ScheduleListView = (): React.ReactElement => { - const enum Dates { - SUNDAY = "SUNDAY", - MONDAY = "MONDAY", - TUESDAY = "TUESDAY", - WEDNESDAY = "WEDNESDAY", - THURSDAY = "THURSDAY", - FRIDAY = "FRIDAY", - SATURDAY = "SATURDAY", - } +// const ScheduleListView = (): React.ReactElement => { +// const enum Dates { +// SUNDAY = "SUNDAY", +// MONDAY = "MONDAY", +// TUESDAY = "TUESDAY", +// WEDNESDAY = "WEDNESDAY", +// THURSDAY = "THURSDAY", +// FRIDAY = "FRIDAY", +// SATURDAY = "SATURDAY", +// } - const [taskData, setTaskData] = useState([]); - const [taskDataColumns, setTaskDataColumns] = useState([]); - const [taskDate, setTaskDate] = useState(Dates.SUNDAY); - const [dailyTaskData, setDailyTaskData] = useState([]); +// const [taskData, setTaskData] = useState([]); +// const [taskDataColumns, setTaskDataColumns] = useState([]); +// const [taskDate, setTaskDate] = useState(Dates.SUNDAY); +// const [dailyTaskData, setDailyTaskData] = useState([]); - useEffect(() => { - setTaskDataColumns(tasksColumnTypes); - setTaskData(scheduleTasksMockData); - if (taskDate === Dates.SUNDAY) { - setDailyTaskData(sundayScheduleTasksMockData); - } else if (taskDate === Dates.MONDAY) { - setDailyTaskData(mondayScheduleTasksMockData); - } else if (taskDate === Dates.TUESDAY) { - setDailyTaskData(tuesdayScheduleTasksMockData); - } else if (taskDate === Dates.WEDNESDAY) { - setDailyTaskData(wednesdayScheduleTasksMockData); - } else if (taskDate === Dates.THURSDAY) { - setDailyTaskData(thursdayScheduleTasksMockData); - } else if (taskDate === Dates.FRIDAY) { - setDailyTaskData(fridayScheduleTasksMockData); - } else if (taskDate === Dates.SATURDAY) { - setDailyTaskData(saturdayScheduleTasksMockData); - } else { - setDailyTaskData(sundayScheduleTasksMockData); - } - }, [taskDate]); +// useEffect(() => { +// setTaskDataColumns(tasksColumnTypes); +// setTaskData(scheduleTasksMockData); +// if (taskDate === Dates.SUNDAY) { +// setDailyTaskData(sundayScheduleTasksMockData); +// } else if (taskDate === Dates.MONDAY) { +// setDailyTaskData(mondayScheduleTasksMockData); +// } else if (taskDate === Dates.TUESDAY) { +// setDailyTaskData(tuesdayScheduleTasksMockData); +// } else if (taskDate === Dates.WEDNESDAY) { +// setDailyTaskData(wednesdayScheduleTasksMockData); +// } else if (taskDate === Dates.THURSDAY) { +// setDailyTaskData(thursdayScheduleTasksMockData); +// } else if (taskDate === Dates.FRIDAY) { +// setDailyTaskData(fridayScheduleTasksMockData); +// } else if (taskDate === Dates.SATURDAY) { +// setDailyTaskData(saturdayScheduleTasksMockData); +// } else { +// setDailyTaskData(sundayScheduleTasksMockData); +// } +// }, [taskDate]); - return ( - <> - - - Weekly Tasks - - {}} - isSelectable - /> - - - - Daily Tasks - - - - { - setTaskDate(Dates.SUNDAY); - }} - > - Sunday - - { - setTaskDate(Dates.MONDAY); - }} - > - Monday - - { - setTaskDate(Dates.TUESDAY); - }} - > - Tuesday - - { - setTaskDate(Dates.WEDNESDAY); - }} - > - Wednesday - - { - setTaskDate(Dates.THURSDAY); - }} - > - Thursday - - { - setTaskDate(Dates.FRIDAY); - }} - > - Friday - - { - setTaskDate(Dates.SATURDAY); - }} - > - Saturday - - - - {}} - isSelectable - /> - - - ); -}; +// return ( +// <> +// +// +// Weekly Tasks +// +// {}} +// isSelectable +// /> +// +// +// +// Daily Tasks +// +// +// +// { +// setTaskDate(Dates.SUNDAY); +// }} +// > +// Sunday +// +// { +// setTaskDate(Dates.MONDAY); +// }} +// > +// Monday +// +// { +// setTaskDate(Dates.TUESDAY); +// }} +// > +// Tuesday +// +// { +// setTaskDate(Dates.WEDNESDAY); +// }} +// > +// Wednesday +// +// { +// setTaskDate(Dates.THURSDAY); +// }} +// > +// Thursday +// +// { +// setTaskDate(Dates.FRIDAY); +// }} +// > +// Friday +// +// { +// setTaskDate(Dates.SATURDAY); +// }} +// > +// Saturday +// +// +// +// {}} +// isSelectable +// /> +// +// +// ); +// }; -export default ScheduleListView; +// export default ScheduleListView; diff --git a/frontend/src/components/pages/schedule/listView/ScheduleTable.tsx b/frontend/src/components/pages/schedule/listView/ScheduleTable.tsx index e0d6da40..65085833 100644 --- a/frontend/src/components/pages/schedule/listView/ScheduleTable.tsx +++ b/frontend/src/components/pages/schedule/listView/ScheduleTable.tsx @@ -1,349 +1,350 @@ -import React, { useState, useEffect } from "react"; -import { - Table, - Thead, - Tbody, - Tr, - Th, - Td, - TableContainer, - Checkbox, - Center, - Box, - Flex, - IconButton, - Icon, -} from "@chakra-ui/react"; -import ModeCommentOutlinedIcon from "@mui/icons-material/ModeCommentOutlined"; -import ChevronLeftOutlinedIcon from "@mui/icons-material/ChevronLeftOutlined"; -import ChevronRightOutlinedIcon from "@mui/icons-material/ChevronRightOutlined"; -import KeyboardArrowUpOutlinedIcon from "@mui/icons-material/KeyboardArrowUpOutlined"; -import KeyboardArrowDownOutlinedIcon from "@mui/icons-material/KeyboardArrowDownOutlined"; +export {}; +// import React, { useState, useEffect } from "react"; +// import { +// Table, +// Thead, +// Tbody, +// Tr, +// Th, +// Td, +// TableContainer, +// Checkbox, +// Center, +// Box, +// Flex, +// IconButton, +// Icon, +// } from "@chakra-ui/react"; +// import ModeCommentOutlinedIcon from "@mui/icons-material/ModeCommentOutlined"; +// import ChevronLeftOutlinedIcon from "@mui/icons-material/ChevronLeftOutlined"; +// import ChevronRightOutlinedIcon from "@mui/icons-material/ChevronRightOutlined"; +// import KeyboardArrowUpOutlinedIcon from "@mui/icons-material/KeyboardArrowUpOutlined"; +// import KeyboardArrowDownOutlinedIcon from "@mui/icons-material/KeyboardArrowDownOutlined"; -type TableTypes = string | number | boolean | Date; +// type TableTypes = string | number | boolean | Date; -export type ColumnInfoTypes = { header: string; key: string }; +// export type ColumnInfoTypes = { header: string; key: string }; -export interface TableData { - [key: string]: TableTypes; -} +// export interface TableData { +// [key: string]: TableTypes; +// } -type Props = { - data: TableData[]; - columnInfo: ColumnInfoTypes[]; - onEdit: (row: unknown) => unknown; - maxResults?: number; - isSelectable?: boolean; -}; +// type Props = { +// data: TableData[]; +// columnInfo: ColumnInfoTypes[]; +// onEdit: (row: unknown) => unknown; +// maxResults?: number; +// isSelectable?: boolean; +// }; -type SortState = { - [key: string]: number; -}; +// type SortState = { +// [key: string]: number; +// }; -const ScheduleTable = ({ - columnInfo, - data, - onEdit, - maxResults = 10, - isSelectable = false, -}: Props): React.ReactElement => { - const [checked, setChecked] = useState(data.map(() => false)); - const [page, setPage] = useState(1); - const [pageArray, setPageArray] = useState([]); - const [sortingColumn, setSortingColumn] = useState({}); - const [originalData, setOriginalData] = useState(data); - const [sortedData, setSortedData] = useState(data); +// const ScheduleTable = ({ +// columnInfo, +// data, +// onEdit, +// maxResults = 10, +// isSelectable = false, +// }: Props): React.ReactElement => { +// const [checked, setChecked] = useState(data.map(() => false)); +// const [page, setPage] = useState(1); +// const [pageArray, setPageArray] = useState([]); +// const [sortingColumn, setSortingColumn] = useState({}); +// const [originalData, setOriginalData] = useState(data); +// const [sortedData, setSortedData] = useState(data); - useEffect(() => { - return Math.ceil(data.length / maxResults) >= 5 - ? setPageArray([1, 2, 3, 4, 5]) - : setPageArray( - Array.from( - { length: Math.ceil(data.length / maxResults) }, - (_, i) => i + 1, - ), - ); - }, [data, maxResults]); +// useEffect(() => { +// return Math.ceil(data.length / maxResults) >= 5 +// ? setPageArray([1, 2, 3, 4, 5]) +// : setPageArray( +// Array.from( +// { length: Math.ceil(data.length / maxResults) }, +// (_, i) => i + 1, +// ), +// ); +// }, [data, maxResults]); - useEffect(() => { - setOriginalData(data); - setSortedData(data); - }, [data]); +// useEffect(() => { +// setOriginalData(data); +// setSortedData(data); +// }, [data]); - // sorting the columns by ascending and descending order based on column indicated - const sortColumn = (column: string) => { - const newSortingColumn: SortState = {}; - columnInfo.forEach((col) => { - newSortingColumn[col.key] = - col.key === column ? sortingColumn[column] : 0; - }); +// // sorting the columns by ascending and descending order based on column indicated +// const sortColumn = (column: string) => { +// const newSortingColumn: SortState = {}; +// columnInfo.forEach((col) => { +// newSortingColumn[col.key] = +// col.key === column ? sortingColumn[column] : 0; +// }); - // increment column sorting state - sortingColumn[column] = sortingColumn[column] - ? sortingColumn[column] + 1 - : 1; +// // increment column sorting state +// sortingColumn[column] = sortingColumn[column] +// ? sortingColumn[column] + 1 +// : 1; - // if at the end, go back to 0 - if (sortingColumn[column] === 3) { - setSortingColumn({ ...sortingColumn, [column]: 0 }); - setSortedData(originalData); - return; - } - setSortingColumn({ - ...newSortingColumn, - [column]: sortingColumn[column], - }); +// // if at the end, go back to 0 +// if (sortingColumn[column] === 3) { +// setSortingColumn({ ...sortingColumn, [column]: 0 }); +// setSortedData(originalData); +// return; +// } +// setSortingColumn({ +// ...newSortingColumn, +// [column]: sortingColumn[column], +// }); - // apply sorting based on which sorting state the column's in - const sorted = [...originalData].sort((a, b) => { - if (sortingColumn[column] === 1) { - return a[column] > b[column] ? 1 : -1; - } - if (sortingColumn[column] === 2) { - return a[column] < b[column] ? 1 : -1; - } - return 0; - }); - setSortedData(sorted); - }; +// // apply sorting based on which sorting state the column's in +// const sorted = [...originalData].sort((a, b) => { +// if (sortingColumn[column] === 1) { +// return a[column] > b[column] ? 1 : -1; +// } +// if (sortingColumn[column] === 2) { +// return a[column] < b[column] ? 1 : -1; +// } +// return 0; +// }); +// setSortedData(sorted); +// }; - // constants for pagination UI - const checkedPage = checked.slice((page - 1) * maxResults, page * maxResults); - const allChecked = checkedPage.every(Boolean); - const isIndeterminate = checkedPage.some(Boolean) && !allChecked; +// // constants for pagination UI +// const checkedPage = checked.slice((page - 1) * maxResults, page * maxResults); +// const allChecked = checkedPage.every(Boolean); +// const isIndeterminate = checkedPage.some(Boolean) && !allChecked; - // pagination functions - const leftPaginate = () => { - if (page > 1) setPage(page - 1); - if (pageArray[0] > 1 && pageArray.length === 5) { - setPageArray(pageArray.map((item) => item - 1)); - } - }; +// // pagination functions +// const leftPaginate = () => { +// if (page > 1) setPage(page - 1); +// if (pageArray[0] > 1 && pageArray.length === 5) { +// setPageArray(pageArray.map((item) => item - 1)); +// } +// }; - const rightPaginate = () => { - if (page < Math.ceil(data.length / maxResults)) setPage(page + 1); - if ( - pageArray[pageArray.length - 1] < Math.ceil(data.length / maxResults) && - pageArray.length === 5 - ) { - setPageArray(pageArray.map((item) => item + 1)); - } - }; +// const rightPaginate = () => { +// if (page < Math.ceil(data.length / maxResults)) setPage(page + 1); +// if ( +// pageArray[pageArray.length - 1] < Math.ceil(data.length / maxResults) && +// pageArray.length === 5 +// ) { +// setPageArray(pageArray.map((item) => item + 1)); +// } +// }; - const numberPaginate = (n: number) => { - setPage(n); - // Sets n as the center of the page array when possible. - if ( - n - 2 >= 1 && - n + 2 <= Math.ceil(data.length / maxResults) && - pageArray.length === 5 - ) { - setPageArray([n - 2, n - 1, n, n + 1, n + 2]); - } - }; +// const numberPaginate = (n: number) => { +// setPage(n); +// // Sets n as the center of the page array when possible. +// if ( +// n - 2 >= 1 && +// n + 2 <= Math.ceil(data.length / maxResults) && +// pageArray.length === 5 +// ) { +// setPageArray([n - 2, n - 1, n, n + 1, n + 2]); +// } +// }; - return ( - - - - - - {isSelectable ? ( - - ) : null} - {columnInfo.map((header, index) => ( - - ))} - - - - {sortedData - .slice((page - 1) * maxResults, page * maxResults) - .map((row, index) => { - return ( - - {isSelectable ? ( - - ) : null} - {columnInfo.map((column, i) => { - const getColor = (status: string) => { - if (status === "Completed") return "#0D8312"; - if (status === "Excused") return "#B07D18"; - if (status === "Incomplete") return "#B21D2F"; - return "black"; - }; +// return ( +// +// +//
- {null} - - - {header.header} - - { - sortColumn(header.key); - }} - /> - { - sortColumn(header.key); - }} - /> - - - -
- { - const newChecked = [...checked]; - newChecked[index + (page - 1) * maxResults] = - e.target.checked; - setChecked(newChecked); - }} - /> -
+// +// +// {isSelectable ? ( +// +// ) : null} +// {columnInfo.map((header, index) => ( +// +// ))} +// +// +// +// {sortedData +// .slice((page - 1) * maxResults, page * maxResults) +// .map((row, index) => { +// return ( +// +// {isSelectable ? ( +// +// ) : null} +// {columnInfo.map((column, i) => { +// const getColor = (status: string) => { +// if (status === "Completed") return "#0D8312"; +// if (status === "Excused") return "#B07D18"; +// if (status === "Incomplete") return "#B21D2F"; +// return "black"; +// }; - const getBoxColor = (status: string) => { - if (status === "Completed") return "#CDEECE"; - if (status === "Excused") return "#FFE5B2"; - if (status === "Incomplete") return "#F8D7DB"; - return "black"; - }; +// const getBoxColor = (status: string) => { +// if (status === "Completed") return "#CDEECE"; +// if (status === "Excused") return "#FFE5B2"; +// if (status === "Incomplete") return "#F8D7DB"; +// return "black"; +// }; - return column.key === "status" ? ( - - ) : ( - - ); - })} - {row.status !== "Incomplete" && ( - - )} - - ); - })} - -
+// {null} +// +// +// {header.header} +// +// { +// sortColumn(header.key); +// }} +// /> +// { +// sortColumn(header.key); +// }} +// /> +// +// +// +//
+// { +// const newChecked = [...checked]; +// newChecked[index + (page - 1) * maxResults] = +// e.target.checked; +// setChecked(newChecked); +// }} +// /> +// - - {row[column.key] ? ( - {String(row[column.key])} - ) : ( - "" - )} - - - {row[column.key] ? String(row[column.key]) : ""} - onEdit(row)}> - -
-
+// return column.key === "status" ? ( +// +// +// {row[column.key] ? ( +// {String(row[column.key])} +// ) : ( +// "" +// )} +// +// +// ) : ( +// +// {row[column.key] ? String(row[column.key]) : ""} +// +// ); +// })} +// {row.status !== "Incomplete" && ( +// onEdit(row)}> +// +// +// )} +// +// ); +// })} +// +// +// - - - {`Showing ${(page - 1) * maxResults + 1} to ${Math.min( - page * maxResults, - data.length, - )} of ${data.length} entries`} - - - - } - onClick={() => leftPaginate()} - /> - {pageArray.map((item, index) => { - return ( -
numberPaginate(item)} - key={index} - > - {item} -
- ); - })} - } - onClick={() => rightPaginate()} - /> -
-
-
-
- ); -}; +// +// +// {`Showing ${(page - 1) * maxResults + 1} to ${Math.min( +// page * maxResults, +// data.length, +// )} of ${data.length} entries`} +// +// +// +// } +// onClick={() => leftPaginate()} +// /> +// {pageArray.map((item, index) => { +// return ( +//
numberPaginate(item)} +// key={index} +// > +// {item} +//
+// ); +// })} +// } +// onClick={() => rightPaginate()} +// /> +//
+//
+//
+// +// ); +// }; -export default ScheduleTable; +// export default ScheduleTable; diff --git a/frontend/src/components/pages/schedule/listView/columnKeys.ts b/frontend/src/components/pages/schedule/listView/columnKeys.ts index 665de895..73c19082 100644 --- a/frontend/src/components/pages/schedule/listView/columnKeys.ts +++ b/frontend/src/components/pages/schedule/listView/columnKeys.ts @@ -1,22 +1,23 @@ -import { ColumnInfoTypes } from "../../../common/CommonTable"; +export {}; +// import { ColumnInfoTypes } from "../../../common/CommonTable"; -export const tasksColumnTypes: ColumnInfoTypes[] = [ - { - header: " Name", - key: "title", - }, - { - header: "Status", - key: "status", - }, - { - header: "Time", - key: "time", - }, - { - header: "Marillac Bucks", - key: "creditValue", - }, -]; +// export const tasksColumnTypes: ColumnInfoTypes[] = [ +// { +// header: " Name", +// key: "title", +// }, +// { +// header: "Status", +// key: "status", +// }, +// { +// header: "Time", +// key: "time", +// }, +// { +// header: "Marillac Bucks", +// key: "creditValue", +// }, +// ]; -export default {}; +// export default {}; diff --git a/frontend/src/components/pages/tasks/TaskModal.tsx b/frontend/src/components/pages/tasks/TaskModal.tsx index e7cd8aab..1b3d603c 100644 --- a/frontend/src/components/pages/tasks/TaskModal.tsx +++ b/frontend/src/components/pages/tasks/TaskModal.tsx @@ -1,393 +1,394 @@ -import React, { useState, useEffect } from "react"; -import { - Button, - Select, - Flex, - FormControl, - FormLabel, - RadioGroup, - Radio, -} from "@chakra-ui/react"; -import colors from "../../../theme/colors"; -import ModalContainer from "../../common/ModalContainer"; -import FormField from "../../common/FormField"; -import { - TaskType, - Task, - CustomTask, - ChoreTask, -} from "../../../types/TaskTypes"; -import { - TaskRequest, - TaskTypeEnum, - RecurrenceFrequency, - DaysOfWeek, -} from "../../../APIClients/Types/TaskType"; +export {}; +// import React, { useState, useEffect } from "react"; +// import { +// Button, +// Select, +// Flex, +// FormControl, +// FormLabel, +// RadioGroup, +// Radio, +// } from "@chakra-ui/react"; +// import colors from "../../../theme/colors"; +// import ModalContainer from "../../common/ModalContainer"; +// import FormField from "../../common/FormField"; +// import { +// TaskType, +// Task, +// CustomTask, +// ChoreTask, +// } from "../../../types/TaskTypes"; +// import { +// TaskRequest, +// TaskTypeEnum, +// RecurrenceFrequency, +// DaysOfWeek, +// } from "../../../APIClients/Types/TaskType"; -type Props = { - isOpen: boolean; - setIsOpen: React.Dispatch>; - task: Task | null; - handleDeleteTask?: (taskId: string) => Promise; - handleSaveClick: (taskId: string, task: TaskRequest) => Promise; -}; +// type Props = { +// isOpen: boolean; +// setIsOpen: React.Dispatch>; +// task: Task | null; +// handleDeleteTask?: (taskId: string) => Promise; +// handleSaveClick: (taskId: string, task: TaskRequest) => Promise; +// }; -// returns an array of times in 30 minute increments -const generateOptions = () => { - const options = []; - for (let hour = 0; hour < 24; hour += 1) { - for (let minute = 0; minute < 60; minute += 30) { - let formattedHour; - if (hour === 0) { - formattedHour = "12"; - } else if (hour > 12) { - formattedHour = `${hour - 12}`; - } else { - formattedHour = `${hour}`; - } - const ampm = hour < 12 ? "am" : "pm"; - const formattedMinute = minute.toString().padStart(2, "0"); - options.push(`${formattedHour}:${formattedMinute}${ampm}`); - } - } - return options; -}; +// // returns an array of times in 30 minute increments +// const generateOptions = () => { +// const options = []; +// for (let hour = 0; hour < 24; hour += 1) { +// for (let minute = 0; minute < 60; minute += 30) { +// let formattedHour; +// if (hour === 0) { +// formattedHour = "12"; +// } else if (hour > 12) { +// formattedHour = `${hour - 12}`; +// } else { +// formattedHour = `${hour}`; +// } +// const ampm = hour < 12 ? "am" : "pm"; +// const formattedMinute = minute.toString().padStart(2, "0"); +// options.push(`${formattedHour}:${formattedMinute}${ampm}`); +// } +// } +// return options; +// }; -const options = generateOptions(); +// const options = generateOptions(); -const TaskModal = ({ - isOpen, - setIsOpen, - task, - handleDeleteTask, - handleSaveClick, -}: Props): React.ReactElement => { - const [taskType, setTaskType] = useState("OPTIONAL"); - const [recurrence, setRecurrence] = useState("Does Not Repeat"); - const [marillacBucks, setMarillacBucks] = useState( - undefined, - ); - // const [comments, setComments] = useState(""); - const [selectedDays, setSelectedDays] = useState([]); - const days = ["Su", "M", "Tu", "W", "Th", "F", "Sa"]; +// const TaskModal = ({ +// isOpen, +// setIsOpen, +// task, +// handleDeleteTask, +// handleSaveClick, +// }: Props): React.ReactElement => { +// const [taskType, setTaskType] = useState("OPTIONAL"); +// const [recurrence, setRecurrence] = useState("Does Not Repeat"); +// const [marillacBucks, setMarillacBucks] = useState( +// undefined, +// ); +// // const [comments, setComments] = useState(""); +// const [selectedDays, setSelectedDays] = useState([]); +// const days = ["Su", "M", "Tu", "W", "Th", "F", "Sa"]; - const [title, setTitle] = useState(""); - const [location, setLocation] = useState(""); - const [dueDate, setDueDate] = useState(""); - const [dueTime, setDueTime] = useState(""); - const [completedOn, setCompletedOn] = useState("every"); - const [endsOn, setEndsOn] = useState("never"); - const [endsOnDate, setEndsOnDate] = useState(""); - // const [isAllDay, setIsAllDay] = useState(false); - // const [recurrenceFrequency, setRecurrenceFrequency] = useState(""); +// const [title, setTitle] = useState(""); +// const [location, setLocation] = useState(""); +// const [dueDate, setDueDate] = useState(""); +// const [dueTime, setDueTime] = useState(""); +// const [completedOn, setCompletedOn] = useState("every"); +// const [endsOn, setEndsOn] = useState("never"); +// const [endsOnDate, setEndsOnDate] = useState(""); +// // const [isAllDay, setIsAllDay] = useState(false); +// // const [recurrenceFrequency, setRecurrenceFrequency] = useState(""); - const [submitPressed, setSubmitPressed] = useState(false); - const [errorSubmitting, setError] = useState(""); - const isEditMode = !!task; +// const [submitPressed, setSubmitPressed] = useState(false); +// const [errorSubmitting, setError] = useState(""); +// const isEditMode = !!task; - const dayIdMap = [ - { key: "MONDAY", short: "M" }, - { key: "TUESDAY", short: "Tu" }, - { key: "WEDNESDAY", short: "W" }, - { key: "THURSDAY", short: "Th" }, - { key: "FRIDAY", short: "F" }, - { key: "SATURDAY", short: "Sa" }, - { key: "SUNDAY", short: "Su" }, - ]; +// const dayIdMap = [ +// { key: "MONDAY", short: "M" }, +// { key: "TUESDAY", short: "Tu" }, +// { key: "WEDNESDAY", short: "W" }, +// { key: "THURSDAY", short: "Th" }, +// { key: "FRIDAY", short: "F" }, +// { key: "SATURDAY", short: "Sa" }, +// { key: "SUNDAY", short: "Su" }, +// ]; - useEffect(() => { - if (!isOpen) { - return; - } - if (task) { - setTaskType(task.type); - setRecurrence( - task.recurrenceFrequency === "ONE_TIME" ? "Does Not Repeat" : "Repeats", - ); - setMarillacBucks(task.creditValue); +// useEffect(() => { +// if (!isOpen) { +// return; +// } +// if (task) { +// setTaskType(task.type); +// setRecurrence( +// task.recurrenceFrequency === "ONE_TIME" ? "Does Not Repeat" : "Repeats", +// ); +// setMarillacBucks(task.creditValue); - if (task.recurrenceFrequency === "ONE_TIME") { - const day = dayIdMap.find( - (dayMp) => dayMp.key === task.specificDay, - )?.short; - if (day) { - setSelectedDays([day]); - } - } else { - setSelectedDays( - task.repeatDays - .map((day) => dayIdMap.find((dayMp) => dayMp.key === day)?.short) - .filter((day): day is string => day !== undefined), - ); - } +// if (task.recurrenceFrequency === "ONE_TIME") { +// const day = dayIdMap.find( +// (dayMp) => dayMp.key === task.specificDay, +// )?.short; +// if (day) { +// setSelectedDays([day]); +// } +// } else { +// setSelectedDays( +// task.repeatDays +// .map((day) => dayIdMap.find((dayMp) => dayMp.key === day)?.short) +// .filter((day): day is string => day !== undefined), +// ); +// } - setTitle(task.title); - setDueDate(task.endDate ? task.endDate.toString() : ""); - setDueTime(""); - } else { - setTaskType("OPTIONAL"); - setRecurrence("Does Not Repeat"); - setMarillacBucks(undefined); - setSelectedDays([]); - setTitle(""); - setDueDate(""); - setDueTime(""); - } - }, [task, isOpen]); +// setTitle(task.title); +// setDueDate(task.endDate ? task.endDate.toString() : ""); +// setDueTime(""); +// } else { +// setTaskType("OPTIONAL"); +// setRecurrence("Does Not Repeat"); +// setMarillacBucks(undefined); +// setSelectedDays([]); +// setTitle(""); +// setDueDate(""); +// setDueTime(""); +// } +// }, [task, isOpen]); - const handleSubmit = () => { - setSubmitPressed(true); - if (title === "") { - console.log("Title is required"); - return; - } +// const handleSubmit = () => { +// setSubmitPressed(true); +// if (title === "") { +// console.log("Title is required"); +// return; +// } - // TODO: API call to add task - let recurrenceFrequency = "ONE_TIME"; - if (recurrence === "Repeats") { - if (completedOn === "every") { - recurrenceFrequency = "REPEATS_PER_WEEK_SELECTED"; - } else { - recurrenceFrequency = "REPEATS_PER_WEEK_ONCE"; - } +// // TODO: API call to add task +// let recurrenceFrequency = "ONE_TIME"; +// if (recurrence === "Repeats") { +// if (completedOn === "every") { +// recurrenceFrequency = "REPEATS_PER_WEEK_SELECTED"; +// } else { +// recurrenceFrequency = "REPEATS_PER_WEEK_ONCE"; +// } - if (selectedDays.length === 0) { - console.log("Days are required"); - return; - } - } +// if (selectedDays.length === 0) { +// console.log("Days are required"); +// return; +// } +// } - const taskRequest: TaskRequest = { - type: taskType as TaskTypeEnum, - title, - description: task?.description || "No field in modal", - creditValue: marillacBucks || 0, - endDate: endsOn === "never" ? undefined : new Date(endsOnDate), - recurrenceFrequency: recurrenceFrequency as RecurrenceFrequency, - repeatDays: - recurrenceFrequency === "ONE_TIME" - ? [] - : selectedDays.map( - (day) => - dayIdMap.find((dayMp) => dayMp.short === day) - ?.key as DaysOfWeek, - ), - }; - handleSaveClick(task?.id || "", taskRequest); - setIsOpen(false); - }; +// const taskRequest: TaskRequest = { +// type: taskType as TaskTypeEnum, +// title, +// description: task?.description || "No field in modal", +// creditValue: marillacBucks || 0, +// endDate: endsOn === "never" ? undefined : new Date(endsOnDate), +// recurrenceFrequency: recurrenceFrequency as RecurrenceFrequency, +// repeatDays: +// recurrenceFrequency === "ONE_TIME" +// ? [] +// : selectedDays.map( +// (day) => +// dayIdMap.find((dayMp) => dayMp.short === day) +// ?.key as DaysOfWeek, +// ), +// }; +// handleSaveClick(task?.id || "", taskRequest); +// setIsOpen(false); +// }; - const resetFormState = () => { - setTitle(""); - setLocation(""); - setDueDate(""); - setDueTime(""); - // setIsAllDay(false); - // setRecurrenceFrequency(""); - setMarillacBucks(undefined); +// const resetFormState = () => { +// setTitle(""); +// setLocation(""); +// setDueDate(""); +// setDueTime(""); +// // setIsAllDay(false); +// // setRecurrenceFrequency(""); +// setMarillacBucks(undefined); - setSubmitPressed(false); - }; +// setSubmitPressed(false); +// }; - const handleMoneyInput = () => { - // const inputValue = marillacBucks.replace(/[^0-9.]/g, ""); // Remove non-numeric and non-period characters - // if (inputValue) { - // const numberValue = parseFloat(inputValue).toFixed(2); - // setMarillacBucks(numberValue); - // } - }; +// const handleMoneyInput = () => { +// // const inputValue = marillacBucks.replace(/[^0-9.]/g, ""); // Remove non-numeric and non-period characters +// // if (inputValue) { +// // const numberValue = parseFloat(inputValue).toFixed(2); +// // setMarillacBucks(numberValue); +// // } +// }; - // delete task api stuff - const handleDelete = () => { - if (handleDeleteTask && task) { - handleDeleteTask(task.id); - setIsOpen(false); - resetFormState(); - } - }; +// // delete task api stuff +// const handleDelete = () => { +// if (handleDeleteTask && task) { +// handleDeleteTask(task.id); +// setIsOpen(false); +// resetFormState(); +// } +// }; - const selectDay = (day: string) => { - if (selectedDays.includes(day)) { - setSelectedDays(selectedDays.filter((d) => d !== day)); - } else { - setSelectedDays([...selectedDays, day]); - } - }; +// const selectDay = (day: string) => { +// if (selectedDays.includes(day)) { +// setSelectedDays(selectedDays.filter((d) => d !== day)); +// } else { +// setSelectedDays([...selectedDays, day]); +// } +// }; - return ( - - - - - Task Type - +// return ( +// +// +// +// +// Task Type +// - - +// +// - setTitle(e.target.value)} - submitPressed={submitPressed} - /> - - - Recurrence - +// setTitle(e.target.value)} +// submitPressed={submitPressed} +// /> +// +// +// Recurrence +// - - - {recurrence === "Repeats" && ( - <> - -
Select Days:
+// +//
+// {recurrence === "Repeats" && ( +// <> +// +//
Select Days:
- {days.map((day, i) => ( - - ))} -
+// {days.map((day, i) => ( +// +// ))} +//
- -
Completed On
+// +//
Completed On
- setCompletedOn(value)} - colorScheme="purple" - style={{ flexDirection: "column", display: "flex" }} - > - Every Selected Day - One of the selected days - -
+// setCompletedOn(value)} +// colorScheme="purple" +// style={{ flexDirection: "column", display: "flex" }} +// > +// Every Selected Day +// One of the selected days +// +//
- -
Ends On
- setEndsOn(value)} - colorScheme="purple" - style={{ flexDirection: "column", display: "flex" }} - > - - Never - - - - On - { - setEndsOnDate(e.target.value); - }} - submitPressed={submitPressed} - /> - - - -
- - )} - { - if (e.target.value) { - setMarillacBucks(parseInt(e.target.value, 10)); - } else if (e.target.value === "") { - setMarillacBucks(undefined); - } - }} - onBlur={handleMoneyInput} - submitPressed={submitPressed} - leftElement="$" - /> - - - - - -
- ); -}; +// +//
Ends On
+// setEndsOn(value)} +// colorScheme="purple" +// style={{ flexDirection: "column", display: "flex" }} +// > +// +// Never +// +// +// +// On +// { +// setEndsOnDate(e.target.value); +// }} +// submitPressed={submitPressed} +// /> +// +// +// +//
+// +// )} +// { +// if (e.target.value) { +// setMarillacBucks(parseInt(e.target.value, 10)); +// } else if (e.target.value === "") { +// setMarillacBucks(undefined); +// } +// }} +// onBlur={handleMoneyInput} +// submitPressed={submitPressed} +// leftElement="$" +// /> +// +// +// +// +// +// +// ); +// }; -export default TaskModal; +// export default TaskModal; diff --git a/frontend/src/components/pages/tasks/TasksPage.tsx b/frontend/src/components/pages/tasks/TasksPage.tsx index cd80dced..45e49925 100644 --- a/frontend/src/components/pages/tasks/TasksPage.tsx +++ b/frontend/src/components/pages/tasks/TasksPage.tsx @@ -1,367 +1,368 @@ -import React, { useEffect, useState } from "react"; -import { - Flex, - Input, - Button, - InputGroup, - InputLeftElement, - Tabs, - TabList, - Tab, - Icon, -} from "@chakra-ui/react"; -import { Add, Search } from "@mui/icons-material"; -import { useMutation, useQuery } from "@apollo/client"; - -import { - CREATE_TASK, - UPDATE_TASK, - DELETE_TASK, - ASSIGN_TASK, - CHANGE_TASK_STATUS, -} from "../../../APIClients/Mutations/TaskMutations"; - -import { - GET_TASK_BY_ID, - GET_TASKS_BY_TYPE, - GET_TASKS_BY_ASSIGNEE_ID, - GET_TASKS_BY_ASSIGNER_ID, - GET_TASKS_BY_START_DATE, - GET_TASKS_BY_STATUS, -} from "../../../APIClients/Queries/TaskQueries"; - -import { - Status, - RecurrenceFrequency, - DaysOfWeek, - TaskTypeEnum, - TaskResponse, - TaskRequest, - TaskAssignedRequest, - TaskAssignedResponse, -} from "../../../APIClients/Types/TaskType"; -import TaskModal from "./TaskModal"; -import { - TaskType, - Task, - CustomTask, - ChoreTask, -} from "../../../types/TaskTypes"; -import CommonTable, { - ColumnInfoTypes, - TableData, -} from "../../common/CommonTable"; -import { - tasksColumnTypes, - customTasksColumnTypes, - choreTasksColumnTypes, -} from "./columnKeys"; -import { - requiredTasksMockData, - optionalTasksMockData, - customTasksMockData, - choreTasksMockData, -} from "../../../mocks/tasks"; // TODO: Replace mock data - -const TasksPage = (): React.ReactElement => { - const [requiredTasks, setRequiredTasks] = useState([]); - const [optionalTasks, setOptionalTasks] = useState([]); - const [customTasks, setCustomTasks] = useState([]); - const [choreTasks, setChoreTasks] = useState([]); - const [isModalOpen, setIsModalOpen] = useState(false); - const [tabIndex, setTabIndex] = useState(0); - - const [taskType, setTaskType] = useState("REQUIRED"); - const [taskData, setTaskData] = useState([]); - const [storedTaskData, setStoredTaskData] = useState([]); - const [taskDataColumns, setTaskDataColumns] = useState([]); - - const [taskFilter, setTaskFilter] = useState(""); - const [modalTask, setModalTask] = useState(null); - - const { loading, error, data, refetch } = useQuery(GET_TASKS_BY_TYPE, { - variables: { type: taskType === "CUSTOM" ? "OPTIONAL" : taskType }, - }); - - const [createTask] = useMutation<{ createTask: TaskResponse }>(CREATE_TASK); - - const [updateTask] = useMutation<{ - taskID: number; - taskId: TaskResponse; - }>(UPDATE_TASK); - - const [deleteTask] = useMutation<{ taskID: number }>(DELETE_TASK); - - // const [assignTask] = useMutation<{ assignTask: TaskAssignedResponse }>( - // ASSIGN_TASK, - // ); - - // const [changeTaskStatus] = useMutation<{ - // taskAssignedID: number; - // status: Status; - // }>(CHANGE_TASK_STATUS); - - // const { - // loading: taskByIdLoading, - // error: taskByIdError, - // data: taskByIdData, - // } = useQuery<{ taskId: number }>(GET_TASK_BY_ID, { - // variables: { taskId: 1 }, - // }); - // const taskById = React.useMemo(() => { - // return taskByIdData; - // }, [taskByIdData]); - - // const { - // loading: tasksbyTypeLoading, - // error: tasksByTypeError, - // data: tassByTypeData, - // } = useQuery<{ type: TaskTypeEnum }>(GET_TASKS_BY_TYPE, { - // variables: {type: TaskTypeEnum.REQUIRED}, - // }); - // const tasksByType = React.useMemo(() => { - // return tassByTypeData; - // }, [tassByTypeData]); - - // const { - // loading: tasksByAssigneeIdLoading, - // error: tasksByAssigneeIdError, - // data: tasksByAssigneeIdData, - // } = useQuery<{ assigneeId: number }>(GET_TASKS_BY_ASSIGNEE_ID, { - // variables: { assigneeId: 4 }, - // }); - // const tasksByAssigneeId = React.useMemo(() => { - // return tasksByAssigneeIdData; - // }, [tasksByAssigneeIdData]); - - // const { - // loading: tasksByAssignerIdLoading, - // error: tasksByAssignerIdError, - // data: tasksByAssignerIdData, - // } = useQuery<{ assignerId: number }>(GET_TASKS_BY_ASSIGNER_ID, { - // variables: { assignerId: 6 }, - // }); - // const tasksByAssignerId = React.useMemo(() => { - // return tasksByAssignerIdData; - // }, [tasksByAssignerIdData]); - - // const startDateVar = new Date("2011-10-05T14:48:00.000Z"); - // const { - // loading: tasksByStartDateLoading, - // error: tasksByStartDateError, - // data: taskByStartDateData, - // } = useQuery<{ startDate: Date }>(GET_TASKS_BY_START_DATE, { - // variables: { startDate: startDateVar }, - // }); - // const tasksByStartDate = React.useMemo(() => { - // return taskByStartDateData; - // }, [taskByStartDateData]); - - // const { - // loading: tasksByStatusLoading, - // error: tasksByStatusError, - // data: tasksByStatusData, - // } = useQuery<{ status: Status }>(GET_TASKS_BY_STATUS, { - // variables: { status: Status.ASSIGNED }, - // }); - // const tasksByStatus = React.useMemo(() => { - // return tasksByStatusData; - // }, [tasksByStatusData]); - - // const printTasks = () => { - // console.log(taskById); - // console.log(tasksByType); - // console.log(tasksByAssigneeId); - // console.log(tasksByAssignerId); - // console.log(tasksByStartDate); - // console.log(tasksByStatus); - // }; - - const handleAddTask = async (task: TaskRequest) => { - try { - await createTask({ variables: { task } }); - await refetch(); - } catch (e) { - console.log(e); - } - }; - - const handleUpdateTask = async (taskId: string, task: TaskRequest) => { - try { - await updateTask({ variables: { taskId: parseInt(taskId, 10), task } }); - } catch (e) { - console.log(e); - } - }; - - const handleSaveClick = async (taskId: string, task: TaskRequest) => { - if (taskId === "") { - await handleAddTask(task); - } else { - await handleUpdateTask(taskId, task); - } - }; - - const handleDeleteTask = async (taskId: string) => { - try { - await deleteTask({ variables: { taskId } }); - await refetch(); - } catch (e) { - console.log(e); - } - }; - - // const handleAssignTask = async () => { - // try { - // const task: TaskAssignedRequest = { - // taskId: 1, - // assigneeId: 4, - // assignerId: 6, - // status: Status.PENDING_APPROVAL, - // startDate: new Date(), - // comments: "asdlkasd", - // }; - - // await assignTask({ variables: { taskAssigned: task } }); - // } catch (e) { - // console.log(e); - // } - // }; - - // const handleChangeTaskStatus = async () => { - // try { - // const taskAssignedId = 1; - // const status = Status.ASSIGNED; - // await changeTaskStatus({ variables: { taskAssignedId, status } }); - // } catch (e) { - // console.log(e); - // } - // }; - - useEffect(() => { - setRequiredTasks(requiredTasks); - setOptionalTasks(optionalTasks); - setCustomTasks(customTasks); - setChoreTasks(choreTasks); - }, []); - - useEffect(() => { - if (tabIndex === 0) { - setTaskType("REQUIRED"); - } else if (tabIndex === 1) { - setTaskType("OPTIONAL"); - } else if (tabIndex === 2) { - setTaskType("CUSTOM"); - } else { - setTaskType("CHORE"); - } - }, [tabIndex]); - - useEffect(() => { - if (taskFilter === "") { - setTaskData(storedTaskData); - } else { - setTaskData( - storedTaskData.filter( - (task) => - typeof task.title === "string" && - task.title.toLowerCase().includes(taskFilter.toLowerCase()), - ), - ); - } - }, [taskFilter, storedTaskData, taskData]); - - useEffect(() => { - if (data) { - if (taskType === "REQUIRED") { - setRequiredTasks(data.getTasksByType); - setTaskDataColumns(tasksColumnTypes); - } else if (taskType === "OPTIONAL") { - setOptionalTasks(data.getTasksByType); - setTaskDataColumns(tasksColumnTypes); - } else if (taskType === "CUSTOM") { - setCustomTasks(data.getTasksByType); - setTaskDataColumns(customTasksColumnTypes); - } else if (taskType === "CHORE") { - setChoreTasks(data.getTasksByType); - setTaskDataColumns(choreTasksColumnTypes); - } - - setStoredTaskData( - data.getTasksByType.map((task: any) => { - return { - ...task, - endDate: new Date(task.endDate).toDateString(), - }; - }), - ); - } - }, [data, taskType]); - - return ( - - setTabIndex(value)} - > - - Required - Optional - Custom - Chores - - - - - - - - - - . - setTaskFilter(e.target.value)} - /> - - - - - {loading || error ? ( -

Loading...

- ) : ( - { - setModalTask(row); - setIsModalOpen(true); - }} - /> - )} - -
-
- ); -}; - -export default TasksPage; +export {}; +// import React, { useEffect, useState } from "react"; +// import { +// Flex, +// Input, +// Button, +// InputGroup, +// InputLeftElement, +// Tabs, +// TabList, +// Tab, +// Icon, +// } from "@chakra-ui/react"; +// import { Add, Search } from "@mui/icons-material"; +// import { useMutation, useQuery } from "@apollo/client"; + +// import { +// CREATE_TASK, +// UPDATE_TASK, +// DELETE_TASK, +// ASSIGN_TASK, +// CHANGE_TASK_STATUS, +// } from "../../../APIClients/Mutations/TaskMutations"; + +// import { +// GET_TASK_BY_ID, +// GET_TASKS_BY_TYPE, +// GET_TASKS_BY_ASSIGNEE_ID, +// GET_TASKS_BY_ASSIGNER_ID, +// GET_TASKS_BY_START_DATE, +// GET_TASKS_BY_STATUS, +// } from "../../../APIClients/Queries/TaskQueries"; + +// import { +// Status, +// RecurrenceFrequency, +// DaysOfWeek, +// TaskTypeEnum, +// TaskResponse, +// TaskRequest, +// TaskAssignedRequest, +// TaskAssignedResponse, +// } from "../../../APIClients/Types/TaskType"; +// import TaskModal from "./TaskModal"; +// import { +// TaskType, +// Task, +// CustomTask, +// ChoreTask, +// } from "../../../types/TaskTypes"; +// import CommonTable, { +// ColumnInfoTypes, +// TableData, +// } from "../../common/CommonTable"; +// import { +// tasksColumnTypes, +// customTasksColumnTypes, +// choreTasksColumnTypes, +// } from "./columnKeys"; +// import { +// requiredTasksMockData, +// optionalTasksMockData, +// customTasksMockData, +// choreTasksMockData, +// } from "../../../mocks/tasks"; // TODO: Replace mock data + +// const TasksPage = (): React.ReactElement => { +// const [requiredTasks, setRequiredTasks] = useState([]); +// const [optionalTasks, setOptionalTasks] = useState([]); +// const [customTasks, setCustomTasks] = useState([]); +// const [choreTasks, setChoreTasks] = useState([]); +// const [isModalOpen, setIsModalOpen] = useState(false); +// const [tabIndex, setTabIndex] = useState(0); + +// const [taskType, setTaskType] = useState("REQUIRED"); +// const [taskData, setTaskData] = useState([]); +// const [storedTaskData, setStoredTaskData] = useState([]); +// const [taskDataColumns, setTaskDataColumns] = useState([]); + +// const [taskFilter, setTaskFilter] = useState(""); +// const [modalTask, setModalTask] = useState(null); + +// const { loading, error, data, refetch } = useQuery(GET_TASKS_BY_TYPE, { +// variables: { type: taskType === "CUSTOM" ? "OPTIONAL" : taskType }, +// }); + +// const [createTask] = useMutation<{ createTask: TaskResponse }>(CREATE_TASK); + +// const [updateTask] = useMutation<{ +// taskID: number; +// taskId: TaskResponse; +// }>(UPDATE_TASK); + +// const [deleteTask] = useMutation<{ taskID: number }>(DELETE_TASK); + +// // const [assignTask] = useMutation<{ assignTask: TaskAssignedResponse }>( +// // ASSIGN_TASK, +// // ); + +// // const [changeTaskStatus] = useMutation<{ +// // taskAssignedID: number; +// // status: Status; +// // }>(CHANGE_TASK_STATUS); + +// // const { +// // loading: taskByIdLoading, +// // error: taskByIdError, +// // data: taskByIdData, +// // } = useQuery<{ taskId: number }>(GET_TASK_BY_ID, { +// // variables: { taskId: 1 }, +// // }); +// // const taskById = React.useMemo(() => { +// // return taskByIdData; +// // }, [taskByIdData]); + +// // const { +// // loading: tasksbyTypeLoading, +// // error: tasksByTypeError, +// // data: tassByTypeData, +// // } = useQuery<{ type: TaskTypeEnum }>(GET_TASKS_BY_TYPE, { +// // variables: {type: TaskTypeEnum.REQUIRED}, +// // }); +// // const tasksByType = React.useMemo(() => { +// // return tassByTypeData; +// // }, [tassByTypeData]); + +// // const { +// // loading: tasksByAssigneeIdLoading, +// // error: tasksByAssigneeIdError, +// // data: tasksByAssigneeIdData, +// // } = useQuery<{ assigneeId: number }>(GET_TASKS_BY_ASSIGNEE_ID, { +// // variables: { assigneeId: 4 }, +// // }); +// // const tasksByAssigneeId = React.useMemo(() => { +// // return tasksByAssigneeIdData; +// // }, [tasksByAssigneeIdData]); + +// // const { +// // loading: tasksByAssignerIdLoading, +// // error: tasksByAssignerIdError, +// // data: tasksByAssignerIdData, +// // } = useQuery<{ assignerId: number }>(GET_TASKS_BY_ASSIGNER_ID, { +// // variables: { assignerId: 6 }, +// // }); +// // const tasksByAssignerId = React.useMemo(() => { +// // return tasksByAssignerIdData; +// // }, [tasksByAssignerIdData]); + +// // const startDateVar = new Date("2011-10-05T14:48:00.000Z"); +// // const { +// // loading: tasksByStartDateLoading, +// // error: tasksByStartDateError, +// // data: taskByStartDateData, +// // } = useQuery<{ startDate: Date }>(GET_TASKS_BY_START_DATE, { +// // variables: { startDate: startDateVar }, +// // }); +// // const tasksByStartDate = React.useMemo(() => { +// // return taskByStartDateData; +// // }, [taskByStartDateData]); + +// // const { +// // loading: tasksByStatusLoading, +// // error: tasksByStatusError, +// // data: tasksByStatusData, +// // } = useQuery<{ status: Status }>(GET_TASKS_BY_STATUS, { +// // variables: { status: Status.ASSIGNED }, +// // }); +// // const tasksByStatus = React.useMemo(() => { +// // return tasksByStatusData; +// // }, [tasksByStatusData]); + +// // const printTasks = () => { +// // console.log(taskById); +// // console.log(tasksByType); +// // console.log(tasksByAssigneeId); +// // console.log(tasksByAssignerId); +// // console.log(tasksByStartDate); +// // console.log(tasksByStatus); +// // }; + +// const handleAddTask = async (task: TaskRequest) => { +// try { +// await createTask({ variables: { task } }); +// await refetch(); +// } catch (e) { +// console.log(e); +// } +// }; + +// const handleUpdateTask = async (taskId: string, task: TaskRequest) => { +// try { +// await updateTask({ variables: { taskId: parseInt(taskId, 10), task } }); +// } catch (e) { +// console.log(e); +// } +// }; + +// const handleSaveClick = async (taskId: string, task: TaskRequest) => { +// if (taskId === "") { +// await handleAddTask(task); +// } else { +// await handleUpdateTask(taskId, task); +// } +// }; + +// const handleDeleteTask = async (taskId: string) => { +// try { +// await deleteTask({ variables: { taskId } }); +// await refetch(); +// } catch (e) { +// console.log(e); +// } +// }; + +// // const handleAssignTask = async () => { +// // try { +// // const task: TaskAssignedRequest = { +// // taskId: 1, +// // assigneeId: 4, +// // assignerId: 6, +// // status: Status.PENDING_APPROVAL, +// // startDate: new Date(), +// // comments: "asdlkasd", +// // }; + +// // await assignTask({ variables: { taskAssigned: task } }); +// // } catch (e) { +// // console.log(e); +// // } +// // }; + +// // const handleChangeTaskStatus = async () => { +// // try { +// // const taskAssignedId = 1; +// // const status = Status.ASSIGNED; +// // await changeTaskStatus({ variables: { taskAssignedId, status } }); +// // } catch (e) { +// // console.log(e); +// // } +// // }; + +// useEffect(() => { +// setRequiredTasks(requiredTasks); +// setOptionalTasks(optionalTasks); +// setCustomTasks(customTasks); +// setChoreTasks(choreTasks); +// }, []); + +// useEffect(() => { +// if (tabIndex === 0) { +// setTaskType("REQUIRED"); +// } else if (tabIndex === 1) { +// setTaskType("OPTIONAL"); +// } else if (tabIndex === 2) { +// setTaskType("CUSTOM"); +// } else { +// setTaskType("CHORE"); +// } +// }, [tabIndex]); + +// useEffect(() => { +// if (taskFilter === "") { +// setTaskData(storedTaskData); +// } else { +// setTaskData( +// storedTaskData.filter( +// (task) => +// typeof task.title === "string" && +// task.title.toLowerCase().includes(taskFilter.toLowerCase()), +// ), +// ); +// } +// }, [taskFilter, storedTaskData, taskData]); + +// useEffect(() => { +// if (data) { +// if (taskType === "REQUIRED") { +// setRequiredTasks(data.getTasksByType); +// setTaskDataColumns(tasksColumnTypes); +// } else if (taskType === "OPTIONAL") { +// setOptionalTasks(data.getTasksByType); +// setTaskDataColumns(tasksColumnTypes); +// } else if (taskType === "CUSTOM") { +// setCustomTasks(data.getTasksByType); +// setTaskDataColumns(customTasksColumnTypes); +// } else if (taskType === "CHORE") { +// setChoreTasks(data.getTasksByType); +// setTaskDataColumns(choreTasksColumnTypes); +// } + +// setStoredTaskData( +// data.getTasksByType.map((task: any) => { +// return { +// ...task, +// endDate: new Date(task.endDate).toDateString(), +// }; +// }), +// ); +// } +// }, [data, taskType]); + +// return ( +// +// setTabIndex(value)} +// > +// +// Required +// Optional +// Custom +// Chores +// +// + +// +// +// +// +// +// +// . +// setTaskFilter(e.target.value)} +// /> +// +// +// + +// {loading || error ? ( +//

Loading...

+// ) : ( +// { +// setModalTask(row); +// setIsModalOpen(true); +// }} +// /> +// )} +// +//
+//
+// ); +// }; + +// export default TasksPage; diff --git a/frontend/src/components/pages/tasks/columnKeys.ts b/frontend/src/components/pages/tasks/columnKeys.ts index dca7c4fc..764d02cf 100644 --- a/frontend/src/components/pages/tasks/columnKeys.ts +++ b/frontend/src/components/pages/tasks/columnKeys.ts @@ -1,54 +1,55 @@ -import { ColumnInfoTypes } from "../../common/CommonTable"; +export {}; +// import { ColumnInfoTypes } from "../../common/CommonTable"; -export const tasksColumnTypes: ColumnInfoTypes[] = [ - { - header: "Task Name", - key: "title", - }, - { - header: "Due Date", - key: "endDate", - }, - { - header: "Marillac Bucks", - key: "creditValue", - }, -]; +// export const tasksColumnTypes: ColumnInfoTypes[] = [ +// { +// header: "Task Name", +// key: "title", +// }, +// { +// header: "Due Date", +// key: "endDate", +// }, +// { +// header: "Marillac Bucks", +// key: "creditValue", +// }, +// ]; -export const customTasksColumnTypes: ColumnInfoTypes[] = [ - { - header: "Task Name", - key: "title", - }, - { - header: "Room #", - key: "roomNumber", - }, - { - header: "Due Date", - key: "endDate", - }, - { - header: "Marillac Bucks", - key: "creditValue", - }, -]; +// export const customTasksColumnTypes: ColumnInfoTypes[] = [ +// { +// header: "Task Name", +// key: "title", +// }, +// { +// header: "Room #", +// key: "roomNumber", +// }, +// { +// header: "Due Date", +// key: "endDate", +// }, +// { +// header: "Marillac Bucks", +// key: "creditValue", +// }, +// ]; -export const choreTasksColumnTypes: ColumnInfoTypes[] = [ - { - header: "Chore Name", - key: "title", - }, - { - header: "Location", - key: "location", - }, - { - header: "Due Date", - key: "endDate", - }, - { - header: "Marillac Bucks", - key: "creditValue", - }, -]; +// export const choreTasksColumnTypes: ColumnInfoTypes[] = [ +// { +// header: "Chore Name", +// key: "title", +// }, +// { +// header: "Location", +// key: "location", +// }, +// { +// header: "Due Date", +// key: "endDate", +// }, +// { +// header: "Marillac Bucks", +// key: "creditValue", +// }, +// ]; diff --git a/frontend/src/constants/AuthConstants.ts b/frontend/src/constants/AuthConstants.ts deleted file mode 100644 index 610ad601..00000000 --- a/frontend/src/constants/AuthConstants.ts +++ /dev/null @@ -1,3 +0,0 @@ -const AUTHENTICATED_USER_KEY = `${window.location.hostname}:AUTHENTICATED_USER`; - -export default AUTHENTICATED_USER_KEY; diff --git a/frontend/src/constants/Routes.ts b/frontend/src/constants/Routes.ts index 2261350f..26750a3f 100644 --- a/frontend/src/constants/Routes.ts +++ b/frontend/src/constants/Routes.ts @@ -1,17 +1,11 @@ -export const HOME_PAGE = "/"; - -export const ANNOUNCEMENTS_PAGE = "/announcements"; - export const LOGIN_PAGE = "/login"; -export const SIGNUP_PAGE = "/signup"; - -export const RESET_PASSWORD_PAGE = "/reset-password"; - -export const TASKS_PAGE = "/tasks"; +export const HOME_PAGE = "/"; export const SCHEDULE_PAGE = "/schedule"; +export const ANNOUNCEMENTS_PAGE = "/announcements"; + export const PARTICIPANTS_PAGE = "/participants"; -export const INSIGHTS_PAGE = "/insights"; +export const TASKS_PAGE = "/tasks"; diff --git a/frontend/src/contexts/AuthContext.ts b/frontend/src/contexts/AuthContext.ts deleted file mode 100644 index 174f6813..00000000 --- a/frontend/src/contexts/AuthContext.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { createContext } from "react"; -import { AuthenticatedUser } from "../types/AuthTypes"; - -type AuthContextType = { - authenticatedUser: AuthenticatedUser; - setAuthenticatedUser: (_authenticatedUser: AuthenticatedUser) => void; -}; - -const AuthContext = createContext({ - authenticatedUser: null, - /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ - setAuthenticatedUser: (_authenticatedUser: AuthenticatedUser): void => {}, -}); - -export default AuthContext; diff --git a/frontend/src/contexts/SampleContext.ts b/frontend/src/contexts/SampleContext.ts deleted file mode 100644 index fca643ba..00000000 --- a/frontend/src/contexts/SampleContext.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { createContext } from "react"; -import { SampleContextType } from "../types/SampleContextTypes"; - -export const DEFAULT_SAMPLE_CONTEXT = { - teamName: "Internal Tools", - numTerms: 3, - members: ["Sherry", "Alex", "Carelynn", "Bruce", "Richard", "Raveen", "Xin"], - isActive: true, -}; - -const SampleContext = createContext(DEFAULT_SAMPLE_CONTEXT); - -export default SampleContext; diff --git a/frontend/src/contexts/SampleContextDispatcherContext.ts b/frontend/src/contexts/SampleContextDispatcherContext.ts deleted file mode 100644 index 94d86cb4..00000000 --- a/frontend/src/contexts/SampleContextDispatcherContext.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createContext } from "react"; -import { SampleContextAction } from "../types/SampleContextTypes"; - -const SampleContextDispatcherContext = createContext< - React.Dispatch ->(() => {}); - -export default SampleContextDispatcherContext; diff --git a/frontend/src/gql/mutations.ts b/frontend/src/gql/mutations.ts new file mode 100644 index 00000000..e865bdd7 --- /dev/null +++ b/frontend/src/gql/mutations.ts @@ -0,0 +1,17 @@ +import { gql } from "@apollo/client"; + +export const CREATE_PARTICIPANT = gql` + mutation createParticipant( + $participantId: String + $roomNumber: Int + $arrival: String + $password: String + ) { + createParticipant( + participantId: $participantId + roomNumber: $roomNumber + arrival: $arrival + password: $password + ) + } +`; diff --git a/frontend/src/gql/queries.ts b/frontend/src/gql/queries.ts new file mode 100644 index 00000000..cc68b994 --- /dev/null +++ b/frontend/src/gql/queries.ts @@ -0,0 +1,33 @@ +import { gql } from "@apollo/client"; + +export const GET_AVAILABLE_ROOMS = gql` + query getAvailableRooms { + getAvailableRooms + } +`; + +export const GET_ALL_PARTICIPANTS = gql` + query getAllParticipants { + getAllParticipants { + participantId + roomNumber + arrival + departure + password + credit + } + } +`; + +export const GET_PARTICIPANT_BY_ID = gql` + query getParticipantById($participantId: String) { + getParticipantById(participantId: $participantId) { + participantId + roomNumber + arrival + departure + password + credit + } + } +`; diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index e6582272..741f72b5 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,90 +1,15 @@ -import axios from "axios"; import React from "react"; import ReactDOM from "react-dom/client"; -import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client"; -import { createUploadLink } from "apollo-upload-client"; -import { setContext } from "@apollo/client/link/context"; -import { jwtDecode } from "jwt-decode"; - -import AUTHENTICATED_USER_KEY from "./constants/AuthConstants"; -import { AuthenticatedUser, DecodedJWT } from "./types/AuthTypes"; -import { - getLocalStorageObjProperty, - setLocalStorageObjProperty, -} from "./utils/LocalStorageUtils"; import "./index.css"; import App from "./App"; -import reportWebVitals from "./reportWebVitals"; - -const REFRESH_MUTATION = ` - mutation Index_Refresh { - refresh - } -`; - -const link = createUploadLink({ - uri: `${process.env.REACT_APP_BACKEND_URL}/graphql`, - credentials: "include", -}); - -const authLink = setContext(async (_, { headers }) => { - // get the authentication token from local storage if it exists - let token: string | null = getLocalStorageObjProperty< - NonNullable, - string - >(AUTHENTICATED_USER_KEY, "accessToken"); - if (token) { - const decodedToken = jwtDecode(token) as DecodedJWT; - - // refresh if decodedToken has expired - if ( - decodedToken && - (typeof decodedToken === "string" || - decodedToken.exp <= Math.round(new Date().getTime() / 1000)) - ) { - const { data } = await axios.post( - `${process.env.REACT_APP_BACKEND_URL}/graphql`, - { query: REFRESH_MUTATION }, - { withCredentials: true }, - ); - - const accessToken: string = data.data.refresh; - setLocalStorageObjProperty( - AUTHENTICATED_USER_KEY, - "accessToken", - accessToken, - ); - token = accessToken; - } - } - // return the headers to the context so httpLink can read them - return { - headers: { - ...headers, - authorization: token ? `Bearer ${token}` : "", - }, - }; -}); - -const apolloClient = new ApolloClient({ - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - link: authLink.concat(link as any), - cache: new InMemoryCache(), -}); const root = ReactDOM.createRoot( document.getElementById("root") as HTMLElement, ); + root.render( - - - + , ); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/frontend/src/mocks/notifications.ts b/frontend/src/mocks/notifications.ts deleted file mode 100644 index cab5bd9e..00000000 --- a/frontend/src/mocks/notifications.ts +++ /dev/null @@ -1,31 +0,0 @@ -export const announcementsMockData = { - "1,2,3": [ - { - author: "John Doe", - message: - "Reminding you all to please respect quiet hours from 10pm to 6am. We have had a few complaints about noise levels during these hours. Thank you for your cooperation!", - createdAt: "2024-01-01T00:00:00Z", - }, - { - author: "John Doe", - message: - "Reminder to please clean up after yourself in the kitchen. We have had a few complaints about the mess left behind. Thank you for your cooperation!", - createdAt: "2023-08-01T00:00:00Z", - }, - ], - "4": [ - { - author: "John Doe", - message: - "Reminding you about your meeting this Saturday! Please be on time, we will be beginning promptly at 10:30am. If you are unable to attend, please let us know as soon as possible. Have a great rest of the week everyone!", - createdAt: "2023-07-01T00:00:00Z", - }, - { - author: "Jane Doe", - message: "Great job on your tasks this week! Keep up the good work!", - createdAt: "2023-09-01T00:00:00Z", - }, - ], -}; - -export default {}; diff --git a/frontend/src/mocks/residents.ts b/frontend/src/mocks/residents.ts deleted file mode 100644 index 0149b7df..00000000 --- a/frontend/src/mocks/residents.ts +++ /dev/null @@ -1,144 +0,0 @@ -export const residentsMockData = [ - { - roomNumber: 1, - departureDate: "", - residentId: 12345, - arrivalDate: "2023-06-15", - password: "marillac-place-resident-234", - }, - { - roomNumber: 2, - departureDate: "", - arrivalDate: "2023-07-20", - residentId: 67890, - password: "1234marillac5678", - }, - { - roomNumber: 3, - departureDate: "", - arrivalDate: "2023-08-10", - residentId: 23456, - password: "909marillacplace", - }, - { - roomNumber: 4, - departureDate: "", - arrivalDate: "2023-09-05", - residentId: 78901, - password: "mar1llac_plac3", - }, - { - roomNumber: 5, - departureDate: "", - arrivalDate: "2023-10-12", - residentId: 34567, - password: "Marillac2024Place", - }, - { - roomNumber: 6, - departureDate: "", - arrivalDate: "2023-11-18", - residentId: 89012, - password: "MarillacPlaceResident2024", - }, - { - roomNumber: 7, - departureDate: "", - arrivalDate: "2023-12-25", - residentId: 45678, - password: "m@rillacpl@ce123", - }, - { - roomNumber: 8, - departureDate: "", - arrivalDate: "2024-01-02", - residentId: 90123, - password: "M_P_resident_", - }, - { - roomNumber: "", - departureDate: "2024-02-14", - arrivalDate: "2023-06-15", - residentId: 56789, - password: "100marillac", - }, - { - roomNumber: "", - departureDate: "2024-03-20", - arrivalDate: "2023-06-15", - residentId: 12346, - password: "200marillac", - }, - { - roomNumber: "", - departureDate: "2024-04-05", - arrivalDate: "2023-06-15", - residentId: 67891, - password: "300marillac", - }, - { - roomNumber: "", - departureDate: "2024-05-08", - arrivalDate: "2023-06-15", - residentId: 23457, - password: "400marillac", - }, - { - roomNumber: "", - departureDate: "2024-06-15", - arrivalDate: "2023-06-15", - residentId: 78902, - password: "500marillac", - }, - { - roomNumber: "", - departureDate: "2024-07-20", - arrivalDate: "2023-06-15", - residentId: 34568, - password: "600marillac", - }, - { - roomNumber: "", - departureDate: "2024-08-10", - arrivalDate: "2023-06-15", - residentId: 89013, - password: "700marillac", - }, - { - roomNumber: "", - departureDate: "2024-09-25", - arrivalDate: "2023-06-15", - residentId: 45679, - password: "800marillac", - }, - { - roomNumber: "", - departureDate: "2024-10-30", - arrivalDate: "2023-06-15", - residentId: 90124, - password: "900marillac", - }, - { - roomNumber: "", - departureDate: "2024-11-18", - arrivalDate: "2023-06-15", - residentId: 56790, - password: "1000marillac", - }, - { - roomNumber: "", - departureDate: "2024-12-05", - arrivalDate: "2023-06-15", - residentId: 12347, - password: "MARILLAC-PLACE-RESIDENT-1", - }, - { - roomNumber: "", - departureDate: "2024-12-25", - arrivalDate: "2023-06-15", - residentId: 67892, - password: "mpresident0", - }, -]; - -export default {}; diff --git a/frontend/src/mocks/scheduletasks.ts b/frontend/src/mocks/scheduletasks.ts deleted file mode 100644 index 342e73a1..00000000 --- a/frontend/src/mocks/scheduletasks.ts +++ /dev/null @@ -1,169 +0,0 @@ -export const scheduleTasksMockData = [ - { - title: "Weekly Review with CM", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Skills Session with PM", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Housing Plan", - status: "Excused", - time: "All day", - creditValue: "$5.00", - }, -]; - -export const sundayScheduleTasksMockData = [ - { - title: "Weekly Review with CM", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Skills Session with PM", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Housing Plan", - status: "Excused", - time: "All day", - creditValue: "$5.00", - }, -]; - -export const mondayScheduleTasksMockData = [ - { - title: "Weekly Review with CM", - status: "Excused", - time: "All day", - creditValue: "$10.00", - }, - { - title: "Skills Session with PM", - status: "Incomplete", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Housing Plan", - status: "Excused", - time: "All day", - creditValue: "$5.00", - }, -]; - -export const tuesdayScheduleTasksMockData = [ - { - title: "Weekly Review with CM1", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Skills Session with PM", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Housing Plan", - status: "Excused", - time: "All day", - creditValue: "$5.00", - }, -]; - -export const wednesdayScheduleTasksMockData = [ - { - title: "Weekly Review with CM2", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Skills Session with PM", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Housing Plan", - status: "Excused", - time: "All day", - creditValue: "$5.00", - }, -]; - -export const thursdayScheduleTasksMockData = [ - { - title: "Weekly Review with CM3", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Skills Session with PM", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Housing Plan", - status: "Excused", - time: "All day", - creditValue: "$5.00", - }, -]; - -export const fridayScheduleTasksMockData = [ - { - title: "Weekly Review with CM4", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Skills Session with PM", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Housing Plan", - status: "Excused", - time: "All day", - creditValue: "$5.00", - }, -]; - -export const saturdayScheduleTasksMockData = [ - { - title: "Weekly Review with CM5", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Skills Session with PM", - status: "Completed", - time: "All day", - creditValue: "$5.00", - }, - { - title: "Housing Plan", - status: "Excused", - time: "All day", - creditValue: "$5.00", - }, -]; - -export default {}; diff --git a/frontend/src/mocks/tasks.ts b/frontend/src/mocks/tasks.ts deleted file mode 100644 index a17ad2b2..00000000 --- a/frontend/src/mocks/tasks.ts +++ /dev/null @@ -1,304 +0,0 @@ -import { RecurrenceFrequency } from "../APIClients/Types/TaskType"; - -export const requiredTasksMockData = [ - { - id: "3", - type: "REQUIRED", - title: "Weekly Review with CM", - dueDate: "Every Friday", - creditValue: 5, - locationId: 1234, - recurrenceFrequency: "REPEATS_PER_WEEK_SELECTED", - specificDay: "", - repeatDays: ["MONDAY", "TUESDAY", "WEDNESDAY"], - }, - { - id: "34989383", - type: "REQUIRED", - title: "Skills Session with PM", - dueDate: "Every Friday", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "fhoudsahfo", - type: "REQUIRED", - title: "Housing Plan", - dueDate: "Every Friday", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, -]; - -export const optionalTasksMockData = [ - { - id: "xvzcpivpcxz", - type: "OPTIONAL", - title: "Weekly House Meeting", - dueDate: "Every Monday at 12:00pm", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "xcovzjxi", - type: "OPTIONAL", - title: "Curfew", - dueDate: "Everyday at 10:00pm", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "voixzczc", - type: "OPTIONAL", - title: "Bedroom Inspection", - dueDate: "Everyday at 10:00pm", - creditValue: 2, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "43rt43rew", - type: "OPTIONAL", - title: "Speaker's Corner", - dueDate: "Everyday at 10:00pm", - creditValue: 10, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "cxzvsd", - type: "OPTIONAL", - title: "Counselling", - dueDate: "Every Friday", - creditValue: 10, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "vxz,cxvcxz", - type: "OPTIONAL", - title: "Community Program/School", - dueDate: "Everyday at 2:00pm", - creditValue: 10, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, -]; - -export const customTasksMockData = [ - { - id: "oipuvxuczp", - type: "CUSTOM", - title: "Room 1's custom task", - roomNumber: 1, - dueDate: "Every Monday at 10:00am", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "jijiljvcxz", - type: "CUSTOM", - title: "Room 2's custom task", - roomNumber: 2, - dueDate: "Every Tuesday at 11:00am", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "asdasldasda", - type: "CUSTOM", - title: "Room 3's custom task", - roomNumber: 3, - dueDate: "Every Wednesday at 12:00pm", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "xzcvcxzvxczvcx", - type: "CUSTOM", - title: "Room 4's custom task", - roomNumber: 4, - dueDate: "Every Thursday at 1:00pm", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "qweoqweiuwoqe", - type: "CUSTOM", - title: "Room 5's custom task", - roomNumber: 5, - dueDate: "Every Friday at 2:00pm", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "sdffjdsafjldsaflsad", - type: "CUSTOM", - title: "Room 6's custom task", - roomNumber: 6, - dueDate: "Every Saturday at 3:00pm", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "31491331r1", - type: "CUSTOM", - title: "Room 7's custom task", - roomNumber: 7, - dueDate: "Every Sunday at 4:00pm", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "safodsafhusda", - type: "CUSTOM", - title: "Room 8's custom task", - roomNumber: 8, - dueDate: "Everyday at 5:00pm", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "Sfhosadfuhdsaf", - type: "CUSTOM", - title: "Room 9's custom task", - roomNumber: 9, - dueDate: "Everyday at 6:00pm", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "asdfosaudfhsdaofuhsdaf", - type: "CUSTOM", - title: "Room 10's custom task", - roomNumber: 10, - dueDate: "Everyday at 7:00pm", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, -]; - -export const choreTasksMockData = [ - { - id: "aosdfasdfsda", - type: "CHORE", - title: "Wash the dishes", - location: "Kitchen", - dueDate: "Everyday at 11:00am", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "ofhosduafsda", - type: "CHORE", - title: "Clean the bathroom", - location: "Bathroom", - dueDate: "Everyday at 11:00am", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "avsuhoudvs", - type: "CHORE", - title: "Mop the floors", - location: "Living Room", - dueDate: "Everyday at 11:00am", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "sdfdsaf", - type: "CHORE", - title: "Take out the trash", - location: "Kitchen", - dueDate: "Everyday at 11:00am", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "asofusad", - type: "CHORE", - title: "Vacuum the carpets", - location: "Living Room", - dueDate: "Everyday at 11:00am", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, - { - id: "1892419413", - type: "CHORE", - title: "Dust the furniture", - location: "Living Room", - dueDate: "Everyday at 11:00am", - creditValue: 5, - locationId: 1, - recurrenceFrequency: "ONE_TIME", - specificDay: "MONDAY", - repeatDays: [], - }, -]; diff --git a/frontend/src/reducers/SampleContextReducer.ts b/frontend/src/reducers/SampleContextReducer.ts deleted file mode 100644 index 9876c8ae..00000000 --- a/frontend/src/reducers/SampleContextReducer.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - SampleContextAction, - SampleContextType, -} from "../types/SampleContextTypes"; - -// Using tools such as immer might help with this process. -export default function sampleContextReducer( - state: SampleContextType, - action: SampleContextAction, -): SampleContextType { - switch (action.type) { - case "EDIT_NAME": - return { - ...state, - teamName: action.value, - }; - case "EDIT_NUM_TERMS": - return { - ...state, - numTerms: action.value, - }; - case "EDIT_MEMBERS": - return { - ...state, - members: action.value, - }; - case "EDIT_IS_ACTIVE": - return { - ...state, - isActive: action.value, - }; - default: - return state; - } -} diff --git a/frontend/src/reportWebVitals.ts b/frontend/src/reportWebVitals.ts deleted file mode 100644 index 41784a36..00000000 --- a/frontend/src/reportWebVitals.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ReportHandler } from "web-vitals"; - -const reportWebVitals = (onPerfEntry?: ReportHandler): void => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import("web-vitals").then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/frontend/src/types/AuthTypes.ts b/frontend/src/types/AuthTypes.ts deleted file mode 100644 index f39e32ad..00000000 --- a/frontend/src/types/AuthTypes.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { UserType } from "./UserTypes"; - -export type AuthenticatedUser = { - id: string; - type: UserType; - email: string; - firstName: string; - lastName: string; - accessToken: string; -} | null; - -export type DecodedJWT = - | string - | null - | { [key: string]: unknown; exp: number }; diff --git a/frontend/src/types/NotificationTypes.ts b/frontend/src/types/NotificationTypes.ts deleted file mode 100644 index c55955c9..00000000 --- a/frontend/src/types/NotificationTypes.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface Announcement { - author: string; - room: string; - message: string; - createdAt: string; -} - -export interface GroupAnnouncements { - [key: string]: Announcement[]; -} diff --git a/frontend/src/types/RoomTypes.ts b/frontend/src/types/RoomTypes.ts deleted file mode 100644 index 37c455b1..00000000 --- a/frontend/src/types/RoomTypes.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface Room { - author: string; - message: string; - createdAt: string; -} diff --git a/frontend/src/types/SampleContextTypes.ts b/frontend/src/types/SampleContextTypes.ts deleted file mode 100644 index f2773644..00000000 --- a/frontend/src/types/SampleContextTypes.ts +++ /dev/null @@ -1,24 +0,0 @@ -export type SampleContextType = { - teamName: string; - numTerms: number; - members: string[]; - isActive: boolean; -}; - -export type SampleContextAction = - | { - type: "EDIT_NAME"; - value: string; - } - | { - type: "EDIT_NUM_TERMS"; - value: number; - } - | { - type: "EDIT_MEMBERS"; - value: string[]; - } - | { - type: "EDIT_IS_ACTIVE"; - value: boolean; - }; diff --git a/frontend/src/types/ScheduleTypes.ts b/frontend/src/types/ScheduleTypes.ts deleted file mode 100644 index d701456c..00000000 --- a/frontend/src/types/ScheduleTypes.ts +++ /dev/null @@ -1,15 +0,0 @@ -export type ScheduleType = "LIST" | "CALENDAR"; - -export interface Task { - title: string; - description: string; - creditValue: number; -} - -export interface CustomTask extends Task { - room: number; -} - -export interface ChoreTask extends Task { - location: string; -} diff --git a/frontend/src/types/TaskTypes.ts b/frontend/src/types/TaskTypes.ts deleted file mode 100644 index 527415f2..00000000 --- a/frontend/src/types/TaskTypes.ts +++ /dev/null @@ -1,34 +0,0 @@ -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 interface Task { - id: string; - type: TaskType; - title: string; - description: string; - creditValue: number; - locationId: number; - endDate?: Date; - recurrenceFrequency: RecurrenceFrequency; - specificDay?: DaysOfWeek; - repeatDays: DaysOfWeek[]; -} - -export interface CustomTask extends Task { - room: number; -} - -export interface ChoreTask extends Task { - location: string; -} diff --git a/frontend/src/types/UserTypes.ts b/frontend/src/types/UserTypes.ts deleted file mode 100644 index fb703b4a..00000000 --- a/frontend/src/types/UserTypes.ts +++ /dev/null @@ -1,25 +0,0 @@ -export type UserType = "STAFF" | "RESIDENT"; - -export interface User { - email: string; - phoneNumber: string | null; - firstName: string; - lastName: string; - displayName: string | null; - profilePictureURL: string | null; -} - -export interface Resident extends User { - userId: number; - residentId: number; - birthDate: Date; - roomNumber: number; - credits: number; - dateJoined: Date; - dateLeft: Date | null; - notes: string | null; -} - -export interface Staff extends User { - isAdmin: boolean; -} diff --git a/frontend/src/utils/CSVUtils.ts b/frontend/src/utils/CSVUtils.ts deleted file mode 100644 index a647b0c1..00000000 --- a/frontend/src/utils/CSVUtils.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { parseAsync, transforms } from "json2csv"; -import { Readable, TransformOptions } from "stream"; - -type GenerateCSVParams = { - data: Readonly | ReadonlyArray | Readable; - fields?: string[]; - transformFunction?: (item: T) => Record; - flattenObjects?: boolean; - flattenArrays?: boolean; - pathsToUnwind?: string[]; - opts?: json2csv.Options; - transformOpts?: TransformOptions; -}; - -/** - * Generate a CSV from a JSON array/object or readable input stream. - * fields, transformFunction, flattenObjects, flattenArrays, and pathsToUnwind belong to json2csv.options but - * they are also provided as parameters here for convenience. - * For examples using each of these params, see the CSVUtils.test.ts file. - * @param data JSON array/object or stream to convert to a CSV string - * @param fields columns to include in the csv - * @param transformFunction function to transform fields of the object before converting to csv - * @param flattenObjects indicates whether each property of object fields should be split into a different column - * @param flattenArrays indicates whether each element of array fields should be split into a different column - * @param pathsToUnwind array fields that should be split into different rows - * @param opts options from json2csv to override or add additional options (https://mircozeiss.com/json2csv/#available-options) - * @param transformOpts transform options from stream module (https://nodejs.org/api/stream.html#stream_new_stream_transform_options) - * @returns CSV string - * @throws Error if JSON is not parsed properly - */ -/* eslint-disable-next-line import/prefer-default-export */ -export const generateCSV = async ({ - data, - fields, - transformFunction, - flattenObjects = false, - flattenArrays = false, - pathsToUnwind, - opts, - transformOpts, -}: GenerateCSVParams): Promise => { - const transformations = [ - transforms.flatten({ - objects: flattenObjects, - arrays: flattenArrays, - }), - ]; - if (transformFunction) { - transformations.push(transformFunction); - } - if (pathsToUnwind) { - transformations.push(transforms.unwind({ paths: pathsToUnwind })); - } - - const options = { - fields, - transforms: transformations, - ...opts, - }; - return parseAsync(data, options, transformOpts); -}; - -/** - * Downloads a CSV file. - * References: https://github.com/mui-org/material-ui-x/blob/fa346f0fbe3d9b9eea9bb403fe4675f544d6abf9/packages/grid/_modules_/grid/utils/exportAs.ts - * @param data CSV string - * @param fileName name of the CSV file - */ -export const downloadCSV = (data: string, fileName: string): void => { - const byteOrderMark = "\uFEFF"; - const csvContent = byteOrderMark + data; - const blob = new Blob([csvContent], { - type: "text/csv, charset=UTF-8", - }); - const url = URL.createObjectURL(blob); - - const a = document.createElement("a"); - a.href = url; - a.download = fileName; - a.click(); - setTimeout(() => { - URL.revokeObjectURL(url); - }); -}; diff --git a/frontend/src/utils/FileUtils.ts b/frontend/src/utils/FileUtils.ts deleted file mode 100644 index efc08e43..00000000 --- a/frontend/src/utils/FileUtils.ts +++ /dev/null @@ -1,9 +0,0 @@ -// eslint-disable-next-line import/prefer-default-export -export const downloadFile = (data: string, fileName: string): void => { - const a = document.createElement("a"); - a.href = data; - a.download = fileName; - a.target = "_blank"; - a.click(); - a.parentNode?.removeChild(a); -}; diff --git a/frontend/src/utils/LocalStorageUtils.ts b/frontend/src/utils/LocalStorageUtils.ts deleted file mode 100644 index 60a13bf0..00000000 --- a/frontend/src/utils/LocalStorageUtils.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Get a string value from localStorage as an object -export const getLocalStorageObj = (localStorageKey: string): O | null => { - const stringifiedObj = localStorage.getItem(localStorageKey); - let object = null; - - if (stringifiedObj) { - try { - object = JSON.parse(stringifiedObj); - } catch (error) { - object = null; - } - } - - return object; -}; - -// Get a property of an object value from localStorage -export const getLocalStorageObjProperty = , P>( - localStorageKey: string, - property: string, -): P | null => { - const object = getLocalStorageObj(localStorageKey); - if (!object) return null; - - return object[property]; -}; - -// Set a property of an object value in localStorage -export const setLocalStorageObjProperty = >( - localStorageKey: string, - property: string, - value: string, -): void => { - const object: Record | null = - getLocalStorageObj(localStorageKey); - - if (!object) return; - - object[property] = value; - localStorage.setItem(localStorageKey, JSON.stringify(object)); -}; diff --git a/frontend/src/utils/StringUtils.ts b/frontend/src/utils/StringUtils.ts deleted file mode 100644 index 62e4fba6..00000000 --- a/frontend/src/utils/StringUtils.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const truncateMessage = (message: string, maxLength: number) => { - if (message.length <= maxLength) { - return message; - } - return `${message.substring(0, maxLength)}...`; -}; - -export default {}; diff --git a/frontend/src/utils/__tests__/CSVUtils.test.ts b/frontend/src/utils/__tests__/CSVUtils.test.ts deleted file mode 100644 index 24878055..00000000 --- a/frontend/src/utils/__tests__/CSVUtils.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -import * as CSVUtils from "../CSVUtils"; - -type Pet = { - name: string; - type: string; -}; - -type Home = { - number: number; - street: string; -}; - -type Person = { - age: number; - name: string; - pets: Pet[]; - home?: Home; -}; - -const testData: Person[] = [ - { - name: "Person1", - age: 20, - pets: [ - { name: "Beans", type: "Cat" }, - { name: "Spot", type: "Dog" }, - ], - }, - { - name: "Person2", - age: 25, - pets: [{ name: "Splash", type: "Fish" }], - }, -]; - -describe("CSVUtils", () => { - it("generateCSV should include all fields, with nested objects/arrays, if no options", async () => { - const result = await CSVUtils.generateCSV({ data: testData }); - expect(result).toEqual( - `"name","age","pets"\n` + - `"Person1",20,"[{""name"":""Beans"",""type"":""Cat""},{""name"":""Spot"",""type"":""Dog""}]"\n` + - `"Person2",25,"[{""name"":""Splash"",""type"":""Fish""}]"`, - ); - }); - - it("generateCSV should only include the fields specified", async () => { - const result = await CSVUtils.generateCSV({ - data: testData, - fields: ["age", "name"], - }); - expect(result).toEqual(`"age","name"\n20,"Person1"\n25,"Person2"`); - }); - - it("generateCSV should transform fields properly if specified", async () => { - const transformFunction = (person: Person) => ({ - ...person, - age: person.age + 1, - petNames: person.pets.map((pet) => pet.name), - numPets: person.pets.length, - }); - const result = await CSVUtils.generateCSV({ - data: testData, - transformFunction, - }); - expect(result).toEqual( - `"name","age","pets","petNames","numPets"\n` + - `"Person1",21,"[{""name"":""Beans"",""type"":""Cat""},{""name"":""Spot"",""type"":""Dog""}]","[""Beans"",""Spot""]",2\n` + - `"Person2",26,"[{""name"":""Splash"",""type"":""Fish""}]","[""Splash""]",1`, - ); - }); - - it("generateCSV should flatten objects properly if specified", async () => { - const testDataWithHomes = [ - { ...testData[0], home: { number: 22, street: "Cool Road" } }, - { ...testData[1], home: { number: 24, street: "Awesome Road" } }, - ]; - const result = await CSVUtils.generateCSV({ - data: testDataWithHomes, - flattenObjects: true, - }); - expect(result).toEqual( - `"name","age","pets","home.number","home.street"\n` + - `"Person1",20,"[{""name"":""Beans"",""type"":""Cat""},{""name"":""Spot"",""type"":""Dog""}]",22,"Cool Road"\n` + - `"Person2",25,"[{""name"":""Splash"",""type"":""Fish""}]",24,"Awesome Road"`, - ); - }); - - it("generateCSV should flatten arrays properly if specified", async () => { - const result = await CSVUtils.generateCSV({ - data: testData, - flattenArrays: true, - }); - expect(result).toEqual( - `"name","age","pets.0","pets.1"\n` + - `"Person1",20,"{""name"":""Beans"",""type"":""Cat""}","{""name"":""Spot"",""type"":""Dog""}"\n` + - `"Person2",25,"{""name"":""Splash"",""type"":""Fish""}",`, - ); - }); - - it("generateCSV should flatten objects and arrays properly if specified", async () => { - const result = await CSVUtils.generateCSV({ - data: testData, - flattenArrays: true, - flattenObjects: true, - }); - expect(result).toEqual( - `"name","age","pets.0.name","pets.0.type","pets.1.name","pets.1.type"\n` + - `"Person1",20,"Beans","Cat","Spot","Dog"\n` + - `"Person2",25,"Splash","Fish",,`, - ); - }); - - it("generateCSV should unwind fields properly if specified", async () => { - const result = await CSVUtils.generateCSV({ - data: testData, - pathsToUnwind: ["pets"], - }); - expect(result).toEqual( - `"name","age","pets"\n` + - `"Person1",20,"{""name"":""Beans"",""type"":""Cat""}"\n` + - `"Person1",20,"{""name"":""Spot"",""type"":""Dog""}"\n` + - `"Person2",25,"{""name"":""Splash"",""type"":""Fish""}"`, - ); - }); - - it("generateCSV should override/add options if opts is specified", async () => { - const result = await CSVUtils.generateCSV({ - data: testData, - opts: { header: false }, - }); - expect(result).toEqual( - `"Person1",20,"[{""name"":""Beans"",""type"":""Cat""},{""name"":""Spot"",""type"":""Dog""}]"\n` + - `"Person2",25,"[{""name"":""Splash"",""type"":""Fish""}]"`, - ); - }); -}); diff --git a/frontend/src/utils/__tests__/LocalStorageUtils.test.ts b/frontend/src/utils/__tests__/LocalStorageUtils.test.ts deleted file mode 100644 index d8d59471..00000000 --- a/frontend/src/utils/__tests__/LocalStorageUtils.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import * as LocalStorageUtils from "../LocalStorageUtils"; - -class LocalStorageMock { - store: Record = {}; - - readonly length: number = 0; - - clear() { - this.store = {}; - } - - getItem(key: string) { - return this.store[key] || null; - } - - setItem(key: string, value: string) { - this.store[key] = value; - } - - removeItem(key: string) { - delete this.store[key]; - } -} - -const mockStorage = new LocalStorageMock(); - -describe("LocalStorageUtils", () => { - beforeAll(() => { - Object.defineProperty(global, "localStorage", { - value: mockStorage, - }); - }); - - afterEach(() => { - localStorage.clear(); - }); - - it("getLocalStorageObj should retrieve obj by key", () => { - localStorage.setItem("hello", JSON.stringify({ value: "world" })); - expect(LocalStorageUtils.getLocalStorageObj("hello")).toEqual({ - value: "world", - }); - }); - - it("getLocalStorageObjProperty should retrieve obj property by key and property", () => { - localStorage.setItem("hello", JSON.stringify({ value: "world" })); - expect( - LocalStorageUtils.getLocalStorageObjProperty("hello", "value"), - ).toEqual("world"); - }); - - it("setLocalStorageObjproperty should set obj property by key, property and value", () => { - localStorage.setItem("club", JSON.stringify({})); - LocalStorageUtils.setLocalStorageObjProperty("club", "name", "Blueprint"); - expect(LocalStorageUtils.getLocalStorageObj("club")).toEqual({ - name: "Blueprint", - }); - }); -}); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index a273b0cf..eeed8859 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -22,5 +22,5 @@ }, "include": [ "src" - ] +, "middleware/apiHandler.ts" ] } diff --git a/hooks/post-merge b/hooks/post-merge deleted file mode 100644 index ce5bd23a..00000000 --- a/hooks/post-merge +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/sh -# update secret files if git pull on master resulted in new changes being merged locally - -branch=`git symbolic-ref HEAD` -root_dir=`git rev-parse --show-toplevel` -# must replace with actual vault_path and default_branch, can run setup.sh -vault_path="kv/internal-tools" -default_branch="main" - -if [ $branch = "refs/heads/${default_branch}" ]; then - if [ -f "${root_dir}/update_secret_files.py" ]; then - vault kv get -format=json $vault_path | python "${root_dir}/update_secret_files.py" - if [ $? -eq 0 ]; then - echo "Successfully pulled secrets from Vault" - else - echo "An error occurred while pulling secrets from Vault" - fi - else - echo "To automatically update secrets after git pull on default branch, place update_secret_files.py in repo root directory" - fi -fi diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..567f3001 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,634 @@ +{ + "name": "marillac-place", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "babel-plugin-graphql-tag": "^3.3.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.5.tgz", + "integrity": "sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.7.tgz", + "integrity": "sha512-SRijHmF0PSPgLIBYlWnG0hyeJLwXE2CgpsXaMOrtt2yp9/86ALw6oUlj9KYuZ0JN07T4eBMVIW4li/9S1j2BGA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/helper-compilation-targets": "^7.26.5", + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helpers": "^7.26.7", + "@babel/parser": "^7.26.7", + "@babel/template": "^7.25.9", + "@babel/traverse": "^7.26.7", + "@babel/types": "^7.26.7", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", + "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.26.5", + "@babel/types": "^7.26.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/compat-data": "^7.26.5", + "@babel/helper-validator-option": "^7.25.9", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", + "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/traverse": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", + "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-module-imports": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9", + "@babel/traverse": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", + "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", + "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", + "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.7.tgz", + "integrity": "sha512-8NHiL98vsi0mbPQmYAGWwfcFaOy4j2HY49fXJCfuDcdE7fMIsH9a7GdaeXpIBsbT7307WU8KCMp5pUVDNL4f9A==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", + "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.26.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", + "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.9", + "@babel/parser": "^7.25.9", + "@babel/types": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", + "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.5", + "@babel/parser": "^7.26.7", + "@babel/template": "^7.25.9", + "@babel/types": "^7.26.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", + "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/babel-literal-to-ast": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/babel-literal-to-ast/-/babel-literal-to-ast-2.1.0.tgz", + "integrity": "sha512-CxfpQ0ysQ0bZOhlaPgcWjl79Em16Rhqc6++UAFn0A3duiXmuyhhj8yyl9PYbj0I0CyjrHovdDbp2QEKT7uIMxw==", + "dev": true, + "license": "CC0-1.0", + "dependencies": { + "@babel/parser": "^7.1.6", + "@babel/traverse": "^7.1.6", + "@babel/types": "^7.1.6" + }, + "peerDependencies": { + "@babel/core": "^7.1.2" + } + }, + "node_modules/babel-plugin-graphql-tag": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/babel-plugin-graphql-tag/-/babel-plugin-graphql-tag-3.3.0.tgz", + "integrity": "sha512-27qfJhFJ2jQg+ktcC3cdppE2RrXZ5uiDz+39YC5lhkmftWPuJW8ET9Ehskwv7ujScZ1jSKhs5/JZKlESCkwIBg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/parser": "^7.3.2", + "babel-literal-to-ast": "^2.1.0", + "debug": "^4.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "graphql-tag": "^2.10.1" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001697", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001697.tgz", + "integrity": "sha512-GwNPlWJin8E+d7Gxq96jxM6w0w+VFeyyXRsjU58emtkYqnbwHqXm5uT2uCmO0RQE9htWknOP4xtBlLmM/gWxvQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0", + "peer": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.92", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.92.tgz", + "integrity": "sha512-BeHgmNobs05N1HMmMZ7YIuHfYBGlq/UmvlsTgg+fsbFs9xVMj+xJHFg19GN04+9Q+r8Xnh9LXqaYIyEWElnNgQ==", + "dev": true, + "license": "ISC", + "peer": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/graphql": { + "version": "16.10.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz", + "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-tag": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "peer": true + }, + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC", + "peer": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..a9532b77 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "babel-plugin-graphql-tag": "^3.3.0" + } +} diff --git a/secret.config b/secret.config deleted file mode 100644 index 651ddc13..00000000 --- a/secret.config +++ /dev/null @@ -1,2 +0,0 @@ -ROOT_ENV_FILE=.env -FRONTEND_ENV_FILE=frontend/.env diff --git a/setup.sh b/setup.sh deleted file mode 100755 index 11c0c954..00000000 --- a/setup.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -vault_path_replacement_str="s|^vault_path=.*|vault_path=\"$1\"|g" -default_branch_replacement_str="s|^default_branch=.*|default_branch=\"$2\"|g" - -# MacOS -if [[ $OSTYPE =~ darwin.* ]]; then - sed -i "" -e $vault_path_replacement_str ./hooks/post-merge - sed -i "" -e $default_branch_replacement_str ./hooks/post-merge -else - sed -i $vault_path_replacement_str ./hooks/post-merge - sed -i $default_branch_replacement_str ./hooks/post-merge -fi -cp ./hooks/post-merge ./.git/hooks/post-merge diff --git a/update_secret_files.py b/update_secret_files.py deleted file mode 100644 index 2629b39f..00000000 --- a/update_secret_files.py +++ /dev/null @@ -1,56 +0,0 @@ -import sys -import json - -# Open secret.config file -configFileNotFound = False -try: - configFile = open('secret.config') -except Exception as e: - print("File secret.config could not be opened in current directory.") - print(e) - configFileNotFound = True - # Script will exit after checking if the Vault request is valid - -# Decode json result -try: - rawInput = ''.join(sys.stdin.readlines()) - decodedJson = json.loads(rawInput) -except Exception as e: - print("Unable to retrieve secrets from Vault and obtain valid json result.") - print("Please ensure you are authenticated and have supplied the correct path argument.") - exit() - -# Extract the data field containting the secrets -if "data" in decodedJson and "data" in decodedJson["data"]: - data = decodedJson["data"]["data"] -else: - print("Unable to access the field data:{data:{}} from result which should contain the secrets.") - print("Please ensure you are authenticated and have supplied the correct path argument.") - exit() - -# Even if the config file is not found, it is useful to still indicate if the Vault request has any problems before exiting -if configFileNotFound: - exit() - -# Read all the secret file locations from secret.config -locations = {} -for line in configFile: - key, val = line.rstrip().partition('=')[::2] - if key in locations: - print("Key <{keyName}> appeared more than once on configuration file. Ignoring second instance of the key.".format(keyName=key)) - else: - locations[key] = val -configFile.close() - -# Write values to the secret file corresponding to their keys -for key in data: - if key in locations: - try: - f = open(locations[key], 'w') - f.write(data[key]) - f.close() - except Exception as e: - print("Could not write the values for key <{keyName}> to location <{locName}>".format(keyName=key, locName=locations[key])) - print(e) - else: - print("File location for key <{keyName}> was not found.".format(keyName=key)) \ No newline at end of file