Skip to content

Conversation

@jagankumar-egov
Copy link
Collaborator

@jagankumar-egov jagankumar-egov commented Jul 11, 2025

Choose the appropriate template for your PR:

Feature PR

Feature Request

JIRA ID

Module

Description

Related Issues


Bugfix PR

Bugfix Request

JIRA ID

Module

Description

Root Cause

Related Issues


Release PR

Summary by CodeRabbit

  • New Features

    • New Login/Sign‑Up v2 flow with carousel banners, sandbox header, login/signup selector, forgot‑organization tooltip, and embedded demo player; new product details page variant.
  • UI/UX Improvements

    • Expanded responsive layouts, privacy checkbox alignment, header/logo tweaks, updated product visuals, and five new SVG icons.
  • Behavioral Changes

    • Timestamp appended to auth URLs, product links include ?from=sandbox, inbox no longer pre‑filters by current user, privacy agreement removed from login.
  • Chores

    • Publish tag standardized; external stylesheet version bumped; Matomo analytics added.
  • Performance

    • Local storage compression introduced; API cache durations reduced.

Hari-egov and others added 30 commits May 25, 2025 12:53
* sandbox-ui-update

* ui-updates

* ui-update and old ui route fix

* ui update and old ui conifgs added to view
* privacy component alignment fix

* package 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: 6

♻️ Duplicate comments (6)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/ForgotOrganizationTooltip.js (3)

5-5: Remove unused prop from component signature.

onSelect isn’t used in this component.

