-
Notifications
You must be signed in to change notification settings - Fork 46
Sandbox login page #2765
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Sandbox login page #2765
Conversation
* sandbox-ui-update * ui-updates * ui-update and old ui route fix * ui update and old ui conifgs added to view
)" This reverts commit 51e8d80.
…IGIT-Frontend into sandbox-ui-update
* privacy component alignment fix * package changes
…IGIT-Frontend into sandbox-ui-update
Co-authored-by: syedkaif866 <[email protected]>
Co-authored-by: syedkaif866 <[email protected]>
There was a problem hiding this 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.
onSelectisn’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 wanttranslateX(-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.
Fragmentis unused (you useReact.Fragment). Also consider removinguseRefifwrapperRefis 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.
wrapperRefis assigned but not used. Remove it and therefprop 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
📒 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.jsmicro-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(…)
| 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); | ||
| }, []); |
There was a problem hiding this comment.
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}}> |
There was a problem hiding this comment.
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.
| <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.
| <Button | ||
| label={t(`SB_FORGOTORGANIZATION_TOOLTIP`)} | ||
| variation={"link"} | ||
| size={"small"} | ||
| onClick={() => setShowTip((prev) => !prev)} | ||
| // isSuffix={true} | ||
| style={{ marginBottom: "0.5rem", paddingLeft: "0.2rem" }} | ||
| ></Button> |
There was a problem hiding this comment.
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 }) => { |
There was a problem hiding this comment.
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.
| 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); | ||
| }, []); |
There was a problem hiding this comment.
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"); |
There was a problem hiding this comment.
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.
| 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.
There was a problem hiding this 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 earliermargin-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 buttonYou set
width: 100%and later override withwidth: auto;. Also with bothleftandrightset, 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 constraintsSame 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 constraintsThis 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 colorYou 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 prefsEnsure 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: unsetremoves layout-critical properties. Also usegapinstead ofgrid-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 colorsReplace
#0B4B66with 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-visibleThe .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 screensMake 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 colorWidth 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: 40removerridesheight: 500px. Keep the intended constraint..wt-iframe { width: 100%; - height: 500px; border: none; min-height: 40rem; }If you need an upper cap, add
max-heightinstead.
1724-1753: Breadcrumbs: add focus-visible and current-page state for a11yAdd 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
📒 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 utilityConcise and clear flex alignment utility; no issues.
406-408: LGTM: submit bar uses theme tokenGood use of theme() for color. No changes needed.
1369-1390: LGTM: new icon/image helpersClear 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-packrule can be safely removed:- -ms-flex-pack: justify; justify-content: space-evenly;
| .SandboxLogoAlignCenter { | ||
| align-items: center; | ||
| justify-content: center; | ||
| display: flex; | ||
| } |
There was a problem hiding this comment.
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/srcLength 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.
| .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
There was a problem hiding this 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 fallbacksThis 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
⛔ Files ignored due to path filters (1)
micro-ui/web/micro-ui-internals/packages/css/package.jsonis 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 EOFNo issues; keeping a trailing newline is standard.
| <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" /> |
There was a problem hiding this comment.
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
doneLength 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.
| <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> |
There was a problem hiding this comment.
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.
There was a problem hiding this 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” entryThe 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 undefinedIf 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 callsLogin-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.
📒 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.jsmicro-ui/web/micro-ui-internals/packages/libraries/src/services/atoms/ApiCacheService.jsmicro-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.jsmicro-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.jsmicro-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.jsmicro-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.jsmicro-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.jsmicro-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 reasonableKeeping 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 thepreserveKeysarray 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.
| cacheTimeInSecs: 0, | ||
| // cacheTimeInSecs: 3600, | ||
| debounceTimeInMS: 100, | ||
| moduleSettings: [ |
There was a problem hiding this comment.
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.
| serviceName: "mdms-v2", | ||
| cacheTimeInSecs: 3600, | ||
| cacheTimeInSecs: 0, | ||
| // cacheTimeInSecs: 3600, | ||
| debounceTimeInMS: 100, | ||
| moduleSettings: [ |
There was a problem hiding this comment.
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.
| 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.
| const moduleCode = ["privacy-policy"]; | ||
| const language = Digit.StoreData.getCurrentLanguage(); | ||
| const modulePrefix = "digit"; | ||
| const { data: store } = Digit.Services.useStore({ | ||
| stateCode, | ||
| moduleCode, | ||
| language, | ||
| modulePrefix | ||
| }); |
There was a problem hiding this comment.
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.
| 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.
| // Clear API cache service | ||
| if (window.Digit?.ApiCacheService) { | ||
| window.Digit.ApiCacheService.clearAllCache(); | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
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.
| // 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.
| // 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]); |
There was a problem hiding this comment.
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
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.
| // 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.
| if (window.Digit?.ApiCacheService) { | ||
| window.Digit.ApiCacheService.clearAllCache(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
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.
| 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.
| 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]); |
There was a problem hiding this comment.
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.
| 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]>
There was a problem hiding this 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
applicationStatusis undefined,.join()on the result of the optional.map()will throw. Also, you readpgrfilterswhile the initial state useswfFilters. 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,
setTotalRecordsmay run on an unmounted component or with a stale tenant. Add a cancellation flag and includetenantIdin 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
pageSizeas 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 !== nullis always true. Gate byisLoadingand default data to [].The current condition renders the inbox even when
complaintsis 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
useCallbackwith tight deps can help avoid unnecessary re-renders in large lists.I can provide a compact
useCallbackpass 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
uuidlater 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. IncludeisAssignedToMe(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 mutateuuid; setassigneeto [] for “ALL” to match new empty-state semantics.Mutating
uuidcauses 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 entirewfQuerywhen one filter entry is empty; delete the specific property instead.Resetting the whole object drops other wf filters unintentionally (TOCTOU-like behavior). Mirror the
pgrQuerylogic.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 (assignedvsassignee), invalid shape, and invalid radio selectedOption.
- Uses
assignedinstead ofassignee.- Sets
code: [](should be string/undefined).- Sets
selectedAssignedto""which breaksRadioButtonsexpecting 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 usesnamebutDropdownexpectsi18nkey; this can render an empty label and break slicing.Align the placeholder object to the
optionKey="i18nkey"you pass toDropdown.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.slicewill 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 importRoundedLabel.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-levelpgrQuery/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 localconst nextPgrQuery = {}/const nextWfQuery = {}anduseRefto avoid unnecessary emits.Would you like a patch that removes the globals and performs a shallow-equal check before calling
onFilterChange?
81-86: Desktop: FrequentonFilterChangecalls—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.
📒 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.jsmicro-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: DefaultingwfFilters.assigneeto 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.
| // const [searchParams, setSearchParams] = useState({ filters: { wfFilters: { assignee: [{ code: uuid }] } }, search: "", sort: {} }); | ||
|
|
||
| const [searchParams, setSearchParams] = useState({ filters: { wfFilters: { assignee: [] } }, search: "", sort: {} }); | ||
|
|
There was a problem hiding this comment.
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.
There was a problem hiding this 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 tamperingPrevious 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.
📒 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
| <!-- 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); | ||
| })(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
🛠️ Refactor suggestion
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.
| <!-- 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.
| 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); | ||
| })(); |
There was a problem hiding this comment.
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.
| 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).
| // 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}` | ||
| ]); | ||
| } | ||
| }); | ||
| })(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
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.
| // 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.
There was a problem hiding this 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: UseconstforconfigThe 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 unusedloginParamsAlign 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 undefineddefaultValueand guardpopulatorsbefore write
defaultValueis not defined and writing topopulators.defaultValuecan throw ifpopulatorsis 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
defaultTenantdefinition:const defaultTenant = Digit?.ULBService?.getStateId?.() || stateInfo?.code;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 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)
| 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`; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| const reqCreate = { | ||
| url: `/user-otp/v1/_send`, | ||
| params: { tenantId: Digit?.ULBService?.getStateId() }, | ||
| body: {}, | ||
| config: { | ||
| enable: false, | ||
| }, | ||
| }; |
There was a problem hiding this comment.
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} />} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| {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} /> |
There was a problem hiding this comment.
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.
| <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.
There was a problem hiding this 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
📒 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]); + } + });
| const handleSubmit = (event) => { | ||
| history.push(`/${getContextPath(window.contextPath)}/user/login?ts=${Date.now()}`); | ||
| }; |
There was a problem hiding this comment.
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.
| 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.
| // 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 |
There was a problem hiding this comment.
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.
| 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(); | ||
| } |
There was a problem hiding this comment.
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.
| 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.
There was a problem hiding this 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
sessionStoragereads/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.mapwhenpropsConfig?.inputsis 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
errorboolean 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
propsConfigis 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.
defaultValueis not defined; also use===/!==. Initializepopulatorsdefensively.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, andisDisabled, notloginParams.-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
undefinedwhenpropsConfig?.inputsisn’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
📒 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.jsmicro-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.jsmicro-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.jsmicro-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.jsmicro-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 callersFound 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.
| const onLogin = async (data) => { | ||
| const inputEmail = (data?.email || "").toLowerCase(); | ||
| const tenantId = data.accountName; | ||
| await mutation.mutate( |
There was a problem hiding this comment.
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.
| 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"; |
There was a problem hiding this comment.
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.
| 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(); |
There was a problem hiding this comment.
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.
| 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; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
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.
| // Mobile detection (simple) | ||
| const isMobile = window.innerWidth <= 768; | ||
|
|
There was a problem hiding this comment.
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.
There was a problem hiding this 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 linksPin 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 hashchangePrevents 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/labelsAvoid 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 incorrectMove 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 hardeningCurrent 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 supportMirror 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 payloadPrevent 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 fallbackSame 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
📒 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> |
There was a problem hiding this comment.
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.
| <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.
There was a problem hiding this 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-bottomis nulled by latermargin: unset
margin: unset !important;wipes out the earliermargin-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 bywidth: autobelow. 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.centerlacks a leading.and targets a non-existent element, not the.digit-card-textclass. Fix selector.-digit-card-text.center { +.digit-card-text.center { text-align: center; }
806-836: Honor reduced-motion preference for loader animationOffer 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
📒 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.jsmicro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/OtpCustomComponent.jsmicro-ui/web/micro-ui-internals/packages/modules/core/src/config/LandingPageConfig.jsmicro-ui/web/micro-ui-internals/packages/modules/sandbox/src/pages/employee/index.jsmicro-ui/web/micro-ui-internals/packages/modules/core/src/pages/employee/Otp/index.jsmicro-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.jsmicro-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.jsmicro-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
.SandboxLogoAlignCenteris inconsistent with prevailing kebab-case in this file. Rename to.sandbox-logo-align-centerand 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 UIsPrior 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 cards650×500 with
!importantis too rigid; prefer responsive constraints as suggested earlier.
235-244: Clean duplicate font-family, reduce large top margin, use theme color tokenThis was previously flagged; please apply the same cleanups here.
273-279: Mobile signup wrapper uses 100vh without small-viewport fallbackReuse the earlier svh/dvh recommendation for consistent behavior on mobile browsers.
283-319: Tooltip should respond to keyboard focus and reduced motionEarlier guidance covered adding
:focus-withinandprefers-reduced-motionhandling. Please apply it here.
321-334: Don’t nuke layout withall: unset; replacegrid-gapUse targeted resets and
gap. This matches the prior recommendation.
346-349: Use CSS variable for brand colorReplace hardcoded
#0B4B66with 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 colorEarlier review covered making the underline track text width and using a color variable. Apply the same fix here.
1879-1886: Responsive padding for.role-blockLarge horizontal padding may overflow on small screens; prior suggestion adds a mobile override. Please apply it.
2105-2110: Conflictingheightandmin-heighton iframeThis was previously flagged; keep only the intended constraint.
2118-2157: Add focus-visible/active styles for breadcrumbsApply 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 addaria-labelandaria-currentfor 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 fineNo 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.
| .loginSignUpSelector{ | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: end; | ||
|
|
||
| } |
There was a problem hiding this comment.
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.
| .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", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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" || trueLength 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.
| "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.
| const OtpConfig = [{ texts: { submitButtonLabel: "CORE_COMMON_SUBMIT" } }]; | ||
|
|
There was a problem hiding this comment.
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.
| 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.
| 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`; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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} />} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| {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.
| const introSection = subsections.find(s => s.type === "introSection") || {}; | ||
| const stepsSection = subsections.find(s => s.type === "stepsSection") || {}; | ||
| const buttonSection = subsections.find(s => s.type === "ButtonWrapper") || {}; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| // 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 }; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| console.log("*** Log ===> ", stepsSection.title); | ||
| console.log("*** First Part ===> ", firstPart); | ||
| console.log("*** Last Part ===> ", lastPart); |
There was a problem hiding this comment.
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.
| 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.
| <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> |
There was a problem hiding this comment.
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.
| <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> |
| {/* 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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| {/* 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.
There was a problem hiding this 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 widthsYou 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
📒 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.jsmicro-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.scssmicro-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.jsmicro-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 scaffoldingStructure and spacing look reasonable.
24-28: LGTM: responsive padding reset over 1280pxMedia override is clear and scoped.
143-146: LGTM: landing wrapper marginsNo issues.
940-1116: LGTM: comprehensive responsive breakpointsBreakpoint 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-caseAlign 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 viewportsvh/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 constraintsHard 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 wrapperApply 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 colorRemove 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 motionShow 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 gapLimit 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 colorReplace 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 typoRemove 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 a11yAdd 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 blocksLarge 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 widthComment 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 iframemin-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 breadcrumbsEnsure 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 tokensSame as above for steps header.
- color: #0B4B66; + color: var(--digit-primary-color, #0B4B66);
1671-1679: Tokenize repeated brand colorUse CSS variable instead of #0b4b66.
- color: #0b4b66; + color: var(--digit-primary-color, #0B4B66);
1565-1568: Tokenize border color and avoid magic numbersUse 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/maxFixed 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.
| display: flex; | ||
| align-items: center; | ||
| align-items: start; | ||
| gap: 0.5rem; | ||
| min-height: 3.2rem; | ||
|
|
||
| margin-bottom: 0.5rem; | ||
| position: relative; | ||
| } |
There was a problem hiding this comment.
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.
| 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.
| <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> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| <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.
| 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>; | ||
| }; |
There was a problem hiding this comment.
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"> |
There was a problem hiding this comment.
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.
| <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.
| <div className="icon-wrap"> | ||
| {Digit.Utils.iconRender(product.icon, "#c84c0e")} | ||
| </div> |
There was a problem hiding this comment.
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.
| title={t(product.heading)} | ||
| > | ||
| {t(product.heading)} | ||
| </div> |
There was a problem hiding this comment.
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.
| 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}`)} |
There was a problem hiding this comment.
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).
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.
| 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.
There was a problem hiding this 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
📒 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 implementationGood 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 systemExcellent 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 propertyThe
grid-gapproperty is deprecated. Use the moderngapproperty 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 overflowThe fixed
padding: 0rem 4remcombined withmargin-left: 2remcould 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 stylingThe 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 propertyBased on the web search results, the
grid-gapproperty has been deprecated in favor of the moderngapproperty. Thegapproperty 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
contentobject 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
handleButtonClickis 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
ProductDetailsComponentUpdatedcomponent 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.
| 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; | ||
| } |
There was a problem hiding this comment.
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.
| 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 <strong> | ||
|
|
||
| return ( | ||
| <p key={i} className="about-description" dangerouslySetInnerHTML={{ __html: decodedpara }}></p> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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
UI/UX Improvements
Behavioral Changes
Chores
Performance