made the api for storing the selections for the itinerary of the user
This commit is contained in:
@@ -1369,6 +1369,7 @@ model ActivityFoodTypes {
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
deletedAt DateTime? @map("deleted_at")
|
||||
itineraryActivitySelectionFoodTypes ItineraryActivitySelectionFoodType[]
|
||||
|
||||
@@map("activity_food_types")
|
||||
@@schema("act")
|
||||
@@ -1419,6 +1420,7 @@ model ActivityEquipments {
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
deletedAt DateTime? @map("deleted_at")
|
||||
ActivityEquipmentTaxes ActivityEquipmentTaxes[]
|
||||
itineraryActivitySelectionEquipments ItineraryActivitySelectionEquipment[]
|
||||
|
||||
@@map("activity_equipments")
|
||||
@@schema("act")
|
||||
@@ -1454,6 +1456,7 @@ model ActivityNavigationModes {
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
deletedAt DateTime? @map("deleted_at")
|
||||
ActivityNavigationModesTaxes ActivityNavigationModesTaxes[]
|
||||
ItineraryActivitySelections ItineraryActivitySelection[]
|
||||
|
||||
@@map("activity_navigation_modes")
|
||||
@@schema("act")
|
||||
@@ -1680,6 +1683,7 @@ model ItineraryMembers {
|
||||
User User? @relation(fields: [userId], references: [id])
|
||||
userId Int?
|
||||
ItineraryDetails ItineraryDetails[]
|
||||
itineraryActivitySelections ItineraryActivitySelection[]
|
||||
|
||||
@@map("itinerary_members")
|
||||
@@schema("itn")
|
||||
@@ -1740,11 +1744,67 @@ model ItineraryActivities {
|
||||
ActivitySOSDetails ActivitySOSDetails[]
|
||||
ActivityFeedbacks ActivityFeedbacks[]
|
||||
ItineraryDetails ItineraryDetails[]
|
||||
itineraryActivitySelections ItineraryActivitySelection[]
|
||||
|
||||
@@map("itinerary_activities")
|
||||
@@schema("itn")
|
||||
}
|
||||
|
||||
model ItineraryActivitySelection {
|
||||
id Int @id @default(autoincrement())
|
||||
itineraryActivityXid Int @map("itinerary_activity_xid")
|
||||
itineraryActivity ItineraryActivities @relation(fields: [itineraryActivityXid], references: [id], onDelete: Cascade)
|
||||
itineraryMemberXid Int @map("itinerary_member_xid")
|
||||
itineraryMember ItineraryMembers @relation(fields: [itineraryMemberXid], references: [id], onDelete: Cascade)
|
||||
isFoodOpted Boolean @default(false) @map("is_food_opted")
|
||||
isTrainerOpted Boolean @default(false) @map("is_trainer_opted")
|
||||
isInActivityNavigationOpted Boolean @default(false) @map("is_in_activity_navigation_opted")
|
||||
activityNavigationModeXid Int? @map("activity_navigation_mode_xid")
|
||||
activityNavigationMode ActivityNavigationModes? @relation(fields: [activityNavigationModeXid], references: [id], onDelete: Restrict)
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
deletedAt DateTime? @map("deleted_at")
|
||||
selectedFoodTypes ItineraryActivitySelectionFoodType[]
|
||||
selectedEquipments ItineraryActivitySelectionEquipment[]
|
||||
|
||||
@@unique([itineraryActivityXid, itineraryMemberXid])
|
||||
@@map("itinerary_activity_selection")
|
||||
@@schema("itn")
|
||||
}
|
||||
|
||||
model ItineraryActivitySelectionFoodType {
|
||||
id Int @id @default(autoincrement())
|
||||
itineraryActivitySelectionXid Int @map("itinerary_activity_selection_xid")
|
||||
itineraryActivitySelection ItineraryActivitySelection @relation(fields: [itineraryActivitySelectionXid], references: [id], onDelete: Cascade)
|
||||
activityFoodTypeXid Int @map("activity_food_type_xid")
|
||||
activityFoodType ActivityFoodTypes @relation(fields: [activityFoodTypeXid], references: [id], onDelete: Cascade)
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
deletedAt DateTime? @map("deleted_at")
|
||||
|
||||
@@unique([itineraryActivitySelectionXid, activityFoodTypeXid])
|
||||
@@map("itinerary_activity_selection_food_type")
|
||||
@@schema("itn")
|
||||
}
|
||||
|
||||
model ItineraryActivitySelectionEquipment {
|
||||
id Int @id @default(autoincrement())
|
||||
itineraryActivitySelectionXid Int @map("itinerary_activity_selection_xid")
|
||||
itineraryActivitySelection ItineraryActivitySelection @relation(fields: [itineraryActivitySelectionXid], references: [id], onDelete: Cascade)
|
||||
activityEquipmentXid Int @map("activity_equipment_xid")
|
||||
activityEquipment ActivityEquipments @relation(fields: [activityEquipmentXid], references: [id], onDelete: Cascade)
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
deletedAt DateTime? @map("deleted_at")
|
||||
|
||||
@@unique([itineraryActivitySelectionXid, activityEquipmentXid])
|
||||
@@map("itinerary_activity_selection_equipment")
|
||||
@@schema("itn")
|
||||
}
|
||||
|
||||
model ActivitySOSDetails {
|
||||
id Int @id @default(autoincrement())
|
||||
itineraryActivityXid Int @map("itinerary_activity_xid")
|
||||
|
||||
@@ -453,6 +453,21 @@ saveUserItinerary:
|
||||
path: /itinerary/save-user-itinerary
|
||||
method: post
|
||||
|
||||
saveItineraryActivitySelections:
|
||||
handler: src/modules/user/handlers/itinerary/saveItineraryActivitySelections.handler
|
||||
memorySize: 512
|
||||
package:
|
||||
patterns:
|
||||
- 'src/modules/user/**'
|
||||
- ${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: /itinerary/save-itinerary-activity-selections
|
||||
method: post
|
||||
|
||||
getAllUserSavedItineraries:
|
||||
handler: src/modules/user/handlers/itinerary/getAllUserSavedItineraries.handler
|
||||
memorySize: 512
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
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 { ItineraryService } from '../../services/itinerary.service';
|
||||
|
||||
const itineraryService = new ItineraryService(prismaClient);
|
||||
|
||||
export const handler = safeHandler(async (
|
||||
event: APIGatewayProxyEvent,
|
||||
context?: Context,
|
||||
): Promise<APIGatewayProxyResult> => {
|
||||
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 verifyUserToken(token);
|
||||
const userId = Number(userInfo.id);
|
||||
|
||||
if (!userId || Number.isNaN(userId)) {
|
||||
throw new ApiError(400, 'Invalid user ID');
|
||||
}
|
||||
|
||||
let body: Record<string, any> = {};
|
||||
if (event.body) {
|
||||
try {
|
||||
body = JSON.parse(event.body);
|
||||
} catch {
|
||||
throw new ApiError(400, 'Invalid JSON body');
|
||||
}
|
||||
}
|
||||
|
||||
const itineraryActivityXid = Number(body.itineraryActivityXid);
|
||||
if (!Number.isInteger(itineraryActivityXid) || itineraryActivityXid <= 0) {
|
||||
throw new ApiError(400, 'itineraryActivityXid is required.');
|
||||
}
|
||||
|
||||
const selectedEquipmentIds = Array.isArray(body.selectedEquipmentIds)
|
||||
? body.selectedEquipmentIds.map((id: unknown) => Number(id))
|
||||
: [];
|
||||
const selectedFoodTypeIds = Array.isArray(body.selectedFoodTypeIds)
|
||||
? body.selectedFoodTypeIds.map((id: unknown) => Number(id))
|
||||
: [];
|
||||
|
||||
if (selectedEquipmentIds.some((id) => !Number.isInteger(id) || id <= 0)) {
|
||||
throw new ApiError(400, 'selectedEquipmentIds must contain valid ids.');
|
||||
}
|
||||
|
||||
if (selectedFoodTypeIds.some((id) => !Number.isInteger(id) || id <= 0)) {
|
||||
throw new ApiError(400, 'selectedFoodTypeIds must contain valid ids.');
|
||||
}
|
||||
|
||||
const toOptionalId = (value: unknown) => {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parsed = Number(value);
|
||||
if (!Number.isInteger(parsed) || parsed <= 0) {
|
||||
throw new ApiError(400, 'One or more selected option ids are invalid.');
|
||||
}
|
||||
|
||||
return parsed;
|
||||
};
|
||||
|
||||
const result = await itineraryService.saveItineraryActivitySelections(userId, {
|
||||
itineraryActivityXid,
|
||||
isFoodOpted:
|
||||
body.isFoodOpted === undefined ? false : Boolean(body.isFoodOpted),
|
||||
selectedFoodTypeIds,
|
||||
isTrainerOpted:
|
||||
body.isTrainerOpted === undefined ? false : Boolean(body.isTrainerOpted),
|
||||
isInActivityNavigationOpted:
|
||||
body.isInActivityNavigationOpted === undefined
|
||||
? false
|
||||
: Boolean(body.isInActivityNavigationOpted),
|
||||
selectedNavigationModeXid: toOptionalId(body.selectedNavigationModeXid),
|
||||
selectedEquipmentIds,
|
||||
});
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
success: true,
|
||||
message: 'Itinerary activity selections saved successfully.',
|
||||
data: result,
|
||||
}),
|
||||
};
|
||||
});
|
||||
@@ -1058,6 +1058,76 @@ export class ItineraryService {
|
||||
paxCount: true,
|
||||
totalAmount: true,
|
||||
bookingStatus: true,
|
||||
itineraryActivitySelections: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
itineraryMember: {
|
||||
memberXid: userXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
},
|
||||
take: 1,
|
||||
select: {
|
||||
id: true,
|
||||
itineraryMemberXid: true,
|
||||
isFoodOpted: true,
|
||||
isTrainerOpted: true,
|
||||
isInActivityNavigationOpted: true,
|
||||
activityNavigationModeXid: true,
|
||||
selectedFoodTypes: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityFoodTypeXid: true,
|
||||
activityFoodType: {
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeXid: true,
|
||||
foodType: {
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
activityNavigationMode: {
|
||||
select: {
|
||||
id: true,
|
||||
navigationModeName: true,
|
||||
isInActivityChargeable: true,
|
||||
navigationModesBasePrice: true,
|
||||
navigationModesTotalPrice: true,
|
||||
},
|
||||
},
|
||||
selectedEquipments: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityEquipmentXid: true,
|
||||
activityEquipment: {
|
||||
select: {
|
||||
id: true,
|
||||
equipmentName: true,
|
||||
isEquipmentChargeable: true,
|
||||
equipmentBasePrice: true,
|
||||
equipmentTotalPrice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
activity: {
|
||||
select: {
|
||||
id: true,
|
||||
@@ -1075,6 +1145,8 @@ export class ItineraryService {
|
||||
inActivityIsChargeable: true,
|
||||
pickUpDropAvailable: true,
|
||||
pickUpDropIsChargeable: true,
|
||||
equipmentAvailable: true,
|
||||
equipmentIsChargeable: true,
|
||||
activityFoodTypes: {
|
||||
where: {
|
||||
isActive: true,
|
||||
@@ -1126,6 +1198,19 @@ export class ItineraryService {
|
||||
navigationModesTotalPrice: true,
|
||||
},
|
||||
},
|
||||
ActivityEquipments: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
equipmentName: true,
|
||||
isEquipmentChargeable: true,
|
||||
equipmentBasePrice: true,
|
||||
equipmentTotalPrice: true,
|
||||
},
|
||||
},
|
||||
ActivityPickUpDetails: {
|
||||
where: {
|
||||
isActive: true,
|
||||
@@ -1228,6 +1313,7 @@ export class ItineraryService {
|
||||
item.activity.ActivitiesMedia[0] ??
|
||||
null
|
||||
: null;
|
||||
const userSelection = item.itineraryActivitySelections[0] ?? null;
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
@@ -1303,6 +1389,17 @@ export class ItineraryService {
|
||||
}),
|
||||
),
|
||||
},
|
||||
equipmentDetails: {
|
||||
equipmentAvailable: item.activity.equipmentAvailable,
|
||||
equipmentIsChargeable: item.activity.equipmentIsChargeable,
|
||||
equipments: item.activity.ActivityEquipments.map((equipment) => ({
|
||||
id: equipment.id,
|
||||
equipmentName: equipment.equipmentName,
|
||||
isEquipmentChargeable: equipment.isEquipmentChargeable,
|
||||
equipmentBasePrice: equipment.equipmentBasePrice,
|
||||
equipmentTotalPrice: equipment.equipmentTotalPrice,
|
||||
})),
|
||||
},
|
||||
pickUpDetails: {
|
||||
pickUpDropAvailable: item.activity.pickUpDropAvailable,
|
||||
pickUpDropIsChargeable:
|
||||
@@ -1340,6 +1437,40 @@ export class ItineraryService {
|
||||
},
|
||||
}
|
||||
: null,
|
||||
userSelections: userSelection
|
||||
? {
|
||||
id: userSelection.id,
|
||||
itineraryMemberXid: userSelection.itineraryMemberXid,
|
||||
isFoodOpted: userSelection.isFoodOpted,
|
||||
selectedFoodTypeIds: userSelection.selectedFoodTypes.map(
|
||||
(foodType) => foodType.activityFoodTypeXid,
|
||||
),
|
||||
selectedFoodTypes: userSelection.selectedFoodTypes.map(
|
||||
(foodType) => ({
|
||||
id: foodType.id,
|
||||
activityFoodTypeXid: foodType.activityFoodTypeXid,
|
||||
activityFoodType: foodType.activityFoodType,
|
||||
}),
|
||||
),
|
||||
isTrainerOpted: userSelection.isTrainerOpted,
|
||||
isInActivityNavigationOpted:
|
||||
userSelection.isInActivityNavigationOpted,
|
||||
selectedNavigationModeXid:
|
||||
userSelection.activityNavigationModeXid,
|
||||
selectedNavigationMode:
|
||||
userSelection.activityNavigationMode ?? null,
|
||||
selectedEquipmentIds: userSelection.selectedEquipments.map(
|
||||
(equipment) => equipment.activityEquipmentXid,
|
||||
),
|
||||
selectedEquipments: userSelection.selectedEquipments.map(
|
||||
(equipment) => ({
|
||||
id: equipment.id,
|
||||
activityEquipmentXid: equipment.activityEquipmentXid,
|
||||
activityEquipment: equipment.activityEquipment,
|
||||
}),
|
||||
),
|
||||
}
|
||||
: null,
|
||||
venue: item.venue,
|
||||
scheduleHeader: item.scheduledHeader,
|
||||
};
|
||||
@@ -1381,6 +1512,382 @@ export class ItineraryService {
|
||||
};
|
||||
}
|
||||
|
||||
async saveItineraryActivitySelections(
|
||||
userXid: number,
|
||||
payload: {
|
||||
itineraryActivityXid: number;
|
||||
isFoodOpted?: boolean;
|
||||
selectedFoodTypeIds?: number[];
|
||||
isTrainerOpted?: boolean;
|
||||
isInActivityNavigationOpted?: boolean;
|
||||
selectedNavigationModeXid?: number | null;
|
||||
selectedEquipmentIds?: number[];
|
||||
},
|
||||
) {
|
||||
const selectedFoodTypeIds = Array.from(
|
||||
new Set((payload.selectedFoodTypeIds ?? []).map(Number)),
|
||||
);
|
||||
const selectedEquipmentIds = Array.from(
|
||||
new Set((payload.selectedEquipmentIds ?? []).map(Number)),
|
||||
);
|
||||
|
||||
return this.prisma.$transaction(async (tx) => {
|
||||
const itineraryActivity = await tx.itineraryActivities.findFirst({
|
||||
where: {
|
||||
id: payload.itineraryActivityXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
itineraryHeader: {
|
||||
ItineraryMembers: {
|
||||
some: {
|
||||
memberXid: userXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
itineraryHeaderXid: true,
|
||||
itineraryType: true,
|
||||
activityXid: true,
|
||||
activity: {
|
||||
select: {
|
||||
id: true,
|
||||
foodAvailable: true,
|
||||
trainerAvailable: true,
|
||||
inActivityAvailable: true,
|
||||
equipmentAvailable: true,
|
||||
activityFoodTypes: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeXid: true,
|
||||
foodType: {
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ActivityNavigationModes: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
navigationModeName: true,
|
||||
isInActivityChargeable: true,
|
||||
navigationModesBasePrice: true,
|
||||
navigationModesTotalPrice: true,
|
||||
},
|
||||
},
|
||||
ActivityEquipments: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
equipmentName: true,
|
||||
isEquipmentChargeable: true,
|
||||
equipmentBasePrice: true,
|
||||
equipmentTotalPrice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!itineraryActivity) {
|
||||
throw new ApiError(404, 'Itinerary activity not found for this user.');
|
||||
}
|
||||
|
||||
if (
|
||||
itineraryActivity.itineraryType !== 'ACTIVITY' ||
|
||||
!itineraryActivity.activityXid ||
|
||||
!itineraryActivity.activity
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'Selections can only be stored for itinerary items linked to an activity.',
|
||||
);
|
||||
}
|
||||
|
||||
const itineraryMember = await tx.itineraryMembers.findFirst({
|
||||
where: {
|
||||
itineraryHeaderXid: itineraryActivity.itineraryHeaderXid,
|
||||
memberXid: userXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!itineraryMember) {
|
||||
throw new ApiError(404, 'Itinerary member record not found.');
|
||||
}
|
||||
|
||||
if (selectedEquipmentIds.some((id) => !Number.isInteger(id) || id <= 0)) {
|
||||
throw new ApiError(400, 'selectedEquipmentIds must contain valid ids.');
|
||||
}
|
||||
|
||||
if (selectedFoodTypeIds.some((id) => !Number.isInteger(id) || id <= 0)) {
|
||||
throw new ApiError(400, 'selectedFoodTypeIds must contain valid ids.');
|
||||
}
|
||||
|
||||
const isFoodOpted = Boolean(payload.isFoodOpted);
|
||||
const isTrainerOpted = Boolean(payload.isTrainerOpted);
|
||||
const isInActivityNavigationOpted = Boolean(
|
||||
payload.isInActivityNavigationOpted,
|
||||
);
|
||||
|
||||
const selectedNavigationModeXid =
|
||||
payload.selectedNavigationModeXid === undefined ||
|
||||
payload.selectedNavigationModeXid === null
|
||||
? null
|
||||
: Number(payload.selectedNavigationModeXid);
|
||||
|
||||
if (
|
||||
selectedNavigationModeXid !== null &&
|
||||
(!Number.isInteger(selectedNavigationModeXid) ||
|
||||
selectedNavigationModeXid <= 0)
|
||||
) {
|
||||
throw new ApiError(400, 'selectedNavigationModeXid must be a valid id.');
|
||||
}
|
||||
|
||||
const availableFoodTypeIds = new Set(
|
||||
itineraryActivity.activity.activityFoodTypes.map((item) => item.id),
|
||||
);
|
||||
const availableNavigationModeIds = new Set(
|
||||
itineraryActivity.activity.ActivityNavigationModes.map((item) => item.id),
|
||||
);
|
||||
const availableEquipmentIds = new Set(
|
||||
itineraryActivity.activity.ActivityEquipments.map((item) => item.id),
|
||||
);
|
||||
|
||||
if (isFoodOpted) {
|
||||
if (!itineraryActivity.activity.foodAvailable) {
|
||||
throw new ApiError(400, 'Food is not available for this activity.');
|
||||
}
|
||||
|
||||
if (
|
||||
itineraryActivity.activity.activityFoodTypes.length > 0 &&
|
||||
!selectedFoodTypeIds.length
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'selectedFoodTypeIds is required when food is opted.',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
selectedFoodTypeIds.some((id) => !availableFoodTypeIds.has(id))
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'One or more selected food types do not belong to this activity.',
|
||||
);
|
||||
}
|
||||
} else if (selectedFoodTypeIds.length) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'selectedFoodTypeIds cannot be sent when food is not opted.',
|
||||
);
|
||||
}
|
||||
|
||||
if (isTrainerOpted && !itineraryActivity.activity.trainerAvailable) {
|
||||
throw new ApiError(400, 'Trainer is not available for this activity.');
|
||||
}
|
||||
|
||||
if (isInActivityNavigationOpted) {
|
||||
if (!itineraryActivity.activity.inActivityAvailable) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'In-activity navigation is not available for this activity.',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
itineraryActivity.activity.ActivityNavigationModes.length > 0 &&
|
||||
!selectedNavigationModeXid
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'selectedNavigationModeXid is required when navigation is opted.',
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
selectedNavigationModeXid &&
|
||||
!availableNavigationModeIds.has(selectedNavigationModeXid)
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'Selected navigation mode does not belong to this activity.',
|
||||
);
|
||||
}
|
||||
} else if (selectedNavigationModeXid) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'selectedNavigationModeXid cannot be sent when navigation is not opted.',
|
||||
);
|
||||
}
|
||||
|
||||
if (selectedEquipmentIds.length) {
|
||||
if (!itineraryActivity.activity.equipmentAvailable) {
|
||||
throw new ApiError(400, 'Equipment is not available for this activity.');
|
||||
}
|
||||
|
||||
if (
|
||||
selectedEquipmentIds.some((id) => !availableEquipmentIds.has(id))
|
||||
) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'One or more selected equipments do not belong to this activity.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const selection = await tx.itineraryActivitySelection.upsert({
|
||||
where: {
|
||||
itineraryActivityXid_itineraryMemberXid: {
|
||||
itineraryActivityXid: itineraryActivity.id,
|
||||
itineraryMemberXid: itineraryMember.id,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
itineraryActivityXid: itineraryActivity.id,
|
||||
itineraryMemberXid: itineraryMember.id,
|
||||
isFoodOpted,
|
||||
isTrainerOpted,
|
||||
isInActivityNavigationOpted,
|
||||
activityNavigationModeXid: isInActivityNavigationOpted
|
||||
? selectedNavigationModeXid
|
||||
: null,
|
||||
isActive: true,
|
||||
},
|
||||
update: {
|
||||
isFoodOpted,
|
||||
isTrainerOpted,
|
||||
isInActivityNavigationOpted,
|
||||
activityNavigationModeXid: isInActivityNavigationOpted
|
||||
? selectedNavigationModeXid
|
||||
: null,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.itineraryActivitySelectionFoodType.deleteMany({
|
||||
where: {
|
||||
itineraryActivitySelectionXid: selection.id,
|
||||
},
|
||||
});
|
||||
|
||||
await tx.itineraryActivitySelectionEquipment.deleteMany({
|
||||
where: {
|
||||
itineraryActivitySelectionXid: selection.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (selectedFoodTypeIds.length) {
|
||||
await tx.itineraryActivitySelectionFoodType.createMany({
|
||||
data: selectedFoodTypeIds.map((activityFoodTypeXid) => ({
|
||||
itineraryActivitySelectionXid: selection.id,
|
||||
activityFoodTypeXid,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
if (selectedEquipmentIds.length) {
|
||||
await tx.itineraryActivitySelectionEquipment.createMany({
|
||||
data: selectedEquipmentIds.map((activityEquipmentXid) => ({
|
||||
itineraryActivitySelectionXid: selection.id,
|
||||
activityEquipmentXid,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
return tx.itineraryActivitySelection.findUnique({
|
||||
where: {
|
||||
id: selection.id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
itineraryActivityXid: true,
|
||||
itineraryMemberXid: true,
|
||||
isFoodOpted: true,
|
||||
isTrainerOpted: true,
|
||||
isInActivityNavigationOpted: true,
|
||||
activityNavigationModeXid: true,
|
||||
selectedFoodTypes: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityFoodTypeXid: true,
|
||||
activityFoodType: {
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeXid: true,
|
||||
foodType: {
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
activityNavigationMode: {
|
||||
select: {
|
||||
id: true,
|
||||
navigationModeName: true,
|
||||
isInActivityChargeable: true,
|
||||
navigationModesBasePrice: true,
|
||||
navigationModesTotalPrice: true,
|
||||
},
|
||||
},
|
||||
selectedEquipments: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityEquipmentXid: true,
|
||||
activityEquipment: {
|
||||
select: {
|
||||
id: true,
|
||||
equipmentName: true,
|
||||
isEquipmentChargeable: true,
|
||||
equipmentBasePrice: true,
|
||||
equipmentTotalPrice: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async getMatchingBucketInterestedActivities(
|
||||
userXid: number,
|
||||
payload: {
|
||||
|
||||
Reference in New Issue
Block a user