import { useCallback, useEffect, useMemo, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import apiClient from '../api/client'; import { useBusiness } from '../context/BusinessContext'; const BASE_PROFILE_KEYS = new Set(['providerName', 'senderId', 'dltEntityId']); const PENDING_SENDER_ID_PROFILE_NAME = 'Pending Sender ID'; function isPendingSenderIdProfile(profile) { const normalizedName = String(profile?.name || '').trim(); const senderId = String(profile?.provider?.senderId || '').trim(); return (profile?.isAutoNamed === true && !senderId) || normalizedName === PENDING_SENDER_ID_PROFILE_NAME; } function normalizeCurlForDisplay(value) { if (!value) return ''; return String(value) .trim() .replace(/\r\n/g, '\n') .replace(/\\r\\n/g, '\n') .replace(/\\n/g, '\n') .replace(/\\t/g, ' ') .replace(/\\'/g, '\'') .replace(/\\"/g, '"'); } function stripWrappingQuotes(value) { if (!value || value.length < 2) return value; if ( (value.startsWith('\'') && value.endsWith('\'')) || (value.startsWith('"') && value.endsWith('"')) ) { return value.slice(1, -1); } return value; } function formatCurlCommand(normalizedCurl) { if (!normalizedCurl) return ''; let output = normalizedCurl; if (!output.includes('\n')) { output = output .replace(/^curl\s+/, 'curl\n ') .replace(/\s+(--request|-X|--url|--header|-H|--data-raw|--data|-d|--compressed|--location|--insecure|--fail)\b/g, '\n $1'); } return output.replace(/\n{3,}/g, '\n\n').trim(); } function extractCurlBody(normalizedCurl) { if (!normalizedCurl) return ''; const quotedMatch = normalizedCurl.match( /(?:--data-raw|--data|-d)\s+(["'])([\s\S]*?)\1(?=\s+(?:--[a-z-]+|-\w)\b|$)/i, ); if (quotedMatch?.[2]) return stripWrappingQuotes(quotedMatch[2].trim()); const braceMatch = normalizedCurl.match( /(?:--data-raw|--data|-d)\s+({[\s\S]*})(?=\s+(?:--[a-z-]+|-\w)\b|$)/i, ); return braceMatch?.[1]?.trim() || ''; } function buildCurlViewModel(value) { const normalizedCurl = normalizeCurlForDisplay(value); const headers = [ ...normalizedCurl.matchAll(/(?:--header|-H)\s+(?:"([^"]+)"|'([^']+)')/g), ] .map((match) => (match[1] || match[2] || '').trim()) .filter(Boolean); const methodMatch = normalizedCurl.match(/(?:--request|-X)\s+([A-Z]+)/i); const method = (methodMatch?.[1] || (/(?:--data-raw|--data|-d)\b/i.test(normalizedCurl) ? 'POST' : 'GET')).toUpperCase(); const url = normalizedCurl.match(/https?:\/\/[^\s'"]+/i)?.[0] || ''; const rawBody = extractCurlBody(normalizedCurl); let payload = stripWrappingQuotes(rawBody || '').trim(); let prettyPayload = ''; let payloadFormat = ''; if (payload) { try { const parsed = JSON.parse(payload); prettyPayload = JSON.stringify(parsed, null, 2); payloadFormat = 'json'; } catch { prettyPayload = payload; payloadFormat = 'text'; } } let host = ''; try { host = url ? new URL(url).host : ''; } catch { host = ''; } const shellLines = []; if (url) { shellLines.push('curl \\'); shellLines.push(` --request ${method} \\`); shellLines.push(` --url '${url}'${headers.length || rawBody ? ' \\' : ''}`); headers.forEach((header, index) => { const hasTrailingSection = index < headers.length - 1 || Boolean(rawBody); shellLines.push(` --header '${header}'${hasTrailingSection ? ' \\' : ''}`); }); if (rawBody) { shellLines.push(` --data-raw '${payloadFormat === 'json' ? '' : rawBody}'`); } } return { command: shellLines.length > 0 ? shellLines.join('\n') : formatCurlCommand(normalizedCurl), headers, host, method, payload: prettyPayload, payloadFormat, url, }; } function formatUpdatedAt(value) { if (!value) return 'Not updated yet'; try { return new Date(value).toLocaleString(); } catch { return 'Not updated yet'; } } function buildProfilePatchPayload(inputs = [], values = {}) { const provider = {}; const profileInputValues = {}; inputs.forEach((input) => { const rawValue = String(values[input.key] ?? '').trim(); if (!rawValue) return; if (BASE_PROFILE_KEYS.has(input.key)) { provider[input.key] = input.key === 'senderId' ? rawValue.toUpperCase() : rawValue; return; } profileInputValues[input.key] = rawValue; }); return { ...(Object.keys(provider).length > 0 ? { provider } : {}), ...(Object.keys(profileInputValues).length > 0 ? { profileInputValues } : {}), }; } function getInitialFormValues(inputs = []) { return inputs.reduce((accumulator, input) => { accumulator[input.key] = input.secret ? '' : (input.value || ''); return accumulator; }, {}); } function getProfileSummary(profile) { const parts = []; const provider = profile?.provider || {}; const missingCount = profile?.executionReadiness?.missingProfileInputs?.length || 0; if (provider.providerName) parts.push(provider.providerName); if (provider.senderId) parts.push(`Sender ${provider.senderId}`); if (provider.dltEntityId) parts.push('DLT ready'); if (missingCount > 0) parts.push(`${missingCount} pending`); return parts.join(' • ') || 'Profile saved. Complete the required fields to use it everywhere.'; } function ProfileStatusPill({ complete }) { return ( {complete ? 'Ready' : 'Needs Fields'} ); } function InspectorRow({ label, value, valueClassName = '' }) { return (

{label}

{value}

); } export default function Providers() { const { businessId } = useParams(); const navigate = useNavigate(); const { refreshOnboardingState } = useBusiness(); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [profiles, setProfiles] = useState([]); const [activeProfileId, setActiveProfileId] = useState(''); const [selectedProfileId, setSelectedProfileId] = useState(''); const [formValues, setFormValues] = useState({}); const [revealedProfiles, setRevealedProfiles] = useState({}); const [showSecretsByProfileId, setShowSecretsByProfileId] = useState({}); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const globalSmsPath = `/${businessId}/global-sms`; const loadProfiles = useCallback(async () => { try { setLoading(true); const res = await apiClient.get(`/api/businesses/${businessId}/global-sms/profiles`); const fetchedProfiles = res.data?.profiles || []; const nextActiveProfileId = String(res.data?.activeProfileId || ''); setProfiles(fetchedProfiles); setActiveProfileId(nextActiveProfileId); setSelectedProfileId((currentSelectedProfileId) => ( fetchedProfiles.some((profile) => profile.id === currentSelectedProfileId) ? currentSelectedProfileId : '' )); } catch (err) { setError(err.response?.data?.error || 'Failed to load provider profiles'); } finally { setLoading(false); } }, [businessId]); useEffect(() => { loadProfiles(); }, [loadProfiles]); const selectedProfile = useMemo( () => profiles.find((profile) => profile.id === selectedProfileId) || null, [profiles, selectedProfileId], ); const selectedProfileInputs = selectedProfile?.profileInputs || []; const isSelectedProfileRevealed = selectedProfile ? showSecretsByProfileId[selectedProfile.id] === true : false; const selectedRevealData = selectedProfile ? revealedProfiles[selectedProfile.id] : null; const selectedDisplayCurl = selectedProfile ? (isSelectedProfileRevealed ? (selectedRevealData?.rawCurl || selectedProfile.maskedCurl) : selectedProfile.maskedCurl) : ''; const selectedCurlView = useMemo( () => buildCurlViewModel(selectedDisplayCurl), [selectedDisplayCurl], ); const missingInputCount = selectedProfile?.executionReadiness?.missingProfileInputs?.length || 0; const curlWarnings = selectedProfile?.curlAnalysis?.warnings || []; useEffect(() => { if (!selectedProfile) { setFormValues({}); return; } setFormValues(getInitialFormValues(selectedProfile.profileInputs)); }, [selectedProfile]); const ensureRevealData = useCallback(async (profileId) => { if (revealedProfiles[profileId]) return revealedProfiles[profileId]; const res = await apiClient.get(`/api/businesses/${businessId}/global-sms/profiles/${profileId}/reveal`); setRevealedProfiles((current) => ({ ...current, [profileId]: res.data })); return res.data; }, [businessId, revealedProfiles]); function handleSelectProfile(profileId) { setSelectedProfileId(profileId); setError(''); setSuccess(''); } function handleReturnToList() { setSelectedProfileId(''); setError(''); setSuccess(''); } async function handleActivate(profile) { if (!profile?.id) return; try { setError(''); setSuccess(''); await apiClient.post(`/api/businesses/${businessId}/global-sms/profiles/${profile.id}/activate`); setSelectedProfileId(profile.id); await loadProfiles(); await refreshOnboardingState(businessId).catch(() => null); setSuccess(`${profile.name} is now the active profile.`); } catch (err) { setError(err.response?.data?.error || 'Failed to activate profile'); } } async function handleToggleReveal(profile) { if (!profile?.id) return; const shouldReveal = !showSecretsByProfileId[profile.id]; if (shouldReveal) { try { const revealData = await ensureRevealData(profile.id); const revealedValues = (revealData?.profileInputs || []).reduce((accumulator, input) => { accumulator[input.key] = input.value || ''; return accumulator; }, {}); setFormValues((current) => ({ ...current, ...revealedValues })); } catch (err) { setError(err.response?.data?.error || 'Failed to reveal saved values'); return; } } setShowSecretsByProfileId((current) => ({ ...current, [profile.id]: shouldReveal, })); } async function handleCopyCurl(profile) { if (!profile?.id) return; try { const revealData = await ensureRevealData(profile.id); if (!revealData?.rawCurl) return; await navigator.clipboard.writeText(revealData.rawCurl); setSuccess(`Copied ${profile.name} cURL.`); } catch (err) { setError(err.response?.data?.error || 'Failed to copy the cURL command.'); } } async function handleSave(event) { event.preventDefault(); if (!selectedProfile?.id) return; setSaving(true); setError(''); setSuccess(''); try { const payload = buildProfilePatchPayload(selectedProfileInputs, formValues); await apiClient.patch(`/api/businesses/${businessId}/global-sms/profiles/${selectedProfile.id}`, payload); await loadProfiles(); await refreshOnboardingState(businessId).catch(() => null); setSuccess(`Provider configuration saved for ${selectedProfile.name}.`); } catch (err) { setError(err.response?.data?.error || 'Failed to save configuration'); } finally { setSaving(false); } } if (loading) { return (
); } return (

Provider Configuration

Review each saved cURL profile and update the required provider or credential fields without changing the stored cURL.

{error && (
{error}
)} {success && (
{success}
)} {!selectedProfile ? (

Saved Profiles

{profiles.length} total
{profiles.length === 0 ? (

No saved profiles yet

Add and validate a cURL profile from Omni-channel SMS before configuring provider details here.

) : (
{profiles.map((profile) => { const isActive = profile.id === activeProfileId; const complete = profile.executionReadiness?.isSetupComplete === true; return ( ); })}
)}
) : (

{selectedProfile.name}

{selectedProfile.id === activeProfileId && ( Active profile )}

The stored cURL is immutable after validation. You can review it, reveal it, and update the profile fields it depends on.

{selectedProfile.id !== activeProfileId && ( )}
{selectedCurlView.method}

{selectedCurlView.url || 'Endpoint not detected from stored cURL'}

{isSelectedProfileRevealed ? 'Saved values are currently rendered inside this request preview.' : 'Sensitive values stay masked until you explicitly reveal them.'}

Updated {formatUpdatedAt(selectedProfile.updatedAt)}

Shell View

                      {selectedCurlView.command || 'No cURL stored.'}
                    
{selectedCurlView.payload && (

Request Payload

{selectedCurlView.payloadFormat === 'json' ? 'JSON' : 'Text'}
                        {selectedCurlView.payload}
                      
)}
)}
); }