Skip to content

[ENG-421] feat: enhance questionnaire with repeatable groups#16399

Open
abhimanyurajeesh wants to merge 13 commits into
developfrom
ENG-421
Open

[ENG-421] feat: enhance questionnaire with repeatable groups#16399
abhimanyurajeesh wants to merge 13 commits into
developfrom
ENG-421

Conversation

@abhimanyurajeesh

@abhimanyurajeesh abhimanyurajeesh commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Proposed Changes

This PR adds support for repeatable question groups and fixes the repeatable question validation issue.

image image

Fixes ENG-421

Tagging: @ohcnetwork/care-fe-code-reviewers

Merge Checklist

  • Add specs that demonstrate the bug or test the new feature.
  • Update product documentation.
  • Ensure that UI text is placed in I18n files.
  • Prepare a screenshot or demo video for the changelog entry and attach it to the issue.
  • Request peer reviews.
  • Complete QA on mobile devices.
  • Complete QA on desktop devices.
  • Add or update Playwright tests for related changes

Summary by CodeRabbit

  • New Features
    • Added support for repeatable question groups, including adding/removing instances and rendering nested answers per instance (including notes, units, and coding).
    • Updated questionnaire response initialization and submission to properly capture and transmit repeatable-group data, including nested results.
  • Bug Fixes
    • Improved on-screen and print rendering so group content only appears when it contains filled answers, including repeatable nested results.
  • Localization
    • Added a new English validation message that blocks structured questions inside repeatable groups.

Copilot AI review requested due to automatic review settings June 1, 2026 06:28
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR adds support for repeatable questionnaire groups, enabling users to create and respond to multiple instances of a group. The implementation extends QuestionnaireResponse with a sub_results field, adds validation to prevent structured questions in repeatable groups, implements form initialization and serialization for nested responses, provides an editor component with add/remove instance controls, and updates print and list response views to display repeatable instances.

Changes

Repeatable Questionnaire Groups

Layer / File(s) Summary
Type system & response contracts
src/types/questionnaire/form.ts, src/components/Questionnaire/QuestionRenderer.tsx, src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx
QuestionnaireResponse adds optional sub_results?: QuestionnaireResponse[][] field. QuestionRenderer callback extends to accept note?: string and subResults?: QuestionnaireResponse[][]. QuestionGroupProps callback extends with optional subResults parameter for nested updates.
Validation rules for repeatable groups
src/components/Questionnaire/QuestionnaireEditor.tsx, public/locale/en.json
'group' removed from HIDE_REPEATABLE_QUESTION_TYPES to allow repeatable groups. New hasStructuredQuestion recursive helper detects structured questions in group hierarchies. handleSave validation rejects repeatable groups containing structured questions and sets manual form error with localized message.
Form initialization & response serialization
src/components/Questionnaire/QuestionnaireForm.tsx
New isResponseFilled helper checks if responses have actual content including nested sub_results. New serializeRepeatableGroupResponse helper converts nested responses to sub_results payloads for API submission. initializeResponses creates sub_results structure for repeatable groups instead of flattening nested questions. Submission normalizes values via serializeValue (date, dateTime, coding, units) and serializes repeatable groups via serializeRepeatableGroupResponse. Callback wiring and state writes extended to handle optional subResults parameter.
Repeatable group editor component
src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx
RepeatableGroupRenderer component manages multiple group instances with add/remove UI controls (with i18n labels). initializeGroupResponses helper recursively initializes sub_results structure for each new instance. Per-instance response updates handled immutably, with nested sub_results changes passed through updateQuestionnaireResponseCB. Imports UI controls (Button, icons Plus, XIcon) for instance management and updated response-clearing logic for repeatable groups.
Repeatable groups in print view
src/components/Facility/ConsultationDetails/PrintAllQuestionnaireResponses.tsx
New hasRenderableContent helper determines if group has actual response content or repeatable sub-results to render. QuestionGroup now accepts responses list and renders repeatable group instances by iterating sub_results with instance numbering and recursively rendering nested groups/questions per instance, skipping structured questions.
Repeatable groups in list view
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
QuestionGroup adds early-return rendering for repeatable groups displaying sub_results instances in bordered table format, filtering non-structured/non-group sub-questions and skipping empty responses. hasResponses predicate updated to recognize sub_results.length > 0 as evidence that repeatable sub-groups have responses.

