Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ const getFieldType = (field) => {
return "text";
case "number":
return "number";
case "textarea":
case "textArea":
return "textarea";
case "time":
return "time";
Expand Down Expand Up @@ -232,14 +232,16 @@ const AppPreview = ({ data = {}, selectedField, t }) => {
placeholder={t(field?.innerLabel) || ""}
populators={{
t: field?.isMdms ? null : t,
prefix: field?.prefixText,
suffix: field?.suffixText,
title: field?.label,
fieldPairClassName: `app-preview-field-pair ${
selectedField?.jsonPath && selectedField?.jsonPath === field?.jsonPath
? `app-preview-selected`
: selectedField?.id && selectedField?.id === field?.id
? `app-preview-selected`
: ``
}`,
} ${field?.["toArray.required"] && getFieldType(field) !== "custom" ? `required` : ``}`,
mdmsConfig: field?.isMdms
? {
moduleName: field?.schemaCode?.split(".")[0],
Expand All @@ -253,7 +255,6 @@ const AppPreview = ({ data = {}, selectedField, t }) => {
? renderField(field, t)
: null,
}}
required={getFieldType(field) === "custom" ? null : field?.["toArray.required"]}
type={getFieldType(field) === "button" || getFieldType(field) === "select" ? "custom" : getFieldType(field) || "text"}
value={field?.value === true ? "" : field?.value || ""}
disabled={field?.readOnly || false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,6 @@ const AppConfigurationParentRedesign = ({
const [localeModule, setLocaleModule] = useState(null);
const [changeLoader, setChangeLoader] = useState(false);

useEffect(() => {
if (currentStep === parentState?.currentTemplate?.length) {
const event = new CustomEvent("lastButtonDisabled", { detail: true });
window.dispatchEvent(event);
} else {
const event = new CustomEvent("lastButtonDisabled", { detail: false });
window.dispatchEvent(event);
}
}, [currentStep, parentState]);

useEffect(() => {
const handleResetStep = () => {
setCurrentStep(1);
Expand All @@ -100,7 +90,7 @@ const AppConfigurationParentRedesign = ({
const { isLoading: isLoadingAppConfigMdmsData, data: AppConfigMdmsData } = Digit.Hooks.useCustomMDMS(
Digit.ULBService.getCurrentTenantId(),
MODULE_CONSTANTS,
[{ name: fieldTypeMaster, limit: 100 }],
[{ name: fieldTypeMaster, limit: 1000 }],
{
cacheTime: Infinity,
staleTime: Infinity,
Expand Down Expand Up @@ -136,7 +126,7 @@ const AppConfigurationParentRedesign = ({
},
};

const { isLoading: isCacheLoading, data: cacheData, refetch: refetchCache, revalidate } = Digit.Hooks.useCustomAPIHook(reqCriteriaForm);
const { isLoading: isCacheLoading, data: cacheData, refetch: refetchCache } = Digit.Hooks.useCustomAPIHook(reqCriteriaForm);

const { mutate: updateMutate } = Digit.Hooks.campaign.useUpdateAppConfig(tenantId);

Expand Down Expand Up @@ -167,6 +157,7 @@ const AppConfigurationParentRedesign = ({
formId &&
AppConfigMdmsData?.[fieldTypeMaster]?.length > 0
) {

const fieldTypeMasterData = AppConfigMdmsData?.[fieldTypeMaster] || [];
const temp = restructure(formData?.data?.pages, fieldTypeMasterData, formData?.data);
parentDispatch({
Expand All @@ -187,19 +178,86 @@ const AppConfigurationParentRedesign = ({
);
}, [parentState?.currentTemplate]);

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]);

Comment on lines +181 to +187
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.

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]);

Comment on lines +188 to +197
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.


// 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]);

Comment on lines +199 to +212
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.

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;
};

Comment on lines +213 to +231
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;
};

const prevStepFrom = (current) => {
const cur = Number(current);
let prev = null;
for (const s of availableSteps) {
if (s < cur - 1e-9) prev = s; else break;
}
return prev != null ? round1(prev) : cur;
};

useEffect(() => {
setStepper(
(parentState?.currentTemplate || [])
?.filter((i) => i.parent === numberTabs.find((j) => j.active)?.parent)
.sort((a, b) => a.order - b.order)
?.map((k, j, t) => ({
name: k.name,
isLast: j === t.length - 1 ? true : false,
isFirst: j === 0 ? true : false,
active: j === currentStep - 1 ? true : false,
}))
currentTabPages.map((k, j, t) => ({
name: k.name,
isLast: j === t.length - 1,
isFirst: j === 0,
// active by exact order match (works for 4.1, 4.2, …)
active: Number(k.order) === Number(currentStep),
}))
);
}, [parentState?.currentTemplate, numberTabs, currentStep]);
}, [currentTabPages, currentStep]);

const mainPagesCount = React.useMemo(() => {
const ints = new Set();
for (const p of currentTabPages) {
const n = parseFloat(String(p?.order || p?.step || p?.name));
if (Number.isFinite(n)) ints.add(Math.floor(n));
}
return ints.size;
}, [currentTabPages]);

useEffect(() => {
if (variant === "app" && parentState?.currentTemplate?.length > 0 && currentStep && numberTabs?.length > 0) {
Expand All @@ -210,11 +268,13 @@ const AppConfigurationParentRedesign = ({
}
}, [parentState?.currentTemplate, currentStep, numberTabs]);


if (isCacheLoading || isLoadingAppConfigMdmsData || !parentState?.currentTemplate || parentState?.currentTemplate?.length === 0) {
return <Loader page={true} variant={"PageLoader"} />;
}

const submit = async (screenData, finalSubmit, tabChange) => {

parentDispatch({
key: "SETBACK",
data: screenData,
Expand All @@ -233,15 +293,15 @@ const AppConfigurationParentRedesign = ({
const reverseFormat =
cacheData && cacheData?.filteredCache?.data?.data
? {
...parentState?.actualTemplate?.actualTemplate,
version: parentState?.actualTemplate?.version + 1,
pages: reverseData,
}
...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,
};
Comment on lines +296 to +304
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.


const updatedFormData = { ...formData, data: reverseFormat };

Expand Down Expand Up @@ -340,15 +400,15 @@ const AppConfigurationParentRedesign = ({
const reverseFormat =
cacheData && cacheData?.filteredCache?.data?.data
? {
...parentState?.actualTemplate?.actualTemplate,
version: parentState?.actualTemplate?.version + 1,
pages: reverseData,
}
...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,
};
Comment on lines +403 to +411
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.


const updatedFormData = { ...formData, data: reverseFormat };

Expand Down Expand Up @@ -392,7 +452,6 @@ const AppConfigurationParentRedesign = ({
setShowToast({ key: "success", label: "APP_CONFIGURATION_SUCCESS" });
setChangeLoader(false);
revalidateForm();
revalidate();
},
}
);
Expand Down Expand Up @@ -426,21 +485,22 @@ const AppConfigurationParentRedesign = ({
},
}
);
setCurrentStep((prev) => prev + 1);
setCurrentStep((prev) => nextStepFrom(prev));
}
};

const back = () => {
if (stepper?.find((i) => i.active)?.isFirst && isPreviousTabAvailable) {
tabStateDispatch({ key: "PREVIOUS_TAB" });
setCurrentStep(1);
return;
} else if (stepper?.find((i) => i.active)?.isFirst && !isPreviousTabAvailable) {
setShowToast({ key: "error", label: "CANNOT_GO_BACK" });
} else {
setCurrentStep((prev) => prev - 1);
}
};
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));
}
};
Comment on lines +493 to +503
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.

if (changeLoader) {
return <Loader className="loader-center" page={true} variant={"Overlayloader"} loaderText={t("HCM_CHANGING_MODULE")} />;
}
Expand Down Expand Up @@ -479,7 +539,8 @@ const AppConfigurationParentRedesign = ({
parentDispatch={parentDispatch}
AppConfigMdmsData={AppConfigMdmsData}
localeModule={localeModule}
pageTag={`${t("CMN_PAGE")} ${currentStep} / ${stepper?.length}`}
parentState={parentState}
pageTag={`${t("CMN_PAGE")} ${currentStep} / ${mainPagesCount}`}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,14 +280,39 @@ const reducer = (state = initialState, action, updateLocalization) => {
},
],
};
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),
};
}
Comment on lines +283 to +306
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),
};
}

default:
return state;
}
};

const MODULE_CONSTANTS = "HCM-ADMIN-CONSOLE";

function AppConfigurationWrapper({ screenConfig, localeModule, pageTag }) {
function AppConfigurationWrapper({ screenConfig, localeModule, pageTag , parentState}) {
const useT = useCustomT();
const queryClient = useQueryClient();
const { locState, addMissingKey, updateLocalization, onSubmit, back, showBack, parentDispatch } = useAppLocalisationContext();
const [state, dispatch] = useReducer((state, action) => reducer(state, action, updateLocalization), initialState);
Expand All @@ -298,7 +323,7 @@ function AppConfigurationWrapper({ screenConfig, localeModule, pageTag }) {
const [popupData, setPopupData] = useState(null);
const [addFieldData, setAddFieldData] = useState(null);
const addFieldDataLabel = useMemo(() => {
return addFieldData?.label ? useCustomT(addFieldData?.label) : null;
return addFieldData?.label ? useT(addFieldData?.label) : null;
}, [addFieldData]);
const searchParams = new URLSearchParams(location.search);
const fieldMasterName = searchParams.get("fieldType");
Expand Down Expand Up @@ -554,7 +579,7 @@ function AppConfigurationWrapper({ screenConfig, localeModule, pageTag }) {
return (
<AppConfigContext.Provider value={{ state, dispatch, openAddFieldPopup }}>
{loading && <Loader page={true} variant={"OverlayLoader"} loaderText={t("SAVING_CONFIG_IN_SERVER")} />}
<AppPreview data={state?.screenData?.[0]} selectedField={state?.drawerField} t={useCustomT} />
<AppPreview data={state?.screenData?.[0]} selectedField={state?.drawerField} t={useT} />
<div className="appConfig-flex-action">
<Button
className="app-configure-action-button"
Expand Down Expand Up @@ -637,7 +662,7 @@ function AppConfigurationWrapper({ screenConfig, localeModule, pageTag }) {
</>
) : (
<DndProvider backend={HTML5Backend}>
<AppFieldScreenWrapper />
<AppFieldScreenWrapper parentState={parentState}/>
</DndProvider>
)}
</SidePanel>
Expand Down Expand Up @@ -727,7 +752,7 @@ function AppConfigurationWrapper({ screenConfig, localeModule, pageTag }) {
required={true}
type={"text"}
label={`${t("ADD_FIELD_LABEL")}`}
value={addFieldData?.label ? useCustomT(addFieldData?.label) : ""}
value={addFieldData?.label ? useT(addFieldData?.label) : ""}
config={{
step: "",
}}
Expand Down
Loading
Loading