Add CreateActivityDto and update createFullActivity method for activity creation

This commit is contained in:
paritosh18
2025-12-18 20:05:30 +05:30
parent 53785bd5f2
commit 2e4f318684
3 changed files with 166 additions and 39 deletions

View File

@@ -0,0 +1,125 @@
import { z } from 'zod';
export const MediaDto = z.object({
mediaType: z.string().optional(),
mediaFileName: z.string(),
});
export const PriceDto = z.object({
noOfSession: z.number().int().optional().default(1),
isPackage: z.boolean().optional().default(false),
sessionValidity: z.number().int().optional().default(0),
sessionValidityFrequency: z.string().optional().default('Days'),
basePrice: z.number().int().optional().default(0),
sellPrice: z.number().int().optional().default(0),
});
export const VenueDto = z.object({
venueName: z.string(),
venueCapacity: z.number().int().optional().default(0),
availableSeats: z.number().int().optional().default(0),
isMinPeopleReqMandatory: z.boolean().optional().default(false),
minPeopleRequired: z.number().int().nullable().optional(),
minReqfullfilledBeforeMins: z.number().int().nullable().optional(),
venueDescription: z.string().optional(),
prices: z.array(PriceDto).optional().default([]),
});
export const PickupDetailDto = z.object({
isPickUp: z.boolean().optional().default(false),
locationLat: z.number().optional().nullable(),
locationLong: z.number().optional().nullable(),
locationAddress: z.string().optional().nullable(),
transportBasePrice: z.number().int().optional().default(0),
transportTotalPrice: z.number().int().optional().default(0),
});
export const PickupTransportDto = z.object({
transportModeXid: z.number().int(),
isTransportModeChargeable: z.boolean().optional().default(false),
pickupDetails: z.array(PickupDetailDto).optional().default([]),
});
export const NavigationModeDto = z.object({
navigationModeXid: z.number().int(),
isInActivityChargeable: z.boolean().optional().default(false),
basePrice: z.number().int().optional().default(0),
totalPrice: z.number().int().optional().default(0),
});
export const EquipmentDto = z.object({
equipmentName: z.string(),
isEquipmentChargeable: z.boolean().optional().default(false),
equipmentBasePrice: z.number().int().optional().default(0),
equipmentTotalPrice: z.number().int().optional().default(0),
});
export const EligibilityDto = z.object({
isAgeRestriction: z.boolean().optional().default(false),
ageRestrictionXid: z.number().int().nullable().optional(),
isWeightRestriction: z.boolean().optional().default(false),
weightRestrictionName: z.string().optional().nullable(),
weightEntered: z.number().int().nullable().optional(),
weightIn: z.string().optional().nullable(),
minWeight: z.number().int().nullable().optional(),
maxWeight: z.number().int().nullable().optional(),
isHeightRestriction: z.boolean().optional().default(false),
heightRestrictionName: z.string().optional().nullable(),
heightEntered: z.number().int().nullable().optional(),
minHeight: z.number().int().nullable().optional(),
maxHeight: z.number().int().nullable().optional(),
});
export const OtherDetailsDto = z.object({
exclusiveNotes: z.string().optional(),
dosNotes: z.string().optional(),
dontsNotes: z.string().optional(),
tipsNotes: z.string().optional(),
termsAndCondition: z.string().optional(),
});
export const CreateActivityDto = z.object({
activityTypeXid: z.number().int(),
frequenciesXid: z.number().int().nullable().optional(),
activityTitle: z.string().optional(),
activityDescription: z.string().optional(),
checkInLat: z.number().optional().nullable(),
checkInLong: z.number().optional().nullable(),
checkInAddress: z.string().optional().nullable(),
isCheckOutSame: z.boolean().optional().default(true),
checkOutLat: z.number().optional().nullable(),
checkOutLong: z.number().optional().nullable(),
checkOutAddress: z.string().optional().nullable(),
energyLevelXid: z.number().int().nullable().optional(),
activityDurationMins: z.number().int().nullable().optional(),
durationHours: z.number().int().optional(),
durationMins: z.number().int().optional(),
foodAvailable: z.boolean().optional().default(false),
foodIsChargeable: z.boolean().optional().default(false),
alcoholAvailable: z.boolean().optional().default(false),
trainerAvailable: z.boolean().optional().default(false),
trainerIsChargeable: z.boolean().optional().default(false),
pickUpDropAvailable: z.boolean().optional().default(false),
pickUpDropIsChargeable: z.boolean().optional().default(false),
inActivityAvailable: z.boolean().optional().default(false),
inActivityIsChargeable: z.boolean().optional().default(false),
equipmentAvailable: z.boolean().optional().default(false),
equipmentIsChargeable: z.boolean().optional().default(false),
cancellationAvailable: z.boolean().optional().default(false),
currencyXid: z.number().int().nullable().optional(),
sustainabilityScore: z.number().int().optional().nullable(),
safetyScore: z.number().int().optional().nullable(),
isInstantBooking: z.boolean().optional().default(false),
media: z.array(MediaDto).optional().default([]),
venues: z.array(VenueDto).optional().default([]),
foodTypeIds: z.array(z.number().int()).optional().default([]),
cuisineIds: z.array(z.number().int()).optional().default([]),
pickupTransports: z.array(PickupTransportDto).optional().default([]),
navigationModes: z.array(NavigationModeDto).optional().default([]),
equipments: z.array(EquipmentDto).optional().default([]),
amenitiesIds: z.array(z.number().int()).optional().default([]),
eligibility: EligibilityDto.optional(),
otherDetails: OtherDetailsDto.optional(),
});
export type CreateActivityInput = z.infer<typeof CreateActivityDto>;

