sending venue details in after booking api
This commit is contained in:
@@ -43,6 +43,7 @@ export const handler = safeHandler(async (
|
||||
}
|
||||
|
||||
const result = await itineraryService.getActivityDetailsAfterBooking(
|
||||
userId,
|
||||
itineraryHeaderXid,
|
||||
);
|
||||
|
||||
|
||||
@@ -269,6 +269,28 @@ const formatDateKey = (date: Date) => {
|
||||
const addMinutes = (date: Date, minutes: number) =>
|
||||
new Date(date.getTime() + minutes * 60 * 1000);
|
||||
|
||||
const formatTicketTime = (value?: string | null) => {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return value.trim().toUpperCase().replace(/\s+(AM|PM)$/i, '$1');
|
||||
};
|
||||
|
||||
const formatTicketTimeRange = (
|
||||
startTime?: string | null,
|
||||
endTime?: string | null,
|
||||
) => {
|
||||
const formattedStartTime = formatTicketTime(startTime);
|
||||
const formattedEndTime = formatTicketTime(endTime);
|
||||
|
||||
if (formattedStartTime && formattedEndTime) {
|
||||
return `${formattedStartTime} - ${formattedEndTime}`;
|
||||
}
|
||||
|
||||
return formattedStartTime ?? formattedEndTime ?? null;
|
||||
};
|
||||
|
||||
const getDateRange = (fromDate: Date, toDate: Date) => {
|
||||
const dates: Date[] = [];
|
||||
const cursor = startOfDay(fromDate);
|
||||
@@ -4512,13 +4534,29 @@ export class ItineraryService {
|
||||
};
|
||||
}
|
||||
|
||||
async getActivityDetailsAfterBooking(itineraryHeaderXid: number) {
|
||||
// Fetch the itinerary header with complete details
|
||||
async getActivityDetailsAfterBooking(
|
||||
userXid: number,
|
||||
itineraryHeaderXid: number,
|
||||
) {
|
||||
const itineraryHeader = await this.prisma.itineraryHeader.findFirst({
|
||||
where: {
|
||||
id: itineraryHeaderXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
OR: [
|
||||
{
|
||||
ownerXid: userXid,
|
||||
},
|
||||
{
|
||||
ItineraryMembers: {
|
||||
some: {
|
||||
memberXid: userXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
@@ -4539,6 +4577,220 @@ export class ItineraryService {
|
||||
mobileNumber: true,
|
||||
},
|
||||
},
|
||||
ItineraryMembers: {
|
||||
where: {
|
||||
memberXid: userXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
memberXid: true,
|
||||
memberRole: true,
|
||||
memberStatus: true,
|
||||
member: {
|
||||
select: {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
emailAddress: true,
|
||||
mobileNumber: true,
|
||||
profileImage: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ItineraryActivities: {
|
||||
where: {
|
||||
activityXid: {
|
||||
not: null,
|
||||
},
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: [
|
||||
{
|
||||
occurenceDate: 'asc',
|
||||
},
|
||||
{
|
||||
startTime: 'asc',
|
||||
},
|
||||
{
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
],
|
||||
select: {
|
||||
id: true,
|
||||
displayOrder: true,
|
||||
occurenceDate: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
paxCount: true,
|
||||
totalAmount: true,
|
||||
bookingStatus: true,
|
||||
venue: {
|
||||
select: {
|
||||
id: true,
|
||||
venueName: true,
|
||||
venueLabel: true,
|
||||
},
|
||||
},
|
||||
ItineraryDetails: {
|
||||
where: {
|
||||
itineraryKind: 'ACTIVITY',
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
itineraryMember: {
|
||||
is: {
|
||||
memberXid: userXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
itineraryMemberXid: true,
|
||||
offlineCode: true,
|
||||
description1: true,
|
||||
description2: true,
|
||||
activityStatus: true,
|
||||
itineraryStatus: true,
|
||||
isPaid: true,
|
||||
paidOn: true,
|
||||
createdAt: true,
|
||||
},
|
||||
},
|
||||
itineraryActivitySelections: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
itineraryMember: {
|
||||
is: {
|
||||
memberXid: userXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
itineraryMemberXid: true,
|
||||
isFoodOpted: true,
|
||||
isTrainerOpted: true,
|
||||
isInActivityNavigationOpted: true,
|
||||
activityNavigationMode: {
|
||||
select: {
|
||||
id: true,
|
||||
navigationModeName: true,
|
||||
},
|
||||
},
|
||||
selectedFoodTypes: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityFoodTypeXid: true,
|
||||
activityFoodType: {
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeXid: true,
|
||||
foodType: {
|
||||
select: {
|
||||
id: true,
|
||||
foodTypeName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
selectedEquipments: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityEquipmentXid: true,
|
||||
activityEquipment: {
|
||||
select: {
|
||||
id: true,
|
||||
equipmentName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
activity: {
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
activityDescription: true,
|
||||
checkInAddress: true,
|
||||
checkInLat: true,
|
||||
checkInLong: true,
|
||||
activityDurationMins: true,
|
||||
foodAvailable: true,
|
||||
trainerAvailable: true,
|
||||
equipmentAvailable: true,
|
||||
pickUpDropAvailable: true,
|
||||
inActivityAvailable: true,
|
||||
ActivitiesMedia: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: {
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
mediaType: true,
|
||||
mediaFileName: true,
|
||||
isCoverImage: true,
|
||||
displayOrder: true,
|
||||
},
|
||||
},
|
||||
ActivityPickUpDetails: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
isPickUp: true,
|
||||
locationLat: true,
|
||||
locationLong: true,
|
||||
locationAddress: true,
|
||||
},
|
||||
},
|
||||
ActivityOtherDetails: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
exclusiveNotes: true,
|
||||
SafetyInstruction: true,
|
||||
Cancellations: true,
|
||||
dosNotes: true,
|
||||
dontsNotes: true,
|
||||
tipsNotes: true,
|
||||
termsAndCondition: true,
|
||||
},
|
||||
take: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -4546,122 +4798,186 @@ export class ItineraryService {
|
||||
throw new ApiError(404, 'Itinerary not found');
|
||||
}
|
||||
|
||||
// Find all itinerary activities linked to this itinerary header
|
||||
const itineraryActivities = await this.prisma.itineraryActivities.findMany({
|
||||
where: {
|
||||
itineraryHeaderXid,
|
||||
activityXid: {
|
||||
not: null,
|
||||
},
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: [
|
||||
{
|
||||
occurenceDate: 'asc',
|
||||
},
|
||||
{
|
||||
startTime: 'asc',
|
||||
},
|
||||
],
|
||||
select: {
|
||||
id: true,
|
||||
occurenceDate: true,
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
activity: {
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
activityDescription: true,
|
||||
checkInAddress: true,
|
||||
checkInLat: true,
|
||||
checkInLong: true,
|
||||
activityDurationMins: true,
|
||||
ActivitiesMedia: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
orderBy: {
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
mediaType: true,
|
||||
mediaFileName: true,
|
||||
isCoverImage: true,
|
||||
displayOrder: true,
|
||||
},
|
||||
},
|
||||
ActivityOtherDetails: {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
exclusiveNotes: true,
|
||||
SafetyInstruction: true,
|
||||
Cancellations: true,
|
||||
dosNotes: true,
|
||||
dontsNotes: true,
|
||||
tipsNotes: true,
|
||||
termsAndCondition: true,
|
||||
},
|
||||
take: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const itineraryMember = itineraryHeader.ItineraryMembers[0] ?? null;
|
||||
|
||||
if (itineraryActivities.length === 0) {
|
||||
if (itineraryHeader.ItineraryActivities.length === 0) {
|
||||
throw new ApiError(404, 'No activities found for this itinerary');
|
||||
}
|
||||
|
||||
// Process all activities
|
||||
const activities = await Promise.all(
|
||||
itineraryActivities.map(async (itineraryActivity) => {
|
||||
// Generate check-in code
|
||||
const checkInCode = generateCheckInCode();
|
||||
|
||||
// Attach presigned URLs to media files
|
||||
itineraryHeader.ItineraryActivities.map(async (itineraryActivity) => {
|
||||
const itineraryDetail = itineraryActivity.ItineraryDetails[0] ?? null;
|
||||
const activitySelection =
|
||||
itineraryActivity.itineraryActivitySelections[0] ?? null;
|
||||
const selectedFoodTypes = (
|
||||
activitySelection?.selectedFoodTypes ?? []
|
||||
).map(
|
||||
(selectedFoodType) =>
|
||||
selectedFoodType.activityFoodType.foodType.foodTypeName,
|
||||
);
|
||||
const selectedEquipments = (
|
||||
activitySelection?.selectedEquipments ?? []
|
||||
).map(
|
||||
(selectedEquipment) =>
|
||||
selectedEquipment.activityEquipment.equipmentName,
|
||||
);
|
||||
const selectedNavigationMode =
|
||||
activitySelection?.activityNavigationMode?.navigationModeName ?? null;
|
||||
const mediaWithUrls = await Promise.all(
|
||||
(itineraryActivity.activity?.ActivitiesMedia || []).map(
|
||||
(itineraryActivity.activity?.ActivitiesMedia ?? []).map(
|
||||
async (media) => ({
|
||||
...media,
|
||||
mediaUrl: await attachPresignedUrl(media.mediaFileName),
|
||||
}),
|
||||
),
|
||||
);
|
||||
const coverImage =
|
||||
mediaWithUrls.find((media) => media.isCoverImage) ??
|
||||
mediaWithUrls[0] ??
|
||||
null;
|
||||
const pickUpDetail =
|
||||
itineraryActivity.activity?.ActivityPickUpDetails.find(
|
||||
(detail) => detail.isPickUp && detail.locationAddress,
|
||||
) ??
|
||||
itineraryActivity.activity?.ActivityPickUpDetails.find(
|
||||
(detail) => detail.locationAddress,
|
||||
) ??
|
||||
null;
|
||||
const dropDetail =
|
||||
itineraryActivity.activity?.ActivityPickUpDetails.find(
|
||||
(detail) => !detail.isPickUp && detail.locationAddress,
|
||||
) ?? null;
|
||||
const bookingDate = formatDateKey(itineraryActivity.occurenceDate);
|
||||
const ticketTime = formatTicketTimeRange(
|
||||
itineraryActivity.startTime,
|
||||
itineraryActivity.endTime,
|
||||
);
|
||||
const venueName =
|
||||
itineraryActivity.venue?.venueLabel ??
|
||||
itineraryActivity.venue?.venueName ??
|
||||
null;
|
||||
const foodIncluded = selectedFoodTypes.length > 0 ? 'Yes' : 'No';
|
||||
const equipmentIncluded = selectedEquipments.length > 0 ? 'Yes' : 'No';
|
||||
const pickAndDropIncluded = itineraryActivity.activity?.pickUpDropAvailable
|
||||
? 'Yes'
|
||||
: 'No';
|
||||
const inActivityNavigation =
|
||||
selectedNavigationMode ??
|
||||
(activitySelection?.isInActivityNavigationOpted ? 'Yes' : 'No');
|
||||
|
||||
// Build activity details
|
||||
return {
|
||||
itineraryActivityId: itineraryActivity.id,
|
||||
activityId: itineraryActivity.activity?.id,
|
||||
activityTitle: itineraryActivity.activity?.activityTitle,
|
||||
activityDescription: itineraryActivity.activity?.activityDescription,
|
||||
bookingId: itineraryHeader.itineraryNo,
|
||||
activityId: itineraryActivity.activity?.id ?? null,
|
||||
activityTitle: itineraryActivity.activity?.activityTitle ?? null,
|
||||
activityDescription:
|
||||
itineraryActivity.activity?.activityDescription ?? null,
|
||||
occurenceDate: itineraryActivity.occurenceDate,
|
||||
startTime: itineraryActivity.startTime,
|
||||
endTime: itineraryActivity.endTime,
|
||||
duration: itineraryActivity.activity?.activityDurationMins,
|
||||
checkInCode,
|
||||
checkInAddress: itineraryActivity.activity?.checkInAddress,
|
||||
checkInLat: itineraryActivity.activity?.checkInLat,
|
||||
checkInLong: itineraryActivity.activity?.checkInLong,
|
||||
duration: itineraryActivity.activity?.activityDurationMins ?? null,
|
||||
checkInCode: itineraryDetail?.offlineCode ?? null,
|
||||
qrCodeValue: itineraryDetail?.offlineCode ?? null,
|
||||
checkInAddress: itineraryActivity.activity?.checkInAddress ?? null,
|
||||
checkInLat: itineraryActivity.activity?.checkInLat ?? null,
|
||||
checkInLong: itineraryActivity.activity?.checkInLong ?? null,
|
||||
bookingStatus:
|
||||
itineraryDetail?.activityStatus ??
|
||||
itineraryActivity.bookingStatus ??
|
||||
null,
|
||||
description1: itineraryDetail?.description1 ?? null,
|
||||
description2: itineraryDetail?.description2 ?? null,
|
||||
itineraryStatus: itineraryDetail?.itineraryStatus ?? null,
|
||||
isPaid: itineraryDetail?.isPaid ?? false,
|
||||
paidOn: itineraryDetail?.paidOn ?? null,
|
||||
paxCount: itineraryActivity.paxCount ?? null,
|
||||
totalAmount: itineraryActivity.totalAmount ?? null,
|
||||
venue: {
|
||||
id: itineraryActivity.venue?.id ?? null,
|
||||
venueName: itineraryActivity.venue?.venueName ?? null,
|
||||
venueLabel: itineraryActivity.venue?.venueLabel ?? null,
|
||||
displayName: venueName,
|
||||
},
|
||||
images: mediaWithUrls.map((media) => ({
|
||||
id: media.id,
|
||||
type: media.mediaType,
|
||||
fileName: media.mediaFileName,
|
||||
url: media.mediaUrl,
|
||||
isCover: media.isCoverImage,
|
||||
order: media.displayOrder,
|
||||
})),
|
||||
coverImage: mediaWithUrls
|
||||
.filter((m) => m.isCoverImage)
|
||||
.map((media) => ({
|
||||
id: media.id,
|
||||
url: media.mediaUrl,
|
||||
})),
|
||||
coverImage: coverImage
|
||||
? {
|
||||
id: coverImage.id,
|
||||
fileName: coverImage.mediaFileName,
|
||||
url: coverImage.mediaUrl,
|
||||
}
|
||||
: null,
|
||||
bookingInformation: {
|
||||
activityName: itineraryActivity.activity?.activityTitle ?? null,
|
||||
date: bookingDate,
|
||||
venue: venueName,
|
||||
venueName: itineraryActivity.venue?.venueName ?? null,
|
||||
venueLabel: itineraryActivity.venue?.venueLabel ?? null,
|
||||
time: ticketTime,
|
||||
startTime: formatTicketTime(itineraryActivity.startTime),
|
||||
endTime: formatTicketTime(itineraryActivity.endTime),
|
||||
},
|
||||
bookingIncluded: {
|
||||
food: foodIncluded,
|
||||
selectedFoodTypes,
|
||||
equipment: equipmentIncluded,
|
||||
selectedEquipments,
|
||||
pickAndDrop: pickAndDropIncluded,
|
||||
pickupLocation:
|
||||
pickUpDetail?.locationAddress ??
|
||||
itineraryActivity.activity?.checkInAddress ??
|
||||
null,
|
||||
dropLocation: dropDetail?.locationAddress ?? null,
|
||||
inActivityNavigation: inActivityNavigation ?? 'No',
|
||||
trainerOrGuide: activitySelection?.isTrainerOpted ? 'Yes' : 'No',
|
||||
},
|
||||
ticketCard: {
|
||||
qrCodeValue: itineraryDetail?.offlineCode ?? null,
|
||||
checkInCode: itineraryDetail?.offlineCode ?? null,
|
||||
activityTitle: itineraryActivity.activity?.activityTitle ?? null,
|
||||
date: bookingDate,
|
||||
venue: venueName,
|
||||
time: ticketTime,
|
||||
food: foodIncluded,
|
||||
equipments: equipmentIncluded,
|
||||
pickAndDrop: pickAndDropIncluded,
|
||||
inActivityNavigation: inActivityNavigation ?? 'No',
|
||||
},
|
||||
userSelections: {
|
||||
isFoodOpted: activitySelection?.isFoodOpted ?? false,
|
||||
selectedFoodTypes,
|
||||
isTrainerOpted: activitySelection?.isTrainerOpted ?? false,
|
||||
isInActivityNavigationOpted:
|
||||
activitySelection?.isInActivityNavigationOpted ?? false,
|
||||
selectedNavigationMode: selectedNavigationMode,
|
||||
selectedEquipments,
|
||||
},
|
||||
pickupDetails: {
|
||||
pickUpDropAvailable:
|
||||
itineraryActivity.activity?.pickUpDropAvailable ?? false,
|
||||
pickUpLocation: pickUpDetail
|
||||
? {
|
||||
id: pickUpDetail.id,
|
||||
locationAddress: pickUpDetail.locationAddress,
|
||||
locationLat: pickUpDetail.locationLat,
|
||||
locationLong: pickUpDetail.locationLong,
|
||||
}
|
||||
: null,
|
||||
dropLocation: dropDetail
|
||||
? {
|
||||
id: dropDetail.id,
|
||||
locationAddress: dropDetail.locationAddress,
|
||||
locationLat: dropDetail.locationLat,
|
||||
locationLong: dropDetail.locationLong,
|
||||
}
|
||||
: null,
|
||||
},
|
||||
activityInfo: {
|
||||
exclusiveNotes:
|
||||
itineraryActivity.activity?.ActivityOtherDetails[0]
|
||||
@@ -4709,7 +5025,24 @@ export class ItineraryService {
|
||||
email: itineraryHeader.owner?.emailAddress,
|
||||
phone: itineraryHeader.owner?.mobileNumber,
|
||||
},
|
||||
viewer: itineraryMember
|
||||
? {
|
||||
itineraryMemberXid: itineraryMember.id,
|
||||
memberXid: itineraryMember.memberXid,
|
||||
memberRole: itineraryMember.memberRole,
|
||||
memberStatus: itineraryMember.memberStatus,
|
||||
firstName: itineraryMember.member.firstName,
|
||||
lastName: itineraryMember.member.lastName,
|
||||
email: itineraryMember.member.emailAddress,
|
||||
phone: itineraryMember.member.mobileNumber,
|
||||
profileImage: itineraryMember.member.profileImage,
|
||||
profileImagePresignedUrl: await attachPresignedUrl(
|
||||
itineraryMember.member.profileImage,
|
||||
),
|
||||
}
|
||||
: null,
|
||||
totalActivities: activities.length,
|
||||
ticketCards: activities.map((activity) => activity.ticketCard),
|
||||
activities,
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user