Merge pull request #256 from shariquerik/email-box-fix
refactor: Email Selector in To, CC, BCC not loading
This commit is contained in:
commit
2f036a2a1b
@ -132,3 +132,36 @@ def set_as_primary(contact, field, value):
|
|||||||
|
|
||||||
contact.save()
|
contact.save()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def search_emails(txt: str):
|
||||||
|
doctype = "Contact"
|
||||||
|
meta = frappe.get_meta(doctype)
|
||||||
|
filters = [["Contact", "email_id", "is", "set"]]
|
||||||
|
|
||||||
|
if meta.get("fields", {"fieldname": "enabled", "fieldtype": "Check"}):
|
||||||
|
filters.append([doctype, "enabled", "=", 1])
|
||||||
|
if meta.get("fields", {"fieldname": "disabled", "fieldtype": "Check"}):
|
||||||
|
filters.append([doctype, "disabled", "!=", 1])
|
||||||
|
|
||||||
|
or_filters = []
|
||||||
|
search_fields = ["full_name", "email_id", "name"]
|
||||||
|
if txt:
|
||||||
|
for f in search_fields:
|
||||||
|
or_filters.append([doctype, f.strip(), "like", f"%{txt}%"])
|
||||||
|
|
||||||
|
results = frappe.get_list(
|
||||||
|
doctype,
|
||||||
|
filters=filters,
|
||||||
|
fields=search_fields,
|
||||||
|
or_filters=or_filters,
|
||||||
|
limit_start=0,
|
||||||
|
limit_page_length=20,
|
||||||
|
order_by='email_id, full_name, name',
|
||||||
|
ignore_permissions=False,
|
||||||
|
as_list=True,
|
||||||
|
strict=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return results
|
||||||
@ -23,18 +23,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showEmailBox" class="flex gap-1.5">
|
|
||||||
<Button
|
|
||||||
:label="__('CC')"
|
|
||||||
@click="toggleCC()"
|
|
||||||
:class="[newEmailEditor.cc ? 'bg-gray-300 hover:bg-gray-200' : '']"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
:label="__('BCC')"
|
|
||||||
@click="toggleBCC()"
|
|
||||||
:class="[newEmailEditor.bcc ? 'bg-gray-300 hover:bg-gray-200' : '']"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-show="showEmailBox"
|
v-show="showEmailBox"
|
||||||
@ -103,7 +91,7 @@ import EmailIcon from '@/components/Icons/EmailIcon.vue'
|
|||||||
import { usersStore } from '@/stores/users'
|
import { usersStore } from '@/stores/users'
|
||||||
import { useStorage } from '@vueuse/core'
|
import { useStorage } from '@vueuse/core'
|
||||||
import { call, createResource } from 'frappe-ui'
|
import { call, createResource } from 'frappe-ui'
|
||||||
import { ref, watch, computed, nextTick } from 'vue'
|
import { ref, watch, computed } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
doctype: {
|
doctype: {
|
||||||
@ -145,7 +133,7 @@ const signature = createResource({
|
|||||||
})
|
})
|
||||||
|
|
||||||
function setSignature(editor) {
|
function setSignature(editor) {
|
||||||
signature.data = signature.data.replace(/\n/g, '<br>')
|
signature.data = signature.data?.replace(/\n/g, '<br>')
|
||||||
let emailContent = editor.getHTML()
|
let emailContent = editor.getHTML()
|
||||||
emailContent = emailContent.startsWith('<p></p>')
|
emailContent = emailContent.startsWith('<p></p>')
|
||||||
? emailContent.slice(7)
|
? emailContent.slice(7)
|
||||||
@ -236,22 +224,6 @@ async function submitComment() {
|
|||||||
emit('scroll')
|
emit('scroll')
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleCC() {
|
|
||||||
newEmailEditor.value.cc = !newEmailEditor.value.cc
|
|
||||||
newEmailEditor.value.cc &&
|
|
||||||
nextTick(() => {
|
|
||||||
newEmailEditor.value.ccInput.setFocus()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleBCC() {
|
|
||||||
newEmailEditor.value.bcc = !newEmailEditor.value.bcc
|
|
||||||
newEmailEditor.value.bcc &&
|
|
||||||
nextTick(() => {
|
|
||||||
newEmailEditor.value.bccInput.setFocus()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleEmailBox() {
|
function toggleEmailBox() {
|
||||||
if (showCommentBox.value) {
|
if (showCommentBox.value) {
|
||||||
showCommentBox.value = false
|
showCommentBox.value = false
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
:label="value"
|
:label="value"
|
||||||
theme="gray"
|
theme="gray"
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
class="rounded-full"
|
class="rounded"
|
||||||
@keydown.delete.capture.stop="removeLastValue"
|
@keydown.delete.capture.stop="removeLastValue"
|
||||||
>
|
>
|
||||||
<template #suffix>
|
<template #suffix>
|
||||||
@ -133,30 +133,26 @@ watchDebounced(
|
|||||||
query,
|
query,
|
||||||
(val) => {
|
(val) => {
|
||||||
val = val || ''
|
val = val || ''
|
||||||
if (text.value === val) return
|
if (text.value === val && options.value?.length) return
|
||||||
text.value = val
|
text.value = val
|
||||||
reload(val)
|
reload(val)
|
||||||
},
|
},
|
||||||
{ debounce: 300, immediate: true }
|
{ debounce: 300, immediate: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
const filterOptions = createResource({
|
const filterOptions = createResource({
|
||||||
url: 'frappe.desk.search.search_link',
|
url: 'crm.api.contact.search_emails',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
cache: [text.value, 'Contact'],
|
cache: [text.value, 'Contact'],
|
||||||
params: {
|
params: { txt: text.value },
|
||||||
txt: text.value,
|
|
||||||
doctype: 'Contact',
|
|
||||||
},
|
|
||||||
transform: (data) => {
|
transform: (data) => {
|
||||||
let allData = data
|
let allData = data
|
||||||
.filter((c) => {
|
|
||||||
return c.description.split(', ')[1]
|
|
||||||
})
|
|
||||||
.map((option) => {
|
.map((option) => {
|
||||||
let email = option.description.split(', ')[1]
|
let fullName = option[0]
|
||||||
|
let email = option[1]
|
||||||
|
let name = option[2]
|
||||||
return {
|
return {
|
||||||
label: option.label || email,
|
label: fullName || name || email,
|
||||||
value: email,
|
value: email,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -177,10 +173,7 @@ const options = computed(() => {
|
|||||||
|
|
||||||
function reload(val) {
|
function reload(val) {
|
||||||
filterOptions.update({
|
filterOptions.update({
|
||||||
params: {
|
params: { txt: val },
|
||||||
txt: val,
|
|
||||||
doctype: 'Contact',
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
filterOptions.reload()
|
filterOptions.reload()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,60 +9,68 @@
|
|||||||
:editable="editable"
|
:editable="editable"
|
||||||
>
|
>
|
||||||
<template #top>
|
<template #top>
|
||||||
<div class="sm:mx-10 mx-4 flex items-center gap-2 border-t py-2.5">
|
<div class="flex flex-col gap-3">
|
||||||
<span class="text-xs text-gray-500">{{ __('SUBJECT') }}:</span>
|
<div class="sm:mx-10 mx-4 flex items-center gap-2 border-t pt-2.5">
|
||||||
<TextInput
|
<span class="text-xs text-gray-500">{{ __('TO') }}:</span>
|
||||||
class="flex-1 border-none bg-white hover:bg-white focus:border-none focus:!shadow-none focus-visible:!ring-0"
|
<MultiselectInput
|
||||||
v-model="subject"
|
class="flex-1"
|
||||||
/>
|
v-model="toEmails"
|
||||||
</div>
|
:validate="validateEmail"
|
||||||
<div
|
:error-message="
|
||||||
class="sm:mx-10 mx-4 flex items-center gap-2 border-t py-2.5"
|
(value) => __('{0} is an invalid email address', [value])
|
||||||
:class="[cc || bcc ? 'border-b' : '']"
|
"
|
||||||
>
|
/>
|
||||||
<span class="text-xs text-gray-500">{{ __('TO') }}:</span>
|
<div class="flex gap-1.5">
|
||||||
<MultiselectInput
|
<Button
|
||||||
class="flex-1"
|
:label="__('CC')"
|
||||||
v-model="toEmails"
|
@click="toggleCC()"
|
||||||
:validate="validateEmail"
|
:class="[cc ? 'bg-gray-300 hover:bg-gray-200' : '']"
|
||||||
:error-message="
|
/>
|
||||||
(value) => __('{0} is an invalid email address', [value])
|
<Button
|
||||||
"
|
:label="__('BCC')"
|
||||||
/>
|
@click="toggleBCC()"
|
||||||
</div>
|
:class="[bcc ? 'bg-gray-300 hover:bg-gray-200' : '']"
|
||||||
<div
|
/>
|
||||||
v-if="cc"
|
</div>
|
||||||
class="sm:mx-10 mx-4 flex items-center gap-2 py-2.5"
|
</div>
|
||||||
:class="bcc ? 'border-b' : ''"
|
<div v-if="cc" class="sm:mx-10 mx-4 flex items-center gap-2">
|
||||||
>
|
<span class="text-xs text-gray-500">{{ __('CC') }}:</span>
|
||||||
<span class="text-xs text-gray-500">{{ __('CC') }}:</span>
|
<MultiselectInput
|
||||||
<MultiselectInput
|
ref="ccInput"
|
||||||
ref="ccInput"
|
class="flex-1"
|
||||||
class="flex-1"
|
v-model="ccEmails"
|
||||||
v-model="ccEmails"
|
:validate="validateEmail"
|
||||||
:validate="validateEmail"
|
:error-message="
|
||||||
:error-message="
|
(value) => __('{0} is an invalid email address', [value])
|
||||||
(value) => __('{0} is an invalid email address', [value])
|
"
|
||||||
"
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
<div v-if="bcc" class="sm:mx-10 mx-4 flex items-center gap-2">
|
||||||
<div v-if="bcc" class="sm:mx-10 mx-4 flex items-center gap-2 py-2.5">
|
<span class="text-xs text-gray-500">{{ __('BCC') }}:</span>
|
||||||
<span class="text-xs text-gray-500">{{ __('BCC') }}:</span>
|
<MultiselectInput
|
||||||
<MultiselectInput
|
ref="bccInput"
|
||||||
ref="bccInput"
|
class="flex-1"
|
||||||
class="flex-1"
|
v-model="bccEmails"
|
||||||
v-model="bccEmails"
|
:validate="validateEmail"
|
||||||
:validate="validateEmail"
|
:error-message="
|
||||||
:error-message="
|
(value) => __('{0} is an invalid email address', [value])
|
||||||
(value) => __('{0} is an invalid email address', [value])
|
"
|
||||||
"
|
/>
|
||||||
/>
|
</div>
|
||||||
|
<div class="sm:mx-10 mx-4 flex items-center gap-2 pb-2.5">
|
||||||
|
<span class="text-xs text-gray-500">{{ __('SUBJECT') }}:</span>
|
||||||
|
<TextInput
|
||||||
|
class="flex-1 border-none bg-white hover:bg-white focus:border-none focus:!shadow-none focus-visible:!ring-0"
|
||||||
|
v-model="subject"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:editor="{ editor }">
|
<template v-slot:editor="{ editor }">
|
||||||
<EditorContent
|
<EditorContent
|
||||||
:class="[
|
:class="[
|
||||||
editable && 'sm:mx-10 mx-4 max-h-[35vh] overflow-y-auto border-t py-3',
|
editable &&
|
||||||
|
'sm:mx-10 mx-4 max-h-[35vh] overflow-y-auto border-t py-3',
|
||||||
]"
|
]"
|
||||||
:editor="editor"
|
:editor="editor"
|
||||||
/>
|
/>
|
||||||
@ -147,7 +155,7 @@ import EmailTemplateSelectorModal from '@/components/Modals/EmailTemplateSelecto
|
|||||||
import { TextEditorFixedMenu, TextEditor, FileUploader, call } from 'frappe-ui'
|
import { TextEditorFixedMenu, TextEditor, FileUploader, call } from 'frappe-ui'
|
||||||
import { validateEmail } from '@/utils'
|
import { validateEmail } from '@/utils'
|
||||||
import { EditorContent } from '@tiptap/vue-3'
|
import { EditorContent } from '@tiptap/vue-3'
|
||||||
import { ref, computed, defineModel } from 'vue'
|
import { ref, computed, defineModel, nextTick } from 'vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
placeholder: {
|
placeholder: {
|
||||||
@ -211,7 +219,7 @@ async function applyEmailTemplate(template) {
|
|||||||
{
|
{
|
||||||
template_name: template.name,
|
template_name: template.name,
|
||||||
doc: modelValue.value,
|
doc: modelValue.value,
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if (template.subject) {
|
if (template.subject) {
|
||||||
@ -225,6 +233,16 @@ async function applyEmailTemplate(template) {
|
|||||||
showEmailTemplateSelectorModal.value = false
|
showEmailTemplateSelectorModal.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleCC() {
|
||||||
|
cc.value = !cc.value
|
||||||
|
cc.value && nextTick(() => ccInput.value.setFocus())
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleBCC() {
|
||||||
|
bcc.value = !bcc.value
|
||||||
|
bcc.value && nextTick(() => bccInput.value.setFocus())
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
editor,
|
editor,
|
||||||
subject,
|
subject,
|
||||||
@ -233,8 +251,6 @@ defineExpose({
|
|||||||
toEmails,
|
toEmails,
|
||||||
ccEmails,
|
ccEmails,
|
||||||
bccEmails,
|
bccEmails,
|
||||||
ccInput,
|
|
||||||
bccInput,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const textEditorMenuButtons = [
|
const textEditorMenuButtons = [
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user