feat: email template update and deletion

This commit is contained in:
Jannat Patel
2025-05-23 13:28:18 +05:30
parent 5e2de35693
commit 5635d2a325
5 changed files with 113 additions and 49 deletions

View File

@@ -10,7 +10,7 @@
</div> </div>
</div> </div>
<div class="flex items-center space-x-5"> <div class="flex items-center space-x-5">
<Button @click="() => (showForm = true)"> <Button @click="openTemplateForm('new')">
<template #prefix> <template #prefix>
<Plus class="h-3 w-3 stroke-1.5" /> <Plus class="h-3 w-3 stroke-1.5" />
</template> </template>
@@ -26,9 +26,7 @@
:options="{ :options="{
showTooltip: false, showTooltip: false,
onRowClick: (row) => { onRowClick: (row) => {
if (readOnlyMode) return openTemplateForm(row.name)
selectedTemplate = row.name
showForm = true
}, },
}" }"
> >
@@ -50,20 +48,26 @@
<ListRow :row="row" v-for="row in emailTemplates.data"> <ListRow :row="row" v-for="row in emailTemplates.data">
<template #default="{ column, item }"> <template #default="{ column, item }">
<ListRowItem :item="row[column.key]" :align="column.align"> <ListRowItem :item="row[column.key]" :align="column.align">
<div v-if="column.key == 'use_html'"> <div class="leading-5 text-sm">
<FormControl
v-model="row[column.key]"
type="checkbox"
:disabled="true"
/>
</div>
<div v-else class="leading-5 text-sm">
{{ row[column.key] }} {{ row[column.key] }}
</div> </div>
</ListRowItem> </ListRowItem>
</template> </template>
</ListRow> </ListRow>
</ListRows> </ListRows>
<ListSelectBanner>
<template #actions="{ unselectAll, selections }">
<div class="flex gap-2">
<Button
variant="ghost"
@click="removeTemplate(selections, unselectAll)"
>
<Trash2 class="h-4 w-4 stroke-1.5" />
</Button>
</div>
</template>
</ListSelectBanner>
</ListView> </ListView>
</div> </div>
</div> </div>
@@ -75,16 +79,17 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { import {
Button,
call,
createListResource, createListResource,
ListView, ListView,
ListHeader, ListHeader,
ListHeaderItem, ListHeaderItem,
ListSelectBanner, ListSelectBanner,
FormControl,
ListRows, ListRows,
ListRow, ListRow,
ListRowItem, ListRowItem,
Button, toast,
} from 'frappe-ui' } from 'frappe-ui'
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { Plus, Trash2 } from 'lucide-vue-next' import { Plus, Trash2 } from 'lucide-vue-next'
@@ -107,29 +112,48 @@ const selectedTemplate = ref(null)
const emailTemplates = createListResource({ const emailTemplates = createListResource({
doctype: 'Email Template', doctype: 'Email Template',
fields: ['name', 'subject', 'use_html', 'response'], fields: ['name', 'subject', 'use_html', 'response', 'response_html'],
auto: true, auto: true,
orderBy: 'modified desc', orderBy: 'modified desc',
cache: 'email-templates', cache: 'email-templates',
}) })
const removeTemplate = (selections, unselectAll) => {
call('lms.lms.api.delete_documents', {
doctype: 'Email Template',
documents: Array.from(selections),
})
.then(() => {
emailTemplates.reload()
toast.success(__('Email Templates deleted successfully'))
unselectAll()
})
.catch((err) => {
toast.error(
cleanError(err.messages[0]) || __('Error deleting email templates')
)
})
}
const openTemplateForm = (templateID) => {
if (readOnlyMode) {
return
}
selectedTemplate.value = templateID
showForm.value = true
}
const columns = computed(() => { const columns = computed(() => {
return [ return [
{ {
label: 'Name', label: 'Name',
key: 'name', key: 'name',
width: '15rem', width: '20rem',
}, },
{ {
label: 'Subject', label: 'Subject',
key: 'subject', key: 'subject',
width: '18rem', width: '25rem',
},
{
label: 'Use HTML',
key: 'use_html',
width: '10rem',
align: 'right',
}, },
] ]
}) })

View File

