feat(mi-dia): botón Afluencia abre el formulario prellenado y actualiza la oportunidad

- Mis oportunidades: 'Afluencia →' navega a /client/?afluencia=<id> con
  nombre, contacto, desarrollo y modelo prellenados
- Alta Afluencia: con oportunidadId el envío ACTUALIZA la oportunidad en
  Monday (perfil completo + etapa Afluencia) en vez de crear duplicado;
  sin oportunidadId el flujo de captura nueva no cambia
- monday.service: actualizarOportunidadAfluencia (mismo builder de la
  creación + name); moverAGrupoSegunEstado tras actualizar
- Tests: 457 API + 16 mi-dia + 116 alta-clientes, verde
This commit is contained in:
2026-09-04 22:53:21 +00:00
parent cbd50e2066
commit 5d7c6f58cd
9 changed files with 266 additions and 22 deletions

View File

@@ -38,6 +38,22 @@ function paramCotizacion(): string | null {
return new URLSearchParams(hash).get('cotizacion')
}
/**
* Lee ?afluencia=<oportunidadId> + prefill (nombre/telefono/email/desarrollo/
* modelo) — botón "Afluencia" de Mis oportunidades en Mi día.
*/
function paramsAfluencia(): { oportunidadId: string; valores: Record<string, string> } | null {
const qs = new URLSearchParams(window.location.search)
const id = qs.get('afluencia')
if (!id) return null
const valores: Record<string, string> = {}
for (const k of ['nombre', 'telefono', 'email', 'desarrollo', 'modelo', 'origen']) {
const v = qs.get(k)
if (v) valores[k] = v
}
return { oportunidadId: id, valores }
}
const formatoFechaHora = (iso: string): string =>
new Date(iso).toLocaleString('es-MX', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })
@@ -104,9 +120,23 @@ export default function AltaClientePage() {
const [ligaCopiada, setLigaCopiada] = useState(false)
const [errorLiga, setErrorLiga] = useState('')
// Afluencia prellenada desde Mis oportunidades (Mi día): actualiza la
// oportunidad existente en vez de crear una nueva
const [afluenciaPrevia, setAfluenciaPrevia] = useState<{
oportunidadId: string
valores: Record<string, string>
} | null>(null)
// Al entrar: ?cotizacion=<id> prellena el alta y salta directo al wizard;
// si no, la entrada ofrece Alta Afluencia (captura ligera) y Alta de Cliente
// ?afluencia=<id> abre el formulario de afluencia prellenado; si no, la
// entrada ofrece Alta Afluencia (captura ligera) y Alta de Cliente
useEffect(() => {
const afl = paramsAfluencia()
if (afl) {
setAfluenciaPrevia(afl)
setPantalla('afluencia')
return
}
const cotizacionId = paramCotizacion()
if (cotizacionId) {
obtenerCotizacion(cotizacionId)
@@ -335,7 +365,12 @@ export default function AltaClientePage() {
</p>
<div className="mt-6 rounded-xl border border-[#E5E7EB] bg-white p-6 shadow-sm">
<AltaAfluenciaForm
onVolver={() => setPantalla('entrada')}
valoresIniciales={afluenciaPrevia?.valores}
oportunidadId={afluenciaPrevia?.oportunidadId}
onVolver={() => {
setAfluenciaPrevia(null)
setPantalla('entrada')
}}
onPasarAAltaCliente={empezarDesdeLead}
/>
</div>