Possibly related PRs

  • ohcnetwork/care_fe#16383: Both PRs modify PrintAllQuestionnaireResponses.tsx rendering guards to ensure nested/sub-question responses are actually rendered in print preview.
  • ohcnetwork/care_fe#16384: Both PRs modify PrintAllQuestionnaireResponses.tsx QuestionGroup rendering logic for repeatable group content and visibility.

Suggested reviewers

  • nihal467
  • bodhish
  • Jacobjeevan
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description includes proposed changes, issue reference, and a mostly complete merge checklist, but lacks detailed bullet points explaining the specific implementation. Add more detailed bullet points under 'Proposed Changes' describing the key features (repeatable group rendering, validation, sub_results handling, etc.) and clarify which checklist items remain incomplete.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main feature being added: enhancing questionnaires with repeatable groups, which aligns with the primary changes across multiple components.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ENG-421

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx Fixed
Comment thread src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx Fixed
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 1, 2026

Copy link
Copy Markdown

Deploying care-preview with  Cloudflare Pages  Cloudflare Pages

Latest commit: 83033bb
Status: ✅  Deploy successful!
Preview URL: https://008f2432.care-preview-a7w.pages.dev
Branch Preview URL: https://eng-421.care-preview-a7w.pages.dev

View logs

This comment was marked as resolved.

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

🎭 Playwright Test Results

Status: ❌ Failed
Test Shards: 3

Metric Count
Total Tests 325
✅ Passed 324
❌ Failed 1
⏭️ Skipped 0

📊 Detailed results are available in the playwright-final-report artifact.

