feat(cotizador): enganche editable directo en su renglon (min 7.5%, tope 90% de anticipo); se quita el espacio separado de adicional

This commit is contained in:
2026-09-29 21:28:53 +00:00
parent 013e13b9df
commit cb1c36b669
2 changed files with 40 additions and 48 deletions

View File

@@ -16,11 +16,13 @@ interface Props {
fechaEntrega?: string | null
/** Tope de mensualidades: los meses que faltan para la entrega (default 36 sin fecha) */
maxMensualidades?: number
/** Enganche adicional capturado (>= 0) */
engancheAdicional: number
onEngancheAdicional: (v: number) => void
/** Máximo de enganche adicional permitido por el tope del 90% de anticipo */
engancheAdicionalMaximo: number
/** Enganche base del esquema (7.5% del precio, neto del excedente del apartado) */
engancheBase: number
/** Enganche total capturado (base + extra); el input vive en el renglón del enganche */
engancheTotal: number
onEngancheTotal: (v: number) => void
/** Máximo de enganche total permitido por el tope del 90% de anticipo */
engancheMax: number
numMensualidades: number
onNumMensualidades: (n: number) => void
escrituracionDiferida: boolean
@@ -44,9 +46,10 @@ export function EsquemaPanel({
esquema,
fechaEntrega,
maxMensualidades = MENSUALIDADES_MAX,
engancheAdicional,
onEngancheAdicional,
engancheAdicionalMaximo,
engancheBase,
engancheTotal,
onEngancheTotal,
engancheMax,
numMensualidades,
onNumMensualidades,
escrituracionDiferida,
@@ -116,10 +119,24 @@ export function EsquemaPanel({
)}
<div className="flex flex-col gap-1 py-2 sm:flex-row sm:items-center sm:justify-between">
<dt className="text-sm text-[#64748B] dark:text-[#94A3B8]">
Enganche ({formatoPorcentaje(PCT_ENGANCHE)}) diferible
Enganche diferible
<span className="mt-0.5 block text-xs font-normal text-[#64748B] dark:text-[#94A3B8]">
Mín. {formatoPorcentaje(PCT_ENGANCHE)} del precio; puedes subirlo hasta el tope del{' '}
{formatoPorcentaje(PCT_ENGANCHE_MAXIMO)} de anticipo total
</span>
</dt>
<dd className="text-sm font-medium text-[#111827] dark:text-[#E2E8F0]">
{formatoMoneda(esquema.enganche)}
<dd className="flex items-center gap-1.5">
<span className="text-sm text-[#64748B] dark:text-[#94A3B8]">$</span>
<input
id="enganche-diferible"
type="number"
min={engancheBase}
max={engancheMax}
step={1000}
value={Math.round(engancheTotal)}
onChange={(e) => onEngancheTotal(e.target.value === '' ? engancheBase : Number(e.target.value))}
className="w-36 rounded-lg border border-[#D1D5DB] bg-white px-3 py-1.5 text-right text-sm font-medium text-[#111827] transition-colors focus:border-accent focus:ring-2 focus:ring-accent/20 focus:outline-none dark:bg-[#0F172A] dark:border-[#334155] dark:text-[#E2E8F0]"
/>
</dd>
</div>
<div className="flex flex-col gap-1 py-2 sm:flex-row sm:items-center sm:justify-between">
@@ -135,20 +152,15 @@ export function EsquemaPanel({
</div>
<div className="flex flex-col gap-1 py-2 sm:flex-row sm:items-center sm:justify-between">
<dt className="text-sm font-semibold text-[#111827] dark:text-[#E2E8F0]">
Total del esquema (15.5% del precio)
Total del esquema
<span className="ml-2 text-xs font-normal text-[#64748B] dark:text-[#94A3B8]">
{formatoPorcentaje(esquema.totalPagado / esquema.precio)} del precio
</span>
</dt>
<dd className="text-sm font-semibold text-[#111827] dark:text-[#E2E8F0]">
{formatoMoneda(esquema.totalPagado)}
</dd>
</div>
{esquema.engancheAdicional > 0 && (
<div className="flex flex-col gap-1 py-2 sm:flex-row sm:items-center sm:justify-between">
<dt className="text-sm text-[#64748B] dark:text-[#94A3B8]">Enganche adicional</dt>
<dd className="text-sm font-medium text-[#111827] dark:text-[#E2E8F0]">
{formatoMoneda(esquema.engancheAdicional)}
</dd>
</div>
)}
<div className="flex flex-col gap-1 py-2 sm:flex-row sm:items-center sm:justify-between">
<dt className="text-sm font-semibold text-[#111827] dark:text-[#E2E8F0]">
Saldo a financiar
@@ -163,31 +175,6 @@ export function EsquemaPanel({
</dl>
</section>
{/* Enganche adicional: anticipo extra hasta completar 90% del precio */}
<section className="rounded-xl border border-[#E5E7EB] bg-white p-6 dark:border-[#1E293B] dark:bg-[#1E293B] shadow-sm">
<label htmlFor="enganche-adicional" className="mb-2 block text-xs font-medium text-[#4B5563] dark:text-[#CBD5E1]">
Enganche adicional (opcional)
</label>
<div className="flex items-center gap-2">
<span className="text-sm text-[#64748B] dark:text-[#94A3B8]">$</span>
<input
id="enganche-adicional"
type="number"
min={0}
max={engancheAdicionalMaximo}
step={1000}
value={engancheAdicional === 0 ? '' : engancheAdicional}
placeholder="0"
onChange={(e) => onEngancheAdicional(e.target.value === '' ? 0 : Number(e.target.value))}
className="w-full rounded-lg border border-[#D1D5DB] bg-white px-3 py-2.5 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]"
/>
</div>
<p className="mt-1 text-xs text-[#64748B] dark:text-[#94A3B8]">
Anticipo extra del cliente; máximo {formatoMoneda(engancheAdicionalMaximo)} para no pasar del{' '}
{formatoPorcentaje(PCT_ENGANCHE_MAXIMO)} de enganche total (queda un mínimo del 10% por financiar)
</p>
</section>
{/* Controles del diferimiento */}
<section className="flex flex-col gap-4 rounded-xl border border-[#E5E7EB] bg-white p-6 dark:border-[#1E293B] dark:bg-[#1E293B] shadow-sm sm:flex-row sm:items-end">
<div className="flex-1">

View File

@@ -792,9 +792,14 @@ export default function CotizadorPage() {
esquema={resultado}
fechaEntrega={unidad?.fechaEntrega ?? null}
maxMensualidades={unidad ? mensualidadesParaUnidad(unidad) : MENSUALIDADES_MAX}
engancheAdicional={engancheAdicional}
onEngancheAdicional={setEngancheAdicional}
engancheAdicionalMaximo={engancheAdicionalMaximo}
engancheBase={resultado.enganche}
engancheTotal={resultado.enganche + engancheAdicional}
onEngancheTotal={(v) =>
setEngancheAdicional(
Math.min(Math.max(0, v - resultado.enganche), engancheAdicionalMaximo),
)
}
engancheMax={resultado.enganche + engancheAdicionalMaximo}
numMensualidades={numMensualidades}
onNumMensualidades={setNumMensualidades}
escrituracionDiferida={escrituracionDiferida}