Skip to content

Commit 360d22e

Browse files
authored
Merge pull request #265 from frappe/develop
chore: Merge develop to main
2 parents eb25d55 + df767cb commit 360d22e

10 files changed

+368
-147
lines changed

crm/api/contact.py

+33
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,36 @@ def set_as_primary(contact, field, value):
132132

133133
contact.save()
134134
return True
135+
136+
137+
@frappe.whitelist()
138+
def search_emails(txt: str):
139+
doctype = "Contact"
140+
meta = frappe.get_meta(doctype)
141+
filters = [["Contact", "email_id", "is", "set"]]
142+
143+
if meta.get("fields", {"fieldname": "enabled", "fieldtype": "Check"}):
144+
filters.append([doctype, "enabled", "=", 1])
145+
if meta.get("fields", {"fieldname": "disabled", "fieldtype": "Check"}):
146+
filters.append([doctype, "disabled", "!=", 1])
147+
148+
or_filters = []
149+
search_fields = ["full_name", "email_id", "name"]
150+
if txt:
151+
for f in search_fields:
152+
or_filters.append([doctype, f.strip(), "like", f"%{txt}%"])
153+
154+
results = frappe.get_list(
155+
doctype,
156+
filters=filters,
157+
fields=search_fields,
158+
or_filters=or_filters,
159+
limit_start=0,
160+
limit_page_length=20,
161+
order_by='email_id, full_name, name',
162+
ignore_permissions=False,
163+
as_list=True,
164+
strict=False,
165+
)
166+
167+
return results