Run: #9636

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/Questionnaire/QuestionnaireForm.tsx`:
- Around line 346-369: serializeRepeatableGroupResponse currently serializes
value types differently than the main submission path causing inconsistent
date/dateTime/unit formatting; extract the shared logic into a single
serializeValue function and use it inside serializeRepeatableGroupResponse and
in the main submission mapping so both paths produce identical output.
Specifically, implement serializeValue(value: QuestionnaireResponseValue) that
applies dateQueryString for 'date', toISOString for 'dateTime', and returns {
value, unit, coding } for unit-bearing values (matching the main submission
logic), then replace the inline value mapping in
serializeRepeatableGroupResponse (and the mapping in the main submission code)
to call serializeValue for each value to ensure consistent formatting.

In `@src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx`:
- Around line 360-362: The component currently uses instanceIndex as the React
key for subResults which causes incorrect re-renders when items are removed;
modify instance creation logic (e.g., initializeGroupResponses and wherever new
instances are pushed/added) to attach a stable unique identifier field like
_instanceId (use a UUID or monotonic counter) to each instance object, then
change the map in QuestionGroup (the subResults.map rendering) to use
instance._instanceId as the key instead of instanceIndex so keys remain stable
across removals and reorders.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a402be7c-a43a-4a65-8f0b-320663b70801

📥 Commits

Reviewing files that changed from the base of the PR and between 22fd5db and ea9dea2.

📒 Files selected for processing (8)
  • public/locale/en.json
  • src/components/Facility/ConsultationDetails/PrintAllQuestionnaireResponses.tsx
  • src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx
  • src/components/Questionnaire/QuestionRenderer.tsx
  • src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx
  • src/components/Questionnaire/QuestionnaireEditor.tsx
  • src/components/Questionnaire/QuestionnaireForm.tsx
  • src/types/questionnaire/form.ts

Comment thread src/components/Questionnaire/QuestionnaireForm.tsx
Comment thread src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx Outdated
Copilot AI review requested due to automatic review settings June 1, 2026 07:39
@abhimanyurajeesh abhimanyurajeesh marked this pull request as ready for review June 1, 2026 07:40
@abhimanyurajeesh abhimanyurajeesh requested review from a team June 1, 2026 07:40
@abhimanyurajeesh abhimanyurajeesh requested a review from a team as a code owner June 1, 2026 07:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.

Comment thread src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx Outdated
Comment thread src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx Outdated
Comment on lines +134 to +136
const hasAnyValue = subResp.values.some(
(v) => v.value || v.coding,
);
Comment on lines +153 to +156
{vidx > 0 && ", "}
{val.value &&
formatValue(val.value, subQuestion.type)}
{val.unit && (
Comment on lines +289 to +293
if (question.type === "structured") return false;
const resp = responses.find((r) => r.question_id === question.id);
if (!resp) return false;
return resp.values.some((v) => v.value || v.coding);
});
Comment on lines +167 to +185
if (question.repeats) {
return (
<RepeatableGroupRenderer
question={question}
encounterId={encounterId}
questionnaireResponses={questionnaireResponses}
updateQuestionnaireResponseCB={updateQuestionnaireResponseCB}
errors={errors}
clearError={clearError}
disabled={disabled}
activeGroupId={activeGroupId}
facilityId={facilityId}
patientId={patientId}
isSubQuestion={isSubQuestion}
questionnaireId={questionnaireId}
questionnaireSlug={questionnaireSlug}
/>
);
}
@greptile-apps

greptile-apps Bot commented Jun 1, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds support for repeatable question groups in the questionnaire feature — allowing users to add/remove multiple instances of a group, with corresponding updates to form initialization, validation, serialization, and both the response list and print views.

  • Form layer: RepeatableGroupRenderer and RepeatableGroupInstance components handle per-instance rendering with stable keys (via instanceKeysRef), stale-closure fixes (via subResultsRef), and correct enable_when context merging. Serialization now includes body_site and method for sub-responses, fixing the previously noted data loss.
  • Display/print layer: New repeatable group rendering is added to QuestionnaireResponsesList and PrintAllQuestionnaireResponses, but both use a falsy check (v.value || v.coding) to decide whether a value is filled — causing numeric 0 to be treated as empty and silently hidden.
  • Editor: Structured questions are now blocked inside repeatable groups with a validation error and toast notifications.

Confidence Score: 3/5

Safe to merge for core form functionality, but the display and print views will silently omit any explicitly-entered zero values from repeatable group responses.

The form, serialization, and key-stability logic are well-implemented. The gap is in the display layer: both the response list and the print view hide rows/groups whose only filled values are numeric 0 — a legitimate clinical value (e.g., pain scale, episode count). This affects every repeatable group response containing a zero-valued answer, which in a medical questionnaire context can be significant.

PrintAllQuestionnaireResponses.tsx (hasRenderableContent) and QuestionnaireResponsesList.tsx (hasAnyValue in renderQuestionsInOrder) both need the falsy value check replaced.

Important Files Changed

Filename Overview
src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx Adds RepeatableGroupRenderer and RepeatableGroupInstance components with stable instance keys via instanceKeysRef, stale-closure fix via subResultsRef, and clearDependentQuestionResponse now passes [] sub_results when disabling repeatable groups.
src/components/Questionnaire/QuestionnaireForm.tsx Adds repeatable group validation, serialization with serializeRepeatableGroupResponse (now including body_site/method), and refactors initializeResponses to initializeGroupResponses.
src/components/Facility/ConsultationDetails/PrintAllQuestionnaireResponses.tsx Adds repeatable group rendering for print view and hasRenderableContent guard. The guard uses v.value
src/components/Facility/ConsultationDetails/QuestionnaireResponsesList.tsx Adds repeatable group rendering with renderQuestionsInOrder helper; hasAnyValue check uses falsy check that hides zero-value rows, inconsistent with non-repeatable rendering path.
src/components/Questionnaire/utils.ts Extracts initializeGroupResponses (now handles initial_selected defaults and repeatable sub-results) and hasStructuredQuestion as shared utilities. Clean implementation.
src/types/questionnaire/form.ts Adds optional sub_results field to QuestionnaireResponse for recursive repeatable group instances.
src/components/Questionnaire/QuestionnaireEditor.tsx Removes group from HIDE_REPEATABLE_QUESTION_TYPES and adds validation to prevent structured questions inside repeatable groups. Clean implementation.
src/components/Questionnaire/QuestionRenderer.tsx Updates onResponseChange signature to pass through optional subResults parameter for repeatable groups.
src/components/Common/PrintTable.tsx CSS class rename from wrap-break-words to wrap-break-word — appears to be aligning with a project-wide custom utility name.

Reviews (9): Last reviewed commit: "fix a validation issue" | Re-trigger Greptile

Comment thread src/components/Questionnaire/QuestionnaireForm.tsx
Comment thread src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx Outdated
Comment thread src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx Outdated
Comment thread src/components/Facility/ConsultationDetails/PrintAllQuestionnaireResponses.tsx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx (2)

366-377: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Internationalize the instance header and aria-label.

The instance number display and remove button aria-label contain literal English strings, violating the i18n requirement for healthcare interfaces.

🌐 Proposed i18n fix

Add translation keys to public/locale/en.json:

{
  "instance_number": "Instance #{{number}}",
  "remove_instance": "Remove instance {{number}}"
}

Then update the component:

  <span className="text-sm font-medium text-gray-500">
-   #{instanceIndex + 1}
+   {t("instance_number", { number: instanceIndex + 1 })}
  </span>
  ...
  <Button
    ...
-   aria-label={`Remove instance ${instanceIndex + 1}`}
+   aria-label={t("remove_instance", { number: instanceIndex + 1 })}
  >

As per coding guidelines: "All literal strings must use i18next for multi-language support in healthcare interfaces".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx` around lines
366 - 377, The instance header and remove button aria-label use hard-coded
English; replace them with i18n lookups using the i18next hook (t) and the
instanceIndex value: update the span that renders "#{instanceIndex + 1}" to use
t('instance_number', { number: instanceIndex + 1 }) and change the Button
aria-label to t('remove_instance', { number: instanceIndex + 1 }); ensure the
component imports and uses the useTranslation hook and add the suggested keys
("instance_number" and "remove_instance") to the locale files so translations
resolve at runtime, keeping the onClick handler handleRemoveInstance unchanged.

