import { useCallback, useEffect, useState, type FormEvent } from 'react' import { X, User, Calendar, CheckSquare, FileText, Users, Search } from 'lucide-react' import type { LeadMiDia, OportunidadMiDia, ApartadoMiDia } from '../lib/mi-dia' import { formatoFechaCorta } from '../lib/mi-dia' import { listarRecordatorios, crearRecordatorio, type Recordatorio } from '../services/recordatorios' type LeadItem = LeadMiDia | OportunidadMiDia | ApartadoMiDia /** Calcula una clave estable del cliente: nombre normalizado + últimos 10 dígitos del teléfono */ function calcularClienteClave(nombre?: string | null, telefono?: string | null): string { const n = (nombre ?? '').trim().toLowerCase() const t = (telefono ?? '').replace(/\s+/g, '').slice(-10) return `${n}|${t}` } function esLead(item: LeadItem): item is LeadMiDia { return 'email' in item && 'origen' in item && !('modeloInteres' in item) } function esOportunidad(item: LeadItem): item is OportunidadMiDia { return 'modeloInteres' in item } type TabId = 'info' | 'cita' | 'tareas' | 'notas' | 'miembros' const TABS: { id: TabId; titulo: string; icono: typeof User }[] = [ { id: 'info', titulo: 'Información del cliente potencial', icono: User }, { id: 'cita', titulo: 'Reserve o actualice una cita', icono: Calendar }, { id: 'tareas', titulo: 'Tareas', icono: CheckSquare }, { id: 'notas', titulo: 'Notas', icono: FileText }, { id: 'miembros', titulo: 'Objetos de los miembros', icono: Users }, ] export default function DetalleLeadModal({ item, onCerrar }: { item: LeadItem; onCerrar: () => void }) { const esOp = esOportunidad(item) const esLd = esLead(item) const [tab, setTab] = useState('info') // --- Notas --- const [notas, setNotas] = useState([]) const [cargandoNotas, setCargandoNotas] = useState(true) const [notaError, setNotaError] = useState('') const [nuevaNota, setNuevaNota] = useState('') const [enviandoNota, setEnviandoNota] = useState(false) const [busquedaNota, setBusquedaNota] = useState('') const cargarNotas = useCallback(async () => { setCargandoNotas(true) try { const todas = await listarRecordatorios() const clave = calcularClienteClave(item.nombre, (item as any).telefono) setNotas(todas.filter((r) => r.leadId === item.id || (r.clienteClave && r.clienteClave === clave))) } catch { setNotaError('Error al cargar notas.') } finally { setCargandoNotas(false) } }, [item.id]) useEffect(() => { void cargarNotas() }, [cargarNotas]) async function agregarNota(e: FormEvent) { e.preventDefault() if (!nuevaNota.trim()) return setEnviandoNota(true) try { await crearRecordatorio({ titulo: nuevaNota.trim(), leadId: item.id, clienteClave: calcularClienteClave(item.nombre, (item as any).telefono), fechaRecordar: new Date().toISOString() }) setNuevaNota('') await cargarNotas() } catch { setNotaError('No se pudo guardar.') } finally { setEnviandoNota(false) } } useEffect(() => { const h = (e: KeyboardEvent) => { if (e.key === 'Escape') onCerrar() } window.addEventListener('keydown', h) return () => window.removeEventListener('keydown', h) }, [onCerrar]) const notasFiltradas = notas.filter((n) => busquedaNota === '' || n.titulo.toLowerCase().includes(busquedaNota.toLowerCase()) ) return (
{/* Header */}

Editar "{item.nombre}"

Añadir y editar oportunidad Detalles, Actividades, notas y Cita.

{/* Sidebar izquierda */} {/* Contenido */}
{tab === 'info' && (
{(esLd || esOp) && item.telefono && } {(esLd || esOp) && item.email && } {(esLd || esOp) && item.desarrollo && } {esOp && item.modeloInteres && } {esOp && item.unidadCodigo && } {esLd && item.origen && } {esOp && item.fechaCita && } {!esLd && !esOp && (item as ApartadoMiDia).unidadCodigo && } {!esLd && !esOp && (item as ApartadoMiDia).fechaLimite && } {item.ultimoCambio && (

Último cambio: {item.ultimoCambio.detalle} · {item.ultimoCambio.usuario ?? '—'} · {formatoFechaCorta(item.ultimoCambio.fechaISO.slice(0, 10))}

)}
)} {tab === 'cita' && (

Reserve o actualice una cita

{esOp && item.fechaCita ? (

Cita programada

{item.fechaCita.replace('T', ' a las ')}

) : (

No hay cita programada. Use los botones de fase en la lista para agendar una.

)}
)} {tab === 'tareas' && (

Tareas pendientes

{notasFiltradas.filter((n) => !n.completado).length === 0 ? (

No hay tareas pendientes.

) : (
    {notasFiltradas.filter((n) => !n.completado).map((n) => (
  • {n.titulo} {formatoFechaCorta(n.createdAt.slice(0, 10))}
  • ))}
)}
)} {tab === 'notas' && (
{/* Barra de herramientas */}

Notas

setBusquedaNota(e.target.value)} placeholder="Buscar notas" className="w-48 rounded-lg border border-[#D1D5DB] bg-white pl-8 pr-3 py-1.5 text-sm dark:border-[#334155] dark:bg-[#0F172A] dark:text-[#E2E8F0]" />
{/* Botón añadir */} {notaError &&

{notaError}

} {/* Formulario inline */}
setNuevaNota(e.target.value)} placeholder="Escribe tu nota…" disabled={enviandoNota} className="flex-1 rounded-lg border border-[#D1D5DB] bg-white px-3 py-2 text-sm dark:border-[#334155] dark:bg-[#0F172A] dark:text-[#E2E8F0] focus:border-accent focus:ring-2 focus:ring-accent/20 disabled:opacity-50" />
{/* Lista de notas */} {cargandoNotas ? (

Cargando notas…

) : notasFiltradas.length === 0 ? (

Todavía no hay notas

Mantenga un registro de los detalles importantes añadiendo su primera nota

) : (
    {[...notasFiltradas].reverse().map((n) => (
  • {n.titulo}

    {n.descripcion &&

    {n.descripcion}

    }

    {formatoFechaCorta(n.createdAt.slice(0, 10))}

  • ))}
)}
)} {tab === 'miembros' && (

Objetos de los miembros

Información de los miembros del equipo asignados a este lead.

Vendedor: {(item as any).vendedor ?? '—'}

Origen: {esLd ? item.origen : '—'}

Último cambio: {item.ultimoCambio ? `${item.ultimoCambio.detalle} por ${item.ultimoCambio.usuario ?? '—'}` : 'Sin cambios registrados'}

)}
) } function InfoCampo({ etiqueta, valor }: { etiqueta: string; valor: string }) { return (

{etiqueta}

{valor || '—'}

) }