From e72c260b1882f746050b5822c9caa4e37c73fc3c Mon Sep 17 00:00:00 2001 From: Mayank Mishra Date: Tue, 2 Dec 2025 13:42:14 +0530 Subject: [PATCH] Add ActivityTrack model to schema and update User and Activities models to include activityTracks relation. Modify seed data to reflect new interest names and activity types. Implement activity reference number generation in HostService for activity creation. --- prisma/schema.prisma | 21 +++++ prisma/seed.ts | 44 +++++++--- src/modules/host/services/host.service.ts | 85 ++++++++++++------- .../minglaradmin/services/minglar.service.ts | 67 +++++++++++++-- .../services/prepopulate.service.ts | 55 +++++++----- 5 files changed, 199 insertions(+), 73 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index e33cac8..33218f0 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -66,6 +66,7 @@ model User { friendOf Friends[] @relation("FriendUser") userAddressDetails UserAddressDetails[] userDocuments UserDocuments[] + activityTracks ActivityTrack[] @@map("users") @@schema("usr") @@ -908,6 +909,7 @@ model Activities { ActivityEquipmentTaxes ActivityEquipmentTaxes[] ScheduleHeader ScheduleHeader[] ItineraryActivities ItineraryActivities[] + activityTracks ActivityTrack[] @@map("activities") @@schema("act") @@ -932,6 +934,25 @@ model ActivityOtherDetails { @@schema("act") } +model ActivityTrack { + id Int @id @default(autoincrement()) + activityXid Int @map("activity_xid") + activity Activities @relation(fields: [activityXid], references: [id], onDelete: Cascade) + trackType String? @default("PQQ") @map("track_type") + updatedByRole String? @map("updated_by_role") + trackStatus String? @map("track_status") + updatedByXid Int? @map("updated_by_xid") + user User? @relation(fields: [updatedByXid], references: [id], onDelete: Cascade) + updatedOn DateTime? @map("updated_on") + 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") + + @@map("activity_track") + @@schema("act") +} + model ActivitiesMedia { id Int @id @default(autoincrement()) activityXid Int @map("activity_xid") diff --git a/prisma/seed.ts b/prisma/seed.ts index e05e7a4..24fd315 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -78,35 +78,51 @@ async function main() { create: { interestName: 'Chill and Zen', displayOrder: 1 }, }); const sweatmode = await prisma.interests.upsert({ - where: { interestName: 'Sweat Mode' }, + where: { interestName: 'Sweat Mode On' }, update: {}, - create: { interestName: 'Sweat Mode', displayOrder: 2 }, + create: { interestName: 'Sweat Mode On', displayOrder: 2 }, }); - const gameon = await prisma.interests.upsert({ - where: { interestName: 'Game On' }, + const trackracer = await prisma.interests.upsert({ + where: { interestName: 'Track Racer' }, update: {}, - create: { interestName: 'Game On', displayOrder: 3 }, + create: { interestName: 'Track Racer', displayOrder: 3 }, + }); + const circuitracer = await prisma.interests.upsert({ + where: { interestName: 'Circuit Racer' }, + update: {}, + create: { interestName: 'Circuit Racer', displayOrder: 4 }, + }); + const thermalGliding = await prisma.interests.upsert({ + where: { interestName: 'Thermal Gliding' }, + update: {}, + create: { interestName: 'Thermal Gliding', displayOrder: 5 }, }); const partycentral = await prisma.interests.upsert({ where: { interestName: 'Party Central' }, update: {}, - create: { interestName: 'Party Central', displayOrder: 4 }, + create: { interestName: 'Party Central', displayOrder: 6 }, }); - const artsy = await prisma.interests.upsert({ - where: { interestName: 'Artsy' }, + const aqua = await prisma.interests.upsert({ + where: { interestName: 'Aqua' }, update: {}, - create: { interestName: 'Artsy', displayOrder: 5 }, + create: { interestName: 'Aqua', displayOrder: 7 }, }); - const foodiediaries = await prisma.interests.upsert({ - where: { interestName: 'Foodie Diaries' }, + const foodie = await prisma.interests.upsert({ + where: { interestName: 'Foodie' }, update: {}, - create: { interestName: 'Foodie Diaries', displayOrder: 6 }, + create: { interestName: 'Foodie', displayOrder: 8 }, }); await prisma.activityTypes.createMany({ data: [ - { interestXid: chillandzen.id, activityTypeName: 'Cricket' }, - { interestXid: chillandzen.id, activityTypeName: 'Football' }, + { interestXid: aqua.id, activityTypeName: 'Scuba-Diving' }, + { interestXid: sweatmode.id, activityTypeName: 'Cloudboarding' }, + { interestXid: partycentral.id, activityTypeName: 'Soaring Glider' }, + { interestXid: sweatmode.id, activityTypeName: 'Speedway Racer' }, + { interestXid: aqua.id, activityTypeName: 'Aerial Surfing' }, + { interestXid: foodie.id, activityTypeName: 'Wine Tasting' }, + { interestXid: trackracer.id, activityTypeName: 'Track Racer' }, + { interestXid: thermalGliding.id, activityTypeName: 'Thermal Gliding' }, ], skipDuplicates: true, }); diff --git a/src/modules/host/services/host.service.ts b/src/modules/host/services/host.service.ts index d8ddb85..5398def 100644 --- a/src/modules/host/services/host.service.ts +++ b/src/modules/host/services/host.service.ts @@ -38,6 +38,20 @@ interface HostDocumentInput { documentName: string; filePath: string; // S3 URL } +export async function generateActivityRefNumber(tx: any) { + const lastrecord = await tx.activities.findFirst({ + orderBy: { + id: 'desc', + }, + select: { + id: true, + }, + }); + + const nextId = lastrecord ? lastrecord.id + 1 : 1; + + return `ACT-${String(nextId).padStart(6, '0')}`;; +} @Injectable() export class HostService { @@ -330,6 +344,7 @@ export class HostService { where: { id: data.hostXid }, data: { stepper: STEPPER.BANK_DETAILS_UPDATED, + currencyXid: data.currencyXid }, }); }); @@ -1206,42 +1221,48 @@ export class HostService { activityTypeXid: number, frequenciesXid?: number, ) { - // Find host header for this user - const host = await this.prisma.hostHeader.findFirst({ - where: { userXid: userId, isActive: true }, - }); - if (!host) { - throw new ApiError(404, 'Host not found for the user'); - } - // Validate activityType exists - const activityType = await this.prisma.activityTypes.findUnique({ - where: { id: activityTypeXid }, - }); - if (!activityType) { - throw new ApiError(404, 'Activity type not found'); - } + return await this.prisma.$transaction(async (tx) => { - // Optionally validate frequency - if (frequenciesXid) { - const freq = await this.prisma.frequencies.findUnique({ - where: { id: frequenciesXid }, + // Fetch host + const host = await tx.hostHeader.findFirst({ + where: { userXid: userId, isActive: true }, }); - if (!freq) throw new ApiError(404, 'Frequency not found'); - } + if (!host) throw new ApiError(404, 'Host not found for the user'); - const created = await this.prisma.activities.create({ - data: { - hostXid: host.id, - activityTypeXid: activityTypeXid, - frequenciesXid: frequenciesXid || null, - activityInternalStatus: ACTIVITY_INTERNAL_STATUS.DRAFT_PQ, - activityDisplayStatus: ACTIVITY_DISPLAY_STATUS.DRAFT_PQ, - amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.DRAFT_PQ, - amDisplayStatus: ACTIVITY_AM_DISPLAY_STATUS.DRAFT_PQ, - }, + // Validate activityType + const activityType = await tx.activityTypes.findUnique({ + where: { id: activityTypeXid }, + }); + if (!activityType) throw new ApiError(404, 'Activity type not found'); + + // Validate frequency + if (frequenciesXid) { + const freq = await tx.frequencies.findUnique({ + where: { id: frequenciesXid }, + }); + if (!freq) throw new ApiError(404, 'Frequency not found'); + } + + // Generate reference number + const referenceNumber = await generateActivityRefNumber(tx); + + // Create activity + const created = await tx.activities.create({ + data: { + hostXid: host.id, + activityTypeXid, + frequenciesXid: frequenciesXid || null, + activityInternalStatus: ACTIVITY_INTERNAL_STATUS.DRAFT_PQ, + activityDisplayStatus: ACTIVITY_DISPLAY_STATUS.DRAFT_PQ, + amInternalStatus: ACTIVITY_AM_INTERNAL_STATUS.DRAFT_PQ, + amDisplayStatus: ACTIVITY_AM_DISPLAY_STATUS.DRAFT_PQ, + activityRefNumber: referenceNumber, + }, + }); + + return created; }); - - return created; } + } diff --git a/src/modules/minglaradmin/services/minglar.service.ts b/src/modules/minglaradmin/services/minglar.service.ts index 8c6e50a..bfc543b 100644 --- a/src/modules/minglaradmin/services/minglar.service.ts +++ b/src/modules/minglaradmin/services/minglar.service.ts @@ -192,6 +192,18 @@ export class MinglarService { isActive: true, userStatus: USER_STATUS.ACTIVE, }, + select: { + id: true, + firstName: true, + lastName: true, + emailAddress: true, + mobileNumber: true, + roleXid: true, + isProfileUpdated: true, + userStatus: true, + profileImage: true, + userPassword: true, + } }); if (!existingUser) { @@ -214,6 +226,16 @@ export class MinglarService { throw new ApiError(401, 'Invalid credentials'); } + if (existingUser?.profileImage) { + const key = existingUser.profileImage.startsWith("http") + ? existingUser.profileImage.split(".com/")[1] + : existingUser.profileImage; + + existingUser.profileImage = await getPresignedUrl(bucket, key); + } + + delete existingUser.userPassword; + return existingUser; } @@ -742,19 +764,36 @@ export class MinglarService { } /** APPLICATION STATUS FILTER (NEW) **/ - const APPLICATION_STATUS_MAP: Record = { - "New": MINGLAR_STATUS_INTERNAL.AM_TO_REVIEW && MINGLAR_STATUS_DISPLAY.NEW, - "To_Review": MINGLAR_STATUS_INTERNAL.AM_TO_REVIEW && MINGLAR_STATUS_DISPLAY.TO_REVIEW, - "Enchancing": MINGLAR_STATUS_INTERNAL.AM_REJECTED, + const APPLICATION_STATUS_MAP: Record< + string, + { internal: string; display: string } + > = { + New: { + internal: MINGLAR_STATUS_INTERNAL.AM_TO_REVIEW, + display: MINGLAR_STATUS_DISPLAY.NEW, + }, + To_Review: { + internal: MINGLAR_STATUS_INTERNAL.AM_TO_REVIEW, + display: MINGLAR_STATUS_DISPLAY.TO_REVIEW, + }, + Enhancing: { + internal: MINGLAR_STATUS_INTERNAL.AM_REJECTED, + display: MINGLAR_STATUS_DISPLAY.ENHANCING, + }, }; + if (applicationStatus?.trim()) { const key = applicationStatus.trim(); - if (APPLICATION_STATUS_MAP[key]) { - filters.adminStatusInternal = APPLICATION_STATUS_MAP[key]; + const statusObj = APPLICATION_STATUS_MAP[key]; + + if (statusObj) { + filters.adminStatusInternal = statusObj.internal; + filters.adminStatusDisplay = statusObj.display; } } + /** ROLE-BASED FILTER **/ if (userRoleXid === ROLE.CO_ADMIN || userRoleXid === ROLE.ACCOUNT_MANAGER) { filters.accountManagerXid = userId; @@ -1513,7 +1552,21 @@ export class MinglarService { async getAMdetailById(id: number) { const user = await this.prisma.user.findUnique({ where: { id: id, isActive: true, userStatus: USER_STATUS.ACTIVE }, - include: { + select: { + id: true, + firstName: true, + lastName: true, + emailAddress: true, + isdCode: true, + mobileNumber: true, + roleXid: true, + userStatus: true, + isProfileUpdated: true, + dateOfBirth: true, + profileImage: true, + isEmailVerfied: true, + isMobileVerfied: true, + isBiometric: true, userAddressDetails: { select: { id: true, diff --git a/src/modules/prepopulate/services/prepopulate.service.ts b/src/modules/prepopulate/services/prepopulate.service.ts index 7960a33..257df81 100644 --- a/src/modules/prepopulate/services/prepopulate.service.ts +++ b/src/modules/prepopulate/services/prepopulate.service.ts @@ -11,15 +11,11 @@ export class PrePopulateService { isActive: true, deletedAt: null, }, - include: { - BankBranches: { - select: { - id: true, - branchAddress: true, - ifscCode: true, - }, - }, + select: { + id: true, + bankName: true, }, + orderBy: { bankName: 'asc' } }); } @@ -28,7 +24,6 @@ export class PrePopulateService { where: { bankXid, isActive: true, - deletedAt: null }, select: { id: true, @@ -60,6 +55,12 @@ export class PrePopulateService { isActive: true, deletedAt: null, }, + select: { + id: true, + currencyName: true, + currencySymbol: true, + }, + orderBy: { currencyName: 'asc' } }); } @@ -68,27 +69,41 @@ export class PrePopulateService { where: { isActive: true }, include: { pqqsubCategories: { - include: { + where: { isActive: true }, + select: { + id: true, + subCategoryName: true, + categoryXid: true, + displayOrder: true, questions: { - include: { + where: { isActive: true }, + select: { + id: true, + questionName: true, + maxPoints: true, + displayOrder: true, PQQAnswers: { - orderBy: { - displayOrder: 'asc' + where: { isActive: true }, + orderBy: { displayOrder: 'asc' }, + select: { + id: true, + answerName: true, + answerPoints: true, + displayOrder: true } } }, - orderBy: { - displayOrder: 'asc' - } - } + orderBy: { displayOrder: 'asc' } + }, }, - orderBy: { displayOrder: 'asc' } - } + orderBy: { displayOrder: 'asc' }, + }, }, - orderBy: { displayOrder: 'asc' } + orderBy: { displayOrder: 'asc' }, }); } + async getAllDocumentTypeWithCountryStateCity() { const [documentDetails, countryDetails, stateDetails, companyTypeDetails] = await this.prisma.$transaction([