Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
d542f80
Fix slug for questionnaire
bodhish Jul 29, 2026
a6d1f30
Update valueset
bodhish Jul 31, 2026
557cfcd
feat(questionnaire-v2): auth-context types, scope, and ENG-737 API ro…
bodhish Jul 31, 2026
7417d94
feat(questionnaire-v2): shared UI primitives (badges, chips, segmente…
bodhish Jul 31, 2026
32391c1
fix(questionnaire-v2): add missing question_type__time i18n key
bodhish Jul 31, 2026
a9d590b
feat(questionnaire-v2): generic list page mounted at facility setting…
bodhish Jul 31, 2026
d2eaceb
feat(questionnaire-v2): detail page with properties sidebar and quest…
bodhish Jul 31, 2026
5b2dd22
fix(questionnaire-v2): preserve dirty form edits and scope update bod…
bodhish Jul 31, 2026
72b8bca
feat(questionnaire-v2): scope-aware create page
bodhish Jul 31, 2026
4dfeae9
test(questionnaire-v2): make detail spec own its data to fix parallel…
bodhish Jul 31, 2026
1071a66
feat(questionnaire-v2): renderer scoped store, enable_when evaluation…
bodhish Jul 31, 2026
65a2d7d
fix(questionnaire-v2): faithful enable_when port and stable renderer …
bodhish Jul 31, 2026
1ae4c49
feat(questionnaire-v2): store-connected renderer inputs + type registry
bodhish Jul 31, 2026
f57215d
feat(questionnaire-v2): renderer assembly — group cards, notes, tree …
bodhish Jul 31, 2026
8c8134f
feat(questionnaire-v2): structured question registry with old-compone…
bodhish Jul 31, 2026
bf60d4b
feat(questionnaire-v2): builder tree reducer
bodhish Jul 31, 2026
f541cab
fix(questionnaire-v2): rename DateTimeInput to DateTimeQuestionInput …
bodhish Jul 31, 2026
44cbd39
fix(questionnaire-v2): harden builder reducer against stale selection…
bodhish Jul 31, 2026
4f9e182
feat(questionnaire-v2): question editor card with type picker, option…
bodhish Jul 31, 2026
e29514d
fix(questionnaire-v2): clear stale sub-question selection and offer t…
bodhish Jul 31, 2026
357eeb2
feat(questionnaire-v2): builder shell with live preview via the new r…
bodhish Jul 31, 2026
ef67cd6
fix(questionnaire-v2): return to edit view on validation failure, gua…
bodhish Jul 31, 2026
b44ba45
feat(questionnaire-v2): clone, JSON export, import, preview shortcuts
bodhish Jul 31, 2026
b4f5c62
feat(questionnaire-v2): versions tab with read-only revision viewer
bodhish Jul 31, 2026
735cdb3
chore(questionnaire-v2): final polish — a11y, border merge, versions …
bodhish Jul 31, 2026
1d996d6
fix(questionnaire-v2): store boolean visibility answers as "Yes"/"No"…
bodhish Jul 31, 2026
333136c
fix(questionnaire-v2): restrict JSON export to definition fields; per…
bodhish Jul 31, 2026
9fb5c26
fix(questionnaire-v2): builder correctness — import id regeneration, …
bodhish Jul 31, 2026
bde5543
fix(questionnaire-v2): renderer pagination skips hidden questions; sa…
bodhish Jul 31, 2026
4ed36f1
test(questionnaire-v2): enable_when, choice, validation, import/clone…
bodhish Jul 31, 2026
ec5bc1d
fix(questionnaire-v2): list, create and dialog design fixes
bodhish Jul 31, 2026
07ccb7d
fix(questionnaire-v2): detail page and versions tab design fixes
bodhish Jul 31, 2026
2e95b0f
fix(questionnaire-v2): builder design and UX fixes
bodhish Jul 31, 2026
dd42f9f
fix(questionnaire-v2): renderer/preview design and UX fixes
bodhish Jul 31, 2026
2f937e1
refactor(questionnaire-v2): drop unused submit type family from the A…
bodhish Jul 31, 2026
b7a4450
refactor(questionnaire-v2): single sources for statuses, subject type…
bodhish Jul 31, 2026
fc662fb
refactor(questionnaire-v2): type SUPPORTED_QUESTION_TYPES and validat…
bodhish Jul 31, 2026
9cd756a
refactor(questionnaire-v2): share basic-info schema and card across c…
bodhish Jul 31, 2026
9fc2a74
refactor(questionnaire-v2): query-key factory and shared update mutation
bodhish Jul 31, 2026
be19a64
refactor(questionnaire-v2): untangle the manage/builder boundary via …
bodhish Jul 31, 2026
a6d02ef
refactor(questionnaire-v2): extract save validation, move dialog and …
bodhish Jul 31, 2026
2fed69d
refactor(questionnaire-v2): one shared dashed questions empty state
bodhish Jul 31, 2026
540f73e
refactor(questionnaire-v2): seed renderer store at creation; narrow i…
bodhish Jul 31, 2026
b0e173e
refactor(questionnaire-v2): derive behaviour badge count from the fla…
bodhish Jul 31, 2026
7845915
test(questionnaire-v2): extract shared create-questionnaire helpers
bodhish Jul 31, 2026
c01b13f
test(questionnaire-v2): align spec directories; move facility list te…
bodhish Jul 31, 2026
105789f
refactor(questionnaire-v2): one user-facing noun — questionnaire
bodhish Jul 31, 2026
7d8636d
docs(questionnaire-v2): module README — architecture, frozen contract…
bodhish Jul 31, 2026
959185a
fix(questionnaire-v2): restore shared i18n values changed for casing;…
bodhish Jul 31, 2026
fd74541
chore(questionnaire-v2): list scoping filters now supported by the ba…
bodhish Aug 1, 2026
875f9f4
feat(questionnaire-v2): repeats support in builder and renderer
bodhish Aug 1, 2026
144e9b5
fix(questionnaire-v2): drop answer-option display text column
bodhish Aug 1, 2026
dcc287e
refactor(questionnaire): extract OrgSelector from the organizations s…
bodhish Aug 1, 2026
2b26f69
feat(questionnaire-v2): remove legacy questionnaire management UI
bodhish Aug 1, 2026
8155ca8
fix(questionnaire-v2): mount FormProvider around CodingEditor so addi…
bodhish Aug 1, 2026
c65651f
feat(questionnaire-v2): search-first observation code picker
bodhish Aug 1, 2026
943f7b0
refactor(questionnaire): remove legacy CodingEditor
bodhish Aug 1, 2026
d4e1f34
test(questionnaire-v2): repair legacy enable-when fixture setup for e…
bodhish Aug 1, 2026
a2bd4d7
test(questionnaire-v2): preview rendering of every simple input type …
bodhish Aug 1, 2026
dd69b31
test(questionnaire-v2): live enable_when matrix — numeric, string, OR…
bodhish Aug 1, 2026
3d97773
test(questionnaire-v2): versions-tab depth via e2e-versioned fixture
bodhish Aug 1, 2026
9d8d35b
test(questionnaire-v2): list pagination and keyboard row activation v…
bodhish Aug 1, 2026
1ed3ce8
test(questionnaire-v2): per-mount scoping isolation and subject-type …
bodhish Aug 1, 2026
1ae2829
test(questionnaire-v2): detail-page actions — expand, kebab reorder, …
bodhish Aug 1, 2026
a55033c
test(questionnaire-v2): builder authoring matrix — types, coding acti…
bodhish Aug 1, 2026
4ea57d6
test(questionnaire-v2): group tooling — bulk select/delete, Move dial…
bodhish Aug 1, 2026
e2ca7ed
test(questionnaire-v2): builder navigation — footer paging, tree-nav …
bodhish Aug 1, 2026
7931b1b
test(questionnaire-v2): visibility-condition authoring — numeric oper…
bodhish Aug 1, 2026
4eacfe9
test(questionnaire-v2): import URL mode, malformed payloads, back/can…
bodhish Aug 1, 2026
fb97e9c
test(questionnaire-v2): create-form validation — empty title, slug bo…
bodhish Aug 1, 2026
78039aa
test(questionnaire-v2): organizations field — instance role-org add/r…
bodhish Aug 1, 2026
cafc5fb
fix(questionnaire-v2): stop infinite skeleton when the facility fetch…
bodhish Aug 1, 2026
2dfaa49
test(questionnaire-v2): nurse permission surface — hidden lists, read…
bodhish Aug 1, 2026
0dbe33b
test(questionnaire-v2): 500-on-save error handling and 375px mobile s…
bodhish Aug 1, 2026
b7c1dfa
test(questionnaire-v2): display question renders as plain text withou…
bodhish Aug 1, 2026
5b13433
feat(questionnaire-v2): per-answer unit selection for quantity questions
bodhish Aug 1, 2026
175f19d
test(questionnaire-v2): quantity unit coverage
bodhish Aug 1, 2026
3b99ce6
feat(questionnaire-v2): inline unit chips and label-suffix units in t…
bodhish Aug 1, 2026
dc6e54b
fix(questionnaire-v2): quantity authoring is valueset-only; unit rows…
bodhish Aug 1, 2026
ba0c368
test(questionnaire-v2): unit-semantics coverage per type; quantity sp…
bodhish Aug 1, 2026
2d1fb14
test(questionnaire-v2): stabilize suite on a clean fixture baseline
bodhish Aug 1, 2026
cd6407c
feat(questionnaire-v2): full-page revision viewer
bodhish Aug 1, 2026
87b1201
test(questionnaire-v2): versions specs follow the full-page revision …
bodhish Aug 1, 2026
fe64607
fix(questionnaire-v2): visibility conditions can no longer target gro…
bodhish Aug 1, 2026
a6363c9
perf(questionnaire-v2): memoize the identity-form Zod schemas on [t]
bodhish Aug 1, 2026
77d8127
docs(questionnaire-v2): reunite downloadQuestionnaireJson with its JSDoc
bodhish Aug 1, 2026
bfc6838
feat(questionnaire-v2): typeform-style question editor header
bodhish Aug 1, 2026
4e4b2c2
feat(questionnaire-v2): question-type icons in the tree navigation
bodhish Aug 1, 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
200 changes: 116 additions & 84 deletions public/locale/en.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/Routers/routes/ConsultationRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ const consultationRoutes: AppRoutes = {
/>
),

"/facility/:facilityId/patient/:patientId/encounter/:encounterId/questionnaire/:slug":
({ facilityId, encounterId, slug, patientId }) => (
"/facility/:facilityId/patient/:patientId/encounter/:encounterId/questionnaire/:questionnaireId":
({ facilityId, encounterId, questionnaireId, patientId }) => (
<EncounterQuestionnaire
facilityId={facilityId}
encounterId={encounterId}
questionnaireSlug={slug}
questionnaireId={questionnaireId}
Comment on lines +106 to +111

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Update remaining slug-based questionnaire routes

When the questionnaire API performs the ID-based lookup introduced by this commit, existing callers that still put slugs in this route will pass the slug through as questionnaireId and receive the not-found state. In particular, the enable-when Playwright suites navigate to .../questionnaire/${QUESTIONNAIRE_SLUG}, while tests/setup/questionnaire.setup.ts also reads and updates the fixture through slug-based detail URLs. Update those flows to retain the created questionnaire ID, or provide an explicit slug-to-ID compatibility path; otherwise these existing questionnaire scenarios break under the new lookup semantics.

Useful? React with 👍 / 👎.

patientId={patientId}
subjectType="encounter"
/>
Expand Down Expand Up @@ -138,7 +138,7 @@ const consultationRoutes: AppRoutes = {
<EncounterQuestionnaire
facilityId={facilityId}
patientId={patientId}
questionnaireSlug="encounter"
questionnaireId="encounter"
subjectType="encounter"
/>
),
Expand Down
41 changes: 34 additions & 7 deletions src/Routers/routes/adminRoutes.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { navigate } from "raviger";

import QuestionnaireEditor from "@/components/Questionnaire/QuestionnaireEditor";
import { QuestionnaireList } from "@/components/Questionnaire/QuestionnaireList";
import { QuestionnaireBuilderPage } from "@/components/QuestionnaireV2/builder/QuestionnaireBuilderPage";
import { QuestionnaireCreatePage } from "@/components/QuestionnaireV2/manage/QuestionnaireCreatePage";
import { QuestionnaireDetailPage } from "@/components/QuestionnaireV2/manage/QuestionnaireDetailPage";
import { QuestionnaireListPage } from "@/components/QuestionnaireV2/manage/QuestionnaireListPage";
import { QuestionnaireRevisionPage } from "@/components/QuestionnaireV2/manage/QuestionnaireRevisionPage";
import { ValueSetEditor } from "@/components/ValueSet/ValueSetEditor";
import { ValueSetList } from "@/components/ValueSet/ValueSetList";

Expand All @@ -16,17 +19,41 @@ import { PlugConfigList } from "@/pages/Apps/PlugConfigList";
import PatientIdentifierConfigForm from "@/pages/settings/patientIdentifierConfig/PatientIdentifierConfigForm";
import PatientIdentifierConfigList from "@/pages/settings/patientIdentifierConfig/PatientIdentifierConfigList";

const INSTANCE_SCOPE = {
authContext: "instance",
basePath: "/admin/questionnaires",
} as const;

const AdminRoutes: AppRoutes = {
"/admin/questionnaire": () => <QuestionnaireList />,
"/admin/questionnaire/create": () => <QuestionnaireEditor />,
"/admin/questionnaire/:slug/edit": ({ slug }) => (
<QuestionnaireEditor slug={slug} />
"/admin/questionnaires": () => (
<QuestionnaireListPage scope={INSTANCE_SCOPE} />
),
// Must be registered before "/admin/questionnaires/:id" — raviger matches

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Routing correctness depending on JavaScript object property insertion order is fragile. The comment explains why, which is good, but the design is the problem. This is a footgun waiting to go off the next time someone does { ...AdminRoutes, ...otherRoutes } or a linter auto-sorts the keys. If raviger supports explicit route ordering or regex-based specificity, use that instead.

// routes in object order, and "new" would otherwise be captured as an :id.
"/admin/questionnaires/new": () => (
<QuestionnaireCreatePage scope={INSTANCE_SCOPE} />
),
// Must be registered before "/admin/questionnaires/:id" for the same
// reason — otherwise "edit" would be captured as an :id.
"/admin/questionnaires/:id/edit": ({ id }) => (
<QuestionnaireBuilderPage scope={INSTANCE_SCOPE} id={id} />
),
// Registered before "/admin/questionnaires/:id" like the routes above.
"/admin/questionnaires/:id/versions/:revisionId": ({ id, revisionId }) => (
<QuestionnaireRevisionPage
scope={INSTANCE_SCOPE}
id={id}
revisionId={revisionId}
/>
),
"/admin/questionnaires/:id": ({ id }) => (
<QuestionnaireDetailPage scope={INSTANCE_SCOPE} id={id} />
),
"/admin/valuesets": () => <ValueSetList />,
"/admin/valuesets/create": () => (
<ValueSetEditor onSuccess={() => navigate(`/admin/valuesets`)} />
),
"/admin/valuesets/:slug/edit": ({ slug }) => <ValueSetEditor slug={slug} />,
"/admin/valuesets/:id/edit": ({ id }) => <ValueSetEditor id={id} />,
"/admin/patient_identifier_config": () => <PatientIdentifierConfigList />,
"/admin/patient_identifier_config/new": () => <PatientIdentifierConfigForm />,
"/admin/patient_identifier_config/:id": ({ id }) => (
Expand Down
34 changes: 18 additions & 16 deletions src/components/Encounter/EncounterCommandDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
RotateCcw,
Users,
} from "lucide-react";
import { useCallback, useEffect, useMemo, useState } from "react";
import { useCallback, useMemo, useState } from "react";

import { PLUGIN_Component } from "@/PluginEngine";
import query from "@/Utils/request/query";
Expand Down Expand Up @@ -85,11 +85,15 @@ export function EncounterCommandDialog({
}),
});

useEffect(() => {
if (!open) {
setSearch("");
}
}, [open]);
const handleOpenChange = useCallback(
(nextOpen: boolean) => {
if (!nextOpen) {
setSearch("");
}
onOpenChange(nextOpen);
},
[onOpenChange],
);

const getShortcutDisplay = useShortcutDisplay();

Expand Down Expand Up @@ -182,8 +186,8 @@ export function EncounterCommandDialog({

// Handle dynamic questionnaire actions
if (actionId.startsWith("questionnaire-")) {
const slug = actionId.replace("questionnaire-", "");
navigate(buildEncounterUrl(`/questionnaire/${slug}`));
const questionnaireId = actionId.replace("questionnaire-", "");
navigate(buildEncounterUrl(`/questionnaire/${questionnaireId}`));
}
},
[navigate, buildEncounterUrl, actions, encounter],
Expand Down Expand Up @@ -407,10 +411,10 @@ export function EncounterCommandDialog({
group: t("questionnaire"),
items: [
...(questionnaires?.results || []).map((option) => ({
id: `questionnaire-${option.slug}`,
id: `questionnaire-${option.id}`,
label: option.title,
icon: <NotebookPen />,
shortcut: getShortcutDisplay(`questionnaire-${option.slug}`),
shortcut: getShortcutDisplay(`questionnaire-${option.id}`),
})),
],
});
Expand All @@ -420,12 +424,10 @@ export function EncounterCommandDialog({
}, [
t,
questionnaires,
search,
getShortcutDisplay,
canWriteSelectedEncounter,
canRestartSelectedEncounter,
encounter.encounter_class,
encounter.status,
encounter,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

You replaced encounter.encounter_class, encounter.status with the whole encounter object. If the parent doesn't memoize the encounter reference (and it probably doesn't), this memo recomputes on every render. It's strictly worse than the specific-field deps you removed. Use the fields the computation actually touches.

]);

const findRecentActions = useCallback(
Expand Down Expand Up @@ -459,9 +461,9 @@ export function EncounterCommandDialog({
(actionId: string) => {
addRecentAction(actionId);
handleAction(actionId);
onOpenChange(false);
handleOpenChange(false);
},
[handleAction, onOpenChange, addRecentAction],
[handleAction, handleOpenChange, addRecentAction],
);

const careApps = useCareApps();
Expand All @@ -471,7 +473,7 @@ export function EncounterCommandDialog({
{trigger}
<CommandDialog
open={open}
onOpenChange={onOpenChange}
onOpenChange={handleOpenChange}
className="md:max-w-2xl"
>
<div className="border-b border-gray-100 shadow-xs">
Expand Down
6 changes: 3 additions & 3 deletions src/components/Patient/EncounterQuestionnaire.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ interface Props {
facilityId?: string;
patientId: string;
encounterId?: string;
questionnaireSlug?: string;
questionnaireId?: string;
subjectType?: string;
}

export default function EncounterQuestionnaire({
facilityId,
patientId,
encounterId,
questionnaireSlug,
questionnaireId,
subjectType,
}: Props) {
const { t } = useTranslation();
Expand Down Expand Up @@ -64,7 +64,7 @@ export default function EncounterQuestionnaire({
patientId={patientId}
subjectType={subjectType}
encounterId={encounterId}
questionnaireSlug={questionnaireSlug}
questionnaireId={questionnaireId}
onSubmit={() => {
if (encounterId && facilityId) {
navigate(
Expand Down
Loading
Loading