183 lines
8.0 KiB
JavaScript
183 lines
8.0 KiB
JavaScript
import { useState, useEffect } from 'react';
|
|
import apiClient from '../api/client';
|
|
|
|
export default function WhitelistModal({ businessId, template, onClose, onSuccess }) {
|
|
const [templateId, setTemplateId] = useState('');
|
|
const [toNumber, setToNumber] = useState('');
|
|
const [saving, setSaving] = useState(false);
|
|
const [error, setError] = useState('');
|
|
|
|
const [providers, setProviders] = useState(null);
|
|
const [form, setForm] = useState({ providerName: '', senderId: '', dltEntityId: '' });
|
|
const [loadingProviders, setLoadingProviders] = useState(true);
|
|
|
|
useEffect(() => {
|
|
async function fetchProviders() {
|
|
try {
|
|
const res = await apiClient.get(`/api/businesses/${businessId}/providers`);
|
|
setProviders(res.data || {});
|
|
setForm({
|
|
providerName: res.data?.providerName || '',
|
|
senderId: res.data?.senderId || '',
|
|
dltEntityId: res.data?.dltEntityId || ''
|
|
});
|
|
} catch {
|
|
setProviders({});
|
|
} finally {
|
|
setLoadingProviders(false);
|
|
}
|
|
}
|
|
fetchProviders();
|
|
}, [businessId]);
|
|
|
|
async function handleSubmit(e) {
|
|
e.preventDefault();
|
|
if (!templateId.trim() || !toNumber.trim()) return;
|
|
setSaving(true);
|
|
setError('');
|
|
try {
|
|
await apiClient.post(`/api/businesses/${businessId}/templates/${template.eventSlug}/publish`, {
|
|
templateId: templateId.trim(),
|
|
toNumber: toNumber.trim(),
|
|
providerName: form.providerName,
|
|
senderId: form.senderId.toUpperCase(),
|
|
dltEntityId: form.dltEntityId
|
|
});
|
|
onSuccess(template.eventSlug, templateId.trim());
|
|
} catch (err) {
|
|
if (err.response?.data?.missingFields) {
|
|
setError(`Missing provider fields: ${err.response.data.missingFields.join(', ')}`);
|
|
} else {
|
|
setError(err.response?.data?.error || 'Failed to publish template');
|
|
}
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
}
|
|
|
|
const missingName = !providers?.providerName;
|
|
const missingSender = !providers?.senderId;
|
|
const missingDlt = !providers?.dltEntityId;
|
|
const hasMissingProviders = missingName || missingSender || missingDlt;
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center bg-gray-900/50 backdrop-blur-sm overflow-y-auto pt-10 pb-10">
|
|
<div className="bg-surface-white border border-border-main rounded-xl p-8 w-full max-w-md shadow-xl my-auto">
|
|
<div className="w-12 h-12 rounded-full bg-orange-bg flex items-center justify-center mx-auto mb-4">
|
|
<span className="text-xl">✅</span>
|
|
</div>
|
|
<h3 className="text-lg font-bold text-text-primary text-center mb-1">Publish Template</h3>
|
|
<p className="text-sm text-text-muted text-center mb-1">
|
|
Provide your DLT details and a test number to publish:
|
|
</p>
|
|
<p className="text-sm font-semibold text-text-primary text-center mb-6 capitalize">
|
|
{template.eventLabel || template.eventSlug.replace(/_/g, ' ')}
|
|
</p>
|
|
|
|
{error && (
|
|
<div className="mb-4 px-4 py-2.5 rounded-md text-error-text bg-delayed-bg border border-delayed-border text-sm font-medium">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
{loadingProviders ? (
|
|
<div className="flex justify-center p-4">
|
|
<span className="w-6 h-6 border-2 border-spinner-track border-t-primary-blue rounded-full animate-spin" />
|
|
</div>
|
|
) : (
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-semibold text-text-primary mb-1.5">DLT Template ID</label>
|
|
<input
|
|
type="text"
|
|
value={templateId}
|
|
onChange={e => setTemplateId(e.target.value)}
|
|
placeholder="e.g. 1234567890987654321"
|
|
className="w-full px-4 py-2.5 rounded-lg bg-page-bg border border-border-main font-mono text-text-primary placeholder-placeholder-bg focus:outline-none focus:ring-2 focus:ring-primary-blue text-sm"
|
|
autoFocus
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-semibold text-text-primary mb-1.5">Destination Phone Number</label>
|
|
<input
|
|
type="text"
|
|
value={toNumber}
|
|
onChange={e => setToNumber(e.target.value)}
|
|
placeholder="e.g. 919876543210"
|
|
className="w-full px-4 py-2.5 rounded-lg bg-page-bg border border-border-main font-mono text-text-primary placeholder-placeholder-bg focus:outline-none focus:ring-2 focus:ring-primary-blue text-sm"
|
|
required
|
|
/>
|
|
<p className="text-xs text-text-muted mt-1">Number to send the initial request on publish</p>
|
|
</div>
|
|
|
|
{hasMissingProviders && (
|
|
<div className="pt-2 pb-2">
|
|
<p className="text-xs text-error-text font-bold mb-3">You MUST provide missing provider details before publishing.</p>
|
|
<div className="space-y-3 p-4 bg-delayed-bg border border-delayed-border rounded-lg">
|
|
{missingName && (
|
|
<div>
|
|
<label className="block text-xs font-semibold text-text-primary mb-1">Provider Name</label>
|
|
<input
|
|
type="text"
|
|
value={form.providerName}
|
|
onChange={e => setForm({ ...form, providerName: e.target.value })}
|
|
className="w-full px-3 py-2 rounded border border-border-main bg-surface-white text-sm"
|
|
required
|
|
/>
|
|
</div>
|
|
)}
|
|
{missingSender && (
|
|
<div>
|
|
<label className="block text-xs font-semibold text-text-primary mb-1">Sender ID (6 Chars)</label>
|
|
<input
|
|
type="text"
|
|
value={form.senderId}
|
|
onChange={e => setForm({ ...form, senderId: e.target.value.toUpperCase() })}
|
|
maxLength={6}
|
|
className="w-full px-3 py-2 rounded border border-border-main bg-surface-white text-sm font-mono uppercase"
|
|
required
|
|
/>
|
|
</div>
|
|
)}
|
|
{missingDlt && (
|
|
<div>
|
|
<label className="block text-xs font-semibold text-text-primary mb-1">DLT Entity ID</label>
|
|
<input
|
|
type="text"
|
|
value={form.dltEntityId}
|
|
onChange={e => setForm({ ...form, dltEntityId: e.target.value })}
|
|
className="w-full px-3 py-2 rounded border border-border-main bg-surface-white text-sm font-mono"
|
|
required
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<div className="flex gap-3 pt-4">
|
|
<button
|
|
type="button"
|
|
onClick={onClose}
|
|
disabled={saving}
|
|
className="flex-1 py-2.5 rounded-lg border border-border-main text-text-primary hover:bg-page-bg text-sm font-medium transition disabled:opacity-50"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
disabled={saving || !templateId.trim() || !toNumber.trim() || (hasMissingProviders && (!form.providerName || !form.senderId || !form.dltEntityId))}
|
|
className="flex-1 py-2.5 rounded-lg bg-primary-blue hover:bg-primary-dark text-white text-sm font-semibold transition shadow-sm disabled:opacity-50 flex items-center justify-center gap-2"
|
|
>
|
|
{saving ? <><span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" /> Publishing…</> : 'Publish'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|