Files
FRASA-CAS/apps/mi-dia/src/services/mi-dia.ts
Consult AS Technology d546460ba4 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)
2026-09-09 10:26:36 +00:00

136 lines
4.7 KiB
TypeScript

// Cliente del API para la app "Mi día" (mismas convenciones que las demás apps)
import type { ResumenMiDia } from '../lib/mi-dia'
import { conAuth, manejar401 } from './auth'
const API_URL = import.meta.env.VITE_API_URL ?? '/api'
async function fetchJson<T>(path: string, init: RequestInit = {}): Promise<T> {
const res = await fetch(`${API_URL}${path}`, conAuth(init))
manejar401(res)
if (!res.ok) {
const body = (await res.json().catch(() => null)) as { error?: string } | null
throw new Error(body?.error ?? `Error ${res.status}`)
}
return (await res.json()) as T
}
export function fetchResumenMiDia(): Promise<ResumenMiDia> {
return fetchJson<ResumenMiDia>('/mi-dia/resumen')
}
export interface LeadCreado {
id: string
estado: string
/** 'capturista' = asignado a quien lo registró; 'round-robin' = entra al reparto */
asignacion?: 'capturista' | 'round-robin'
}
/** Crea un lead; en duplicado (409) lanza Error con el mensaje del API */
export async function crearLead(datos: {
nombre: string
telefono?: string
email?: string
origen?: string
desarrollo?: string
notas?: string
}): Promise<LeadCreado> {
return fetchJson<LeadCreado>('/leads', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(datos),
})
}
export function cambiarEstadoLead(itemId: string, estado: string, fecha?: string, fuente?: 'lead' | 'oportunidad'): Promise<unknown> {
return fetchJson(`/leads/${itemId}/estado`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
estado,
...(fecha !== undefined && fecha !== '' ? { fecha } : {}),
...(fuente !== undefined ? { fuente } : {}),
}),
})
}
export function descartarLead(itemId: string, motivo: string, comentario?: string, fuente?: 'lead' | 'oportunidad', subcategoria?: string): Promise<unknown> {
return fetchJson(`/leads/${itemId}/descartar`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
motivo,
...(subcategoria !== undefined && subcategoria !== '' ? { subcategoria } : {}),
...(comentario !== undefined && comentario !== '' ? { comentario } : {}),
...(fuente !== undefined ? { fuente } : {}),
}),
})
}
/** POST /oportunidades/:itemId/etapa — botones de fase de "Mis oportunidades" */
export function cambiarEtapaOportunidad(itemId: string, etapa: string, fecha?: string): Promise<unknown> {
return fetchJson(`/oportunidades/${itemId}/etapa`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
etapa,
...(fecha !== undefined && fecha !== '' ? { fecha } : {}),
}),
})
}
/** POST /leads/:itemId/convertir — botón "Agendo": convierte el lead en oportunidad */
export function convertirLead(itemId: string): Promise<{ oportunidadId: string }> {
return fetchJson(`/leads/${itemId}/convertir`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({}),
})
}
/** POST /apartados/:itemId/estado — botones de fase de "Mis apartados" */
export function cambiarEstadoApartado(
itemId: string,
estado: string,
motivo?: string,
subcategoria?: string,
comentario?: string,
): Promise<unknown> {
return fetchJson(`/apartados/${itemId}/estado`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
estado,
...(motivo !== undefined && motivo !== '' ? { motivo } : {}),
...(subcategoria !== undefined && subcategoria !== '' ? { subcategoria } : {}),
...(comentario !== undefined && comentario !== '' ? { comentario } : {}),
}),
})
}
// ---------- Precarga de documentos del expediente (Mis apartados) ----------
export interface DocumentoApartado {
id: string
tipo: string // CAPTURA_COTIZADOR | CAPTURA_PAGO_APARTADO | OTRO | INE | ...
nombreArchivo: string
createdAt: string // ISO
}
/** GET /apartados/:itemId/documentos — documentos ya cargados del cliente ligado */
export function listarDocumentosApartado(itemId: string): Promise<DocumentoApartado[]> {
return fetchJson<DocumentoApartado[]>(`/apartados/${itemId}/documentos`)
}
/** POST /apartados/:itemId/documentos — multipart; el archivo se guarda en el cliente */
export async function subirDocumentoApartado(itemId: string, tipo: string, archivo: File): Promise<void> {
const form = new FormData()
form.append('tipo', tipo)
form.append('archivos', archivo)
const res = await fetch(`${API_URL}/apartados/${itemId}/documentos`, conAuth({ method: 'POST', body: form }))
manejar401(res)
if (!res.ok) {
const body = (await res.json().catch(() => null)) as { error?: string } | null
throw new Error(body?.error ?? `Error ${res.status}`)
}
}