From cfda6696b600d0e91120a5f7a46f99648504f7b5 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Tue, 22 Jul 2025 18:53:35 +0400 Subject: [PATCH] chore: fix copy otp --- .../components/WhatsappTemplateParser.vue | 281 +----------------- .../WhatsappTemplates/TemplateParser.vue | 251 ++++++++++++---- .../whatsapp/template_processor_service.rb | 32 +- 3 files changed, 224 insertions(+), 340 deletions(-) diff --git a/app/javascript/dashboard/components-next/NewConversation/components/WhatsappTemplateParser.vue b/app/javascript/dashboard/components-next/NewConversation/components/WhatsappTemplateParser.vue index 3479686a8..16379f296 100644 --- a/app/javascript/dashboard/components-next/NewConversation/components/WhatsappTemplateParser.vue +++ b/app/javascript/dashboard/components-next/NewConversation/components/WhatsappTemplateParser.vue @@ -47,22 +47,6 @@ const footerComponent = computed(() => { ); }); -const buttonComponents = computed(() => { - return templateComponents.value.filter( - component => component.type === 'BUTTONS' - ); -}); - -const legacyParams = computed(() => { - const params = {}; - Object.keys(processedParams.value).forEach(key => { - if (!['header', 'body', 'footer', 'buttons'].includes(key)) { - params[key] = processedParams.value[key]; - } - }); - return params; -}); - const processVariable = str => { return str.replace(/{{|}}/g, ''); }; @@ -113,26 +97,7 @@ const generateVariables = () => { allVariables.body = {}; bodyVars.forEach(variable => { const key = processVariable(variable); - // Special handling for authentication templates - if (props.template?.category === 'AUTHENTICATION') { - if ( - key === '1' || - key.toLowerCase().includes('otp') || - key.toLowerCase().includes('code') - ) { - allVariables.body.otp_code = ''; - } else if ( - key === '2' || - key.toLowerCase().includes('expiry') || - key.toLowerCase().includes('minute') - ) { - allVariables.body.expiry_minutes = ''; - } else { - allVariables.body[key] = ''; - } - } else { - allVariables.body[key] = ''; - } + allVariables.body[key] = ''; }); } @@ -172,64 +137,9 @@ const generateVariables = () => { } } - // Process button variables - buttonComponents.value.forEach(buttonComponent => { - if (buttonComponent.buttons) { - buttonComponent.buttons.forEach((button, index) => { - // Handle URL buttons with variables - if (button.type === 'URL' && button.url && button.url.includes('{{')) { - const buttonVars = button.url.match(/{{([^}]+)}}/g) || []; - if (buttonVars.length > 0) { - if (!allVariables.buttons) allVariables.buttons = []; - allVariables.buttons[index] = { - type: 'url', - parameter: '', - url: button.url, - variables: buttonVars.map(v => processVariable(v)), - }; - } - } - - // Handle copy code buttons - if (button.type === 'COPY_CODE') { - if (!allVariables.buttons) allVariables.buttons = []; - allVariables.buttons[index] = { - type: 'copy_code', - parameter: '', - }; - } - - // Handle interactive buttons with dynamic text - if (['QUICK_REPLY', 'URL', 'PHONE_NUMBER'].includes(button.type)) { - if (button.text && button.text.includes('{{')) { - if (!allVariables.buttons) allVariables.buttons = []; - allVariables.buttons[index] = { - type: button.type.toLowerCase(), - parameter: '', - text: button.text, - }; - } - } - }); - } - }); - processedParams.value = allVariables; }; -const validateButtonParameter = (button, index) => { - const parameter = processedParams.value.buttons[index].parameter; - - if (button.type === 'copy_code') { - if (parameter && parameter.length > 15) { - processedParams.value.buttons[index].parameter = parameter.substring( - 0, - 15 - ); - } - } -}; - const getHeaderFieldLabel = key => { if (key === 'media_url') { const mediaType = processedParams.value.header?.media_type || 'media'; @@ -268,88 +178,10 @@ const getHeaderFieldPlaceholder = key => { return `Enter ${key} value`; }; -const getBodyParameterLabel = key => { - if (props.template?.category === 'AUTHENTICATION') { - switch (key) { - case 'otp_code': - return t('WHATSAPP_TEMPLATES.PARSER.OTP_CODE') || 'OTP Code'; - case 'expiry_minutes': - return ( - t('WHATSAPP_TEMPLATES.PARSER.EXPIRY_MINUTES') || 'Expiry (minutes)' - ); - default: - return key; - } - } - return key; -}; - -const getBodyParameterPlaceholder = key => { - if (props.template?.category === 'AUTHENTICATION') { - switch (key) { - case 'otp_code': - return ( - t('WHATSAPP_TEMPLATES.PARSER.OTP_CODE_PLACEHOLDER') || - 'Enter 4-8 digit OTP code' - ); - case 'expiry_minutes': - return ( - t('WHATSAPP_TEMPLATES.PARSER.EXPIRY_MINUTES_PLACEHOLDER') || - 'Enter expiry time in minutes' - ); - default: - return `Enter ${key} value`; - } - } - return `Enter ${key} value`; -}; - -const getBodyParameterType = key => { - if (props.template?.category === 'AUTHENTICATION') { - switch (key) { - case 'otp_code': - return 'tel'; - case 'expiry_minutes': - return 'number'; - default: - return 'text'; - } - } - return 'text'; -}; - -const getBodyParameterMaxLength = key => { - if (props.template?.category === 'AUTHENTICATION') { - switch (key) { - case 'otp_code': - return 8; - case 'expiry_minutes': - return 3; - default: - return null; - } - } - return null; -}; - const sendMessage = async () => { const isValid = await v$.value.$validate(); if (!isValid) return; - // Auto-populate button parameters for authentication templates - const finalParams = { ...processedParams.value }; - if (props.template?.category === 'AUTHENTICATION' && finalParams.buttons) { - finalParams.buttons.forEach((button, index) => { - if ( - button.type === 'url' && - button.variables?.includes('1') && - finalParams.body?.otp_code - ) { - finalParams.buttons[index].parameter = finalParams.body.otp_code; - } - }); - } - const payload = { message: processedString.value, templateParams: { @@ -357,7 +189,7 @@ const sendMessage = async () => { category: props.template.category, language: props.template.language, namespace: props.template.namespace, - processed_params: finalParams, + processed_params: processedParams.value, }, }; emit('sendMessage', payload); @@ -427,103 +259,9 @@ onMounted(() => { - -
-

- {{ - t('WHATSAPP_TEMPLATES.PARSER.BODY_PARAMETERS') || 'Body Parameters' - }} -

-
- - {{ getBodyParameterLabel(key) }} - - -
-
- - -
-

- {{ - t('WHATSAPP_TEMPLATES.PARSER.FOOTER_PARAMETERS') || - 'Footer Parameters' - }} -

-
- - {{ key }} - - -
-
- - -
-

- {{ - t('WHATSAPP_TEMPLATES.PARSER.BUTTON_PARAMETERS') || - 'Button Parameters' - }} -

-
- - {{ - button.type === 'copy_code' - ? t('WHATSAPP_TEMPLATES.PARSER.COUPON_CODE') || 'Coupon Code' - : `${t('WHATSAPP_TEMPLATES.PARSER.BUTTON') || 'Button'} ${index + 1}` - }} - - -
-
- - +
@@ -533,10 +271,19 @@ onMounted(() => { {{ key }}
diff --git a/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplateParser.vue b/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplateParser.vue index 23f070867..d5ffc49e5 100644 --- a/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplateParser.vue +++ b/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplateParser.vue @@ -79,23 +79,84 @@ export default { ); const generateVariables = () => { - const params = {}; + const allVariables = {}; - // Add media URL field if template has media header - if (hasMediaHeader.value) { - params.media_url = ''; - } - - // Add body variables + // Process body variables const matchedVariables = templateString.value.match(/{{([^}]+)}}/g); if (matchedVariables) { - const finalVars = matchedVariables.map(i => processVariable(i)); - finalVars.forEach(variable => { - params[variable] = ''; + allVariables.body = {}; + matchedVariables.forEach(variable => { + const key = processVariable(variable); + // Special handling for authentication templates + if (props.template?.category === 'AUTHENTICATION') { + if ( + key === '1' || + key.toLowerCase().includes('otp') || + key.toLowerCase().includes('code') + ) { + allVariables.body.otp_code = ''; + } else if ( + key === '2' || + key.toLowerCase().includes('expiry') || + key.toLowerCase().includes('minute') + ) { + allVariables.body.expiry_minutes = ''; + } else { + allVariables.body[key] = ''; + } + } else { + allVariables.body[key] = ''; + } }); } - processedParams.value = params; + // Add media URL field if template has media header + if (hasMediaHeader.value) { + if (!allVariables.header) allVariables.header = {}; + allVariables.header.media_url = ''; + allVariables.header.media_type = + headerComponent.value.format.toLowerCase(); + } + + // Process button variables + const buttonComponents = props.template.components.filter( + component => component.type === 'BUTTONS' + ); + + buttonComponents.forEach(buttonComponent => { + if (buttonComponent.buttons) { + buttonComponent.buttons.forEach((button, index) => { + // Handle URL buttons with variables + if ( + button.type === 'URL' && + button.url && + button.url.includes('{{') + ) { + const buttonVars = button.url.match(/{{([^}]+)}}/g) || []; + if (buttonVars.length > 0) { + if (!allVariables.buttons) allVariables.buttons = []; + allVariables.buttons[index] = { + type: 'url', + parameter: '', + url: button.url, + variables: buttonVars.map(v => processVariable(v)), + }; + } + } + + // Handle copy code buttons + if (button.type === 'COPY_CODE') { + if (!allVariables.buttons) allVariables.buttons = []; + allVariables.buttons[index] = { + type: 'copy_code', + parameter: '', + }; + } + }); + } + }); + + processedParams.value = allVariables; }; const resetTemplate = () => { @@ -106,27 +167,22 @@ export default { v$.value.$touch(); if (v$.value.$invalid) return; - // Prepare enhanced template parameters for media support - const enhancedParams = {}; - - // Handle media header - if (hasMediaHeader.value && processedParams.value.media_url) { - enhancedParams.header = { - media_url: processedParams.value.media_url, - media_type: headerComponent.value.format.toLowerCase(), - }; - } - - // Handle body variables - const bodyParams = {}; - Object.keys(processedParams.value).forEach(key => { - if (key !== 'media_url') { - bodyParams[key] = processedParams.value[key]; - } - }); - - if (Object.keys(bodyParams).length > 0) { - enhancedParams.body = bodyParams; + // Auto-populate button parameters for authentication templates + const finalParams = { ...processedParams.value }; + if ( + props.template?.category === 'AUTHENTICATION' && + finalParams.buttons + ) { + finalParams.buttons.forEach((button, index) => { + if (button.type === 'url') { + // For authentication templates, auto-populate URL button parameter with OTP + if (finalParams.body?.['1']) { + finalParams.buttons[index].parameter = finalParams.body['1']; + } else if (finalParams.body?.otp_code) { + finalParams.buttons[index].parameter = finalParams.body.otp_code; + } + } + }); } const payload = { @@ -136,7 +192,7 @@ export default { category: props.template.category, language: props.template.language, namespace: props.template.namespace, - processed_params: enhancedParams, + processed_params: finalParams, }, }; emit('sendMessage', payload); @@ -170,63 +226,132 @@ export default {
-

+

{{ - headerComponent.format.charAt(0) + - headerComponent.format.slice(1).toLowerCase() + $t('WHATSAPP_TEMPLATES.PARSER.MEDIA_HEADER_LABEL', { + type: + headerComponent.format.charAt(0) + + headerComponent.format.slice(1).toLowerCase(), + }) || + `${ + headerComponent.format.charAt(0) + + headerComponent.format.slice(1).toLowerCase() + } Header` }} - Header

-
+
- {{ headerComponent.format.toLowerCase() }} URL + {{ + $t('WHATSAPP_TEMPLATES.PARSER.MEDIA_URL_LABEL', { + type: headerComponent.format.toLowerCase(), + }) || `${headerComponent.format.toLowerCase()} URL` + }}
- -
-

+ +

+

{{ $t('WHATSAPP_TEMPLATES.PARSER.VARIABLES_LABEL') }}

- - + + {{ + key === 'otp_code' + ? $t('WHATSAPP_TEMPLATES.PARSER.OTP_CODE') || 'OTP Code' + : key === 'expiry_minutes' + ? $t('WHATSAPP_TEMPLATES.PARSER.EXPIRY_MINUTES') || + 'Expiry (minutes)' + : key + }} + + +
+
+ + +
+

+ {{ + $t('WHATSAPP_TEMPLATES.PARSER.BUTTON_PARAMETERS') || + 'Button Parameters' + }} +

+
+ + {{ + button.type === 'copy_code' + ? $t('WHATSAPP_TEMPLATES.PARSER.COUPON_CODE') || 'Coupon Code' + : $t('WHATSAPP_TEMPLATES.PARSER.BUTTON_LABEL', { + index: index + 1, + }) || `Button ${index + 1}` + }} + +

{{ $t('WHATSAPP_TEMPLATES.PARSER.FORM_ERROR_MESSAGE') }}

-