170 lines
4.7 KiB
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>
|