import { useCallback, useEffect, useState, type FormEvent } from 'react'
import KpiCard from '../components/KpiCard'
import {
esActividadVencida,
esRolConEquipo,
formatoFechaCorta,
hoyLocal,
DESARROLLOS_LEAD,
MOTIVOS_NO_COMPRA,
ORIGENES_LEAD,
textoDiasRestantes,
type LeadMiDia,
type MotivoNoCompra,
type ResumenMiDia,
} from '../lib/mi-dia'
import { cambiarEstadoLead, crearLead, descartarLead, fetchResumenMiDia } from '../services/mi-dia'
import { getSesion } from '../services/auth'
function TituloSeccion({ children }: { children: string }) {
return
{children}
}
function EstadoVacio({ mensaje }: { mensaje: string }) {
return (
)
}
const CLASE_BOTON_ACCION =
'border border-accent px-3 py-1.5 text-xs font-bold uppercase tracking-widest text-accent transition-colors duration-150 hover:bg-accent hover:text-white disabled:opacity-50'
/** Modal de descarte: motivo (dropdown CRM) + comentario opcional */
function ModalDescartar({
lead,
onCancelar,
onConfirmar,
enviando,
}: {
lead: LeadMiDia
onCancelar: () => void
onConfirmar: (motivo: MotivoNoCompra, comentario: string) => void
enviando: boolean
}) {
const [motivo, setMotivo] = useState(MOTIVOS_NO_COMPRA[0])
const [comentario, setComentario] = useState('')
return (
Descartar lead
{lead.nombre}
)
}
/** Fila de lead con acciones rápidas (contactado / intento / cita / descartar) */
function FilaLead({
lead,
onAccion,
onDescartar,
enviando,
}: {
lead: LeadMiDia
onAccion: (estado: string, fecha?: string) => void
onDescartar: () => void
enviando: boolean
}) {
const [conCita, setConCita] = useState(false)
const [fechaCita, setFechaCita] = useState('')
return (
{lead.nombre}
{lead.telefono || 'Sin teléfono'}
{lead.origen ? ` · ${lead.origen}` : ''}
{lead.estado || 'Sin estado'}
{conCita && (
setFechaCita(e.target.value)}
className="border border-neutral-300 px-2 py-1.5 text-xs focus:border-accent focus:outline-none"
/>
)}
)
}
export default function MiDiaPage() {
const nombreVendedor = getSesion()?.usuario.nombre ?? ''
const rol = getSesion()?.usuario.rol ?? 'vendedor'
const [resumen, setResumen] = useState(null)
const [error, setError] = useState(null)
const [cargando, setCargando] = useState(true)
const [enviando, setEnviando] = useState(false)
const [aviso, setAviso] = useState(null)
const [descartando, setDescartando] = useState(null)
// Captura rápida
const [capNombre, setCapNombre] = useState('')
const [capTelefono, setCapTelefono] = useState('')
const [capEmail, setCapEmail] = useState('')
const [capOrigen, setCapOrigen] = useState(ORIGENES_LEAD[0])
const [capDesarrollo, setCapDesarrollo] = useState('')
const [capNotas, setCapNotas] = useState('')
const [capEnviando, setCapEnviando] = useState(false)
const [capMensaje, setCapMensaje] = useState<{ tipo: 'ok' | 'error'; texto: string } | null>(null)
const cargar = useCallback(async () => {
setCargando(true)
try {
setResumen(await fetchResumenMiDia())
setError(null)
} catch (err) {
setError(err instanceof Error ? err.message : 'No se pudo cargar tu día')
} finally {
setCargando(false)
}
}, [])
useEffect(() => {
void cargar()
}, [cargar])
/** Ejecuta una acción sobre un lead y refresca el resumen */
async function ejecutar(accion: () => Promise, mensajeError: string) {
setEnviando(true)
setAviso(null)
try {
await accion()
await cargar()
} catch (err) {
setAviso(err instanceof Error ? err.message : mensajeError)
} finally {
setEnviando(false)
}
}
async function enviarCaptura(e: FormEvent) {
e.preventDefault()
setCapEnviando(true)
setCapMensaje(null)
try {
await crearLead({
nombre: capNombre.trim(),
...(capTelefono.trim() !== '' ? { telefono: capTelefono.trim() } : {}),
...(capEmail.trim() !== '' ? { email: capEmail.trim() } : {}),
...(capOrigen !== '' ? { origen: capOrigen } : {}),
...(capDesarrollo !== '' ? { desarrollo: capDesarrollo } : {}),
...(capNotas.trim() !== '' ? { notas: capNotas.trim() } : {}),
})
setCapMensaje({ tipo: 'ok', texto: 'Lead registrado, se asignará por round-robin' })
setCapNombre('')
setCapTelefono('')
setCapEmail('')
setCapDesarrollo('')
setCapNotas('')
} catch (err) {
setCapMensaje({ tipo: 'error', texto: err instanceof Error ? err.message : 'No se pudo registrar el lead' })
} finally {
setCapEnviando(false)
}
}
const hoy = hoyLocal()
return (
FRASA · Mi día
{nombreVendedor}
{error && (
Sin datos del API — {error}
)}
{aviso && (
{aviso}
)}
{resumen?.sinMapeoMonday && (
Tu usuario aún no está vinculado a Monday; las acciones siguen funcionando
)}
{!resumen && cargando && }
{resumen && (
<>
Mis actividades
{resumen.misActividadesHoy.length === 0 ? (
) : (
)}
{resumen.apartadosPorVencer.length > 0 && (
)}
Mis leads
{resumen.misLeads.length === 0 ? (
) : (
{resumen.misLeads.map((lead) => (
void ejecutar(() => cambiarEstadoLead(lead.id, estado, fecha), 'No se pudo actualizar el lead')}
onDescartar={() => setDescartando(lead)}
/>
))}
)}
{esRolConEquipo(rol) && resumen.equipo && (
Equipo
Leads nuevos sin asignar: {resumen.equipo.leadsNuevosSinAsignar}
{resumen.equipo.porVendedor.length === 0 ? (
) : (
)}
)}
>
)}
{descartando && (
setDescartando(null)}
onConfirmar={(motivo, comentario) => {
const lead = descartando
setDescartando(null)
void ejecutar(
() => descartarLead(lead.id, motivo, comentario === '' ? undefined : comentario),
'No se pudo descartar el lead',
)
}}
/>
)}
)
}