bolt-templates-sms-extensio.../client/src/components/TestSmsModal.jsx

106 lines
4.7 KiB
JavaScript

import { useState } from 'react';
import apiClient from '../api/client';
export default function TestSmsModal({ businessId, template, onClose }) {
const [toNumber, setToNumber] = useState('');
const [sending, setSending] = useState(false);
const [result, setResult] = useState(null);
const [error, setError] = useState('');
async function handleSend(e) {
e.preventDefault();
if (!toNumber.trim()) return;
setSending(true);
setError('');
setResult(null);
try {
const res = await apiClient.post(
`/api/businesses/${businessId}/templates/${template.eventSlug}/test`,
{ toNumber: toNumber.trim() }
);
setResult(res.data);
} catch (err) {
setError(err.response?.data?.error || err.response?.data?.details || 'Failed to send test SMS');
} finally {
setSending(false);
}
}
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-gray-900/50 backdrop-blur-sm">
<div className="bg-white border border-gray-200 rounded-lg p-5 w-full max-w-md ">
<div className="w-12 h-12 rounded-full bg-white flex items-center justify-center mx-auto mb-4">
<span className="text-xl">📱</span>
</div>
<h3 className="text-lg font-bold text-gray-800 text-center mb-1">Test SMS</h3>
<p className="text-sm text-gray-500 text-center mb-6">
Enter a phone number to send a real test SMS for <span className="font-semibold text-gray-800 capitalize">{template.eventLabel || template.eventSlug.replace(/_/g, ' ')}</span>
</p>
{!result ? (
<form onSubmit={handleSend} className="space-y-4">
{error && (
<div className="px-4 py-2 rounded-md bg-white border border-gray-200 text-gray-700 text-sm font-medium">
{error}
</div>
)}
<div>
<label className="block text-sm font-semibold text-gray-700 mb-1.5">Phone Number</label>
<input
type="tel"
value={toNumber}
onChange={e => setToNumber(e.target.value)}
placeholder="e.g. 919876543210 (with country code)"
className="w-full px-4 py-2 rounded-lg bg-white border border-gray-300 font-mono text-gray-800 placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-primary-blue text-sm"
autoFocus
required
/>
<p className="text-xs text-gray-400 mt-1.5 font-medium">Include country code without +. Not stored.</p>
</div>
<div className="flex gap-3 pt-2">
<button
type="button"
onClick={onClose}
disabled={sending}
className="flex-1 py-2 rounded-lg border border-gray-300 text-gray-700 hover:bg-gray-50 text-sm font-medium transition disabled:opacity-50"
>
Cancel
</button>
<button
type="submit"
disabled={sending || !toNumber.trim()}
className="flex-1 py-2 rounded-lg bg-green-600 hover:bg-green-700 text-white text-sm font-semibold transition disabled:opacity-50 flex items-center justify-center gap-2"
>
{sending ? <><span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" /> Sending</> : 'Send Test SMS'}
</button>
</div>
</form>
) : (
<div className="space-y-4">
<div className={`px-4 py-2 rounded-lg border text-sm font-medium ${result.success ? 'bg-white border-gray-200 text-green-800' : 'bg-white border-gray-200 text-red-800'}`}>
{result.success ? '✓ SMS sent successfully!' : '✗ SMS send failed'}
{result.statusCode && <span className="ml-2 opacity-60">HTTP {result.statusCode}</span>}
</div>
{result.response && (
<div>
<label className="block text-xs font-bold text-gray-500 uppercase tracking-wider mb-2">Provider Response</label>
<pre className="p-3 bg-white border border-gray-200 rounded-lg text-xs font-mono text-gray-700 overflow-x-auto whitespace-pre-wrap break-all">
{typeof result.response === 'string' ? result.response : JSON.stringify(result.response, null, 2)}
</pre>
</div>
)}
<button
onClick={onClose}
className="w-full py-2 rounded-lg bg-gray-900 hover:bg-gray-800 text-white text-sm font-semibold transition"
>
Close
</button>
</div>
)}
</div>
</div>
);
}