diff --git a/crm/api/contact.py b/crm/api/contact.py index b8d8f578..6a812d5f 100644 --- a/crm/api/contact.py +++ b/crm/api/contact.py @@ -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 diff --git a/crm/api/settings.py b/crm/api/settings.py new file mode 100644 index 00000000..b4756371 --- /dev/null +++ b/crm/api/settings.py @@ -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, + }, +} diff --git a/crm/locale/main.pot b/crm/locale/main.pot index b8ed0b59..499aeadb 100644 --- a/crm/locale/main.pot +++ b/crm/locale/main.pot @@ -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 "" diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 0e776395..82afdb19 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -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'] diff --git a/frontend/public/images/frappe-mail.svg b/frontend/public/images/frappe-mail.svg new file mode 100644 index 00000000..822f56e1 --- /dev/null +++ b/frontend/public/images/frappe-mail.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/public/images/gmail.png b/frontend/public/images/gmail.png new file mode 100644 index 00000000..22e45fb5 Binary files /dev/null and b/frontend/public/images/gmail.png differ diff --git a/frontend/public/images/outlook.png b/frontend/public/images/outlook.png new file mode 100644 index 00000000..ded0d6b3 Binary files /dev/null and b/frontend/public/images/outlook.png differ diff --git a/frontend/public/images/sendgrid.png b/frontend/public/images/sendgrid.png new file mode 100644 index 00000000..eb1b82b4 Binary files /dev/null and b/frontend/public/images/sendgrid.png differ diff --git a/frontend/public/images/sparkpost.webp b/frontend/public/images/sparkpost.webp new file mode 100644 index 00000000..be586c3f Binary files /dev/null and b/frontend/public/images/sparkpost.webp differ diff --git a/frontend/public/images/yahoo.png b/frontend/public/images/yahoo.png new file mode 100644 index 00000000..24357ecd Binary files /dev/null and b/frontend/public/images/yahoo.png differ diff --git a/frontend/public/images/yandex.png b/frontend/public/images/yandex.png new file mode 100644 index 00000000..eb900a7a Binary files /dev/null and b/frontend/public/images/yandex.png differ diff --git a/frontend/src/components/Activities/DataFields.vue b/frontend/src/components/Activities/DataFields.vue index 25feb8e3..e73fb891 100644 --- a/frontend/src/components/Activities/DataFields.vue +++ b/frontend/src/components/Activities/DataFields.vue @@ -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 }, +) diff --git a/frontend/src/components/Settings/EmailConfig.vue b/frontend/src/components/Settings/EmailConfig.vue new file mode 100644 index 00000000..3d02cacf --- /dev/null +++ b/frontend/src/components/Settings/EmailConfig.vue @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + diff --git a/frontend/src/components/Settings/Settings.vue b/frontend/src/components/Settings/Settings.vue index 5eb093cc..e0215463 100644 --- a/frontend/src/components/Settings/Settings.vue +++ b/frontend/src/components/Settings/Settings.vue @@ -6,8 +6,8 @@ > - - + + {{ __('Settings') }} @@ -34,7 +34,7 @@ { component: markRaw(InviteMemberPage), condition: () => isManager(), }, + { + label: __('Email Accounts'), + icon: Email2Icon, + component: markRaw(EmailConfig), + condition: () => isManager(), + }, ], }, { diff --git a/frontend/src/components/Settings/emailConfig.ts b/frontend/src/components/Settings/emailConfig.ts new file mode 100644 index 00000000..c078ee13 --- /dev/null +++ b/frontend/src/components/Settings/emailConfig.ts @@ -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 '' +} diff --git a/frontend/src/pages/Contact.vue b/frontend/src/pages/Contact.vue index bfe7ea76..18fccd2d 100644 --- a/frontend/src/pages/Contact.vue +++ b/frontend/src/pages/Contact.vue @@ -168,7 +168,11 @@ - + diff --git a/frontend/src/pages/Deal.vue b/frontend/src/pages/Deal.vue index eb844696..428997c9 100644 --- a/frontend/src/pages/Deal.vue +++ b/frontend/src/pages/Deal.vue @@ -267,7 +267,11 @@ - + - + +