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.
60 lines
2.6 KiB
TypeScript
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 }
|
|
}
|