crm/frontend/src/components/CommunicationArea.vue

264 lines
6.8 KiB
Vue

<template>
<div class="flex justify-between gap-3 border-t px-4 py-2.5 sm:px-10">
<div class="flex gap-1.5">
<Button
ref="sendEmailRef"
variant="ghost"
:class="[
showEmailBox ? '!bg-surface-gray-4 hover:!bg-surface-gray-3' : '',
]"
:label="__('Reply')"
@click="toggleEmailBox()"
>
<template #prefix>
<Email2Icon class="h-4" />
</template>
</Button>
<Button
variant="ghost"
:label="__('Comment')"
:class="[
showCommentBox ? '!bg-surface-gray-4 hover:!bg-surface-gray-3' : '',
]"
@click="toggleCommentBox()"
>
<template #prefix>
<CommentIcon class="h-4" />
</template>
</Button>
</div>
</div>
<div
v-show="showEmailBox"
@keydown.ctrl.enter.capture.stop="submitEmail"
@keydown.meta.enter.capture.stop="submitEmail"
>
<EmailEditor
ref="newEmailEditor"
v-model:content="newEmail"
:submitButtonProps="{
variant: 'solid',
onClick: submitEmail,
disabled: emailEmpty,
}"
:discardButtonProps="{
onClick: () => {
showEmailBox = false
newEmailEditor.subject = subject
newEmailEditor.toEmails = doc.data.email ? [doc.data.email] : []
newEmailEditor.ccEmails = []
newEmailEditor.bccEmails = []
newEmailEditor.cc = false
newEmailEditor.bcc = false
newEmail = ''
},
}"
:editable="showEmailBox"
v-model="doc.data"
v-model:attachments="attachments"
:doctype="doctype"
:subject="subject"
:placeholder="
__('Hi John, \n\nCan you please provide more details on this...')
"
/>
</div>
<div v-show="showCommentBox">
<CommentBox
ref="newCommentEditor"
v-model:content="newComment"
:submitButtonProps="{
variant: 'solid',
onClick: submitComment,
disabled: commentEmpty,
}"
:discardButtonProps="{
onClick: () => {
showCommentBox = false
newComment = ''
},
}"
:editable="showCommentBox"
v-model="doc.data"
v-model:attachments="attachments"
:doctype="doctype"
:placeholder="__('@John, can you please check this?')"
/>
</div>
</template>
<script setup>
import EmailEditor from '@/components/EmailEditor.vue'
import CommentBox from '@/components/CommentBox.vue'
import CommentIcon from '@/components/Icons/CommentIcon.vue'
import Email2Icon from '@/components/Icons/Email2Icon.vue'
import { capture } from '@/telemetry'
import { usersStore } from '@/stores/users'
import { useStorage } from '@vueuse/core'
import { call, createResource } from 'frappe-ui'
import { useOnboarding } from 'frappe-ui/frappe'
import { ref, watch, computed } from 'vue'
const props = defineProps({
doctype: {
type: String,
default: 'CRM Lead',
},
})
const doc = defineModel()
const reload = defineModel('reload')
const emit = defineEmits(['scroll'])
const { getUser } = usersStore()
const { updateOnboardingStep } = useOnboarding('frappecrm')
const showEmailBox = ref(false)
const showCommentBox = ref(false)
const newEmail = useStorage('emailBoxContent', '')
const newComment = useStorage('commentBoxContent', '')
const newEmailEditor = ref(null)
const newCommentEditor = ref(null)
const sendEmailRef = ref(null)
const attachments = ref([])
const subject = computed(() => {
let prefix = ''
if (doc.value.data?.lead_name) {
prefix = doc.value.data.lead_name
} else if (doc.value.data?.organization) {
prefix = doc.value.data.organization
}
return `${prefix} (#${doc.value.data.name})`
})
const signature = createResource({
url: 'crm.api.get_user_signature',
cache: 'user-email-signature',
auto: true,
})
function setSignature(editor) {
if (!signature.data) return
signature.data = signature.data.replace(/\n/g, '<br>')
let emailContent = editor.getHTML()
emailContent = emailContent.startsWith('<p></p>')
? emailContent.slice(7)
: emailContent
editor.commands.setContent(signature.data + emailContent)
editor.commands.focus('start')
}
watch(
() => showEmailBox.value,
(value) => {
if (value) {
let editor = newEmailEditor.value.editor
editor.commands.focus()
setSignature(editor)
}
},
)
watch(
() => showCommentBox.value,
(value) => {
if (value) {
newCommentEditor.value.editor.commands.focus()
}
},
)
const commentEmpty = computed(() => {
return !newComment.value || newComment.value === '<p></p>'
})
const emailEmpty = computed(() => {
return !newEmail.value || newEmail.value === '<p></p>'
})
async function sendMail() {
let recipients = newEmailEditor.value.toEmails
let subject = newEmailEditor.value.subject
let cc = newEmailEditor.value.ccEmails || []
let bcc = newEmailEditor.value.bccEmails || []
if (attachments.value.length) {
capture('email_attachments_added')
}
await call('frappe.core.doctype.communication.email.make', {
recipients: recipients.join(', '),
attachments: attachments.value.map((x) => x.name),
cc: cc.join(', '),
bcc: bcc.join(', '),
subject: subject,
content: newEmail.value,
doctype: props.doctype,
name: doc.value.data.name,
send_email: 1,
sender: getUser().email,
sender_full_name: getUser()?.full_name || undefined,
})
}
async function sendComment() {
let comment = await call('frappe.desk.form.utils.add_comment', {
reference_doctype: props.doctype,
reference_name: doc.value.data.name,
content: newComment.value,
comment_email: getUser().email,
comment_by: getUser()?.full_name || undefined,
})
if (comment && attachments.value.length) {
capture('comment_attachments_added')
await call('crm.api.comment.add_attachments', {
name: comment.name,
attachments: attachments.value.map((x) => x.name),
})
}
}
async function submitEmail() {
if (emailEmpty.value) return
showEmailBox.value = false
await sendMail()
newEmail.value = ''
reload.value = true
emit('scroll')
capture('email_sent', { doctype: props.doctype })
updateOnboardingStep('send_first_email')
}
async function submitComment() {
if (commentEmpty.value) return
showCommentBox.value = false
await sendComment()
newComment.value = ''
reload.value = true
emit('scroll')
capture('comment_sent', { doctype: props.doctype })
updateOnboardingStep('add_first_comment')
}
function toggleEmailBox() {
if (showCommentBox.value) {
showCommentBox.value = false
}
showEmailBox.value = !showEmailBox.value
}
function toggleCommentBox() {
if (showEmailBox.value) {
showEmailBox.value = false
}
showCommentBox.value = !showCommentBox.value
}
defineExpose({
show: showEmailBox,
showComment: showCommentBox,
editor: newEmailEditor,
})
</script>