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/frontend/components.d.ts b/frontend/components.d.ts index f09d0554..ef8a8359 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'] @@ -140,6 +146,7 @@ declare module 'vue' { ListIcon: typeof import('./src/components/Icons/ListIcon.vue')['default'] ListRows: typeof import('./src/components/ListViews/ListRows.vue')['default'] LoadingIndicator: typeof import('./src/components/Icons/LoadingIndicator.vue')['default'] + LucidePlus: typeof import('~icons/lucide/plus')['default'] MarkAsDoneIcon: typeof import('./src/components/Icons/MarkAsDoneIcon.vue')['default'] MaximizeIcon: typeof import('./src/components/Icons/MaximizeIcon.vue')['default'] MenuIcon: typeof import('./src/components/Icons/MenuIcon.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/Settings/EmailAccountCard.vue b/frontend/src/components/Settings/EmailAccountCard.vue new file mode 100644 index 00000000..09fa11c2 --- /dev/null +++ b/frontend/src/components/Settings/EmailAccountCard.vue @@ -0,0 +1,55 @@ + + + + + + + + {{ emailAccount.email_account_name }} + + {{ emailAccount.email_id }} + + + + + + + + + + + + diff --git a/frontend/src/components/Settings/EmailAccountList.vue b/frontend/src/components/Settings/EmailAccountList.vue new file mode 100644 index 00000000..786a9e09 --- /dev/null +++ b/frontend/src/components/Settings/EmailAccountList.vue @@ -0,0 +1,50 @@ + + + + + Email Accounts + + + + + + + + + + + + + + + Please add an email account to continue. + + + + + diff --git a/frontend/src/components/Settings/EmailAdd.vue b/frontend/src/components/Settings/EmailAdd.vue new file mode 100644 index 00000000..bc5e21af --- /dev/null +++ b/frontend/src/components/Settings/EmailAdd.vue @@ -0,0 +1,122 @@ + + + + + Setup Email + + Choose the email service provider you want to configure. + + + + + + + + + + + + + + {{ selectedService.info }} + here + . + + + + + + + + + + + + + {{ field.description }} + + + + + + + + + + + + + + + + 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/EmailEdit.vue b/frontend/src/components/Settings/EmailEdit.vue new file mode 100644 index 00000000..73b29e1b --- /dev/null +++ b/frontend/src/components/Settings/EmailEdit.vue @@ -0,0 +1,186 @@ + + + + + Edit Email + + + + + + + + + {{ info.description }} + here + . + + + + + + + + + + + + + {{ field.description }} + + + + + + + + + + + + + diff --git a/frontend/src/components/Settings/EmailProviderIcon.vue b/frontend/src/components/Settings/EmailProviderIcon.vue new file mode 100644 index 00000000..a5078320 --- /dev/null +++ b/frontend/src/components/Settings/EmailProviderIcon.vue @@ -0,0 +1,29 @@ + + + + + + {{ serviceName }} + + + + + + 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/types.ts b/frontend/src/types.ts new file mode 100644 index 00000000..be1f6525 --- /dev/null +++ b/frontend/src/types.ts @@ -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 +}
+ {{ emailAccount.email_account_name }} +
+ Choose the email service provider you want to configure. +
{{ field.description }}
+ {{ serviceName }} +