port-nimara-client-portal/server/api/email/generate-eoi-document.ts

281 lines
8.7 KiB
TypeScript

import { getInterestById, updateInterest } from '~/server/utils/nocodb';
interface DocumensoRecipient {
id: number;
name: string;
email: string;
role: 'SIGNER' | 'APPROVER';
signingOrder: number;
signingUrl?: string;
}
interface DocumensoResponse {
id: string;
recipients: DocumensoRecipient[];
}
export default defineEventHandler(async (event) => {
const xTagHeader = getRequestHeader(event, "x-tag");
if (!xTagHeader || (xTagHeader !== "094ut234" && xTagHeader !== "pjnvü1230")) {
throw createError({ statusCode: 401, statusMessage: "unauthenticated" });
}
try {
const body = await readBody(event);
const { interestId } = body;
if (!interestId) {
throw createError({ statusCode: 400, statusMessage: "Interest ID is required" });
}
// Get the interest data
const interest = await getInterestById(interestId);
if (!interest) {
throw createError({ statusCode: 404, statusMessage: "Interest not found" });
}
// Validate required fields
const requiredFields = [
{ field: 'Full Name', value: interest['Full Name'] },
{ field: 'Email Address', value: interest['Email Address'] },
{ field: 'Address', value: interest['Address'] },
{ field: 'Yacht Name', value: interest['Yacht Name'] },
{ field: 'Length', value: interest['Length'] },
{ field: 'Width', value: interest['Width'] },
{ field: 'Depth', value: interest['Depth'] }
];
const missingFields = requiredFields.filter(f => !f.value).map(f => f.field);
if (missingFields.length > 0) {
throw createError({
statusCode: 400,
statusMessage: `Missing required fields: ${missingFields.join(', ')}`
});
}
// Get linked berths
const berthsResponse = await $fetch<{ list: Array<{ 'Mooring Number': string }> }>(
"/api/get-interest-berths",
{
headers: {
"x-tag": xTagHeader,
},
params: {
interestId: interestId,
linkType: "berths",
},
}
);
const berths = berthsResponse.list || [];
if (berths.length === 0) {
throw createError({
statusCode: 400,
statusMessage: "No berths linked to this interest. Please link at least one berth."
});
}
// Concatenate berth numbers
const berthNumbers = berths.map(b => b['Mooring Number']).join(', ');
// Documenso API configuration
const documensoApiKey = process.env.NUXT_DOCUMENSO_API_KEY;
const documensoBaseUrl = process.env.NUXT_DOCUMENSO_BASE_URL;
const templateId = '9';
if (!documensoApiKey || !documensoBaseUrl) {
throw createError({
statusCode: 500,
statusMessage: "Documenso configuration missing. Please check NUXT_DOCUMENSO_API_KEY and NUXT_DOCUMENSO_BASE_URL environment variables."
});
}
// 1. Get template (optional - just to verify it exists)
try {
const templateResponse = await fetch(`${documensoBaseUrl}/api/v1/templates/${templateId}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${documensoApiKey}`
}
});
if (!templateResponse.ok) {
throw new Error(`Template not found: ${templateResponse.statusText}`);
}
} catch (error) {
console.error('Failed to verify template:', error);
throw createError({
statusCode: 500,
statusMessage: "Failed to verify template. Please check Documenso configuration."
});
}
// 2. Create document
const createDocumentPayload = {
meta: {
message: `Dear ${interest['Full Name']},\n\nThank you for your interest in a berth at Port Nimara. Please click the link above to sign your LOI.\n\nBest Regards,\nPort Nimara Team`,
subject: "Your LOI is ready to be signed",
redirectUrl: "https://portnimara.com",
distributionMethod: "SEQUENTIAL"
},
title: `${interest['Full Name']}-EOI-NDA`,
externalId: `loi-${interestId}`,
formValues: {
"Name": interest['Full Name'],
"Draft": interest['Depth'],
"Email": interest['Email Address'],
"Width": interest['Width'],
"Length": interest['Length'],
"Address": interest['Address'],
"Lease_10": false,
"Purchase": true,
"Yacht Name": interest['Yacht Name'],
"Berth Number": berthNumbers
},
recipients: [
{
id: 155,
name: interest['Full Name'],
role: "SIGNER",
email: interest['Email Address'],
signingOrder: 1
},
{
id: 156,
name: "David Mizrahi",
role: "SIGNER",
email: "dm@portnimara.com",
signingOrder: 3
},
{
id: 157,
name: "Oscar Faragher",
role: "APPROVER",
email: "sales@portnimara.com",
signingOrder: 2
}
]
};
let documentResponse: DocumensoResponse;
try {
const response = await fetch(`${documensoBaseUrl}/api/v1/templates/${templateId}/generate-document`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${documensoApiKey}`
},
body: JSON.stringify(createDocumentPayload)
});
if (!response.ok) {
const errorText = await response.text();
console.error('Failed to create document:', errorText);
throw new Error(`Failed to create document: ${response.statusText}`);
}
documentResponse = await response.json();
} catch (error) {
console.error('Document creation error:', error);
throw createError({
statusCode: 500,
statusMessage: "Failed to create EOI document. Please try again."
});
}
// 3. Setup completion emails
try {
const completionResponse = await fetch(`${documensoBaseUrl}/api/v1/documents/${documentResponse.id}/send`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${documensoApiKey}`
},
body: JSON.stringify({
sendEmail: false,
sendCompletionEmails: true
})
});
if (!completionResponse.ok) {
console.error('Failed to setup completion emails:', await completionResponse.text());
// Don't fail the whole process if this fails
}
} catch (error) {
console.error('Completion email setup error:', error);
// Continue anyway
}
// Extract signing URLs from recipients
const signingLinks: Record<string, string> = {};
if (documentResponse.recipients) {
documentResponse.recipients.forEach(recipient => {
if (recipient.signingUrl) {
if (recipient.email === interest['Email Address']) {
signingLinks['Client'] = recipient.signingUrl;
} else if (recipient.email === 'dm@portnimara.com') {
signingLinks['David Mizrahi'] = recipient.signingUrl;
} else if (recipient.email === 'sales@portnimara.com') {
signingLinks['Oscar Faragher'] = recipient.signingUrl;
}
}
});
}
// 4. Update interest record
const currentDate = new Date();
const dateTimeString = currentDate.toLocaleString('en-US', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
hour12: true
});
const extraComments = interest['Extra Comments'] || '';
const updatedComments = extraComments + (extraComments ? '\n' : '') + `EOI Sent ${dateTimeString}`;
const updateData: any = {
'EOI Status': 'Waiting for Signatures',
'Sales Process Level': 'LOI and NDA Sent',
'EOI Time Sent': currentDate.toISOString(),
'Extra Comments': updatedComments
};
// Add signing links to update data
if (signingLinks['Client']) {
updateData['EOI Client Link'] = signingLinks['Client'];
}
if (signingLinks['David Mizrahi']) {
updateData['EOI David Link'] = signingLinks['David Mizrahi'];
}
if (signingLinks['Oscar Faragher']) {
updateData['EOI Oscar Link'] = signingLinks['Oscar Faragher'];
}
await updateInterest(interestId, updateData);
return {
success: true,
documentId: documentResponse.id,
clientSigningUrl: signingLinks['Client'] || '',
signingLinks: signingLinks
};
} catch (error) {
console.error('Failed to generate EOI document:', error);
if (error instanceof Error) {
throw createError({
statusCode: 500,
statusMessage: error.message || "Failed to generate EOI document"
});
} else {
throw createError({
statusCode: 500,
statusMessage: "An unexpected error occurred",
});
}
}
});