feat: API Gateway (Express 5 + Prisma + Monday) y conexion de apps al API
- api-gateway: servicio Monday (retry 429/complejidad, paginacion cursor, column IDs por titulo), endpoints unidades/cotizaciones/clientes/planes, webhook con handshake challenge, Prisma SQLite dev, 55 tests - cotizador: inventario desde /api/unidades con fallback a seed - alta-clientes: POST /api/clientes + upload multipart de documentos - cobranza: persistencia via API con respaldo offline en localStorage - .env.example con board IDs del workspace Frasa-CAS
This commit is contained in:
@@ -1,21 +1,71 @@
|
||||
import type { AltaCliente } from '../types'
|
||||
import { uuid } from '../lib/uuid'
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL ?? 'http://192.168.10.123:3000/api'
|
||||
// TODO produccion: '/api' same-origin detras de NGINX
|
||||
|
||||
export interface AltaClienteCreada {
|
||||
id: string
|
||||
}
|
||||
|
||||
/** Registra el alta de cliente en el backend */
|
||||
export async function crearAltaCliente(payload: AltaCliente): Promise<AltaClienteCreada> {
|
||||
// TODO: POST /api/clientes
|
||||
await new Promise((resolve) => setTimeout(resolve, 400))
|
||||
console.log('[api] crearAltaCliente (stub):', payload.datosGenerales.nombreCompleto)
|
||||
return { id: uuid() }
|
||||
interface DocumentoMeta {
|
||||
tipo: string
|
||||
nombre: string
|
||||
tamano: number
|
||||
mime: string
|
||||
}
|
||||
|
||||
/** Sube los documentos del expediente del cliente */
|
||||
export async function subirDocumentos(clienteId: string, payload: AltaCliente): Promise<void> {
|
||||
// TODO: POST multipart a /api/clientes/:id/documentos
|
||||
await new Promise((resolve) => setTimeout(resolve, 200))
|
||||
console.log(`[api] subirDocumentos (stub) cliente=${clienteId}:`, payload.documentos.length, 'archivos')
|
||||
/**
|
||||
* Serializa el alta para el POST JSON: los File no viajan en el cuerpo,
|
||||
* solo sus metadatos; la firma ya es un string base64 (data URL).
|
||||
*/
|
||||
const serializarAlta = (payload: AltaCliente) => ({
|
||||
...payload,
|
||||
documentos: payload.documentos.map(
|
||||
(d): DocumentoMeta => ({ tipo: d.slot, nombre: d.nombre, tamano: d.tamano, mime: d.tipoMime }),
|
||||
),
|
||||
})
|
||||
|
||||
/**
|
||||
* Registra el alta en el API Gateway (POST /clientes). El backend persiste en
|
||||
* SQLite y replica en Monday. Si el API no responde devuelve null (con
|
||||
* console.warn) y el flujo continúa solo local: los PDFs se generan igual.
|
||||
*/
|
||||
export async function crearAltaCliente(payload: AltaCliente): Promise<AltaClienteCreada | null> {
|
||||
try {
|
||||
const res = await fetch(`${API_URL}/clientes`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(serializarAlta(payload)),
|
||||
})
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
||||
const data = (await res.json()) as { id: string }
|
||||
return { id: data.id }
|
||||
} catch (err) {
|
||||
console.warn('[api] crearAltaCliente: sin conexión al servidor, el expediente se genera solo localmente', err)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sube los documentos reales del expediente (POST multipart
|
||||
* /clientes/:id/documentos, campo 'archivos'). El backend acepta un solo
|
||||
* `tipo` por petición, así que se envía una petición por documento con su
|
||||
* slot como tipo. Los fallos se reportan con console.warn sin interrumpir
|
||||
* el flujo local.
|
||||
*/
|
||||
export async function subirDocumentos(clienteId: string, payload: AltaCliente): Promise<void> {
|
||||
for (const doc of payload.documentos) {
|
||||
try {
|
||||
const form = new FormData()
|
||||
form.append('tipo', doc.slot)
|
||||
form.append('archivos', doc.archivo, doc.nombre)
|
||||
const res = await fetch(`${API_URL}/clientes/${clienteId}/documentos`, {
|
||||
method: 'POST',
|
||||
body: form,
|
||||
})
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`)
|
||||
} catch (err) {
|
||||
console.warn(`[api] subirDocumentos: no se pudo subir ${doc.nombre} (${doc.slot})`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user