191-211: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Apply consistent simplification for styling metadata access.

The redundant && pattern was correctly simplified in RepeatableGroupRenderer (lines 346, 387), but the same pattern remains in the original QuestionGroup component.

♻️ Proposed fix for consistency
     <div
       className={cn(
         "sm:rounded-lg bg-gray-100 md:bg-transparent",
         isActive && "ring-2 ring-primary",
-        question.styling_metadata?.classes && question.styling_metadata.classes,
+        question.styling_metadata?.classes,
       )}
     >
       ...
       <div
         className={cn(
           "gap-1",
-          question.styling_metadata?.containerClasses &&
-            question.styling_metadata.containerClasses,
+          question.styling_metadata?.containerClasses,
         )}
       >
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx` around lines
191 - 211, The JSX uses the redundant "&&" pattern when accessing styling
metadata in QuestionGroup; replace occurrences like
question.styling_metadata?.classes && question.styling_metadata.classes and
question.styling_metadata?.containerClasses &&
question.styling_metadata.containerClasses with the simplified optional chaining
form (e.g., question.styling_metadata?.classes and
question.styling_metadata?.containerClasses) so the className/cn calls mirror
the simplified approach already used in RepeatableGroupRenderer; update all
usages inside the QuestionGroup component (including the className for the
wrapper and the container className) to this simplified form.
♻️ Duplicate comments (1)
src/components/Questionnaire/QuestionnaireForm.tsx (1)

354-356: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Potential runtime error: dateTime value assumed to be Date object.

The date case (lines 347-352) properly converts value.value to a Date object before calling methods on it, but the dateTime case directly calls .toISOString() without conversion. If value.value is a string (common for form inputs), this will throw TypeError.

🐛 Proposed fix: handle string values consistently with date case
 if (value.type === "dateTime" && value.value) {
-  return { ...value, value: value.value.toISOString() };
+  const date = value.value instanceof Date ? value.value : new Date(value.value);
+  if (isNaN(date.getTime())) {
+    return { ...value, value: "" };
+  }
+  return { ...value, value: date.toISOString() };
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Questionnaire/QuestionnaireForm.tsx` around lines 354 - 356,
The dateTime branch in QuestionnaireForm (the block checking value.type ===
"dateTime") assumes value.value is a Date and calls .toISOString(), which can
throw if it's a string; update that branch to mirror the date handling: coerce
value.value into a Date (e.g., new Date(value.value)) only when value.value
exists and is not already a Date, guard against invalid dates, then return the
object with value set to the ISO string; use the same checks/logic pattern used
in the date case to ensure consistency and avoid runtime TypeError.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@public/locale/en.json`:
- Line 4895: Update the value for the localization key
"repeatable_group_cannot_contain_structured" to the suggested, clearer wording;
locate the string named repeatable_group_cannot_contain_structured and replace
its current value with: "A repeatable group cannot contain structured questions.
Disable repeatable mode or remove structured questions."

---

Outside diff comments:
In `@src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx`:
- Around line 366-377: The instance header and remove button aria-label use
hard-coded English; replace them with i18n lookups using the i18next hook (t)
and the instanceIndex value: update the span that renders "#{instanceIndex + 1}"
to use t('instance_number', { number: instanceIndex + 1 }) and change the Button
aria-label to t('remove_instance', { number: instanceIndex + 1 }); ensure the
component imports and uses the useTranslation hook and add the suggested keys
("instance_number" and "remove_instance") to the locale files so translations
resolve at runtime, keeping the onClick handler handleRemoveInstance unchanged.
- Around line 191-211: The JSX uses the redundant "&&" pattern when accessing
styling metadata in QuestionGroup; replace occurrences like
question.styling_metadata?.classes && question.styling_metadata.classes and
question.styling_metadata?.containerClasses &&
question.styling_metadata.containerClasses with the simplified optional chaining
form (e.g., question.styling_metadata?.classes and
question.styling_metadata?.containerClasses) so the className/cn calls mirror
the simplified approach already used in RepeatableGroupRenderer; update all
usages inside the QuestionGroup component (including the className for the
wrapper and the container className) to this simplified form.

---

Duplicate comments:
In `@src/components/Questionnaire/QuestionnaireForm.tsx`:
- Around line 354-356: The dateTime branch in QuestionnaireForm (the block
checking value.type === "dateTime") assumes value.value is a Date and calls
.toISOString(), which can throw if it's a string; update that branch to mirror
the date handling: coerce value.value into a Date (e.g., new Date(value.value))
only when value.value exists and is not already a Date, guard against invalid
dates, then return the object with value set to the ISO string; use the same
checks/logic pattern used in the date case to ensure consistency and avoid
runtime TypeError.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4f6a595e-24ea-4909-a1e3-0a0453e70c72

📥 Commits

Reviewing files that changed from the base of the PR and between ea9dea2 and 8b21ef2.

📒 Files selected for processing (3)
  • public/locale/en.json
  • src/components/Questionnaire/QuestionTypes/QuestionGroup.tsx
  • src/components/Questionnaire/QuestionnaireForm.tsx

Comment thread public/locale/en.json Outdated
@abhimanyurajeesh abhimanyurajeesh changed the title ENG-421 feat: enhance questionnaire with repeatable groups [ENG-421] feat: enhance questionnaire with repeatable groups Jun 4, 2026
Copilot AI review requested due to automatic review settings June 16, 2026 10:22

This comment was marked as resolved.

Comment on lines +801 to +804
q.questions!.forEach((subQ) => {
if (subQ.type === "group" && subQ.questions) {
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Required questions in nested sub-groups silently skipped

When a repeatable group contains a nested non-repeatable group, validation bails out early with return instead of recursing into the nested group's children. Since the editor allows groups inside repeatable groups (only boolean, display, and structured are excluded via HIDE_REPEATABLE_QUESTION_TYPES), a user can submit without filling required questions that live inside a nested group within a repeatable group. The non-repeatable path correctly calls q.questions.forEach(validateQuestion) recursively — the repeatable path needs a similar recursive descent for subQ.type === "group" children across each instance.

@yash-learner yash-learner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Image

I think this is out of scope of this PR, maybe a separate ticket. we need to fix the font of the text for group inside in a group because it looks out when there are sub questions of non group type inside a group along with it

like same on questionnairereponselist on encounter home

Comment thread src/types/questionnaire/form.ts
<QuestionDescription question={question} />
</div>
)}
<div className="space-y-4 p-2">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Image

I feel there is not lot of unnecessary vertical space and why can't we have the number and question name on same line ?

#2 New Sub Question

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

thought so but there is a Close button to

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

image

this looks good on phone

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@yash-learner I hope this is better
image

Comment thread src/components/Questionnaire/utils.ts
Comment thread src/components/Questionnaire/QuestionnaireForm.tsx
Comment on lines +332 to +368
function isValueFilled(v: ResponseValue): boolean {
if (v.coding) return true;
if (v.value == null || v.value === "") return false;
if (Array.isArray(v.value) && v.value.length === 0) return false;
return true;
}

const processQuestion = (q: Question) => {
if (q.type === "group" && q.questions) {
q.questions.forEach(processQuestion);
} else {
let defaultValues: ResponseValue[] = [];
if (q.answer_option && q.answer_option.length > 0) {
const defaultOptions: AnswerOption[] = q.answer_option.filter(
(o) => o.initial_selected === true,
);
if (defaultOptions.length > 0) {
defaultValues = defaultOptions.map((opt) => ({
type: "string",
value: opt.value,
coding: opt.code ?? undefined,
}));
}
}
responses.push({
question_id: q.id,
link_id: q.link_id,
values: defaultValues,
structured_type: q.structured_type ?? null,
});
function isResponseFilled(r: QuestionnaireResponse): boolean {
if (r.structured_type) return false;
if (r.values.length > 0 && r.values.some(isValueFilled)) return true;
if (r.sub_results?.some((inst) => inst.some(isResponseFilled))) return true;
return false;
}

function serializeValue(value: ResponseValue): Record<string, unknown> {
if (value.type === "date" && value.value) {
const date = new Date(value.value);
if (isNaN(date.getTime())) {
return { ...value, value: "" };
}
return { ...value, value: dateQueryString(date) };
}
if (value.type === "dateTime" && value.value) {
return { ...value, value: value.value.toISOString() };
}
if (value.unit) {
return {
value: value.value?.toString(),
unit: value.unit,
coding: value.coding,
};
}
if (value.coding) return { coding: value.coding };
return { value: String(value.value) };
}

function serializeRepeatableGroupResponse(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I feel all these methods should inside the utils src/components/Questionnaire/utils.ts

what do you think ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

could be but i dont see a real use case

Comment on lines +214 to +221
function hasStructuredQuestion(questions?: Question[]): boolean {
if (!questions) return false;
return questions.some(
(q) =>
q.type === "structured" ||
(q.type === "group" && hasStructuredQuestion(q.questions)),
);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is the duplicate of the

const findStructuredQuestions = (questions: Question[]): boolean => {

so can we move this to util and use at both places ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yap these two are similar, can be moved to utility

Copilot AI review requested due to automatic review settings June 19, 2026 11:06

This comment was marked as resolved.

Comment thread src/components/Questionnaire/QuestionnaireForm.tsx Outdated
@greptile-apps

greptile-apps Bot commented Jun 19, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Try greploops.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs review needs testing Type Changes Contains changes in typescript types

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants