Merge pull request #256 from shariquerik/email-box-fix

refactor: Email Selector in To, CC, BCC not loading
This commit is contained in:
Shariq Ansari 2024-07-10 18:40:23 +05:30 committed by GitHub
commit 2f036a2a1b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 114 additions and 100 deletions

View File

@ -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

View File

@ -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

View File

@ -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()
} }

View File

@ -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 = [