Skip to content

Issue/1815#1929

Open
MaryamMehd wants to merge 7 commits into
mainfrom
issue/1815
Open

Issue/1815#1929
MaryamMehd wants to merge 7 commits into
mainfrom
issue/1815

Conversation

@MaryamMehd

Copy link
Copy Markdown
Collaborator

No description provided.

MARYAMMEHDIZ and others added 7 commits May 1, 2026 16:52
… informative messages

Previously, when the pre-population service could not retrieve data from the FHIR server
(e.g. resource type not supported, permission denied, or no matching data), the failure was
either completely silent (playground) or only shown as a vague "view console" message gated
behind developer mode. Clinicians had no way to know why fields were left blank.

Changes:
- Add formatPopulateIssuesForUser() utility that converts an OperationOutcome into plain-English
  messages distinguishing between server data retrieval failures (not-found) and expression
  evaluation failures (invalid), so clinicians understand the cause without reading console output
- PrePopulateMenuItem: add persistent warning snackbar for both total failure and partial
  populate (issues present); previously showed nothing in either case
- usePopulate: show the formatted issue message to all users regardless of showDeveloperMessages
  config; previously clinicians in non-developer mode saw no feedback on partial failure
- RepopulateAction: replace generic "view console for details" with the same formatted message
- Console warnings are preserved in all paths so developers retain full diagnostic detail

Relates to issue #1815

Made-with: Cursor
…o match test contract

- When showDeveloperMessages is true (developer mode): keep the existing
  'View console for details' snackbar so existing tests continue to pass
- When showDeveloperMessages is false (clinical users): show the new
  user-friendly formatted message from formatPopulateIssuesForUser
- Restore console.warn(issues) without prefix to match test spy expectations

Co-authored-by: Cursor <cursoragent@cursor.com>
…nline warnings

Implements per-field pre-population failure indicators so clinicians can see at a
glance exactly which fields were not pre-populated, rather than only receiving a
general snackbar message.

Changes:
- sdc-populate: add optional linkId parameter to createInvalidWarningIssue() and
  record it in OperationOutcomeIssue.expression so consumers can map issues back to
  specific questionnaire items; pass the linkId at both evaluateExpressions call sites
  where it is available in scope (initialExpressions and itemPopulationContexts loops)
- smart-forms-renderer: add prepopulationWarningLinkIds (Set<string>) to RendererConfig
  and rendererConfigStore; ItemFieldGrid reads the set and renders an amber
  'This field could not be pre-populated.' caption below any field whose linkId is in
  the set
- smart-forms-app: add extractWarningLinkIds() utility that collects linkIds from
  OperationOutcome.issue[].expression for invalid-coded issues; wire it into
  PrePopulateMenuItem, usePopulate, and RepopulateAction so the renderer store is
  updated with affected linkIds after each pre/re-population, and cleared when
  population succeeds without issues

Relates to issue #1815

Co-authored-by: Cursor <cursoragent@cursor.com>
…n rendererConfigStore in usePopulate

- Import statement for extractWarningLinkIds and formatPopulateIssuesForUser collapsed to single line
- rendererConfigStore?.getState() uses optional chaining so tests that mock @aehrc/smart-forms-renderer without rendererConfigStore do not throw before reaching enqueueSnackbar and console.warn
- Inline .getState() chain in RepopulateAction to satisfy prettier

Co-authored-by: Cursor <cursoragent@cursor.com>
… warning icons and improve failure messages

Previously, pre-population failures displayed a repeated text warning beneath
every affected field. This commit replaces that approach with a cleaner
group-level indicator and improves the quality of all user-facing messages.

Group-level warning icons (renderer package):
- Add prepopulationWarnings.ts utility with getGroupPrepopWarning(), which
  traverses a group's immediate named children (skipping unnamed layout
  containers like unlabelled grid groups) to collect affected field names and
  the failure reason. Tooltips show contextual names (e.g. "Body height")
  rather than repeated generic labels (e.g. "Value, Date performed").
- GroupHeading.tsx: render a WarningAmberIcon + MUI Tooltip on any group whose
  descendants have pre-population warnings. Tooltip shows the failure reason
  and lists the affected immediate child names.
- GridRow.tsx: same icon + tooltip on grid table row labels when any cell in
  that row has a warning.
- ItemFieldGrid.tsx: remove the per-field inline Typography warning text
  (replaced by the group-level icon).
- SingleItemView.tsx: remove the per-repeated-item inline Typography warning
  (replaced by the group-level icon).

