मुख्य कंटेंट तक स्किप करें

Template authoring guide

Clinical templates drive the document editor, validation and print/PDF output from one JSON definition. Library templates live in code; clinic admins clone, edit checked properties and publish immutable versions.

Who can do this: Owner with clinical.template.manage. Where: Admin → Clinical Templates (/app/clinical-templates).

1. Template shape​

A template version contains schema, uiSchema, computed, print, phrases, optionSets, settings and metadata such as code, specialty, version, derivedFrom and licensing notice. Published versions are immutable. Documents store templateVersionId and schemaHash.

2. Restricted schema subset​

KeywordApplies toRules
typeallOne of string, number, integer, boolean, array, object
title, descriptionallStrings only
propertiesobjectRequired; names match ^[A-Za-z][A-Za-z0-9_]{0,63}$; constructor/prototype banned
requiredobjectUnique names that exist in properties
additionalPropertiesobjectMust be false
itemsarrayRequired single schema
enumstring/number/integer/boolean1 to 1000 unique values
minimum, maximumnumber/integerFinite numbers
minLength, maxLengthstringNon-negative integers
minItems, maxItemsarrayNon-negative integers
formatstringdate, date-time, time, email, phone-in, pincode-in

Forbidden keywords include pattern, $ref, $defs, oneOf, anyOf, allOf, not, if, then, else, const, default, multipleOf, uniqueItems, contains, prefixItems, propertyNames, unevaluated* and dependent*. Unknown fields are errors; data is never silently stripped.

3. Field type reference​

TypeStored valueValidationPrint
textstringstringEscaped inline text
textareastringstringEscaped text, newlines preserved
richtextmarkdown-lite stringRaw HTML rejected with FORMATSafe paragraphs, bold, italic and lists
numbernumberfinite, min, max, precisionNumber plus unit
date / datetime / timestringFixed date/time formatsLocale date/time
select / radioscalarIn options/option setLocalized label
multiselectscalar arrayMembers only, no duplicatesComma-separated labels
checkboxbooleanbooleanYes/No
yesnoyes, no, naenumYes/No/N/A
tablerow objects arrayColumns and no unknown cellsHTML table
repeateritem objects arrayitemFields recursivelyNumbered list
attachmentattachment arrayid, MIME, size, countAttachment list
tooth-chartobjectFDI tooth dataOdontogram SVG + table
body-mapobjectRegion markersBody map SVG + marker list
audiogramobjectEar, conduction, frequency, thresholdAudiogram + PTA
growth-chartobject0 to 5 year measurementsWHO chart if data installed
milestone-gridarrayMilestone status rowsDomain × age grid
rom-mmt-gridarrayROM/MMT rowsROM/MMT table
scalenumberVAS/NRS/Likert configBar or label
sensory-quadrantarrayQuadrant items/scoresQuadrant bars + disclaimer
photo-comparisonarrayBefore/after photo pairs with region + capture datesSide-by-side thumbnails (or placeholders) + table

4. uiSchema options​

Use key, widget, label, help, width, visibleIf, requiredIf, readOnly, copyForward, vital, defaultFrom, print.hideIfEmpty, print.style, options, optionSetRef, unit, min, max, precision, columns, fixedRows and itemFields. English labels are required; Hindi labels are recommended.

5. Conditions syntax​

Leaf condition:

{ "field": "smoker", "op": "eq", "value": "yes" }

Operators: eq, neq, in, nin, gt, gte, lt, lte, empty, notEmpty. Combine with all, any and not. Inside repeater item fields, use @.key for the current item. Invalid conditions lint as INVALID_CONDITION, CONDITION_UNKNOWN_FIELD or CONDITION_ITEM_REF_OUTSIDE_REPEATER.

6. Computed-function reference​

FunctionArgumentsNotes
sum, avg, min, maxvaluesMissing input returns null
countvaluesNon-empty count
roundvalue, digitsHalf-away-from-zero
percentpart, totalnull for invalid denominator
bmiweightKg, heightCmBMI
bandvalue, bandsFirst matching band; max: null catch-all
ageYears, ageMonthsdob, optional asOfNever reads wall clock
egfrCkdEpi2021creatinine, age, sexAdults 18 to 130
ldlFriedewaldTC, HDL, TGnull when TG is 400 or more or result negative
meanArterialPressuresystolic, diastolicMAP
quadrantScoreitems, optional quadrantSensory totals
ptaaudiogram, ear, conduction, frequenciesPure-tone average
countWherelist, key, valueCount matching rows
growthZScore, growthPercentilegrowth, indicator, whichWHO LMS if reference data installed

The server recomputes computed values on save/sign and sanitizes NaN, Infinity and undefined to null.

7. Prefill, smart phrases, normal macros and copy-forward​

Allowed bindings are patient.fullName, patient.ageYears, patient.sex, patient.dob, patient.uhid, aliases patient.age and patient.name, encounter.*, vitals.latest.*, practitioner.*, clinic.* and previous.data.*.

Smart phrases use dot triggers such as .fever. Tokens like {{patient.age}} resolve only from the whitelist; unknown tokens stay as typed. *** marks the next cursor selection. Scope precedence is personal, clinic, then pack.

Normal macros fill empty fields from section.normal.values and optional normal text. Copy-forward excludes copyForward:false, vitals, fields with defaultFrom and per-visit facts.

8. Print options​

Document hints include paper, orientation, title, hideEmptyFields, showSectionHeadings, signature.label, signature.showRegistrationNumber, layout, footer and confidential. Field hints include hideIfEmpty and style. Drafts print with DRAFT watermark; finals print signature block, document number, QR verification, page numbering and end-of-report marker.

9. Lint and validation codes​

CodeSeverityMeaning
ROOT_NOT_OBJECTErrorRoot schema is not an object
FORBIDDEN_KEYWORDErrorForbidden schema keyword
UI_KEY_NOT_IN_SCHEMA / SCHEMA_KEY_NOT_IN_UIErrorSchema/UI mismatch
DUPLICATE_KEY / INVALID_KEYErrorBad or repeated key
UNKNOWN_WIDGET / WIDGET_SCHEMA_MISMATCHErrorWidget problem
MISSING_OPTIONS, INVALID_OPTION, DUPLICATE_OPTION, OPTIONS_ENUM_MISMATCH, UNKNOWN_OPTION_SETErrorOption problem
MISSING_COLUMNS, INVALID_COLUMN_TYPE, MISSING_ITEM_FIELDSErrorTable/repeater problem
CONDITION_UNKNOWN_FIELD, INVALID_CONDITION, CONDITION_ITEM_REF_OUTSIDE_REPEATERErrorCondition problem
INVALID_BINDINGErrorPrefill/phrase binding not allowed
UNKNOWN_COMPUTED_FN, UNKNOWN_COMPUTED_REF, INVALID_COMPUTED_ARG, INVALID_PRECISIONErrorComputed problem
TOO_MANY_FIELDSErrorMore than 400 fields
NESTING_TOO_DEEPErrorMore than 3 property levels
MISSING_EN_LABELErrorEnglish label missing
TEXT_WITHOUT_MAX_LENGTHWarningFree text lacks maxLength
MISSING_HI_LABELWarningHindi label missing

Document validation errors include REQUIRED, TYPE, ENUM, MIN, MAX, MIN_LENGTH, MAX_LENGTH, MIN_ITEMS, MAX_ITEMS, FORMAT, UNKNOWN_FIELD, widget PRECISION and DUPLICATE.

10. Versioning and publish rules​

  1. Clone a library template. Library templates are never edited in place.
  2. Edit a draft using supported transformations: labels, hide/show, required, options, reorder, normal macro, smart phrases, print hints or co-signature.
  3. Save draft and fix checks.
  4. Preview with sample data.
  5. Publish. A new immutable version is created; existing documents keep their old version.
  6. Archive templates that should not be used for new documents.

Shipped pack maintainers edit packs/<pack>/src/templates.mjs, bump version, rebuild generated JSON and run pack checks.

11. Licensing notice​

Copyright

Do not copy proprietary assessment instruments unless the clinic holds a valid licence and the licence permits it. Sensory Profile 2, PLS and Bayley scales are not shipped. PHQ-9 and GAD-7 are included because their published notices allow reproduction, translation, display and distribution; Hindi wording is an Achal translation and not a validated translation. WHO growth reference data is a separate non-commercial data set.

12. Testing checklist​

  • Save a valid draft and test empty required fields.
  • Test every condition branch and computed value, including missing inputs.
  • Test option labels in English and Hindi.
  • Test normal macro, smart phrases and {{patient.age}} tokens.
  • Test print/PDF with empty fields, long text, Hindi and attachments.
  • Confirm restricted templates create restricted documents where needed.
  • Confirm licensed/proprietary content is not copied without permission.

13. Tenant editor workflow in detail (limited editor, R1)​

Who: users with clinical.template.manage. Where: Clinical Templates (/app/clinical-templates).

  1. Clone a library template (or one of your own) and give it a unique code. Library templates are never edited in place; your clone records derivedFrom so you are told when the library publishes an update.

  2. Edit the draft — every change is a small, checked transformation of the template JSON:

    ChangeWhat is writtenRule
    Renamelabel.en / label.hi of sections, fields and optionsEnglish is required (checked on save)
    Hide / showhidden: true + a never-true visibleIf on the field (a previous visibleIf is kept and restored)The schema property stays; hidden fields are skipped by validation, so they are never required
    Requiredthe parent object's required listOnly for top-level fields and list-item fields; properties are never added or removed
    Optionsoptions of the field or the shared optionSets[...]; the schema enum of every field using the list is kept in syncValues are typed by the schema (text or number), unique, at least one option remains
    Reorderorder of sections, rows/fields inside a section, list-item fieldsFields never move to another section (so “All normal” targets stay valid)
    All normalsection.normal { text, textField, values }Targets must be fields of the same section; values must match the field type
    Smart phrasesphrases[{ trigger, text, label }]Trigger 1–32 of a–z 0–9 _ -, unique, ≤ 200 phrases
    Print hintfield.print.hideIfEmpty—
    Co-signaturesettings.requiresCosign—
  3. Save draft — the server validates the draft and returns the check results (errors and warnings) shown in the Checks panel. Saving uses the template's version number; if someone else saved first you are asked to reload.

  4. Preview — the Preview tab renders the draft in the real document editor with sample data.

  5. Publish — blocked while there are errors; creates a new immutable version. Documents keep the version they were written with.

  6. Archive — retires a custom template for new documents.

The full drag-and-drop builder (new fields, rules, import/export) is planned for R5.

14. Specialty widgets reference (detailed)​

Specialty widgets are field types registered by the platform (clinical-core registerSpecialtyWidgets()); the editor renders them with keyboard-operable, labelled controls and a text alternative, and the print renders a static SVG (no scripts) plus a findings table. Bind each widget to a top-level key (data may nest at most 3 levels).

WidgetStored valueConfiguration on the fieldPrint
tooth-chart{ numbering: FDI|UNIVERSAL, dentition: permanent|primary|mixed, teeth: [{ tooth, status, surfaces[], procedure, notes }] } — teeth always stored as FDI codes (11–48, 51–85)—Odontogram (5 surfaces per tooth, crossed missing / extracted teeth), legend, findings table
body-map{ markers: [{ region, kind: pain|lesion|injury|swelling|numbness|other, severity 0–10, notes }] } — 50 named front/back regions (f_knee_r, b_lower_back…)—Front + back outline with numbered markers, marker list
audiogram{ points: [{ ear R|L, conduction AC|BC, frequency 250…8000, threshold −10…120 (5 dB steps), masked, noResponse }] } — bone conduction up to 4 kHz—Audiogram with ASHA symbols (O △ X □ < [ > ], NR arrow), threshold table with PTA
growth-chart{ sex, dob, measurements: [{ date, weightKg, lengthCm, headCm, position lying|standing }] } — 0–5 years—One chart per indicator vs WHO −3/−2/0/+2/+3 SD curves; table with z-score and percentile
milestone-grid[{ key, status achieved|emerging|notYet|notAssessed, notes }] (+ custom rows c_* with label, domain, band)milestones: [{ key, domain, band, label }], optional domains, bands, allowCustomDomain × age-band grid with ✓ ◐ ✗ symbols and counts
rom-mmt-grid[{ key, romLeft, romRight, mmtLeft, mmtRight, notes }] (+ custom rows c_* with label)rows: [{ key, joint, movement, normal: { min, max } }], showRom, showMmt, allowCustomTable with normal range; ROM < 90 % of normal flagged ↓; MMT Oxford/MRC 0–5 (±)
scalenumberscaleType: vas|nrs|likert, min, max, step, anchors: { min, max }, options (Likert)Value / max with a bar and anchors, or the Likert label
sensory-quadrant[{ key, quadrant, score }]items: [{ key, quadrant, label }], optional quadrants, scale, bandsQuadrant bars, totals table, item responses, "not a standardized instrument" note
photo-comparison[{ region, before?: { attachmentId, name, mime (image/*), size, capturedOn }, after?: { … }, notes }] — up to 12 pairs; "after" cannot be dated before "before"; a photo is used onceoptional maxSizeBytes (default 10 MB)Two-cell table per pair: validated raster thumbnails from the renderer context (attachmentThumbnails) or labelled placeholders; interval in days; text table of file names / dates

Computed functions added with the widgets:

  • pta(audiogram, ear, conduction = 'AC', frequencies = [500, 1000, 2000]) — pure-tone average; no value when a frequency is missing or marked no-response.
  • countWhere(list, key, value) — e.g. milestones achieved, goals met.
  • growthZScore(growth, indicator, which = 'latest') / growthPercentile(…) — indicator is wfa, lhfa, hcfa or bfa; WHO LMS method with the restricted application beyond ±3 SD for weight-based indicators and the ±0.7 cm lying/standing length correction.
WHO growth reference data

The WHO Child Growth Standards tables are shipped as a separate data file set (not part of the engine) because WHO publishes them under a non-commercial licence (CC BY-NC-SA 3.0 IGO). If the files are removed, growth charts still record measurements but z-scores and curves are not shown. See backend/packages/clinical-core/assets/who/README.md.

Pack templates shipped with the platform​

Platform templates live in backend/packages/clinical-core/packs/<pack>/. They are authored as code (src/templates.mjs, one declaration per field) and generated into templates/*.json with node backend/packages/clinical-core/packs/_tools/build.mjs; node …/_tools/check.mjs lints every template. A test fails when the JSON is stale. Every shipped template passes the publishing linter with no errors and no warnings (all labels in English and Hindi, every free-text field capped).