feat: WhatsApp enhanced templates front end changes (#12117)

Part of the https://github.com/chatwoot/chatwoot/pull/11997

Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
This commit is contained in:
Muhsin Keloth
2025-08-12 18:53:19 +05:30
committed by GitHub
parent dbb164a37d
commit 5c560c7628
15 changed files with 1635 additions and 436 deletions

View File

@@ -1,263 +0,0 @@
export const templates = [
{
name: 'sample_flight_confirmation',
status: 'approved',
category: 'TICKET_UPDATE',
language: 'pt_BR',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{ type: 'HEADER', format: 'DOCUMENT' },
{
text: 'Esta é a sua confirmação de voo para {{1}}-{{2}} em {{3}}.',
type: 'BODY',
},
{
text: 'Esta mensagem é de uma empresa não verificada.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_issue_resolution',
status: 'approved',
category: 'ISSUE_RESOLUTION',
language: 'pt_BR',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text: 'Oi, {{1}}. Nós conseguimos resolver o problema que você estava enfrentando?',
type: 'BODY',
},
{
text: 'Esta mensagem é de uma empresa não verificada.',
type: 'FOOTER',
},
{
type: 'BUTTONS',
buttons: [
{ text: 'Sim', type: 'QUICK_REPLY' },
{ text: 'Não', type: 'QUICK_REPLY' },
],
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_issue_resolution',
status: 'approved',
category: 'ISSUE_RESOLUTION',
language: 'es',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text: 'Hola, {{1}}. ¿Pudiste solucionar el problema que tenías?',
type: 'BODY',
},
{
text: 'Este mensaje proviene de un negocio no verificado.',
type: 'FOOTER',
},
{
type: 'BUTTONS',
buttons: [
{ text: 'Sí', type: 'QUICK_REPLY' },
{ text: 'No', type: 'QUICK_REPLY' },
],
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_issue_resolution',
status: 'approved',
category: 'ISSUE_RESOLUTION',
language: 'id',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text: 'Halo {{1}}, apakah kami bisa mengatasi masalah yang sedang Anda hadapi?',
type: 'BODY',
},
{
text: 'Pesan ini berasal dari bisnis yang tidak terverifikasi.',
type: 'FOOTER',
},
{
type: 'BUTTONS',
buttons: [
{ text: 'Ya', type: 'QUICK_REPLY' },
{ text: 'Tidak', type: 'QUICK_REPLY' },
],
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_shipping_confirmation',
status: 'approved',
category: 'SHIPPING_UPDATE',
language: 'pt_BR',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text: 'Seu pacote foi enviado. Ele será entregue em {{1}} dias úteis.',
type: 'BODY',
},
{
text: 'Esta mensagem é de uma empresa não verificada.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_shipping_confirmation',
status: 'approved',
category: 'SHIPPING_UPDATE',
language: 'id',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text: 'Paket Anda sudah dikirim. Paket akan sampai dalam {{1}} hari kerja.',
type: 'BODY',
},
{
text: 'Pesan ini berasal dari bisnis yang tidak terverifikasi.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_shipping_confirmation',
status: 'approved',
category: 'SHIPPING_UPDATE',
language: 'es',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text: 'ó tu paquete. La entrega se realizará en {{1}} dí.',
type: 'BODY',
},
{
text: 'Este mensaje proviene de un negocio no verificado.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_flight_confirmation',
status: 'approved',
category: 'TICKET_UPDATE',
language: 'id',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{ type: 'HEADER', format: 'DOCUMENT' },
{
text: 'Ini merupakan konfirmasi penerbangan Anda untuk {{1}}-{{2}} di {{3}}.',
type: 'BODY',
},
{
text: 'Pesan ini berasal dari bisnis yang tidak terverifikasi.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_issue_resolution',
status: 'approved',
category: 'ISSUE_RESOLUTION',
language: 'en_US',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text: 'Hi {{1}}, were we able to solve the issue that you were facing?',
type: 'BODY',
},
{ text: 'This message is from an unverified business.', type: 'FOOTER' },
{
type: 'BUTTONS',
buttons: [
{ text: 'Yes', type: 'QUICK_REPLY' },
{ text: 'No', type: 'QUICK_REPLY' },
],
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_flight_confirmation',
status: 'approved',
category: 'TICKET_UPDATE',
language: 'es',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{ type: 'HEADER', format: 'DOCUMENT' },
{
text: 'Confirmamos tu vuelo a {{1}}-{{2}} para el {{3}}.',
type: 'BODY',
},
{
text: 'Este mensaje proviene de un negocio no verificado.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
{
name: 'sample_flight_confirmation',
status: 'approved',
category: 'TICKET_UPDATE',
language: 'en_US',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{ type: 'HEADER', format: 'DOCUMENT' },
{
text: 'This is your flight confirmation for {{1}}-{{2}} on {{3}}.',
type: 'BODY',
},
{ text: 'This message is from an unverified business.', type: 'FOOTER' },
],
rejected_reason: 'NONE',
},
{
name: 'sample_shipping_confirmation',
status: 'approved',
category: 'SHIPPING_UPDATE',
language: 'en_US',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
text: 'Your package has been shipped. It will be delivered in {{1}} business days.',
type: 'BODY',
},
{ text: 'This message is from an unverified business.', type: 'FOOTER' },
],
rejected_reason: 'NONE',
},
{
name: 'no_variable_template',
status: 'approved',
category: 'TICKET_UPDATE',
language: 'pt_BR',
namespace: 'ed41a221_133a_4558_a1d6_192960e3aee9',
components: [
{
type: 'HEADER',
format: 'DOCUMENT',
},
{
text: 'This is a test whatsapp template',
type: 'BODY',
},
{
text: 'Esta mensagem é de uma empresa não verificada.',
type: 'FOOTER',
},
],
rejected_reason: 'NONE',
},
];

View File

@@ -1,61 +0,0 @@
import { shallowMount } from '@vue/test-utils';
import TemplateParser from '../../../../dashboard/components/widgets/conversation/WhatsappTemplates/TemplateParser.vue';
import { templates } from './fixtures';
import { nextTick } from 'vue';
const config = {
global: {
stubs: {
NextButton: { template: '<button />' },
WootInput: { template: '<input />' },
},
},
};
describe('#WhatsAppTemplates', () => {
it('returns all variables from a template string', async () => {
const wrapper = shallowMount(TemplateParser, {
...config,
props: { template: templates[0] },
});
await nextTick();
expect(wrapper.vm.variables).toEqual(['{{1}}', '{{2}}', '{{3}}']);
});
it('returns no variables from a template string if it does not contain variables', async () => {
const wrapper = shallowMount(TemplateParser, {
...config,
props: { template: templates[12] },
});
await nextTick();
expect(wrapper.vm.variables).toBeNull();
});
it('returns the body of a template', async () => {
const wrapper = shallowMount(TemplateParser, {
...config,
props: { template: templates[1] },
});
await nextTick();
const expectedOutput =
templates[1].components.find(i => i.type === 'BODY')?.text || '';
expect(wrapper.vm.templateString).toEqual(expectedOutput);
});
it('generates the templates from variable input', async () => {
const wrapper = shallowMount(TemplateParser, {
...config,
props: { template: templates[0] },
});
await nextTick();
// Instead of using `setData`, directly modify the `processedParams` using the component's logic
await wrapper.vm.$nextTick();
wrapper.vm.processedParams = { 1: 'abc', 2: 'xyz', 3: 'qwerty' };
await wrapper.vm.$nextTick();
const expectedOutput =
'Esta é a sua confirmação de voo para abc-xyz em qwerty.';
expect(wrapper.vm.processedString).toEqual(expectedOutput);
});
});