updates
This commit is contained in:
parent
cf4af2cbff
commit
09c32ae6cb
|
|
@ -91,7 +91,10 @@
|
||||||
<v-icon :size="mobile ? 'small' : 'default'">mdi-account</v-icon>
|
<v-icon :size="mobile ? 'small' : 'default'">mdi-account</v-icon>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</template>
|
</template>
|
||||||
<v-list-item-title :class="mobile ? 'text-body-2' : ''">Client Signature Link</v-list-item-title>
|
<v-list-item-title :class="mobile ? 'text-body-2' : ''">
|
||||||
|
Client Signature Link
|
||||||
|
<v-icon v-if="signatureStatus.clientSigned" color="success" :size="mobile ? 'small' : 'default'" class="ml-1">mdi-check-circle</v-icon>
|
||||||
|
</v-list-item-title>
|
||||||
<v-list-item-subtitle :class="mobile ? 'text-caption' : ''">{{ interest['Full Name'] }}</v-list-item-subtitle>
|
<v-list-item-subtitle :class="mobile ? 'text-caption' : ''">{{ interest['Full Name'] }}</v-list-item-subtitle>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<v-btn-group variant="text" :size="mobile ? 'small' : 'default'">
|
<v-btn-group variant="text" :size="mobile ? 'small' : 'default'">
|
||||||
|
|
@ -114,7 +117,10 @@
|
||||||
<v-icon :size="mobile ? 'small' : 'default'">mdi-account-check</v-icon>
|
<v-icon :size="mobile ? 'small' : 'default'">mdi-account-check</v-icon>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</template>
|
</template>
|
||||||
<v-list-item-title :class="mobile ? 'text-body-2' : ''">CC Signature Link</v-list-item-title>
|
<v-list-item-title :class="mobile ? 'text-body-2' : ''">
|
||||||
|
CC Signature Link
|
||||||
|
<v-icon v-if="isSignedByEmail('sales@portnimara.com')" color="success" :size="mobile ? 'small' : 'default'" class="ml-1">mdi-check-circle</v-icon>
|
||||||
|
</v-list-item-title>
|
||||||
<v-list-item-subtitle :class="mobile ? 'text-caption' : ''">Oscar Faragher</v-list-item-subtitle>
|
<v-list-item-subtitle :class="mobile ? 'text-caption' : ''">Oscar Faragher</v-list-item-subtitle>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<v-btn-group variant="text" :size="mobile ? 'small' : 'default'">
|
<v-btn-group variant="text" :size="mobile ? 'small' : 'default'">
|
||||||
|
|
@ -137,7 +143,10 @@
|
||||||
<v-icon :size="mobile ? 'small' : 'default'">mdi-account-tie</v-icon>
|
<v-icon :size="mobile ? 'small' : 'default'">mdi-account-tie</v-icon>
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
</template>
|
</template>
|
||||||
<v-list-item-title :class="mobile ? 'text-body-2' : ''">Developer Signature Link</v-list-item-title>
|
<v-list-item-title :class="mobile ? 'text-body-2' : ''">
|
||||||
|
Developer Signature Link
|
||||||
|
<v-icon v-if="isSignedByEmail('dm@portnimara.com')" color="success" :size="mobile ? 'small' : 'default'" class="ml-1">mdi-check-circle</v-icon>
|
||||||
|
</v-list-item-title>
|
||||||
<v-list-item-subtitle :class="mobile ? 'text-caption' : ''">David Mizrahi</v-list-item-subtitle>
|
<v-list-item-subtitle :class="mobile ? 'text-caption' : ''">David Mizrahi</v-list-item-subtitle>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<v-btn-group variant="text" :size="mobile ? 'small' : 'default'">
|
<v-btn-group variant="text" :size="mobile ? 'small' : 'default'">
|
||||||
|
|
@ -313,6 +322,18 @@ const selectedFile = ref<File | null>(null);
|
||||||
const showDeleteConfirmDialog = ref(false);
|
const showDeleteConfirmDialog = ref(false);
|
||||||
const isDeleting = ref(false);
|
const isDeleting = ref(false);
|
||||||
|
|
||||||
|
// Signature status tracking
|
||||||
|
const signatureStatus = ref({
|
||||||
|
documentStatus: '',
|
||||||
|
clientSigned: false,
|
||||||
|
allSigned: false,
|
||||||
|
signedRecipients: [] as any[],
|
||||||
|
unsignedRecipients: [] as any[]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Polling interval for signature status
|
||||||
|
let statusPollingInterval: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
const hasEOI = computed(() => {
|
const hasEOI = computed(() => {
|
||||||
return !!(props.interest['Signature Link Client'] ||
|
return !!(props.interest['Signature Link Client'] ||
|
||||||
props.interest['Signature Link CC'] ||
|
props.interest['Signature Link CC'] ||
|
||||||
|
|
@ -520,4 +541,102 @@ const deleteEOI = async () => {
|
||||||
isDeleting.value = false;
|
isDeleting.value = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Check signature status from Documenso
|
||||||
|
const checkSignatureStatus = async () => {
|
||||||
|
if (!hasEOI.value || isEOISigned.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await $fetch<{
|
||||||
|
success: boolean;
|
||||||
|
documentStatus: string;
|
||||||
|
clientSigned: boolean;
|
||||||
|
allSigned: boolean;
|
||||||
|
signedRecipients: any[];
|
||||||
|
unsignedRecipients: any[];
|
||||||
|
}>('/api/eoi/check-signature-status', {
|
||||||
|
headers: {
|
||||||
|
'x-tag': '094ut234'
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
interestId: props.interest.Id.toString()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.success) {
|
||||||
|
signatureStatus.value = {
|
||||||
|
documentStatus: response.documentStatus,
|
||||||
|
clientSigned: response.clientSigned,
|
||||||
|
allSigned: response.allSigned,
|
||||||
|
signedRecipients: response.signedRecipients,
|
||||||
|
unsignedRecipients: response.unsignedRecipients
|
||||||
|
};
|
||||||
|
|
||||||
|
// If all signed, update the EOI status
|
||||||
|
if (response.allSigned && props.interest['EOI Status'] !== 'Signed') {
|
||||||
|
try {
|
||||||
|
await $fetch('/api/update-interest', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'x-tag': '094ut234'
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
id: props.interest.Id.toString(),
|
||||||
|
data: {
|
||||||
|
'EOI Status': 'Signed',
|
||||||
|
'Sales Process Level': 'Made Reservation'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
emit('update'); // Refresh parent data
|
||||||
|
stopSignaturePolling(); // Stop polling once signed
|
||||||
|
} catch (updateError) {
|
||||||
|
console.error('Failed to update EOI status:', updateError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to check signature status:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if a specific email has signed
|
||||||
|
const isSignedByEmail = (email: string) => {
|
||||||
|
return signatureStatus.value.signedRecipients.some(
|
||||||
|
recipient => recipient.email === email
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Start polling for signature status
|
||||||
|
const startSignaturePolling = () => {
|
||||||
|
// Initial check
|
||||||
|
checkSignatureStatus();
|
||||||
|
|
||||||
|
// Poll every 30 seconds
|
||||||
|
statusPollingInterval = setInterval(() => {
|
||||||
|
checkSignatureStatus();
|
||||||
|
}, 30000);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stop polling
|
||||||
|
const stopSignaturePolling = () => {
|
||||||
|
if (statusPollingInterval) {
|
||||||
|
clearInterval(statusPollingInterval);
|
||||||
|
statusPollingInterval = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Watch for changes in EOI status
|
||||||
|
watch(() => hasEOI.value, (newValue) => {
|
||||||
|
if (newValue && !isEOISigned.value) {
|
||||||
|
startSignaturePolling();
|
||||||
|
} else {
|
||||||
|
stopSignaturePolling();
|
||||||
|
}
|
||||||
|
}, { immediate: true });
|
||||||
|
|
||||||
|
// Cleanup on unmount
|
||||||
|
onUnmounted(() => {
|
||||||
|
stopSignaturePolling();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ interface EmailMessage {
|
||||||
timestamp: string;
|
timestamp: string;
|
||||||
direction: 'sent' | 'received';
|
direction: 'sent' | 'received';
|
||||||
threadId?: string;
|
threadId?: string;
|
||||||
|
attachments?: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
|
|
@ -304,6 +305,15 @@ async function fetchImapEmails(
|
||||||
return;
|
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 = {
|
const email: EmailMessage = {
|
||||||
id: parsed.messageId || `${Date.now()}-${seqno}`,
|
id: parsed.messageId || `${Date.now()}-${seqno}`,
|
||||||
from: fromEmail,
|
from: fromEmail,
|
||||||
|
|
@ -312,7 +322,8 @@ async function fetchImapEmails(
|
||||||
body: parsed.text || '',
|
body: parsed.text || '',
|
||||||
html: parsed.html || undefined,
|
html: parsed.html || undefined,
|
||||||
timestamp: parsed.date?.toISOString() || new Date().toISOString(),
|
timestamp: parsed.date?.toISOString() || new Date().toISOString(),
|
||||||
direction: fromEmail.toLowerCase().includes(userEmail.toLowerCase()) ? 'sent' : 'received'
|
direction: fromEmail.toLowerCase().includes(userEmail.toLowerCase()) ? 'sent' : 'received',
|
||||||
|
attachments: attachments
|
||||||
};
|
};
|
||||||
|
|
||||||
if (parsed.headers.has('in-reply-to')) {
|
if (parsed.headers.has('in-reply-to')) {
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,8 @@ export default defineEventHandler(async (event) => {
|
||||||
html: htmlBody,
|
html: htmlBody,
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
direction: 'sent',
|
direction: 'sent',
|
||||||
interestId: interestId
|
interestId: interestId,
|
||||||
|
attachments: attachments // Include attachment info
|
||||||
};
|
};
|
||||||
|
|
||||||
const objectName = `interest-${interestId}/${Date.now()}-sent.json`;
|
const objectName = `interest-${interestId}/${Date.now()}-sent.json`;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue