import type { DB } from './db' /** * Global quick-search (D28) for the Ctrl-K palette: clients, issued documents, and * tickets in one call. Each hit carries the route to jump to. Top-N per type (a jump * box, not a report) — the flag says when a type was capped so it never reads as "all". */ export interface SearchHit { type: 'client' | 'document' | 'ticket' id: string; label: string; sub: string; to: string } export interface SearchResult { hits: SearchHit[]; capped: string[] } export async function globalSearch(db: DB, q: string, perType = 6): Promise { const term = q.trim() if (term.length < 2) return { hits: [], capped: [] } const like = `%${term}%` const capped: string[] = [] const over = perType + 1 // fetch one extra to detect "more exist" const clients = await db.all<{ id: string; code: string; name: string; status: string }>( `SELECT id, code, name, status FROM client WHERE LOWER(name) LIKE LOWER(?) OR LOWER(code) LIKE LOWER(?) ORDER BY name LIMIT ?`, like, like, over, ) if (clients.length > perType) capped.push('clients') const docs = await db.all<{ id: string; doc_no: string; doc_type: string; cname: string }>( `SELECT d.id, d.doc_no, d.doc_type, c.name AS cname FROM document d JOIN client c ON c.id = d.client_id WHERE d.doc_no IS NOT NULL AND (LOWER(d.doc_no) LIKE LOWER(?) OR LOWER(c.name) LIKE LOWER(?)) ORDER BY d.doc_no DESC LIMIT ?`, like, like, over, ) if (docs.length > perType) capped.push('documents') const tickets = await db.all<{ id: string; client_id: string; kind: string; description: string; cname: string }>( `SELECT t.id, t.client_id, t.kind, t.description, c.name AS cname FROM ticket t JOIN client c ON c.id = t.client_id WHERE LOWER(t.description) LIKE LOWER(?) OR LOWER(t.kind) LIKE LOWER(?) ORDER BY t.opened_on DESC LIMIT ?`, like, like, over, ) if (tickets.length > perType) capped.push('tickets') const hits: SearchHit[] = [ ...clients.slice(0, perType).map((c) => ({ type: 'client' as const, id: c.id, label: `${c.code} · ${c.name}`, sub: c.status, to: `/clients/${c.id}`, })), ...docs.slice(0, perType).map((d) => ({ type: 'document' as const, id: d.id, label: d.doc_no, sub: `${d.doc_type} · ${d.cname}`, to: `/documents/${d.id}`, })), ...tickets.slice(0, perType).map((t) => ({ type: 'ticket' as const, id: t.id, label: t.description !== '' ? t.description : (t.kind !== '' ? t.kind : '(ticket)'), sub: `Ticket · ${t.cname}`, to: `/clients/${t.client_id}?tab=tickets`, })), ] return { hits, capped } }