feat(8-mejoras): cancelar+motivo, brocker, etapas, calendario, modal cliente
1. Cancelar con motivo: Mis apartados y Escrituración piden motivo (modal reutilizado) 2. Origen BROCKER agregado en alta afluencia y captura rápida 3. Etapas oportunidades reordenadas: Agendo Cita → Afluencia → Cotizador 4. Cotizador: mensualidades diferibles (6, 12, 14, 18, 24 meses) 5. Calendario semanal por vendedor en Mi día 6. Hora en citas (datetime-local en vez de solo fecha) 7. Click en nombre → modal DetalleLeadModal (info + notas + historial)
This commit is contained in:
@@ -38,6 +38,10 @@ interface UseCotizacionResult {
|
||||
propuestaIndex: number
|
||||
setPropuestaIndex: (i: number) => void
|
||||
precioLista: number
|
||||
/** Número de mensualidades elegido por el vendedor; null = usa el calculado */
|
||||
numMensualidades: number | null
|
||||
setNumMensualidades: (n: number | null) => void
|
||||
/** Número efectivo de mensualidades (la elegida o la calculada) */
|
||||
numMeses: number | null
|
||||
propuestas: PropuestaResultado[]
|
||||
/** Restaura el estado completo desde una cotización guardada (historial) */
|
||||
@@ -66,6 +70,7 @@ export function useCotizacion(): UseCotizacionResult {
|
||||
const [enganchePct, setEnganchePct] = useState<number | null>(null)
|
||||
const [descuentos, setDescuentos] = useState<DescuentosPropuestas>(descuentosDefault(DESARROLLO_DEFAULT))
|
||||
const [propuestaIndex, setPropuestaIndex] = useState(0)
|
||||
const [numMensualidades, setNumMensualidades] = useState<number | null>(null)
|
||||
|
||||
const params = obtenerDesarrollo(desarrollo)
|
||||
|
||||
@@ -75,6 +80,7 @@ export function useCotizacion(): UseCotizacionResult {
|
||||
setAcabados(false)
|
||||
setDescuentos(descuentosDefault(id))
|
||||
setPropuestaIndex(0)
|
||||
setNumMensualidades(null)
|
||||
}
|
||||
|
||||
const setDescuento = (clave: keyof DescuentosPropuestas, valor: number) => {
|
||||
@@ -103,13 +109,17 @@ export function useCotizacion(): UseCotizacionResult {
|
||||
setEnganchePct(null)
|
||||
setDescuentos(descuentosDefault(desarrollo))
|
||||
setPropuestaIndex(0)
|
||||
setNumMensualidades(null)
|
||||
}
|
||||
|
||||
const numMeses = useMemo(
|
||||
const numMesesCalculado = useMemo(
|
||||
() => (unidad ? resolverNumMensualidades(unidad, params) : null),
|
||||
[unidad, params],
|
||||
)
|
||||
|
||||
/** Número efectivo de mensualidades: la elegida por el vendedor, o la calculada */
|
||||
const numMeses = numMensualidades ?? numMesesCalculado
|
||||
|
||||
const precioLista = useMemo(
|
||||
() => (unidad ? obtenerPrecioLista(unidad, acabados) : 0),
|
||||
[unidad, acabados],
|
||||
@@ -125,8 +135,8 @@ export function useCotizacion(): UseCotizacionResult {
|
||||
|
||||
const propuestas = useMemo(() => {
|
||||
if (!unidad) return []
|
||||
return calcularPropuestas({ unidad, acabados, apartado: apartadoEfectivo, descuentos, desarrollo, enganchePct: engancheEfectivo })
|
||||
}, [unidad, acabados, apartadoEfectivo, descuentos, desarrollo, engancheEfectivo])
|
||||
return calcularPropuestas({ unidad, acabados, apartado: apartadoEfectivo, descuentos, desarrollo, enganchePct: engancheEfectivo, numMeses: numMeses ?? undefined })
|
||||
}, [unidad, acabados, apartadoEfectivo, descuentos, desarrollo, engancheEfectivo, numMeses])
|
||||
|
||||
return {
|
||||
desarrollo,
|
||||
@@ -148,6 +158,8 @@ export function useCotizacion(): UseCotizacionResult {
|
||||
propuestaIndex,
|
||||
setPropuestaIndex,
|
||||
precioLista,
|
||||
numMensualidades,
|
||||
setNumMensualidades,
|
||||
numMeses,
|
||||
propuestas,
|
||||
restaurar,
|
||||
|
||||
@@ -71,6 +71,8 @@ export default function CotizadorPage() {
|
||||
propuestaIndex,
|
||||
setPropuestaIndex,
|
||||
precioLista,
|
||||
numMensualidades,
|
||||
setNumMensualidades,
|
||||
numMeses,
|
||||
propuestas,
|
||||
restaurar,
|
||||
@@ -465,6 +467,33 @@ export default function CotizadorPage() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="mensualidades" className={etiquetaCampo}>
|
||||
Mensualidades
|
||||
</label>
|
||||
<select
|
||||
id="mensualidades"
|
||||
value={numMensualidades ?? ''}
|
||||
onChange={(e) => {
|
||||
const v = e.target.value
|
||||
setNumMensualidades(v === '' ? null : Number(v))
|
||||
}}
|
||||
className={campoInput}
|
||||
>
|
||||
<option value="">Calcular automáticamente</option>
|
||||
{[6, 12, 14, 18, 24].map((n) => (
|
||||
<option key={n} value={n}>
|
||||
{n} mensualidades
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{numMeses !== null && (
|
||||
<p className="mt-1 text-xs text-[#64748B] dark:text-[#94A3B8]">
|
||||
Se usarán {numMeses} mensualidades para el cálculo
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<BuscadorCliente
|
||||
onSeleccion={(lead) => {
|
||||
setValue('nombre', lead.nombre, { shouldValidate: true })
|
||||
|
||||
Reference in New Issue
Block a user