Skip to content

Commit e18f70f

Browse files
committed
feat: update visual template selection flow in campaigns
1 parent fa27550 commit e18f70f

3 files changed

Lines changed: 102 additions & 52 deletions

File tree

frontend/src/components/Editor.vue

Lines changed: 100 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,25 @@
3737
</b-select>
3838
</b-field>
3939

40-
<b-field v-else :label="$t('globals.terms.copyVisualTemplate')" label-position="on-border">
41-
<b-select :placeholder="$t('globals.terms.none')" v-model="templateId" name="template" :disabled="disabled" class="copy-visual-template-list">
42-
<option :value="null" key="none" v-if="templateId !== null">
43-
{{ $t('globals.terms.none') }}
44-
</option>
45-
<template v-for="t in applicableTemplates">
46-
<option v-if="t.type === 'campaign' || t.type === 'campaign_visual'" :value="t.id" :key="t.id">
47-
{{ t.name }}
48-
</option>
49-
</template>
50-
</b-select>
51-
</b-field>
40+
<div v-else>
41+
<b-button v-if="!isVisualTplSelector" @click="onShowVisualTplSelector" type="is-ghost" icon-left="file-find-outline" data-cy="btn-select-visual-tpl">
42+
{{ $t('globals.terms.copyVisualTemplate') }}
43+
</b-button>
44+
45+
<b-field v-else :label="$t('globals.terms.copyVisualTemplate')" label-position="on-border">
46+
<b-select :placeholder="$t('globals.terms.none')" v-model="visualTemplateId" name="template" :disabled="disabled" class="copy-visual-template-list">
47+
<template v-for="t in applicableTemplates">
48+
<option :value="t.id" :key="t.id">
49+
{{ t.name }}
50+
</option>
51+
</template>
52+
</b-select>
53+
54+
<b-button :disabled="isVisualTplApplied" class="ml-3" @click="onApplyVisualTpl" type="is-primary" icon-left="content-save-outline" data-cy="btn-save-visual-tpl">
55+
{{ $t('globals.terms.apply') }}
56+
</b-button>
57+
</b-field>
58+
</div>
5259
</div>
5360
<div class="column is- has-text-right">
5461
<b-button @click="onTogglePreview" type="is-primary" icon-left="file-find-outline" data-cy="btn-preview">
@@ -120,8 +127,11 @@ export default {
120127
data() {
121128
return {
122129
isPreviewing: false,
130+
isVisualTplSelector: false,
131+
isVisualTplApplied: false,
123132
contentType: this.$props.value.contentType,
124133
templateId: '',
134+
visualTemplateId: ''
125135
};
126136
},
127137
@@ -151,6 +161,8 @@ export default {
151161
152162
convertContentType(to, from) {
153163
let body;
164+
let skip = false;
165+
154166
if ((from === 'richtext' || from === 'html') && to === 'plain') {
155167
// richtext, html => plain
156168
@@ -168,20 +180,31 @@ export default {
168180
// richtext => html
169181
body = this.beautifyHTML(this.computedValue.body);
170182
} else if (from === 'markdown' && (to === 'richtext' || to === 'html')) {
183+
// Skip default update.
184+
skip = true;
171185
// markdown => richtext, html.
172186
this.$api.convertCampaignContent({
173187
id: 1, body: this.computedValue.body, from, to,
174188
}).then((data) => {
175189
this.$nextTick(() => {
176190
this.computedValue.body = this.beautifyHTML(data.trim());
191+
this.computedValue.bodySource = null;
177192
});
178193
});
179194
}
180195
181-
// Update the current body.
182-
this.$nextTick(() => {
183-
this.computedValue.body = body;
184-
});
196+
if (!skip) {
197+
// Update the current body.
198+
this.$nextTick(() => {
199+
this.computedValue.body = body;
200+
201+
// If not visual editor then set bodySource to null
202+
// this makes sure previous bodySource is not used when switching to visual editor.
203+
if (to !== 'visual') {
204+
this.computedValue.bodySource = null;
205+
}
206+
});
207+
}
185208
186209
// Reset template ID only if its converted to or from visual template.
187210
if (to === 'visual' || from === 'visual') {
@@ -233,6 +256,53 @@ export default {
233256
234257
return out.join('\n').replace(/\n\s*\n\s*\n/g, '\n\n');
235258
},
259+
260+
onShowVisualTplSelector() {
261+
this.isVisualTplSelector = true;
262+
this.setDefaultTemplate();
263+
},
264+
265+
onApplyVisualTpl() {
266+
this.$utils.confirm(
267+
this.$t('campaigns.confirmApplyVisualTemplate'),
268+
() => {
269+
let found = false;
270+
this.templates.forEach((t) => {
271+
if (t.id === this.visualTemplateId) {
272+
found = true;
273+
this.computedValue.body = t.body;
274+
this.computedValue.bodySource = t.bodySource;
275+
276+
// Deplay update so that applied template is propogated to visual editor
277+
// and it doesn't enable the apply button again. Delay here is arbitrary.
278+
setTimeout(() => {
279+
this.isVisualTplApplied = true;
280+
}, 250);
281+
}
282+
});
283+
284+
if (!found) {
285+
this.computedValue.body = '';
286+
this.computedValue.bodySource = null;
287+
288+
// Deplay update so that applied template is propogated to visual editor
289+
// and it doesn't enable the apply button again. Delay here is arbitrary.
290+
setTimeout(() => {
291+
this.isVisualTplApplied = true;
292+
}, 250);
293+
}
294+
}
295+
);
296+
},
297+
298+
setDefaultTemplate() {
299+
if (this.computedValue.contentType === 'visual') {
300+
this.visualTemplateId = this.applicableTemplates[0]?.id || null;
301+
} else {
302+
const defaultTemplate = this.applicableTemplates.find((t) => t.isDefault === true);
303+
this.templateId = defaultTemplate?.id || this.applicableTemplates[0]?.id || null;
304+
}
305+
}
236306
},
237307
238308
mounted() {
@@ -262,9 +332,8 @@ export default {
262332
applicableTemplates() {
263333
if (this.computedValue.contentType === 'visual') {
264334
return this.templates.filter((t) => t.type === 'campaign_visual');
265-
} else {
266-
return this.templates.filter((t) => t.type === 'campaign');
267335
}
336+
return this.templates.filter((t) => t.type === 'campaign');
268337
},
269338
},
270339
@@ -278,45 +347,25 @@ export default {
278347
this.convertContentType(to, from);
279348
},
280349
281-
applicableTemplates(to) {
282-
if (this.computedValue.contentType !== 'visual') {
283-
const ctps = this.templates.filter((t) => t.type === 'campaign')
284-
if (!ctps.find(t => t.id === this.templateId)) {
285-
const defaultTemplate = ctps.find(t => t.isDefault === true);
286-
this.templateId = defaultTemplate?.id || ctps[0]?.id || null;
287-
}
288-
}
350+
applicableTemplates() {
351+
this.setDefaultTemplate();
289352
},
290353
291-
templateId(to, from) {
354+
templateId(to) {
292355
if (this.computedValue.templateId === to) {
293356
return;
294357
}
358+
this.computedValue.templateId = to;
359+
},
295360
296-
if (this.computedValue.contentType === 'visual') {
297-
this.$utils.confirm(
298-
this.$t('campaigns.confirmApplyVisualTemplate'),
299-
() => {
300-
this.computedValue.templateId = to;
301-
302-
if (!to) {
303-
this.computedValue.body = '';
304-
this.computedValue.bodySource = null;
305-
} else {
306-
this.templates.forEach((t) => {
307-
if (t.id === to) {
308-
this.computedValue.body = t.body;
309-
this.computedValue.bodySource = t.bodySource;
310-
}
311-
});
312-
}
313-
},
314-
() => {
315-
this.templateId = from;
316-
},
317-
);
318-
} else {
319-
this.computedValue.templateId = to;
361+
// eslint-disable-next-line func-names
362+
'computedValue.bodySource': function (to, from) {
363+
this.isVisualTplApplied = !(JSON.stringify(to) !== JSON.stringify(from));
364+
},
365+
366+
visualTemplateId(to, from) {
367+
if (from && from !== to) {
368+
this.isVisualTplApplied = false;
320369
}
321370
},
322371
},

frontend/src/views/Templates.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
<b-icon icon="file-multiple-outline" size="is-small" />
7676
</b-tooltip>
7777
</a>
78-
<a v-if="!props.row.isDefault && props.row.type !== 'tx'" href="#"
78+
<a v-if="!props.row.isDefault && props.row.type === 'campaign'" href="#"
7979
@click.prevent="$utils.confirm(null, () => makeTemplateDefault(props.row))" data-cy="btn-set-default"
8080
:aria-label="$t('templates.makeDefault')">
8181
<b-tooltip :label="$t('templates.makeDefault')" type="is-dark">

i18n/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@
245245
"globals.terms.user": "User | Users",
246246
"globals.terms.users": "Users",
247247
"globals.terms.year": "Year | Years",
248+
"globals.terms.apply": "Apply",
248249
"import.alreadyRunning": "An import is already running. Wait for it to finish or stop it before trying again.",
249250
"import.blocklist": "Blocklist",
250251
"import.csvDelim": "CSV delimiter",

0 commit comments

Comments
 (0)