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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM egovio/flyway:4.1.2
FROM egovio/flyway:10.7.1

COPY ./migration/main /flyway/sql

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@egovernments/digit-ui-module-core":"1.7.40",
"@egovernments/digit-ui-module-dss":"1.7.40",
"@egovernments/digit-ui-react-components":"1.7.40",
"@egovernments/digit-ui-module-finance": "0.0.1-sandbox",
Copy link
Collaborator

Choose a reason for hiding this comment

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

remove the sandbox word from version

"http-proxy-middleware": "1.0.5",
"react": "17.0.2",
"react-dom": "17.0.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { initBillsComponents, BillsModule } from "@egovernments/digit-ui-module-
// import { subFormRegistry } from "@egovernments/digit-ui-libraries";

import { pgrCustomizations, pgrComponents } from "./pgr";
import { initFinanceComponents, FinanceModule } from "@egovernments/digit-ui-module-finance";

var Digit = window.Digit || {};

Expand All @@ -59,6 +60,7 @@ const enabledModules = [
"Bills",
"SW",
"BillAmendment",
"Finance"
];

const initTokens = (stateCode) => {
Expand Down Expand Up @@ -103,6 +105,8 @@ const initDigitUI = () => {
HRMSModule,
ReceiptsModule,
BillsModule,
FinanceModule,
// FireNocCard

// TLModule,
// TLLinks,
Expand All @@ -122,6 +126,7 @@ const initDigitUI = () => {
initWSComponents();
initCommonPTComponents();
initBillsComponents();
initFinanceComponents();

// initCustomisationComponents();

Expand Down
2 changes: 2 additions & 0 deletions frontend/micro-ui/web/micro-ui-internals/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"dev:bills": "cd packages/modules/bills && yarn start",
"dev:reports": "cd packages/modules/reports && yarn start",
"dev:example": "cd example && yarn start",
"dev:finance": "cd packages/modules/finance && yarn start",
"build": "run-p build:**",
"build:libraries": "cd packages/libraries && yarn build",
"build:components": "cd packages/react-components && yarn build",
Expand All @@ -61,6 +62,7 @@
"build:ws": "cd packages/modules/ws && yarn build",
"buildD:bills": "cd packages/modules/bills && yarn build",
"buildR:reports": "cd packages/modules/reports && yarn build",
"build:finance": "cd packages/modules/finance && yarn build",
"deploy:jenkins": "./scripts/jenkins.sh"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# digit-ui-module-finance

## Install

```bash
npm install --save @egovernments/digit-ui-module-finance
```

## Limitation

```bash
This Package is more specific to Urban
```

## Usage

After adding the dependency make sure you have this dependency in

```bash
frontend/micro-ui/web/package.json
```

## Changelog

### Summary for Version [0.0.1] - 2025-06-13

Created finance module with submenu card and iframe pages

### Contributors

[kamalesh-egov]
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Add missing trailing newline.

The file should end with a single newline character as flagged by the linter.

 [kamalesh-egov]
+
📝 Committable suggestion

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

Suggested change
[kamalesh-egov]
[kamalesh-egov]
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

31-31: Files should end with a single newline character

(MD047, single-trailing-newline)

🤖 Prompt for AI Agents
In frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/README.md
at line 31, add a single newline character at the end of the file to satisfy the
linter requirement for a trailing newline.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@egovernments/digit-ui-module-finance",
"version": "0.0.1-sandbox",
"description": "FINANCE",
"license": "MIT",
"main": "dist/index.js",
"module": "dist/index.modern.js",
"source": "src/Module.js",
"files": [
"dist"
],
"scripts": {
"start": "microbundle-crl watch --no-compress --format modern,cjs",
"build": "microbundle-crl --compress --no-sourcemap --format cjs",
"prepublish": "yarn build"
},
"dependencies": {
"@egovernments/digit-ui-react-components": "^1.7.0-beta.2",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-hook-form": "6.15.8",
"react-i18next": "11.16.2",
"react-query": "3.6.1",
"react-router-dom": "5.3.0"
Comment on lines +19 to +24
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify React version consistency across the monorepo.

The React version is pinned to 17.0.2. Ensure this aligns with other packages in the monorepo to avoid version conflicts.


🏁 Script executed:

#!/bin/bash
# Description: Check React versions across the monorepo for consistency
# Expected: All packages should use the same React version to avoid conflicts

echo "Checking React versions across packages:"
fd -t f "package.json" . --exec grep -l '"react":' {} \; | head -10 | xargs grep '"react":'

Length of output: 1387


Align React versions across the monorepo

A discrepancy was found in one package:

  • frontend/mono-ui/web/ui-uploader/package.json pins "react": "^16.6.3", while the rest of the repo uses "17.0.2".

Please update this entry to "17.0.2" to ensure consistency and prevent potential runtime conflicts.

🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/package.json
around lines 19 to 24, the React version is set to 17.0.2, but another package
in the monorepo uses React 16.6.3. To maintain consistency and avoid conflicts,
update the React version in frontend/mono-ui/web/ui-uploader/package.json from
"^16.6.3" to "17.0.2" to align with the rest of the repository.

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { CollectionIcon, Header } from "@egovernments/digit-ui-react-components";
import React from "react";
import { useTranslation } from "react-i18next";
import LandingPageSubMenuCard from "./components/LandingPageSubMenuCard";

const buildMenuDict = (items) => {
const root = {};
items?.forEach(item => {
const parts = item.path.split('.');
let current = root;
parts?.forEach((part, idx) => {
if (!current[part]) current[part] = {};
if (idx === parts.length - 1) {
// attach leaf data
current[part] = {
...current[part],
_meta: {
label: item.displayName,
link: item.navigationURL,
}
};
}
current = current[part];
});
});
return root;
}
Comment on lines +6 to +27
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Add defensive programming for buildMenuDict.

The function could be more robust with better null/undefined checks and error handling.

 const buildMenuDict = (items) => {
   const root = {};
+  if (!Array.isArray(items)) return root;
+  
   items?.forEach(item => {
+    if (!item?.path) return;
     const parts = item.path.split('.');
     let current = root;
     parts?.forEach((part, idx) => {
+      if (!part) return;
       if (!current[part]) current[part] = {};
       if (idx === parts.length - 1) {
         // attach leaf data
         current[part] = {
           ...current[part],
           _meta: {
             label: item.displayName,
             link: item.navigationURL,
           }
         };
       }
       current = current[part];
     });
   });
   return root;
 }
📝 Committable suggestion

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

Suggested change
const buildMenuDict = (items) => {
const root = {};
items?.forEach(item => {
const parts = item.path.split('.');
let current = root;
parts?.forEach((part, idx) => {
if (!current[part]) current[part] = {};
if (idx === parts.length - 1) {
// attach leaf data
current[part] = {
...current[part],
_meta: {
label: item.displayName,
link: item.navigationURL,
}
};
}
current = current[part];
});
});
return root;
}
const buildMenuDict = (items) => {
const root = {};
if (!Array.isArray(items)) return root;
items?.forEach(item => {
if (!item?.path) return;
const parts = item.path.split('.');
let current = root;
parts?.forEach((part, idx) => {
if (!part) return;
if (!current[part]) current[part] = {};
if (idx === parts.length - 1) {
// attach leaf data
current[part] = {
...current[part],
_meta: {
label: item.displayName,
link: item.navigationURL,
}
};
}
current = current[part];
});
});
return root;
}
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/FinanceHomeCard.js
between lines 6 and 27, the buildMenuDict function lacks sufficient null and
undefined checks which could cause runtime errors. Add defensive programming by
verifying that items is an array before iterating, check that item.path is a
non-empty string before splitting, and ensure parts is a valid array before
processing. Also, consider wrapping the logic in try-catch to handle unexpected
errors gracefully.


const FinanceCard = () => {
const { t } = useTranslation();
const userRoles = Digit.SessionStorage.get("User")?.info?.roles;
const allowedRoles = [
"EMPLOYEE_FINANCE",
"EGF_VOUCHER_CREATOR",
"EGF_MASTER_ADMIN",
"EGF_REPORT_VIEW",
"EGF_BILL_CREATOR",
"EGF_ADMINISTRATOR",
"EGF_BILL_APPROVER",
"SYS_INTEGRATOR_FINANCE",
"EGF_PAYMENT_CREATOR",
"EGF_VOUCHER_APPROVER",
"WORKS_FINANCIAL_APPROVER",
"EGF_PAYMENT_APPROVER"
];
const isFinanceEmployee = userRoles.find((role) => allowedRoles.includes(role.code));
Comment on lines +31 to +46
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add defensive programming for role checking.

The role checking logic assumes userRoles is an array and each role has a code property, which could cause runtime errors.

   const userRoles = Digit.SessionStorage.get("User")?.info?.roles;
   const allowedRoles = [
     "EMPLOYEE_FINANCE", 
     "EGF_VOUCHER_CREATOR", 
     "EGF_MASTER_ADMIN", 
     "EGF_REPORT_VIEW", 
     "EGF_BILL_CREATOR", 
     "EGF_ADMINISTRATOR", 
     "EGF_BILL_APPROVER", 
     "SYS_INTEGRATOR_FINANCE", 
     "EGF_PAYMENT_CREATOR", 
     "EGF_VOUCHER_APPROVER", 
     "WORKS_FINANCIAL_APPROVER", 
     "EGF_PAYMENT_APPROVER"
   ];
-  const isFinanceEmployee = userRoles.find((role) => allowedRoles.includes(role.code));
+  const isFinanceEmployee = Array.isArray(userRoles) && userRoles.find((role) => role?.code && allowedRoles.includes(role.code));
📝 Committable suggestion

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

Suggested change
const userRoles = Digit.SessionStorage.get("User")?.info?.roles;
const allowedRoles = [
"EMPLOYEE_FINANCE",
"EGF_VOUCHER_CREATOR",
"EGF_MASTER_ADMIN",
"EGF_REPORT_VIEW",
"EGF_BILL_CREATOR",
"EGF_ADMINISTRATOR",
"EGF_BILL_APPROVER",
"SYS_INTEGRATOR_FINANCE",
"EGF_PAYMENT_CREATOR",
"EGF_VOUCHER_APPROVER",
"WORKS_FINANCIAL_APPROVER",
"EGF_PAYMENT_APPROVER"
];
const isFinanceEmployee = userRoles.find((role) => allowedRoles.includes(role.code));
const userRoles = Digit.SessionStorage.get("User")?.info?.roles;
const allowedRoles = [
"EMPLOYEE_FINANCE",
"EGF_VOUCHER_CREATOR",
"EGF_MASTER_ADMIN",
"EGF_REPORT_VIEW",
"EGF_BILL_CREATOR",
"EGF_ADMINISTRATOR",
"EGF_BILL_APPROVER",
"SYS_INTEGRATOR_FINANCE",
"EGF_PAYMENT_CREATOR",
"EGF_VOUCHER_APPROVER",
"WORKS_FINANCIAL_APPROVER",
"EGF_PAYMENT_APPROVER"
];
const isFinanceEmployee = Array.isArray(userRoles) && userRoles.find((role) => role?.code && allowedRoles.includes(role.code));
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/FinanceHomeCard.js
between lines 31 and 46, the code assumes userRoles is always an array and each
role has a code property, which may cause runtime errors if userRoles is
undefined or not an array. Add defensive checks to ensure userRoles is an array
before calling find, and verify each role object has a code property before
accessing it. This will prevent errors when userRoles is missing or malformed.

const { isLoading, data } = Digit.Hooks.useAccessControl();
const menuDict = buildMenuDict(data?.actions)?.Finance;

if (!isFinanceEmployee || !menuDict) return null;
return (
<div>
<Header styles={{ marginLeft: "24px", paddingTop: "10px", fontSize: "32px" }}>{t("ACTION_TEST_FINANCE")}</Header>
<div className="moduleCardWrapper gridModuleWrapper">
{Object.entries(menuDict).map(([key, value]) => (
<LandingPageSubMenuCard key={key} t={t} Icon={<CollectionIcon />} moduleName={key} menuDict={value} />
))}
</div>
</div>
)
};

export default FinanceCard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { useEffect } from "react";
import { useRouteMatch } from "react-router-dom";
import { useTranslation } from "react-i18next";
import EmployeeApp from "./pages/employee";
import FinanceCard from "./FinanceHomeCard";

export const FinanceModule = ({ stateCode, userType, tenants }) => {
const tenantId = Digit.SessionStorage.get("CITIZEN.COMMON.HOME.CITY")?.code || Digit.ULBService.getCurrentTenantId();
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider defensive coding for tenantId retrieval.

The current implementation may fail if the session storage data structure changes. Consider adding null checks or default values.

-  const tenantId =  Digit.SessionStorage.get("CITIZEN.COMMON.HOME.CITY")?.code || Digit.ULBService.getCurrentTenantId();
+  const tenantId = Digit.SessionStorage.get("CITIZEN.COMMON.HOME.CITY")?.code || 
+                   Digit.ULBService.getCurrentTenantId() || 
+                   stateCode;
📝 Committable suggestion

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

Suggested change
const tenantId = Digit.SessionStorage.get("CITIZEN.COMMON.HOME.CITY")?.code || Digit.ULBService.getCurrentTenantId();
const tenantId = Digit.SessionStorage.get("CITIZEN.COMMON.HOME.CITY")?.code ||
Digit.ULBService.getCurrentTenantId() ||
stateCode;
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/Module.js
at line 8, the retrieval of tenantId from session storage lacks defensive checks
and may fail if the data structure changes. Update the code to safely check if
the session storage key exists and if the nested code property is accessible
before accessing it. Provide a sensible default value or fallback to
Digit.ULBService.getCurrentTenantId() only after confirming the session storage
data is valid to prevent runtime errors.

const moduleCode = ["finance"];

const language = Digit.StoreData.getCurrentLanguage();
const { isLoading, data: store } = Digit.Services.useStore({ stateCode, moduleCode, language });
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Remove unused store data.

The store variable from the useStore hook is never used in the component. Consider removing it if not needed or implement the intended functionality.

-  const { isLoading, data: store } = Digit.Services.useStore({ stateCode, moduleCode, language });
+  // Remove if not needed, or implement the intended store usage

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

🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/Module.js
at line 12, the variable `store` destructured from the useStore hook is not used
anywhere in the component. Remove `store` from the destructuring assignment to
clean up unused variables, or if the store data is intended to be used,
implement the necessary logic to utilize it.

const { path, url } = useRouteMatch();

Digit.SessionStorage.set("FINANCE_TENANTS", tenants);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Move side effects to useEffect.

Setting session storage directly in the render function can cause unnecessary re-renders and side effects. Move this to a useEffect hook.

+  useEffect(() => {
+    Digit.SessionStorage.set("FINANCE_TENANTS", tenants);
+  }, [tenants]);
+
-  Digit.SessionStorage.set("FINANCE_TENANTS", tenants);
📝 Committable suggestion

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

Suggested change
Digit.SessionStorage.set("FINANCE_TENANTS", tenants);
useEffect(() => {
Digit.SessionStorage.set("FINANCE_TENANTS", tenants);
}, [tenants]);
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/Module.js
at line 15, the call to Digit.SessionStorage.set is causing a side effect
directly in the render function. To fix this, move the session storage setting
logic inside a useEffect hook that runs when the tenants data changes. This will
prevent unnecessary re-renders and keep side effects properly managed.


if (userType === "employee") {
return <EmployeeApp path={path} url={url} userType={"employee"} />;
} else return null;
};
Comment on lines +7 to +20
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add PropTypes validation.

The component lacks prop validation which could lead to runtime errors. Add PropTypes to ensure type safety.

Add PropTypes import and validation:

import React, { useEffect } from "react";
import { useRouteMatch } from "react-router-dom";
import { useTranslation } from "react-i18next";
+import PropTypes from "prop-types";
import EmployeeApp from "./pages/employee";
import FinanceCard from "./FinanceHomeCard";

export const FinanceModule = ({ stateCode, userType, tenants }) => {
  // ... component implementation
};

+FinanceModule.propTypes = {
+  stateCode: PropTypes.string.isRequired,
+  userType: PropTypes.string.isRequired,
+  tenants: PropTypes.array
+};
📝 Committable suggestion

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

Suggested change
export const FinanceModule = ({ stateCode, userType, tenants }) => {
const tenantId = Digit.SessionStorage.get("CITIZEN.COMMON.HOME.CITY")?.code || Digit.ULBService.getCurrentTenantId();
const moduleCode = ["finance"];
const language = Digit.StoreData.getCurrentLanguage();
const { isLoading, data: store } = Digit.Services.useStore({ stateCode, moduleCode, language });
const { path, url } = useRouteMatch();
Digit.SessionStorage.set("FINANCE_TENANTS", tenants);
if (userType === "employee") {
return <EmployeeApp path={path} url={url} userType={"employee"} />;
} else return null;
};
import React, { useEffect } from "react";
import { useRouteMatch } from "react-router-dom";
import { useTranslation } from "react-i18next";
import PropTypes from "prop-types";
import EmployeeApp from "./pages/employee";
import FinanceCard from "./FinanceHomeCard";
export const FinanceModule = ({ stateCode, userType, tenants }) => {
const tenantId =
Digit.SessionStorage.get("CITIZEN.COMMON.HOME.CITY")?.code ||
Digit.ULBService.getCurrentTenantId();
const moduleCode = ["finance"];
const language = Digit.StoreData.getCurrentLanguage();
const { isLoading, data: store } = Digit.Services.useStore({
stateCode,
moduleCode,
language,
});
const { path, url } = useRouteMatch();
Digit.SessionStorage.set("FINANCE_TENANTS", tenants);
if (userType === "employee") {
return <EmployeeApp path={path} url={url} userType={"employee"} />;
} else return null;
};
FinanceModule.propTypes = {
stateCode: PropTypes.string.isRequired,
userType: PropTypes.string.isRequired,
tenants: PropTypes.array,
};
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/Module.js
around lines 7 to 20, the FinanceModule component lacks PropTypes validation.
Import PropTypes at the top of the file and define PropTypes for the component
specifying the expected types for stateCode, userType, and tenants props to
ensure type safety and prevent runtime errors.


const componentsToRegister = {
FinanceModule,
FinanceCard,
};

export const initFinanceComponents = () => {
Object.entries(componentsToRegister).forEach(([key, value]) => {
Digit.ComponentRegistryService.setComponent(key, value);
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { fromPairs } from "lodash";
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Remove unused import.

The fromPairs import from lodash is not used anywhere in this component.

-import { fromPairs } from "lodash";
📝 Committable suggestion

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

Suggested change
import { fromPairs } from "lodash";
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/EGF/index.js
at line 1, the lodash import 'fromPairs' is not used anywhere in the file.
Remove this unused import statement to clean up the code and avoid unnecessary
dependencies.

import React, { Component } from "react";

class EGFFinance extends Component {
constructor(props) {
super(props);
this.onFrameLoad = this.onFrameLoad.bind(this);
this.resetIframe = this.resetIframe.bind(this);
}
onFrameLoad() {
document.getElementById("erp_iframe").style.display = "block";
}

render() {
let auth_token = Digit.UserService.getUser()?.access_token,
locale = localStorage.getItem("locale"),
menuUrl = this.props.location,
loc = window.location,
subdomainurl,
domainurl,
finEnv,
hostname = loc.hostname,
winheight = window.innerHeight - 200,
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Move inline height calculation to proper styling.

Avoid inline calculations in render method and use proper CSS styling.

-    winheight = window.innerHeight - 200,

Consider moving this to a computed property or CSS-in-JS solution for better maintainability.

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

🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/EGF/index.js
at line 23, the inline calculation of winheight using window.innerHeight - 200
should be removed from the render method. Instead, move this height calculation
to a computed property or implement it using CSS or CSS-in-JS styling to
separate concerns and improve maintainability. Update the component to reference
the computed style or class rather than performing the calculation inline.

erp_url,
tenantId = Digit.ULBService.getCurrentTenantId();
//Reading domain name from the request url
domainurl = hostname.substring(hostname.indexOf(".") + 1);
// Reading environment name (ex: dev, qa, uat, fin-uat etc) from the globalconfigs if exists else reading from the .env file
finEnv = this.globalConfigExists() ? window.globalConfigs.getConfig("FIN_ENV") : process.env.REACT_APP_FIN_ENV;
// Preparing finance subdomain url using the above environment name and the domain url
subdomainurl = !!(finEnv) ? "-" + finEnv + "." + domainurl : "." + domainurl;
erp_url = loc.protocol + "//" + tenantId.split(".")[1] + subdomainurl + menuUrl;
Comment on lines +29 to +32
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Validate environment configuration and URL construction.

The URL construction logic could be vulnerable to injection attacks if environment variables are not properly validated.

Add input validation:

+    // Validate finEnv to prevent injection
+    if (finEnv && !/^[a-zA-Z0-9-]+$/.test(finEnv)) {
+      console.error('Invalid FIN_ENV value detected');
+      return null;
+    }
+    
+    // Validate tenantId format
+    if (!tenantId || !tenantId.includes('.')) {
+      console.error('Invalid tenantId format');
+      return null;
+    }
📝 Committable suggestion

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

Suggested change
finEnv = this.globalConfigExists() ? window.globalConfigs.getConfig("FIN_ENV") : process.env.REACT_APP_FIN_ENV;
// Preparing finance subdomain url using the above environment name and the domain url
subdomainurl = !!(finEnv) ? "-" + finEnv + "." + domainurl : "." + domainurl;
erp_url = loc.protocol + "//" + tenantId.split(".")[1] + subdomainurl + menuUrl;
finEnv = this.globalConfigExists()
? window.globalConfigs.getConfig("FIN_ENV")
: process.env.REACT_APP_FIN_ENV;
// Validate finEnv to prevent injection
if (finEnv && !/^[a-zA-Z0-9-]+$/.test(finEnv)) {
console.error('Invalid FIN_ENV value detected');
return null;
}
// Validate tenantId format
if (!tenantId || !tenantId.includes('.')) {
console.error('Invalid tenantId format');
return null;
}
// Preparing finance subdomain url using the above environment name and the domain url
subdomainurl = !!finEnv
? "-" + finEnv + "." + domainurl
: "." + domainurl;
erp_url = loc.protocol
+ "//"
+ tenantId.split(".")[1]
+ subdomainurl
+ menuUrl;
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/EGF/index.js
around lines 29 to 32, the environment variable finEnv and tenantId are used
directly to construct URLs, which can lead to injection vulnerabilities. Add
validation to ensure finEnv and tenantId contain only expected safe characters
(e.g., alphanumeric and allowed symbols) before using them in URL construction.
Sanitize or reject invalid inputs to prevent injection attacks.


return (
<div>
<iframe name="erp_iframe" id="erp_iframe" height={winheight} width="100%"/>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add accessibility title to iframe.

The iframe is missing a title attribute, which is required for screen reader accessibility.

-        <iframe name="erp_iframe" id="erp_iframe" height={winheight} width="100%"/>
+        <iframe 
+          name="erp_iframe" 
+          id="erp_iframe" 
+          height={winheight} 
+          width="100%" 
+          title="Finance ERP System"
+        />
🧰 Tools
🪛 Biome (2.1.2)

[error] 36-36: Provide a title attribute when using iframe elements.

Screen readers rely on the title set on an iframe to describe the content being displayed.

(lint/a11y/useIframeTitle)

🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/EGF/index.js
at line 36, the iframe element lacks a title attribute, which is necessary for
screen reader accessibility. Add a descriptive title attribute to the iframe tag
to improve accessibility, for example, title="ERP iframe" or another
context-appropriate description.

<form action={erp_url} id="erp_form" method="post" target="erp_iframe">
<input readOnly hidden="true" name="auth_token" value={auth_token} />
<input readOnly hidden="true" name="tenantId" value={tenantId} />
<input readOnly hidden="true" name="locale" value={locale} />
<input readOnly hidden="true" name="formPage" value="true" />
</form>
</div>
);
}
componentDidMount() {
window.addEventListener("message", this.onMessage, false);
window.addEventListener("loacaleChangeEvent", this.resetIframe, false);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in event listener.

There's a typo in the event name that will prevent the locale change listener from working.

-    window.addEventListener("loacaleChangeEvent", this.resetIframe, false);
+    window.addEventListener("localeChangeEvent", this.resetIframe, false);
📝 Committable suggestion

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

Suggested change
window.addEventListener("loacaleChangeEvent", this.resetIframe, false);
window.addEventListener("localeChangeEvent", this.resetIframe, false);
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/EGF/index.js
at line 48, correct the typo in the event name from "loacaleChangeEvent" to
"localeChangeEvent" in the window.addEventListener call to ensure the locale
change listener functions properly.

// document.getElementById("erp_iframe").addEventListener("load", this.onFrameLoad);
document.forms["erp_form"].submit();
}
Comment on lines +46 to +51
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add componentWillUnmount to clean up event listeners.

Event listeners should be removed when the component unmounts to prevent memory leaks.

Add this method:

+  componentWillUnmount() {
+    window.removeEventListener("message", this.onMessage, false);
+    window.removeEventListener("localeChangeEvent", this.resetIframe, false);
+  }
📝 Committable suggestion

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

Suggested change
componentDidMount() {
window.addEventListener("message", this.onMessage, false);
window.addEventListener("loacaleChangeEvent", this.resetIframe, false);
// document.getElementById("erp_iframe").addEventListener("load", this.onFrameLoad);
document.forms["erp_form"].submit();
}
componentDidMount() {
window.addEventListener("message", this.onMessage, false);
window.addEventListener("loacaleChangeEvent", this.resetIframe, false);
// document.getElementById("erp_iframe").addEventListener("load", this.onFrameLoad);
document.forms["erp_form"].submit();
}
componentWillUnmount() {
window.removeEventListener("message", this.onMessage, false);
window.removeEventListener("localeChangeEvent", this.resetIframe, false);
}
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/EGF/index.js
around lines 46 to 51, the componentDidMount method adds event listeners but
there is no componentWillUnmount to remove them. To fix this, add a
componentWillUnmount lifecycle method that removes the "message" and
"loacaleChangeEvent" event listeners using window.removeEventListener with the
same handler functions. This will prevent memory leaks by cleaning up event
listeners when the component unmounts.

componentDidUpdate() {
let isSecure = window.location.protocol === "https";
let localeCookie = "locale=" + localStorage.getItem("locale") + ";path=/;domain=." + this.getSubdomain();
if (isSecure) {
localeCookie += ";secure";
}
window.document.cookie = localeCookie;
document.forms["erp_form"].submit();
}
Comment on lines +52 to +60
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize componentDidUpdate to prevent unnecessary form submissions.

The form is resubmitted on every component update, which could cause performance issues and iframe flickering.

-  componentDidUpdate() {
+  componentDidUpdate(prevProps) {
+    const currentLocale = localStorage.getItem("locale");
+    const localeChanged = prevProps.location !== this.props.location;
+    
+    if (!localeChanged) return;
+    
     let isSecure = window.location.protocol === "https";
-    let localeCookie = "locale=" + localStorage.getItem("locale") + ";path=/;domain=." + this.getSubdomain();
+    let localeCookie = "locale=" + currentLocale + ";path=/;domain=." + this.getSubdomain();
     if (isSecure) {
       localeCookie += ";secure";
     }
     window.document.cookie = localeCookie;
     document.forms["erp_form"].submit();
-  }
+  }

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

🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/EGF/index.js
around lines 52 to 60, the componentDidUpdate method submits the form on every
update causing unnecessary form submissions and performance issues. Modify
componentDidUpdate to check if the relevant state or props have actually changed
before submitting the form, or use a condition to ensure the form submission
only happens once or when truly needed to prevent repeated submissions and
iframe flickering.

onMessage = (event) => {
if (event.data != "close") return;
// document.getElementById('erp_iframe').style.display='none';
this.props.history.push("/inbox");
};
Comment on lines +61 to +65
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Improve message handler consistency and safety.

Several improvements needed for the message handler:

-  onMessage = (event) => {
-    if (event.data != "close") return;
+  onMessage(event) {
+    // Validate message origin for security
+    if (!this.validateMessageOrigin(event.origin)) return;
+    
+    if (event.data !== "close") return;
     // document.getElementById('erp_iframe').style.display='none';
     this.props.history.push("/inbox");
-  };
+  }

Add origin validation method:

+  validateMessageOrigin(origin) {
+    const allowedOrigins = process.env.REACT_APP_ALLOWED_ORIGINS?.split(',') || [];
+    return allowedOrigins.includes(origin);
+  }

Also bind this method in the constructor for consistency.

🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/EGF/index.js
around lines 61 to 65, the onMessage handler lacks origin validation and is not
bound in the constructor. To fix this, implement a method to validate the event
origin against allowed origins, bind the onMessage method in the constructor to
ensure correct 'this' context, and update the onMessage handler to first check
the event origin before processing the message.

resetIframe() {
this.forceUpdate();
}
Comment on lines +66 to +68
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using forceUpdate in favor of proper state management.

Using forceUpdate() is an anti-pattern in React and should be replaced with proper state management.

+  constructor(props) {
+    super(props);
+    this.state = { iframeKey: 0 };
+    this.onFrameLoad = this.onFrameLoad.bind(this);
+    this.resetIframe = this.resetIframe.bind(this);
+  }
+  
   resetIframe() {
-    this.forceUpdate();
+    this.setState(prevState => ({ iframeKey: prevState.iframeKey + 1 }));
   }

Then use the key in the iframe:

-        <iframe name="erp_iframe" id="erp_iframe" height={winheight} width="100%"/>
+        <iframe 
+          key={this.state.iframeKey}
+          name="erp_iframe" 
+          id="erp_iframe" 
+          height={winheight} 
+          width="100%"
+        />

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

🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/EGF/index.js
around lines 66 to 68, the resetIframe method uses forceUpdate(), which is an
anti-pattern in React. Replace forceUpdate() by introducing a state variable
(e.g., iframeKey) in the component's state, update this key to a new value in
resetIframe to trigger a re-render, and then pass this key as the key prop to
the iframe element to properly reset it.

getSubdomain() {
let hostname = window.location.hostname;
return hostname.substring(hostname.indexOf(".") + 1);
}
Comment on lines +69 to +72
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Extract duplicate domain parsing logic.

The subdomain extraction logic is duplicated between render() and getSubdomain() methods.

Consider consolidating the domain parsing logic and using getSubdomain() in the render method instead of duplicating the logic.

🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/EGF/index.js
around lines 69 to 72, the logic to extract the subdomain from the hostname is
duplicated in both the render() method and getSubdomain(). Refactor by removing
the duplicate code in render() and calling the getSubdomain() method instead to
centralize the domain parsing logic and avoid redundancy.

globalConfigExists() {
return typeof window.globalConfigs !== "undefined" && typeof window.globalConfigs.getConfig === "function";
}
}

export default EGFFinance;
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { useHistory } from 'react-router-dom';
import {
ArrowForward,
ArrowVectorDown,
} from "@egovernments/digit-ui-react-components";

const RenderMenuDict = ({ t, tree, level = 0 }) => {
const history = useHistory();
const [openKeys, setOpenKeys] = useState({});

const toggle = (key) => {
setOpenKeys(prev => ({ ...prev, [key]: !prev[key] }));
};

const indentPx = level * 16;
const buttonBaseStyle = {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '8px 4px',
cursor: 'pointer',
fontSize: '16px',
color: "#F47738",
};

return Object.entries(tree).sort().map(([key, node], idx) => {
const meta = node._meta || {};
const hasChildren = Object.keys(node).some(childKey => childKey !== '_meta');
const isOpen = openKeys[key];

return (
<div key={level + '-' + idx}>
<span
style={{ ...buttonBaseStyle, paddingLeft: `${indentPx + 8}px` }}
onClick={() => hasChildren ? toggle(key) : meta.link && (
meta.link.includes(`${window.contextPath}/`) ? history.push(meta.link) : window.location.href = meta.link
)}
Comment on lines +37 to +39
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider security implications of external navigation.

Direct assignment to window.location.href can be a security risk if meta.link contains malicious URLs. Consider validating URLs or using a whitelist approach.

+const isValidURL = (url) => {
+  try {
+    const urlObj = new URL(url);
+    return ['http:', 'https:'].includes(urlObj.protocol);
+  } catch {
+    return false;
+  }
+};

          onClick={() => hasChildren ? toggle(key) : meta.link && (
-            meta.link.includes(`${window.contextPath}/`) ? history.push(meta.link) : window.location.href = meta.link
+            meta.link.includes(`${window.contextPath}/`) ? history.push(meta.link) : 
+            isValidURL(meta.link) ? window.location.href = meta.link : console.warn('Invalid URL blocked:', meta.link)
          )}
📝 Committable suggestion

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

Suggested change
onClick={() => hasChildren ? toggle(key) : meta.link && (
meta.link.includes(`${window.contextPath}/`) ? history.push(meta.link) : window.location.href = meta.link
)}
// add this helper (e.g. at the top of the file or inside your component)
const isValidURL = (url) => {
try {
const urlObj = new URL(url);
return ['http:', 'https:'].includes(urlObj.protocol);
} catch {
return false;
}
};
// around line 37–39 in LandingPageSubMenuCard.js
onClick={() =>
hasChildren
? toggle(key)
: meta.link && (
meta.link.includes(`${window.contextPath}/`)
? history.push(meta.link)
: isValidURL(meta.link)
? (window.location.href = meta.link)
: console.warn('Invalid URL blocked:', meta.link)
)
}
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/LandingPageSubMenuCard.js
around lines 37 to 39, the code directly assigns meta.link to
window.location.href without validation, which poses a security risk. To fix
this, implement URL validation or a whitelist check before assigning meta.link
to window.location.href to ensure only safe, trusted URLs are navigated to. This
prevents potential navigation to malicious sites.

>
<p>{t(meta.label || key)}</p>
{hasChildren && (isOpen ? <ArrowVectorDown styles={{width: "18px", height: "18px"}} /> : <div className='primary-label-btn'><ArrowForward /></div>)}
Comment on lines +35 to +42
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix accessibility violations.

The interactive span element lacks keyboard accessibility and proper semantic meaning. Use a button element with proper accessibility attributes.

-        <span
-          style={{ ...buttonBaseStyle, paddingLeft: `${indentPx + 8}px` }}
-          onClick={() => hasChildren ? toggle(key) : meta.link && (
-            meta.link.includes(`${window.contextPath}/`) ? history.push(meta.link) : window.location.href = meta.link
-          )}
-        >
+        <button
+          style={{ ...buttonBaseStyle, paddingLeft: `${indentPx + 8}px`, border: 'none', background: 'none' }}
+          onClick={() => hasChildren ? toggle(key) : meta.link && (
+            meta.link.includes(`${window.contextPath}/`) ? history.push(meta.link) : window.location.href = meta.link
+          )}
+          onKeyDown={(e) => {
+            if (e.key === 'Enter' || e.key === ' ') {
+              e.preventDefault();
+              hasChildren ? toggle(key) : meta.link && (
+                meta.link.includes(`${window.contextPath}/`) ? history.push(meta.link) : window.location.href = meta.link
+              );
+            }
+          }}
+          aria-expanded={hasChildren ? isOpen : undefined}
+          role={hasChildren ? "button" : "link"}
+        >
           <p>{t(meta.label || key)}</p>
           {hasChildren && (isOpen ? <ArrowVectorDown styles={{width: "18px", height: "18px"}}  /> : <div className='primary-label-btn'><ArrowForward /></div>)}
-        </span>
+        </button>
📝 Committable suggestion

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

Suggested change
<span
style={{ ...buttonBaseStyle, paddingLeft: `${indentPx + 8}px` }}
onClick={() => hasChildren ? toggle(key) : meta.link && (
meta.link.includes(`${window.contextPath}/`) ? history.push(meta.link) : window.location.href = meta.link
)}
>
<p>{t(meta.label || key)}</p>
{hasChildren && (isOpen ? <ArrowVectorDown styles={{width: "18px", height: "18px"}} /> : <div className='primary-label-btn'><ArrowForward /></div>)}
<button
style={{ ...buttonBaseStyle, paddingLeft: `${indentPx + 8}px`, border: 'none', background: 'none' }}
onClick={() =>
hasChildren
? toggle(key)
: meta.link &&
(
meta.link.includes(`${window.contextPath}/`)
? history.push(meta.link)
: (window.location.href = meta.link)
)
}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
hasChildren
? toggle(key)
: meta.link &&
(
meta.link.includes(`${window.contextPath}/`)
? history.push(meta.link)
: (window.location.href = meta.link)
);
}
}}
aria-expanded={hasChildren ? isOpen : undefined}
role={hasChildren ? "button" : "link"}
>
<p>{t(meta.label || key)}</p>
{hasChildren && (
isOpen
? <ArrowVectorDown styles={{ width: "18px", height: "18px" }} />
: <div className='primary-label-btn'><ArrowForward /></div>
)}
</button>
🧰 Tools
🪛 Biome (2.1.2)

[error] 35-40: 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] 35-40: 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)

🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/LandingPageSubMenuCard.js
around lines 35 to 42, replace the interactive span element with a button
element to ensure proper semantic meaning and keyboard accessibility. Transfer
the existing styles and onClick handler to the button, and add necessary
accessibility attributes like type="button" to prevent form submission. This
change will fix accessibility violations by making the element focusable and
operable via keyboard.

</span>
{hasChildren && isOpen && (
<div>
<RenderMenuDict t={t} tree={Object.fromEntries(
Object.entries(node).filter(([k]) => k !== '_meta')
)} level={level + 1} />
</div>
)}
</div>
);
});
};

const LandingPageSubMenuCard = ({ t, Icon, moduleName, menuDict = {}}) => {
return (
<div className='employeeCard customEmployeeCard card-home home-action-cards'>
<div className="complaint-links-container">
<div className="header" >
<span className="text removeHeight">{t(moduleName)}</span>
<span className="logo removeBorderRadiusLogo">{Icon}</span>
</div>
<div className="body" style={{ margin: "0px", padding: "0px" }}>
<div className="links-wrapper" style={{ width: "90%" }}>
<RenderMenuDict t={t} tree={menuDict} />
</div>
</div>
</div>
</div>
);
};

LandingPageSubMenuCard.propTypes = {
icon: PropTypes.node,
moduleName: PropTypes.string.isRequired,
menuDict: PropTypes.object
};
Comment on lines +74 to +78
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix PropTypes to match component interface.

The PropTypes don't match the actual props. The component receives Icon and t but PropTypes defines icon and missing t.

 LandingPageSubMenuCard.propTypes = {
-  icon: PropTypes.node,
+  t: PropTypes.func.isRequired,
+  Icon: PropTypes.node,
   moduleName: PropTypes.string.isRequired,
   menuDict: PropTypes.object
 };
📝 Committable suggestion

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

Suggested change
LandingPageSubMenuCard.propTypes = {
icon: PropTypes.node,
moduleName: PropTypes.string.isRequired,
menuDict: PropTypes.object
};
LandingPageSubMenuCard.propTypes = {
t: PropTypes.func.isRequired,
Icon: PropTypes.node,
moduleName: PropTypes.string.isRequired,
menuDict: PropTypes.object
};
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/components/LandingPageSubMenuCard.js
around lines 74 to 78, update the PropTypes to match the actual component props
by replacing `icon` with `Icon` and adding a PropType definition for the `t`
prop. Ensure `Icon` is typed appropriately (likely as a node or element type)
and `t` is defined as a function since it is typically a translation function.


export default LandingPageSubMenuCard;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React, { useState, useEffect } from "react";
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Remove unused imports.

The useState and useEffect hooks are imported but never used in this component.

-import React, { useState, useEffect } from "react";
+import React from "react";
📝 Committable suggestion

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

Suggested change
import React, { useState, useEffect } from "react";
import React from "react";
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/pages/employee/EGF.js
at line 1, the imports useState and useEffect are included but not used anywhere
in the file. Remove useState and useEffect from the import statement to clean up
unused imports and avoid unnecessary code.

import EGFFinance from "../../components/EGF";

const EGF = () => {
let location = window.location.pathname;
location = location.substring(location.indexOf("/services"));
Comment on lines +5 to +6
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Consider adding error handling for path manipulation.

The current path manipulation assumes "/services" will always be present in the pathname. Consider adding validation to handle edge cases where this substring might not exist.

 let location = window.location.pathname;
-location = location.substring(location.indexOf("/services"));
+const servicesIndex = location.indexOf("/services");
+location = servicesIndex !== -1 ? location.substring(servicesIndex) : location;
📝 Committable suggestion

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

Suggested change
let location = window.location.pathname;
location = location.substring(location.indexOf("/services"));
let location = window.location.pathname;
const servicesIndex = location.indexOf("/services");
location = servicesIndex !== -1
? location.substring(servicesIndex)
: location;
🤖 Prompt for AI Agents
In
frontend/micro-ui/web/micro-ui-internals/packages/modules/finance/src/pages/employee/EGF.js
around lines 5 to 6, the code assumes "/services" is always present in the
pathname, which can cause errors if it's missing. Add a check to verify if
"/services" exists in window.location.pathname before calling substring. If not
found, handle the case gracefully, such as setting location to a default value
or skipping the substring operation.

return (
<div>
<EGFFinance location={location} />
</div>
);
};

export default EGF;
Loading