Add CreateActivityDto and update createFullActivity method for activity creation
This commit is contained in:
125
src/modules/host/dto/createActivity.schema.ts
Normal file
125
src/modules/host/dto/createActivity.schema.ts
Normal 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>;
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user