From 171a3aded657be11cead4b255fb7023dc48caf43 Mon Sep 17 00:00:00 2001 From: Mayank Mishra Date: Fri, 6 Feb 2026 19:27:37 +0530 Subject: [PATCH] feat: add getActivityDetailsById API endpoint to retrieve activity details by ID --- serverless/functions/user.yml | 15 ++ .../activities/getByIdActivityDetails.ts | 53 ++++ src/modules/user/services/user.service.ts | 246 ++++++++++++++++++ 3 files changed, 314 insertions(+) create mode 100644 src/modules/user/handlers/activities/getByIdActivityDetails.ts diff --git a/serverless/functions/user.yml b/serverless/functions/user.yml index dcf8adf..28c8050 100644 --- a/serverless/functions/user.yml +++ b/serverless/functions/user.yml @@ -105,4 +105,19 @@ getLandingPageDetails: events: - httpApi: path: /user/activities/get-landing-page-details + method: get + +getActivityDetailsById: + handler: src/modules/user/handlers/activities/getByIdActivityDetails.handler + memorySize: 384 + package: + patterns: + - 'src/modules/user/activities/**' + - ${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: /user/activities/get-activity-details-by-id/{activity_xid} method: get \ No newline at end of file diff --git a/src/modules/user/handlers/activities/getByIdActivityDetails.ts b/src/modules/user/handlers/activities/getByIdActivityDetails.ts new file mode 100644 index 0000000..2fd5698 --- /dev/null +++ b/src/modules/user/handlers/activities/getByIdActivityDetails.ts @@ -0,0 +1,53 @@ +import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; +import { safeHandler } from '../../../../common/utils/handlers/safeHandler'; +import { prismaClient } from '../../../../common/database/prisma.lambda.service'; +import ApiError from '../../../../common/utils/helper/ApiError'; +import { UserService } from '../../services/user.service'; +import { verifyUserToken } from '../../../../common/middlewares/jwt/authForUser'; + +const userService = new UserService(prismaClient); + +export const handler = safeHandler(async ( + event: APIGatewayProxyEvent, + context?: Context +): Promise => { + // Extract token from headers + 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.'); + } + + // Verify token and get user info + const userInfo = await verifyUserToken(token); + const userId = Number(userInfo.id); + + if (!userId || isNaN(userId)) { + throw new ApiError(400, 'Invalid user ID'); + } + + const activityXid = Number(event.pathParameters?.activityXid); + + if (!activityXid || isNaN(activityXid)) { + throw new ApiError(400, 'Valid activityXid is required'); +} + + // Fetch user with their HostHeader stepper info + const result = await userService.getActivityDetailsById( + userId, + activityXid + ); + + return { + statusCode: 200, + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + }, + body: JSON.stringify({ + success: true, + message: 'Data retrieved successfully', + data: result, + }), + }; +}); + diff --git a/src/modules/user/services/user.service.ts b/src/modules/user/services/user.service.ts index 9081d85..3bd0ef3 100644 --- a/src/modules/user/services/user.service.ts +++ b/src/modules/user/services/user.service.ts @@ -850,5 +850,251 @@ export class UserService { return data; } + async getActivityDetailsById( + userId: number, + activityXid: number) { + return await this.prisma.$transaction(async (tx) => { + const activity = await tx.activities.findUnique({ + where: { + id: activityXid, + isActive: true, + activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED, + amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED + }, + select: { + id: true, + activityTitle: true, + activityDurationMins: true, + sustainabilityScore: true, + checkInLat: true, + checkInLong: true, + activityRefNumber: true, + checkInAddress: true, + checkOutAddress: true, + checkOutLat: true, + checkOutLong: true, + activityDescription: true, + foodAvailable: true, + foodIsChargeable: true, + alcoholAvailable: true, + trainerAvailable: true, + trainerIsChargeable: true, + pickUpDropAvailable: true, + pickUpDropIsChargeable: true, + inActivityAvailable: true, + inActivityIsChargeable: true, + isLateCheckingAllowed: true, + equipmentAvailable: true, + equipmentIsChargeable: true, + cancellationAvailable: true, + cancellationAllowedBeforeMins: true, + activityType: { + select: { + interestXid: true, // ✅ VERY IMPORTANT + energyLevel: { + select: { + id: true, + energyLevelName: true, + energyColor: true, + energyIcon: true, + }, + }, + }, + }, + + ActivityOtherDetails: { + where: { isActive: true }, + select: { + id: true, + exclusiveNotes: true, + dosNotes: true, + dontsNotes: true, + tipsNotes: true, + termsAndCondition: true + } + }, + + ActivityEligibility: { + where: { isActive: true }, + select: { + id: true, + isAgeRestriction: true, + ageRestrictionName: true, + ageEntered: true, + ageIn: true, + minAge: true, + maxAge: true, + isWeightRestriction: true, + weightRestrictionName: true, + weightEntered: true, + weightIn: true, + minWeight: true, + maxWeight: true, + isHeightRestriction: true, + heightRestrictionName: true, + heightEntered: true, + heightIn: true, + minHeight: true, + maxHeight: true + } + }, + + ActivityTrainers: { + where: { isActive: true }, + select: { + id: true, + totalAmount: true + } + }, + + ActivityAllowedEntry: { + where: { isActive: true }, + select: { + id: true, + allowedEntryTypeXid: true, + allowedEntryType: { + select: { + id: true, + allowedEntryTypeName: true + } + } + } + }, + + ActivityFoodCost: { + where: { isActive: true }, + select: { + id: true, + totalAmount: true + } + }, + + activityFoodTypes: { + where: { isActive: true }, + select: { + id: true, + foodTypeXid: true, + foodType: { + select: { + id: true, + foodTypeName: true + } + } + } + }, + + ActivityEquipments: { + where: { isActive: true }, + select: { + id: true, + equipmentName: true, + isEquipmentChargeable: true, + equipmentTotalPrice: true + } + }, + + ActivityNavigationModes: { + where: { isActive: true }, + select: { + id: true, + navigationModeXid: true, + navigationMode: { + select: { + id: true, + navigationModeName: true, + navigationModeIcon: true + } + }, + isInActivityChargeable: true, + navigationModesTotalPrice: true + } + }, + + ActivityAmenities: { + where: { isActive: true }, + select: { + id: true, + amenitiesXid: true, + amenities: { + select: { + id: true, + amenitiesName: true + } + } + } + }, + + ActivityPickUpDetails: { + where: { isActive: true }, + select: { + id: true, + isPickUp: true, + locationLat: true, + locationLong: true, + locationAddress: true, + transportTotalPrice: true + } + }, + + activityCuisines: { + where: { isActive: true }, + select: { + id: true, + foodCuisineXid: true, + foodCuisine: { + select: { + id: true, + cuisineName: true + } + } + } + }, + + ActivityVenues: { + where: { + isActive: true + }, + select: { + venueName: true, + venueLabel: true, + venueCapacity: true, + availableSeats: true, + isMinPeopleReqMandatory: true, + minPeopleRequired: true, + minReqfullfilledBeforeMins: true, + venueDescription: true, + ActivityPrices: { + select: { + id: true, + sellPrice: true, + }, + }, + }, + }, + + ActivitiesMedia: { + where: { isActive: true }, + select: { + id: true, + mediaFileName: true, + mediaType: true, + }, + }, + }, + }) + + const interestedCount = await tx.userBucketInterested.count({ + where: { + activityXid, + isActive: true, + }, + }) + + return { + activity, + interestedCount, + } + }) + } } \ No newline at end of file