Refactor EOI management into dedicated component
Extract EOI links and generation functionality from InterestDetailsModal into a new reusable EOISection component. This improves code organization and maintainability while adding debounce support for form submissions. - Create new EOISection.vue component for EOI management - Remove inline EOI links section from InterestDetailsModal - Add debounce utility for form submission handling - Update email generation and thread fetching logic - Update related types and utilities
This commit is contained in:
parent
76d04a1e2a
commit
d9fb94a76c
|
|
@ -0,0 +1,177 @@
|
|||
<template>
|
||||
<div class="border rounded-lg p-4 bg-gray-50">
|
||||
<h3 class="text-lg font-semibold mb-4">EOI Management</h3>
|
||||
|
||||
<!-- Generate EOI Button -->
|
||||
<div v-if="!hasEOI" class="mb-4">
|
||||
<button
|
||||
@click="generateEOI"
|
||||
:disabled="isGenerating"
|
||||
class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{{ isGenerating ? 'Generating EOI...' : 'Generate EOI' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- EOI Status Badge -->
|
||||
<div v-if="hasEOI" class="mb-4">
|
||||
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium"
|
||||
:class="{
|
||||
'bg-yellow-100 text-yellow-800': interest['EOI Status'] === 'Waiting for Signatures',
|
||||
'bg-green-100 text-green-800': interest['EOI Status'] === 'Signed',
|
||||
'bg-gray-100 text-gray-800': interest['EOI Status'] === 'Awaiting Further Details'
|
||||
}">
|
||||
{{ interest['EOI Status'] }}
|
||||
</span>
|
||||
<span v-if="interest['EOI Time Sent']" class="ml-2 text-sm text-gray-600">
|
||||
Sent: {{ formatDate(interest['EOI Time Sent']) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Signature Links -->
|
||||
<div v-if="hasEOI" class="space-y-3">
|
||||
<div class="border rounded p-3 bg-white">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<span class="font-medium">Client Signature Link</span>
|
||||
<span class="text-sm text-gray-500 ml-2">({{ interest['Full Name'] }})</span>
|
||||
</div>
|
||||
<button
|
||||
@click="copyLink(interest['Signature Link Client'])"
|
||||
class="text-blue-600 hover:text-blue-800 text-sm flex items-center"
|
||||
>
|
||||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded p-3 bg-white">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<span class="font-medium">CC Signature Link</span>
|
||||
<span class="text-sm text-gray-500 ml-2">(Oscar Faragher)</span>
|
||||
</div>
|
||||
<button
|
||||
@click="copyLink(interest['Signature Link CC'])"
|
||||
class="text-blue-600 hover:text-blue-800 text-sm flex items-center"
|
||||
>
|
||||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded p-3 bg-white">
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<span class="font-medium">Developer Signature Link</span>
|
||||
<span class="text-sm text-gray-500 ml-2">(David Mizrahi)</span>
|
||||
</div>
|
||||
<button
|
||||
@click="copyLink(interest['Signature Link Developer'])"
|
||||
class="text-blue-600 hover:text-blue-800 text-sm flex items-center"
|
||||
>
|
||||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Regenerate Button -->
|
||||
<div v-if="hasEOI && interest['EOI Status'] !== 'Signed'" class="mt-4">
|
||||
<button
|
||||
@click="generateEOI"
|
||||
:disabled="isGenerating"
|
||||
class="text-sm text-gray-600 hover:text-gray-800 underline"
|
||||
>
|
||||
Regenerate EOI
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Interest } from '~/utils/types';
|
||||
|
||||
const props = defineProps<{
|
||||
interest: Interest;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
'eoi-generated': [data: { signingLinks: Record<string, string> }];
|
||||
'update': [];
|
||||
}>();
|
||||
|
||||
const { showToast } = useToast();
|
||||
const isGenerating = ref(false);
|
||||
|
||||
const hasEOI = computed(() => {
|
||||
return !!(props.interest['Signature Link Client'] ||
|
||||
props.interest['Signature Link CC'] ||
|
||||
props.interest['Signature Link Developer']);
|
||||
});
|
||||
|
||||
const generateEOI = async () => {
|
||||
isGenerating.value = true;
|
||||
|
||||
try {
|
||||
const response = await $fetch<{
|
||||
success: boolean;
|
||||
documentId: string | number;
|
||||
clientSigningUrl: string;
|
||||
signingLinks: Record<string, string>;
|
||||
}>('/api/email/generate-eoi-document', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'x-tag': '094ut234'
|
||||
},
|
||||
body: {
|
||||
interestId: props.interest.Id.toString()
|
||||
}
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
showToast(response.documentId === 'existing'
|
||||
? 'EOI already exists - signature links retrieved'
|
||||
: 'EOI generated successfully');
|
||||
|
||||
emit('eoi-generated', { signingLinks: response.signingLinks });
|
||||
emit('update'); // Trigger parent to refresh data
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Failed to generate EOI:', error);
|
||||
showToast(error.data?.statusMessage || 'Failed to generate EOI');
|
||||
} finally {
|
||||
isGenerating.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const copyLink = (link: string | undefined) => {
|
||||
if (!link) return;
|
||||
|
||||
navigator.clipboard.writeText(link).then(() => {
|
||||
showToast('Signature link copied to clipboard');
|
||||
}).catch(() => {
|
||||
showToast('Failed to copy link');
|
||||
});
|
||||
};
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
|
@ -244,7 +244,7 @@
|
|||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<v-form @submit.prevent="saveInterest">
|
||||
<v-form @submit.prevent="handleFormSubmit">
|
||||
<!-- Contact Information Section -->
|
||||
<v-card variant="flat" class="mb-6">
|
||||
<v-card-title class="text-h6 d-flex align-center pb-4">
|
||||
|
|
@ -626,79 +626,14 @@
|
|||
</v-card-text>
|
||||
</v-card>
|
||||
|
||||
<!-- EOI Links Section (only shows if EOI has been sent) -->
|
||||
<v-card
|
||||
v-if="hasEOILinks"
|
||||
variant="flat"
|
||||
class="mb-6"
|
||||
>
|
||||
<v-card-title class="text-h6 d-flex align-center pb-4">
|
||||
<v-icon class="mr-2" color="primary">mdi-link-variant</v-icon>
|
||||
EOI Links
|
||||
</v-card-title>
|
||||
<v-card-text class="pt-2">
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-if="(interest as any)['EOI Client Link']"
|
||||
class="mb-2"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-avatar color="primary" size="40">
|
||||
<v-icon>mdi-account</v-icon>
|
||||
</v-avatar>
|
||||
</template>
|
||||
<v-list-item-title>Client ({{ interest['Full Name'] }})</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-truncate">{{ (interest as any)['EOI Client Link'] }}</v-list-item-subtitle>
|
||||
<template v-slot:append>
|
||||
<v-btn
|
||||
icon="mdi-content-copy"
|
||||
variant="text"
|
||||
@click="copyToClipboard((interest as any)['EOI Client Link'], 'Client')"
|
||||
></v-btn>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item
|
||||
v-if="(interest as any)['EOI Oscar Link']"
|
||||
class="mb-2"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-avatar color="success" size="40">
|
||||
<v-icon>mdi-account-check</v-icon>
|
||||
</v-avatar>
|
||||
</template>
|
||||
<v-list-item-title>Oscar Faragher (Approver)</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-truncate">{{ (interest as any)['EOI Oscar Link'] }}</v-list-item-subtitle>
|
||||
<template v-slot:append>
|
||||
<v-btn
|
||||
icon="mdi-content-copy"
|
||||
variant="text"
|
||||
@click="copyToClipboard((interest as any)['EOI Oscar Link'], 'Oscar Faragher')"
|
||||
></v-btn>
|
||||
</template>
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item
|
||||
v-if="(interest as any)['EOI David Link']"
|
||||
class="mb-2"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-avatar color="secondary" size="40">
|
||||
<v-icon>mdi-account-tie</v-icon>
|
||||
</v-avatar>
|
||||
</template>
|
||||
<v-list-item-title>David Mizrahi (Signer)</v-list-item-title>
|
||||
<v-list-item-subtitle class="text-truncate">{{ (interest as any)['EOI David Link'] }}</v-list-item-subtitle>
|
||||
<template v-slot:append>
|
||||
<v-btn
|
||||
icon="mdi-content-copy"
|
||||
variant="text"
|
||||
@click="copyToClipboard((interest as any)['EOI David Link'], 'David Mizrahi')"
|
||||
></v-btn>
|
||||
</template>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card-text>
|
||||
<!-- EOI Management Section -->
|
||||
<v-card variant="flat" class="mb-6">
|
||||
<EOISection
|
||||
v-if="interest"
|
||||
:interest="interest"
|
||||
@eoi-generated="onEOIGenerated"
|
||||
@update="onInterestUpdated"
|
||||
/>
|
||||
</v-card>
|
||||
|
||||
<!-- Email Communication Section -->
|
||||
|
|
@ -714,10 +649,30 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, watch, onMounted } from "vue";
|
||||
import { ref, computed, watch, onMounted, onUnmounted } from "vue";
|
||||
import type { Interest, Berth } from "@/utils/types";
|
||||
|
||||
// Simple debounce implementation
|
||||
function debounce<T extends (...args: any[]) => any>(
|
||||
func: T,
|
||||
wait: number
|
||||
): ((...args: Parameters<T>) => void) & { cancel: () => void } {
|
||||
let timeout: NodeJS.Timeout | null = null;
|
||||
|
||||
const debounced = (...args: Parameters<T>) => {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func(...args), wait);
|
||||
};
|
||||
|
||||
debounced.cancel = () => {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
};
|
||||
|
||||
return debounced;
|
||||
}
|
||||
import PhoneInput from "./PhoneInput.vue";
|
||||
import EmailCommunication from "./EmailCommunication.vue";
|
||||
import EOISection from "./EOISection.vue";
|
||||
import {
|
||||
InterestSalesProcessLevelFlow,
|
||||
InterestLeadCategoryFlow,
|
||||
|
|
@ -753,6 +708,10 @@ const toast = useToast();
|
|||
// Local copy of the interest for editing
|
||||
const interest = ref<Interest | null>(null);
|
||||
|
||||
// Auto-save related
|
||||
const hasUnsavedChanges = ref(false);
|
||||
const autoSaveTimer = ref<NodeJS.Timeout | null>(null);
|
||||
|
||||
// Loading states for buttons
|
||||
const isSaving = ref(false);
|
||||
const isRequestingMoreInfo = ref(false);
|
||||
|
|
@ -768,11 +727,32 @@ const selectedBerthRecommendations = ref<number[]>([]);
|
|||
const originalBerths = ref<number[]>([]);
|
||||
const originalBerthRecommendations = ref<number[]>([]);
|
||||
|
||||
// Auto-save function (debounced)
|
||||
const autoSave = debounce(async () => {
|
||||
if (!hasUnsavedChanges.value || !interest.value) return;
|
||||
|
||||
console.log('Auto-saving interest...');
|
||||
await saveInterest(true); // Pass true to indicate auto-save
|
||||
}, 2000); // 2 second delay
|
||||
|
||||
// Watch for changes to trigger auto-save
|
||||
watch(
|
||||
() => interest.value,
|
||||
(newValue, oldValue) => {
|
||||
if (newValue && oldValue && JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
|
||||
hasUnsavedChanges.value = true;
|
||||
autoSave();
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// Sync the local copy with the prop
|
||||
watch(
|
||||
() => props.selectedInterest,
|
||||
async (newInterest) => {
|
||||
if (newInterest) {
|
||||
hasUnsavedChanges.value = false;
|
||||
interest.value = { ...newInterest };
|
||||
// Load linked berths and recommendations
|
||||
await loadLinkedBerths();
|
||||
|
|
@ -812,7 +792,11 @@ const closeModal = () => {
|
|||
isOpen.value = false;
|
||||
};
|
||||
|
||||
const saveInterest = async () => {
|
||||
const handleFormSubmit = () => {
|
||||
saveInterest();
|
||||
};
|
||||
|
||||
const saveInterest = async (isAutoSave = false) => {
|
||||
if (interest.value) {
|
||||
isSaving.value = true;
|
||||
try {
|
||||
|
|
@ -833,12 +817,21 @@ const saveInterest = async () => {
|
|||
},
|
||||
});
|
||||
|
||||
toast.success("Interest saved successfully!");
|
||||
emit("save", interest.value);
|
||||
closeModal();
|
||||
hasUnsavedChanges.value = false;
|
||||
|
||||
if (!isAutoSave) {
|
||||
toast.success("Interest saved successfully!");
|
||||
emit("save", interest.value);
|
||||
closeModal();
|
||||
} else {
|
||||
// For auto-save, just emit save to refresh parent
|
||||
emit("save", interest.value);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to save interest:", error);
|
||||
toast.error("Failed to save interest. Please try again.");
|
||||
if (!isAutoSave) {
|
||||
toast.error("Failed to save interest. Please try again.");
|
||||
}
|
||||
} finally {
|
||||
isSaving.value = false;
|
||||
}
|
||||
|
|
@ -1188,6 +1181,12 @@ const copyToClipboard = async (text: string, recipient: string) => {
|
|||
}
|
||||
};
|
||||
|
||||
// Handle EOI generated event
|
||||
const onEOIGenerated = (data: { signingLinks: Record<string, string> }) => {
|
||||
console.log('EOI generated with links:', data.signingLinks);
|
||||
// The EOISection component will trigger the update event, so we just need to handle that
|
||||
};
|
||||
|
||||
// Handle interest updated event from EmailCommunication
|
||||
const onInterestUpdated = async () => {
|
||||
// Reload the interest data
|
||||
|
|
@ -1216,4 +1215,13 @@ const onInterestUpdated = async () => {
|
|||
onMounted(() => {
|
||||
loadAvailableBerths();
|
||||
});
|
||||
|
||||
// Cleanup on unmount
|
||||
onUnmounted(() => {
|
||||
if (autoSaveTimer.value) {
|
||||
clearTimeout(autoSaveTimer.value);
|
||||
}
|
||||
// Cancel any pending auto-save
|
||||
autoSave.cancel();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -131,6 +131,37 @@ NUXT_DOCUMENSO_API_KEY=your-actual-api-key
|
|||
NUXT_DOCUMENSO_BASE_URL=https://signatures.portnimara.dev
|
||||
```
|
||||
|
||||
### 12. EOI Management System
|
||||
- **Added EOI Section Component**: New component to manage EOI document generation and signature links
|
||||
- **Features Implemented**:
|
||||
- Generate EOI button that creates documents via Documenso API
|
||||
- Checks for existing EOI before generating new one (prevents duplicates)
|
||||
- Displays all 3 signature links (Client, CC, Developer) with copy-to-clipboard functionality
|
||||
- Shows EOI status badge (Awaiting Further Details, Waiting for Signatures, Signed)
|
||||
- Regenerate option for non-signed documents
|
||||
- **Auto-Updates on EOI Generation**:
|
||||
- EOI Status → "Waiting for Signatures"
|
||||
- Sales Process Level → "LOI and NDA Sent"
|
||||
- EOI Time Sent → Current timestamp
|
||||
- Extra Comments → Appends "EOI Sent [timestamp]"
|
||||
- **Database Storage**: Links stored in new columns:
|
||||
- `Signature Link Client`
|
||||
- `Signature Link CC`
|
||||
- `Signature Link Developer`
|
||||
|
||||
### 13. Auto-Save Functionality
|
||||
- **Implemented**: Interest details now auto-save after 2 seconds of inactivity
|
||||
- **Features**:
|
||||
- Debounced save to prevent excessive API calls
|
||||
- Silent save without success notifications
|
||||
- Automatically triggers parent refresh to keep data in sync
|
||||
- Cancels pending saves on component unmount
|
||||
|
||||
### 14. IMAP BCC Search Fix
|
||||
- **Problem**: IMAP search was failing with "Cannot read properties of null"
|
||||
- **Cause**: BCC search criteria not supported by all IMAP servers
|
||||
- **Solution**: Removed BCC from search criteria, now only searches FROM, TO, and CC fields
|
||||
|
||||
## How It Works Now
|
||||
|
||||
1. **Email Session Management**:
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@
|
|||
"": {
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@vite-pwa/nuxt": "^0.10.6",
|
||||
"formidable": "^3.5.4",
|
||||
"imap": "^0.8.19",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mailparser": "^3.7.3",
|
||||
"mime-types": "^3.0.1",
|
||||
"minio": "^8.0.5",
|
||||
|
|
@ -3710,6 +3712,21 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/lodash": {
|
||||
"version": "4.17.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.17.tgz",
|
||||
"integrity": "sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/lodash-es": {
|
||||
"version": "4.17.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz",
|
||||
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/lodash": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mailparser": {
|
||||
"version": "3.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/mailparser/-/mailparser-3.4.6.tgz",
|
||||
|
|
@ -8821,6 +8838,12 @@
|
|||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash-es": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
|
|
|
|||
|
|
@ -8,9 +8,11 @@
|
|||
"postinstall": "nuxt prepare"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@vite-pwa/nuxt": "^0.10.6",
|
||||
"formidable": "^3.5.4",
|
||||
"imap": "^0.8.19",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mailparser": "^3.7.3",
|
||||
"mime-types": "^3.0.1",
|
||||
"minio": "^8.0.5",
|
||||
|
|
|
|||
|
|
@ -203,12 +203,12 @@ async function fetchImapEmails(
|
|||
console.log(`Searching in folder: ${folderName}`);
|
||||
|
||||
// Search for emails both sent and received with this client
|
||||
// Note: BCC search might not be supported by all IMAP servers
|
||||
const searchCriteria = [
|
||||
'OR',
|
||||
['FROM', clientEmail],
|
||||
['TO', clientEmail],
|
||||
['CC', clientEmail],
|
||||
['BCC', clientEmail]
|
||||
['CC', clientEmail]
|
||||
];
|
||||
|
||||
imap.search(searchCriteria, (err, results) => {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,21 @@ export default defineEventHandler(async (event) => {
|
|||
throw createError({ statusCode: 404, statusMessage: "Interest not found" });
|
||||
}
|
||||
|
||||
// Check if EOI already exists (has signature links)
|
||||
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']
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
const requiredFields = [
|
||||
{ field: 'Full Name', value: interest['Full Name'] },
|
||||
|
|
@ -260,15 +275,15 @@ export default defineEventHandler(async (event) => {
|
|||
'Extra Comments': updatedComments
|
||||
};
|
||||
|
||||
// Add signing links to update data
|
||||
// Add signing links to update data with new column names
|
||||
if (signingLinks['Client']) {
|
||||
updateData['EOI Client Link'] = signingLinks['Client'];
|
||||
updateData['Signature Link Client'] = signingLinks['Client'];
|
||||
}
|
||||
if (signingLinks['David Mizrahi']) {
|
||||
updateData['EOI David Link'] = signingLinks['David Mizrahi'];
|
||||
updateData['Signature Link Developer'] = signingLinks['David Mizrahi'];
|
||||
}
|
||||
if (signingLinks['Oscar Faragher']) {
|
||||
updateData['EOI Oscar Link'] = signingLinks['Oscar Faragher'];
|
||||
updateData['Signature Link CC'] = signingLinks['Oscar Faragher'];
|
||||
}
|
||||
|
||||
await updateInterest(interestId, updateData);
|
||||
|
|
|
|||
|
|
@ -98,9 +98,13 @@ export const updateInterest = async (id: string, data: Partial<Interest>, retryC
|
|||
"Contract Status",
|
||||
// Add the EOI link fields
|
||||
"EOI Client Link",
|
||||
"EOI David Link",
|
||||
"EOI David Link",
|
||||
"EOI Oscar Link",
|
||||
"EOI Document"
|
||||
"EOI Document",
|
||||
// Add the new signature link fields
|
||||
"Signature Link Client",
|
||||
"Signature Link CC",
|
||||
"Signature Link Developer"
|
||||
];
|
||||
|
||||
// Filter the data to only include allowed fields
|
||||
|
|
|
|||
|
|
@ -119,6 +119,14 @@ export interface Interest {
|
|||
"Contract Sent Status": ContractSentStatus;
|
||||
"Deposit 10% Status": Deposit10PercentStatus;
|
||||
"Contract Status": ContractStatus;
|
||||
// EOI Link fields
|
||||
"EOI Client Link"?: string;
|
||||
"EOI David Link"?: string;
|
||||
"EOI Oscar Link"?: string;
|
||||
// New signature link fields
|
||||
"Signature Link Client"?: string;
|
||||
"Signature Link CC"?: string;
|
||||
"Signature Link Developer"?: string;
|
||||
}
|
||||
|
||||
export interface InterestsResponse {
|
||||
|
|
|
|||
Loading…
Reference in New Issue