Flowchart Updated
This commit is contained in:
@ -30,21 +30,14 @@ const configs = {
|
||||
sample: {
|
||||
subject: 'SIF Form received for Project',
|
||||
fields: [
|
||||
// Customer Information
|
||||
'Principal_Investigator', 'Email', 'Company_Institution', 'Contact_Number', 'Address', 'City', 'State', 'Pin',
|
||||
'Secondary_Contact', 'Secondary_Email', 'Secondary_Company_Institution', 'Secondary_Contact_Number',
|
||||
|
||||
// Sample Information
|
||||
'Project_Title', 'Number_of_Samples', 'Sample_Type', 'Sample_Type_Other', 'Sample_Source', 'Sample_Source_Other',
|
||||
'Pathogenicity', 'Sample_Remarks',
|
||||
|
||||
// Service Information
|
||||
'Service_Requested', 'Service_Requested_Other', 'Type_of_Library', 'Type_of_Library_Other',
|
||||
'Required_Library_Size', 'Required_Library_Size_Other', 'Index_Information', 'Kit_Information',
|
||||
'Sequencing_Platform', 'Sequencing_Platform_Other', 'Sequencing_Read_Length', 'Sequencing_Read_Length_Other',
|
||||
'Total_Data_Requirement', 'Service_Remarks',
|
||||
|
||||
// Bioinformatics Information
|
||||
'Analysis_Requested', 'Analysis_Details', 'Reference_Genome_Available', 'Genome_Size', 'Special_Consideration'
|
||||
],
|
||||
required: [
|
||||
@ -58,6 +51,24 @@ const configs = {
|
||||
}
|
||||
};
|
||||
|
||||
// Serial number tracker
|
||||
let serialTracker = {};
|
||||
function generateSerialNumber() {
|
||||
const today = new Date();
|
||||
const dateKey = today.toISOString().slice(0, 10); // "YYYY-MM-DD"
|
||||
if (!serialTracker[dateKey]) {
|
||||
serialTracker[dateKey] = 1;
|
||||
} else {
|
||||
serialTracker[dateKey] += 1;
|
||||
}
|
||||
const serialNum = String(serialTracker[dateKey]).padStart(2, '0');
|
||||
return {
|
||||
serialNum,
|
||||
formatted: `Operify Tech. ${today.getFullYear()}.${String(today.getMonth() + 1).padStart(2, '0')}.${String(today.getDate()).padStart(2, '0')}.${serialNum}`,
|
||||
dateString: dateKey
|
||||
};
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
function isValidEmail(email) {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
@ -77,7 +88,6 @@ export async function GET() {
|
||||
|
||||
export async function POST(request) {
|
||||
try {
|
||||
// Parse form data
|
||||
const formData = await request.formData();
|
||||
const data = {};
|
||||
const files = {};
|
||||
@ -92,7 +102,6 @@ export async function POST(request) {
|
||||
|
||||
const form_type = data.form_type;
|
||||
|
||||
// Validate form type
|
||||
if (!form_type || !configs[form_type]) {
|
||||
return NextResponse.json({
|
||||
error: 'Invalid form type: ' + (form_type || 'missing')
|
||||
@ -102,7 +111,6 @@ export async function POST(request) {
|
||||
const config = configs[form_type];
|
||||
const errors = [];
|
||||
|
||||
// Validate required fields
|
||||
for (const required_field of config.required) {
|
||||
if (!data[required_field] || String(data[required_field]).trim() === '') {
|
||||
errors.push(`The "${fieldName(required_field)}" field is required.`);
|
||||
@ -111,37 +119,28 @@ export async function POST(request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Validate file upload for career form
|
||||
if (form_type === 'career') {
|
||||
const fileField = config.file_field;
|
||||
const uploadedFile = files[fileField];
|
||||
|
||||
if (!uploadedFile || uploadedFile.size === 0) {
|
||||
errors.push('Please upload your resume.');
|
||||
} else {
|
||||
const allowedExtensions = ['pdf', 'doc', 'docx'];
|
||||
const fileName = uploadedFile.name.toLowerCase();
|
||||
const fileExtension = fileName.split('.').pop();
|
||||
|
||||
const fileExtension = uploadedFile.name.toLowerCase().split('.').pop();
|
||||
if (!allowedExtensions.includes(fileExtension)) {
|
||||
errors.push('Invalid file type. Please upload a PDF, DOC, or DOCX file.');
|
||||
}
|
||||
|
||||
if (uploadedFile.size > 10 * 1024 * 1024) { // 10MB limit
|
||||
if (uploadedFile.size > 10 * 1024 * 1024) {
|
||||
errors.push('File is too large. Maximum size is 10MB.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
return NextResponse.json({
|
||||
error: errors.join(' ')
|
||||
}, { status: 400 });
|
||||
return NextResponse.json({ error: errors.join(' ') }, { status: 400 });
|
||||
}
|
||||
|
||||
// Construct email body
|
||||
let emailBody = `<h2>${config.subject}</h2><table style="border: 1px solid #b5b5b5; padding: 5px;">`;
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
if (config.fields.includes(key) && key !== 'form_type' && key !== 'sample_details') {
|
||||
emailBody += `<tr>
|
||||
@ -151,7 +150,6 @@ export async function POST(request) {
|
||||
}
|
||||
}
|
||||
|
||||
// Add file info if uploaded
|
||||
if (form_type === 'career' && files.resume) {
|
||||
emailBody += `<tr>
|
||||
<td style="border: 1px solid #b5b5b5; padding: 5px;"><strong>Resume</strong></td>
|
||||
@ -159,20 +157,17 @@ export async function POST(request) {
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
// Add sample details for sample form
|
||||
if (form_type === 'sample' && data.sample_details) {
|
||||
try {
|
||||
const sampleDetails = JSON.parse(data.sample_details);
|
||||
if (sampleDetails && sampleDetails.length > 0) {
|
||||
if (sampleDetails.length > 0) {
|
||||
emailBody += `<tr>
|
||||
<td colspan="2" style="border: 1px solid #b5b5b5; padding: 10px; background-color: #e8f5f3; text-align: center;"><strong>SAMPLE DETAILS</strong></td>
|
||||
</tr>`;
|
||||
|
||||
sampleDetails.forEach((sample, index) => {
|
||||
emailBody += `<tr>
|
||||
<td colspan="2" style="border: 1px solid #b5b5b5; padding: 8px; background-color: #f0f8f5; font-weight: bold;">Sample ${index + 1}</td>
|
||||
</tr>`;
|
||||
|
||||
Object.entries(sample).forEach(([key, value]) => {
|
||||
if (value && String(value).trim() !== '') {
|
||||
emailBody += `<tr>
|
||||
@ -183,25 +178,16 @@ export async function POST(request) {
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing sample details:', error);
|
||||
} catch {
|
||||
emailBody += `<tr>
|
||||
<td colspan="2" style="border: 1px solid #b5b5b5; padding: 5px; color: red;">Error: Could not parse sample details</td>
|
||||
</tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
emailBody += '</table>';
|
||||
|
||||
// Determine reply-to email based on form type
|
||||
let replyToEmail;
|
||||
if (form_type === 'sample') {
|
||||
replyToEmail = data.Email;
|
||||
} else {
|
||||
replyToEmail = data.email;
|
||||
}
|
||||
let replyToEmail = form_type === 'sample' ? data.Email : data.email;
|
||||
|
||||
// Create transporter
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: 'smtp.gmail.com',
|
||||
port: 587,
|
||||
@ -212,17 +198,24 @@ export async function POST(request) {
|
||||
},
|
||||
});
|
||||
|
||||
// Prepare email options
|
||||
// SERIAL NUMBER LOGIC
|
||||
let serialInfo;
|
||||
if (form_type === 'sample') {
|
||||
serialInfo = generateSerialNumber();
|
||||
}
|
||||
|
||||
// Internal mail
|
||||
const mailOptions = {
|
||||
from: `${emailConfig.from_email_name} <${emailConfig.from_email}>`,
|
||||
to: `${emailConfig.to_email_name} <${emailConfig.to_email}>`,
|
||||
replyTo: replyToEmail || emailConfig.from_email,
|
||||
subject: config.subject,
|
||||
subject: form_type === 'sample'
|
||||
? `${data.Company_Institution} | ${data.Principal_Investigator} | ${serialInfo.dateString} | ${serialInfo.formatted}`
|
||||
: config.subject,
|
||||
html: emailBody,
|
||||
text: emailBody.replace(/<[^>]*>/g, ''), // Strip HTML for text version
|
||||
text: emailBody.replace(/<[^>]*>/g, '')
|
||||
};
|
||||
|
||||
// Add attachment for career form
|
||||
if (form_type === 'career' && files.resume) {
|
||||
const fileBuffer = await files.resume.arrayBuffer();
|
||||
mailOptions.attachments = [{
|
||||
@ -231,18 +224,24 @@ export async function POST(request) {
|
||||
}];
|
||||
}
|
||||
|
||||
// Send email
|
||||
await transporter.sendMail(mailOptions);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: config.successMessage
|
||||
});
|
||||
// PI email for sample form
|
||||
if (form_type === 'sample') {
|
||||
const piMailOptions = {
|
||||
from: `${emailConfig.from_email_name} <${emailConfig.from_email}>`,
|
||||
to: `${data.Principal_Investigator} <${data.Email}>`,
|
||||
subject: `SIF Form received for Project with - ${serialInfo.formatted}`,
|
||||
html: emailBody,
|
||||
text: emailBody.replace(/<[^>]*>/g, '')
|
||||
};
|
||||
await transporter.sendMail(piMailOptions);
|
||||
}
|
||||
|
||||
return NextResponse.json({ success: true, message: config.successMessage });
|
||||
|
||||
} catch (error) {
|
||||
console.error('Email sending error:', error);
|
||||
return NextResponse.json({
|
||||
error: 'Error sending email. Please try again later.'
|
||||
}, { status: 500 });
|
||||
return NextResponse.json({ error: 'Error sending email. Please try again later.' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user