feat(mejoras): 25 mejoras UX en todos los módulos + purga Monday
Backend: mi-dia siempre local, vocabulario canónico, AplicacionPago con usuario Mi día: modal español real, cita desde modal, score falso eliminado Cotizador: barra sticky, persistencia local con folio Alta Clientes: redirect eliminado, foto conectada, asesor desde sesión, refs dinámicas Cobranza: contrato Mora arreglado, link a Plan, abono próximo pago, WhatsApp Landing: contactos locales, ModalContacto eliminado, ModoTrabajo eliminado Dashboards: MiTablero unificado, funnel con descartados, drill-down, rango fechas Chat FRASA: corpus 5 desarrollos, 6 herramientas, historial conversaciones 69 archivos, 7 apps construidas
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { z } from 'zod'
|
||||
import { useForm, FormProvider } from 'react-hook-form'
|
||||
import { useForm, FormProvider, useFieldArray } from 'react-hook-form'
|
||||
import { zodResolver } from '@hookform/resolvers/zod'
|
||||
import type { Referencia } from '../types'
|
||||
import { referenciasSchema } from '../lib/schemas'
|
||||
@@ -11,13 +11,18 @@ import type { PasoProps } from './DatosGeneralesForm'
|
||||
// RHF trabaja con objetos raíz; el array va envuelto
|
||||
const schemaFormulario = z.object({ referencias: referenciasSchema })
|
||||
|
||||
/** Paso 5: referencias personales (4 filas, mínimo 1 con nombre y teléfono) */
|
||||
const MAX_REFERENCIAS = 4
|
||||
|
||||
const referenciaVacia = (): Referencia => ({ nombre: '', telefono: '', parentesco: '', direccion: '' })
|
||||
|
||||
/** Paso 5: referencias personales (1 a 4 bloques; al menos 1 con nombre y teléfono) */
|
||||
export default function ReferenciasForm({ valores, onAnterior, onSiguiente }: PasoProps<Referencia[]>) {
|
||||
const metodos = useForm<{ referencias: Referencia[] }>({
|
||||
resolver: zodResolver(schemaFormulario),
|
||||
defaultValues: { referencias: valores },
|
||||
mode: 'onSubmit',
|
||||
})
|
||||
const { fields, append, remove } = useFieldArray({ control: metodos.control, name: 'referencias' })
|
||||
const errorRaiz = metodos.formState.errors.referencias
|
||||
|
||||
return (
|
||||
@@ -30,9 +35,24 @@ export default function ReferenciasForm({ valores, onAnterior, onSiguiente }: Pa
|
||||
titulo="Referencias personales"
|
||||
descripcion="Captura al menos 1 referencia con nombre y teléfono."
|
||||
>
|
||||
{[0, 1, 2, 3].map((i) => (
|
||||
<div key={i} className="rounded-xl border border-[#E5E7EB] p-6">
|
||||
<p className="mb-4 text-xs font-semibold uppercase tracking-wider text-[#64748B]">Referencia {i + 1}</p>
|
||||
{fields.map((field, i) => (
|
||||
<div key={field.id} className="rounded-xl border border-[#E5E7EB] p-6">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<p className="text-xs font-semibold uppercase tracking-wider text-[#64748B]">
|
||||
Referencia {i + 1}
|
||||
</p>
|
||||
{fields.length > 1 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => remove(i)}
|
||||
aria-label={`Eliminar referencia ${i + 1}`}
|
||||
title="Eliminar referencia"
|
||||
className="flex h-7 w-7 items-center justify-center rounded-full border border-red-200 text-sm leading-none text-red-600 transition-colors duration-100 hover:bg-red-50"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-3">
|
||||
<CampoTexto nombre={`referencias.${i}.nombre`} etiqueta="Nombre completo" />
|
||||
<CampoTexto nombre={`referencias.${i}.telefono`} etiqueta="Teléfono" />
|
||||
@@ -41,6 +61,17 @@ export default function ReferenciasForm({ valores, onAnterior, onSiguiente }: Pa
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div className="md:col-span-2 lg:col-span-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => append(referenciaVacia())}
|
||||
disabled={fields.length >= MAX_REFERENCIAS}
|
||||
title={fields.length >= MAX_REFERENCIAS ? `Máximo ${MAX_REFERENCIAS} referencias` : undefined}
|
||||
className="rounded-lg border border-accent bg-white px-4 py-2 text-sm font-medium text-accent transition-colors duration-100 hover:bg-accent hover:text-white disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
+ Agregar referencia
|
||||
</button>
|
||||
</div>
|
||||
</SectionCard>
|
||||
{errorRaiz && typeof errorRaiz.message === 'string' && (
|
||||
<p className="mt-2 px-6 text-xs font-semibold text-red-600">{errorRaiz.message}</p>
|
||||
|
||||
Reference in New Issue
Block a user