Skip to content

Conversation

Ramkrishna-egov
Copy link
Contributor

@Ramkrishna-egov Ramkrishna-egov commented Sep 30, 2025

Choose the appropriate template for your PR:

Feature/Bugfix Request

JIRA ID

Module

Description

Summary by CodeRabbit

  • New Features

    • Fullscreen Display Logic editor and Conditional Navigation editor with multi-condition rules, AND/OR joiners, per-rule validation.
  • Improvements

    • MDMS-backed dynamic dropdowns for value selection in rule builders.
    • Unified translation hook for consistent localization across editors.
    • Preview now shows field prefix/suffix, treats "textArea" like "textarea", and marks required fields via styling.
    • Enhanced filter/search popup and selection UI in templates.
  • Behavior

    • parentState is now propagated into config wrappers to support nested, state-aware editors.

@Ramkrishna-egov Ramkrishna-egov requested a review from a team as a code owner September 30, 2025 09:37
Copy link
Contributor

coderabbitai bot commented Sep 30, 2025

📝 Walkthrough

Walkthrough

Adds NavigationLogicWrapper and DependentFieldsWrapper modules, refactors useCustomT to return a translator function (with a direct-string shortcut), introduces local useT aliases across components, threads parentState through multiple wrappers, augments appConfig shapes with prefix/suffix and conditionalNavigateTo, and adds reducer handling PATCH_PAGE_CONDITIONAL_NAV.

Changes

Cohort / File(s) Summary
Navigation logic feature + integration
health/.../appConfigurationRedesign/NavigationLogicWrapper.js, health/.../appConfigurationRedesign/AppFieldScreenWrapper.js
Add NavigationLogicWrapper (multi-rule conditional navigation editor with MDMS dropdown, parsing/serialization, validation). Integrate into AppFieldScreenWrapper, render navigation UI, use useT alias, accept/forward parentState, and dispatch PATCH_PAGE_CONDITIONAL_NAV({ pageName, data }).
Display logic feature + integration
health/.../appConfigurationRedesign/DependentFieldsWrapper.js, health/.../appConfigurationRedesign/RenderConditionalField.js
Add DependentFieldsWrapper (fullscreen display-logic editor, portal, MDMS dropdown, expression parse/serialize). Render from RenderConditionalField on dependencyFieldWrapper, wire onExpressionChange, and pass t, currentState, parentState, and screenConfig.
Localization hook overhaul
health/.../appConfigurationRedesign/useCustomT.js
Rework useCustomT to accept optional arg and by default return a memoized translator function; preserve direct-string shortcut, add safe lookups, missing-key handling, and stable memoization.
Localization aliasing across components
health/.../appConfigurationRedesign/AppConfigurationWrapper.js, health/.../AppFieldScreenWrapper.js, health/.../DrawerFieldComposer.js, health/.../RenderConditionalField.js, health/.../ImpelComponentWrapper.js, health/.../AppLocalisationWrapper.js
Introduce const useT = useCustomT() alias and replace many useCustomT(...) call-sites with useT(...) for label/help/info/option translations; thread parentState prop through wrappers.
Reducer integration
health/.../appConfigurationRedesign/AppConfigurationWrapper.js
Add reducer action handling PATCH_PAGE_CONDITIONAL_NAV that patches screenConfig and screenData for a specified pageName with provided conditional navigation data.
App config shape augmentation
health/.../utils/appConfigHelpers.js
Add prefixText/suffixText to field shapes and propagate conditionalNavigateTo for pages/sections in restructure and reverseRestructure; adjust order mappings.
Rendering wrapper & parentState threading
health/.../appConfigurationRedesign/ImpelComponentWrapper.js, health/.../AppLocalisationWrapper.js, health/.../AppConfigurationParentLayer.js
Make dev localisation wrapper render unconditionally, add parentState parameter to wrappers and pass it through; adjust step/tab navigation logic and related state in parent layer.
UI / template adjustments
health/.../components/AppPreview.js, health/.../utils/template_components/RegistrationComponents.js, health/.../DrawerFieldComposer.js
Map textAreatextarea, include prefixText/suffixText in populators, infer required via className, replace some i18n usages with useT, and add PopUp/filter UI in registration templates.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant AppField as AppFieldScreenWrapper
  participant Nav as NavigationLogicWrapper
  participant Store as Reducer/Store

  User->>AppField: Open page config
  AppField->>Nav: Render (t, parentState, currentState)
  User->>Nav: Add/Edit navigation rule(s)
  Nav-->>Nav: Validate & serialize payload
  Nav->>AppField: onConditionalNavigateChange(payload)
  AppField->>Store: dispatch(PATCH_PAGE_CONDITIONAL_NAV(pageName, payload))
  Store-->>AppField: State updated
Loading
sequenceDiagram
  autonumber
  actor User
  participant RenderField as RenderConditionalField
  participant DepLogic as DependentFieldsWrapper
  participant Parent as Parent Component

  User->>RenderField: Open conditional field config
  RenderField->>DepLogic: Render (t, currentState, parentState, screenConfig)
  User->>DepLogic: Define display logic rules
  DepLogic-->>DepLogic: Validate & serialize expression
  DepLogic->>RenderField: onExpressionChange(expression)
  RenderField->>Parent: Propagate expression to parent config
Loading
sequenceDiagram
  autonumber
  participant Component as AnyComponent
  participant Hook as useCustomT
  participant Loc as Localization State

  Component->>Hook: const useT = useCustomT()
  Hook->>Loc: prepare memoized translator
  Component->>Component: useT("some.code")  -- translate via returned function
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • nabeelmd-eGov
  • jagankumar-egov

Poem

I twitch my whiskers, rules align,
Paths and fields now dance in line.
useT hums labels soft and clear,
Logic hops forward, far and near.
Carrots, configs — cheer! 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description only contains the placeholder template headings for JIRA ID, Module, and Description without any actual content or task references, so it fails to provide the required context and implementation details required by the repository’s template. Please populate the PR description with a valid JIRA ID or task reference, specify the affected module, and include a detailed description of the feature or bugfix to fully satisfy the repository’s template requirements.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title “Patch of Navigation and Dependent Fields Changes” directly references the two primary features added in this PR—conditional navigation logic and dependent fields UI—and thus aligns with the main changes summarized in the diff; although the phrasing is a bit verbose, it accurately captures the key scope of the updates.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch uat-navigation-depnedency

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 22

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 25a3ddb and ce1e7a5.

