Prüfung / Lint, Typen, Tests (push) Canceled after 0s
Prüfung / Images bauen (pdf) (push) Canceled after 0s
Prüfung / Images bauen (web) (push) Canceled after 0s
Prüfung / Images bauen (worker) (push) Canceled after 0s
Images / bauen (pdf, docker/pdf.Dockerfile) (push) Canceled after 0s
Images / bauen (web, docker/web.Dockerfile) (push) Canceled after 0s
Images / bauen (worker, docker/worker.Dockerfile) (push) Canceled after 0s
**Suche.** Sie startete auf den Fallakten, und man musste die Art vorher wählen — die falsche Reihenfolge: wer einen Namen eintippt, weiss noch nicht, ob er eine Fallakte, ein Dokument oder eine Personalakte sucht. Er will wissen, wo der Name vorkommt. „Alle" ist jetzt der Auslieferungszustand und zeigt je Art fünf Treffer als eigenen Block, mit dem Weg in die volle Liste daneben. Getrennte Blöcke statt einer gemischten Liste nach Rang: ein Aktenzeichen neben einem Namen neben einer Dokumentnummer hat keine erkennbare Ordnung. Die einzelnen Arten bleiben als Filter, und nur dort gibt es Facetten und Blättern — beides bezieht sich auf eine Grundmenge, ein Statusfilter über Fall- und Personalakten zugleich wäre bei der einen ein Filter und bei der anderen ein Ausschluss. **Kopfzeile.** Die Bereiche waren Textlinks: von einem Fliesstextlink nicht zu unterscheiden und ohne Hinweis darauf, wo man gerade ist. Jetzt dieselbe Knopfform wie alles Bedienbare, der aktive Bereich gefüllt. Der gefüllte Zustand trägt hellen Text statt der Akzentfarbe: auf der getönten Fläche wäre Akzent auf Akzent und der Kontrast fiele unter die Untergrenze. Dazu etwas grosszügigere Polsterung an beiden Knopfgrössen, .btn-count für Zahlen in Knöpfen, und kein Doppelring mehr am Profilbild. Nachgesehen mit dem gebauten CSS im Browser, hell und dunkel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
72 lines
2.5 KiB
TypeScript
72 lines
2.5 KiB
TypeScript
/**
|
|
* 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.
|
|
*/
|
|
|
|
/** Die Arten, über die einzeln gesucht werden kann. */
|
|
export const SEARCH_KINDS = ['case', 'document', 'personnel'] as const
|
|
export type SearchKind = (typeof SEARCH_KINDS)[number]
|
|
|
|
/**
|
|
* Was in der Adresse stehen kann. `all` ist der Auslieferungszustand: wer
|
|
* einen Namen eintippt, will wissen, wo er überhaupt vorkommt, und nicht
|
|
* zuerst entscheiden, wonach er sucht. Die einzelnen Arten bleiben als Filter.
|
|
*/
|
|
export const SEARCH_SCOPES = ['all', ...SEARCH_KINDS] as const
|
|
export type SearchScope = (typeof SEARCH_SCOPES)[number]
|
|
|
|
export type SearchFilters = {
|
|
q: string
|
|
kind: SearchScope
|
|
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<string, string | undefined>): 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_SCOPES as readonly string[]).includes(kind ?? '')
|
|
? (kind as SearchScope)
|
|
: 'all',
|
|
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<SearchFilters>): 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()
|
|
}
|