feat(7-mejoras): notas persisten + soporte + captura visible
1. Notas propagan entre etapas (convertirLead/apartar updateMany) 2. Afluencia: conexión → aviso amarillo 3. Escrituración: click nombre abre bitácora 4. Co-acreditado: opción 'no tiene' 5. Nota del alta de lead → bitácora 6. Módulo Soporte: formulario + listado solicitudes 7. Captura visible para todos
This commit is contained in:
@@ -174,14 +174,14 @@ describe('AltaAfluenciaForm', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('con error de red muestra un mensaje reintentable', async () => {
|
||||
it('con error de red muestra aviso amarillo: el registro se guardó igualmente', async () => {
|
||||
fetchMock.mockRejectedValueOnce(new Error('red caída'))
|
||||
render(<AltaAfluenciaForm onVolver={() => undefined} onPasarAAltaCliente={() => undefined} />)
|
||||
llenarRequeridos()
|
||||
fireEvent.click(screen.getByRole('button', { name: /Registrar en afluencia/ }))
|
||||
|
||||
expect((await screen.findByRole('alert')).textContent).toContain(
|
||||
'No se pudo registrar el lead. Revisa tu conexión e intenta de nuevo.',
|
||||
expect((await screen.findByRole('status')).textContent).toContain(
|
||||
'La afluencia se registró, pero hubo un problema de conexión. Verifica en Mi día.',
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ export default function AltaAfluenciaForm({ onVolver, onPasarAAltaCliente, valor
|
||||
const [leadCreado, setLeadCreado] = useState<LeadResumen | null>(null)
|
||||
const [asignacion, setAsignacion] = useState<'capturista' | 'round-robin' | null>(null)
|
||||
const [aviso, setAviso] = useState('')
|
||||
const [avisoTipo, setAvisoTipo] = useState<'error' | 'advertencia'>('error')
|
||||
|
||||
const cambiar = (campo: keyof typeof FORM_INICIAL) => (e: { target: { value: string } }) => {
|
||||
setForm((prev) => ({ ...prev, [campo]: e.target.value }))
|
||||
@@ -190,8 +191,15 @@ export default function AltaAfluenciaForm({ onVolver, onPasarAAltaCliente, valor
|
||||
})
|
||||
setAsignacion(res.lead.asignacion ?? 'round-robin')
|
||||
} else if (res.motivo === 'duplicado') {
|
||||
setAvisoTipo('error')
|
||||
setAviso(`Ya existe un lead con ese teléfono: ${res.nombre}.`)
|
||||
} else if (res.motivo === 'conexion') {
|
||||
// El backend es local: el registro YA se guardó aunque el fetch se haya
|
||||
// cortado (timeout/red). Aviso amarillo en vez de error rojo.
|
||||
setAvisoTipo('advertencia')
|
||||
setAviso('La afluencia se registró, pero hubo un problema de conexión. Verifica en Mi día.')
|
||||
} else {
|
||||
setAvisoTipo('error')
|
||||
setAviso('No se pudo registrar el lead. Revisa tu conexión e intenta de nuevo.')
|
||||
}
|
||||
}
|
||||
@@ -583,7 +591,13 @@ export default function AltaAfluenciaForm({ onVolver, onPasarAAltaCliente, valor
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{aviso && (
|
||||
{aviso && avisoTipo === 'advertencia' && (
|
||||
<p role="status" className="mt-6 rounded-lg border border-yellow-200 bg-yellow-50 px-4 py-3 text-xs font-medium text-yellow-800">
|
||||
{aviso}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{aviso && avisoTipo === 'error' && (
|
||||
<p role="alert" className="mt-6 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-xs text-red-700">
|
||||
{aviso}
|
||||
</p>
|
||||
|
||||
@@ -17,9 +17,28 @@ export default function CoacreditadoForm({ valores, onAnterior, onSiguiente }: P
|
||||
})
|
||||
const tieneCoacreditado = metodos.watch('tieneCoacreditado')
|
||||
|
||||
// "El cliente no tiene co-acreditado" es el espejo del toggle: marcado oculta
|
||||
// todos los campos y el schema valida sin datos (superRefine solo exige el
|
||||
// nombre cuando hay co-acreditado).
|
||||
const cambiarSinCoacreditado = (e: { target: { checked: boolean } }) => {
|
||||
metodos.setValue('tieneCoacreditado', !e.target.checked, { shouldValidate: false })
|
||||
metodos.clearErrors()
|
||||
}
|
||||
|
||||
return (
|
||||
<FormProvider {...metodos}>
|
||||
<form onSubmit={metodos.handleSubmit(onSiguiente)} noValidate>
|
||||
<label htmlFor="sinCoacreditado" className="flex items-center gap-3 px-6 py-5">
|
||||
<input
|
||||
id="sinCoacreditado"
|
||||
type="checkbox"
|
||||
className="h-5 w-5 accent-accent"
|
||||
checked={!tieneCoacreditado}
|
||||
onChange={cambiarSinCoacreditado}
|
||||
/>
|
||||
<span className="text-sm font-medium text-[#111827]">El cliente no tiene co-acreditado</span>
|
||||
</label>
|
||||
|
||||
<label htmlFor="tieneCoacreditado" className="flex items-center gap-3 px-6 py-5">
|
||||
<input
|
||||
id="tieneCoacreditado"
|
||||
|
||||
Reference in New Issue
Block a user