@@ -25,21 +25,36 @@
v-model="template.name" v-model="template.name"
type="text" type="text"
:required="true" :required="true"
:placeholder="__('Batch Enrollment Confirmation')"
/> />
<FormControl <FormControl
:label="__('Subject')" :label="__('Subject')"
v-model="template.subject" v-model="template.subject"
type="text" type="text"
:required="true" :required="true"
:placeholder="__('Your enrollment in {{ batch_name }} is confirmed')"
/> />
<FormControl <FormControl
:label="__('Use HTML')" :label="__('Use HTML')"
v-model="template.use_html" v-model="template.use_html"
type="checkbox" type="checkbox"
/> />
<div> <FormControl
v-if="template.use_html"
:label="__('Content')"
v-model="template.response_html"
type="textarea"
:required="true"
:rows="10"
:placeholder="
__(
'<p>Dear {{ member_name }},</p>\n\n<p>You have been enrolled in our upcoming batch {{ batch_name }}.</p>\n\n<p>Thanks,</p>\n<p>Frappe Learning</p>'
)
"
/>
<div v-else>
<div class="text-xs text-ink-gray-5 mb-2"> <div class="text-xs text-ink-gray-5 mb-2">
{{ __('Response') }} {{ __('Content') }}
<span class="text-ink-red-3">*</span> <span class="text-ink-red-3">*</span>
</div> </div>
<TextEditor <TextEditor
@@ -47,6 +62,11 @@
@change="(val) => (template.response = val)" @change="(val) => (template.response = val)"
:editable="true" :editable="true"
:fixedMenu="true" :fixedMenu="true"
:placeholder="
__(
'Dear {{ member_name }},\n\nYou have been enrolled in our upcoming batch {{ batch_name }}.\n\nThanks,\nFrappe Learning'
)
"
editorClass="prose-sm max-w-none border-b border-x bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem] max-h-[18rem] overflow-y-auto" editorClass="prose-sm max-w-none border-b border-x bg-surface-gray-2 rounded-b-md py-1 px-2 min-h-[7rem] max-h-[18rem] overflow-y-auto"
/> />
</div> </div>
@@ -55,8 +75,8 @@
</Dialog> </Dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Dialog, FormControl, TextEditor, toast } from 'frappe-ui' import { call, Dialog, FormControl, TextEditor, toast } from 'frappe-ui'
import { reactive, ref, watch } from 'vue' import { reactive, watch } from 'vue'
import { cleanError } from '@/utils' import { cleanError } from '@/utils'
const props = defineProps({ const props = defineProps({
@@ -66,8 +86,6 @@ const props = defineProps({
}, },
}) })
console.log(props.templateID)
const show = defineModel() const show = defineModel()
const emailTemplates = defineModel('emailTemplates') const emailTemplates = defineModel('emailTemplates')
const template = reactive({ const template = reactive({
@@ -75,6 +93,7 @@ const template = reactive({
subject: '', subject: '',
use_html: false, use_html: false,
response: '', response: '',
response_html: '',
}) })
const saveTemplate = (close) => { const saveTemplate = (close) => {
@@ -88,19 +107,17 @@ const saveTemplate = (close) => {
const createNewTemplate = (close) => { const createNewTemplate = (close) => {
emailTemplates.value.insert.submit( emailTemplates.value.insert.submit(
{ {
__newname: template.value.name, __newname: template.name,
...template.value, ...template,
}, },
{ {
onSuccess() { onSuccess() {
emailTemplates.value.reload() emailTemplates.value.reload()
close() refreshForm(close)
refreshForm()
toast.success(__('Email Template created successfully')) toast.success(__('Email Template created successfully'))
}, },
onError(err) { onError(err) {
close() refreshForm(close)
refreshForm()
toast.error( toast.error(
cleanError(err.messages[0]) || __('Error creating email template') cleanError(err.messages[0]) || __('Error creating email template')
) )
@@ -109,27 +126,42 @@ const createNewTemplate = (close) => {
) )
} }
const updateTemplate = (close) => { const updateTemplate = async (close) => {
console.log(show) if (props.templateID != template.name) {
emailTemplates.value.setValue.submit({ await renameDoc()
name: props.templateID, }
...template, setValue(close)
}), }
const setValue = (close) => {
emailTemplates.value.setValue.submit(
{
...template,
name: template.name,
},
{ {
onSuccess() { onSuccess() {
emailTemplates.value.reload() emailTemplates.value.reload()
close() refreshForm(close)
refreshForm() console.log('template', template)
toast.success(__('Email Template updated successfully')) toast.success(__('Email Template updated successfully'))
}, },
onError(err) { onError(err) {
close() refreshForm(close)
refreshForm()
toast.error( toast.error(
cleanError(err.messages[0]) || __('Error updating email template') cleanError(err.messages[0]) || __('Error updating email template')
) )
}, },
} }
)
}
const renameDoc = async () => {
await call('frappe.client.rename_doc', {
doctype: 'Email Template',
old_name: props.templateID,
new_name: template.name,
})
} }
watch( watch(
@@ -142,6 +174,7 @@ watch(
template.subject = row.subject template.subject = row.subject
template.use_html = row.use_html template.use_html = row.use_html
template.response = row.response template.response = row.response
template.response_html = row.response_html
} }
}) })
} }
@@ -149,10 +182,12 @@ watch(
{ flush: 'post' } { flush: 'post' }
) )
const refreshForm = () => { const refreshForm = (close) => {
close()
template.name = '' template.name = ''
template.subject = '' template.subject = ''
template.use_html = false template.use_html = false
template.response = '' template.response = ''
template.response_html = ''
} }
</script> </script>

View File

@@ -57,7 +57,7 @@
:description="activeTab.description" :description="activeTab.description"
/> />
<PaymentSettings <PaymentSettings
v-else-if="activeTab.label === 'Payment'" v-else-if="activeTab.label === 'Payment Gateway'"
:label="activeTab.label" :label="activeTab.label"
:description="activeTab.description" :description="activeTab.description"
:data="data" :data="data"
@@ -176,7 +176,7 @@ const tabsStructure = computed(() => {
hideLabel: true, hideLabel: true,
items: [ items: [
{ {
label: 'Payment', label: 'Payment Gateway',
icon: 'DollarSign', icon: 'DollarSign',
description: description:
'Configure the payment gateway and other payment related settings', 'Configure the payment gateway and other payment related settings',

View File

@@ -67,7 +67,7 @@ const paymentGateway = createResource({
}) })
const arrangeFields = (fields) => { const arrangeFields = (fields) => {
fields = data.fields.sort((a, b) => { fields = fields.sort((a, b) => {
if (a.type === 'Upload' && b.type !== 'Upload') { if (a.type === 'Upload' && b.type !== 'Upload') {
return 1 return 1
} else if (a.type !== 'Upload' && b.type === 'Upload') { } else if (a.type !== 'Upload' && b.type === 'Upload') {

View File

@@ -153,6 +153,11 @@
doctype="Email Template" doctype="Email Template"
:label="__('Email Template')" :label="__('Email Template')"
v-model="batch.confirmation_email_template" v-model="batch.confirmation_email_template"
:onCreate="
(value, close) => {
openSettings('Email Templates', close)
}
"
/> />
</div> </div>
<div class="space-y-5"> <div class="space-y-5">