@patientos/website-kit
v0.2.19
Published
PatientOS clinic website components and patient-facing interactive islands.
Maintainers
Readme
@patientos/website-kit
React components and client-side patient surfaces for PatientOS clinic websites.
Install
npm install @patientos/website-kit @patientos/public-sdk react react-domCertificate funnel
import { CertificateFunnelClient } from '@patientos/website-kit/islands-impl'
import '@patientos/website-kit/website-kit.css'
export function CertificateFunnel() {
return (
<CertificateFunnelClient
publicApi={{ publishableKey: 'rcp_pk_…', apiBase: 'https://clinic.example.com' }}
services={[{ key: 'medical-certificate', label: 'Medical certificate' }]}
clinicName="Example Clinic"
clinicPhone="02 9000 0000"
/>
)
}clinicPhone is shown to the patient exactly as you pass it and dialled in E.164 — pass
1300 362 555 and every escape link renders tel:+611300362555.
publicApi.publishableKey is public by design and is validated against the consuming
site's origin by PatientOS. Never put server credentials or patient data in these props.
For a PatientOS clinic that explicitly registers http://localhost:*, the kit can carry a
verified patient session back to a dynamically allocated localhost dev server. The
one-time return credential is scrubbed from the URL and exchanged for a tab-scoped
bearer; deployed sites continue to use host-only patient cookies.
Store, cart and checkout
Use the runtime implementations in an ordinary hydrated React site. Import each surface
from its OWN subpath — the islands-impl barrel pulls the payment stack (BPOINT, the
address picker, Medicare) into every page that touches it:
import { StoreClient } from '@patientos/website-kit/islands-impl/store'
import { CartClient } from '@patientos/website-kit/islands-impl/cart'
import { CartButtonClient } from '@patientos/website-kit/islands-impl/cart-button'
import { CheckoutClient } from '@patientos/website-kit/islands-impl/checkout'
import '@patientos/website-kit/website-kit.css'
const storeApiOrigin = 'https://my.example-clinic.com'
<StoreClient storeApiOrigin={storeApiOrigin} productHandle="vitamin-d" />
<CartClient storeApiOrigin={storeApiOrigin} />
<CheckoutClient
storeApiOrigin={storeApiOrigin}
ordersHref="/account/orders"
completionHref="/checkout/complete"
/>Use productHandle for one exact product or categoryHandle for a category-filtered
catalogue; an exact product takes precedence when both are supplied.
Server-rendering the shop
Fetch the catalogue in your own per-request route loader and pass it as
initialCatalog. The grid then renders on the server — real product markup for a crawler
and no loading swap — and the island still refreshes in the background, so prices stay
honest. Never bake a catalogue into a statically built artifact.
import { createStoreClient, fetchStoreCatalog } from '@patientos/website-kit'
import type { StoreCatalog } from '@patientos/website-kit'
// in the route loader (runs per request, on the server)
const catalog = await fetchStoreCatalog(createStoreClient({ apiOrigin: storeApiOrigin }))
// in the component
<StoreClient storeApiOrigin={storeApiOrigin} initialCatalog={catalog} hideCartLink />fetchStoreCatalog returns null on any failure, and StoreClient then falls back to
its own client fetch. Pass hideCartLink when your site has its own header cart.
The header cart
<CartButtonClient/> is a count badge that opens a cart drawer. It costs no requests:
the count comes from the cart already in the shopper's browser, and the drawer quotes
POST /api/store/quote only when opened. The badge is absent (never 0) until the client
effect runs, so it adds nothing to server-rendered HTML and your header stays cacheable.
<CartButtonClient
storeApiOrigin={storeApiOrigin}
label="Cart"
cartHref="/cart"
checkoutHref="/checkout"
/>To build your own affordance instead, import the cart module directly:
import { cartItemCount, readCart, subscribeToCart } from '@patientos/website-kit/cart-storage'It is browser-only by design (readCart() answers empty on the server) and is shared by
every surface in one page, so a store block adding an item updates your badge with no
shared React tree.
Omit storeApiOrigin when PatientOS serves the clinic website and store on one origin.
For a separately deployed site it must be the clinic's verified patient/API origin, and
the site Origin must be registered on that clinic's active web-form channel. The client
always sends credentials: 'include' and cache: 'no-store'; checkout remains patient
sign-in-required.
The hosting clinic site owns completionHref. It defaults to /checkout/complete, must
resolve on that same origin, and receives the paid order ID as its order query parameter.
Cross-origin or malformed destinations safely fall back to the default. Cart lines carry
package-owned opaque IDs so paid reconciliation removes only the exact line generation
captured by that checkout attempt.
Document verification
DocumentVerifier is the marker for the document-verifier island. React sites can
import DocumentVerifierClient from @patientos/website-kit/islands-impl/document-verifier
and mount it client-side with { apiBase: 'https://patientos.com.au', publishableKey: 'your-site-public-key', token?: string }.
Include the kit stylesheet. A /verify page offers camera scanning and link entry;
a /verify/$token page can pass the path token for an automatic check. Verification
calls the existing anonymous API directly from the browser; camera frames stay local.
The component never follows scanned links. It checks the issued record, not altered
PDF bytes or the presenter's identity. Existing printed QR destinations remain valid.
Patient account overview (PAT-971)
The new runtime export PortalDashboardClient (./islands-impl/dashboard) combines appointment and document metadata using the existing portal client. Import ./portal-dashboard.css for its scoped patient-facing styles. Pass the verified portalApiOrigin, optional portalUrl, and site-owned appointmentsHref, documentsHref and bookHref; never pass patient data or a patient identifier. Mount client-only inside the site's account shell. No request runs during SSR.
This is a source addition awaiting a new package release; the published 0.2.11 does not include it. See docs/patient-dashboard-integration.md in the repository for behaviour, local pairing, test evidence and release gates.