Improved failure messages (smart-forms-app):
- prepopulateIssues.ts:
  - Add hasServerSideError() to detect HTTP 5xx responses and surface a
    "server returned an error, try again" message to clinicians.
  - Add hasMissingContextError() to detect fhirpath.js "undefined environment
    variable" errors, distinguishing a missing launch context (%patient not
    provided) from a generic expression failure.
  - extractWarningMessages() now selects the field-level tooltip reason
    per-issue: data not in health history (not-found cascade), missing patient
    context (undefined env variable), or standalone expression failure.
  - getWarningFieldNames() now prefixes each field name with its immediate
    parent group name (e.g. "Body height > Value") so the snackbar debug list
    is unambiguous.
  - All snackbar messages rewritten in plain clinical language with no
    FHIRPath, HTTP codes, or server internals exposed to the clinician.
- usePopulate.tsx: add optional chaining on questionnaireStore access to
  prevent a throw in test environments where the store is undefined.

Co-authored-by: Cursor <cursoragent@cursor.com>
…store branch coverage

Co-authored-by: Cursor <cursoragent@cursor.com>
@MaryamMehd MaryamMehd requested a review from leoniedickson May 26, 2026 05:12
@leoniedickson

Copy link
Copy Markdown
Collaborator

What the PR does

  • Adds extractWarningMessages to parse an OperationOutcome into a Map<linkId, message> stored on rendererConfigStore
  • Adds warning icons to GroupHeading and GridRow with tooltips listing affected field names
  • Adds a persistent snackbar with a categorised human-readable error message
  • Updates sdc-populate to attach linkId to per-item invalid issues so the affected item can be identified
  • Adds unit tests for the new utility functions

Blockers

1. Merge conflicts

The PR has mergeable: CONFLICTING — needs a rebase onto main before it can be merged.

2. config.json — accidental change

showDeveloperMessages was changed from truefalse. This is a local testing leftover and must be reverted.

-  "showDeveloperMessages": false
+  "showDeveloperMessages": true

3. "Pre-fill" language — inconsistent with the rest of the codebase

The entire prepopulateIssues.ts file uses "pre-fill" throughout ("Pre-fill incomplete", "could not be pre-filled", "pre-fill calculations"). The rest of the app consistently uses "populate" / "pre-populated" / "pre-population" (e.g., 'Form populated', 'Form partially populated', "Form does not support pre-population"). All strings in the new file need updating to match.

4. Snackbar message content issues

Several messages have inaccurate wording or include content that shouldn't be shown to clinical users:

  • "The required health record data may be unavailable" — appended in the generic fallback. A FHIRPath evaluation error has nothing to do with data availability. Remove it.
  • [Debug — affected fields: ...] — the word "Debug" must not appear in a clinical-facing UI. Remove the field name list from the snackbar.
  • not-found messagecreateNotFoundWarningIssue is only called when a specific resource reference (e.g., Patient/123) can't be resolved, or when an individual batch context entry can't be loaded. A FHIR search returning 0 results is not a not-found issue. The current message "no matching records were found in the patient's health history" sounds like a search-results message; it's a reference resolution failure. Better wording: "A required health record reference could not be retrieved."
  • 404 — "check that the server supports the required resource types" is developer-facing; a clinician can't act on it. Better wording: "Pre-population incomplete: some health record data could not be retrieved from the server. Contact your administrator to check the server configuration."
  • Network/CORS — "check your network connection" doesn't cover CORS, which is a browser security policy blocking cross-origin access rather than a connectivity problem. CORS is also not something the user can fix themselves. Better wording: "Pre-population incomplete: could not connect to the health record server. This may be a network issue or a server configuration problem — contact your administrator if it persists."

5. showDeveloperMessages branching is inconsistent across the three call sites

In usePopulate.tsx, when showDeveloperMessages is true, the old vague message is shown:

"Form partially populated, there might be pre-population issues. View console for details."

But prepopulationWarningMessages is still set (before the check), so inline warning icons appear regardless. Meanwhile PrePopulateMenuItem.tsx and RepopulateAction.tsx always show the new user-friendly snackbar — no check at all.

Fix: remove the showDeveloperMessages branching in usePopulate.tsx and the old message path entirely. Always show the new user-friendly snackbar.

6. formatPopulateIssuesForUser — first-match-wins limitation

formatPopulateIssuesForUser uses a first-match-wins priority order and returns after the first detected category. If multiple error types occur simultaneously (e.g., one resource returns 403, another 404), only the highest-priority one is shown. In practice errors tend to be consistent across resource fetches, but this is a known limitation. See design question 8 for an approach that would resolve this.


Design questions

7. Where should warning indicators appear? (errors with a linkId)

This covers invalid issues that carry a linkId — i.e. field-level expression failures where the affected item is known. (Errors without a linkId are covered in question 8.)

The current PR propagates warnings to every ancestor group, which means a single failed field produces identical detailed tooltips on every level of the hierarchy (tab, section, subsection, etc.). This is noisy and confusing — it's not clear which level is actionable, and the repeated tooltip adds no information.

