port-nimara-client-portal/components/EmailComposer.vue

302 lines
9.1 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"
readonly
/>
<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"
>
<v-icon start>mdi-link</v-icon>
Insert EOI Link
</v-btn>
<v-btn
variant="outlined"
size="small"
@click="insertFormLink"
:disabled="sending"
>
<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)&#10;FR: +33 7 81 25 66 22&#10;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;
}
const props = defineProps<Props>();
const emit = defineEmits<Emits>();
const user = useDirectusUser();
const toast = useToast();
const form = ref();
const sending = 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 = () => {
// Generate EOI link similar to the existing EOI Send to Sales functionality
const eoiLink = `https://portnimara.com/eoi/${props.interest.Id}`;
email.value.body += `\n\nPlease click here to complete your Expression of Interest: ${eoiLink}\n`;
toast.success('EOI link inserted');
};
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;">
<div style="font-weight: bold;">${sig.name || 'Your Name'}</div>
<div style="color: #666;">${sig.title || 'Your Title'}</div>
<br>
<div style="font-weight: bold;">${sig.company || 'Company Name'}</div>
<br>
${contactLines ? contactLines + '<br>' : ''}
<a href="mailto:${userEmail}" style="color: #0066cc;">${userEmail}</a>
<br><br>
<img src="${process.env.NUXT_EMAIL_LOGO_URL || 'https://portnimara.com/logo.png'}" alt="Logo" style="height: 80px;">
<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 });
});
</script>