177 lines
8.0 KiB
JavaScript
177 lines
8.0 KiB
JavaScript
import { useState, useEffect } from 'react';
|
|
import { useParams } from 'react-router-dom';
|
|
import apiClient from '../api/client';
|
|
|
|
export default function Providers() {
|
|
const { businessId } = useParams();
|
|
const [loading, setLoading] = useState(true);
|
|
const [saving, setSaving] = useState(false);
|
|
const [activeProfile, setActiveProfile] = useState(null);
|
|
const [form, setForm] = useState({
|
|
providerName: '',
|
|
senderId: '',
|
|
dltEntityId: '',
|
|
authKey: '',
|
|
});
|
|
const [error, setError] = useState('');
|
|
const [success, setSuccess] = useState('');
|
|
|
|
useEffect(() => {
|
|
async function load() {
|
|
try {
|
|
const [activeRes, providerRes] = await Promise.all([
|
|
apiClient.get(`/api/businesses/${businessId}/global-sms/active`),
|
|
apiClient.get(`/api/businesses/${businessId}/providers`),
|
|
]);
|
|
|
|
setActiveProfile(activeRes.data?.activeProfile || null);
|
|
setForm({
|
|
providerName: providerRes.data?.providerName || '',
|
|
senderId: providerRes.data?.senderId || '',
|
|
dltEntityId: providerRes.data?.dltEntityId || '',
|
|
authKey: providerRes.data?.authKey || '',
|
|
});
|
|
} catch (err) {
|
|
setError(err.response?.data?.error || 'Failed to load provider configuration');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
load();
|
|
}, [businessId]);
|
|
|
|
function handleChange(field, value) {
|
|
setForm(prev => ({ ...prev, [field]: value }));
|
|
}
|
|
|
|
async function handleSave(e) {
|
|
e.preventDefault();
|
|
setSaving(true);
|
|
setError('');
|
|
setSuccess('');
|
|
if (form.senderId && !/^[A-Za-z]{6}$/.test(form.senderId)) {
|
|
setError('DLT Sender ID must be exactly 6 alphabet characters');
|
|
setSaving(false);
|
|
return;
|
|
}
|
|
try {
|
|
const res = await apiClient.post(`/api/businesses/${businessId}/providers`, form);
|
|
setForm({
|
|
providerName: res.data?.providerName || '',
|
|
senderId: res.data?.senderId || '',
|
|
dltEntityId: res.data?.dltEntityId || '',
|
|
authKey: res.data?.authKey || '',
|
|
});
|
|
setSuccess('Provider configuration saved successfully.');
|
|
} catch (err) {
|
|
setError(err.response?.data?.error || 'Failed to save configuration');
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
}
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center h-64">
|
|
<div className="w-8 h-8 border-2 border-refresh-active border-t-indigo-600 rounded-full animate-spin" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-2xl mx-auto">
|
|
<div className="pb-5 mb-6 border-b border-gray-200">
|
|
<h1 className="text-2xl font-bold text-gray-900 tracking-tight">Provider Configuration</h1>
|
|
<p className="text-sm text-gray-500 mt-1 font-medium">Edit the provider details stored on the active cURL profile.</p>
|
|
{activeProfile && (
|
|
<p className="text-xs text-gray-500 mt-2 font-semibold uppercase tracking-wide">
|
|
Active Profile: {activeProfile.name}
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{error && (
|
|
<div className="mb-6 px-4 py-3 rounded-md bg-red-50 border border-red-200 text-red-700 font-medium text-sm flex items-center justify-between">
|
|
{error}
|
|
<button onClick={() => setError('')} className="text-red-500 hover:text-red-700 font-bold">×</button>
|
|
</div>
|
|
)}
|
|
{success && (
|
|
<div className="mb-6 px-4 py-3 rounded-md bg-green-50 border border-green-200 text-green-700 font-medium text-sm flex items-center justify-between shadow-sm">
|
|
{success}
|
|
<button onClick={() => setSuccess('')} className="text-green-500 hover:text-green-700 font-bold">×</button>
|
|
</div>
|
|
)}
|
|
|
|
<form onSubmit={handleSave} className="bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden">
|
|
<div className="p-6 space-y-6">
|
|
<div>
|
|
<label className={`block text-sm font-semibold mb-1.5 tracking-wide ${!form.providerName ? 'text-error-text' : 'text-text-primary'}`}>
|
|
Provider Name {(!form.providerName) && <span className="text-error-text">*</span>}
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={form.providerName}
|
|
onChange={e => handleChange('providerName', e.target.value)}
|
|
className={`w-full px-4 py-2.5 rounded-lg bg-surface-white border ${!form.providerName ? 'border-error-text focus:ring-error-text' : 'border-border-main focus:ring-primary-blue'} text-text-primary placeholder-placeholder-bg font-medium focus:outline-none focus:ring-2 focus:border-transparent transition text-sm shadow-sm`}
|
|
placeholder="e.g. MSG91, Gupshup"
|
|
/>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
|
<div>
|
|
<label className={`block text-sm font-semibold mb-1.5 tracking-wide ${!form.senderId ? 'text-error-text' : 'text-text-primary'}`}>
|
|
DLT Sender ID {(!form.senderId) && <span className="text-error-text">*</span>}
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={form.senderId}
|
|
onChange={e => handleChange('senderId', e.target.value.toUpperCase())}
|
|
maxLength={6}
|
|
className={`w-full px-4 py-2.5 rounded-lg bg-surface-white border ${!form.senderId ? 'border-error-text focus:ring-error-text' : 'border-border-main focus:ring-primary-blue'} text-text-primary font-mono tracking-widest placeholder-placeholder-bg focus:outline-none focus:ring-2 focus:border-transparent transition text-sm shadow-sm uppercase`}
|
|
placeholder="6 CHARS"
|
|
/>
|
|
<p className="text-xs text-gray-500 mt-2 font-medium">Exactly 6 alphabetic characters (e.g. MOKOBA).</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label className={`block text-sm font-semibold mb-1.5 tracking-wide ${!form.dltEntityId ? 'text-error-text' : 'text-text-primary'}`}>
|
|
DLT Entity ID {(!form.dltEntityId) && <span className="text-error-text">*</span>}
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={form.dltEntityId}
|
|
onChange={e => handleChange('dltEntityId', e.target.value)}
|
|
className={`w-full px-4 py-2.5 rounded-lg bg-surface-white border ${!form.dltEntityId ? 'border-error-text focus:ring-error-text' : 'border-border-main focus:ring-primary-blue'} text-text-primary font-mono placeholder-placeholder-bg focus:outline-none focus:ring-2 focus:border-transparent transition text-sm shadow-sm`}
|
|
placeholder="19-digit DLT PE ID"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-semibold text-text-primary mb-1.5 tracking-wide">API Auth Key <span className="text-text-muted font-normal text-xs">(Optional)</span></label>
|
|
<input
|
|
type="password"
|
|
value={form.authKey}
|
|
onChange={e => handleChange('authKey', e.target.value)}
|
|
className="w-full px-4 py-2.5 rounded-lg bg-surface-white border border-border-main text-text-primary font-mono placeholder-placeholder-bg focus:outline-none focus:ring-2 focus:ring-primary-blue focus:border-transparent transition text-sm shadow-sm"
|
|
placeholder="Authorization key for your SMS provider"
|
|
/>
|
|
<p className="text-xs text-gray-500 mt-2 font-medium">Used as the Authorization header in your SMS requests.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="px-6 py-4 bg-gray-50 border-t border-gray-200 flex justify-end">
|
|
<button
|
|
type="submit"
|
|
disabled={saving}
|
|
className="px-6 py-2.5 rounded-lg bg-primary-blue hover:bg-primary-dark text-white font-semibold text-sm shadow-sm transition 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" /> Saving…</> : 'Save Configuration'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
);
|
|
}
|