const DLT_PLACEHOLDER_REGEX = /\{#(?:var|numeric|url|urlott|cbn|email|alphanumeric)#\}/g; const PLACEHOLDER_SAMPLE_FIELD_CANDIDATES = { '{#var#}': ['firstName', 'customerName', 'fullName', 'brandName', 'eventDisplayName'], '{#numeric#}': ['otp', 'amount', 'refundAmount', 'pincode', 'toNumber'], '{#url#}': ['trackingUrl', 'url', 'trackUrl', 'trackingLink'], '{#urlott#}': ['trackingUrl', 'url', 'trackUrl', 'trackingLink'], '{#cbn#}': ['callbackNumber', 'toNumber', 'customerPhone', 'mobile', 'phone'], '{#email#}': ['email', 'customerEmail'], '{#alphanumeric#}': ['orderId', 'transactionId', 'shipmentId', 'awbNumber', 'awbNo'], }; const EVENT_SAMPLE_OVERRIDES = { payment_failed: { shipment: { payment_status: 'failed', transaction_id: 'TXN9012457812', amount: '2499', failure_reason: 'UPI mandate expired', }, }, payment_initiated: { shipment: { payment_status: 'initiated', transaction_id: 'TXN9012457812', amount: '2499', }, }, refund_initiated: { shipment: { refund_status: 'initiated', refund_amount: '2499', refund_id: 'RFD1204982', }, }, refund_completed: { shipment: { refund_status: 'completed', refund_amount: '2499', refund_id: 'RFD1204982', }, }, out_for_delivery: { shipment: { otp: '482193', estimated_delivery_slot: '6:00 PM to 8:00 PM', }, }, delivery_attempt_failed: { shipment: { failure_reason: 'Customer unavailable', callback_number: '919876543210', }, }, delivery_done: { shipment: { delivered_at: '2026-04-06T14:18:00.000Z', otp: '482193', }, }, order_placed: { shipment: { payment_status: 'paid', expected_dispatch_date: '2026-04-07', }, }, }; function normalizeScalarText(value) { if (typeof value === 'string') return value.trim(); if (typeof value === 'number' && Number.isFinite(value)) return String(value); if (typeof value === 'boolean') return value ? 'true' : 'false'; return ''; } function firstNonEmptyText(...values) { for (const value of values) { const normalized = normalizeScalarText(value); if (normalized) return normalized; } return ''; } function toCamelCase(text) { return String(text || '') .replace(/([a-z0-9])([A-Z])/g, '$1 $2') .replace(/[^a-zA-Z0-9]+/g, ' ') .trim() .split(/\s+/) .filter(Boolean) .map((part, index) => { const lower = part.toLowerCase(); return index === 0 ? lower : lower.charAt(0).toUpperCase() + lower.slice(1); }) .join(''); } function setValueIndexEntry(valueIndex, key, value) { if (!key || valueIndex.has(key)) return; valueIndex.set(key, value); } function normalizeRenderableValue(value) { return normalizeScalarText(value).replace(/\s+/g, ' ').trim(); } function splitFullName(value) { const fullName = normalizeRenderableValue(value); if (!fullName) return { firstName: '', lastName: '', fullName: '' }; const parts = fullName.split(/\s+/).filter(Boolean); return { firstName: parts[0] || '', lastName: parts.length > 1 ? parts.slice(1).join(' ') : '', fullName, }; } function indexPayloadValues(value, pathParts = [], valueIndex = new Map()) { if (Array.isArray(value)) { value.forEach((entry) => indexPayloadValues(entry, pathParts, valueIndex)); return valueIndex; } if (value && typeof value === 'object') { Object.entries(value).forEach(([key, entry]) => { indexPayloadValues(entry, [...pathParts, key], valueIndex); }); return valueIndex; } const normalizedValue = normalizeRenderableValue(value); if (!normalizedValue || pathParts.length === 0) return valueIndex; const leafKey = toCamelCase(pathParts[pathParts.length - 1]); const fullKey = toCamelCase(pathParts.join(' ')); setValueIndexEntry(valueIndex, leafKey, normalizedValue); setValueIndexEntry(valueIndex, fullKey, normalizedValue); return valueIndex; } function buildShipmentValueIndex(shipment) { const valueIndex = indexPayloadValues(shipment); const firstBag = shipment?.bags?.[0] || {}; const customerName = splitFullName( firstNonEmptyText( shipment?.user?.first_name && shipment?.user?.last_name ? `${shipment.user.first_name} ${shipment.user.last_name}` : '', shipment?.delivery_address?.name, shipment?.delivery_address?.contact_person, shipment?.billing_address?.name, shipment?.billing_address?.contact_person, ) ); const primaryTrackingUrl = firstNonEmptyText( shipment?.delivery_partner_details?.track_url, shipment?.meta?.tracking_url, firstBag?.meta?.tracking_url, shipment?.affiliate_details?.shipment_meta?.tracking_url, ); const primaryAwbNumber = firstNonEmptyText( shipment?.delivery_partner_details?.awb_no, shipment?.meta?.awb_number, firstBag?.meta?.awb_number, ); const primaryCourierName = firstNonEmptyText( shipment?.delivery_partner_details?.display_name, shipment?.delivery_partner_details?.name, shipment?.meta?.courier_partner_name, firstBag?.meta?.dp_name, ); const brandName = firstNonEmptyText( shipment?.bags?.[0]?.brand?.brand_name, shipment?.bags?.[0]?.item?.attributes?.brand_name, shipment?.affiliate_details?.company_affiliate_tag, ); const toNumber = firstNonEmptyText( shipment?.user?.mobile, shipment?.delivery_address?.phone, shipment?.billing_address?.phone, ); const emailAddress = firstNonEmptyText( shipment?.user?.email, shipment?.delivery_address?.email, shipment?.billing_address?.email, ); const eventKey = firstNonEmptyText( shipment?.status, shipment?.shipment_status?.status, shipment?.shipment_status?.current_shipment_status, ); const eventDisplayName = firstNonEmptyText( shipment?.shipment_status?.display_name, shipment?.shipment_status?.current_shipment_status, ); const shipmentId = firstNonEmptyText( shipment?.shipment_id, shipment?.shipment_status?.shipment_id, ); const resolvedFullName = firstNonEmptyText( shipment?.user?.first_name || shipment?.user?.last_name ? `${normalizeRenderableValue(shipment?.user?.first_name)} ${normalizeRenderableValue(shipment?.user?.last_name)}`.trim() : '', customerName.fullName, ); const resolvedFirstName = firstNonEmptyText(shipment?.user?.first_name, customerName.firstName); const resolvedLastName = firstNonEmptyText(shipment?.user?.last_name, customerName.lastName); setValueIndexEntry(valueIndex, 'firstName', resolvedFirstName); setValueIndexEntry(valueIndex, 'lastName', resolvedLastName); setValueIndexEntry(valueIndex, 'fullName', resolvedFullName); setValueIndexEntry(valueIndex, 'customerFirstName', resolvedFirstName); setValueIndexEntry(valueIndex, 'customerLastName', resolvedLastName); setValueIndexEntry(valueIndex, 'customerName', resolvedFullName); setValueIndexEntry(valueIndex, 'phone', toNumber); setValueIndexEntry(valueIndex, 'mobile', toNumber); setValueIndexEntry(valueIndex, 'toNumber', toNumber); setValueIndexEntry(valueIndex, 'customerPhone', toNumber); setValueIndexEntry(valueIndex, 'customerMobile', toNumber); setValueIndexEntry(valueIndex, 'email', emailAddress); setValueIndexEntry(valueIndex, 'customerEmail', emailAddress); setValueIndexEntry(valueIndex, 'orderId', normalizeRenderableValue(shipment?.order_id)); setValueIndexEntry(valueIndex, 'orderNumber', normalizeRenderableValue(shipment?.order_id)); setValueIndexEntry(valueIndex, 'shipmentId', shipmentId); setValueIndexEntry(valueIndex, 'event', eventKey); setValueIndexEntry(valueIndex, 'status', eventKey); setValueIndexEntry(valueIndex, 'eventDisplayName', eventDisplayName); setValueIndexEntry(valueIndex, 'displayName', eventDisplayName); setValueIndexEntry(valueIndex, 'brandName', brandName); setValueIndexEntry(valueIndex, 'trackingUrl', primaryTrackingUrl); setValueIndexEntry(valueIndex, 'trackUrl', primaryTrackingUrl); setValueIndexEntry(valueIndex, 'trackingLink', primaryTrackingUrl); setValueIndexEntry(valueIndex, 'url', primaryTrackingUrl); setValueIndexEntry(valueIndex, 'awbNo', primaryAwbNumber); setValueIndexEntry(valueIndex, 'awbNumber', primaryAwbNumber); setValueIndexEntry(valueIndex, 'awb', primaryAwbNumber); setValueIndexEntry(valueIndex, 'dpName', primaryCourierName); setValueIndexEntry(valueIndex, 'courierName', primaryCourierName); setValueIndexEntry(valueIndex, 'deliveryPartnerName', primaryCourierName); return valueIndex; } function mergeDeep(baseValue, overrideValue) { if (Array.isArray(baseValue) || Array.isArray(overrideValue)) { return overrideValue !== undefined ? overrideValue : baseValue; } if (baseValue && typeof baseValue === 'object' && overrideValue && typeof overrideValue === 'object') { const nextValue = { ...baseValue }; Object.entries(overrideValue).forEach(([key, value]) => { nextValue[key] = key in nextValue ? mergeDeep(nextValue[key], value) : value; }); return nextValue; } return overrideValue !== undefined ? overrideValue : baseValue; } function titleCaseFromSlug(slug) { return String(slug || '') .split('_') .filter(Boolean) .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(' '); } function isRenderablePreviewValueForToken(token, value) { const normalizedValue = normalizeRenderableValue(value); if (!normalizedValue) return false; switch (token) { case '{#numeric#}': return /^\d+$/.test(normalizedValue); case '{#url#}': case '{#urlott#}': return /^https?:\/\//i.test(normalizedValue); case '{#cbn#}': return /^\+?[0-9][0-9\s-]{5,}$/.test(normalizedValue); case '{#email#}': return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(normalizedValue); case '{#alphanumeric#}': return /^[A-Za-z0-9]+$/.test(normalizedValue); default: return true; } } function resolvePlaceholderSampleFallback(token, shipmentValueIndex) { const candidateFields = PLACEHOLDER_SAMPLE_FIELD_CANDIDATES[token] || []; for (const fieldName of candidateFields) { const resolvedValue = shipmentValueIndex.get(fieldName) || ''; if (isRenderablePreviewValueForToken(token, resolvedValue)) { return { fieldName, value: resolvedValue, }; } } return null; } export function getTemplateSamplePayload(template = {}) { const eventSlug = String(template?.eventSlug || '').trim(); const eventLabel = String(template?.eventLabel || '').trim() || titleCaseFromSlug(eventSlug); const brandName = String(template?.brandName || '').trim() || 'Your Brand'; const override = EVENT_SAMPLE_OVERRIDES[eventSlug] || {}; const basePayload = { payload: { event: eventSlug, company_id: 'dev_merchant_001', application_id: 'application-demo-001', shipment: { application_id: 'application-demo-001', order_id: 'FY5E53AFAA091115C235', shipment_id: 'SHP784512', status: eventSlug || 'order_placed', shipment_status: { status: eventSlug || 'order_placed', current_shipment_status: eventSlug || 'order_placed', display_name: eventLabel || 'Order Update', shipment_id: 'SHP784512', }, user: { first_name: 'Aarav', last_name: 'Sharma', mobile: '919876543210', email: 'aarav.sharma@example.com', }, delivery_address: { name: 'Aarav Sharma', phone: '919876543210', email: 'aarav.sharma@example.com', city: 'Bengaluru', pincode: '560001', }, billing_address: { name: 'Aarav Sharma', phone: '919876543210', email: 'aarav.sharma@example.com', }, delivery_partner_details: { display_name: 'Blue Dart', track_url: 'https://tracking.example.com/SHP784512', awb_no: '78451236985', }, affiliate_details: { affiliate_id: 'application-demo-001', company_affiliate_tag: brandName, shipment_meta: { tracking_url: 'https://tracking.example.com/SHP784512', courier_partner_name: 'Blue Dart', }, }, meta: { tracking_url: 'https://tracking.example.com/SHP784512', awb_number: '78451236985', courier_partner_name: 'Blue Dart', }, bags: [ { brand: { brand_name: brandName }, item: { name: 'Midnight Duffle', attributes: { brand_name: brandName }, }, meta: { tracking_url: 'https://tracking.example.com/SHP784512', awb_number: '78451236985', }, }, ], }, }, }; return mergeDeep(basePayload, override); } export function buildTemplateSampleRender(templateText, variableMap = {}, samplePayload = {}) { const text = String(templateText || ''); if (!text) { return { text: '', fallbackPlaceholders: [], unresolvedPlaceholders: [], }; } const shipment = samplePayload?.payload?.shipment || samplePayload?.shipment || {}; const shipmentValueIndex = buildShipmentValueIndex(shipment); let placeholderIndex = 0; const fallbackPlaceholders = []; const unresolvedPlaceholders = []; const renderedText = text.replace(DLT_PLACEHOLDER_REGEX, (token) => { const mappingKey = `${token}[${placeholderIndex}]`; const mappedFieldName = normalizeScalarText(variableMap?.[mappingKey] || variableMap?.[token]); placeholderIndex += 1; const resolvedMappedValue = mappedFieldName ? shipmentValueIndex.get(toCamelCase(mappedFieldName)) || '' : ''; if (resolvedMappedValue) return resolvedMappedValue; const fallback = resolvePlaceholderSampleFallback(token, shipmentValueIndex); if (fallback) { fallbackPlaceholders.push({ mappingKey, token, mappedFieldName, sampleFieldName: fallback.fieldName, }); return fallback.value; } unresolvedPlaceholders.push({ mappingKey, token, mappedFieldName, }); return token; }); return { text: renderedText, fallbackPlaceholders, unresolvedPlaceholders, }; } export function renderTemplateWithSamplePayload(templateText, variableMap = {}, samplePayload = {}) { return buildTemplateSampleRender(templateText, variableMap, samplePayload).text; } export function getTemplateWorkspaceDescription(template = {}) { const eventLabel = String(template?.eventLabel || '').trim() || titleCaseFromSlug(template?.eventSlug); if (!eventLabel) return 'Review the current SMS template, sample payload, and delivery details.'; return `Use this SMS template for ${eventLabel.toLowerCase()} notifications.`; } export function getTemplateWorkspaceVariableCount(template = {}) { return (String(template?.selectedTemplate || '').match(DLT_PLACEHOLDER_REGEX) || []).length; }