diff --git a/app/javascript/dashboard/components-next/NewConversation/components/WhatsappTemplateParser.vue b/app/javascript/dashboard/components-next/NewConversation/components/WhatsappTemplateParser.vue index 9e747023d..0a2702b1c 100644 --- a/app/javascript/dashboard/components-next/NewConversation/components/WhatsappTemplateParser.vue +++ b/app/javascript/dashboard/components-next/NewConversation/components/WhatsappTemplateParser.vue @@ -53,23 +53,6 @@ const buttonComponents = computed(() => { ); }); -const interactiveComponents = computed(() => { - return templateComponents.value.filter(component => - ['LIST', 'PRODUCT', 'CATALOG'].includes(component.type) - ); -}); - -const isInteractiveTemplate = computed(() => { - const hasInteractiveButtons = buttonComponents.value.some(component => - component.buttons?.some(button => - ['quick_reply', 'url', 'phone_number', 'catalog_browse'].includes( - button.type - ) - ) - ); - return hasInteractiveButtons || interactiveComponents.value.length > 0; -}); - const legacyParams = computed(() => { const params = {}; Object.keys(processedParams.value).forEach(key => { @@ -256,29 +239,6 @@ const generateVariables = () => { } }); - // Process interactive components (LIST, PRODUCT, CATALOG) - interactiveComponents.value.forEach(component => { - if (component.type === 'LIST') { - allVariables.interactive = { - type: 'list', - button_text: 'Select Option', - sections: component.sections || [], - }; - } else if (component.type === 'PRODUCT') { - allVariables.interactive = { - type: 'product', - catalog_id: component.catalog_id || '', - product_id: component.product_id || '', - }; - } else if (component.type === 'CATALOG') { - allVariables.interactive = { - type: 'catalog', - catalog_id: component.catalog_id || '', - title: component.title || 'Browse Products', - }; - } - }); - processedParams.value = allVariables; }; @@ -465,17 +425,18 @@ onMounted(() => {
-
+
- Location Details + {{ + t('WHATSAPP_TEMPLATES.PARSER.LOCATION_DETAILS') + }}
{ placeholder="37.7749 (San Francisco)" :message-type="getFieldErrorType('header.location.latitude')" /> - Range: -90.0 to 90.0 + {{ + t('WHATSAPP_TEMPLATES.PARSER.LATITUDE_RANGE') + }}
{ placeholder="-122.4194 (San Francisco)" :message-type="getFieldErrorType('header.location.longitude')" /> - Range: -180.0 to 180.0 + {{ + t('WHATSAPP_TEMPLATES.PARSER.LONGITUDE_RANGE') + }}
{
{
- 💡 Tip: You can get coordinates by searching your - location on Google Maps, right-clicking the pin, and copying the - latitude/longitude values. + {{ t('WHATSAPP_TEMPLATES.PARSER.TIP_LABEL') }} + {{ t('WHATSAPP_TEMPLATES.PARSER.LOCATION_TIP') }}
@@ -621,101 +587,6 @@ onMounted(() => {
- -
-

- {{ - t('WHATSAPP_TEMPLATES.PARSER.INTERACTIVE_PARAMETERS') || - 'Interactive Parameters' - }} -

- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- List sections are configured in the template and cannot be modified - here. -
-
-
-

diff --git a/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplatesPicker.vue b/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplatesPicker.vue index fff118873..7a8470cbe 100644 --- a/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplatesPicker.vue +++ b/app/javascript/dashboard/components/widgets/conversation/WhatsappTemplates/TemplatesPicker.vue @@ -35,8 +35,15 @@ export default { return false; } - // Since we support all formats now, include all approved templates - // This includes templates with media headers (IMAGE, VIDEO, DOCUMENT) + // Filter out interactive templates (LIST, PRODUCT, CATALOG) + const hasInteractiveComponents = template.components.some(component => + ['LIST', 'PRODUCT', 'CATALOG'].includes(component.type) + ); + + if (hasInteractiveComponents) { + return false; + } + return true; }); }, @@ -106,7 +113,7 @@ export default { - {{ $t('WHATSAPP_TEMPLATES.PICKER.LABELS.LANGUAGE') }} : + {{ $t('WHATSAPP_TEMPLATES.PICKER.LABELS.LANGUAGE') }}: {{ template.language }}

diff --git a/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json b/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json index fc3de96d0..defa9f41b 100644 --- a/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json +++ b/app/javascript/dashboard/i18n/locale/en/whatsappTemplates.json @@ -25,7 +25,16 @@ "VARIABLE_PLACEHOLDER": "Enter {variable} value", "GO_BACK_LABEL": "Go Back", "SEND_MESSAGE_LABEL": "Send Message", - "FORM_ERROR_MESSAGE": "Please fill all variables before sending" + "FORM_ERROR_MESSAGE": "Please fill all variables before sending", + "LOCATION_DETAILS": "Location Details", + "LATITUDE": "Latitude", + "LONGITUDE": "Longitude", + "LOCATION_NAME": "Location Name", + "FULL_ADDRESS": "Full Address", + "LATITUDE_RANGE": "Range: -90.0 to 90.0", + "LONGITUDE_RANGE": "Range: -180.0 to 180.0", + "LOCATION_TIP": "You can get coordinates by searching your location on Google Maps, right-clicking the pin, and copying the latitude/longitude values.", + "TIP_LABEL": "Tip:" } } } diff --git a/app/services/whatsapp/providers/whatsapp_cloud_service.rb b/app/services/whatsapp/providers/whatsapp_cloud_service.rb index f35909f95..d1230e63f 100644 --- a/app/services/whatsapp/providers/whatsapp_cloud_service.rb +++ b/app/services/whatsapp/providers/whatsapp_cloud_service.rb @@ -12,15 +12,6 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi end def send_template(phone_number, template_info) - # Check if this is an interactive template - if template_info[:parameters].is_a?(Hash) && template_info[:parameters][:type] == 'interactive' - send_interactive_template(phone_number, template_info) - else - send_regular_template(phone_number, template_info) - end - end - - def send_regular_template(phone_number, template_info) template_body = template_body_parameters(template_info) request_body = { @@ -40,32 +31,6 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi process_response(response) end - def send_interactive_template(phone_number, template_info) - interactive_data = template_info[:parameters][:interactive_data] - - request_body = { - messaging_product: 'whatsapp', - recipient_type: 'individual', - to: phone_number, - type: 'interactive', - interactive: { - type: interactive_data[:type], - body: { - text: build_template_body_text(template_info) - }, - action: interactive_data[:action] - } - } - - response = HTTParty.post( - "#{phone_id_path}/messages", - headers: api_headers, - body: request_body.to_json - ) - - process_response(response) - end - def sync_templates # ensuring that channels with wrong provider config wouldn't keep trying to sync templates whatsapp_channel.mark_message_templates_updated @@ -221,13 +186,4 @@ class Whatsapp::Providers::WhatsappCloudService < Whatsapp::Providers::BaseServi process_response(response) end - - private - - def build_template_body_text(template_info) - # Build the template body text with parameter substitution - # This is a simplified version - you might want to enhance this - # to properly substitute template variables - template_info[:name] || 'Interactive Template' - end end diff --git a/app/services/whatsapp/template_processor_service.rb b/app/services/whatsapp/template_processor_service.rb index ea2cf964e..52176a396 100644 --- a/app/services/whatsapp/template_processor_service.rb +++ b/app/services/whatsapp/template_processor_service.rb @@ -75,11 +75,8 @@ class Whatsapp::TemplateProcessorService template = find_template return if template.blank? - # Check for interactive templates first - if has_interactive_components?(template) - process_interactive_template_params(template) # Handle enhanced template parameters structure - elsif template_params['processed_params'].is_a?(Hash) && template_params['processed_params'].key?('body') + if template_params['processed_params'].is_a?(Hash) && template_params['processed_params'].key?('body') process_enhanced_template_params(template) else # Check if we have special header types that need processing @@ -190,7 +187,7 @@ class Whatsapp::TemplateProcessorService elsif key == 'location' && processed_params['header']['location_type'] == 'location' location_param = build_location_parameter(value) header_params << location_param if location_param - elsif !%w[media_type location_type].include?(key) + elsif %w[media_type location_type].exclude?(key) header_params << build_parameter(value) end end @@ -448,7 +445,7 @@ class Whatsapp::TemplateProcessorService when 'expiry' # Expiry time in minutes expiry_minutes = sanitized_value.to_i - raise ArgumentError, 'Expiry minutes must be a positive number' unless expiry_minutes > 0 + raise ArgumentError, 'Expiry minutes must be a positive number' unless expiry_minutes.positive? { type: 'text', text: expiry_minutes.to_s } else @@ -494,145 +491,6 @@ class Whatsapp::TemplateProcessorService template['components'].find { |obj| obj['type'] == 'BODY' && obj.key?('text') } end - def has_interactive_components?(template) - # Check if template has interactive buttons like quick replies, call-to-actions, etc. - interactive_types = %w[quick_reply url phone_number copy_code list catalog_browse] - template['components']&.any? do |component| - (component['type'] == 'BUTTONS' && component['buttons']&.any? { |button| interactive_types.include?(button['type']) }) || - (component['type'] == 'LIST' && component['sections'].present?) || - (component['type'] == 'PRODUCT' && component['product_id'].present?) || - (component['type'] == 'CATALOG' && component['catalog_id'].present?) - end - end - - def process_interactive_template_params(template) - components = [] - - # Process body parameters - if template_params['processed_params'].present? - body_params = template_params['processed_params'].map { |_, value| { type: 'text', text: value } } - components << { type: 'body', parameters: body_params } if body_params.present? - end - - @template_params = { - type: 'interactive', - components: components, - interactive_data: extract_interactive_data(template) - } - end - - def extract_interactive_data(template) - # Check if this is a catalog browse template - catalog_component = template['components'].find { |c| c['type'] == 'CATALOG' } - return extract_catalog_data(catalog_component) if catalog_component&.dig('catalog_id') - - # Check if this is a product template - product_component = template['components'].find { |c| c['type'] == 'PRODUCT' } - return extract_product_data(product_component) if product_component&.dig('product_id') - - # Check if this is a list template - list_component = template['components'].find { |c| c['type'] == 'LIST' } - return extract_list_data(list_component) if list_component&.dig('sections') - - # Default to button template - interactive_data = { type: 'button', action: { buttons: [] } } - - button_component = template['components'].find { |c| c['type'] == 'BUTTONS' } - return interactive_data unless button_component&.dig('buttons') - - buttons = button_component['buttons'].map.with_index do |button, index| - # Process dynamic button text if parameters are provided - button_text = process_dynamic_button_text(button['text'] || '', index) - - case button['type'] - when 'quick_reply' - { - type: 'reply', - reply: { - id: "reply_#{index}", - title: button_text || "Reply #{index + 1}" - } - } - when 'url' - { - type: 'reply', - reply: { - id: "url_#{index}", - title: button_text || 'Visit Link' - } - } - when 'phone_number' - { - type: 'reply', - reply: { - id: "call_#{index}", - title: button_text || 'Call Now' - } - } - end - end.compact.first(3) # WhatsApp allows max 3 reply buttons - - interactive_data[:action][:buttons] = buttons - interactive_data - end - - def extract_list_data(list_component) - interactive_data = { - type: 'list', - action: { - button: 'Select an Option', - sections: [] - } - } - - sections = list_component['sections'].map.with_index do |section, section_index| - rows = section['rows']&.map&.with_index do |row, row_index| - { - id: "row_#{section_index}_#{row_index}", - title: row['title'] || "Option #{row_index + 1}", - description: row['description'] || nil - } - end&.first(10) || [] # WhatsApp allows max 10 rows per section - - { - title: section['title'] || "Section #{section_index + 1}", - rows: rows - } - end.first(10) # WhatsApp allows max 10 sections - - interactive_data[:action][:sections] = sections - interactive_data - end - - def extract_product_data(product_component) - { - type: 'product', - action: { - catalog_id: product_component['catalog_id'] || '', - product_retailer_id: product_component['product_id'] - } - } - end - - def extract_catalog_data(catalog_component) - { - type: 'product_list', - action: { - catalog_id: catalog_component['catalog_id'], - sections: [ - { - title: catalog_component['title'] || 'Browse Products', - product_items: catalog_component['products']&.map do |product| - { - product_retailer_id: product['id'] || product['product_id'] - } - end || [] - } - ] - } - } - end - def process_dynamic_button_text(button_text, button_index) return button_text unless button_text.include?('{{') @@ -651,7 +509,7 @@ class Whatsapp::TemplateProcessorService button_text end - def has_rich_formatting?(text) + def rich_formatting?(text) # Check if text contains WhatsApp rich formatting markers text.match?(/\*[^*]+\*/) || # Bold: *text* text.match?(/_[^_]+_/) || # Italic: _text_