feat: enhance scheduling service to support instant booking and late check-in options, and improve activity listing logic
This commit is contained in:
@@ -7,6 +7,7 @@ import { ACTIVITY_AM_INTERNAL_STATUS, ACTIVITY_INTERNAL_STATUS } from '../../../
|
||||
import { getPresignedUrl } from '../../../common/middlewares/aws/getPreSignedUrl';
|
||||
|
||||
import config from '@/config/config';
|
||||
import { isNotIn } from 'class-validator';
|
||||
// function deg2rad(deg) {
|
||||
// return deg * (Math.PI / 180);
|
||||
// }
|
||||
@@ -28,6 +29,28 @@ import config from '@/config/config';
|
||||
// return R * c;
|
||||
// }
|
||||
|
||||
const attachMediaWithPresignedUrl = async (mediaArr = []) => {
|
||||
return (
|
||||
await Promise.all(
|
||||
mediaArr.map(async (m) => {
|
||||
if (!m?.mediaFileName) return null;
|
||||
|
||||
const key = m.mediaFileName.startsWith('http')
|
||||
? new URL(m.mediaFileName).pathname.replace(/^\/+/, '')
|
||||
: m.mediaFileName;
|
||||
|
||||
return {
|
||||
id: m.id,
|
||||
mediaType: m.mediaType,
|
||||
mediaFileName: m.mediaFileName,
|
||||
presignedUrl: await getPresignedUrl(bucket, key),
|
||||
};
|
||||
})
|
||||
)
|
||||
).filter(Boolean);
|
||||
};
|
||||
|
||||
|
||||
|
||||
const bucket = config.aws.bucketName;
|
||||
|
||||
@@ -62,7 +85,7 @@ export class UserService {
|
||||
}
|
||||
|
||||
async getAllInterestDetails() {
|
||||
return await this.prisma.interests.findMany({
|
||||
const interests = await this.prisma.interests.findMany({
|
||||
where: { isActive: true },
|
||||
select: {
|
||||
id: true,
|
||||
@@ -72,6 +95,20 @@ export class UserService {
|
||||
displayOrder: true
|
||||
}
|
||||
})
|
||||
|
||||
for (const interest of interests) {
|
||||
if (interest.interestImage) {
|
||||
const key = interest.interestImage.startsWith('http')
|
||||
? new URL(interest.interestImage).pathname.replace(/^\/+/, '')
|
||||
: interest.interestImage;
|
||||
|
||||
(interest as any).presignedUrl = await getPresignedUrl(bucket, key);
|
||||
} else {
|
||||
(interest as any).presignedUrl = null;
|
||||
}
|
||||
}
|
||||
|
||||
return interests;
|
||||
}
|
||||
|
||||
|
||||
@@ -250,6 +287,25 @@ export class UserService {
|
||||
|
||||
async getLandingPageAllDetails(userId: number) {
|
||||
const data = await this.prisma.$transaction(async (tx) => {
|
||||
const userAddressDetails = await tx.userAddressDetails.findFirst({
|
||||
where: { userXid: userId },
|
||||
select: {
|
||||
id: true,
|
||||
address1: true,
|
||||
address2: true,
|
||||
pinCode: true,
|
||||
locationName: true,
|
||||
stateXid: true,
|
||||
cityXid: true,
|
||||
countryXid: true,
|
||||
locationLat: true,
|
||||
locationLong: true,
|
||||
}
|
||||
})
|
||||
|
||||
const userStateXid = userAddressDetails?.stateXid ?? null;
|
||||
const userCountryXid = userAddressDetails?.countryXid ?? null;
|
||||
|
||||
const userInterests = await tx.userInterests.findMany({
|
||||
where: { userXid: userId, isActive: true },
|
||||
select: {
|
||||
@@ -278,22 +334,6 @@ export class UserService {
|
||||
}
|
||||
})
|
||||
|
||||
const userAddressDetails = await tx.userAddressDetails.findFirst({
|
||||
where: { userXid: userId },
|
||||
select: {
|
||||
id: true,
|
||||
address1: true,
|
||||
address2: true,
|
||||
pinCode: true,
|
||||
locationName: true,
|
||||
stateXid: true,
|
||||
cityXid: true,
|
||||
countryXid: true,
|
||||
locationLat: true,
|
||||
locationLong: true,
|
||||
}
|
||||
})
|
||||
|
||||
if (!activitiyTypesOfUserInterests.length) {
|
||||
return {
|
||||
userAddressDetails,
|
||||
@@ -355,6 +395,92 @@ export class UserService {
|
||||
},
|
||||
});
|
||||
|
||||
const otherStatesActivities = await tx.activities.findMany({
|
||||
where: {
|
||||
isActive: true,
|
||||
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
|
||||
// ✅ Only user's interest types
|
||||
activityTypeXid: {
|
||||
in: activitiyTypesOfUserInterests.map(at => at.id),
|
||||
},
|
||||
|
||||
// ✅ Exclude user's state
|
||||
...(userStateXid && {
|
||||
checkInStateXid: { not: userStateXid },
|
||||
}),
|
||||
...(userCountryXid && {
|
||||
checkInCountryXid: userCountryXid,
|
||||
}),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
activityType: {
|
||||
select: {
|
||||
energyLevel: {
|
||||
select: {
|
||||
id: true,
|
||||
energyLevelName: true,
|
||||
energyColor: true,
|
||||
energyIcon: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true },
|
||||
select: {
|
||||
id: true,
|
||||
mediaFileName: true,
|
||||
mediaType: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const overSeasActivity = await tx.activities.findMany({
|
||||
where: {
|
||||
isActive: true,
|
||||
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
|
||||
// ✅ Only user's interest types
|
||||
activityTypeXid: {
|
||||
in: activitiyTypesOfUserInterests.map(at => at.id),
|
||||
},
|
||||
|
||||
// ✅ Exclude user's state
|
||||
...(userCountryXid && {
|
||||
checkInCountryXid: { not: userCountryXid },
|
||||
}),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
activityType: {
|
||||
select: {
|
||||
energyLevel: {
|
||||
select: {
|
||||
id: true,
|
||||
energyLevelName: true,
|
||||
energyColor: true,
|
||||
energyIcon: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true },
|
||||
select: {
|
||||
id: true,
|
||||
mediaFileName: true,
|
||||
mediaType: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const formattedActivities = await Promise.all(
|
||||
activities.map(async (activity) => {
|
||||
@@ -371,35 +497,39 @@ export class UserService {
|
||||
}
|
||||
}
|
||||
|
||||
const media = await Promise.all(
|
||||
(activity.ActivitiesMedia ?? []).map(async (m) => {
|
||||
if (!m?.mediaFileName) return null;
|
||||
|
||||
const key = m.mediaFileName.startsWith('http')
|
||||
? new URL(m.mediaFileName).pathname.replace(/^\/+/, '')
|
||||
: m.mediaFileName;
|
||||
|
||||
return {
|
||||
id: m.id,
|
||||
mediaType: m.mediaType,
|
||||
mediaFileName: m.mediaFileName,
|
||||
presignedUrl: await getPresignedUrl(bucket, key),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
interestXid: activity.activityType.interestXid,
|
||||
activityTitle: activity.activityTitle,
|
||||
activityDurationMins: activity.activityDurationMins,
|
||||
sustainabilityScore: activity.sustainabilityScore,
|
||||
energyLevel: activity.activityType?.energyLevel ?? null,
|
||||
cheapestPrice,
|
||||
media: media.filter(Boolean), // ✅ IMPORTANT
|
||||
rating: 4,
|
||||
distanceFromUser: 2,
|
||||
connectionsCount: 10,
|
||||
energyLevel: activity.activityType?.energyLevel ?? null,
|
||||
media: await attachMediaWithPresignedUrl(activity.ActivitiesMedia),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const formattedOtherStatesActivities = await Promise.all(
|
||||
otherStatesActivities.map(async (activity) => ({
|
||||
activityTitle: activity.activityTitle,
|
||||
energyLevel: activity.activityType.energyLevel,
|
||||
media: await attachMediaWithPresignedUrl(activity.ActivitiesMedia),
|
||||
}))
|
||||
);
|
||||
|
||||
const formattedOverSeasActivities = await Promise.all(
|
||||
overSeasActivity.map(async (activity) => ({
|
||||
activityTitle: activity.activityTitle,
|
||||
energyLevel: activity.activityType.energyLevel,
|
||||
media: await attachMediaWithPresignedUrl(activity.ActivitiesMedia),
|
||||
}))
|
||||
);
|
||||
|
||||
|
||||
|
||||
const interestsWithActivities = userInterests.map(ui => {
|
||||
const activitiesForInterest = formattedActivities.filter(
|
||||
act => act.interestXid === ui.interestXid
|
||||
@@ -419,7 +549,13 @@ export class UserService {
|
||||
|
||||
return {
|
||||
userAddressDetails,
|
||||
experiencesLogged: 25,
|
||||
citiesDiscovered: 10,
|
||||
loggedInNetworkCount: 0,
|
||||
citiesInNetworkCount: 0,
|
||||
interests: interestsWithActivities,
|
||||
otherStatesActivities: formattedOtherStatesActivities,
|
||||
overSeasActivities: formattedOverSeasActivities
|
||||
};
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user