+
selectedOption[optionsKey] === option[optionsKey]) ? true : false}
+ onChange={(e) => isPropsNeeded?onSelectToAddToQueue(e, option,props):isOBPSMultiple?onSelectToAddToQueue(e, option,BlockNumber):onSelectToAddToQueue(e, option)}
+ style={{minWidth: "24px", width: "100%"}}
+ disabled={option.isDisabled || false}
+ />
+
+
+
+
{t(option[optionsKey]&&typeof option[optionsKey]=="string" && option[optionsKey])}
+
+ );
+
+ const Menu = () => {
+ const filteredOptions =
+ searchQuery?.length > 0 ? options.filter((option) => t(option[optionsKey]&&typeof option[optionsKey]=="string" && option[optionsKey].toUpperCase()).toLowerCase().indexOf(searchQuery.toLowerCase()) >= 0) : options;
+ return filteredOptions?.map((option, index) =>
+
+
+
setActive(true)} value={searchQuery} onChange={onSearch} placeholder={t(placeholder)} />
+
+
{alreadyQueuedSelectedState.length > 0 ? `${isSurvey? alreadyQueuedSelectedState?.filter((ob) => ob?.i18nKey !== undefined).length : alreadyQueuedSelectedState.length} ${defaultUnit}` : defaultLabel}
+
+
+
+ {active ? (
+
+
+
+ ) : null}
+
+
+ {config?.isDropdownWithChip ?
+ {alreadyQueuedSelectedState.length > 0 &&
+ alreadyQueuedSelectedState.map((value, index) => {
+ return onSelectToAddToQueue(e, value,props)
+ : (e) => onSelectToAddToQueue(e, value)
+ } />;
+ })}
+
: null}
+
+ );
+};
+
+export default MultiSelectDropdown;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/hrms/src/components/SearchUserForm.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/hrms/src/components/SearchUserForm.js
new file mode 100644
index 000000000..9e1a3a999
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/hrms/src/components/SearchUserForm.js
@@ -0,0 +1,443 @@
+import { Loader, Header, Dropdown, LabelFieldPair, CardLabel, LinkLabel, SubmitBar, Toast } from "@egovernments/digit-ui-react-components";
+import React, { useState, useMemo, useEffect } from "react";
+import { useTranslation } from "react-i18next";
+import { Controller, useForm, useWatch } from "react-hook-form";
+import MultiSelectDropdown from "./MultiSelectDropdown";
+function filterKeys(data, keys) {
+ return data.map((item) => {
+ const filteredItem = {};
+ keys.forEach((key) => {
+ if (item.hasOwnProperty(key)) {
+ filteredItem[key] = item[key];
+ }
+ });
+ return filteredItem;
+ });
+}
+
+function getUniqueLeafCodes(tree) {
+ const codes = new Set();
+
+ function traverse(node) {
+ if (!node || typeof node !== "object") return;
+
+ const keys = Object.keys(node).filter((key) => key !== "options" && key !== "codes");
+
+ // Check if it's a leaf node (all remaining keys' values are strings)
+ const isLeafNode = keys.every((key) => typeof node[key] === "string");
+
+ if (isLeafNode && node.code) {
+ codes.add(node.code);
+ } else {
+ // Traverse every other key except options and codes
+ keys.forEach((key) => {
+ if (typeof node[key] === "object") {
+ traverse(node[key]);
+ }
+ });
+ }
+ }
+
+ traverse(tree);
+
+ return Array.from(codes);
+}
+
+function buildTree(data, hierarchy) {
+ const tree = { options: [] };
+
+ data.forEach((item) => {
+ // Ignore items without zoneCode
+ if (!item.zoneCode) return;
+
+ let currentLevel = tree;
+
+ hierarchy.forEach(({ level }, index) => {
+ const value = item[level];
+
+ if (!currentLevel[value]) {
+ // Clone the item and delete the options property from it
+ const clonedItem = { ...item };
+ delete clonedItem.options;
+
+ // Initialize the current level with the cloned item
+ currentLevel[value] = { ...clonedItem, options: [] };
+
+ // Push the cloned item to the options array without the options property
+ currentLevel.options.push({ ...clonedItem });
+ }
+
+ if (index === hierarchy.length - 1) {
+ currentLevel[value].codes = currentLevel[value].codes || [];
+ currentLevel[value].codes.push(item.code);
+ }
+
+ currentLevel = currentLevel[value];
+ });
+ });
+
+ return tree;
+}
+
+const SearchUserForm = ({ uniqueTenants, setUniqueTenants, roles, setUniqueRoles }) => {
+ const { t } = useTranslation();
+ const [showToast, setShowToast] = useState(null);
+ const [hierarchy, setHierarchy] = useState([
+ { level: "zoneCode", value: 1, optionsKey: "zoneName", isMandatory: true },
+ { level: "circleCode", value: 2, optionsKey: "circleName", isMandatory: true },
+ { level: "divisionCode", value: 3, optionsKey: "divisionName", isMandatory: true },
+ { level: "subDivisionCode", value: 4, optionsKey: "subDivisionName", isMandatory: false },
+ { level: "sectionCode", value: 5, optionsKey: "sectionName", isMandatory: false },
+ // { "level": "schemeCode", "value": 6,"optionsKey":"schemeName" },
+ { level: "code", value: 7, optionsKey: "name", isMandatory: false },
+ ]);
+ const [tree, setTree] = useState(null);
+ const [rolesOptions,setRolesOptions] = useState(null)
+ // const [zones,setZones] = useState([])
+ // const [circles,setCircles] = useState([])
+ // const [divisions,setDivisions] = useState([])
+ // const [subDivisions,setSubDivisions] = useState([])
+ // const [sections,setSections] = useState([])
+ // const [schemes,setSchemes] = useState([])
+ // const [codes,setCodes] = useState([])
+
+ const requestCriteria = {
+ url: "/mdms-v2/v1/_search",
+ params: { tenantId: Digit.ULBService.getStateId() },
+ body: {
+ MdmsCriteria: {
+ tenantId: Digit.ULBService.getStateId(),
+ moduleDetails: [
+ {
+ moduleName: "tenant",
+ masterDetails: [
+ {
+ name: "tenants",
+ },
+ ],
+ },
+ {
+ moduleName: "ws-services-masters",
+ masterDetails: [
+ {
+ name: "WSServiceRoles",
+ },
+ ],
+ }
+ ],
+ },
+ },
+ config: {
+ cacheTime: Infinity,
+ select: (data) => {
+ const requiredKeys = [
+ "code",
+ "name",
+ "zoneCode",
+ "zoneName",
+ "circleCode",
+ "circleName",
+ "divisionCode",
+ "divisionName",
+ "subDivisionCode",
+ "subDivisionName",
+ "sectionCode",
+ "sectionName",
+ "schemeCode",
+ "schemeName",
+ ];
+ const result = data?.MdmsRes?.tenant?.tenants;
+ const filteredResult = filterKeys(result, requiredKeys);
+ const resultInTree = buildTree(filteredResult, hierarchy);
+ const excludeCodes = ["HRMS_ADMIN", "LOC_ADMIN", "MDMS_ADMIN", "EMPLOYEE", "SYSTEM"];
+ setRolesOptions(data?.MdmsRes?.["ws-services-masters"]?.["WSServiceRoles"]?.filter(row => !excludeCodes.includes(row?.code)))
+ //updating to state roles as requested
+ // setRolesOptions([
+ // // {
+ // // code: "",
+ // // name: "Select All",
+ // // description: "",
+ // // },
+ // {
+ // code: "EMPLOYEE",
+ // name: "EMPLOYEE",
+ // labelKey: "ACCESSCONTROL_ROLES_ROLES_EMPLOYEE",
+ // },
+ // {
+ // code: "DIV_ADMIN",
+ // name: "DIVISION ADMIN",
+ // labelKey: "ACCESSCONTROL_ROLES_ROLES_DIV_ADMIN",
+ // },
+ // {
+ // code: "HRMS_ADMIN",
+ // name: "HRMS_ADMIN",
+ // labelKey: "ACCESSCONTROL_ROLES_ROLES_HRMS_ADMIN",
+ // },
+ // {
+ // code: "MDMS_ADMIN",
+ // name: "MDMS Admin",
+ // description: "Mdms admin",
+ // },
+
+ // ])
+ setTree(resultInTree);
+ return result;
+ },
+ },
+ };
+
+ const { isLoading, data, revalidate, isFetching, error } = Digit.Hooks.useCustomAPIHook(requestCriteria);
+
+ const {
+ register,
+ handleSubmit,
+ setValue,
+ getValues,
+ reset,
+ watch,
+ trigger,
+ control,
+ formState,
+ errors,
+ setError,
+ clearErrors,
+ unregister,
+ } = useForm({
+ defaultValues: {
+ "zoneCode": "",
+ "circleCode": "",
+ "divisionCode": "",
+ "subDivisionCode": "",
+ "sectionCode": "",
+ "code": "",
+ "roles": []
+ },
+ });
+
+ const formData = watch();
+
+ const clearSearch = () => {
+ reset({
+ "zoneCode": "",
+ "circleCode": "",
+ "divisionCode": "",
+ "subDivisionCode": "",
+ "sectionCode": "",
+ "code": "",
+ "roles": []
+ });
+ setUniqueRoles(null);
+ setUniqueTenants(null);
+
+ // dispatch({
+ // type: uiConfig?.type === "filter"?"clearFilterForm" :"clearSearchForm",
+ // state: { ...uiConfig?.defaultValues }
+ // //need to pass form with empty strings
+ // })
+ //here reset tableForm as well
+ // dispatch({
+ // type: "tableForm",
+ // state: { limit:10,offset:0 }
+ // //need to pass form with empty strings
+ // })
+ };
+
+ const onSubmit = (data) => {
+ //assuming atleast one hierarchy is entered
+
+ if (Object.keys(data).length === 0 || Object.values(data).every((value) => !value)) {
+ //toast message
+ setShowToast({ warning: true, label: t("ES_COMMON_MIN_SEARCH_CRITERIA_MSG") });
+ setTimeout(closeToast, 5000);
+ return;
+ }
+ //other validations if any
+ //check mandatory fields
+ let areMandatoryFieldsNotFilled = false;
+ hierarchy.forEach(({ level, isMandatory }) => {
+ if (isMandatory && (!data[level] || data[level]?.length === 0)) {
+ areMandatoryFieldsNotFilled = true;
+ return; // Exit the loop early
+ }
+ });
+
+ if (areMandatoryFieldsNotFilled) {
+ setShowToast({ warning: true, label: t("ES_COMMON_MIN_SEARCH_CRITERIA_MSG") });
+ setTimeout(closeToast, 5000);
+ return;
+ }
+
+ //checking roles
+ if(data?.roles?.length === 0 || !data?.roles){
+ setShowToast({ warning: true, label: t("ES_COMMON_MIN_SEARCH_CRITERIA_MSG") });
+ setTimeout(closeToast, 5000);
+ return;
+ }
+
+ //here apply a logic to compute the subtree based on the hierarchy selected
+ const levels = hierarchy.map(({ level }) => level);
+ //compute current level
+ let maxSelectedLevel = levels[0];
+ levels.forEach((level) => {
+ if (formData[level]) {
+ maxSelectedLevel = level;
+ } else {
+ return;
+ }
+ });
+
+ const levelIndex = levels.indexOf(maxSelectedLevel);
+
+ let currentLevel = tree;
+ for (let i = 0; i <= levelIndex; i++) {
+ const code = data?.[levels[i]]?.[levels[i]];
+ if (!code || !currentLevel[code]) return [];
+ currentLevel = currentLevel[code];
+ }
+
+ //this is the list of tenants under the current subtree
+ const listOfUniqueTenants = getUniqueLeafCodes(currentLevel);
+ setUniqueTenants(() => listOfUniqueTenants);
+ setUniqueRoles(() => data?.roles?.filter(row=>row.code)?.map(role=> role.code));
+ };
+
+ const optionsForHierarchy = (level, value) => {
+ if (!tree) return [];
+
+ const levels = hierarchy.map(({ level }) => level);
+ const levelIndex = levels.indexOf(level);
+
+ //zoneCode(1st level(highest parent))
+ if (levelIndex === -1 || levelIndex === 0) return tree.options;
+
+ let currentLevel = tree;
+ for (let i = 0; i < levelIndex; i++) {
+ const code = formData[levels[i]]?.[levels[i]];
+ if (!code || !currentLevel[code]) return [];
+ currentLevel = currentLevel[code];
+ }
+ return currentLevel.options || [];
+ };
+
+ const closeToast = () => {
+ setShowToast(null);
+ };
+
+ const renderHierarchyFields = useMemo(() => {
+ return hierarchy.map(({ level, optionsKey, isMandatory, ...rest }, idx) => (
+