frontend/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"serve": "vite preview"
1010
},
1111
"dependencies": {
12+
"@tiptap/extension-paragraph": "^2.4.0",
1213
"@twilio/voice-sdk": "^2.10.2",
1314
"@vueuse/core": "^10.3.0",
1415
"@vueuse/integrations": "^10.3.0",

frontend/src/components/Activities.vue

+13-7
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@
442442
'outgoing_call',
443443
].includes(activity.activity_type),
444444
'bg-white': ['added', 'removed', 'changed'].includes(
445-
activity.activity_type
445+
activity.activity_type,
446446
),
447447
}"
448448
>
@@ -528,7 +528,10 @@
528528
<span v-if="activity.data.bcc">{{ activity.data.bcc }}</span>
529529
</div>
530530
<EmailContent :content="activity.data.content" />
531-
<div class="flex flex-wrap gap-2">
531+
<div
532+
v-if="activity.data?.attachments?.length"
533+
class="flex flex-wrap gap-2"
534+
>
532535
<AttachmentItem
533536
v-for="a in activity.data.attachments"
534537
:key="a.file_url"
@@ -1102,7 +1105,7 @@ const defaultActions = computed(() => {
11021105
},
11031106
]
11041107
return actions.filter((action) =>
1105-
action.condition ? action.condition() : true
1108+
action.condition ? action.condition() : true,
11061109
)
11071110
})
11081111
@@ -1120,12 +1123,12 @@ const activities = computed(() => {
11201123
} else if (props.title == 'Emails') {
11211124
if (!all_activities.data?.versions) return []
11221125
activities = all_activities.data.versions.filter(
1123-
(activity) => activity.activity_type === 'communication'
1126+
(activity) => activity.activity_type === 'communication',
11241127
)
11251128
} else if (props.title == 'Comments') {
11261129
if (!all_activities.data?.versions) return []
11271130
activities = all_activities.data.versions.filter(
1128-
(activity) => activity.activity_type === 'comment'
1131+
(activity) => activity.activity_type === 'comment',
11291132
)
11301133
} else if (props.title == 'Calls') {
11311134
if (!all_activities.data?.calls) return []
@@ -1338,12 +1341,15 @@ function reply(email, reply_all = false) {
13381341
editor.bccEmails = bcc
13391342
}
13401343
1344+
let repliedMessage = `<blockquote>${message}</blockquote>`
1345+
13411346
editor.editor
13421347
.chain()
13431348
.clearContent()
1344-
.insertContent(message)
1349+
.insertContent('<p>.</p>')
1350+
.updateAttributes('paragraph', {class:'reply-to-content'})
1351+
.insertContent(repliedMessage)
13451352
.focus('all')
1346-
.setBlockquote()
13471353
.insertContentAt(0, { type: 'paragraph' })
13481354
.focus('start')
13491355
.run()

frontend/src/components/CommentBox.vue

+25-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
<template v-slot:editor="{ editor }">
1313
<EditorContent
1414
:class="[
15-
editable && 'sm:mx-10 mx-4 max-h-[50vh] overflow-y-auto border-t py-3',
15+
editable &&
16+
'sm:mx-10 mx-4 max-h-[50vh] overflow-y-auto border-t py-3',
1617
]"
1718
:editor="editor"
1819
/>
@@ -37,11 +38,19 @@
3738
<div
3839
class="flex justify-between gap-2 overflow-hidden border-t sm:px-10 px-4 py-2.5"
3940
>
40-
<div class="flex items-center overflow-x-auto">
41-
<TextEditorFixedMenu
42-
class="-ml-1"
43-
:buttons="textEditorMenuButtons"
44-
/>
41+
<div class="flex gap-1 items-center overflow-x-auto">
42+
<TextEditorBubbleMenu :buttons="textEditorMenuButtons" />
43+
<IconPicker
44+
v-model="emoji"
45+
v-slot="{ togglePopover }"
46+
@update:modelValue="() => appendEmoji()"
47+
>
48+
<Button variant="ghost" @click="togglePopover()">
49+
<template #icon>
50+
<SmileIcon class="h-4" />
51+
</template>
52+
</Button>
53+
</IconPicker>
4554
<FileUploader
4655
:upload-args="{
4756
doctype: doctype,
@@ -77,10 +86,12 @@
7786
</TextEditor>
7887
</template>
7988
<script setup>
89+
import IconPicker from '@/components/IconPicker.vue'
90+
import SmileIcon from '@/components/Icons/SmileIcon.vue'
8091
import AttachmentIcon from '@/components/Icons/AttachmentIcon.vue'
8192
import AttachmentItem from '@/components/AttachmentItem.vue'
8293
import { usersStore } from '@/stores/users'
83-
import { TextEditorFixedMenu, TextEditor, FileUploader } from 'frappe-ui'
94+
import { TextEditorBubbleMenu, TextEditor, FileUploader } from 'frappe-ui'
8495
import { EditorContent } from '@tiptap/vue-3'
8596
import { ref, computed, defineModel } from 'vue'
8697
@@ -118,11 +129,18 @@ const content = defineModel('content')
118129
const { users: usersList } = usersStore()
119130
120131
const textEditor = ref(null)
132+
const emoji = ref('')
121133
122134
const editor = computed(() => {
123135
return textEditor.value.editor
124136
})
125137
138+
function appendEmoji() {
139+
editor.value.commands.insertContent(emoji.value)
140+
editor.value.commands.focus()
141+
emoji.value = ''
142+
}
143+
126144
function removeAttachment(attachment) {
127145
attachments.value = attachments.value.filter((a) => a !== attachment)
128146
}

frontend/src/components/CommunicationArea.vue

+2-29
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,6 @@
2323
</template>
2424
</Button>
2525
</div>
26-
<div v-if="showEmailBox" class="flex gap-1.5">
27-
<Button
28-
:label="__('CC')"
29-
@click="toggleCC()"
30-
:class="[newEmailEditor.cc ? 'bg-gray-300 hover:bg-gray-200' : '']"
31-
/>
32-
<Button
33-
:label="__('BCC')"
34-
@click="toggleBCC()"
35-
:class="[newEmailEditor.bcc ? 'bg-gray-300 hover:bg-gray-200' : '']"
36-
/>
37-
</div>
3826
</div>
3927
<div
4028
v-show="showEmailBox"
@@ -103,7 +91,7 @@ import EmailIcon from '@/components/Icons/EmailIcon.vue'
10391
import { usersStore } from '@/stores/users'
10492
import { useStorage } from '@vueuse/core'
10593
import { call, createResource } from 'frappe-ui'
106-
import { ref, watch, computed, nextTick } from 'vue'
94+
import { ref, watch, computed } from 'vue'
10795
10896
const props = defineProps({
10997
doctype: {
@@ -145,6 +133,7 @@ const signature = createResource({
145133
})
146134
147135
function setSignature(editor) {
136+
if (!signature.data) return
148137
signature.data = signature.data.replace(/\n/g, '<br>')
149138
let emailContent = editor.getHTML()
150139
emailContent = emailContent.startsWith('<p></p>')
@@ -236,22 +225,6 @@ async function submitComment() {
236225
emit('scroll')
237226
}
238227
239-
function toggleCC() {
240-
newEmailEditor.value.cc = !newEmailEditor.value.cc
241-
newEmailEditor.value.cc &&
242-
nextTick(() => {
243-
newEmailEditor.value.ccInput.setFocus()
244-
})
245-
}
246-
247-
function toggleBCC() {
248-
newEmailEditor.value.bcc = !newEmailEditor.value.bcc
249-
newEmailEditor.value.bcc &&
250-
nextTick(() => {
251-
newEmailEditor.value.bccInput.setFocus()
252-
})
253-
}
254-
255228
function toggleEmailBox() {
256229
if (showCommentBox.value) {
257230
showCommentBox.value = false

frontend/src/components/Controls/MultiselectInput.vue

+10-17
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
:label="value"
99
theme="gray"
1010
variant="subtle"
11-
class="rounded-full"
11+
class="rounded"
1212
@keydown.delete.capture.stop="removeLastValue"
1313
>
1414
<template #suffix>
@@ -133,30 +133,26 @@ watchDebounced(
133133
query,
134134
(val) => {
135135
val = val || ''
136-
if (text.value === val) return
136+
if (text.value === val && options.value?.length) return
137137
text.value = val
138138
reload(val)
139139
},
140-
{ debounce: 300, immediate: true }
140+
{ debounce: 300, immediate: true },
141141
)
142142
143143
const filterOptions = createResource({
144-
url: 'frappe.desk.search.search_link',
144+
url: 'crm.api.contact.search_emails',
145145
method: 'POST',
146146
cache: [text.value, 'Contact'],
147-
params: {
148-
txt: text.value,
149-
doctype: 'Contact',
150-
},
147+
params: { txt: text.value },
151148
transform: (data) => {
152149
let allData = data
153-
.filter((c) => {
154-
return c.description.split(', ')[1]
155-
})
156150
.map((option) => {
157-
let email = option.description.split(', ')[1]
151+
let fullName = option[0]
152+
let email = option[1]
153+
let name = option[2]
158154
return {
159-
label: option.label || email,
155+
label: fullName || name || email,
160156
value: email,
161157
}
162158
})
@@ -177,10 +173,7 @@ const options = computed(() => {
177173
178174
function reload(val) {
179175
filterOptions.update({
180-
params: {
181-
txt: val,
182-
doctype: 'Contact',
183-
},
176+
params: { txt: val },
184177
})
185178
filterOptions.reload()
186179
}

0 commit comments

Comments
 (0)