The right placement depends on item type:

  • Non-repeating leaf item — warning on the SingleItem directly; this is the element identified by expression in the OperationOutcome and the PR currently misses it entirely
  • Non-grid repeating group — warning on each instance; each instance is a distinct entry (e.g. each medication card) and needs individual attention
  • Grid — one warning on the grid heading, not on every row; if onset date failed to populate, showing the icon on every row is noisy and doesn't add information

Group-level rollup: the inline item-level warning is always correct regardless of form structure and is probably sufficient for a first pass. A count badge at the page/tab level (e.g. ⚠ 2) could be a useful navigation aid for heavily paginated forms, but form structure varies enough (tabs, accordions, flat pages) that there's no clean "top-level navigation unit" to consistently attach it to — worth revisiting as a future enhancement.

8. Issues without a linkId — proposed display approach

All errors should be visible to the user, but the current approach makes this difficult. Only invalid issues with an expression array carry a linkId and can produce inline field-level warnings — all other error types (HTTP errors, network errors, not-found reference resolution failures, questionnaire-level invalid issues) have no linkId and can only go in the snackbar. Because the snackbar collapses everything into one message, simultaneous errors can only be shown one at a time — which is why the current messages are so long and specific.

A proposed approach: treat errors without a linkId as form-level errors and display them persistently:

  • A warning icon in the form header (in the playground: alongside the existing context info; in the main renderer: in the top bar)
  • Clicking or hovering shows a popover listing all form-level errors with their reasons from details.text
  • The snackbar becomes a brief, non-persistent "Form partially populated — some fields could not be pre-populated" and auto-dismisses

This cleanly separates the two concerns: field-level errors → inline on the item; form-level errors → persistent icon in the header. It also resolves blocker 6 — multiple simultaneous errors are listed together in the popover rather than one winning.

A note on questionnaire-level invalid issues (no linkId): when a %variable evaluation fails, dependent items also fail and generate their own invalid issues with linkIds, so those items still get inline warnings. The questionnaire-level issue itself belongs in the header display. The same applies to the evaluateItemPopulationContexts call site in evaluateExpressions.ts (line 146), which operates on named context variables rather than questionnaire items and correctly has no linkId.

9. Should the details.text reason from the OperationOutcome be shown?

Currently neither the snackbar nor the inline tooltip shows the actual reason from details.text — everything falls back to a generic message. The issue was created in part because people needed more visibility into why prepopulation failed, so a case can be made for surfacing it.

For showing details.text: more detail is available and surfacing it gives the user something to act on — once coding lookup failures are surfaced (see item 14), their messages describing which code or system failed could be meaningful.
Against: many details.text values are raw FHIRPath evaluation error messages (e.g. "toString: The input collection contains multiple items") that are opaque to clinical users and give them nothing to act on.

10. Should filling in a field clear its warning?

Currently nothing clears individual entries from prepopulationWarningMessages when the user interacts with a field. The warning persists until the next population run — meaning a clinician who manually fills in a field still sees the warning icon, which could be misleading.

The purpose of the warning is to draw attention to fields that need manual entry. Once the user enters a value, the warning has arguably been addressed. If clearing on interaction is the desired behaviour: in SingleItem, when the item's answer changes from empty to non-empty, remove the corresponding linkId from prepopulationWarningMessages.


Smaller issues

11. Unhandled OperationOutcome codes

FHIR servers can pass through not-supported codes via issues.push(...resource.issue). These have no linkId and currently fall through to the generic fallback. If not-supported is a realistic response from target servers it probably warrants its own message similar to 404.

12. Duplicate tooltip JSX

GroupHeading.tsx and GridRow.tsx contain structurally identical tooltip markup (same <Tooltip>, <Box>, reason + fieldNames list). Extract to a shared <PrepopWarningIcon> component in the renderer.

13. Optional chaining inconsistency

usePopulate.tsx uses rendererConfigStore?.getState() (optional chaining). RepopulateAction.tsx and PrePopulateMenuItem.tsx use rendererConfigStore.getState() without it. Should be consistent — the store is always defined, so the optional chaining in usePopulate.tsx is unnecessary and could be removed everywhere.


Follow-up

14. Coding lookup failures are silently swallowed

Coding lookup failures (where a coding is provided for prepopulation but the $lookup to retrieve its display fails) are described in the original issue but are not currently surfaced — resolveLookupPromises.ts uses Promise.allSettled() and silently continues without creating an OperationOutcome issue, so they are invisible to this entire display layer. This may be out of scope for this PR and could be tracked as a follow-up issue: resolveLookupPromises.ts would need to emit an invalid issue when a lookup fails so the affected field can surface a warning.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants