Consent inside an antd Form
patientRoutineThe common case. `signatureRequired()` treats a decline as a valid answer — a rule demanding outcome === "signed" would make refusal impossible to submit.
import { Form } from "antd";
import { Signature, signatureRequired } from "@oxygenui-design/signature";
import "@oxygenui-design/signature/styles.css";
<Form.Item name="consent" rules={[signatureRequired()]}>
<Signature
now={serverTime}
meaning="consent"
attestation="I agree to the treatment described above."
/>
</Form.Item>;
The patient who refused to sign
consentDeclinedSeven outcomes, not two. A refusal and an unopened form are different clinical facts, and the union makes it impossible to record them the same way.
// The value is a discriminated union, so this compiles only if every
// outcome is handled — including the three that are not "signed".
switch (value.outcome) {
case "signed": return file(value.image, value.signedAt);
case "declined": return recordRefusal(value.reason, value.recordedAt);
case "unable": return recordUnable(value.reason, value.witness); // witness is required
case "verbal": return recordVerbal(value.witness);
case "on-paper": return awaitScan();
case "pending": return null;
case "revoked": return revoke(value.revokedAt);
}
What it emits, and why it is not a Consent
provenanceConsentConsent carries no signature element in R4 or R5 — only Provenance.signature does. The component emits a transaction Bundle so the two land together or not at all.
import { toFhirBundle } from "@oxygenui-design/signature";
import { patientRoutine } from "@oxygenui-design/fixtures";
// A transaction Bundle: the Consent and the Provenance that signs it, posted
// together or not at all. Emitting the Consent alone would store an agreement
// with nothing proving anyone made it.
const bundle = toFhirBundle(value, {
release: "R4",
subject: {
display: "Amara Okonkwo",
reference: `Patient/${patientRoutine.id}`,
},
});
// bundle.entry[0] → POST Consent
// bundle.entry[1] → POST Provenance, carrying Provenance.signature
// The signer travels on the value, not in these options: who signed is part of
// what was captured, and re-supplying it here would let the two disagree.