Ansora UI
Components

Vyhledání pacienta

Najde pacienta podle jména, rodného čísla nebo telefonu a nabídne cestu ven, když nalezen není.

identityinput

Install

pnpm dlx shadcn@4.23.0 add @ansora/patient-search

Examples

Usage

Fully controlled patient lookup: a field, a native radio group of candidates, and the "nenalezen -> zalozit" exit. It fetches nothing, debounces nothing, caches nothing and filters nothing — results are whatever your screen hands it, in your order.

Required: value (PatientCandidate | null) + onChange, query + onQueryChange, results, status ("idle" | "loading" | "error"), caption (the VISIBLE field label), resultsLabel (the INVISIBLE name of the group), loadingLabel, and empty / error, each one object of { title, description } so half an empty state cannot be supplied. Optional: create: { label, onCreate }, clear: { label }, className. No rest spread — className is the only escape hatch.

value and query are independent, and that is the point. The receptionist's live-call screen arrives with value already set to the AI's guess from the calling number while query is still empty, because nobody typed anything; reservations' Nova rezervace starts with both empty and is typed at. A combobox that projects its selection into its own text field would show a name nobody searched for on the first screen, and clearing the box would clear the patient. Never write one prop from the other.

The confirmed patient is always among the options, even when a newer results no longer contains it — it is prepended. So a background re-query, a failed one included, can never make the chosen patient disappear.

Never in the URL. The search query is the one filter that must NOT become a query parameter, and that is a deliberate, documented exception to the web-ui-conventions default. A rodne cislo in browser history, in a server access log or in a pasted link is a data incident. Hold query in component state on the screen; put the CHOSEN patient's id in the URL if you need one there.

Validate the query where you issue it, not here. A query that IS a birth number should be looked up exactly rather than fuzzily — call isValidRodneCislo from @ansora/core/schema in your data layer and switch the lookup, then say so in the row's flags. It is deliberately NOT imported by this component: the component does not choose which patients appear, so the check would decide nothing here, and that module builds a zod schema at module scope — importing it from a "use client" file put 947 092 B raw / 263 136 B gzip of zod into one client chunk the last time it happened (packages/core/package.test.ts).

A failed search never offers creation. create renders on the empty branch and beneath a populated list, never on error: a search that fell over is not evidence that the patient does not exist, and a create button there is how a duplicate record is made for somebody already in the system. clear is never withheld — un-choosing a wrong guess invents nothing.

Rows are native <input type="radio"> in a <fieldset>: arrow keys move and select, so "confirm or override in one keystroke" comes from the platform rather than from a keydown handler nothing in this repo can execute. The mark is accent-foreground, never bg-primary — green is the brand colour, not a state. An identifier renders through .identifier (slashed zero) and never truncates; a birth date renders through .num and is formatted here by formatDate; the NAME is what gives way.

import { PatientSearch, type PatientCandidate } from "@/components/ansora/patient-search"
import { StatusPill } from "@/components/ui/ansora/status-pill"

const [query, setQuery] = React.useState("")
const [patient, setPatient] = React.useState<PatientCandidate | null>(null)

<PatientSearch
  caption="Pacient"
  resultsLabel="Nalezeni pacienti"
  loadingLabel="Hledam…"
  query={query}
  onQueryChange={setQuery}
  value={patient}
  onChange={setPatient}
  status={state.status}
  results={state.results.map((p) => ({
    id: p.id,
    name: p.name,
    identifier: { caption: "Rodne cislo", value: p.rodneCislo },
    born: { caption: "Narozena", at: p.bornAt },
    contact: p.phone,
    flags: p.exact ? <StatusPill tone="info">Shoda podle rodneho cisla</StatusPill> : undefined,
  }))}
  empty={{
    title: "Nikdo takovy v kartotece neni",
    description: "Zkuste rodne cislo nebo telefon. Pokud pacient chybi, zalozte ho.",
  }}
  error={{
    title: "Vyhledavani se nezdarilo",
    description: "Spojeni s kartotekou vypadlo. Zkuste to prosim za okamzik znovu.",
  }}
  create={{ label: "Zalozit noveho pacienta", onCreate: openCreateDialog }}
  clear={{ label: "Neni to on ani ona" }}
/>

Files

Dependencies

  • @ansora/base
  • @ansora/empty-state
  • https://ui.shadcn.com/r/styles/base-nova/input.json
  • https://ui.shadcn.com/r/styles/base-nova/button.json

On this page