fixed the coverimage issue for random activities and addbucket issue also resolved and taking the group count
This commit is contained in:
@@ -47,6 +47,10 @@ export const handler = safeHandler(async (
|
||||
? Number(body.energyLevelXid)
|
||||
: undefined,
|
||||
entryTypeXid: Number(body.entryTypeXid),
|
||||
groupCount:
|
||||
body.groupCount !== undefined && body.groupCount !== null
|
||||
? Number(body.groupCount)
|
||||
: undefined,
|
||||
page: body.page !== undefined ? Number(body.page) : 1,
|
||||
limit: body.limit !== undefined ? Number(body.limit) : 20,
|
||||
};
|
||||
@@ -61,6 +65,7 @@ export const handler = safeHandler(async (
|
||||
(payload.energyLevelXid !== undefined &&
|
||||
Number.isNaN(payload.energyLevelXid)) ||
|
||||
Number.isNaN(payload.entryTypeXid) ||
|
||||
(payload.groupCount !== undefined && Number.isNaN(payload.groupCount)) ||
|
||||
Number.isNaN(payload.page) ||
|
||||
Number.isNaN(payload.limit)
|
||||
) {
|
||||
|
||||
@@ -36,7 +36,6 @@ export const handler = safeHandler(async (
|
||||
}
|
||||
|
||||
const activities = Array.isArray(body.activities) ? body.activities : [];
|
||||
const members = Array.isArray(body.members) ? body.members : [];
|
||||
|
||||
if (!body.startDate || !body.endDate || !body.startTime || !body.endTime) {
|
||||
throw new ApiError(
|
||||
@@ -71,10 +70,6 @@ export const handler = safeHandler(async (
|
||||
endDate: body.endDate,
|
||||
startTime: body.startTime,
|
||||
endTime: body.endTime,
|
||||
members: members.map((member: any) => ({
|
||||
memberXid: Number(member.memberXid),
|
||||
memberRole: member.memberRole,
|
||||
})),
|
||||
activities: activities.map((activity: any) => ({
|
||||
activityXid: Number(activity.activityXid),
|
||||
venueXid: Number(activity.venueXid),
|
||||
@@ -113,7 +108,6 @@ export const handler = safeHandler(async (
|
||||
};
|
||||
|
||||
if (
|
||||
payload.members.some((member) => Number.isNaN(member.memberXid)) ||
|
||||
payload.activities.some(
|
||||
(activity) =>
|
||||
Number.isNaN(activity.activityXid) ||
|
||||
|
||||
@@ -447,10 +447,6 @@ export class ItineraryService {
|
||||
endDate: string;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
members?: Array<{
|
||||
memberXid: number;
|
||||
memberRole?: string;
|
||||
}>;
|
||||
activities: Array<{
|
||||
activityXid: number;
|
||||
venueXid: number;
|
||||
@@ -503,64 +499,8 @@ export class ItineraryService {
|
||||
|
||||
const itineraryNo = `ITN-${Date.now()}`;
|
||||
const itineraryTitle = payload.title?.trim() || itineraryNo;
|
||||
const invitedMembers = Array.from(
|
||||
new Map(
|
||||
(payload.members ?? [])
|
||||
.filter((member) => member.memberXid && member.memberXid !== ownerXid)
|
||||
.map((member) => [member.memberXid, member]),
|
||||
).values(),
|
||||
);
|
||||
|
||||
return this.prisma.$transaction(async (tx) => {
|
||||
if (invitedMembers.length) {
|
||||
const ownerConnectionDetails = await tx.connectDetails.findMany({
|
||||
where: {
|
||||
userXid: ownerXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
schoolCompanyXid: true,
|
||||
},
|
||||
});
|
||||
|
||||
const ownerSchoolCompanyXids = ownerConnectionDetails.map(
|
||||
(item) => item.schoolCompanyXid,
|
||||
);
|
||||
|
||||
if (!ownerSchoolCompanyXids.length) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'You can invite members only when you have school/company connection details.',
|
||||
);
|
||||
}
|
||||
|
||||
const validMembers = await tx.connectDetails.findMany({
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
schoolCompanyXid: { in: ownerSchoolCompanyXids },
|
||||
userXid: { in: invitedMembers.map((member) => member.memberXid) },
|
||||
},
|
||||
distinct: ['userXid'],
|
||||
select: {
|
||||
userXid: true,
|
||||
},
|
||||
});
|
||||
|
||||
const validMemberIds = new Set(validMembers.map((item) => item.userXid));
|
||||
const invalidMembers = invitedMembers.filter(
|
||||
(member) => !validMemberIds.has(member.memberXid),
|
||||
);
|
||||
|
||||
if (invalidMembers.length) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'All invited members must belong to the same school/company network as the owner.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const itineraryHeader = await tx.itineraryHeader.create({
|
||||
data: {
|
||||
itineraryNo,
|
||||
@@ -586,24 +526,6 @@ export class ItineraryService {
|
||||
},
|
||||
});
|
||||
|
||||
const createdMembers =
|
||||
invitedMembers.length > 0
|
||||
? await Promise.all(
|
||||
invitedMembers.map((member) =>
|
||||
tx.itineraryMembers.create({
|
||||
data: {
|
||||
itineraryHeaderXid: itineraryHeader.id,
|
||||
memberXid: member.memberXid,
|
||||
memberRole: member.memberRole?.trim() || 'MEMBER',
|
||||
memberStatus: 'pending',
|
||||
invitedByXid: ownerXid,
|
||||
isActive: true,
|
||||
},
|
||||
}),
|
||||
),
|
||||
)
|
||||
: [];
|
||||
|
||||
const createdActivities = await Promise.all(
|
||||
payload.activities.map(async (activityItem) => {
|
||||
const scheduleHeader = await tx.scheduleHeader.findFirst({
|
||||
@@ -840,7 +762,7 @@ export class ItineraryService {
|
||||
title: itineraryHeader.title,
|
||||
itineraryStatus: itineraryHeader.itineraryStatus,
|
||||
ownerMemberXid: ownerMember.id,
|
||||
membersCount: createdMembers.length + 1,
|
||||
membersCount: 1,
|
||||
activitiesCount: createdActivities.length,
|
||||
members: [
|
||||
{
|
||||
@@ -849,12 +771,6 @@ export class ItineraryService {
|
||||
memberRole: ownerMember.memberRole,
|
||||
memberStatus: ownerMember.memberStatus,
|
||||
},
|
||||
...createdMembers.map((member) => ({
|
||||
id: member.id,
|
||||
memberXid: member.memberXid,
|
||||
memberRole: member.memberRole,
|
||||
memberStatus: member.memberStatus,
|
||||
})),
|
||||
],
|
||||
activities: createdActivities,
|
||||
};
|
||||
@@ -1122,6 +1038,7 @@ export class ItineraryService {
|
||||
endTime: string;
|
||||
energyLevelXid?: number;
|
||||
entryTypeXid: number;
|
||||
groupCount?: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
},
|
||||
@@ -1143,8 +1060,39 @@ export class ItineraryService {
|
||||
);
|
||||
}
|
||||
|
||||
if (payload.groupCount !== undefined) {
|
||||
if (!Number.isInteger(payload.groupCount) || payload.groupCount <= 0) {
|
||||
throw new ApiError(400, 'groupCount must be a positive integer.');
|
||||
}
|
||||
}
|
||||
|
||||
const rangeStartDay = startOfDay(requestedStart);
|
||||
const rangeEndDay = startOfDay(requestedEnd);
|
||||
const requestedEntryType = await this.prisma.allowedEntryTypes.findFirst({
|
||||
where: {
|
||||
id: payload.entryTypeXid,
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
allowedEntryTypeName: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!requestedEntryType) {
|
||||
throw new ApiError(404, 'Selected entry type not found.');
|
||||
}
|
||||
|
||||
const isGroupEntryType =
|
||||
requestedEntryType.allowedEntryTypeName.trim().toLowerCase() === 'group';
|
||||
|
||||
if (isGroupEntryType && payload.groupCount === undefined) {
|
||||
throw new ApiError(
|
||||
400,
|
||||
'groupCount is required when entryTypeXid is for group.',
|
||||
);
|
||||
}
|
||||
|
||||
const activityEntries = await this.prisma.userBucketInterested.findMany({
|
||||
where: {
|
||||
@@ -1318,7 +1266,10 @@ export class ItineraryService {
|
||||
where: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
maxCapacity: { gt: 0 },
|
||||
maxCapacity:
|
||||
isGroupEntryType && payload.groupCount !== undefined
|
||||
? { gte: payload.groupCount }
|
||||
: { gt: 0 },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
@@ -1446,6 +1397,14 @@ export class ItineraryService {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (
|
||||
isGroupEntryType &&
|
||||
payload.groupCount !== undefined &&
|
||||
slot.maxCapacity < payload.groupCount
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
scheduleHeaderXid: header.id,
|
||||
slotId: slot.id,
|
||||
@@ -1634,6 +1593,7 @@ export class ItineraryService {
|
||||
endTime: payload.endTime,
|
||||
energyLevelXid: payload.energyLevelXid,
|
||||
entryTypeXid: payload.entryTypeXid,
|
||||
groupCount: payload.groupCount,
|
||||
page: sanitizedPage,
|
||||
limit: sanitizedLimit,
|
||||
interestTypes: distinctInterests,
|
||||
|
||||
@@ -1148,7 +1148,9 @@ export class UserService {
|
||||
// 6️⃣ RANDOM ACTIVITIES (5 ONLY - SIMPLE)
|
||||
// =====================================================
|
||||
|
||||
const totalActiveCount = await tx.activities.count({
|
||||
let randomActivities: any[] = [];
|
||||
|
||||
const eligibleRandomActivityIds = await tx.activities.findMany({
|
||||
where: {
|
||||
isActive: true,
|
||||
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
@@ -1157,53 +1159,44 @@ export class UserService {
|
||||
id: {
|
||||
notIn: allUserExcludedActivityIds.length
|
||||
? allUserExcludedActivityIds
|
||||
: [-1], // prevent empty notIn issue
|
||||
: [-1],
|
||||
},
|
||||
ActivitiesMedia: {
|
||||
some: {
|
||||
isActive: true,
|
||||
isCoverImage: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
let randomActivities: any[] = [];
|
||||
if (eligibleRandomActivityIds.length > 0) {
|
||||
const takeCount = Math.min(5, eligibleRandomActivityIds.length);
|
||||
const selectedIds = eligibleRandomActivityIds
|
||||
.sort(() => Math.random() - 0.5)
|
||||
.slice(0, takeCount)
|
||||
.map((activity) => activity.id);
|
||||
|
||||
if (totalActiveCount > 0) {
|
||||
const takeCount = Math.min(5, totalActiveCount);
|
||||
|
||||
const randomOffsets = new Set<number>();
|
||||
while (randomOffsets.size < takeCount) {
|
||||
randomOffsets.add(Math.floor(Math.random() * totalActiveCount));
|
||||
}
|
||||
|
||||
const randomFetched = await Promise.all(
|
||||
Array.from(randomOffsets).map((offset) =>
|
||||
tx.activities.findFirst({
|
||||
skip: offset,
|
||||
where: {
|
||||
isActive: true,
|
||||
activityInternalStatus:
|
||||
ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
amInternalStatus:
|
||||
ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
deletedAt: null,
|
||||
id: {
|
||||
notIn: allUserExcludedActivityIds.length
|
||||
? allUserExcludedActivityIds
|
||||
: [-1], // prevent empty notIn issue
|
||||
},
|
||||
},
|
||||
const randomFetched = await tx.activities.findMany({
|
||||
where: {
|
||||
id: { in: selectedIds },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true, isCoverImage: true },
|
||||
orderBy: { displayOrder: 'asc' },
|
||||
take: 1,
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true, isCoverImage: true },
|
||||
orderBy: { displayOrder: 'asc' },
|
||||
take: 1,
|
||||
select: {
|
||||
mediaFileName: true,
|
||||
},
|
||||
},
|
||||
mediaFileName: true,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
randomActivities = await Promise.all(
|
||||
randomFetched
|
||||
@@ -1817,7 +1810,9 @@ export class UserService {
|
||||
RANDOM ACTIVITIES (5 COVER IMAGES)
|
||||
===================================================== */
|
||||
|
||||
const totalActiveCount = await tx.activities.count({
|
||||
let randomActivities: any[] = [];
|
||||
|
||||
const eligibleRandomActivityIds = await tx.activities.findMany({
|
||||
where: {
|
||||
isActive: true,
|
||||
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
@@ -1826,50 +1821,43 @@ export class UserService {
|
||||
id: {
|
||||
notIn: safeExcludedIds,
|
||||
},
|
||||
ActivitiesMedia: {
|
||||
some: {
|
||||
isActive: true,
|
||||
isCoverImage: true,
|
||||
},
|
||||
},
|
||||
...excludeUserInterestCondition,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
let randomActivities: any[] = [];
|
||||
if (eligibleRandomActivityIds.length > 0) {
|
||||
const takeCount = Math.min(5, eligibleRandomActivityIds.length);
|
||||
const selectedIds = eligibleRandomActivityIds
|
||||
.sort(() => Math.random() - 0.5)
|
||||
.slice(0, takeCount)
|
||||
.map((activity) => activity.id);
|
||||
|
||||
if (totalActiveCount > 0) {
|
||||
const takeCount = Math.min(5, totalActiveCount);
|
||||
|
||||
const randomOffsets = new Set<number>();
|
||||
|
||||
while (randomOffsets.size < takeCount) {
|
||||
randomOffsets.add(Math.floor(Math.random() * totalActiveCount));
|
||||
}
|
||||
|
||||
const randomFetched = await Promise.all(
|
||||
Array.from(randomOffsets).map((offset) =>
|
||||
tx.activities.findFirst({
|
||||
skip: offset,
|
||||
where: {
|
||||
isActive: true,
|
||||
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
deletedAt: null,
|
||||
id: {
|
||||
notIn: safeExcludedIds,
|
||||
},
|
||||
...excludeUserInterestCondition,
|
||||
},
|
||||
const randomFetched = await tx.activities.findMany({
|
||||
where: {
|
||||
id: { in: selectedIds },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true, isCoverImage: true },
|
||||
orderBy: { displayOrder: 'asc' },
|
||||
take: 1,
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true, isCoverImage: true },
|
||||
orderBy: { displayOrder: 'asc' },
|
||||
take: 1,
|
||||
select: {
|
||||
mediaFileName: true,
|
||||
},
|
||||
},
|
||||
mediaFileName: true,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
randomActivities = await Promise.all(
|
||||
randomFetched
|
||||
@@ -3634,7 +3622,9 @@ export class UserService {
|
||||
RANDOM ACTIVITIES FROM CONNECTION USERS (5 COVER IMAGES)
|
||||
===================================================== */
|
||||
|
||||
const totalActiveCount = await tx.activities.count({
|
||||
let randomActivities: any[] = [];
|
||||
|
||||
const eligibleRandomActivityIds = await tx.activities.findMany({
|
||||
where: {
|
||||
id: { in: connectionActivityIds },
|
||||
isActive: true,
|
||||
@@ -3642,47 +3632,42 @@ export class UserService {
|
||||
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
activityTypeXid: { in: activityTypeIds },
|
||||
deletedAt: null,
|
||||
ActivitiesMedia: {
|
||||
some: {
|
||||
isActive: true,
|
||||
isCoverImage: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
let randomActivities: any[] = [];
|
||||
if (eligibleRandomActivityIds.length > 0) {
|
||||
const takeCount = Math.min(5, eligibleRandomActivityIds.length);
|
||||
const selectedIds = eligibleRandomActivityIds
|
||||
.sort(() => Math.random() - 0.5)
|
||||
.slice(0, takeCount)
|
||||
.map((activity) => activity.id);
|
||||
|
||||
if (totalActiveCount > 0) {
|
||||
const takeCount = Math.min(5, totalActiveCount);
|
||||
|
||||
const randomOffsets = new Set<number>();
|
||||
|
||||
while (randomOffsets.size < takeCount) {
|
||||
randomOffsets.add(Math.floor(Math.random() * totalActiveCount));
|
||||
}
|
||||
|
||||
const randomFetched = await Promise.all(
|
||||
Array.from(randomOffsets).map((offset) =>
|
||||
tx.activities.findFirst({
|
||||
skip: offset,
|
||||
where: {
|
||||
id: { in: connectionActivityIds },
|
||||
isActive: true,
|
||||
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
activityTypeXid: { in: activityTypeIds },
|
||||
deletedAt: null,
|
||||
},
|
||||
const randomFetched = await tx.activities.findMany({
|
||||
where: {
|
||||
id: { in: selectedIds },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true, isCoverImage: true },
|
||||
orderBy: { displayOrder: 'asc' },
|
||||
take: 1,
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
ActivitiesMedia: {
|
||||
where: { isActive: true, isCoverImage: true },
|
||||
orderBy: { displayOrder: "asc" },
|
||||
take: 1,
|
||||
select: {
|
||||
mediaFileName: true,
|
||||
},
|
||||
},
|
||||
mediaFileName: true,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
randomActivities = await Promise.all(
|
||||
randomFetched
|
||||
@@ -4151,56 +4136,54 @@ export class UserService {
|
||||
async getFiveRandomActivities() {
|
||||
return await this.prisma.$transaction(async (tx) => {
|
||||
|
||||
// Step 1: Count eligible activities
|
||||
const totalCount = await tx.activities.count({
|
||||
const eligibleRandomActivityIds = await tx.activities.findMany({
|
||||
where: {
|
||||
isActive: true,
|
||||
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
deletedAt: null,
|
||||
ActivitiesMedia: {
|
||||
some: {
|
||||
isActive: true,
|
||||
isCoverImage: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (totalCount === 0) return [];
|
||||
if (eligibleRandomActivityIds.length === 0) return [];
|
||||
|
||||
// Step 2: Generate 5 unique random offsets
|
||||
const takeCount = Math.min(5, totalCount);
|
||||
const randomOffsets = new Set<number>();
|
||||
const takeCount = Math.min(5, eligibleRandomActivityIds.length);
|
||||
const selectedIds = eligibleRandomActivityIds
|
||||
.sort(() => Math.random() - 0.5)
|
||||
.slice(0, takeCount)
|
||||
.map((activity) => activity.id);
|
||||
|
||||
while (randomOffsets.size < takeCount) {
|
||||
randomOffsets.add(Math.floor(Math.random() * totalCount));
|
||||
}
|
||||
|
||||
// Step 3: Fetch activities using skip (efficient for small limit like 5)
|
||||
const activities = await Promise.all(
|
||||
Array.from(randomOffsets).map((offset) =>
|
||||
tx.activities.findFirst({
|
||||
skip: offset,
|
||||
const activities = await tx.activities.findMany({
|
||||
where: {
|
||||
id: { in: selectedIds },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
ActivitiesMedia: {
|
||||
where: {
|
||||
isActive: true,
|
||||
activityInternalStatus: ACTIVITY_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.ACTIVITY_LISTED,
|
||||
deletedAt: null,
|
||||
isCoverImage: true,
|
||||
},
|
||||
orderBy: {
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
take: 1,
|
||||
select: {
|
||||
id: true,
|
||||
activityTitle: true,
|
||||
ActivitiesMedia: {
|
||||
where: {
|
||||
isActive: true,
|
||||
},
|
||||
orderBy: {
|
||||
displayOrder: 'asc',
|
||||
},
|
||||
take: 1,
|
||||
select: {
|
||||
mediaFileName: true,
|
||||
},
|
||||
},
|
||||
mediaFileName: true,
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Step 4: Attach presigned URLs
|
||||
const result = await Promise.all(
|
||||
@@ -4243,7 +4226,7 @@ export class UserService {
|
||||
}
|
||||
|
||||
const existing = await this.prisma.userBucketInterested.findFirst({
|
||||
where: { userXid, activityXid },
|
||||
where: { userXid, activityXid, isActive: true },
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
|
||||
Reference in New Issue
Block a user