135 lines
6.5 KiB
TypeScript
135 lines
6.5 KiB
TypeScript
import { useForm, FormProvider } from 'react-hook-form'
|
|
import { useEffect } from 'react'
|
|
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import type { ViviendaCredito, TipoCredito } from '../types'
|
|
import { viviendaCreditoSchema } from '../lib/schemas'
|
|
import { CampoTexto, CampoSelect, MensajeError } from '../components/Campos'
|
|
import SectionCard from '../components/SectionCard'
|
|
import Navegacion from './Navegacion'
|
|
import type { PasoProps } from './DatosGeneralesForm'
|
|
import { PROYECTOS, MODELOS_POR_PROYECTO, TIPOS_CREDITO, TIPOS_PRODUCTO } from '../data/constantes'
|
|
|
|
/** Paso 6: datos de la vivienda, crédito e identificación oficial (PLD) */
|
|
export default function ViviendaCreditoForm({ valores, onAnterior, onSiguiente }: PasoProps<ViviendaCredito>) {
|
|
const metodos = useForm<ViviendaCredito>({
|
|
resolver: zodResolver(viviendaCreditoSchema),
|
|
defaultValues: valores,
|
|
mode: 'onSubmit',
|
|
})
|
|
const proyecto = metodos.watch('proyecto')
|
|
const tiposSeleccionados = metodos.watch('tiposCredito') ?? []
|
|
type Proyecto = (typeof PROYECTOS)[number]
|
|
const modelos = MODELOS_POR_PROYECTO[proyecto as Proyecto]
|
|
|
|
// Al cambiar de proyecto, limpia el modelo si no aplica en la lista nueva
|
|
useEffect(() => {
|
|
const actual = metodos.getValues('modelo')
|
|
if (modelos && actual && !modelos.includes(actual)) {
|
|
metodos.setValue('modelo', '', { shouldDirty: true })
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [proyecto])
|
|
|
|
/**
|
|
* El tipo de crédito es de selección ÚNICA (Infonavit, Apoyo Infonavit,
|
|
* Cofinavit, Segundo crédito, etc.). Se guarda como arreglo de un elemento
|
|
* para no cambiar el contrato de datos; volver a tocar el elegido lo quita.
|
|
*/
|
|
const seleccionarCredito = (valor: TipoCredito) => {
|
|
const actuales = metodos.getValues('tiposCredito') ?? []
|
|
metodos.setValue('tiposCredito', actuales.includes(valor) ? [] : [valor], { shouldDirty: true })
|
|
}
|
|
|
|
return (
|
|
<FormProvider {...metodos}>
|
|
<form onSubmit={metodos.handleSubmit(onSiguiente)} noValidate>
|
|
<div>
|
|
<SectionCard titulo="Datos vivienda">
|
|
<CampoSelect
|
|
nombre="proyecto"
|
|
etiqueta="Proyecto"
|
|
requerido
|
|
opciones={PROYECTOS.map((p) => ({ valor: p, etiqueta: p === 'OTRO' ? 'Otro' : p }))}
|
|
/>
|
|
{proyecto === 'OTRO' && (
|
|
<CampoTexto nombre="proyectoOtro" etiqueta="Nombre del proyecto" requerido />
|
|
)}
|
|
{modelos ? (
|
|
<CampoSelect
|
|
nombre="modelo"
|
|
etiqueta="Modelo"
|
|
opciones={modelos.map((m) => ({ valor: m, etiqueta: m }))}
|
|
/>
|
|
) : (
|
|
<CampoTexto nombre="modelo" etiqueta="Modelo" />
|
|
)}
|
|
<CampoTexto nombre="manzana" etiqueta="Manzana" />
|
|
<CampoTexto nombre="lote" etiqueta="Lote" />
|
|
<CampoTexto nombre="ubicacion" etiqueta="Ubicación" />
|
|
<CampoTexto nombre="superficieM2" etiqueta="Superficie (m²)" />
|
|
<CampoTexto nombre="valorVenta" etiqueta="Valor venta ($)" />
|
|
<CampoTexto nombre="enganche" etiqueta="Enganche ($)" />
|
|
<CampoTexto nombre="equipamientoAdicional" etiqueta="Equipamiento adicional" />
|
|
<CampoTexto nombre="fechaPagoEnganche" etiqueta="Fecha pago de enganche" tipo="date" />
|
|
<CampoTexto nombre="firmaContrato" etiqueta="Firma de contrato" tipo="date" />
|
|
</SectionCard>
|
|
|
|
<SectionCard titulo="Tipo de crédito">
|
|
<div className="sm:col-span-2 lg:col-span-3">
|
|
<div className="flex flex-wrap gap-2">
|
|
{TIPOS_CREDITO.map((tc) => (
|
|
<label
|
|
key={tc.valor}
|
|
htmlFor={`tipoCredito-${tc.valor}`}
|
|
className={`cursor-pointer rounded-full border px-3 py-1.5 text-xs font-medium transition-all duration-100 ease-out ${
|
|
tiposSeleccionados.includes(tc.valor)
|
|
? 'border-accent bg-accent text-white shadow-sm'
|
|
: 'border-[#D1D5DB] bg-white text-[#4B5563] hover:border-accent hover:text-accent'
|
|
}`}
|
|
>
|
|
<input
|
|
id={`tipoCredito-${tc.valor}`}
|
|
type="radio"
|
|
name="tipoCreditoUnico"
|
|
className="sr-only"
|
|
checked={tiposSeleccionados.includes(tc.valor)}
|
|
onChange={() => seleccionarCredito(tc.valor)}
|
|
/>
|
|
{tc.etiqueta}
|
|
</label>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<CampoTexto nombre="montoCreditoInfoFovi" etiqueta="Monto de crédito Info o Fovi ($)" />
|
|
<CampoTexto nombre="ahorroVoluntarioSubcuenta" etiqueta="Ahorro voluntario o subcuenta ($)" />
|
|
<CampoTexto nombre="descuentoMensualInfoFovissste" etiqueta="Desc. mensual Info o Fovissste ($)" />
|
|
<CampoSelect nombre="tipoProducto" etiqueta="Tipo de producto" opciones={TIPOS_PRODUCTO} />
|
|
</SectionCard>
|
|
|
|
<SectionCard
|
|
titulo="Identificación oficial (PLD)"
|
|
descripcion="Datos de la identificación vigente para los formatos de ley anti-lavado."
|
|
>
|
|
<CampoTexto nombre="idAutoridadEmisora" etiqueta="Autoridad que la emite" className="sm:col-span-2" />
|
|
<CampoTexto nombre="idNumero" etiqueta="Número de identificación" />
|
|
<CampoTexto nombre="idVigencia" etiqueta="Vigencia" tipo="date" />
|
|
</SectionCard>
|
|
|
|
<SectionCard titulo="Domicilio fiscal (opcional)" descripcion="Art. 29 y 29-A CFF.">
|
|
<CampoTexto nombre="domicilioFiscal.calle" etiqueta="Calle o avenida" className="sm:col-span-2" />
|
|
<CampoTexto nombre="domicilioFiscal.numExt" etiqueta="Núm. exterior" />
|
|
<CampoTexto nombre="domicilioFiscal.numInt" etiqueta="Núm. interior" />
|
|
<CampoTexto nombre="domicilioFiscal.colonia" etiqueta="Colonia" />
|
|
<CampoTexto nombre="domicilioFiscal.municipio" etiqueta="Municipio" />
|
|
<CampoTexto nombre="domicilioFiscal.estado" etiqueta="Estado" />
|
|
<CampoTexto nombre="domicilioFiscal.cp" etiqueta="Código postal" />
|
|
<CampoTexto nombre="domicilioFiscal.pais" etiqueta="País" />
|
|
</SectionCard>
|
|
</div>
|
|
<MensajeError nombre="proyectoOtro" />
|
|
<Navegacion onAnterior={onAnterior} />
|
|
</form>
|
|
</FormProvider>
|
|
)
|
|
}
|