diff --git a/components/EmailDetailsDialog.vue b/components/EmailDetailsDialog.vue index 0a88fd3..e8d433d 100644 --- a/components/EmailDetailsDialog.vue +++ b/components/EmailDetailsDialog.vue @@ -139,25 +139,14 @@ const getAttachmentName = (attachment: any) => { }; const getAttachmentUrl = (attachment: any) => { - // If attachment is just a string (filename), assume it's in the client-emails bucket - if (typeof attachment === 'string') { - return `/api/files/proxy-download?fileName=${encodeURIComponent(attachment)}&bucket=client-emails`; + // If attachment has a path and bucket, construct the download URL + if (attachment.path && attachment.bucket) { + return `/api/files/proxy-download?path=${encodeURIComponent(attachment.path)}&bucket=${attachment.bucket}`; } - - // If it has a path property, use that - if (attachment?.path) { - const bucket = attachment.bucket || 'client-emails'; - return `/api/files/proxy-download?fileName=${encodeURIComponent(attachment.path)}&bucket=${bucket}`; - } - - // If it has a url property, use that directly - if (attachment?.url) { - return attachment.url; - } - - // Default fallback - const name = attachment?.name || attachment?.filename || 'attachment'; - return `/api/files/proxy-download?fileName=${encodeURIComponent(name)}&bucket=client-emails`; + // If it's just a URL, return it + if (attachment.url) return attachment.url; + // Otherwise return a placeholder + return '#'; }; diff --git a/components/InterestDetailsModal.vue b/components/InterestDetailsModal.vue index d41026e..ff5297a 100644 --- a/components/InterestDetailsModal.vue +++ b/components/InterestDetailsModal.vue @@ -196,7 +196,7 @@ - - + + + + + + + { // Update original values originalBerths.value = [...newBerths]; - - // Show success message - if (toAdd.length > 0 || toRemove.length > 0) { - toast.success("Berths updated successfully"); - } } catch (error) { console.error("Failed to update berths:", error); - // Show more specific error message on mobile - const errorMessage = mobile.value - ? "Could not update berths. Please check your connection and try again." - : "Failed to update berths. Please try again."; - toast.error(errorMessage); + toast.error("Failed to update berths. Please try again."); // Revert to original values on error selectedBerths.value = [...originalBerths.value]; } @@ -1273,20 +1302,6 @@ const onInterestUpdated = async () => { } }; -// Handle mobile save - call the save function directly without debounce -const handleMobileSave = () => { - console.log('Mobile save button clicked'); - // Cancel any pending debounced saves - if (debouncedSaveInterest) { - debouncedSaveInterest.cancel(); - } - if (autoSave) { - autoSave.cancel(); - } - // Call save directly - saveInterest(); -}; - // Load berths when component mounts onMounted(() => { loadAvailableBerths(); diff --git a/pages/dashboard/interest-list.vue b/pages/dashboard/interest-list.vue index bcbcbbe..4234dcc 100644 --- a/pages/dashboard/interest-list.vue +++ b/pages/dashboard/interest-list.vue @@ -125,7 +125,7 @@ :headers="headers" :items="filteredInterests" :search="search" - :sort-by="[{ key: 'Id', order: 'desc' }]" + :sort-by="[{ key: 'Created At', order: 'desc' }, { key: 'Full Name', order: 'asc' }]" must-sort hover :loading="loading" diff --git a/server/api/email/fetch-thread.ts b/server/api/email/fetch-thread.ts index d2ddcfe..af97bda 100644 --- a/server/api/email/fetch-thread.ts +++ b/server/api/email/fetch-thread.ts @@ -13,7 +13,6 @@ interface EmailMessage { timestamp: string; direction: 'sent' | 'received'; threadId?: string; - attachments?: any[]; } export default defineEventHandler(async (event) => { @@ -305,15 +304,6 @@ async function fetchImapEmails( return; } - // Extract attachments - const attachments = parsed.attachments ? parsed.attachments.map((att: any) => ({ - filename: att.filename || 'attachment', - name: att.filename || 'attachment', - size: att.size || 0, - type: att.contentType || 'application/octet-stream', - cid: att.cid || undefined - })) : []; - const email: EmailMessage = { id: parsed.messageId || `${Date.now()}-${seqno}`, from: fromEmail, @@ -322,8 +312,7 @@ async function fetchImapEmails( body: parsed.text || '', html: parsed.html || undefined, timestamp: parsed.date?.toISOString() || new Date().toISOString(), - direction: fromEmail.toLowerCase().includes(userEmail.toLowerCase()) ? 'sent' : 'received', - attachments: attachments + direction: fromEmail.toLowerCase().includes(userEmail.toLowerCase()) ? 'sent' : 'received' }; if (parsed.headers.has('in-reply-to')) { diff --git a/server/api/email/generate-eoi-document.ts b/server/api/email/generate-eoi-document.ts index 360ee2e..b401fa3 100644 --- a/server/api/email/generate-eoi-document.ts +++ b/server/api/email/generate-eoi-document.ts @@ -29,9 +29,6 @@ export default defineEventHandler(async (event) => { throw createError({ statusCode: 401, statusMessage: "unauthenticated" }); } - // Set longer timeout for this endpoint to prevent 502 errors - event.node.res.setTimeout(60000); // 60 seconds - try { const body = await readBody(event); const { interestId } = body; @@ -46,83 +43,19 @@ export default defineEventHandler(async (event) => { throw createError({ statusCode: 404, statusMessage: "Interest not found" }); } - // Documenso API configuration (declare early for use in regeneration) - const documensoApiKey = process.env.NUXT_DOCUMENSO_API_KEY; - const documensoBaseUrl = process.env.NUXT_DOCUMENSO_BASE_URL; - // Check if EOI already exists (has signature links) - const hasExistingEOI = !!(interest['Signature Link Client'] && interest['Signature Link CC'] && interest['Signature Link Developer']); - - if (hasExistingEOI) { - console.log('EOI already exists, checking if regeneration is needed'); - - // For regeneration, we need to delete the old document first - const regenerateRequested = body.regenerate === true; - - if (!regenerateRequested) { - console.log('Returning existing EOI links'); - return { - success: true, - documentId: 'existing', - clientSigningUrl: interest['Signature Link Client'], - signingLinks: { - 'Client': interest['Signature Link Client'], - 'CC': interest['Signature Link CC'], - 'Developer': interest['Signature Link Developer'] - } - }; - } else { - console.log('Regeneration requested, deleting old document first'); - - // Try to delete the old document from Documenso - try { - const externalId = `loi-${interestId}`; - - // Import the delete utility functions - const { getDocumesoDocumentByExternalId, checkDocumentSignatureStatus } = await import('~/server/utils/documeso'); - - const document = await getDocumesoDocumentByExternalId(externalId); - if (document) { - // Check if all parties have signed - const signatureStatus = await checkDocumentSignatureStatus(document.id); - - if (signatureStatus.allSigned) { - throw createError({ - statusCode: 403, - statusMessage: 'Cannot regenerate: All parties have already signed this document', - }); - } - - console.log('Deleting old document from Documenso:', document.id); - - const deleteResponse = await fetch(`${documensoBaseUrl}/api/v1/documents/${document.id}`, { - method: 'DELETE', - headers: { - 'Authorization': `Bearer ${documensoApiKey}`, - 'Content-Type': 'application/json' - } - }); - - if (!deleteResponse.ok) { - console.warn('Failed to delete old document from Documenso, continuing with new generation'); - } else { - console.log('Successfully deleted old document from Documenso'); - } - } - } catch (error: any) { - console.warn('Error during old document cleanup:', error.message); - // Continue with new document generation even if cleanup fails + if (interest['Signature Link Client'] && interest['Signature Link CC'] && interest['Signature Link Developer']) { + console.log('EOI already exists, returning existing links'); + return { + success: true, + documentId: 'existing', + clientSigningUrl: interest['Signature Link Client'], + signingLinks: { + 'Client': interest['Signature Link Client'], + 'CC': interest['Signature Link CC'], + 'Developer': interest['Signature Link Developer'] } - - // Reset signature links so new ones will be generated - await updateInterest(interestId, { - 'Signature Link Client': undefined, - 'Signature Link CC': undefined, - 'Signature Link Developer': undefined - }); - - console.log('Old document cleanup completed, proceeding with new generation'); - } + }; } // Validate required fields @@ -172,6 +105,8 @@ export default defineEventHandler(async (event) => { 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) { diff --git a/server/api/email/send.ts b/server/api/email/send.ts index 9a5ee89..6957d87 100644 --- a/server/api/email/send.ts +++ b/server/api/email/send.ts @@ -181,8 +181,7 @@ export default defineEventHandler(async (event) => { html: htmlBody, timestamp: new Date().toISOString(), direction: 'sent', - interestId: interestId, - attachments: attachments // Include attachment info + interestId: interestId }; const objectName = `interest-${interestId}/${Date.now()}-sent.json`; diff --git a/server/api/eoi/delete-generated-document.ts b/server/api/eoi/delete-generated-document.ts deleted file mode 100644 index c98943e..0000000 --- a/server/api/eoi/delete-generated-document.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { getInterestById, updateInterest } from '~/server/utils/nocodb'; -import { getDocumesoDocumentByExternalId, checkDocumentSignatureStatus } from '~/server/utils/documeso'; - -export default defineEventHandler(async (event) => { - const xTagHeader = getRequestHeader(event, "x-tag"); - - console.log('[EOI Delete Generated] Request received with x-tag:', xTagHeader); - - if (!xTagHeader || (xTagHeader !== "094ut234" && xTagHeader !== "pjnvü1230")) { - console.error('[EOI Delete Generated] Authentication failed - invalid x-tag'); - throw createError({ statusCode: 401, statusMessage: "unauthenticated" }); - } - - try { - const body = await readBody(event); - const { interestId } = body; - - console.log('[EOI Delete Generated] Interest ID:', interestId); - - if (!interestId) { - console.error('[EOI Delete Generated] No interest ID provided'); - throw createError({ - statusCode: 400, - statusMessage: 'Interest ID is required', - }); - } - - // Get current interest data - const interest = await getInterestById(interestId); - if (!interest) { - throw createError({ - statusCode: 404, - statusMessage: 'Interest not found', - }); - } - - // Check if there are signature links (indicating a generated document) - const hasSignatureLinks = !!(interest['Signature Link Client'] || - interest['Signature Link CC'] || - interest['Signature Link Developer']); - - if (!hasSignatureLinks) { - throw createError({ - statusCode: 400, - statusMessage: 'No generated EOI document found for this interest', - }); - } - - // Check signature status to prevent deletion of fully signed documents - const externalId = `loi-${interestId}`; - let documentId: number | null = null; - - try { - const document = await getDocumesoDocumentByExternalId(externalId); - if (document) { - documentId = document.id; - - // Check if all parties have signed - const signatureStatus = await checkDocumentSignatureStatus(documentId); - - if (signatureStatus.allSigned) { - throw createError({ - statusCode: 403, - statusMessage: 'Cannot delete: All parties have already signed this document', - }); - } - - console.log('[EOI Delete Generated] Document found in Documenso:', documentId); - console.log('[EOI Delete Generated] Signature status:', { - allSigned: signatureStatus.allSigned, - clientSigned: signatureStatus.clientSigned, - signedCount: signatureStatus.signedRecipients.length, - unsignedCount: signatureStatus.unsignedRecipients.length - }); - } - } catch (error: any) { - console.warn('[EOI Delete Generated] Could not find or check Documenso document:', error.message); - // Continue with deletion even if Documenso document not found - } - - // Delete document from Documenso if it exists - if (documentId) { - try { - const documensoApiKey = process.env.NUXT_DOCUMENSO_API_KEY; - const documensoBaseUrl = process.env.NUXT_DOCUMENSO_BASE_URL; - - if (!documensoApiKey || !documensoBaseUrl) { - console.error('[EOI Delete Generated] Documenso configuration missing'); - throw createError({ - statusCode: 500, - statusMessage: 'Documenso configuration missing', - }); - } - - console.log('[EOI Delete Generated] Deleting document from Documenso:', documentId); - - const deleteResponse = await fetch(`${documensoBaseUrl}/api/v1/documents/${documentId}`, { - method: 'DELETE', - headers: { - 'Authorization': `Bearer ${documensoApiKey}`, - 'Content-Type': 'application/json' - } - }); - - if (!deleteResponse.ok) { - const errorText = await deleteResponse.text(); - console.error('[EOI Delete Generated] Failed to delete from Documenso:', errorText); - throw new Error(`Failed to delete document from Documenso: ${deleteResponse.statusText}`); - } - - console.log('[EOI Delete Generated] Successfully deleted document from Documenso'); - } catch (error: any) { - console.error('[EOI Delete Generated] Error deleting from Documenso:', error); - // Don't throw here - continue with database cleanup even if Documenso deletion fails - } - } - - // Reset interest fields - const updateData = { - 'EOI Status': 'Awaiting Further Details' as const, - 'Sales Process Level': 'Specific Qualified Interest' as const, - 'EOI Time Sent': undefined, - 'Signature Link Client': undefined, - 'Signature Link CC': undefined, - 'Signature Link Developer': undefined, - 'Documeso ID': undefined, - 'reminder_enabled': false - }; - - console.log('[EOI Delete Generated] Resetting interest fields'); - - // Update the interest - await updateInterest(interestId, updateData); - - console.log('[EOI Delete Generated] Delete completed successfully'); - return { - success: true, - message: 'Generated EOI document deleted successfully', - }; - } catch (error: any) { - console.error('[EOI Delete Generated] Failed to delete generated EOI document:', error); - console.error('[EOI Delete Generated] Error stack:', error.stack); - throw createError({ - statusCode: error.statusCode || 500, - statusMessage: error.statusMessage || error.message || 'Failed to delete generated EOI document', - }); - } -}); diff --git a/server/utils/nocodb.ts b/server/utils/nocodb.ts index fd7c3e6..3d79a59 100644 --- a/server/utils/nocodb.ts +++ b/server/utils/nocodb.ts @@ -196,7 +196,6 @@ export const createInterest = async (data: Partial) => { "Date Added", "Width", "Depth", - "Created At", "Source", "Contact Method Preferred", "Lead Category", @@ -214,11 +213,6 @@ export const createInterest = async (data: Partial) => { } } - // Set Created At to current timestamp if not provided - if (!cleanData["Created At"]) { - cleanData["Created At"] = new Date().toISOString(); - } - // Remove any computed or relation fields that shouldn't be sent delete cleanData.Id; delete cleanData.Berths;