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

170 lines
4.7 KiB
Vue

<template>
<v-dialog
v-model="isOpen"
:max-width="mobile ? '100%' : '800'"
:fullscreen="mobile"
:transition="mobile ? 'dialog-bottom-transition' : 'dialog-transition'"
>
<v-card v-if="email">
<v-card-title class="d-flex align-center">
<v-icon class="mr-2" :color="email.direction === 'sent' ? 'primary' : 'success'">
{{ email.direction === 'sent' ? 'mdi-email-send' : 'mdi-email-receive' }}
</v-icon>
Email Details
<v-spacer />
<v-btn icon="mdi-close" variant="text" @click="close"></v-btn>
</v-card-title>
<v-divider />
<v-card-text>
<v-list density="comfortable">
<v-list-item>
<template v-slot:prepend>
<v-icon>mdi-account</v-icon>
</template>
<v-list-item-title>{{ email.direction === 'sent' ? 'To' : 'From' }}</v-list-item-title>
<v-list-item-subtitle>{{ email.direction === 'sent' ? email.to : email.from }}</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<template v-slot:prepend>
<v-icon>mdi-text-box</v-icon>
</template>
<v-list-item-title>Subject</v-list-item-title>
<v-list-item-subtitle>{{ email.subject }}</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<template v-slot:prepend>
<v-icon>mdi-calendar-clock</v-icon>
</template>
<v-list-item-title>Date & Time</v-list-item-title>
<v-list-item-subtitle>{{ formatDate(email.timestamp) }}</v-list-item-subtitle>
</v-list-item>
</v-list>
<v-divider class="my-4" />
<div class="email-body">
<div class="text-subtitle-2 mb-2">Message</div>
<div class="email-content" v-html="formatEmailContent(email.content || email.body)"></div>
</div>
<!-- Attachments -->
<div v-if="email.attachments && email.attachments.length > 0" class="mt-4">
<div class="text-subtitle-2 mb-2">Attachments ({{ email.attachments.length }})</div>
<v-chip
v-for="(attachment, i) in email.attachments"
:key="i"
size="small"
color="primary"
variant="tonal"
prepend-icon="mdi-paperclip"
class="mr-2 mb-2"
:href="getAttachmentUrl(attachment)"
:download="getAttachmentName(attachment)"
component="a"
target="_blank"
>
{{ getAttachmentName(attachment) }}
</v-chip>
</div>
</v-card-text>
<v-divider />
<v-card-actions>
<v-spacer />
<v-btn @click="close" variant="text">Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
interface Props {
modelValue: boolean;
email: any;
}
const props = defineProps<Props>();
const emit = defineEmits(['update:modelValue']);
const { mobile } = useDisplay();
const isOpen = computed({
get: () => props.modelValue,
set: (value) => emit('update:modelValue', value)
});
const close = () => {
isOpen.value = false;
};
const formatDate = (dateString: string) => {
if (!dateString) return '';
const date = new Date(dateString);
return date.toLocaleString('en-GB', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: false
});
};
const formatEmailContent = (content: string) => {
if (!content) return '';
// If it's already HTML, return as is
if (content.includes('<p>') || content.includes('<br>')) {
return content;
}
// Convert plain text to HTML
return content
.split('\n')
.map(line => line.trim() ? `<p>${line}</p>` : '<br>')
.join('');
};
const getAttachmentName = (attachment: any) => {
if (typeof attachment === 'string') return attachment;
return attachment.name || attachment.filename || 'attachment';
};
const getAttachmentUrl = (attachment: any) => {
// 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's just a URL, return it
if (attachment.url) return attachment.url;
// Otherwise return a placeholder
return '#';
};
</script>
<style scoped>
.email-content {
font-family: inherit;
line-height: 1.6;
word-wrap: break-word;
}
.email-content :deep(p) {
margin: 0 0 0.5em 0;
}
.email-content :deep(br) {
display: block;
content: "";
margin: 0.25em 0;
}
</style>