From 1c3ee8b557bf9371aad2d6803876095a1c0a8026 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 24 Jun 2025 15:35:48 +0530 Subject: [PATCH 01/16] refactor: added email templates in settings modal --- frontend/components.d.ts | 1 + .../Settings/EmailTemplate/EmailTemplates.vue | 203 ++++++++++++++++++ frontend/src/components/Settings/Settings.vue | 6 + 3 files changed, 210 insertions(+) create mode 100644 frontend/src/components/Settings/EmailTemplate/EmailTemplates.vue diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 2db22144..e3c9f39b 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -96,6 +96,7 @@ declare module 'vue' { 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'] + EmailTemplates: typeof import('./src/components/Settings/EmailTemplate/EmailTemplates.vue')['default'] EmailTemplateSelectorModal: typeof import('./src/components/Modals/EmailTemplateSelectorModal.vue')['default'] EmailTemplatesListView: typeof import('./src/components/ListViews/EmailTemplatesListView.vue')['default'] ERPNextIcon: typeof import('./src/components/Icons/ERPNextIcon.vue')['default'] diff --git a/frontend/src/components/Settings/EmailTemplate/EmailTemplates.vue b/frontend/src/components/Settings/EmailTemplate/EmailTemplates.vue new file mode 100644 index 00000000..4ae30422 --- /dev/null +++ b/frontend/src/components/Settings/EmailTemplate/EmailTemplates.vue @@ -0,0 +1,203 @@ + + diff --git a/frontend/src/components/Settings/Settings.vue b/frontend/src/components/Settings/Settings.vue index e4662770..e0939285 100644 --- a/frontend/src/components/Settings/Settings.vue +++ b/frontend/src/components/Settings/Settings.vue @@ -51,6 +51,7 @@ import InviteUserPage from '@/components/Settings/InviteUserPage.vue' import ProfileSettings from '@/components/Settings/ProfileSettings.vue' import WhatsAppSettings from '@/components/Settings/WhatsAppSettings.vue' import ERPNextSettings from '@/components/Settings/ERPNextSettings.vue' +import EmailTemplates from '@/components/Settings/EmailTemplate/EmailTemplates.vue' import TelephonySettings from '@/components/Settings/TelephonySettings.vue' import EmailConfig from '@/components/Settings/EmailConfig.vue' import SidebarLink from '@/components/SidebarLink.vue' @@ -107,6 +108,11 @@ const tabs = computed(() => { component: markRaw(EmailConfig), condition: () => isManager(), }, + { + label: __('Email Templates'), + icon: Email2Icon, + component: markRaw(EmailTemplates), + }, ], }, { From ec6b1558b15432490ba3e1933c2448b2d32c5a4b Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 24 Jun 2025 15:36:54 +0530 Subject: [PATCH 02/16] fix: only show search if users are more than 10 --- frontend/src/components/Settings/Users.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Settings/Users.vue b/frontend/src/components/Settings/Users.vue index 0d4670ed..a4b07a03 100644 --- a/frontend/src/components/Settings/Users.vue +++ b/frontend/src/components/Settings/Users.vue @@ -63,7 +63,10 @@ class="flex flex-col overflow-hidden" v-if="!users.loading && users.data?.crmUsers?.length > 1" > -
+
Date: Tue, 24 Jun 2025 18:52:00 +0530 Subject: [PATCH 03/16] feat: Create/Edit & List page for email template & implemented delete from list --- frontend/components.d.ts | 5 + .../EmailTemplate/EditEmailTemplate.vue | 3 + .../EmailTemplate/EmailTemplatePage.vue | 51 +++++++ .../Settings/EmailTemplate/EmailTemplates.vue | 142 +++++++++++++----- .../EmailTemplate/NewEmailTemplate.vue | 1 + frontend/src/components/Settings/Settings.vue | 4 +- 6 files changed, 164 insertions(+), 42 deletions(-) create mode 100644 frontend/src/components/Settings/EmailTemplate/EditEmailTemplate.vue create mode 100644 frontend/src/components/Settings/EmailTemplate/EmailTemplatePage.vue create mode 100644 frontend/src/components/Settings/EmailTemplate/NewEmailTemplate.vue diff --git a/frontend/components.d.ts b/frontend/components.d.ts index e3c9f39b..4aa246ed 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -81,6 +81,7 @@ declare module 'vue' { DropdownItem: typeof import('./src/components/DropdownItem.vue')['default'] DuplicateIcon: typeof import('./src/components/Icons/DuplicateIcon.vue')['default'] DurationIcon: typeof import('./src/components/Icons/DurationIcon.vue')['default'] + EditEmailTemplate: typeof import('./src/components/Settings/EmailTemplate/EditEmailTemplate.vue')['default'] 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'] @@ -96,6 +97,7 @@ declare module 'vue' { 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'] + EmailTemplatePage: typeof import('./src/components/Settings/EmailTemplate/EmailTemplatePage.vue')['default'] EmailTemplates: typeof import('./src/components/Settings/EmailTemplate/EmailTemplates.vue')['default'] EmailTemplateSelectorModal: typeof import('./src/components/Modals/EmailTemplateSelectorModal.vue')['default'] EmailTemplatesListView: typeof import('./src/components/ListViews/EmailTemplatesListView.vue')['default'] @@ -159,8 +161,10 @@ declare module 'vue' { ListRows: typeof import('./src/components/ListViews/ListRows.vue')['default'] LoadingIndicator: typeof import('./src/components/Icons/LoadingIndicator.vue')['default'] LucideInfo: typeof import('~icons/lucide/info')['default'] + LucideMoreHorizontal: typeof import('~icons/lucide/more-horizontal')['default'] LucidePlus: typeof import('~icons/lucide/plus')['default'] LucideSearch: typeof import('~icons/lucide/search')['default'] + LucideX: typeof import('~icons/lucide/x')['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'] @@ -176,6 +180,7 @@ declare module 'vue' { MultiSelectUserInput: typeof import('./src/components/Controls/MultiSelectUserInput.vue')['default'] MuteIcon: typeof import('./src/components/Icons/MuteIcon.vue')['default'] NestedPopover: typeof import('./src/components/NestedPopover.vue')['default'] + NewEmailTemplate: typeof import('./src/components/Settings/EmailTemplate/NewEmailTemplate.vue')['default'] NoteArea: typeof import('./src/components/Activities/NoteArea.vue')['default'] NoteIcon: typeof import('./src/components/Icons/NoteIcon.vue')['default'] NoteModal: typeof import('./src/components/Modals/NoteModal.vue')['default'] diff --git a/frontend/src/components/Settings/EmailTemplate/EditEmailTemplate.vue b/frontend/src/components/Settings/EmailTemplate/EditEmailTemplate.vue new file mode 100644 index 00000000..b9f1a8b2 --- /dev/null +++ b/frontend/src/components/Settings/EmailTemplate/EditEmailTemplate.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/frontend/src/components/Settings/EmailTemplate/EmailTemplatePage.vue b/frontend/src/components/Settings/EmailTemplate/EmailTemplatePage.vue new file mode 100644 index 00000000..f41b4d5d --- /dev/null +++ b/frontend/src/components/Settings/EmailTemplate/EmailTemplatePage.vue @@ -0,0 +1,51 @@ + + + diff --git a/frontend/src/components/Settings/EmailTemplate/EmailTemplates.vue b/frontend/src/components/Settings/EmailTemplate/EmailTemplates.vue index 4ae30422..4eb9c622 100644 --- a/frontend/src/components/Settings/EmailTemplate/EmailTemplates.vue +++ b/frontend/src/components/Settings/EmailTemplate/EmailTemplates.vue @@ -15,7 +15,12 @@

-
@@ -100,27 +105,15 @@ @update:model-value="toggleEmailTemplate(template)" /> @@ -148,34 +141,18 @@ import { TextInput, FormControl, Switch, - createListResource, Dropdown, + FeatherIcon, } from 'frappe-ui' -import { ref, computed } from 'vue' +import { ref, computed, inject, h } from 'vue' -const templates = createListResource({ - type: 'list', - doctype: 'Email Template', - cache: 'emailTemplates', - fields: [ - 'name', - 'enabled', - 'use_html', - 'reference_doctype', - 'subject', - 'response', - 'response_html', - 'modified', - 'owner', - ], - auto: true, - filters: { reference_doctype: ['in', ['CRM Lead', 'CRM Deal']] }, - orderBy: 'modified desc', - pageLength: 20, -}) +const emit = defineEmits(['updateStep']) + +const templates = inject('templates') const search = ref('') const currentDoctype = ref('All') +const confirmDelete = ref(false) const templatesList = computed(() => { let list = templates.data || [] @@ -200,4 +177,89 @@ function toggleEmailTemplate(template) { enabled: template.enabled ? 1 : 0, }) } + +function deleteTemplate(template) { + confirmDelete.value = false + templates.delete.submit(template.name) +} + +function getDropdownOptions(template) { + let options = [ + { + label: __('Edit'), + component: (props) => + TemplateOption({ + option: __('Edit'), + icon: 'edit-2', + active: props.active, + onClick: () => { + emit('updateStep', 'edit-template', { ...template }) + }, + }), + }, + { + label: __('Duplicate'), + component: (props) => + TemplateOption({ + option: __('Duplicate'), + icon: 'copy', + active: props.active, + onClick: () => {}, + }), + }, + { + label: __('Delete'), + component: (props) => + TemplateOption({ + option: __('Delete'), + icon: 'trash-2', + active: props.active, + onClick: (e) => { + e.preventDefault() + e.stopPropagation() + confirmDelete.value = true + }, + }), + condition: () => !confirmDelete.value, + }, + { + label: __('Confirm Delete'), + component: (props) => + TemplateOption({ + option: __('Confirm Delete'), + icon: 'trash-2', + active: props.active, + variant: 'danger', + onClick: () => deleteTemplate(template), + }), + condition: () => confirmDelete.value, + }, + ] + + return options.filter((option) => option.condition?.() || true) +} + +function TemplateOption({ active, option, variant, icon, onClick }) { + return h( + 'button', + { + class: [ + active ? 'bg-surface-gray-2' : 'text-ink-gray-8', + 'group flex w-full gap-2 items-center rounded-md px-2 py-2 text-sm', + variant == 'danger' ? 'text-ink-red-3 hover:bg-ink-red-1' : '', + ], + onClick: onClick, + }, + [ + icon + ? h(FeatherIcon, { + name: icon, + class: ['h-4 w-4 shrink-0'], + 'aria-hidden': true, + }) + : null, + h('span', { class: 'whitespace-nowrap' }, option), + ], + ) +} diff --git a/frontend/src/components/Settings/EmailTemplate/NewEmailTemplate.vue b/frontend/src/components/Settings/EmailTemplate/NewEmailTemplate.vue new file mode 100644 index 00000000..b3ee3954 --- /dev/null +++ b/frontend/src/components/Settings/EmailTemplate/NewEmailTemplate.vue @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/components/Settings/Settings.vue b/frontend/src/components/Settings/Settings.vue index e0939285..57606486 100644 --- a/frontend/src/components/Settings/Settings.vue +++ b/frontend/src/components/Settings/Settings.vue @@ -51,7 +51,7 @@ import InviteUserPage from '@/components/Settings/InviteUserPage.vue' import ProfileSettings from '@/components/Settings/ProfileSettings.vue' import WhatsAppSettings from '@/components/Settings/WhatsAppSettings.vue' import ERPNextSettings from '@/components/Settings/ERPNextSettings.vue' -import EmailTemplates from '@/components/Settings/EmailTemplate/EmailTemplates.vue' +import EmailTemplatePage from '@/components/Settings/EmailTemplate/EmailTemplatePage.vue' import TelephonySettings from '@/components/Settings/TelephonySettings.vue' import EmailConfig from '@/components/Settings/EmailConfig.vue' import SidebarLink from '@/components/SidebarLink.vue' @@ -111,7 +111,7 @@ const tabs = computed(() => { { label: __('Email Templates'), icon: Email2Icon, - component: markRaw(EmailTemplates), + component: markRaw(EmailTemplatePage), }, ], }, From e4f728d809b20e54d4057cbb9e699bfe1c2bc06f Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 24 Jun 2025 18:52:36 +0530 Subject: [PATCH 04/16] fix: Create email template --- .../EmailTemplate/NewEmailTemplate.vue | 180 +++++++++++++++++- 1 file changed, 179 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Settings/EmailTemplate/NewEmailTemplate.vue b/frontend/src/components/Settings/EmailTemplate/NewEmailTemplate.vue index b3ee3954..1022031b 100644 --- a/frontend/src/components/Settings/EmailTemplate/NewEmailTemplate.vue +++ b/frontend/src/components/Settings/EmailTemplate/NewEmailTemplate.vue @@ -1 +1,179 @@ - \ No newline at end of file + + From dd3d297dab4fb2380889b4cec39aeba53f102896 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 24 Jun 2025 19:37:13 +0530 Subject: [PATCH 05/16] fix: Edit email template --- .../EmailTemplate/EditEmailTemplate.vue | 256 +++++++++++++++++- .../EmailTemplate/EmailTemplatePage.vue | 6 +- .../Settings/EmailTemplate/EmailTemplates.vue | 25 +- 3 files changed, 278 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/Settings/EmailTemplate/EditEmailTemplate.vue b/frontend/src/components/Settings/EmailTemplate/EditEmailTemplate.vue index b9f1a8b2..927302e7 100644 --- a/frontend/src/components/Settings/EmailTemplate/EditEmailTemplate.vue +++ b/frontend/src/components/Settings/EmailTemplate/EditEmailTemplate.vue @@ -1,3 +1,255 @@ \ No newline at end of file +
+ +
+
+
+ + {{ __(template.name) }} +
+
+
+
+
+ + +
+
+
{{ __('Enabled') }}
+ +
+
+
+ +
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+ {{ __('Content') }} + * +
+ +
+
+
+
+ +
+
+ + diff --git a/frontend/src/components/Settings/EmailTemplate/EmailTemplatePage.vue b/frontend/src/components/Settings/EmailTemplate/EmailTemplatePage.vue index f41b4d5d..e0ed3e07 100644 --- a/frontend/src/components/Settings/EmailTemplate/EmailTemplatePage.vue +++ b/frontend/src/components/Settings/EmailTemplate/EmailTemplatePage.vue @@ -6,7 +6,7 @@ /> @@ -19,7 +19,7 @@ import { createListResource } from 'frappe-ui' import { provide, ref } from 'vue' const step = ref('template-list') -const templateData = ref(null) +const template = ref(null) const templates = createListResource({ type: 'list', @@ -46,6 +46,6 @@ provide('templates', templates) function updateStep(newStep, data) { step.value = newStep - templateData.value = data + template.value = data } diff --git a/frontend/src/components/Settings/EmailTemplate/EmailTemplates.vue b/frontend/src/components/Settings/EmailTemplate/EmailTemplates.vue index 4eb9c622..682d31e9 100644 --- a/frontend/src/components/Settings/EmailTemplate/EmailTemplates.vue +++ b/frontend/src/components/Settings/EmailTemplate/EmailTemplates.vue @@ -143,6 +143,7 @@ import { Switch, Dropdown, FeatherIcon, + toast, } from 'frappe-ui' import { ref, computed, inject, h } from 'vue' @@ -172,10 +173,26 @@ const templatesList = computed(() => { }) function toggleEmailTemplate(template) { - templates.setValue.submit({ - name: template.name, - enabled: template.enabled ? 1 : 0, - }) + templates.setValue.submit( + { + name: template.name, + enabled: template.enabled ? 1 : 0, + }, + { + onSuccess: () => { + toast.success( + template.enabled + ? __('Template enabled successfully') + : __('Template disabled successfully'), + ) + }, + onError: (error) => { + toast.error(error.messages[0] || __('Failed to update template')) + // Revert the change if there was an error + template.enabled = !template.enabled + }, + }, + ) } function deleteTemplate(template) { From 7e6d5c3e544b784b07ae0975b4ae784fb9f9ee46 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 24 Jun 2025 19:46:27 +0530 Subject: [PATCH 06/16] fix: Duplicate email template --- .../EmailTemplate/EmailTemplatePage.vue | 6 ++++- .../Settings/EmailTemplate/EmailTemplates.vue | 15 +++++++---- .../EmailTemplate/NewEmailTemplate.vue | 25 ++++++++++++++++--- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/Settings/EmailTemplate/EmailTemplatePage.vue b/frontend/src/components/Settings/EmailTemplate/EmailTemplatePage.vue index e0ed3e07..c148a6dc 100644 --- a/frontend/src/components/Settings/EmailTemplate/EmailTemplatePage.vue +++ b/frontend/src/components/Settings/EmailTemplate/EmailTemplatePage.vue @@ -1,5 +1,9 @@ diff --git a/frontend/src/components/Settings/Users.vue b/frontend/src/components/Settings/Users.vue index a4b07a03..127c035b 100644 --- a/frontend/src/components/Settings/Users.vue +++ b/frontend/src/components/Settings/Users.vue @@ -1,7 +1,7 @@