feat(cotizador): historial, compartir WhatsApp, folio+QR, comparador de unidades, alertas de inventario
- Mis cotizaciones (guardar con cliente, retomar, nueva) asociadas al vendedor del token - Compartir: resumen copiable + deep link wa.me; PDF con folio, vigencia y QR - GET /api/cotizaciones/:id/publico (público, datos mínimos sin PII del cliente) - Comparador lado a lado de 2 unidades (mismo o distinto desarrollo) - POST /api/cotizaciones/alertas-inventario: avisa al vendedor si una unidad cotizada cambia de estatus (dedup; wire con syncs documentado) - Fix: labels de propuesta mapeados a los reales del board Oportunidades - Tests: api 201, cotizador 82
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
import type { Unidad } from '../types'
|
||||
import type { Unidad, TipoPropuesta } from '../types'
|
||||
import { INVENTARIO_SEED } from '../data/unidades'
|
||||
import { conAuth, manejar401 } from './auth'
|
||||
|
||||
// TODO produccion: '/api' same-origin detras de NGINX
|
||||
const API_URL = import.meta.env.VITE_API_URL ?? 'http://192.168.10.123:3000/api'
|
||||
|
||||
export { API_URL }
|
||||
|
||||
export interface GetUnidadesResult {
|
||||
unidades: Unidad[]
|
||||
/** true cuando se usó el seed local por no poder contactar el API */
|
||||
@@ -28,3 +30,79 @@ export async function getUnidades(): Promise<GetUnidadesResult> {
|
||||
return { unidades: INVENTARIO_SEED, usandoSeed: true }
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- Cotizaciones guardadas (historial del vendedor) ----------
|
||||
|
||||
/** Tipo de propuesta tal como lo espera el API (body de POST /cotizaciones) */
|
||||
export type TipoPropuestaApi = 'BANCARIO' | 'INFONAVIT' | 'CONTADO' | 'SEMI_CONTADO'
|
||||
|
||||
export const TIPO_A_API: Record<TipoPropuesta, TipoPropuestaApi> = {
|
||||
CREDITO_BANCARIO: 'BANCARIO',
|
||||
CREDITO_INFONAVIT_COFINAVIT: 'INFONAVIT',
|
||||
CONTADO: 'CONTADO',
|
||||
SEMI_CONTADO: 'SEMI_CONTADO',
|
||||
}
|
||||
|
||||
export const TIPO_DE_API: Record<TipoPropuestaApi, TipoPropuesta> = {
|
||||
BANCARIO: 'CREDITO_BANCARIO',
|
||||
INFONAVIT: 'CREDITO_INFONAVIT_COFINAVIT',
|
||||
CONTADO: 'CONTADO',
|
||||
SEMI_CONTADO: 'SEMI_CONTADO',
|
||||
}
|
||||
|
||||
export interface CotizacionGuardada {
|
||||
id: string
|
||||
folio?: string
|
||||
unidadCodigo: string
|
||||
desarrollo: string
|
||||
clienteNombre: string
|
||||
clienteTelefono: string | null
|
||||
clienteEmail: string | null
|
||||
tipoPropuesta: TipoPropuestaApi
|
||||
acabados: boolean
|
||||
apartado: number
|
||||
descuento: number
|
||||
precioLista: number
|
||||
precioFinal: number
|
||||
enganche: number
|
||||
numMensualidades: number
|
||||
mensualidad: number | null
|
||||
pagoFinal: number
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface GuardarCotizacionPayload {
|
||||
unidadCodigo: string
|
||||
clienteNombre: string
|
||||
clienteEmail?: string
|
||||
clienteTelefono?: string
|
||||
tipoPropuesta: TipoPropuestaApi
|
||||
acabados: boolean
|
||||
apartado?: number
|
||||
descuento?: number
|
||||
}
|
||||
|
||||
/** URL pública de una cotización (destino del QR del PDF) */
|
||||
export function urlPublicaCotizacion(id: string): string {
|
||||
return `${API_URL}/cotizaciones/${id}/publico`
|
||||
}
|
||||
|
||||
/** POST /cotizaciones — persiste y registra la oportunidad en Monday */
|
||||
export async function guardarCotizacion(payload: GuardarCotizacionPayload): Promise<CotizacionGuardada> {
|
||||
const resp = await fetch(`${API_URL}/cotizaciones`, conAuth({
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
}))
|
||||
manejar401(resp)
|
||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
|
||||
return (await resp.json()) as CotizacionGuardada
|
||||
}
|
||||
|
||||
/** GET /cotizaciones/mias — últimas 50 del vendedor autenticado */
|
||||
export async function getMisCotizaciones(): Promise<CotizacionGuardada[]> {
|
||||
const resp = await fetch(`${API_URL}/cotizaciones/mias`, conAuth())
|
||||
manejar401(resp)
|
||||
if (!resp.ok) throw new Error(`HTTP ${resp.status}`)
|
||||
return (await resp.json()) as CotizacionGuardada[]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user