diff --git a/serverless/functions/host.yml b/serverless/functions/host.yml index 81f8ba1..8f6f46b 100644 --- a/serverless/functions/host.yml +++ b/serverless/functions/host.yml @@ -324,6 +324,22 @@ inviteHostMember: path: /settings/invite-member method: post +getAllInvitedCoadminAndOperator: + handler: src/modules/host/handlers/settings/getAllInvitedCoadminAndOperator.handler + memorySize: 384 + package: + patterns: + - 'src/modules/host/handlers/settings/**' + - 'src/modules/host/services/**' + - ${file(./serverless/patterns/base.yml):pattern1} + - ${file(./serverless/patterns/base.yml):pattern2} + - ${file(./serverless/patterns/base.yml):pattern3} + - ${file(./serverless/patterns/base.yml):pattern4} + events: + - httpApi: + path: /settings/invited-coadmin-operators + method: get + saveRolePermissions: handler: src/modules/host/handlers/settings/saveRolePermissions.handler memorySize: 384 diff --git a/src/modules/host/handlers/settings/getAllInvitedCoadminAndOperator.ts b/src/modules/host/handlers/settings/getAllInvitedCoadminAndOperator.ts new file mode 100644 index 0000000..1b2e332 --- /dev/null +++ b/src/modules/host/handlers/settings/getAllInvitedCoadminAndOperator.ts @@ -0,0 +1,61 @@ +import { + APIGatewayProxyEvent, + APIGatewayProxyResult, + Context, +} from 'aws-lambda'; + +import { prismaClient } from '../../../../common/database/prisma.lambda.service'; +import { verifyHostToken } from '../../../../common/middlewares/jwt/authForHost'; +import { paginationService } from '../../../../common/utils/pagination/pagination.service'; +import { safeHandler } from '../../../../common/utils/handlers/safeHandler'; +import ApiError from '../../../../common/utils/helper/ApiError'; +import { HostMemberService } from '../../services/hostMember.service'; + +const hostMemberService = new HostMemberService(prismaClient); + +export const handler = safeHandler(async ( + event: APIGatewayProxyEvent, + context?: Context, +): Promise => { + const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token']; + + if (!token) { + throw new ApiError( + 400, + 'This is a protected route. Please provide a valid token.', + ); + } + + const userInfo = await verifyHostToken(token); + const search = event.queryStringParameters?.search || ''; + + const paginationParams = paginationService.getPaginationFromEvent(event); + const paginationOptions = + paginationService.parsePaginationParams(paginationParams); + + const { data, totalCount } = + await hostMemberService.getAllInvitedCoadminAndOperator({ + hostUserXid: userInfo.id, + search, + paginationOptions, + }); + + const paginatedResponse = paginationService.createPaginatedResponse( + data, + totalCount, + paginationOptions, + ); + + return { + statusCode: 200, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + body: JSON.stringify({ + success: true, + message: 'Invited co-admin and operator members fetched successfully', + ...paginatedResponse, + }), + }; +}); diff --git a/src/modules/host/services/hostMember.service.ts b/src/modules/host/services/hostMember.service.ts index 7025659..cafea46 100644 --- a/src/modules/host/services/hostMember.service.ts +++ b/src/modules/host/services/hostMember.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common'; import { PrismaClient } from '@prisma/client'; import { ROLE, USER_STATUS } from '../../../common/utils/constants/common.constant'; +import { PaginationOptions } from '../../../common/utils/pagination/pagination.types'; import ApiError from '../../../common/utils/helper/ApiError'; const ALLOWED_MEMBER_ROLES = new Set([ROLE.CO_ADMIN, ROLE.OPERATOR]); @@ -24,6 +25,210 @@ function normalizeIdArray(values: unknown): number[] { export class HostMemberService { constructor(private prisma: PrismaClient) {} + async getAllInvitedCoadminAndOperator(input: { + hostUserXid: number; + search?: string; + paginationOptions?: PaginationOptions; + }) { + const host = await this.prisma.hostHeader.findFirst({ + where: { + userXid: input.hostUserXid, + isActive: true, + deletedAt: null, + }, + select: { + id: true, + companyName: true, + }, + }); + + if (!host) { + throw new ApiError(404, 'Host company not found for the logged-in user.'); + } + + const filters: any = { + hostXid: host.id, + roleXid: { + in: [ROLE.CO_ADMIN, ROLE.OPERATOR], + }, + memberStatus: 'invited', + isActive: true, + deletedAt: null, + user: { + isActive: true, + deletedAt: null, + }, + }; + + if (input.search?.trim()) { + const term = input.search.trim(); + filters.user = { + ...filters.user, + OR: [ + { emailAddress: { contains: term, mode: 'insensitive' as const } }, + { firstName: { contains: term, mode: 'insensitive' as const } }, + { lastName: { contains: term, mode: 'insensitive' as const } }, + { mobileNumber: { contains: term, mode: 'insensitive' as const } }, + { userRefNumber: { contains: term, mode: 'insensitive' as const } }, + ], + }; + } + + const totalCount = await this.prisma.hostMembers.count({ + where: filters, + }); + + const members = await this.prisma.hostMembers.findMany({ + where: filters, + select: { + id: true, + hostXid: true, + userXid: true, + roleXid: true, + memberStatus: true, + invitedOn: true, + acceptedOn: true, + hostRolePermissionMasterXid: true, + user: { + select: { + id: true, + firstName: true, + lastName: true, + emailAddress: true, + mobileNumber: true, + userRefNumber: true, + userStatus: true, + role: { + select: { + id: true, + roleName: true, + }, + }, + }, + }, + role: { + select: { + id: true, + roleName: true, + }, + }, + invitedBy: { + select: { + id: true, + firstName: true, + lastName: true, + emailAddress: true, + }, + }, + hostRolePermissionMaster: { + select: { + id: true, + permissionMasterXids: true, + }, + }, + managedActivities: { + where: { + isActive: true, + deletedAt: null, + }, + select: { + activityXid: true, + activity: { + select: { + id: true, + activityTitle: true, + }, + }, + }, + orderBy: { + activityXid: 'asc', + }, + }, + }, + orderBy: { + invitedOn: 'desc', + }, + skip: input.paginationOptions?.skip ?? 0, + take: input.paginationOptions?.limit ?? 10, + }); + + const permissionIds = Array.from( + new Set( + members.flatMap((member) => + normalizeIdArray(member.hostRolePermissionMaster?.permissionMasterXids), + ), + ), + ); + + const permissionMasters = permissionIds.length + ? await this.prisma.hostPermissionMasters.findMany({ + where: { + id: { in: permissionIds }, + isActive: true, + deletedAt: null, + }, + select: { + id: true, + displayLabel: true, + }, + }) + : []; + + const permissionLabelMap = new Map( + permissionMasters.map((permission) => [permission.id, permission.displayLabel]), + ); + + const data = members.map((member) => { + const permissionMasterXids = normalizeIdArray( + member.hostRolePermissionMaster?.permissionMasterXids, + ); + + return { + hostMemberId: member.id, + hostXid: member.hostXid, + hostCompanyName: host.companyName, + userXid: member.userXid, + roleXid: member.roleXid, + roleName: member.role?.roleName ?? member.user.role?.roleName ?? null, + permissionMasterXid: member.hostRolePermissionMasterXid, + permissionMasterXids, + permissionLabels: permissionMasterXids + .map((permissionId) => permissionLabelMap.get(permissionId)) + .filter(Boolean), + memberStatus: member.memberStatus, + invitedOn: member.invitedOn, + acceptedOn: member.acceptedOn, + invitedBy: member.invitedBy + ? { + id: member.invitedBy.id, + firstName: member.invitedBy.firstName, + lastName: member.invitedBy.lastName, + emailAddress: member.invitedBy.emailAddress, + } + : null, + user: { + id: member.user.id, + firstName: member.user.firstName, + lastName: member.user.lastName, + emailAddress: member.user.emailAddress, + mobileNumber: member.user.mobileNumber, + userRefNumber: member.user.userRefNumber, + userStatus: member.user.userStatus, + }, + activities: member.managedActivities.map((activityLink) => ({ + id: activityLink.activity.id, + activityXid: activityLink.activityXid, + activityTitle: activityLink.activity.activityTitle, + })), + }; + }); + + return { + data, + totalCount, + }; + } + async inviteMember(input: { inviterUserXid: number; emailAddress: string;