'use client' import { useState, useEffect } from 'react' import { Save, Plus, Trash2, Bot, MessageSquare, Key, Link as LinkIcon, Lock, Send, CheckCircle2, XCircle, Loader2, Sparkles, Box } from 'lucide-react' import { cn } from '@/lib/utils' import { AIServiceConfig, AppSettings } from '@/lib/types' import { DashboardLayout } from '@/components/layout/DashboardLayout' export default function SettingsPage() { const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [settings, setSettings] = useState({ telegram: { botToken: '', chatId: '' }, aiProviders: [] }) const [message, setMessage] = useState<{ text: string, type: 'success' | 'error' } | null>(null) // Test loading states const [testingTelegram, setTestingTelegram] = useState(false) const [testingAI, setTestingAI] = useState(null) const [detectingModels, setDetectingModels] = useState(null) const [availableModels, setAvailableModels] = useState>({}) useEffect(() => { fetch('/api/settings') .then(res => res.json()) .then(data => { setSettings(data) setLoading(false) }) .catch(err => { console.error(err) setLoading(false) setMessage({ text: 'Error cargando configuración', type: 'error' }) }) }, []) const handleSave = async () => { setSaving(true) setMessage(null) try { const res = await fetch('/api/settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(settings) }) if (!res.ok) throw new Error('Error saving') setMessage({ text: 'Configuración guardada correctamente', type: 'success' }) } catch (err) { setMessage({ text: 'Error al guardar la configuración', type: 'error' }) } finally { setSaving(false) } } const testTelegram = async () => { setTestingTelegram(true) setMessage(null) try { const res = await fetch('/api/test/telegram', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(settings.telegram) }) const data = await res.json() if (data.success) { setMessage({ text: 'Mensaje de prueba enviado con éxito ✅', type: 'success' }) } else { setMessage({ text: `Error: ${data.error}`, type: 'error' }) } } catch (err: any) { setMessage({ text: 'Error de conexión al probar Telegram', type: 'error' }) } finally { setTestingTelegram(false) } } const testAI = async (provider: AIServiceConfig) => { setTestingAI(provider.id) setMessage(null) try { const res = await fetch('/api/test/ai', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(provider) }) const data = await res.json() if (data.success) { setMessage({ text: `Conexión exitosa con ${provider.model || provider.name} (${data.latency}ms) ✅`, type: 'success' }) } else { setMessage({ text: `Error con ${provider.name}: ${data.error}`, type: 'error' }) } } catch (err) { setMessage({ text: 'Error al conectar con el proveedor', type: 'error' }) } finally { setTestingAI(null) } } const detectModels = async (provider: AIServiceConfig) => { setDetectingModels(provider.id) setMessage(null) try { const res = await fetch('/api/proxy/models', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ endpoint: provider.endpoint, token: provider.token }) }) const data = await res.json() if (data.success && data.models.length > 0) { setAvailableModels(prev => ({ ...prev, [provider.id]: data.models })) // Auto select first if none selected if (!provider.model) { updateProvider(provider.id, 'model', data.models[0]) } setMessage({ text: `Se detectaron ${data.models.length} modelos ✅`, type: 'success' }) } else { setMessage({ text: `No se pudieron detectar modelos. Ingrésalo manualmente.`, type: 'error' }) } } catch (err) { console.error(err) setMessage({ text: 'Error al consultar modelos', type: 'error' }) } finally { setDetectingModels(null) } } const addProvider = () => { if (settings.aiProviders.length >= 3) return setSettings(prev => ({ ...prev, aiProviders: [ ...prev.aiProviders, { id: crypto.randomUUID(), name: '', endpoint: '', token: '', model: '' } ] })) } const removeProvider = (id: string) => { setSettings(prev => ({ ...prev, aiProviders: prev.aiProviders.filter(p => p.id !== id) })) } const updateProvider = (id: string, field: keyof AIServiceConfig, value: string) => { setSettings(prev => ({ ...prev, aiProviders: prev.aiProviders.map(p => p.id === id ? { ...p, [field]: value } : p ) })) } if (loading) return
Cargando configuración...
return (

Configuración

Gestiona la integración con Telegram e Inteligencia Artificial.

{message && (
{message.type === 'success' ? : } {message.text}
)} {/* Telegram Configuration */}

Telegram Bot

setSettings({ ...settings, telegram: { ...settings.telegram, botToken: e.target.value } })} className="w-full px-4 py-3 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 text-white font-mono text-sm outline-none transition-all placeholder:text-slate-700" />

El token que te da @BotFather.

setSettings({ ...settings, telegram: { ...settings.telegram, chatId: e.target.value } })} className="w-full px-4 py-3 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500 text-white font-mono text-sm outline-none transition-all placeholder:text-slate-700" />

Tu ID numérico de Telegram (o el ID del grupo).

{/* AI Providers Configuration */}

Proveedores de IA

{settings.aiProviders.length === 0 && (
No hay proveedores de IA configurados. Agrega uno para empezar.
)} {settings.aiProviders.map((provider, index) => (

Provider #{index + 1}

updateProvider(provider.id, 'name', e.target.value)} className="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-purple-500/50 focus:border-purple-500 text-white text-sm outline-none" />
updateProvider(provider.id, 'endpoint', e.target.value)} className="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-purple-500/50 focus:border-purple-500 text-white font-mono text-sm outline-none" />
updateProvider(provider.id, 'token', e.target.value)} className="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-purple-500/50 focus:border-purple-500 text-white font-mono text-sm outline-none" />
{/* Model Selection */}
{availableModels[provider.id] ? ( ) : ( updateProvider(provider.id, 'model', e.target.value)} className="w-full px-4 py-2.5 bg-slate-950 border border-slate-800 rounded-lg focus:ring-2 focus:ring-purple-500/50 focus:border-purple-500 text-white font-mono text-sm outline-none" /> )}
))}
) }