import { useState, useEffect, useCallback, useRef } from 'react'; import { useParams, useSearchParams } from 'react-router-dom'; import apiClient from '../api/client'; import WhitelistModal from '../components/WhitelistModal'; import TestSmsModal from '../components/TestSmsModal'; const STATUS_CONFIG = { generated: { label: 'Generated', bg: 'bg-page-bg', text: 'text-text-muted', border: 'border-border-main' }, pending_whitelisting: { label: 'Pending Whitelisting', bg: 'bg-white', text: 'text-gray-700', border: 'border-gray-200' }, whitelisted: { label: 'Published', bg: 'bg-white', text: 'text-gray-700', border: 'border-gray-200' }, }; export default function Templates() { const { businessId } = useParams(); const [searchParams] = useSearchParams(); const [templates, setTemplates] = useState([]); const [profilesById, setProfilesById] = useState({}); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [whitelistTarget, setWhitelistTarget] = useState(null); const [testTarget, setTestTarget] = useState(null); const [activeTab, setActiveTab] = useState('published'); // 'published' | 'pending' const [highlightedEventSlug, setHighlightedEventSlug] = useState(''); const templateCardRefs = useRef({}); const highlightTimeoutRef = useRef(null); const handledFocusSlugRef = useRef(''); const getTabForStatus = useCallback((status) => { if (status === 'pending_whitelisting') return 'pending'; if (status === 'whitelisted') return 'published'; return null; }, []); const loadTemplates = useCallback(async () => { setLoading(true); setError(''); try { const [templatesRes, profilesRes] = await Promise.all([ apiClient.get(`/api/businesses/${businessId}/templates`), apiClient.get(`/api/businesses/${businessId}/global-sms/profiles`).catch(() => ({ data: { profiles: [] } })), ]); const all = (templatesRes.data.templates || []).filter(t => t.selectedTemplate); const profileMap = Object.fromEntries((profilesRes.data.profiles || []).map(profile => [profile.id, profile])); setTemplates(all); setProfilesById(profileMap); } catch { setError('Failed to load templates'); } finally { setLoading(false); } }, [businessId]); useEffect(() => { loadTemplates(); }, [loadTemplates]); useEffect(() => { return () => { if (highlightTimeoutRef.current) { window.clearTimeout(highlightTimeoutRef.current); } }; }, []); useEffect(() => { const targetEventSlug = searchParams.get('event'); if (!targetEventSlug || templates.length === 0) return; if (handledFocusSlugRef.current === targetEventSlug) return; const targetTemplate = templates.find(tmpl => tmpl.eventSlug === targetEventSlug); if (!targetTemplate) return; const targetTab = getTabForStatus(targetTemplate.status); if (targetTab && activeTab !== targetTab) { setActiveTab(targetTab); return; } const targetCard = templateCardRefs.current[targetEventSlug]; if (!targetCard) return; handledFocusSlugRef.current = targetEventSlug; targetCard.scrollIntoView({ behavior: 'smooth', block: 'center' }); setHighlightedEventSlug(targetEventSlug); if (highlightTimeoutRef.current) { window.clearTimeout(highlightTimeoutRef.current); } highlightTimeoutRef.current = window.setTimeout(() => { setHighlightedEventSlug(currentSlug => (currentSlug === targetEventSlug ? '' : currentSlug)); highlightTimeoutRef.current = null; }, 2200); }, [activeTab, getTabForStatus, searchParams, templates]); async function handleWhitelistSuccess() { setWhitelistTarget(null); await loadTemplates(); } if (loading) { return (
); } return (

Templates

Track whitelisting status and test your SMS templates.

{error && (
{error}
)}
{templates.length === 0 ? (

No Templates Yet

Generate and select templates in the Events section first.

) : (() => { const publishedTabs = templates.filter(t => t.status === 'whitelisted'); const pendingTabs = templates.filter(t => t.status === 'pending_whitelisting'); const visibleTemplates = activeTab === 'published' ? publishedTabs : pendingTabs; if (visibleTemplates.length === 0) { return (

No templates in {activeTab === 'published' ? 'Published' : 'Pending'}.

); } return (
{visibleTemplates.map(tmpl => { const statusCfg = STATUS_CONFIG[tmpl.status] || STATUS_CONFIG.generated; const boundProfile = tmpl.curlProfileId ? profilesById[tmpl.curlProfileId] || null : null; const isBoundProfileMissing = !boundProfile; const boundProfileMessage = tmpl.curlProfileId ? 'The cURL profile used for this template no longer exists. Re-select this template from Events to continue.' : 'This template is not bound to a cURL profile. Re-select it from Events to continue.'; return (
{ if (node) { templateCardRefs.current[tmpl.eventSlug] = node; } else { delete templateCardRefs.current[tmpl.eventSlug]; } }} className={`rounded-lg bg-white border overflow-hidden transition-all duration-300 ${ highlightedEventSlug === tmpl.eventSlug ? 'border-primary-blue animate-pulse' : 'border-gray-200' }`} >

{tmpl.eventLabel || tmpl.eventSlug.replace(/_/g, ' ')}

{tmpl.eventSlug}

{statusCfg.label}
{boundProfile ? (
{boundProfile.name} {boundProfile.id}
) : (
{boundProfileMessage}
)}
{tmpl.selectedTemplate}
{tmpl.templateId && (

{tmpl.templateId}

)} {tmpl.variableMap && Object.keys(tmpl.variableMap).length > 0 && (
{Object.entries(tmpl.variableMap).map(([key, val]) => (
{key} {val}
))}
)}
{!isBoundProfileMissing && tmpl.status === 'pending_whitelisting' && ( )} {!isBoundProfileMissing && tmpl.status === 'whitelisted' && ( )} {tmpl.status === 'pending_whitelisting' && !isBoundProfileMissing && (

Submit to the DLT portal, then complete publish from here.

)}
); })}
); })()} {whitelistTarget && ( setWhitelistTarget(null)} onSuccess={handleWhitelistSuccess} /> )} {testTarget && ( setTestTarget(null)} /> )}
); }