'use client'; import React, { useState } from 'react'; import { Plus, Key, Copy, Check, X, ExternalLink, ShieldCheck, Building2, Mail, Lock, Sparkles, Loader2, CheckCircle2 } from 'lucide-react'; interface Tenant { id: string; name: string; slug: string; created_at: string; } interface ClientManagementProps { initialTenants: Tenant[]; } export default function ClientManagement({ initialTenants }: ClientManagementProps) { const [tenants, setTenants] = useState(initialTenants); const [isOpen, setIsOpen] = useState(false); const [loading, setLoading] = useState(false); const [errorMsg, setErrorMsg] = useState(null); // Form state const [companyName, setCompanyName] = useState(''); const [slug, setSlug] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [role, setRole] = useState<'client' | 'team' | 'admin'>('client'); const [autoSlug, setAutoSlug] = useState(true); // Created access card state const [createdClient, setCreatedClient] = useState<{ company: string; slug: string; email: string; password: string; role: string; } | null>(null); const [copied, setCopied] = useState(false); // Auto slug generation from company name const handleNameChange = (val: string) => { setCompanyName(val); if (autoSlug) { const generated = val .toLowerCase() .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') .replace(/[^a-z0-9]+/g, '-') .replace(/(^-|-$)+/g, ''); setSlug(generated); } }; const generateRandomPassword = () => { const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789!@#$%&*'; let pw = ''; for (let i = 0; i < 14; i++) { pw += chars.charAt(Math.floor(Math.random() * chars.length)); } setPassword(pw); }; const handleCreate = async (e: React.FormEvent) => { e.preventDefault(); setErrorMsg(null); setLoading(true); try { const res = await fetch('/api/admin/create-client', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ companyName, slug, email, password, role }) }); const data = await res.json(); if (!res.ok || data.error) { throw new Error(data.error || 'Error al crear cliente'); } // Add to local state if not exists const newTenant: Tenant = { id: data.client.tenant_id, name: companyName, slug: slug, created_at: new Date().toISOString() }; setTenants(prev => { const filtered = prev.filter(t => t.slug !== slug); return [...filtered, newTenant]; }); setCreatedClient({ company: companyName, slug: slug, email: email, password: password, role: role }); // Clear input fields setCompanyName(''); setSlug(''); setEmail(''); setPassword(''); } catch (err: any) { setErrorMsg(err.message || 'Error inesperado'); } finally { setLoading(false); } }; const copyCredentials = () => { if (!createdClient) return; const text = `🔐 *Acceso a tu Centro de Mando — 4Ventures* Empresa: ${createdClient.company} Portal Web: https://app.4ventures.es/login Usuario: ${createdClient.email} Contraseña: ${createdClient.password} Rol: ${createdClient.role === 'client' ? 'Cliente' : 'Agencia'} Este portal cuenta con aislamiento de datos cifrado y métricas en tiempo real.`; navigator.clipboard.writeText(text); setCopied(true); setTimeout(() => setCopied(false), 2500); }; return (
{/* Table Header with Action Button */}

Cartera de Clientes Activos ({tenants.length})

Cada cliente cuenta con aislamiento nativo Row Level Security (RLS) en Postgres.

{/* Tenants Table */}
{tenants.map((t) => ( ))}
Nombre del Cliente Tenant Slug UUID Acceso Portal Estado
{t.name} {t.slug} {t.id} Abrir Portal Activo
{/* Modal: Onboard New Client */} {isOpen && (
{/* Modal Header */}

Alta de Nuevo Cliente

Crea el portal corporativo y activa sus credenciales de acceso

{/* Modal Body */}
{createdClient ? ( /* Success Screen with Credentials */

¡Cliente Creado con Éxito!

El tenant ha sido configurado en Postgres con aislamiento RLS y el usuario ya puede iniciar sesión.

Empresa: {createdClient.company}
URL Acceso: https://app.4ventures.es/login
Email: {createdClient.email}
Contraseña: {createdClient.password}
) : ( /* Creation Form */
{errorMsg && (
{errorMsg}
)}
handleNameChange(e.target.value)} className="w-full pl-9 pr-3 py-2 bg-gray-900 border border-gray-200 rounded-lg text-sm text-gray-900 placeholder-gray-500 focus:outline-none focus:border-brand-500" />
{ setAutoSlug(false); setSlug(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '')); }} className="w-full px-3 py-2 bg-gray-900 border border-gray-200 rounded-lg text-sm font-mono text-brand-400 placeholder-gray-500 focus:outline-none focus:border-brand-500" />
setEmail(e.target.value)} className="w-full pl-9 pr-3 py-2 bg-gray-900 border border-gray-200 rounded-lg text-sm text-gray-900 placeholder-gray-500 focus:outline-none focus:border-brand-500" />
setPassword(e.target.value)} className="w-full pl-9 pr-3 py-2 bg-gray-900 border border-gray-200 rounded-lg text-sm font-mono text-gray-900 placeholder-gray-500 focus:outline-none focus:border-brand-500" />
)}
)}
); }