feat(pendientes): cotizador (mensualidades, bloqueo de unidad, totales, numMensualidades), cobranza (recibos, atrasos, mora configurable, recordatorios), soporte (adjuntos, filtros, estados), clientes maestro (catalogos reales), contactos (ficha y duplicados) y permisos de captura por rol
This commit is contained in:
84
apps/cobranza/src/lib/usePlanesCobranza.ts
Normal file
84
apps/cobranza/src/lib/usePlanesCobranza.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { getSesion } from '../services/auth'
|
||||
import {
|
||||
listarPlanes,
|
||||
miosPorDefecto,
|
||||
sinPlanesAsignados,
|
||||
type ResultadoPlanes,
|
||||
} from '../services/repository'
|
||||
|
||||
/** Alcance del listado: solo los planes del usuario o todos */
|
||||
export type VistaPlanes = 'mios' | 'todos'
|
||||
|
||||
export interface EstadoPlanes {
|
||||
mios: boolean
|
||||
/** Cambia el toggle; null borra el parámetro y vuelve al valor por defecto del rol */
|
||||
definirVista: (vista: VistaPlanes | null) => void
|
||||
resultado: ResultadoPlanes | null
|
||||
errorCarga: string | null
|
||||
/**
|
||||
* true cuando "Mis planes" vino vacío (el usuario no tiene planes asignados:
|
||||
* los históricos tienen vendedorId nulo) y se está mostrando el listado
|
||||
* completo para no dejar la pantalla en blanco.
|
||||
*/
|
||||
sinAsignados: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Carga de planes compartida por el dashboard y "Cobranza del día": el toggle
|
||||
* "Mis planes / Todos" vive en la URL (?vista=mios|todos) para que ambas
|
||||
* pantallas respeten la misma elección; sin parámetro arranca según el rol.
|
||||
*/
|
||||
export function usePlanesCobranza(): EstadoPlanes {
|
||||
const [searchParams, setSearchParams] = useSearchParams()
|
||||
const paramVista = searchParams.get('vista')
|
||||
const mios = paramVista !== null ? paramVista === 'mios' : miosPorDefecto(getSesion()?.usuario.rol)
|
||||
|
||||
const [resultado, setResultado] = useState<ResultadoPlanes | null>(null)
|
||||
const [errorCarga, setErrorCarga] = useState<string | null>(null)
|
||||
const [sinAsignados, setSinAsignados] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
let activo = true
|
||||
setResultado(null)
|
||||
setErrorCarga(null)
|
||||
void (async () => {
|
||||
try {
|
||||
const r = await listarPlanes(undefined, mios)
|
||||
if (!activo) return
|
||||
if (sinPlanesAsignados(r, mios)) {
|
||||
const todos = await listarPlanes(undefined, false)
|
||||
if (!activo) return
|
||||
setSinAsignados(true)
|
||||
setResultado(todos)
|
||||
return
|
||||
}
|
||||
setSinAsignados(false)
|
||||
setResultado(r)
|
||||
} catch (e) {
|
||||
if (activo) setErrorCarga(e instanceof Error ? e.message : 'Error al cargar los planes')
|
||||
}
|
||||
})()
|
||||
return () => {
|
||||
activo = false
|
||||
}
|
||||
}, [mios])
|
||||
|
||||
const definirVista = useCallback(
|
||||
(vista: VistaPlanes | null) => {
|
||||
setSearchParams(
|
||||
(prev) => {
|
||||
const next = new URLSearchParams(prev)
|
||||
if (vista === null) next.delete('vista')
|
||||
else next.set('vista', vista)
|
||||
return next
|
||||
},
|
||||
{ replace: true },
|
||||
)
|
||||
},
|
||||
[setSearchParams],
|
||||
)
|
||||
|
||||
return { mios, definirVista, resultado, errorCarga, sinAsignados }
|
||||
}
|
||||
Reference in New Issue
Block a user