198 lines
8.2 KiB
TypeScript

'use client';
import { useState } from 'react';
import { Bot, Eye, EyeOff, Loader2, CheckCircle2, XCircle, Plug, Building2 } from 'lucide-react';
import { cn } from '@/lib/utils';
interface CloseBotCardProps {
settings: Record<string, string>;
onSave: (updates: Record<string, string>) => Promise<void>;
connected: boolean | null;
onTestConnection: () => Promise<void>;
testing: boolean;
agencyName?: string;
sourcesCount?: number;
}
function maskApiKey(val: string): string {
if (!val || val.length < 10) return val ? '••••••••' : '';
const prefix = val.slice(0, 3);
const suffix = val.slice(-4);
return `${prefix}••••••••${suffix}`;
}
export default function CloseBotCard({ settings, onSave, connected, onTestConnection, testing, agencyName, sourcesCount }: CloseBotCardProps) {
const [apiKey, setApiKey] = useState('');
const [webhookSourceId, setWebhookSourceId] = useState('');
const [showKey, setShowKey] = useState(false);
const [saving, setSaving] = useState(false);
const [editing, setEditing] = useState(false);
const envKey = settings.closebot_api_key || '';
const envSourceId = settings.closebot_webhook_source_id || '';
async function handleSave() {
setSaving(true);
try {
const updates: Record<string, string> = {};
if (apiKey) updates.closebot_api_key = apiKey;
if (webhookSourceId) updates.closebot_webhook_source_id = webhookSourceId;
await onSave(updates);
setEditing(false);
setApiKey('');
setWebhookSourceId('');
} finally {
setSaving(false);
}
}
return (
<div className="card p-6 space-y-5">
{/* Header */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-purple-500/10 border border-purple-500/20">
<Bot className="h-5 w-5 text-purple-400" />
</div>
<div>
<h3 className="text-sm font-semibold text-white">CloseBot Connection</h3>
<p className="text-xs text-slate-500">AI chatbot API credentials</p>
</div>
</div>
{/* Connection status */}
<div className="flex items-center gap-2">
{connected === null ? (
<span className="text-xs text-slate-500">Not tested</span>
) : connected ? (
<>
<div className="h-2.5 w-2.5 rounded-full bg-green-500 shadow-[0_0_6px_rgba(34,197,94,0.5)]" />
<span className="text-xs font-medium text-green-400">Connected</span>
</>
) : (
<>
<div className="h-2.5 w-2.5 rounded-full bg-red-500 shadow-[0_0_6px_rgba(239,68,68,0.5)]" />
<span className="text-xs font-medium text-red-400">Disconnected</span>
</>
)}
</div>
</div>
{/* Agency Info — shown when connected */}
{connected && (agencyName || sourcesCount !== undefined) && (
<div className="rounded-lg border border-green-500/20 bg-green-500/5 p-4 space-y-2.5">
{agencyName && (
<div className="flex items-center gap-2.5">
<Building2 className="h-4 w-4 text-green-400 flex-shrink-0" />
<div>
<p className="text-[10px] uppercase tracking-wider text-slate-500 font-medium">Agency</p>
<p className="text-sm font-semibold text-white">{agencyName}</p>
</div>
</div>
)}
{sourcesCount !== undefined && (
<div className="flex items-center gap-2.5">
<Plug className="h-4 w-4 text-cyan-400 flex-shrink-0" />
<div>
<p className="text-[10px] uppercase tracking-wider text-slate-500 font-medium">Active Sources</p>
<p className="text-sm font-semibold text-white">{sourcesCount} connected</p>
</div>
</div>
)}
</div>
)}
{/* Fields */}
<div className="space-y-4">
{/* API Key */}
<div>
<label className="block text-xs font-medium text-slate-400 mb-1.5">API Key</label>
{editing ? (
<div className="relative">
<input
type={showKey ? 'text' : 'password'}
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
placeholder="cb_xxxxxxxxxxxxxxxxxxxxxxxx"
className="w-full rounded-lg border border-slate-700/50 bg-slate-800/50 px-3 py-2 pr-10 text-sm text-slate-200 placeholder-slate-600 outline-none transition-colors focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 font-mono"
/>
<button
type="button"
onClick={() => setShowKey(!showKey)}
className="absolute right-2 top-1/2 -translate-y-1/2 p-1 text-slate-500 hover:text-slate-300 transition-colors"
>
{showKey ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
</button>
</div>
) : (
<div className="w-full rounded-lg border border-slate-700/30 bg-slate-800/30 px-3 py-2 text-sm text-slate-400 font-mono">
{envKey ? maskApiKey(envKey) : <span className="text-slate-600 italic">Not configured</span>}
</div>
)}
</div>
{/* Webhook Source ID */}
<div>
<label className="block text-xs font-medium text-slate-400 mb-1.5">Webhook Source ID</label>
{editing ? (
<input
type="text"
value={webhookSourceId}
onChange={(e) => setWebhookSourceId(e.target.value)}
placeholder="Source ID from CloseBot webhook settings"
className="w-full rounded-lg border border-slate-700/50 bg-slate-800/50 px-3 py-2 text-sm text-slate-200 placeholder-slate-600 outline-none transition-colors focus:border-cyan-500/50 focus:ring-1 focus:ring-cyan-500/20 font-mono"
/>
) : (
<div className="w-full rounded-lg border border-slate-700/30 bg-slate-800/30 px-3 py-2 text-sm text-slate-400 font-mono">
{envSourceId || <span className="text-slate-600 italic">Not configured</span>}
</div>
)}
</div>
</div>
{/* Actions */}
<div className="flex items-center gap-3 pt-2">
<button
onClick={onTestConnection}
disabled={testing}
className="flex items-center gap-2 rounded-lg border border-slate-700/50 bg-slate-800/50 px-4 py-2 text-xs font-medium text-slate-300 transition-all hover:bg-slate-700/50 hover:text-white disabled:opacity-50"
>
{testing ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : connected ? (
<CheckCircle2 className="h-3.5 w-3.5 text-green-400" />
) : (
<XCircle className="h-3.5 w-3.5 text-slate-500" />
)}
Test Connection
</button>
{editing ? (
<div className="flex items-center gap-2 ml-auto">
<button
onClick={() => { setEditing(false); setApiKey(''); setWebhookSourceId(''); }}
className="rounded-lg border border-slate-700/50 px-4 py-2 text-xs font-medium text-slate-400 transition-colors hover:text-white"
>
Cancel
</button>
<button
onClick={handleSave}
disabled={saving || (!apiKey && !webhookSourceId)}
className="flex items-center gap-2 rounded-lg bg-cyan-500/20 border border-cyan-500/30 px-4 py-2 text-xs font-medium text-cyan-400 transition-all hover:bg-cyan-500/30 disabled:opacity-50"
>
{saving && <Loader2 className="h-3.5 w-3.5 animate-spin" />}
Save Changes
</button>
</div>
) : (
<button
onClick={() => setEditing(true)}
className="ml-auto rounded-lg bg-slate-700/50 border border-slate-600/30 px-4 py-2 text-xs font-medium text-slate-300 transition-all hover:bg-slate-600/50 hover:text-white"
>
Edit Credentials
</button>
)}
</div>
</div>
);
}