sms-extension-1777874553/client/src/pages/Providers.jsx

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">&times;</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">&times;</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>
);
}