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 fechaEntrega?: string | null
/** Tope de mensualidades: los meses que faltan para la entrega (default 36 sin fecha) */ /** Tope de mensualidades: los meses que faltan para la entrega (default 36 sin fecha) */
maxMensualidades?: number maxMensualidades?: number
/** Enganche adicional capturado (>= 0) */ /** Enganche base del esquema (7.5% del precio, neto del excedente del apartado) */
engancheAdicional: number engancheBase: number
onEngancheAdicional: (v: number) => void /** Enganche total capturado (base + extra); el input vive en el renglón del enganche */
/** Máximo de enganche adicional permitido por el tope del 90% de anticipo */ engancheTotal: number
engancheAdicionalMaximo: number onEngancheTotal: (v: number) => void
/** Máximo de enganche total permitido por el tope del 90% de anticipo */
engancheMax: number
numMensualidades: number numMensualidades: number
onNumMensualidades: (n: number) => void onNumMensualidades: (n: number) => void
escrituracionDiferida: boolean escrituracionDiferida: boolean
@@ -44,9 +46,10 @@ export function EsquemaPanel({
esquema, esquema,
fechaEntrega, fechaEntrega,
maxMensualidades = MENSUALIDADES_MAX, maxMensualidades = MENSUALIDADES_MAX,
engancheAdicional, engancheBase,
onEngancheAdicional, engancheTotal,
engancheAdicionalMaximo, onEngancheTotal,
engancheMax,
numMensualidades, numMensualidades,
onNumMensualidades, onNumMensualidades,
escrituracionDiferida, 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"> <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]"> <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> </dt>
<dd className="text-sm font-medium text-[#111827] dark:text-[#E2E8F0]"> <dd className="flex items-center gap-1.5">
{formatoMoneda(esquema.enganche)} <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> </dd>
</div> </div>
<div className="flex flex-col gap-1 py-2 sm:flex-row sm:items-center sm:justify-between"> <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>
<div className="flex flex-col gap-1 py-2 sm:flex-row sm:items-center sm:justify-between"> <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]"> <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> </dt>
<dd className="text-sm font-semibold text-[#111827] dark:text-[#E2E8F0]"> <dd className="text-sm font-semibold text-[#111827] dark:text-[#E2E8F0]">
{formatoMoneda(esquema.totalPagado)} {formatoMoneda(esquema.totalPagado)}
</dd> </dd>
</div> </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"> <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]"> <dt className="text-sm font-semibold text-[#111827] dark:text-[#E2E8F0]">
Saldo a financiar Saldo a financiar
@@ -163,31 +175,6 @@ export function EsquemaPanel({
</dl> </dl>
</section> </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 */} {/* 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"> <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"> <div className="flex-1">

View File

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