This commit is contained in:
paritosh18
2025-11-25 12:04:50 +05:30
15 changed files with 811 additions and 325 deletions

View File

@@ -65,7 +65,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
const fields: Record<string, any> = {};
const files: Array<{ buffer: Buffer; mimeType: string; fileName: string; fieldName: string }> = [];
// 3) parse with Busboy - FIXED VERSION
// 3) parse with Busboy
await new Promise<void>((resolve, reject) => {
const bb = Busboy({
headers: {
@@ -105,7 +105,6 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
});
bb.on('field', (fieldname, val) => {
// Store as string initially, parse later in normalizeJsonField
fields[fieldname] = val;
});
@@ -121,6 +120,9 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
bb.end();
});
// Extract isDraft flag from fields (default to false if not provided)
const isDraft = fields.isDraft === 'true' || fields.isDraft === true;
if (fields.userProfile) {
const userProfileRaw = normalizeJsonField(fields, "userProfile");
if (userProfileRaw) {
@@ -157,39 +159,31 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
if (existingHost) {
hostId = existingHost.id;
} else {
// For new hosts, we'll use user ID temporarily and update after host creation
hostId = userInfo.id;
}
// Define uploadToS3 function with proper folder structure using fieldName for filenames
// Define uploadToS3 function with proper folder structure using fieldName for filenames
// Define uploadToS3 function (same as before)
async function uploadToS3(buffer: Buffer, mimeType: string, originalName: string, folderType: 'logo' | 'documents' | 'parent_company', documentTypeXid?: number, fieldName?: string) {
let s3Key: string;
// Sanitize file name: remove special characters and spaces
const sanitizeFileName = (name: string) => {
return name
.toLowerCase()
.replace(/[^a-z0-9.]/g, '_') // Replace special characters with underscore
.replace(/_+/g, '_') // Replace multiple underscores with single
.replace(/^_+|_+$/g, ''); // Remove leading/trailing underscores
.replace(/[^a-z0-9.]/g, '_')
.replace(/_+/g, '_')
.replace(/^_+|_+$/g, '');
};
// Get file extension from original file name
const fileExtension = originalName.split('.').pop() || 'pdf';
// Determine folder structure based on type
if (folderType === 'logo') {
// Logo: Documents/Host/logo/{HostID}/{sanitized_filename}
const sanitizedFileName = sanitizeFileName(originalName);
s3Key = `Documents/Host/${hostId}/logo/${sanitizedFileName}`;
} else if (folderType === 'documents' && documentTypeXid && fieldName) {
// Host Documents: Documents/Host/documents/{HostID}/{documentTypeXid}_{fieldName}.{extension}
const fileName = `${documentTypeXid}_${fieldName}.${fileExtension}`;
const sanitizedFileName = sanitizeFileName(fileName);
s3Key = `Documents/Host/${hostId}/documents/${sanitizedFileName}`;
} else if (folderType === 'parent_company' && documentTypeXid && fieldName) {
// Parent Documents: Documents/Host/parent_company/{HostID}/{documentTypeXid}_{fieldName}.{extension}
const fileName = `${documentTypeXid}_${fieldName}.${fileExtension}`;
const sanitizedFileName = sanitizeFileName(fileName);
s3Key = `Documents/Host/${hostId}/parent_company/${sanitizedFileName}`;
@@ -197,7 +191,6 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
throw new ApiError(400, 'Invalid folder type or missing documentTypeXid/fieldName');
}
// Upload new file (S3 will automatically replace if same key exists)
await s3
.upload({
Bucket: config.aws.bucketName,
@@ -225,7 +218,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
console.log('Company logo uploaded:', logoPath);
}
// 6) Zod validation for companyDetails (includes optional parentCompany)
// 6) Zod validation for companyDetails
const companyValidation = hostCompanyDetailsSchema.safeParse(companyDetailsRaw);
if (!companyValidation.success) {
const message = companyValidation.error.issues.map((i) => i.message).join(', ');
@@ -246,7 +239,7 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
}
const documentsMetadata = documentsMetadataRaw.map((d: any) => ({
...d,
owner: d.owner || 'host', // default to host
owner: d.owner || 'host',
}));
// 8) Map uploaded files to metadata
@@ -260,12 +253,14 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
const hostDocs = documentMetadata.filter((d) => d.owner === 'host');
const parentDocs = documentMetadata.filter((d) => d.owner === 'parent');
// 10) Ensure required docs for host exist (IDs 1,2,3,4)
const hostUploadedTypes = hostDocs.map((d) => d.documentTypeXid);
const requiredHostTypes = Object.values(REQUIRED_DOC_TYPES);
const missingHostDocs = requiredHostTypes.filter((typeId) => !hostUploadedTypes.includes(typeId));
if (missingHostDocs.length > 0) {
throw new ApiError(400, `Missing mandatory documents for host: ${missingHostDocs.join(', ')}`);
// 10) If NOT draft, validate required documents
if (!isDraft) {
const hostUploadedTypes = hostDocs.map((d) => d.documentTypeXid);
const requiredHostTypes = Object.values(REQUIRED_DOC_TYPES);
const missingHostDocs = requiredHostTypes.filter((typeId) => !hostUploadedTypes.includes(typeId));
if (missingHostDocs.length > 0) {
throw new ApiError(400, `Missing mandatory documents for host: ${missingHostDocs.join(', ')}`);
}
}
// 11) If isSubsidairy === true and parentCompany provided -> validate parent company & docs
@@ -282,61 +277,65 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
}
parsedParentCompany = parsedCompany.parentCompany;
const parentUploadedTypes = parentDocs.map((d) => d.documentTypeXid);
const requiredParentTypes = Object.values(REQUIRED_DOC_TYPES);
const missingParentDocs = requiredParentTypes.filter((typeId) => !parentUploadedTypes.includes(typeId));
if (missingParentDocs.length > 0) {
throw new ApiError(400, `Missing mandatory documents for parent company: ${missingParentDocs.join(', ')}`);
// If NOT draft, validate required parent documents
if (!isDraft) {
const parentUploadedTypes = parentDocs.map((d) => d.documentTypeXid);
const requiredParentTypes = Object.values(REQUIRED_DOC_TYPES);
const missingParentDocs = requiredParentTypes.filter((typeId) => !parentUploadedTypes.includes(typeId));
if (missingParentDocs.length > 0) {
throw new ApiError(400, `Missing mandatory documents for parent company: ${missingParentDocs.join(', ')}`);
}
}
}
// 12) Upload files to S3 with proper folder structure using fieldName for filenames
// 12) Upload files to S3 (same for both draft and final submission)
const uploadedHostDocs: Array<{ documentTypeXid: number; documentName: string; filePath: string }> = [];
const uploadedParentDocs: Array<{ documentTypeXid: number; documentName: string; filePath: string }> = [];
// Upload host documents with proper folder structure using fieldName
// Upload host documents
for (const doc of hostDocs) {
const filePath = await uploadToS3(
doc.file.buffer,
doc.file.mimeType,
doc.file.fileName, // Use original file name for extension
doc.file.fileName,
'documents',
doc.documentTypeXid,
doc.fieldName // Use fieldName for the filename
doc.fieldName
);
uploadedHostDocs.push({
documentTypeXid: doc.documentTypeXid,
documentName: doc.fieldName, // Keep documentName for database
documentName: doc.fieldName,
filePath,
});
}
// Upload parent company documents with proper folder structure using fieldName
// Upload parent company documents
if (parentDocs.length > 0) {
for (const doc of parentDocs) {
const filePath = await uploadToS3(
doc.file.buffer,
doc.file.mimeType,
doc.file.fileName, // Use original file name for extension
doc.file.fileName,
'parent_company',
doc.documentTypeXid,
doc.fieldName // Use fieldName for the filename
doc.fieldName
);
uploadedParentDocs.push({
documentTypeXid: doc.documentTypeXid,
documentName: doc.documentName, // Keep documentName for database
documentName: doc.documentName,
filePath,
});
}
}
// 13) Persist using hostService
// 13) Persist using hostService - PASS isDraft flag
const createdOrUpdated = await hostService.addOrUpdateCompanyDetails(
userInfo.id,
parsedCompany,
uploadedHostDocs,
parsedParentCompany,
uploadedParentDocs
uploadedParentDocs,
isDraft // Pass the isDraft flag
);
if (!createdOrUpdated) throw new ApiError(400, 'Failed to add/update company details.');
@@ -347,25 +346,28 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
console.log(`Host created with ID: ${hostId}`);
}
const getSuggestionDetails = await hostService.getSuggestionDetails(userInfo.id)
// 14) Send emails only for FINAL submission (not draft)
if (!isDraft) {
const getSuggestionDetails = await hostService.getSuggestionDetails(userInfo.id)
if (getSuggestionDetails.hostDetails.accountManagerXid !== null) {
await sendEmailToAM(
getSuggestionDetails.hostDetails.accountManager.emailAddress,
getSuggestionDetails.hostDetails.accountManager.firstName,
getSuggestionDetails.hostDetails.companyName,
getSuggestionDetails.hostDetails.hostRefNumber
);
} else {
await sendEmailToMinglarAdmin(
config.MinglarAdminEmail,
config.MinglarAdminName,
getSuggestionDetails.hostDetails.companyName,
getSuggestionDetails.hostDetails.hostRefNumber
)
if (getSuggestionDetails.hostDetails.accountManagerXid !== null) {
await sendEmailToAM(
getSuggestionDetails.hostDetails.accountManager.emailAddress,
getSuggestionDetails.hostDetails.accountManager.firstName,
getSuggestionDetails.hostDetails.companyName,
getSuggestionDetails.hostDetails.hostRefNumber
);
} else {
await sendEmailToMinglarAdmin(
config.MinglarAdminEmail,
config.MinglarAdminName,
getSuggestionDetails.hostDetails.companyName,
getSuggestionDetails.hostDetails.hostRefNumber
)
}
}
// 14) Success
// 15) Success response
return {
statusCode: 200,
headers: {
@@ -374,8 +376,14 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
},
body: JSON.stringify({
success: true,
message: 'Company (and parent if provided) details and documents uploaded successfully.',
data: { id: createdOrUpdated.id, hostRefNumber: (createdOrUpdated as any).hostRefNumber }
message: isDraft
? 'Company details saved as draft successfully.'
: 'Company (and parent if provided) details and documents uploaded successfully.',
data: {
id: createdOrUpdated.id,
hostRefNumber: (createdOrUpdated as any).hostRefNumber,
isDraft
}
}),
};
} catch (error: any) {

View File

@@ -22,17 +22,10 @@ export const handler = safeHandler(async (
const userInfo = await verifyMinglarAdminHostToken(token);
const userId = Number(userInfo.id);
let body: { question_xid: number, activity_xid: number };
const question_xid = Number(event.queryStringParameters?.question_xid);
const activity_xid = Number(event.queryStringParameters?.activity_xid);
try {
body = event.body ? JSON.parse(event.body) : {};
} catch (error) {
throw new ApiError(400, 'Invalid JSON in request body');
}
const { question_xid, activity_xid } = body;
if(!question_xid || !activity_xid){
if (!question_xid || !activity_xid) {
throw new ApiError(400, "Question and activity xid are required.")
}

View File

@@ -182,14 +182,14 @@ export const handler = safeHandler(async (event: APIGatewayProxyEvent): Promise<
if (existingHeader) {
console.log("🔄 Updating existing PQQ header");
// Update existing header (comments can be null)
const header = await pqqService.updateHeader(
header = await pqqService.updateHeader(
existingHeader.id,
comments
);
} else {
console.log("🆕 Creating new PQQ header");
// Create new header (comments can be null)
const header = await pqqService.createHeader(
header = await pqqService.createHeader(
activityXid,
pqqQuestionXid,
pqqAnswerXid,

View File

@@ -9,7 +9,7 @@ import { z } from 'zod';
import { hostCompanyDetailsSchema } from '@/common/utils/validation/host/hostCompanyDetails.validation';
import { HOST_STATUS_DISPLAY, HOST_STATUS_INTERNAL, STEPPER } from '@/common/utils/constants/host.constant';
import { MINGLAR_STATUS_DISPLAY, MINGLAR_STATUS_INTERNAL } from '@/common/utils/constants/minglar.constant';
import { ROLE, USER_STATUS } from '@/common/utils/constants/common.constant';
import { ROLE, ROLE_NAME, USER_STATUS } from '@/common/utils/constants/common.constant';
import { getPresignedUrl } from '@/common/middlewares/aws/getPreSignedUrl';
import config from '@/config/config';
@@ -281,7 +281,8 @@ export class HostService {
companyData: HostCompanyDetailsInput,
documents: HostDocumentInput[],
parentCompanyData?: any | null,
parentDocuments?: HostDocumentInput[]
parentDocuments?: HostDocumentInput[],
isDraft: boolean = false // Add isDraft parameter with default false
) {
return await this.prisma.$transaction(async (tx) => {
// Check if host already has a company
@@ -290,13 +291,32 @@ export class HostService {
include: { hostParent: true },
});
// Determine status based on isDraft flag
const hostStatusInternal = isDraft
? HOST_STATUS_INTERNAL.DRAFT
: HOST_STATUS_INTERNAL.HOST_SUBMITTED;
const hostStatusDisplay = isDraft
? HOST_STATUS_DISPLAY.DRAFT
: HOST_STATUS_DISPLAY.UNDER_REVIEW;
const minglarStatusInternal = isDraft
? MINGLAR_STATUS_INTERNAL.DRAFT
: MINGLAR_STATUS_INTERNAL.ADMIN_TO_REVIEW;
const minglarStatusDisplay = isDraft
? MINGLAR_STATUS_DISPLAY.DRAFT
: MINGLAR_STATUS_DISPLAY.NEW;
// CREATE
if (!existingHostCompany) {
// Optionally check unique registration number
const existingByPan = await tx.hostHeader.findFirst({
where: { panNumber: companyData.panNumber },
});
if (existingByPan) throw new ApiError(400, 'Company already exists with this pan/bin number');
// Optionally check unique registration number (only for final submission)
if (!isDraft) {
const existingByPan = await tx.hostHeader.findFirst({
where: { panNumber: companyData.panNumber },
});
if (existingByPan) throw new ApiError(400, 'Company already exists with this pan/bin number');
}
const refNumber = await this.generateHostRefNumber(tx);
@@ -323,12 +343,11 @@ export class HostService {
facebookUrl: companyData.facebookUrl || null,
linkedinUrl: companyData.linkedinUrl || null,
twitterUrl: companyData.twitterUrl || null,
// currencyXid: companyData.currencyXid,
stepper: STEPPER.UNDER_REVIEW,
hostStatusInternal: HOST_STATUS_INTERNAL.HOST_SUBMITTED,
hostStatusDisplay: HOST_STATUS_DISPLAY.UNDER_REVIEW,
adminStatusInternal: MINGLAR_STATUS_INTERNAL.ADMIN_TO_REVIEW,
adminStatusDisplay: MINGLAR_STATUS_DISPLAY.NEW,
hostStatusInternal: hostStatusInternal,
hostStatusDisplay: hostStatusDisplay,
adminStatusInternal: minglarStatusInternal,
adminStatusDisplay: minglarStatusDisplay,
},
});
@@ -385,7 +404,6 @@ export class HostService {
}
// UPDATE existing
// Prevent changing hostRefNumber
const updatedHost = await tx.hostHeader.update({
where: { id: existingHostCompany.id },
data: {
@@ -408,9 +426,11 @@ export class HostService {
facebookUrl: companyData.facebookUrl || null,
linkedinUrl: companyData.linkedinUrl || null,
twitterUrl: companyData.twitterUrl || null,
// currencyXid: companyData.currencyXid,
stepper: STEPPER.UNDER_REVIEW
// hostRefNumber: DO NOT UPDATE
stepper: STEPPER.UNDER_REVIEW,
hostStatusInternal: hostStatusInternal,
hostStatusDisplay: hostStatusDisplay,
adminStatusInternal: minglarStatusInternal,
adminStatusDisplay: minglarStatusDisplay
},
});
@@ -428,7 +448,6 @@ export class HostService {
// Parent company create/update and replace parent docs
if (companyData.isSubsidairy) {
// existingHostCompany.hostParent may be array or single object depending on Prisma schema
let parentRecord = (existingHostCompany as any).hostParent;
if (Array.isArray(parentRecord)) parentRecord = parentRecord[0];
@@ -519,6 +538,19 @@ export class HostService {
}
}
const hostDetails = await this.prisma.hostHeader.findFirst({
where: { userXid: user_xid },
})
await this.prisma.hostTrack.create({
data: {
hostXid: hostDetails.id,
updatedByRole: ROLE_NAME.HOST,
updatedByXid: user_xid,
trackStatus: hostDetails.hostStatusInternal,
}
})
return updatedHost;
});
}
@@ -668,7 +700,12 @@ export class HostService {
let totalMaxPoints = 0;
// For category-wise scoring
const categories: any = {}; // { [categoryId]: { userPoints, maxPoints, name } }
const categories: Record<number, {
categoryId: number;
categoryName: string;
userPoints: number;
maxPoints: number;
}> = {};
for (const item of answers) {
const question = item.pqqQuestions;
@@ -680,7 +717,7 @@ export class HostService {
totalUserPoints += userPoints;
totalMaxPoints += maxPoints;
// Category info
// Category
const category = question.pqqSubCategories.category;
const categoryId = category.id;
@@ -689,7 +726,7 @@ export class HostService {
categoryId,
categoryName: category.categoryName,
userPoints: 0,
maxPoints: 0
maxPoints: 0,
};
}
@@ -698,19 +735,26 @@ export class HostService {
}
// Overall percent
const overallPercentage = totalMaxPoints > 0
? (totalUserPoints / totalMaxPoints) * 100
: 0;
const overallPercentage =
totalMaxPoints > 0 ? (totalUserPoints / totalMaxPoints) * 100 : 0;
// Category percentages
const categoryWise: any = {};
// ---------- 🔥 ONLY FIRST 2 CATEGORIES ----------
const categoryArray = Object.values(categories);
for (const catId in categories) {
const c = categories[catId];
// Sort by categoryId (or change to displayOrder if needed)
categoryArray.sort((a, b) => a.categoryId - b.categoryId);
// Take only first 2 categories
const topTwo = categoryArray.slice(0, 2);
const categoryWise: Record<string, number> = {};
for (const c of topTwo) {
categoryWise[c.categoryName] =
c.maxPoints > 0 ? (c.userPoints / c.maxPoints) * 100 : 0;
}
// Return final score object
return {
overallPercentage,
categoryWise
@@ -718,6 +762,7 @@ export class HostService {
}
async createHeader(
activityXid: number,
pqqQuestionXid: number,

View File

@@ -0,0 +1,53 @@
import { verifyOnlyMinglarAdminToken } from '@/common/middlewares/jwt/authForOnlyMinglarAdmin';
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { PrismaService } from '../../../common/database/prisma.service';
import { safeHandler } from '../../../common/utils/handlers/safeHandler';
import ApiError from '../../../common/utils/helper/ApiError';
import { MinglarService } from '../services/minglar.service';
const prismaService = new PrismaService();
const minglarService = new MinglarService(prismaService);
/**
* Get all host applications handler
* Returns host details with status, submission date, and account manager info
*/
export const handler = safeHandler(async (
event: APIGatewayProxyEvent,
context?: Context
): Promise<APIGatewayProxyResult> => {
// Verify authentication token
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
if (!token) {
throw new ApiError(401, 'This is a protected route. Please provide a valid token.');
}
// Verify token and get user info
const userInfo = await verifyOnlyMinglarAdminToken(token);
// Get user details including role
const user = await prismaService.user.findUnique({
where: { id: userInfo.id },
select: { id: true, roleXid: true }
});
if (!user) {
throw new ApiError(404, 'User not found');
}
// Get all host applications from service based on user role
const hostApplications = await minglarService.getAllOnboardingHostApplications();
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({
success: true,
message: 'Host applications retrieved successfully',
data: hostApplications,
}),
};
});

View File

@@ -0,0 +1,53 @@
import { verifyOnlyMinglarAdminToken } from '@/common/middlewares/jwt/authForOnlyMinglarAdmin';
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
import { PrismaService } from '../../../common/database/prisma.service';
import { safeHandler } from '../../../common/utils/handlers/safeHandler';
import ApiError from '../../../common/utils/helper/ApiError';
import { MinglarService } from '../services/minglar.service';
const prismaService = new PrismaService();
const minglarService = new MinglarService(prismaService);
/**
* Get all host applications handler
* Returns host details with status, submission date, and account manager info
*/
export const handler = safeHandler(async (
event: APIGatewayProxyEvent,
context?: Context
): Promise<APIGatewayProxyResult> => {
// Verify authentication token
const token = event.headers['x-auth-token'] || event.headers['X-Auth-Token'];
if (!token) {
throw new ApiError(401, 'This is a protected route. Please provide a valid token.');
}
// Verify token and get user info
const userInfo = await verifyOnlyMinglarAdminToken(token);
// Get user details including role
const user = await prismaService.user.findUnique({
where: { id: userInfo.id },
select: { id: true, roleXid: true }
});
if (!user) {
throw new ApiError(404, 'User not found');
}
// Get all host applications from service based on user role
const hostApplications = await minglarService.getAllOnboardingHostApplications_New();
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
body: JSON.stringify({
success: true,
message: 'Host applications retrieved successfully',
data: hostApplications,
}),
};
});

View File

@@ -1,4 +1,4 @@
import { ROLE, USER_STATUS } from '@/common/utils/constants/common.constant';
import { ROLE, ROLE_NAME, USER_STATUS } from '@/common/utils/constants/common.constant';
import {
HOST_STATUS_DISPLAY,
HOST_STATUS_INTERNAL,
@@ -19,7 +19,7 @@ import { sendAMEmailForHostAssign } from './AMEmail.service';
@Injectable()
export class MinglarService {
constructor(private prisma: PrismaService) {}
constructor(private prisma: PrismaService) { }
async createPassword(user_xid: number, password: string): Promise<boolean> {
// Find user by id
@@ -597,7 +597,7 @@ export class MinglarService {
userStatus &&
userStatus.trim().toLowerCase() === MINGLAR_STATUS_DISPLAY.NEW.toLowerCase()
) {
filters.adminStatusDisplay = MINGLAR_STATUS_DISPLAY.NEW;
filters.adminStatusInternal = MINGLAR_STATUS_INTERNAL.ADMIN_TO_REVIEW;
}
/** -----------------------------------
@@ -670,6 +670,85 @@ export class MinglarService {
}));
}
async getAllOnboardingHostApplications() {
return await this.prisma.hostHeader.findMany({
where: { isActive: true },
select: {
id: true,
hostRefNumber: true,
companyName: true,
adminStatusDisplay: true,
assignedOn: true,
accountManagerXid: true,
createdAt: true,
user: {
select: {
id: true,
firstName: true,
lastName: true,
}
},
accountManager: {
select: {
id: true,
firstName: true,
lastName: true,
profileImage: true
}
}
}
})
}
async getAllOnboardingHostApplications_New() {
return await this.prisma.hostHeader.findMany({
where: { isActive: true, adminStatusInternal: MINGLAR_STATUS_INTERNAL.ADMIN_TO_REVIEW },
select: {
id: true,
hostRefNumber: true,
companyName: true,
adminStatusDisplay: true,
assignedOn: true,
accountManagerXid: true,
createdAt: true,
cities: {
select: {
id: true,
cityName: true
}
},
countries: {
select: {
id: true,
countryName: true,
}
},
states: {
select: {
id: true,
stateName: true
}
},
user: {
select: {
id: true,
firstName: true,
lastName: true,
}
},
accountManager: {
select: {
id: true,
firstName: true,
lastName: true,
profileImage: true
}
}
}
})
}
async getAllCoadminAndAM() {
// 1. Fetch all required users (Admin, Co-Admin, AM)
@@ -770,7 +849,7 @@ export class MinglarService {
if (
hostDetails.adminStatusInternal !==
MINGLAR_STATUS_INTERNAL.AM_NOT_ASSIGNED &&
MINGLAR_STATUS_INTERNAL.AM_NOT_ASSIGNED &&
hostDetails.adminStatusDisplay !== MINGLAR_STATUS_DISPLAY.AM_NOT_ASSIGNED
) {
throw new ApiError(400, 'Invalid host status');
@@ -861,7 +940,7 @@ export class MinglarService {
) {
// Check if host exists
const ActivityHeader = await this.prisma.activityPQQheader.findUnique({
where: { id: activity_pqq_header_xid , isActive:true},
where: { id: activity_pqq_header_xid, isActive: true },
select: { id: true },
});
@@ -934,41 +1013,65 @@ export class MinglarService {
}
async acceptHostApplication(host_xid: number, user_xid: number) {
return await this.prisma.hostHeader.update({
where: {
id: host_xid,
hostStatusInternal: HOST_STATUS_INTERNAL.HOST_SUBMITTED,
hostStatusDisplay: HOST_STATUS_DISPLAY.UNDER_REVIEW,
adminStatusInternal: MINGLAR_STATUS_INTERNAL.AM_TO_REVIEW,
adminStatusDisplay: MINGLAR_STATUS_DISPLAY.TO_REVIEW,
},
data: {
hostStatusInternal: HOST_STATUS_INTERNAL.APPROVED,
hostStatusDisplay: HOST_STATUS_DISPLAY.APPROVED,
adminStatusInternal: MINGLAR_STATUS_INTERNAL.AM_APPROVED,
adminStatusDisplay: MINGLAR_STATUS_DISPLAY.APPROVED,
stepper: STEPPER.COMPANY_DETAILS_APPROVED,
},
});
return await this.prisma.$transaction(async (tx) => {
await this.prisma.hostHeader.update({
where: {
id: host_xid,
hostStatusInternal: HOST_STATUS_INTERNAL.HOST_SUBMITTED,
hostStatusDisplay: HOST_STATUS_DISPLAY.UNDER_REVIEW,
adminStatusInternal: MINGLAR_STATUS_INTERNAL.AM_TO_REVIEW,
adminStatusDisplay: MINGLAR_STATUS_DISPLAY.TO_REVIEW,
},
data: {
hostStatusInternal: HOST_STATUS_INTERNAL.APPROVED,
hostStatusDisplay: HOST_STATUS_DISPLAY.APPROVED,
adminStatusInternal: MINGLAR_STATUS_INTERNAL.AM_APPROVED,
adminStatusDisplay: MINGLAR_STATUS_DISPLAY.APPROVED,
stepper: STEPPER.COMPANY_DETAILS_APPROVED,
},
});
await this.prisma.hostTrack.create({
data: {
hostXid: host_xid,
updatedByRole: ROLE_NAME.ACCOUNT_MANAGER,
updatedByXid: user_xid,
trackStatus: MINGLAR_STATUS_INTERNAL.AM_APPROVED,
}
})
})
}
async acceptHostApplicationMinglarAdmin(host_xid: number, user_xid: number) {
return await this.prisma.hostHeader.update({
where: {
id: host_xid,
hostStatusInternal: HOST_STATUS_INTERNAL.HOST_SUBMITTED,
hostStatusDisplay: HOST_STATUS_DISPLAY.UNDER_REVIEW,
adminStatusInternal: MINGLAR_STATUS_INTERNAL.ADMIN_TO_REVIEW,
adminStatusDisplay: MINGLAR_STATUS_DISPLAY.NEW,
},
data: {
isApproved: true,
hostStatusInternal: HOST_STATUS_INTERNAL.HOST_SUBMITTED,
hostStatusDisplay: HOST_STATUS_DISPLAY.UNDER_REVIEW,
adminStatusInternal: MINGLAR_STATUS_INTERNAL.AM_NOT_ASSIGNED,
adminStatusDisplay: MINGLAR_STATUS_DISPLAY.AM_NOT_ASSIGNED,
},
});
return await this.prisma.$transaction(async (tx) => {
await tx.hostHeader.update({
where: {
id: host_xid,
hostStatusInternal: HOST_STATUS_INTERNAL.HOST_SUBMITTED,
hostStatusDisplay: HOST_STATUS_DISPLAY.UNDER_REVIEW,
adminStatusInternal: MINGLAR_STATUS_INTERNAL.ADMIN_TO_REVIEW,
adminStatusDisplay: MINGLAR_STATUS_DISPLAY.NEW,
},
data: {
isApproved: true,
hostStatusInternal: HOST_STATUS_INTERNAL.HOST_SUBMITTED,
hostStatusDisplay: HOST_STATUS_DISPLAY.UNDER_REVIEW,
adminStatusInternal: MINGLAR_STATUS_INTERNAL.AM_NOT_ASSIGNED,
adminStatusDisplay: MINGLAR_STATUS_DISPLAY.AM_NOT_ASSIGNED,
},
});
await this.prisma.hostTrack.create({
data: {
hostXid: host_xid,
updatedByRole: ROLE_NAME.MINGLAR_ADMIN,
updatedByXid: user_xid,
trackStatus: MINGLAR_STATUS_INTERNAL.AM_NOT_ASSIGNED,
}
})
})
}
async rejectHostApplication(host_xid: number, user_xid: number) {
@@ -994,6 +1097,15 @@ export class MinglarService {
},
});
await this.prisma.hostTrack.create({
data: {
hostXid: hostDetails.id,
updatedByRole: ROLE_NAME.MINGLAR_ADMIN,
updatedByXid: user_xid,
trackStatus: MINGLAR_STATUS_INTERNAL.ADMIN_REJECTED,
}
})
await tx.user.update({
where: { id: hostDetails.userXid },
data: {
@@ -1004,25 +1116,36 @@ export class MinglarService {
}
async rejectHostApplicationAM(host_xid: number, user_xid: number) {
const hostDetails = await this.prisma.hostHeader.findFirst({
where: { id: host_xid },
select: { id: true, userXid: true },
});
if (!hostDetails) {
throw new Error('Host not found');
}
await this.prisma.hostHeader.update({
where: {
id: host_xid,
hostStatusInternal: HOST_STATUS_INTERNAL.HOST_SUBMITTED,
hostStatusDisplay: HOST_STATUS_DISPLAY.UNDER_REVIEW,
},
data: {
hostStatusInternal: HOST_STATUS_INTERNAL.REJECTED,
hostStatusDisplay: HOST_STATUS_DISPLAY.REJECTED,
adminStatusInternal: MINGLAR_STATUS_INTERNAL.ADMIN_REJECTED,
adminStatusDisplay: MINGLAR_STATUS_DISPLAY.REJECTED,
},
});
return await this.prisma.$transaction(async (tx) => {
const hostDetails = await this.prisma.hostHeader.findFirst({
where: { id: host_xid },
select: { id: true, userXid: true },
});
if (!hostDetails) {
throw new Error('Host not found');
}
await this.prisma.hostHeader.update({
where: {
id: host_xid,
hostStatusInternal: HOST_STATUS_INTERNAL.HOST_SUBMITTED,
hostStatusDisplay: HOST_STATUS_DISPLAY.UNDER_REVIEW,
},
data: {
hostStatusInternal: HOST_STATUS_INTERNAL.HOST_TO_UPDATE,
hostStatusDisplay: HOST_STATUS_DISPLAY.ENHANCING,
adminStatusInternal: MINGLAR_STATUS_INTERNAL.AM_REJECTED,
adminStatusDisplay: MINGLAR_STATUS_DISPLAY.ENHANCING,
},
});
await this.prisma.hostTrack.create({
data: {
hostXid: hostDetails.id,
updatedByRole: ROLE_NAME.ACCOUNT_MANAGER,
updatedByXid: user_xid,
trackStatus: MINGLAR_STATUS_INTERNAL.AM_REJECTED,
}
})
})
}
}

View File

@@ -0,0 +1,53 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from "aws-lambda";
import { PrismaService } from "../../../common/database/prisma.service";
import { safeHandler } from "../../../common/utils/handlers/safeHandler";
import ApiError from "../../../common/utils/helper/ApiError";
import { PrePopulateService } from "../services/prepopulate.service";
import { verifyHostToken } from "@/common/middlewares/jwt/authForHost";
const prismaService = new PrismaService();
const prePopulateService = new PrePopulateService(prismaService);
export const handler = safeHandler(async (
event: APIGatewayProxyEvent,
context?: Context
): Promise<APIGatewayProxyResult> => {
// 1) Token check
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."
);
}
// 2) Authenticate user
await verifyHostToken(token);
// 3) Get bankXid from query params
const bankXid = Number(event.queryStringParameters?.bankXid);
if (!bankXid || isNaN(bankXid)) {
throw new ApiError(400, "Valid bankXid is required in query params.");
}
// 4) Fetch branches for the bank
const branches = await prePopulateService.getBankBranchesByBankId(bankXid);
return {
statusCode: 200,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({
success: true,
message: "Bank branches retrieved successfully",
data: branches
}),
};
});

View File

@@ -0,0 +1,53 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from "aws-lambda";
import { PrismaService } from "../../../common/database/prisma.service";
import { safeHandler } from "../../../common/utils/handlers/safeHandler";
import ApiError from "../../../common/utils/helper/ApiError";
import { PrePopulateService } from "../services/prepopulate.service";
import { verifyHostToken } from "@/common/middlewares/jwt/authForHost";
const prismaService = new PrismaService();
const prePopulateService = new PrePopulateService(prismaService);
export const handler = safeHandler(async (
event: APIGatewayProxyEvent,
context?: Context
): Promise<APIGatewayProxyResult> => {
// 1) Token check
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."
);
}
// 2) Authenticate user
await verifyHostToken(token);
// 3) Get bankXid from query params
const stateXid = Number(event.queryStringParameters?.stateXid);
if (!stateXid || isNaN(stateXid)) {
throw new ApiError(400, "Valid stateXid is required in query params.");
}
// 4) Fetch branches for the bank
const branches = await prePopulateService.getCityByStateId(stateXid);
return {
statusCode: 200,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify({
success: true,
message: "Bank branches retrieved successfully",
data: branches
}),
};
});

View File

@@ -3,7 +3,7 @@ import { PrismaService } from '../../../common/database/prisma.service';
@Injectable()
export class PrePopulateService {
constructor(private prisma: PrismaService) {}
constructor(private prisma: PrismaService) { }
async getAllBankDetails() {
return await this.prisma.banks.findMany({
@@ -23,6 +23,37 @@ export class PrePopulateService {
});
}
async getBankBranchesByBankId(bankXid: number) {
return await this.prisma.bankBranches.findMany({
where: {
bankXid,
isActive: true,
deletedAt: null
},
select: {
id: true,
branchAddress: true,
ifscCode: true,
}
});
}
async getCityByStateId(stateXid: number) {
return await this.prisma.cities.findMany({
where: {
stateXid,
isActive: true,
deletedAt: null
},
select: {
id: true,
cityName: true,
}
});
}
async getAllCurrencyDetails() {
return await this.prisma.currencies.findMany({
where: {
@@ -32,31 +63,31 @@ export class PrePopulateService {
});
}
async getAllPQQQuesAndAns() {
return await this.prisma.pQQCategories.findMany({
where: { isActive: true },
include: {
pqqsubCategories: {
include: {
questions: {
include: {
PQQAnswers: {
orderBy: {
displayOrder: 'asc'
}
}
},
orderBy: {
displayOrder: 'asc'
}
}
},
orderBy: { displayOrder: 'asc' }
async getAllPQQQuesAndAns() {
return await this.prisma.pQQCategories.findMany({
where: { isActive: true },
include: {
pqqsubCategories: {
include: {
questions: {
include: {
PQQAnswers: {
orderBy: {
displayOrder: 'asc'
}
}
},
orderBy: { displayOrder: 'asc' }
});
}
},
orderBy: {
displayOrder: 'asc'
}
}
},
orderBy: { displayOrder: 'asc' }
}
},
orderBy: { displayOrder: 'asc' }
});
}
async getAllDocumentTypeWithCountryStateCity() {
const [documentDetails, countryDetails, stateDetails, cityDetails] =