fix: Edit email template
(cherry picked from commit dd3d297dab4fb2380889b4cec39aeba53f102896)
This commit is contained in:
parent
d2527da5a4
commit
ab6ea90c66
@ -1,3 +1,255 @@
|
|||||||
<template>
|
<template>
|
||||||
Edit
|
<div class="flex h-full flex-col gap-6 p-8 text-ink-gray-8">
|
||||||
</template>
|
<!-- Header -->
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div class="flex gap-1 w-9/12">
|
||||||
|
<div
|
||||||
|
class="flex text-ink-gray-7 gap-2 cursor-pointer"
|
||||||
|
:tabindex="0"
|
||||||
|
@keydown.enter="() => emit('updateStep', 'template-list')"
|
||||||
|
@click="() => emit('updateStep', 'template-list')"
|
||||||
|
>
|
||||||
|
<FeatherIcon name="chevron-left" class="size-5" />
|
||||||
|
<span class="text-xl font-semibold">{{ __(template.name) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex item-center space-x-2 w-3/12 justify-end">
|
||||||
|
<Button
|
||||||
|
:label="__('Update')"
|
||||||
|
icon-left="plus"
|
||||||
|
variant="solid"
|
||||||
|
:disabled="!dirty"
|
||||||
|
:loading="renameDoc.loading || templates.setValue.loading"
|
||||||
|
@click="updateTemplate"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Fields -->
|
||||||
|
<div class="flex flex-1 flex-col gap-4 overflow-y-auto">
|
||||||
|
<div
|
||||||
|
class="flex justify-between items-center cursor-pointer border-b py-3"
|
||||||
|
@click="() => (template.enabled = !template.enabled)"
|
||||||
|
>
|
||||||
|
<div class="text-base text-ink-gray-7">{{ __('Enabled') }}</div>
|
||||||
|
<Switch v-model="template.enabled" @click.stop />
|
||||||
|
</div>
|
||||||
|
<div class="flex sm:flex-row flex-col gap-4">
|
||||||
|
<div class="flex-1">
|
||||||
|
<FormControl
|
||||||
|
size="md"
|
||||||
|
v-model="template.name"
|
||||||
|
:placeholder="__('Payment Reminder')"
|
||||||
|
:label="__('Name')"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<FormControl
|
||||||
|
type="select"
|
||||||
|
size="md"
|
||||||
|
v-model="template.reference_doctype"
|
||||||
|
:label="__('For')"
|
||||||
|
:options="[
|
||||||
|
{
|
||||||
|
label: __('Deal'),
|
||||||
|
value: 'CRM Deal',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Lead'),
|
||||||
|
value: 'CRM Lead',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:placeholder="__('Deal')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<FormControl
|
||||||
|
ref="subjectRef"
|
||||||
|
size="md"
|
||||||
|
v-model="template.subject"
|
||||||
|
:label="__('Subject')"
|
||||||
|
:placeholder="__('Payment Reminder from Frappé - (#{{ name }})')"
|
||||||
|
:required="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="border-t pt-4">
|
||||||
|
<FormControl
|
||||||
|
type="select"
|
||||||
|
size="md"
|
||||||
|
v-model="template.content_type"
|
||||||
|
:label="__('Content Type')"
|
||||||
|
default="Rich Text"
|
||||||
|
:options="['Rich Text', 'HTML']"
|
||||||
|
:placeholder="__('Rich Text')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<FormControl
|
||||||
|
v-if="template.content_type === 'HTML'"
|
||||||
|
size="md"
|
||||||
|
type="textarea"
|
||||||
|
:label="__('Content')"
|
||||||
|
:required="true"
|
||||||
|
ref="content"
|
||||||
|
:rows="10"
|
||||||
|
v-model="template.response_html"
|
||||||
|
:placeholder="
|
||||||
|
__(
|
||||||
|
'<p>Dear {{ lead_name }},</p>\n\n<p>This is a reminder for the payment of {{ grand_total }}.</p>\n\n<p>Thanks,</p>\n<p>Frappé</p>',
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
<div v-else>
|
||||||
|
<div class="mb-1.5 text-base text-ink-gray-5">
|
||||||
|
{{ __('Content') }}
|
||||||
|
<span class="text-ink-red-3">*</span>
|
||||||
|
</div>
|
||||||
|
<TextEditor
|
||||||
|
ref="content"
|
||||||
|
editor-class="!prose-sm overflow-auto min-h-[180px] max-h-80 py-1.5 px-2 rounded border border-[--surface-gray-2] bg-surface-gray-2 placeholder-ink-gray-4 hover:border-outline-gray-modals hover:bg-surface-gray-3 hover:shadow-sm focus:bg-surface-white focus:border-outline-gray-4 focus:shadow-sm focus:ring-0 focus-visible:ring-2 focus-visible:ring-outline-gray-3 text-ink-gray-8 transition-colors"
|
||||||
|
:bubbleMenu="true"
|
||||||
|
:content="template.response"
|
||||||
|
@change="(val) => (template.response = val)"
|
||||||
|
:placeholder="
|
||||||
|
__(
|
||||||
|
'Dear {{ lead_name }}, \n\nThis is a reminder for the payment of {{ grand_total }}. \n\nThanks, \nFrappé',
|
||||||
|
)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="errorMessage">
|
||||||
|
<ErrorMessage :message="__(errorMessage)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
TextEditor,
|
||||||
|
FormControl,
|
||||||
|
Switch,
|
||||||
|
toast,
|
||||||
|
call,
|
||||||
|
createResource,
|
||||||
|
} from 'frappe-ui'
|
||||||
|
import { computed, inject, onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
templateData: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['updateStep'])
|
||||||
|
const errorMessage = ref('')
|
||||||
|
|
||||||
|
const templates = inject('templates')
|
||||||
|
const template = ref({})
|
||||||
|
|
||||||
|
const updateTemplate = async () => {
|
||||||
|
errorMessage.value = ''
|
||||||
|
if (!template.value.name) {
|
||||||
|
errorMessage.value = __('Name is required')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!template.value.subject) {
|
||||||
|
errorMessage.value = __('Subject is required')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (template.value.content_type === 'Rich Text' && !template.value.response) {
|
||||||
|
errorMessage.value = __('Content is required')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (template.value.content_type === 'HTML' && !template.value.response_html) {
|
||||||
|
errorMessage.value = __('Content is required')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
template.value.use_html = template.value.content_type === 'HTML'
|
||||||
|
|
||||||
|
const old = {
|
||||||
|
...props.templateData,
|
||||||
|
use_html: Boolean(props.templateData.use_html),
|
||||||
|
}
|
||||||
|
const newEmailTemplate = {
|
||||||
|
...template.value,
|
||||||
|
use_html: Boolean(template.value.use_html),
|
||||||
|
}
|
||||||
|
|
||||||
|
delete newEmailTemplate.content_type
|
||||||
|
|
||||||
|
const nameChanged = old.name !== newEmailTemplate.name
|
||||||
|
delete old.name
|
||||||
|
delete newEmailTemplate.name
|
||||||
|
|
||||||
|
const otherFieldChanged =
|
||||||
|
JSON.stringify(old) !== JSON.stringify(newEmailTemplate)
|
||||||
|
const values = newEmailTemplate
|
||||||
|
|
||||||
|
if (!nameChanged && !otherFieldChanged) return
|
||||||
|
|
||||||
|
let name = props.templateData.name
|
||||||
|
|
||||||
|
if (nameChanged) {
|
||||||
|
name = await renameDoc.fetch()
|
||||||
|
if (!otherFieldChanged) {
|
||||||
|
emit('updateStep', 'template-list')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (otherFieldChanged) {
|
||||||
|
templates.setValue.submit(
|
||||||
|
{ ...values, name },
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
emit('updateStep', 'template-list')
|
||||||
|
toast.success(__('Template updated successfully'))
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
errorMessage.value =
|
||||||
|
error.messages[0] || __('Failed to update template')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const dirty = computed(() => {
|
||||||
|
return (
|
||||||
|
template.value.name !== props.templateData.name ||
|
||||||
|
template.value.reference_doctype !== props.templateData.reference_doctype ||
|
||||||
|
template.value.subject !== props.templateData.subject ||
|
||||||
|
template.value.response_html !== props.templateData.response_html ||
|
||||||
|
template.value.response !== props.templateData.response ||
|
||||||
|
template.value.use_html !== props.templateData.use_html ||
|
||||||
|
Boolean(template.value.enabled) !== Boolean(props.templateData.enabled)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const renameDoc = createResource({
|
||||||
|
url: 'frappe.client.rename_doc',
|
||||||
|
method: 'POST',
|
||||||
|
makeParams() {
|
||||||
|
return {
|
||||||
|
doctype: 'Email Template',
|
||||||
|
old_name: props.templateData.name,
|
||||||
|
new_name: template.value.name,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
templates.reload()
|
||||||
|
toast.success(__('Template renamed successfully'))
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
errorMessage.value = error.messages[0] || __('Failed to rename template')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
template.value = { ...props.templateData }
|
||||||
|
template.value.content_type = template.value.use_html ? 'HTML' : 'Rich Text'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
/>
|
/>
|
||||||
<EditEmailTemplate
|
<EditEmailTemplate
|
||||||
v-else-if="step === 'edit-template'"
|
v-else-if="step === 'edit-template'"
|
||||||
:data="templateData"
|
:templateData="template"
|
||||||
@updateStep="updateStep"
|
@updateStep="updateStep"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
@ -19,7 +19,7 @@ import { createListResource } from 'frappe-ui'
|
|||||||
import { provide, ref } from 'vue'
|
import { provide, ref } from 'vue'
|
||||||
|
|
||||||
const step = ref('template-list')
|
const step = ref('template-list')
|
||||||
const templateData = ref(null)
|
const template = ref(null)
|
||||||
|
|
||||||
const templates = createListResource({
|
const templates = createListResource({
|
||||||
type: 'list',
|
type: 'list',
|
||||||
@ -46,6 +46,6 @@ provide('templates', templates)
|
|||||||
|
|
||||||
function updateStep(newStep, data) {
|
function updateStep(newStep, data) {
|
||||||
step.value = newStep
|
step.value = newStep
|
||||||
templateData.value = data
|
template.value = data
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -143,6 +143,7 @@ import {
|
|||||||
Switch,
|
Switch,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
FeatherIcon,
|
FeatherIcon,
|
||||||
|
toast,
|
||||||
} from 'frappe-ui'
|
} from 'frappe-ui'
|
||||||
import { ref, computed, inject, h } from 'vue'
|
import { ref, computed, inject, h } from 'vue'
|
||||||
|
|
||||||
@ -172,10 +173,26 @@ const templatesList = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
function toggleEmailTemplate(template) {
|
function toggleEmailTemplate(template) {
|
||||||
templates.setValue.submit({
|
templates.setValue.submit(
|
||||||
name: template.name,
|
{
|
||||||
enabled: template.enabled ? 1 : 0,
|
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) {
|
function deleteTemplate(template) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user