import { prisma } from './prisma.js' export interface BuscadoItem { tipo: 'lead' | 'oportunidad' | 'contacto' | 'unidad' id: string nombre: string detalle: string url: string } /** * Búsqueda global: LeadCaptura (leads), Cliente (contactos), Cotizacion * (oportunidades con cotización), y Unidad (inventario). * Case-insensitive sobre los campos indicados; limita a `limite` resultados. */ export async function buscar( termino: string, limite = 20, alcance?: { usuarioId: string; soloPropio: boolean }, ): Promise { const q = termino.trim() if (q === '') return [] const pat = `%${q}%` // Privacidad por vendedor: cada asesor sólo encuentra SUS leads, contactos // y cotizaciones. Los roles de supervisión llegan sin filtro (soloPropio). const propioLead = alcance?.soloPropio ? { usuarioId: alcance.usuarioId } : {} const propioCliente = alcance?.soloPropio ? { capturadoPorId: alcance.usuarioId } : {} const propiaCotizacion = alcance?.soloPropio ? { vendedorId: alcance.usuarioId } : {} const [leads, clientes, cotizaciones, unidades] = await Promise.all([ prisma.leadCaptura.findMany({ where: { AND: [propioLead, { OR: [ { nombreLead: { contains: q } }, { telefono: { contains: q } }, { email: { contains: q } }, ], }], }, orderBy: { updatedAt: 'desc' }, take: limite, }), prisma.cliente.findMany({ where: { AND: [propioCliente, { OR: [ { nombreCompleto: { contains: q } }, { curp: { contains: q } }, { rfc: { contains: q } }, { email: { contains: q } }, { celular: { contains: q } }, ], }], }, orderBy: { createdAt: 'desc' }, take: limite, }), prisma.cotizacion.findMany({ where: { AND: [propiaCotizacion, { OR: [ { clienteNombre: { contains: q } }, { clienteEmail: { contains: q } }, { unidadCodigo: { contains: q } }, ], }], }, orderBy: { createdAt: 'desc' }, take: limite, }), prisma.unidad.findMany({ where: { OR: [ { codigo: { contains: q } }, { desarrollo: { contains: q } }, ], }, orderBy: { updatedAt: 'desc' }, take: limite, }), ]) const items: BuscadoItem[] = [ ...leads.map((l) => ({ tipo: 'lead' as const, id: l.leadId, nombre: l.nombreLead, detalle: [l.telefono, l.email].filter(Boolean).join(' · ') || l.origen || l.board, url: `/app/#leads`, })), ...clientes.map((c) => ({ tipo: 'contacto' as const, id: c.id, nombre: c.nombreCompleto, detalle: [c.email, c.celular, c.proyecto].filter(Boolean).join(' · ') || '', url: `/app/#clientes`, })), ...cotizaciones.map((ct) => ({ tipo: 'oportunidad' as const, id: ct.id, nombre: ct.clienteNombre, detalle: [ct.unidadCodigo, ct.desarrollo].filter(Boolean).join(' · ') || ct.tipoPropuesta, url: `/quote/?cotizacion=${ct.id}`, })), ...unidades.map((u) => ({ tipo: 'unidad' as const, id: u.id, nombre: u.codigo, detalle: [u.desarrollo, u.modelo, u.status].filter(Boolean).join(' · '), url: `/quote/?unidad=${u.codigo}`, })), ] // Mezclar y limitar: prioridad por tipo (leads primero, luego contactos, // oportunidades, unidades) y recientes primero (ya ordenados por fecha desc) const porTipo: BuscadoItem['tipo'][] = ['lead', 'contacto', 'oportunidad', 'unidad'] items.sort((a, b) => { const ia = porTipo.indexOf(a.tipo) const ib = porTipo.indexOf(b.tipo) if (ia !== ib) return ia - ib return 0 }) return items.slice(0, limite) }