347 lines
10 KiB
Vue
347 lines
10 KiB
Vue
<template>
|
|
<v-card>
|
|
<v-card-title class="text-h6 d-flex align-center">
|
|
<v-icon class="mr-2">mdi-email-edit</v-icon>
|
|
Compose Email
|
|
</v-card-title>
|
|
<v-card-text>
|
|
<v-form @submit.prevent="sendEmail" ref="form">
|
|
<v-text-field
|
|
v-model="email.to"
|
|
label="To"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
prepend-inner-icon="mdi-email-outline"
|
|
:rules="[rules.required, rules.email]"
|
|
:disabled="sending"
|
|
/>
|
|
|
|
<v-text-field
|
|
v-model="email.subject"
|
|
label="Subject"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
prepend-inner-icon="mdi-format-title"
|
|
:rules="[rules.required]"
|
|
:disabled="sending"
|
|
/>
|
|
|
|
<v-textarea
|
|
v-model="email.body"
|
|
label="Message"
|
|
variant="outlined"
|
|
rows="8"
|
|
:disabled="sending"
|
|
placeholder="Type your message here..."
|
|
/>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="d-flex ga-2 mb-4">
|
|
<v-btn
|
|
variant="outlined"
|
|
size="small"
|
|
@click="insertEOILink"
|
|
:disabled="sending || generatingEOI"
|
|
:loading="generatingEOI"
|
|
>
|
|
<v-icon start>mdi-link</v-icon>
|
|
{{ generatingEOI ? 'Generating...' : 'Insert EOI Link' }}
|
|
</v-btn>
|
|
<v-btn
|
|
variant="outlined"
|
|
size="small"
|
|
@click="insertFormLink"
|
|
:disabled="sending"
|
|
v-show="false"
|
|
>
|
|
<v-icon start>mdi-form-select</v-icon>
|
|
Insert Form Link
|
|
</v-btn>
|
|
<v-spacer />
|
|
<v-btn
|
|
variant="text"
|
|
size="small"
|
|
@click="showSignatureSettings = !showSignatureSettings"
|
|
:disabled="sending"
|
|
>
|
|
<v-icon start>mdi-card-account-details</v-icon>
|
|
Signature Settings
|
|
</v-btn>
|
|
</div>
|
|
|
|
<!-- Signature Settings -->
|
|
<v-expand-transition>
|
|
<v-card v-if="showSignatureSettings" variant="outlined" class="mb-4">
|
|
<v-card-title class="text-subtitle-1">
|
|
Customize Email Signature
|
|
</v-card-title>
|
|
<v-card-text>
|
|
<v-text-field
|
|
v-model="signatureConfig.name"
|
|
label="Your Name"
|
|
variant="outlined"
|
|
density="compact"
|
|
class="mb-2"
|
|
/>
|
|
<v-text-field
|
|
v-model="signatureConfig.title"
|
|
label="Job Title"
|
|
variant="outlined"
|
|
density="compact"
|
|
class="mb-2"
|
|
/>
|
|
<v-text-field
|
|
v-model="signatureConfig.email"
|
|
label="Email (if different)"
|
|
variant="outlined"
|
|
density="compact"
|
|
class="mb-3"
|
|
/>
|
|
|
|
<v-text-field
|
|
v-model="signatureConfig.company"
|
|
label="Company Name"
|
|
variant="outlined"
|
|
density="compact"
|
|
class="mb-3"
|
|
/>
|
|
|
|
<div class="text-subtitle-2 mb-2">Contact Information</div>
|
|
<v-textarea
|
|
v-model="signatureConfig.contactInfo"
|
|
label="Contact Details (one per line)"
|
|
variant="outlined"
|
|
density="compact"
|
|
rows="4"
|
|
class="mb-3"
|
|
placeholder="UK: +44 7 557 959 690 (WhatsApp) FR: +33 7 81 25 66 22 your.email@portnimara.com"
|
|
/>
|
|
|
|
<div class="text-subtitle-2 mb-2">Signature Preview</div>
|
|
<v-card variant="outlined" class="pa-3 mb-3">
|
|
<div v-html="getSignaturePreview()" style="font-family: Arial, sans-serif; font-size: 14px;"></div>
|
|
</v-card>
|
|
|
|
<v-checkbox
|
|
v-model="includeSignature"
|
|
label="Include signature in this email"
|
|
density="compact"
|
|
/>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-expand-transition>
|
|
|
|
<v-btn
|
|
type="submit"
|
|
color="primary"
|
|
block
|
|
size="large"
|
|
:loading="sending"
|
|
:disabled="sending"
|
|
>
|
|
<v-icon start>mdi-send</v-icon>
|
|
Send Email
|
|
</v-btn>
|
|
</v-form>
|
|
</v-card-text>
|
|
</v-card>
|
|
</template>
|
|
|
|
<script lang="ts" setup>
|
|
import { ref, onMounted, watch } from 'vue';
|
|
import type { Interest } from '@/utils/types';
|
|
|
|
interface Props {
|
|
interest: Interest;
|
|
}
|
|
|
|
interface Emits {
|
|
(e: 'sent', messageId: string): void;
|
|
(e: 'eoiGenerated'): void;
|
|
}
|
|
|
|
const props = defineProps<Props>();
|
|
const emit = defineEmits<Emits>();
|
|
|
|
const user = useDirectusUser();
|
|
const toast = useToast();
|
|
|
|
const form = ref();
|
|
const sending = ref(false);
|
|
const generatingEOI = ref(false);
|
|
const showSignatureSettings = ref(false);
|
|
const includeSignature = ref(true);
|
|
|
|
const email = ref({
|
|
to: '',
|
|
subject: '',
|
|
body: ''
|
|
});
|
|
|
|
const signatureConfig = ref({
|
|
name: '',
|
|
title: 'Sales & Marketing Director',
|
|
email: '',
|
|
company: 'Port Nimara',
|
|
contactInfo: 'UK: +44 7 557 959 690 (WhatsApp)\nFR: +33 7 81 25 66 22'
|
|
});
|
|
|
|
const rules = {
|
|
required: (v: string) => !!v || 'Required',
|
|
email: (v: string) => /.+@.+\..+/.test(v) || 'Invalid email'
|
|
};
|
|
|
|
const getSessionId = () => {
|
|
return sessionStorage.getItem('emailSessionId') || '';
|
|
};
|
|
|
|
const insertEOILink = async () => {
|
|
// Check if we're already generating
|
|
if (generatingEOI.value) return;
|
|
|
|
generatingEOI.value = true;
|
|
|
|
try {
|
|
const response = await $fetch<{
|
|
success: boolean;
|
|
clientSigningUrl: string;
|
|
documentId: string;
|
|
}>('/api/email/generate-eoi-document', {
|
|
method: 'POST',
|
|
headers: {
|
|
'x-tag': user.value?.email ? '094ut234' : 'pjnvü1230',
|
|
},
|
|
body: {
|
|
interestId: props.interest.Id
|
|
}
|
|
});
|
|
|
|
if (response.success && response.clientSigningUrl) {
|
|
email.value.body += `\n\nPlease click here to sign your Letter of Intent: ${response.clientSigningUrl}\n`;
|
|
toast.success('EOI generated and link inserted!');
|
|
|
|
// Emit event to refresh interest data
|
|
emit('eoiGenerated');
|
|
}
|
|
} catch (error: any) {
|
|
console.error('Failed to generate EOI:', error);
|
|
toast.error(error.data?.statusMessage || 'Failed to generate EOI document');
|
|
} finally {
|
|
generatingEOI.value = false;
|
|
}
|
|
};
|
|
|
|
const insertFormLink = () => {
|
|
// Generate form link with pre-filled data
|
|
const formData = {
|
|
name: props.interest['Full Name'],
|
|
email: props.interest['Email Address'],
|
|
phone: props.interest['Phone Number'],
|
|
interestId: props.interest.Id
|
|
};
|
|
const encodedData = btoa(JSON.stringify(formData));
|
|
const formLink = `https://portnimara.com/interest-form?data=${encodedData}`;
|
|
email.value.body += `\n\nPlease click here to access your personalized form: ${formLink}\n`;
|
|
toast.success('Form link inserted');
|
|
};
|
|
|
|
const getSignaturePreview = () => {
|
|
const sig = signatureConfig.value;
|
|
const userEmail = sig.email || email.value.to;
|
|
const contactLines = sig.contactInfo.split('\n').filter(line => line.trim()).join('<br>');
|
|
|
|
return `
|
|
<div style="margin-top: 20px;">
|
|
<img src="/Port_Nimara_Logo_2_Colour_New_Transparent.png" alt="Port Nimara" style="height: 60px; max-width: 200px; margin-bottom: 15px;">
|
|
<br>
|
|
<div style="font-weight: bold;">${sig.name || 'Your Name'}</div>
|
|
<div style="color: #666; margin-bottom: 8px;">${sig.title || 'Your Title'}</div>
|
|
<div style="font-weight: bold; margin-bottom: 12px;">${sig.company || 'Company Name'}</div>
|
|
${contactLines ? contactLines + '<br>' : ''}
|
|
<a href="mailto:${userEmail}" style="color: #0066cc;">${userEmail}</a>
|
|
<br><br>
|
|
<div style="color: #666; font-size: 12px; margin-top: 10px;">
|
|
The information in this message is confidential and may be privileged.<br>
|
|
It is intended for the addressee alone.<br>
|
|
If you are not the intended recipient it is prohibited to disclose, use or copy this information.<br>
|
|
Please contact the Sender immediately should this message have been transmitted incorrectly.
|
|
</div>
|
|
</div>`;
|
|
};
|
|
|
|
const sendEmail = async () => {
|
|
const { valid } = await form.value.validate();
|
|
if (!valid) return;
|
|
|
|
sending.value = true;
|
|
|
|
try {
|
|
const response = await $fetch<{ success: boolean; message: string; messageId: string }>('/api/email/send', {
|
|
method: 'POST',
|
|
headers: {
|
|
'x-tag': user.value?.email ? '094ut234' : 'pjnvü1230',
|
|
},
|
|
body: {
|
|
to: email.value.to,
|
|
subject: email.value.subject,
|
|
body: email.value.body,
|
|
interestId: props.interest.Id,
|
|
sessionId: getSessionId(),
|
|
includeSignature: includeSignature.value,
|
|
signatureConfig: includeSignature.value ? signatureConfig.value : undefined
|
|
}
|
|
});
|
|
|
|
if (response.success) {
|
|
toast.success('Email sent successfully!');
|
|
// Clear form
|
|
email.value.subject = '';
|
|
email.value.body = '';
|
|
emit('sent', response.messageId);
|
|
}
|
|
} catch (error: any) {
|
|
console.error('Failed to send email:', error);
|
|
toast.error(error.data?.statusMessage || 'Failed to send email');
|
|
} finally {
|
|
sending.value = false;
|
|
}
|
|
};
|
|
|
|
// Initialize on mount
|
|
onMounted(() => {
|
|
// Set recipient email
|
|
email.value.to = props.interest['Email Address'];
|
|
|
|
// Load saved signature config from localStorage
|
|
const savedSignature = localStorage.getItem('emailSignatureConfig');
|
|
if (savedSignature) {
|
|
try {
|
|
const parsed = JSON.parse(savedSignature);
|
|
signatureConfig.value = { ...signatureConfig.value, ...parsed };
|
|
} catch (e) {
|
|
console.error('Failed to parse saved signature:', e);
|
|
}
|
|
}
|
|
|
|
// Save signature config when it changes
|
|
watch(signatureConfig, (newConfig) => {
|
|
localStorage.setItem('emailSignatureConfig', JSON.stringify(newConfig));
|
|
}, { deep: true });
|
|
});
|
|
|
|
// Method to set reply data
|
|
const setReplyData = (replyData: { to: string; subject: string; body: string }) => {
|
|
email.value.to = replyData.to;
|
|
email.value.subject = replyData.subject;
|
|
email.value.body = replyData.body;
|
|
|
|
// Scroll to the top of the composer
|
|
form.value?.$el.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
};
|
|
|
|
// Expose the setReplyData method to parent
|
|
defineExpose({
|
|
setReplyData
|
|
});
|
|
</script>
|