View File

@@ -5,6 +5,7 @@ import {
Context,
} from 'aws-lambda';
import { prismaClient } from '../../../../../common/database/prisma.lambda.service';
import { CreateActivityDto } from '../../../dto/createActivity.schema';
import { safeHandler } from '../../../../../common/utils/handlers/safeHandler';
import ApiError from '../../../../../common/utils/helper/ApiError';
import { HostService } from '../../../services/host.service';
@@ -29,26 +30,23 @@ export const handler = safeHandler(
// Verify token and get user info
const userInfo = await verifyHostToken(token);
let body: any = {};
let rawBody: any = {};
try {
body = event.body ? JSON.parse(event.body) : {};
rawBody = event.body ? JSON.parse(event.body) : {};
} catch (err) {
throw new ApiError(400, 'Invalid JSON in request body');
}
// Accept multiple possible keys from the frontend payload
const rawActivityType = body.activityTypeXid ?? body.activityType ?? body.activity_type_xid;
const rawFrequencies = body.frequenciesXid ?? body.frequencies ?? body.frequencies_xid;
const activityTypeXid = rawActivityType !== undefined && rawActivityType !== null ? Number(rawActivityType) : undefined;
const frequenciesXid = rawFrequencies !== undefined && rawFrequencies !== null ? Number(rawFrequencies) : undefined;
if (!activityTypeXid || isNaN(activityTypeXid)) {
throw new ApiError(400, 'activityTypeXid is required and must be a number');
// Validate request body with Zod DTO
let dto: any;
try {
dto = CreateActivityDto.parse(rawBody);
} catch (err: any) {
throw new ApiError(400, 'Invalid payload: ' + (err?.message || 'validation failed'));
}
// Create full activity and related records
const createdData = await hostService.createFullActivity(userInfo.id, body);
const createdData = await hostService.createFullActivity(userInfo.id, dto as any);
return {
statusCode: 200,

View File

@@ -1874,18 +1874,18 @@ export class HostService {
* ActivityPickUpTransport/Details + ActivityNavigationModes + ActivityEquipments +
* ActivityAmenities + ActivityEligibility and also seed PQQ headers.
*/
async createFullActivity(userId: number, payload: any) {
async createFullActivity(userId: number, payload: import('../dto/createActivity.schema').CreateActivityInput) {
return await this.prisma.$transaction(async (tx) => {
const host = await tx.hostHeader.findFirst({ where: { userXid: userId, isActive: true } });
if (!host) throw new ApiError(404, 'Host not found for the user');
const activityTypeXid = payload.activityTypeXid ?? payload.activityType ?? payload.activity_type_xid;
const activityTypeXid = payload.activityTypeXid;
if (!activityTypeXid) throw new ApiError(400, 'activityTypeXid is required');
const activityType = await tx.activityTypes.findUnique({ where: { id: Number(activityTypeXid) } });
if (!activityType) throw new ApiError(404, 'Activity type not found');
const frequenciesXid = payload.frequenciesXid ?? payload.frequencies ?? payload.frequencies_xid;
const frequenciesXid = payload.frequenciesXid ?? null;
if (frequenciesXid) {
const freq = await tx.frequencies.findUnique({ where: { id: Number(frequenciesXid) } });
if (!freq) throw new ApiError(404, 'Frequency not found');
@@ -1903,12 +1903,12 @@ export class HostService {
// Simple mappings
if (payload.activityTitle) activityData.activityTitle = String(payload.activityTitle).substring(0, 30);
if (payload.activityDescription) activityData.activityDescription = String(payload.activityDescription).substring(0, 80);
if (payload.checkInLat) activityData.checkInLat = Number(payload.checkInLat);
if (payload.checkInLong) activityData.checkInLong = Number(payload.checkInLong);
if (payload.checkInLat !== undefined && payload.checkInLat !== null) activityData.checkInLat = Number(payload.checkInLat);
if (payload.checkInLong !== undefined && payload.checkInLong !== null) activityData.checkInLong = Number(payload.checkInLong);
if (payload.checkInAddress) activityData.checkInAddress = String(payload.checkInAddress).substring(0,150);
if (payload.isCheckOutSame !== undefined) activityData.isCheckOutSame = Boolean(payload.isCheckOutSame);
if (payload.checkOutLat) activityData.checkOutLat = Number(payload.checkOutLat);
if (payload.checkOutLong) activityData.checkOutLong = Number(payload.checkOutLong);
if (payload.checkOutLat !== undefined && payload.checkOutLat !== null) activityData.checkOutLat = Number(payload.checkOutLat);
if (payload.checkOutLong !== undefined && payload.checkOutLong !== null) activityData.checkOutLong = Number(payload.checkOutLong);
if (payload.checkOutAddress) activityData.checkOutAddress = String(payload.checkOutAddress).substring(0,150);
if (payload.energyLevelXid) activityData.energyLevelXid = Number(payload.energyLevelXid);
@@ -1920,35 +1920,39 @@ export class HostService {
activityData.activityDurationMins = hrs * 60 + mins;
}
// Booleans
const boolFields = [
'foodAvailable','foodIsChargeable','alcoholAvailable','trainerAvailable','trainerIsChargeable',
'pickUpDropAvailable','pickUpDropIsChargeable','inActivityAvailable','inActivityIsChargeable',
'equipmentAvailable','equipmentIsChargeable','cancellationAvailable','isInstantBooking'
];
for (const k of boolFields) {
if (payload[k] !== undefined) {
activityData[k] = Boolean(payload[k]);
}
}
// Booleans (assign explicitly for type-safety)
if (payload.foodAvailable !== undefined) activityData.foodAvailable = payload.foodAvailable;
if (payload.foodIsChargeable !== undefined) activityData.foodIsChargeable = payload.foodIsChargeable;
if (payload.alcoholAvailable !== undefined) activityData.alcoholAvailable = payload.alcoholAvailable;
if (payload.trainerAvailable !== undefined) activityData.trainerAvailable = payload.trainerAvailable;
if (payload.trainerIsChargeable !== undefined) activityData.trainerIsChargeable = payload.trainerIsChargeable;
if (payload.pickUpDropAvailable !== undefined) activityData.pickUpDropAvailable = payload.pickUpDropAvailable;
if (payload.pickUpDropIsChargeable !== undefined) activityData.pickUpDropIsChargeable = payload.pickUpDropIsChargeable;
if (payload.inActivityAvailable !== undefined) activityData.inActivityAvailable = payload.inActivityAvailable;
if (payload.inActivityIsChargeable !== undefined) activityData.inActivityIsChargeable = payload.inActivityIsChargeable;
if (payload.equipmentAvailable !== undefined) activityData.equipmentAvailable = payload.equipmentAvailable;
if (payload.equipmentIsChargeable !== undefined) activityData.equipmentIsChargeable = payload.equipmentIsChargeable;
if (payload.cancellationAvailable !== undefined) activityData.cancellationAvailable = payload.cancellationAvailable;
if (payload.isInstantBooking !== undefined) activityData.isInstantBooking = payload.isInstantBooking;
if (payload.currencyXid) activityData.currencyXid = Number(payload.currencyXid);
if (payload.sustainabilityScore) activityData.sustainabilityScore = Number(payload.sustainabilityScore);
if (payload.safetyScore) activityData.safetyScore = Number(payload.safetyScore);
if (payload.sustainabilityScore !== undefined && payload.sustainabilityScore !== null) activityData.sustainabilityScore = Number(payload.sustainabilityScore);
if (payload.safetyScore !== undefined && payload.safetyScore !== null) activityData.safetyScore = Number(payload.safetyScore);
// Create activity
const createdActivity = await tx.activities.create({ data: activityData });
// Other details
if (payload.exclusiveNotes || payload.dosNotes || payload.dontsNotes || payload.tipsNotes || payload.termsAndCondition) {
// Other details (nested DTO)
const od = (payload as any).otherDetails;
if (od && (od.exclusiveNotes || od.dosNotes || od.dontsNotes || od.tipsNotes || od.termsAndCondition)) {
await tx.activityOtherDetails.create({
data: {
activityXid: createdActivity.id,
exclusiveNotes: payload.exclusiveNotes ? String(payload.exclusiveNotes).substring(0,50) : null,
dosNotes: payload.dosNotes ? String(payload.dosNotes).substring(0,200) : null,
dontsNotes: payload.dontsNotes ? String(payload.dontsNotes).substring(0,200) : null,
tipsNotes: payload.tipsNotes ? String(payload.tipsNotes).substring(0,100) : null,
termsAndCondition: payload.termsAndCondition ? String(payload.termsAndCondition).substring(0,500) : null,
exclusiveNotes: od.exclusiveNotes ? String(od.exclusiveNotes).substring(0,50) : null,
dosNotes: od.dosNotes ? String(od.dosNotes).substring(0,200) : null,
dontsNotes: od.dontsNotes ? String(od.dontsNotes).substring(0,200) : null,
tipsNotes: od.tipsNotes ? String(od.tipsNotes).substring(0,100) : null,
termsAndCondition: od.termsAndCondition ? String(od.termsAndCondition).substring(0,500) : null,
}
});
}