import { useCallback, useEffect, useMemo, useRef, 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'; const WORKSPACE_TABS = [ { id: 'authorization', label: 'Authorization' }, { id: 'headers', label: 'Headers' }, { id: 'body', label: 'Body' }, { id: 'curl', label: 'Raw cURL' }, ]; const CURL_DATA_FLAGS = new Set(['--data', '--data-raw', '--data-binary', '-d']); function formatAuthMode(value) { const normalized = String(value || '').trim().toLowerCase(); if (!normalized) return 'Not inferred'; if (normalized === 'api_key') return 'API Key'; if (normalized === 'bearer') return 'Bearer Token'; if (normalized === 'basic') return 'Basic Auth'; return normalized.charAt(0).toUpperCase() + normalized.slice(1); } 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 getInputInitialValues(inputs = []) { return inputs.reduce((accumulator, input) => { accumulator[input.key] = input.value || ''; return accumulator; }, {}); } function mergeProfileInputs(...groups) { const mergedInputs = new Map(); groups.forEach((group) => { (Array.isArray(group) ? group : []).forEach((input) => { const key = String(input?.key || '').trim(); if (!key || input?.source === 'runtime') return; const current = mergedInputs.get(key) || {}; mergedInputs.set(key, { ...current, ...input, key, label: input?.label || current.label || key, required: input?.required !== false || current.required === true, secret: input?.secret === true || current.secret === true, hasValue: input?.hasValue === true || current.hasValue === true, maskedValue: input?.maskedValue || current.maskedValue || '', value: Object.prototype.hasOwnProperty.call(input || {}, 'value') ? input.value : (current.value || ''), }); }); }); return Array.from(mergedInputs.values()); } function isRelevantPersistentProfileInput(input, missingInputKeys = new Set()) { if (!input || input.source === 'runtime') return false; if (input.secret !== true) return false; const token = String(input.token || '').trim(); if (token) return true; return missingInputKeys.has(String(input.key || '').trim()); } function skipShellIndentation(input, index) { let cursor = index; while (cursor < input.length && /[\t \f\v\u00a0]/.test(input[cursor])) { cursor += 1; } return cursor; } function normalizeCurlCommand(command) { const input = String(command || '') .replace(/\r\n/g, '\n') .replace(/\r/g, '\n'); let output = ''; let quote = null; for (let index = 0; index < input.length; index += 1) { const char = input[index]; if (quote === '\'') { output += char; if (char === '\'') quote = null; continue; } if (quote === '"') { output += char; if (char === '\\' && index + 1 < input.length) { output += input[index + 1]; index += 1; continue; } if (char === '"') { quote = null; } continue; } if (char === '\'' || char === '"') { quote = char; output += char; continue; } if (char === '\\') { const nextChar = input[index + 1]; if (nextChar === '\n') { output += ' '; index = skipShellIndentation(input, index + 2) - 1; continue; } if (nextChar === 'n') { output += ' '; index = skipShellIndentation(input, index + 2) - 1; continue; } if (nextChar === 'r' && input[index + 2] === 'n') { output += ' '; index = skipShellIndentation(input, index + 3) - 1; continue; } } output += char; } return output.trim(); } function tokenizeCurlCommand(command) { const input = normalizeCurlCommand(command); const tokens = []; let current = ''; let quote = null; let escaping = false; for (let index = 0; index < input.length; index += 1) { const char = input[index]; if (escaping) { current += char; escaping = false; continue; } if (quote === '\'') { if (char === '\'') { quote = null; } else { current += char; } continue; } if (quote === '"') { if (char === '"') { quote = null; continue; } if (char === '\\') { const nextChar = input[index + 1]; if (nextChar) { current += nextChar; index += 1; continue; } } current += char; continue; } if (char === '\\') { escaping = true; continue; } if (char === '\'' || char === '"') { quote = char; continue; } if (/\s/.test(char)) { if (current) { tokens.push(current); current = ''; } continue; } current += char; } if (current) { tokens.push(current); } return tokens; } function getCurlDataArguments(args = []) { const dataArgs = []; for (let index = 0; index < args.length; index += 1) { const argument = args[index]; if (CURL_DATA_FLAGS.has(argument) && index + 1 < args.length) { dataArgs.push(String(args[index + 1] || '')); index += 1; continue; } const flag = Array.from(CURL_DATA_FLAGS).find((entry) => argument.startsWith(`${entry}=`)); if (flag) { dataArgs.push(argument.slice(flag.length + 1)); } } return dataArgs; } function parseStructuredBodyValue(value = '') { const trimmed = String(value || '').trim(); if (!trimmed) return null; try { return JSON.parse(trimmed); } catch { if (!(trimmed.startsWith('"') && trimmed.endsWith('"'))) { return null; } } try { const parsedString = JSON.parse(trimmed); if (typeof parsedString !== 'string') { return parsedString; } return JSON.parse(parsedString); } catch { return null; } } function isLikelyFormEncoded(value = '') { const normalized = String(value || '').trim(); return normalized.includes('=') && !normalized.startsWith('{') && !normalized.startsWith('['); } function buildBodyPreviewFromCurl(curlStr = '') { const source = String(curlStr || '').trim(); if (!source) { return { type: 'empty', label: 'Body', content: '' }; } if (!source.toLowerCase().startsWith('curl')) { return { type: 'empty', label: 'Body', content: '' }; } try { const tokens = tokenizeCurlCommand(source); if (tokens.length === 0 || tokens[0] !== 'curl') { return { type: 'empty', label: 'Body', content: '' }; } const dataArgs = getCurlDataArguments(tokens.slice(1)); if (dataArgs.length === 0) { return { type: 'none', label: 'No Body', content: 'No request body detected.' }; } if (dataArgs.length === 1) { const structuredValue = parseStructuredBodyValue(dataArgs[0]); if (structuredValue !== null) { return { type: 'json', label: 'JSON Body', content: JSON.stringify(structuredValue, null, 2), }; } } const allFormEncoded = dataArgs.every((entry) => isLikelyFormEncoded(entry) && parseStructuredBodyValue(entry) === null); if (allFormEncoded) { const formLines = []; dataArgs.forEach((entry) => { const params = new URLSearchParams(String(entry || '')); Array.from(params.entries()).forEach(([key, value]) => { formLines.push(`${key}=${value}`); }); }); return { type: 'form', label: 'Form Body', content: formLines.join('\n'), }; } return { type: 'text', label: dataArgs.length === 1 ? 'Raw Body' : 'Combined Body', content: dataArgs.join('\n\n'), }; } catch { return { type: 'invalid', label: 'Body', content: 'Unable to parse the request body from this cURL.', }; } } function cloneRequestPreview(requestPreview = null) { if (!requestPreview || typeof requestPreview !== 'object') { return { method: 'POST', url: '', maskedUrl: '', urlMasked: false, headers: [], }; } return { method: requestPreview.method || 'POST', url: requestPreview.url || '', maskedUrl: requestPreview.maskedUrl || requestPreview.url || '', urlMasked: requestPreview.urlMasked === true, headers: Array.isArray(requestPreview.headers) ? requestPreview.headers.map((header, index) => ({ id: header?.id || `header-${index}`, key: header?.key || '', value: header?.value || '', maskedValue: header?.maskedValue || header?.value || '', masked: header?.masked === true, secret: header?.secret === true, enabled: header?.enabled !== false, })) : [], }; } function areRequestPreviewsEqual(left = null, right = null) { const leftRequest = cloneRequestPreview(left); const rightRequest = cloneRequestPreview(right); if (leftRequest.url !== rightRequest.url) return false; if (leftRequest.headers.length !== rightRequest.headers.length) return false; return leftRequest.headers.every((header, index) => { const comparison = rightRequest.headers[index]; if (!comparison) return false; return header.id === comparison.id && header.key === comparison.key && header.value === comparison.value && header.enabled === comparison.enabled; }); } function hasInputFormChanges(initialValues = {}, currentValues = {}) { const keys = new Set([ ...Object.keys(initialValues || {}), ...Object.keys(currentValues || {}), ]); for (const key of keys) { if (String(initialValues?.[key] ?? '') !== String(currentValues?.[key] ?? '')) { return true; } } return false; } function mergeRevealIntoRequestPreview(targetRequest, revealRequest, options = {}) { const baseRequest = cloneRequestPreview(targetRequest); const revealedRequest = cloneRequestPreview(revealRequest); const force = options.force === true; return { ...baseRequest, url: force || baseRequest.urlMasked ? (revealedRequest.url || baseRequest.url) : baseRequest.url, maskedUrl: revealedRequest.maskedUrl || baseRequest.maskedUrl, urlMasked: force ? false : baseRequest.urlMasked, headers: baseRequest.headers.map((header, index) => { const revealedHeader = revealedRequest.headers.find((candidate) => candidate.id === header.id) || revealedRequest.headers[index]; if (!revealedHeader) return header; const remainsMaskable = header.masked === true || revealedHeader.masked === true; if (force || remainsMaskable) { return { ...header, value: revealedHeader.value, maskedValue: revealedHeader.maskedValue || header.maskedValue, masked: remainsMaskable, secret: revealedHeader.secret ?? header.secret, }; } return { ...header, maskedValue: revealedHeader.maskedValue || header.maskedValue, secret: revealedHeader.secret ?? header.secret, }; }), }; } function DeleteProfileModal({ preview, loading, deleting, onCancel, onConfirm }) { if (!preview) return null; const impactedTemplates = Array.isArray(preview.impactedTemplates) ? preview.impactedTemplates : []; return (

Delete cURL Profile

{preview.profile?.name || 'This profile'} will be deleted. Bound templates will be removed, but event definitions will stay.

{loading ? (
Loading bound templates…
) : impactedTemplates.length > 0 ? ( <>

Affected templates

{impactedTemplates.map((template) => (

{template.eventLabel || template.eventSlug}

{template.status || 'generated'}
{template.templateId && (

DLT Template ID: {template.templateId}

)}
))}
) : (
No templates are currently bound to this profile.
)}
); } export default function GlobalSms() { const { businessId } = useParams(); const navigate = useNavigate(); const { isSetupComplete, setHasGlobalSms, setIsSetupComplete } = useBusiness(); const hasLoadedProfilesRef = useRef(false); const requestPreviewVersionRef = useRef({}); const urlEditorRef = useRef(null); const [initialLoading, setInitialLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [profiles, setProfiles] = useState([]); const [activeProfileId, setActiveProfileId] = useState(null); const [selectedProfileId, setSelectedProfileId] = useState(null); const [workspaceTab, setWorkspaceTab] = useState('authorization'); const [attemptedSave, setAttemptedSave] = useState(false); const [showImportModal, setShowImportModal] = useState(false); const [saving, setSaving] = useState(false); const [savingInputs, setSavingInputs] = useState(false); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const [formCurl, setFormCurl] = useState(''); const [formSetActive, setFormSetActive] = useState(true); const [inputForm, setInputForm] = useState({}); const [requestDrafts, setRequestDrafts] = useState({}); const [savedRequestPreviews, setSavedRequestPreviews] = useState({}); const [editingUrlProfileId, setEditingUrlProfileId] = useState(''); const [editingHeader, setEditingHeader] = useState(null); const [revealedProfiles, setRevealedProfiles] = useState({}); const [curlVisibleProfileIds, setCurlVisibleProfileIds] = useState({}); const [headerVisibleProfileIds, setHeaderVisibleProfileIds] = useState({}); const [inputVisibility, setInputVisibility] = useState({}); const [deletePreview, setDeletePreview] = useState(null); const [loadingDeleteImpactId, setLoadingDeleteImpactId] = useState(''); const [deletingProfileId, setDeletingProfileId] = useState(''); const selectedProfile = useMemo( () => profiles.find((profile) => profile.id === selectedProfileId) || profiles.find((profile) => profile.id === activeProfileId) || profiles[0] || null, [profiles, selectedProfileId, activeProfileId], ); const profileInputs = useMemo( () => (selectedProfile?.profileInputs || []).filter((input) => input.source !== 'runtime'), [selectedProfile], ); const missingInputs = useMemo( () => selectedProfile?.executionReadiness?.missingProfileInputs || [], [selectedProfile], ); const missingInputKeys = useMemo( () => new Set(missingInputs.map((input) => input.key)), [missingInputs], ); const authorizationInputs = useMemo( () => mergeProfileInputs( profileInputs.filter((input) => isRelevantPersistentProfileInput(input, missingInputKeys)), missingInputs, ), [profileInputs, missingInputKeys, missingInputs], ); const isSelectedProfileActive = selectedProfile?.id === activeProfileId; const initialInputValues = useMemo(() => getInputInitialValues(authorizationInputs), [authorizationInputs]); const selectedSavedRequest = useMemo( () => (selectedProfile ? cloneRequestPreview(savedRequestPreviews[selectedProfile.id] || selectedProfile.requestPreview) : null), [savedRequestPreviews, selectedProfile], ); const selectedRequestDraft = useMemo( () => (selectedProfile ? cloneRequestPreview(requestDrafts[selectedProfile.id] || selectedProfile.requestPreview) : null), [requestDrafts, selectedProfile], ); const hasRequestChanges = useMemo( () => (selectedProfile ? !areRequestPreviewsEqual(selectedRequestDraft, selectedSavedRequest) : false), [selectedProfile, selectedRequestDraft, selectedSavedRequest], ); const hasInputChanges = useMemo( () => (selectedProfile ? hasInputFormChanges(initialInputValues, inputForm) : false), [initialInputValues, inputForm, selectedProfile], ); const hasPendingProfileChanges = selectedProfile ? (hasRequestChanges || hasInputChanges) : false; const eventsPath = `/${businessId}/events`; const loadProfiles = useCallback(async ({ background = false } = {}) => { try { if (background) { setRefreshing(true); } else { setInitialLoading(true); } const response = await apiClient.get(`/api/businesses/${businessId}/global-sms/profiles`); const fetchedProfiles = response.data?.profiles || []; const nextActiveProfileId = response.data?.activeProfileId || null; const nextActiveProfile = fetchedProfiles.find((profile) => profile.id === nextActiveProfileId) || null; const nextIsSetupComplete = nextActiveProfile?.executionReadiness?.isSetupComplete === true; setProfiles(fetchedProfiles); setActiveProfileId(nextActiveProfileId); setHasGlobalSms(fetchedProfiles.length > 0); setIsSetupComplete(nextIsSetupComplete); setError(''); return { profiles: fetchedProfiles, activeProfile: nextActiveProfile, activeProfileId: nextActiveProfileId, hasProfile: !!nextActiveProfile, complete: nextIsSetupComplete, }; } catch { setError('Failed to load cURL profiles'); setHasGlobalSms(false); setIsSetupComplete(false); return { profiles: [], activeProfile: null, activeProfileId: null, hasProfile: false, complete: false, }; } finally { if (background) { setRefreshing(false); } else { hasLoadedProfilesRef.current = true; setInitialLoading(false); } } }, [businessId, setHasGlobalSms, setIsSetupComplete]); useEffect(() => { loadProfiles({ background: hasLoadedProfilesRef.current }); }, [loadProfiles]); useEffect(() => { if (!profiles.length) { setSelectedProfileId(null); return; } if (selectedProfileId && profiles.some((profile) => profile.id === selectedProfileId)) { return; } setSelectedProfileId(activeProfileId || profiles[0]?.id || null); }, [profiles, activeProfileId, selectedProfileId]); useEffect(() => { setAttemptedSave(false); setInputForm(initialInputValues); }, [initialInputValues, selectedProfile?.id]); useEffect(() => { if (!success) return undefined; const timeoutId = window.setTimeout(() => setSuccess(''), 2800); return () => window.clearTimeout(timeoutId); }, [success]); useEffect(() => { if (!selectedProfile?.id) return; const nextVersion = selectedProfile.updatedAt || `${selectedProfile.id}:${selectedProfile.requestPreview?.headers?.length || 0}`; if (requestPreviewVersionRef.current[selectedProfile.id] === nextVersion) return; const nextRequestPreview = cloneRequestPreview(selectedProfile.requestPreview); requestPreviewVersionRef.current[selectedProfile.id] = nextVersion; setSavedRequestPreviews((current) => ({ ...current, [selectedProfile.id]: nextRequestPreview, })); setRequestDrafts((current) => ({ ...current, [selectedProfile.id]: cloneRequestPreview(nextRequestPreview), })); setRevealedProfiles((current) => { if (!current[selectedProfile.id]) return current; const nextState = { ...current }; delete nextState[selectedProfile.id]; return nextState; }); setCurlVisibleProfileIds((current) => { if (!current[selectedProfile.id]) return current; const nextState = { ...current }; delete nextState[selectedProfile.id]; return nextState; }); setHeaderVisibleProfileIds((current) => { if (!current[selectedProfile.id]) return current; const nextState = { ...current }; delete nextState[selectedProfile.id]; return nextState; }); }, [selectedProfile]); const clearTransientRevealState = useCallback(() => { setCurlVisibleProfileIds({}); setHeaderVisibleProfileIds({}); setInputVisibility({}); }, []); useEffect(() => { setEditingHeader(null); setEditingUrlProfileId(''); clearTransientRevealState(); }, [clearTransientRevealState, workspaceTab, selectedProfile?.id]); useEffect(() => { const handleHide = () => { setEditingHeader(null); setEditingUrlProfileId(''); clearTransientRevealState(); }; const handleVisibilityChange = () => { if (document.hidden) { handleHide(); } }; window.addEventListener('blur', handleHide); document.addEventListener('visibilitychange', handleVisibilityChange); return () => { window.removeEventListener('blur', handleHide); document.removeEventListener('visibilitychange', handleVisibilityChange); }; }, [clearTransientRevealState]); useEffect(() => { if (!editingUrlProfileId || !urlEditorRef.current) return; urlEditorRef.current.focus(); urlEditorRef.current.select(); }, [editingUrlProfileId, selectedProfile?.id, selectedRequestDraft?.url, selectedRequestDraft?.maskedUrl, selectedRequestDraft?.urlMasked]); const ensureRevealData = useCallback(async (profileId) => { if (revealedProfiles[profileId]) return revealedProfiles[profileId]; const response = await apiClient.get(`/api/businesses/${businessId}/global-sms/profiles/${profileId}/reveal`); setRevealedProfiles((current) => ({ ...current, [profileId]: response.data })); return response.data; }, [businessId, revealedProfiles]); const applyRevealedRequestPreview = useCallback((profileId, revealRequestPreview) => { if (!profileId || !revealRequestPreview) return; setSavedRequestPreviews((current) => ({ ...current, [profileId]: mergeRevealIntoRequestPreview(current[profileId], revealRequestPreview, { force: true }), })); setRequestDrafts((current) => ({ ...current, [profileId]: mergeRevealIntoRequestPreview(current[profileId], revealRequestPreview), })); }, []); const updateRequestDraft = useCallback((profileId, updater) => { if (!profileId) return; setRequestDrafts((current) => { const baseDraft = cloneRequestPreview(current[profileId] || selectedProfile?.requestPreview); const nextDraft = typeof updater === 'function' ? updater(baseDraft) : updater; return { ...current, [profileId]: cloneRequestPreview(nextDraft), }; }); }, [selectedProfile]); const mergeRequestPayloadWithReveal = useCallback((draftRequest, savedRequest, revealRequest) => { const draft = cloneRequestPreview(draftRequest); const saved = cloneRequestPreview(savedRequest); const revealed = cloneRequestPreview(revealRequest); return { url: saved.urlMasked && draft.url === saved.url ? (revealed.url || draft.url) : draft.url, headers: draft.headers.map((header, index) => { const savedHeader = saved.headers.find((candidate) => candidate.id === header.id) || saved.headers[index]; const revealedHeader = revealed.headers.find((candidate) => candidate.id === header.id) || revealed.headers[index]; const shouldRestoreRevealedValue = savedHeader?.masked === true && header.value === savedHeader.value; return { id: header.id, key: header.key, value: shouldRestoreRevealedValue ? (revealedHeader?.value || header.value) : header.value, enabled: header.enabled !== false, }; }), }; }, []); async function handleSubmit(event) { event.preventDefault(); if (!formCurl.trim()) return; setSaving(true); setError(''); setSuccess(''); const shouldAutoAdvance = !isSetupComplete; try { const response = await apiClient.post(`/api/businesses/${businessId}/global-sms/profiles`, { rawCurl: formCurl.trim(), setActive: formSetActive, }); const createdProfileId = response.data?.id || response.data?.profile?.id || null; setFormCurl(''); setFormSetActive(true); setSuccess('Profile created successfully.'); setShowImportModal(false); const nextState = await loadProfiles({ background: true }); const nextSelectedProfileId = createdProfileId || nextState.activeProfileId || nextState.profiles[0]?.id || null; const createdProfile = nextState.profiles.find((profile) => profile.id === nextSelectedProfileId) || null; setSelectedProfileId(nextSelectedProfileId); setWorkspaceTab(createdProfile?.executionReadiness?.missingProfileInputs?.length ? 'authorization' : 'headers'); if (shouldAutoAdvance && nextState.complete) { navigate(eventsPath); } } catch (saveError) { setError(saveError.response?.data?.error || 'Failed to save cURL profile'); } finally { setSaving(false); } } async function handleActivate(profileId) { const shouldAutoAdvance = !isSetupComplete; setError(''); setSuccess(''); try { await apiClient.post(`/api/businesses/${businessId}/global-sms/profiles/${profileId}/activate`); const nextState = await loadProfiles({ background: true }); setSelectedProfileId(profileId); setSuccess('Active profile updated.'); if (shouldAutoAdvance && nextState.complete) { navigate(eventsPath); } } catch (activateError) { setError(activateError.response?.data?.error || 'Failed to activate profile'); } } async function handleCopyCurl(profile) { try { const revealData = await ensureRevealData(profile.id); const textToCopy = revealData?.rawCurl || profile.maskedCurl || ''; if (!textToCopy) return; await navigator.clipboard.writeText(textToCopy); setSuccess(`Copied ${profile.name} cURL.`); } catch { setError('Failed to copy the cURL command.'); } } async function handleToggleCurl(profile) { setError(''); if (!curlVisibleProfileIds[profile.id]) { try { const revealData = await ensureRevealData(profile.id); applyRevealedRequestPreview(profile.id, revealData?.requestPreview); } catch (revealError) { setError(revealError.response?.data?.error || 'Failed to reveal stored cURL'); return; } } setCurlVisibleProfileIds((current) => ({ ...current, [profile.id]: !current[profile.id], })); } async function handleToggleHeaderReveal(profile, header) { if (!profile?.id || !header?.id || header.masked !== true) return; setError(''); const currentVisibility = headerVisibleProfileIds[profile.id]?.[header.id] === true; if (!currentVisibility) { try { const revealData = await ensureRevealData(profile.id); applyRevealedRequestPreview(profile.id, revealData?.requestPreview); } catch (revealError) { setError(revealError.response?.data?.error || 'Failed to reveal stored header value'); return; } } setHeaderVisibleProfileIds((current) => ({ ...current, [profile.id]: { ...(current[profile.id] || {}), [header.id]: !currentVisibility, }, })); } async function handleToggleInputVisibility(input) { if (!selectedProfile?.id || !input?.key || input.secret !== true) return; const isVisible = inputVisibility[input.key] === true; if (!isVisible && input.hasValue) { try { await ensureRevealData(selectedProfile.id); } catch (revealError) { setError(revealError.response?.data?.error || 'Failed to reveal stored authorization value'); return; } } setInputVisibility((current) => ({ ...current, [input.key]: !isVisible, })); } async function handleBeginUrlEdit() { if (!selectedProfile?.id) return; if (selectedSavedRequest?.urlMasked) { try { const revealData = await ensureRevealData(selectedProfile.id); applyRevealedRequestPreview(selectedProfile.id, revealData?.requestPreview); } catch (revealError) { setError(revealError.response?.data?.error || 'Failed to reveal stored request URL'); return; } } setEditingUrlProfileId(selectedProfile.id); } async function handleBeginHeaderEdit(headerId, field, header) { if (!selectedProfile?.id) return; if (field === 'value' && header?.masked) { try { const revealData = await ensureRevealData(selectedProfile.id); applyRevealedRequestPreview(selectedProfile.id, revealData?.requestPreview); } catch (revealError) { setError(revealError.response?.data?.error || 'Failed to reveal stored header value'); return; } setHeaderVisibleProfileIds((current) => ({ ...current, [selectedProfile.id]: { ...(current[selectedProfile.id] || {}), [headerId]: true, }, })); } setEditingHeader({ profileId: selectedProfile.id, headerId, field, }); } async function handleSaveSelectedProfile() { if (!selectedProfile?.id) return; if (!hasPendingProfileChanges) return; setSavingInputs(true); setError(''); setSuccess(''); const shouldAutoAdvance = !isSetupComplete && isSelectedProfileActive; try { const payload = {}; if (hasRequestChanges && selectedRequestDraft && selectedSavedRequest) { let requestPayload = { url: selectedRequestDraft.url, headers: selectedRequestDraft.headers.map((header) => ({ id: header.id, key: header.key, value: header.value, enabled: header.enabled !== false, })), }; const needsRevealMerge = selectedSavedRequest.urlMasked || selectedSavedRequest.headers.some((header) => header.masked); if (needsRevealMerge) { const revealData = await ensureRevealData(selectedProfile.id); applyRevealedRequestPreview(selectedProfile.id, revealData?.requestPreview); requestPayload = mergeRequestPayloadWithReveal(selectedRequestDraft, selectedSavedRequest, revealData?.requestPreview); } payload.request = requestPayload; } if (hasInputChanges) { Object.assign(payload, buildProfilePatchPayload(authorizationInputs, inputForm)); } if (!Object.keys(payload).length) { return; } await apiClient.patch(`/api/businesses/${businessId}/global-sms/profiles/${selectedProfile.id}`, payload); const nextState = await loadProfiles({ background: true }); setSelectedProfileId(selectedProfile.id); setSuccess('Changes saved.'); if (shouldAutoAdvance && nextState.complete) { navigate(eventsPath); } } catch (patchError) { setError(patchError.response?.data?.error || 'Failed to save profile changes'); } finally { setSavingInputs(false); } } async function handleDeleteRequest(profile) { setError(''); setDeletePreview({ profile: { id: profile.id, name: profile.name, }, impactedTemplates: null, }); setLoadingDeleteImpactId(profile.id); try { const response = await apiClient.get(`/api/businesses/${businessId}/global-sms/profiles/${profile.id}/delete-impact`); setDeletePreview(response.data); } catch (deleteImpactError) { setDeletePreview(null); setError(deleteImpactError.response?.data?.error || 'Failed to load delete impact'); } finally { setLoadingDeleteImpactId(''); } } async function handleDeleteConfirm() { if (!deletePreview?.profile?.id) return; const deletedProfileId = deletePreview.profile.id; setDeletingProfileId(deletedProfileId); setError(''); setSuccess(''); try { await apiClient.delete(`/api/businesses/${businessId}/global-sms/profiles/${deletedProfileId}`); setDeletePreview(null); setRevealedProfiles((current) => { const nextState = { ...current }; delete nextState[deletedProfileId]; return nextState; }); setCurlVisibleProfileIds((current) => { const nextState = { ...current }; delete nextState[deletedProfileId]; return nextState; }); setHeaderVisibleProfileIds((current) => { const nextState = { ...current }; delete nextState[deletedProfileId]; return nextState; }); setSavedRequestPreviews((current) => { const nextState = { ...current }; delete nextState[deletedProfileId]; return nextState; }); setRequestDrafts((current) => { const nextState = { ...current }; delete nextState[deletedProfileId]; return nextState; }); const nextState = await loadProfiles({ background: true }); setSelectedProfileId(nextState.activeProfileId || nextState.profiles[0]?.id || null); setSuccess('Profile deleted successfully.'); } catch (deleteError) { setError(deleteError.response?.data?.error || 'Failed to delete profile'); } finally { setDeletingProfileId(''); } } const selectedRevealData = selectedProfile ? revealedProfiles[selectedProfile.id] : null; const isSelectedCurlVisible = selectedProfile ? curlVisibleProfileIds[selectedProfile.id] === true : false; const selectedDisplayCurl = selectedProfile ? (isSelectedCurlVisible ? (selectedRevealData?.rawCurl || selectedProfile.maskedCurl || selectedProfile.curlAnalysis?.normalizedCurlTemplate || '') : (selectedProfile.maskedCurl || selectedProfile.curlAnalysis?.normalizedCurlTemplate || '')) : ''; const selectedBodyPreview = useMemo( () => buildBodyPreviewFromCurl(selectedProfile ? selectedDisplayCurl : formCurl), [formCurl, selectedDisplayCurl, selectedProfile], ); const selectedRevealProfileInputs = useMemo( () => Object.fromEntries( (selectedRevealData?.profileInputs || []).map((input) => [input.key, input]), ), [selectedRevealData], ); const selectedHeaderVisibility = selectedProfile ? (headerVisibleProfileIds[selectedProfile.id] || {}) : {}; const selectedRequestUrl = selectedRequestDraft ? ((editingUrlProfileId === selectedProfile?.id || !selectedRequestDraft.urlMasked) ? selectedRequestDraft.url : selectedRequestDraft.maskedUrl) : ''; const handleValidateAndSave = async (event) => { event.preventDefault(); if (saving || savingInputs) return; if (selectedProfile) { const hasEmpty = missingInputs.some((input) => !String(inputForm[input.key] || '').trim()); if (hasEmpty) { setAttemptedSave(true); setWorkspaceTab('authorization'); return; } await handleSaveSelectedProfile(); return; } if (!selectedProfile && formCurl) { await handleSubmit(new Event('submit')); } }; const beautifyCurl = (curlStr) => { if (!curlStr) return 'No cURL stored.'; let out = curlStr .replace(/ (-X|--request) /g, ' \\\n $1 ') .replace(/ (--url) /g, ' \\\n $1 ') .replace(/ (-H|--header) /g, ' \\\n $1 ') .replace(/ (-d|--data-raw|--data-binary|--data) /g, ' \\\n $1 '); out = out.replace(/(-d|--data-raw|--data-binary|--data)\s+'([^]*?)'/m, (match, flag, body) => { try { return `${flag} '\n${JSON.stringify(JSON.parse(body), null, 2)}\n'`; } catch { return match; } }); out = out.replace(/(-d|--data-raw|--data-binary|--data)\s+"([^]*?)"/m, (match, flag, body) => { try { return `${flag} "\n${JSON.stringify(JSON.parse(body.replace(/\\"/g, '"')), null, 2)}\n"`; } catch { return match; } }); return out; }; if (initialLoading) { return (
); } return ( <> setDeletePreview(null)} onConfirm={handleDeleteConfirm} /> {showImportModal && (

Request Builder

Import Provider cURL