- api-gateway: servicio Monday (retry 429/complejidad, paginacion cursor, column IDs por titulo), endpoints unidades/cotizaciones/clientes/planes, webhook con handshake challenge, Prisma SQLite dev, 55 tests - cotizador: inventario desde /api/unidades con fallback a seed - alta-clientes: POST /api/clientes + upload multipart de documentos - cobranza: persistencia via API con respaldo offline en localStorage - .env.example con board IDs del workspace Frasa-CAS
149 lines
5.0 KiB
TypeScript
149 lines
5.0 KiB
TypeScript
import { useState } from 'react'
|
|
import Stepper from '../components/Stepper'
|
|
import DatosGeneralesForm from '../forms/DatosGeneralesForm'
|
|
import EmpleoForm from '../forms/EmpleoForm'
|
|
import EconomicosForm from '../forms/EconomicosForm'
|
|
import CoacreditadoForm from '../forms/CoacreditadoForm'
|
|
import ReferenciasForm from '../forms/ReferenciasForm'
|
|
import ViviendaCreditoForm from '../forms/ViviendaCreditoForm'
|
|
import DocumentosForm from '../forms/DocumentosForm'
|
|
import FirmasForm from '../forms/FirmasForm'
|
|
import { useAltaCliente, PASOS } from '../hooks/useAltaCliente'
|
|
import { crearAltaCliente, subirDocumentos } from '../services/api'
|
|
|
|
export default function AltaClientePage() {
|
|
const { paso, siguiente, anterior, irAPaso, datos, alta, actualizar, agregarDocumento, quitarDocumento } =
|
|
useAltaCliente()
|
|
const [registro, setRegistro] = useState<string>('')
|
|
const [sinConexion, setSinConexion] = useState(false)
|
|
|
|
// Registra el alta en el API al llegar al paso de firmas; si el API no
|
|
// responde, el flujo continúa solo local (los PDFs se generan igual)
|
|
const registrarAlta = async () => {
|
|
const creada = await crearAltaCliente(alta)
|
|
if (creada) {
|
|
await subirDocumentos(creada.id, alta)
|
|
setRegistro(creada.id)
|
|
setSinConexion(false)
|
|
} else {
|
|
setRegistro('')
|
|
setSinConexion(true)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-white">
|
|
<header className="border-b border-neutral-200 px-4 py-6">
|
|
<div className="mx-auto flex max-w-6xl items-baseline gap-3">
|
|
<span className="text-sm font-bold uppercase tracking-widest">FRASA</span>
|
|
<h1 className="text-2xl font-light tracking-wide">Alta de Clientes</h1>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="mx-auto max-w-6xl px-4 py-16">
|
|
<Stepper pasos={[...PASOS]} pasoActual={paso} onIrAPaso={irAPaso} />
|
|
|
|
<div className="mt-16">
|
|
{paso === 0 && (
|
|
<DatosGeneralesForm
|
|
valores={datos.datosGenerales}
|
|
onSiguiente={(v) => {
|
|
actualizar('datosGenerales', v)
|
|
siguiente()
|
|
}}
|
|
/>
|
|
)}
|
|
{paso === 1 && (
|
|
<EmpleoForm
|
|
valores={datos.empleo}
|
|
onAnterior={anterior}
|
|
onSiguiente={(v) => {
|
|
actualizar('empleo', v)
|
|
siguiente()
|
|
}}
|
|
/>
|
|
)}
|
|
{paso === 2 && (
|
|
<EconomicosForm
|
|
valores={datos.economicos}
|
|
onAnterior={anterior}
|
|
onSiguiente={(v) => {
|
|
actualizar('economicos', v)
|
|
siguiente()
|
|
}}
|
|
/>
|
|
)}
|
|
{paso === 3 && (
|
|
<CoacreditadoForm
|
|
valores={datos.coacreditado}
|
|
onAnterior={anterior}
|
|
onSiguiente={(v) => {
|
|
actualizar('coacreditado', v)
|
|
siguiente()
|
|
}}
|
|
/>
|
|
)}
|
|
{paso === 4 && (
|
|
<ReferenciasForm
|
|
valores={datos.referencias}
|
|
onAnterior={anterior}
|
|
onSiguiente={(v) => {
|
|
actualizar('referencias', v)
|
|
siguiente()
|
|
}}
|
|
/>
|
|
)}
|
|
{paso === 5 && (
|
|
<ViviendaCreditoForm
|
|
valores={datos.viviendaCredito}
|
|
onAnterior={anterior}
|
|
onSiguiente={(v) => {
|
|
actualizar('viviendaCredito', v)
|
|
siguiente()
|
|
}}
|
|
/>
|
|
)}
|
|
{paso === 6 && (
|
|
<DocumentosForm
|
|
documentos={alta.documentos}
|
|
firmaDataUrl={alta.firma.firmaDataUrl}
|
|
onAgregar={agregarDocumento}
|
|
onQuitar={quitarDocumento}
|
|
onAplicarDatosGenerales={(campos) =>
|
|
actualizar('datosGenerales', { ...datos.datosGenerales, ...campos })
|
|
}
|
|
onAnterior={anterior}
|
|
onSiguiente={() => {
|
|
void registrarAlta()
|
|
siguiente()
|
|
}}
|
|
/>
|
|
)}
|
|
{paso === 7 && (
|
|
<>
|
|
{registro && (
|
|
<p className="mb-8 border border-accent bg-white px-4 py-3 text-xs font-bold uppercase tracking-widest text-accent">
|
|
Alta registrada en el servidor: {registro}
|
|
</p>
|
|
)}
|
|
{sinConexion && (
|
|
<p
|
|
role="alert"
|
|
className="mb-8 border border-amber-300 bg-amber-50 px-4 py-3 text-xs font-bold uppercase tracking-widest text-amber-700"
|
|
>
|
|
Sin conexión al servidor — el expediente se generó solo localmente
|
|
</p>
|
|
)}
|
|
<FirmasForm
|
|
alta={alta}
|
|
onCambiarFirma={(f) => actualizar('firma', f)}
|
|
onAnterior={anterior}
|
|
/>
|
|
</>
|
|
)}
|
|
</div>
|
|
</main>
|
|
</div>
|
|
)
|
|
}
|