Skip to content
Open
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
137 commits
Select commit Hold shift + click to select a range
565cee6
feat(questionnaire-v2): builder model groundwork for the studio
bodhish Aug 1, 2026
e0edbde
feat(questionnaire-v2): full renderer (form/) — one-scroll canvas wit…
bodhish Aug 1, 2026
048e994
feat(questionnaire-v2): WYSIWYG studio — 3-pane builder with realtime…
bodhish Aug 1, 2026
38262b7
fix(questionnaire-v2): studio review fixes + one-scroll spec alignment
bodhish Aug 1, 2026
c544723
test(questionnaire-v2): scope the protected-note unlock assertion to …
bodhish Aug 1, 2026
dac7148
feat(questionnaire-v2): studio reference-gap pass — inspector tabs, l…
bodhish Aug 2, 2026
49f0d22
feat(questionnaire-v2): inspector Question tab styled per the reference
bodhish Aug 2, 2026
7f38019
feat(questionnaire-v2): revision viewer on the full renderer, save-ve…
bodhish Aug 2, 2026
e51c166
fix(questionnaire-v2): one border per input on the canvas
bodhish Aug 2, 2026
dcf96ec
feat(questionnaire-v2): fullscreen studio — no app sidebar, viewport …
bodhish Aug 2, 2026
0497e4f
refactor(questionnaire-v2): deep-review pass — perf, a11y, dedup, con…
bodhish Aug 2, 2026
22d0b61
refactor(questionnaire-v2): close out deep-review stragglers
bodhish Aug 2, 2026
84922e9
feat(questionnaire-v2): surface the selected structured type everywhe…
bodhish Aug 2, 2026
d4a8df7
refactor(questionnaire): canonical StructuredQuestionType in the type…
bodhish Aug 2, 2026
734bbb6
feat(questionnaire-v2): structured-type contract — one definition per…
bodhish Aug 2, 2026
6141bb7
feat(questionnaire-v2): fill-mode engine seams — draft seed, real cle…
bodhish Aug 2, 2026
e5d42bb
feat(questionnaire-v2): fill submit pipeline — validate, compose one …
bodhish Aug 2, 2026
cbad1c3
feat(questionnaire-v2): local fill drafts — debounced autosave with s…
bodhish Aug 2, 2026
b967515
feat(questionnaire-v2): the fill page — two-tab shell, outline, autos…
bodhish Aug 2, 2026
28a6859
feat(questionnaire-v2): mount the fill page on the questionnaire routes
bodhish Aug 2, 2026
60987a8
test(questionnaire-v2): fill-page specs — validation, autosave, shell…
bodhish Aug 2, 2026
98c0ce4
fix(questionnaire-v2): verification-round fixes — pristine-before-nav…
bodhish Aug 2, 2026
0544321
docs(questionnaire-v2): tighten draft-layer comments
bodhish Aug 2, 2026
b321629
feat(questionnaire-v2): explicit resume/discard prompt for local fill…
bodhish Aug 2, 2026
30b9066
feat(questionnaire-v2): multi-questionnaire fill sessions — per-form …
bodhish Aug 2, 2026
b116298
fix(questionnaire-v2): persist the fill session when its shape changes
bodhish Aug 2, 2026
1328e27
feat(questionnaire-v2): subject-generic fill — FillSubject union, sub…
bodhish Aug 2, 2026
d2184c9
feat(questionnaire-v2): restore the /consultation picker filter, hard…
bodhish Aug 2, 2026
16f5b74
feat(questionnaire-v2): structured types declare their subjects — pic…
bodhish Aug 2, 2026
a06d855
feat(questionnaire-v2): plugin-extensible structured types — runtime …
bodhish Aug 2, 2026
44c4e44
fix(questionnaire-v2): re-resolve structured types when a plugin regi…
bodhish Aug 2, 2026
c1ead36
feat(questionnaire-v2): action registry + descriptor/invoke Scribe co…
bodhish Aug 2, 2026
33f29cd
fix(questionnaire-v2): tighten the action choke point after review
bodhish Aug 2, 2026
e00388e
refactor(questionnaire-v2): preview encounter drafts through the v2 r…
bodhish Aug 2, 2026
4d5b5f7
refactor(questionnaire-v2): delete the legacy fill stack and the v2 s…
bodhish Aug 2, 2026
8b141c5
refactor(questionnaire-v2): relocate the engine under form/engine
bodhish Aug 2, 2026
4616fb5
docs(questionnaire-v2): rewrite the module map for one renderer, one …
bodhish Aug 2, 2026
f4c933f
feat(questionnaire-v2): restore the explicit server draft on the fill…
bodhish Aug 2, 2026
251b2e1
fix(questionnaire-v2): encounter-only server drafts, non-mutating dra…
bodhish Aug 2, 2026
3aecd14
fix(questionnaire-v2): give the fixed encounter form an encounter sub…
bodhish Aug 2, 2026
bd93fb4
test(questionnaire-v2): drive the legacy clinical flows through the v…
bodhish Aug 2, 2026
b03b7bc
fix(questionnaire-v2): correct the local-draft lifecycle around struc…
bodhish Aug 3, 2026
1643171
fix(questionnaire-v2): submit time answers as HH:mm:ss and drop clear…
bodhish Aug 3, 2026
d15894a
fix(questionnaire-v2): contain structured failures and stop them bloc…
bodhish Aug 3, 2026
36d433f
fix(questionnaire-v2): enforce plugin namespace ownership and tidy th…
bodhish Aug 3, 2026
04ebeb6
docs(questionnaire-v2): state the server-draft gate and compose diver…
bodhish Aug 3, 2026
8b70bb8
test(questionnaire-v2): pin the draft lifecycle, time format and pati…
bodhish Aug 3, 2026
f321cb3
fix(questionnaire-v2): keep un-fetchable added forms in the draft on …
bodhish Aug 3, 2026
e13430d
Questionnaire v2: submit-path hardening and render-failure containmen…
bodhish Aug 3, 2026
47e70ff
test(questionnaire-v2): paginate fixture slug lookup past the first 1…
bodhish Aug 3, 2026
468a6f0
fix(questionnaire-v2): evaluate exists enable-when like the backend, …
bodhish Aug 3, 2026
374e188
fix(questionnaire-v2): clear fill drafts only after successful auth; …
bodhish Aug 3, 2026
a74983b
fix(questionnaire-v2): scope post-auth draft clear to other users; dr…
bodhish Aug 3, 2026
0d65959
fix(questionnaire-v2): studio save race, type-change residue, valuese…
bodhish Aug 3, 2026
018827a
fix(questionnaire-v2): metadata-side save race, choice-precedence gat…
bodhish Aug 3, 2026
be716d9
fix(questionnaire-v2): plugin boundary resets on re-register; namespa…
bodhish Aug 3, 2026
bf2ffa9
fix(questionnaire-v2): valueset quantity answers submit their coding
bodhish Aug 3, 2026
02b9dc1
fix(questionnaire-v2): fall back a quantity's coding to its default unit
bodhish Aug 3, 2026
065e5b0
fix(questionnaire-v2): submit guards — draft linkage, batch cap prefl…
bodhish Aug 3, 2026
0f9ed9e
fix(questionnaire-v2): isolate the P1-9 spec, in-memory batch-cap fil…
bodhish Aug 3, 2026
9a2d1a8
fix(questionnaire-v2): required structured sections in a broken state…
bodhish Aug 3, 2026
6973ae9
fix(questionnaire-v2): notices stop contradicting the required hard-b…
bodhish Aug 3, 2026
b0c68c0
fix(questionnaire-v2): fill mounts enforce subject family and encount…
bodhish Aug 3, 2026
9c4393d
fix(questionnaire-v2): freeze the session during submit; extract sess…
bodhish Aug 3, 2026
9d956c1
fix(questionnaire-v2): gate the note popover and draft-resume control…
bodhish Aug 3, 2026
8467e50
refactor(questionnaire-v2): submit bodies typed to the real backend c…
bodhish Aug 3, 2026
4216cc9
perf(questionnaire-v2): per-form autosave signatures; deferred studio…
bodhish Aug 3, 2026
f1688b5
fix(questionnaire-v2): drop dead session signature, seed primary form…
bodhish Aug 3, 2026
a82110d
fix(questionnaire-v2): checkpoint fixes — server-draft cache revert, …
bodhish Aug 4, 2026
3cbed9a
feat(questionnaire-v2): edit-log vocabulary on the questionnaire resp…
bodhish Aug 4, 2026
6a56d69
feat(questionnaire-v2): row identity and structural equality for the …
bodhish Aug 4, 2026
97c336d
fix(questionnaire-v2): reconcile RowEdit with Task 1's shipped edit v…
bodhish Aug 4, 2026
5e3155e
fix(questionnaire-v2): close the Partial<TRow> gap in the vocabulary-…
bodhish Aug 4, 2026
6cf9f8f
feat(questionnaire-v2): edit-log reducer with one-edit-per-row coales…
bodhish Aug 4, 2026
792e60c
fix(questionnaire-v2): resolve edit-log resurrection ops by baseline …
bodhish Aug 4, 2026
9f07ba0
feat(questionnaire-v2): baseline+edits projection with display-only o…
bodhish Aug 4, 2026
f91c660
fix(questionnaire-v2): resolve projectRows' add-loop by the same last…
bodhish Aug 4, 2026
8684ea7
feat(questionnaire-v2): resolve an edit log into create/update/remove…
bodhish Aug 4, 2026
0a1de95
fix(questionnaire-v2): resolveChanges consults baseline for orphans a…
bodhish Aug 4, 2026
a711b5a
fix(questionnaire-v2): reclassify an add colliding with baseline to u…
bodhish Aug 4, 2026
9f9f8f5
feat(questionnaire-v2): dual-contract structured definitions with a r…
bodhish Aug 4, 2026
3b41fe4
feat(questionnaire-v2): useStructuredRows — the baseline/edits/projec…
bodhish Aug 4, 2026
0c8ffd3
fix(questionnaire-v2): refuse contract-v2 plugin registrations for th…
bodhish Aug 4, 2026
f2342dd
feat(questionnaire-v2): dual-contract shim across submit, validation …
bodhish Aug 4, 2026
c540a56
fix(questionnaire-v2): fix the initialEdits seed latch and extract th…
bodhish Aug 4, 2026
94940e1
fix(questionnaire-v2): restore mergePatch's normalizePatch-undefined …
bodhish Aug 4, 2026
7154d11
fix(questionnaire-v2): pin the untyped-structured draft gate and subs…
bodhish Aug 4, 2026
0658eec
test(questionnaire-v2): pin the structured fixture questionnaires beh…
bodhish Aug 4, 2026
dc442ea
feat(questionnaire-v2): port time_of_death to contract v2 and make it…
bodhish Aug 4, 2026
ee4c2fd
feat(questionnaire-v2): appointment differ and slot rule for contract…
bodhish Aug 4, 2026
f7fea04
test(questionnaire-v2): pin appointment's PROJECTION AND SUBMIT AGREE…
bodhish Aug 4, 2026
626a3c4
fix(questionnaire-v2): retain dropped orphan edits, pin the prune's w…
bodhish Aug 4, 2026
87536d5
fix(questionnaire-v2): agree projection/differ on empty rows, fix the…
bodhish Aug 4, 2026
5fe1772
fix(questionnaire-v2): filter blanks before the singleton pick and co…
bodhish Aug 4, 2026
00799c8
fix(questionnaire-v2): agree the singleton's projection and differ un…
bodhish Aug 4, 2026
e8d7c0d
fix(questionnaire-v2): dedupe droppedEdits under StrictMode, pin the …
bodhish Aug 4, 2026
80c54bd
feat(questionnaire-v2): one structured field error primitive; port ap…
bodhish Aug 4, 2026
4231f3d
fix(questionnaire-v2): correct the ordering claim in resolveSingleton…
bodhish Aug 4, 2026
c489b21
feat(questionnaire-v2): response-backed charge item rows with one dis…
bodhish Aug 4, 2026
fdd08c0
fix(questionnaire-v2): stop the appointment slot-sheet render loop an…
bodhish Aug 4, 2026
1ff76c4
feat(questionnaire-v2): responsive structured list primitive; port ch…
bodhish Aug 4, 2026
966020d
fix(questionnaire-v2): correct the inline-field-error allow-list and …
bodhish Aug 4, 2026
5a4814c
feat(questionnaire-v2): encounter row derivation and differ as pure f…
bodhish Aug 4, 2026
89fa7fd
fix(questionnaire-v2): make StructuredList's mobile error unreachable…
bodhish Aug 4, 2026
f9e1bdc
feat(questionnaire-v2): port encounter to single-row contract v2 with…
bodhish Aug 4, 2026
2134d26
fix(questionnaire-v2): render and force-expand on an unmatched field_…
bodhish Aug 4, 2026
d46bb35
feat(questionnaire-v2): port files to contract v2, the sole draft-exc…
bodhish Aug 4, 2026
dd4283f
docs(questionnaire-v2): correct encounter's discharge-disposition doc…
bodhish Aug 4, 2026
cdf211b
fix(questionnaire-v2): stop the files editor from committing rejected…
bodhish Aug 4, 2026
caf5e5d
fix(questionnaire-v2): allow-list files for inline field errors
bodhish Aug 4, 2026
25d07a5
test(questionnaire-v2): per-type matrix for the structured singletons
bodhish Aug 4, 2026
b7e8028
fix(questionnaire-v2): defer the initialEdits seed's commit past the …
bodhish Aug 4, 2026
56ce670
test(questionnaire-v2): per-type matrix for structured lists, includi…
bodhish Aug 4, 2026
5fe3395
test(questionnaire-v2): pin zero-upsert, refetch-safety and structure…
bodhish Aug 4, 2026
d8bb0f5
fix(questionnaire-v2): add liveness guard to the deferred seed commit…
bodhish Aug 4, 2026
e06643c
feat(questionnaire-v2): port allergy_intolerance to contract v2, add …
bodhish Aug 4, 2026
0e1bcb7
feat(questionnaire-v2): port symptom to contract v2
bodhish Aug 4, 2026
f425c16
feat(questionnaire-v2): port diagnosis to contract v2
bodhish Aug 4, 2026
63e44ef
feat(questionnaire-v2): rebuild plugin fill capability as a generic a…
bodhish Aug 4, 2026
c6184a2
feat(questionnaire-v2): port medication_statement to contract v2
bodhish Aug 4, 2026
e3f6d99
feat(questionnaire-v2): port medication_request to contract v2
bodhish Aug 4, 2026
300ed55
feat(questionnaire-v2): port service_request to contract v2, add shar…
bodhish Aug 4, 2026
f288c76
fix(questionnaire-v2): close the three Phase-1 structured-core carry-…
bodhish Aug 4, 2026
52586cf
feat(questionnaire-v2): compatibility-aware draft merge (spec amendme…
bodhish Aug 4, 2026
63cb2ad
fix(questionnaire-v2): remove the dual-contract shim now that every t…
bodhish Aug 4, 2026
dbdee23
feat(questionnaire-v2): delete the legacy QuestionTypes stack (Batch …
bodhish Aug 4, 2026
651fd3a
feat(questionnaire-v2): medication_request templates, per-row actions…
bodhish Aug 5, 2026
a3156e6
fix(questionnaire-v2): repair nine post-rearchitecture spec failures,…
bodhish Aug 5, 2026
c10e8cc
refactor(questionnaire-v2): quality pass — comment cleanup, dead code…
bodhish Aug 5, 2026
bb803bc
fix(questionnaire-v2): add the locale keys the fill and builder surfa…
bodhish Aug 5, 2026
110a826
fix(questionnaire-v2): stop the fill session losing a clinician's work
bodhish Aug 5, 2026
af9f5ea
fix(questionnaire-v2): correct the structured row core and the per-ty…
bodhish Aug 5, 2026
000cd3a
fix(questionnaire-v2): correct quantity, integer and note handling in…
bodhish Aug 5, 2026
e622f76
fix(questionnaire-v2): correct enable_when authoring and gate the rea…
bodhish Aug 5, 2026
54b84d7
test(questionnaire-v2): close coverage gaps, de-flake the E2E suite, …
bodhish Aug 5, 2026
226aaf8
test(questionnaire-v2): add tests for decimal input handling in chara…
navaspavil Aug 7, 2026
e439152
test(facility-questionnaire): add E2E tests for filling facility-subj…
abhimanyurajeesh Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 65 additions & 1 deletion public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@
"add_product_knowledge": "Add Product Knowledge",
"add_products_to_receive": "Add products to receive",
"add_question": "Add Question",
"add_question_here": "Add question here",
"add_questionnaire": "Add Questionnaire",
"add_range": "Add Range",
"add_remarks": "Add remarks",
Expand Down Expand Up @@ -683,11 +684,15 @@
"already_a_member": "Already a member?",
"already_marked_as_error": "Already marked as error",
"already_selected": "Already selected",
"also_capture": "Also capture",
"also_capture_hint": "Extra fields recorded alongside the answer.",
"alt": "Alt.",
"alternate": "Alternate",
"alternate_phone_number": "Alternate Phone Number",
"alternative_names": "Alternative Names",
"alternatives_coming_soon": "Alternatives coming soon",
"always_shown": "Always shown",
"always_shown_hint": "Add a rule to show this question only in certain cases.",
"ambulance_driver_name": "Name of ambulance driver",
"ambulance_number": "Ambulance No",
"ambulance_phone_number": "Phone number of Ambulance",
Expand All @@ -709,6 +714,7 @@
"answer": "Answer",
"answer_options": "Answer options",
"answer_options_hint": "Define possible answers for this question",
"answer_type": "Answer type",
"antenatal": "Antenatal",
"any_additional_information": "Any Additional Information",
"any_condition_or": "Any condition is true (OR)",
Expand Down Expand Up @@ -1004,6 +1010,7 @@
"beds": "Beds",
"beds_creation_failed": "Failed to create beds",
"before": "before",
"before_you_save": "Before you save",
"begin_clinical_encounter": "Begin a new clinical encounter for <strong>{{patientName}}</strong>. Select the appropriate encounter type, status, and priority to ensure proper documentation and care delivery.",
"behavioral": "Behavioral",
"behaviour": "Behaviour",
Expand Down Expand Up @@ -1037,8 +1044,8 @@
"blood_group": "Blood Group",
"blood_group_is_required": "Blood group is required",
"blood_pressure_error": {
"missing": "Field is required. Either specify both or clear both.",
"exceed": "Value cannot exceed 250 mmHg.",
"missing": "Field is required. Either specify both or clear both.",
"systolic_less_than_diastolic": "Systolic must be greater than diastolic."
},
"blood_pressure_systolic_diastolic": "Blood Pressure (Systolic + Diastolic)",
Expand Down Expand Up @@ -1272,6 +1279,7 @@
"click": "Click",
"click_add_department_team": "Click <strong>Add Department/Team</strong> to create a new department/team.",
"click_add_main_location": "Click <strong>Add Location</strong> to add a main location.",
"click_any_question_to_edit": "Click any question to edit it",
"click_link_department_to_get_started": "Click on a department to get started",
"click_manage_create_users": "Click <strong>See Details</strong><CareIcon /> to create or manage users and departments/teams within the corresponding dept/team.",
"click_manage_create_users_mobile": "Click <CareIcon /> to create or manage users and departments/teams within the corresponding dept/team.",
Expand Down Expand Up @@ -1312,7 +1320,9 @@
"code": "Code",
"code_verified": "Code Verified",
"code_verified_successfully": "Code verified successfully",
"coding": "Coding",
"coding_details": "Coding Details",
"coding_tab_intro": "Link this question to a standard code so answers are comparable across facilities.",
"collapse": "Collapse",
"collapse_all": "Collapse All",
"collapse_sidebar": "Collapse Sidebar",
Expand Down Expand Up @@ -1394,6 +1404,8 @@
"condition_operation__equality": "Equals to",
"condition_operation__has_tag": "Has tag",
"condition_operation__in_range": "In range",
"condition_rule": "“{{question}}” {{operator}} “{{answer}}”",
"condition_rule_answered": "“{{question}}” is answered",
"condition_target_not_answerable": "Visibility conditions can't target group or display questions — pick a question that records an answer",
"condition_target_required": "Every visibility condition needs a target question",
"conditional": "Conditional",
Expand Down Expand Up @@ -2061,6 +2073,7 @@
"draft_error_loading": "Failed to load draft",
"draft_forms": "Draft Forms",
"draft_invoice_created": "Draft Invoice Created",
"draft_load_failed": "Couldn't load this draft. Go back and try again.",
"draft_not_found": "The draft you are trying to continue could not be found.",
"draft_not_recoverable": "Draft cannot be recovered",
"draft_questionnaire_mismatch": "This draft cannot be recovered because the questionnaire has changed or is no longer available.",
Expand All @@ -2078,6 +2091,7 @@
"duplicate_patient_record_birth_unknown": "Please contact your district care coordinator, the shifting facility or the patient themselves if you are not sure about the patient's year of birth.",
"duplicate_patient_record_confirmation": "Admit the patient record to your facility by adding the year of birth",
"duplicate_patient_record_rejection": "I confirm that the patient I want to create is not on the list.",
"duplicate_question": "Duplicate question",
"duration": "Duration",
"duration_input_placeholder": "Type eg. 5 days, 2 weeks",
"duration_period_title": "Fixed dates",
Expand Down Expand Up @@ -2169,6 +2183,7 @@
"edit_value_set": "Edit ValueSet",
"edited_by": "Edited by",
"edited_on": "Edited on",
"editing": "Editing",
"education": "Education",
"effect": "Effect",
"eg_abc": "Eg. ABC",
Expand Down Expand Up @@ -2661,7 +2676,14 @@
"filed_by": "filed by",
"files": "Files",
"fill_color": "Fill Color",
"fill_draft_form_dropped": "Couldn't restore \"{{title}}\" — the questionnaire changed since the draft was saved.",
"fill_draft_form_unavailable": "Couldn't load \"{{title}}\" just now — it's still in your saved draft.",
"fill_draft_includes_added_forms_one": "Includes {{count}} added questionnaire.",
"fill_draft_includes_added_forms_other": "Includes {{count}} added questionnaires.",
"fill_draft_resume_prompt": "You have an unsaved entry from {{time}}. Resume where you left off?",
"fill_draft_structured_skipped": "Structured answers (medications, symptoms, files…) aren't covered by local drafts — please re-check those sections.",
"fill_my_details": "Fill My Details",
"fill_questionnaire": "Fill questionnaire",
"filler_order": "Filler Order",
"filter": "Filter",
"filter_bed_availability_by_ward_and_status": "Filter bed availability by ward and status",
Expand Down Expand Up @@ -2690,6 +2712,7 @@
"final": "Final",
"final_price": "Final price",
"financial_overview": "Financial Overview",
"find_a_question": "Find a question…",
"finished": "Finished",
"first_name": "First Name",
"fixed_amount": "Fixed Amount",
Expand All @@ -2700,7 +2723,11 @@
"footer_image": "Footer Image",
"forget_password": "Forgot password?",
"forget_password_instruction": "Enter your username, and if it exists, we will send you a link to reset your password.",
"form_canvas": "Form canvas",
"form_properties": "Questionnaire Properties",
"form_settings": "Form Settings",
"form_settings_hint": "Applies to the whole form.",
"form_settings_invalid": "Fix the highlighted form settings before saving",
"form_submission_discard_failed": "Failed to discard form submission",
"form_submission_discarded": "Form submission discarded",
"form_submissions": "Form Submissions",
Expand Down Expand Up @@ -2810,7 +2837,11 @@
"help_provisional": "This is a tentative diagnosis - still a candidate that is under consideration.",
"help_refuted": "This condition has been ruled out by subsequent diagnostic and clinical evidence.",
"help_unconfirmed": "There is not sufficient diagnostic and/or clinical evidence to treat this as a confirmed condition.",
"helper_text": "Helper text",
"helper_text_hint": "Shown under the question",
"helper_text_placeholder": "Add guidance for the person filling this in",
"hey_user": "Hey {{user}} 👋",
"hidden_by_conditions": "Hidden by conditions",
"hide": "Hide",
"hide_advanced_fields": "Hide Advanced Fields",
"hide_category_grouping": "Hide Category Grouping",
Expand Down Expand Up @@ -2872,6 +2903,7 @@
"in": "in",
"in_cart": "In Cart",
"in_consultation": "In-Consultation",
"in_plain_words": "In plain words",
"in_progress": "In Progress",
"in_range": "In Range",
"in_service": "In Service",
Expand Down Expand Up @@ -3061,6 +3093,8 @@
"issue_invoice": "Issue Invoice",
"issued": "Issued",
"issuer_type": "Issuer Type",
"issues_to_fix_one": "{{count}} to fix",
"issues_to_fix_other": "{{count}} to fix",
"item": "Item",
"item_condition": "Item Condition?",
"item_location": "Item Location",
Expand Down Expand Up @@ -3261,6 +3295,8 @@
"log_updates": "Log Updates",
"logged_by": "Logged by",
"logged_in_as": "Logged in as",
"logic": "Logic",
"logic_tab_intro": "Choose when this question appears. With no rules it is always shown.",
"login": "Login",
"login_already_registered": "Already registered? Log in to your account.",
"logo": "Logo",
Expand Down Expand Up @@ -3553,6 +3589,8 @@
"move_down": "Move Down",
"move_left": "Move Left",
"move_n_questions": "Move {{count}} questions",
"move_question_down": "Move question down",
"move_question_up": "Move question up",
"move_questions_count": "Move <strong>{{count}}</strong> questions",
"move_right": "Move Right",
"move_this_to_draft": "move this to draft",
Expand All @@ -3578,6 +3616,10 @@
"my_schedules": "My Schedules",
"n_discontinued_medications_hidden": "{{count}} discontinued medication(s) hidden",
"n_discontinued_nutritional_products_hidden": "{{count}} discontinued nutritional product(s) hidden",
"n_questions_one": "{{count}} question",
"n_questions_other": "{{count}} questions",
"n_sections_one": "{{count}} section",
"n_sections_other": "{{count}} sections",
"na": "N/A",
"name": "Name",
"name_is_required": "Name is required",
Expand Down Expand Up @@ -3639,6 +3681,7 @@
"no_allergies_recorded": "No allergies recorded",
"no_allergies_recorded_description": "No Allergies have been recorded",
"no_alternative_names_added": "No alternative names added",
"no_answers_to_submit": "Nothing to submit yet — answer at least one question",
"no_appointments": "No appointments found",
"no_appointments_found_for_today": "No matching appointment found for today. Redirecting to Patient Home...",
"no_apps_found": "No apps found",
Expand Down Expand Up @@ -3877,6 +3920,7 @@
"no_products_in_category": "This category doesn't have any products yet",
"no_purchase_orders_found": "No purchase orders found",
"no_purchase_orders_found_description": "No purchase order found for selected status or priority.",
"no_question_matches_search": "No question matches that search.",
"no_questionnaire_responses": "No Questionnaire Responses",
"no_questionnaires_found": "No questionnaires found",
"no_questions_added_yet": "No questions added yet!",
Expand Down Expand Up @@ -3996,6 +4040,7 @@
"not_specified": "Not Specified",
"not_started": "Not started",
"note": "Note",
"note_added": "Note added",
"notes": "Notes",
"notes__all_discussions": "All Discussions",
"notes__all_discussions_description": "View and manage encounternotes discussion threads",
Expand Down Expand Up @@ -4251,6 +4296,7 @@
"patient_body": "Patient Body",
"patient_care": "Patient Care",
"patient_category": "Patient Category",
"patient_clinical_history": "Patient Clinical History",
"patient_clinical_history_page_title": "{{ name }}'s clinical history",
"patient_consultation__admission": "Date of admission",
"patient_consultation__consultation_notes": "General Instructions",
Expand Down Expand Up @@ -4417,6 +4463,8 @@
"pk_form_definitional_description": "Specify definitional details about the product - dosage, route, etc.",
"pk_form_intended_routes_description": "Add intended routes of administration",
"pk_form_storage_guidelines_description": "Add storage guidelines for this product",
"plain_words_always": "This question is always shown.",
"plain_words_shown_when": "Show this question when {{summary}}.",
"plan": "Plan",
"planned": "Planned",
"planned_location": "Planned Location",
Expand Down Expand Up @@ -4793,8 +4841,10 @@
"reactivate_prescription": "Reactivate Prescription",
"read_more": "Read more",
"read_only": "Read only",
"read_only_toggle_hint": "Displayed, but can't be answered.",
"readmission": "Re-admission",
"ready_for_admission": "Ready For Admission",
"ready_to_save": "Ready to save",
"real_time_bed_status": "Real-time bed status",
"reason": "Reason",
"reason_for_discontinuation": "Reason for discontinuation",
Expand Down Expand Up @@ -4915,6 +4965,7 @@
"remove_medication_confirmation": "Are you sure you want to remove {{medication}}?",
"remove_name": "Remove {{name}}",
"remove_organization": "Remove organization",
"remove_questionnaire": "Remove {{title}}",
"remove_questions_confirmation": "Are you sure do you want to remove the selected questions ?",
"remove_questions_count": "Remove {{count}} questions",
"remove_root_observation_interpretation": "Remove Root Observation Interpretation",
Expand All @@ -4933,6 +4984,7 @@
"rename_file": "Rename File",
"reopen_order": "Reopen Order",
"repeatable": "Repeatable",
"repeatable_toggle_hint": "Lets the person record more than one answer.",
"replace_home_facility": "Replace Home Facility",
"replace_home_facility_confirm": "Are you sure you want to replace",
"replace_home_facility_confirm_as": "as home facility for user",
Expand Down Expand Up @@ -5006,6 +5058,7 @@
"required_method": "Required Method",
"required_processing_storage": "Required Processing & Storage",
"required_quantity": "Required Quantity",
"required_toggle_hint": "The form can't be submitted without it.",
"required_type": "Required Type",
"requirement": "Requirement",
"requirements": "Requirements",
Expand Down Expand Up @@ -5569,6 +5622,7 @@
"select_purchase_order": "Select Purchase Order",
"select_question": "Select question",
"select_questionnaire": "Select Questionnaire",
"select_questionnaire_to_fill": "Select a questionnaire to fill",
"select_reconciliation_type": "Select reconciliation type",
"select_register_patient": "Select/Register Patient",
"select_report": "Select Report",
Expand Down Expand Up @@ -5792,6 +5846,9 @@
"showing_count_templates": "Showing {{count}} template(s)",
"showing_latest_of_versions": "Showing latest {{shown}} of {{total}} versions",
"showing_x_of_y": "Showing <strong>{{x}}</strong> of <strong>{{y}}</strong>",
"shown_when": "Shown when {{summary}}",
"shown_when_n_rules_one": "Shown when {{count}} rule matches",
"shown_when_n_rules_other": "Shown when {{count}} rules match",
"sidebar": "sidebar",
"sidebar_description": "sidebar provides navigation to different sections",
"sign_in": "Sign in",
Expand Down Expand Up @@ -5929,7 +5986,11 @@
"stream_uuid": "Stream UUID",
"string": "String",
"stroke": "Stroke",
"structure": "Structure",
"structured_question_render_failed": "This section couldn't be displayed. Reload the page, or contact support if it keeps happening.",
"structured_question_requires_context": "This section is available when filling the form with {{contexts}}.",
"structured_question_submit_failed": "This section couldn't be prepared for submission. Review or remove the entries, then try again.",
"structured_question_validate_failed": "This section couldn't be checked. Review or remove the entries, then try again.",
"structured_type__allergy_intolerance": "Allergies",
"structured_type__appointment": "Appointment",
"structured_type__charge_item": "Charge Items",
Expand All @@ -5941,6 +6002,9 @@
"structured_type__service_request": "Service Request",
"structured_type__symptom": "Symptoms",
"structured_type__time_of_death": "Time of Death",
"structured_type_plugin_missing": "This question requires a plugin that isn't enabled ({{type}}).",
"structured_type_subject_mismatch": "{{type}} questions can't be used on a {{subject}} questionnaire.",
"structured_type_unknown": "Unknown structured type",
"style_list": "List",
"style_text": "Text",
"styling": "Styling",
Expand Down
26 changes: 23 additions & 3 deletions src/PluginEngine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import React, { Suspense, useEffect, useMemo, useRef } from "react";
import ErrorBoundary from "@/components/Common/ErrorBoundary";
import Loading from "@/components/Common/Loading";
import { PluginErrorBoundary } from "@/components/Common/PluginErrorBoundary";
import { registerPluginStructuredType } from "@/components/QuestionnaireV2/structured/pluginRegistry";
import { addOverride } from "@/lib/override";
import { PlugConfig, PlugConfigMeta } from "@/types/plugConfig";
import plugConfigApi from "@/types/plugConfig/plugConfigApi";
Expand Down Expand Up @@ -116,7 +117,7 @@ export default function PluginEngine({
window.__CARE_PLUGIN_RUNTIME__ = deepFreeze({ meta: pluginMeta });
}, [pluginMeta]);

// Register plugin overrides
// Register plugin overrides and structured question types
const overrideCleanupRef = useRef<(() => void)[]>([]);

useEffect(() => {
Expand All @@ -126,9 +127,9 @@ export default function PluginEngine({

// Register new overrides from all loaded plugins
for (const plugin of pluginsQuery) {
if (plugin.isLoading || !plugin.overrides) continue;
if (plugin.isLoading) continue;

for (const override of plugin.overrides) {
for (const override of plugin.overrides ?? []) {
const cleanup = addOverride(override.component, {
component: override.replacement,
condition: override.condition,
Expand All @@ -138,6 +139,25 @@ export default function PluginEngine({
});
overrideCleanupRef.current.push(cleanup);
}

// Structured question types the plugin contributes. Registration
// throws on a non-namespaced id — one malformed definition is logged
// and skipped, never fatal to the app. The registering plugin's slug
// goes in so the registry can verify the `{plugin_slug}.` half of the
// id actually belongs to it: namespacing is the isolation guarantee,
// and it is only a guarantee if someone checks.
for (const definition of plugin.structuredQuestionTypes ?? []) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error log here uses plugin.plugin — the value the remote manifest declared about itself, explicitly called out two comments above as untrusted and potentially spoofed. The backend-issued, trusted identity for the plugin is plugin.slug. Logging an attacker-controlled string in the error message is at best misleading, at worst used to obfuscate which plugin actually misbehaved.

console.error(`Invalid structured type from plugin ${plugin.slug}`, error);

try {
overrideCleanupRef.current.push(
registerPluginStructuredType(definition, plugin.plugin),
);
} catch (error) {
console.error(
`Invalid structured type from plugin ${plugin.plugin}`,
error,
);
}
}
}

// Cleanup on unmount
Expand Down
2 changes: 2 additions & 0 deletions src/Providers/AuthUserProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { navigate, usePath } from "raviger";
import { useCallback, useEffect, useState } from "react";

import Loading from "@/components/Common/Loading";
import { clearQuestionnaireFillDrafts } from "@/components/QuestionnaireV2/fill/draft/fillDraftCache";

import { AuthUserContext } from "@/hooks/useAuthUser";

Expand Down Expand Up @@ -163,6 +164,7 @@ export default function AuthUserProvider({
localStorage.removeItem(LocalStorageKeys.accessToken);
localStorage.removeItem(LocalStorageKeys.refreshToken);
localStorage.removeItem(LocalStorageKeys.patientTokenKey);
clearQuestionnaireFillDrafts();
setAccessToken(null);
setPatientToken(null);

Expand Down
10 changes: 9 additions & 1 deletion src/Routers/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,16 @@ const PATHS_WITHOUT_SIDEBAR = [
/^\/facility\/[^/]+\/locations\/[^/]+\/external_supply\/purchase_orders\/[^/]+\/edit$/,
/^\/facility\/[^/]+\/locations\/[^/]+\/external_supply\/deliveries\/[^/]+$/,
/^\/facility\/[^/]+\/queues\/[^/]+\/tokens\/[^/]+$/,
// Questionnaire form routes
// Questionnaire fill routes (fullscreen v2 fill experience)
/^\/facility\/[^/]+\/patient\/[^/]+\/encounter\/[^/]+\/questionnaire(\/[^/]+)?$/,
/^\/facility\/[^/]+\/patient\/[^/]+\/questionnaire(\/[^/]+)?$/,
/^\/facility\/[^/]+\/patient\/[^/]+\/consultation$/,
/^\/patient\/[^/]+\/questionnaire(\/[^/]+)?$/,
/^\/facility\/[^/]+\/locations\/[^/]+\/questionnaire(\/[^/]+)?$/,
/^\/facility\/[^/]+\/settings\/devices\/[^/]+\/questionnaire(\/[^/]+)?$/,
// Questionnaire studio (fullscreen builder) routes
/^\/facility\/[^/]+\/settings\/questionnaires\/[^/]+\/edit$/,
/^\/admin\/questionnaires\/[^/]+\/edit$/,
];

export type RouteParams<T extends string> =
Expand Down
Loading
Loading