📒 Files selected for processing (7)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationWrapper.js (4 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js (6 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js (1 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DrawerFieldComposer.js (3 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js (1 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/RenderConditionalField.js (5 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

check

Files:

  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DrawerFieldComposer.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationWrapper.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/RenderConditionalField.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js
🧬 Code graph analysis (7)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js (3)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationWrapper.js (1)
  • useT (291-291)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js (2)
  • useCustomT (12-40)
  • useCustomT (12-40)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js (1)
  • parentState (60-60)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js (1)
micro-ui/web/micro-ui-internals/packages/react-components/src/atoms/Dropdown.js (1)
  • Dropdown (94-281)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DrawerFieldComposer.js (5)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationWrapper.js (1)
  • useT (291-291)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js (1)
  • useT (33-33)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js (1)
  • useT (73-73)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/RenderConditionalField.js (1)
  • useT (20-20)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js (2)
  • useCustomT (12-40)
  • useCustomT (12-40)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationWrapper.js (2)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js (2)
  • useCustomT (12-40)
  • useCustomT (12-40)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/AppPreview.js (1)
  • AppPreview (172-280)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/RenderConditionalField.js (2)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js (1)
  • useT (73-73)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js (2)
  • useCustomT (12-40)
  • useCustomT (12-40)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js (1)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppLocalisationWrapper.js (2)
  • locState (46-46)
  • addMissingKey (53-55)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js (3)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js (5)
  • tenantId (17-17)
  • module (18-18)
  • list (20-31)
  • options (33-36)
  • selectedOption (38-42)
micro-ui/web/micro-ui-internals/packages/react-components/src/atoms/Dropdown.js (1)
  • Dropdown (94-281)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js (2)
  • useCustomT (12-40)
  • useCustomT (12-40)
🪛 Biome (2.1.2)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js

[error] 619-619: The elements with this role can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)


[error] 618-624: The HTML element with the interactive role "button" is not focusable.

A non-interactive HTML element that is not focusable may not be reachable for users that rely on keyboard navigation, even with an added role like "button".
Add a tabIndex attribute to make this element focusable.

(lint/a11y/useFocusableInteractive)


[error] 618-624: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 633-633: The elements with this role can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)


[error] 632-638: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 632-638: The HTML element with the interactive role "button" is not focusable.

A non-interactive HTML element that is not focusable may not be reachable for users that rely on keyboard navigation, even with an added role like "button".
Add a tabIndex attribute to make this element focusable.

(lint/a11y/useFocusableInteractive)


[error] 949-961: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 960-960: The elements with this role can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)


[error] 949-961: The HTML element with the interactive role "button" is not focusable.

A non-interactive HTML element that is not focusable may not be reachable for users that rely on keyboard navigation, even with an added role like "button".
Add a tabIndex attribute to make this element focusable.

(lint/a11y/useFocusableInteractive)


[error] 689-689: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js

[error] 514-515: The elements with this role can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)


[error] 513-520: The HTML element with the interactive role "button" is not focusable.

A non-interactive HTML element that is not focusable may not be reachable for users that rely on keyboard navigation, even with an added role like "button".
Add a tabIndex attribute to make this element focusable.

(lint/a11y/useFocusableInteractive)


[error] 513-520: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 528-529: The elements with this role can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)


[error] 527-534: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 527-534: The HTML element with the interactive role "button" is not focusable.

A non-interactive HTML element that is not focusable may not be reachable for users that rely on keyboard navigation, even with an added role like "button".
Add a tabIndex attribute to make this element focusable.

(lint/a11y/useFocusableInteractive)


[error] 917-924: Static Elements should not be interactive.

To add interactivity such as a mouse or key event listener to a static element, give the element an appropriate role value.

(lint/a11y/noStaticElementInteractions)


[error] 917-924: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 598-598: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

⏰ Context from checks skipped due to timeout of 10000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (19)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DrawerFieldComposer.js (3)

88-88: LGTM! Translation hook properly initialized.

The local useT alias is correctly initialized via useCustomT() and follows the new translation pattern introduced across the codebase.


196-196: LGTM! Translation usage updated correctly.

The refactored call to useT(drawerState?.[panelItem?.bindTo]) properly replaces the previous pattern and handles localisable text fields as expected.


355-355: LGTM! Localization auto-population works correctly.

The change to useT(option.code) correctly retrieves the localized value for the DetailsCard field. The value is properly bound to the input and updates via updateLocalization on change.

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationWrapper.js (4)

291-291: LGTM! Translation hook initialized correctly.

The local useT alias is properly initialized and aligns with the standardized translation pattern across the module.


302-302: LGTM! Memoized translation value.

The useMemo hook correctly derives the localized label using useT, ensuring the value updates only when addFieldData changes.


558-558: LGTM! Translation function passed to AppPreview.

Passing useT as the t prop is valid. The AppPreview component expects a translation function and will call it with localization keys. This aligns with the refactored translation pattern.


731-731: LGTM! Translation used correctly in popup field.

The ternary expression correctly handles the case when addFieldData?.label exists, providing a localized value via useT.

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/RenderConditionalField.js (4)

6-6: LGTM! Import added correctly.

The DependentFieldsWrapper import is properly placed and supports the new conditional rendering case below.


20-20: LGTM! Translation hook initialized.

The local useT alias is correctly initialized and follows the standardized translation pattern.


38-38: LGTM! Translation usage updated.

The refactored call to useT(drawerState?.[cField?.bindTo]) correctly retrieves localized text when isLocalisable is true.


90-90: LGTM! Option name localized correctly.

The change to useT(item?.name) properly retrieves the localized option name for display.

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js (3)

24-24: LGTM! Import added correctly.

The NavigationLogicWrapper import supports the new navigation logic feature below.


33-33: LGTM! Translation hook initialized.

The local useT alias is correctly initialized and follows the standardized translation pattern.


68-68: LGTM! Translation calls updated consistently.

All translation calls have been correctly refactored to use the useT alias for header values, field labels, help text, info text, inner labels, and action labels.

Also applies to: 94-94, 134-136, 220-220

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js (2)

1-3: LGTM! Import added correctly.

The useMemo import is necessary for the memoized translator function below.


12-40: LGTM! Well-designed translation refactor.

The refactored useCustomT implementation is solid:

  1. Memoization: The translate function is correctly memoized with appropriate dependencies (locState, addMissingKey, currentLocale).
  2. Safety: Validates input and handles missing entries gracefully.
  3. Backward compatibility: Supports the legacy direct-call pattern.
  4. Missing key tracking: Calls addMissingKey for absent translations.

This design enables the stable useT alias pattern used across all other files.

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js (1)

405-405: Verify dependency array with JSON.stringify.

The useEffect dependency includes JSON.stringify(existingConditional), which creates a new string on every render. While this works, it can cause unnecessary re-renders if existingConditional is recreated with the same values.

Consider using a deep comparison library or restructuring to avoid the JSON.stringify pattern if performance becomes a concern.

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js (2)

461-468: Review close button behavior with validation.

The handleFooterClose function prevents closing the popup if the rule is incomplete and shows a validation error. However, this behavior differs from NavigationLogicWrapper.js where discardAndCloseEditor always closes (line 433-438).

Confirm that preventing the close action when validation fails is the intended UX. Users might expect the "Cancel" button to always discard changes and close, regardless of validation state. Consider whether this should align with NavigationLogicWrapper's behavior.


152-179: No changes needed—filter logic matches intended behavior.

Comment on lines +201 to +212
<NavigationLogicWrapper
t={t}
parentState={parentState}
currentState={currentCard}
onConditionalNavigateChange={(data) => {
dispatch({
type: "PATCH_PAGE_CONDITIONAL_NAV",
pageName: currentCard?.name, // optional but safer
data, // array built by NavigationLogicWrapper on Submit
});
}}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical issue: Undefined variable parentState.

The NavigationLogicWrapper component receives parentState as a prop on line 203, but this variable is not defined in the function scope. No parentState is declared in this file or passed as a parameter to AppFieldScreenWrapper.

This will cause a runtime error.

#!/bin/bash
# Search for parentState definition in AppFieldScreenWrapper and related files
rg -n 'parentState' --type=js health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js

# Check if parentState should come from context or props
rg -n 'parentState|parentDispatch' --type=js health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/

Ensure parentState is either:

  1. Obtained from useAppConfigContext() or another context
  2. Passed as a prop to AppFieldScreenWrapper
  3. Defined locally if it represents a local state
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js
around lines 201–212, the prop parentState passed into <NavigationLogicWrapper>
is undefined in this component scope; fix by ensuring parentState is supplied
from a valid source: either (A) read it from the existing app config/context
(e.g., call useAppConfigContext() or the appropriate context hook and pass the
returned state as parentState), or (B) accept parentState as a prop on
AppFieldScreenWrapper (add it to the component parameters and update all callers
to forward parentState), or (C) declare a local state variable
(useState/useReducer/select from Redux) named parentState if it should be local;
also update prop types / TS interfaces and any parent component usages
accordingly so the variable is defined at runtime.

Comment on lines +206 to +210
dispatch({
type: "PATCH_PAGE_CONDITIONAL_NAV",
pageName: currentCard?.name, // optional but safer
data, // array built by NavigationLogicWrapper on Submit
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify reducer action exists.

The dispatch calls a PATCH_PAGE_CONDITIONAL_NAV action (line 207) which should be handled by the reducer in AppConfigurationWrapper. Ensure this action type exists and correctly updates the navigation logic state.


🏁 Script executed:

#!/bin/bash
# Verify PATCH_PAGE_CONDITIONAL_NAV action exists in the reducer
rg -n 'PATCH_PAGE_CONDITIONAL_NAV' --type=js health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/

Length of output: 197


#!/bin/bash
# locate AppConfigurationWrapper file
rg -n 'AppConfigurationWrapper' --type=js health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/

# search for reducer handling of PATCH_PAGE_CONDITIONAL_NAV globally
rg -n 'case\s*["\']PATCH_PAGE_CONDITIONAL_NAV["\']' --type=js -P -C3 .

🏁 Script executed:

#!/bin/bash
# Search for reducer case handling PATCH_PAGE_CONDITIONAL_NAV in campaign-manager module
rg -n "case\s*['\"]PATCH_PAGE_CONDITIONAL_NAV['\"]" --type=js health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/ -C2

Length of output: 0


#!/bin/bash
# 1. Locate AppConfigurationWrapper file
rg -l "AppConfigurationWrapper" --type=js health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/

# 2. Search within that file for the reducer case
FILE=$(rg -l "AppConfigurationWrapper" --type=js health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/)
rg -n "case\s*['\"]PATCH_PAGE_CONDITIONAL_NAV['\"]" --type=js "$FILE" -C2

#!/bin/bash
# 1. List all JS files in appConfigurationRedesign directory
fd -t f . health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign

# 2. Locate file containing AppConfigurationWrapper export
rg -l "AppConfigurationWrapper" --type=js health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign

# 3. Search for useReducer or reducer definitions in that directory
rg -n "useReducer" --type=js health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign

Implement reducer case for PATCH_PAGE_CONDITIONAL_NAV
Add a handler for the PATCH_PAGE_CONDITIONAL_NAV action in the appropriate reducer (e.g., within AppConfigurationWrapper) to update the navigation state.

🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js
around lines 206 to 210, the dispatch sends an action type
"PATCH_PAGE_CONDITIONAL_NAV" but there is no reducer case to handle it; add a
case in the AppConfigurationWrapper reducer (or the reducer managing navigation
state) that matches "PATCH_PAGE_CONDITIONAL_NAV", reads pageName and data from
action.payload (or action fields), merges or replaces the existing navigation
state for the specified page with the provided data (ensuring immutable
updates), and returns the updated state; also add any necessary initialState
keys and unit tests to cover the new branch.

Comment on lines +17 to +20
function BodyPortal({ children }) {
if (typeof document === "undefined") return null; // SSR guard
return ReactDOM.createPortal(children, document.body);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Code duplication: BodyPortal is duplicated.

This component is identical to the one in NavigationLogicWrapper.js. Extract to a shared utility file.

🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js
around lines 17-20, the BodyPortal component is duplicated (also exists in
NavigationLogicWrapper.js); extract it to a single shared module (e.g.,
health/.../shared/components/BodyPortal.js), export it (default or named), keep
the SSR guard (if typeof document === "undefined" return null) and
ReactDOM.createPortal(children, document.body), then replace the in-file
definitions in both DependentFieldsWrapper.js and NavigationLogicWrapper.js with
an import from the new shared file and update any relative import paths; ensure
build passes and no additional behavior changes are introduced.

Comment on lines +22 to +63
function MdmsValueDropdown({ schemaCode, value, onChange, t }) {
const tenantId = Digit?.ULBService?.getCurrentTenantId?.();
const [module = "", master = ""] = (schemaCode || "").split(".");

const { isLoading, data: list = [] } = Digit.Hooks.useCustomMDMS(
tenantId,
module,
[{ name: master }],
{
cacheTime: Infinity,
staleTime: Infinity,
select: (data) => data?.[module]?.[master] || [],
},
{ schemaCode: "DROPDOWN_MASTER_DATA" },
true // mdmsv2
);

const options = React.useMemo(
() => (Array.isArray(list) ? list.map((it) => ({ code: it.code, name: it.name })) : []),
[list]
);

// normalize the selected value to an option object
const selectedOption = React.useMemo(() => {
if (!value) return undefined;
const match = options.find((o) => String(o.code) === String(value));
return match || { code: value, name: value };
}, [options, value]);

return (
<Dropdown
option={options}
optionKey="code"
name={`mdms-${module}-${master}`}
t={t}
optionCardStyles={{ maxHeight: 300, overflow: "auto", position: "relative", zIndex: 10000 }}
select={(e) => onChange(e.code)}
disabled={isLoading || !module || !master}
selected={selectedOption}
/>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Code duplication: MdmsValueDropdown is duplicated.

This component is identical to the one in NavigationLogicWrapper.js. See the earlier comment in that file for the refactoring suggestion.

Comment on lines +187 to +220
const isStringLike = (field) => {
const tpe = (field?.type || "").toLowerCase();
const fmt = (field?.format || "").toLowerCase();
if (fmt === "dropdown" || fmt === "radio" || tpe === "selection") return true;
return ["string", "text", "textinput", "textarea"].includes(tpe);
};
const isCheckboxField = (field) => (field?.type || "").toLowerCase() === "checkbox";
const isNumericField = (field) => {
const tpe = (field?.type || "").toLowerCase();
const fmt = (field?.format || "").toLowerCase();
const numericTags = ["number", "numeric", "integer"];
return numericTags.includes(tpe) || numericTags.includes(fmt);
};
const isDobLike = (field) => {
const tpe = (field?.type || "").toLowerCase();
const fmt = (field?.format || "").toLowerCase();
return tpe === "datepicker" && fmt === "dob";
};
const isDatePickerNotDob = (field) => {
const tpe = (field?.type || "").toLowerCase();
const fmt = (field?.format || "").toLowerCase();
return tpe === "datepicker" && fmt !== "dob";
};
const isSelectLike = (field) => {
const fmt = (field?.format || "").toLowerCase();
const tpe = (field?.type || "").toLowerCase();
return (
fmt === "dropdown" ||
fmt === "radio" ||
tpe === "selection" ||
(Array.isArray(field?.enums) && field.enums.length > 0) ||
!!field?.schemaCode
);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider consolidating type-checking helpers.

Functions isStringLike, isCheckboxField, isNumericField, isDobLike, isDatePickerNotDob, and isSelectLike have significant overlap with similar functions in NavigationLogicWrapper.js. Both files implement nearly identical logic for determining field types.

Extract these type-checking utilities into a shared module (e.g., fieldTypeHelpers.js) to eliminate duplication and ensure consistent behavior across both wrappers.

🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js
around lines 187-220, the file contains several field type-checking helpers
(isStringLike, isCheckboxField, isNumericField, isDobLike, isDatePickerNotDob,
isSelectLike) that duplicate logic found in NavigationLogicWrapper.js; extract
these utilities into a new shared module (e.g., fieldTypeHelpers.js) that
exports small pure functions for each predicate, update both
DependentFieldsWrapper.js and NavigationLogicWrapper.js to import and use the
shared helpers, ensure the helpers accept a single field argument and normalize
type/format/enums/schemaCode checks inside the module, add unit tests or update
existing ones if present, and remove the duplicated functions from both files to
keep behavior consistent.

Comment on lines +949 to +966
<div
style={{
marginLeft: "auto",
display: "flex",
alignItems: "center",
gap: "0.25rem",
cursor: "pointer",
}}
onClick={() => removeCondition(editorIndex, idx)}
title={removeConditionLabel}
aria-label={removeConditionLabel}
role="button"
>
<SVG.Delete fill={"#C84C0E"} width={"1.25rem"} height={"1.25rem"} />
<span style={{ color: "#C84C0E", fontSize: "0.875rem", fontWeight: 500 }}>
{removeConditionLabel}
</span>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix accessibility violations: remove condition button needs keyboard support.

The "remove condition" action uses a <div> with role="button" but lacks keyboard event handlers and tabIndex.

Apply this diff:

                                 <div
                                     style={{
                                         marginLeft: "auto",
                                         display: "flex",
                                         alignItems: "center",
                                         gap: "0.25rem",
                                         cursor: "pointer",
                                     }}
+                                    role="button"
+                                    tabIndex={0}
                                     onClick={() => removeCondition(editorIndex, idx)}
+                                    onKeyDown={(e) => {
+                                        if (e.key === 'Enter' || e.key === ' ') {
+                                            e.preventDefault();
+                                            removeCondition(editorIndex, idx);
+                                        }
+                                    }}
                                     title={removeConditionLabel}
                                     aria-label={removeConditionLabel}
-                                    role="button"
                                 >

Based on static analysis hints.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div
style={{
marginLeft: "auto",
display: "flex",
alignItems: "center",
gap: "0.25rem",
cursor: "pointer",
}}
onClick={() => removeCondition(editorIndex, idx)}
title={removeConditionLabel}
aria-label={removeConditionLabel}
role="button"
>
<SVG.Delete fill={"#C84C0E"} width={"1.25rem"} height={"1.25rem"} />
<span style={{ color: "#C84C0E", fontSize: "0.875rem", fontWeight: 500 }}>
{removeConditionLabel}
</span>
</div>
<div
style={{
marginLeft: "auto",
display: "flex",
alignItems: "center",
gap: "0.25rem",
cursor: "pointer",
}}
role="button"
tabIndex={0}
onClick={() => removeCondition(editorIndex, idx)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
removeCondition(editorIndex, idx);
}
}}
title={removeConditionLabel}
aria-label={removeConditionLabel}
>
<SVG.Delete fill={"#C84C0E"} width={"1.25rem"} height={"1.25rem"} />
<span style={{ color: "#C84C0E", fontSize: "0.875rem", fontWeight: 500 }}>
{removeConditionLabel}
</span>
</div>
🧰 Tools
🪛 Biome (2.1.2)

[error] 949-961: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 960-960: The elements with this role can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)


[error] 949-961: The HTML element with the interactive role "button" is not focusable.

A non-interactive HTML element that is not focusable may not be reachable for users that rely on keyboard navigation, even with an added role like "button".
Add a tabIndex attribute to make this element focusable.

(lint/a11y/useFocusableInteractive)

🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js
around lines 949 to 966, the "remove condition" control is a div with
role="button" but no keyboard support or focusability; replace the div with a
semantic <button type="button"> (preserving inline styles, title and aria-label)
or add tabIndex={0} and onKeyDown handler that triggers removeCondition on
Enter/Space if you must keep the div; ensure the clickable area uses onClick and
the accessible name remains, remove the redundant role when using a native
button, and confirm styling and SVG remain unchanged so keyboard users can focus
and activate the control.

Comment on lines +989 to +992
onClick={() => {
setValidationStarted(false);
setGlobalFormError(null)
}}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix inconsistent error state handling.

Line 991 sets globalFormError to null, but elsewhere in the file it's set to empty string "" (lines 410, 417, 423, 435, 552). This inconsistency could cause type-related issues.

Apply this diff:

                                     onClick={() => {
                                         setValidationStarted(false);
-                                        setGlobalFormError(null)
+                                        setGlobalFormError("")
                                     }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onClick={() => {
setValidationStarted(false);
setGlobalFormError(null)
}}
onClick={() => {
setValidationStarted(false);
setGlobalFormError("")
}}
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js
around lines 989-992, the handler sets globalFormError to null which is
inconsistent with other places in the file that use an empty string; change the
setGlobalFormError(null) call to setGlobalFormError("") so the error state type
is consistent, and scan the file to ensure all assignments to globalFormError
use the same empty-string convention.

Comment on lines +219 to +229
case "dependencyFieldWrapper":
return (
<DependentFieldsWrapper
t={t}
currentState={state}
parentState={parentState}
onExpressionChange={handleExpressionChange}
screenConfig={screenConfig}
selectedFieldItem={selectedField}
/>
);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical issue: Undefined variables passed to DependentFieldsWrapper.

The new case references several variables that are not defined in the function scope:

  • parentState (line 224)
  • handleExpressionChange (line 225)
  • screenConfig (line 226)
  • selectedField (line 227)

These variables are not present in the function parameters or local scope, which will cause runtime errors.

#!/bin/bash
# Verify if these variables are defined elsewhere in the file or should be passed as props
rg -n -A5 -B5 'parentState|handleExpressionChange|screenConfig|selectedField' --type=js health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/RenderConditionalField.js

Ensure these variables are either:

  1. Added to the function parameters
  2. Derived from the existing parameters (state, drawerState, etc.)
  3. Removed if not needed by DependentFieldsWrapper

Comment on lines +4 to +11
/**
* useCustomT
*
* SAFE usage (preferred):
* const tt = useCustomT();
* const label = tt("SOME_CODE");
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Good addition: Documentation for safe usage.

The JSDoc comment clearly explains the preferred usage pattern. Consider completing the example on line 10.

  * SAFE usage (preferred):
  *   const tt = useCustomT();
  *   const label = tt("SOME_CODE");
- *
+ *
+ * LEGACY usage (backward-compatible):
+ *   const label = useCustomT("SOME_CODE");
  */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* useCustomT
*
* SAFE usage (preferred):
* const tt = useCustomT();
* const label = tt("SOME_CODE");
*/
/**
* useCustomT
*
* SAFE usage (preferred):
* const tt = useCustomT();
* const label = tt("SOME_CODE");
*
* LEGACY usage (backward-compatible):
* const label = useCustomT("SOME_CODE");
*/
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js
around lines 4 to 11, the JSDoc shows a started "SAFE usage" example but line 10
is incomplete; finish the example so it demonstrates obtaining the translator
and using it in one concise snippet. Update the comment to show the full usage
flow (e.g., call useCustomT(), assign to a variable, and call that variable with
a translation key and optional default/fallback) so consumers can copy the
complete example directly from the docblock.

Comment on lines +18 to +33
const translate = useMemo(() => {
const list = Array.isArray(locState) ? locState : [];
return (code) => {
if (!code) {
console.warn("useCustomT: code parameter is required");
return "";
}
const entry = list.find((item) => item?.code === code);
if (!entry) {
addMissingKey(code);
return "";
}
const msg = entry?.[currentLocale];
return msg || "";
};
}, [locState, addMissingKey, currentLocale]);
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider performance for large locState arrays.

The translate function uses Array.find() for each translation lookup, which is O(n) for the locState array. If locState contains thousands of entries and translations are called frequently, this could impact performance.

Consider memoizing a lookup map:

  const translate = useMemo(() => {
    const list = Array.isArray(locState) ? locState : [];
+   const lookupMap = new Map(list.map(item => [item?.code, item]));
    return (code) => {
      if (!code) {
        console.warn("useCustomT: code parameter is required");
        return "";
      }
-     const entry = list.find((item) => item?.code === code);
+     const entry = lookupMap.get(code);
      if (!entry) {
        addMissingKey(code);
        return "";
      }
      const msg = entry?.[currentLocale];
      return msg || "";
    };
  }, [locState, addMissingKey, currentLocale]);

This reduces lookup time from O(n) to O(1).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const translate = useMemo(() => {
const list = Array.isArray(locState) ? locState : [];
return (code) => {
if (!code) {
console.warn("useCustomT: code parameter is required");
return "";
}
const entry = list.find((item) => item?.code === code);
if (!entry) {
addMissingKey(code);
return "";
}
const msg = entry?.[currentLocale];
return msg || "";
};
}, [locState, addMissingKey, currentLocale]);
const translate = useMemo(() => {
const list = Array.isArray(locState) ? locState : [];
const lookupMap = new Map(list.map(item => [item?.code, item]));
return (code) => {
if (!code) {
console.warn("useCustomT: code parameter is required");
return "";
}
const entry = lookupMap.get(code);
if (!entry) {
addMissingKey(code);
return "";
}
const msg = entry?.[currentLocale];
return msg || "";
};
}, [locState, addMissingKey, currentLocale]);
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js
around lines 18 to 33, the translate function does a linear Array.find on
locState for every lookup which is O(n) and will be slow for large arrays; fix
by using useMemo to build a lookup map (object or Map) keyed by item.code from
locState and then change translate to do an O(1) lookup against that map,
preserving the existing behavior for falsy code and missing entries (call
addMissingKey and return ""), and include locState in the memo deps (plus
addMissingKey/currentLocale if used) so the map rebuilds when locState changes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js (1)

203-203: Critical: parentState is undefined.

Line 203 passes parentState to NavigationLogicWrapper, but this variable is not defined in AppFieldScreenWrapper's scope. This will cause a runtime ReferenceError.

Based on the relevant code snippets:

  • AppConfigurationParentLayer.js (line 59) shows parentState from useReducer(dispatcher, {})
  • AppConfigurationWrapper.js (line 317) destructures parentDispatch from context

Fix required: Either:

  1. Accept parentState as a prop to AppFieldScreenWrapper and pass it from the parent component
  2. Import it from a context (check if useAppLocalisationContext should expose it)
  3. Remove the prop if NavigationLogicWrapper doesn't require it
#!/bin/bash
# Verify whether parentState is available in context or needs to be passed as prop
rg -n 'parentState' --type=js -C3 health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce1e7a5 and 85641f3.

📒 Files selected for processing (3)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationWrapper.js (5 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js (7 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/appConfigHelpers.js (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

check

Files:

  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationWrapper.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/appConfigHelpers.js
🧬 Code graph analysis (2)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationWrapper.js (2)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js (2)
  • useCustomT (12-40)
  • useCustomT (12-40)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/AppPreview.js (1)
  • AppPreview (172-280)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js (4)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationWrapper.js (1)
  • useT (315-315)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js (2)
  • useCustomT (12-40)
  • useCustomT (12-40)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/ConsoleToolTip.js (1)
  • ConsoleTooltip (34-69)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js (1)
  • parentState (60-60)
⏰ Context from checks skipped due to timeout of 10000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (5)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/appConfigHelpers.js (1)

225-225: LGTM! Field addition is consistent.

The conditionalNavigateTo field is correctly exposed in both restructure and reverseRestructure functions, following the same pattern as the existing navigateTo field. The optional chaining ensures safe handling when the field is absent.

Also applies to: 298-298

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationWrapper.js (1)

315-315: LGTM! Translation hook alias pattern is consistent.

The introduction of useT as a local alias for useCustomT() and its usage across multiple localization points (lines 326, 582, 755) follows the updated pattern from useCustomT.js and aligns with similar changes across other components in this PR.

Also applies to: 326-327, 582-582, 755-755

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppFieldScreenWrapper.js (3)

33-33: LGTM! Translation hook usage is consistent.

The useT alias and its usage across multiple localization points (lines 68, 94, 134-136, 229) correctly follows the updated translation pattern and aligns with changes in other files in this PR.

Also applies to: 68-68, 94-94, 134-136, 229-229


205-211: LGTM! Dispatch correctly wired to the reducer.

The PATCH_PAGE_CONDITIONAL_NAV action is properly handled in the reducer (verified in AppConfigurationWrapper.js lines 283-306). The dispatch provides both pageName and data as expected by the reducer logic.


194-214: Navigation logic integration is correct. NavigationLogicWrapper properly consumes currentState, invokes onConditionalNavigateChange(data) with an array of {condition: string, navigateTo: {name, type}}, and the reducer’s PATCH_PAGE_CONDITIONAL_NAV case applies that data directly to conditionalNavigateTo.

Comment on lines +283 to +306
case "PATCH_PAGE_CONDITIONAL_NAV": {
const { pageName, data } = action; // data is the array from onConditionalNavigateChange

const patchArray = (arr) => {
if (!Array.isArray(arr) || arr.length === 0) return arr;

// If pageName is provided, try to patch by name
if (pageName) {
const idx = arr.findIndex((p) => p?.name === pageName);
if (idx !== -1) {
return arr.map((p, i) => (i === idx ? { ...p, conditionalNavigateTo: data } : p));
}
}

// Fallback: patch the first page (your “current page is first” invariant)
return arr;
};

return {
...state,
screenConfig: patchArray(state.screenConfig),
screenData: patchArray(state.screenData),
};
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fallback logic doesn't match the comment.

The PATCH_PAGE_CONDITIONAL_NAV reducer case has an inconsistency:

  • Line 297 comment states: "Fallback: patch the first page (your 'current page is first' invariant)"
  • Line 298 code returns arr unchanged without patching anything

If pageName is not provided or not found, the current implementation silently fails to update the state. Consider either:

  1. Removing the misleading comment if no-op fallback is intentional, or
  2. Implementing the first-page patch as the comment suggests:
-        // Fallback: patch the first page (your "current page is first" invariant)
-        return arr;
+        // Fallback: patch the first page if no pageName provided
+        return arr.map((p, i) => (i === 0 ? { ...p, conditionalNavigateTo: data } : p));

Additionally, consider validating that data is an array as expected by the comment.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case "PATCH_PAGE_CONDITIONAL_NAV": {
const { pageName, data } = action; // data is the array from onConditionalNavigateChange
const patchArray = (arr) => {
if (!Array.isArray(arr) || arr.length === 0) return arr;
// If pageName is provided, try to patch by name
if (pageName) {
const idx = arr.findIndex((p) => p?.name === pageName);
if (idx !== -1) {
return arr.map((p, i) => (i === idx ? { ...p, conditionalNavigateTo: data } : p));
}
}
// Fallback: patch the first page (your “current page is first” invariant)
return arr;
};
return {
...state,
screenConfig: patchArray(state.screenConfig),
screenData: patchArray(state.screenData),
};
}
case "PATCH_PAGE_CONDITIONAL_NAV": {
const { pageName, data } = action; // data is the array from onConditionalNavigateChange
const patchArray = (arr) => {
if (!Array.isArray(arr) || arr.length === 0) return arr;
// If pageName is provided, try to patch by name
if (pageName) {
const idx = arr.findIndex((p) => p?.name === pageName);
if (idx !== -1) {
return arr.map((p, i) => (i === idx ? { ...p, conditionalNavigateTo: data } : p));
}
}
// Fallback: patch the first page if no pageName provided
return arr.map((p, i) => (i === 0 ? { ...p, conditionalNavigateTo: data } : p));
};
return {
...state,
screenConfig: patchArray(state.screenConfig),
screenData: patchArray(state.screenData),
};
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 (3)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/ImpelComponentWrapper.js (3)

83-83: Remove unreachable break statement.

The break statement on line 83 is unreachable because it follows a return statement.

           validation: {
             min: field.min,
             max: field.max,
           },
         };
-        break;

29-55: Consider improving code clarity.

The restructure function has several code quality issues that reduce readability:

  1. Variable name xx is not descriptive
  2. Map callback parameters like index, arr, m, n, o, p, q, r are declared but never used

Consider this refactor for clarity:

-  function restructure(temp) {
-    const xx = temp.map((item, index, arr) => {
+  function restructure(data) {
+    return data.map((item) => {
       return {
         type: "page",
         name: item?.name,
-        components: item?.cards?.map((m, n, o) => {
+        components: item?.cards?.map((card, cardIndex) => {
           return {
-            title: m?.headerFields?.find((i) => i.jsonPath === "SectionHeading")?.value,
-            order: n + 1,
-            description: m?.headerFields?.find((i) => i.jsonPath === "Description")?.value,
-            attributes: m?.fields?.map((p, q, r) => {
+            title: card?.headerFields?.find((i) => i.jsonPath === "SectionHeading")?.value,
+            order: cardIndex + 1,
+            description: card?.headerFields?.find((i) => i.jsonPath === "Description")?.value,
+            attributes: card?.fields?.map((field, fieldIndex) => {
               return {
-                name: p.label,
-                label: p.label,
+                name: field.label,
+                label: field.label,
                 type: "additionalField",
-                attribute: p.type,
-                order: q + 1,
-                ...(AppConfigMdmsData?.AppFieldType?.find((i) => i.type === p.type)?.metadata || {}),
+                attribute: field.type,
+                order: fieldIndex + 1,
+                ...(AppConfigMdmsData?.AppFieldType?.find((i) => i.type === field.type)?.metadata || {}),
               };
             }),
           };
         }),
       };
     });
-
-    return xx;
   }

104-104: Remove or handle extra parameters in correctField
The function is defined as const correctField = (key) => {…} but on line 104 it’s called with three arguments (field.type, field.isApi, field.isMdms), so the latter two are ignored. Either drop those extra args in the call or extend the signature to (key, isApi, isMdms) and incorporate them into the function.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 85641f3 and 843271a.

📒 Files selected for processing (1)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/ImpelComponentWrapper.js (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

check

Files:

  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/ImpelComponentWrapper.js
🧬 Code graph analysis (1)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/ImpelComponentWrapper.js (1)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js (2)
  • back (433-443)
  • localeModule (66-66)

//production mode
import { AppLocalisationWrapper, Loader, useCustomT } from "@egovernments/digit-ui-components";
// import AppLocalisationWrapperDev from "./AppLocalisationWrapper";
import AppLocalisationWrapperDev from "./AppLocalisationWrapper";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Development wrapper will be used in production.

The conditional check for process.env.NODE_ENV === "development" has been commented out, causing AppLocalisationWrapperDev to be rendered unconditionally in all environments, including production. This is a critical issue that will result in:

  • Development-mode code running in production
  • Potential performance degradation
  • Possible exposure of debug features or sensitive information
  • Different behavior between environments

This change does not align with the PR title ("Navigation and Dependent Fields Changes") and appears unintentional.

Restore the environment check to use the appropriate wrapper:

-  // if (process.env.NODE_ENV === "development") {
+  if (process.env.NODE_ENV === "development") {
     return (
       //development mode
       <AppLocalisationWrapperDev
         onSubmit={onSubmit}
         back={back}
         showBack={showBack}
         screenConfig={screenConfig}
         parentDispatch={parentDispatch}
         localeModule={localeModule}
         pageTag={pageTag}
       />
     );
-  // }
-  // return (
-  //   //production mode
-  //   <AppLocalisationWrapper
-  //     onSubmit={onSubmit}
-  //     back={back}
-  //     showBack={showBack}
-  //     screenConfig={screenConfig}
-  //     parentDispatch={parentDispatch}
-  //     localeModule={localeModule}
-  //     pageTag={pageTag}
-  //   >
-  //     <AppPreview />
-  //   </AppLocalisationWrapper>
-  // );
+  }
+  return (
+    //production mode
+    <AppLocalisationWrapper
+      onSubmit={onSubmit}
+      back={back}
+      showBack={showBack}
+      screenConfig={screenConfig}
+      parentDispatch={parentDispatch}
+      localeModule={localeModule}
+      pageTag={pageTag}
+    >
+      <AppPreview />
+    </AppLocalisationWrapper>
+  );

Also applies to: 178-190

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

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

⚠️ Outside diff range comments (2)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/template_components/RegistrationComponents.js (2)

127-147: Use a semantic button instead of a clickable div

The CTA is a div with onClick; switch to for keyboard and screen-reader support.

-      <div
+      <button
+        type="button"
         style={{
           display: "inline-flex",
           alignItems: "center",
           justifyContent: "center",
           gap: "8px",
           padding: "8px 16px",
           background: "#FFFFFF",
           color: "rgba(244, 119, 56, 1)",
           borderRadius: "4px",
           cursor: "pointer",
           fontWeight: 400,
           fontSize: "16px",
           lineHeight: "100%",
-          border: "none",
+          border: "none"
         }}
         className="digit-search-action"
         onClick={props.onClick}
       >
         {props.addMember ? <CustomSVG.AddFilled width={"16px"} height={"16px"} fill={"#F47738"} /> : <EditIcon />}
         <span className="digit-search-text">{props?.label || "EDIT_LABEL"}</span>
-      </div>
+      </button>

As per coding guidelines.


214-219: Add missing keys to mapped fragments

Each mapped child should have a stable key to avoid React warnings.

-        {attributes.map((attr, index) => (
-          <React.Fragment>
+        {attributes.map((attr, index) => (
+          <React.Fragment key={index}>
             <span>{attr.value}</span>
             {index < attributes.length - 1 && <div style={styles.separator} />}
           </React.Fragment>
         ))}

♻️ Duplicate comments (21)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js (10)

17-20: Extract BodyPortal to shared util (duplication).

Same implementation exists in NavigationLogicWrapper; move to a shared module and import in both files.


22-63: Extract MdmsValueDropdown to shared component (duplication).

Identical logic to NavigationLogicWrapper; create one shared MdmsValueDropdown and reuse.


187-220: Consolidate field type predicates (duplication).

These helpers duplicate logic in NavigationLogicWrapper; extract to fieldTypeHelpers.js and import.


269-293: Extract serialize logic to shared utility (duplication).

serializeRule/buildFinalExpression mirror NavigationLogicWrapper; extract to a shared serializer with page-accessor param.


295-332: Extract tokenize to shared utility (duplication).

tokenize is duplicated; move to a packages/shared/utils/tokenizeExpression.js and import.


410-413: Remove redundant ternary; assign once.

Both branches return same object.

-        const jr =
-            rules.length > 0
-                ? { selectedPage: {}, selectedField: {}, comparisonType: {}, fieldValue: "", joiner: { code: "&&", name: "AND" } }
-                : { selectedPage: {}, selectedField: {}, comparisonType: {}, fieldValue: "", joiner: { code: "&&", name: "AND" } };
+        const jr = { selectedPage: {}, selectedField: {}, comparisonType: {}, fieldValue: "", joiner: { code: "&&", name: "AND" } };

513-525: A11y: use button or add keyboard/focus support.

Replace interactive div with button for Edit.

-                <div
-                    role="button"
-                    title={editLabel}
-                    aria-label={editLabel}
-                    onClick={() => onEdit(idx)}
-                    style={{ display: "inline-flex", alignItems: "center", cursor: "pointer" }}
-                >
+                <button
+                    type="button"
+                    title={editLabel}
+                    aria-label={editLabel}
+                    onClick={() => onEdit(idx)}
+                    style={{ display: "inline-flex", alignItems: "center", cursor: "pointer", background:"none", border:"none", padding:0 }}
+                >
                   {SVG?.Edit ? (
                     <SVG.Edit fill={"#C84C0E"} width={"1.1rem"} height={"1.1rem"} />
                   ) : (
                     <Button variation="secondary" label={editLabel} onClick={() => onEdit(idx)} />
                   )}
-                </div>
+                </button>

527-540: A11y: use button or add keyboard/focus support.

Replace interactive div with button for Delete.

-                <div
-                    role="button"
-                    title={deleteRuleLabel}
-                    aria-label={deleteRuleLabel}
-                    onClick={() => onDelete(idx)}
-                    style={{ display: "inline-flex", alignItems: "center", cursor: "pointer" }}
-                >
+                <button
+                    type="button"
+                    title={deleteRuleLabel}
+                    aria-label={deleteRuleLabel}
+                    onClick={() => onDelete(idx)}
+                    style={{ display: "inline-flex", alignItems: "center", cursor: "pointer", background:"none", border:"none", padding:0 }}
+                >
                   {SVG?.Delete ? (
                     <SVG.Delete fill={"#C84C0E"} width={"1.1rem"} height={"1.1rem"} />
                   ) : (
                     <Button variation="secondary" label={deleteRuleLabel} onClick={() => onDelete(idx)} />
                   )}
-                </div>
+                </button>

598-599: Use JSX children instead of children prop on PopUp.

Refactor to nested JSX; keeps API idiomatic.

-                        <PopUp
+                        <PopUp
                             className="digit-popup--fullscreen"
                             type={"default"}
                             heading={editLabel}
-                            children={[
-                                <div key="single-rule-editor" style={{ display: "grid", gap: "1rem" }}>
+                        >
+                            <div style={{ display: "grid", gap: "1rem" }}>
                                 {/* ...existing content... */}
-                                </div>,
-                            ]}
+                            </div>
                             onOverlayClick={discardAndCloseEditor}
                             onClose={discardAndCloseEditor}
                             equalWidthButtons={false}
                             footerChildren={[ /* unchanged */ ]}
-                        />
+                        </PopUp>

910-926: A11y + state consistency: make close control a button and avoid null string state.

Use semantic button with keyboard support and set error to "" consistently.

-                                                    {SVG?.Close ? (
+                                                    {SVG?.Close ? (
                                                         <SVG.Close
                                                             width={"1.1rem"}
                                                             height={"1.1rem"}
                                                             fill={"#7F1D1D"}
-                                                            onClick={() => {
-                                                                setGlobalFormError(null);
-                                                                setValidationStarted(false);
-                                                            }}
+                                                            role="button"
+                                                            tabIndex={0}
+                                                            onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); setGlobalFormError(""); setValidationStarted(false); } }}
+                                                            onClick={() => { setGlobalFormError(""); setValidationStarted(false); }}
                                                             style={{ cursor: "pointer" }}
                                                         />
                                                     ) : (
-                                                        <span
-                                                            style={{ cursor: "pointer", color: "#7F1D1D" }}
-                                                            onClick={() => {
-                                                                setGlobalFormError(null);
-                                                                setValidationStarted(false);
-                                                            }}
-                                                        >
-                                                            ×
-                                                        </span>
+                                                        <button
+                                                            type="button"
+                                                            aria-label={t("CLOSE") || "Close"}
+                                                            style={{ cursor: "pointer", color: "#7F1D1D", background:"none", border:"none", padding:0 }}
+                                                            onClick={() => { setGlobalFormError(""); setValidationStarted(false); }}
+                                                        >×</button>
                                                     )}
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js (11)

16-55: Extract MdmsValueDropdown to shared component (duplication).

Same code as in DependentFieldsWrapper; move to a shared module and import.


57-61: Extract BodyPortal to shared util (duplication).

DRY with DependentFieldsWrapper; share one SSR-safe portal.


235-272: Extract tokenize to shared utility (duplication with DependentFieldsWrapper).

Avoid two copies; create shared tokenizeExpression.js and import.


274-305: Extract serializeSingle/serializeAll to shared utility.

Mirror of DependentFieldsWrapper logic; centralize to keep expression DSL consistent.


137-185: Consolidate field-type helpers across wrappers.

isDobLike/isDatePickerNotDob/isStringLike/isCheckboxField/isNumericLike duplicate other file; extract to shared fieldTypeHelpers.js.


480-484: Avoid using logical OR for side effects.

Make sequential calls explicit.

-    const addCondition = (ruleIdx) =>
-        setValidationStarted(true) ||
-        setRules((prev) =>
-            prev.map((r, i) => (i === ruleIdx ? { ...r, conds: [...r.conds, initialEmptyCondition()] } : r))
-        );
+    const addCondition = (ruleIdx) => {
+        setValidationStarted(true);
+        setRules((prev) =>
+            prev.map((r, i) => (i === ruleIdx ? { ...r, conds: [...r.conds, initialEmptyCondition()] } : r))
+        );
+    };

617-630: A11y: convert Edit control to button.

Replace interactive div with semantic button.

-                <div
-                    role="button"
+                <button
+                    type="button"
                     title={editLabel}
                     aria-label={editLabel}
                     onClick={() => openEditor(idx)}
-                    style={{ display: "inline-flex", alignItems: "center", cursor: "pointer" }}
+                    style={{ display: "inline-flex", alignItems: "center", cursor: "pointer", background:"none", border:"none", padding:0 }}
                 >
                   {SVG?.Edit ? (
                     <SVG.Edit fill={"#C84C0E"} width={"1.1rem"} height={"1.1rem"} />
                   ) : (
                     <Button variation="secondary" label={editLabel} onClick={() => openEditor(idx)} />
                   )}
-                </div>
+                </button>

632-641: A11y: convert Delete control to button.

Replace interactive div with semantic button.

-                <div
-                    role="button"
+                <button
+                    type="button"
                     title={deleteRuleLabel}
                     aria-label={deleteRuleLabel}
                     onClick={() => deleteRuleFromList(idx)}
-                    style={{ display: "inline-flex", alignItems: "center", cursor: "pointer" }}
+                    style={{ display: "inline-flex", alignItems: "center", cursor: "pointer", background:"none", border:"none", padding:0 }}
                 >
                   <SVG.Delete fill={"#C84C0E"} width={"1.1rem"} height={"1.1rem"} />
-                </div>
+                </button>

689-691: Use JSX children instead of children prop on PopUp.

Refactor to nested JSX for readability/API consistency.

-                        <PopUp
+                        <PopUp
                             className="digit-popup--fullscreen"
                             type={"default"}
                             heading={editLabel}
-                            children={[
-                                <div key="single-rule-editor" style={{ display: "grid", gap: "1rem" }}>
+                        >
+                            <div style={{ display: "grid", gap: "1rem" }}>
                               {(() => { /* ...existing content... */ })()}
-                                </div>,
-                            ]}
+                            </div>
                             onOverlayClick={discardAndCloseEditor}
                             onClose={discardAndCloseEditor}
                             equalWidthButtons={false}
                             footerChildren={[ /* unchanged */ ]}
-                        />
+                        </PopUp>

949-966: A11y: make “Remove condition” control keyboard-accessible.

Use button or add tabIndex and onKeyDown for Enter/Space.

-                                <div
+                                <button
+                                    type="button"
                                     style={{
                                         marginLeft: "auto",
                                         display: "flex",
                                         alignItems: "center",
                                         gap: "0.25rem",
-                                        cursor: "pointer",
+                                        cursor: "pointer", background:"none", border:"none", padding:0
                                     }}
-                                    onClick={() => removeCondition(editorIndex, idx)}
+                                    onClick={() => removeCondition(editorIndex, idx)}
                                     title={removeConditionLabel}
                                     aria-label={removeConditionLabel}
-                                    role="button"
-                                >
+                                >
                                     <SVG.Delete fill={"#C84C0E"} width={"1.25rem"} height={"1.25rem"} />
                                     <span style={{ color: "#C84C0E", fontSize: "0.875rem", fontWeight: 500 }}>
                                         {removeConditionLabel}
                                     </span>
-                                </div>
+                                </button>

989-992: State consistency: avoid null for globalFormError.

Use empty string consistently to represent “no error”.

-                                            setGlobalFormError(null)
+                                            setGlobalFormError("")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fad6773 and 5e0a345.

📒 Files selected for processing (5)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/AppPreview.js (2 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js (1 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js (1 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/appConfigHelpers.js (4 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/template_components/RegistrationComponents.js (14 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

check

Files:

  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/appConfigHelpers.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/AppPreview.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/template_components/RegistrationComponents.js
🧠 Learnings (1)
📚 Learning: 2024-10-17T12:00:51.749Z
Learnt from: Ramkrishna-egov
PR: egovernments/DIGIT-Frontend#1545
File: health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/ActivityCard.js:30-35
Timestamp: 2024-10-17T12:00:51.749Z
Learning: In our codebase, the 'Card' component is commonly used by passing children via the 'children' prop.

Applied to files:

  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js
🧬 Code graph analysis (2)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js (1)
micro-ui/web/micro-ui-internals/packages/react-components/src/atoms/Dropdown.js (1)
  • Dropdown (94-281)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js (3)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js (5)
  • tenantId (23-23)
  • module (24-24)
  • list (26-37)
  • options (39-42)
  • selectedOption (45-49)
micro-ui/web/micro-ui-internals/packages/react-components/src/atoms/Dropdown.js (1)
  • Dropdown (94-281)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/useCustomT.js (2)
  • useCustomT (12-40)
  • useCustomT (12-40)
🪛 Biome (2.1.2)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/DependentFieldsWrapper.js

[error] 514-515: The elements with this role can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)


[error] 513-520: The HTML element with the interactive role "button" is not focusable.

A non-interactive HTML element that is not focusable may not be reachable for users that rely on keyboard navigation, even with an added role like "button".
Add a tabIndex attribute to make this element focusable.

(lint/a11y/useFocusableInteractive)


[error] 513-520: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 528-529: The elements with this role can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)


[error] 527-534: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 527-534: The HTML element with the interactive role "button" is not focusable.

A non-interactive HTML element that is not focusable may not be reachable for users that rely on keyboard navigation, even with an added role like "button".
Add a tabIndex attribute to make this element focusable.

(lint/a11y/useFocusableInteractive)


[error] 917-924: Static Elements should not be interactive.

To add interactivity such as a mouse or key event listener to a static element, give the element an appropriate role value.

(lint/a11y/noStaticElementInteractions)


[error] 917-924: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 598-598: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js

[error] 619-619: The elements with this role can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)


[error] 618-624: The HTML element with the interactive role "button" is not focusable.

A non-interactive HTML element that is not focusable may not be reachable for users that rely on keyboard navigation, even with an added role like "button".
Add a tabIndex attribute to make this element focusable.

(lint/a11y/useFocusableInteractive)


[error] 618-624: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 633-633: The elements with this role can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)


[error] 632-638: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 632-638: The HTML element with the interactive role "button" is not focusable.

A non-interactive HTML element that is not focusable may not be reachable for users that rely on keyboard navigation, even with an added role like "button".
Add a tabIndex attribute to make this element focusable.

(lint/a11y/useFocusableInteractive)


[error] 949-961: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)


[error] 960-960: The elements with this role can be changed to the following elements:

For examples and more information, see WAI-ARIA Roles

(lint/a11y/useSemanticElements)


[error] 949-961: The HTML element with the interactive role "button" is not focusable.

A non-interactive HTML element that is not focusable may not be reachable for users that rely on keyboard navigation, even with an added role like "button".
Add a tabIndex attribute to make this element focusable.

(lint/a11y/useFocusableInteractive)


[error] 689-689: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/template_components/RegistrationComponents.js

[error] 39-39: Alternative text title element cannot be empty

For accessibility purposes, SVGs should have an alternative text, provided via title element. If the svg element has role="img", you should add the aria-label or aria-labelledby attribute.

(lint/a11y/noSvgWithoutTitle)


[error] 39-39: Enforce to have the onClick mouse event with the onKeyUp, the onKeyDown, or the onKeyPress keyboard event.

Actions triggered using mouse events should have corresponding keyboard events to account for keyboard-only navigation.

(lint/a11y/useKeyWithClickEvents)

⏰ Context from checks skipped due to timeout of 10000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/appConfigHelpers.js (2)

181-182: LGTM! Consistent field handling.

The addition of prefixText and suffixText follows the established pattern for optional string fields in this function, defaulting to empty strings when not present.


287-288: LGTM! Symmetric transformation.

The addition of prefixText and suffixText in reverseRestructure correctly mirrors the implementation in restructure (lines 181-182), maintaining bidirectional consistency.

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/AppPreview.js (3)

140-141: Good: handle "textArea" alias.

Mapping textArea → textarea improves robustness for inconsistent configs.


235-237: Remove FieldV1 prefix/suffix verification
FieldV1 isn’t defined or imported in this package – confirm the intended component before checking prefix/suffix support.

Likely an incorrect or invalid review comment.


244-245: Confirm .required CSS scope

CSS for .required lives only in pages/employee styles (index.scss 1124: &.required::after, villageView.scss 75: .comment-label .required). Ensure these rules apply here (or aren’t loaded) so that adding .required to the field-pair container won’t produce unintended pseudo-elements or duplicate markers on checkbox fields that already use .required.

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/template_components/RegistrationComponents.js (1)

233-234: Hook primary/secondary buttons to passed callbacks
Replace the two no-op onClick handlers in HouseholdOverViewMemberCard with the passed-in callbacks, e.g.:

- onClick={() => {}}
+ onClick={props.primaryBtn?.onClick || (() => {})}

- onClick={() => {}}
+ onClick={props.secondaryBtn?.onClick || (() => {})}

Verify that upstream supplies primaryBtn.onClick and secondaryBtn.onClick where needed.

Comment on lines +519 to +541
const buildPayload = (rs) =>
rs
.map((r) => {
const name = r.targetPage?.code || r.targetPage?.name || "";
const page =
allPageOptions.find((p) => p.code === name) ||
currentTemplate.find((p) => p?.name === name);
const navigateType = page?.type === "template" ? "template" : "form";

return {
condition: serializeAll(r.conds),
navigateTo: {
name,
type: navigateType,
},
};
})
.filter((r) => r.condition && r.navigateTo.name);

const syncParent = (nextRules) => {
const payload = buildPayload(nextRules);
onConditionalNavigateChange?.(payload);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

Optional: centralize payload building and syncing.

buildPayload/syncParent are local; consider lifting to a shared helper to keep PATCH payloads uniform across editors.


🏁 Script executed:

#!/bin/bash
# Locate other payload builders to assess consolidation opportunities
rg -nP 'buildPayload|conditionalNavigateTo|PATCH_PAGE_CONDITIONAL_NAV' -g '!**/node_modules/**'

Length of output: 148


🏁 Script executed:

#!/bin/bash
# locate other instances of buildPayload and syncParent across the codebase
rg -nP --type js 'buildPayload|syncParent' -g '*' -g '!node_modules/**'

Length of output: 1105


Optional: centralize buildPayload and syncParent
Search across the codebase found no other occurrences of these helpers; centralization isn’t currently warranted unless similar logic is added elsewhere.

🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js
around lines 519 to 541: the reviewer noted buildPayload and syncParent appear
unused elsewhere so centralization isn’t necessary; no code change
required—leave these helpers inline for now, but if you later find duplicate
logic elsewhere, extract buildPayload and syncParent into a shared utility
(export them and update callers) and add unit tests for the shared function.

Comment on lines +985 to +995
<SVG.Close
width={"1.1rem"}
height={"1.1rem"}
fill={"#7F1D1D"}
onClick={() => {
setValidationStarted(false);
setGlobalFormError(null)
}}
tabIndex={0}
style={{ cursor: "pointer" }}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

A11y: add keyboard handler to the Close icon.

Has tabIndex but no keyboard activation; support Enter/Space.

                         <SVG.Close
                             width={"1.1rem"}
                             height={"1.1rem"}
                             fill={"#7F1D1D"}
-                            onClick={() => {
+                            onClick={() => {
                                 setValidationStarted(false);
-                                setGlobalFormError(null)
+                                setGlobalFormError("")
                             }}
+                            onKeyDown={(e) => {
+                                if (e.key === "Enter" || e.key === " ") {
+                                    e.preventDefault();
+                                    setValidationStarted(false);
+                                    setGlobalFormError("");
+                                }
+                            }}
                             tabIndex={0}
                             style={{ cursor: "pointer" }}
                         />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<SVG.Close
width={"1.1rem"}
height={"1.1rem"}
fill={"#7F1D1D"}
onClick={() => {
setValidationStarted(false);
setGlobalFormError(null)
}}
tabIndex={0}
style={{ cursor: "pointer" }}
/>
<SVG.Close
width={"1.1rem"}
height={"1.1rem"}
fill={"#7F1D1D"}
onClick={() => {
setValidationStarted(false);
setGlobalFormError("");
}}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
setValidationStarted(false);
setGlobalFormError("");
}
}}
tabIndex={0}
style={{ cursor: "pointer" }}
/>
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/NavigationLogicWrapper.js
around lines 985 to 995, the Close SVG has tabIndex but no keyboard activation;
add an onKeyDown handler that listens for Enter (key === "Enter") and Space (key
=== " " or key === "Spacebar") and invokes the same actions as onClick
(setValidationStarted(false); setGlobalFormError(null)), call
event.preventDefault() for Space to avoid scrolling, and ensure the element has
role="button" and a descriptive aria-label so assistive tech announces it
correctly.

allowCommentsAdditionAt: ["body"],
},
navigateTo: page?.navigateTo || {},
conditionalNavigateTo: page?.conditionalNavigateTo,
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider providing a default value or conditional inclusion.

Unlike navigateTo (line 226) which defaults to {}, conditionalNavigateTo will be undefined when not present. This inconsistency could cause issues if consuming code expects a consistent shape.

Consider one of these approaches:

Option 1: Provide a default value

-       conditionalNavigateTo: page?.conditionalNavigateTo,
+       conditionalNavigateTo: page?.conditionalNavigateTo || null,

Option 2: Conditionally include the property

+       ...(page?.conditionalNavigateTo && { conditionalNavigateTo: page.conditionalNavigateTo }),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
conditionalNavigateTo: page?.conditionalNavigateTo,
navigateTo: page?.navigateTo || {},
conditionalNavigateTo: page?.conditionalNavigateTo || null,
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/appConfigHelpers.js
around line 227, conditionalNavigateTo is left undefined when
page.conditionalNavigateTo is absent which is inconsistent with navigateTo (line
226) and can break consumers expecting a consistent shape; fix by either
supplying a default (make conditionalNavigateTo default to an empty object so
its shape always exists) or only include the conditionalNavigateTo property when
page.conditionalNavigateTo is defined (omit the key entirely when undefined);
prefer the default approach to match navigateTo and update the object
construction accordingly.

order: index + 1,
properties,
navigateTo: section?.navigateTo || {},
conditionalNavigateTo: section?.conditionalNavigateTo,
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider providing a default value or conditional inclusion.

Same concern as line 227: conditionalNavigateTo will be undefined when not present, while navigateTo (line 301) defaults to {}. For consistency and to avoid potential issues with undefined properties, consider applying the same fix as suggested for line 227.

🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/appConfigHelpers.js
around line 302, conditionalNavigateTo is being set to
section?.conditionalNavigateTo which can be undefined; make it consistent with
navigateTo by either providing a default (e.g., conditionalNavigateTo:
section?.conditionalNavigateTo || {}) or only including the
conditionalNavigateTo property when section?.conditionalNavigateTo is defined
(conditional property inclusion), so the result never unexpectedly contains
undefined.

const SearchBar = (props) => (
<div style={{ width: "100%" }}>
<FieldV1
style={{ width: "100vh" }}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix width unit on search input

Using width: "100vh" ties width to viewport height and can break layouts. Use percentage width.

-      style={{ width: "100vh" }}
+      style={{ width: "100%" }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
style={{ width: "100vh" }}
style={{ width: "100%" }}
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/template_components/RegistrationComponents.js
around line 27, the search input uses style={{ width: "100vh" }} which ties
width to viewport height; change the width unit to a percentage (for example
"100%") or another appropriate horizontal unit so the input scales with
container width instead of viewport height, e.g., replace "100vh" with "100%".

Comment on lines +175 to 179
display: "flex",
alignItems: "center",
minWidth: "100vh",
paddingBottom: "1rem",
}}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid horizontal scroll: replace minWidth 100vh

minWidth tied to viewport height causes overflow. Use responsive width.

-              minWidth: "100vh",
+              width: "100%",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
display: "flex",
alignItems: "center",
minWidth: "100vh",
paddingBottom: "1rem",
}}
display: "flex",
alignItems: "center",
width: "100%",
paddingBottom: "1rem",
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/template_components/RegistrationComponents.js
around lines 175 to 179, the style uses minWidth: "100vh" which ties width to
viewport height and causes horizontal overflow; replace that with a responsive
width such as width: "100%" or minWidth: "100%" (or remove minWidth and use
maxWidth with responsive breakpoints) so the component uses viewport width
instead of height and prevents horizontal scrolling.

Comment on lines +204 to +209
<TextButton
alignment={"flex-end"}
label={props.t(props.editIndividual?.label || "")}
hidden={props.editIndividual?.hidden}
onClick={() => console.log("Edit Individual")}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid console logs in UI interactions; wire through provided handler

Use provided editIndividual.onClick or a no-op.

-          onClick={() => console.log("Edit Individual")}
+          onClick={props.editIndividual?.onClick || (() => {})}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<TextButton
alignment={"flex-end"}
label={props.t(props.editIndividual?.label || "")}
hidden={props.editIndividual?.hidden}
onClick={() => console.log("Edit Individual")}
/>
<TextButton
alignment={"flex-end"}
label={props.t(props.editIndividual?.label || "")}
hidden={props.editIndividual?.hidden}
onClick={props.editIndividual?.onClick || (() => {})}
/>
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/template_components/RegistrationComponents.js
around lines 204-209, the TextButton onClick currently logs to console; replace
that with the provided handler by calling props.editIndividual.onClick if it
exists, otherwise call a no-op (e.g., () => {}), and pass that as the onClick
prop (ensure you guard for editIndividual being undefined before accessing
onClick).

secondaryBtn={secondaryBtn}
t={t}
/>
<HouseholdOverViewMemberCard name="Joseph Sergio" editIndividual={editIndividual} primaryBtn={primaryBtn} secondaryBtn={secondaryBtn} t={t} />
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid hard-coded member name

Use data/config-driven name or a translated placeholder.

-        <HouseholdOverViewMemberCard name="Joseph Sergio" editIndividual={editIndividual} primaryBtn={primaryBtn} secondaryBtn={secondaryBtn} t={t} />
+        <HouseholdOverViewMemberCard name={formatMap?.MemberName?.label ? t(formatMap.MemberName.label) : t?.("NAME") || "Name"} editIndividual={editIndividual} primaryBtn={primaryBtn} secondaryBtn={secondaryBtn} t={t} />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<HouseholdOverViewMemberCard name="Joseph Sergio" editIndividual={editIndividual} primaryBtn={primaryBtn} secondaryBtn={secondaryBtn} t={t} />
<HouseholdOverViewMemberCard
name={
formatMap?.MemberName?.label
? t(formatMap.MemberName.label)
: t?.("NAME") || "Name"
}
editIndividual={editIndividual}
primaryBtn={primaryBtn}
secondaryBtn={secondaryBtn}
t={t}
/>
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/template_components/RegistrationComponents.js
around line 372, the HouseholdOverViewMemberCard is rendered with a hard-coded
name "Joseph Sergio"; replace this literal with a data-driven value or
translation placeholder (e.g., member.name from the relevant prop/state or
t('member.namePlaceholder')) so the component receives the name from the
component props or mapping data and supports i18n/config-driven content; ensure
the parent passes the correct member object or fallback to a translated
placeholder when name is missing.

{finalTableHeading}
</h1>
<h1 style={{ fontWeight: "bold", marginBottom: "0.5rem", fontSize: "25px" }}>{finalTableHeading}</h1>

Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Heading levels inside sections

Multiple H1s inside components may hurt document outline. Consider h2/h3 for in-card headings.

Also applies to: 514-514

🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/template_components/RegistrationComponents.js
around lines 455 and 514, the components use H1 headings inside cards which
breaks the document outline; change those in-card H1 elements to appropriate
lower-level headings (H2 or H3) that reflect their position in the page
hierarchy, update any associated CSS classes or styles to preserve visual
appearance if needed, and run an accessibility/semantic check to ensure the
overall heading structure remains logical.

Comment on lines +547 to +548
return <BeneficiaryTableWrapper finalTableHeading={finalTableHeading} columns={columns} data={data} t={t} />;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Stub data/date formatting

The table uses static data and an English date string. Consider i18n-friendly date formatting and replacing stubs with real data or guarded placeholders.

Also applies to: 543-545

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

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

⚠️ Outside diff range comments (1)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js (1)

263-269: Type‑strict match may miss the active page (string vs number).

Use numeric comparison so “3.1” (string) matches 3.1 (number).

-      setCurrentScreen(parentState?.currentTemplate.filter((i) => i?.parent === findActiveParent)?.filter((i) => i?.order === currentStep));
+      setCurrentScreen(
+        parentState?.currentTemplate
+          .filter((i) => i?.parent === findActiveParent)
+          .filter((i) => Number(i?.order) === Number(currentStep))
+      );
♻️ Duplicate comments (2)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/appConfigHelpers.js (2)

227-227: Provide a default value for consistency.

The past review comment remains valid: conditionalNavigateTo can be undefined when page?.conditionalNavigateTo is absent, while navigateTo (line 226) defaults to {}. This inconsistency may break consuming code expecting a consistent shape.

Apply this diff to provide a default value:

-       conditionalNavigateTo: page?.conditionalNavigateTo,
+       conditionalNavigateTo: page?.conditionalNavigateTo || {},

302-302: Provide a default value for consistency.

The past review comment remains valid: conditionalNavigateTo can be undefined when section?.conditionalNavigateTo is absent, while navigateTo (line 301) defaults to {}. For consistency and to avoid issues with undefined properties, apply the same pattern as navigateTo.

Apply this diff to provide a default value:

-       conditionalNavigateTo: section?.conditionalNavigateTo,
+       conditionalNavigateTo: section?.conditionalNavigateTo || {},
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e0a345 and ddfe94c.

📒 Files selected for processing (2)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js (9 hunks)
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/appConfigHelpers.js (5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

check

Files:

  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/appConfigHelpers.js
  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js
🧠 Learnings (1)
📚 Learning: 2025-06-26T10:38:13.794Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js:326-345
Timestamp: 2025-06-26T10:38:13.794Z
Learning: In the DIGIT Frontend sandbox module, configuration data comes from MDMS (Master Data Management System) which provides reliable, well-structured data for UI components, reducing the likelihood of malformed configuration data that would require extensive error handling.

Applied to files:

  • health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js
🧬 Code graph analysis (1)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js (4)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/NewCampaignCreate/AppFeatures.js (4)
  • isCacheLoading (117-117)
  • Digit (75-75)
  • Digit (421-421)
  • reqCriteriaForm (97-115)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationTabLayer.js (1)
  • numberTabs (111-111)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationWrapper.js (1)
  • isLoadingAppConfigMdmsData (337-363)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/ImpelComponentWrapper.js (1)
  • isLoadingAppConfigMdmsData (13-25)
⏰ Context from checks skipped due to timeout of 10000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build application amd64
  • GitHub Check: Build application arm64
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (9)
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/appConfigHelpers.js (4)

181-182: LGTM! Consistent field additions.

The prefixText and suffixText fields with empty string defaults ensure a consistent shape for field cards, aligning with their usage in AppPreview components.


287-288: LGTM! Symmetric implementation.

The prefixText and suffixText fields mirror the implementation in restructure (lines 181-182), ensuring consistent bidirectional transformation with empty string defaults.


269-269: Verify field.order presence or default. Changing from fieldIndex + 1 to field?.order makes ordering explicit but will yield undefined if any field lacks an order property—potentially breaking downstream logic. Ensure every field sets order or apply a safe fallback.


299-299: Ensure section.order is always defined.

reverseRestructure maps sections with order: section.order (no fallback), and restructure sorts pages by page.order; if section.order is ever undefined, sorting or reverse logic may break. Confirm all sections in updatedData include a valid order property.

health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js (5)

243-251: Stepper active detection is robust (numeric comparison).

Looks good and matches decimal order semantics.


253-261: mainPagesCount correctly counts distinct integer parts per active tab.

Solid approach; O(n) over current tab pages.


488-489: Step advance after cache save depends on availableSteps scope.

Once availableSteps is tab‑scoped (see above), this will advance within the current tab as expected.


542-544: Propagating parentState and pageTag to ImpelComponentWrapper is correct.

No concerns; aligns with the wrappers introduced elsewhere.


93-94: High MDMS fetch limit (1000) risks large payloads
At AppConfigurationParentLayer.js:93 – fetching up to 1000 records can bloat the response and slow first paint. Implement pagination or tighter server‐side filters, and benchmark typical payload size and TTFB in UAT. If responses exceed ~1–2 MB or TTFB >300 ms, lower the limit.

Comment on lines +181 to +187
const currentTabPages = React.useMemo(() => {
const activeParent = numberTabs.find((j) => j.active)?.parent;
return (parentState?.currentTemplate || [])
.filter((i) => i.parent === activeParent)
.sort((a, b) => Number(a.order) - Number(b.order));
}, [parentState?.currentTemplate, numberTabs]);

Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

currentTabPages derivation: safe, but add a fallback active parent to avoid empty lists.

If no tab is marked active, fall back to the first parent to stabilize UI on initial renders.

-  const activeParent = numberTabs.find((j) => j.active)?.parent;
+  const activeParent = (numberTabs.find((j) => j.active) ?? numberTabs[0])?.parent;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const currentTabPages = React.useMemo(() => {
const activeParent = numberTabs.find((j) => j.active)?.parent;
return (parentState?.currentTemplate || [])
.filter((i) => i.parent === activeParent)
.sort((a, b) => Number(a.order) - Number(b.order));
}, [parentState?.currentTemplate, numberTabs]);
const currentTabPages = React.useMemo(() => {
const activeParent = (numberTabs.find((j) => j.active) ?? numberTabs[0])?.parent;
return (parentState?.currentTemplate || [])
.filter((i) => i.parent === activeParent)
.sort((a, b) => Number(a.order) - Number(b.order));
}, [parentState?.currentTemplate, numberTabs]);
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js
around lines 181 to 187, currentTabPages uses numberTabs.find(j =>
j.active)?.parent which can be undefined and yield empty lists; change the
derivation to pick a fallback parent when no active tab exists (e.g., const
activeParent = numberTabs.find(j => j.active)?.parent || numberTabs[0]?.parent)
and then use that activeParent in the filter; ensure numberTabs may be empty by
short-circuiting to an empty array for currentTabPages if no parent can be
determined.

Comment on lines +188 to +197
useEffect(() => {
const last = currentTabPages.length
? Number(currentTabPages[currentTabPages.length - 1].order)
: null;

const isLast = last != null && Math.abs(Number(currentStep) - last) < 1e-6;

window.dispatchEvent(new CustomEvent("lastButtonDisabled", { detail: isLast }));
}, [currentStep, currentTabPages]);

Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Global event dispatch is fine; ensure consumers tolerate rapid changes.

No blocker. Document the event name in a constants file to prevent drift across modules.

🤖 Prompt for AI Agents
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js
lines 188-197: the code dispatches a global CustomEvent using a hard-coded
string "lastButtonDisabled" which can drift across modules; create (or use
existing) shared event-name constant (e.g., LAST_BUTTON_DISABLED) in a
module-level constants file, export it, import it into this file and replace the
string literal with the constant when constructing the CustomEvent, and update
any consumers to import and use the same constant; also add a short JSDoc or
comment above the constant describing the event semantics so consumers tolerate
rapid changes.

Comment on lines +199 to +212
// Build the ordered list of valid steps once.
// 👉 Replace p.step / p.order / p.name with whatever your source-of-truth field is.
const availableSteps = React.useMemo(() => {
const raw = (parentState?.steps
|| parentState?.stepOrder
|| (parentState?.currentTemplate || []).map((p) => p?.step || p?.order || p?.name)
);

return (raw || [])
.map((x) => parseFloat(String(x)))
.filter((n) => Number.isFinite(n))
.sort((a, b) => a - b);
}, [parentState]);

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

availableSteps is built from ALL pages, but navigation/stepper are tab‑scoped → cross‑tab drift.

This can advance currentStep to a page in a different tab without switching the tab, yielding blank content. Build availableSteps from currentTabPages instead.

-  const availableSteps = React.useMemo(() => {
-    const raw = (parentState?.steps
-      || parentState?.stepOrder
-      || (parentState?.currentTemplate || []).map((p) => p?.step || p?.order || p?.name)
-    );
-    return (raw || [])
-      .map((x) => parseFloat(String(x)))
-      .filter((n) => Number.isFinite(n))
-      .sort((a, b) => a - b);
-  }, [parentState]);
+  const availableSteps = React.useMemo(() => {
+    return (currentTabPages || [])
+      .map((p) => parseFloat(String(p?.step ?? p?.order ?? p?.name)))
+      .filter((n) => Number.isFinite(n))
+      .sort((a, b) => a - b);
+  }, [currentTabPages]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Build the ordered list of valid steps once.
// 👉 Replace p.step / p.order / p.name with whatever your source-of-truth field is.
const availableSteps = React.useMemo(() => {
const raw = (parentState?.steps
|| parentState?.stepOrder
|| (parentState?.currentTemplate || []).map((p) => p?.step || p?.order || p?.name)
);
return (raw || [])
.map((x) => parseFloat(String(x)))
.filter((n) => Number.isFinite(n))
.sort((a, b) => a - b);
}, [parentState]);
// Build the ordered list of valid steps once.
// 👉 Replace p.step / p.order / p.name with whatever your source-of-truth field is.
const availableSteps = React.useMemo(() => {
return (currentTabPages || [])
.map((p) => parseFloat(String(p?.step ?? p?.order ?? p?.name)))
.filter((n) => Number.isFinite(n))
.sort((a, b) => a - b);
}, [currentTabPages]);
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js
around lines 199 to 212, availableSteps is currently computed from parentState.*
(all pages) which allows cross-tab steps to be chosen; change the source to the
tab-scoped pages (e.g. parentState.currentTabPages or whatever property holds
the pages for the active tab) so availableSteps is built only from the current
tab’s pages. Keep the same parsing/numbering/filtering/sorting logic but replace
the raw input with the tab-scoped array and update the useMemo dependency to
include the tab pages (instead of whole parentState) so navigation/stepper
cannot drift into other tabs.

Comment on lines +213 to +231
const round1 = (n) => Number(n.toFixed(1));

const nextStepFrom = (current) => {
const cur = Number(current);

// Prefer the next known step from the canonical list
if (availableSteps.length) {
const next = availableSteps.find((s) => s > cur + 1e-9);
if (next != null) return round1(next);
}

// Fallbacks if no canonical "next" exists:
// - if we're on an integer, try the nearest .1
// - otherwise jump to next integer
const frac10 = Math.round((cur - Math.floor(cur)) * 10);
if (frac10 === 0) return round1(cur + 0.1);
return Math.floor(cur) + 1;
};

Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Decimal stepping fallback assumes 1‑decimal granularity. Confirm policy.

If orders like 4.10/4.11 exist, rounding to 1 decimal alters semantics. Consider removing the decimal fallback once availableSteps is correctly tab‑scoped (it should always be non‑empty).

-  const round1 = (n) => Number(n.toFixed(1));
+  const round1 = (n) => n; // not used if availableSteps is always populated

If decimals >1 digit are possible, keep exact numbers and skip rounding.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const round1 = (n) => Number(n.toFixed(1));
const nextStepFrom = (current) => {
const cur = Number(current);
// Prefer the next known step from the canonical list
if (availableSteps.length) {
const next = availableSteps.find((s) => s > cur + 1e-9);
if (next != null) return round1(next);
}
// Fallbacks if no canonical "next" exists:
// - if we're on an integer, try the nearest .1
// - otherwise jump to next integer
const frac10 = Math.round((cur - Math.floor(cur)) * 10);
if (frac10 === 0) return round1(cur + 0.1);
return Math.floor(cur) + 1;
};
const round1 = (n) => n; // not used if availableSteps is always populated
const nextStepFrom = (current) => {
const cur = Number(current);
// Prefer the next known step from the canonical list
if (availableSteps.length) {
const next = availableSteps.find((s) => s > cur + 1e-9);
if (next != null) return round1(next);
}
// Fallbacks if no canonical "next" exists:
// - if we're on an integer, try the nearest .1
// - otherwise jump to next integer
const frac10 = Math.round((cur - Math.floor(cur)) * 10);
if (frac10 === 0) return round1(cur + 0.1);
return Math.floor(cur) + 1;
};

Comment on lines +296 to +304
...parentState?.actualTemplate?.actualTemplate,
version: parentState?.actualTemplate?.version + 1,
pages: reverseData,
}
: {
...parentState?.actualTemplate,
version: parentState?.actualTemplate?.version + 1,
pages: reverseData,
};
...parentState?.actualTemplate,
version: parentState?.actualTemplate?.version + 1,
pages: reverseData,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Possible shape mismatch when spreading actualTemplate.actualTemplate.

parentState.actualTemplate is set from template: cacheData?.filteredCache?.data. Spreading ...parentState?.actualTemplate?.actualTemplate assumes an inner object exists; otherwise fields may be lost. Use a safe fallback.

-            ...parentState?.actualTemplate?.actualTemplate,
+            ...(parentState?.actualTemplate?.actualTemplate || parentState?.actualTemplate),
             version: parentState?.actualTemplate?.version + 1,
             pages: reverseData,

Please confirm the intended shape for masterName updates.



🏁 Script executed:

#!/bin/bash
# Search for occurrences of actualTemplate in the target file
rg -n "actualTemplate" -C3 -- health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js

# Search for definition/declaration of parentState in the same file
rg -n "parentState" -C3 -- health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js

Length of output: 9878


Guard against undefined nested actualTemplate when spreading
Use a fallback to avoid dropping fields if actualTemplate.actualTemplate is missing:

-            ...parentState?.actualTemplate?.actualTemplate,
+            ...(parentState?.actualTemplate?.actualTemplate || parentState?.actualTemplate),
             version: parentState.actualTemplate.version + 1,
             pages: reverseData,

Confirm the intended structure for masterName updates.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js
around lines 296 to 304, the spread currently assumes
parentState?.actualTemplate?.actualTemplate exists and will drop fields when it
is undefined; change the spread to use a fallback object (e.g. spread
parentState?.actualTemplate?.actualTemplate ?? parentState?.actualTemplate ??
{}) so all fields are preserved, then set version to
parentState?.actualTemplate?.version + 1 and pages to reverseData; also
explicitly handle masterName according to the intended structure (either
preserve existing masterName from the chosen source or set it to a specific
value) to avoid accidental loss or incorrect updates.

Comment on lines +403 to +411
...parentState?.actualTemplate?.actualTemplate,
version: parentState?.actualTemplate?.version + 1,
pages: reverseData,
}
: {
...parentState?.actualTemplate,
version: parentState?.actualTemplate?.version + 1,
pages: reverseData,
};
...parentState?.actualTemplate,
version: parentState?.actualTemplate?.version + 1,
pages: reverseData,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Repeat of shape concern for tabChange path.

Mirror the safe spread to keep structures consistent.

-            ...parentState?.actualTemplate?.actualTemplate,
+            ...(parentState?.actualTemplate?.actualTemplate || parentState?.actualTemplate),
             version: parentState?.actualTemplate?.version + 1,
             pages: reverseData,

🏁 Script executed:

#!/bin/bash
set -e

# Locate the file
file=$(fd -g 'AppConfigurationParentLayer.js' -t f)
echo "Found file: $file"

# Print lines 380–420 for context
echo "---- snippet lines 380–420 ----"
sed -n '380,420p' "$file"

# Find all occurrences of actualTemplate to understand its usage
echo "---- occurrences of actualTemplate ----"
rg -n -C3 "actualTemplate" "$file"

Length of output: 5030


🏁 Script executed:

#!/bin/bash
set -e
file=$(fd -g 'AppConfigurationParentLayer.js' -t f)
echo "---- useReducer declaration/context ----"
rg -n -C3 "useReducer" "$file"
echo "---- cacheData usages ----"
rg -n -C3 "cacheData" "$file"

Length of output: 4964


🏁 Script executed:

#!/bin/bash
set -e
file=$(fd -g 'AppConfigurationParentLayer.js' -t f)
echo "---- dispatcher definition ----"
rg -n -C5 "function dispatcher" "$file" || rg -n -C5 "const dispatcher" "$file"

Length of output: 464


Fallback to outer actualTemplate when nested one is missing
In both reverseFormat blocks, replace

...parentState?.actualTemplate?.actualTemplate,

with

...(parentState?.actualTemplate?.actualTemplate || parentState?.actualTemplate),

to ensure metadata isn’t dropped when actualTemplate.actualTemplate is undefined.

🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js
around lines 403 to 411, the code spreads
parentState?.actualTemplate?.actualTemplate which can be undefined and drop
metadata; update the spread to fall back to parentState?.actualTemplate when
actualTemplate.actualTemplate is missing by using the logical fallback
(parentState?.actualTemplate?.actualTemplate || parentState?.actualTemplate) so
the version and pages are applied without losing metadata.

Comment on lines +493 to +503
const activeStep = stepper?.find((i) => i.active);
if (activeStep?.isFirst && isPreviousTabAvailable) {
tabStateDispatch({ key: "PREVIOUS_TAB" });
setCurrentStep(availableSteps[0] || 1);
return;
} else if (activeStep?.isFirst && !isPreviousTabAvailable) {
setShowToast({ key: "error", label: "CANNOT_GO_BACK" });
} else {
setCurrentStep((prev) => prevStepFrom(prev));
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Back navigation jumps to the global first step, not previous tab’s first page.

Compute the previous tab’s first step and navigate there.

-  if (activeStep?.isFirst && isPreviousTabAvailable) {
-    tabStateDispatch({ key: "PREVIOUS_TAB" });
-    setCurrentStep(availableSteps[0] || 1);
-    return;
-  } else if (activeStep?.isFirst && !isPreviousTabAvailable) {
+  if (activeStep?.isFirst && isPreviousTabAvailable) {
+    const activeIdx = numberTabs.findIndex((t) => t.active);
+    const prevParent = numberTabs[activeIdx - 1]?.parent;
+    const prevTabPages = (parentState?.currentTemplate || [])
+      .filter((p) => p?.parent === prevParent)
+      .sort((a, b) => Number(a.order) - Number(b.order));
+    tabStateDispatch({ key: "PREVIOUS_TAB" });
+    setCurrentStep(Number(prevTabPages?.[0]?.order) || 1);
+    return;
+  } else if (activeStep?.isFirst && !isPreviousTabAvailable) {
     setShowToast({ key: "error", label: "CANNOT_GO_BACK" });
   } else {
     setCurrentStep((prev) => prevStepFrom(prev));
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const activeStep = stepper?.find((i) => i.active);
if (activeStep?.isFirst && isPreviousTabAvailable) {
tabStateDispatch({ key: "PREVIOUS_TAB" });
setCurrentStep(availableSteps[0] || 1);
return;
} else if (activeStep?.isFirst && !isPreviousTabAvailable) {
setShowToast({ key: "error", label: "CANNOT_GO_BACK" });
} else {
setCurrentStep((prev) => prevStepFrom(prev));
}
};
const activeStep = stepper?.find((i) => i.active);
if (activeStep?.isFirst && isPreviousTabAvailable) {
const activeIdx = numberTabs.findIndex((t) => t.active);
const prevParent = numberTabs[activeIdx - 1]?.parent;
const prevTabPages = (parentState?.currentTemplate || [])
.filter((p) => p?.parent === prevParent)
.sort((a, b) => Number(a.order) - Number(b.order));
tabStateDispatch({ key: "PREVIOUS_TAB" });
setCurrentStep(Number(prevTabPages?.[0]?.order) || 1);
return;
} else if (activeStep?.isFirst && !isPreviousTabAvailable) {
setShowToast({ key: "error", label: "CANNOT_GO_BACK" });
} else {
setCurrentStep((prev) => prevStepFrom(prev));
}
};
🤖 Prompt for AI Agents
In
health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/appConfigurationRedesign/AppConfigurationParentLayer.js
around lines 493-503, the "go back" branch currently jumps to the global first
step (availableSteps[0] || 1); instead compute the previous tab's first step and
navigate to it. After dispatching PREVIOUS_TAB, determine which tab is now
active (or compute previousTabIndex from the current tab index), then find that
tab's first step ID from the steps/tabs data structure (e.g., filter
availableSteps or steps by tab identifier and take the first element), and call
setCurrentStep with that first-step ID (fallback to 1 only if no step found).
Keep the tabStateDispatch call and replace availableSteps[0] with the computed
previous-tab-first-step.

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.

1 participant