You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sims-hq/apps/hq/src/repos-search.ts

60 lines
2.6 KiB
TypeScript

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<SearchResult> {
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 }
}