'use client'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import Link from 'next/link'; import { ArrowLeft, Building2, Cable, Check, ImageIcon, Loader2, Power, Shield, Sparkles, Trash2, UserPlus, Users, } from 'lucide-react'; import ClientIntegrationsPanel from '@/components/ClientIntegrationsPanel'; import { PageHeader, SegmentedControl } from '@/components/ui'; import { DASHBOARD_MODULES, DashboardModuleId, TenantModuleMap, TenantSettings, resolveTenantModules, suggestModulesFromIntegrations, } from '@/lib/tenant-modules'; type Tab = 'general' | 'conexiones' | 'accesos' | 'modulos'; type Integ = { provider: string; status?: string; enabled?: boolean; }; type PortalUser = { id: string; email: string; role: string; createdAt: string; lastSignIn: string | null; }; export default function ClientFicha({ tenantId, initialName, }: { tenantId: string; initialName: string; }) { const [tab, setTab] = useState('general'); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [msg, setMsg] = useState(null); const [err, setErr] = useState(null); const [name, setName] = useState(initialName); const [slug, setSlug] = useState(''); const [notes, setNotes] = useState(''); const [followed, setFollowed] = useState(true); const [logoUrl, setLogoUrl] = useState(null); const [settings, setSettings] = useState({}); const [integrations, setIntegrations] = useState([]); const [users, setUsers] = useState([]); const [inviteEmail, setInviteEmail] = useState(''); const [inviteBusy, setInviteBusy] = useState(false); const [tempPassword, setTempPassword] = useState(null); const [modules, setModules] = useState>( () => resolveTenantModules(null, []) ); const logoSrc = logoUrl ? `/api/branding/logo?tenantId=${encodeURIComponent(tenantId)}&t=${encodeURIComponent(logoUrl)}` : null; const load = useCallback(async () => { setLoading(true); setErr(null); try { const [fichaRes, usersRes] = await Promise.all([ fetch(`/api/admin/tenants?tenantId=${encodeURIComponent(tenantId)}`), fetch( `/api/admin/tenants/users?tenantId=${encodeURIComponent(tenantId)}` ), ]); const ficha = await fichaRes.json(); if (!fichaRes.ok) throw new Error(ficha.error || 'Error al cargar ficha'); setName(ficha.tenant.name || initialName); setSlug(ficha.tenant.slug || ''); setNotes(ficha.tenant.notes || ''); setFollowed(ficha.tenant.followed !== false); setLogoUrl(ficha.tenant.logoUrl || null); setSettings(ficha.tenant.settings || {}); setIntegrations(ficha.integrations || []); setModules( resolveTenantModules( ficha.tenant.settings || {}, ficha.integrations || [] ) ); if (usersRes.ok) { const u = await usersRes.json(); setUsers(u.users || []); } } catch (e) { setErr(e instanceof Error ? e.message : 'Error'); } finally { setLoading(false); } }, [tenantId, initialName]); useEffect(() => { void load(); }, [load]); async function patchTenant(body: Record) { setSaving(true); setMsg(null); setErr(null); const res = await fetch('/api/admin/tenants', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tenantId, ...body }), }); const data = await res.json(); setSaving(false); if (!res.ok) { setErr(data.error || 'No se pudo guardar'); return false; } setMsg('Guardado'); return true; } async function saveGeneral() { const ok = await patchTenant({ name, notes, followed }); if (ok) await load(); } async function saveModules() { const next: TenantSettings = { ...settings, modules: modules as TenantModuleMap, modulesLocked: true, }; const ok = await patchTenant({ settings: next }); if (ok) { setSettings(next); setMsg('Módulos del portal cliente actualizados'); } } function applySuggested() { setModules(suggestModulesFromIntegrations(integrations)); } async function onLogoFile(file: File | null) { if (!file) return; setSaving(true); setErr(null); const fd = new FormData(); fd.set('tenantId', tenantId); fd.set('file', file); const res = await fetch('/api/admin/tenants/logo', { method: 'POST', body: fd, }); const data = await res.json(); setSaving(false); if (!res.ok) { setErr(data.error || 'Error al subir logo'); return; } setLogoUrl(data.logoUrl); setMsg('Logo actualizado — se muestra en la vista del cliente'); } async function clearLogo() { const ok = await patchTenant({ logoUrl: null }); if (ok) setLogoUrl(null); } async function invite() { if (!inviteEmail.includes('@')) return; setInviteBusy(true); setTempPassword(null); setErr(null); const res = await fetch('/api/admin/tenants/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tenantId, email: inviteEmail, companyName: name, }), }); const data = await res.json(); setInviteBusy(false); if (!res.ok) { setErr(data.error || 'No se pudo dar acceso'); return; } if (data.temporaryPassword) { setTempPassword(data.temporaryPassword); setMsg(data.hint || 'Usuario creado con contraseña temporal'); } else { setMsg(`Invitación enviada a ${inviteEmail}`); } setInviteEmail(''); await load(); } async function removeUser(userId: string) { if (!confirm('¿Quitar acceso de este usuario al portal?')) return; const res = await fetch('/api/admin/tenants/users', { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tenantId, userId }), }); if (!res.ok) { const data = await res.json(); setErr(data.error || 'Error'); return; } await load(); } async function enterAsClient() { await fetch('/api/admin/active-tenant', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ tenantId }), }); window.location.href = '/dashboard/kpis'; } const connectedCount = useMemo( () => integrations.filter( (i) => i.enabled !== false && i.status === 'connected' ).length, [integrations] ); if (loading) { return (
Cargando ficha…
); } return (
Portafolio
} /> {(msg || err) && (

{err || msg}

)} {tab === 'general' && (
Identidad