feat: Add media URL input support for WhatsApp templates

Add support for media URL input fields in WhatsApp templates with IMAGE, VIDEO,
or DOCUMENT headers, enabling users to provide media links for template messages.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Sojan Jose
2025-07-21 17:49:08 +04:00
co-authored by Claude
parent 9329813ff5
commit 44d29fa708
2 changed files with 102 additions and 9 deletions
@@ -116,13 +116,25 @@ const generateVariables = () => {
});
// Process header variables
if (headerComponent.value?.text) {
const headerVars = headerComponent.value.text.match(/{{([^}]+)}}/g) || [];
headerVars.forEach(variable => {
const key = processVariable(variable);
if (headerComponent.value) {
if (headerComponent.value.text) {
// Text headers with variables
const headerVars = headerComponent.value.text.match(/{{([^}]+)}}/g) || [];
headerVars.forEach(variable => {
const key = processVariable(variable);
if (!allVariables.header) allVariables.header = {};
allVariables.header[key] = '';
});
} else if (
headerComponent.value.format &&
['IMAGE', 'VIDEO', 'DOCUMENT'].includes(headerComponent.value.format)
) {
// Media headers need URL input
if (!allVariables.header) allVariables.header = {};
allVariables.header[key] = '';
});
allVariables.header.media_url = '';
allVariables.header.media_type =
headerComponent.value.format.toLowerCase();
}
}
// Process footer variables
@@ -177,6 +189,44 @@ const validateButtonParameter = (button, index) => {
}
};
const getHeaderFieldLabel = key => {
if (key === 'media_url') {
const mediaType = processedParams.value.header?.media_type || 'media';
return (
t('WHATSAPP_TEMPLATES.PARSER.MEDIA_URL', {
type: mediaType.toUpperCase(),
}) || `${mediaType.toUpperCase()} URL`
);
}
return key;
};
const getHeaderFieldPlaceholder = key => {
if (key === 'media_url') {
const mediaType = processedParams.value.header?.media_type || 'media';
switch (mediaType) {
case 'image':
return (
t('WHATSAPP_TEMPLATES.PARSER.IMAGE_URL_PLACEHOLDER') ||
'Enter image URL (https://example.com/image.jpg)'
);
case 'video':
return (
t('WHATSAPP_TEMPLATES.PARSER.VIDEO_URL_PLACEHOLDER') ||
'Enter video URL (https://example.com/video.mp4)'
);
case 'document':
return (
t('WHATSAPP_TEMPLATES.PARSER.DOCUMENT_URL_PLACEHOLDER') ||
'Enter document URL (https://example.com/document.pdf)'
);
default:
return 'Enter media URL';
}
}
return `Enter ${key} value`;
};
const sendMessage = async () => {
const isValid = await v$.value.$validate();
if (!isValid) return;
@@ -243,13 +293,15 @@ onMounted(() => {
<span
class="flex items-center h-8 text-sm min-w-6 ltr:text-left rtl:text-right text-n-slate-10"
>
{{ key }}
{{ getHeaderFieldLabel(key) }}
</span>
<Input
v-model="processedParams.header[key]"
custom-input-class="!h-8 w-full !bg-transparent"
class="w-full"
:message-type="getFieldErrorType(`header.${key}`)"
:placeholder="getHeaderFieldPlaceholder(key)"
:type="key === 'media_url' ? 'url' : 'text'"
/>
</div>
</div>
@@ -96,8 +96,16 @@ class Whatsapp::TemplateProcessorService
# Process header parameters
if processed_params['header'].present?
header_params = processed_params['header'].map { |_, value| build_parameter(value) }
components << { type: 'header', parameters: header_params }
header_params = processed_params['header'].filter_map do |key, value|
if key == 'media_url' && processed_params['header']['media_type'].present?
build_media_parameter(value, processed_params['header']['media_type'])
else
build_parameter(value)
end
end
# Only add header component if we have valid parameters
components << { type: 'header', parameters: header_params } if header_params.present?
end
# Process footer parameters (rarely used but supported)
@@ -216,6 +224,39 @@ class Whatsapp::TemplateProcessorService
raise ArgumentError, 'Invalid URL format'
end
def build_media_parameter(url, media_type)
return nil if url.blank?
sanitized_url = sanitize_parameter(url)
validate_url(sanitized_url)
case media_type.downcase
when 'image'
{
type: 'image',
image: {
link: sanitized_url
}
}
when 'video'
{
type: 'video',
video: {
link: sanitized_url
}
}
when 'document'
{
type: 'document',
document: {
link: sanitized_url
}
}
else
raise ArgumentError, "Unsupported media type: #{media_type}"
end
end
def validated_body_object(template)
# we don't care if its not approved template
return if template['status'] != 'approved'