diff --git a/serverless/functions/user.yml b/serverless/functions/user.yml index 6b95c35..568ebce 100644 --- a/serverless/functions/user.yml +++ b/serverless/functions/user.yml @@ -166,4 +166,19 @@ checkAvailabilityDetails: events: - httpApi: path: /user/activities/check-availability/{activity_xid} + method: get + +searchActivities: + handler: src/modules/user/handlers/activities/getSpecificSearchApi.handler + memorySize: 384 + package: + patterns: + - 'src/modules/user/handlers/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/search method: get \ No newline at end of file diff --git a/src/modules/user/handlers/activities/getSpecificSearchApi.ts b/src/modules/user/handlers/activities/getSpecificSearchApi.ts new file mode 100644 index 0000000..cf61f4f --- /dev/null +++ b/src/modules/user/handlers/activities/getSpecificSearchApi.ts @@ -0,0 +1,57 @@ +import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda'; +import { prismaClient } from '../../../../common/database/prisma.lambda.service'; +import { verifyUserToken } from '../../../../common/middlewares/jwt/authForUser'; +import { safeHandler } from '../../../../common/utils/handlers/safeHandler'; +import ApiError from '../../../../common/utils/helper/ApiError'; +import { UserService } from '../../services/user.service'; + +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'); + } + + // Extract query parameters for search + const activityTitle = event.queryStringParameters?.activityTitle?.trim(); + const activityType = event.queryStringParameters?.activityType?.trim(); + const checkInCity = event.queryStringParameters?.checkInCity?.trim(); + + // At least one search parameter should be provided + if (!activityTitle && !activityType && !checkInCity) { + throw new ApiError(400, 'At least one search parameter (activityTitle, activityType, or checkInCity) must be provided'); + } + + // Fetch activities based on search criteria + const result = await userService.searchActivities( + userId, + { activityTitle, activityType, checkInCity } + ); + + 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 f513daf..630bac9 100644 --- a/src/modules/user/services/user.service.ts +++ b/src/modules/user/services/user.service.ts @@ -618,7 +618,7 @@ export class UserService { return { userAddressDetails, interests: [], - otherStatesActivities: null, + otherStatesActivities: null, overSeasActivities: null, }; } @@ -1516,4 +1516,111 @@ export class UserService { } }) } + + async searchActivities( + userId: number, + searchCriteria: { + activityTitle?: string; + activityType?: string; + checkInCity?: string; + } + ) { + const { activityTitle, activityType, checkInCity } = searchCriteria; + + // Build the where clause dynamically + const where: any = { + isActive: true, + activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED, + amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED, + }; + + // Add activityTitle filter if provided + if (activityTitle) { + where.activityTitle = { + contains: activityTitle, + mode: 'insensitive', + }; + } + + // Add activityType filter if provided + if (activityType) { + where.activityType = { + activityTypeName: { + contains: activityType, + mode: 'insensitive', + }, + }; + } + + // Add checkInCity filter if provided + if (checkInCity) { + where.checkInCity = { + cityName: { + contains: checkInCity, + mode: 'insensitive', + }, + }; + } + + const activities = await this.prisma.activities.findMany({ + where, + select: { + id: true, + activityTitle: true, + activityDescription: true, + checkInAddress: true, + activityDurationMins: true, + sustainabilityScore: true, + activityRefNumber: true, + activityType: { + select: { + activityTypeName: true, + energyLevel: { + select: { + energyLevelName: true, + energyColor: true, + energyIcon: true, + }, + }, + }, + }, + checkInCity: { + select: { + cityName: true, + }, + }, + ActivitiesMedia: { + where: { isActive: true }, + select: { + id: true, + mediaFileName: true, + mediaType: true, + }, + take: 1, // Get first media item + }, + }, + take: 50, // Limit results to prevent too many + }); + + // Get interested count for each activity + const activitiesWithCounts = await Promise.all( + activities.map(async (activity) => { + const interestedCount = await this.prisma.userBucketInterested.count({ + where: { + activityXid: activity.id, + isActive: true, + }, + }); + + return { + ...activity, + interestedCount, + rating: 0, // Placeholder + distance: 0, // Placeholder + }; + }) + ); + + return activitiesWithCounts; + } } \ No newline at end of file