-const ForgotOrganizationTooltip = ({ onSelect }) => {
+const ForgotOrganizationTooltip = () => {

43-51: Make Button self-closing and drop commented prop.

No children are rendered; prefer self-closing JSX and remove commented code.

-                <Button
-                        label={t(`SB_FORGOTORGANIZATION_TOOLTIP`)}
-                        variation={"link"}
-                        size={"small"}
-                        onClick={() => setShowTip((prev) => !prev)}
-                        // isSuffix={true}
-                        style={{ marginBottom: "0.5rem", paddingLeft: "0.2rem" }}
-                ></Button>
+                <Button
+                        label={t(`SB_FORGOTORGANIZATION_TOOLTIP`)}
+                        variation={"link"}
+                        size={"small"}
+                        onClick={() => setShowTip((prev) => !prev)}
+                        style={{ marginBottom: "0.5rem", paddingLeft: "0.2rem" }}
+                />

53-67: Move extensive inline styles to CSS and center-position the tooltip.

Inline styles reduce maintainability. Also, with left: 50% you likely want translateX(-50%) for centering.

Option A (preferred): extract to CSS

-                    <div
-                    style={{
-                        position: "absolute",
-                        bottom: "100%",
-                        left: "50%",
-                        transform: "translateY(-4px)",
-                        backgroundColor: "#0b4b66",
-                        color: "white",
-                        padding: "6px 10px",
-                        borderRadius: "4px",
-                        whiteSpace: "normal",
-                        wordBreak: "break-word",
-                        maxWidth: "20rem",
-                        zIndex: 1000,
-                        fontSize: "0.875rem"
-                    }}
-                    >
+                    <div className="forgot-organization-tooltip">

CSS (new):

/* forgot-organization-tooltip.css */
.forgot-organization-tooltip {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translate(-50%, -4px); /* center horizontally */
  background-color: #0b4b66;
  color: #fff;
  padding: 6px 10px;
  border-radius: 4px;
  white-space: normal;
  word-break: break-word;
  max-width: 20rem;
  z-index: 1000;
  font-size: 0.875rem;
}

Option B (if keeping inline styles): minimally fix centering

-                        transform: "translateY(-4px)",
+                        transform: "translate(-50%, -4px)",
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js (3)

1-1: Remove unused Fragment import and tighten imports.

Fragment is unused (you use React.Fragment). Also consider removing useRef if wrapperRef is not used for DOM operations.

-import React, { useState, useEffect, Fragment,useRef } from "react";
+import React, { useState, useEffect, useRef } from "react";

If you remove wrapperRef, then:

-import React, { useState, useEffect, useRef } from "react";
+import React, { useState, useEffect } from "react";

7-7: Drop unused ref if not leveraging DOM APIs.

wrapperRef is assigned but not used. Remove it and the ref prop to reduce noise.

-    const wrapperRef = useRef(null);
+    // (removed) wrapperRef

-            <div ref={wrapperRef} className="loginSignUpSelector" style={{ marginTop: topMargin }}>
+            <div className="loginSignUpSelector" style={{ marginTop: topMargin }}>

Also applies to: 45-45


48-55: Use self-closing syntax for Button elements.

No children are provided; prefer self-closing JSX.

-                        ></Button>
+                        />
-                        ></Button>
+                        />

Also applies to: 56-63

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab05ad1 and 1b7f446.

📒 Files selected for processing (2)
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/components/ForgotOrganizationTooltip.js (1 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit Configuration File

check

Files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/components/ForgotOrganizationTooltip.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js
🧠 Learnings (3)
📚 Learning: 2025-06-27T05:52:33.156Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/components/PrivacyComponent.js:0-0
Timestamp: 2025-06-27T05:52:33.156Z
Learning: The PrivacyComponent in `micro-ui/web/micro-ui-internals/packages/modules/core/src/components/PrivacyComponent.js` is mostly used in login and signup pages, especially in formcomposer where the UI is stable, making layout changes less likely to cause regressions.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js
📚 Learning: 2025-06-26T09:43:00.597Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js:24-24
Timestamp: 2025-06-26T09:43:00.597Z
Learning: In LoginSignupSelector component, the URL checking using window.location.href.includes("sandbox-ui/user/sign-up") is intentional for URL validation purposes, not navigation. The team prefers this approach over React Router's useLocation hook for specific URL checking needs.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js
📚 Learning: 2024-10-17T11:59:00.327Z
Learnt from: Ramkrishna-egov
PR: egovernments/DIGIT-Frontend#1545
File: health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/ActivityCard.js:1-1
Timestamp: 2024-10-17T11:59:00.327Z
Learning: In `ActivityCard.js`, `React.Fragment` is used in the code.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js
🧬 Code Graph Analysis (1)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/ForgotOrganizationTooltip.js (1)
micro-ui/web/micro-ui-internals/packages/react-components/src/atoms/Dropdown.js (1)
  • t (98-98)
⏰ 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 (1)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js (1)

15-16: No SSR guard needed at this time
This component only runs in the browser (and our tests use a JSDOM-like environment), so all direct window calls are safe. If you later introduce true server‐side rendering or another non-browser context, you can wrap each usage in:

const ctx = typeof window !== "undefined" ? window : undefined;
ctx?.location.replace();
ctx?.screen…
ctx?.addEventListener()

Comment on lines +11 to +28
useEffect(() => {
const computeTopMargin = () => {
if (
window.screen.availWidth <= 1366 ||
(window.screen.availHeight <= 768 &&
window.devicePixelRatio > 1.0)
) {
return "-0.1rem";
}
return "-2rem";
};

setTopMargin(computeTopMargin());

const handleResize = () => setTopMargin(computeTopMargin());
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Use viewport dimensions (innerWidth/innerHeight) and consider DRYing this logic.

screen.availWidth/availHeight are OS-level; UI often needs window.innerWidth/innerHeight. Also, the same logic exists in LoginSignupSelector; extract to a shared util/hook.

-        window.screen.availWidth <= 1366 ||
-        (window.screen.availHeight <= 768 &&
+        window.innerWidth <= 1366 ||
+        (window.innerHeight <= 768 &&
           window.devicePixelRatio > 1.0)

Suggestion: create a small shared hook:

// useResponsiveTopMargin.js
import { useEffect, useState } from "react";
export const useResponsiveTopMargin = (small = "-0.1rem", large = "-2rem") => {
  const [topMargin, setTopMargin] = useState(large);
  useEffect(() => {
    const compute = () =>
      window.innerWidth <= 1366 || (window.innerHeight <= 768 && window.devicePixelRatio > 1)
        ? small
        : large;
    setTopMargin(compute());
    const onResize = () => setTopMargin(compute());
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, [small, large]);
  return topMargin;
};
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/ForgotOrganizationTooltip.js
around lines 11 to 28, the component uses window.screen.availWidth/availHeight
(OS-level) and duplicates responsive logic found in LoginSignupSelector; replace
screen.* with window.innerWidth/innerHeight, extract the computeTopMargin logic
into a shared hook or util (e.g., useResponsiveTopMargin) that accepts
small/large margin values, update this component to call that hook, and update
LoginSignupSelector to reuse the same hook so the logic is DRY and uses viewport
dimensions; ensure the hook sets initial state, adds/removes resize listener,
and depends on the margin args.

};
}, []);
return (
<div ref={wrapperRef} className="loginSignUpSelector" style={{ position: "relative", marginTop: topMargin}}>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Rename container class to reflect the component.

className="loginSignUpSelector" looks copy-pasted; use a tooltip-specific class for clarity.

-            <div ref={wrapperRef} className="loginSignUpSelector" style={{ position: "relative", marginTop: topMargin}}>
+            <div ref={wrapperRef} className="forgotOrganizationTooltip" style={{ position: "relative", marginTop: topMargin}}>
📝 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 ref={wrapperRef} className="loginSignUpSelector" style={{ position: "relative", marginTop: topMargin}}>
<div ref={wrapperRef} className="forgotOrganizationTooltip" style={{ position: "relative", marginTop: topMargin}}>
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/ForgotOrganizationTooltip.js
around line 42, the container uses a generic className "loginSignUpSelector"
which appears copy-pasted; change it to a tooltip-specific class like
"forgotOrganizationTooltip" or "forgot-org-tooltip" to reflect the component,
update any related CSS/SCSS selectors to match the new class, and run a
repo-wide search to replace/remove other accidental uses of
"loginSignUpSelector" so styles and tests remain correct.

Comment on lines +43 to +50
<Button
label={t(`SB_FORGOTORGANIZATION_TOOLTIP`)}
variation={"link"}
size={"small"}
onClick={() => setShowTip((prev) => !prev)}
// isSuffix={true}
style={{ marginBottom: "0.5rem", paddingLeft: "0.2rem" }}
></Button>
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

Add basic accessibility attributes for the tooltip.

Improve screen-reader and keyboard support with ARIA and Escape-to-close handling.

-                <Button
+                <Button
                         label={t(`SB_FORGOTORGANIZATION_TOOLTIP`)}
                         variation={"link"}
                         size={"small"}
-                        onClick={() => setShowTip((prev) => !prev)}
+                        aria-expanded={showTip}
+                        aria-controls="forgot-org-tooltip"
+                        onClick={() => setShowTip((prev) => !prev)}
                         style={{ marginBottom: "0.5rem", paddingLeft: "0.2rem" }}
                 />
-                    <div
+                    <div
+                    id="forgot-org-tooltip"
+                    role="tooltip"

Optional: add key handler

-    useEffect(() => {
+    useEffect(() => {
+      const handleKeyDown = (e) => { if (e.key === "Escape") setShowTip(false); };
+      document.addEventListener("keydown", handleKeyDown);
+      return () => document.removeEventListener("keydown", handleKeyDown);
-    }, []);
+    }, []);

Also applies to: 53-69

import React, { useState, useEffect, Fragment,useRef } from "react";
import { useTranslation } from "react-i18next";
import { CheckBox, PopUp, Button, HeaderComponent } from "@egovernments/digit-ui-components";
const LoginSignupSelector = ({ onSelect, formData, control, formState, ...props }) => {
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

Make onSelect optional to avoid runtime errors.

If parent doesn’t pass onSelect, calling it will throw. Default to a no-op.

-const LoginSignupSelector = ({ onSelect, formData, control, formState, ...props }) => {
+const LoginSignupSelector = ({ onSelect = () => {}, formData, control, formState, ...props }) => {

No other changes required; your useEffect will keep working:

useEffect(() => {
  onSelect("check", isChecked);
}, [isChecked, onSelect]);

Also applies to: 11-13

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js
around lines 4 and also affecting lines 11-13, the prop onSelect is assumed to
exist and calling it can throw if the parent doesn't pass it; make onSelect
optional by providing a no-op default (e.g., default parameter in the function
signature or a fallback when destructuring) so the useEffect and other callers
can safely invoke onSelect without runtime errors.

Comment on lines +18 to +35
useEffect(() => {
const computeTopMargin = () => {
if (
window.screen.availWidth <= 1366 ||
(window.screen.availHeight <= 768 &&
window.devicePixelRatio > 1.0)
) {
return "-0.1rem";
}
return "-2rem";
};

setTopMargin(computeTopMargin());

const handleResize = () => setTopMargin(computeTopMargin());
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Deduplicate computeTopMargin logic (shared with ForgotOrganizationTooltip).

Extract to a small shared hook/util to keep behavior consistent and testable.

See suggested useResponsiveTopMargin hook in the other file’s comment; you can use it here to replace local state/effect.

Also applies to: 11-13

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js
around lines 18 to 35, the computeTopMargin logic is duplicated (also used in
ForgotOrganizationTooltip); extract that logic into a shared hook or util (e.g.,
useResponsiveTopMargin) in a common utilities/hooks folder, export it, then
replace the local useEffect/useState in this file with a call to the shared hook
(import it and use its returned topMargin), remove the duplicated
computeTopMargin function and event listener code here, and update tests/usage
to rely on the new hook to keep behavior consistent and testable.

window.location.replace(`/${window?.contextPath}/user/sign-up`);
};

const isSignupPage = window.location.href.includes("sandbox-ui/user/sign-up");
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Avoid hardcoding context path in URL check.

Use the same contextPath you employ for navigation to make this resilient across environments.

-    const isSignupPage = window.location.href.includes("sandbox-ui/user/sign-up");
+    const isSignupPage = window.location.href.includes(`/${window?.contextPath}/user/sign-up`);
📝 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 isSignupPage = window.location.href.includes("sandbox-ui/user/sign-up");
const isSignupPage = window.location.href.includes(`/${window?.contextPath}/user/sign-up`);
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js
around line 42, avoid hardcoding the context path in the URL check; replace the
literal "sandbox-ui/user/sign-up" with the same contextPath used for navigation
(e.g. check window.location.href.includes(`${contextPath}/user/sign-up`)),
importing or referencing the existing contextPath value the module uses for
navigation and providing a safe fallback (empty string) if it’s undefined.

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

🔭 Outside diff range comments (3)
micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss (3)

10-15: Conflicting margin declarations: margin-bottom is overridden by later margin: unset

margin: unset !important; wipes out the earlier margin-bottom: 1.5rem;. Reorder or remove the generic margin reset.

Apply this diff to preserve bottom spacing while still clearing others:

 .sandbox-module-card {
   min-height: 263px !important;
   width: 263px !important;
-  margin-bottom: 1.5rem;
   background-color: #fff;
-  padding: unset !important;
-  margin: unset !important;
+  padding: 0 !important;
+  margin: 0 !important;
+  margin-bottom: 1.5rem;
   position: relative;
 }

18-25: Redundant/contradictory width declarations on absolutely positioned button

You set width: 100% and later override with width: auto;. Also with both left and right set, the width will stretch automatically. Keep one approach.

 .sandbox-module-button {
   position: absolute;
-  width: 100%;
   bottom: 1.5rem;
   left: 1rem;
   right: 1rem;
   margin: auto;
-  width: auto;
 }

1129-1133: Invalid CSS: color value is quoted string

color: "#c84c0e"; is invalid; the quotes cause the declaration to be ignored.

 .highlight-sandbox {
-  color: "#c84c0e";
+  color: #c84c0e;
   /* or your specific orange color code */
 }
♻️ Duplicate comments (12)
micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss (12)

116-119: Add modern viewport fallbacks (svh) and sensible min constraints

Same concern previously noted: vh/vw alone can be unreliable on mobile dynamic viewports.

   height: 80vh !important;
+  height: 80svh !important;
   width: 30vw !important;
+  min-width: 320px !important;
   max-width: 500px !important;
   max-height: 650px !important;

149-151: Fixed 650×500 with !important is too rigid; use responsive constraints

This was raised earlier. Prefer min/max/clamp to avoid overflow on small screens and remove reliance on !important.

-    height: 650px !important;
-    width: 500px !important;
+    height: min(650px, 100svh);
+    width: min(500px, 100%);

231-239: Duplicate font-family, excessive top margin, and hardcoded color

You set font-family twice, use an 8rem top margin, and hardcode color.

 .digit-card-label.sandbox-custom-otp-subheader {
-  font-family: "Roboto";
-  margin-bottom: 3rem;
-  font-family: Roboto;
-  margin-top: 8rem !important;
-  color: #0b4b66 !important;
+  font-family: "Roboto";
+  margin-bottom: 3rem;
+  margin-top: 2rem !important; /* adjust via media queries as needed */
+  color: var(--digit-primary-color, #0B4B66) !important;
   text-align: center !important;
   font-size: 2rem !important;
   font-weight: 700 !important;
 }

268-314: Tooltip is hover-only and ignores reduced-motion; add keyboard focus and motion prefs

Ensure accessibility with focus-visible or focus-within, and respect prefers-reduced-motion.

-      &:hover .infotext {
+      &:hover .infotext,
+      &:focus-within .infotext {
         visibility: visible;
         opacity: 1;
       }
       .infotext {
         visibility: hidden;
         opacity: 0;
         transition: opacity 0.3s;
+        pointer-events: none;
+        @media (prefers-reduced-motion: reduce) {
+          transition: none;
+        }

320-329: Avoid all: unset; use targeted resets and replace deprecated grid-gap

all: unset removes layout-critical properties. Also use gap instead of grid-gap.

-.sandbox-signup-form .digit-field .label-field-wrapper {
-  all: unset !important;
-  grid-gap: .25rem !important;
+.sandbox-signup-form .digit-field .label-field-wrapper {
+  display: grid; /* keep intended layout */
+  gap: .25rem !important;
+  margin: 0;
+  padding: 0;
+  border: 0;
+  background: transparent;

341-344: Use CSS variables/tokens instead of hardcoded colors

Replace #0B4B66 with a theme variable for maintainability and theming.

-      color: #0B4B66;
+      color: var(--digit-primary-color, #0B4B66);
-      font-size: 2rem;
+      font-size: 2rem; /* keep */

616-620: Typo in comment: duplicate word “components”

Fix the comment for clarity.

-  /* Ensure spacing between the two components components  */
+  /* Ensure spacing between the two components */

1392-1419: Don’t blanket-suppress hover and remove all affordances; preserve focus-visible

The .no-hover rule nukes interaction feedback and relies on many !important flags. Keep keyboard focus visible.

 .no-hover:hover {
   background-color: inherit !important;
   color: inherit !important;
   opacity: 1 !important;
   transform: none !important;
   box-shadow: none !important;
   text-decoration: none !important;
   font-size: inherit !important;
   font-weight: inherit !important;
 }
+/* Preserve keyboard focus visibility */
+.no-hover:focus-visible {
+  outline: 2px solid var(--digit-focus-color, #0B4B66);
+  outline-offset: 2px;
+}

1484-1492: Large horizontal padding may overflow small screens

Make padding responsive to avoid cramped layouts on narrow devices.

 .role-block {
   gap: 1rem;
   width: 100%;
-  padding: 0rem 8rem;
+  padding: 0 8rem;
+}
+@media (max-width: 768px) {
+  .role-block {
+    padding: 0 1rem;
+  }
 }

1553-1573: Underline comment mismatches behavior; also use theme vars for color

Width is fixed at 8rem but comment claims “match text width”. Either match text or correct the comment; also swap hardcoded color.

 .cs-title::after {
   content: "";
   display: block;
-  width: 8rem; /* match text width */
+  width: 100%; /* match text width */
+  max-width: 8rem;
   height: 4px;
-  background-color: #215B73;
+  background-color: var(--digit-secondary-color, #215B73);
   margin: 6px auto 0 auto; /* center under text */
 }

1711-1716: Conflicting height and min-height on iframe

min-height: 40rem overrides height: 500px. Keep the intended constraint.

 .wt-iframe {
   width: 100%;
-  height: 500px;
   border: none;
   min-height: 40rem;
 }

If you need an upper cap, add max-height instead.


1724-1753: Breadcrumbs: add focus-visible and current-page state for a11y

Add a visible focus style and style the current page (aria-current="page").

 .nav-breadcrumb a {
   color: #e67e22 !important;
   text-decoration: none;
   transition: color 0.2s ease;
 }
 .nav-breadcrumb a:hover {
   color: #d35400;
 }
+.nav-breadcrumb a:focus-visible {
+  outline: 2px solid var(--digit-focus-color, #0B4B66);
+  outline-offset: 2px;
+  text-decoration: underline;
+}
+.nav-breadcrumb a[aria-current="page"] {
+  color: #333;
+  font-weight: 600;
+  text-decoration: none;
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8fa3d31 and affefda.

📒 Files selected for processing (1)
  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss (10 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-06-26T10:24:08.628Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss:1642-1662
Timestamp: 2025-06-26T10:24:08.628Z
Learning: In the sandbox SCSS file, tab styling with margin-bottom and border changes that cause layout shifts are intentional design requirements per updated UI specifications, not bugs to be fixed.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
📚 Learning: 2024-10-26T15:11:49.938Z
Learnt from: nabeelmd-eGov
PR: egovernments/DIGIT-Frontend#1680
File: health/micro-ui/web/micro-ui-internals/example/src/UICustomizations.js:1032-1094
Timestamp: 2024-10-26T15:11:49.938Z
Learning: In the DIGIT-Frontend project, when reviewing the `UICustomizations.js` file, avoid suggesting refactoring of repetitive rendering logic in switch cases into helper functions, unless explicitly requested.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
📚 Learning: 2025-06-26T10:16:36.004Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/css/src/components/Carousel.scss:0-0
Timestamp: 2025-06-26T10:16:36.004Z
Learning: In the DIGIT-Frontend codebase, signup-container CSS classes cannot be namespaced under parent classes due to existing style constraints and requirements.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
⏰ 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 (4)
micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss (4)

349-354: LGTM: login/signup selector layout utility

Concise and clear flex alignment utility; no issues.


406-408: LGTM: submit bar uses theme token

Good use of theme() for color. No changes needed.


1369-1390: LGTM: new icon/image helpers

Clear and neutral layout utilities for headers and images. No functional issues spotted.


121-123: Drop obsolete IE flex fallback
After checking the repo, there’s no browserslist config targeting IE10/11, so the -ms-flex-pack rule can be safely removed:

-  -ms-flex-pack: justify;
   justify-content: space-evenly;

Comment on lines +89 to +93
.SandboxLogoAlignCenter {
align-items: center;
justify-content: center;
display: flex;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Class naming is inconsistent with project conventions

SandboxLogoAlignCenter is PascalCase, while most classes here use kebab/camel. Unless there’s a precedent, prefer a consistent convention (e.g., .sandbox-logo-align-center).

If renaming is acceptable across usages, I can generate a repo-wide rename patch. Confirm the preferred convention for this module.


🏁 Script executed:

#!/bin/bash
echo "PascalCase class definitions in css/src:"
rg -n "^\s*\.[A-Z][A-Za-z0-9]*" -tscss micro-ui/web/micro-ui-internals/packages/css/src

echo
echo "kebab-case class definitions in css/src:"
rg -n "^\s*\.[a-z0-9][a-z0-9-]*" -tscss micro-ui/web/micro-ui-internals/packages/css/src

Length of output: 428


🏁 Script executed:

#!/bin/bash
# Search for PascalCase class selectors in SCSS files under css/src
rg -n "^\s*\.[A-Z][A-Za-z0-9]*" micro-ui/web/micro-ui-internals/packages/css/src --glob "*.scss"

echo
# Search for kebab-case class selectors in SCSS files under css/src
rg -n "^\s*\.[a-z0-9][a-z0-9-]*" micro-ui/web/micro-ui-internals/packages/css/src --glob "*.scss"

Length of output: 226990


Align class naming to kebab-case in sandbox.scss
The sandbox.scss file predominantly uses kebab-case selectors (e.g. .sandbox-module-container, .sandbox-success-signup, .sandbox-url-footer), but at line 89 we introduce a PascalCase selector:

File: micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
Lines: 89-93

-.SandboxLogoAlignCenter {
+.sandbox-logo-align-center {
   align-items: center;
   justify-content: center;
   display: flex;
 }

Please rename .SandboxLogoAlignCenter (and its usages in markup/JSX) to .sandbox-logo-align-center to match the existing pattern.

📝 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
.SandboxLogoAlignCenter {
align-items: center;
justify-content: center;
display: flex;
}
.sandbox-logo-align-center {
align-items: center;
justify-content: center;
display: flex;
}
🤖 Prompt for AI Agents
In micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
around lines 89 to 93, the selector `.SandboxLogoAlignCenter` uses PascalCase
while the file uses kebab-case; rename the class to `.sandbox-logo-align-center`
and update every usage across markup/JSX/components to match the new kebab-case
name (search/replace occurrences, update tests or snapshots, and rebuild to
confirm no missing references).

* Update package.json

* Update index.html
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: 2

♻️ Duplicate comments (1)
micro-ui/web/public/index.html (1)

19-39: Fix invalid HTML (style tag outside head) + correct brittle centering + add font fallbacks

This repeats a previously raised issue:

  • The style block lives outside , which is invalid HTML and can yield inconsistent behavior.
  • The tooltip is centered using left: 50% with margin-left: -10.3rem against a width of 7.5rem, which is incorrect and extremely brittle. Use transform: translateX(-50%) instead.
  • Add font fallbacks for Roboto.

Apply this diff to remove the misplaced style block:

-<style>
-   .digit-header-content.label .info-icon .infotext {
-    position: absolute;
-    text-align: center;
-    font-family: Roboto;
-    font-style: normal;
-    font-weight: 400;
-    line-height: 1.37rem;
-    visibility: hidden;
-    width: 7.5rem;
-    background-color: #555;
-    color: #fff;
-    padding: .5rem 0;
-    z-index: 1;
-    bottom: 100%;
-    left: 50%;
-    margin-left: -10.3rem;
-    border-radius: .375rem;
-}
-
-</style>

Insert the corrected style inside the head (just before ):

<style>
  .digit-header-content.label .info-icon .infotext {
    position: absolute;
    text-align: center;
    font-family: "Roboto", Arial, sans-serif;
    font-style: normal;
    font-weight: 400;
    line-height: 1.37rem;
    visibility: hidden;
    width: 7.5rem;
    background-color: #555;
    color: #fff;
    padding: .5rem 0;
    z-index: 1;
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    border-radius: .375rem;
  }
</style>

Optional: If this tooltip is used across multiple entry points, move it into a shared stylesheet to avoid duplication.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between affefda and 8fe2200.

⛔ Files ignored due to path filters (1)
  • micro-ui/web/micro-ui-internals/packages/css/package.json is excluded by !**/*.json
📒 Files selected for processing (1)
  • micro-ui/web/public/index.html (3 hunks)
⏰ 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 (1)
micro-ui/web/public/index.html (1)

56-56: LGTM: newline at EOF

No issues; keeping a trailing newline is standard.

Comment on lines +10 to 11
<link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected].38/dist/index.css" />
<link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected]/dist/index.css" />
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Upgrade to [email protected]: verify downstream styling and consider SRI

  • Good to see exact version pinning. Please verify no breaking visual regressions across Login/Signup V2 and header/tooltip components due to CSS changes between 1.8.26 → 1.8.38.
  • Consider adding Subresource Integrity (integrity, crossorigin) for external CSS to mitigate supply-chain risks, and decide if a self-hosted fallback is needed for environments with restricted CDN access.

Would you like me to enumerate notable changes between these versions and generate a quick visual regression checklist?


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="micro-ui/web/public/index.html"
echo "=== File check: $file ==="
if [ -f "$file" ]; then
  sed -n '1,200p' "$file"
else
  echo "File not found: $file"
fi

echo
echo "=== Search for digit-ui CSS usage ==="
rg -n "@egovernments/digit-ui-css|digit-ui-components-css" || true

echo
echo "=== Search for Login/Signup/Header/Tooltip occurrences (case-insensitive) in micro-ui ==="
rg -n "(login|signup|header|tooltip)" micro-ui || true

echo
echo "=== Compute SRI (sha384) for the referenced CSS URLs ==="
urls=(
"https://unpkg.com/@egovernments/[email protected]/dist/index.css"
"https://unpkg.com/@egovernments/[email protected]/dist/index.css"
)
for url in "${urls[@]}"; do
  echo
  echo "URL: $url"
  if command -v curl >/dev/null 2>&1; then
    status=$(curl -s -o /dev/null -w "%{http_code}" -I -L "$url" || echo "000")
    echo "HTTP status: $status"
    if [ "$status" != "200" ]; then
      echo "Failed to fetch (non-200). Skipping hash."
      continue
    fi
    if command -v openssl >/dev/null 2>&1; then
      hash=$(curl -sSL "$url" | openssl dgst -sha384 -binary | openssl base64 -A || true)
    else
      hash=$(curl -sSL "$url" | python -c 'import sys,hashlib,base64; data=sys.stdin.buffer.read(); print(base64.b64encode(hashlib.sha384(data).digest()).decode())' || true)
    fi
  elif command -v wget >/dev/null 2>&1; then
    status=$(wget --server-response --spider -q "$url" 2>&1 | sed -n '1p' | awk '{print $2}' || echo "000")
    echo "HTTP status (wget): $status"
    if [ "$status" != "200" ]; then
      echo "Failed to fetch (non-200). Skipping hash."
      continue
    fi
    if command -v openssl >/dev/null 2>&1; then
      hash=$(wget -q -O - "$url" | openssl dgst -sha384 -binary | openssl base64 -A || true)
    else
      hash=$(wget -q -O - "$url" | python -c 'import sys,hashlib,base64; data=sys.stdin.buffer.read(); print(base64.b64encode(hashlib.sha384(data).digest()).decode())' || true)
    fi
  else
    echo "No curl/wget available; cannot fetch"
    continue
  fi

  if [ -z "${hash:-}" ]; then
    echo "Failed to compute hash"
  else
    echo "Suggested integrity attribute: sha384-$hash"
    echo "Suggested crossorigin attribute: crossorigin=\"anonymous\""
  fi
done

Length of output: 158972


Upgrade to [email protected] — SRI computed; please add SRI and verify UI

Short: I verified micro-ui/web/public/index.html and fetched the two CSS files (HTTP 200). I computed SHA-384 SRI hashes — add integrity + crossorigin and run visual regression checks (Login/SignUp V2, header/tooltip). Also reconcile other files that reference older digit-ui-css versions.

Files to action

  • micro-ui/web/public/index.html — add integrity & crossorigin to the two external links (suggested diff below).
  • Visual-check (priority):
    • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/login.scss
    • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/tooltip.scss
    • micro-ui/web/micro-ui-internals/packages/css/src/index.scss (imports tooltip)
    • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/** and Login-v2/**
  • Check/align other references (may be docs/examples or intentional):
    • micro-ui/web/micro-ui-internals/packages/modules/sandbox/README.md (references @egovernments/[email protected])
    • micro-ui/web/micro-ui-internals/packages/modules/workbench/README.md (1.2.114)
    • micro-ui/web/micro-ui-internals/packages/css/README.md (mentions 1.8.14)
    • micro-ui/web/micro-ui-internals/example/public/index.html (example commented versions)

Suggested replacement (update these two tags in micro-ui/web/public/index.html)

Notes

  • Both URLs returned 200 and the above integrity hashes were computed from the current CDN payloads — if you re-publish assets the hash will change.
  • Decide whether to add a self-hosted fallback (e.g., local copy served when CDN fails) for restricted environments.

Would you like me to enumerate notable changes between 1.8.26 → 1.8.38 and generate a short visual-regression checklist for Login/Signup V2 and header/tooltip?

🤖 Prompt for AI Agents
In micro-ui/web/public/index.html around lines 10-11, add SRI integrity and
crossorigin attributes to the two external CSS link tags using the provided
SHA-384 hashes (add
integrity="sha384-ujgPEZxME0vI6gml3R9fLnJBJJtuX7crZuyXk7dSmmGevF1r5ZO0O7ty3PBJPFkC"
crossorigin="anonymous" for [email protected] and
integrity="sha384-2EfdlCC1EigMYJ59qOhrK2CfvNH6nzQpPONIej1kQQqdSp4/kpLXsw9Zm3/y7XnC"
crossorigin="anonymous" for [email protected]), then run visual
regression checks focused on Login/SignUp V2 and header/tooltip by verifying
styles in
micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/login.scss,
tooltip.scss, index.scss and the SignUp-v2/Login-v2 pages under modules/core;
finally search and update or document other references to older digit-ui-css
versions in the listed README and example files (sandbox README, workbench
README, css README, example public/index.html) and decide whether to add a local
fallback for restricted environments.

Comment on lines 19 to 39
<style>
.digit-header-content.label .info-icon .infotext {
position: absolute;
text-align: center;
font-family: Roboto;
font-style: normal;
font-weight: 400;
line-height: 1.37rem;
visibility: hidden;
width: 7.5rem;
background-color: #555;
color: #fff;
padding: .5rem 0;
z-index: 1;
bottom: 100%;
left: 50%;
margin-left: -10.3rem;
border-radius: .375rem;
}

</style>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Accessibility nit: ensure the tooltip is operable and announced

If this tooltip is triggered via hover/focus, ensure the HTML for the trigger and tooltip include appropriate roles/ARIA (e.g., role="tooltip", aria-describedby on trigger) and that it’s reachable via keyboard. Consider motion-safe positioning and a larger z-index context if inside complex headers.

Happy to propose an a11y-compliant pattern aligned with your component library.

🤖 Prompt for AI Agents
In micro-ui/web/public/index.html around lines 19 to 39, the tooltip CSS alone
does not make the tooltip accessible or keyboard-operable; update the markup and
behavior so the trigger element is focusable and references the tooltip with
aria-describedby, give the tooltip role="tooltip", and ensure visibility is
controlled for both :hover and :focus (and via JS for keyboard/assistive tech)
so it becomes visible and announced when focused; also ensure aria-hidden is
toggled appropriately when showing/hiding, use prefers-reduced-motion-safe
positioning adjustments, and increase z-index so the tooltip is above complex
header stacking contexts.

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 (3)
micro-ui/web/micro-ui-internals/packages/libraries/src/services/atoms/ApiCacheService.js (3)

74-76: getSettingByServiceUrl fails for “access/v1/actions” entry

The default config contains a key "access/v1/actions", but getSettingByServiceUrl only uses the first path segment (e.g., "access"), which won’t match and will likely cause a TypeError downstream when serviceSetting is undefined.

Harden the URL parsing and try a multi-segment match before falling back:

-  getSettingByServiceUrl: (serviceUrl, moduleName) => {
-    return getSetting(serviceUrl.split("/")[1], moduleName);
-  },
+  getSettingByServiceUrl: (serviceUrl, moduleName) => {
+    const parts = (serviceUrl || "").split("/").filter(Boolean); // e.g., ['access','v1','actions']
+    // Try 3-part (e.g., 'access/v1/actions'), then 2-part, then 1-part
+    const candidates = [
+      parts.slice(0, 3).join("/"),
+      parts.slice(0, 2).join("/"),
+      parts[0],
+    ].filter(Boolean);
+    for (const name of candidates) {
+      const s = getSetting(name, moduleName);
+      if (typeof s?.cacheTimeInSecs !== "undefined") return s;
+    }
+    // Fallback to first segment if nothing matched
+    return getSetting(parts[0], moduleName);
+  },

51-69: Null-deref risk: serviceSetting can be undefined

If a serviceName isn’t found, serviceSetting is undefined and the next line will throw when accessing serviceSetting.cacheTimeInSecs.

Defensive fix:

-  const serviceSetting = setting.find((item) => item.serviceName === serviceName);
-  const responseSetting = {
-    cacheTimeInSecs: serviceSetting.cacheTimeInSecs,
-    debounceTimeInMS: serviceSetting.debounceTimeInMS || 100,
-  };
+  const serviceSetting = setting.find((item) => item.serviceName === serviceName);
+  const responseSetting = {
+    cacheTimeInSecs: serviceSetting?.cacheTimeInSecs ?? 0,
+    debounceTimeInMS: serviceSetting?.debounceTimeInMS ?? 100,
+  };

70-77: Expose clearAllCache to align with component calls

Login-v2 and SignUp-v2 call window.Digit.ApiCacheService.clearAllCache(), but this API isn’t exported here. Add a no-op-safe method so those calls don’t throw, and optionally let it reset persisted settings.

 export const ApiCacheService = {
   saveSetting: (setting) => {
     PersistantStorage.set(storageKey, setting || defaultApiCachingSettings);
   },
+  clearAllCache: () => {
+    // No centralized cache here; reset to defaults so future reads start from defaults.
+    PersistantStorage.set(storageKey, defaultApiCachingSettings);
+  },
   getSettingByServiceUrl: (serviceUrl, moduleName) => {
     return getSetting(serviceUrl.split("/")[1], moduleName);
   },
 };
♻️ Duplicate comments (13)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js (6)

81-83: Fix tenantId fallback: current code always uses "DEFAULT"
This was already flagged in earlier reviews; reverse the OR.

-        "tenantId":"DEFAULT"|| stateCode,
+        "tenantId": stateCode || "DEFAULT",

3-7: Remove duplicate router imports; import hooks in a single statement
This matches the pattern already flagged in SignUp-v2.

-import { Route, Switch, useRouteMatch } from "react-router-dom";
+import { Route, Switch, useRouteMatch, useHistory, useLocation } from "react-router-dom";
 import { SignUpConfig as defaultSignUpConfig  } from "./config-v2";
 import Login from "./signUp";
-import { useHistory, useLocation } from "react-router-dom";

96-114: Tighten select handler and logging
Nit: spacing/variable names and clearer log context improve readability. Also, validate response shape.

-      select: (response) => {
+      select: (response) => {
         try {
-          const fetchedBanners =response?.MdmsRes?.sandbox?.BannerImages?.[0]?.bannerImages;
+          const fetchedBanners = response?.MdmsRes?.sandbox?.BannerImages?.[0]?.bannerImages;
           if (Array.isArray(fetchedBanners) && fetchedBanners.length > 0) {
             return fetchedBanners;
           }
           return null;
         } catch (error) {
-          console.error("Error processing MDMS data from useCustomAPIHook:", error);
+          console.error("Error processing banner images from MDMS:", error);
           return null;
         }
       },

138-146: Use strict equality and simplify the loading check
Align with prior feedback; also keep spacing consistent.

-  useEffect(() => {
-    if(isLoading == false && mdmsData?.config)
-    {  
-      setSignUpConfig(mdmsData?.config)
-    }else{
-      setSignUpConfig(defaultSignUpConfig)
-    }
-  },[mdmsData, isLoading])
+  useEffect(() => {
+    if (!isLoading && mdmsData?.config) {  
+      setSignUpConfig(mdmsData.config);
+    } else {
+      setSignUpConfig(defaultSignUpConfig);
+    }
+  }, [mdmsData, isLoading]);

Also remove the commented line at Line 138 to keep the code clean.


150-165: finalConfig ignores SignUpConfig state; use state to reflect MDMS overrides
As noted before, using defaultSignUpConfig makes the MDMS-based update ineffective.

-   const finalConfig = useMemo(() => {
-    if (loadingimages) {
-      return defaultSignUpConfig;
-    }
-    if (mdmsBannerImages) {
-      return [
-        {
-          ...defaultSignUpConfig[0],
-          bannerImages: mdmsBannerImages,
-        },
-      ];
-    }
-    return defaultSignUpConfig;
-  }, [loadingimages, mdmsBannerImages]);
+  const finalConfig = useMemo(() => {
+    const base = SignUpConfig || defaultSignUpConfig;
+    if (loadingimages) return base;
+    if (mdmsBannerImages) {
+      return [{ ...base[0], bannerImages: mdmsBannerImages }];
+    }
+    return base;
+  }, [loadingimages, mdmsBannerImages, SignUpConfig]);

182-184: Include MDMS config loading in Loader condition
Show loader until both banners and config are ready.

-  if (loadingimages) {
+  if (loadingimages || isLoading) {
     return <Loader />;
   }
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js (7)

79-81: Fix tenantId fallback: current code always uses "DEFAULT"
Reverse the OR to prefer stateCode.

-        "tenantId": "DEFAULT" || stateCode,
+        "tenantId": stateCode || "DEFAULT",

3-7: Remove duplicate router imports; import hooks in a single statement
Already flagged earlier; same fix applies here.

-import { Route, Switch, useRouteMatch } from "react-router-dom";
+import { Route, Switch, useRouteMatch, useHistory, useLocation } from "react-router-dom";
 import { SignUpConfig as defaultSignUpConfig  } from "./config";
 import Login from "./signUp";
-import { useHistory, useLocation } from "react-router-dom";

13-21: Remove unused store fetch (moduleCode/useStore) or add TODO
The store data is computed but unused; remove to avoid unnecessary work.

-  const moduleCode = ["privacy-policy"];
-  const language = Digit.StoreData.getCurrentLanguage();
-  const modulePrefix = "digit";
-  const { data: store } = Digit.Services.useStore({
-    stateCode,
-    moduleCode,
-    language,
-    modulePrefix
-  });

136-136: Remove commented-out legacy state logic
Keep the file free of commented code for clarity.

-  // let SignUpConfig = mdmsData?.config ? mdmsData?.config : defaultSignUpConfig;

138-144: Use strict equality and simplify condition
Replace loose equality.

-  useEffect(() => {
-    if(isLoading == false && mdmsData?.config)
-    {  
-      setSignUpConfig(mdmsData?.config)
-    }else{
-      setSignUpConfig(defaultSignUpConfig)
-    }
-  },[mdmsData, isLoading])
+  useEffect(() => {
+    if (!isLoading && mdmsData?.config) {  
+      setSignUpConfig(mdmsData.config);
+    } else {
+      setSignUpConfig(defaultSignUpConfig);
+    }
+  }, [mdmsData, isLoading]);

146-161: finalConfig should use SignUpConfig state (not default)
Mirror prior advice; otherwise MDMS-driven config is ignored.

-   const finalConfig = useMemo(() => {
-      
-      if (loadingimages) {
-        return defaultSignUpConfig;
-      }
-      if (mdmsBannerImages) {
-        return [
-          {
-            ...defaultSignUpConfig[0],
-            bannerImages: mdmsBannerImages,
-          },
-        ];
-      }
-      return defaultSignUpConfig;
-  
-    }, [loadingimages, mdmsBannerImages]);
+  const finalConfig = useMemo(() => {
+    const base = SignUpConfig || defaultSignUpConfig;
+    if (loadingimages) return base;
+    if (mdmsBannerImages) {
+      return [{ ...base[0], bannerImages: mdmsBannerImages }];
+    }
+    return base;
+  }, [loadingimages, mdmsBannerImages, SignUpConfig]);

178-180: Include MDMS config loading in Loader condition
Show loader while either resource is loading.

-    if (loadingimages) {
+    if (loadingimages || isLoading) {
       return <Loader />;
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8fe2200 and 1d4d805.

📒 Files selected for processing (3)
  • micro-ui/web/micro-ui-internals/packages/libraries/src/services/atoms/ApiCacheService.js (3 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js (1 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit Configuration File

check

Files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js
  • micro-ui/web/micro-ui-internals/packages/libraries/src/services/atoms/ApiCacheService.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js
🧠 Learnings (8)
📚 Learning: 2025-06-27T05:52:33.156Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/components/PrivacyComponent.js:0-0
Timestamp: 2025-06-27T05:52:33.156Z
Learning: The PrivacyComponent in `micro-ui/web/micro-ui-internals/packages/modules/core/src/components/PrivacyComponent.js` is mostly used in login and signup pages, especially in formcomposer where the UI is stable, making layout changes less likely to cause regressions.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js
📚 Learning: 2025-06-26T09:43:00.597Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js:24-24
Timestamp: 2025-06-26T09:43:00.597Z
Learning: In LoginSignupSelector component, the URL checking using window.location.href.includes("sandbox-ui/user/sign-up") is intentional for URL validation purposes, not navigation. The team prefers this approach over React Router's useLocation hook for specific URL checking needs.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js
📚 Learning: 2025-06-04T04:08:48.234Z
Learnt from: jagankumar-egov
PR: egovernments/DIGIT-Frontend#2488
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login/login.js:152-152
Timestamp: 2025-06-04T04:08:48.234Z
Learning: In the employee login component (micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login/login.js), prefer using a `defaultTenant` variable to store `Digit.ULBService.getStateId()` instead of calling the function multiple times inline in the `defaultValue` object. This improves code readability and avoids function call duplication.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js
📚 Learning: 2024-10-08T20:11:07.772Z
Learnt from: siddhant-nawale-egov
PR: egovernments/DIGIT-Frontend#876
File: micro-ui/web/micro-ui-internals/packages/modules/hcm-microplanning/src/Module.js:13-40
Timestamp: 2024-10-08T20:11:07.772Z
Learning: Error handling for the `Digit.Services.useStore` method is managed internally and does not require explicit external error checks.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js
📚 Learning: 2024-11-07T11:02:33.520Z
Learnt from: rachana-egov
PR: egovernments/DIGIT-Frontend#1770
File: health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/pages/employee/viewVillage.js:320-322
Timestamp: 2024-11-07T11:02:33.520Z
Learning: In `health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/pages/employee/viewVillage.js`, the `data?.additionalFields` object is guaranteed to be defined, so checking for its existence before accessing its keys is unnecessary.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js
📚 Learning: 2025-01-02T04:41:15.957Z
Learnt from: jagankumar-egov
PR: egovernments/DIGIT-Frontend#2089
File: micro-ui/web/micro-ui-internals/packages/modules/workbench/src/pages/employee/MDMSAddV2.js:111-111
Timestamp: 2025-01-02T04:41:15.957Z
Learning: In micro-ui/web/micro-ui-internals/packages/modules/workbench/src/pages/employee/MDMSAddV2.js, always use "mdms[0].uniqueIdentifier" for the jsonPath instead of differentiating between "SOR" and other masters.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js
📚 Learning: 2024-06-14T14:10:20.359Z
Learnt from: siddhant-nawale-egov
PR: egovernments/DIGIT-Frontend#876
File: micro-ui/web/micro-ui-internals/packages/modules/hcm-microplanning/src/components/CustomScaleControl.js:3-39
Timestamp: 2024-06-14T14:10:20.359Z
Learning: The `CustomScaleControl` component in the `micro-ui/web/micro-ui-internals/packages/modules/hcm-microplanning/src/components/CustomScaleControl.js` file should not include a "loading map" message as there is another component handling this functionality to avoid redundancy.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js
📚 Learning: 2025-06-26T10:17:02.717Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.js:0-0
Timestamp: 2025-06-26T10:17:02.717Z
Learning: In the DIGIT Frontend project, banner images and similar configuration data should be managed through MDMS (Master Data Management Service) rather than hardcoded arrays or separate config files, as indicated by the user Hari-egov.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js
🧬 Code Graph Analysis (2)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js (2)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/config.js (2)
  • SignUpConfig (1-110)
  • SignUpConfig (1-110)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js (2)
  • Digit (66-66)
  • Login (9-162)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js (2)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/config-v2.js (2)
  • SignUpConfig (1-121)
  • SignUpConfig (1-121)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js (3)
  • Digit (82-82)
  • history (17-17)
  • Login (10-168)
⏰ 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 (3)
micro-ui/web/micro-ui-internals/packages/libraries/src/services/atoms/ApiCacheService.js (1)

6-7: Localization cache to 1 hour looks reasonable

Keeping localization cached for an hour is a good balance for UX and network savings.

micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js (1)

27-59: Storage purge: manual audit required for preserved keys
Our automated scan didn’t detect any other calls to Digit.LocalStorage/SessionStorage or native localStorage/sessionStorage getters/setters, but absence of matches isn’t proof of absence. Please perform a manual audit to ensure no other critical keys—such as language preferences, telemetry consent flags, feature-toggle states or sandbox-specific tokens—are being stored and would be lost by this clear-and-restore logic. Update the preserveKeys array as needed to include any additional keys you identify.

micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js (1)

26-74: Storage purge: verify preserve list suffices for signup flow
Confirm no other critical keys (e.g., language, feature flags) are being wiped for SignUp.

Reuse the scan script from the Login-v2 comment to enumerate keys used across the codebase and adjust preserveKeys accordingly.

Comment on lines +18 to 21
cacheTimeInSecs: 0,
// cacheTimeInSecs: 3600,
debounceTimeInMS: 100,
moduleSettings: [
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

🛠️ Refactor suggestion

Disabling MDMS cache entirely can thrash MDMS and slow down UX

cacheTimeInSecs: 0 forces a network call on every read. For banner/config-driven pages, that can degrade performance and hammer MDMS. Prefer a short-but-nonzero TTL in production, with 0 only for local/dev.

Example improvement (env-gated):

-    cacheTimeInSecs: 0,
-    // cacheTimeInSecs: 3600,
+    cacheTimeInSecs: process.env.NODE_ENV === "production" ? 300 : 0,
+    // If needed, override via ApiCacheService.saveSetting() from app init.
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/libraries/src/services/atoms/ApiCacheService.js
around lines 18-21, the config currently sets cacheTimeInSecs: 0 which forces a
network call on every read; change this to use a short-but-nonzero TTL in
production and 0 only for local/dev by reading an env-guarded value (e.g.,
process.env.MDMS_CACHE_TTL or NODE_ENV check) and falling back to a sensible
default (eg 60–3600 seconds) so production uses caching while development can
disable it.

Comment on lines 29 to 33
serviceName: "mdms-v2",
cacheTimeInSecs: 3600,
cacheTimeInSecs: 0,
// cacheTimeInSecs: 3600,
debounceTimeInMS: 100,
moduleSettings: [
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

🛠️ Refactor suggestion

Same concern for mdms-v2: avoid cache=0 in production

Apply the same nonzero TTL guidance to mdms-v2 for consistency and to reduce load.

-    cacheTimeInSecs: 0,
-    // cacheTimeInSecs: 3600,
+    cacheTimeInSecs: process.env.NODE_ENV === "production" ? 300 : 0,
+    // cacheTimeInSecs: 3600,
📝 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
serviceName: "mdms-v2",
cacheTimeInSecs: 3600,
cacheTimeInSecs: 0,
// cacheTimeInSecs: 3600,
debounceTimeInMS: 100,
moduleSettings: [
serviceName: "mdms-v2",
- cacheTimeInSecs: 0,
cacheTimeInSecs: process.env.NODE_ENV === "production" ? 300 : 0,
// cacheTimeInSecs: 3600,
debounceTimeInMS: 100,
moduleSettings: [
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/libraries/src/services/atoms/ApiCacheService.js
around lines 29 to 33, the mdms-v2 entry sets cacheTimeInSecs to 0 which
disables caching in production; change it to a nonzero TTL (e.g., 3600 or a
value from a shared config/env var) to avoid cache=0 in production, remove or
update the commented-out example as needed, and ensure the value is driven by
environment/config so different environments can override it.

Comment on lines +14 to +22
const moduleCode = ["privacy-policy"];
const language = Digit.StoreData.getCurrentLanguage();
const modulePrefix = "digit";
const { data: store } = Digit.Services.useStore({
stateCode,
moduleCode,
language,
modulePrefix
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Remove unused store fetch (moduleCode/useStore) or add TODO
The fetched store is not used; drop it to reduce network and render work.

-  const moduleCode = ["privacy-policy"];
-  const language = Digit.StoreData.getCurrentLanguage();
-  const modulePrefix = "digit";
-  const { data: store } = Digit.Services.useStore({
-    stateCode,
-    moduleCode,
-    language,
-    modulePrefix
-  });

If you plan to use it later, add a TODO instead.

📝 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 moduleCode = ["privacy-policy"];
const language = Digit.StoreData.getCurrentLanguage();
const modulePrefix = "digit";
const { data: store } = Digit.Services.useStore({
stateCode,
moduleCode,
language,
modulePrefix
});
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js
around lines 14 to 22, the call to Digit.Services.useStore with
moduleCode/language/modulePrefix fetches a store that is never used; either
remove the moduleCode const and the useStore call entirely to avoid unnecessary
network/render work, or keep the call but add a clear TODO comment above it
explaining why the store fetch is retained for future use so reviewers know it’s
intentional.

Comment on lines +65 to +69
// Clear API cache service
if (window.Digit?.ApiCacheService) {
window.Digit.ApiCacheService.clearAllCache();
}

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

⚠️ Potential issue

Guard against missing ApiCacheService.clearAllCache to avoid runtime error
ApiCacheService in this repo doesn’t export clearAllCache by default. Either implement it (preferred) or guard the call.

-    if (window.Digit?.ApiCacheService) {
-      window.Digit.ApiCacheService.clearAllCache();
-    }
+    if (typeof window.Digit?.ApiCacheService?.clearAllCache === "function") {
+      window.Digit.ApiCacheService.clearAllCache();
+    }
📝 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
// Clear API cache service
if (window.Digit?.ApiCacheService) {
window.Digit.ApiCacheService.clearAllCache();
}
// Clear API cache service
if (typeof window.Digit?.ApiCacheService?.clearAllCache === "function") {
window.Digit.ApiCacheService.clearAllCache();
}
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js
around lines 65 to 69, the code assumes
window.Digit.ApiCacheService.clearAllCache exists and will throw if it is
undefined; either add a clearAllCache export on ApiCacheService implementation
or guard the call here by checking that
window.Digit?.ApiCacheService?.clearAllCache is a function before invoking it;
update the implementation if you opt to add the method to ApiCacheService so it
performs the intended cache clearing and export it, or if guarding, call it only
when present to avoid runtime errors.

Comment on lines +117 to +127
// Timestamp handling
useEffect(() => {
const query = new URLSearchParams(location.search);
if (!query.get("ts")) {
const ts = Date.now();
history.replace({
pathname: location.pathname,
search: `?ts=${ts}`
});
}
}, [location, history]);
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

⚠️ Potential issue

Preserve existing query params when appending ts
Current logic drops existing params. Merge ts into the existing query instead.

-      const query = new URLSearchParams(location.search);
-      if (!query.get("ts")) {
-        const ts = Date.now();
-        history.replace({
-          pathname: location.pathname,
-          search: `?ts=${ts}`
-        });
-      }
+      const query = new URLSearchParams(location.search);
+      if (!query.get("ts")) {
+        query.set("ts", String(Date.now()));
+        history.replace({
+          pathname: location.pathname,
+          search: `?${query.toString()}`
+        });
+      }
📝 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
// Timestamp handling
useEffect(() => {
const query = new URLSearchParams(location.search);
if (!query.get("ts")) {
const ts = Date.now();
history.replace({
pathname: location.pathname,
search: `?ts=${ts}`
});
}
}, [location, history]);
// Timestamp handling
useEffect(() => {
const query = new URLSearchParams(location.search);
if (!query.get("ts")) {
query.set("ts", String(Date.now()));
history.replace({
pathname: location.pathname,
search: `?${query.toString()}`
});
}
}, [location, history]);
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js
around lines 117 to 127, the useEffect that appends a ts query parameter
currently discards any existing query params; update it to parse the existing
URLSearchParams from location.search, set/overwrite the "ts" param on that same
object, and then call history.replace with the pathname and the serialized
updated search so all other params are preserved; also use location.search (or
the existing search string) in the effect dependency array to avoid unnecessary
reruns and ensure the new search string is used when building the replace call.

Comment on lines +65 to +67
if (window.Digit?.ApiCacheService) {
window.Digit.ApiCacheService.clearAllCache();
}
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

⚠️ Potential issue

Guard against missing ApiCacheService.clearAllCache to avoid runtime error
Same as Login-v2; make the call safe.

-    if (window.Digit?.ApiCacheService) {
-      window.Digit.ApiCacheService.clearAllCache();
-    }
+    if (typeof window.Digit?.ApiCacheService?.clearAllCache === "function") {
+      window.Digit.ApiCacheService.clearAllCache();
+    }
📝 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
if (window.Digit?.ApiCacheService) {
window.Digit.ApiCacheService.clearAllCache();
}
if (typeof window.Digit?.ApiCacheService?.clearAllCache === "function") {
window.Digit.ApiCacheService.clearAllCache();
}
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js
around lines 65 to 67, the code calls
window.Digit.ApiCacheService.clearAllCache() without confirming the method
exists, which can throw if ApiCacheService or clearAllCache is missing; update
the guard to check that window.Digit?.ApiCacheService?.clearAllCache is a
function (e.g., typeof ... === "function") before invoking it so the call is
safe at runtime.

Comment on lines +116 to +125
useEffect(() => {
const query = new URLSearchParams(location.search);
if (!query.get("ts")) {
const ts = Date.now();
history.replace({
pathname: location.pathname,
search: `?ts=${ts}`
});
}
}, [location, history]);
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

Preserve existing query params when appending ts
Don’t discard other query params.

-      const query = new URLSearchParams(location.search);
-      if (!query.get("ts")) {
-        const ts = Date.now();
-        history.replace({
-          pathname: location.pathname,
-          search: `?ts=${ts}`
-        });
-      }
+      const query = new URLSearchParams(location.search);
+      if (!query.get("ts")) {
+        query.set("ts", String(Date.now()));
+        history.replace({
+          pathname: location.pathname,
+          search: `?${query.toString()}`
+        });
+      }
📝 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
useEffect(() => {
const query = new URLSearchParams(location.search);
if (!query.get("ts")) {
const ts = Date.now();
history.replace({
pathname: location.pathname,
search: `?ts=${ts}`
});
}
}, [location, history]);
useEffect(() => {
const query = new URLSearchParams(location.search);
if (!query.get("ts")) {
query.set("ts", String(Date.now()));
history.replace({
pathname: location.pathname,
search: `?${query.toString()}`
});
}
}, [location, history]);
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/index.js
around lines 116 to 125, the effect currently overwrites the URL search string
when adding the ts param and thus discards any existing query parameters;
instead, build a URLSearchParams from location.search, set or append the ts
value on that same params object, and call history.replace with
location.pathname and the updated params.toString() so that all existing query
params are preserved while adding the ts.

* Update package.json

* Update index.html

* pgr inbox default filter set to all

---------

Co-authored-by: syedkaif866 <[email protected]>
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 (17)
micro-ui/web/micro-ui-internals/packages/modules/pgr/src/pages/employee/Inbox.js (6)

21-23: Bug: unsafe optional chaining before join() + filter-key mismatch will throw/under-count.

If applicationStatus is undefined, .join() on the result of the optional .map() will throw. Also, you read pgrfilters while the initial state uses wfFilters. Use a safe default and fall back across both keys.

Apply this diff:

-      const applicationStatus = searchParams?.filters?.pgrfilters?.applicationStatus?.map(e => e.code).join(",")
+      // Support both legacy and current filter buckets; default to []
+      const activeFilters =
+        searchParams?.filters?.pgrfilters ?? searchParams?.filters?.wfFilters ?? {};
+      const applicationStatus = (activeFilters?.applicationStatus ?? [])
+        .map((e) => e.code)
+        .join(",");

19-27: Stale state update risk in async effect + missing dependency on tenantId.

If the component unmounts or tenant changes quickly, setTotalRecords may run on an unmounted component or with a stale tenant. Add a cancellation flag and include tenantId in deps.

Apply this diff:

-  useEffect(() => {
-    (async () => {
+  useEffect(() => {
+    let cancelled = false;
+    (async () => {
       const applicationStatus = searchParams?.filters?.pgrfilters?.applicationStatus?.map(e => e.code).join(",")
       let response = await Digit.PGRService.count(tenantId, applicationStatus?.length > 0  ? {applicationStatus} : {} );
-      if (response?.count) {
-        setTotalRecords(response.count);
-      }
+      if (!cancelled && typeof response?.count === "number") {
+        setTotalRecords(response.count);
+      }
     })();
-  }, [searchParams]);
+    return () => {
+      cancelled = true;
+    };
+  }, [tenantId, searchParams]);

Note: If you apply the earlier “activeFilters” change, reuse that variable here to avoid duplication.


29-35: Pagination step hard-coded to 10 and can go negative; also offset should reset on page-size change.

Use pageSize as the step and clamp to zero. Reset offset when page size changes to avoid empty pages.

Apply this diff:

   const fetchNextPage = () => {
-    setPageOffset((prevState) => prevState + 10);
+    setPageOffset((prev) => prev + pageSize);
   };

   const fetchPrevPage = () => {
-    setPageOffset((prevState) => prevState - 10);
+    setPageOffset((prev) => Math.max(prev - pageSize, 0));
   };

   const handlePageSizeChange = (e) => {
-    setPageSize(Number(e.target.value));
+    const next = Number(e.target.value);
+    setPageSize(next);
+    setPageOffset(0);
   };

Also applies to: 37-39


41-43: Reset pagination on filter change to avoid empty pages.

When filters change, the current offset may point beyond the new result set. Reset to the first page.

Apply this diff:

   const handleFilterChange = (filterParam) => {
-    setSearchParams({ ...searchParams, filters: filterParam });
+    setPageOffset(0);
+    setSearchParams({ ...searchParams, filters: filterParam });
   };

54-81: Loader logic is effectively dead; complaints?.length !== null is always true. Gate by isLoading and default data to [].

The current condition renders the inbox even when complaints is undefined. Show Loader while loading, then render based on device.

Apply this diff:

-  if (complaints?.length !== null) {
-    if (isMobile) {
-      return (
-        <MobileInbox data={complaints} isLoading={isLoading} onFilterChange={handleFilterChange} onSearch={onSearch} searchParams={searchParams} />
-      );
-    } else {
-      return (
-        <div>
-          <Header>{t("ES_COMMON_INBOX")}</Header>
-          <DesktopInbox
-            data={complaints}
-            isLoading={isLoading}
-            onFilterChange={handleFilterChange}
-            onSearch={onSearch}
-            searchParams={searchParams}
-            onNextPage={fetchNextPage}
-            onPrevPage={fetchPrevPage}
-            onPageSizeChange={handlePageSizeChange}
-            currentPage={Math.floor(pageOffset / pageSize)}
-            totalRecords={totalRecords}
-            pageSizeLimit={pageSize}
-          />
-        </div>
-      );
-    }
-  } else {
-    return <Loader />;
-  }
+  if (isLoading) {
+    return <Loader />;
+  }
+
+  if (isMobile) {
+    return (
+      <MobileInbox
+        data={complaints || []}
+        isLoading={isLoading}
+        onFilterChange={handleFilterChange}
+        onSearch={onSearch}
+        searchParams={searchParams}
+      />
+    );
+  }
+
+  return (
+    <div>
+      <Header>{t("ES_COMMON_INBOX")}</Header>
+      <DesktopInbox
+        data={complaints || []}
+        isLoading={isLoading}
+        onFilterChange={handleFilterChange}
+        onSearch={onSearch}
+        searchParams={searchParams}
+        onNextPage={fetchNextPage}
+        onPrevPage={fetchPrevPage}
+        onPageSizeChange={handlePageSizeChange}
+        currentPage={Math.floor(pageOffset / pageSize)}
+        totalRecords={totalRecords}
+        pageSizeLimit={pageSize}
+      />
+    </div>
+  );

49-51: Minor: Prefer memoizing handlers to reduce downstream renders if DesktopInbox/MobileInbox depend on prop identity.

Not a blocker, but wrapping handlers in useCallback with tight deps can help avoid unnecessary re-renders in large lists.

I can provide a compact useCallback pass if desired.

micro-ui/web/micro-ui-internals/packages/modules/pgr/src/components/inbox/Filter.js (11)

11-14: Fix “Assigned to me” detection: compare to current user’s UUID instead of checking mere presence.

Current logic sets “Assigned to me” when any assignee code exists, even if it’s not the current user. Also avoid mutating uuid later by renaming it immutably.

Apply this diff:

-  let { uuid } = Digit.UserService.getUser().info;
+  const { uuid: userUuid } = Digit.UserService.getUser().info;
   const { searchParams } = props;
   const { t } = useTranslation();
-  const isAssignedToMe = searchParams?.filters?.wfFilters?.assignee && searchParams?.filters?.wfFilters?.assignee[0]?.code ? true : false;
+  const isAssignedToMe =
+    (searchParams?.filters?.wfFilters?.assignee?.[0]?.code ?? "") === userUuid;

26-26: Sync selected radio when searchParams change; expand effect dependencies.

With [t] only, switching filters externally won’t update the selected radio. Include isAssignedToMe (and the options) in dependencies.

Apply this diff:

-useEffect(() => setSelectedAssigned(isAssignedToMe ? assignedToOptions[0] : assignedToOptions[1]), [t]);
+useEffect(
+  () => setSelectedAssigned(isAssignedToMe ? assignedToOptions[0] : assignedToOptions[1]),
+  [t, isAssignedToMe, assignedToOptions]
+);

49-53: Do not mutate uuid; set assignee to [] for “ALL” to match new empty-state semantics.

Mutating uuid causes a hard bug: once set to "", switching back to “Assigned to me” won’t recover the real UUID. Also avoid using [{ code: "" }] for ALL.

Apply this diff:

   const onRadioChange = (value) => {
     setSelectedAssigned(value);
-    uuid = value.code === "ASSIGNED_TO_ME" ? uuid : "";
-    setWfFilters({ ...wfFilters, assignee: [{ code: uuid }] });
+    const assigneeCode = value.code === "ASSIGNED_TO_ME" ? userUuid : undefined;
+    setWfFilters({
+      ...wfFilters,
+      assignee: assigneeCode ? [{ code: assigneeCode }] : [],
+    });
   };

69-78: Don’t wipe the entire wfQuery when one filter entry is empty; delete the specific property instead.

Resetting the whole object drops other wf filters unintentionally (TOCTOU-like behavior). Mirror the pgrQuery logic.

Apply this diff:

   for (const property in wfFilters) {
     if (Array.isArray(wfFilters[property])) {
       let params = wfFilters[property].map((prop) => prop.code).join();
       if (params) {
         wfQuery[property] = params;
-      } else {
-        wfQuery = {};
+      } else {
+        delete wfQuery?.[property];
       }
     }
   }

142-151: Fix CLEAR ALL: wrong key (assigned vs assignee), invalid shape, and invalid radio selectedOption.

  • Uses assigned instead of assignee.
  • Sets code: [] (should be string/undefined).
  • Sets selectedAssigned to "" which breaks RadioButtons expecting an option object.

Apply this diff:

   function clearAll() {
     let pgrReset = { serviceCode: [], locality: [], applicationStatus: [] };
-    let wfRest = { assigned: [{ code: [] }] };
+    let wfReset = { assignee: [] };
     setPgrFilters(pgrReset);
-    setWfFilters(wfRest);
+    setWfFilters(wfReset);
     pgrQuery = {};
     wfQuery = {};
-    setSelectedAssigned("");
+    setSelectedAssigned(assignedToOptions[1]); // ASSIGNED_TO_ALL
     setSelectedComplaintType(null);
     setSelectedLocality(null);
   }

116-122: Locality selected label uses name but Dropdown expects i18nkey; this can render an empty label and break slicing.

Align the placeholder object to the optionKey="i18nkey" you pass to Dropdown.

Apply this diff:

 useEffect(() => {
   if (pgrfilters.locality.length > 1) {
-    setSelectedLocality({ name: `${pgrfilters.locality.length} selected` });
+    setSelectedLocality({ i18nkey: `${pgrfilters.locality.length} selected` });
   } else {
     setSelectedLocality(pgrfilters.locality[0]);
   }
 }, [pgrfilters.locality]);

Also applies to: 208-209


158-165: Nit: “lable” typo in helper; minor readability improvement.

Apply this diff:

-const GetSelectOptions = (lable, options, selected = null, select, optionKey, onRemove, key) => {
+const GetSelectOptions = (label, options, selected = null, select, optionKey, onRemove, key) => {
   selected = selected || { [optionKey]: " ", code: "" };
   return (
     <div>
-      <div className="filter-label">{lable}</div>
+      <div className="filter-label">{label}</div>
       {<Dropdown option={options} selected={selected} select={(value) => select(value, key)} optionKey={optionKey} />}

166-169: Guard against undefined labels in tags; avoid always appending ellipsis.

value[optionKey] can be undefined and .slice will throw. Also append ... only when truncated.

Apply this diff:

-          {pgrfilters[key].length > 0 &&
-            pgrfilters[key].map((value, index) => {
-              return <RemoveableTag key={index} text={`${value[optionKey].slice(0, 22)} ...`} onClick={() => onRemove(index, key)} />;
-            })}
+          {pgrfilters[key].length > 0 &&
+            pgrfilters[key].map((value, index) => {
+              const text = value?.[optionKey] || "";
+              const displayText = text.length > 22 ? `${text.slice(0, 22)} ...` : text;
+              return <RemoveableTag key={index} text={displayText} onClick={() => onRemove(index, key)} />;
+            })}

2-2: Remove unused import RoundedLabel.

Apply this diff:

-import { Dropdown, RadioButtons, ActionBar, RemoveableTag, RoundedLabel } from "@egovernments/digit-ui-react-components";
+import { Dropdown, RadioButtons, ActionBar, RemoveableTag } from "@egovernments/digit-ui-react-components";

7-9: Avoid mutable module-level pgrQuery/wfQuery; compute per-render or keep in refs/state.

Global mutable objects can leak across component instances and cause stale reads in React’s concurrent rendering. Build queries inside the effect, compare with previous, then emit via onFilterChange, e.g., use local const nextPgrQuery = {}/const nextWfQuery = {} and useRef to avoid unnecessary emits.

Would you like a patch that removes the globals and performs a shallow-equal check before calling onFilterChange?


81-86: Desktop: Frequent onFilterChange calls—consider dedupe/debounce to avoid excessive network traffic.

Auto-apply on every state change is UX-friendly but can be chatty. Guard by comparing the newly computed queries to the previous ones, or add a small debounce.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1d4d805 and e1303e5.

📒 Files selected for processing (2)
  • micro-ui/web/micro-ui-internals/packages/modules/pgr/src/components/inbox/Filter.js (1 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/pgr/src/pages/employee/Inbox.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

check

Files:

  • micro-ui/web/micro-ui-internals/packages/modules/pgr/src/pages/employee/Inbox.js
  • micro-ui/web/micro-ui-internals/packages/modules/pgr/src/components/inbox/Filter.js
⏰ 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 (1)
micro-ui/web/micro-ui-internals/packages/modules/pgr/src/components/inbox/Filter.js (1)

38-42: Defaulting wfFilters.assignee to an empty array changes initial semantics—verify downstream handling.

Initializing to [] is reasonable, but ensure all handlers treat “Assigned to All” as an empty list (not [{ code: "" }]) and that query builders and counts don’t assume a one-item default. Follow-up fixes are proposed in separate comments for Lines 49-53, 69-78, and 142-151.

Comment on lines +15 to +18
// const [searchParams, setSearchParams] = useState({ filters: { wfFilters: { assignee: [{ code: uuid }] } }, search: "", sort: {} });

const [searchParams, setSearchParams] = useState({ filters: { wfFilters: { assignee: [] } }, search: "", sort: {} });

Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Defaulting assignee to [] changes the initial scope to "all" — verify product intent and cross-file consistency.

Previously this inbox opened “Assigned to me”. With assignee: [] it will fetch everything unless user narrows filters, which can hurt performance and UX for large tenants. Also, this file initializes wfFilters, but the count effect below reads from pgrfilters, risking a mismatch between the grid and the count. Decide one canonical key and confirm the default behavior with Product.

If the intent is to start unassigned, I can align Filter.js and the API adapters on a single key and wire an “Assigned to me” quick filter as the default radio without injecting into initial state. Want me to raise a follow-up PR?

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/pgr/src/pages/employee/Inbox.js
around lines 15-18, the initial state was changed from assignee: [{ code: uuid
}] to assignee: [], which expands the inbox from “Assigned to me” to “All” and
also exposes a mismatch between the wfFilters key here and the pgrfilters key
used by the count effect; confirm product intent (should default be “Assigned to
me” or “All”), then make one of two fixes: if default should be “Assigned to
me”, restore assignee to [{ code: uuid }] and ensure the count effect reads the
same wfFilters key; otherwise, if default should be “All”, keep assignee: [] but
update Filter.js and API adapters to consistently use wfFilters (or rename
wfFilters→pgrfilters everywhere) and change the count effect to read the
canonical key so grid and count align, and wire the “Assigned to me”
quick-filter as a default radio option instead of injecting into initial state.

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: 3

♻️ Duplicate comments (2)
micro-ui/web/public/index.html (2)

10-11: Add SRI + crossorigin to external CSS (repeat): protect against CDN tampering

Previous review already requested this; still missing on both links.

Apply this patch (rehash if assets change):

-  <link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected]/dist/index.css" />
-  <link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected]/dist/index.css" />
+  <link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected]/dist/index.css"
+        integrity="sha384-ujgPEZxME0vI6gml3R9fLnJBJJtuX7crZuyXk7dSmmGevF1r5ZO0O7ty3PBJPFkC"
+        crossorigin="anonymous" />
+  <link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected]/dist/index.css"
+        integrity="sha384-2EfdlCC1EigMYJ59qOhrK2CfvNH6nzQpPONIej1kQQqdSp4/kpLXsw9Zm3/y7XnC"
+        crossorigin="anonymous" />

100-119: Invalid HTML: <style> is outside and centering is incorrect (repeat)

Move this block into the head. The current negative margin (-10.3rem) is wrong for a 7.5rem-wide tooltip; use transform for robust centering and add font fallbacks.

Remove from here:

-<style>
-  .digit-header-content.label .info-icon .infotext {
-    position: absolute;
-    text-align: center;
-    font-family: Roboto;
-    font-style: normal;
-    font-weight: 400;
-    line-height: 1.37rem;
-    visibility: hidden;
-    width: 7.5rem;
-    background-color: #555;
-    color: #fff;
-    padding: .5rem 0;
-    z-index: 1;
-    bottom: 100%;
-    left: 50%;
-    margin-left: -10.3rem;
-    border-radius: .375rem;
-  }
-</style>

Add inside the head (just before </head>):

<style>
  .digit-header-content.label .info-icon .infotext {
    position: absolute;
    text-align: center;
    font-family: "Roboto", Arial, sans-serif;
    font-style: normal;
    font-weight: 400;
    line-height: 1.37rem;
    visibility: hidden;
    width: 7.5rem;
    background-color: #555;
    color: #fff;
    padding: .5rem 0;
    z-index: 1000;
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    border-radius: .375rem;
  }
</style>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e1303e5 and 6502817.

📒 Files selected for processing (1)
  • micro-ui/web/public/index.html (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-03-25T10:47:10.916Z
Learnt from: Tulika-eGov
PR: egovernments/DIGIT-Frontend#2288
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Landing/LandingComponent.js:33-33
Timestamp: 2025-03-25T10:47:10.916Z
Learning: In DIGIT-Frontend, window.contextPath is dynamically determined and cannot be given a static fallback value like 'digit-ui'. The application relies on this dynamic context path for proper routing.

Applied to files:

  • micro-ui/web/public/index.html

Comment on lines 17 to 33
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setExcludedQueryParams", ["account", "accountnum", "address", "address1", "address2", "address3", "addressline1", "addressline2", "adres", "adresse", "age", "alter", "auth", "authpw", "bic", "billingaddress", "billingaddress1", "billingaddress2", "calle", "cardnumber", "cc", "ccc", "cccsc", "cccvc", "cccvv", "ccexpiry", "ccexpmonth", "ccexpyear", "ccname", "ccnumber", "cctype", "cell", "cellphone", "city", "clientid", "clientsecret", "company", "consumerkey", "consumersecret", "contrasenya", "contrase\u00f1a", "creditcard", "creditcardnumber", "cvc", "cvv", "dateofbirth", "debitcard", "direcci\u00f3n", "dob", "domain", "ebost", "email", "emailaddress", "emailadresse", "epos", "epost", "eposta", "exp", "familyname", "firma", "firstname", "formlogin", "fullname", "gender", "geschlecht", "gst", "gstnumber", "handynummer", "has\u0142o", "heslo", "iban", "ibanaccountnum", "ibanaccountnumber", "id", "identifier", "indirizzo", "kartakredytowa", "kennwort", "keyconsumerkey", "keyconsumersecret", "konto", "kontonr", "kontonummer", "kredietkaart", "kreditkarte", "kreditkort", "lastname", "login", "mail", "mobiili", "mobile", "mobilne", "nachname", "name", "nickname", "osoite", "parole", "pass", "passord", "password", "passwort", "pasword", "paswort", "paword", "phone", "pin", "plz", "postalcode", "postcode", "postleitzahl", "privatekey", "publickey", "pw", "pwd", "pword", "pwrd", "rue", "secret", "secretq", "secretquestion", "shippingaddress", "shippingaddress1", "shippingaddress2", "socialsec", "socialsecuritynumber", "socsec", "sokak", "ssn", "steuernummer", "strasse", "street", "surname", "swift", "tax", "taxnumber", "tel", "telefon", "telefonnr", "telefonnummer", "telefono", "telephone", "token", "token_auth", "tokenauth", "t\u00e9l\u00e9phone", "ulica", "user", "username", "vat", "vatnumber", "via", "vorname", "wachtwoord", "wagwoord", "webhooksecret", "website", "zip", "zipcode"]]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function () {
var u = "https://unified-demo.digit.org/matomo/";
_paq.push(['setTrackerUrl', u + 'matomo.php']);
_paq.push(['setSiteId', '1']);
// Adding a second tracker
_paq.push(['addTracker', 'https://digit-analytics.matomo.cloud/matomo.php', '2']);
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
g.async = true; g.src = u + 'matomo.js';
s.parentNode.insertBefore(g, s);
})();
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

🛠️ Refactor suggestion

⚠️ Potential issue

Matomo: ensure both trackers receive the initial pageview; add privacy controls

Currently, the first trackPageView fires before addTracker, so tracker "2" may miss the initial PV. Also add basic privacy settings (GDPR-friendly).

   <script>
     var _paq = window._paq = window._paq || [];
-    /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
+    /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
+    // Privacy hardening
+    _paq.push(['disableCookies']);           // cookieless analytics
+    _paq.push(['anonymizeIp']);              // mask IP
+    _paq.push(['setDoNotTrack', true]);      // honor DNT
+    _paq.push(['setCookieSameSite', 'Lax']); // safer defaults for any residual cookies
     _paq.push(["setExcludedQueryParams", [/* …existing list… */]]);
-    _paq.push(['trackPageView']);
-    _paq.push(['enableLinkTracking']);
     (function () {
       var u = "https://unified-demo.digit.org/matomo/";
       _paq.push(['setTrackerUrl', u + 'matomo.php']);
       _paq.push(['setSiteId', '1']);
-      // Adding a second tracker
-      _paq.push(['addTracker', 'https://digit-analytics.matomo.cloud/matomo.php', '2']);
+      // Add second tracker before tracking so both get the initial PV
+      _paq.push(['addTracker', 'https://digit-analytics.matomo.cloud/matomo.php', '2']);
+      _paq.push(['enableLinkTracking']);
+      _paq.push(['trackPageView']); // initial PV goes to all configured trackers
       var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
       g.async = true; g.src = u + 'matomo.js';
       s.parentNode.insertBefore(g, s);
     })();

Optional: If consent is required, gate tracking until consent with _paq.push(['requireConsent']); or _paq.push(['requireCookieConsent']); and trigger once obtained.

📝 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
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
_paq.push(["setExcludedQueryParams", ["account", "accountnum", "address", "address1", "address2", "address3", "addressline1", "addressline2", "adres", "adresse", "age", "alter", "auth", "authpw", "bic", "billingaddress", "billingaddress1", "billingaddress2", "calle", "cardnumber", "cc", "ccc", "cccsc", "cccvc", "cccvv", "ccexpiry", "ccexpmonth", "ccexpyear", "ccname", "ccnumber", "cctype", "cell", "cellphone", "city", "clientid", "clientsecret", "company", "consumerkey", "consumersecret", "contrasenya", "contrase\u00f1a", "creditcard", "creditcardnumber", "cvc", "cvv", "dateofbirth", "debitcard", "direcci\u00f3n", "dob", "domain", "ebost", "email", "emailaddress", "emailadresse", "epos", "epost", "eposta", "exp", "familyname", "firma", "firstname", "formlogin", "fullname", "gender", "geschlecht", "gst", "gstnumber", "handynummer", "has\u0142o", "heslo", "iban", "ibanaccountnum", "ibanaccountnumber", "id", "identifier", "indirizzo", "kartakredytowa", "kennwort", "keyconsumerkey", "keyconsumersecret", "konto", "kontonr", "kontonummer", "kredietkaart", "kreditkarte", "kreditkort", "lastname", "login", "mail", "mobiili", "mobile", "mobilne", "nachname", "name", "nickname", "osoite", "parole", "pass", "passord", "password", "passwort", "pasword", "paswort", "paword", "phone", "pin", "plz", "postalcode", "postcode", "postleitzahl", "privatekey", "publickey", "pw", "pwd", "pword", "pwrd", "rue", "secret", "secretq", "secretquestion", "shippingaddress", "shippingaddress1", "shippingaddress2", "socialsec", "socialsecuritynumber", "socsec", "sokak", "ssn", "steuernummer", "strasse", "street", "surname", "swift", "tax", "taxnumber", "tel", "telefon", "telefonnr", "telefonnummer", "telefono", "telephone", "token", "token_auth", "tokenauth", "t\u00e9l\u00e9phone", "ulica", "user", "username", "vat", "vatnumber", "via", "vorname", "wachtwoord", "wagwoord", "webhooksecret", "website", "zip", "zipcode"]]);
_paq.push(['trackPageView']);
_paq.push(['enableLinkTracking']);
(function () {
var u = "https://unified-demo.digit.org/matomo/";
_paq.push(['setTrackerUrl', u + 'matomo.php']);
_paq.push(['setSiteId', '1']);
// Adding a second tracker
_paq.push(['addTracker', 'https://digit-analytics.matomo.cloud/matomo.php', '2']);
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
g.async = true; g.src = u + 'matomo.js';
s.parentNode.insertBefore(g, s);
})();
<!-- Matomo -->
<script>
var _paq = window._paq = window._paq || [];
/* tracker methods like "setCustomDimension" should be called before "trackPageView" */
// Privacy hardening
_paq.push(['disableCookies']); // cookieless analytics
_paq.push(['anonymizeIp']); // mask IP
_paq.push(['setDoNotTrack', true]); // honor DNT
_paq.push(['setCookieSameSite', 'Lax']); // safer defaults for any residual cookies
_paq.push(["setExcludedQueryParams", ["account", "accountnum", "address", "address1", "address2", "address3", "addressline1", "addressline2", "adres", "adresse", "age", "alter", "auth", "authpw", "bic", "billingaddress", "billingaddress1", "billingaddress2", "calle", "cardnumber", "cc", "ccc", "cccsc", "cccvc", "cccvv", "ccexpiry", "ccexpmonth", "ccexpyear", "ccname", "ccnumber", "cctype", "cell", "cellphone", "city", "clientid", "clientsecret", "company", "consumerkey", "consumersecret", "contrasenya", "contrase\u00f1a", "creditcard", "creditcardnumber", "cvc", "cvv", "dateofbirth", "debitcard", "direcci\u00f3n", "dob", "domain", "ebost", "email", "emailaddress", "emailadresse", "epos", "epost", "eposta", "exp", "familyname", "firma", "firstname", "formlogin", "fullname", "gender", "geschlecht", "gst", "gstnumber", "handynummer", "has\u0142o", "heslo", "iban", "ibanaccountnum", "ibanaccountnumber", "id", "identifier", "indirizzo", "kartakredytowa", "kennwort", "keyconsumerkey", "keyconsumersecret", "konto", "kontonr", "kontonummer", "kredietkaart", "kreditkarte", "kreditkort", "lastname", "login", "mail", "mobiili", "mobile", "mobilne", "nachname", "name", "nickname", "osoite", "parole", "pass", "passord", "password", "passwort", "pasword", "paswort", "paword", "phone", "pin", "plz", "postalcode", "postcode", "postleitzahl", "privatekey", "publickey", "pw", "pwd", "pword", "pwrd", "rue", "secret", "secretq", "secretquestion", "shippingaddress", "shippingaddress1", "shippingaddress2", "socialsec", "socialsecuritynumber", "socsec", "sokak", "ssn", "steuernummer", "strasse", "street", "surname", "swift", "tax", "taxnumber", "tel", "telefon", "telefonnr", "telefonnummer", "telefono", "telephone", "token", "token_auth", "tokenauth", "t\u00e9l\u00e9phone", "ulica", "user", "username", "vat", "vatnumber", "via", "vorname", "wachtwoord", "wagwoord", "webhooksecret", "website", "zip", "zipcode"]]);
(function () {
var u = "https://unified-demo.digit.org/matomo/";
_paq.push(['setTrackerUrl', u + 'matomo.php']);
_paq.push(['setSiteId', '1']);
// Add second tracker before tracking so both get the initial PV
_paq.push(['addTracker', 'https://digit-analytics.matomo.cloud/matomo.php', '2']);
_paq.push(['enableLinkTracking']);
_paq.push(['trackPageView']); // initial PV goes to all configured trackers
var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
g.async = true; g.src = u + 'matomo.js';
s.parentNode.insertBefore(g, s);
})();
</script>
🤖 Prompt for AI Agents
In micro-ui/web/public/index.html around lines 17-33, the initial
_paq.push(['trackPageView']) runs before the second tracker is added so tracker
"2" may miss the first pageview; move the trackPageView and enableLinkTracking
calls to after _paq.push(['addTracker', ...]) (or explicitly send pageview to
both trackers via tracker-scoped calls), and add privacy controls by pushing
requireConsent or requireCookieConsent at init and honoring navigator.doNotTrack
(e.g., push ['setDoNotTrack', true] or skip tracking when DNT is set) so
tracking is gated until consent.

Comment on lines +35 to +66
let lastUrl = location.href;

function trackUrlChange() {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
_paq.push(['setCustomUrl', url]);
_paq.push(['trackPageView']);
}
}

// Wrap pushState
history.pushState = (function (original) {
return function pushState() {
const result = original.apply(this, arguments);
trackUrlChange();
return result;
};
})(history.pushState);

// Wrap replaceState
history.replaceState = (function (original) {
return function replaceState() {
const result = original.apply(this, arguments);
trackUrlChange();
return result;
};
})(history.replaceState);

// Listen for back/forward
window.addEventListener('popstate', trackUrlChange);
})();
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

SPA tracking wrapper: add idempotency + hashchange; verify no duplicate PVs

Avoid accidental double-wrapping and track hash-based navigations if any routes use hashes. Verify no duplicate pageviews fire on initial render or single navigation.

 (function () {
   let lastUrl = location.href;
+  if (window.__matomoSpaWrapped) return;
+  window.__matomoSpaWrapped = true;
 
   function trackUrlChange() {
     const url = location.href;
     if (url !== lastUrl) {
       lastUrl = url;
       _paq.push(['setCustomUrl', url]);
       _paq.push(['trackPageView']);
     }
   }
 
   // Wrap pushState
   history.pushState = (function (original) {
     return function pushState() {
       const result = original.apply(this, arguments);
       trackUrlChange();
       return result;
     };
   })(history.pushState);
 
   // Wrap replaceState
   history.replaceState = (function (original) {
     return function replaceState() {
       const result = original.apply(this, arguments);
       trackUrlChange();
       return result;
     };
   })(history.replaceState);
 
   // Listen for back/forward
   window.addEventListener('popstate', trackUrlChange);
+  // Also cover hash-based routing (if any)
+  window.addEventListener('hashchange', trackUrlChange);
 })();

Manual check: open DevTools network tab, filter “matomo.php”, navigate across routes; expect exactly one PV per route transition across both trackers.

📝 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
let lastUrl = location.href;
function trackUrlChange() {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
_paq.push(['setCustomUrl', url]);
_paq.push(['trackPageView']);
}
}
// Wrap pushState
history.pushState = (function (original) {
return function pushState() {
const result = original.apply(this, arguments);
trackUrlChange();
return result;
};
})(history.pushState);
// Wrap replaceState
history.replaceState = (function (original) {
return function replaceState() {
const result = original.apply(this, arguments);
trackUrlChange();
return result;
};
})(history.replaceState);
// Listen for back/forward
window.addEventListener('popstate', trackUrlChange);
})();
(function () {
let lastUrl = location.href;
if (window.__matomoSpaWrapped) return;
window.__matomoSpaWrapped = true;
function trackUrlChange() {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
_paq.push(['setCustomUrl', url]);
_paq.push(['trackPageView']);
}
}
// Wrap pushState
history.pushState = (function (original) {
return function pushState() {
const result = original.apply(this, arguments);
trackUrlChange();
return result;
};
})(history.pushState);
// Wrap replaceState
history.replaceState = (function (original) {
return function replaceState() {
const result = original.apply(this, arguments);
trackUrlChange();
return result;
};
})(history.replaceState);
// Listen for back/forward
window.addEventListener('popstate', trackUrlChange);
// Also cover hash-based routing (if any)
window.addEventListener('hashchange', trackUrlChange);
})();
🤖 Prompt for AI Agents
In micro-ui/web/public/index.html around lines 35 to 66, the SPA history
wrappers can be double-wrapped and they don’t handle hash-based navigation which
may cause duplicate pageviews; make the wrapper idempotent by checking and
skipping if pushState/replaceState have already been wrapped (e.g. by a marker
on the function or a flag), add window.addEventListener('hashchange',
trackUrlChange) so hash navigations are tracked, and ensure trackUrlChange
prevents duplicate pageviews on initial render by initializing lastUrl after
initial push or by guarding the first call (so only one PV fires per route
transition).

Comment on lines +68 to +95
// Track clicks on buttons & links
(function () {
document.addEventListener("click", function (e) {
const selector = `
button,
input[type="button"],
input[type="submit"],
input[type="actionButton"],
[role="button"],
a[href]:not([href^="mailto:"]):not([href^="tel:"]):not([href^="#"]):not([href^="javascript:"])
`;
const target = e.target.closest(selector);
if (target) {
const elementType = target.tagName.toLowerCase();
const text = target.innerText.trim() || target.value || 'Unnamed';
const id = target.id || 'no-id';
const classes = target.className || 'no-class';
const href = target.getAttribute('href') || '';

_paq.push([
'trackEvent',
'UI Interaction', // Category
`${elementType} click`, // Action
`Text: ${text}, ID: ${id}, Class: ${classes}, Href: ${href}`
]);
}
});
})();
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

⚠️ Potential issue

Click tracking may leak PII via full href/text/classes; sanitize before sending

The event label currently includes raw href (with query), full text, id, and classes. On auth pages this risks capturing tokens or user-identifying info.

   // Track clicks on buttons & links
   (function () {
     document.addEventListener("click", function (e) {
       const selector = `
                       button,
                       input[type="button"],
                       input[type="submit"],
                       input[type="actionButton"],
                       [role="button"],
                       a[href]:not([href^="mailto:"]):not([href^="tel:"]):not([href^="#"]):not([href^="javascript:"])
                     `;
       const target = e.target.closest(selector);
-      if (target) {
+      if (target && !target.closest('[data-analytics="off"]')) {
         const elementType = target.tagName.toLowerCase();
-        const text = target.innerText.trim() || target.value || 'Unnamed';
-        const id = target.id || 'no-id';
-        const classes = target.className || 'no-class';
-        const href = target.getAttribute('href') || '';
+        const rawText = (target.innerText || target.value || '').trim();
+        const text = rawText.slice(0, 120) || 'Unnamed';
+        const id = target.id ? 'has-id' : 'no-id';
+        const classes = (target.dataset && target.dataset.analyticsLabel) ? target.dataset.analyticsLabel : 'generic';
+        const hrefAttr = target.getAttribute('href') || '';
+        let sanitizedHref = '';
+        try {
+          const u = new URL(hrefAttr, location.href);
+          sanitizedHref = u.origin + u.pathname; // drop query/hash entirely
+        } catch (_) {}
 
         _paq.push([
           'trackEvent',
           'UI Interaction',            // Category
           `${elementType} click`,      // Action
-          `Text: ${text}, ID: ${id}, Class: ${classes}, Href: ${href}`
+          `Text: ${text}, ID: ${id}, Label: ${classes}, Href: ${sanitizedHref}`
         ]);
       }
     });
   })();

Usage: allow components to set a stable label via data-analytics-label="login-submit" and opt out via data-analytics="off".

📝 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
// Track clicks on buttons & links
(function () {
document.addEventListener("click", function (e) {
const selector = `
button,
input[type="button"],
input[type="submit"],
input[type="actionButton"],
[role="button"],
a[href]:not([href^="mailto:"]):not([href^="tel:"]):not([href^="#"]):not([href^="javascript:"])
`;
const target = e.target.closest(selector);
if (target) {
const elementType = target.tagName.toLowerCase();
const text = target.innerText.trim() || target.value || 'Unnamed';
const id = target.id || 'no-id';
const classes = target.className || 'no-class';
const href = target.getAttribute('href') || '';
_paq.push([
'trackEvent',
'UI Interaction', // Category
`${elementType} click`, // Action
`Text: ${text}, ID: ${id}, Class: ${classes}, Href: ${href}`
]);
}
});
})();
// Track clicks on buttons & links
(function () {
document.addEventListener("click", function (e) {
const selector = `
button,
input[type="button"],
input[type="submit"],
input[type="actionButton"],
[role="button"],
a[href]:not([href^="mailto:"]):not([href^="tel:"]):not([href^="#"]):not([href^="javascript:"])
`;
const target = e.target.closest(selector);
// opt out of analytics if ancestor has data-analytics="off"
if (target && !target.closest('[data-analytics="off"]')) {
const elementType = target.tagName.toLowerCase();
const rawText = (target.innerText || target.value || '').trim();
const text = rawText.slice(0, 120) || 'Unnamed';
// only indicate presence of an ID rather than its value
const id = target.id ? 'has-id' : 'no-id';
// use a stable, component-provided label if present
const classes = target.dataset.analyticsLabel || 'generic';
const hrefAttr = target.getAttribute('href') || '';
let sanitizedHref = '';
try {
const u = new URL(hrefAttr, location.href);
// drop query and hash to avoid leaking tokens/user data
sanitizedHref = u.origin + u.pathname;
} catch (_) {}
_paq.push([
'trackEvent',
'UI Interaction',
`${elementType} click`,
`Text: ${text}, ID: ${id}, Label: ${classes}, Href: ${sanitizedHref}`
]);
}
});
})();
🤖 Prompt for AI Agents
In micro-ui/web/public/index.html around lines 68 to 95, the click-tracking code
currently sends full href, innerText, id and classes which can leak PII or
tokens; update it to first check for an opt-out attribute data-analytics="off"
and skip tracking, then prefer a stable label from data-analytics-label if
present (use that as the action/label), otherwise sanitize any generated label
by stripping href query/fragment (keep only origin+pathname), truncating
innerText to a safe length (e.g. 100 chars) and omitting or hashing classes/ids
instead of sending raw values; ensure links without href or sensitive protocols
are handled, and only push the sanitized values to _paq.

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: 4

♻️ Duplicate comments (4)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js (4)

80-80: Use const for config

The variable isn’t reassigned.

-  let config = [{ body: propsConfig?.inputs }];
+  const config = [{ body: propsConfig?.inputs }];

170-176: Clean up PropTypes to reflect actual props and remove unused loginParams

Align PropTypes with the component signature.

-Login.propTypes = {
-  loginParams: PropTypes.any,
-};
-
-Login.defaultProps = {
-  loginParams: null,
-};
+Login.propTypes = {
+  config: PropTypes.object.isRequired,
+  t: PropTypes.func.isRequired,
+  isDisabled: PropTypes.bool,
+};

67-71: Clear OTP-related sessionStorage after verification (follow-up)

Email and tenant are stored but not cleared post-OTP. Ensure these are removed on successful OTP verification to reduce exposure.


82-91: Fix undefined defaultValue and guard populators before write

defaultValue is not defined and writing to populators.defaultValue can throw if populators is undefined. Also tighten equality checks.

-  if (
-    mode === "admin" &&
-    config?.[0]?.body?.[2]?.disable == false &&
-    config?.[0]?.body?.[2]?.populators?.defaultValue == undefined
-  ) {
+  if (
+    mode === "admin" &&
+    config?.[0]?.body?.[2]?.disable !== true &&
+    config?.[0]?.body?.[2]?.populators?.defaultValue === undefined
+  ) {
     config[0].body[2].disable = true;
     config[0].body[2].isMandatory = false;
-    config[0].body[2].populators.defaultValue = defaultValue;
+    config[0].body[2].populators = {
+      ...(config[0].body[2].populators || {}),
+      defaultValue: defaultTenant,
+    };
   }

Add (outside this block, see prior comment) the defaultTenant definition:

const defaultTenant = Digit?.ULBService?.getStateId?.() || stateInfo?.code;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6502817 and b4b21fa.

📒 Files selected for processing (1)
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

check

Files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
🧠 Learnings (3)
📚 Learning: 2025-06-27T05:52:33.156Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/components/PrivacyComponent.js:0-0
Timestamp: 2025-06-27T05:52:33.156Z
Learning: The PrivacyComponent in `micro-ui/web/micro-ui-internals/packages/modules/core/src/components/PrivacyComponent.js` is mostly used in login and signup pages, especially in formcomposer where the UI is stable, making layout changes less likely to cause regressions.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
📚 Learning: 2025-06-04T04:08:48.234Z
Learnt from: jagankumar-egov
PR: egovernments/DIGIT-Frontend#2488
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login/login.js:152-152
Timestamp: 2025-06-04T04:08:48.234Z
Learning: In the employee login component (micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login/login.js), prefer using a `defaultTenant` variable to store `Digit.ULBService.getStateId()` instead of calling the function multiple times inline in the `defaultValue` object. This improves code readability and avoids function call duplication.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
📚 Learning: 2024-11-07T11:02:33.520Z
Learnt from: rachana-egov
PR: egovernments/DIGIT-Frontend#1770
File: health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/pages/employee/viewVillage.js:320-322
Timestamp: 2024-11-07T11:02:33.520Z
Learning: In `health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/pages/employee/viewVillage.js`, the `data?.additionalFields` object is guaranteed to be defined, so checking for its existence before accessing its keys is unnecessary.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
🧬 Code graph analysis (1)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js (4)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/OtpCustomComponent.js (2)
  • reqCreate (37-44)
  • mutation (45-45)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/SandBoxHeader.js (1)
  • SandBoxHeader (7-23)
micro-ui/web/micro-ui-internals/packages/react-components/src/atoms/Toast.js (1)
  • Toast (6-50)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/ImageComponent.js (1)
  • ImageComponent (4-32)

Comment on lines +19 to +26
function buildOtpUrl(contextPath, tenantId) {
const ctx = (contextPath || "").split("/").filter(Boolean).join("/");
if (ctx.includes("/")) {
return `/${ctx}/employee/user/login/otp`;
} else {
return `/${ctx}/${tenantId}/employee/user/login/otp`;
}
}
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

Fix OTP URL builder to avoid leading // and ensure stable path assembly

Current logic can produce //{tenantId}/... when contextPath is empty and can also omit tenantId unpredictably. Replace with a robust join that always returns /{context}/{tenantId}/employee/user/login/otp.

-  function buildOtpUrl(contextPath, tenantId) {
-    const ctx = (contextPath || "").split("/").filter(Boolean).join("/");
-    if (ctx.includes("/")) {
-      return `/${ctx}/employee/user/login/otp`;
-    } else {
-      return `/${ctx}/${tenantId}/employee/user/login/otp`;
-    }
-  }
+  function buildOtpUrl(contextPath, tenantId) {
+    const ctx = (contextPath || "").replace(/^\/+|\/+$/g, "");
+    const parts = [ctx, tenantId, "employee", "user", "login", "otp"].filter(Boolean);
+    return `/${parts.join("/")}`;
+  }
📝 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
function buildOtpUrl(contextPath, tenantId) {
const ctx = (contextPath || "").split("/").filter(Boolean).join("/");
if (ctx.includes("/")) {
return `/${ctx}/employee/user/login/otp`;
} else {
return `/${ctx}/${tenantId}/employee/user/login/otp`;
}
}
function buildOtpUrl(contextPath, tenantId) {
const ctx = (contextPath || "").replace(/^\/+|\/+$/g, "");
const parts = [ctx, tenantId, "employee", "user", "login", "otp"].filter(Boolean);
return `/${parts.join("/")}`;
}
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
around lines 19 to 26, the buildOtpUrl can produce double leading slashes or
omit tenantId; normalize the contextPath by trimming leading/trailing slashes,
build an array of path segments [normalizedContext, tenantId, "employee",
"user", "login", "otp"], filter out empty segments, join with "/" and prefix
with a single "/" so the result is always
"/{context?}/{tenantId}/employee/user/login/otp" (when context is empty it
becomes "/{tenantId}/employee/..."), and ensure tenantId is included or
throw/handle if missing.

Comment on lines +28 to +35
const reqCreate = {
url: `/user-otp/v1/_send`,
params: { tenantId: Digit?.ULBService?.getStateId() },
body: {},
config: {
enable: false,
},
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Reuse a single defaultTenant and avoid duplicate state-id lookups

Define defaultTenant once and reuse it to configure the mutation request.

-  const reqCreate = {
-    url: `/user-otp/v1/_send`,
-    params: { tenantId: Digit?.ULBService?.getStateId() },
+  const reqCreate = {
+    url: `/user-otp/v1/_send`,
+    params: { tenantId: defaultTenant },
     body: {},
     config: {
       enable: false,
     },
   };

Add this near Line 14 (after stateInfo is available):

// derive once; falls back to stateInfo.code if available
const defaultTenant = Digit?.ULBService?.getStateId?.() || stateInfo?.code;
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
around lines 28 to 35, avoid repeated Digit?.ULBService?.getStateId() calls by
deriving a single defaultTenant once (after stateInfo is available, around line
14) using fallback to stateInfo.code, then replace the params value in reqCreate
with that defaultTenant; add the const defaultTenant =
Digit?.ULBService?.getStateId?.() || stateInfo?.code near line 14 and update
reqCreate.params to use defaultTenant.

>
<SandBoxHeader showTenant={false} />
</FormComposerV2>
{showToast && <Toast type="error" label={t(showToast?.label)} onClose={closeToast} />}
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

Incorrect Toast API usage; use error prop instead of type="error"

The Toast component expects a boolean error prop. Passing type won’t render error styling.

-      {showToast && <Toast type="error" label={t(showToast?.label)} onClose={closeToast} />}
+      {showToast && (
+        <Toast
+          error
+          label={t(showToast?.label || "CORE_COMMON_SOMETHING_WENT_WRONG")}
+          onClose={closeToast}
+        />
+      )}
📝 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
{showToast && <Toast type="error" label={t(showToast?.label)} onClose={closeToast} />}
{showToast && (
<Toast
error
label={t(showToast?.label || "CORE_COMMON_SOMETHING_WENT_WRONG")}
onClose={closeToast}
/>
)}
🤖 Prompt for AI Agents
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
around line 137: the Toast is using the wrong prop name (type="error") which the
component doesn't recognize; replace type="error" with the boolean error prop
(e.g., error={true} or error) so the error styling is applied, keep the label
and onClose props as-is and ensure the passed value is a boolean (convert
showToast?.type or set error when showToast indicates an error).

return (
<div style={{ display: "flex", height: "100vh" }}>
<div style={{ width: "70%", position: "relative" }}>
<Carousel bannerImages={propsConfig.bannerImages} />
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Defensive access for banner images

Avoid potential undefined access when propsConfig isn’t provided.

-        <Carousel bannerImages={propsConfig.bannerImages} />
+        <Carousel bannerImages={propsConfig?.bannerImages || []} />
📝 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
<Carousel bannerImages={propsConfig.bannerImages} />
<Carousel bannerImages={propsConfig?.bannerImages || []} />
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
around line 162, the JSX accesses propsConfig.bannerImages directly which can
throw if propsConfig is undefined; update the prop to safely access bannerImages
(e.g., use optional chaining or a default value) so Carousel receives an array
(or undefined handled by Carousel) rather than causing a runtime error — replace
propsConfig.bannerImages with propsConfig?.bannerImages or (propsConfig &&
propsConfig.bannerImages) or derive a const bannerImages =
propsConfig?.bannerImages || [] above and pass bannerImages into the Carousel.

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: 3

Caution

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

⚠️ Outside diff range comments (2)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/LanguageSelection/index.js (2)

93-101: Use a stable key for language items.

Index keys can cause unnecessary re-mounts; prefer a unique value.

-            <div className="language-button-container" key={index}>
+            <div className="language-button-container" key={language.value || index}>

111-112: Avoid calling focus on a possibly null window handle.

Popup blockers can return null.

-            window.open(window?.globalConfigs?.getConfig?.("DIGIT_HOME_URL"), "_blank").focus();
+            const w = window.open(window?.globalConfigs?.getConfig?.("DIGIT_HOME_URL"), "_blank");
+            if (w) w.focus();
♻️ Duplicate comments (1)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/LanguageSelection/index.js (1)

25-28: Harden getContextPath against leading slashes and empty segments.

Current logic returns an empty string for inputs like "/foo", causing paths like "///user/login". Use filtered segments.

-  function getContextPath(contextPath) {
-    if (!contextPath || typeof contextPath !== "string") return "";
-    return contextPath.split("/")[0];
-  }
+  function getContextPath(contextPath) {
+    if (!contextPath || typeof contextPath !== "string") return "";
+    const segments = contextPath.split("/").filter((segment) => segment.length > 0);
+    return segments.length > 0 ? segments[0] : "";
+  }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b4b21fa and 31ac522.

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

⚙️ CodeRabbit configuration file

check

Files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/LanguageSelection/index.js
🧠 Learnings (1)
📚 Learning: 2024-06-10T19:25:42.992Z
Learnt from: siddhant-nawale-egov
PR: egovernments/DIGIT-Frontend#0
File: :0-0
Timestamp: 2024-06-10T19:25:42.992Z
Learning: The Button component from `egovernments/digit-ui-components` has compatibility issues with the ActionBar, leading to the preference for using the Button from `egovernments/digit-ui-react-components` in certain scenarios.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/LanguageSelection/index.js
🧬 Code graph analysis (1)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/LanguageSelection/index.js (3)
micro-ui/web/micro-ui-internals/packages/libraries/src/utils/index.js (1)
  • contextPath (130-130)
micro-ui/web/micro-ui-internals/packages/libraries/src/hooks/pgr/useComplaintDetails.js (1)
  • window (31-31)
micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/RemovableTags.js (1)
  • sessionData (135-135)
🔇 Additional comments (1)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/LanguageSelection/index.js (1)

51-66: Don't nuke entire storages; clear non-preserved keys only and use Digit wrappers.

Full clears break other microfrontends and multi-tab sessions — remove only keys not in preserveKeys and restore preserved values via Digit wrappers (fallback to native storage).

-    // Clear both session and local storage
-    window.sessionStorage.clear();
-    window.localStorage.clear();
-    
-    // Restore preserved keys
-    Object.keys(sessionData).forEach(key => {
-      window.Digit.SessionStorage.set(key, sessionData[key]);
-    });
-    
-    Object.keys(localData).forEach(key => {
-      if (window.Digit.LocalStorage?.set) {
-        window.Digit.LocalStorage.set(key, localData[key]);
-      } else {
-        localStorage.setItem(key, localData[key]);
-      }
-    });
+    // Clear storages non-destructively: remove only non-preserved keys
+    const clearExcept = (storage, preserve) => {
+      const keys = [];
+      for (let i = 0; i < storage.length; i++) keys.push(storage.key(i));
+      keys.forEach((k) => {
+        if (!preserve.includes(k)) storage.removeItem(k);
+      });
+    };
+    clearExcept(window.sessionStorage, preserveKeys);
+    clearExcept(window.localStorage, preserveKeys);
+
+    // Restore preserved keys via Digit wrappers if available
+    Object.keys(sessionData).forEach((key) => {
+      if (window.Digit?.SessionStorage?.set) {
+        window.Digit.SessionStorage.set(key, sessionData[key]);
+      } else {
+        window.sessionStorage.setItem(key, sessionData[key]);
+      }
+    });
+
+    Object.keys(localData).forEach((key) => {
+      if (window.Digit?.LocalStorage?.set) {
+        window.Digit.LocalStorage.set(key, localData[key]);
+      } else {
+        window.localStorage.setItem(key, localData[key]);
+      }
+    });

Comment on lines +30 to 32
const handleSubmit = (event) => {
history.push(`/${getContextPath(window.contextPath)}/user/login?ts=${Date.now()}`);
};
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

Avoid double slashes in login URL when contextPath is empty.

Assemble the base path conditionally after sanitizing contextPath.

-  const handleSubmit = (event) => {    
-    history.push(`/${getContextPath(window.contextPath)}/user/login?ts=${Date.now()}`);
-  };
+  const handleSubmit = () => {
+    const ctx = getContextPath(window.contextPath);
+    const base = ctx ? `/${ctx}` : "";
+    history.push(`${base}/user/login?ts=${Date.now()}`);
+  };
📝 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 handleSubmit = (event) => {
history.push(`/${getContextPath(window.contextPath)}/user/login?ts=${Date.now()}`);
};
const handleSubmit = () => {
const ctx = getContextPath(window.contextPath);
const base = ctx ? `/${ctx}` : "";
history.push(`${base}/user/login?ts=${Date.now()}`);
};
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/LanguageSelection/index.js
around lines 30 to 32, the history.push constructs a URL that can contain a
double slash when getContextPath(window.contextPath) returns an empty string;
sanitize the contextPath (trim leading/trailing slashes) and conditionally
assemble the base path so you only prefix a single slash when a context path
exists (e.g., compute a base string that's either '' or '/sanitizedContext' then
append '/user/login?ts=...' ), and use that base in history.push to guarantee no
double slashes.

Comment on lines +35 to +82
// Clear old data and refresh localization when accessing login page
useEffect(() => {
// Clear expired/stale data but preserve essential config
const preserveKeys = ['Employee.tenantId', 'Citizen.tenantId', 'CONTEXT_PATH'];
const sessionData = {};
const localData = {};

// Backup preserved keys from both storages
preserveKeys.forEach(key => {
const sessionValue = window.Digit.SessionStorage.get(key);
const localValue = window.Digit.LocalStorage?.get(key) || localStorage.getItem(key);

if (sessionValue) sessionData[key] = sessionValue;
if (localValue) localData[key] = localValue;
});

// Clear both session and local storage
window.sessionStorage.clear();
window.localStorage.clear();

// Restore preserved keys
Object.keys(sessionData).forEach(key => {
window.Digit.SessionStorage.set(key, sessionData[key]);
});

Object.keys(localData).forEach(key => {
if (window.Digit.LocalStorage?.set) {
window.Digit.LocalStorage.set(key, localData[key]);
} else {
localStorage.setItem(key, localData[key]);
}
});

// Clear React Query cache for fresh data
if (window.Digit?.QueryClient) {
window.Digit.QueryClient.clear();
}

// Clear API cache service
if (window.Digit?.ApiCacheService) {
window.Digit.ApiCacheService.clearAllCache();
}

// Trigger fresh localization loading
if (window.Digit?.Localization) {
window.Digit.Localization.invalidateLocalizationCache();
}
}, []); // Run only once when component mounts
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Operational check: confirm broad cache/storage resets don’t regress other MFEs or tabs.

This runs on mount and affects origin-wide storage. Validate with multi-tab sessions and embedded host apps; add telemetry for unexpected clears if possible.

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/LanguageSelection/index.js
around lines 35 to 82, the mount effect clears origin-wide storage which can
disrupt other MFEs or open tabs; restrict the clearing to avoid regressions, add
telemetry, and coordinate across tabs: only run the clearing when the current
window is on the intended login route and not embedded (e.g., check
window.location.pathname and a host-embedding flag), limit clear() to removing
keys except the preservedKeys rather than wiping entire storages, broadcast
intent via BroadcastChannel or set a sentinel in localStorage and skip if
another tab already processed it, and emit a telemetry event (success/failure
and which keys were removed) before/after the clear so host apps can detect
unexpected clears.

Comment on lines +69 to +81
if (window.Digit?.QueryClient) {
window.Digit.QueryClient.clear();
}

// Clear API cache service
if (window.Digit?.ApiCacheService) {
window.Digit.ApiCacheService.clearAllCache();
}

// Trigger fresh localization loading
if (window.Digit?.Localization) {
window.Digit.Localization.invalidateLocalizationCache();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Guard optional APIs before invoking.

Ensure methods exist to avoid runtime errors across envs where these services may be undefined or shaped differently.

-    if (window.Digit?.QueryClient) {
-      window.Digit.QueryClient.clear();
-    }
+    if (typeof window.Digit?.QueryClient?.clear === "function") {
+      window.Digit.QueryClient.clear();
+    }
...
-    if (window.Digit?.ApiCacheService) {
-      window.Digit.ApiCacheService.clearAllCache();
-    }
+    if (typeof window.Digit?.ApiCacheService?.clearAllCache === "function") {
+      window.Digit.ApiCacheService.clearAllCache();
+    }
...
-    if (window.Digit?.Localization) {
-      window.Digit.Localization.invalidateLocalizationCache();
-    }
+    if (typeof window.Digit?.Localization?.invalidateLocalizationCache === "function") {
+      window.Digit.Localization.invalidateLocalizationCache();
+    }
📝 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
if (window.Digit?.QueryClient) {
window.Digit.QueryClient.clear();
}
// Clear API cache service
if (window.Digit?.ApiCacheService) {
window.Digit.ApiCacheService.clearAllCache();
}
// Trigger fresh localization loading
if (window.Digit?.Localization) {
window.Digit.Localization.invalidateLocalizationCache();
}
if (typeof window.Digit?.QueryClient?.clear === "function") {
window.Digit.QueryClient.clear();
}
// Clear API cache service
if (typeof window.Digit?.ApiCacheService?.clearAllCache === "function") {
window.Digit.ApiCacheService.clearAllCache();
}
// Trigger fresh localization loading
if (typeof window.Digit?.Localization?.invalidateLocalizationCache === "function") {
window.Digit.Localization.invalidateLocalizationCache();
}
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/LanguageSelection/index.js
around lines 69 to 81, the code calls methods on optional global services
without verifying those methods exist; update each check to ensure the service
and the specific function are present (e.g., verify window.Digit?.QueryClient &&
typeof window.Digit.QueryClient.clear === 'function') before invoking, and do
the same for ApiCacheService.clearAllCache and
Localization.invalidateLocalizationCache so no runtime error occurs in
environments where the APIs are undefined or shaped differently.

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: 5

♻️ Duplicate comments (11)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js (9)

19-26: Harden OTP URL builder (avoid //, ensure tenant, normalize path).

Replace ad-hoc splitting with stable join; fall back to defaultTenant when tenantId is falsy.

-  function buildOtpUrl(contextPath, tenantId) {
-    const ctx = (contextPath || "").split("/").filter(Boolean).join("/");
-    if (ctx.includes("/")) {
-      return `/${ctx}/employee/user/login/otp`;
-    } else {
-      return `/${ctx}/${tenantId}/employee/user/login/otp`;
-    }
-  }
+  function buildOtpUrl(contextPath, tenantId) {
+    const ctx = (contextPath || "").replace(/^\/+|\/+$/g, "");
+    const resolvedTenant = tenantId || defaultTenant;
+    const parts = [ctx, resolvedTenant, "employee", "user", "login", "otp"].filter(Boolean);
+    return `/${parts.join("/")}`;
+  }

67-71: Avoid storing PII in sessionStorage; navigate via history with state.

Use SPA navigation and pass state instead of persisting email/tenant. If sessionStorage must be used elsewhere, ensure it’s cleared post-OTP verification.

-          sessionStorage.setItem("otpEmail", inputEmail);
-          sessionStorage.setItem("otpTenant", tenantId);
-          const url = buildOtpUrl(window?.contextPath, tenantId);
-          window.location.replace(url);
+          history.push({
+            pathname: buildOtpUrl(window?.contextPath, tenantId),
+            state: { email: inputEmail, tenant: tenantId },
+          });

I can add removal of any legacy sessionStorage reads/writes in the OTP page in a follow-up commit.


80-81: Use const and guard against undefined inputs.

Prevent crash on config[0].body.map when propsConfig?.inputs is missing.

-  let config = [{ body: propsConfig?.inputs }];
+  const config = [{ body: Array.isArray(propsConfig?.inputs) ? propsConfig.inputs : [] }];

137-137: Fix Toast API usage and add fallback i18n key.

Use error boolean prop; type="error" is ignored.

-      {showToast && <Toast type="error" label={t(showToast?.label)} onClose={closeToast} />}
+      {showToast && (
+        <Toast
+          error
+          label={t(showToast?.label || "CORE_COMMON_SOMETHING_WENT_WRONG")}
+          onClose={closeToast}
+        />
+      )}

162-162: Defensive access for banner images.

Prevent runtime errors when propsConfig is undefined.

-        <Carousel bannerImages={propsConfig.bannerImages} />
+        <Carousel bannerImages={propsConfig?.bannerImages || []} />

170-176: Clean up PropTypes; remove unused loginParams and declare actual props.

Align with component signature.

-Login.propTypes = {
-  loginParams: PropTypes.any,
-};
-
-Login.defaultProps = {
-  loginParams: null,
-};
+Login.propTypes = {
+  config: PropTypes.object.isRequired,
+  t: PropTypes.func.isRequired,
+  isDisabled: PropTypes.bool,
+};
+
+Login.defaultProps = {
+  isDisabled: false,
+};

13-18: Derive and reuse defaultTenant once.

Follow prior guidance: cache state ID once and reuse.

   const { stateInfo } = storeData || {};
+  // Derive once; fallback to stateInfo.code
+  const defaultTenant = Digit?.ULBService?.getStateId?.() || stateInfo?.code;

28-36: Use defaultTenant for request params.

Avoid inline getStateId calls; use the derived value.

   const reqCreate = {
     url: `/user-otp/v1/_send`,
-    params: { tenantId: Digit?.ULBService?.getStateId() },
+    params: { tenantId: defaultTenant },
     body: {},
     config: {
       enable: false,
     },
   };

82-91: Fix undefined variable and use strict equality.

defaultValue is not defined; also use ===/!==. Initialize populators defensively.

   if (
     mode === "admin" &&
-    config?.[0]?.body?.[2]?.disable == false &&
-    config?.[0]?.body?.[2]?.populators?.defaultValue == undefined
+    config?.[0]?.body?.[2]?.disable === false &&
+    config?.[0]?.body?.[2]?.populators?.defaultValue === undefined
   ) {
-    config[0].body[2].disable = true;
-    config[0].body[2].isMandatory = false;
-    config[0].body[2].populators.defaultValue = defaultValue;
+    config[0].body[2].disable = true;
+    config[0].body[2].isMandatory = false;
+    config[0].body[2].populators = config[0].body[2].populators || {};
+    config[0].body[2].populators.defaultValue = defaultTenant;
   }
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js (2)

165-171: Fix PropTypes/defaultProps to match actual props.

The component accepts config, t, and isDisabled, not loginParams.

-Login.propTypes = {
-  loginParams: PropTypes.any,
-};
-
-Login.defaultProps = {
-  loginParams: null,
-};
+Login.propTypes = {
+  config: PropTypes.object,
+  t: PropTypes.func,
+  isDisabled: PropTypes.bool,
+};
+
+Login.defaultProps = {
+  config: null,
+  t: () => {},
+  isDisabled: false,
+};

65-65: Make config immutable and guard against missing inputs.

Avoid mapping over undefined when propsConfig?.inputs isn’t an array; also follow “use const” guidance.

-  let config = [{ body: propsConfig?.inputs }];
+  const formBody = Array.isArray(propsConfig?.inputs) ? propsConfig.inputs : [];
+  const config = [{ body: formBody }];
...
-    const keys = config[0].body.map((field) => field.key);
+    const keys = (config[0]?.body || []).map((field) => field.key);

Also applies to: 79-79

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31ac522 and bc7292d.

📒 Files selected for processing (2)
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js (1 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

check

Files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
🧠 Learnings (4)
📚 Learning: 2025-06-04T04:08:48.234Z
Learnt from: jagankumar-egov
PR: egovernments/DIGIT-Frontend#2488
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login/login.js:152-152
Timestamp: 2025-06-04T04:08:48.234Z
Learning: In the employee login component (micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login/login.js), prefer using a `defaultTenant` variable to store `Digit.ULBService.getStateId()` instead of calling the function multiple times inline in the `defaultValue` object. This improves code readability and avoids function call duplication.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
📚 Learning: 2025-06-27T05:52:33.156Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/components/PrivacyComponent.js:0-0
Timestamp: 2025-06-27T05:52:33.156Z
Learning: The PrivacyComponent in `micro-ui/web/micro-ui-internals/packages/modules/core/src/components/PrivacyComponent.js` is mostly used in login and signup pages, especially in formcomposer where the UI is stable, making layout changes less likely to cause regressions.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
📚 Learning: 2024-11-07T11:02:33.520Z
Learnt from: rachana-egov
PR: egovernments/DIGIT-Frontend#1770
File: health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/pages/employee/viewVillage.js:320-322
Timestamp: 2024-11-07T11:02:33.520Z
Learning: In `health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/pages/employee/viewVillage.js`, the `data?.additionalFields` object is guaranteed to be defined, so checking for its existence before accessing its keys is unnecessary.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
📚 Learning: 2025-06-26T09:43:00.597Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js:24-24
Timestamp: 2025-06-26T09:43:00.597Z
Learning: In LoginSignupSelector component, the URL checking using window.location.href.includes("sandbox-ui/user/sign-up") is intentional for URL validation purposes, not navigation. The team prefers this approach over React Router's useLocation hook for specific URL checking needs.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js
🧬 Code graph analysis (2)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js (5)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js (15)
  • Login (10-168)
  • Digit (82-82)
  • storeData (12-12)
  • storeData (13-13)
  • showToast (14-14)
  • disable (15-15)
  • history (17-17)
  • reqCreate (28-35)
  • mutation (36-36)
  • onLogin (38-74)
  • closeToast (76-78)
  • config (80-80)
  • onFormValueChange (93-110)
  • isMobile (113-113)
  • renderFormSection (116-149)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp/signUp.js (14)
  • Login (8-121)
  • cities (9-9)
  • Digit (63-63)
  • storeData (10-10)
  • storeData (11-11)
  • showToast (12-12)
  • disable (13-13)
  • history (15-15)
  • reqCreate (17-24)
  • mutation (25-25)
  • onLogin (27-55)
  • closeToast (57-59)
  • config (61-61)
  • onFormValueChange (70-81)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/SandBoxHeader.js (3)
  • storeData (8-8)
  • storeData (9-9)
  • SandBoxHeader (7-23)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/ImageComponent.js (1)
  • ImageComponent (4-32)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/CarouselComponent/CarouselComponent.js (1)
  • Carousel (3-156)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js (5)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js (15)
  • Login (9-163)
  • storeData (11-11)
  • storeData (12-12)
  • Digit (67-67)
  • showToast (13-13)
  • disable (14-14)
  • history (16-16)
  • reqCreate (18-25)
  • mutation (26-26)
  • onLogin (28-59)
  • closeToast (61-63)
  • config (65-65)
  • onFormValueChange (78-95)
  • isMobile (98-98)
  • renderFormSection (101-145)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/OtpCustomComponent.js (4)
  • showToast (13-13)
  • reqCreate (37-44)
  • mutation (45-45)
  • closeToast (18-20)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/SandBoxHeader.js (1)
  • SandBoxHeader (7-23)
micro-ui/web/micro-ui-internals/packages/react-components/src/atoms/Toast.js (1)
  • Toast (6-50)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/ImageComponent.js (1)
  • ImageComponent (4-32)
🔇 Additional comments (2)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js (1)

38-57: Confirm OTP /user-otp/v1/_send payload and unify callers

Found mixed usage: some callsites send an empty body {} with tenantId as a query param, while others post body: { otp: { ... } }. Confirm the expected API schema with the backend and align all callers to that shape to avoid 4xx.

Callsites:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login/login.js — uses body: {} (and also constructs an otp object elsewhere).
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/OtpCustomComponent.js — uses body: {}.
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js — initial send uses body: {}, later code posts { otp: { ... } }.
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/ChangePassword/changePassword.js — posts { otp: { ... } }.
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/ForgotPassword/forgotPassword.js — posts { otp: { ... } }.
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/citizen/Login/index.js — uses sendOtp({ otp: ... }) in multiple places.
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js (1)

52-55: Confirm OTP route correctness.

SignUp-v2 intentionally navigates to /${window?.globalPath}/user/otp — App.js registers that route (packages/modules/core/src/App.js). Employee login flows use /${window?.contextPath}/employee/user/login/otp (packages/modules/core/src/pages/employee/index.js and Login/login.js, which pass isLogin). No change required unless you want the sign-up flow to land on the login-specific OTP route.

Comment on lines +38 to +41
const onLogin = async (data) => {
const inputEmail = (data?.email || "").toLowerCase();
const tenantId = data.accountName;
await mutation.mutate(
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

Validate tenantId and normalize input before mutate.

Prevent bad requests/injection by checking format and empties.

-  const onLogin = async (data) => {
-    const inputEmail = (data?.email || "").toLowerCase();
-    const tenantId = data.accountName;
+  const onLogin = async (data) => {
+    const inputEmail = (data?.email || "").toLowerCase();
+    const tenantId = String(data?.accountName || "").trim();
+    if (!tenantId) {
+      setShowToast({ key: "error", label: "CORE_TENANT_REQUIRED" });
+      setTimeout(closeToast, 4000);
+      return;
+    }
+    if (!/^[A-Za-z0-9._-]{1,50}$/.test(tenantId)) {
+      setShowToast({ key: "error", label: "CORE_TENANT_INVALID" });
+      setTimeout(closeToast, 4000);
+      return;
+    }
📝 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 onLogin = async (data) => {
const inputEmail = (data?.email || "").toLowerCase();
const tenantId = data.accountName;
await mutation.mutate(
const onLogin = async (data) => {
const inputEmail = (data?.email || "").toLowerCase();
const tenantId = String(data?.accountName || "").trim();
if (!tenantId) {
setShowToast({ key: "error", label: "CORE_TENANT_REQUIRED" });
setTimeout(closeToast, 4000);
return;
}
if (!/^[A-Za-z0-9._-]{1,50}$/.test(tenantId)) {
setShowToast({ key: "error", label: "CORE_TENANT_INVALID" });
setTimeout(closeToast, 4000);
return;
}
await mutation.mutate(
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js
around lines 38-41, validate and normalize inputs before calling mutation: trim
and lowercase the email (const inputEmail = (data?.email ||
"").trim().toLowerCase()), trim the tenantId (const tenantId =
(data?.accountName || "").trim()), verify tenantId is non-empty and matches an
allowed pattern (e.g. /^[A-Za-z0-9-_]+$/) and return or show a validation error
if it fails, and only then call mutation.mutate with the sanitized values to
prevent empty/invalid or injection-prone tenantId values.

import { useHistory } from "react-router-dom";
import SandBoxHeader from "../../../components/SandBoxHeader";
import ImageComponent from "../../../components/ImageComponent";
import Carousel from "../SignUp-v2/CarouselComponent/CarouselComponent";
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Tighten the Carousel import path.

Use a direct relative path to avoid unnecessary directory traversal.

-import Carousel from "../SignUp-v2/CarouselComponent/CarouselComponent";
+import Carousel from "./CarouselComponent/CarouselComponent";
📝 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
import Carousel from "../SignUp-v2/CarouselComponent/CarouselComponent";
import Carousel from "./CarouselComponent/CarouselComponent";
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js
around line 7, the Carousel import uses an indirect path; change it to a direct
relative path pointing exactly to the CarouselComponent file (e.g., adjust the
import to reference the component file location without unnecessary "../"
traversal) so the import resolves directly to the component module.

import Carousel from "../SignUp-v2/CarouselComponent/CarouselComponent";

const Login = ({ config: propsConfig, t, isDisabled }) => {
const { data: cities, isLoading } = Digit.Hooks.useTenants();
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

Drop unused tenants fetch; avoid extra API call and simplify loader.

useTenants() result isn’t used, yet it triggers a network call and couples loader state. Remove it and gate only on isStoreLoading.

-  const { data: cities, isLoading } = Digit.Hooks.useTenants();
...
-  if (isLoading || isStoreLoading) return <Loader />;
+  // tenants fetch removed; rely on store loading
+  if (isStoreLoading) return <Loader />;

Also applies to: 147-147

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js
around lines 10 and 147, remove the unused call to Digit.Hooks.useTenants()
(which currently does const { data: cities, isLoading } =
Digit.Hooks.useTenants();) because it triggers an unnecessary API request;
delete that destructuring and any references to its isLoading, then change the
component's loading gating to rely solely on isStoreLoading (or the existing
store loading flag) so the loader no longer depends on the tenants hook.

Comment on lines +67 to +76
const { mode } = Digit.Hooks.useQueryParams();
if (
mode === "admin" &&
config?.[0]?.body?.[2]?.disable === false &&
config?.[0]?.body?.[2]?.populators?.defaultValue === undefined
) {
config[0].body[2].disable = true;
config[0].body[2].isMandatory = false;
config[0].body[2].populators.defaultValue = defaultValue;
}
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

⚠️ Potential issue

Fix ReferenceError: defaultValue is undefined in admin mode.

Use a stable default tenant per team convention; assign it instead of the undefined defaultValue.

   const history = useHistory();
+  // Stable default tenant for admin mode
+  const defaultTenant = Digit.ULBService.getStateId();
...
-    config[0].body[2].populators.defaultValue = defaultValue;
+    config[0].body[2].populators.defaultValue = defaultTenant;

Also applies to: 16-17

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js
around lines 67-76 (also applies to lines 16-17): the code sets
config[0].body[2].populators.defaultValue to an undefined defaultValue when mode
=== "admin"; replace that undefined reference with the team’s stable default
tenant value (use the project constant/utility for default tenant, e.g.,
DEFAULT_TENANT or Digit.Config.DEFAULT_TENANT), ensure that constant is imported
or defined in this file, and set both populators.defaultValue and any other
spots (lines 16-17) to that stable default tenant while keeping disable and
isMandatory updates intact.

Comment on lines +97 to +99
// Mobile detection (simple)
const isMobile = window.innerWidth <= 768;

Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Make mobile detection responsive to resize.

window.innerWidth is evaluated once. Prefer a resize-aware approach.

-  const isMobile = window.innerWidth <= 768;
+  const [isMobile, setIsMobile] = useState(window.innerWidth <= 768);
+  useEffect(() => {
+    const onResize = () => setIsMobile(window.innerWidth <= 768);
+    window.addEventListener("resize", onResize);
+    return () => window.removeEventListener("resize", onResize);
+  }, []);

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

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js
around lines 97 to 99, the mobile check currently uses a one-time const =
window.innerWidth <= 768; — make it responsive by keeping isMobile in component
state and updating it on resize: initialize state from window.innerWidth (or use
window.matchMedia) and add a useEffect that registers a resize or media query
listener which sets isMobile accordingly; ensure the effect cleans up the
listener on unmount to avoid leaks.

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 (9)
micro-ui/web/public/index.html (5)

10-11: Add SRI + crossorigin to external CSS links

Pin integrity to mitigate supply-chain risk and ensure tamper detection.

Apply this diff:

-  <link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected]/dist/index.css" />
-  <link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected]/dist/index.css" />
+  <link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected]/dist/index.css" integrity="sha384-ujgPEZxME0vI6gml3R9fLnJBJJtuX7crZuyXk7dSmmGevF1r5ZO0O7ty3PBJPFkC" crossorigin="anonymous" />
+  <link rel="stylesheet" href="https://unpkg.com/@egovernments/[email protected]/dist/index.css" integrity="sha384-2EfdlCC1EigMYJ59qOhrK2CfvNH6nzQpPONIej1kQQqdSp4/kpLXsw9Zm3/y7XnC" crossorigin="anonymous" />

32-64: SPA tracking: make wrapper idempotent and handle hashchange

Prevents double-wrapping and tracks hash-based routes.

Apply this diff:

-    (function () {
+    (function () {
+      if (window.__matomoSpaWrapped) return;
+      window.__matomoSpaWrapped = true;
       let lastUrl = location.href;
@@
       // Listen for back/forward
       window.addEventListener('popstate', trackUrlChange);
+      // Handle hash-based routing (if any)
+      window.addEventListener('hashchange', trackUrlChange);
     })();

66-93: Click tracking: sanitize to avoid PII leakage; allow opt-out/labels

Avoid sending raw href (query/hash), ids, or long innerText; support data-analytics controls.

Apply this diff:

-      document.addEventListener("click", function (e) {
+      document.addEventListener("click", function (e) {
         const selector = `
@@
-        const target = e.target.closest(selector);
-        if (target) {
+        const target = e.target.closest(selector);
+        if (target && !target.closest('[data-analytics="off"]')) {
           const elementType = target.tagName.toLowerCase();
-          const text = target.innerText.trim() || target.value || 'Unnamed';
-          const id = target.id || 'no-id';
-          const classes = target.className || 'no-class';
-          const href = target.getAttribute('href') || '';
+          const rawText = (target.innerText || target.value || '').trim();
+          const text = rawText.slice(0, 120) || 'Unnamed';
+          const id = target.id ? 'has-id' : 'no-id';
+          const classes = (target.dataset && target.dataset.analyticsLabel) ? target.dataset.analyticsLabel : 'generic';
+          const hrefAttr = target.getAttribute('href') || '';
+          let sanitizedHref = '';
+          try {
+            const u = new URL(hrefAttr, location.href);
+            sanitizedHref = u.origin + u.pathname;
+          } catch (_) {}
@@
-            `Text: ${text}, ID: ${id}, Class: ${classes}, Href: ${href}`
+            `Text: ${text}, ID: ${id}, Label: ${classes}, Href: ${sanitizedHref}`
           ]);
         }
       });

98-117: Style block is outside and centering is incorrect

Move CSS into , add font fallbacks, and use transform for centering.

Apply this diff to remove the misplaced block:

-<style>
-  .digit-header-content.label .info-icon .infotext {
-    position: absolute;
-    text-align: center;
-    font-family: Roboto;
-    font-style: normal;
-    font-weight: 400;
-    line-height: 1.37rem;
-    visibility: hidden;
-    width: 7.5rem;
-    background-color: #555;
-    color: #fff;
-    padding: .5rem 0;
-    z-index: 1;
-    bottom: 100%;
-    left: 50%;
-    margin-left: -10.3rem;
-    border-radius: .375rem;
-  }
-</style>

Insert this inside the before :

<style>
  .digit-header-content.label .info-icon .infotext {
    position: absolute;
    text-align: center;
    font-family: "Roboto", Arial, sans-serif;
    font-style: normal;
    font-weight: 400;
    line-height: 1.37rem;
    visibility: hidden;
    width: 7.5rem;
    background-color: #555;
    color: #fff;
    padding: .5rem 0;
    z-index: 1;
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    border-radius: .375rem;
  }
</style>

19-31: Reorder Matomo queue: set trackerUrl/siteId before trackPageView; add privacy hardening

Current order risks missing the initial PV and weak privacy defaults. Move tracking after configuration; add anonymization and DNT.

Apply this diff:

     var _paq = window._paq = window._paq || [];
     /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
     _paq.push(["setExcludedQueryParams", ["account", "accountnum", "address", "address1", "address2", "address3", "addressline1", "addressline2", "adres", "adresse", "age", "alter", "auth", "authpw", "bic", "billingaddress", "billingaddress1", "billingaddress2", "calle", "cardnumber", "cc", "ccc", "cccsc", "cccvc", "cccvv", "ccexpiry", "ccexpmonth", "ccexpyear", "ccname", "ccnumber", "cctype", "cell", "cellphone", "city", "clientid", "clientsecret", "company", "consumerkey", "consumersecret", "contrasenya", "contraseña", "creditcard", "creditcardnumber", "cvc", "cvv", "dateofbirth", "debitcard", "dirección", "dob", "domain", "ebost", "email", "emailaddress", "emailadresse", "epos", "epost", "eposta", "exp", "familyname", "firma", "firstname", "formlogin", "fullname", "gender", "geschlecht", "gst", "gstnumber", "handynummer", "hasło", "heslo", "iban", "ibanaccountnum", "ibanaccountnumber", "id", "identifier", "indirizzo", "kartakredytowa", "kennwort", "keyconsumerkey", "keyconsumersecret", "konto", "kontonr", "kontonummer", "kredietkaart", "kreditkarte", "kreditkort", "lastname", "login", "mail", "mobiili", "mobile", "mobilne", "nachname", "name", "nickname", "osoite", "parole", "pass", "passord", "password", "passwort", "pasword", "paswort", "paword", "phone", "pin", "plz", "postalcode", "postcode", "postleitzahl", "privatekey", "publickey", "pw", "pwd", "pword", "pwrd", "rue", "secret", "secretq", "secretquestion", "shippingaddress", "shippingaddress1", "shippingaddress2", "socialsec", "socialsecuritynumber", "socsec", "sokak", "ssn", "steuernummer", "strasse", "street", "surname", "swift", "tax", "taxnumber", "tel", "telefon", "telefonnr", "telefonnummer", "telefono", "telephone", "token", "token_auth", "tokenauth", "téléphone", "ulica", "user", "username", "vat", "vatnumber", "via", "vorname", "wachtwoord", "wagwoord", "webhooksecret", "website", "zip", "zipcode"]]);
-    _paq.push(['trackPageView']);
-    _paq.push(['enableLinkTracking']);
+    _paq.push(['disableCookies']);
+    _paq.push(['anonymizeIp']);
+    _paq.push(['setDoNotTrack', true]);
+    _paq.push(['setCookieSameSite', 'Lax']);
     (function () {
       var u = window?.globalConfigs?.getConfig("MATOMO_URL") || "https://unified-demo.digit.org/matomo/";
       _paq.push(['setTrackerUrl', u + 'matomo.php']);
       _paq.push(['setSiteId', '1']);
+      _paq.push(['enableLinkTracking']);
+      _paq.push(['trackPageView']);
       var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
       g.async = true; g.src = u + 'matomo.js';
       s.parentNode.insertBefore(g, s);
     })();
micro-ui/web/micro-ui-internals/example/public/index.html (4)

19-33: Reorder Matomo queue and add privacy settings (same as main index.html)

Ensure siteId/trackerUrl are set before initial PV; anonymize and honor DNT.

Apply this diff:

     var _paq = window._paq = window._paq || [];
     /* tracker methods like "setCustomDimension" should be called before "trackPageView" */
     _paq.push(["setExcludedQueryParams", ["account", "accountnum", "address", "address1", "address2", "address3", "addressline1", "addressline2", "adres", "adresse", "age", "alter", "auth", "authpw", "bic", "billingaddress", "billingaddress1", "billingaddress2", "calle", "cardnumber", "cc", "ccc", "cccsc", "cccvc", "cccvv", "ccexpiry", "ccexpmonth", "ccexpyear", "ccname", "ccnumber", "cctype", "cell", "cellphone", "city", "clientid", "clientsecret", "company", "consumerkey", "consumersecret", "contrasenya", "contraseña", "creditcard", "creditcardnumber", "cvc", "cvv", "dateofbirth", "debitcard", "dirección", "dob", "domain", "ebost", "email", "emailaddress", "emailadresse", "epos", "epost", "eposta", "exp", "familyname", "firma", "firstname", "formlogin", "fullname", "gender", "geschlecht", "gst", "gstnumber", "handynummer", "hasło", "heslo", "iban", "ibanaccountnum", "ibanaccountnumber", "id", "identifier", "indirizzo", "kartakredytowa", "kennwort", "keyconsumerkey", "keyconsumersecret", "konto", "kontonr", "kontonummer", "kredietkaart", "kreditkarte", "kreditkort", "lastname", "login", "mail", "mobiili", "mobile", "mobilne", "nachname", "name", "nickname", "osoite", "parole", "pass", "passord", "password", "passwort", "pasword", "paswort", "paword", "phone", "pin", "plz", "postalcode", "postcode", "postleitzahl", "privatekey", "publickey", "pw", "pwd", "pword", "pwrd", "rue", "secret", "secretq", "secretquestion", "shippingaddress", "shippingaddress1", "shippingaddress2", "socialsec", "socialsecuritynumber", "socsec", "sokak", "ssn", "steuernummer", "strasse", "street", "surname", "swift", "tax", "taxnumber", "tel", "telefon", "telefonnr", "telefonnummer", "telefono", "telephone", "token", "token_auth", "tokenauth", "téléphone", "ulica", "user", "username", "vat", "vatnumber", "via", "vorname", "wachtwoord", "wagwoord", "webhooksecret", "website", "zip", "zipcode"]]);
-    _paq.push(['trackPageView']);
-    _paq.push(['enableLinkTracking']);
+    _paq.push(['disableCookies']);
+    _paq.push(['anonymizeIp']);
+    _paq.push(['setDoNotTrack', true]);
+    _paq.push(['setCookieSameSite', 'Lax']);
     (function () {
       var u = window?.globalConfigs?.getConfig("MATOMO_URL") || "https://unified-demo.digit.org/matomo/";
       _paq.push(['setTrackerUrl', u + 'matomo.php']);
       _paq.push(['setSiteId', '1']);
+      _paq.push(['enableLinkTracking']);
+      _paq.push(['trackPageView']);
       var d = document, g = d.createElement('script'), s = d.getElementsByTagName('script')[0];
       g.async = true; g.src = u + 'matomo.js';
       s.parentNode.insertBefore(g, s);
     })();

34-66: SPA wrapper idempotency + hashchange support

Mirror the main index improvement to avoid double-wrapping and cover hash routes.

Apply this diff:

-    (function () {
+    (function () {
+      if (window.__matomoSpaWrapped) return;
+      window.__matomoSpaWrapped = true;
       let lastUrl = location.href;
@@
       window.addEventListener('popstate', trackUrlChange);
+      window.addEventListener('hashchange', trackUrlChange);
     })();

68-95: Sanitize click tracking payload

Prevent accidental PII/token capture; support opt-out/labels.

Apply this diff (same as main index):

-        if (target) {
+        if (target && !target.closest('[data-analytics="off"]')) {
           const elementType = target.tagName.toLowerCase();
-          const text = target.innerText.trim() || target.value || 'Unnamed';
-          const id = target.id || 'no-id';
-          const classes = target.className || 'no-class';
-          const href = target.getAttribute('href') || '';
+          const rawText = (target.innerText || target.value || '').trim();
+          const text = rawText.slice(0, 120) || 'Unnamed';
+          const id = target.id ? 'has-id' : 'no-id';
+          const classes = (target.dataset && target.dataset.analyticsLabel) ? target.dataset.analyticsLabel : 'generic';
+          const hrefAttr = target.getAttribute('href') || '';
+          let sanitizedHref = '';
+          try { const u = new URL(hrefAttr, location.href); sanitizedHref = u.origin + u.pathname; } catch (_) {}
@@
-            `Text: ${text}, ID: ${id}, Class: ${classes}, Href: ${href}`
+            `Text: ${text}, ID: ${id}, Label: ${classes}, Href: ${sanitizedHref}`

101-120: Move style into and fix centering/font fallback

Same structural/a11y nits as main index.

Apply this diff to remove the misplaced block:

-<style>
-  .digit-header-content.label .info-icon .infotext {
-    position: absolute;
-    text-align: center;
-    font-family: Roboto;
-    font-style: normal;
-    font-weight: 400;
-    line-height: 1.37rem;
-    visibility: hidden;
-    width: 7.5rem;
-    background-color: #555;
-    color: #fff;
-    padding: .5rem 0;
-    z-index: 1;
-    bottom: 100%;
-    left: 50%;
-    margin-left: -6.5rem;
-    border-radius: .375rem;
-  }
-</style>

Insert inside :

<style>
  .digit-header-content.label .info-icon .infotext {
    position: absolute;
    text-align: center;
    font-family: "Roboto", Arial, sans-serif;
    font-style: normal;
    font-weight: 400;
    line-height: 1.37rem;
    visibility: hidden;
    width: 7.5rem;
    background-color: #555;
    color: #fff;
    padding: .5rem 0;
    z-index: 1;
    bottom: 100%;
    left: 50%;
    transform: translateX(-50%);
    border-radius: .375rem;
  }
</style>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc7292d and 7560424.

📒 Files selected for processing (2)
  • micro-ui/web/micro-ui-internals/example/public/index.html (2 hunks)
  • micro-ui/web/public/index.html (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-02-05T10:26:52.452Z
Learnt from: Tulika-eGov
PR: egovernments/DIGIT-Frontend#2188
File: micro-ui/web/micro-ui-internals/packages/modules/pgr/src/configs/UICustomizations.js:22-130
Timestamp: 2025-02-05T10:26:52.452Z
Learning: UI customizations in DIGIT-Frontend should not be restructured at this time as a new methodology for formcomposer will be implemented in the future.

Applied to files:

  • micro-ui/web/public/index.html
📚 Learning: 2025-03-25T10:47:10.916Z
Learnt from: Tulika-eGov
PR: egovernments/DIGIT-Frontend#2288
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Landing/LandingComponent.js:33-33
Timestamp: 2025-03-25T10:47:10.916Z
Learning: In DIGIT-Frontend, window.contextPath is dynamically determined and cannot be given a static fallback value like 'digit-ui'. The application relies on this dynamic context path for proper routing.

Applied to files:

  • micro-ui/web/public/index.html

<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#00bcd1" />
<title>DIGIT</title>
<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Defer third-party script to avoid blocking render

xlsx can be deferred without breaking behavior.

Apply this diff:

-  <script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
+  <script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js" defer></script>
📝 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
<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js"></script>
<script src="https://unpkg.com/xlsx/dist/xlsx.full.min.js" defer></script>
🤖 Prompt for AI Agents
In micro-ui/web/public/index.html around line 15, the external xlsx script is
included without defer which blocks HTML parsing; update the script tag to
include the defer attribute (i.e., <script
src="https://unpkg.com/xlsx/dist/xlsx.full.min.js" defer></script>) so the
browser can continue parsing and the script will execute after the document is
parsed; ensure this change preserves any required execution order with other
scripts on the page.

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: 21

Caution

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

⚠️ Outside diff range comments (5)
micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss (5)

7-15: Fix overridden margin: margin-bottom is nulled by later margin: unset

margin: unset !important; wipes out the earlier margin-bottom: 1.5rem;. Keep one intent.

 .sandbox-module-card {
   min-height: 263px !important;
   width: 263px !important;
-  margin-bottom: 1.5rem;
+  margin-bottom: 1.5rem;
   background-color: #fff;
   padding: unset !important;
-  margin: unset !important;
+  /* remove blanket margin reset to preserve bottom spacing */
+  /* margin: unset !important; */
   position: relative;
 }

17-25: Remove duplicate width declarations

width: 100% is overridden by width: auto below. Keep only the intended one.

 .sandbox-module-button {
   position: absolute;
-  width: 100%;
   bottom: 1.5rem;
   left: 1rem;
   right: 1rem;
   margin: auto;
-  width: auto;
+  width: auto;
 }

593-596: Missing class dot: unintended type selector

digit-card-text.center lacks a leading . and targets a non-existent element, not the .digit-card-text class. Fix selector.

-digit-card-text.center {
+.digit-card-text.center {
   text-align: center;
 }

806-836: Honor reduced-motion preference for loader animation

Offer a no/low-animation fallback for users with motion sensitivity.

 .sandbox-loader {
   width: 50px;
   aspect-ratio: 1;
   display: grid;
   border-radius: 50%;
   background: linear-gradient(0deg, rgba(0, 0, 0, 0.5) 30%, transparent 0 70%, rgba(0, 0, 0, 1) 0) 50% / 8% 100%,
     linear-gradient(90deg, rgba(0, 0, 0, 0.25) 30%, transparent 0 70%, rgba(0, 0, 0, 0.75) 0) 50% / 100% 8%;
   background-repeat: no-repeat;
   animation: l23 1s infinite steps(12);
 }
+@media (prefers-reduced-motion: reduce) {
+  .sandbox-loader {
+    animation: none;
+  }
+}

 @keyframes l23 {
   100% {
     transform: rotate(1turn);
   }
 }

Also applies to: 838-846


1466-1468: Invalid color value: quoted hex string

color: "#c84c0e"; is a string and will be ignored. Use the hex value (or a CSS variable) without quotes.

 .highlight-sandbox {
-  color: "#c84c0e";
+  color: #c84c0e; /* or var(--digit-accent-color, #c84c0e) */
   /* or your specific orange color code */
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7560424 and 32c0374.

📒 Files selected for processing (7)
  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss (19 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/config/LandingPageConfig.js (1 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/OtpCustomComponent.js (1 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.js (4 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/CarouselComponent/CarouselComponent.js (1 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/Landing/LandingComponent.js (5 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

check

Files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/CarouselComponent/CarouselComponent.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/OtpCustomComponent.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/config/LandingPageConfig.js
  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js
  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.js
  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/Landing/LandingComponent.js
🧠 Learnings (10)
📚 Learning: 2025-08-19T07:07:45.217Z
Learnt from: syedkaifegov
PR: egovernments/DIGIT-Frontend#3012
File: micro-ui/web/micro-ui-internals/packages/css/src/index.scss:692-696
Timestamp: 2025-08-19T07:07:45.217Z
Learning: CSS changes for .employee-app-wrapper.sandbox are specific to Urban repo's sandbox branch implementation, not related to sandbox-login-page components found in the main codebase.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js
  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
📚 Learning: 2024-11-07T11:02:33.520Z
Learnt from: rachana-egov
PR: egovernments/DIGIT-Frontend#1770
File: health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/pages/employee/viewVillage.js:320-322
Timestamp: 2024-11-07T11:02:33.520Z
Learning: In `health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/pages/employee/viewVillage.js`, the `data?.additionalFields` object is guaranteed to be defined, so checking for its existence before accessing its keys is unnecessary.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js
📚 Learning: 2025-06-26T09:43:00.597Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js:24-24
Timestamp: 2025-06-26T09:43:00.597Z
Learning: In LoginSignupSelector component, the URL checking using window.location.href.includes("sandbox-ui/user/sign-up") is intentional for URL validation purposes, not navigation. The team prefers this approach over React Router's useLocation hook for specific URL checking needs.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js
📚 Learning: 2025-06-26T10:21:42.619Z
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:279-280
Timestamp: 2025-06-26T10:21:42.619Z
Learning: In the sandbox ProductDetailsComponentUpdated.js component, string concatenation is intentionally used for translation keys (e.g., `t(`${module}` + "_SECTION1_ROLE_1")`) instead of template literals for easier config identification and to make the key structure more explicit.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js
📚 Learning: 2024-10-26T15:11:49.938Z
Learnt from: nabeelmd-eGov
PR: egovernments/DIGIT-Frontend#1680
File: health/micro-ui/web/micro-ui-internals/example/src/UICustomizations.js:1032-1094
Timestamp: 2024-10-26T15:11:49.938Z
Learning: In the DIGIT-Frontend project, when reviewing the `UICustomizations.js` file, avoid suggesting refactoring of repetitive rendering logic in switch cases into helper functions, unless explicitly requested.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.js
  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
📚 Learning: 2025-06-04T04:08:48.234Z
Learnt from: jagankumar-egov
PR: egovernments/DIGIT-Frontend#2488
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login/login.js:152-152
Timestamp: 2025-06-04T04:08:48.234Z
Learning: In the employee login component (micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login/login.js), prefer using a `defaultTenant` variable to store `Digit.ULBService.getStateId()` instead of calling the function multiple times inline in the `defaultValue` object. This improves code readability and avoids function call duplication.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.js
📚 Learning: 2025-06-26T10:17:02.717Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.js:0-0
Timestamp: 2025-06-26T10:17:02.717Z
Learning: In the DIGIT Frontend project, banner images and similar configuration data should be managed through MDMS (Master Data Management Service) rather than hardcoded arrays or separate config files, as indicated by the user Hari-egov.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.js
📚 Learning: 2024-10-17T11:59:00.327Z
Learnt from: Ramkrishna-egov
PR: egovernments/DIGIT-Frontend#1545
File: health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/ActivityCard.js:1-1
Timestamp: 2024-10-17T11:59:00.327Z
Learning: In `ActivityCard.js`, `React.Fragment` is used in the code.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/Landing/LandingComponent.js
📚 Learning: 2025-06-26T10:24:08.628Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss:1642-1662
Timestamp: 2025-06-26T10:24:08.628Z
Learning: In the sandbox SCSS file, tab styling with margin-bottom and border changes that cause layout shifts are intentional design requirements per updated UI specifications, not bugs to be fixed.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
📚 Learning: 2025-06-26T10:16:36.004Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/css/src/components/Carousel.scss:0-0
Timestamp: 2025-06-26T10:16:36.004Z
Learning: In the DIGIT-Frontend codebase, signup-container CSS classes cannot be namespaced under parent classes due to existing style constraints and requirements.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
🧬 Code graph analysis (3)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js (2)
micro-ui/web/micro-ui-internals/packages/modules/workbench/src/pages/employee/index.js (2)
  • location (79-79)
  • App (78-149)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/index.js (1)
  • ProductDetails (8-39)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.js (6)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login/login.js (7)
  • setEmployeeDetail (10-24)
  • Digit (159-159)
  • showToast (31-31)
  • user (30-30)
  • closeToast (143-145)
  • history (35-35)
  • config (157-157)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/signUp.js (7)
  • Digit (67-67)
  • showToast (13-13)
  • closeToast (61-63)
  • history (16-16)
  • isMobile (98-98)
  • renderFormSection (101-145)
  • config (65-65)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/index.js (2)
  • mdmsBannerImages (77-115)
  • history (24-24)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/SandBoxHeader.js (1)
  • SandBoxHeader (7-23)
micro-ui/web/micro-ui-internals/packages/react-components/src/atoms/Toast.js (1)
  • Toast (6-50)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/ImageComponent.js (1)
  • ImageComponent (4-32)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/Landing/LandingComponent.js (3)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/Landing/SupaDemoPlayer.js (1)
  • SupaDemoPlayer (21-40)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/Landing/SandboxConfig.js (2)
  • sandboxConfig (1-85)
  • sandboxConfig (1-85)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/Landing/index.js (1)
  • sandboxConfig (6-19)
🪛 Biome (2.1.2)
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/CarouselComponent/CarouselComponent.js

[error] 137-151: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)


[error] 165-193: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)


[error] 197-212: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)

⏰ 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 (38)
micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss (13)

89-93: Align selector naming to kebab-case

.SandboxLogoAlignCenter is inconsistent with prevailing kebab-case in this file. Rename to .sandbox-logo-align-center and update usages.

-.SandboxLogoAlignCenter {
+.sandbox-logo-align-center {
   align-items: center;
   justify-content: center;
   display: flex;
 }

121-125: Add modern viewport fallbacks (svh/dvh) and sensible min/max for dynamic mobile UIs

Prior feedback already requested svh/dvh and constraints for these wrappers. Please apply it here as well.

Also applies to: 186-196


153-156: Avoid rigid fixed dimensions on cards

650×500 with !important is too rigid; prefer responsive constraints as suggested earlier.


235-244: Clean duplicate font-family, reduce large top margin, use theme color token

This was previously flagged; please apply the same cleanups here.


273-279: Mobile signup wrapper uses 100vh without small-viewport fallback

Reuse the earlier svh/dvh recommendation for consistent behavior on mobile browsers.


283-319: Tooltip should respond to keyboard focus and reduced motion

Earlier guidance covered adding :focus-within and prefers-reduced-motion handling. Please apply it here.


321-334: Don’t nuke layout with all: unset; replace grid-gap

Use targeted resets and gap. This matches the prior recommendation.


346-349: Use CSS variable for brand color

Replace hardcoded #0B4B66 with the project token as previously suggested.


623-623: Typo in comment (“components components”)

Apply the earlier nit to fix the duplicate word.


1958-1966: Underline width/comment mismatch and hardcoded color

Earlier review covered making the underline track text width and using a color variable. Apply the same fix here.


1879-1886: Responsive padding for .role-block

Large horizontal padding may overflow on small screens; prior suggestion adds a mobile override. Please apply it.


2105-2110: Conflicting height and min-height on iframe

This was previously flagged; keep only the intended constraint.


2118-2157: Add focus-visible/active styles for breadcrumbs

Apply the earlier a11y enhancements for keyboard users and current-page indication.

micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/SignUp-v2/CarouselComponent/CarouselComponent.js (3)

137-153: Add explicit type and labels to the left arrow button.

Missing type="button" (Biome error) and accessible name.

-        <button
+        <button
+          type="button"
+          aria-label="Previous slide"
+          title="Previous slide"
           onClick={goToPrev}
@@
-        >
-
+        >
+          <span aria-hidden="true">‹</span>
         </button>

165-193: Add explicit type and ARIA to dot buttons.

Missing type="button" (Biome error). Also add aria-label and aria-current for SR users.

-            <button
+            <button
+              type="button"
               key={index}
               onClick={() => goToSlide(index)}
+              aria-label={`Go to slide ${index + 1}`}
+              aria-current={index === currentIndex ? 'true' : undefined}
               style={{

197-213: Add explicit type and labels to the right arrow button.

Same Biome error as left arrow. Add accessible name.

-        <button
+        <button
+          type="button"
+          aria-label="Next slide"
+          title="Next slide"
           onClick={goToNext}
@@
-        >
-
+        >
+          <span aria-hidden="true">›</span>
         </button>
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js (6)

4-4: LGTM! Proper useLocation import.

The addition of useLocation hook from react-router-dom is correctly imported and provides access to the current location object, which is appropriately used for conditional rendering logic in the component.


39-39: Simplify unnecessary ternary expressions.

The ternary operators are unnecessary as the conditions already evaluate to boolean values.

Apply this diff to simplify the expressions:

-      show: location.pathname.includes("productDetailsPage") || location.pathname.includes("productPage") ? true : false,
+      show: location.pathname.includes("productDetailsPage") || location.pathname.includes("productPage"),
-      show: location.pathname.includes("productDetailsPage") ? true : false,
+      show: location.pathname.includes("productDetailsPage"),

Also applies to: 44-44


64-68: LGTM! Clean conditional class logic implementation.

The implementation correctly uses useLocation to access the pathname and conditionally applies the "ground-container" class based on the current route. The logic is clear and follows the pattern established in similar components within the codebase.


70-72: LGTM! Proper wrapper structure with conditional styling.

The addition of the "employee-ui" wrapper div and the conditional application of the "ground-container" class to AppContainer provides better layout control. The conditional logic correctly removes the ground-container class for product detail pages, which aligns with the UI requirements.


97-113: Consider cleaning up commented code and verify ProductDetails integration.

The new ProductDetails route implementation with isUpdated={true} looks correct and properly passes the prop to enable the updated component variant. However, the commented-out code should be removed once the new implementation is confirmed stable.

#!/bin/bash
# Description: Verify ProductDetails component properly handles the isUpdated prop and that the old route can be safely removed

# Check if ProductDetailsComponentUpdated is properly imported and used in ProductDetails/index.js
ast-grep --pattern $'import $_ from "$_ProductDetailsComponentUpdated$_"'

# Verify the isUpdated prop is handled correctly in ProductDetails component
ast-grep --pattern $'const ProductDetails = ({ isUpdated$$$}) => {
  $$$
}'

24-27: LGTM! Clean implementation of route-based module extraction.

The code properly extracts the module from the pathname using split('/').pop() and includes appropriate spacing adjustments. The implementation is consistent with the routing structure established elsewhere in the file.

micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/OtpCustomComponent.js (2)

80-80: No-op whitespace change.

Nothing to review here.


84-85: Verify i18n keys exist across locales. t("SANDBOX_ENTER_OTP") and t("CS_OTP_EMAIL") are used in micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/OtpCustomComponent.js (lines 83–84); repository search found the usages but no matching entries in locale/translation files — confirm these keys exist in all supported locale files to avoid runtime fallbacks.

micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.js (13)

2-8: Imports look fine

No issues spotted with the reorganized imports.


40-45: MDMS role URL extraction LGTM
Null-safe selection and RoleLandingUrl derivation look correct.


55-57: Passing email/tenant to OtpComponent is appropriate
No issues here.


168-170: Navigation logic LGTM
Redirects post‑auth look consistent with the MDMS landing URL usage.


182-184: Error mapping LGTM
The error prioritization order is fine.


11-11: Add rationale for multi-root tenant early return
Clarify why employee details aren’t set for multi-root tenants.

Apply:

   const setEmployeeDetail = (userObject, token) => {
-  if (Digit.Utils.getMultiRootTenant()) return;
+  // Skip setting employee details for multi-root tenants; their session is managed differently.
+  if (Digit.Utils.getMultiRootTenant()) return;

59-96: Hardcoded banner images — move to MDMS (as per team guidance)
Per earlier guidance (Hari‑egov), banner images must be MDMS‑driven, not hardcoded. Remove this array and rely on MDMS response.

       ],
-      bannerImages:  [{
-      id: 1,
-      image: 'https://images.unsplash.com/photo-1746277121508-f44615ff09bb',
-      title: 'Digital Headquarters for National Health Agencies',
-      description: "Set up and configure multiple campaigns, access real-time data dashboards, manage centralized help desks and complaints, and easily integrate with DHIS2 and other open-source products."
-    },
-    {
-      id: 2,
-      image: 'https://images.unsplash.com/photo-1581094271901-8022df4466f9',
-      title: 'Digital Headquarters for National Health Agencies',
-      description: "Set up and configure multiple campaigns, access real-time data dashboards, manage centralized help desks and complaints, and easily integrate with DHIS2 and other open-source products."
-    },
-    {
-      id: 3,
-      image: 'https://images.unsplash.com/photo-1624555130581-1d9cca783bc0',
-      title: 'Digital Headquarters for National Health Agencies',
-      description: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate aut autem aperiam et modi saepe obcaecati doloremque voluptatem iusto quidem!"
-    },
-    {
-      id: 4,
-      image: 'https://images.unsplash.com/photo-1547481887-a26e2cacb5b2',
-      title: 'Digital Headquarters for National Health Agencies',
-      description: "Set up and configure multiple campaigns, access real-time data dashboards, manage centralized help desks and complaints, and easily integrate with DHIS2 and other open-source products."
-    },
-    {
-      id: 5,
-      image: 'https://images.unsplash.com/photo-1536782376847-5c9d14d97cc0',
-      title: 'Digital Headquarters for National Health Agencies',
-      description: "Set up and configure multiple campaigns, access real-time data dashboards, manage centralized help desks and complaints, and easily integrate with DHIS2 and other open-source products."
-    },
-    {
-      id: 6,
-      image: 'https://images.unsplash.com/photo-1490730141103-6cac27aaab94',
-      title: 'Digital Headquarters for National Health Agencies',
-      description: "Set up and configure multiple campaigns, access real-time data dashboards, manage centralized help desks and complaints, and easily integrate with DHIS2 and other open-source products."
-    },
-    ],
+      // bannerImages must come from MDMS; see mdmsBannerImages below.
     }

Follow‑up change to render path is included further below.


103-117: Bug: tenantId fallback uses wrong precedence ("DEFAULT" || tenant)
This always evaluates to "DEFAULT". Use tenant || "DEFAULT".

-        "tenantId": "DEFAULT" || tenant,
+        "tenantId": tenant || "DEFAULT",

189-191: Responsive check should handle resize / UA
Replace static window.innerWidth with a mobile detector or a resize‑aware hook.

-  const isMobile = window.innerWidth <= 768;
+  const isMobile = Digit?.Utils?.browser?.isMobile?.() || window.innerWidth <= 768;

Or introduce a useIsMobile hook (resize listener) and use it here.


192-203: Replace inline styles with CSS classes
Move the heavy inline styles to CSS classes to align with design system and improve maintainability.

-    <div   style={{
-      padding: isMobile ? "1rem" : "2rem",
-      width: isMobile ? "100%" : "30%",
-      backgroundColor: "#fff",
-      overflowY: "auto",
-      justifyContent: "center",
-      display: "flex",
-      alignItems: "center",
-      flexDirection: "column",
-      height : isMobile ? "100vh" : "auto"
-    }}>
+    <div className={`otp-form-section ${isMobile ? 'otp-form-section--mobile' : 'otp-form-section--desktop'}`}>
@@
-        buttonClassName="sandbox-otp-submit-button"
-        buttonStyle={{ 
-          width: "100%", 
-          maxWidth: "408px",
-          backgroundColor: "#F47738",
-          borderColor: "#F47738",
-          color: "#fff",
-          fontWeight: "500",
-          padding: "12px 24px",
-          borderRadius: "4px",
-          fontSize: "16px",
-          textAlign: "center",
-          display: "flex",
-          justifyContent: "center",
-          alignItems: "center"
-        }}
+        buttonClassName="sandbox-otp-submit-button"

Add CSS classes in the appropriate stylesheet (otp-form-section base + mobile/desktop variants, and sandbox-otp-submit-button).

Also applies to: 217-233


254-265: Prefer MDMS banners only; remove hardcoded fallback and dead comment
Render Carousel with MDMS data (or nothing) and delete commented code.

-        ) : (
-          <Carousel bannerImages={mdmsBannerImages || config[0].bannerImages} />
-        )}
-        {/* <Carousel bannerImages={config[0].bannerImages} /> */}
+        ) : mdmsBannerImages ? (
+          <Carousel bannerImages={mdmsBannerImages} />
+        ) : null}

Please confirm Carousel gracefully handles an empty/absent banner list.


34-36: Resolved — otpEmail/otpTenant are set upstream in signUp.js. sessionStorage.setItem("otpEmail", inputEmail) and sessionStorage.setItem("otpTenant", tenantId) exist in micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login-v2/signUp.js (lines 67–68) immediately before window.location.replace(...) to the OTP page.


174-174: Tenant fallback in authenticate request
Guard against empty tenant to avoid auth failures for cross‑tenant OTP flows.

-    const requestData = { username: email, password: formData?.OtpComponent?.otp, tenantId: tenant, userType: "EMPLOYEE" };
+    const requestData = { username: email, password: formData?.OtpComponent?.otp, tenantId: tenant || "DEFAULT", userType: "EMPLOYEE" };
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/Landing/LandingComponent.js (1)

150-153: CTA copy derivation now dynamic—verify translations exist.

With the new derivation of buttonSection.title/subtitle, ensure i18n keys exist in MDMS/locales to avoid empty headers.

Provide the i18n keys you expect (e.g., SANDBOX_USER_CAPABILITIES_TITLE and SANDBOX_CLICK_CONTINUE) in locale files.

Comment on lines +354 to +359
.loginSignUpSelector{
display: flex;
align-items: center;
justify-content: end;

}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Use widely supported value for flex alignment

justify-content: end; is less compatible than flex-end.

 .loginSignUpSelector{
   display: flex;
   align-items: center;
-  justify-content: end;
+  justify-content: flex-end;
 }
📝 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
.loginSignUpSelector{
display: flex;
align-items: center;
justify-content: end;
}
.loginSignUpSelector{
display: flex;
align-items: center;
justify-content: flex-end;
}
🤖 Prompt for AI Agents
In micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
around lines 354 to 359, the rule uses the less-compatible value
`justify-content: end;`; replace it with the widely supported equivalent
`justify-content: flex-end;` to ensure broader browser compatibility and
consistent flex alignment.

},
{
"title": "What can you do on Sandbox?",
"title": "What can you do on",
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

🧩 Analysis chain

Incomplete section title; restores intended copy.

The title reads “What can you do on” and appears truncated. This is user-facing and will ship as-is. Recommend restoring the full phrase.

Apply this diff:

-            "title": "What can you do on",
+            "title": "What can you do on Sandbox?"

If the product name is injected elsewhere at runtime, use the appropriate placeholder instead (e.g., “What can you do on {{appName}}?”). Please confirm the intended copy with UX.


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "PWD: $(pwd)"
echo
echo "Searching for LandingPageConfig.js files..."
rg -n --hidden --no-ignore-vcs "LandingPageConfig\.js$" || true
echo

FILE="micro-ui/web/micro-ui-internals/packages/modules/core/src/config/LandingPageConfig.js"
if [ -f "$FILE" ]; then
  echo "===== Showing $FILE ====="
  sed -n '1,240p' "$FILE"
else
  echo "$FILE not found; searching for related files..."
  rg -n --hidden --no-ignore-vcs "LandingPage.*Config|LandingPageConfig|LandingPage" || true
fi

echo
echo "Searching repo for literal string: 'What can you do on'"
rg -n --hidden --no-ignore-vcs "What can you do on" || true

echo
echo "Searching repo for product-name placeholders ({{appName}}, ${appName}, etc.)"
rg -n --hidden --no-ignore-vcs --pcre2 "\{\{ *appName *\}\}|{{ *appName *}}|\$\{ *appName *\}|\{\{ *productName *\}\}|{{ *productName *}}" || true

echo
echo "Searching repo for 'Sandbox' occurrences"
rg -n --hidden --no-ignore-vcs "Sandbox" || true

Length of output: 2337


Incomplete section title — restore intended copy.
micro-ui/web/micro-ui-internals/packages/modules/core/src/config/LandingPageConfig.js:15 — "title" is truncated ("What can you do on"); replace with the product-aware copy or a placeholder and confirm with UX.

-            "title": "What can you do on",
+            "title": "What can you do on Sandbox?"

Use "What can you do on {{appName}}?" instead if the product name is injected at runtime. Confirm intended copy with UX.

📝 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
"title": "What can you do on",
"title": "What can you do on Sandbox?",
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/config/LandingPageConfig.js
around line 15, the "title" value is truncated ("What can you do on"); replace
it with the complete product-aware string "What can you do on {{appName}}?" (or
a UX-approved static product name) so that the title renders correctly; update
the config entry to use the placeholder if the product name is injected at
runtime and confirm the final copy with UX.

Comment on lines +140 to +141
const OtpConfig = [{ texts: { submitButtonLabel: "CORE_COMMON_SUBMIT" } }];

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

Remove dead code (unused OtpConfig)
This constant isn’t used.

-  const OtpConfig = [{ texts: { submitButtonLabel: "CORE_COMMON_SUBMIT" } }];
-
📝 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 OtpConfig = [{ texts: { submitButtonLabel: "CORE_COMMON_SUBMIT" } }];
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.js
around lines 140-141, the constant OtpConfig is declared but never used; remove
the unused OtpConfig declaration to eliminate dead code and any associated
imports or references if present, then run tests/lint to ensure no remaining
references break the build.

Comment on lines 147 to 151
const filteredRoles = user?.info?.roles?.filter((role) => role.tenantId === Digit.SessionStorage.get("Employee.tenantId"));
if (user?.info?.roles?.length > 0) user.info.roles = filteredRoles;
Digit.UserService.setUser(user);
setEmployeeDetail(user?.info, user?.access_token);
let redirectPath = `/${window?.globalPath}/user/setup`;

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

Avoid emptying user roles when tenant filter yields no match
Only override roles if the filtered set is non‑empty; otherwise preserve original roles.

-    const filteredRoles = user?.info?.roles?.filter((role) => role.tenantId === Digit.SessionStorage.get("Employee.tenantId"));
-    if (user?.info?.roles?.length > 0) user.info.roles = filteredRoles;
+    const filteredRoles = Array.isArray(user?.info?.roles)
+      ? user.info.roles.filter((role) => role?.tenantId === Digit.SessionStorage.get("Employee.tenantId"))
+      : [];
+    if (filteredRoles.length > 0) {
+      user.info.roles = filteredRoles;
+    }
📝 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 filteredRoles = user?.info?.roles?.filter((role) => role.tenantId === Digit.SessionStorage.get("Employee.tenantId"));
if (user?.info?.roles?.length > 0) user.info.roles = filteredRoles;
Digit.UserService.setUser(user);
setEmployeeDetail(user?.info, user?.access_token);
let redirectPath = `/${window?.globalPath}/user/setup`;
const filteredRoles = Array.isArray(user?.info?.roles)
? user.info.roles.filter((role) => role?.tenantId === Digit.SessionStorage.get("Employee.tenantId"))
: [];
if (filteredRoles.length > 0) {
user.info.roles = filteredRoles;
}
Digit.UserService.setUser(user);
setEmployeeDetail(user?.info, user?.access_token);
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.js
around lines 147 to 151, the code replaces user.info.roles with the
filteredRoles even when the filter returns an empty array, which unintentionally
clears roles; change the logic to only assign user.info.roles = filteredRoles
when filteredRoles exists and has length > 0 (otherwise leave user.info.roles
unchanged), then continue calling Digit.UserService.setUser(user) and
setEmployeeDetail(user?.info, user?.access_token).

</FormComposerV2>
{showToast && <Toast type={"error"} label={t(showToast)} onClose={closeToast} />}
<div className="EmployeeLoginFooter">
{showToast && <Toast type="error" label={t(showToast)} onClose={closeToast} />}
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

Bug: Toast API uses boolean flags, not type prop
Digit Toast expects error/warning booleans. type="error" renders as success.

-      {showToast && <Toast type="error" label={t(showToast)} onClose={closeToast} />}
+      {showToast && <Toast error label={t(showToast)} onClose={closeToast} />}
📝 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
{showToast && <Toast type="error" label={t(showToast)} onClose={closeToast} />}
{showToast && <Toast error label={t(showToast)} onClose={closeToast} />}
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.js
around line 238, the Toast is being rendered with a type="error" prop but the
Digit Toast API expects boolean flags (error/warning) instead of a type string;
replace the type prop with the appropriate boolean flag (e.g., use error={true}
for error toasts or warning={true} for warnings), remove type="error", and keep
the label and onClose as-is; if the severity is dynamic, map your severity value
to the correct boolean prop before rendering.

Comment on lines +53 to 56
const introSection = subsections.find(s => s.type === "introSection") || {};
const stepsSection = subsections.find(s => s.type === "stepsSection") || {};
const buttonSection = subsections.find(s => s.type === "ButtonWrapper") || {};

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

Config schema mismatch: selectors won’t match MDMS/local SandboxConfig.

type === "introSection" | "stepsSection" | "ButtonWrapper" does not exist in the provided SandboxConfig (paragraph, both, step). This yields undefined titles and broken UI.

Apply this diff to normalize selection and derive button copy:

-  const introSection = subsections.find(s => s.type === "introSection") || {};
-  const stepsSection = subsections.find(s => s.type === "stepsSection") || {};
-  const buttonSection = subsections.find(s => s.type === "ButtonWrapper") || {};
+  const introSection =
+    subsections.find((s) => s.type === "introSection") ||
+    subsections.find((s) => s.type === "paragraph") ||
+    {};
+  const stepsSection =
+    subsections.find((s) => s.type === "stepsSection") ||
+    subsections.find((s) => s.type === "both") ||
+    {};
+  // Use the last "both" section's paragraph as the CTA subtitle
+  const lastBoth = [...subsections].reverse().find((s) => s.type === "both");
+  const buttonSection = {
+    title: lastBoth?.title || "",
+    subtitle:
+      lastBoth?.content?.slice().reverse().find((c) => c.type === "paragraph")
+        ?.text || "",
+  };
📝 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 introSection = subsections.find(s => s.type === "introSection") || {};
const stepsSection = subsections.find(s => s.type === "stepsSection") || {};
const buttonSection = subsections.find(s => s.type === "ButtonWrapper") || {};
const introSection =
subsections.find((s) => s.type === "introSection") ||
subsections.find((s) => s.type === "paragraph") ||
{};
const stepsSection =
subsections.find((s) => s.type === "stepsSection") ||
subsections.find((s) => s.type === "both") ||
{};
// Use the last "both" section's paragraph as the CTA subtitle
const lastBoth = [...subsections].reverse().find((s) => s.type === "both");
const buttonSection = {
title: lastBoth?.title || "",
subtitle:
lastBoth?.content?.slice().reverse().find((c) => c.type === "paragraph")
?.text || "",
};
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/Landing/LandingComponent.js
around lines 53 to 56, the current selectors use non-existent types
("introSection", "stepsSection", "ButtonWrapper") so subsections resolve
undefined; update the finds to match the SandboxConfig types: select
introSection where type === "paragraph", stepsSection where type === "step", and
resolve buttonSection by preferring type === "both" then fallback to "step";
also derive the button copy from the chosen buttonSection (e.g., use
buttonSection.buttonText or a sensible default) so titles and button text render
correctly.

Comment on lines +62 to +77
// Logic to split stepsSection.title into two parts
const splitStepsTitle = () => {
const title = t(stepsSection.title);
const words = title.split(' ');

if (words.length <= 1) {
return { firstPart: title, lastPart: '' };
}

// Get the last word (end part)
const lastPart = words[words.length - 1];
// Get remaining words (everything except the last word)
const firstPart = words.slice(0, -1).join(' ');

return { firstPart, lastPart };
};
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

Title splitting runs on undefined and every render; add guards and memoize.

When stepsSection.title is missing, .split on translated “undefined” is brittle. Also compute once per dependency change.

Apply this diff:

-  const splitStepsTitle = () => {
-    const title = t(stepsSection.title);
-    const words = title.split(' ');
+  const splitStepsTitle = () => {
+    const rawKey = stepsSection?.title;
+    if (!rawKey) return { firstPart: "", lastPart: "" };
+    const title = t(rawKey);
+    const words = title.trim().split(/\s+/);
     if (words.length <= 1) {
       return { firstPart: title, lastPart: '' };
     }
-    // Get the last word (end part)
-    const lastPart = words[words.length - 1];
-    // Get remaining words (everything except the last word)
-    const firstPart = words.slice(0, -1).join(' ');
+    const lastPart = words.pop();
+    const firstPart = words.join(' ');
     return { firstPart, lastPart };
   };
-
-  const { firstPart, lastPart } = splitStepsTitle();
+  const { firstPart, lastPart } = React.useMemo(splitStepsTitle, [stepsSection?.title, t]);
-              <HeaderComponent className="custom-landing-sub-header">
-                {t(introSection.title)}
-              </HeaderComponent>
+              {introSection?.title ? (
+                <HeaderComponent className="custom-landing-sub-header">
+                  {t(introSection.title)}
+                </HeaderComponent>
+              ) : null}

Also applies to: 79-79, 100-103

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/Landing/LandingComponent.js
around lines 62-77 (also apply same fix at 79 and 100-103): the splitStepsTitle
logic currently calls t(stepsSection.title).split(' ') on every render and will
throw if stepsSection.title is missing; change it to first guard that
stepsSection and its title exist and that t(...) is a non-empty string, then
memoize the computed split with React.useMemo so it only recalculates when
stepsSection.title or the translation function changes; return a safe default ({
firstPart: '', lastPart: '' } or suitable fallback) when title is missing and
ensure the rest of the component uses the memoized values.

Comment on lines +81 to +83
console.log("*** Log ===> ", stepsSection.title);
console.log("*** First Part ===> ", firstPart);
console.log("*** Last Part ===> ", lastPart);
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Remove debug console logs.

Avoid noisy logs in production and leaking translation keys.

Apply this diff:

-  console.log("*** Log ===> ", stepsSection.title);
-  console.log("*** First Part ===> ", firstPart);
-  console.log("*** Last Part ===> ", lastPart);
📝 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
console.log("*** Log ===> ", stepsSection.title);
console.log("*** First Part ===> ", firstPart);
console.log("*** Last Part ===> ", lastPart);
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/Landing/LandingComponent.js
around lines 81 to 83, remove the three debug console.log statements that print
stepsSection.title, firstPart, and lastPart; replace them with no-op or, if
runtime diagnostics are required, use a conditional debug logger tied to
NODE_ENV or a feature flag (or use a logger that respects log levels) so these
translation keys are not leaked to production logs.

Comment on lines +116 to 119
<div className="custom-video-section" style={{ minHeight: "400px", height: "400px", maxWidth: "500px", width: "100%" }}>
<SupaDemoPlayer src={config.url ? config.url : sandboxConfig.url} overlay={true} title="Sandbox Demo" />
{/* <YoutubeVideo link={url} overlay={true} /> */}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Consider responsive sizing for the demo player.

Hard-coded height: "400px" can cause overflow on small screens. Prefer aspect-ratio or CSS classes.

Example:

-  <div className="custom-video-section" style={{ minHeight: "400px", height: "400px", maxWidth: "500px", width: "100%" }}>
+  <div className="custom-video-section" style={{ aspectRatio: "16 / 9", width: "100%", maxWidth: "640px" }}>
📝 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 className="custom-video-section" style={{ minHeight: "400px", height: "400px", maxWidth: "500px", width: "100%" }}>
<SupaDemoPlayer src={config.url ? config.url : sandboxConfig.url} overlay={true} title="Sandbox Demo" />
{/* <YoutubeVideo link={url} overlay={true} /> */}
</div>
<div className="custom-video-section" style={{ aspectRatio: "16 / 9", width: "100%", maxWidth: "640px" }}>
<SupaDemoPlayer src={config.url ? config.url : sandboxConfig.url} overlay={true} title="Sandbox Demo" />
{/* <YoutubeVideo link={url} overlay={true} /> */}
</div>

Comment on lines +133 to +143
{/* Right Column - Steps list */}
<div className="middle-right-column">
<ul className="custom-steps-list">
{stepsSection?.content?.map((item, index) => (
<div key={index} className="step-item-container">
<p className="step-item">{t(item?.text)}</p>
<BreakLine style={{ maxWidth: "100%", borderColor: "#D6D5D4", marginLeft: "0px", marginRight: "3rem" }} />
</div>
))}
</ul>
</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

Fix list semantics and filter only step items; avoid index keys.

ul must contain li, and the config mixes paragraph/step. Index keys hinder reconciliation.

Apply this diff:

-          <div className="middle-right-column">
-            <ul className="custom-steps-list">
-              {stepsSection?.content?.map((item, index) => (
-                <div key={index} className="step-item-container">
-                  <p className="step-item">{t(item?.text)}</p>
-                  <BreakLine style={{ maxWidth: "100%", borderColor: "#D6D5D4", marginLeft: "0px", marginRight: "3rem" }} />
-                </div>
-              ))}
-            </ul>
-          </div>
+          <div className="middle-right-column">
+            <ul className="custom-steps-list">
+              {stepsSection?.content
+                ?.filter((item) => item?.type === "step")
+                ?.map((item) => (
+                  <li key={item.id ?? item.text} className="step-item-container">
+                    <p className="step-item">{t(item.text)}</p>
+                    <BreakLine
+                      style={{
+                        maxWidth: "100%",
+                        borderColor: "#D6D5D4",
+                        marginLeft: "0px",
+                        marginRight: "3rem",
+                      }}
+                    />
+                  </li>
+                ))}
+            </ul>
+          </div>
📝 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
{/* Right Column - Steps list */}
<div className="middle-right-column">
<ul className="custom-steps-list">
{stepsSection?.content?.map((item, index) => (
<div key={index} className="step-item-container">
<p className="step-item">{t(item?.text)}</p>
<BreakLine style={{ maxWidth: "100%", borderColor: "#D6D5D4", marginLeft: "0px", marginRight: "3rem" }} />
</div>
))}
</ul>
</div>
{/* Right Column - Steps list */}
<div className="middle-right-column">
<ul className="custom-steps-list">
{stepsSection?.content
?.filter((item) => item?.type === "step")
?.map((item) => (
<li key={item.id ?? item.text} className="step-item-container">
<p className="step-item">{t(item.text)}</p>
<BreakLine
style={{
maxWidth: "100%",
borderColor: "#D6D5D4",
marginLeft: "0px",
marginRight: "3rem",
}}
/>
</li>
))}
</ul>
</div>
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/Landing/LandingComponent.js
around lines 133-143, the steps list uses a UL but renders DIVs (invalid
semantics), maps over mixed content (paragraph/step) without filtering, and uses
array index as key; change the map to first filter stepsSection?.content to only
step items, render each entry as an LI (not a DIV) inside the UL, and use a
stable unique key (e.g., item.id or item.key, falling back to a
hashed/text-based fallback) instead of index; keep the existing child elements
(the paragraph text and BreakLine) but place them inside the LI to preserve
appearance and accessibility.

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: 8

Caution

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

⚠️ Outside diff range comments (3)
micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss (2)

1440-1451: Min-width 75rem can overflow at intermediate widths

You override at many breakpoints, but ensure tablet/legacy widths don’t get stuck with 1200px+ min width.

Please confirm these breakpoints fully cover all app layouts using this card. If any route renders this card without the 768–1439 overrides, consider:

 .custom-landing-card {
-  min-width: 75rem;
+  min-width: min(75rem, 100%);
 }

1495-1497: Critical: invalid CSS value (hex color in quotes)

Quoted hex is ignored by browsers; color won’t apply.

 .highlight-sandbox {
-  color: "#c84c0e";
+  color: #c84c0e;
   /* or your specific orange color code */
 }
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js (1)

52-55: Typo in i18n key likely breaks translation.

Change APPLICATON → APPLICATION.

-      content: t(`APPLICATON_MODULE_CRUMB_${searchParams.get("module")}`),
+      content: t(`APPLICATION_MODULE_CRUMB_${searchParams.get("module")}`),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 32c0374 and 0493e25.

📒 Files selected for processing (3)
  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss (22 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductPage/ProductPageComponent.js (1 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

check

Files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductPage/ProductPageComponent.js
  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js
🧠 Learnings (7)
📚 Learning: 2025-08-19T07:07:45.217Z
Learnt from: syedkaifegov
PR: egovernments/DIGIT-Frontend#3012
File: micro-ui/web/micro-ui-internals/packages/css/src/index.scss:692-696
Timestamp: 2025-08-19T07:07:45.217Z
Learning: CSS changes for .employee-app-wrapper.sandbox are specific to Urban repo's sandbox branch implementation, not related to sandbox-login-page components found in the main codebase.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js
📚 Learning: 2025-06-26T10:24:08.628Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss:1642-1662
Timestamp: 2025-06-26T10:24:08.628Z
Learning: In the sandbox SCSS file, tab styling with margin-bottom and border changes that cause layout shifts are intentional design requirements per updated UI specifications, not bugs to be fixed.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
📚 Learning: 2024-10-26T15:11:49.938Z
Learnt from: nabeelmd-eGov
PR: egovernments/DIGIT-Frontend#1680
File: health/micro-ui/web/micro-ui-internals/example/src/UICustomizations.js:1032-1094
Timestamp: 2024-10-26T15:11:49.938Z
Learning: In the DIGIT-Frontend project, when reviewing the `UICustomizations.js` file, avoid suggesting refactoring of repetitive rendering logic in switch cases into helper functions, unless explicitly requested.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
📚 Learning: 2025-06-26T10:16:36.004Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/css/src/components/Carousel.scss:0-0
Timestamp: 2025-06-26T10:16:36.004Z
Learning: In the DIGIT-Frontend codebase, signup-container CSS classes cannot be namespaced under parent classes due to existing style constraints and requirements.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
📚 Learning: 2025-06-26T10:21:42.619Z
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:279-280
Timestamp: 2025-06-26T10:21:42.619Z
Learning: In the sandbox ProductDetailsComponentUpdated.js component, string concatenation is intentionally used for translation keys (e.g., `t(`${module}` + "_SECTION1_ROLE_1")`) instead of template literals for easier config identification and to make the key structure more explicit.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductPage/ProductPageComponent.js
  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js
📚 Learning: 2024-11-07T11:02:33.520Z
Learnt from: rachana-egov
PR: egovernments/DIGIT-Frontend#1770
File: health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/pages/employee/viewVillage.js:320-322
Timestamp: 2024-11-07T11:02:33.520Z
Learning: In `health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/pages/employee/viewVillage.js`, the `data?.additionalFields` object is guaranteed to be defined, so checking for its existence before accessing its keys is unnecessary.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js
📚 Learning: 2025-06-26T09:43:00.597Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/components/LoginSignupSelector.js:24-24
Timestamp: 2025-06-26T09:43:00.597Z
Learning: In LoginSignupSelector component, the URL checking using window.location.href.includes("sandbox-ui/user/sign-up") is intentional for URL validation purposes, not navigation. The team prefers this approach over React Router's useLocation hook for specific URL checking needs.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js
🧬 Code graph analysis (2)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductPage/ProductPageComponent.js (1)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductPage/index.js (1)
  • detailsConfig (6-19)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js (2)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/AppModules.js (2)
  • location (16-16)
  • hideClass (39-40)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/index.js (1)
  • ProductDetails (8-39)
🔇 Additional comments (28)
micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss (23)

1-22: LGTM: products header/container scaffolding

Structure and spacing look reasonable.


24-28: LGTM: responsive padding reset over 1280px

Media override is clear and scoped.


143-146: LGTM: landing wrapper margins

No issues.


940-1116: LGTM: comprehensive responsive breakpoints

Breakpoint matrix (1024–2560+) is thorough and consistent; overrides look coherent.

Please confirm these rules don’t conflict with existing global layout tokens (index.scss) for the same components.

Also applies to: 1117-1181


118-122: Rename PascalCase selector to kebab-case

Align with prevailing naming in this file.

-.SandboxLogoAlignCenter {
+.sandbox-logo-align-center {
   align-items: center;
   justify-content: center;
   display: flex;
 }

150-157: Add svh/dvh fallbacks and min-size caps for dynamic viewports

vh/vw can misbehave on mobile; add modern viewport units and sensible mins.

 .sandbox-onboarding-wrapper {
-  height: 80vh !important;
+  height: 80vh !important;
+  height: 80svh !important;
-  width: 30vw !important;
+  width: 30vw !important;
+  min-width: 320px !important;
   max-width: 500px !important;
   max-height: 650px !important;
   display: flex;
   -ms-flex-pack: justify;
   justify-content: space-evenly;
 }

182-185: Avoid rigid fixed px dimensions; use responsive constraints

Hard 500×650 with !important will overflow smaller screens.

 .employeeCard {
-  height: 650px !important;
-  width: 500px !important;
+  height: min(650px, 100svh) !important;
+  width: min(500px, 100%) !important;
 }

215-225: Mirror viewport fallbacks on mobile wrapper

Apply svh and caps in the mobile breakpoint too.

@media (max-width: 768px) {
  .sandbox-onboarding-wrapper {
-    height: 80vh !important;
+    height: 80vh !important;
+    height: 80svh !important;
     width: 80vw !important;
     max-width: 500px !important;
     max-height: 650px !important;
     display: flex;
     -ms-flex-pack: justify;
     justify-content: space-between;
  }
}

264-273: Clean up duplicate font-family, huge top margin, and hardcoded color

Remove duplicate font-family, tame 8rem top margin, and use theme var for color.

 .digit-card-label.sandbox-custom-otp-subheader {
-  font-family: "Roboto";
-  margin-bottom: 3rem;
-  font-family: Roboto;
-  margin-top: 8rem !important;
-  color: #0b4b66 !important;
+  font-family: "Roboto";
+  margin-bottom: 3rem;
+  margin-top: 2rem !important;
+  color: var(--digit-primary-color, #0B4B66) !important;
   text-align: center !important;
   font-size: 2rem !important;
   font-weight: 700 !important;
 }

302-349: Tooltip: support keyboard focus and reduced motion

Show tooltip on focus, and respect prefers-reduced-motion; consider pointer-events to avoid flicker.

 @media (max-width: 768px) {
   .sandbox-signup-form {
     height: 100vh !important;
     align-items: center !important;
     display: flex !important;
   }
-
-  }
+  }
 
   .sandbox-signup-form {
   .label-container {
 
     .info-icon {
       position: relative; 
       display: inline-flex;
-      &:hover .infotext {
+      &:hover .infotext,
+      &:focus-within .infotext {
         visibility: visible;
         opacity: 1;
       }
 
       .infotext {
         visibility: hidden;
         opacity: 0;
-        transition: opacity 0.3s;
+        transition: opacity 0.3s;
+        @media (prefers-reduced-motion: reduce) {
+          transition: none;
+        }
 
         position: absolute;
         width: 15rem; 
         background-color: #555;
         color: #fff;
         font-family: Roboto, sans-serif;
         font-style: normal;
         font-weight: 400;
         text-align: center;
         line-height: 1.25rem;
         padding: 0.5rem;
         border-radius: 0.375rem;
         z-index: 10; 
+        pointer-events: none;
 
         bottom: 125%; 
         left: 50%;
         margin-left: -10.3rem; 
       }
     }
   }
 }

354-363: Don’t use “all: unset”; replace grid-gap with gap

Limit resets to spacing properties and keep layout intact; use modern gap.

-.sandbox-signup-form .digit-field .label-field-wrapper {
-  all: unset !important;
-  grid-gap: .25rem !important;
+.sandbox-signup-form .digit-field .label-field-wrapper {
+  display: grid;
+  gap: .25rem !important;
+  margin: 0;
+  padding: 0;
+  background: transparent;
+  border: 0;
   .digit-field{
     margin-bottom: unset !important;
   }
   .digit-header-content .label-container .label-styles{
     margin-bottom: 5px !important;
   }
 }

375-379: Use CSS variable for brand color

Replace hardcoded color with tokenized variable.

-      color: #0B4B66;
+      color: var(--digit-primary-color, #0B4B66);

383-388: Use flex-end, not end, for broader support

‘end’ is less compatible.

 .loginSignUpSelector{
   display: flex;
   align-items: center;
-  justify-content: end;
+  justify-content: flex-end;
 }

652-653: Fix comment typo

Remove duplicate word.

-  /* Ensure spacing between the two components components  */
+  /* Ensure spacing between the two components */

1862-1881: Don’t nuke all hover; preserve focus-visible for a11y

Add keyboard focus styles and reduce !important where possible.

 .no-hover:hover {
   background-color: inherit !important;
   color: inherit !important;
   opacity: 1 !important;
   transform: none !important;
   box-shadow: none !important;
   text-decoration: none !important;
   font-size: inherit !important;
   font-weight: inherit !important;
 }
+/* Preserve keyboard focus */
+.no-hover:focus-visible {
+  outline: 2px solid var(--digit-focus-color, #0B4B66);
+  outline-offset: 2px;
+}

1946-1954: Responsive padding for role blocks

Large horizontal padding can overflow on small screens.

 .role-block {
   gap: 1rem;
   width: 100%;
-  padding: 0rem 8rem;
+  padding: 0 clamp(1rem, 5vw, 8rem);
 }

2015-2035: Underline width and color should match tokens and text width

Comment contradicts fixed width; use variables and scale with text.

 .cs-title::after {
-  width: 8rem; /* match text width */
-  background-color: #215B73;
+  width: 100%; /* match text width */
+  max-width: 8rem;
+  background-color: var(--digit-secondary-color, #215B73);
 }

2173-2178: Conflicting height and min-height on iframe

min-height wins; keep intended constraint.

 .wt-iframe {
   width: 100%;
-  height: 500px;
   border: none;
   min-height: 40rem;
 }

2186-2225: Add focus-visible and current-state styles to breadcrumbs

Ensure keyboard focus and aria-current are styled.

 .nav-breadcrumb a {
   color: #e67e22 !important;
   text-decoration: none;
   transition: color 0.2s ease;
 }
 .nav-breadcrumb a:hover {
   color: #d35400;
 }
+.nav-breadcrumb a:focus-visible {
+  outline: 2px solid var(--digit-focus-color, #0B4B66);
+  outline-offset: 2px;
+  text-decoration: underline;
+}
+.nav-breadcrumb a[aria-current="page"] {
+  color: #333;
+  font-weight: 600;
+  text-decoration: none;
+}

1689-1690: Consistent color tokens

Same as above for steps header.

-  color: #0B4B66;
+  color: var(--digit-primary-color, #0B4B66);

1671-1679: Tokenize repeated brand color

Use CSS variable instead of #0b4b66.

-  color: #0b4b66;
+  color: var(--digit-primary-color, #0B4B66);

1565-1568: Tokenize border color and avoid magic numbers

Use theme variable for the accent color.

-  border-top: 2px solid #c84c0e;
+  border-top: 2px solid var(--digit-accent-color, #c84c0e);

1768-1772: Rigid card height; prefer content-driven with min/max

Fixed 280px height may clip content; keep min-height, drop fixed height.

-  min-height: 280px;
-  height: 280px;
+  min-height: 280px;
+  height: auto;
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductPage/ProductPageComponent.js (1)

43-45: Adopt prior fix; also render null instead of an empty element and localize the label.

Agree with the earlier comment: use strict equality and self‑closing. Additionally, avoid injecting an extra empty div; render null, and use i18n for the label.

-            { 
-            ((product.module == "PGR") ? <Chip text="Configure" /> : <div></div>   )
-            }
+            {
+              product.module === "PGR" ? <Chip text={t("COMMON_CONFIGURE")} /> : null
+            }
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js (4)

64-68: LGTM: hideClass logic is consistent with sandbox patterns.

Conditional container class correctly hides ground-container on productDetailsPage.


39-40: Remove redundant ternaries returning booleans.

Directly use the boolean expressions.

-      show: location.pathname.includes("productDetailsPage") || location.pathname.includes("productPage") ? true : false,
+      show: location.pathname.includes("productDetailsPage") || location.pathname.includes("productPage"),
@@
-      show: location.pathname.includes("productDetailsPage") ? true : false,
+      show: location.pathname.includes("productDetailsPage"),

Also applies to: 44-45


97-101: Remove dead, commented route block.

The updated ProductDetails route is live below; this block should go.

-          {/* <PrivateRoute path={`${path}/productDetailsPage/:module`}>
-            <div className="employee-app-wrapper sandbox-landing-wrapper">
-              <ProductDetails />
-            </div>
-          </PrivateRoute> */}

4-4: Confirm react-router-dom major version (Switch vs Routes). Repository files were not accessible to verify; confirm whether the project uses react-router-dom v6 — if so, replace Switch with Routes/Route and update imports/usages in micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js.

Comment on lines 1775 to 1780
display: flex;
align-items: center;
align-items: start;
gap: 0.5rem;
min-height: 3.2rem;

margin-bottom: 0.5rem;
position: relative;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Use flex-start for alignment

‘align-items: start’ is less compatible than ‘flex-start’.

 .product-header {
   display: flex;
-  align-items: start;
+  align-items: flex-start;
   gap: 0.5rem;
   margin-bottom: 0.5rem;
   position: relative;
 }
📝 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;
align-items: center;
align-items: start;
gap: 0.5rem;
min-height: 3.2rem;
margin-bottom: 0.5rem;
position: relative;
}
display: flex;
align-items: flex-start;
gap: 0.5rem;
margin-bottom: 0.5rem;
position: relative;
}
🤖 Prompt for AI Agents
In micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
around lines 1775 to 1780, replace the less-compatible align-items: start with
the standard align-items: flex-start to ensure consistent cross-browser flexbox
alignment; update that single property and keep the rest of the rule unchanged.

Comment on lines +70 to +117
<div className="employee-ui">
<Switch>
<AppContainer className= {hideClass ? "" : "ground-container" }>
<React.Fragment>
<ProjectBreadCrumb location={location} defaultPath={path} />
</React.Fragment>
<PrivateRoute path={`${path}/tenant-response`} component={() => <SandboxResponse />} />
<PrivateRoute path={`${path}/tenant-create`} component={() => <SandboxCreate />} />
<PrivateRoute path={`${path}/tenant-search`} component={() => <SandboxSearch />} />
<PrivateRoute path={`${path}/tenant-management/create`} component={() => <TenantCreate />} />
<PrivateRoute path={`${path}/tenant-management/search`} component={() => <TenantView />} />
<PrivateRoute path={`${path}/tenant-management/update`} component={() => <TenantUpdate />} />
<PrivateRoute path={`${path}/tenant-management/config/upload`} component={() => <TenantConfigUpload />} />
<PrivateRoute path={`${path}/application-management/home`} component={() => <ApplicationHome />} />
<PrivateRoute path={`${path}/application-management/module-master`} component={() => <SandboxSearch />} />
<PrivateRoute path={`${path}/application-management/setup-master`} component={() => <SetupMaster />} />
<PrivateRoute path={`${path}/application-management/module`} component={() => <ModuleMasterTable />} />
<PrivateRoute path={`${path}/landing`}>
<div className="sandbox-landing-wrapper">
<Landing />
</div>
</PrivateRoute>
<PrivateRoute path={`${path}/productPage`}>
<div className="sandbox-landing-wrapper">
<ProductPage />
</div>
</PrivateRoute>
{/* <PrivateRoute path={`${path}/productDetailsPage/:module`}>
<div className="employee-app-wrapper sandbox-landing-wrapper">
<ProductDetails />
</div>
</PrivateRoute> */}
<PrivateRoute path={`${path}/landing/select-role`}>
<div className="employee-app-wrapper sandbox-landing-wrapper">
<RoleLanding />
</div>
</PrivateRoute>
<React.Fragment>
<PrivateRoute path={`${path}/productDetailsPage/:module`}>
<div>
<ProductDetails isUpdated={true}/>
</div>
</PrivateRoute>
</React.Fragment>

</AppContainer>
</Switch>
</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

Switch should wrap routes, not AppContainer.

Placing AppContainer as the sole child of Switch defeats Switch’s first-match behavior and risks multiple PrivateRoutes rendering simultaneously (e.g., landing and landing/select-role). Move Switch inside AppContainer; remove no-op Fragments and the commented block.

-    <div className="employee-ui">
-      <Switch>
-        <AppContainer className= {hideClass ? "" : "ground-container" }>
-          <React.Fragment>
-            <ProjectBreadCrumb location={location} defaultPath={path} />
-          </React.Fragment>
+    <div className="employee-ui">
+      <AppContainer className={hideClass ? "" : "ground-container"}>
+        <ProjectBreadCrumb location={location} defaultPath={path} />
+        <Switch>
           <PrivateRoute path={`${path}/tenant-response`} component={() => <SandboxResponse />} />
           <PrivateRoute path={`${path}/tenant-create`} component={() => <SandboxCreate />} />
           <PrivateRoute path={`${path}/tenant-search`} component={() => <SandboxSearch />} />
           <PrivateRoute path={`${path}/tenant-management/create`} component={() => <TenantCreate />} />
           <PrivateRoute path={`${path}/tenant-management/search`} component={() => <TenantView />} />
           <PrivateRoute path={`${path}/tenant-management/update`} component={() => <TenantUpdate />} />
           <PrivateRoute path={`${path}/tenant-management/config/upload`} component={() => <TenantConfigUpload />} />
           <PrivateRoute path={`${path}/application-management/home`} component={() => <ApplicationHome />} />
           <PrivateRoute path={`${path}/application-management/module-master`} component={() => <SandboxSearch />} />
           <PrivateRoute path={`${path}/application-management/setup-master`} component={() => <SetupMaster />} />
           <PrivateRoute path={`${path}/application-management/module`} component={() => <ModuleMasterTable />} />
           <PrivateRoute path={`${path}/landing`}>
             <div className="sandbox-landing-wrapper">
               <Landing />
             </div>
           </PrivateRoute>
           <PrivateRoute path={`${path}/productPage`}>
             <div className="sandbox-landing-wrapper">
               <ProductPage />
             </div>
           </PrivateRoute>
-          {/* <PrivateRoute path={`${path}/productDetailsPage/:module`}>
-            <div className="employee-app-wrapper sandbox-landing-wrapper">
-              <ProductDetails />
-            </div>
-          </PrivateRoute> */}
           <PrivateRoute path={`${path}/landing/select-role`}>
             <div className="employee-app-wrapper sandbox-landing-wrapper">
               <RoleLanding />
             </div>
           </PrivateRoute>
-          <React.Fragment>
-            <PrivateRoute path={`${path}/productDetailsPage/:module`}>
-              <div>
-                <ProductDetails isUpdated={true}/>
-              </div>
-            </PrivateRoute>
-          </React.Fragment>
-
-        </AppContainer>
-      </Switch>
+          <PrivateRoute path={`${path}/productDetailsPage/:module`}>
+            <div>
+              <ProductDetails isUpdated={true} />
+            </div>
+          </PrivateRoute>
+        </Switch>
+      </AppContainer>
     </div>
📝 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 className="employee-ui">
<Switch>
<AppContainer className= {hideClass ? "" : "ground-container" }>
<React.Fragment>
<ProjectBreadCrumb location={location} defaultPath={path} />
</React.Fragment>
<PrivateRoute path={`${path}/tenant-response`} component={() => <SandboxResponse />} />
<PrivateRoute path={`${path}/tenant-create`} component={() => <SandboxCreate />} />
<PrivateRoute path={`${path}/tenant-search`} component={() => <SandboxSearch />} />
<PrivateRoute path={`${path}/tenant-management/create`} component={() => <TenantCreate />} />
<PrivateRoute path={`${path}/tenant-management/search`} component={() => <TenantView />} />
<PrivateRoute path={`${path}/tenant-management/update`} component={() => <TenantUpdate />} />
<PrivateRoute path={`${path}/tenant-management/config/upload`} component={() => <TenantConfigUpload />} />
<PrivateRoute path={`${path}/application-management/home`} component={() => <ApplicationHome />} />
<PrivateRoute path={`${path}/application-management/module-master`} component={() => <SandboxSearch />} />
<PrivateRoute path={`${path}/application-management/setup-master`} component={() => <SetupMaster />} />
<PrivateRoute path={`${path}/application-management/module`} component={() => <ModuleMasterTable />} />
<PrivateRoute path={`${path}/landing`}>
<div className="sandbox-landing-wrapper">
<Landing />
</div>
</PrivateRoute>
<PrivateRoute path={`${path}/productPage`}>
<div className="sandbox-landing-wrapper">
<ProductPage />
</div>
</PrivateRoute>
{/* <PrivateRoute path={`${path}/productDetailsPage/:module`}>
<div className="employee-app-wrapper sandbox-landing-wrapper">
<ProductDetails />
</div>
</PrivateRoute> */}
<PrivateRoute path={`${path}/landing/select-role`}>
<div className="employee-app-wrapper sandbox-landing-wrapper">
<RoleLanding />
</div>
</PrivateRoute>
<React.Fragment>
<PrivateRoute path={`${path}/productDetailsPage/:module`}>
<div>
<ProductDetails isUpdated={true}/>
</div>
</PrivateRoute>
</React.Fragment>
</AppContainer>
</Switch>
</div>
<div className="employee-ui">
<AppContainer className={hideClass ? "" : "ground-container"}>
<ProjectBreadCrumb location={location} defaultPath={path} />
<Switch>
<PrivateRoute path={`${path}/tenant-response`} component={() => <SandboxResponse />} />
<PrivateRoute path={`${path}/tenant-create`} component={() => <SandboxCreate />} />
<PrivateRoute path={`${path}/tenant-search`} component={() => <SandboxSearch />} />
<PrivateRoute path={`${path}/tenant-management/create`} component={() => <TenantCreate />} />
<PrivateRoute path={`${path}/tenant-management/search`} component={() => <TenantView />} />
<PrivateRoute path={`${path}/tenant-management/update`} component={() => <TenantUpdate />} />
<PrivateRoute path={`${path}/tenant-management/config/upload`} component={() => <TenantConfigUpload />} />
<PrivateRoute path={`${path}/application-management/home`} component={() => <ApplicationHome />} />
<PrivateRoute path={`${path}/application-management/module-master`} component={() => <SandboxSearch />} />
<PrivateRoute path={`${path}/application-management/setup-master`} component={() => <SetupMaster />} />
<PrivateRoute path={`${path}/application-management/module`} component={() => <ModuleMasterTable />} />
<PrivateRoute path={`${path}/landing`}>
<div className="sandbox-landing-wrapper">
<Landing />
</div>
</PrivateRoute>
<PrivateRoute path={`${path}/productPage`}>
<div className="sandbox-landing-wrapper">
<ProductPage />
</div>
</PrivateRoute>
<PrivateRoute path={`${path}/landing/select-role`}>
<div className="employee-app-wrapper sandbox-landing-wrapper">
<RoleLanding />
</div>
</PrivateRoute>
<PrivateRoute path={`${path}/productDetailsPage/:module`}>
<div>
<ProductDetails isUpdated={true} />
</div>
</PrivateRoute>
</Switch>
</AppContainer>
</div>
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js
around lines 70 to 117, the AppContainer is currently the sole child of Switch
which breaks Switch’s first-match behavior; move the Switch so it is a child of
AppContainer (i.e., AppContainer should wrap layout and inside it Switch should
directly contain the PrivateRoute entries), remove the no-op React.Fragment
wrappers, and delete the commented productDetails block; ensure Switch’s direct
children are only the PrivateRoute routes so only the first matching route
renders.

Comment on lines +14 to +27
const Chip = ({ text = "Configure", color = "#215B730D", borderColor = "#215B73AD", textColor = "#215B73AD" }) => {
const chipStyle = {
padding: "2px 6px",
border: `1px solid ${borderColor}`,
backgroundColor: color,
color: textColor,
borderRadius: "4px",
fontSize: "12px",
fontWeight: "400",
cursor: "pointer",
display: "inline-block",
};
return <div style={chipStyle}>{text}</div>;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Chip UX/i18n: remove pointer affordance and add a localized fallback.

The Chip isn’t clickable; drop the pointer cursor. Also default to a translated label when no text is passed.

-    const Chip = ({ text = "Configure", color = "#215B730D", borderColor = "#215B73AD", textColor = "#215B73AD" }) => {
+    const Chip = ({ text, color = "#215B730D", borderColor = "#215B73AD", textColor = "#215B73AD" }) => {
     const chipStyle = {
       padding: "2px 6px",
       border: `1px solid ${borderColor}`,
       backgroundColor: color,
       color: textColor,
       borderRadius: "4px",
       fontSize: "12px",
       fontWeight: "400",
-      cursor: "pointer",
       display: "inline-block",
     };
-    return <div style={chipStyle}>{text}</div>;
+    return <div style={chipStyle}>{text ?? t("COMMON_CONFIGURE")}</div>;

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

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductPage/ProductPageComponent.js
around lines 14 to 27, the Chip component currently uses a pointer cursor and a
hardcoded default label; remove the pointer affordance (delete or change the
cursor style to default/auto) and replace the hardcoded default text with a
localized fallback by using the app's i18n (e.g., import and use the project's
translation hook/function such as useTranslation/t or i18n.t and set the default
text to t('chip.configure') or the equivalent key). Ensure the translation
import is added at the top of the file and used inside the component so when no
text prop is passed it displays the localized label.

{detailsConfig?.map((product, index) => (
{detailsConfig?.map((product, index) => {
return (
<Card key={index} className="product-card">
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Prefer a stable key over the array index.

Use a deterministic key (module) to prevent reconciliation glitches on reorders.

-          <Card key={index} className="product-card">
+          <Card key={product?.module || index} className="product-card">
📝 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
<Card key={index} className="product-card">
<Card key={product?.module || index} className="product-card">
🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductPage/ProductPageComponent.js
around line 42, the Card is using the array index as the React key which can
cause reconciliation bugs on reorder; replace the index with a stable
deterministic identifier from the product (e.g., product.id, product.module,
product.sku or another unique field) by passing that value as the key in the map
callback (or compute a stable key before rendering), and if no stable id exists
add one to the data model or derive a stable fallback (not the array index) so
keys remain consistent across renders.

Comment on lines +47 to +49
<div className="icon-wrap">
{Digit.Utils.iconRender(product.icon, "#c84c0e")}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Avoid hard‑coded icon color.

Prefer theme tokens or config‑driven color (e.g., product.iconColor) to keep branding consistent.

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductPage/ProductPageComponent.js
around lines 47-49, the icon color is hard-coded as "#c84c0e"; change this to
use a configurable source such as product.iconColor with a fallback to a theme
token or CSS variable (e.g., Digit theme token or a CSS var like
var(--brand-accent)) so branding is configurable; update the
Digit.Utils.iconRender call to pass product.iconColor || themeToken (or read the
CSS variable) instead of the literal string and ensure product.iconColor is
populated from product data or config.

Comment on lines +52 to +55
title={t(product.heading)}
>
{t(product.heading)}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Guard title attributes to avoid “title='undefined'”.

Only set title when keys exist.

-                title={t(product.heading)}
+                title={product?.heading ? t(product.heading) : undefined}
...
-              title={t(product?.cardDescription)}
+              title={product?.cardDescription ? t(product.cardDescription) : undefined}

Also applies to: 57-62

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductPage/ProductPageComponent.js
around lines 52-55 (and similarly 57-62), the title attribute is being set
unconditionally which can produce title="undefined"; change to only set the
title when the key exists (e.g., check product.heading and that
t(product.heading) returns a non-empty value) by conditionally including the
title prop (or passing null/undefined when absent) so the attribute is omitted
when there is no valid heading.

Comment on lines +64 to 71
className="explore-button-updated no-hover"
size={"medium"}
style={{
padding: "0px", justifyContent: "start", display: "flex",height: "1rem"
}}
variation="secondary"
label={`${t("COMMON_EXPLORE")} `}
label={`${t("COMMON_EXPLORE")} `}
onClick={() => handleNavigate(`/${window?.contextPath}/employee/sandbox/productDetailsPage/${product?.module}`)}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Add an accessible label to the Explore button.

If Button forwards unknown props, add aria‑label; otherwise skip.

-            <Button
+            <Button
+              aria-label={`${t("COMMON_EXPLORE")} ${t(product.heading)}`}
               className="explore-button-updated no-hover"

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

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductPage/ProductPageComponent.js
around lines 64–71, the Explore button lacks an accessible name; add an
aria-label prop with a descriptive label (for example use the translated text
plus the product name: aria-label={`${t("COMMON_EXPLORE")} ${product?.name ||
""}`}) so screen readers get a clear name that matches the visible label; if
this Button component does not forward unknown props to the underlying DOM
element, do not add aria-label and instead ensure the underlying DOM button
element receives the accessible name (or wrap with an element that provides it).

⚠️ Potential issue

Harden navigation URL: normalize contextPath and encode module.

Prevents accidental double slashes and unsafe characters breaking routing.

-            <Button
+            <Button
               className="explore-button-updated no-hover"
               size={"medium"}
               style={{
                 padding: "0px", justifyContent: "start", display: "flex",height: "1rem"
               }}
               variation="secondary"
               label={`${t("COMMON_EXPLORE")} ➔`}
-              onClick={() => handleNavigate(`/${window?.contextPath}/employee/sandbox/productDetailsPage/${product?.module}`)}
+              onClick={() => {
+                const ctx = String(window?.contextPath || "").replace(/^\/+|\/+$/g, "");
+                handleNavigate(`/${ctx}/employee/sandbox/productDetailsPage/${encodeURIComponent(product?.module || "")}`);
+              }}
             />
📝 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
className="explore-button-updated no-hover"
size={"medium"}
style={{
padding: "0px", justifyContent: "start", display: "flex",height: "1rem"
}}
variation="secondary"
label={`${t("COMMON_EXPLORE")} →`}
label={`${t("COMMON_EXPLORE")} ➔`}
onClick={() => handleNavigate(`/${window?.contextPath}/employee/sandbox/productDetailsPage/${product?.module}`)}
<Button
className="explore-button-updated no-hover"
size={"medium"}
style={{
padding: "0px", justifyContent: "start", display: "flex", height: "1rem"
}}
variation="secondary"
label={`${t("COMMON_EXPLORE")} ➔`}
onClick={() => {
const ctx = String(window?.contextPath || "").replace(/^\/+|\/+$/g, "");
const moduleSeg = encodeURIComponent(String(product?.module || ""));
const path = ctx
? `/${ctx}/employee/sandbox/productDetailsPage/${moduleSeg}`
: `/employee/sandbox/productDetailsPage/${moduleSeg}`;
handleNavigate(path);
}}
/>
🤖 Prompt for AI Agents
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductPage/ProductPageComponent.js
lines 64-71: the onClick builds a navigation URL using window.contextPath and
product.module directly, which can produce double slashes or unsafe characters;
normalize contextPath by trimming any leading/trailing slashes and ensure it is
prefixed with a single "/", and encode the product.module portion with
encodeURIComponent (or fallback to an empty string) before interpolating into
the path; update the onClick to construct the safe path using the normalized
contextPath + "employee/sandbox/productDetailsPage/" + encoded module and then
call handleNavigate with that sanitized path.

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: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0493e25 and b139194.

📒 Files selected for processing (2)
  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss (22 hunks)
  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.js

⚙️ CodeRabbit configuration file

check

Files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js
🧠 Learnings (11)
📚 Learning: 2025-08-19T07:07:45.217Z
Learnt from: syedkaifegov
PR: egovernments/DIGIT-Frontend#3012
File: micro-ui/web/micro-ui-internals/packages/css/src/index.scss:692-696
Timestamp: 2025-08-19T07:07:45.217Z
Learning: CSS changes for .employee-app-wrapper.sandbox are specific to Urban repo's sandbox branch implementation, not related to sandbox-login-page components found in the main codebase.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
📚 Learning: 2025-06-26T10:24:08.628Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss:1642-1662
Timestamp: 2025-06-26T10:24:08.628Z
Learning: In the sandbox SCSS file, tab styling with margin-bottom and border changes that cause layout shifts are intentional design requirements per updated UI specifications, not bugs to be fixed.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
📚 Learning: 2024-10-26T15:11:49.938Z
Learnt from: nabeelmd-eGov
PR: egovernments/DIGIT-Frontend#1680
File: health/micro-ui/web/micro-ui-internals/example/src/UICustomizations.js:1032-1094
Timestamp: 2024-10-26T15:11:49.938Z
Learning: In the DIGIT-Frontend project, when reviewing the `UICustomizations.js` file, avoid suggesting refactoring of repetitive rendering logic in switch cases into helper functions, unless explicitly requested.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
📚 Learning: 2025-06-26T10:16:36.004Z
Learnt from: Hari-egov
PR: egovernments/DIGIT-Frontend#2644
File: micro-ui/web/micro-ui-internals/packages/css/src/components/Carousel.scss:0-0
Timestamp: 2025-06-26T10:16:36.004Z
Learning: In the DIGIT-Frontend codebase, signup-container CSS classes cannot be namespaced under parent classes due to existing style constraints and requirements.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
📚 Learning: 2025-06-26T10:21:42.619Z
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:279-280
Timestamp: 2025-06-26T10:21:42.619Z
Learning: In the sandbox ProductDetailsComponentUpdated.js component, string concatenation is intentionally used for translation keys (e.g., `t(`${module}` + "_SECTION1_ROLE_1")`) instead of template literals for easier config identification and to make the key structure more explicit.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js
📚 Learning: 2024-11-07T11:02:33.520Z
Learnt from: rachana-egov
PR: egovernments/DIGIT-Frontend#1770
File: health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/pages/employee/viewVillage.js:320-322
Timestamp: 2024-11-07T11:02:33.520Z
Learning: In `health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/pages/employee/viewVillage.js`, the `data?.additionalFields` object is guaranteed to be defined, so checking for its existence before accessing its keys is unnecessary.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js
📚 Learning: 2025-06-04T04:08:48.234Z
Learnt from: jagankumar-egov
PR: egovernments/DIGIT-Frontend#2488
File: micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login/login.js:152-152
Timestamp: 2025-06-04T04:08:48.234Z
Learning: In the employee login component (micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Login/login.js), prefer using a `defaultTenant` variable to store `Digit.ULBService.getStateId()` instead of calling the function multiple times inline in the `defaultValue` object. This improves code readability and avoids function call duplication.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js
📚 Learning: 2024-06-10T19:25:42.992Z
Learnt from: siddhant-nawale-egov
PR: egovernments/DIGIT-Frontend#698
File: micro-ui/web/micro-ui-internals/packages/modules/hcm-microplanning/src/components/MicroplanPreview.js:1-1
Timestamp: 2024-06-10T19:25:42.992Z
Learning: The imports in `MicroplanPreview.js` are from different libraries: `egovernments/digit-ui-components` and `egovernments/digit-ui-react-components`.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js
📚 Learning: 2024-06-10T19:25:42.992Z
Learnt from: siddhant-nawale-egov
PR: egovernments/DIGIT-Frontend#675
File: micro-ui/web/micro-ui-internals/packages/modules/hcm-microplanning/src/components/MicroplanPreview.js:4-10
Timestamp: 2024-06-10T19:25:42.992Z
Learning: Imports in `MicroplanPreview.js` are either already grouped appropriately or come from different directories, as clarified by the user.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js
📚 Learning: 2024-06-10T19:25:42.992Z
Learnt from: siddhant-nawale-egov
PR: egovernments/DIGIT-Frontend#0
File: :0-0
Timestamp: 2024-06-10T19:25:42.992Z
Learning: The `onStepClick` logic in `CreateMicroplan.js` is intentionally commented out to allow developers to navigate between screens during development, even though not all screens are made yet.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js
📚 Learning: 2024-06-14T14:10:38.086Z
Learnt from: siddhant-nawale-egov
PR: egovernments/DIGIT-Frontend#876
File: micro-ui/web/micro-ui-internals/packages/css/src/components/microplanning.scss:1940-2392
Timestamp: 2024-06-14T14:10:38.086Z
Learning: Classes related to interactive elements in the microplan preview section are mostly passed to Higher Order Components (HOCs), and ARIA attributes for non-HOC elements will be managed directly by adding them where necessary.

Applied to files:

  • micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js
🧬 Code graph analysis (1)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js (3)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/index.js (1)
  • config (10-25)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.js (2)
  • module (27-27)
  • useTranslation (25-25)
micro-ui/web/micro-ui-internals/packages/modules/core/src/components/RoleBasedEmployeeHome.js (1)
  • getLinkByType (73-79)
🪛 Biome (2.1.2)
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js

[error] 247-252: 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] 247-252: 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] 111-111: This variable is used before its declaration.

The variable is declared here:

(lint/correctness/noInvalidUseBeforeDeclaration)


[error] 80-80: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 104-104: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)


[error] 150-165: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)

⏰ 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 (35)
micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss (19)

118-122: Duplicate comment: Class naming inconsistency already flagged


150-153: Duplicate comment: Viewport unit fallbacks already suggested


183-184: Duplicate comment: Rigid fixed dimensions already addressed


264-273: Duplicate comment: Font-family duplication and theme variables already noted


312-348: Duplicate comment: Tooltip accessibility improvements already suggested


375-377: Duplicate comment: CSS variables for colors already suggested


383-388: Duplicate comment: justify-content compatibility already noted


652-652: Duplicate comment: Typo in comment already identified


1518-1543: LGTM! Responsive design implementation

Good responsive breakpoint implementation for the video section and product cards with appropriate flex adjustments for different screen sizes.


1775-1780: Duplicate comment: align-items compatibility already noted


1854-1881: Duplicate comment: Hover state suppression already addressed


2017-2037: Duplicate comment: Title underline and color variables already noted


2175-2180: Duplicate comment: Conflicting height constraints already identified


2188-2217: Duplicate comment: Breadcrumb accessibility already addressed


940-1182: LGTM! Comprehensive responsive design system

Excellent implementation of responsive breakpoints covering a wide range of devices from small laptops to ultra-wide displays. The progressive scaling of typography, spacing, and layout elements is well thought out and maintains design consistency across different screen sizes.


354-363: Use modern CSS grid gap property

The grid-gap property is deprecated. Use the modern gap property instead.

 .sandbox-signup-form .digit-field .label-field-wrapper {
   all: unset !important;
-  grid-gap: .25rem !important;
+  gap: .25rem !important;

1947-1956: Large horizontal padding may cause overflow

The fixed padding: 0rem 4rem combined with margin-left: 2rem could cause content to overflow on smaller screens.

 .role-block {
   display: flex;
   align-items: center !important;
   justify-content: flex-start !important;
   gap: 1rem;
   width: 100%;
-  padding: 0rem 4rem;
-  margin-left: 2rem;
+  padding: 0 clamp(1rem, 5vw, 4rem);
+  margin-left: clamp(1rem, 3vw, 2rem);
 }

1565-1568: Consider using CSS custom properties for border styling

The hardcoded border color could benefit from using CSS variables for consistency with the design system.

 .custom-section-paragraph {
   font-size: 1rem;
   color: #363636;
   line-height: 1.6;
   margin-bottom: 1rem;
   margin-top: 0.5rem;
   padding-top: 0.5rem;
-  border-top: 2px solid #c84c0e;
+  border-top: 2px solid var(--digit-accent-color, #c84c0e);
   text-align: justify;
 }

354-363: Confirmed: Use modern CSS gap property

Based on the web search results, the grid-gap property has been deprecated in favor of the modern gap property. The gap property can be used on flex items in addition to grid containers, making it more versatile.

 .sandbox-signup-form .digit-field .label-field-wrapper {
   all: unset !important;
-  grid-gap: .25rem !important;
+  gap: .25rem !important;
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js (16)

74-74: Use self-closing tags for JSX elements without children.

Also applies to: 116-116, 272-272


111-111: Fix the incorrect function parameter syntax.


150-165: Add explicit type attribute to button element.


156-160: Harden window.open against reverse tabnabbing.

Also applies to: 330-333


246-253: Add keyboard accessibility to interactive tabs.


264-270: Add tablist role to tab container for accessibility.


336-358: Refactor duplicated logic in helper functions.


363-363: Remove console.log statement.


378-380: Remove unused state variables.


404-404: Fix experience.description to be a string, not an array.


419-419: Remove redundant assignment to content.experience.roles.


444-463: Add validation for text format before splitting.


280-328: Avoid mutating module-scoped object with render-time data.

The content object is defined at module scope and then mutated with props during render, which can cause state leakage across renders and component instances.

Move content derivation into the component using useMemo:

-const content = {
-    heroTitle: "Local Business License Issuing System",
-    // ... rest of static content
-};

 const ProductDetailsComponentUpdated = ({ config, module }) => {
+    const { t } = useTranslation();
+    
+    const content = React.useMemo(() => {
+        const baseContent = {
+            heroTitle: t(config[0].heading),
+            heroHeadline: [],
+            about: {
+                title: t(config[0].subsections[0].title),
+                paragraphs: config[0].subsections[0].content
+                    .filter(c => c.type === "paragraph")
+                    .map(c => t(c.text)),
+                roles: []
+            },
+            experience: {
+                title: t(config[0].subsections[2].title),
+                description: "",
+                roles: []
+            }
+        };
+        
+        // Build heroHeadline
+        const heroHeadlineItems = config[0].subsections[1].content.filter(c => c.type === "heroHeadline");
+        if (heroHeadlineItems.length > 0) {
+            baseContent.heroHeadline = [
+                { text: t(heroHeadlineItems[0]?.text || "Data missing"), color: "#215B73" },
+                { text: t(heroHeadlineItems[1]?.text || "Data missing"), color: "#C84C0E" },
+                { text: t(heroHeadlineItems[2]?.text || "Data missing"), color: "#215B73" },
+            ];
+        } else {
+            baseContent.heroHeadline = [
+                { text: t(`${module}_SECTION2_HL1`), color: "#215B73" },
+                { text: t(`${module}_SECTION2_HL2`), color: "#C84C0E" },
+                { text: t(`${module}_SECTION2_HL3`), color: "#215B73" },
+            ];
+        }
+        
+        // Build roles
+        const roleItems = config[0].subsections[0].content.filter(c => c.type === "role");
+        baseContent.about.roles = roleItems.length > 0
+            ? roleItems.map(c => ({ description: t(c.text || "Data missing") }))
+            : [
+                { description: t(`${module}_SECTION1_ROLE_1`) },
+                { description: t(`${module}_SECTION1_ROLE_2`) }
+            ];
+        
+        // Build experience description
+        const descParagraphs = config[0].subsections[2].content
+            .filter(c => c.type === "paragraph")
+            .map(c => t(c.text));
+        baseContent.experience.description = descParagraphs.join(" ");
+        
+        // Build experience roles (keep existing logic)
+        // ... (roles building logic here)
+        
+        return baseContent;
+    }, [config, module, t]);

Also applies to: 384-464


141-147: Add fallback for unknown icon names.

The code doesn't handle cases where an unknown icon key might be provided.

 {cards.map(({ icon, text }, idx) => {
-    const IconComponent = iconMap[icon];
+    const IconComponent = iconMap[icon] || Graph;
     return (
         <div key={idx} className="cs-card">
             <IconComponent className="cs-icon" />
             <span>{text}</span>
         </div>
     );
 })}

1-23: Reorganize imports for better maintainability.

Group all imports together at the top and remove the excessive empty lines.

 import React, { useState } from "react";
+import { useHistory } from "react-router-dom";
+import { useTranslation } from "react-i18next";
 import { SVG } from "@egovernments/digit-ui-components";
+import he from 'he';
 import { ReactComponent as Graph } from '../../../../src/components/images/graph.svg';
 import { ReactComponent as FeatureSearch } from '../../../../src/components/images/feature_search.svg';
 import { ReactComponent as Chat } from '../../../../src/components/images/chat.svg';
 import { ReactComponent as Calculate } from '../../../../src/components/images/calculate.svg';
 import { ReactComponent as BarChart } from '../../../../src/components/images/bar_chart.svg';
 import { ReactComponent as Employeeicon } from '../../../../src/components/images/employee.svg';
 import { ReactComponent as Citizenicon } from '../../../../src/components/images/Citizen.svg';
-import { useTranslation } from "react-i18next";
-import he from 'he';
+
 const iconMap = {
     Graph,
     FeatureSearch,
     Chat,
     Calculate,
     BarChart
 };
-
-
-
-
-import { useHistory } from "react-router-dom";

330-333: Move handleButtonClick inside the component.

The function handleButtonClick is defined at module scope but used inside components, causing a temporal dead zone error since it's used before components that reference it.

Move it inside the ProductDetailsComponentUpdated component or before the components that use it:

-const handleButtonClick = (action) => {
-    const url = '/' + window.contextPath + action + "?from=sandbox";
-    window.open(url, "_blank");
-};

 const ProductDetailsComponentUpdated = ({ config, module }) => {
+    const handleButtonClick = (action) => {
+        const url = '/' + window.contextPath + action + "?from=sandbox";
+        const w = window.open(url, "_blank", "noopener,noreferrer");
+        if (w) w.opener = null;
+    };
     
     // ... rest of component
     
     return (
         <div style={{ paddingLeft: '0.5rem' }}>
             {/* Pass handleButtonClick as prop */}
-            <ExperienceSection experience={content.experience} t={t} />
+            <ExperienceSection experience={content.experience} t={t} handleButtonClick={handleButtonClick} />

Then update the component signatures to accept and pass down the prop.

Comment on lines +1462 to +1474
font-family: 'Roboto Condensed';
font-weight: 700;
font-size: 2rem;
font-size: 40px;
line-height: 100%;
letter-spacing: 0px;
margin-bottom: 1.25rem;
color: #0b4b66;
text-align: left;
max-width: 70%;
position: relative;
display: flex;
flex-direction: column;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick

Use appropriate font fallbacks for custom fonts

The 'Roboto Condensed' font should include fallbacks for better cross-platform support.

 .custom-landing-header-grey {
-  font-family: 'Roboto Condensed';
+  font-family: 'Roboto Condensed', 'Roboto', Arial, sans-serif;
📝 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
font-family: 'Roboto Condensed';
font-weight: 700;
font-size: 2rem;
font-size: 40px;
line-height: 100%;
letter-spacing: 0px;
margin-bottom: 1.25rem;
color: #0b4b66;
text-align: left;
max-width: 70%;
position: relative;
display: flex;
flex-direction: column;
}
.custom-landing-header-grey {
font-family: 'Roboto Condensed', 'Roboto', Arial, sans-serif;
font-weight: 700;
font-size: 40px;
line-height: 100%;
letter-spacing: 0px;
margin-bottom: 1.25rem;
color: #0b4b66;
text-align: left;
max-width: 70%;
position: relative;
display: flex;
flex-direction: column;
}
🤖 Prompt for AI Agents
In micro-ui/web/micro-ui-internals/packages/css/src/pages/employee/sandbox.scss
around lines 1462 to 1474, the font-family declaration uses only 'Roboto
Condensed' which lacks fallbacks; update the font-family to include a sensible
fallback stack (e.g., next include the system/close match like Roboto, then
common sans-serif fonts such as 'Helvetica Neue' and Arial) and ensure the
generic family (sans-serif) is last so browsers that can't load the custom font
will use a reasonable substitute.

const decodedpara = he.decode(para); // decode &lt;strong&gt;

return (
<p key={i} className="about-description" dangerouslySetInnerHTML={{ __html: decodedpara }}></p>
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

Consider sanitizing HTML to prevent XSS attacks.

The use of dangerouslySetInnerHTML with user-controlled content from translations poses a security risk. While he.decode() handles HTML entity decoding, it doesn't sanitize against malicious scripts.

Consider using a library like DOMPurify to sanitize the HTML:

+import DOMPurify from 'dompurify';

 const AboutSection = ({ about }) => (
     // ...
     {about.paragraphs.map((para, i) => {
         const decodedpara = he.decode(para);
+        const sanitizedPara = DOMPurify.sanitize(decodedpara);
         return (
-            <p key={i} className="about-description" dangerouslySetInnerHTML={{ __html: decodedpara }}></p>
+            <p key={i} className="about-description" dangerouslySetInnerHTML={{ __html: sanitizedPara }}></p>
         )
     })}

Apply similar sanitization for the RoleBlock component.

Also applies to: 104-104

🧰 Tools
🪛 Biome (2.1.2)

[error] 80-80: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🤖 Prompt for AI Agents
In
micro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/ProductDetails/ProductDetailsComponentUpdated.js
around lines 80 and 104, you are passing decoded translation HTML directly to
dangerouslySetInnerHTML which risks XSS; instead import and use a sanitizer
(e.g., DOMPurify) to sanitize the decoded HTML before assigning it to
dangerouslySetInnerHTML (e.g., const safeHtml = DOMPurify.sanitize(decodedpara))
and then use safeHtml in the dangerouslySetInnerHTML prop; apply the same
sanitization step for the RoleBlock content at line 104 and ensure the sanitizer
is only used in the browser build or guarded for SSR if needed.

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.

5 participants