Merge branch 'main-hotfix' into mergify/bp/main-hotfix/pr-753
@ -30,15 +30,7 @@ def get_contact(name):
|
||||
|
||||
if not len(contact):
|
||||
frappe.throw(_("Contact not found"), frappe.DoesNotExistError)
|
||||
contact = contact.pop()
|
||||
|
||||
contact["doctype"] = "Contact"
|
||||
contact["email_ids"] = frappe.get_all(
|
||||
"Contact Email", filters={"parent": name}, fields=["name", "email_id", "is_primary"]
|
||||
)
|
||||
contact["phone_nos"] = frappe.get_all(
|
||||
"Contact Phone", filters={"parent": name}, fields=["name", "phone", "is_primary_mobile_no"]
|
||||
)
|
||||
return contact
|
||||
|
||||
|
||||
|
||||
99
crm/api/settings.py
Normal file
@ -0,0 +1,99 @@
|
||||
import frappe
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_email_account(data):
|
||||
service = data.get("service")
|
||||
service_config = email_service_config.get(service)
|
||||
if not service_config:
|
||||
return "Service not supported"
|
||||
|
||||
try:
|
||||
email_doc = frappe.get_doc(
|
||||
{
|
||||
"doctype": "Email Account",
|
||||
"email_id": data.get("email_id"),
|
||||
"email_account_name": data.get("email_account_name"),
|
||||
"service": service,
|
||||
"enable_incoming": data.get("enable_incoming"),
|
||||
"enable_outgoing": data.get("enable_outgoing"),
|
||||
"default_incoming": data.get("default_incoming"),
|
||||
"default_outgoing": data.get("default_outgoing"),
|
||||
"email_sync_option": "ALL",
|
||||
"initial_sync_count": 100,
|
||||
"create_contact": 1,
|
||||
"track_email_status": 1,
|
||||
"use_tls": 1,
|
||||
"use_imap": 1,
|
||||
"smtp_port": 587,
|
||||
**service_config,
|
||||
}
|
||||
)
|
||||
if service == "Frappe Mail":
|
||||
email_doc.api_key = data.get("api_key")
|
||||
email_doc.api_secret = data.get("api_secret")
|
||||
email_doc.frappe_mail_site = data.get("frappe_mail_site")
|
||||
email_doc.append_to = "CRM Lead"
|
||||
else:
|
||||
email_doc.append("imap_folder", {"append_to": "CRM Lead", "folder_name": "INBOX"})
|
||||
email_doc.password = data.get("password")
|
||||
# validate whether the credentials are correct
|
||||
email_doc.get_incoming_server()
|
||||
|
||||
# if correct credentials, save the email account
|
||||
email_doc.save()
|
||||
except Exception as e:
|
||||
frappe.throw(str(e))
|
||||
|
||||
|
||||
email_service_config = {
|
||||
"Frappe Mail": {
|
||||
"domain": None,
|
||||
"password": None,
|
||||
"awaiting_password": 0,
|
||||
"ascii_encode_password": 0,
|
||||
"login_id_is_different": 0,
|
||||
"login_id": None,
|
||||
"use_imap": 0,
|
||||
"use_ssl": 0,
|
||||
"validate_ssl_certificate": 0,
|
||||
"use_starttls": 0,
|
||||
"email_server": None,
|
||||
"incoming_port": 0,
|
||||
"always_use_account_email_id_as_sender": 1,
|
||||
"use_tls": 0,
|
||||
"use_ssl_for_outgoing": 0,
|
||||
"smtp_server": None,
|
||||
"smtp_port": None,
|
||||
"no_smtp_authentication": 0,
|
||||
},
|
||||
"GMail": {
|
||||
"email_server": "imap.gmail.com",
|
||||
"use_ssl": 1,
|
||||
"smtp_server": "smtp.gmail.com",
|
||||
},
|
||||
"Outlook": {
|
||||
"email_server": "imap-mail.outlook.com",
|
||||
"use_ssl": 1,
|
||||
"smtp_server": "smtp-mail.outlook.com",
|
||||
},
|
||||
"Sendgrid": {
|
||||
"smtp_server": "smtp.sendgrid.net",
|
||||
"smtp_port": 587,
|
||||
},
|
||||
"SparkPost": {
|
||||
"smtp_server": "smtp.sparkpostmail.com",
|
||||
},
|
||||
"Yahoo": {
|
||||
"email_server": "imap.mail.yahoo.com",
|
||||
"use_ssl": 1,
|
||||
"smtp_server": "smtp.mail.yahoo.com",
|
||||
"smtp_port": 587,
|
||||
},
|
||||
"Yandex": {
|
||||
"email_server": "imap.yandex.com",
|
||||
"use_ssl": 1,
|
||||
"smtp_server": "smtp.yandex.com",
|
||||
"smtp_port": 587,
|
||||
},
|
||||
}
|
||||
@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Frappe CRM VERSION\n"
|
||||
"Report-Msgid-Bugs-To: shariq@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-04-13 09:36+0000\n"
|
||||
"PO-Revision-Date: 2025-04-13 09:36+0000\n"
|
||||
"POT-Creation-Date: 2025-04-20 09:35+0000\n"
|
||||
"PO-Revision-Date: 2025-04-20 09:35+0000\n"
|
||||
"Last-Translator: shariq@frappe.io\n"
|
||||
"Language-Team: shariq@frappe.io\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -16,7 +16,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: Babel 2.13.1\n"
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:1206
|
||||
#: frontend/src/components/ViewControls.vue:1219
|
||||
msgid " (New)"
|
||||
msgstr ""
|
||||
|
||||
@ -159,8 +159,8 @@ msgid "Account SID"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/CustomActions.vue:73
|
||||
#: frontend/src/components/ViewControls.vue:669
|
||||
#: frontend/src/components/ViewControls.vue:1098
|
||||
#: frontend/src/components/ViewControls.vue:682
|
||||
#: frontend/src/components/ViewControls.vue:1111
|
||||
msgid "Actions"
|
||||
msgstr ""
|
||||
|
||||
@ -933,13 +933,13 @@ msgstr ""
|
||||
#: frontend/src/components/Modals/TaskModal.vue:8
|
||||
#: frontend/src/components/Modals/ViewModal.vue:16
|
||||
#: frontend/src/pages/CallLogs.vue:11 frontend/src/pages/Contacts.vue:13
|
||||
#: frontend/src/pages/Contacts.vue:57 frontend/src/pages/Deals.vue:13
|
||||
#: frontend/src/pages/Deals.vue:233 frontend/src/pages/EmailTemplates.vue:13
|
||||
#: frontend/src/pages/EmailTemplates.vue:58 frontend/src/pages/Leads.vue:13
|
||||
#: frontend/src/pages/Leads.vue:259 frontend/src/pages/Notes.vue:7
|
||||
#: frontend/src/pages/Contacts.vue:60 frontend/src/pages/Deals.vue:13
|
||||
#: frontend/src/pages/Deals.vue:236 frontend/src/pages/EmailTemplates.vue:13
|
||||
#: frontend/src/pages/EmailTemplates.vue:61 frontend/src/pages/Leads.vue:13
|
||||
#: frontend/src/pages/Leads.vue:262 frontend/src/pages/Notes.vue:7
|
||||
#: frontend/src/pages/Notes.vue:93 frontend/src/pages/Organizations.vue:13
|
||||
#: frontend/src/pages/Organizations.vue:57 frontend/src/pages/Tasks.vue:11
|
||||
#: frontend/src/pages/Tasks.vue:182
|
||||
#: frontend/src/pages/Organizations.vue:60 frontend/src/pages/Tasks.vue:11
|
||||
#: frontend/src/pages/Tasks.vue:185
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
|
||||
@ -979,7 +979,7 @@ msgid "Create Task"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/ViewModal.vue:9
|
||||
#: frontend/src/components/ViewControls.vue:673
|
||||
#: frontend/src/components/ViewControls.vue:686
|
||||
msgid "Create View"
|
||||
msgstr ""
|
||||
|
||||
@ -1155,8 +1155,8 @@ msgstr ""
|
||||
#: frontend/src/components/ListBulkActions.vue:96
|
||||
#: frontend/src/components/ListBulkActions.vue:104
|
||||
#: frontend/src/components/ListBulkActions.vue:186
|
||||
#: frontend/src/components/ViewControls.vue:1150
|
||||
#: frontend/src/components/ViewControls.vue:1161
|
||||
#: frontend/src/components/ViewControls.vue:1163
|
||||
#: frontend/src/components/ViewControls.vue:1174
|
||||
#: frontend/src/pages/Contact.vue:105 frontend/src/pages/Contact.vue:320
|
||||
#: frontend/src/pages/MobileContact.vue:81
|
||||
#: frontend/src/pages/MobileContact.vue:295
|
||||
@ -1164,7 +1164,7 @@ msgstr ""
|
||||
#: frontend/src/pages/MobileOrganization.vue:72
|
||||
#: frontend/src/pages/MobileOrganization.vue:289
|
||||
#: frontend/src/pages/Notes.vue:40 frontend/src/pages/Organization.vue:83
|
||||
#: frontend/src/pages/Organization.vue:327 frontend/src/pages/Tasks.vue:365
|
||||
#: frontend/src/pages/Organization.vue:327 frontend/src/pages/Tasks.vue:368
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
@ -1176,8 +1176,8 @@ msgstr ""
|
||||
msgid "Delete Task"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:1146
|
||||
#: frontend/src/components/ViewControls.vue:1154
|
||||
#: frontend/src/components/ViewControls.vue:1159
|
||||
#: frontend/src/components/ViewControls.vue:1167
|
||||
msgid "Delete View"
|
||||
msgstr ""
|
||||
|
||||
@ -1288,7 +1288,7 @@ msgid "Due Date"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Modals/ViewModal.vue:15
|
||||
#: frontend/src/components/ViewControls.vue:1102
|
||||
#: frontend/src/components/ViewControls.vue:1115
|
||||
msgid "Duplicate"
|
||||
msgstr ""
|
||||
|
||||
@ -1345,7 +1345,7 @@ msgstr ""
|
||||
#: frontend/src/components/FieldLayoutEditor.vue:319
|
||||
#: frontend/src/components/FieldLayoutEditor.vue:345
|
||||
#: frontend/src/components/ListBulkActions.vue:179
|
||||
#: frontend/src/components/ViewControls.vue:1120
|
||||
#: frontend/src/components/ViewControls.vue:1133
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
@ -1799,7 +1799,7 @@ msgstr ""
|
||||
#. Label of the group_by_tab (Tab Break) field in DocType 'CRM View Settings'
|
||||
#: crm/fcrm/doctype/crm_view_settings/crm_view_settings.json
|
||||
#: frontend/src/components/ViewControls.vue:378
|
||||
#: frontend/src/components/ViewControls.vue:597 frontend/src/utils/view.js:16
|
||||
#: frontend/src/components/ViewControls.vue:610 frontend/src/utils/view.js:16
|
||||
msgid "Group By"
|
||||
msgstr ""
|
||||
|
||||
@ -2073,7 +2073,7 @@ msgstr ""
|
||||
#. Label of the kanban_tab (Tab Break) field in DocType 'CRM View Settings'
|
||||
#: crm/fcrm/doctype/crm_view_settings/crm_view_settings.json
|
||||
#: frontend/src/components/ViewControls.vue:383
|
||||
#: frontend/src/components/ViewControls.vue:586 frontend/src/utils/view.js:20
|
||||
#: frontend/src/components/ViewControls.vue:599 frontend/src/utils/view.js:20
|
||||
msgid "Kanban"
|
||||
msgstr ""
|
||||
|
||||
@ -2229,7 +2229,7 @@ msgstr ""
|
||||
#: crm/fcrm/doctype/crm_form_script/crm_form_script.json
|
||||
#: crm/fcrm/doctype/crm_view_settings/crm_view_settings.json
|
||||
#: frontend/src/components/ViewControls.vue:373
|
||||
#: frontend/src/components/ViewControls.vue:575 frontend/src/utils/view.js:12
|
||||
#: frontend/src/components/ViewControls.vue:588 frontend/src/utils/view.js:12
|
||||
msgid "List"
|
||||
msgstr ""
|
||||
|
||||
@ -2279,18 +2279,18 @@ msgstr ""
|
||||
msgid "Make Call"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:1135
|
||||
#: frontend/src/components/ViewControls.vue:1148
|
||||
msgid "Make Private"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:1135
|
||||
#: frontend/src/components/ViewControls.vue:1148
|
||||
msgid "Make Public"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/Activities/Activities.vue:792
|
||||
#: frontend/src/components/Activities/ActivityHeader.vue:142
|
||||
#: frontend/src/components/Activities/ActivityHeader.vue:185
|
||||
#: frontend/src/pages/Deals.vue:509 frontend/src/pages/Leads.vue:532
|
||||
#: frontend/src/pages/Deals.vue:512 frontend/src/pages/Leads.vue:535
|
||||
msgid "Make a Call"
|
||||
msgstr ""
|
||||
|
||||
@ -2433,7 +2433,7 @@ msgstr ""
|
||||
#: crm/fcrm/doctype/crm_dropdown_item/crm_dropdown_item.json
|
||||
#: crm/fcrm/doctype/fcrm_settings/fcrm_settings.json
|
||||
#: frontend/src/components/Modals/EmailTemplateModal.vue:24
|
||||
#: frontend/src/components/ViewControls.vue:768
|
||||
#: frontend/src/components/ViewControls.vue:781
|
||||
#: frontend/src/pages/MobileOrganization.vue:527
|
||||
#: frontend/src/pages/Organization.vue:562
|
||||
msgid "Name"
|
||||
@ -2478,7 +2478,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/Activities/ActivityHeader.vue:42
|
||||
#: frontend/src/components/Activities/ActivityHeader.vue:148
|
||||
#: frontend/src/pages/Deals.vue:515 frontend/src/pages/Leads.vue:538
|
||||
#: frontend/src/pages/Deals.vue:518 frontend/src/pages/Leads.vue:541
|
||||
msgid "New Note"
|
||||
msgstr ""
|
||||
|
||||
@ -2498,7 +2498,7 @@ msgstr ""
|
||||
|
||||
#: frontend/src/components/Activities/ActivityHeader.vue:52
|
||||
#: frontend/src/components/Activities/ActivityHeader.vue:153
|
||||
#: frontend/src/pages/Deals.vue:520 frontend/src/pages/Leads.vue:543
|
||||
#: frontend/src/pages/Deals.vue:523 frontend/src/pages/Leads.vue:546
|
||||
msgid "New Task"
|
||||
msgstr ""
|
||||
|
||||
@ -2601,13 +2601,13 @@ msgstr ""
|
||||
msgid "No {0} Available"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/pages/CallLogs.vue:53 frontend/src/pages/Contact.vue:165
|
||||
#: frontend/src/pages/Contacts.vue:56 frontend/src/pages/Deals.vue:232
|
||||
#: frontend/src/pages/EmailTemplates.vue:57 frontend/src/pages/Leads.vue:258
|
||||
#: frontend/src/pages/CallLogs.vue:56 frontend/src/pages/Contact.vue:165
|
||||
#: frontend/src/pages/Contacts.vue:59 frontend/src/pages/Deals.vue:235
|
||||
#: frontend/src/pages/EmailTemplates.vue:60 frontend/src/pages/Leads.vue:261
|
||||
#: frontend/src/pages/MobileContact.vue:154
|
||||
#: frontend/src/pages/MobileOrganization.vue:143
|
||||
#: frontend/src/pages/Notes.vue:92 frontend/src/pages/Organization.vue:157
|
||||
#: frontend/src/pages/Organizations.vue:56 frontend/src/pages/Tasks.vue:181
|
||||
#: frontend/src/pages/Organizations.vue:59 frontend/src/pages/Tasks.vue:184
|
||||
msgid "No {0} Found"
|
||||
msgstr ""
|
||||
|
||||
@ -2912,7 +2912,7 @@ msgstr ""
|
||||
msgid "Phone Numbers"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:1127
|
||||
#: frontend/src/components/ViewControls.vue:1140
|
||||
msgid "Pin View"
|
||||
msgstr ""
|
||||
|
||||
@ -2921,7 +2921,7 @@ msgstr ""
|
||||
msgid "Pinned"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:663
|
||||
#: frontend/src/components/ViewControls.vue:676
|
||||
msgid "Pinned Views"
|
||||
msgstr ""
|
||||
|
||||
@ -3004,7 +3004,7 @@ msgstr ""
|
||||
msgid "Public"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:658
|
||||
#: frontend/src/components/ViewControls.vue:671
|
||||
msgid "Public Views"
|
||||
msgstr ""
|
||||
|
||||
@ -3027,7 +3027,7 @@ msgstr ""
|
||||
msgid "Quick Filters"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:719
|
||||
#: frontend/src/components/ViewControls.vue:732
|
||||
msgid "Quick Filters updated successfully"
|
||||
msgstr ""
|
||||
|
||||
@ -3358,7 +3358,7 @@ msgstr ""
|
||||
msgid "Save Changes"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:653
|
||||
#: frontend/src/components/ViewControls.vue:666
|
||||
msgid "Saved Views"
|
||||
msgstr ""
|
||||
|
||||
@ -3437,7 +3437,7 @@ msgstr ""
|
||||
msgid "Set as Primary Contact"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:1112
|
||||
#: frontend/src/components/ViewControls.vue:1125
|
||||
msgid "Set as default"
|
||||
msgstr ""
|
||||
|
||||
@ -3512,7 +3512,7 @@ msgstr ""
|
||||
msgid "Standard Form Scripts can not be modified, duplicate the Form Script instead."
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:620
|
||||
#: frontend/src/components/ViewControls.vue:633
|
||||
msgid "Standard Views"
|
||||
msgstr ""
|
||||
|
||||
@ -3852,11 +3852,11 @@ msgstr ""
|
||||
msgid "Unknown"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:1127
|
||||
#: frontend/src/components/ViewControls.vue:1140
|
||||
msgid "Unpin View"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:964
|
||||
#: frontend/src/components/ViewControls.vue:977
|
||||
msgid "Unsaved Changes"
|
||||
msgstr ""
|
||||
|
||||
@ -3878,7 +3878,7 @@ msgstr ""
|
||||
#: frontend/src/components/Settings/SettingsPage.vue:31
|
||||
#: frontend/src/components/Settings/TelephonySettings.vue:70
|
||||
#: frontend/src/components/Telephony/ExotelCallUI.vue:219
|
||||
#: frontend/src/components/ViewControls.vue:969
|
||||
#: frontend/src/components/ViewControls.vue:982
|
||||
msgid "Update"
|
||||
msgstr ""
|
||||
|
||||
@ -4051,7 +4051,7 @@ msgstr ""
|
||||
msgid "You do not have mobile number set in your Telephony Agent"
|
||||
msgstr ""
|
||||
|
||||
#: frontend/src/components/ViewControls.vue:965
|
||||
#: frontend/src/components/ViewControls.vue:978
|
||||
msgid "You have unsaved changes. Do you want to save them?"
|
||||
msgstr ""
|
||||
|
||||
|
||||
6
frontend/components.d.ts
vendored
@ -78,11 +78,17 @@ declare module 'vue' {
|
||||
EditIcon: typeof import('./src/components/Icons/EditIcon.vue')['default']
|
||||
EditValueModal: typeof import('./src/components/Modals/EditValueModal.vue')['default']
|
||||
Email2Icon: typeof import('./src/components/Icons/Email2Icon.vue')['default']
|
||||
EmailAccountCard: typeof import('./src/components/Settings/EmailAccountCard.vue')['default']
|
||||
EmailAccountList: typeof import('./src/components/Settings/EmailAccountList.vue')['default']
|
||||
EmailAdd: typeof import('./src/components/Settings/EmailAdd.vue')['default']
|
||||
EmailArea: typeof import('./src/components/Activities/EmailArea.vue')['default']
|
||||
EmailAtIcon: typeof import('./src/components/Icons/EmailAtIcon.vue')['default']
|
||||
EmailConfig: typeof import('./src/components/Settings/EmailConfig.vue')['default']
|
||||
EmailContent: typeof import('./src/components/Activities/EmailContent.vue')['default']
|
||||
EmailEdit: typeof import('./src/components/Settings/EmailEdit.vue')['default']
|
||||
EmailEditor: typeof import('./src/components/EmailEditor.vue')['default']
|
||||
EmailIcon: typeof import('./src/components/Icons/EmailIcon.vue')['default']
|
||||
EmailProviderIcon: typeof import('./src/components/Settings/EmailProviderIcon.vue')['default']
|
||||
EmailTemplateModal: typeof import('./src/components/Modals/EmailTemplateModal.vue')['default']
|
||||
EmailTemplateSelectorModal: typeof import('./src/components/Modals/EmailTemplateSelectorModal.vue')['default']
|
||||
EmailTemplatesListView: typeof import('./src/components/ListViews/EmailTemplatesListView.vue')['default']
|
||||
|
||||
4
frontend/public/images/frappe-mail.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.5714 44L31.4286 44C38.3716 44 44 38.3716 44 31.4286L44 12.5714C44 5.62842 38.3716 0 31.4286 0L12.5714 0C5.62842 0 0 5.62842 0 12.5714L0 31.4286C0 38.3716 5.62842 44 12.5714 44Z" fill="#0466DC"/>
|
||||
<path d="M9.42859 12.5715V14.8972L12.5714 17.4587L18.5743 22.3458C19.5329 23.1315 20.7586 23.5715 22 23.5715C23.2414 23.5715 24.4672 23.1315 25.4257 22.3458L31.4286 17.443V28.2701H12.5714V21.5287L9.42859 18.9672V27.4844C9.42859 29.653 11.1886 31.413 13.3572 31.413H30.6429C32.8115 31.413 34.5715 29.653 34.5715 27.4844V12.5715H9.42859ZM23.4457 19.9101C22.6286 20.5701 21.3714 20.5701 20.57 19.9101L15.4157 15.7144H28.6L23.4457 19.9101Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 763 B |
BIN
frontend/public/images/gmail.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
frontend/public/images/outlook.png
Normal file
|
After Width: | Height: | Size: 215 KiB |
BIN
frontend/public/images/sendgrid.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
frontend/public/images/sparkpost.webp
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
frontend/public/images/yahoo.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
BIN
frontend/public/images/yandex.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
@ -64,7 +64,7 @@ import LoadingIndicator from '@/components/Icons/LoadingIndicator.vue'
|
||||
import { createToast } from '@/utils'
|
||||
import { usersStore } from '@/stores/users'
|
||||
import { isMobileView } from '@/composables/settings'
|
||||
import { ref } from 'vue'
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
doctype: {
|
||||
@ -114,4 +114,20 @@ const tabs = createResource({
|
||||
function saveChanges() {
|
||||
data.save.submit()
|
||||
}
|
||||
|
||||
watch(
|
||||
() => data.doc,
|
||||
(newValue, oldValue) => {
|
||||
if (!oldValue) return
|
||||
if (newValue && oldValue) {
|
||||
const isDirty =
|
||||
JSON.stringify(newValue) !== JSON.stringify(data.originalDoc)
|
||||
data.isDirty = isDirty
|
||||
if (isDirty) {
|
||||
data.save.loading = false
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
</script>
|
||||
|
||||
27
frontend/src/components/Settings/EmailConfig.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div class="flex-1 p-8">
|
||||
<div v-if="step === 'email-add'" class="h-full">
|
||||
<EmailAdd @update:step="updateStep" />
|
||||
</div>
|
||||
<div v-else-if="step === 'email-list'" class="h-full">
|
||||
<EmailAccountList @update:step="updateStep" />
|
||||
</div>
|
||||
<div v-else-if="step === 'email-edit'" class="h-full">
|
||||
<EmailEdit :account-data="accountData" @update:step="updateStep" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import EmailAdd from "./EmailAdd.vue";
|
||||
import EmailAccountList from "./EmailAccountList.vue";
|
||||
import EmailEdit from "./EmailEdit.vue";
|
||||
|
||||
const step = ref("email-list");
|
||||
const accountData = ref(null);
|
||||
function updateStep(newStep, data) {
|
||||
step.value = newStep;
|
||||
accountData.value = data;
|
||||
}
|
||||
</script>
|
||||
@ -6,8 +6,8 @@
|
||||
>
|
||||
<template #body>
|
||||
<div class="flex h-[calc(100vh_-_8rem)]">
|
||||
<div class="flex w-52 shrink-0 flex-col bg-surface-gray-2 p-2">
|
||||
<h1 class="mb-3 px-2 pt-2 text-lg font-semibold text-ink-gray-9">
|
||||
<div class="flex flex-col p-2 w-52 shrink-0 bg-surface-gray-2">
|
||||
<h1 class="px-2 pt-2 mb-3 text-lg font-semibold text-ink-gray-9">
|
||||
{{ __('Settings') }}
|
||||
</h1>
|
||||
<div v-for="tab in tabs">
|
||||
@ -34,7 +34,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="flex relative flex-1 flex-col overflow-y-auto bg-surface-modal"
|
||||
class="relative flex flex-col flex-1 overflow-y-auto bg-surface-modal"
|
||||
>
|
||||
<Button
|
||||
class="absolute right-5 top-5"
|
||||
@ -53,12 +53,14 @@ import WhatsAppIcon from '@/components/Icons/WhatsAppIcon.vue'
|
||||
import ERPNextIcon from '@/components/Icons/ERPNextIcon.vue'
|
||||
import PhoneIcon from '@/components/Icons/PhoneIcon.vue'
|
||||
import InviteIcon from '@/components/Icons/InviteIcon.vue'
|
||||
import Email2Icon from '@/components/Icons/Email2Icon.vue'
|
||||
import GeneralSettings from '@/components/Settings/GeneralSettings.vue'
|
||||
import InviteMemberPage from '@/components/Settings/InviteMemberPage.vue'
|
||||
import ProfileSettings from '@/components/Settings/ProfileSettings.vue'
|
||||
import WhatsAppSettings from '@/components/Settings/WhatsAppSettings.vue'
|
||||
import ERPNextSettings from '@/components/Settings/ERPNextSettings.vue'
|
||||
import TelephonySettings from '@/components/Settings/TelephonySettings.vue'
|
||||
import EmailConfig from '@/components/Settings/EmailConfig.vue'
|
||||
import SidebarLink from '@/components/SidebarLink.vue'
|
||||
import { usersStore } from '@/stores/users'
|
||||
import {
|
||||
@ -101,6 +103,12 @@ const tabs = computed(() => {
|
||||
component: markRaw(InviteMemberPage),
|
||||
condition: () => isManager(),
|
||||
},
|
||||
{
|
||||
label: __('Email Accounts'),
|
||||
icon: Email2Icon,
|
||||
component: markRaw(EmailConfig),
|
||||
condition: () => isManager(),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
179
frontend/src/components/Settings/emailConfig.ts
Normal file
@ -0,0 +1,179 @@
|
||||
import { validateEmail } from '../../utils'
|
||||
|
||||
const LogoGmail = '/images/gmail.png'
|
||||
const LogoOutlook = '/images/outlook.png'
|
||||
const LogoSendgrid = '/images/sendgrid.png'
|
||||
const LogoSparkpost = '/images/sparkpost.webp'
|
||||
const LogoYahoo = '/images/yahoo.png'
|
||||
const LogoYandex = '/images/yandex.png'
|
||||
const LogoFrappeMail = '/images/frappe-mail.svg'
|
||||
|
||||
const fixedFields = [
|
||||
{
|
||||
label: 'Account Name',
|
||||
name: 'email_account_name',
|
||||
type: 'text',
|
||||
placeholder: 'Support / Sales',
|
||||
},
|
||||
{
|
||||
label: 'Email ID',
|
||||
name: 'email_id',
|
||||
type: 'email',
|
||||
placeholder: 'johndoe@example.com',
|
||||
},
|
||||
]
|
||||
|
||||
export const incomingOutgoingFields = [
|
||||
{
|
||||
label: 'Enable Incoming',
|
||||
name: 'enable_incoming',
|
||||
type: 'checkbox',
|
||||
description:
|
||||
'If enabled, tickets can be created from the incoming emails on this account.',
|
||||
},
|
||||
{
|
||||
label: 'Enable Outgoing',
|
||||
name: 'enable_outgoing',
|
||||
type: 'checkbox',
|
||||
description: 'If enabled, outgoing emails can be sent from this account.',
|
||||
},
|
||||
{
|
||||
label: 'Default Incoming',
|
||||
name: 'default_incoming',
|
||||
type: 'checkbox',
|
||||
description:
|
||||
'If enabled, all replies to your company (eg: replies@yourcomany.com) will come to this account. Note: Only one account can be default incoming.',
|
||||
},
|
||||
{
|
||||
label: 'Default Outgoing',
|
||||
name: 'default_outgoing',
|
||||
type: 'checkbox',
|
||||
description:
|
||||
'If enabled, all outgoing emails will be sent from this account. Note: Only one account can be default outgoing.',
|
||||
},
|
||||
]
|
||||
|
||||
export const popularProviderFields = [
|
||||
...fixedFields,
|
||||
{
|
||||
label: 'Password',
|
||||
name: 'password',
|
||||
type: 'password',
|
||||
placeholder: '********',
|
||||
},
|
||||
]
|
||||
|
||||
export const customProviderFields = [
|
||||
...fixedFields,
|
||||
{
|
||||
label: 'Frappe Mail Site',
|
||||
name: 'frappe_mail_site',
|
||||
type: 'text',
|
||||
placeholder: 'https://frappemail.com',
|
||||
},
|
||||
{
|
||||
label: 'API Key',
|
||||
name: 'api_key',
|
||||
type: 'text',
|
||||
placeholder: '********',
|
||||
},
|
||||
{
|
||||
label: 'API Secret',
|
||||
name: 'api_secret',
|
||||
type: 'password',
|
||||
placeholder: '********',
|
||||
},
|
||||
]
|
||||
|
||||
export const services = [
|
||||
{
|
||||
name: 'GMail',
|
||||
icon: LogoGmail,
|
||||
info: `Setting up GMail requires you to enable two factor authentication
|
||||
and app specific passwords. Read more`,
|
||||
link: 'https://support.google.com/accounts/answer/185833',
|
||||
custom: false,
|
||||
},
|
||||
{
|
||||
name: 'Outlook',
|
||||
icon: LogoOutlook,
|
||||
info: `Setting up Outlook requires you to enable two factor authentication
|
||||
and app specific passwords. Read more`,
|
||||
link: 'https://support.microsoft.com/en-us/account-billing/how-to-get-and-use-app-passwords-5896ed9b-4263-e681-128a-a6f2979a7944',
|
||||
custom: false,
|
||||
},
|
||||
{
|
||||
name: 'Sendgrid',
|
||||
icon: LogoSendgrid,
|
||||
info: `Setting up Sendgrid requires you to enable two factor authentication
|
||||
and app specific passwords. Read more `,
|
||||
link: 'https://sendgrid.com/docs/ui/account-and-settings/two-factor-authentication/',
|
||||
custom: false,
|
||||
},
|
||||
{
|
||||
name: 'SparkPost',
|
||||
icon: LogoSparkpost,
|
||||
info: `Setting up SparkPost requires you to enable two factor authentication
|
||||
and app specific passwords. Read more `,
|
||||
link: 'https://support.sparkpost.com/docs/my-account-and-profile/enabling-two-factor-authentication',
|
||||
custom: false,
|
||||
},
|
||||
{
|
||||
name: 'Yahoo',
|
||||
icon: LogoYahoo,
|
||||
info: `Setting up Yahoo requires you to enable two factor authentication
|
||||
and app specific passwords. Read more `,
|
||||
link: 'https://help.yahoo.com/kb/SLN15241.html',
|
||||
custom: false,
|
||||
},
|
||||
{
|
||||
name: 'Yandex',
|
||||
icon: LogoYandex,
|
||||
info: `Setting up Yandex requires you to enable two factor authentication
|
||||
and app specific passwords. Read more `,
|
||||
link: 'https://yandex.com/support/id/authorization/app-passwords.html',
|
||||
custom: false,
|
||||
},
|
||||
{
|
||||
name: 'Frappe Mail',
|
||||
icon: LogoFrappeMail,
|
||||
info: `Setting up Frappe Mail requires you to have an API key and API Secret of your email account. Read more `,
|
||||
link: 'https://github.com/frappe/mail',
|
||||
custom: true,
|
||||
},
|
||||
]
|
||||
|
||||
export const emailIcon = {
|
||||
GMail: LogoGmail,
|
||||
Outlook: LogoOutlook,
|
||||
Sendgrid: LogoSendgrid,
|
||||
SparkPost: LogoSparkpost,
|
||||
Yahoo: LogoYahoo,
|
||||
Yandex: LogoYandex,
|
||||
'Frappe Mail': LogoFrappeMail,
|
||||
}
|
||||
|
||||
export function validateInputs(state, isCustom) {
|
||||
if (!state.email_account_name) {
|
||||
return 'Account name is required'
|
||||
}
|
||||
if (!state.email_id) {
|
||||
return 'Email ID is required'
|
||||
}
|
||||
const validEmail = validateEmail(state.email_id)
|
||||
if (!validEmail) {
|
||||
return 'Invalid email ID'
|
||||
}
|
||||
if (!isCustom && !state.password) {
|
||||
return 'Password is required'
|
||||
}
|
||||
if (isCustom) {
|
||||
if (!state.api_key) {
|
||||
return 'API Key is required'
|
||||
}
|
||||
if (!state.api_secret) {
|
||||
return
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
@ -168,7 +168,11 @@
|
||||
</template>
|
||||
</Tabs>
|
||||
</div>
|
||||
<ErrorPage v-else :errorTitle="errorTitle" :errorMessage="errorMessage" />
|
||||
<ErrorPage
|
||||
v-else-if="errorTitle"
|
||||
:errorTitle="errorTitle"
|
||||
:errorMessage="errorMessage"
|
||||
/>
|
||||
<AddressModal v-model="showAddressModal" v-model:address="_address" />
|
||||
</template>
|
||||
|
||||
|
||||
@ -267,7 +267,11 @@
|
||||
</div>
|
||||
</Resizer>
|
||||
</div>
|
||||
<ErrorPage v-else :errorTitle="errorTitle" :errorMessage="errorMessage" />
|
||||
<ErrorPage
|
||||
v-else-if="errorTitle"
|
||||
:errorTitle="errorTitle"
|
||||
:errorMessage="errorMessage"
|
||||
/>
|
||||
<OrganizationModal
|
||||
v-model="showOrganizationModal"
|
||||
v-model:organization="_organization"
|
||||
|
||||
@ -191,7 +191,11 @@
|
||||
</div>
|
||||
</Resizer>
|
||||
</div>
|
||||
<ErrorPage v-else :errorTitle="errorTitle" :errorMessage="errorMessage" />
|
||||
<ErrorPage
|
||||
v-else-if="errorTitle"
|
||||
:errorTitle="errorTitle"
|
||||
:errorMessage="errorMessage"
|
||||
/>
|
||||
<Dialog
|
||||
v-model="showConvertToDealModal"
|
||||
:options="{
|
||||
|
||||
@ -160,7 +160,11 @@
|
||||
</template>
|
||||
</Tabs>
|
||||
</div>
|
||||
<ErrorPage v-else :errorTitle="errorTitle" :errorMessage="errorMessage" />
|
||||
<ErrorPage
|
||||
v-else-if="errorTitle"
|
||||
:errorTitle="errorTitle"
|
||||
:errorMessage="errorMessage"
|
||||
/>
|
||||
<QuickEntryModal
|
||||
v-if="showQuickEntryModal"
|
||||
v-model="showQuickEntryModal"
|
||||
|
||||
13
frontend/src/types.ts
Normal file
@ -0,0 +1,13 @@
|
||||
export interface EmailAccount {
|
||||
email_account_name: string
|
||||
email_id: string
|
||||
service: string
|
||||
api_key?: string
|
||||
api_secret?: string
|
||||
password?: string
|
||||
frappe_mail_site?: string
|
||||
enable_outgoing?: boolean
|
||||
enable_incoming?: boolean
|
||||
default_outgoing?: boolean
|
||||
default_incoming?: boolean
|
||||
}
|
||||