feat: Create/Edit & List page for email template & implemented delete from list

This commit is contained in:
Shariq Ansari 2025-06-24 18:52:00 +05:30
parent ec6b1558b1
commit cd7bab9184
6 changed files with 164 additions and 42 deletions

View File

@ -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']

View File

@ -0,0 +1,3 @@
<template>
Edit
</template>

View File

@ -0,0 +1,51 @@
<template>
<NewEmailTemplate v-if="step === 'new-template'" @updateStep="updateStep" />
<EmailTemplates
v-else-if="step === 'template-list'"
@updateStep="updateStep"
/>
<EditEmailTemplate
v-else-if="step === 'edit-template'"
:data="templateData"
@updateStep="updateStep"
/>
</template>
<script setup>
import NewEmailTemplate from './NewEmailTemplate.vue'
import EditEmailTemplate from './EditEmailTemplate.vue'
import EmailTemplates from './EmailTemplates.vue'
import { createListResource } from 'frappe-ui'
import { provide, ref } from 'vue'
const step = ref('template-list')
const templateData = ref(null)
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,
})
provide('templates', templates)
function updateStep(newStep, data) {
step.value = newStep
templateData.value = data
}
</script>

View File

@ -15,7 +15,12 @@
</p>
</div>
<div class="flex item-center space-x-2 w-3/12 justify-end">
<Button :label="__('New')" icon-left="plus" variant="solid" />
<Button
:label="__('New')"
icon-left="plus"
variant="solid"
@click="emit('updateStep', 'new-template')"
/>
</div>
</div>
@ -100,27 +105,15 @@
@update:model-value="toggleEmailTemplate(template)"
/>
<Dropdown
:options="[
{
label: __('Edit'),
icon: 'edit-2',
onClick: () => {},
},
{
label: __('Duplicate'),
icon: 'copy',
onClick: () => {},
},
{
label: __('Delete'),
icon: 'trash-2',
onClick: () => {},
},
]"
:options="getDropdownOptions(template)"
placement="right"
:button="{
icon: 'more-horizontal',
variant: 'ghost',
onblur: (e) => {
e.stopPropagation()
confirmDelete = false
},
}"
/>
</div>
@ -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),
],
)
}
</script>

View File

@ -0,0 +1 @@
<template>New</template>

View File

@ -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),
},
],
},