/** * Die Filter einer Suche, als Adressparameter und als Objekt. * * Ohne Abhängigkeiten: die Suchseite baut damit ihre Adresse, und gespeicherte * Ansichten sind nichts anderes als eine solche Adresse. Dass beides dieselbe * Darstellung benutzt, ist der Punkt — eine Ansicht ist ein Lesezeichen. */ export const SEARCH_KINDS = ['case', 'document', 'personnel'] as const export type SearchKind = (typeof SEARCH_KINDS)[number] export type SearchFilters = { q: string kind: SearchKind status?: string templateKey?: string classification?: number /** Nur Akten, an denen der Suchende beteiligt ist. */ mine?: boolean /** Wiedervorlage bis einschliesslich diesem Tag. */ dueBefore?: string /** Seit diesem Zeitpunkt geändert, ISO. */ changedSince?: string /** Seit diesem Zeitpunkt **nicht** geändert, ISO. */ idleSince?: string cursor?: string } export function parseFilters(params: URLSearchParams | Record): SearchFilters { const get = (name: string): string | undefined => { const wert = params instanceof URLSearchParams ? params.get(name) : params[name] return wert && wert.length > 0 ? wert : undefined } const kind = get('kind') const stufe = get('classification') return { q: (get('q') ?? '').slice(0, 200), kind: (SEARCH_KINDS as readonly string[]).includes(kind ?? '') ? (kind as SearchKind) : 'case', status: get('status'), templateKey: get('templateKey'), classification: stufe !== undefined && /^[0-5]$/.test(stufe) ? Number(stufe) : undefined, mine: get('mine') === '1', dueBefore: get('dueBefore'), changedSince: get('changedSince'), idleSince: get('idleSince'), cursor: get('cursor'), } } /** Zurück in eine Adresse. Leere Filter fallen weg, damit sie kurz bleibt. */ export function toSearchParams(filters: Partial): string { const params = new URLSearchParams() for (const [name, wert] of Object.entries(filters)) { if (wert === undefined || wert === '' || wert === false) continue params.set(name, wert === true ? '1' : String(wert)) } return params.toString() }