From b799e8dbb4be55b3787ec01495749e316c8ecf46 Mon Sep 17 00:00:00 2001
From: Shariq Ansari
Date: Tue, 24 Jun 2025 18:52:00 +0530
Subject: [PATCH] feat: Create/Edit & List page for email template &
implemented delete from list
(cherry picked from commit cd7bab918448e914516b43536d346068bbdfe659)
---
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 e09a16f1..ed71d6c9 100644
--- a/frontend/components.d.ts
+++ b/frontend/components.d.ts
@@ -79,6 +79,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']
@@ -94,6 +95,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']
@@ -156,8 +158,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']
@@ -173,6 +177,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 @@
+
+ Edit
+
\ 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 @@
+New
\ 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),
},
],
},