feat(scheduled-messages): add WhatsApp templates (#205)
* feat(scheduled-messages): add WhatsApp templates * fix: update inboxId prop type to accept both Number and String; localize template labels in conversation.json
This commit is contained in:
parent
e84875ca04
commit
da466c88a6
@ -109,6 +109,8 @@ const templateName = computed(() => {
|
|||||||
return templateParams.name || templateParams.id;
|
return templateParams.name || templateParams.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const hasTemplate = computed(() => Boolean(templateName.value));
|
||||||
|
|
||||||
const attachment = computed(() => props.scheduledMessage?.attachment);
|
const attachment = computed(() => props.scheduledMessage?.attachment);
|
||||||
const attachmentName = computed(() => attachment.value?.filename);
|
const attachmentName = computed(() => attachment.value?.filename);
|
||||||
const attachmentUrl = computed(() => attachment.value?.file_url);
|
const attachmentUrl = computed(() => attachment.value?.file_url);
|
||||||
@ -247,6 +249,20 @@ watch(previewContent, () => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="hasTemplate"
|
||||||
|
class="flex items-center gap-1.5 text-xs text-n-slate-11"
|
||||||
|
>
|
||||||
|
<Icon icon="i-lucide-zap" class="size-3 shrink-0" />
|
||||||
|
<span class="truncate">
|
||||||
|
{{
|
||||||
|
t('SCHEDULED_MESSAGES.ITEM.TEMPLATE_LABEL', {
|
||||||
|
name: templateName,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-if="shouldShowAttachmentLine"
|
v-if="shouldShowAttachmentLine"
|
||||||
class="flex items-center gap-1.5 text-xs text-n-slate-11"
|
class="flex items-center gap-1.5 text-xs text-n-slate-11"
|
||||||
|
|||||||
@ -35,6 +35,10 @@ const props = defineProps({
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
sendButtonLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['sendMessage', 'resetTemplate', 'back']);
|
const emit = defineEmits(['sendMessage', 'resetTemplate', 'back']);
|
||||||
@ -43,6 +47,12 @@ const { t } = useI18n();
|
|||||||
|
|
||||||
const processedParams = ref({});
|
const processedParams = ref({});
|
||||||
|
|
||||||
|
const sendButtonText = computed(() => {
|
||||||
|
return (
|
||||||
|
props.sendButtonLabel || t('WHATSAPP_TEMPLATES.PARSER.SEND_MESSAGE_LABEL')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const languageLabel = computed(() => {
|
const languageLabel = computed(() => {
|
||||||
return `${t('WHATSAPP_TEMPLATES.PARSER.LANGUAGE')}: ${props.template.language || DEFAULT_LANGUAGE}`;
|
return `${t('WHATSAPP_TEMPLATES.PARSER.LANGUAGE')}: ${props.template.language || DEFAULT_LANGUAGE}`;
|
||||||
});
|
});
|
||||||
@ -305,6 +315,7 @@ defineExpose({
|
|||||||
:go-back="goBack"
|
:go-back="goBack"
|
||||||
:is-valid="!v$.$invalid"
|
:is-valid="!v$.$invalid"
|
||||||
:disabled="isFormInvalid"
|
:disabled="isFormInvalid"
|
||||||
|
:send-button-text="sendButtonText"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -12,9 +12,13 @@ export default {
|
|||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
inboxId: {
|
inboxId: {
|
||||||
type: Number,
|
type: [Number, String],
|
||||||
default: undefined,
|
default: undefined,
|
||||||
},
|
},
|
||||||
|
sendButtonLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ['onSend', 'cancel', 'update:show'],
|
emits: ['onSend', 'cancel', 'update:show'],
|
||||||
data() {
|
data() {
|
||||||
@ -39,6 +43,13 @@ export default {
|
|||||||
: this.$t('WHATSAPP_TEMPLATES.MODAL.SUBTITLE');
|
: this.$t('WHATSAPP_TEMPLATES.MODAL.SUBTITLE');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
show(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.selectedWaTemplate = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
pickTemplate(template) {
|
pickTemplate(template) {
|
||||||
this.selectedWaTemplate = template;
|
this.selectedWaTemplate = template;
|
||||||
@ -71,6 +82,7 @@ export default {
|
|||||||
<WhatsAppTemplateReply
|
<WhatsAppTemplateReply
|
||||||
v-else
|
v-else
|
||||||
:template="selectedWaTemplate"
|
:template="selectedWaTemplate"
|
||||||
|
:send-button-label="sendButtonLabel"
|
||||||
@reset-template="onResetTemplate"
|
@reset-template="onResetTemplate"
|
||||||
@send-message="onSendMessage"
|
@send-message="onSendMessage"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -2,11 +2,15 @@
|
|||||||
import WhatsAppTemplateParser from 'dashboard/components-next/whatsapp/WhatsAppTemplateParser.vue';
|
import WhatsAppTemplateParser from 'dashboard/components-next/whatsapp/WhatsAppTemplateParser.vue';
|
||||||
import NextButton from 'dashboard/components-next/button/Button.vue';
|
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||||
|
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
template: {
|
template: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
|
sendButtonLabel: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['sendMessage', 'resetTemplate']);
|
const emit = defineEmits(['sendMessage', 'resetTemplate']);
|
||||||
@ -23,11 +27,14 @@ const handleResetTemplate = () => {
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<WhatsAppTemplateParser
|
<WhatsAppTemplateParser
|
||||||
:template="template"
|
:template="props.template"
|
||||||
|
:send-button-label="props.sendButtonLabel"
|
||||||
@send-message="handleSendMessage"
|
@send-message="handleSendMessage"
|
||||||
@reset-template="handleResetTemplate"
|
@reset-template="handleResetTemplate"
|
||||||
>
|
>
|
||||||
<template #actions="{ sendMessage, resetTemplate, disabled }">
|
<template
|
||||||
|
#actions="{ sendMessage, resetTemplate, disabled, sendButtonText }"
|
||||||
|
>
|
||||||
<footer class="flex gap-2 justify-end">
|
<footer class="flex gap-2 justify-end">
|
||||||
<NextButton
|
<NextButton
|
||||||
faded
|
faded
|
||||||
@ -38,7 +45,7 @@ const handleResetTemplate = () => {
|
|||||||
/>
|
/>
|
||||||
<NextButton
|
<NextButton
|
||||||
type="button"
|
type="button"
|
||||||
:label="$t('WHATSAPP_TEMPLATES.PARSER.SEND_MESSAGE_LABEL')"
|
:label="sendButtonText"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@click="sendMessage"
|
@click="sendMessage"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -410,6 +410,7 @@
|
|||||||
"SCHEDULED_FOR": "Scheduled for {time}",
|
"SCHEDULED_FOR": "Scheduled for {time}",
|
||||||
"NO_SCHEDULE": "No schedule",
|
"NO_SCHEDULE": "No schedule",
|
||||||
"TEMPLATE_PREVIEW": "Template: {name}",
|
"TEMPLATE_PREVIEW": "Template: {name}",
|
||||||
|
"TEMPLATE_LABEL": "Template: {name}",
|
||||||
"ATTACHMENT_PREVIEW": "Attachment: {filename}",
|
"ATTACHMENT_PREVIEW": "Attachment: {filename}",
|
||||||
"ATTACHMENT_LABEL": "Attachment: {filename}",
|
"ATTACHMENT_LABEL": "Attachment: {filename}",
|
||||||
"EMPTY_PREVIEW": "No content",
|
"EMPTY_PREVIEW": "No content",
|
||||||
@ -426,6 +427,9 @@
|
|||||||
"ATTACHMENT_LABEL": "Attachment",
|
"ATTACHMENT_LABEL": "Attachment",
|
||||||
"ATTACHMENT_ADD": "Attach file",
|
"ATTACHMENT_ADD": "Attach file",
|
||||||
"ATTACHMENT_CURRENT": "Current attachment: {filename}",
|
"ATTACHMENT_CURRENT": "Current attachment: {filename}",
|
||||||
|
"TEMPLATE_SELECT": "Use template",
|
||||||
|
"TEMPLATE_SELECTED": "Template: {name}",
|
||||||
|
"TEMPLATE_ACTION": "Schedule message",
|
||||||
"CANCEL": "Cancel",
|
"CANCEL": "Cancel",
|
||||||
"SAVE_DRAFT": "Save as draft",
|
"SAVE_DRAFT": "Save as draft",
|
||||||
"SCHEDULE": "Schedule"
|
"SCHEDULE": "Schedule"
|
||||||
|
|||||||
@ -408,7 +408,8 @@
|
|||||||
"ITEM": {
|
"ITEM": {
|
||||||
"SCHEDULED_FOR": "Agendada para {time}",
|
"SCHEDULED_FOR": "Agendada para {time}",
|
||||||
"NO_SCHEDULE": "Sem agendamento",
|
"NO_SCHEDULE": "Sem agendamento",
|
||||||
"TEMPLATE_PREVIEW": "Template: {name}",
|
"TEMPLATE_PREVIEW": "Modelo: {name}",
|
||||||
|
"TEMPLATE_LABEL": "Modelo: {name}",
|
||||||
"ATTACHMENT_PREVIEW": "Anexo: {filename}",
|
"ATTACHMENT_PREVIEW": "Anexo: {filename}",
|
||||||
"ATTACHMENT_LABEL": "Anexo: {filename}",
|
"ATTACHMENT_LABEL": "Anexo: {filename}",
|
||||||
"EMPTY_PREVIEW": "Sem conteúdo",
|
"EMPTY_PREVIEW": "Sem conteúdo",
|
||||||
@ -425,6 +426,9 @@
|
|||||||
"ATTACHMENT_LABEL": "Anexo",
|
"ATTACHMENT_LABEL": "Anexo",
|
||||||
"ATTACHMENT_ADD": "Anexar arquivo",
|
"ATTACHMENT_ADD": "Anexar arquivo",
|
||||||
"ATTACHMENT_CURRENT": "Anexo atual: {filename}",
|
"ATTACHMENT_CURRENT": "Anexo atual: {filename}",
|
||||||
|
"TEMPLATE_SELECT": "Usar modelo",
|
||||||
|
"TEMPLATE_SELECTED": "Modelo: {name}",
|
||||||
|
"TEMPLATE_ACTION": "Agendar mensagem",
|
||||||
"CANCEL": "Cancelar",
|
"CANCEL": "Cancelar",
|
||||||
"SAVE_DRAFT": "Salvar como rascunho",
|
"SAVE_DRAFT": "Salvar como rascunho",
|
||||||
"SCHEDULE": "Agendar"
|
"SCHEDULE": "Agendar"
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import DropdownContainer from 'next/dropdown-menu/base/DropdownContainer.vue';
|
|||||||
import DropdownBody from 'next/dropdown-menu/base/DropdownBody.vue';
|
import DropdownBody from 'next/dropdown-menu/base/DropdownBody.vue';
|
||||||
import DropdownSection from 'next/dropdown-menu/base/DropdownSection.vue';
|
import DropdownSection from 'next/dropdown-menu/base/DropdownSection.vue';
|
||||||
import DropdownItem from 'next/dropdown-menu/base/DropdownItem.vue';
|
import DropdownItem from 'next/dropdown-menu/base/DropdownItem.vue';
|
||||||
|
import WhatsappTemplates from 'dashboard/components/widgets/conversation/WhatsappTemplates/Modal.vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
show: {
|
show: {
|
||||||
@ -59,12 +60,21 @@ const isUpdating = computed(() => uiFlags.value.isUpdating);
|
|||||||
const isSubmitting = computed(() => isCreating.value || isUpdating.value);
|
const isSubmitting = computed(() => isCreating.value || isUpdating.value);
|
||||||
const currentInbox = computed(() => inboxGetter.value(props.inboxId));
|
const currentInbox = computed(() => inboxGetter.value(props.inboxId));
|
||||||
|
|
||||||
|
const whatsAppTemplates = computed(() => {
|
||||||
|
return store.getters['inboxes/getWhatsAppTemplates'](props.inboxId) || [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const showWhatsappTemplates = computed(() => {
|
||||||
|
return whatsAppTemplates.value.length > 0;
|
||||||
|
});
|
||||||
|
|
||||||
const messageContent = ref('');
|
const messageContent = ref('');
|
||||||
const scheduledDateTime = ref(null);
|
const scheduledDateTime = ref(null);
|
||||||
const attachments = ref([]);
|
const attachments = ref([]);
|
||||||
const existingAttachment = ref(null);
|
const existingAttachment = ref(null);
|
||||||
const templateParams = ref(null);
|
const templateParams = ref(null);
|
||||||
const showConfirmClose = ref(false);
|
const showConfirmClose = ref(false);
|
||||||
|
const showWhatsAppTemplatesModal = ref(false);
|
||||||
const datePickerOpen = ref(false);
|
const datePickerOpen = ref(false);
|
||||||
const contentError = ref(false);
|
const contentError = ref(false);
|
||||||
const contentLengthError = ref(false);
|
const contentLengthError = ref(false);
|
||||||
@ -172,7 +182,18 @@ const hasTemplate = computed(
|
|||||||
() => templateParams.value && Object.keys(templateParams.value).length
|
() => templateParams.value && Object.keys(templateParams.value).length
|
||||||
);
|
);
|
||||||
const hasExistingAttachment = computed(() => !!existingAttachment.value);
|
const hasExistingAttachment = computed(() => !!existingAttachment.value);
|
||||||
const showAttachmentUpload = computed(() => !hasNewAttachment.value);
|
const showAttachmentUpload = computed(
|
||||||
|
() => !hasNewAttachment.value && !hasTemplate.value
|
||||||
|
);
|
||||||
|
|
||||||
|
const templateName = computed(() => {
|
||||||
|
return templateParams.value?.name || templateParams.value?.id || null;
|
||||||
|
});
|
||||||
|
|
||||||
|
const clearTemplate = () => {
|
||||||
|
templateParams.value = null;
|
||||||
|
messageContent.value = '';
|
||||||
|
};
|
||||||
|
|
||||||
const maxLength = computed(() => {
|
const maxLength = computed(() => {
|
||||||
const channelType = currentInbox.value?.channel_type;
|
const channelType = currentInbox.value?.channel_type;
|
||||||
@ -354,6 +375,21 @@ const closeModal = () => {
|
|||||||
resetForm();
|
resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openWhatsAppTemplatesModal = () => {
|
||||||
|
showWhatsAppTemplatesModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideWhatsAppTemplatesModal = () => {
|
||||||
|
showWhatsAppTemplatesModal.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onTemplateSelect = messagePayload => {
|
||||||
|
templateParams.value = messagePayload.templateParams;
|
||||||
|
messageContent.value = messagePayload.message;
|
||||||
|
hideWhatsAppTemplatesModal();
|
||||||
|
contentError.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
const submit = async status => {
|
const submit = async status => {
|
||||||
if (!validatePayload(status)) return;
|
if (!validatePayload(status)) return;
|
||||||
|
|
||||||
@ -458,14 +494,16 @@ watch(
|
|||||||
<WootMessageEditor
|
<WootMessageEditor
|
||||||
v-model="messageContent"
|
v-model="messageContent"
|
||||||
class="message-editor min-h-[10rem] max-h-[20rem] !px-3 resize-y overflow-auto"
|
class="message-editor min-h-[10rem] max-h-[20rem] !px-3 resize-y overflow-auto"
|
||||||
:class="
|
:class="[
|
||||||
contentError || contentLengthError
|
contentError || contentLengthError
|
||||||
? 'border border-n-ruby-9 rounded-xl'
|
? 'border border-n-ruby-9 rounded-xl'
|
||||||
: ''
|
: '',
|
||||||
"
|
hasTemplate ? 'opacity-60 cursor-not-allowed' : '',
|
||||||
|
]"
|
||||||
:placeholder="t('SCHEDULED_MESSAGES.MODAL.MESSAGE_PLACEHOLDER')"
|
:placeholder="t('SCHEDULED_MESSAGES.MODAL.MESSAGE_PLACEHOLDER')"
|
||||||
:channel-type="currentInbox?.channel_type"
|
:channel-type="currentInbox?.channel_type"
|
||||||
:medium="currentInbox?.medium"
|
:medium="currentInbox?.medium"
|
||||||
|
:disabled="hasTemplate"
|
||||||
override-line-breaks
|
override-line-breaks
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
() => {
|
() => {
|
||||||
@ -518,7 +556,7 @@ watch(
|
|||||||
@change="onDateTimeChange"
|
@change="onDateTimeChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="showAttachmentUpload" class="flex items-center h-10">
|
<div v-if="showAttachmentUpload" class="flex items-center gap-2 h-10">
|
||||||
<FileUpload
|
<FileUpload
|
||||||
:accept="ALLOWED_FILE_TYPES"
|
:accept="ALLOWED_FILE_TYPES"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
@ -534,6 +572,33 @@ watch(
|
|||||||
class="pointer-events-none"
|
class="pointer-events-none"
|
||||||
/>
|
/>
|
||||||
</FileUpload>
|
</FileUpload>
|
||||||
|
<NextButton
|
||||||
|
v-if="showWhatsappTemplates"
|
||||||
|
ghost
|
||||||
|
xs
|
||||||
|
icon="i-lucide-zap"
|
||||||
|
:label="t('SCHEDULED_MESSAGES.MODAL.TEMPLATE_SELECT')"
|
||||||
|
@click="openWhatsAppTemplatesModal"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="hasTemplate"
|
||||||
|
class="flex items-center gap-2 text-xs text-n-slate-11"
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
t('SCHEDULED_MESSAGES.MODAL.TEMPLATE_SELECTED', {
|
||||||
|
name: templateName,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<NextButton
|
||||||
|
ghost
|
||||||
|
xs
|
||||||
|
slate
|
||||||
|
icon="i-lucide-x"
|
||||||
|
@click="clearTemplate"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span
|
<span
|
||||||
v-if="existingAttachment && !attachments.length"
|
v-if="existingAttachment && !attachments.length"
|
||||||
@ -632,5 +697,13 @@ watch(
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</woot-modal>
|
</woot-modal>
|
||||||
|
|
||||||
|
<WhatsappTemplates
|
||||||
|
v-model:show="showWhatsAppTemplatesModal"
|
||||||
|
:inbox-id="inboxId"
|
||||||
|
:send-button-label="t('SCHEDULED_MESSAGES.MODAL.TEMPLATE_ACTION')"
|
||||||
|
@on-send="onTemplateSelect"
|
||||||
|
@cancel="hideWhatsAppTemplatesModal"
|
||||||
|
/>
|
||||||
</woot-modal>
|
</woot-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user