import type { AjustesCredito, ResultadoOpcionCredito, TipoCredito } from '../lib/opciones-credito' import { formatoMoneda, formatoPorcentaje } from '../lib/formatters' interface Props { opciones: ResultadoOpcionCredito[] ajustes: Record onCambiarAjustes: (tipo: TipoCredito, ajustes: AjustesCredito) => void elegida: TipoCredito onElegir: (tipo: TipoCredito) => void } const cardBase = 'flex flex-col rounded-xl border border-[#E5E7EB] bg-white p-5 shadow-sm dark:border-[#1E293B] dark:bg-[#0F172A]' const etiqueta = 'mb-1 block text-xs font-medium text-[#4B5563] dark:text-[#CBD5E1]' const inputClase = 'w-full rounded-lg border border-[#D1D5DB] bg-white px-3 py-2 text-sm transition-colors focus:border-accent focus:ring-2 focus:ring-accent/20 focus:outline-none dark:bg-[#0F172A] dark:border-[#334155] dark:text-[#E2E8F0]' const fila = 'flex flex-col gap-1 py-1.5 sm:flex-row sm:items-center sm:justify-between' const filaEtiqueta = 'text-sm text-[#64748B] dark:text-[#94A3B8]' const filaMonto = 'text-sm font-medium text-[#111827] dark:text-[#E2E8F0]' /** Clamp básico de los ajustes al cambiar un input (evita estados inválidos) */ function ajustar(tipo: TipoCredito, previo: AjustesCredito, parcial: Partial): AjustesCredito { const siguiente = { ...previo, ...parcial } if (tipo === 'SEMI_CONTADO') { const pct = Number(siguiente.pctContado ?? 0) siguiente.pctContado = Math.min(1, Math.max(0, Number.isFinite(pct) ? pct : 0)) } const tasa = Number(siguiente.tasaAnual) siguiente.tasaAnual = Math.min(0.5, Math.max(0, Number.isFinite(tasa) ? tasa : 0)) const plazo = Number(siguiente.plazoAnos) siguiente.plazoAnos = Math.min(35, Math.max(1, Number.isFinite(plazo) ? Math.round(plazo) : 20)) return siguiente } function Fila({ titulo, monto, negritas = false }: { titulo: string; monto: string; negritas?: boolean }) { return (
{titulo}
{monto}
) } function TarjetaOpcion({ r, ajuste, elegida, onCambiarAjustes, onElegir, }: { r: ResultadoOpcionCredito ajuste: AjustesCredito elegida: boolean onCambiarAjustes: (tipo: TipoCredito, ajustes: AjustesCredito) => void onElegir: (tipo: TipoCredito) => void }) { const conCredito = r.tipo !== 'CONTADO' const esSemi = r.tipo === 'SEMI_CONTADO' return (

{r.titulo}

{r.descuento > 0 && ( Descuento {formatoPorcentaje(r.descuento)} )}
{!r.esquemaVigente ? (

El apartado supera el 10% del precio final con descuento; revisa el monto de apartado.

) : ( <>
{r.tipo === 'CONTADO' ? ( <>
Se liquida de contado
{formatoMoneda(r.saldoAFinanciar)}
) : ( )} {esSemi && ( )}
{esSemi && (
{ const v = Number(e.target.value) const pct = Number.isFinite(v) ? Math.min(100, Math.max(0, v)) / 100 : 0 onCambiarAjustes(r.tipo, ajustar(r.tipo, ajuste, { pctContado: pct })) }} className={inputClase} />
)} {conCredito && ( <>
{ const v = Number(e.target.value) onCambiarAjustes( r.tipo, ajustar(r.tipo, ajuste, { tasaAnual: Number.isFinite(v) ? v / 100 : 0 }), ) }} className={inputClase} />

Mensualidad estimada

{formatoMoneda(r.mensualidadEstimada ?? 0)}

Mensualidad estimada con tasa y plazo ajustables; sujeta a aprobación del banco. No constituye preaprobación.

)} )}
) } /** * 4 opciones de financiamiento del saldo (84.5% del precio final) sobre el * esquema FRASA 2026: Crédito Bancario, Infonavit-Cofinavit, Semi Contado y * Contado. Cada tarjeta ajusta su tasa/plazo (y % de contado para la semi), * estima la mensualidad del crédito y permite elegir la propuesta que sale * en el PDF. */ export function OpcionesCredito({ opciones, ajustes, onCambiarAjustes, elegida, onElegir }: Props) { return (

Opciones de financiamiento del saldo (84.5% del precio final)

{opciones.map((r) => ( ))}
) }