+
+ {
+ configs?.sections?.links?.show &&
+
+
+
+
+
+ }
+ {
+ configs?.type === 'search' && configs?.sections?.search?.show &&
+
+
+
+
+
+ }
+ {
+ configs?.type === 'search' && configs?.sections?.filter?.show &&
+
+
+
+
+
+
+ }
+ {
+ configs?.type === 'inbox' && configs?.sections?.search?.show &&
+
+
+
+
+
+ }
+ {
+ configs?.type === 'inbox' && configs?.sections?.filter?.show &&
+
+
+
+
+
+ }
+ { (configs?.type === 'inbox' || configs?.type === 'search') &&
+
+
+ {
+ configs?.sections?.search?.show && (
+ {
+ setType("SEARCH");
+ setPopup(true);
+ }}
+ />
+ )}
+ {configs?.sections?.filter?.show && (
+ {
+ setType("FILTER");
+ setPopup(true);
+ }}
+ />
+ )}
+
+
+ }
+ {
+ (configs?.type === 'inbox' || configs?.type === 'search') && (configs?.showAsRemovableTagsInMobile) &&
+
+
+
+ }
+ {
+ configs?.sections?.searchResult?.show &&
+
0 ? (!(isLoading || isFetching) ?{ overflowX: "auto" }: {}) : { }} >
+
+ {/* configs?.sections?.searchResult?.show &&
+ 0 ? (!(isLoading || isFetching) ?{ overflowX: "scroll", borderRadius : "4px" }: {}) : { }} > */}
+
+
+
+
+ {/* */}
+
+
+
+ }
+ {popup && (
+
+ {type === "FILTER" && (
+
+
+
+ )}
+ {/* {type === "SORT" && (
+
+ { }
+
+ )} */}
+ {type === "SEARCH" && (
+
+
+
+ )}
+
+ )}
+
+
+ {/* One can use this Parent to add additional sub parents to render more sections */}
+
+
+
+ )
+}
+
+export default InboxSearchComposerV2;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/MobileView/MobileSearchComponent.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/MobileView/MobileSearchComponent.js
new file mode 100644
index 0000000..12c1453
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/MobileView/MobileSearchComponent.js
@@ -0,0 +1,236 @@
+import React, { useContext, useEffect, useState } from "react";
+import { useForm } from "react-hook-form";
+import { useTranslation } from "react-i18next";
+import { InboxContext } from "../InboxSearchComposerContext";
+import { FilterIcon, SearchIcon, CloseSvg, RefreshIcon, DownloadImgIcon,SortSvg } from "../../atoms/svgindex";
+import ActionBar from "../../atoms/ActionBar";
+import SubmitBar from "../../atoms/SubmitBar";
+import LinkLabel from "../../atoms/LinkLabel";
+import RenderFormFields from "../../molecules/RenderFormFields";
+import Toast from "../../atoms/Toast";
+import _ from "lodash";
+import Button from "../../atoms/Button"
+
+
+const MobileSearchComponent = ({ uiConfig, modalType, header = "", screenType = "search", fullConfig, data, onClose, defaultValues,browserSession }) => {
+ const { t } = useTranslation();
+ const { state, dispatch } = useContext(InboxContext)
+ const [showToast,setShowToast] = useState(null)
+ let updatedFields = [];
+ const {apiDetails} = fullConfig
+
+ if (fullConfig?.postProcessResult){
+ //conditions can be added while calling postprocess function to pass different params
+ Digit?.Customizations?.[apiDetails?.masterName]?.[apiDetails?.moduleName]?.postProcess(data, uiConfig)
+ }
+
+ //define session for modal form
+ //uiConfig.type === filter || sort
+ //we need to sync browsersession and mobileSearchSession
+ // const mobileSearchSession = Digit.Hooks.useSessionStorage(`MOBILE_SEARCH_MODAL_FORM_${uiConfig?.type}_${fullConfig?.label}`,
+ // {...uiConfig?.defaultValues}
+ // );
+
+ // const [sessionFormData, setSessionFormData, clearSessionFormData] = mobileSearchSession;
+ const [session,setSession,clearSession] = browserSession || []
+
+
+ const defValuesFromSession = uiConfig?.typeMobile === "filter" ? session?.searchForm : session?.filterForm
+
+ const {
+ register,
+ handleSubmit,
+ setValue,
+ getValues,
+ reset,
+ watch,
+ control,
+ formState,
+ errors,
+ setError,
+ clearErrors,
+ } = useForm({
+ // defaultValues: {...uiConfig?.defaultValues,...sessionFormData},
+ defaultValues: {...uiConfig?.defaultValues,...defValuesFromSession}
+ // defaultValues:{...uiConfig?.defaultValues}
+ });
+ const formData = watch();
+
+ const checkKeyDown = (e) => {
+ const keyCode = e.keyCode ? e.keyCode : e.key ? e.key : e.which;
+ if (keyCode === 13) {
+ e.preventDefault();
+ }
+ };
+
+ useEffect(() => {
+ updatedFields = Object.values(formState?.dirtyFields)
+ }, [formState]);
+
+
+ // //on form value change, update session data with form data
+ // useEffect(()=>{
+ // if (!_.isEqual(sessionFormData, formData)) {
+ // // const difference = _.pickBy(sessionFormData, (v, k) => !_.isEqual(formData[k], v));
+ // setSessionFormData({ ...sessionFormData,...formData, });
+ // }
+ // },[formData]);
+
+ // useEffect(()=>{
+ // clearSessionFormData();
+ // },[]);
+
+ const onSubmit = (data) => {
+ onClose?.()
+ if(updatedFields.length >= uiConfig?.minReqFields) {
+ // here based on screenType call respective dispatch fn
+ dispatch({
+ type: modalType === "SEARCH" ? "searchForm" : "filterForm",
+ state: {
+ ...data
+ }
+ })
+ } else {
+ setShowToast({ warning: true, label: t("ES_COMMON_MIN_SEARCH_CRITERIA_MSG") })
+ setTimeout(closeToast, 3000);
+ }
+ }
+
+ const clearSearch = () => {
+ // clearSessionFormData();
+ reset(uiConfig?.defaultValues)
+ dispatch({
+ type: uiConfig?.type === "filter"?"clearFilterForm" :"clearSearchForm",
+ state: { ...uiConfig?.defaultValues }
+ //need to pass form with empty strings
+ })
+ }
+
+ const closeToast = () => {
+ setShowToast(null);
+ }
+
+const renderHeader = () => {
+ switch(uiConfig?.typeMobile) {
+ case "filter" : {
+ return (
+
+
+
+ {t(`${uiConfig?.headerLabel || "ES_COMMON_SEARCH_BY"}`)}
+
+
+ {/* */}
+
+
+
+
+ )
+ }
+ case "sort" : {
+ return (
+
+
+
+ {t(`${uiConfig?.headerLabel || "ES_COMMON_SEARCH_BY"}`)}
+
+
+
+
+
+
+ )
+ }
+ case "search" : {
+ return (
+
+
+
+ {t(`${uiConfig?.headerLabel || "ES_COMMON_SEARCH_BY"}`)}
+
+
+
+
+
+ )
+ }
+ default : {
+ return (
+
+
+
+ {t(`${uiConfig?.headerLabel || "ES_COMMON_SEARCH_BY"}`)}
+
+
+
+
+
+ )
+ }
+ }
+}
+
+ return (
+
+
+
{renderHeader()}
+
+ {showToast && (
+
+ )}
+
+
+ );
+};
+
+export default MobileSearchComponent;
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/MobileView/MobileSearchResults.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/MobileView/MobileSearchResults.js
new file mode 100644
index 0000000..db7a122
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/MobileView/MobileSearchResults.js
@@ -0,0 +1,193 @@
+import React, { useMemo, useContext, useEffect } from 'react'
+import { useForm } from "react-hook-form";
+import { useTranslation } from 'react-i18next';
+import { Details } from "../../molecules/DetailsCard";
+import { Link } from "react-router-dom";
+import NoResultsFound from "../../atoms/NoResultsFound";
+import { Loader } from "../../atoms/Loader";
+import _ from "lodash";
+import { InboxContext } from '../InboxSearchComposerContext';
+import Table from "../../atoms/Table";
+
+const MobileSearchResults = ({ config, data, isLoading, isFetching,fullConfig }) => {
+ const {apiDetails} = fullConfig
+ const { t } = useTranslation();
+ const resultsKey = config.resultsJsonPath
+ //let searchResult = data?.[resultsKey]?.length>0 ? data?.[resultsKey] : []
+ //searchResult = searchResult.reverse()
+ //const tenantId = Digit.ULBService.getCurrentTenantId();
+
+ let searchResult = _.get(data,resultsKey,[])
+ searchResult = searchResult?.length>0 ? searchResult : []
+ searchResult = searchResult.reverse();
+ const tenantId = Digit.ULBService.getCurrentTenantId();
+ const headerLocale = Digit.Utils.locale.getTransformedLocale(tenantId);
+ //reversing reason -> for some reason if we enable sorting on columns results from the api are reversed and shown, for now -> reversing the results(max size 50 so not a performance issue)
+
+ // if (fullConfig?.postProcessResult){
+ // var { isPostProcessFetching,
+ // isPostProcessLoading,
+ // combinedResponse } = Digit?.Customizations?.[apiDetails?.masterName]?.[apiDetails?.moduleName]?.postProcess(searchResult)
+
+ // if(combinedResponse?.length > 0){
+ // searchResult = combinedResponse
+ // }
+ // }
+
+ const {state,dispatch} = useContext(InboxContext)
+
+ const {
+ register,
+ handleSubmit,
+ setValue,
+ getValues,
+ reset,
+ watch,
+ trigger,
+ control,
+ formState,
+ errors,
+ setError,
+ clearErrors,
+ unregister,
+} = useForm({
+ defaultValues: {
+ offset: 0,
+ limit: 10,
+ },
+});
+
+ useEffect(() => {
+ register("offset", 0);
+ register("limit", 10);
+ }, [register]);
+
+ function onPageSizeChange(e) {
+ setValue("limit", Number(e.target.value));
+ handleSubmit(onSubmit)();
+ }
+
+ function nextPage() {
+ setValue("offset", getValues("offset") + getValues("limit"));
+ handleSubmit(onSubmit)();
+ }
+
+ function previousPage() {
+ const offsetValue = getValues("offset") - getValues("limit")
+ setValue("offset", offsetValue>0 ? offsetValue : 0);
+ handleSubmit(onSubmit)();
+ }
+
+ const onSubmit = (data) => {
+ //here update the reducer state
+ //call a dispatch to update table's part of the state and update offset, limit
+ // this will in turn make the api call and give search results and table will be rendered acc to the new data
+ dispatch({
+ type:"tableForm",
+ state:{...data}
+ })
+
+ }
+
+ const columns = [
+ {
+ Header: "",
+ accessor: "_searchResults",
+ id : "_searchResults"
+ }
+ ]
+
+ const propsMobileInboxCards = useMemo(() => {
+ if (isLoading) {
+ return [];
+ }
+ let cardData = searchResult.map((details) => {
+ let mapping = {};
+ let additionalCustomization = {};
+ let cols = config?.columns;
+ for(let columnIndex = 0; columnIndex
{
+ return {
+ _searchResults :
+
+ {Object.keys(row.mapping).map(key => {
+ let toRender;
+ if(row.additionalCustomization[key]){
+ toRender = (
+ {}}
+ row={row.mapping} />)
+ }
+ else {
+ toRender = row.mapping[key]? (
+ {}}
+ row={row.mapping} />
+ ) : (
+ {}}
+ row={row.mapping} /> )
+ }
+ return toRender
+ })}
+
+ }
+ })
+
+ function RenderResult() {
+ if (searchResult?.length === 0) {
+ return ( );
+ }
+
+ if (isLoading || isFetching ) return
+ if(!data) return <>>
+ return
+
{
+ return {
+ style: {width : "200vw"},
+ };
+ }}
+ disableSort={config?.enableColumnSort ? false : true}
+ autoSort={config?.enableColumnSort ? true : false}
+ // globalSearch={config?.enableGlobalSearch ? filterValue : undefined}
+ // onSearch={config?.enableGlobalSearch ? searchQuery : undefined}
+ />
+
+ }
+
+ if (isLoading)
+ { return }
+ return (
+
+
+
+ );
+};
+
+export default MobileSearchResults;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/MobileView/MobileSearchResultsv1.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/MobileView/MobileSearchResultsv1.js
new file mode 100644
index 0000000..01719c2
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/MobileView/MobileSearchResultsv1.js
@@ -0,0 +1,106 @@
+import React, { useMemo, useContext, useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
+import DetailsCard from '../../molecules/DetailsCard';
+import { Link } from 'react-router-dom';
+import NoResultsFound from '../../atoms/NoResultsFound';
+import { Loader } from '../../atoms/Loader';
+import _ from 'lodash';
+import { useHistory } from 'react-router-dom';
+
+// const sampleSearchResult = [
+// {
+// businessObject:{
+// testId:"AW28929",
+// treatmentProcess:"KA - 25235",
+// stage:"Jagadamba Cleaners",
+// outputType:"KA - 25235",
+// pendingDate:"12/02/2013",
+// status:"Pending results",
+// sla:12
+// }
+// }
+// ]
+
+const convertRowToDetailCardData = (row,config,t,apiDetails,searchResult) => {
+ const resultantObj = {
+ apiResponse:{...row,hidden:true}
+ }
+
+ config.columns.map((column,idx) => {
+ resultantObj[t(column.label)] = column.additionalCustomization ? Digit?.Customizations?.[apiDetails?.masterName]?.[apiDetails?.moduleName]?.additionalCustomizations(row,column?.label,column, _.get(row,column.jsonPath,""),t, searchResult) : String(_.get(row,column.jsonPath,"") ? column.translate? t(Digit.Utils.locale.getTransformedLocale(column.prefix?`${column.prefix}${_.get(row,column.jsonPath,"")}`:_.get(row,column.jsonPath,""))) : _.get(row,column.jsonPath,"") : t("ES_COMMON_NA"));
+ })
+
+ return resultantObj
+}
+
+const convertDataForDetailsCard = (config,searchResult,t,apiDetails) => {
+//map over columns and generate data accordingly
+
+ const result = searchResult?.map((row,idx) => {
+ return convertRowToDetailCardData(row,config,t,apiDetails,searchResult)
+ } )
+
+ return result
+}
+
+const MobileSearchResultsv1 = ({
+ config,
+ data,
+ isLoading,
+ isFetching,
+ fullConfig,
+}) => {
+ const { t } = useTranslation();
+ const history = useHistory()
+ const { apiDetails } = fullConfig;
+ const resultsKey = config.resultsJsonPath;
+
+ let searchResult = _.get(data, resultsKey, []);
+
+ //for sample result
+ // let searchResult = _.get(sampleSearchResult, resultsKey, []);
+
+ searchResult = searchResult?.length > 0 ? searchResult : [];
+ // searchResult = searchResult?.reverse();
+
+ const tenantId = Digit.ULBService.getCurrentTenantId();
+
+ const RenderResult = () => {
+ const dataForDetailsCard = convertDataForDetailsCard(config,searchResult,t,apiDetails)
+ const propsForDetailsCard = {
+ t,
+ data:dataForDetailsCard,
+ showActionBar:config?.showActionBarMobileCard, // to show action button on detail card
+ submitButtonLabel:config?.actionButtonLabelMobileCard,
+ handleDetailCardClick:(obj)=>{ //fn when action button on card is clicked
+ const linkToPushTo = Digit?.Customizations?.[apiDetails?.masterName]?.[apiDetails?.moduleName]?.onCardActionClick(obj)
+ history.push(linkToPushTo)
+ },
+ handleSelect:(obj)=>{
+ const linkToPushTo = Digit?.Customizations?.[apiDetails?.masterName]?.[apiDetails?.moduleName]?.onCardClick(obj)
+ history.push(linkToPushTo)
+ // Digit?.Customizations?.[apiDetails?.masterName]?.[apiDetails?.moduleName]?.onCardActionClick(obj)
+ }, //fn when card container is clicked
+ mode:"tqm",
+ apiDetails,
+ }
+
+ return
+ }
+
+ if (isLoading || isFetching) {
+ return ;
+ }
+
+ if (searchResult?.length === 0) {
+ return ( );
+ }
+
+ return (
+
+
+
+ );
+};
+
+export default MobileSearchResultsv1;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Modal.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Modal.js
new file mode 100644
index 0000000..dda09aa
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Modal.js
@@ -0,0 +1,63 @@
+import React, { useEffect } from "react";
+
+import PopUp from "../atoms/PopUp";
+import HeaderBar from "../atoms/HeaderBar";
+import ButtonSelector from "../atoms/ButtonSelector";
+import Toast from "../atoms/Toast";
+
+const Modal = ({
+ headerBarMain,
+ headerBarEnd,
+ popupStyles,
+ children,
+ actionCancelLabel,
+ actionCancelOnSubmit,
+ actionSaveLabel,
+ actionSaveOnSubmit,
+ error,
+ setError,
+ formId,
+ isDisabled,
+ hideSubmit,
+ style = {},
+ popupModuleMianStyles,
+ headerBarMainStyle,
+ isOBPSFlow = false,
+ popupModuleActionBarStyles = {},
+ popmoduleClassName = "",
+ popUpContainerClassName="",
+ popupModuleActionBarClass = "",
+ popupMainModuleClass = "",
+ customTheme = "",
+ actionSingleLabel,
+ actionSingleSubmit,
+
+}) => {
+ /**
+ * TODO: It needs to be done from the desgin changes
+ */
+ const mobileView = Digit.Utils.browser.isMobile() ? true : false;
+ useEffect(() => {
+ document.body.style.overflowY = 'hidden';
+ return () => {
+ document.body.style.overflowY = 'auto';
+ }
+ }, [])
+ return (
+
+
+
+
+ {children}
+
+ {actionCancelLabel ?
:
}
+ {!hideSubmit ?
: null}
+
+
+
+ {error && setError(null)} error />}
+
+ );
+};
+
+export default Modal;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/RemovableTags.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/RemovableTags.js
new file mode 100644
index 0000000..c6d525a
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/RemovableTags.js
@@ -0,0 +1,186 @@
+import React, { useState, useEffect, useContext } from 'react';
+import RemoveableTagNew from '../atoms/RemovableTagNew';
+import { useTranslation } from 'react-i18next';
+import { Loader } from '../atoms/Loader';
+import { InboxContext } from './InboxSearchComposerContext';
+import _ from "lodash";
+
+const generateTagsFromFields = (fields, sessionData, t,data) => {
+ //filetering the fields
+
+ const fieldsToShow = fields
+ ?.filter((row) => row?.removableTagConf)
+ ?.map((row) => row?.removableTagConf);
+
+ const crumbs = [];
+ fieldsToShow?.forEach((field, idx) => {
+ //one field can have multiple crumbs
+ // we need to fill
+
+ //setting the text
+ const value = _.get(sessionData, field.sessionJsonPath, '');
+ if (!value || value?.length === 0) {
+ return;
+ }
+
+ //convert this to switch case and write a separate fn for it
+ switch (field?.type) {
+ case 'multi':
+ value?.forEach((val) => {
+ crumbs?.push({
+ label: t(field.label) || '',
+ value: `${t(
+ Digit.Utils.locale.getTransformedLocale(
+ _.get(val, field.valueJsonPath, '')
+ )
+ )}`,
+ removableTagConf: {
+ ...field,
+ value: val,
+ },
+ });
+ });
+ break;
+ case 'single':
+ if(_.get(value, field.valueJsonPath, '')){
+ crumbs?.push({
+ label: t(field.label) || '',
+ value: `${t(
+ Digit.Utils.locale.getTransformedLocale(
+ _.get(value, field.valueJsonPath, '')
+ )
+ )}`,
+ removableTagConf: { ...field, value },
+ });
+ }else if(typeof value === "string" && value){
+ crumbs?.push({
+ label: t(field.label) || '',
+ value: `${t(
+ Digit.Utils.locale.getTransformedLocale(
+ value
+ )
+ )}`,
+ removableTagConf: { ...field, value },
+ });
+ }
+ break;
+ case 'dateRange':
+ if(_.get(value, field.valueJsonPath, '')){
+ crumbs?.push({
+ label: t(field.label) || '',
+ value: _.get(value, field.valueJsonPath, ''),
+ removableTagConf: { ...field, value },
+ });
+ }
+ break;
+ case 'workflowStatusFilter':
+ if(!data || !value || Object?.keys(value)?.length===0){
+ return
+ }
+ const statusIds = Object?.keys(value)?.map(key => value[key] ? key : false)?.filter(val => val)
+ const statusObj = data?.statusMap?.map(status => {
+ if(statusIds?.includes(status?.statusid)){
+ return {
+ ...status
+ }
+ }else {
+ return false
+ }
+ })?.filter(val => val)
+ statusObj?.forEach(obj => {
+ crumbs?.push({
+ label: t(field.label) || '',
+ value: field?.valuePrefix ? t(Digit.Utils.locale.getTransformedLocale(`${field.valuePrefix}${obj?.applicationstatus}`)):t(Digit.Utils.locale.getTransformedLocale(obj?.applicationstatus)) ,
+ removableTagConf: { ...field, dynamicId:obj.statusid },
+ });
+ })
+ break;
+ default:
+ break;
+ }
+
+ // if (field?.type === 'multi') {
+ // value?.forEach((val) => {
+ // crumbs?.push({
+ // label: t(field.label) || '',
+ // value: `${t(
+ // Digit.Utils.locale.getTransformedLocale(
+ // _.get(val, field.valueJsonPath, '')
+ // )
+ // )}`,
+ // removableTagConf:{
+ // ...field,
+ // value:val
+ // }
+ // });
+ // });
+ // } else if (field?.type === 'single') {
+ // crumbs?.push({
+ // label: t(field.label) || '',
+ // value: `${t(
+ // Digit.Utils.locale.getTransformedLocale(
+ // _.get(val, field.valueJsonPath, '')
+ // )
+ // )}`,
+ // removableTagConf:{...field,value:val}
+ // });
+ // }
+ });
+
+ return crumbs;
+};
+
+const RemovableTags = ({ config, browserSession, fields,data, ...props }) => {
+ const { t } = useTranslation();
+ const [sessionData, setSessionData, clearSessionData] = browserSession;
+
+ const [removableTags, setRemovableTags] = useState([]);
+ const { state, dispatch } = useContext(InboxContext);
+
+ //sessionData will be the single source of truth for showing removable tags
+ //on click of tags we'll call dispatch and update the state which in turns updates the browser session
+
+ // On onclick of removable tag you need to update the state accordingly so that browser session gets updated accordingly
+ // map over fields in search and filter section
+ // for each field, refer field.removableTagConf
+
+ //an effect for generating selected filter tags
+ useEffect(() => {
+ setRemovableTags(generateTagsFromFields(fields, sessionData, t,data));
+ return () => {};
+ }, [fields, sessionData,data]);
+
+
+ // function to handle deletion of tags
+ //flow -> onClick -> Update state(dispatch with jsonPath) -> session gets updated automatically due to effect in parent
+ const handleCrumbDeletion = (tag) => {
+ dispatch(
+ {
+ type:"jsonPath",
+ tag
+ }
+ )
+ };
+
+ if (removableTags?.length === 0) {
+ return null;
+ }
+
+ return (
+
+ {removableTags?.map((tag, index) => {
+ return (
+ {
+ handleCrumbDeletion(tag);
+ }}
+ />
+ );
+ })}
+
+ );
+};
+
+export default RemovableTags;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ResponseComposer.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ResponseComposer.js
new file mode 100644
index 0000000..c6c11a9
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ResponseComposer.js
@@ -0,0 +1,61 @@
+import React from "react";
+import Card from "../atoms/Card";
+import KeyNote from "../atoms/KeyNote";
+import SubmitBar from "../atoms/SubmitBar";
+import PropTypes from "prop-types";
+import CitizenInfoLabel from "../atoms/CitizenInfoLabel";
+import { useTranslation } from "react-i18next";
+
+const ResponseComposer = ({ data, template, actionButtonLabel, onSubmit }) => {
+ const { t } = useTranslation();
+ return (
+
+ {data.map((result, i) => {
+ return (
+
+ {template.map((field, j) => {
+ return (
+
+ );
+ })}
+ {actionButtonLabel && result.status !== "INACTIVE" && (
+ {
+ onSubmit(result);
+ }}
+ />
+ )}
+ {result.status === "INACTIVE" && (
+
+ )}
+
+ );
+ })}
+
+ );
+};
+
+ResponseComposer.propTypes = {
+ data: PropTypes.array,
+ template: PropTypes.array,
+ actionButtonLabel: PropTypes.string,
+ onSubmit: PropTypes.func,
+};
+
+ResponseComposer.defaultProps = {
+ data: [],
+ template: [],
+ actionButtonLabel: "",
+ onSubmit: () => {},
+};
+
+export default ResponseComposer;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ResultsTable.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ResultsTable.js
new file mode 100644
index 0000000..11f134f
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ResultsTable.js
@@ -0,0 +1,240 @@
+import React, { useMemo, useCallback, useState, useEffect, Fragment,useContext } from 'react'
+import { useTranslation } from 'react-i18next';
+import Table from '../atoms/Table'
+import TextInput from '../atoms/TextInput'
+import { useForm, Controller } from "react-hook-form";
+import _ from "lodash";
+import { InboxContext } from './InboxSearchComposerContext';
+import { Loader } from '../atoms/Loader';
+import NoResultsFound from '../atoms/NoResultsFound';
+import { InfoIcon,EditIcon } from "../atoms/svgindex";
+
+const ResultsTable = ({ tableContainerClass, config,data,isLoading,isFetching,fullConfig,revalidate,type,activeLink,browserSession,additionalConfig }) => {
+ const {apiDetails} = fullConfig
+ const { t } = useTranslation();
+ const resultsKey = config.resultsJsonPath
+ const [showResultsTable,setShowResultsTable] = useState(true)
+ const [session,setSession,clearSession] = browserSession || []
+ // let searchResult = data?.[resultsKey]?.length>0 ? data?.[resultsKey] : []
+ let searchResult = _.get(data,resultsKey,[])
+ searchResult = searchResult?.length>0 ? searchResult : []
+ searchResult = searchResult.reverse();
+ const tenantId = Digit.ULBService.getCurrentTenantId();
+ const headerLocale = Digit.Utils.locale.getTransformedLocale(tenantId);
+
+ //reversing reason -> for some reason if we enable sorting on columns results from the api are reversed and shown, for now -> reversing the results(max size 50 so not a performance issue)
+
+ // if (fullConfig?.postProcessResult){
+ // var { isPostProcessFetching,
+ // isPostProcessLoading,
+ // combinedResponse } = Digit?.Customizations?.[apiDetails?.masterName]?.[apiDetails?.moduleName]?.postProcess(searchResult)
+
+ // if(combinedResponse?.length > 0){
+ // searchResult = combinedResponse
+ // }
+ // }
+
+
+
+
+
+ const {state,dispatch} = useContext(InboxContext)
+ //here I am just checking state.searchForm has all empty keys or not(when clicked on clear search)
+ useEffect(() => {
+ if(apiDetails?.minParametersForSearchForm !== 0 && Object.keys(state.searchForm).length > 0 && !Object.keys(state.searchForm).some(key => state.searchForm[key]!=="") && type==="search" && activeLink?.minParametersForSearchForm !== 0){
+ setShowResultsTable(false)
+ }
+ // else{
+ // setShowResultsTable(true)
+ // }
+ return ()=>{
+ setShowResultsTable(true)
+ }
+ }, [state])
+
+
+
+ const tableColumns = useMemo(() => {
+ //test if accessor can take jsonPath value only and then check sort and global search work properly
+ return config?.columns?.map(column => {
+ if(column?.svg) {
+ // const icon = Digit.ComponentRegistryService.getComponent(column.svg);
+ return {
+ Header: t(column?.label) || t("ES_COMMON_NA"),
+ accessor:column.jsonPath,
+ Cell: ({ value, col, row }) => {
+ return additionalConfig?.resultsTable?.onClickSvg(row)}>
+ }
+ }
+ }
+ if (column.additionalCustomization){
+ return {
+ Header: t(column?.label) || t("ES_COMMON_NA"),
+ accessor:column.jsonPath,
+ headerAlign: column?.headerAlign,
+ disableSortBy:column?.disableSortBy ? column?.disableSortBy :false,
+ Cell: ({ value, col, row }) => {
+ return Digit?.Customizations?.[apiDetails?.masterName]?.[apiDetails?.moduleName]?.additionalCustomizations(row.original,column?.label,column, value,t, searchResult);
+ }
+ }
+ }
+ return {
+ Header: t(column?.label) || t("ES_COMMON_NA"),
+ accessor: column.jsonPath,
+ headerAlign: column?.headerAlign,
+ disableSortBy:column?.disableSortBy ? column?.disableSortBy :false,
+ Cell: ({ value, col, row }) => {
+ return String(value ? column.translate? t(Digit.Utils.locale.getTransformedLocale(column.prefix?`${column.prefix}${value}`:value)) : value : t("ES_COMMON_NA"));
+ }
+ }
+ })
+ }, [config, searchResult])
+
+ const defaultValuesFromSession = config?.customDefaultPagination ? config?.customDefaultPagination : (session?.tableForm ? {...session?.tableForm} : {limit:10,offset:0})
+
+ const {
+ register,
+ handleSubmit,
+ setValue,
+ getValues,
+ reset,
+ watch,
+ trigger,
+ control,
+ formState,
+ errors,
+ setError,
+ clearErrors,
+ unregister,
+ } = useForm({
+ defaultValues: defaultValuesFromSession
+ });
+
+ //call this fn whenever session gets updated
+ const setDefaultValues = () => {
+ reset(defaultValuesFromSession)
+ }
+
+ //adding this effect because simply setting session to default values is not working
+ useEffect(() => {
+ setDefaultValues()
+ }, [session])
+
+ const isMobile = window.Digit.Utils.browser.isMobile();
+ const [searchQuery, onSearch] = useState("");
+
+ const filterValue = useCallback((rows, id, filterValue = "") => {
+
+ return rows.filter((row) => {
+ const res = Object.keys(row?.values).find((key) => {
+ if (typeof row?.values?.[key] === "object") {
+ return Object.keys(row?.values?.[key]).find((id) => {
+ if (id === "insight") {
+ return String(Math.abs(row?.values?.[key]?.[id]) + "%")
+ .toLowerCase()
+ .startsWith(filterValue?.toLowerCase());
+ }
+ return String(row?.values?.[key]?.[id])?.toLowerCase().includes(filterValue?.toLowerCase());
+ });
+ }
+ return (
+ String(row?.values?.[key]).toLowerCase()?.includes(filterValue?.toLowerCase()) ||
+ String(t(row?.values?.[key])).toLowerCase()?.includes(filterValue?.toLowerCase())
+ );
+ });
+ return res;
+ });
+ }, []);
+
+ useEffect(() => {
+ register("offset",session?.tableForm?.offset ||state.tableForm.offset|| config?.customDefaultPagination?.offset|| 0);
+ register("limit",session?.tableForm?.limit ||state.tableForm.limit|| config?.customDefaultPagination?.limit || 10);
+ });
+
+ // useEffect(() => {
+ // setValue("offset",state.tableForm.offset)
+ // setValue("limit",state.tableForm.limit)
+ // })
+
+ function onPageSizeChange(e) {
+ setValue("limit", Number(e.target.value));
+ handleSubmit(onSubmit)();
+ }
+
+ function nextPage() {
+ setValue("offset", getValues("offset") + getValues("limit"));
+ handleSubmit(onSubmit)();
+ }
+ function previousPage() {
+ const offsetValue = getValues("offset") - getValues("limit")
+ setValue("offset", offsetValue>0 ? offsetValue : 0);
+ handleSubmit(onSubmit)();
+ }
+
+ const onSubmit = (data) => {
+ //here update the reducer state
+ //call a dispatch to update table's part of the state and update offset, limit
+ // this will in turn make the api call and give search results and table will be rendered acc to the new data
+
+ dispatch({
+ type:"tableForm",
+ state:{...data}
+ })
+
+ }
+
+
+ if (isLoading || isFetching ) return
+ if(!data) return <>>
+ if(!showResultsTable) return <>>
+ if (searchResult?.length === 0) return
+ return (
+
+ {config?.enableGlobalSearch &&
+ onSearch(e.target.value)} style={{ border: "none", borderRadius: "200px" }} />
+
}
+ {
+ config?.showTableInstruction && (
+
+
{t(config?.showTableInstruction)}
+
)
+ }
+ {searchResult?.length > 0 &&
{
+ return {
+ style: {
+ padding: "20px 18px",
+ fontSize: "16px",
+ whiteSpace: "normal",
+ },
+ };
+ }}
+ onClickRow={additionalConfig?.resultsTable?.onClickRow}
+ manualPagination={config.manualPagination}
+ noColumnBorder={config?.noColumnBorder}
+ />}
+
+ )
+}
+
+export default ResultsTable
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/SubformComposer.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/SubformComposer.js
new file mode 100644
index 0000000..f2a6b9e
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/SubformComposer.js
@@ -0,0 +1,38 @@
+import React, { useEffect, useState } from "react";
+import { useForm, Controller } from "react-hook-form";
+import BreakLine from "../atoms/BreakLine";
+import Card from "../atoms/Card";
+import CardLabel from "../atoms/CardLabel";
+import CardText from "../atoms/CardText";
+// import CardLabelError from "../atoms/CardLabelError";
+import CardSubHeader from "../atoms/CardSubHeader";
+import CardSectionHeader from "../atoms/CardSectionHeader";
+import CardLabelDesc from "../atoms/CardLabelDesc";
+import CardLabelError from "../atoms/CardLabelError";
+import ActionBar from "../atoms/ActionBar";
+import SubmitBar from "../atoms/SubmitBar";
+import LabelFieldPair from "../atoms/LabelFieldPair";
+
+import { useTranslation } from "react-i18next";
+import TextInput from "../atoms/TextInput";
+import Dropdown from "../atoms/Dropdown";
+import MobileNumber from "../atoms/MobileNumber";
+import DatePicker from "../atoms/DatePicker";
+import TextArea from "../atoms/TextArea";
+
+export const SubFormComposer = ({
+ userType,
+ setValue,
+ onSelect,
+ config,
+ data,
+ formData,
+ register,
+ errors,
+ props,
+ setError,
+ clearErrors,
+ formState,
+ onBlur,
+ control,
+}) => {};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Tutorial/Help.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Tutorial/Help.js
new file mode 100644
index 0000000..0e4b630
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Tutorial/Help.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { HelpOutlineIcon } from "../../atoms/svgindex"
+import Label from '../../atoms/Label';
+import { useTranslation } from 'react-i18next';
+
+const Help = ({startTour, ...props}) => {
+ const { t } = useTranslation()
+ return (
+
+ {t('Help')}
+
+
+ );
+};
+
+export default Help;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Tutorial/Readme.md b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Tutorial/Readme.md
new file mode 100644
index 0000000..385fc64
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Tutorial/Readme.md
@@ -0,0 +1,49 @@
+# Underlying library used:
+
+react-joyride
+
+## React Tutorial Component
+
+This is a set of React components for creating interactive tutorials within your React application.
+There are 3 things that are exported from react components library:
+1. TourProvider -> This is a HOC which maintains a globalState for tour and provides it to all child components. Wrap your app with this component. A useTourState hook is also exported from this component which can be imported in any child component to access and update the globalState.
+2. Help -> This is basically a dummy component for help icon which accepts a callback called startTour as prop. StartTour is fired on onClick of Help icon to start the tour.
+3. Tutorial -> This is the main component which makes use of the globalState to show the tutorial.
+
+### Usage
+
+1. Wrap your app component with TourProvider
+2. Render Tutorial somewhere inside the app
+3. Render Help component
+4. On onClick of help update the TourState. You need to pass an object similar to the following in globalState
+ {
+ run: boolean,
+ steps: Array of steps,
+ tourActive: dummy state maintained to add custom logic like re-direction,
+ }
+Array of steps looks like this:
+[
+ {
+ content:
+ 'Welcome to the master data search screen. Here you can search the master data added under this master',
+ target: '.action-bar-wrap',
+ disableBeacon: true,
+ placement: 'bottom',
+ title:"Manage Master Data"
+ },
+ {
+ content:
+ 'To add new master data under this master click on the Add Master Data button',
+ target: '.action-bar-wrap',
+ disableBeacon: true,
+ placement: 'auto',
+ title:"Manage Master Data"
+ },
+]
+
+#### References:
+
+Refer the documentation of joyride for more information: https://docs.react-joyride.com/
+Refer the following commit for an exemplar done in workbench-ui: https://github.com/egovernments/DIGIT-Frontend/commit/0ab01509fc93dd94065146d9c218fd0e59310936
+```bash
+
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Tutorial/TourProvider.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Tutorial/TourProvider.js
new file mode 100644
index 0000000..38fb79e
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Tutorial/TourProvider.js
@@ -0,0 +1,22 @@
+import React, { createContext, useContext, useState } from 'react';
+
+const GlobalStateContext = createContext();
+
+export const useTourState = () => {
+ return useContext(GlobalStateContext);
+};
+
+export const TourProvider = ({ children }) => {
+ const [tourState, setTourState] = useState({
+ run: false,
+ // stepIndex: 0,
+ steps:[],
+ tourActive: false,
+ });
+
+ return (
+
+ {children}
+
+ );
+};
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Tutorial/Tutorial.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Tutorial/Tutorial.js
new file mode 100644
index 0000000..0e9c24a
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/Tutorial/Tutorial.js
@@ -0,0 +1,62 @@
+import React, { useEffect, useState } from 'react';
+import Joyride, { ACTIONS, EVENTS, LIFECYCLE, STATUS } from 'react-joyride';
+import { useHistory } from 'react-router-dom';
+
+let theme = {
+ // primaryColor: '#ad7bff',
+ // arrowColor: '#000',
+ // textColor: '#fff',
+ primaryColor: '#F47738',
+ arrowColor: '#FFFFFF',
+ textColor: '#505A5F',
+ zIndex:9999
+};
+
+const Tutorial = ({ tutorial, updateTutorial, ...props }) => {
+ const history = useHistory()
+ const { run, stepIndex, steps } = tutorial;
+
+ //UseEffect to update theme externally
+ useEffect(()=>{
+ if(props?.theme){
+ theme = {...theme,...props?.theme}
+ }
+ },[props?.theme])
+
+ const handleCallback = (event) => {
+
+ const {type,action,status,step} = event
+ //when we want to end the tutorial and reset the state
+ if(type==="tour:end" || action==="close"){
+ updateTutorial({
+ run: false,
+ steps: [],
+ tourActive: false,
+ });
+ }
+ }
+ return (
+
+ );
+};
+
+export default Tutorial;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/UploadFileComposer.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/UploadFileComposer.js
new file mode 100644
index 0000000..c81ef7e
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/UploadFileComposer.js
@@ -0,0 +1,154 @@
+import React from 'react'
+import { useTranslation } from 'react-i18next'
+import LabelFieldPair from '../atoms/LabelFieldPair'
+import CardLabel from '../atoms/CardLabel'
+import CardLabelError from '../atoms/CardLabelError'
+import CitizenInfoLabel from '../atoms/CitizenInfoLabel'
+import Header from '../atoms/Header'
+import { Loader } from '../atoms/Loader'
+import MultiUploadWrapper from '../molecules/MultiUploadWrapper'
+import TextInput from '../atoms/TextInput'
+
+const UploadFileComposer = ({module, config, Controller, control, register, formData, errors, localePrefix, customClass, customErrorMsg,mdmsModuleName='works'}) => {
+ const { t } = useTranslation()
+
+ //fetch mdms config based on module name
+ const tenant = Digit.ULBService.getStateId();
+ const { isLoading, data } = Digit.Hooks.useCustomMDMS(
+ tenant,
+ mdmsModuleName,
+ [
+ {
+ "name": "DocumentConfig",
+ "filter": `[?(@.module=='${module}')]`
+ }
+ ]
+ );
+
+
+
+ const docConfig = data?.[mdmsModuleName]?.DocumentConfig?.[0]
+
+ let documentFileTypeMappings = {
+ docx : "vnd.openxmlformats-officedocument.wordprocessingml.document",
+ doc : "application/msword",
+ png : "png",
+ pdf : "pdf",
+ jpeg : "jpeg",
+ jpg : "jpeg",
+ xls : "vnd.ms-excel",
+ xlsx : "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ csv : "csv"
+ }
+
+ const getRegex = (allowedFormats) => {
+ // console.log(allowedFormats);
+ // if(allowedFormats?.length) {
+ // const obj = { "expression" : `/(.*?)(${allowedFormats?.join('|')})$/`}
+ // const stringified = JSON.stringify(obj);
+ // console.log(new RegExp(JSON.parse(stringified).expression.slice(1, -1)));
+ // return new RegExp(JSON.parse(stringified).expression.slice(1, -1));
+ // } else if(docConfig?.allowedFileTypes?.length) {
+ // const obj = { "expression" : `/(.*?)(${docConfig?.allowedFileTypes?.join('|')})$/`}
+ // const stringified = JSON.stringify(obj);
+ // console.log(new RegExp(JSON.parse(stringified).expression.slice(1, -1)))
+ // return new RegExp(JSON.parse(stringified).expression.slice(1, -1));
+ // }
+ // return /(.*?)(pdf|docx|jpeg|jpg|png|msword|openxmlformats-officedocument|wordprocessingml|document|spreadsheetml|sheet)$/
+ if(allowedFormats?.length) {
+ let exceptedFileTypes = [];
+ allowedFormats?.forEach(allowedFormat=>{
+ exceptedFileTypes.push(documentFileTypeMappings[allowedFormat]);
+ });
+ exceptedFileTypes = exceptedFileTypes.join("|");
+ return new RegExp(`(.*?)(${exceptedFileTypes})$`)
+ }
+ return /(.*?)(pdf|docx|jpeg|jpg|png|msword|openxmlformats-officedocument|wordprocessingml|document|spreadsheetml|sheet)$/
+ }
+
+ // if(isLoading) return
+ return (
+
+ {t('WORKS_RELEVANT_DOCUMENTS')}
+
+ {
+ docConfig?.documents?.map((item, index) => {
+ if(!item?.active) return
+ return (
+
+ { item.code && (
+
+ { t(`${localePrefix}_${item?.code}`)} { item?.isMandatory ? " * " : null }
+ )
+ }
+
+
+ {
+ item?.showTextInput ?
+
:
+ null
+ }
+
+ {
+ function getFileStoreData(filesData) {
+ const numberOfFiles = filesData.length;
+ let finalDocumentData = [];
+ if (numberOfFiles > 0) {
+ filesData.forEach((value) => {
+ finalDocumentData.push({
+ fileName: value?.[0],
+ fileStoreId: value?.[1]?.fileStoreId?.fileStoreId,
+ documentType: value?.[1]?.file?.type,
+ });
+ });
+ }
+ onChange(numberOfFiles>0?filesData:[]);
+ }
+ return (
+
+ )
+ }}
+ rules={{validate:(value) => {
+ return !(item?.isMandatory && value?.length === 0)
+ }}}
+ defaultValue={formData?.[item?.name]}
+ name={`${config?.name}.${item?.name}`}
+ control={control}
+ />
+ { errors && errors[`${config?.name}`]?.[`${item?.name}`] && Object.keys(errors[`${config?.name}`]?.[`${item?.name}`]).length ? (
+
+ {t(config?.error)}
+ ) : null
+ }
+
+
+
+ )
+ })
+ }
+
+ )
+}
+
+export default UploadFileComposer
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ViewComposer/Readme.md b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ViewComposer/Readme.md
new file mode 100644
index 0000000..b4889bd
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ViewComposer/Readme.md
@@ -0,0 +1,126 @@
+# Intro:
+
+ViewComposer is basically a replacement of ApplicationDetails component which was a generic component for View Screens. That got very cluttered with custom logic. So to keep the business logic abstract and generic we made this component.
+
+## High level steps to use:
+1. Simply import it from react-components library
+2. It expects two things as props -> data,isLoading
+3. data contains all the details to show in the view screen with specific format, basically we'll be returning this data object from the hook call.
+
+### Format of data expected by ViewComposer
+Below is an example object:
+
+
+
+
+4. Basically cards is an array of objects each representing a Card in the View Screen.
+5. Each card can have multiple sections with the following types defined: [DATA,DOCUMENTS,WFHISTORY,WFACTIONS,COMPONENT]
+6. We can render content based on these types defined.
+
+
+#### Final Summary
+1. Import ViewComposer
+2. Write hook call that returns data expected by the ViewComposer
+3. Pass in the data and isLoading props and Viola!
+
+
+
+
+
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ViewComposer/index.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ViewComposer/index.js
new file mode 100644
index 0000000..fd9874a
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ViewComposer/index.js
@@ -0,0 +1,209 @@
+import React, { Fragment, useState } from "react";
+import Card from "../../atoms/Card";
+import { Loader } from "../../atoms/Loader";
+import { RenderDataSection, RenderDocumentsSection, RenderWfActions, RenderWfHistorySection } from "./renderUtils";
+import HorizontalNav from "../../atoms/HorizontalNav";
+import CardSubHeader from "../../atoms/CardSubHeader";
+import { SVG } from "../../atoms/SVG";
+import { useRef } from "react";
+
+// format of data expected by this component
+
+// {
+// cards:[
+// {
+// sections: [
+// {
+// type: "DATA",
+// sectionHeader: { value: "Section 1", inlineStyles: {} },
+// cardHeader: { value: "Card 2", inlineStyles: {} },
+// values: [
+// {
+// key: "key 1",
+// value: "value 1",
+// },
+// {
+// key: "key 2",
+// value: "value 2",
+// },
+// {
+// key: "key 3",
+// value: "value 3",
+// },
+// ],
+// },
+// {
+// type: "DATA",
+// sectionHeader: { value: "Section 2", inlineStyles: { marginTop: "2rem" } },
+// // cardHeader:{value:"Card 1",inlineStyles:{}},
+// values: [
+// {
+// key: "key 1",
+// value: "value 1",
+// },
+// {
+// key: "key 2",
+// value: "value 2",
+// },
+// {
+// key: "key 3",
+// value: "value 3",
+// },
+// ],
+// },
+// {
+// type: "DOCUMENTS",
+// documents: [
+// {
+// title: "WORKS_RELEVANT_DOCUMENTS",
+// BS: "Works",
+// values: [
+// {
+// title: "Proposal document",
+// documentType: "PROJECT_PROPOSAL",
+// documentUid: "cfed582b-31b0-42e9-985f-fb9bb4543670",
+// fileStoreId: "cfed582b-31b0-42e9-985f-fb9bb4543670",
+// },
+// {
+// title: "Finalised worklist",
+// documentType: "FINALIZED_WORKLIST",
+// documentUid: "f7543894-d3a1-4263-acb2-58b1383eebec",
+// fileStoreId: "f7543894-d3a1-4263-acb2-58b1383eebec",
+// },
+// {
+// title: "Feasibility analysis",
+// documentType: "FEASIBILITY_ANALYSIS",
+// documentUid: "c4fb4f5d-a4c3-472e-8991-e05bc2d671f5",
+// fileStoreId: "c4fb4f5d-a4c3-472e-8991-e05bc2d671f5",
+// },
+// ],
+// },
+// ],
+// inlineStyles: {
+// marginTop: "1rem",
+// },
+// },
+// {
+// type: "WFHISTORY",
+// businessService: "ESTIMATE",
+// applicationNo: "ES/2023-24/000828",
+// tenantId: "pg.citya",
+// timelineStatusPrefix: "TEST",
+// },
+// {
+// type: "WFACTIONS",
+// forcedActionPrefix: "TEST",
+// businessService: "ESTIMATE",
+// applicationNo: "ES/2023-24/000828",
+// tenantId: "pg.citya",
+// applicationDetails: {},
+// url: "/estimate/v1/_update",
+// moduleCode: "Estimate",
+// editApplicationNumber: undefined,
+// },
+// ],
+// },
+// ],
+// apiResponse:{},
+// additionalDetails:{}
+// }
+
+const renderCardSectionJSX = (section, cardErrors) => {
+ const { type } = section;
+ switch (type) {
+ case "DATA":
+ return ;
+ case "DOCUMENTS":
+ return ;
+ case "WFHISTORY":
+ return ;
+ case "WFACTIONS":
+ return ;
+ case "COMPONENT":
+ const Component = Digit.ComponentRegistryService.getComponent(section.component);
+ return (
+ <>
+
+ {section.cardHeader && (
+ i?.name === section?.name)?.length > 0 ? "error" : ""}
+ style={section?.cardHeader?.inlineStyles}
+ >
+ {section.cardHeader.value}
+ {cardErrors?.filter((i) => i?.name === section?.name)?.length > 0 ? : null}
+
+ )}
+ {section.cardSecondaryAction ? section.cardSecondaryAction : null}
+
+ i?.name === section?.name)} />
+ >
+ );
+ default:
+ return null;
+ }
+};
+
+//data is the response of the hook call for View Screen
+const ViewComposer = ({ isLoading = false, data, cardErrors, ...props }) => {
+ const { cards } = data;
+ const [activeNav, setActiveNav] = useState(data?.horizontalNav?.activeByDefault);
+ const cardRefs = useRef([]);
+
+ const scrollToCard = (index) => {
+ if (cardRefs.current[index]) {
+ const cardTopPosition = cardRefs.current[index].offsetTop;
+ window.scrollTo({ top: cardTopPosition, behavior: "smooth" });
+ }
+ };
+
+ if (isLoading) return ;
+
+ return (
+ <>
+ {/* This first {} is for rendering cards at the top without navigationKey(out of navbar) */}
+ {cards
+ ?.filter((card) => !card?.navigationKey && card?.sections)
+ ?.map((card, cardIdx) => {
+ const { sections } = card;
+ const hasErrors = cardErrors?.[card?.errorName]?.filter((i) => i?.name === card?.name)?.length > 0;
+ return (
+ (cardRefs.current[cardIdx] = el) : null}
+
+>
+ {hasErrors && scrollToCard(cardIdx)}
+ {sections?.map((section, sectionIdx) => {
+ return renderCardSectionJSX(section, cardErrors?.[card?.errorName ? card?.errorName : card?.name]);
+ })}
+
+ );
+ })}
+ {/* This second section is for rendering cards that are part of the navBar) */}
+
+
+ {cards
+ ?.filter((card) => card?.navigationKey)
+ ?.map((card, cardIdx) => {
+ const { sections } = card;
+ return (
+
+ {sections?.map((section, sectionIdx) => {
+ return renderCardSectionJSX(section);
+ })}
+
+ );
+ })}
+
+ >
+ );
+};
+
+export default ViewComposer;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ViewComposer/renderUtils.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ViewComposer/renderUtils.js
new file mode 100644
index 0000000..b5d1392
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/ViewComposer/renderUtils.js
@@ -0,0 +1,154 @@
+import React, { Fragment, useState, useEffect } from "react";
+import CardSectionHeader from "../../atoms/CardSectionHeader";
+import { StatusTable, Row } from "../../atoms/StatusTable";
+import CardSubHeader from "../../atoms/CardSubHeader";
+import { useTranslation } from "react-i18next";
+import { PDFSvg } from "../../atoms/svgindex";
+import WorkflowTimeline from "../../atoms/WorkflowTimeline";
+import WorkflowActions from "../../atoms/WorkflowActions";
+import { Link } from "react-router-dom";
+
+export const RenderDataSection = ({ section }) => {
+ const { t } = useTranslation();
+ return (
+ <>
+
+ {section.cardHeader && {section.cardHeader.value} }
+ {section.cardSecondaryAction ? section.cardSecondaryAction : null}
+
+
+ {section.sectionHeader && {section.sectionHeader.value} }
+ {section.values.map((row, rowIdx) => {
+ return (
+
+
+
+ {row?.value}
+
+
+
+ ) : row?.isSla ? (
+ {row?.value}
+ ) : (
+ row.value
+ )
+ }
+ last={rowIdx === section.values?.length - 1}
+ caption={row.caption}
+ className="border-none"
+ /* privacy object set to the Row Component */
+ privacy={row?.value?.privacy}
+ rowContainerStyle={{}}
+ textStyle={{}}
+ labelStyle={{}}
+ amountStyle={{}}
+ />
+ );
+ })}
+
+ >
+ );
+};
+
+export const RenderDocumentsSection = ({ section }) => {
+ const { documents } = section;
+ const { t } = useTranslation();
+ const [filesArray, setFilesArray] = useState(() => []);
+ const tenantId = Digit.ULBService.getCurrentTenantId();
+ const [pdfFiles, setPdfFiles] = useState({});
+
+ useEffect(() => {
+ let acc = [];
+ documents?.forEach((element, index, array) => {
+ acc = [...acc, ...(element.values ? element.values : [])];
+ });
+ setFilesArray(acc?.map((value) => value?.fileStoreId));
+ }, [documents]);
+
+ useEffect(() => {
+ if (filesArray?.length && documents?.[0]?.BS === "BillAmend") {
+ Digit.UploadServices.Filefetch(filesArray, Digit.ULBService.getCurrentTenantId()).then((res) => {
+ setPdfFiles(res?.data);
+ });
+ } else if (filesArray?.length) {
+ Digit.UploadServices.Filefetch(filesArray, Digit.ULBService.getCurrentTenantId()).then((res) => {
+ setPdfFiles(res?.data);
+ });
+ }
+ }, [filesArray]);
+
+ return (
+
+ {documents?.map((document, index) => (
+
+ {document?.title ? {t(document?.title)} : null}
+
+ {document?.values && document?.values.length > 0
+ ? document?.values?.map((value, index) => (
+
+
+ {t(value?.title)}
+
+ ))
+ : !window.location.href.includes("citizen") && (
+
+
{t("BPA_NO_DOCUMENTS_UPLOADED_LABEL")}
+
+ )}
+
+
+ ))}
+
+ );
+};
+
+export const RenderWfHistorySection = ({ section }) => {
+ const { businessService, applicationNo, tenantId, timelineStatusPrefix = undefined, statusAttribute = undefined } = section;
+ return (
+
+ );
+};
+
+export const RenderWfActions = ({ section }) => {
+ const {
+ forcedActionPrefix = undefined,
+ businessService,
+ applicationNo,
+ tenantId,
+ applicationDetails,
+ url,
+ moduleCode = "Estimate",
+ editApplicationNumber,
+ } = section;
+
+ return (
+
+ );
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/formUtils.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/formUtils.js
new file mode 100644
index 0000000..f49eb1b
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/formUtils.js
@@ -0,0 +1,26 @@
+import { buildYup } from "json-schema-to-yup";
+
+
+
+export const buildYupConfig = (jsonConfig,t)=>{
+ const errorConfig = {
+ // for error messages...
+ errMessages: {
+ }
+ };
+ jsonConfig?.required?.map(code=>{
+ errorConfig.errMessages[code]={required:`please enter the value for ${code}`}
+ })
+ Object.keys(jsonConfig?.properties)?.map(code=>{
+ if(errorConfig.errMessages[code]){
+ errorConfig.errMessages[code]={...errorConfig.errMessages[code],format:`invalid value entered for ${code}`}
+ }else{
+ errorConfig.errMessages[code]={format:`invalid value entered for ${code}`,"typeError":`invalid type, enter a valid ${code}`}
+ }
+ })
+
+ const yupSchema = buildYup(jsonConfig, errorConfig);
+ return yupSchema
+
+}
+
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/index.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/index.js
new file mode 100644
index 0000000..5d387e3
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/index.js
@@ -0,0 +1,601 @@
+import ActionBar from "./atoms/ActionBar";
+import ActionLinks from "./atoms/ActionLinks";
+import AppContainer from "./atoms/AppContainer";
+import ApplyFilterBar from "./atoms/ApplyFilterBar";
+import BackButton from "./atoms/BackButton";
+import Banner from "./atoms/Banner";
+import Body from "./atoms/Body";
+import BreadCrumb from "./atoms/BreadCrumb";
+import BreakLine from "./atoms/BreakLine";
+import ButtonSelector from "./atoms/ButtonSelector";
+import Button from "./atoms/Button";
+import Card from "./atoms/Card";
+import CardCaption from "./atoms/CardCaption";
+import CardHeader from "./atoms/CardHeader";
+import CardLabel from "./atoms/CardLabel";
+import CardLabelDesc from "./atoms/CardLabelDesc";
+import CardLabelError from "./atoms/CardLabelError";
+import CardSectionHeader from "./atoms/CardSectionHeader";
+import CardSectionSubText from "./atoms/CardSectionSubText";
+import CardSubHeader from "./atoms/CardSubHeader";
+import CardText from "./atoms/CardText";
+import CardTextButton from "./atoms/CardTextButton";
+import CheckBox from "./atoms/CheckBox";
+import CitizenHomeCard from "./atoms/CitizenHomeCard";
+import CitizenInfoLabel from "./atoms/CitizenInfoLabel";
+import { CheckPoint, ConnectingCheckPoints } from "./atoms/ConnectingCheckPoints";
+import CustomButton from "./atoms/CustomButton";
+import DatePicker from "./atoms/DatePicker";
+import DateRange from "./molecules/DateRange";
+import DateRangeNew from "./molecules/DateRangeNew";
+import DateWrap from "./atoms/DateWrap";
+import DisplayPhotos from "./atoms/DisplayPhotos";
+import Dropdown from "./atoms/Dropdown";
+import SearchableDropdown from "./atoms/SearchableDropdown";
+import EllipsisMenu from "./atoms/EllipsisMenu";
+import EmployeeAppContainer from "./atoms/EmployeeAppContainer";
+import { EmployeeModuleCard, ModuleCardFullWidth } from "./atoms/EmployeeModuleCard";
+import GreyOutText from "./atoms/GreyOutText";
+import Hamburger from "./atoms/Hamburger";
+import Header from "./atoms/Header";
+import HeaderBar from "./atoms/HeaderBar";
+import HomeLink from "./atoms/HomeLink";
+import { ImageUploadHandler } from "./atoms/ImageUploadHandler";
+import ImageViewer from "./atoms/ImageViewer";
+import InfoBanner from "./atoms/InfoBanner";
+import KeyNote from "./atoms/KeyNote";
+import Label from "./atoms/Label";
+import LabelFieldPair from "./atoms/LabelFieldPair";
+import LinkButton from "./atoms/LinkButton";
+import LinkLabel from "./atoms/LinkLabel";
+import { Loader } from "./atoms/Loader";
+import LocationSearch from "./atoms/LocationSearch";
+import Menu from "./atoms/Menu";
+import MobileNumber from "./atoms/MobileNumber";
+import MultiLink from "./atoms/MultiLink";
+import MultiSelectDropdown from "./atoms/MultiSelectDropdown";
+import NavBar from "./atoms/NavBar";
+import OTPInput from "./atoms/OTPInput";
+import PopUp from "./atoms/PopUp";
+import { PrivateRoute } from "./atoms/PrivateRoute";
+import RadioButtons from "./atoms/RadioButtons";
+import Rating from "./atoms/Rating";
+import UnMaskComponent from "./atoms/UnMaskComponent";
+import RoundedLabel from "./atoms/RoundedLabel";
+import SectionalDropdown from "./atoms/SectionalDropdown";
+import { LastRow, MediaRow, Row, StatusTable } from "./atoms/StatusTable";
+import SubmitBar from "./atoms/SubmitBar";
+import StandaloneSearchBar from "./atoms/StandaloneSearchBar";
+import ULBHomeCard from "./atoms/ULBHomeCard";
+import ViewDetailsCard from "./atoms/ViewDetailsCard";
+
+import {
+ AnnouncementIcon,
+ ArrowDown,
+ ArrowLeft,
+ ArrowLeftWhite,
+ ArrowRightInbox,
+ Calender,
+ CaseIcon,
+ CitizenTruck,
+ CloseSvg,
+ Close,
+ ComplaintIcon,
+ Details,
+ DocumentSVG,
+ DownloadIcon,
+ DownloadImgIcon,
+ DownwardArrow,
+ DropIcon,
+ Ellipsis,
+ EmailIcon,
+ FilterIcon,
+ GetApp,
+ HomeIcon,
+ PrevIcon,
+ ViewsIcon,
+ LanguageIcon,
+ FilterSvg,
+ LogoutIcon,
+ Person,
+ PersonIcon,
+ Poll,
+ PrintIcon,
+ PropertyHouse,
+ PTIcon,
+ ReceiptIcon,
+ RefreshIcon,
+ RefreshSVG,
+ RupeeIcon,
+ SearchIconSvg,
+ ShareIcon,
+ ShippingTruck,
+ SortDown,
+ SortSvg,
+ GenericFileIcon,
+ SortUp,
+ UpwardArrow,
+ WhatsappIcon,
+ OBPSIcon,
+ EDCRIcon,
+ BPAIcon,
+ BPAHomeIcon,
+ DocumentIcon,
+ ExternalLinkIcon,
+ PMBIcon,
+ PDFSvg,
+ DownloadPrefixIcon,
+ HelpIcon,
+ TickMark,
+ NotificationBell,
+ MapMarker,
+ Clock,
+ EventCalendar,
+ ImageIcon,
+ EditIcon,
+ SearchIcon,
+ DeleteIcon,
+ CreateLoiIcon,
+ OBPSIconSolidBg,
+ DocumentIconSolid,
+ PMBIconSolid,
+ EventsIconSolid,
+ SurveyIconSolid,
+ DustbinIcon,
+ InfoBannerIcon,
+ WSICon,
+ ArrowForward,
+ ArrowVectorDown,
+ ArrowDirection,
+ CameraIcon,
+ EditPencilIcon,
+ GalleryIcon,
+ RemoveIcon,
+ CheckSvg,
+ AddressBookIcon,
+ LocationIcon,
+ CollectionsBookmarIcons,
+ FinanceChartIcon,
+ CollectionIcon,
+ FSMIcon,
+ MCollectIcon,
+ PGRIcon,
+ TLIcon,
+ BillsIcon,
+ ErrorIcon,
+ PrintBtnCommon,
+ WhatsappIconGreen,
+ HelpLineIcon,
+ ServiceCenterIcon,
+ TimerIcon,
+ RupeeSymbol,
+ ValidityTimeIcon,
+ AddIcon,
+ SubtractIcon,
+ AddNewIcon,
+ InboxIcon,
+ ViewReportIcon,
+ PrivacyMaskIcon,
+ DeathIcon,
+ BirthIcon,
+ FirenocIcon,
+ CreateEstimateIcon,
+ GotoInboxIcon,
+ AddFilled,
+ AddFileFilled,
+ LocateIcon,
+
+ /* Works Management */
+
+ NoResultsFoundIcon,
+ WorksMgmtIcon,
+ BioMetricIcon,
+ MuktaHomeIcon,
+ HRIcon,
+ ProjectIcon,
+ EstimateIcon,
+ ContractIcon,
+ AttendanceIcon,
+ WageseekerIcon,
+ OrganisationIcon,
+ HelperIcon,
+ DashboardIcon,
+ ExpenditureIcon,
+ PaymentIcon,
+ HistoryIcon,
+ WarningIcon,
+ AttentionListIcon,
+ UploadIcon,
+ FileIcon,
+ DeleteIconv2,
+ InfoIconOutline,
+ HelpOutlineIcon,
+ InputIcon,
+ TreatmentQualityIcon,
+} from "./atoms/svgindex";
+import Table from "./atoms/Table";
+import TelePhone from "./atoms/TelePhone";
+import { Phone } from "./atoms/svgindex";
+import TextArea from "./atoms/TextArea";
+import InputTextAmount from "./atoms/InputTextAmount";
+import TextInput from "./atoms/TextInput";
+import Toast from "./atoms/Toast";
+import TopBar from "./atoms/TopBar";
+import UploadFile from "./atoms/UploadFile";
+import UploadImages from "./atoms/UploadImages";
+import CardBasedOptions from "./atoms/CardBasedOptions";
+import WhatsNewCard from "./atoms/WhatsNewCard";
+import EventCalendarView from "./atoms/EventCalendarView";
+import InboxLinks from "./atoms/InboxLinks";
+import PopupHeadingLabel from "./atoms/PopupHeadingLabel";
+
+import { FormComposer } from "./hoc/FormComposer";
+import { FormComposer as FormComposerV2 } from "./hoc/FormComposerV2";
+import RenderFormFields from "./molecules/RenderFormFields";
+import Modal from "./hoc/Modal";
+import FileUploadModal from "./hoc/FileUploadModal"
+import ResponseComposer from "./hoc/ResponseComposer";
+import InboxComposer from "./hoc/InboxComposer";
+
+import CityMohalla from "./molecules/CityMohalla";
+import DashboardBox from "./molecules/DashboardBox";
+import DetailsCard from "./molecules/DetailsCard";
+import WorkflowModal from "./molecules/WorkflowModal";
+import FilterAction from "./molecules/FilterAction";
+import FormStep from "./molecules/FormStep";
+import CustomDropdown from "./molecules/CustomDropdown";
+import InputCard from "./molecules/InputCard";
+import Localities from "./molecules/Localities";
+import LocationSearchCard from "./molecules/LocationSearchCard";
+import PitDimension from "./molecules/PitDimension";
+import RadioOrSelect from "./molecules/RadioOrSelect";
+import RatingCard from "./molecules/RatingCard";
+import RemoveableTag from "./atoms/RemoveableTag";
+import SearchAction from "./molecules/SearchAction";
+import SortAction from "./molecules/SortAction";
+import { SearchField, SearchForm } from "./molecules/SearchForm";
+import TextInputCard from "./molecules/TextInputCard";
+import TypeSelectCard from "./molecules/TypeSelectCard";
+import PageBasedInput from "./molecules/PageBasedInput";
+import SearchOnRadioButtons from "./molecules/SearchOnRadioButtons";
+import OnGroundEventCard from "./molecules/OnGroundEventCard";
+import MultiUploadWrapper from "./molecules/MultiUploadWrapper";
+import { FilterForm, FilterFormField } from "./molecules/FilterForm";
+import OpenLinkContainer from "./atoms/OpenLinkContainer";
+import UploadPitPhoto from "./molecules/UploadPitPhoto";
+import { DownloadBtnCommon } from "./atoms/svgindex";
+import ToggleSwitch from "./atoms/ToggleSwitch";
+import WeekPicker from "./atoms/WeekPicker";
+import CollapseAndExpandGroups from "./atoms/CollapseAndExpandGroups";
+import HorizontalNav from "./atoms/HorizontalNav";
+import NoResultsFound from "./atoms/NoResultsFound";
+import { ViewImages } from "./atoms/ViewImages";
+import InboxSearchComposer from "./hoc/InboxSearchComposer";
+import InboxSearchComposerV2 from "./hoc/InboxSearchComposerV2";
+import MobileSearchResults from "./hoc/MobileView/MobileSearchResults";
+import MobileSearchComponent from "./hoc/MobileView/MobileSearchComponent";
+import ResultsTable from "./hoc/ResultsTable";
+import InboxSearchLinks from "./atoms/InboxSearchLinks";
+import UploadFileComposer from "./hoc/UploadFileComposer";
+import WorkflowTimeline from "./atoms/WorkflowTimeline";
+import WorkflowActions from "./atoms/WorkflowActions";
+import Amount from "./atoms/Amount";
+import Paragraph from "./atoms/Paragraph";
+import CitizenConsentForm from "./atoms/CitizenConsentForm";
+import CloseButton from "./atoms/CloseButton";
+import LoaderWithGap from "./atoms/LoaderWithGap";
+// Importing From SVG Library
+import {SVG} from "./atoms/SVG";
+
+//Tutorial
+import Tutorial from "./hoc/Tutorial/Tutorial";
+import { useTourState,TourProvider } from "./hoc/Tutorial/TourProvider";
+import Help from "./hoc/Tutorial/Help";
+
+//View Composer
+import ViewComposer from "./hoc/ViewComposer";
+import XlsPreview from "./atoms/XlsPreview";
+export {
+ XlsPreview,
+ ViewComposer,
+ HelpOutlineIcon,
+ InputIcon,
+ TreatmentQualityIcon,
+ Help,
+ Tutorial,
+ useTourState,
+ TourProvider,
+ InputTextAmount,
+ Button,
+ ViewImages,
+ Phone,
+ CitizenConsentForm,
+ Body,
+ Loader,
+ TopBar,
+ OpenLinkContainer,
+ HomeLink,
+ AppContainer,
+ EmployeeAppContainer,
+ Header,
+ ActionBar,
+ Menu,
+ LinkLabel,
+ BackButton,
+ BreakLine,
+ Card,
+ CardCaption,
+ CardHeader,
+ CardText,
+ CardLabel,
+ CardLabelDesc,
+ CardLabelError,
+ CardTextButton,
+ RadioButtons,
+ DashboardBox,
+ Dropdown,
+ SearchableDropdown,
+ TextInput,
+ TextArea,
+ Paragraph,
+ Banner,
+ CardSubHeader,
+ CardSectionHeader,
+ CardSectionSubText,
+ SubmitBar,
+ ButtonSelector,
+ LinkButton,
+ StatusTable,
+ Row,
+ LastRow,
+ MediaRow,
+ DisplayPhotos,
+ ConnectingCheckPoints,
+ CheckPoint,
+ Rating,
+ CheckBox,
+ OTPInput,
+ LocationSearch,
+ UploadFile,
+ UploadImages,
+ ImageViewer,
+ ImageUploadHandler,
+ TypeSelectCard,
+ LocationSearchCard,
+ TextInputCard,
+ CityMohalla,
+ DetailsCard,
+ WorkflowModal,
+ FileUploadModal,
+ Label,
+ Table,
+ PopUp,
+ HeaderBar,
+ Toast,
+ DateWrap,
+ KeyNote,
+ TelePhone,
+ GreyOutText,
+ ActionLinks,
+ PrivateRoute,
+ SectionalDropdown,
+ RoundedLabel,
+ LabelFieldPair,
+ BreadCrumb,
+ DatePicker,
+ WeekPicker,
+ InfoBanner,
+ MobileNumber,
+ EllipsisMenu,
+ CitizenHomeCard,
+ EmployeeModuleCard,
+ StandaloneSearchBar,
+ CardBasedOptions,
+ WhatsNewCard,
+ EventCalendarView,
+ InboxLinks,
+ PopupHeadingLabel,
+ ToggleSwitch,
+ ULBHomeCard,
+ ViewDetailsCard,
+ CollapseAndExpandGroups,
+ HorizontalNav,
+ NoResultsFound,
+ Amount,
+ LoaderWithGap,
+ // Icons
+ GetApp,
+ ArrowLeft,
+ ArrowLeftWhite,
+ AddFileFilled,
+ HomeIcon,
+ LanguageIcon,
+ LogoutIcon,
+ NavBar,
+ Hamburger,
+ CustomButton,
+ CitizenInfoLabel,
+ SearchIconSvg,
+ ArrowRightInbox,
+ ArrowDown,
+ SortDown,
+ SortUp,
+ SortSvg,
+ ShippingTruck,
+ CloseSvg,
+ Close,
+ PropertyHouse,
+ MultiLink,
+ MultiSelectDropdown,
+ CaseIcon,
+ PTIcon,
+ DocumentIcon,
+ DocumentIconSolid,
+ PMBIconSolid,
+ EventsIconSolid,
+ SurveyIconSolid,
+ PMBIcon,
+ DustbinIcon,
+ GenericFileIcon,
+ HelpIcon,
+ InfoBannerIcon,
+ NotificationBell,
+ ImageIcon,
+ OBPSIconSolidBg,
+ AddFilled,
+ // Molecule
+ InputCard,
+ FormStep,
+ RatingCard,
+ SearchAction,
+ FilterAction,
+ ApplyFilterBar,
+ RemoveableTag,
+ RadioOrSelect,
+ Localities,
+ SearchForm,
+ SearchField,
+ PageBasedInput,
+ SearchOnRadioButtons,
+ OnGroundEventCard,
+ MultiUploadWrapper,
+ FilterForm,
+ FilterFormField,
+ SortAction,
+ CustomDropdown,
+ CloseButton,
+ // hoc
+ FormComposer,
+ FormComposerV2,
+ RenderFormFields,
+ WorkflowTimeline,
+ WorkflowActions,
+ ResponseComposer,
+ PitDimension,
+ Modal,
+ UpwardArrow,
+ DownwardArrow,
+ DownloadImgIcon,
+ ViewsIcon,
+ PrevIcon,
+ DownloadIcon,
+ ExternalLinkIcon,
+ Ellipsis,
+ RefreshIcon,
+ RefreshSVG,
+ Poll,
+ Details,
+ InboxComposer,
+ InboxSearchComposer,
+ InboxSearchComposerV2,
+ MobileSearchResults,
+ MobileSearchComponent,
+ ResultsTable,
+ InboxSearchLinks,
+ UploadFileComposer,
+ // Icons
+ FilterIcon,
+ FilterSvg,
+ PrintIcon,
+ ShareIcon,
+ Calender,
+ DropIcon,
+ RupeeIcon,
+ ComplaintIcon,
+ Person,
+ WhatsappIcon,
+ EmailIcon,
+ DocumentSVG,
+ PersonIcon,
+ ReceiptIcon,
+ AnnouncementIcon,
+ OBPSIcon,
+ CitizenTruck,
+ EDCRIcon,
+ BPAIcon,
+ BPAHomeIcon,
+ MapMarker,
+ Clock,
+ EventCalendar,
+ TickMark,
+ PDFSvg,
+ DownloadPrefixIcon,
+ DateRange,
+ DateRangeNew,
+ EditIcon,
+ SearchIcon,
+ DeleteIcon,
+ CreateLoiIcon,
+ WSICon,
+ CameraIcon,
+ EditPencilIcon,
+ GalleryIcon,
+ RemoveIcon,
+ UploadPitPhoto,
+ CheckSvg,
+ ModuleCardFullWidth,
+ ArrowForward,
+ ArrowVectorDown,
+ ArrowDirection,
+ AddressBookIcon,
+ LocationIcon,
+ CollectionsBookmarIcons,
+ FinanceChartIcon,
+ CollectionIcon,
+ FSMIcon,
+ MCollectIcon,
+ PGRIcon,
+ TLIcon,
+ BillsIcon,
+ ErrorIcon,
+ DownloadBtnCommon,
+ PrintBtnCommon,
+ WhatsappIconGreen,
+ HelpLineIcon,
+ ServiceCenterIcon,
+ TimerIcon,
+ RupeeSymbol,
+ ValidityTimeIcon,
+ AddIcon,
+ SubtractIcon,
+ AddNewIcon,
+ ViewReportIcon,
+ InboxIcon,
+ UnMaskComponent,
+ PrivacyMaskIcon,
+ DeathIcon,
+ BirthIcon,
+ FirenocIcon,
+ CreateEstimateIcon,
+ GotoInboxIcon,
+ LocateIcon,
+ UploadIcon,
+ FileIcon,
+ DeleteIconv2,
+ InfoIconOutline,
+
+ /* Works Management */
+
+ NoResultsFoundIcon,
+ WorksMgmtIcon,
+ BioMetricIcon,
+ MuktaHomeIcon,
+ HRIcon,
+ ProjectIcon,
+ EstimateIcon,
+ ContractIcon,
+ AttendanceIcon,
+ WageseekerIcon,
+ OrganisationIcon,
+ HelperIcon,
+ DashboardIcon,
+ ExpenditureIcon,
+ PaymentIcon,
+ HistoryIcon,
+ WarningIcon,
+ AttentionListIcon,
+
+
+ // Exported all svgs from svg-component library
+ SVG
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/ApiCheckboxes.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/ApiCheckboxes.js
new file mode 100644
index 0000000..1fc776c
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/ApiCheckboxes.js
@@ -0,0 +1,46 @@
+import React, { Fragment, useState, useEffect } from "react";
+import { Loader } from "../atoms/Loader";
+import { useTranslation } from "react-i18next";
+import _ from "lodash";
+import CheckBox from "../atoms/CheckBox";
+import CardLabel from "../atoms/CardLabel";
+
+const ApiCheckboxes = ({ populators, formData, props }) => {
+ //based on the reqCriteria and populators we will render options in checkboxes
+
+ const [options, setOptions] = useState([]);
+ const { t } = useTranslation();
+
+ const reqCriteria = Digit?.Customizations?.[populators?.masterName]?.[populators?.moduleName]?.[populators?.customfn](populators)
+
+ const { isLoading: isApiLoading, data: apiData, revalidate, isFetching: isApiFetching } = Digit.Hooks.useCustomAPIHook(reqCriteria);
+
+ useEffect(() => {
+ setOptions(apiData);
+ }, [apiData]);
+
+ if (isApiLoading) return ;
+
+ return (
+ <>
+ {options?.map((row,idx) => {
+ return (
+ {
+ const obj = {
+ ...props.value,
+ [e.target.value]: e.target.checked,
+ };
+ props.onChange(obj);
+ }}
+ value={row?.[populators?.optionsKey]}
+ checked={formData?.[populators.name]?.[row?.[populators?.optionsKey]]}
+ label={t(row?.[populators?.labelKey])}
+ />
+ );
+ })}
+ >
+ );
+};
+
+export default ApiCheckboxes;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/ApiDropdown.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/ApiDropdown.js
new file mode 100644
index 0000000..30a95d6
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/ApiDropdown.js
@@ -0,0 +1,74 @@
+import React, { Fragment, useState, useEffect } from "react";
+import MultiSelectDropdown from "../atoms/MultiSelectDropdown";
+import Dropdown from "../atoms/Dropdown";
+import { Loader } from "../atoms/Loader";
+import { useTranslation } from "react-i18next";
+import _ from "lodash";
+const ApiDropdown = ({ populators, formData, props, inputRef, errors }) => {
+ //based on type (ward/locality) we will render dropdowns respectively
+ //here we will render two types of dropdown based on allowMultiSelect boolean
+ // for singleSelect render
+
+ const [options, setOptions] = useState([]);
+
+ const { t } = useTranslation();
+
+ const reqCriteria = Digit?.Customizations?.[populators?.masterName]?.[populators?.moduleName]?.[populators?.customfn](populators)
+
+ const { isLoading: isApiLoading, data: apiData, revalidate, isFetching: isApiFetching } = Digit.Hooks.useCustomAPIHook(reqCriteria);
+
+ useEffect(() => {
+ setOptions(apiData);
+ }, [apiData]);
+
+ if (isApiLoading) return ;
+
+ return (
+ <>
+ {populators.allowMultiSelect && (
+
+ {
+ props.onChange(
+ e
+ ?.map((row) => {
+ return row?.[1] ? row[1] : null;
+ })
+ .filter((e) => e)
+ );
+ }}
+ selected={props?.value}
+ defaultLabel={t(populators?.defaultText) }
+ defaultUnit={t(populators?.selectedText) || t("COMMON_SELECTED")}
+ config={populators}
+
+ />
+
+ )}
+ {!populators.allowMultiSelect && (
+ {
+ props.onChange([e], populators.name);
+ }}
+ selected={props.value?.[0] || populators.defaultValue}
+ defaultValue={props.value?.[0] || populators.defaultValue}
+ t={t}
+ errorStyle={errors?.[populators.name]}
+ optionCardStyles={populators?.optionsCustomStyle}
+ />
+ )}
+ >
+ );
+};
+
+export default ApiDropdown;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/CityMohalla.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/CityMohalla.js
new file mode 100644
index 0000000..d992bdd
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/CityMohalla.js
@@ -0,0 +1,76 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+import Card from "../atoms/Card";
+import CardHeader from "../atoms/CardHeader";
+import CardText from "../atoms/CardText";
+import SubmitBar from "../atoms/SubmitBar";
+import CardSubHeader from "../atoms/CardSubHeader";
+import CardLabel from "../atoms/CardLabel";
+import Dropdown from "../atoms/Dropdown";
+
+const CityMohalla = ({
+ header,
+ subHeader,
+ cardText,
+ cardLabelCity,
+ cardLabelMohalla,
+ nextText,
+ selectedCity,
+ cities,
+ localities,
+ selectCity,
+ selectLocalities,
+ onSave,
+ selectedLocality,
+}) => {
+ return (
+
+ {subHeader}
+ {header}
+
+ {/* Choose the locality/mohalla of the complaint from the list given below. */}
+ {cardText}
+
+ {cardLabelCity}*
+
+ {cardLabelMohalla} *
+
+
+
+ );
+};
+
+CityMohalla.propTypes = {
+ header: PropTypes.string,
+ subHeader: PropTypes.string,
+ cardText: PropTypes.string,
+ cardLabelCity: PropTypes.string,
+ cardLabelMohalla: PropTypes.string,
+ nextText: PropTypes.string,
+ selectedCity: PropTypes.string,
+ cities: PropTypes.array,
+ localities: PropTypes.array,
+ selectCity: PropTypes.string,
+ selectedLocality: PropTypes.string,
+ selectLocalities: PropTypes.func,
+ onSave: PropTypes.func,
+};
+
+CityMohalla.defaultProps = {
+ header: "",
+ subHeader: "",
+ cardText: "",
+ cardLabelCity: "",
+ cardLabelMohalla: "",
+ nextText: "",
+ selectedCity: "",
+ cities: [],
+ localities: [],
+ selectCity: "",
+ selectedLocality: "",
+ selectLocalities: undefined,
+ onSave: undefined,
+};
+
+export default CityMohalla;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/CityMohalla.stories.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/CityMohalla.stories.js
new file mode 100644
index 0000000..b367b2e
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/CityMohalla.stories.js
@@ -0,0 +1,26 @@
+import React from "react";
+
+import CityMohalla from "./CityMohalla";
+
+export default {
+ title: "Molecule/CityMohalla",
+ component: CityMohalla,
+};
+
+const Template = (args) => ;
+
+export const Default = Template.bind({});
+
+Default.args = {
+ header: "Header",
+ subHeader: "Sub Header",
+ cardText: "Card Text",
+ cardLabelCity: "City",
+ cardLabelMohalla: "Mohalla",
+ nextText: "Next",
+ selectedCity: "first city",
+ cities: ["first city", "second city", "third city"],
+ localities: ["first locality", "second locality", "third locality"],
+ selectCity: "first city",
+ selectedLocality: "first locality",
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/CustomDropdown.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/CustomDropdown.js
new file mode 100644
index 0000000..6b7de35
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/CustomDropdown.js
@@ -0,0 +1,182 @@
+import _ from "lodash";
+import React, { useEffect, useState } from "react";
+import { Loader } from "../atoms/Loader";
+import RadioButtons from "../atoms/RadioButtons";
+import Dropdown from "../atoms/Dropdown";
+import MultiSelectDropdown from "../atoms/MultiSelectDropdown";
+
+/**
+ * Custom Dropdown / Radio Button component can be used mostly via formcomposer
+ *
+ * @author jagankumar-egov
+ *
+ * @example
+ *
+ *
+ * {
+ * component: "CustomDropdown",
+ isMandatory: false,
+ key: "gender",
+ type: "radio",
+ label: "Enter Gender",
+ disable: false,
+ populators: {
+ name: "gender",
+ optionsKey: "name",
+ error: "sample required message",
+ required: false,
+ options: [
+ {
+ code: "MALE",
+ name: "MALE",
+ },
+ {
+ code: "FEMALE",
+ name: "FEMALE",
+ },
+ {
+ code: "TRANSGENDER",
+ name: "TRANSGENDER",
+ },
+ ],
+ },
+ }
+or
+ {
+ component: "CustomDropdown",
+ isMandatory: true,
+ key: "genders",
+ type: "radioordropdown",
+ label: "Enter Gender",
+ disable: false,
+ populators: {
+ name: "genders",
+ optionsKey: "name",
+ error: "sample required message",
+ required: true,
+ mdmsConfig: {
+ masterName: "GenderType",
+ moduleName: "common-masters",
+ localePrefix: "COMMON_GENDER",
+ },
+ },
+ },
+ *
+ */
+const CustomDropdown = ({ t, config, inputRef, label, onChange, value, errorStyle, disable, type, additionalWrapperClass = "",mdmsv2=false,props}) => {
+ const master = { name: config?.mdmsConfig?.masterName };
+ if (config?.mdmsConfig?.filter) {
+ master["filter"] = config?.mdmsConfig?.filter;
+ }
+ const { isLoading, data } = Digit.Hooks.useCustomMDMS(Digit.ULBService.getStateId(), config?.mdmsConfig?.moduleName, [master], {
+ select: config?.mdmsConfig?.select
+ ? Digit.Utils.createFunction(config?.mdmsConfig?.select)
+ : (data) => {
+ const optionsData = _.get(data, `${config?.mdmsConfig?.moduleName}.${config?.mdmsConfig?.masterName}`, []);
+ return optionsData
+ .filter((opt) => (opt?.hasOwnProperty("active") ? opt.active : true))
+ .map((opt) => ({ ...opt, name: `${config?.mdmsConfig?.localePrefix}_${Digit.Utils.locale.getTransformedLocale(opt.code)}` }));
+ },
+ enabled: (config?.mdmsConfig || config?.mdmsv2) ? true : false,
+ },mdmsv2);
+ if (isLoading) {
+ return ;
+ }
+
+ // const getValue = () => {
+ // let selectedValue = ""
+ // if(data?.length === 1 || config?.options?.length === 1) {
+ // selectedValue = data?.[0] || config?.options?.[0]
+ // } else {
+ // selectedValue = value
+ // }
+ // return selectedValue
+ // }
+
+ if(config?.optionsDisable && config?.options && config?.defaultOptions){
+ config?.options?.forEach(obj1 => obj1.isDisabled = config?.defaultOptions.some(obj2 => obj2.type === obj1.code));
+ }
+
+ return (
+
+ {/*
+
+ {t(label)}
+ {config.required ? " * " : null}
+ */}
+
+ { (config.allowMultiSelect && type==="dropdown") ?
+
+ {
+ props?.onChange
+ ? props.onChange(
+ e
+ ?.map((row) => {
+ return row?.[1] ? row[1] : null;
+ })
+ .filter((e) => e)
+ )
+ : onChange(
+ e
+ ?.map((row) => {
+ return row?.[1] ? row[1] : null;
+ })
+ .filter((e) => e)
+ );
+ }}
+ selected={props?.value || value}
+ defaultLabel={t(config?.defaultText) }
+ defaultUnit={t(config?.selectedText) || t("COMMON_SELECTED")}
+ config={config}
+ disable={disable}
+ optionsDisable={config?.optionsDisable}
+ />
+
: type === "radio" ? (
+ {
+ onChange(e, config.name);
+ }}
+ disable={disable}
+ selectedOption={value}
+ defaultValue={value}
+ t={t}
+ errorStyle={errorStyle}
+ additionalWrapperClass={additionalWrapperClass}
+ innerStyles={config?.innerStyles}
+ />
+ ) : (
+ {
+ onChange(e, config.name);
+ }}
+ disable={disable}
+ selected={value || config.defaultValue}
+ defaultValue={value || config.defaultValue}
+ t={t}
+ errorStyle={errorStyle}
+ optionCardStyles={config?.optionsCustomStyle}
+ />
+ )
+ }
+
+ );
+};
+
+export default CustomDropdown;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DashboardBox.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DashboardBox.js
new file mode 100644
index 0000000..f58e1af
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DashboardBox.js
@@ -0,0 +1,68 @@
+import React from "react";
+import { Link } from "react-router-dom";
+
+const ArrowRight = ({ to }) => (
+
+
+
+
+
+
+);
+
+const DashboardBox = ({ t = (val) => val, svgIcon, header, info, subHeader, links, total }) => {
+ const mobileView = innerWidth <= 640;
+ const employeeCardStyles = mobileView
+ ? {
+ width: "96vw",
+ margin: "8px auto",
+ }
+ : {
+ width: "328px",
+ };
+ return (
+
+
+
+
+ {/*
+
+
+ */}
+ {svgIcon}
+
+
{t(header)}
+
+
+
+ {Object.keys(info).map((key, index) => {
+ return (
+
+ {t(info[key])}
+ {t(key)}
+
+ );
+ })}
+
+
+
+
+ {links.map((link, index) => (
+
+
+ {t(link.label)}
+
+ {!isNaN(link.total) && {link.total} }
+ { }
+
+ ))}
+
+
+
+ );
+};
+
+export default DashboardBox;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DateRange.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DateRange.js
new file mode 100644
index 0000000..195301b
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DateRange.js
@@ -0,0 +1,171 @@
+import React, { Fragment, useEffect, useMemo, useRef, useState } from "react";
+// import { ArrowDown, Modal, ButtonSelector, Calender } from "@egovernments/digit-ui-react-components";
+import ButtonSelector from "../atoms/ButtonSelector";
+import { ArrowDown, Calender } from "../atoms/svgindex";
+import Modal from "../hoc/Modal";
+import { DateRangePicker, createStaticRanges } from "react-date-range";
+import { format, addMonths, addHours, startOfToday, endOfToday, endOfYesterday, addMinutes, addSeconds, isEqual, subYears, startOfYesterday, startOfWeek, endOfWeek, startOfYear, endOfYear, startOfMonth, endOfMonth, startOfQuarter, endOfQuarter } from "date-fns";
+
+import { enGB } from 'date-fns/locale'
+import { addDays } from 'date-fns'
+function isEndDateFocused(focusNumber) {
+ return focusNumber === 1;
+}
+
+function isStartDateFocused(focusNumber) {
+ return focusNumber === 0;
+}
+
+const DateRange = ({ values, onFilterChange, t,labelClass, filterLabel,...props }) => {
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [focusedRange, setFocusedRange] = useState([0, 0]);
+ const [selectionRange, setSelectionRange] = useState({
+ ...values,
+ startDate: values?.startDate,
+ endDate: values?.endDate
+ });
+ const wrapperRef = useRef(null);
+
+ useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
+ setIsModalOpen(false);
+ }
+ };
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ };
+ }, [wrapperRef]);
+
+ useEffect(() => {
+ if (!isModalOpen && selectionRange?.startDate instanceof Date && selectionRange?.endDate instanceof Date) {
+ const startDate = selectionRange?.startDate;
+ const endDate = selectionRange?.endDate;
+ const duration = getDuration(selectionRange?.startDate, selectionRange?.endDate);
+ const title = `${format(selectionRange?.startDate, "dd-MM-yy")} - ${format(selectionRange?.endDate, "dd-MM-yy")}`;
+ onFilterChange({ range: { startDate, endDate, duration, title }, requestDate: { startDate, endDate, duration, title } });
+ }
+ }, [selectionRange, isModalOpen]);
+
+ const staticRanges = useMemo(() => {
+ return createStaticRanges([
+ {
+ label: t("DSS_TODAY"),
+ range: () => ({
+ startDate: startOfToday(new Date()),
+ endDate: endOfToday(new Date()),
+ })
+ },
+ {
+ label: t("DSS_YESTERDAY"),
+ range: () => ({
+ startDate: startOfYesterday(new Date()),
+ endDate: endOfYesterday(new Date()),
+ })
+ },
+ {
+ label: t("DSS_THIS_WEEK"),
+ range: () => ({
+ startDate: startOfWeek(new Date(), { weekStartsOn: 1 }),
+ endDate: endOfWeek(new Date(), { weekStartsOn: 1 }),
+ })
+ },
+ {
+ label: t('DSS_THIS_MONTH'),
+ range: () => ({
+ startDate: startOfMonth(new Date()),
+ endDate: endOfMonth(new Date()),
+ })
+ },
+ {
+ label: t('DSS_THIS_QUARTER'),
+ range: () => ({
+ startDate: startOfQuarter(new Date()),
+ endDate: endOfQuarter(new Date()),
+ })
+ },
+ {
+ label: t('DSS_PREVIOUS_YEAR'),
+ range: () => ({
+ startDate: subYears(addMonths(startOfYear(new Date()), 3), 1),
+ endDate: subYears(addMonths(endOfYear(new Date()), 3), 1)
+ })
+ },
+ {
+ label: t('DSS_THIS_YEAR'),
+ range: () => ({
+ startDate: addMonths(startOfYear(new Date()), 3),
+ endDate: addMonths(endOfYear(new Date()), 3)
+ })
+ }
+ ])
+ }, [])
+
+ const getDuration = (startDate, endDate) => {
+ let noOfDays = (new Date(endDate).getTime() - new Date(startDate).getTime()) / (1000 * 3600 * 24);
+ if (noOfDays > 91) {
+ return "month";
+ }
+ if (noOfDays < 90 && noOfDays >= 14) {
+ return "week";
+ }
+ if (noOfDays <= 14) {
+ return "day";
+ }
+ };
+
+ const handleSelect = (ranges) => {
+ const { range1: selection } = ranges;
+ const { startDate, endDate, title, duration } = selection;
+ if (isStartDateFocused(focusedRange[1])) {
+ setSelectionRange(selection);
+ }
+ if (isEndDateFocused(focusedRange[1])) {
+ setSelectionRange({ title, duration, startDate, endDate: addSeconds(addMinutes(addHours(endDate, 23), 59), 59) });
+ setIsModalOpen(false);
+ }
+ };
+
+ const handleFocusChange = (focusedRange) => {
+ const [rangeIndex, rangeStep] = focusedRange;
+ setFocusedRange(focusedRange);
+ };
+
+ const handleClose = () => {
+ setIsModalOpen(false);
+ };
+
+ return (
+ <>
+ {t(`${filterLabel}`)}
+
+
+
+ setIsModalOpen((prevState) => !prevState)} />
+
+ {isModalOpen && (
+
+ day.addDays(6, 'days')}
+
+ />
+
+ )}
+
+ >
+ );
+};
+
+export default DateRange;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DateRangeNew.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DateRangeNew.js
new file mode 100644
index 0000000..fa3a521
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DateRangeNew.js
@@ -0,0 +1,171 @@
+import React, { Fragment, useEffect, useMemo, useRef, useState } from "react";
+// import { ArrowDown, Modal, ButtonSelector, Calender } from "@egovernments/digit-ui-react-components";
+import ButtonSelector from "../atoms/ButtonSelector";
+import { ArrowDown, Calender } from "../atoms/svgindex";
+import Modal from "../hoc/Modal";
+import { DateRange, createStaticRanges } from "react-date-range";
+import { format, addMonths, addHours, startOfToday, endOfToday, endOfYesterday, addMinutes, addSeconds, isEqual, subYears, startOfYesterday, startOfWeek, endOfWeek, startOfYear, endOfYear, startOfMonth, endOfMonth, startOfQuarter, endOfQuarter } from "date-fns";
+
+function isEndDateFocused(focusNumber) {
+ return focusNumber === 1;
+}
+
+function isStartDateFocused(focusNumber) {
+ return focusNumber === 0;
+}
+
+const DateRangeNew = ({populators, values, onFilterChange, t, labelClass, label, customStyles, inputRef}) => {
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [focusedRange, setFocusedRange] = useState([0, 0]);
+ const [selectionRange, setSelectionRange] = useState({
+ ...values,
+ startDate: typeof values?.startDate === "string" ? new Date(values?.startDate) : values?.startDate,
+ endDate: typeof values?.endDate === "string" ? new Date(values?.endDate) : values?.endDate
+ });
+ const wrapperRef = useRef(inputRef);
+
+ useEffect(() => {
+ const handleClickOutside = (event) => {
+ if (wrapperRef.current && !wrapperRef.current.contains(event.target)) {
+ setIsModalOpen(false);
+ }
+ };
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => {
+ document.removeEventListener("mousedown", handleClickOutside);
+ };
+ }, [wrapperRef]);
+
+ useEffect(() => {
+ if (!isModalOpen && selectionRange?.startDate instanceof Date && selectionRange?.endDate instanceof Date) {
+ const startDate = selectionRange?.startDate;
+ const endDate = selectionRange?.endDate;
+ const duration = getDuration(selectionRange?.startDate, selectionRange?.endDate);
+ const title = `${format(selectionRange?.startDate, 'dd/MM/yyyy')} - ${format(selectionRange?.endDate, 'dd/MM/yyyy')}`;
+ onFilterChange({ range: { startDate, endDate, duration, title }, requestDate: { startDate, endDate, duration, title } });
+ }
+ }, [selectionRange, isModalOpen]);
+
+ const staticRanges = useMemo(() => {
+ return createStaticRanges([
+ {
+ label: t("DSS_TODAY"),
+ range: () => ({
+ startDate: startOfToday(new Date()),
+ endDate: endOfToday(new Date()),
+ })
+ },
+ {
+ label: t("DSS_YESTERDAY"),
+ range: () => ({
+ startDate: startOfYesterday(new Date()),
+ endDate: endOfYesterday(new Date()),
+ })
+ },
+ {
+ label: t("DSS_THIS_WEEK"),
+ range: () => ({
+ startDate: startOfWeek(new Date()),
+ endDate: endOfWeek(new Date()),
+ })
+ },
+ {
+ label: t('DSS_THIS_MONTH'),
+ range: () => ({
+ startDate: startOfMonth(new Date()),
+ endDate: endOfMonth(new Date()),
+ })
+ },
+ {
+ label: t('DSS_THIS_QUARTER'),
+ range: () => ({
+ startDate: startOfQuarter(new Date()),
+ endDate: endOfQuarter(new Date()),
+ })
+ },
+ {
+ label: t('DSS_PREVIOUS_YEAR'),
+ range: () => ({
+ startDate: subYears(addMonths(startOfYear(new Date()), 3), 1),
+ endDate: subYears(addMonths(endOfYear(new Date()), 3), 1)
+ })
+ },
+ {
+ label: t('DSS_THIS_YEAR'),
+ range: () => ({
+ startDate: addMonths(startOfYear(new Date()), 3),
+ endDate: addMonths(endOfYear(new Date()), 3)
+ })
+ }
+ ])
+ }, [])
+
+ const getDuration = (startDate, endDate) => {
+ let noOfDays = (new Date(endDate).getTime() - new Date(startDate).getTime()) / (1000 * 3600 * 24);
+ if (noOfDays > 91) {
+ return "month";
+ }
+ if (noOfDays < 90 && noOfDays >= 14) {
+ return "week";
+ }
+ if (noOfDays <= 14) {
+ return "day";
+ }
+ };
+
+ const handleSelect = (ranges) => {
+ const { range1: selection } = ranges;
+ const { startDate, endDate, title, duration } = selection;
+ if (isStartDateFocused(focusedRange[1])) {
+ setSelectionRange(selection);
+ }
+ if (isEndDateFocused(focusedRange[1])) {
+ setSelectionRange({ title, duration, startDate, endDate: addSeconds(addMinutes(addHours(endDate, 23), 59), 59) });
+ setIsModalOpen(false);
+ }
+ };
+
+ const handleFocusChange = (focusedRange) => {
+ const [rangeIndex, rangeStep] = focusedRange;
+ setFocusedRange(focusedRange);
+ };
+
+ const handleClose = () => {
+ setIsModalOpen(false);
+ };
+
+ return (
+ <>
+ {label}
+
+
+
+ setIsModalOpen((prevState) => !prevState)} />
+
+ {isModalOpen && (
+
+
+
+ )}
+
+ >
+ );
+};
+
+export default DateRangeNew;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DetailsCard.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DetailsCard.js
new file mode 100644
index 0000000..7428893
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DetailsCard.js
@@ -0,0 +1,143 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { Link } from "react-router-dom";
+import CitizenInfoLabel from "../atoms/CitizenInfoLabel";
+import ActionBar from "../atoms/ActionBar";
+import SubmitBar from "../atoms/SubmitBar";
+import Button from "../atoms/Button";
+export const Details = ({ label, name, onClick}) => {
+ return (
+
+
+ {label}
+
+ {name}
+
+ );
+};
+
+const DetailsCard = ({ data, serviceRequestIdKey, linkPrefix, handleSelect, selectedItems, keyForSelected, handleDetailCardClick, isTwoDynamicPrefix = false, getRedirectionLink, handleClickEnabled = true, t, showActionBar = true, showCitizenInfoLabel = false,submitButtonLabel,mode="default",apiDetails }) => {
+ if (linkPrefix && serviceRequestIdKey) {
+ return (
+
+ {data.map((object, itemIndex) => {
+ return (
+
+
+ {Object.keys(object).map((name, index) => {
+ if (name === "applicationNo" || name === "Vehicle Log") return null;
+ return ;
+ })}
+
+
+ );
+ })}
+
+ );
+ }
+
+ return (
+
+ {data.map((object, itemIndex) => {
+ return (
+
{
+ e.stopPropagation()
+ handleClickEnabled && handleSelect(object)
+ }}
+ >
+ {Object.keys(object)
+ .filter(
+ (rowEle) =>
+ !(
+ typeof object[rowEle] == 'object' &&
+ object[rowEle]?.hidden == true
+ )
+ )
+ .map((name, index) => {
+ return (
+ {
+ e.stopPropagation()
+ handleClickEnabled && handleSelect(object)
+ }
+ }
+ />
+ );
+ })}
+ {showCitizenInfoLabel ? (
+
+ ) : null}
+ {showActionBar && mode==="default" ? (
+ handleDetailCardClick(object)}
+ label={submitButtonLabel}
+ />
+ ) : null}
+ {showActionBar && mode==="tqm" && (
+ {
+ e.stopPropagation()
+ handleDetailCardClick(object)
+ }}
+ className={'header-btn'}
+ style={{width:"100%"}}
+ textStyles={{fontWeight:700}}
+ />
+ )}
+
+ );
+ })}
+
+ );
+};
+
+DetailsCard.propTypes = {
+ data: PropTypes.array,
+};
+
+DetailsCard.defaultProps = {
+ data: [],
+};
+
+export default DetailsCard;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DetailsCard.stories.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DetailsCard.stories.js
new file mode 100644
index 0000000..00cb4a2
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/DetailsCard.stories.js
@@ -0,0 +1,16 @@
+import React from "react";
+
+import DetailsCard from "./DetailsCard";
+
+export default {
+ title: "Molecule/DetailsCard",
+ component: DetailsCard,
+};
+
+const Template = (args) => ;
+
+export const Default = Template.bind({});
+
+Default.args = {
+ data: [{ Name: "Joe Doe", City: "San Francisco" }],
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/FilterAction.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/FilterAction.js
new file mode 100644
index 0000000..9dcb49b
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/FilterAction.js
@@ -0,0 +1,12 @@
+import React from "react";
+import { FilterSvg,FilterIcon,SortSvg } from "../atoms/svgindex";
+import RoundedLabel from "../atoms/RoundedLabel";
+
+const FilterAction = ({ text, handleActionClick, ...props }) => (
+
+
+ {text}
+
+);
+
+export default FilterAction;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/FilterForm.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/FilterForm.js
new file mode 100644
index 0000000..65ef0c6
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/FilterForm.js
@@ -0,0 +1,74 @@
+import React from "react";
+import { CloseSvg } from "../atoms/svgindex";
+import { useTranslation } from "react-i18next";
+import SubmitBar from "../atoms/SubmitBar";
+
+const FilterFormField = ({ children, className }) => {
+ return {children}
;
+};
+
+const FilterForm = ({
+ onMobileExclusiveFilterPopupFormClose = () => null,
+ closeButton = () => null,
+ showMobileFilterFormPopup = false,
+ children,
+ id = "",
+ onSubmit,
+ handleSubmit,
+ onResetFilterForm = () => null,
+ className = "",
+}) => {
+ const { t } = useTranslation();
+ const isMobile = window.Digit.Utils.browser.isMobile();
+ const isEnabledCommonModules =
+ window.location.href.includes("/obps/") ||
+ window.location.href.includes("/noc/") ||
+ window.location.href.includes("/ws/water/bill-amendment/inbox") ||
+ window.location.href.includes("/ws/sewerage/bill-amendment/inbox");
+
+ // min-height: calc(100% - 110
+ // px
+ // );
+ return (
+
+
+ {closeButton()}
+
+
+
+
+
+
+
+
{t("FILTERS_FILTER_CARD_CAPTION")}:
+
+
+ {t("ES_COMMON_CLEAR_ALL")}
+
+
+
+
+
+
+ {showMobileFilterFormPopup ? (
+
+
+
+ ) : null}
+
+
+
+
+
+ );
+};
+
+export { FilterForm, FilterFormField };
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/FormStep.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/FormStep.js
new file mode 100644
index 0000000..032aeb5
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/FormStep.js
@@ -0,0 +1,110 @@
+import React from "react";
+import { useForm } from "react-hook-form";
+import PropTypes from "prop-types";
+import TextArea from "../atoms/TextArea";
+import CardLabel from "../atoms/CardLabel";
+import CardLabelError from "../atoms/CardLabelError";
+import TextInput from "../atoms/TextInput";
+import InputCard from "./InputCard";
+
+const FormStep = ({
+ t,
+ children,
+ config,
+ onSelect,
+ onSkip,
+ value,
+ onChange,
+ isDisabled,
+ _defaultValues = {},
+ forcedError,
+ componentInFront,
+ onAdd,
+ cardStyle = {},
+ isMultipleAllow = false,
+ showErrorBelowChildren = false,
+ childrenAtTheBottom = true,
+ textInputStyle
+}) => {
+ const { register, watch, errors, handleSubmit } = useForm({
+ defaultValues: _defaultValues,
+ });
+
+ const goNext = (data) => {
+ onSelect(data);
+ };
+
+ var isDisable = isDisabled ? true : config.canDisable && Object.keys(errors).filter((i) => errors[i]).length;
+
+ const inputs = config.inputs?.map((input, index) => {
+ if (input.type === "text") {
+ return (
+
+ {t(input.label)}
+ {errors[input.name] && {t(input.error)} }
+
+ {componentInFront ? {componentInFront} : null}
+
+
+
+ );
+ }
+ if (input.type === "textarea")
+ return (
+
+ {t(input.label)}
+
+
+ );
+ });
+
+ return (
+
+ );
+};
+
+FormStep.propTypes = {
+ config: PropTypes.shape({}),
+ onSelect: PropTypes.func,
+ onSkip: PropTypes.func,
+ onAdd: PropTypes.func,
+ t: PropTypes.func,
+};
+
+FormStep.defaultProps = {
+ config: {},
+ onSelect: undefined,
+ onSkip: undefined,
+ onAdd: undefined,
+ t: (value) => value,
+};
+
+export default FormStep;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/FormStep.stories.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/FormStep.stories.js
new file mode 100644
index 0000000..33618f2
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/FormStep.stories.js
@@ -0,0 +1,18 @@
+import React from "react";
+
+import FormStep from "./FormStep";
+
+export default {
+ title: "Molecule/FormStep",
+ component: FormStep,
+};
+
+const Template = (args) => ;
+
+export const Default = Template.bind({});
+
+Default.args = {
+ config: {
+ inputs: [{ type: "text", name: "firstName", label: "First Name", error: "This field is required", validation: null }],
+ },
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/InputCard.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/InputCard.js
new file mode 100644
index 0000000..4600248
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/InputCard.js
@@ -0,0 +1,60 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+import Card from "../atoms/Card";
+import CardHeader from "../atoms/CardHeader";
+import CardText from "../atoms/CardText";
+import SubmitBar from "../atoms/SubmitBar";
+import LinkButton from "../atoms/LinkButton";
+import CardCaption from "../atoms/CardCaption";
+import TextInput from "../atoms/TextInput";
+
+const InputCard = ({
+ t,
+ children,
+ texts = {},
+ submit = false,
+ inputs = [],
+ inputRef,
+ onNext,
+ onSkip,
+ isDisable,
+ onAdd,
+ isMultipleAllow = false,
+ cardStyle = {},
+}) => {
+ const isMobile = window.Digit.Utils.browser.isMobile();
+ // TODO: inputs handle
+ return (
+
+ {texts.headerCaption && {t(texts.headerCaption)} }
+ {texts?.header && {t(texts.header)} }
+ {texts?.cardText && {t(texts.cardText)} }
+ {children}
+ {texts.submitBarLabel ? : null}
+ {texts.skipLabel ? {t(texts.skipLabel)} : null}
+ {texts.skipText ? : null}
+ {isMultipleAllow && texts.addMultipleText ? : null}
+
+ );
+};
+
+InputCard.propTypes = {
+ text: PropTypes.object,
+ submit: PropTypes.bool,
+ onNext: PropTypes.func,
+ onSkip: PropTypes.func,
+ onAdd: PropTypes.func,
+ t: PropTypes.func,
+};
+
+InputCard.defaultProps = {
+ texts: {},
+ submit: false,
+ onNext: undefined,
+ onSkip: undefined,
+ onAdd: undefined,
+ t: (value) => value,
+};
+
+export default InputCard;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/InputCard.stories.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/InputCard.stories.js
new file mode 100644
index 0000000..0b94f76
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/InputCard.stories.js
@@ -0,0 +1,22 @@
+import React from "react";
+
+import InputCard from "./InputCard";
+
+export default {
+ title: "Molecule/InputCard",
+ component: InputCard,
+};
+
+const Template = (args) => ;
+
+export const Default = Template.bind({});
+
+Default.args = {
+ texts: {
+ headerCaption: "Header Caption",
+ header: "Header",
+ cardText: "Card Text",
+ nextText: "Next",
+ skipText: "Skip",
+ },
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/Localities.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/Localities.js
new file mode 100644
index 0000000..68eb85a
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/Localities.js
@@ -0,0 +1,29 @@
+import React from "react";
+import { Loader } from "../atoms/Loader";
+import Dropdown from "../atoms/Dropdown";
+import { useTranslation } from "react-i18next";
+
+const Localities = ({ selectLocality, tenantId, boundaryType, keepNull, selected, optionCardStyles, style, disable, disableLoader, sortFn }) => {
+ const { t } = useTranslation();
+
+ const { data: tenantlocalties, isLoading } = Digit.Hooks.useBoundaryLocalities(tenantId, boundaryType, { enabled: !disable }, t);
+ if (isLoading && !disableLoader) {
+ return ;
+ }
+
+ return (
+
+ );
+ // ABCD
+};
+
+export default Localities;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/LocationDropdownWrapper.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/LocationDropdownWrapper.js
new file mode 100644
index 0000000..5857028
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/LocationDropdownWrapper.js
@@ -0,0 +1,97 @@
+import React,{Fragment,useState,useEffect} from 'react'
+import MultiSelectDropdown from '../atoms/MultiSelectDropdown'
+import Dropdown from '../atoms/Dropdown'
+import { Loader } from '../atoms/Loader'
+import { useTranslation } from 'react-i18next'
+const LocationDropdownWrapper = ({populators,formData,props,inputRef,errors,setValue}) => {
+ //based on type (ward/locality) we will render dropdowns respectively
+ //here we will render two types of dropdown based on allowMultiSelect boolean
+ // for singleSelect render
+
+ const [options,setOptions] = useState([])
+
+ const tenantId = Digit.ULBService.getCurrentTenantId()
+ const headerLocale = Digit.Utils.locale.getTransformedLocale(tenantId);
+ const { t } = useTranslation()
+ const { isLoading, data: wardsAndLocalities } = Digit.Hooks.useLocation(
+ tenantId, 'Ward',
+ {
+ select: (data) => {
+
+ const wards = []
+ const localities = {}
+ data?.TenantBoundary[0]?.boundary.forEach((item) => {
+ localities[item?.code] = item?.children.map(item => ({ code: item.code, name: item.name, i18nKey: `${headerLocale}_ADMIN_${item?.code}`, label: item?.label }))
+ wards.push({ code: item.code, name: item.name, i18nKey: `${headerLocale}_ADMIN_${item?.code}` })
+ });
+
+ return {
+ wards, localities
+ }
+ }
+ });
+
+
+ useEffect(() => {
+ if(wardsAndLocalities) {
+ if(populators.type==="ward"){
+ setOptions(wardsAndLocalities?.wards)
+ }
+ else{
+ //here you need to set the localities based on the selected ward
+ let locs = []
+ const selectedWardsCodes = formData?.ward?.map(row=>row.code)
+ selectedWardsCodes?.forEach(code=>{
+ locs=[...locs,...wardsAndLocalities?.localities?.[code]]
+ })
+ setOptions(locs)
+ }
+ }
+ }, [wardsAndLocalities,formData?.ward])
+
+
+ if(isLoading) return
+
+ return (
+ <>
+ {populators.allowMultiSelect &&
+ {
+ if(populators.type === "ward"){
+ setValue('locality',[])
+ }
+ props.onChange(e?.map(row => { return row?.[1] ? row[1] : null }).filter(e => e))
+ }}
+ selected={props?.value}
+ defaultLabel={t(populators?.defaultText)}
+ defaultUnit={t(populators?.selectedText)}
+ config={populators}
+ />
+
}
+ {!populators.allowMultiSelect &&
+ {
+ props.onChange([e], populators.name);
+ }}
+ selected={props.value?.[0] || populators.defaultValue}
+ defaultValue={props.value?.[0] || populators.defaultValue}
+ t={t}
+ errorStyle={errors?.[populators.name]}
+ optionCardStyles={populators?.optionsCustomStyle}
+ />
+ }
+ >
+ )
+}
+
+export default LocationDropdownWrapper
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/LocationSearchCard.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/LocationSearchCard.js
new file mode 100644
index 0000000..a894348
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/LocationSearchCard.js
@@ -0,0 +1,107 @@
+import React from "react";
+import PropTypes from "prop-types";
+import { useTranslation } from "react-i18next";
+
+import Card from "../atoms/Card";
+import CardHeader from "../atoms/CardHeader";
+import CardText from "../atoms/CardText";
+import CardLabelError from "../atoms/CardLabelError";
+import LocationSearch from "../atoms/LocationSearch";
+import SubmitBar from "../atoms/SubmitBar";
+import LinkButton from "../atoms/LinkButton";
+
+const LocationSearchCard = ({
+ header,
+ cardText,
+ nextText,
+ t,
+ skipAndContinueText,
+ forcedError,
+ skip,
+ onSave,
+ onChange,
+ position,
+ disabled,
+ cardBodyStyle = {},
+ isPTDefault,
+ PTdefaultcoord,
+ isPlaceRequired,
+ handleRemove,
+ Webview=false,
+ isPopUp=false,
+}) => {
+ let isDisabled = false || disabled;
+ const onLocationChange = (val, location) => {
+ isDisabled = val ? false : true;
+ onChange(val, location);
+ };
+
+ const onLocationChangewithPlace = (val, location, place) => {
+ isDisabled = val ? false : true;
+ onChange(val, location, place);
+ };
+
+ return (
+
+ {header}
+
+ {isPopUp &&
+
+
+
+
+
+
+ }
+ style={{width: "100px", display:"inline"}}
+ onClick={(e) => handleRemove()}
+ />}
+
+ {/* Click and hold to drop the pin to complaint location. If you are not
+ able to pin the location you can skip the continue for next step. */}
+ {cardText}
+
+
+
+ {forcedError && {t(forcedError)} }
+
+
+ {skip ? : null}
+
+ );
+};
+
+LocationSearchCard.propTypes = {
+ header: PropTypes.string,
+ cardText: PropTypes.string,
+ nextText: PropTypes.string,
+ skipAndContinueText: PropTypes.string,
+ skip: PropTypes.func,
+ onSave: PropTypes.func,
+ onChange: PropTypes.func,
+ position: PropTypes.any,
+ isPTDefault: PropTypes.any,
+ PTdefaultcoord: PropTypes.any,
+ isPlaceRequired: PropTypes.any,
+};
+
+LocationSearchCard.defaultProps = {
+ header: "",
+ cardText: "",
+ nextText: "",
+ skipAndContinueText: "",
+ skip: () => {},
+ onSave: null,
+ onChange: () => {},
+ position: undefined,
+ isPTDefault: false,
+ PTdefaultcoord: {},
+ isPlaceRequired: false,
+ handleRemove: () => {},
+ Webview:false,
+ isPopUp:false,
+};
+
+export default LocationSearchCard;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/LocationSearchCard.stories.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/LocationSearchCard.stories.js
new file mode 100644
index 0000000..9651779
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/LocationSearchCard.stories.js
@@ -0,0 +1,20 @@
+import React from "react";
+
+import LocationSearchCard from "./LocationSearchCard";
+
+export default {
+ title: "Molecule/LocationSearchCard",
+ component: LocationSearchCard,
+};
+
+const Template = (args) => ;
+
+export const Default = Template.bind({});
+
+Default.args = {
+ header: "Header",
+ cardText: "Card Text",
+ nextText: "Next Text",
+ skipAndContinueText: "Skip",
+ skip: true,
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/MultiUploadWrapper.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/MultiUploadWrapper.js
new file mode 100644
index 0000000..d0a4323
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/MultiUploadWrapper.js
@@ -0,0 +1,144 @@
+import React, { useEffect, useReducer, useState } from "react"
+import UploadFile from "../atoms/UploadFile"
+
+const displayError = ({ t, error, name }, customErrorMsg) => (
+
+ {customErrorMsg ? t(customErrorMsg) : t(error)}
+ {customErrorMsg ? '' : `${t('ES_COMMON_DOC_FILENAME')} : ${name} ...`}
+
+)
+
+const fileValidationStatus = (file, regex, maxSize, t) => {
+
+ const status = { valid: true, name: file?.name?.substring(0, 15), error: '' };
+ if (!file) return;
+
+ if (!regex.test(file.type) && (file.size / 1024 / 1024) > maxSize) {
+ status.valid = false; status.error = t(`NOT_SUPPORTED_FILE_TYPE_AND_FILE_SIZE_EXCEEDED_5MB`);
+ }
+
+ if (!regex.test(file.type)) {
+ status.valid = false; status.error = t(`NOT_SUPPORTED_FILE_TYPE`);
+ }
+
+ if ((file.size / 1024 / 1024) > maxSize) {
+ status.valid = false; status.error = t(`FILE_SIZE_EXCEEDED_5MB`);
+ }
+
+ return status;
+}
+const checkIfAllValidFiles = (files, regex, maxSize, t, maxFilesAllowed, state) => {
+ if (!files.length || !regex || !maxSize) return [{}, false];
+
+ // added another condition files.length > 0 , for when user uploads files more than maxFilesAllowed in one go the
+ const uploadedFiles = state.length + 1
+ if ( maxFilesAllowed && (uploadedFiles > maxFilesAllowed || files.length > maxFilesAllowed)) return [[{ valid: false, name: files[0]?.name?.substring(0, 15), error: t(`FILE_LIMIT_EXCEEDED`)}],true]
+
+ // Adding a check for fileSize > maxSize
+ // const maxSizeInBytes = maxSize * 1000000
+ // if(files?.some(file => file.size > maxSizeInBytes)){
+ // return [[{ valid: false, name: "", error: t(`FILE_SIZE_EXCEEDED_5MB`) }], true]
+ // }
+
+ const messages = [];
+ let isInValidGroup = false;
+ for (let file of files) {
+ const fileStatus = fileValidationStatus(file, regex, maxSize, t);
+ if (!fileStatus.valid) {
+ isInValidGroup = true;
+ }
+ messages.push(fileStatus);
+ }
+
+ return [messages, isInValidGroup];
+}
+
+// can use react hook form to set validations @neeraj-egov
+const MultiUploadWrapper = ({ t, module = "PGR", tenantId = Digit.ULBService.getStateId(), getFormState, requestSpecifcFileRemoval, extraStyleName = "", setuploadedstate = [], showHintBelow, hintText, allowedFileTypesRegex = /(.*?)(jpg|jpeg|webp|aif|png|image|pdf|msword|openxmlformats-officedocument|xls|xlsx|openxmlformats-officedocument|wordprocessingml|document|spreadsheetml|sheet|ms-excel)$/i, allowedMaxSizeInMB = 10, acceptFiles = "image/*, .jpg, .jpeg, .webp, .aif, .png, .image, .pdf, .msword, .openxmlformats-officedocument, .dxf, .xlsx, .xls, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", maxFilesAllowed, customClass="", customErrorMsg,containerStyles }) => {
+ const FILES_UPLOADED = "FILES_UPLOADED"
+ const TARGET_FILE_REMOVAL = "TARGET_FILE_REMOVAL"
+
+ const [fileErrors, setFileErrors] = useState([]);
+ const [enableButton, setEnableButton] = useState(true)
+
+ const uploadMultipleFiles = (state, payload) => {
+ const { files, fileStoreIds } = payload;
+ const filesData = Array.from(files)
+ const newUploads = filesData?.map((file, index) => [file.name, { file, fileStoreId: fileStoreIds[index] }])
+ return [...state, ...newUploads]
+ }
+
+ const removeFile = (state, payload) => {
+ const __indexOfItemToDelete = state.findIndex(e => e[1].fileStoreId.fileStoreId === payload.fileStoreId.fileStoreId)
+ const mutatedState = state.filter((e, index) => index !== __indexOfItemToDelete)
+ setFileErrors([])
+ return [...mutatedState]
+ }
+
+ const uploadReducer = (state, action) => {
+ switch (action.type) {
+ case FILES_UPLOADED:
+ return uploadMultipleFiles(state, action.payload)
+ case TARGET_FILE_REMOVAL:
+ return removeFile(state, action.payload)
+ default:
+ break;
+ }
+ }
+
+ const [state, dispatch] = useReducer(uploadReducer, [...setuploadedstate])
+
+ const onUploadMultipleFiles = async (e) => {
+ setEnableButton(false)
+ setFileErrors([])
+ const files = Array.from(e.target.files);
+
+ if (!files.length) return;
+ const [validationMsg, error] = checkIfAllValidFiles(files, allowedFileTypesRegex, allowedMaxSizeInMB, t, maxFilesAllowed, state);
+
+ if (!error) {
+ try {
+ const { data: { files: fileStoreIds } = {} } = await Digit.UploadServices.MultipleFilesStorage(module, e.target.files, tenantId)
+ setEnableButton(true)
+ return dispatch({ type: FILES_UPLOADED, payload: { files: e.target.files, fileStoreIds } })
+ } catch (err) {
+ setEnableButton(true)
+ }
+ } else {
+ setFileErrors(validationMsg)
+ setEnableButton(true)
+ }
+ }
+
+ useEffect(() => getFormState(state), [state])
+
+ useEffect(() => {
+ requestSpecifcFileRemoval ? dispatch({ type: TARGET_FILE_REMOVAL, payload: requestSpecifcFileRemoval }) : null
+ }, [requestSpecifcFileRemoval])
+
+ return (
+
+ onUploadMultipleFiles(e)}
+ removeTargetedFile={(fileDetailsData) => dispatch({ type: TARGET_FILE_REMOVAL, payload: fileDetailsData })}
+ uploadedFiles={state}
+ multiple={true}
+ showHintBelow={showHintBelow}
+ hintText={hintText}
+ extraStyleName={extraStyleName}
+ onDelete={() => {
+ setFileErrors([])
+ }}
+ accept={acceptFiles}
+ customClass={customClass}
+ enableButton={enableButton}
+ />
+
+ {fileErrors.length ? fileErrors.map(({ valid, name, type, size, error }) => (
+ valid ? null : displayError({ t, error, name }, customErrorMsg)
+ )) : null}
+
+
)
+}
+
+export default MultiUploadWrapper
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/OnGroundEventCard.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/OnGroundEventCard.js
new file mode 100644
index 0000000..317c79f
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/OnGroundEventCard.js
@@ -0,0 +1,28 @@
+import React from "react"
+import EventCalendarView from "../atoms/EventCalendarView"
+import { MapMarker, Clock } from "../atoms/svgindex"
+
+const OnGroundEventCard = ({onClick = () => null, name, id, eventDetails, onGroundEventMonth="MAR", onGroundEventDate="12 - 16", onGroundEventName="To the moon", onGroundEventLocation="Moon", onGroundEventTimeRange="10:00 am - 1:00 pm", eventCategory, showEventCatergory }) => {
+
+ const onEventCardClick = () => onClick(id)
+
+ return
+
+
+
{name}
+ {!showEventCatergory ?
+
+
{eventDetails?.address}
+
: null}
+ {!showEventCatergory ?
+
+
{onGroundEventTimeRange}
+
: null}
+ {showEventCatergory ?
: null}
+
+
+}
+
+export default OnGroundEventCard
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/PageBasedInput.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/PageBasedInput.js
new file mode 100644
index 0000000..0615e05
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/PageBasedInput.js
@@ -0,0 +1,19 @@
+import React from "react";
+import Card from "../atoms/Card";
+import SubmitBar from "../atoms/SubmitBar";
+
+const PageBasedInput = ({ children, texts, onSubmit }) => {
+ return (
+
+
+ {children}
+
+
+
+
+
+
+ );
+};
+
+export default PageBasedInput;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/PitDimension.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/PitDimension.js
new file mode 100644
index 0000000..733d555
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/PitDimension.js
@@ -0,0 +1,52 @@
+import React from "react";
+import TextInput from "../atoms/TextInput";
+import CardText from "../atoms/CardText";
+
+const DimentionInput = ({ name, value, onChange, disable }) => (
+
+);
+
+const PitDimension = ({ sanitationType, t, size = {}, handleChange, disable = false }) => {
+ return sanitationType?.dimension === "dd" ? (
+
+
+
+
+ {t("CS_FILE_PROPERTY_DIAMETER")}
+
+
+
x
+
+
+
+ {t("CS_FILE_PROPERTY_HEIGHT")}
+
+
+
+ ) : (
+
+
+
+
+ {t("CS_FILE_PROPERTY_LENGTH")}
+
+
+
x
+
+
+
+ {t("CS_FILE_PROPERTY_WIDTH")}
+
+
+
x
+
+
+
+ {t("CS_FILE_PROPERTY_HEIGHT")}
+
+
+
+ );
+};
+
+export default PitDimension;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/RadioOrSelect.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/RadioOrSelect.js
new file mode 100644
index 0000000..75e201c
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/RadioOrSelect.js
@@ -0,0 +1,55 @@
+import React from "react";
+import RadioButtons from "../atoms/RadioButtons";
+import Dropdown from "../atoms/Dropdown";
+
+const RadioOrSelect = ({
+ options,
+ onSelect,
+ optionKey,
+ selectedOption,
+ isMandatory,
+ t,
+ labelKey,
+ dropdownStyle = {},
+ isDependent = false,
+ disabled = false,
+ optionCardStyles,
+ isPTFlow=false,
+ isDropDown = false,
+ innerStyles = {},
+ inputStyle = {}
+}) => {
+ return (
+
+ {options?.length < 5 && !isDropDown ? (
+
+ ) : (
+
+ )}
+
+ );
+};
+
+export default RadioOrSelect;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/RatingCard.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/RatingCard.js
new file mode 100644
index 0000000..104a633
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/RatingCard.js
@@ -0,0 +1,108 @@
+import React, { useState } from "react";
+import { useForm } from "react-hook-form";
+import PropTypes from "prop-types";
+
+import TextArea from "../atoms/TextArea";
+import CardLabel from "../atoms/CardLabel";
+import Rating from "../atoms/Rating";
+import CheckBox from "../atoms/CheckBox";
+import Card from "../atoms/Card";
+import CardHeader from "../atoms/CardHeader";
+import SubmitBar from "../atoms/SubmitBar";
+import RadioButtons from "../atoms/RadioButtons";
+import Dropdown from "../atoms/Dropdown";
+
+const RatingCard = ({ config, onSelect, t }) => {
+ const { register, watch, handleSubmit } = useForm();
+ const [comments, setComments] = useState("");
+ const [rating, setRating] = useState(0);
+
+ const onSubmit = (data) => {
+ data.rating = rating;
+ onSelect(data);
+ };
+
+ const feedback = (e, ref, index) => {
+ setRating(index);
+ };
+
+ const segments = config.inputs?.map((input, index) => {
+ if (input.type === "rate") {
+ return (
+
+ {t(input.label)}
+ {input?.error}
+ feedback(e, ref, i)} />
+
+ );
+ }
+
+ if (input.type === "checkbox") {
+ return (
+
+ {t(input.label)}
+ {input.checkLabels &&
+ input.checkLabels.map((label, index) => )}
+
+ );
+ }
+
+ if (input.type === "radio") {
+ return (
+
+ {t(input.label)}
+
+
+ );
+ }
+
+ if (input.type === "textarea") {
+ return (
+
+ {t(input.label)}
+
+
+ );
+ }
+
+ if (input.type === "dropDown") {
+ return (
+
+ {t(input.label)}
+
+
+ );
+ }
+ });
+ return (
+
+ );
+};
+
+RatingCard.propTypes = {
+ config: PropTypes.object,
+ onSubmit: PropTypes.func,
+ t: PropTypes.func,
+};
+
+RatingCard.defaultProps = {
+ config: {},
+ onSubmit: undefined,
+ t: (value) => value,
+};
+
+export default RatingCard;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/RatingCard.stories.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/RatingCard.stories.js
new file mode 100644
index 0000000..ee8a52c
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/RatingCard.stories.js
@@ -0,0 +1,37 @@
+import React from "react";
+
+import RatingCard from "./RatingCard";
+
+export default {
+ title: "Molecule/RatingCard",
+ component: RatingCard,
+};
+
+const Template = (args) => ;
+
+export const InputTypeRate = Template.bind({});
+
+InputTypeRate.args = {
+ config: {
+ inputs: [{ type: "rate", label: "Label" }],
+ texts: { header: "Header", submitBarLabel: "Submit" },
+ },
+};
+
+export const InputTypeCheckbox = Template.bind({});
+
+InputTypeCheckbox.args = {
+ config: {
+ inputs: [{ type: "checkbox", label: "Label", checkLabels: ["Check Label"] }],
+ texts: { header: "Header", submitBarLabel: "Submit" },
+ },
+};
+
+export const InputTypeTextArea = Template.bind({});
+
+InputTypeTextArea.args = {
+ config: {
+ inputs: [{ type: "textarea", label: "Label", name: "name" }],
+ texts: { header: "Header", submitBarLabel: "Submit" },
+ },
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/RenderFormFields.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/RenderFormFields.js
new file mode 100644
index 0000000..1cdec6a
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/RenderFormFields.js
@@ -0,0 +1,389 @@
+import React from 'react';
+import { useTranslation } from "react-i18next";
+import { Controller } from "react-hook-form";
+import CardLabelError from "../atoms/CardLabelError";
+import LabelFieldPair from '../atoms/LabelFieldPair';
+import CardLabel from "../atoms/CardLabel";
+import TextInput from "../atoms/TextInput";
+import TextArea from "../atoms/TextArea";
+import CustomDropdown from './CustomDropdown';
+import MobileNumber from '../atoms/MobileNumber';
+import DateRangeNew from './DateRangeNew';
+import MultiUploadWrapper from "./MultiUploadWrapper";
+import MultiSelectDropdown from '../atoms/MultiSelectDropdown';
+import LocationDropdownWrapper from './LocationDropdownWrapper';
+import WorkflowStatusFilter from './WorkflowStatusFilter';
+import ApiDropdown from './ApiDropdown';
+import ApiCheckboxes from './ApiCheckboxes';
+const RenderFormFields = ({data,...props}) => {
+ const { t } = useTranslation();
+ const { fields, control, formData, errors, register, setValue, getValues, setError, clearErrors, apiDetails} = props
+
+ const fieldSelector = (type, populators, isMandatory, disable = false, component, config) => {
+ const Component = typeof component === "string" ? Digit.ComponentRegistryService.getComponent(component) : component;
+ let customValidations = config?.additionalValidation ? Digit?.Customizations?.[apiDetails?.masterName]?.[apiDetails?.moduleName]?.additionalValidations(config?.additionalValidation?.type, formData, config?.additionalValidation?.keys) : null
+ const customRules = customValidations ? { validate: customValidations} : {}
+ switch (type) {
+ case "date":
+ case "text":
+ case "number":
+ case "password":
+ case "time":
+ return (
+ (
+
+ )}
+ name={populators.name}
+ rules={{required: isMandatory, ...populators.validation, ...customRules }}
+ control={control}
+ />
+ );
+
+ case "textarea":
+ return (
+ (
+
+ )}
+ name={populators.name}
+ rules={{ required: isMandatory, ...populators.validation }}
+ control={control}
+ />
+ );
+ case "mobileNumber":
+ return (
+ (
+
+ )}
+ defaultValue={populators.defaultValue}
+ name={populators?.name}
+ rules={{ required: isMandatory, ...populators.validation }}
+ control={control}
+ />
+ );
+ case "multiupload":
+ return (
+ {
+ function getFileStoreData(filesData) {
+ const numberOfFiles = filesData.length;
+ let finalDocumentData = [];
+ if (numberOfFiles > 0) {
+ filesData.forEach((value) => {
+ finalDocumentData.push({
+ fileName: value?.[0],
+ fileStoreId: value?.[1]?.fileStoreId?.fileStoreId,
+ documentType: value?.[1]?.file?.type,
+ });
+ });
+ }
+ onChange(numberOfFiles>0?filesData:[]);
+ }
+ return (
+
+ );
+ }}
+ />
+ );
+ case "custom":
+ return (
+ populators.component({ ...props, setValue }, populators.customProps)}
+ defaultValue={populators.defaultValue}
+ name={populators?.name}
+ control={control}
+ />
+ );
+
+ case "radio":
+ case "dropdown":
+ return (
+ (
+
+ )}
+ rules={{ required: isMandatory, ...populators.validation }}
+ defaultValue={formData?.[populators.name]}
+ name={populators?.name}
+ control={control}
+ />
+ );
+
+ case "multiselectdropdown":
+ return (
+ {
+ return (
+
+ {
+ props.onChange(e?.map(row=>{return row?.[1] ? row[1] : null}).filter(e=>e))
+ }}
+ selected={props?.value}
+ defaultLabel={t(populators?.defaultText)}
+ defaultUnit={t(populators?.selectedText)}
+ config={populators}
+ />
+
+ );
+ }}
+ />
+ );
+
+ case "locationdropdown":
+ return (
+ {
+ return (
+
+
+
+ );
+ }}
+ />
+ );
+
+ case "apidropdown":
+ return (
+ {
+ return (
+
+ );
+ }}
+ />
+ );
+
+ case "apicheckboxes":
+ return (
+ {
+ return (
+
+ );
+ }}
+ />
+ );
+ case "workflowstatesfilter":
+ return (
+ {
+ return (
+
+
+
+ );
+ }}
+ />
+ );
+ case "dateRange":
+ return (
+ (
+
+ )}
+ rules={{ required: isMandatory, ...populators.validation }}
+ defaultValue={formData?.[populators.name]}
+ name={populators?.name}
+ control={control}
+ />
+ );
+
+ case "component":
+ return (
+ (
+
+ )}
+ name={config?.key}
+ control={control}
+ />
+ );
+
+ default:
+ return populators?.dependency !== false ? populators : null;
+ }
+ };
+
+ return (
+
+ {fields?.map((item, index) => {
+ return (
+
+ { item.label && (
+
+ {t(item.label)}{ item?.isMandatory ? " * " : null }
+ )
+ }
+
+ { fieldSelector(item.type, item.populators, item.isMandatory, item?.disable, item?.component, item) }
+
+ { item?.populators?.name && errors && errors[item?.populators?.name] && Object.keys(errors[item?.populators?.name]).length ? (
+
+ {t(item?.populators?.error)}
+ ) : null
+ }
+
+ )
+ })}
+
+ )
+}
+
+export default RenderFormFields;
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/SearchAction.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/SearchAction.js
new file mode 100644
index 0000000..b11f8b8
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/SearchAction.js
@@ -0,0 +1,10 @@
+import React from "react";
+import { SearchIconSvg,FilterSvg,FilterIcon } from "../atoms/svgindex";
+
+const SearchAction = ({ text, handleActionClick }) => (
+
+ {text}
+
+);
+
+export default SearchAction;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/SearchForm.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/SearchForm.js
new file mode 100644
index 0000000..9bb7fef
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/SearchForm.js
@@ -0,0 +1,44 @@
+import React from "react";
+
+export const SearchField = ({ children, className }) => {
+ const isMobile = window.Digit.Utils.browser.isMobile();
+ const isEnabledCommonModules =
+ window.location.href.includes("/obps/") ||
+ window.location.href.includes("/noc/") ||
+ window.location.href.includes("/ws/water/bill-amendment/inbox") ||
+ window.location.href.includes("/ws/sewerage/bill-amendment/inbox");
+
+ const disbaleModules = window.location.href.includes("obps/search") || window.location.href.includes("noc/search");
+ if (isEnabledCommonModules && !isMobile && !disbaleModules) {
+ return (
+
+ {children}
+
+ );
+ }
+ return {children}
;
+};
+
+export const SearchForm = ({ children, onSubmit, handleSubmit, id, className = "" }) => {
+ const isMobile = window.Digit.Utils.browser.isMobile();
+ const isEnabledCommonModules =
+ window.location.href.includes("/obps/") ||
+ window.location.href.includes("/noc/") ||
+ window.location.href.includes("/ws/water/bill-amendment/inbox") ||
+ window.location.href.includes("/ws/sewerage/bill-amendment/inbox");
+
+ const disbaleModules = window.location.href.includes("obps/search") || window.location.href.includes("noc/search");
+
+ if (isEnabledCommonModules && !isMobile && !disbaleModules) {
+ return (
+
+ );
+ }
+ return (
+
+ );
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/SearchOnRadioButtons.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/SearchOnRadioButtons.js
new file mode 100644
index 0000000..318dd4b
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/SearchOnRadioButtons.js
@@ -0,0 +1,27 @@
+import React, { useReducer } from "react"
+import RadioButtons from "../atoms/RadioButtons"
+import TextInput from "../atoms/TextInput"
+import { SearchIconSvg } from "../atoms/svgindex"
+
+const SearchOnRadioButtons = ({options,optionsKey,additionalWrapperClass,onSelect,selectedOption, SignatureImage = () => , onSearchQueryChange, SearchQueryValue, placeholder}) => {
+
+ function optionsReducer(state, action){
+ switch (action.type){
+ case "filter":
+ return action.options.filter(i => i[optionsKey].toUpperCase().includes(action.payload.toUpperCase()))
+ }
+ }
+
+ const [ filteredOptions, optionsDispatch ] = useReducer(optionsReducer, options)
+
+ function defaultSearchQueryChange(e){
+ optionsDispatch({type: "filter", payload: e.target.value, options})
+ }
+
+ return
+ } onChange={onSearchQueryChange || defaultSearchQueryChange} value={SearchQueryValue} placeholder={placeholder} />
+
+
+}
+
+export default SearchOnRadioButtons
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/SortAction.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/SortAction.js
new file mode 100644
index 0000000..3584776
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/SortAction.js
@@ -0,0 +1,12 @@
+import React from "react";
+import { SortSvg } from "../atoms/svgindex";
+import RoundedLabel from "../atoms/RoundedLabel";
+
+const SortAction = ({ text, handleActionClick, ...props }) => (
+
+
+ {text}
+
+);
+
+export default SortAction;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/TextInputCard.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/TextInputCard.js
new file mode 100644
index 0000000..0e31686
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/TextInputCard.js
@@ -0,0 +1,58 @@
+import React from "react";
+import { useTranslation } from "react-i18next";
+import PropTypes from "prop-types";
+
+import Card from "../atoms/Card";
+import CardHeader from "../atoms/CardHeader";
+import CardText from "../atoms/CardText";
+import SubmitBar from "../atoms/SubmitBar";
+import LinkButton from "../atoms/LinkButton";
+import CardSubHeader from "../atoms/CardSubHeader";
+import CardLabel from "../atoms/CardLabel";
+import TextInput from "../atoms/TextInput";
+
+const TextInputCard = ({ header, subHeader, cardText, cardLabel, nextText, skipAndContinueText, skip, onSave, onSkip, textInput }) => {
+ return (
+
+ {subHeader}
+ {header}
+
+ {/* If you know the pincode of the complaint address, provide below. It will
+ help us identify complaint location easily or you can skip and continue */}
+ {cardText}
+
+ {cardLabel}
+
+
+ {skip ? : null}
+
+ );
+};
+
+TextInputCard.propTypes = {
+ header: PropTypes.string,
+ subHeader: PropTypes.string,
+ cardText: PropTypes.string,
+ cardLabel: PropTypes.string,
+ nextText: PropTypes.string,
+ skipAndContinueText: PropTypes.string,
+ skip: PropTypes.bool,
+ onSave: PropTypes.func,
+ onSkip: PropTypes.func,
+ textInput: PropTypes.string,
+};
+
+TextInputCard.defaultProps = {
+ header: "",
+ subHeader: "",
+ cardText: "",
+ cardLabel: "",
+ nextText: "",
+ skipAndContinueText: "",
+ skip: true,
+ onSave: undefined,
+ onSkip: undefined,
+ textInput: "",
+};
+
+export default TextInputCard;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/TextInputCard.stories.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/TextInputCard.stories.js
new file mode 100644
index 0000000..4ca37c0
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/TextInputCard.stories.js
@@ -0,0 +1,23 @@
+import React from "react";
+
+import TextInputCard from "./TextInputCard";
+
+export default {
+ title: "Molecule/TextInputCard",
+ component: TextInputCard,
+};
+
+const Template = (args) => ;
+
+export const Default = Template.bind({});
+
+Default.args = {
+ header: "Header",
+ subHeader: "Sub Header",
+ cardText: "Card Text",
+ cardLabel: "Card Label",
+ nextText: "Next",
+ skipAndContinueText: "Skip",
+ skip: true,
+ textInput: "Text Input",
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/TypeSelectCard.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/TypeSelectCard.js
new file mode 100644
index 0000000..9f3d105
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/TypeSelectCard.js
@@ -0,0 +1,50 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+import Card from "../atoms/Card";
+import CardCaption from "../atoms/CardCaption";
+import CardHeader from "../atoms/CardHeader";
+import CardText from "../atoms/CardText";
+import RadioButtons from "../atoms/RadioButtons";
+import SubmitBar from "../atoms/SubmitBar";
+
+const TypeSelectCard = ({
+ t,
+ headerCaption,
+ header,
+ cardText,
+ disabled = false,
+ submitBarLabel,
+ selectedOption,
+ menu,
+ optionsKey,
+ selected,
+ onSave,
+}) => {
+ return (
+
+ {t(headerCaption)}
+ {t(header)}
+ {t(cardText)}
+ {menu ? : null}
+
+
+ );
+};
+
+TypeSelectCard.propTypes = {
+ headerCaption: PropTypes.string,
+ header: PropTypes.string,
+ cardText: PropTypes.string,
+ submitBarLabel: PropTypes.string,
+ selectedOption: PropTypes.any,
+ menu: PropTypes.any,
+ optionsKey: PropTypes.string,
+ selected: PropTypes.func,
+ onSave: PropTypes.func,
+ t: PropTypes.func,
+};
+
+TypeSelectCard.defaultProps = {};
+
+export default TypeSelectCard;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/TypeSelectCard.stories.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/TypeSelectCard.stories.js
new file mode 100644
index 0000000..dce755e
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/TypeSelectCard.stories.js
@@ -0,0 +1,22 @@
+import React from "react";
+
+import TypeSelectCard from "./TypeSelectCard";
+
+export default {
+ title: "Molecule/TypeSelectCard",
+ component: TypeSelectCard,
+};
+
+const Template = (args) => ;
+
+export const Default = Template.bind({});
+
+Default.args = {
+ headerCaption: "Header Caption",
+ header: "Header",
+ cardText: "Card Text",
+ submitBarLabel: "Submit",
+ selectedOption: "first option",
+ menu: ["first option", "second option", "third option"],
+ optionsKey: "",
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/UploadPitPhoto.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/UploadPitPhoto.js
new file mode 100644
index 0000000..a533ce9
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/UploadPitPhoto.js
@@ -0,0 +1,146 @@
+import React, { useCallback, useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+import UploadImages from "../atoms/UploadImages";
+
+const UploadPitPhoto = (props) => {
+ const { t } = useTranslation();
+
+ const [image, setImage] = useState(null);
+ const [uploadedImagesThumbs, setUploadedImagesThumbs] = useState(null);
+ const [uploadedImagesIds, setUploadedImagesIds] = useState(props.uploadedImages);
+
+ const [rerender, setRerender] = useState(1);
+ const [imageFile, setImageFile] = useState(null);
+ const [isDeleting, setIsDeleting] = useState(false);
+ const [error, setError] = useState("");
+
+ useEffect(() => {
+ if (image) {
+ uploadImage();
+ }
+ }, [image]);
+
+ useEffect(() => {
+
+ (async () => {
+ if (uploadedImagesIds !== null) {
+ await submit();
+ setRerender(rerender + 1);
+ props.onPhotoChange(uploadedImagesIds);
+ }
+ })();
+
+ }, [uploadedImagesIds]);
+
+ useEffect(() => {
+ if (imageFile && imageFile.size > 2097152) {
+ setError("File is too large");
+ } else {
+ setImage(imageFile);
+ }
+ }, [imageFile]);
+
+ const addUploadedImageIds = useCallback(
+ (imageIdData) => {
+ if (uploadedImagesIds === null) {
+ var arr = [];
+ } else {
+ arr = uploadedImagesIds;
+ }
+ return [...arr, imageIdData.data.files[0].fileStoreId];
+ },
+ [uploadedImagesIds]
+ );
+
+ function getImage(e) {
+ setError(null);
+ setImageFile(e.target.files[0]);
+ }
+
+ const uploadImage = useCallback(async () => {
+ if (uploadedImagesIds === null || uploadedImagesIds.length < 3) {
+ const response = await Digit.UploadServices.Filestorage("FSM", image, props.tenantId);
+ setUploadedImagesIds(addUploadedImageIds(response));
+ }
+ }, [addUploadedImageIds, image]);
+
+ function addImageThumbnails(thumbnailsData) {
+ var keys = Object.keys(thumbnailsData.data);
+ var index = keys.findIndex((key) => key === "fileStoreIds");
+ if (index > -1) {
+ keys.splice(index, 1);
+ }
+ var thumbnails = [];
+ // if (uploadedImagesThumbs !== null) {
+ // thumbnails = uploadedImagesThumbs.length > 0 ? uploadedImagesThumbs.filter((thumb) => thumb.key !== keys[0]) : [];
+ // }
+
+ const newThumbnails = keys.map((key) => {
+ return { image: thumbnailsData.data[key].split(",")[2], key };
+ });
+
+ setUploadedImagesThumbs([...thumbnails, ...newThumbnails]);
+ }
+
+ const submit = useCallback(async () => {
+ if (uploadedImagesIds !== null && uploadedImagesIds.length > 0) {
+ const res = await Digit.UploadServices.Filefetch(uploadedImagesIds, props.tenantId);
+ addImageThumbnails(res);
+ }
+ }, [uploadedImagesIds]);
+
+ function deleteImage(img) {
+ setIsDeleting(true);
+ var deleteImageKey = uploadedImagesThumbs.filter((o, index) => o.image === img);
+
+ var uploadedthumbs = uploadedImagesThumbs;
+ var newThumbsList = uploadedthumbs.filter((thumbs) => thumbs != deleteImageKey[0]);
+
+ var newUploadedImagesIds = uploadedImagesIds.filter((key) => key !== deleteImageKey[0].key);
+ setUploadedImagesThumbs(newThumbsList);
+ setUploadedImagesIds(newUploadedImagesIds);
+ Digit.SessionStorage.set("PGR_CREATE_IMAGES", newUploadedImagesIds);
+ }
+
+ const handleUpload = (event) => {
+ if (uploadedImagesIds === null || uploadedImagesIds.length < 3) {
+ hiddenFileInput.current.click();
+ }
+ }
+ const hiddenFileInput = React.useRef(null);
+
+ // Upload photo from storage by clicking the button
+ // and can view preview in pop down
+ return (
+
+
+ o.image) : []} />
+
+
+
+ {t("UPLOAD_PIT_PHOTO")}
+
+
+
+ {error &&
setError(null)} />}
+
+ );
+};
+
+export default UploadPitPhoto
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/WorkflowModal.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/WorkflowModal.js
new file mode 100644
index 0000000..334ae02
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/WorkflowModal.js
@@ -0,0 +1,52 @@
+import React from 'react'
+import Modal from '../hoc/Modal'
+import { FormComposer } from '../hoc/FormComposer'
+import { useTranslation } from 'react-i18next'
+
+
+const Heading = (props) => {
+ return {props.label} ;
+};
+
+const Close = () => (
+
+
+
+
+);
+
+const CloseBtn = (props) => {
+ return (
+
+
+
+ );
+};
+
+const WorkflowModal = ({ config, onSubmit, closeModal,popupModuleActionBarStyles,popupModuleMianStyles }) => {
+ const { t } = useTranslation()
+
+ return }
+ headerBarEnd={ }
+ actionCancelLabel={t(config.label.cancel)}
+ actionCancelOnSubmit={closeModal}
+ actionSaveLabel={t(config.label.submit)}
+ actionSaveOnSubmit={() => { }}
+ formId="modal-action"
+ popupModuleActionBarStyles={popupModuleActionBarStyles}
+ popupModuleMianStyles={popupModuleMianStyles}
+ >
+
+
+}
+
+export default WorkflowModal
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/WorkflowStatusFilter.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/WorkflowStatusFilter.js
new file mode 100644
index 0000000..f202f72
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/molecules/WorkflowStatusFilter.js
@@ -0,0 +1,57 @@
+import React, { Fragment, useEffect, useState } from "react";
+import CheckBox from "../atoms/CheckBox";
+import { Loader } from "../atoms/Loader";
+import CardLabel from "../atoms/CardLabel";
+
+const WorkflowStatusFilter = ({ props, t, populators, formData, inboxResponse }) => {
+ //from inbox response get the statusMap and show the relevant statuses
+ //here need to filter these options based on logged in user(and test based on single roles in every inbox)(new requirement from vasanth)
+
+ const [statusMap, setStatusMap] = useState(null);
+ useEffect(() => {
+ if (inboxResponse) {
+ setStatusMap(
+ inboxResponse.statusMap?.map((row) => {
+ return {
+ uuid: row.statusid,
+ state: row.state || row.applicationstatus,
+ businessService: row?.businessservice,
+ count: row?.count,
+ };
+ })
+ );
+ }
+ }, [inboxResponse]);
+
+ if (!statusMap && !inboxResponse) return ;
+
+ return (
+ <>
+ {statusMap && statusMap.length > 0 && populators?.componentLabel && (
+
+ {t(populators?.componentLabel)}
+ {populators?.isMandatory ? " * " : null}
+
+ )}
+ {statusMap?.map((row) => {
+ return (
+ {
+ const obj = {
+ ...props.value,
+ [e.target.value]: e.target.checked,
+ };
+ props.onChange(obj);
+ // TODO:: set formData in such a way that it's an array of objects instead of array of ids so that we are able to show removable crumbs for this
+ }}
+ value={row.uuid}
+ checked={formData?.[populators.name]?.[row.uuid] ? true : false}
+ label={`${t(Digit.Utils.locale.getTransformedLocale(`${populators.labelPrefix}${row?.businessService}_STATE_${row?.state}`))} (${row?.count || 0})`}
+ />
+ );
+ })}
+ >
+ );
+};
+
+export default WorkflowStatusFilter;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Button.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Button.js
new file mode 100644
index 0000000..1bde42d
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Button.js
@@ -0,0 +1,50 @@
+import React from "react";
+import PropTypes from "prop-types";
+import "./button.css";
+
+/**
+ * Primary UI component for user interaction
+ */
+export const Button = ({ primary, backgroundColor, size, label, ...props }) => {
+ const mode = primary ? "storybook-button--primary" : "storybook-button--secondary";
+ return (
+
+ {label}
+
+ );
+};
+
+Button.propTypes = {
+ /**
+ * Is this the principal call to action on the page?
+ */
+ primary: PropTypes.bool,
+ /**
+ * What background color to use
+ */
+ backgroundColor: PropTypes.string,
+ /**
+ * How large should the button be?
+ */
+ size: PropTypes.oneOf(["small", "medium", "large"]),
+ /**
+ * Button contents
+ */
+ label: PropTypes.string.isRequired,
+ /**
+ * Optional click handler
+ */
+ onClick: PropTypes.func,
+};
+
+Button.defaultProps = {
+ backgroundColor: null,
+ primary: false,
+ size: "medium",
+ onClick: undefined,
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Button.stories.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Button.stories.js
new file mode 100644
index 0000000..44d0936
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Button.stories.js
@@ -0,0 +1,36 @@
+import React from "react";
+
+import { Button } from "./Button";
+
+export default {
+ title: "Example/Button",
+ component: Button,
+ argTypes: {
+ backgroundColor: { control: "color" },
+ },
+};
+
+const Template = (args) => ;
+
+export const Primary = Template.bind({});
+Primary.args = {
+ primary: true,
+ label: "Button",
+};
+
+export const Secondary = Template.bind({});
+Secondary.args = {
+ label: "Button",
+};
+
+export const Large = Template.bind({});
+Large.args = {
+ size: "large",
+ label: "Button",
+};
+
+export const Small = Template.bind({});
+Small.args = {
+ size: "small",
+ label: "Button",
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Header.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Header.js
new file mode 100644
index 0000000..69feb95
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Header.js
@@ -0,0 +1,43 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+import { Button } from "./Button";
+import "./header.css";
+
+export const Header = ({ user, onLogin, onLogout, onCreateAccount }) => (
+
+);
+
+Header.propTypes = {
+ user: PropTypes.shape({}),
+ onLogin: PropTypes.func.isRequired,
+ onLogout: PropTypes.func.isRequired,
+ onCreateAccount: PropTypes.func.isRequired,
+};
+
+Header.defaultProps = {
+ user: null,
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Header.stories.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Header.stories.js
new file mode 100644
index 0000000..5326d2a
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Header.stories.js
@@ -0,0 +1,18 @@
+import React from "react";
+
+import { Header } from "./Header";
+
+export default {
+ title: "Example/Header",
+ component: Header,
+};
+
+const Template = (args) => ;
+
+export const LoggedIn = Template.bind({});
+LoggedIn.args = {
+ user: {},
+};
+
+export const LoggedOut = Template.bind({});
+LoggedOut.args = {};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Introduction.stories.mdx b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Introduction.stories.mdx
new file mode 100644
index 0000000..f6ffe1f
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Introduction.stories.mdx
@@ -0,0 +1,207 @@
+import { Meta } from '@storybook/addon-docs/blocks';
+import Code from './assets/code-brackets.svg';
+import Colors from './assets/colors.svg';
+import Comments from './assets/comments.svg';
+import Direction from './assets/direction.svg';
+import Flow from './assets/flow.svg';
+import Plugin from './assets/plugin.svg';
+import Repo from './assets/repo.svg';
+import StackAlt from './assets/stackalt.svg';
+
+
+
+
+
+# Welcome to Storybook
+
+Storybook helps you build UI components in isolation from your app's business logic, data, and context.
+That makes it easy to develop hard-to-reach states. Save these UI states as **stories** to revisit during development, testing, or QA.
+
+Browse example stories now by navigating to them in the sidebar.
+View their code in the `src/stories` directory to learn how they work.
+We recommend building UIs with a [**component-driven**](https://componentdriven.org) process starting with atomic components and ending with pages.
+
+Configure
+
+
+
+Learn
+
+
+
+
+ Tip Edit the Markdown in{' '}
+ src/stories/Introduction.stories.mdx
+
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Page.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Page.js
new file mode 100644
index 0000000..8a72b86
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Page.js
@@ -0,0 +1,64 @@
+import React from "react";
+import PropTypes from "prop-types";
+
+import { Header } from "./Header";
+import "./page.css";
+
+export const Page = ({ user, onLogin, onLogout, onCreateAccount }) => (
+
+
+
+
+ Pages in Storybook
+
+ We recommend building UIs with a{" "}
+
+ component-driven
+ {" "}
+ process starting with atomic components and ending with pages.
+
+
+ Render pages with mock data. This makes it easy to build and review page states without needing to navigate to them in your app. Here are some
+ handy patterns for managing page data in Storybook:
+
+
+ Use a higher-level connected component. Storybook helps you compose such data from the "args" of child component stories
+ Assemble data in the page component from your services. You can mock these services out using Storybook.
+
+
+ Get a guided tutorial on component-driven development at{" "}
+
+ Learn Storybook
+
+ . Read more in the{" "}
+
+ docs
+
+ .
+
+
+
Tip Adjust the width of the canvas with the{" "}
+
+
+
+
+
+ Viewports addon in the toolbar
+
+
+
+);
+Page.propTypes = {
+ user: PropTypes.shape({}),
+ onLogin: PropTypes.func.isRequired,
+ onLogout: PropTypes.func.isRequired,
+ onCreateAccount: PropTypes.func.isRequired,
+};
+
+Page.defaultProps = {
+ user: null,
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Page.stories.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Page.stories.js
new file mode 100644
index 0000000..cdb90c4
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/Page.stories.js
@@ -0,0 +1,21 @@
+import React from "react";
+
+import { Page } from "./Page";
+import * as HeaderStories from "./Header.stories";
+
+export default {
+ title: "Example/Page",
+ component: Page,
+};
+
+const Template = (args) => ;
+
+export const LoggedIn = Template.bind({});
+LoggedIn.args = {
+ ...HeaderStories.LoggedIn.args,
+};
+
+export const LoggedOut = Template.bind({});
+LoggedOut.args = {
+ ...HeaderStories.LoggedOut.args,
+};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/code-brackets.svg b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/code-brackets.svg
new file mode 100644
index 0000000..73de947
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/code-brackets.svg
@@ -0,0 +1 @@
+illustration/code-brackets
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/colors.svg b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/colors.svg
new file mode 100644
index 0000000..17d58d5
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/colors.svg
@@ -0,0 +1 @@
+illustration/colors
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/comments.svg b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/comments.svg
new file mode 100644
index 0000000..6493a13
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/comments.svg
@@ -0,0 +1 @@
+illustration/comments
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/direction.svg b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/direction.svg
new file mode 100644
index 0000000..65676ac
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/direction.svg
@@ -0,0 +1 @@
+illustration/direction
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/flow.svg b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/flow.svg
new file mode 100644
index 0000000..8ac27db
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/flow.svg
@@ -0,0 +1 @@
+illustration/flow
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/plugin.svg b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/plugin.svg
new file mode 100644
index 0000000..29e5c69
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/plugin.svg
@@ -0,0 +1 @@
+illustration/plugin
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/repo.svg b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/repo.svg
new file mode 100644
index 0000000..f386ee9
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/repo.svg
@@ -0,0 +1 @@
+illustration/repo
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/stackalt.svg b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/stackalt.svg
new file mode 100644
index 0000000..9b7ad27
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/assets/stackalt.svg
@@ -0,0 +1 @@
+illustration/stackalt
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/button.css b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/button.css
new file mode 100644
index 0000000..663afa4
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/button.css
@@ -0,0 +1,30 @@
+.storybook-button {
+ font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: 700;
+ border: 0;
+ border-radius: 3em;
+ cursor: pointer;
+ display: inline-block;
+ line-height: 1;
+}
+.storybook-button--primary {
+ color: white;
+ background-color: #1ea7fd;
+}
+.storybook-button--secondary {
+ color: #333;
+ background-color: transparent;
+ box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
+}
+.storybook-button--small {
+ font-size: 12px;
+ padding: 10px 16px;
+}
+.storybook-button--medium {
+ font-size: 14px;
+ padding: 11px 20px;
+}
+.storybook-button--large {
+ font-size: 16px;
+ padding: 12px 24px;
+}
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/header.css b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/header.css
new file mode 100644
index 0000000..7508bb2
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/header.css
@@ -0,0 +1,26 @@
+.wrapper {
+ font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+ padding: 15px 20px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+svg {
+ display: inline-block;
+ vertical-align: top;
+}
+
+h1 {
+ font-weight: 900;
+ font-size: 20px;
+ line-height: 1;
+ margin: 6px 0 6px 10px;
+ display: inline-block;
+ vertical-align: top;
+}
+
+button + button {
+ margin-left: 10px;
+}
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/page.css b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/page.css
new file mode 100644
index 0000000..857f0d4
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/stories/page.css
@@ -0,0 +1,69 @@
+section {
+ font-family: "Nunito Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 14px;
+ line-height: 24px;
+ padding: 48px 20px;
+ margin: 0 auto;
+ max-width: 600px;
+ color: #333;
+}
+
+h2 {
+ font-weight: 900;
+ font-size: 32px;
+ line-height: 1;
+ margin: 0 0 4px;
+ display: inline-block;
+ vertical-align: top;
+}
+
+p {
+ margin: 1em 0;
+}
+
+a {
+ text-decoration: none;
+ color: #1ea7fd;
+}
+
+ul {
+ padding-left: 30px;
+ margin: 1em 0;
+}
+
+li {
+ margin-bottom: 8px;
+}
+
+.tip {
+ display: inline-block;
+ border-radius: 1em;
+ font-size: 11px;
+ line-height: 12px;
+ font-weight: 700;
+ background: #e7fdd8;
+ color: #66bf3c;
+ padding: 4px 12px;
+ margin-right: 10px;
+ vertical-align: top;
+}
+
+.tip-wrapper {
+ font-size: 13px;
+ line-height: 20px;
+ margin-top: 40px;
+ margin-bottom: 40px;
+}
+
+.tip-wrapper svg {
+ display: inline-block;
+ height: 12px;
+ width: 12px;
+ margin-right: 4px;
+ vertical-align: top;
+ margin-top: 3px;
+}
+
+.tip-wrapper svg path {
+ fill: #1ea7fd;
+}
diff --git a/frontend/micro-ui/web/micro-ui-internals/publish.sh b/frontend/micro-ui/web/micro-ui-internals/publish.sh
new file mode 100644
index 0000000..00514ce
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/publish.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+BASEDIR="$(cd "$(dirname "$0")" && pwd)"
+
+msg() {
+ echo -e "\n\n\033[32;32m$1\033[0m"
+}
+
+# msg "Pre-building all packages"
+# yarn build
+# sleep 5
+
+msg "Building and publishing css"
+cd "$BASEDIR/packages/css" && rm -rf dist && yarn && npm publish --access public
+
+# msg "Building and publishing svg components"
+cd "$BASEDIR/packages/svg-components" && rm -rf dist && yarn && npm publish --access public
+
+msg "Building and publishing react-components"
+cd "$BASEDIR/packages/react-components" && rm -rf dist && yarn && npm publish --access public
+
+msg "Building and publishing libraries"
+cd "$BASEDIR/packages/libraries" && rm -rf dist && yarn && npm publish --access public
+
+
+
+# sleep 10
+# msg "Updating dependencies"
+# cd "$BASEDIR" && yarn upgrade -S @egovernments
+# sleep 5
+
+# msg "Building and publishing Engagement module"
+cd "$BASEDIR/packages/modules/engagement" && rm -rf dist && yarn&& npm publish --access public
+
+# msg "Building and publishing hrms module"
+cd "$BASEDIR/packages/modules/hrms" && rm -rf dist && yarn&& npm publish --access public
+
+# msg "Building and publishing DSS module"
+cd "$BASEDIR/packages/modules/dss" && rm -rf dist && yarn&& npm publish --access public
+
+# msg "Building and publishing Common module"
+cd "$BASEDIR/packages/modules/common" && rm -rf dist && yarn&& npm publish --access public
+
+# msg "Building and publishing Core module"
+cd "$BASEDIR/packages/modules/core" && rm -rf dist && yarn&& npm publish --access public
+
+# msg "Building and publishing Utilities module"
+cd "$BASEDIR/packages/modules/utilities" && rm -rf dist && yarn&& npm publish --access public
+
+
+# msg "Building and publishing workbench module"
+cd "$BASEDIR/packages/modules/workbench" && rm -rf dist && yarn&& npm publish --access public
+
+# msg "Building and publishing pgr module"
+cd "$BASEDIR/packages/modules/pgr" && rm -rf dist && yarn&& npm publish --access public
diff --git a/frontend/micro-ui/web/micro-ui-internals/scripts/create.sh b/frontend/micro-ui/web/micro-ui-internals/scripts/create.sh
new file mode 100644
index 0000000..9de7233
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/scripts/create.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./scripts/run.sh core utilities
diff --git a/frontend/micro-ui/web/micro-ui-internals/scripts/deploy.sh b/frontend/micro-ui/web/micro-ui-internals/scripts/deploy.sh
new file mode 100644
index 0000000..5b0c7b8
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/scripts/deploy.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+curl -v -X POST https://builds.digit.org/job/builds/job/digit-ui/buildWithParameters \
+ --user saurabh-egov:114cbf3df675835931688b2d3f0014a1f7 \
+ --data-urlencode json='{"parameter": [{"name":"BRANCH", "value":"origin/'$1'"}]}'
+
+# curl https://builds.digit.org/job/builds/job/digit-ui/lastBuild/api/json | grep --color result\":null
+
diff --git a/frontend/micro-ui/web/micro-ui-internals/scripts/jenkins.sh b/frontend/micro-ui/web/micro-ui-internals/scripts/jenkins.sh
new file mode 100644
index 0000000..a1711fe
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/scripts/jenkins.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./scripts/deploy.sh dev
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/scripts/run.sh b/frontend/micro-ui/web/micro-ui-internals/scripts/run.sh
new file mode 100644
index 0000000..f00c59f
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/scripts/run.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+MODULES=( "components" "core" "libraries" "example" )
+
+RUNARGS=()
+BUILDARGS=()
+
+for var in "$@"
+do
+ BUILDARGS=( ${BUILDARGS[@]} build:"$var" )
+ RUNARGS=( ${RUNARGS[@]} dev:"$var" )
+done
+
+a=0
+while [ "$a" -lt 3 ]
+do
+ BUILD[$a]=build:${MODULES[$a]}
+ a=` expr $a + 1 `
+done
+
+echo "BUILDING MODULES:-" ${BUILD[*]} ${BUILDARGS[*]}
+yarn run-p ${BUILD[*]} ${BUILDARGS[*]}
+
+b=0
+while [ "$b" -lt 4 ]
+do
+ RUN[$b]=dev:${MODULES[$b]}
+ b=` expr $b + 1 `
+done
+
+echo "SERVING MODULES:-" ${RUN[*]} ${RUNARGS[*]}
+yarn run-p ${RUN[*]} ${RUNARGS[*]}
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/test.js b/frontend/micro-ui/web/micro-ui-internals/test.js
new file mode 100644
index 0000000..2c2442e
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/test.js
@@ -0,0 +1,30 @@
+const middleWare_1 = (data, _break, _next) => {
+ data.a = "a";
+ _next(data);
+};
+
+const middleWare_2 = (data, _break, _next) => {
+ data.b = "b";
+ // _break();
+ _next(data);
+};
+
+const middleWare_3 = (data, _break, _next) => {
+ data.c = "c";
+ _next(data);
+};
+
+let middleWares = [middleWare_1, middleWare_2, middleWare_3];
+
+const callMiddlewares = () => {
+ let applyBreak = false;
+ let itr = -1;
+ let _break = () => (applyBreak = true);
+ let _next = (data) => {
+ if (!applyBreak && ++itr < middleWares.length) middleWares[itr](data, _break, _next);
+ else return;
+ };
+ _next({});
+};
+
+callMiddlewares();
diff --git a/frontend/micro-ui/web/package.json b/frontend/micro-ui/web/package.json
new file mode 100644
index 0000000..595aae9
--- /dev/null
+++ b/frontend/micro-ui/web/package.json
@@ -0,0 +1,85 @@
+{
+ "name": "micro-ui",
+ "version": "1.0.1",
+ "author": "Jagankumar ",
+ "license": "MIT",
+ "private": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "workspaces": [
+ "micro-ui-internals/packages/libraries",
+ "micro-ui-internals/packages/react-components",
+ "micro-ui-internals/packages/modules/core"
+ ],
+ "homepage": "/digit-ui",
+ "dependencies": {
+ "@egovernments/digit-ui-libraries": "1.8.2-beta.6",
+ "@egovernments/digit-ui-module-core": "1.8.2-beta.12",
+ "@egovernments/digit-ui-components": "0.0.2-beta.38",
+ "@egovernments/digit-ui-react-components": "1.8.2-beta.12",
+ "babel-loader": "8.1.0",
+ "react": "17.0.2",
+ "react-dom": "17.0.2",
+ "clean-webpack-plugin": "4.0.0",
+ "react-router-dom": "5.3.0",
+ "webpack-cli": "4.10.0",
+ "web-vitals": "1.1.2",
+ "react-hook-form": "6.15.8",
+ "react-i18next": "11.16.2",
+ "terser-brunch": "^4.1.0",
+ "css-loader": "5.2.6",
+ "style-loader": "2.0.0",
+ "react-query": "3.6.1"
+ },
+ "devDependencies": {
+ "@babel/plugin-proposal-private-property-in-object": "7.21.0",
+ "http-proxy-middleware": "1.3.1",
+ "lodash": "4.17.21",
+ "microbundle-crl": "0.13.11",
+ "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",
+ "husky": "7.0.4",
+ "lint-staged": "12.3.7",
+ "npm-run-all": "4.1.5",
+ "prettier": "2.1.2"
+ },
+ "resolutions": {
+ "**/babel-loader": "8.2.2",
+ "**/@babel/core": "7.14.0",
+ "**/@babel/preset-env": "7.14.0",
+ "**/@babel/plugin-transform-modules-commonjs": "7.14.0",
+ "**/polished":"4.2.2",
+ "fast-uri":"2.1.0"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "GENERATE_SOURCEMAP=false SKIP_PREFLIGHT_CHECK=true react-scripts build",
+ "build:prepare": "./build.sh",
+ "build:libraries": "cd micro-ui-internals && yarn build",
+ "build:prod": "webpack --mode production",
+ "build:webpack": "yarn build:libraries &&cd .. && ls && cd ./web && ls && yarn build:prod",
+ "clean": "rm -rf node_modules"
+ },
+ "eslintConfig": {
+ "extends": [
+ "react-app"
+ ]
+ },
+ "browserslist": {
+ "production": [
+ ">0.2%",
+ "not dead",
+ "not op_mini all"
+ ],
+ "development": [
+ "last 1 chrome version",
+ "last 1 firefox version",
+ "last 1 safari version"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/frontend/micro-ui/web/public/index.html b/frontend/micro-ui/web/public/index.html
new file mode 100644
index 0000000..428c7a8
--- /dev/null
+++ b/frontend/micro-ui/web/public/index.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+ DIGIT
+
+
+
+
+
+
+
+ You need to enable JavaScript to run this app.
+
+
+
+
+
diff --git a/frontend/micro-ui/web/public/robots.txt b/frontend/micro-ui/web/public/robots.txt
new file mode 100644
index 0000000..e9e57dc
--- /dev/null
+++ b/frontend/micro-ui/web/public/robots.txt
@@ -0,0 +1,3 @@
+# https://www.robotstxt.org/robotstxt.html
+User-agent: *
+Disallow:
diff --git a/frontend/micro-ui/web/src/App.js b/frontend/micro-ui/web/src/App.js
new file mode 100644
index 0000000..913bff0
--- /dev/null
+++ b/frontend/micro-ui/web/src/App.js
@@ -0,0 +1,63 @@
+import React from "react";
+import { initLibraries } from "@egovernments/digit-ui-libraries";
+
+import { DigitUI } from "@egovernments/digit-ui-module-core";
+
+
+
+
+import { UICustomizations } from "./Customisations/UICustomizations";
+
+window.contextPath = window?.globalConfigs?.getConfig("CONTEXT_PATH");
+
+const enabledModules = [
+ "DSS",
+ "NDSS",
+ "Utilities",
+ "HRMS",
+ "Engagement",
+ // "Workbench",
+ "PGR"
+];
+
+const moduleReducers = (initData) => ({
+ initData,
+});
+
+const initDigitUI = () => {
+ window.Digit.ComponentRegistryService.setupRegistry({
+
+ });
+
+ // initCoreComponents();
+
+
+ window.Digit.Customizations = {
+ PGR: {},
+ commonUiConfig: UICustomizations,
+ };
+};
+
+initLibraries().then(() => {
+ initDigitUI();
+});
+
+function App() {
+ window.contextPath = window?.globalConfigs?.getConfig("CONTEXT_PATH");
+ const stateCode =
+ window.globalConfigs?.getConfig("STATE_LEVEL_TENANT_ID") ||
+ process.env.REACT_APP_STATE_LEVEL_TENANT_ID;
+ if (!stateCode) {
+ return stateCode is not defined ;
+ }
+ return (
+
+ );
+}
+
+export default App;
\ No newline at end of file
diff --git a/frontend/micro-ui/web/src/ComponentRegistry.js b/frontend/micro-ui/web/src/ComponentRegistry.js
new file mode 100644
index 0000000..9bafce3
--- /dev/null
+++ b/frontend/micro-ui/web/src/ComponentRegistry.js
@@ -0,0 +1,11 @@
+class Registry {
+ constructor(registry = {}) {
+ this._registry = registry;
+ }
+
+ getComponent(id) {
+ return this._registry[id];
+ }
+}
+
+export default Registry;
diff --git a/frontend/micro-ui/web/src/Customisations/UICustomizations.js b/frontend/micro-ui/web/src/Customisations/UICustomizations.js
new file mode 100644
index 0000000..b78ac0e
--- /dev/null
+++ b/frontend/micro-ui/web/src/Customisations/UICustomizations.js
@@ -0,0 +1,651 @@
+import { Link } from "react-router-dom";
+import _ from "lodash";
+import { useLocation } from "react-router-dom";
+import { useParams } from "react-router-dom";
+
+//create functions here based on module name set in mdms(eg->SearchProjectConfig)
+//how to call these -> Digit?.Customizations?.[masterName]?.[moduleName]
+// these functions will act as middlewares
+var Digit = window.Digit || {};
+
+const businessServiceMap = {
+ "muster roll": "MR",
+};
+
+const inboxModuleNameMap = {
+ "muster-roll-approval": "muster-roll-service",
+};
+// eslint-disable-next-line
+export const UICustomizations = {
+ businessServiceMap,
+ updatePayload: (applicationDetails, data, action, businessService) => {
+ if (businessService === businessServiceMap.estimate) {
+ const workflow = {
+ comment: data.comments,
+ documents: data?.documents?.map((document) => {
+ return {
+ documentType: action?.action + " DOC",
+ fileName: document?.[1]?.file?.name,
+ fileStoreId: document?.[1]?.fileStoreId?.fileStoreId,
+ documentUid: document?.[1]?.fileStoreId?.fileStoreId,
+ tenantId: document?.[1]?.fileStoreId?.tenantId,
+ };
+ }),
+ assignees: data?.assignees?.uuid ? [data?.assignees?.uuid] : null,
+ action: action.action,
+ };
+ //filtering out the data
+ Object.keys(workflow).forEach((key, index) => {
+ if (!workflow[key] || workflow[key]?.length === 0) delete workflow[key];
+ });
+
+ return {
+ estimate: applicationDetails,
+ workflow,
+ };
+ }
+ if (businessService === businessServiceMap.contract) {
+ const workflow = {
+ comment: data?.comments,
+ documents: data?.documents?.map((document) => {
+ return {
+ documentType: action?.action + " DOC",
+ fileName: document?.[1]?.file?.name,
+ fileStoreId: document?.[1]?.fileStoreId?.fileStoreId,
+ documentUid: document?.[1]?.fileStoreId?.fileStoreId,
+ tenantId: document?.[1]?.fileStoreId?.tenantId,
+ };
+ }),
+ assignees: data?.assignees?.uuid ? [data?.assignees?.uuid] : null,
+ action: action.action,
+ };
+ //filtering out the data
+ Object.keys(workflow).forEach((key, index) => {
+ if (!workflow[key] || workflow[key]?.length === 0) delete workflow[key];
+ });
+
+ return {
+ contract: applicationDetails,
+ workflow,
+ };
+ }
+ if (businessService === businessServiceMap?.["muster roll"]) {
+ const workflow = {
+ comment: data?.comments,
+ documents: data?.documents?.map((document) => {
+ return {
+ documentType: action?.action + " DOC",
+ fileName: document?.[1]?.file?.name,
+ fileStoreId: document?.[1]?.fileStoreId?.fileStoreId,
+ documentUid: document?.[1]?.fileStoreId?.fileStoreId,
+ tenantId: document?.[1]?.fileStoreId?.tenantId,
+ };
+ }),
+ assignees: data?.assignees?.uuid ? [data?.assignees?.uuid] : null,
+ action: action.action,
+ };
+ //filtering out the data
+ Object.keys(workflow).forEach((key, index) => {
+ if (!workflow[key] || workflow[key]?.length === 0) delete workflow[key];
+ });
+
+ return {
+ musterRoll: applicationDetails,
+ workflow,
+ };
+ }
+ if (businessService === businessServiceMap?.["works.purchase"]) {
+ const workflow = {
+ comment: data.comments,
+ documents: data?.documents?.map((document) => {
+ return {
+ documentType: action?.action + " DOC",
+ fileName: document?.[1]?.file?.name,
+ fileStoreId: document?.[1]?.fileStoreId?.fileStoreId,
+ documentUid: document?.[1]?.fileStoreId?.fileStoreId,
+ tenantId: document?.[1]?.fileStoreId?.tenantId,
+ };
+ }),
+ assignees: data?.assignees?.uuid ? [data?.assignees?.uuid] : null,
+ action: action.action,
+ };
+ //filtering out the data
+ Object.keys(workflow).forEach((key, index) => {
+ if (!workflow[key] || workflow[key]?.length === 0) delete workflow[key];
+ });
+
+ const additionalFieldsToSet = {
+ projectId: applicationDetails.additionalDetails.projectId,
+ invoiceDate: applicationDetails.billDate,
+ invoiceNumber: applicationDetails.referenceId.split("_")?.[1],
+ contractNumber: applicationDetails.referenceId.split("_")?.[0],
+ documents: applicationDetails.additionalDetails.documents,
+ };
+ return {
+ bill: { ...applicationDetails, ...additionalFieldsToSet },
+ workflow,
+ };
+ }
+ },
+ enableModalSubmit: (businessService, action, setModalSubmit, data) => {
+ if (businessService === businessServiceMap?.["muster roll"] && action.action === "APPROVE") {
+ setModalSubmit(data?.acceptTerms);
+ }
+ },
+ enableHrmsSearch: (businessService, action) => {
+ if (businessService === businessServiceMap.estimate) {
+ return action.action.includes("TECHNICALSANCTION") || action.action.includes("VERIFYANDFORWARD");
+ }
+ if (businessService === businessServiceMap.contract) {
+ return action.action.includes("VERIFY_AND_FORWARD");
+ }
+ if (businessService === businessServiceMap?.["muster roll"]) {
+ return action.action.includes("VERIFY");
+ }
+ if (businessService === businessServiceMap?.["works.purchase"]) {
+ return action.action.includes("VERIFY_AND_FORWARD");
+ }
+ return false;
+ },
+ getBusinessService: (moduleCode) => {
+ if (moduleCode?.includes("estimate")) {
+ return businessServiceMap?.estimate;
+ } else if (moduleCode?.includes("contract")) {
+ return businessServiceMap?.contract;
+ } else if (moduleCode?.includes("muster roll")) {
+ return businessServiceMap?.["muster roll"];
+ } else if (moduleCode?.includes("works.purchase")) {
+ return businessServiceMap?.["works.purchase"];
+ } else if (moduleCode?.includes("works.wages")) {
+ return businessServiceMap?.["works.wages"];
+ } else if (moduleCode?.includes("works.supervision")) {
+ return businessServiceMap?.["works.supervision"];
+ } else {
+ return businessServiceMap;
+ }
+ },
+ getInboxModuleName: (moduleCode) => {
+ if (moduleCode?.includes("estimate")) {
+ return inboxModuleNameMap?.estimate;
+ } else if (moduleCode?.includes("contract")) {
+ return inboxModuleNameMap?.contracts;
+ } else if (moduleCode?.includes("attendence")) {
+ return inboxModuleNameMap?.attendencemgmt;
+ } else {
+ return inboxModuleNameMap;
+ }
+ },
+
+ AttendanceInboxConfig: {
+ preProcess: (data) => {
+ //set tenantId
+ data.body.inbox.tenantId = Digit.ULBService.getCurrentTenantId();
+ data.body.inbox.processSearchCriteria.tenantId = Digit.ULBService.getCurrentTenantId();
+
+ const musterRollNumber = data?.body?.inbox?.moduleSearchCriteria?.musterRollNumber?.trim();
+ if (musterRollNumber) data.body.inbox.moduleSearchCriteria.musterRollNumber = musterRollNumber;
+
+ const attendanceRegisterName = data?.body?.inbox?.moduleSearchCriteria?.attendanceRegisterName?.trim();
+ if (attendanceRegisterName) data.body.inbox.moduleSearchCriteria.attendanceRegisterName = attendanceRegisterName;
+
+ // deleting them for now(assignee-> need clarity from pintu,ward-> static for now,not implemented BE side)
+ const assignee = _.clone(data.body.inbox.moduleSearchCriteria.assignee);
+ delete data.body.inbox.moduleSearchCriteria.assignee;
+ if (assignee?.code === "ASSIGNED_TO_ME") {
+ data.body.inbox.moduleSearchCriteria.assignee = Digit.UserService.getUser().info.uuid;
+ }
+
+ //cloning locality and workflow states to format them
+ // let locality = _.clone(data.body.inbox.moduleSearchCriteria.locality ? data.body.inbox.moduleSearchCriteria.locality : []);
+
+ let selectedOrg = _.clone(data.body.inbox.moduleSearchCriteria.orgId ? data.body.inbox.moduleSearchCriteria.orgId : null);
+ delete data.body.inbox.moduleSearchCriteria.orgId;
+ if (selectedOrg) {
+ data.body.inbox.moduleSearchCriteria.orgId = selectedOrg?.[0]?.applicationNumber;
+ }
+
+ // let selectedWard = _.clone(data.body.inbox.moduleSearchCriteria.ward ? data.body.inbox.moduleSearchCriteria.ward : null);
+ // delete data.body.inbox.moduleSearchCriteria.ward;
+ // if(selectedWard) {
+ // data.body.inbox.moduleSearchCriteria.ward = selectedWard?.[0]?.code;
+ // }
+
+ let states = _.clone(data.body.inbox.moduleSearchCriteria.state ? data.body.inbox.moduleSearchCriteria.state : []);
+ let ward = _.clone(data.body.inbox.moduleSearchCriteria.ward ? data.body.inbox.moduleSearchCriteria.ward : []);
+ // delete data.body.inbox.moduleSearchCriteria.locality;
+ delete data.body.inbox.moduleSearchCriteria.state;
+ delete data.body.inbox.moduleSearchCriteria.ward;
+
+ // locality = locality?.map((row) => row?.code);
+ states = Object.keys(states)?.filter((key) => states[key]);
+ ward = ward?.map((row) => row?.code);
+
+ // //adding formatted data to these keys
+ // if (locality.length > 0) data.body.inbox.moduleSearchCriteria.locality = locality;
+ if (states.length > 0) data.body.inbox.moduleSearchCriteria.status = states;
+ if (ward.length > 0) data.body.inbox.moduleSearchCriteria.ward = ward;
+ const projectType = _.clone(data.body.inbox.moduleSearchCriteria.projectType ? data.body.inbox.moduleSearchCriteria.projectType : {});
+ if (projectType?.code) data.body.inbox.moduleSearchCriteria.projectType = projectType.code;
+
+ //adding tenantId to moduleSearchCriteria
+ data.body.inbox.moduleSearchCriteria.tenantId = Digit.ULBService.getCurrentTenantId();
+
+ //setting limit and offset becoz somehow they are not getting set in muster inbox
+ data.body.inbox.limit = data.state.tableForm.limit;
+ data.body.inbox.offset = data.state.tableForm.offset;
+ delete data.state;
+ return data;
+ },
+ postProcess: (responseArray, uiConfig) => {
+ const statusOptions = responseArray?.statusMap
+ ?.filter((item) => item.applicationstatus)
+ ?.map((item) => ({ code: item.applicationstatus, i18nKey: `COMMON_MASTERS_${item.applicationstatus}` }));
+ if (uiConfig?.type === "filter") {
+ let fieldConfig = uiConfig?.fields?.filter((item) => item.type === "dropdown" && item.populators.name === "musterRollStatus");
+ if (fieldConfig.length) {
+ fieldConfig[0].populators.options = statusOptions;
+ }
+ }
+ },
+ additionalCustomizations: (row, key, column, value, t, searchResult) => {
+ if (key === "ATM_MUSTER_ROLL_ID") {
+ return (
+
+
+ {String(value ? (column.translate ? t(column.prefix ? `${column.prefix}${value}` : value) : value) : t("ES_COMMON_NA"))}
+
+
+ );
+ }
+ if (key === "ATM_ATTENDANCE_WEEK") {
+ const week = `${Digit.DateUtils.ConvertTimestampToDate(value?.startDate, "dd/MM/yyyy")}-${Digit.DateUtils.ConvertTimestampToDate(
+ value?.endDate,
+ "dd/MM/yyyy"
+ )}`;
+ return {week}
;
+ }
+ if (key === "ATM_NO_OF_INDIVIDUALS") {
+ return {value?.length}
;
+ }
+ if (key === "ATM_AMOUNT_IN_RS") {
+ return {value ? Digit.Utils.dss.formatterWithoutRound(value, "number") : t("ES_COMMON_NA")} ;
+ }
+ if (key === "ATM_SLA") {
+ return parseInt(value) > 0 ? (
+ {t(value) || ""}
+ ) : (
+ {t(value) || ""}
+ );
+ }
+ if (key === "COMMON_WORKFLOW_STATES") {
+ return {t(`WF_MUSTOR_${value}`)} ;
+ }
+ //added this in case we change the key and not updated here , it'll throw that nothing was returned from cell error if that case is not handled here. To prevent that error putting this default
+ return {t(`CASE_NOT_HANDLED`)} ;
+ },
+ MobileDetailsOnClick: (row, tenantId) => {
+ let link;
+ Object.keys(row).map((key) => {
+ if (key === "ATM_MUSTER_ROLL_ID")
+ link = `/${window.contextPath}/employee/attendencemgmt/view-attendance?tenantId=${tenantId}&musterRollNumber=${row[key]}`;
+ });
+ return link;
+ },
+ populateReqCriteria: () => {
+ const tenantId = Digit.ULBService.getCurrentTenantId();
+ return {
+ url: "/org-services/organisation/v1/_search",
+ params: { limit: 50, offset: 0 },
+ body: {
+ SearchCriteria: {
+ tenantId: tenantId,
+ functions: {
+ type: "CBO",
+ },
+ },
+ },
+ config: {
+ enabled: true,
+ select: (data) => {
+ return data?.organisations;
+ },
+ },
+ };
+ },
+ },
+ SearchWageSeekerConfig: {
+ customValidationCheck: (data) => {
+ //checking both to and from date are present
+ const { createdFrom, createdTo } = data;
+ if ((createdFrom === "" && createdTo !== "") || (createdFrom !== "" && createdTo === ""))
+ return { warning: true, label: "ES_COMMON_ENTER_DATE_RANGE" };
+
+ return false;
+ },
+ preProcess: (data) => {
+ data.params = { ...data.params, tenantId: Digit.ULBService.getCurrentTenantId() };
+
+ let requestBody = { ...data.body.Individual };
+ const pathConfig = {
+ name: "name.givenName",
+ };
+ const dateConfig = {
+ createdFrom: "daystart",
+ createdTo: "dayend",
+ };
+ const selectConfig = {
+ wardCode: "wardCode[0].code",
+ socialCategory: "socialCategory.code",
+ };
+ const textConfig = ["name", "individualId"];
+ let Individual = Object.keys(requestBody)
+ .map((key) => {
+ if (selectConfig[key]) {
+ requestBody[key] = _.get(requestBody, selectConfig[key], null);
+ } else if (typeof requestBody[key] == "object") {
+ requestBody[key] = requestBody[key]?.code;
+ } else if (textConfig?.includes(key)) {
+ requestBody[key] = requestBody[key]?.trim();
+ }
+ return key;
+ })
+ .filter((key) => requestBody[key])
+ .reduce((acc, curr) => {
+ if (pathConfig[curr]) {
+ _.set(acc, pathConfig[curr], requestBody[curr]);
+ } else if (dateConfig[curr] && dateConfig[curr]?.includes("day")) {
+ _.set(acc, curr, Digit.Utils.date.convertDateToEpoch(requestBody[curr], dateConfig[curr]));
+ } else {
+ _.set(acc, curr, requestBody[curr]);
+ }
+ return acc;
+ }, {});
+
+ data.body.Individual = { ...Individual };
+ return data;
+ },
+ additionalCustomizations: (row, key, column, value, t, searchResult) => {
+ //here we can add multiple conditions
+ //like if a cell is link then we return link
+ //first we can identify which column it belongs to then we can return relevant result
+ switch (key) {
+ case "MASTERS_WAGESEEKER_ID":
+ return (
+
+
+ {String(value ? (column.translate ? t(column.prefix ? `${column.prefix}${value}` : value) : value) : t("ES_COMMON_NA"))}
+
+
+ );
+
+ case "MASTERS_SOCIAL_CATEGORY":
+ return value ? {String(t(`MASTERS_${value}`))} : t("ES_COMMON_NA");
+
+ case "CORE_COMMON_PROFILE_CITY":
+ return value ? {String(t(Digit.Utils.locale.getCityLocale(value)))} : t("ES_COMMON_NA");
+
+ case "MASTERS_WARD":
+ return value ? (
+ {String(t(Digit.Utils.locale.getMohallaLocale(value, row?.tenantId)))}
+ ) : (
+ t("ES_COMMON_NA")
+ );
+
+ case "MASTERS_LOCALITY":
+ return value ? (
+ {String(t(Digit.Utils.locale.getMohallaLocale(value, row?.tenantId)))}
+ ) : (
+ t("ES_COMMON_NA")
+ );
+ default:
+ return t("ES_COMMON_NA");
+ }
+ },
+ MobileDetailsOnClick: (row, tenantId) => {
+ let link;
+ Object.keys(row).map((key) => {
+ if (key === "MASTERS_WAGESEEKER_ID")
+ link = `/${window.contextPath}/employee/masters/view-wageseeker?tenantId=${tenantId}&wageseekerId=${row[key]}`;
+ });
+ return link;
+ },
+ additionalValidations: (type, data, keys) => {
+ if (type === "date") {
+ return data[keys.start] && data[keys.end] ? () => new Date(data[keys.start]).getTime() <= new Date(data[keys.end]).getTime() : true;
+ }
+ },
+ },
+ SearchDefaultConfig: {
+
+ customValidationCheck: (data) => {
+ //checking both to and from date are present
+ const { createdFrom, createdTo } = data;
+ if ((createdFrom === "" && createdTo !== "") || (createdFrom !== "" && createdTo === ""))
+ return { warning: true, label: "ES_COMMON_ENTER_DATE_RANGE" };
+
+ return false;
+ },
+ preProcess: (data) => {
+ // eslint-disable-next-line
+ const location = useLocation();
+ data.params = { ...data.params };
+ // eslint-disable-next-line
+ const { masterName } = useParams();
+
+ const searchParams = new URLSearchParams(location.search);
+ const paths = {
+ "SearchProjectConfig": {
+ basePath: "Projects",
+ pathConfig: {
+ // id: "id[0]",
+ tenantId: "tenantId",
+ },
+ dateConfig: {
+ endDate: "dayend",
+ startDate: "daystart"
+ },
+ selectConfig: {
+ },
+ textConfig :["id", "tenantId", "name", "projectNumber", "projectSubType" , "projectType"]
+ },
+ "SearchProductConfig": {
+ basePath: "Product",
+ pathConfig: {
+ id: "id[0]",
+ },
+ dateConfig: {
+ },
+ selectConfig: {
+ },
+ textConfig :["id", "manufacturer", "name", "type"]
+ },
+ "SearchHouseholdConfig": {
+ basePath: "Household",
+ pathConfig: {
+ id: "id[0]",
+ clientReferenceId: "clientReferenceId[0]",
+ },
+ dateConfig: {
+ },
+ selectConfig: {
+ },
+ textConfig :["boundaryCode", "clientReferenceId", "id"]
+ },
+ "SearchProductVariantConfig": {
+ basePath: "ProductVariant",
+ pathConfig: {
+ id: "id[0]",
+ },
+ dateConfig: {
+ },
+ selectConfig: {
+ },
+ textConfig :["productId", "sku", "variation"]
+ },
+ "SearchProjectBeneficiaryConfig": {
+ basePath: "ProjectBeneficiary",
+ pathConfig: {
+ id: "id[0]",
+ clientReferenceId: "clientReferenceId[0]",
+
+ },
+ dateConfig: {
+ dateOfRegistration: "daystart"
+ },
+ selectConfig: {
+ },
+ textConfig :["beneficiaryId", "projectId"]
+ },
+ "SearchProjectStaffConfig": {
+ basePath: "ProjectStaff",
+ pathConfig: {
+ id: "id[0]",
+ },
+ dateConfig: {
+ startDate: "daystart",
+ endDate: "dayend",
+ },
+ selectConfig: {
+ },
+ textConfig :["projectId", "userId"]
+ },
+ "SearchProjectResourceConfig": {
+ basePath: "ProjectResource",
+ pathConfig: {
+ id: "id[0]"
+ },
+ dateConfig: {
+ },
+ selectConfig: {
+ },
+ textConfig : []
+ },
+ "SearchProjectTaskConfig": {
+ basePath: "Task",
+ pathConfig: {
+ id: "id[0]",
+ clientReferenceId: "clientReferenceId[0]",
+ },
+ dateConfig: {
+ plannedEndDate: "dayend",
+ plannedStartDate: "daystart",
+ actualEndDate: "dayend",
+ actualStartDate: "daystart",
+ },
+ selectConfig: {
+ },
+ textConfig :["projectId","localityCode", "projectBeneficiaryId", "status"]
+ },
+ "SearchFacilityConfig": {
+ basePath: "Facility",
+ pathConfig: {
+ id: "id[0]"
+ },
+ dateConfig: {
+ },
+ selectConfig: {
+ },
+ textConfig :["faciltyUsage","localityCode", "storageCapacity","id"]
+ }
+ }
+
+ const id = searchParams.get("config")|| masterName;
+
+ if(!paths||!paths?.[id]){
+ return data;
+ }
+ let requestBody = { ...data.body[paths[id]?.basePath] };
+ const pathConfig = paths[id]?.pathConfig;
+ const dateConfig = paths[id]?.dateConfig;
+ const selectConfig = paths[id]?.selectConfig;
+ const textConfig = paths[id]?.textConfig
+
+ if(paths[id].basePath == "Projects"){
+ data.state.searchForm={...data.state.searchForm,tenantId:"mz"}
+ }
+ let Product = Object.keys(requestBody)
+ .map((key) => {
+ if (selectConfig[key]) {
+ requestBody[key] = _.get(requestBody, selectConfig[key], null);
+ } else if (typeof requestBody[key] == "object") {
+ requestBody[key] = requestBody[key]?.code;
+ } else if (textConfig?.includes(key)) {
+ requestBody[key] = requestBody[key]?.trim();
+ }
+ return key;
+ })
+ .filter((key) => requestBody[key])
+ .reduce((acc, curr) => {
+ if (pathConfig[curr]) {
+ _.set(acc, pathConfig[curr], requestBody[curr]);
+ } else if (dateConfig[curr] && dateConfig[curr]?.includes("day")) {
+ _.set(acc, curr, Digit.Utils.date.convertDateToEpoch(requestBody[curr], dateConfig[curr]));
+ } else {
+ _.set(acc, curr, requestBody[curr]);
+ }
+ return acc;
+ }, {});
+
+ if(paths[id].basePath == "Projects"){
+
+ data.body[paths[id].basePath] = [{ ...Product}];
+ }
+ else data.body[paths[id].basePath] = { ...Product};
+ return data;
+ },
+ additionalCustomizations: (row, key, column, value, t, searchResult) => {
+ //here we can add multiple conditions
+ //like if a cell is link then we return link
+ //first we can identify which column it belongs to then we can return relevant result
+ switch (key) {
+ case "MASTERS_WAGESEEKER_ID":
+ return (
+
+
+ {String(value ? (column.translate ? t(column.prefix ? `${column.prefix}${value}` : value) : value) : t("ES_COMMON_NA"))}
+
+
+ );
+
+ case "MASTERS_SOCIAL_CATEGORY":
+ return value ? {String(t(`MASTERS_${value}`))} : t("ES_COMMON_NA");
+
+ case "CORE_COMMON_PROFILE_CITY":
+ return value ? {String(t(Digit.Utils.locale.getCityLocale(value)))} : t("ES_COMMON_NA");
+
+ case "MASTERS_WARD":
+ return value ? (
+ {String(t(Digit.Utils.locale.getMohallaLocale(value, row?.tenantId)))}
+ ) : (
+ t("ES_COMMON_NA")
+ );
+
+ case "MASTERS_LOCALITY":
+ return value ? (
+ {String(t(Digit.Utils.locale.getMohallaLocale(value, row?.tenantId)))}
+ ) : (
+ t("ES_COMMON_NA")
+ );
+ default:
+ return t("ES_COMMON_NA");
+ }
+ },
+ MobileDetailsOnClick: (row, tenantId) => {
+ let link;
+ Object.keys(row).map((key) => {
+ if (key === "MASTERS_WAGESEEKER_ID")
+ link = `/${window.contextPath}/employee/masters/view-wageseeker?tenantId=${tenantId}&wageseekerId=${row[key]}`;
+ });
+ return link;
+ },
+ additionalValidations: (type, data, keys) => {
+ if (type === "date") {
+ return data[keys.start] && data[keys.end] ? () => new Date(data[keys.start]).getTime() <= new Date(data[keys.end]).getTime() : true;
+ }
+ },
+ }
+};
diff --git a/frontend/micro-ui/web/src/Customisations/index.js b/frontend/micro-ui/web/src/Customisations/index.js
new file mode 100644
index 0000000..803b1e8
--- /dev/null
+++ b/frontend/micro-ui/web/src/Customisations/index.js
@@ -0,0 +1,19 @@
+import { ptComponents } from "./pt";
+import { tlComponents } from "./tl";
+
+var Digit = window.Digit || {};
+
+const customisedComponent = {
+ ...ptComponents,
+ ...tlComponents
+}
+
+
+
+export const initCustomisationComponents = () => {
+ Object.entries(customisedComponent).forEach(([key, value]) => {
+ Digit.ComponentRegistryService.setComponent(key, value);
+ });
+};
+
+
diff --git a/frontend/micro-ui/web/src/Customisations/pt/index.js b/frontend/micro-ui/web/src/Customisations/pt/index.js
new file mode 100644
index 0000000..0063fcd
--- /dev/null
+++ b/frontend/micro-ui/web/src/Customisations/pt/index.js
@@ -0,0 +1,13 @@
+import PropertyUsageType from "./pageComponents/PropertyUsageType";
+import PTVasikaDetails from "./pageComponents/PTVasikaDetails";
+import PTAllotmentDetails from "./pageComponents/PTAllotmentDetails";
+import PTBusinessDetails from "./pageComponents/PTBusinessDetails";
+
+
+
+export const ptComponents = {
+ PropertyUsageType: PropertyUsageType,
+ PTVasikaDetail:PTVasikaDetails,
+ PTAllotmentDetails:PTAllotmentDetails,
+ PTBusinessDetails:PTBusinessDetails
+};
diff --git a/frontend/micro-ui/web/src/Customisations/pt/pageComponents/PTAllotmentDetails.js b/frontend/micro-ui/web/src/Customisations/pt/pageComponents/PTAllotmentDetails.js
new file mode 100644
index 0000000..569aa45
--- /dev/null
+++ b/frontend/micro-ui/web/src/Customisations/pt/pageComponents/PTAllotmentDetails.js
@@ -0,0 +1,64 @@
+import { CardLabel, CitizenInfoLabel, FormStep, LabelFieldPair, TextInput,CardLabelError } from "@egovernments/digit-ui-react-components";
+import React, { useState } from "react";
+var validation ={};
+const PTAllotmentDetails = ({ t, config, onSelect, value, userType, formData }) => {
+
+ const [
+ val, setValue
+ ] = useState(formData?.[config.key]?.alotmentDetails||"");
+
+ const goNext = () => {
+ onSelect(config.key, {alotmentDetails:val});
+ };
+
+
+ if (userType === "employee") {
+ return (
+
+
+ {t("PT_VASIKA_NO_LABEL") }
+
+ setValue(e?.target?.value)}
+ // autoFocus={presentInModifyApplication}
+ />
+
+
+
+ );
+ }
+ return (
+
+
+
+ {`${t("PT_VASIKA_ALLOTMENT_LABEL")}`}
+ setValue(e?.target?.value)}
+
+ />
+
+
+ { }
+
+ );
+};
+
+export default PTAllotmentDetails;
diff --git a/frontend/micro-ui/web/src/Customisations/pt/pageComponents/PTBusinessDetails.js b/frontend/micro-ui/web/src/Customisations/pt/pageComponents/PTBusinessDetails.js
new file mode 100644
index 0000000..3d28785
--- /dev/null
+++ b/frontend/micro-ui/web/src/Customisations/pt/pageComponents/PTBusinessDetails.js
@@ -0,0 +1,68 @@
+import { CardLabel, CitizenInfoLabel, FormStep, LabelFieldPair, TextInput,CardLabelError } from "@egovernments/digit-ui-react-components";
+import React, { useState } from "react";
+var validation ={};
+const PTBusinessDetails = ({ t, config, onSelect, value, userType, formData }) => {
+
+
+ const [
+ val, setValue
+ ] = useState(formData?.[config.key]?.businessDetails||"");
+
+ const goNext = () => {
+ onSelect(config.key, {businessDetails:val});
+ };
+
+
+ if (userType === "employee") {
+ return (
+
+
+ {t("PT_VASIKA_NO_LABEL") }
+
+ setValue(e?.target?.value)}
+ // autoFocus={presentInModifyApplication}
+ />
+
+
+
+
+ );
+ }
+ return (
+
+
+
+
+ {`${t("PT_VASIKA_BUS_DETAILS_LABEL")}`}
+ setValue(e?.target?.value)}
+
+ />
+
+
+
+ { }
+
+ );
+};
+
+export default PTBusinessDetails;
diff --git a/frontend/micro-ui/web/src/Customisations/pt/pageComponents/PTVasikaDetails.js b/frontend/micro-ui/web/src/Customisations/pt/pageComponents/PTVasikaDetails.js
new file mode 100644
index 0000000..0e4b689
--- /dev/null
+++ b/frontend/micro-ui/web/src/Customisations/pt/pageComponents/PTVasikaDetails.js
@@ -0,0 +1,79 @@
+import { CardLabel, CitizenInfoLabel, FormStep, LabelFieldPair, TextInput,CardLabelError } from "@egovernments/digit-ui-react-components";
+import React, { useState } from "react";
+var validation ={};
+const PTVasikaDetails = ({ t, config, onSelect, value, userType, formData }) => {
+
+
+ const [
+ val, setValue
+ ] = useState(formData?.[config.key]?.vasikaNo||"");
+ const [
+ other, setOther
+ ] = useState(formData?.[config.key]?.vasikaArea||"");
+ const goNext = () => {
+ onSelect(config.key, {vasikaNo:val,vasikaArea:other});
+ };
+
+
+ if (userType === "employee") {
+ return (
+
+
+ {t("PT_VASIKA_NO_LABEL") }
+
+ setValue(e?.target?.value)}
+ // autoFocus={presentInModifyApplication}
+ />
+
+
+
+
+ );
+ }
+ return (
+
+
+
+
+ {`${t("PT_VASIKA_NO_LABEL")}`}
+ setValue(e?.target?.value)}
+
+ />
+
+ {`${t("PT_VASIKA_AREA_LABEL")}`}
+ setOther(e?.target?.value)}
+ />
+
+ { }
+
+ );
+};
+
+export default PTVasikaDetails;
diff --git a/frontend/micro-ui/web/src/Customisations/pt/pageComponents/PropertyUsageType.js b/frontend/micro-ui/web/src/Customisations/pt/pageComponents/PropertyUsageType.js
new file mode 100644
index 0000000..deade4f
--- /dev/null
+++ b/frontend/micro-ui/web/src/Customisations/pt/pageComponents/PropertyUsageType.js
@@ -0,0 +1,134 @@
+import {
+ CardLabel, CardLabelError, CitizenInfoLabel, Dropdown, FormStep, LabelFieldPair, RadioButtons
+} from "@egovernments/digit-ui-react-components";
+import React, { useEffect, useState } from "react";
+import { useLocation } from "react-router-dom";
+
+var Digit = window.Digit || {};
+
+const PropertyUsageType = ({ t, config, onSelect, userType, formData, formState, setError, clearErrors, onBlur }) => {
+ const [usageCategoryMajor, setPropertyPurpose] = useState(
+ formData?.usageCategoryMajor && formData?.usageCategoryMajor?.code === "NONRESIDENTIAL.OTHERS"
+ ? { code: `${formData?.usageCategoryMajor?.code}`, i18nKey: `PROPERTYTAX_BILLING_SLAB_OTHERS` }
+ : formData?.usageCategoryMajor
+ );
+
+ const tenantId = Digit.ULBService.getCurrentTenantId();
+ const stateId = tenantId.split(".")[0];
+ const { data: Menu = { }, isLoading: menuLoading } = Digit.Hooks.pt.usePropertyMDMS(stateId, "PropertyTax", "UsageCategory") || { };
+ let usagecat = [];
+ usagecat = Menu?.PropertyTax?.UsageCategory || [];
+ let i;
+ let menu = [];
+
+ const { pathname } = useLocation();
+ const presentInModifyApplication = pathname.includes("modify");
+
+ function usageCategoryMajorMenu(usagecat) {
+ if (userType === "employee") {
+ const catMenu = usagecat
+ ?.filter((e) => e?.code.split(".").length <= 2 && e.code !== "NONRESIDENTIAL")
+ ?.map((item) => {
+ const arr = item?.code.split(".");
+ if (arr.length == 2) return { i18nKey: "PROPERTYTAX_BILLING_SLAB_" + arr[1], code: item?.code };
+ else return { i18nKey: "PROPERTYTAX_BILLING_SLAB_" + item?.code, code: item?.code };
+ });
+ return catMenu;
+ } else {
+ for (i = 0; i < 10; i++) {
+ if (
+ Array.isArray(usagecat) &&
+ usagecat.length > 0 &&
+ usagecat[i].code.split(".")[0] == "NONRESIDENTIAL" &&
+ usagecat[i].code.split(".").length == 2
+ ) {
+ menu.push({ i18nKey: "PROPERTYTAX_BILLING_SLAB_" + usagecat[i].code.split(".")[1], code: usagecat[i].code });
+ }
+ }
+ return menu;
+ }
+ }
+
+ useEffect(() => {
+ if (!menuLoading && presentInModifyApplication && userType === "employee") {
+ const original = formData?.originalData?.usageCategory;
+ const selectedOption = usageCategoryMajorMenu(usagecat).filter((e) => e.code === original)[0];
+ setPropertyPurpose(selectedOption);
+ }
+ }, [menuLoading]);
+
+ const onSkip = () => onSelect();
+
+
+ function selectPropertyPurpose(value) {
+ setPropertyPurpose(value);
+ }
+
+ function goNext() {
+ if (usageCategoryMajor?.i18nKey === "PROPERTYTAX_BILLING_SLAB_OTHERS") {
+ usageCategoryMajor.i18nKey = "PROPERTYTAX_BILLING_SLAB_NONRESIDENTIAL";
+ onSelect(config.key, usageCategoryMajor);
+ } else {
+ onSelect(config.key, usageCategoryMajor);
+ }
+ }
+
+ useEffect(() => {
+ if (userType === "employee") {
+ if (!usageCategoryMajor) {
+ setError(config.key, { type: "required", message: t(`CORE_COMMON_REQUIRED_ERRMSG`) });
+ } else {
+ clearErrors(config.key);
+ }
+ goNext();
+ }
+ }, [usageCategoryMajor]);
+
+ if (userType === "employee") {
+ return (
+
+
+ {t("PT_ASSESMENT_INFO_USAGE_TYPE") + " *"}
+ {
+ selectPropertyPurpose(e);
+ }}
+ optionKey="i18nKey"
+ onBlur={onBlur}
+ t={t}
+ />
+
+ {formState.touched[config.key] ? (
+
+ {formState.errors?.[config.key]?.message}
+
+ ) : null}
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+ { }
+
+ );
+};
+
+export default PropertyUsageType;
diff --git a/frontend/micro-ui/web/src/Customisations/tl/TLCustomisation.js b/frontend/micro-ui/web/src/Customisations/tl/TLCustomisation.js
new file mode 100644
index 0000000..642acc5
--- /dev/null
+++ b/frontend/micro-ui/web/src/Customisations/tl/TLCustomisation.js
@@ -0,0 +1,5 @@
+export const TLCustomisations = {
+ customiseCreateFormData: (formData, licenceObject) => licenceObject,
+ customiseRenewalCreateFormData: (formData, licenceObject) => licenceObject,
+ customiseSendbackFormData: (formData, licenceObject) => licenceObject
+}
\ No newline at end of file
diff --git a/frontend/micro-ui/web/src/Customisations/tl/index.js b/frontend/micro-ui/web/src/Customisations/tl/index.js
new file mode 100644
index 0000000..fe2ae4f
--- /dev/null
+++ b/frontend/micro-ui/web/src/Customisations/tl/index.js
@@ -0,0 +1,7 @@
+import TLUsageType from "./pageComponents/PropertyUsageType";
+
+
+
+export const tlComponents = {
+ TLPropertyUsageType: TLUsageType,
+};
diff --git a/frontend/micro-ui/web/src/Customisations/tl/pageComponents/PropertyUsageType.js b/frontend/micro-ui/web/src/Customisations/tl/pageComponents/PropertyUsageType.js
new file mode 100644
index 0000000..5520a66
--- /dev/null
+++ b/frontend/micro-ui/web/src/Customisations/tl/pageComponents/PropertyUsageType.js
@@ -0,0 +1,136 @@
+import {
+ CardLabel, CardLabelError, CitizenInfoLabel, Dropdown, FormStep, LabelFieldPair, RadioButtons
+} from "@egovernments/digit-ui-react-components";
+import React, { useEffect, useState } from "react";
+import { useLocation } from "react-router-dom";
+
+var Digit = window.Digit || {};
+
+const TLUsageType = ({ t, config, onSelect, userType, formData, formState, setError, clearErrors, onBlur }) => {
+ const [usageCategoryMajor, setPropertyPurpose] = useState(
+ formData?.usageCategoryMajor && formData?.usageCategoryMajor?.code === "NONRESIDENTIAL.OTHERS"
+ ? { code: `${formData?.usageCategoryMajor?.code}`, i18nKey: `PROPERTYTAX_BILLING_SLAB_OTHERS` }
+ : formData?.usageCategoryMajor
+ );
+
+ const tenantId = Digit.ULBService.getCurrentTenantId();
+ const stateId = tenantId.split(".")[0];
+ const { data: Menu = { }, isLoading: menuLoading } = Digit.Hooks.pt.usePropertyMDMS(stateId, "PropertyTax", "UsageCategory") || { };
+ let usagecat = [];
+ usagecat = Menu?.PropertyTax?.UsageCategory || [];
+ let i;
+ let menu = [];
+
+ const { pathname } = useLocation();
+ const presentInModifyApplication = pathname.includes("modify");
+
+ function usageCategoryMajorMenu(usagecat) {
+ if (userType === "employee") {
+ const catMenu = usagecat
+ ?.filter((e) => e?.code.split(".").length <= 2 && e.code !== "NONRESIDENTIAL")
+ ?.map((item) => {
+ const arr = item?.code.split(".");
+ if (arr.length == 2) return { i18nKey: "PROPERTYTAX_BILLING_SLAB_" + arr[1], code: item?.code };
+ else return { i18nKey: "PROPERTYTAX_BILLING_SLAB_" + item?.code, code: item?.code };
+ });
+ return catMenu;
+ } else {
+ for (i = 0; i < 10; i++) {
+ if (
+ Array.isArray(usagecat) &&
+ usagecat.length > 0 &&
+ usagecat[i].code.split(".")[0] == "NONRESIDENTIAL" &&
+ usagecat[i].code.split(".").length == 2
+ ) {
+ menu.push({ i18nKey: "PROPERTYTAX_BILLING_SLAB_" + usagecat[i].code.split(".")[1], code: usagecat[i].code });
+ }
+ }
+ return menu;
+ }
+ }
+
+ useEffect(() => {
+ if (!menuLoading && presentInModifyApplication && userType === "employee") {
+ const original = formData?.originalData?.usageCategory;
+ const selectedOption = usageCategoryMajorMenu(usagecat).filter((e) => e.code === original)[0];
+ setPropertyPurpose(selectedOption);
+ }
+ }, [menuLoading]);
+
+ const onSkip = () => onSelect();
+
+
+ function selectPropertyPurpose(value) {
+ setPropertyPurpose(value);
+ }
+
+ function goNext() {
+ if (usageCategoryMajor?.i18nKey === "PROPERTYTAX_BILLING_SLAB_OTHERS") {
+ usageCategoryMajor.i18nKey = "PROPERTYTAX_BILLING_SLAB_NONRESIDENTIAL";
+ onSelect(config.key, usageCategoryMajor);
+ } else {
+ onSelect(config.key, usageCategoryMajor);
+ }
+ }
+
+ useEffect(() => {
+ if (userType === "employee") {
+ if (!usageCategoryMajor) {
+ setError(config.key, { type: "required", message: t(`CORE_COMMON_REQUIRED_ERRMSG`) });
+ } else {
+ clearErrors(config.key);
+ }
+ goNext();
+ }
+ }, [usageCategoryMajor]);
+
+ if (userType === "employee") {
+ return (
+
+
+ {t("PT_ASSESMENT_INFO_USAGE_TYPE") + " *"}
+ {
+ selectPropertyPurpose(e);
+ }}
+ optionKey="i18nKey"
+ onBlur={onBlur}
+ t={t}
+ />
+
+ {formState.touched[config.key] ? (
+
+ {formState.errors?.[config.key]?.message}
+
+ ) : null}
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ to say this is different element
+
+
+ { }
+
+ );
+};
+
+export default TLUsageType;
diff --git a/frontend/micro-ui/web/src/index.css b/frontend/micro-ui/web/src/index.css
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/micro-ui/web/src/index.js b/frontend/micro-ui/web/src/index.js
new file mode 100644
index 0000000..9f20bf1
--- /dev/null
+++ b/frontend/micro-ui/web/src/index.js
@@ -0,0 +1,62 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import { initLibraries } from "@egovernments/digit-ui-libraries";
+import "./index.css";
+import App from './App';
+import { TLCustomisations } from './Customisations/tl/TLCustomisation';
+
+
+initLibraries();
+
+
+window.Digit.Customizations = { PGR: {} ,TL:TLCustomisations};
+
+const user = window.Digit.SessionStorage.get("User");
+
+if (!user || !user.access_token || !user.info) {
+ // login detection
+
+ const parseValue = (value) => {
+ try {
+ return JSON.parse(value)
+ } catch (e) {
+ return value
+ }
+ }
+
+ const getFromStorage = (key) => {
+ const value = window.localStorage.getItem(key);
+ return value && value !== "undefined" ? parseValue(value) : null;
+ }
+
+ const token = getFromStorage("token")
+
+ const citizenToken = getFromStorage("Citizen.token")
+ const citizenInfo = getFromStorage("Citizen.user-info")
+ const citizenTenantId = getFromStorage("Citizen.tenant-id")
+
+ const employeeToken = getFromStorage("Employee.token")
+ const employeeInfo = getFromStorage("Employee.user-info")
+ const employeeTenantId = getFromStorage("Employee.tenant-id")
+ const userType = token === citizenToken ? "citizen" : "employee";
+
+ window.Digit.SessionStorage.set("user_type", userType);
+ window.Digit.SessionStorage.set("userType", userType);
+
+ const getUserDetails = (access_token, info) => ({ token: access_token, access_token, info })
+
+ const userDetails = userType === "citizen" ? getUserDetails(citizenToken, citizenInfo) : getUserDetails(employeeToken, employeeInfo)
+
+ window.Digit.SessionStorage.set("User", userDetails);
+ window.Digit.SessionStorage.set("Citizen.tenantId", citizenTenantId);
+ window.Digit.SessionStorage.set("Employee.tenantId", employeeTenantId);
+ // end
+}
+
+ReactDOM.render(
+
+
+ ,
+ document.getElementById('root')
+);
+
diff --git a/frontend/micro-ui/web/src/setupProxy.js b/frontend/micro-ui/web/src/setupProxy.js
new file mode 100644
index 0000000..1b8eda9
--- /dev/null
+++ b/frontend/micro-ui/web/src/setupProxy.js
@@ -0,0 +1,30 @@
+const { createProxyMiddleware } = require("http-proxy-middleware");
+const createProxy = createProxyMiddleware({
+ target: process.env.REACT_APP_PROXY_URL,
+ changeOrigin: true,
+});
+module.exports = function (app) {
+ [
+ "/egov-mdms-service",
+ "/egov-location",
+ "/localization",
+ "/egov-workflow-v2",
+ "/pgr-services",
+ "/filestore",
+ "/egov-hrms",
+ "/user-otp",
+ "/user",
+ "/fsm",
+ "/billing-service",
+ "/collection-services",
+ "/pdf-service",
+ "/pg-service",
+ "/vehicle",
+ "/vendor",
+ "/property-services",
+ "/fsm-calculator/v1/billingSlab/_search",
+ "/muster-roll"
+ ].forEach((location) =>
+ app.use(location, createProxy)
+ );
+};
diff --git a/frontend/micro-ui/web/webpack.config.js b/frontend/micro-ui/web/webpack.config.js
new file mode 100644
index 0000000..5cf9f4d
--- /dev/null
+++ b/frontend/micro-ui/web/webpack.config.js
@@ -0,0 +1,50 @@
+const path = require("path");
+const HtmlWebpackPlugin = require("html-webpack-plugin");
+const { CleanWebpackPlugin } = require("clean-webpack-plugin");
+// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
+
+module.exports = {
+ // mode: 'development',
+ entry: "./src/index.js",
+ devtool: "none",
+ module: {
+ rules: [
+ {
+ test: /\.(js)$/,
+ exclude: /node_modules/,
+ use: {
+ loader: "babel-loader",
+ options: {
+ presets: ["@babel/preset-env", "@babel/preset-react"],
+ plugins: ["@babel/plugin-proposal-optional-chaining"]
+ }
+ }
+ },
+ {
+ test: /\.css$/i,
+ use: ["style-loader", "css-loader"],
+ }
+ ],
+ },
+ output: {
+ filename: "[name].bundle.js",
+ path: path.resolve(__dirname, "build"),
+ publicPath: "/program-ui/",
+ },
+ optimization: {
+ splitChunks: {
+ chunks: 'all',
+ minSize: 20000,
+ maxSize: 50000,
+ enforceSizeThreshold: 50000,
+ minChunks: 1,
+ maxAsyncRequests: 30,
+ maxInitialRequests: 30
+ },
+ },
+ plugins: [
+ new CleanWebpackPlugin(),
+ // new BundleAnalyzerPlugin(),
+ new HtmlWebpackPlugin({ inject: true, template: "public/index.html" }),
+ ],
+};
\ No newline at end of file
From d5e916f924f78daa1a9290af4fd9729e4971340e Mon Sep 17 00:00:00 2001
From: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com>
Date: Tue, 1 Oct 2024 14:39:17 +0530
Subject: [PATCH 02/12] Updated UI flow
---
.../application/docs/ApplicationCreate.puml | 74 +++++
.../application/docs/ApplicationUpdate.puml | 68 +++++
.../bankaccounts/docs/BankAccountSearch.puml | 2 +-
.../src/main/resources/application.properties | 6 +-
.../src/main/resources/application.properties | 12 +-
.../verification/docs/VerificationCreate.puml | 71 +++++
.../src/main/resources/application.properties | 3 +-
.../micro-ui-internals/example/src/index.js | 4 +-
.../libraries/src/services/elements/MDMS.js | 7 +-
.../packages/modules/core/src/Module.js | 13 +
.../components/CustomUploadFileComposer.js | 173 ++++++++++++
.../components/MultiUploadWrapper.js | 145 ++++++++++
.../pages/individual/components/UploadFile.js | 257 ++++++++++++++++++
.../configs/IndividualCreateConfig.js | 123 ++++++---
.../pages/individual/configs/schemeConfigs.js | 5 +-
.../core/src/pages/individual/index.js | 10 +-
.../core/src/pages/individual/pages/Enroll.js | 95 ++++++-
.../src/pages/individual/pages/Program.js | 6 +-
.../src/pages/individual/pages/Programs.js | 4 +-
.../src/pages/individual/utils/createUtils.js | 131 ++++++---
.../src/hoc/UploadFileComposer.js | 5 +-
utilities/bff-service/src/api.js | 47 ++++
utilities/bff-service/src/app.js | 2 +
utilities/bff-service/src/config.js | 5 +-
.../src/routes/submitApplication.js | 238 ++++++++++++++++
25 files changed, 1400 insertions(+), 106 deletions(-)
create mode 100644 backend/application/docs/ApplicationCreate.puml
create mode 100644 backend/application/docs/ApplicationUpdate.puml
create mode 100644 backend/verification/docs/VerificationCreate.puml
create mode 100644 frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/components/CustomUploadFileComposer.js
create mode 100644 frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/components/MultiUploadWrapper.js
create mode 100644 frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/components/UploadFile.js
create mode 100644 utilities/bff-service/src/routes/submitApplication.js
diff --git a/backend/application/docs/ApplicationCreate.puml b/backend/application/docs/ApplicationCreate.puml
new file mode 100644
index 0000000..e993361
--- /dev/null
+++ b/backend/application/docs/ApplicationCreate.puml
@@ -0,0 +1,74 @@
+@startuml
+title Submit Application
+actor user as "User" order 0 #ddff41
+participant APPLICATION as "Application" order 1 #4Ff2AF
+participant INDIVIDUAL as "Individual" order 2 #4Ff2AF
+participant MDMS as "MDMS" order 3 #4Ff2AF
+participant ID_GEN as "ID Generator" order 4 #4Ff2AF
+participant WORKFLOW as "Workflow" order 5 #4Ff2AF
+participant PERSISTER as "Persister Service" order 6 #4Ff2AF
+queue KAFKA as "KAFKA" order 6 #4Ff2AF
+database REDIS as "Redis" order 8 #4Ff2AF
+database DB as "PostgreSQL" order 8 #4Ff2AF
+
+user -> APPLICATION: Call _create API
+activate user #Fafd30
+note top: Enter Individual Id,\nProgram code, Document ids
+activate APPLICATION #00fff0
+APPLICATION -> APPLICATION: Validate request
+note right: Vaidate required fields \nto create application
+alt if: Request is not valid
+ user <-[#ff0000]- APPLICATION: [400]: Error response\n with invalid field values
+end
+
+group #fffff1 Validate Program Code is valid:
+ APPLICATION -> MDMS: Call _search API
+ note right: Get Program Details
+ activate MDMS #908390
+ APPLICATION <-- MDMS: Program Details
+ deactivate MDMS
+ alt if: Program details not found
+ user <-[#ff0000]- APPLICATION: [400]: Error response\n with invalid reference error
+ end
+end
+
+group #fffffa Validate Individual is registered:
+ APPLICATION -> INDIVIDUAL: Call _search API
+ note right: Get Individual Details
+ activate INDIVIDUAL #008390
+ APPLICATION <-- INDIVIDUAL: Individual Details
+ deactivate INDIVIDUAL
+ alt if: Individual details not found
+ user <-[#ff0000]- APPLICATION: [400]: Error response\n with invalid reference error
+ end
+end
+
+APPLICATION -> ID_GEN: call _generate: \nSend module name & number of ids required
+activate ID_GEN #F1aF0F
+APPLICATION <-- ID_GEN: Return ids
+deactivate ID_GEN
+
+APPLICATION -> APPLICATION: Enrich request
+note right: Enrich request with \nUUID, audit details
+
+APPLICATION -> WORKFLOW: Call _transition API\nSend business service, ID and current state
+activate WORKFLOW #FFf0aF
+APPLICATION <-- WORKFLOW: Return response
+deactivate WORKFLOW
+
+APPLICATION -> REDIS: Save data in redis by key APPLICATIION_{APPLICAITION.ID}
+
+APPLICATION -> KAFKA: Send request to ubp-application-create topic for persister
+APPLICATION -> KAFKA: Send request to ubp-application-verification topic for verification
+
+user <-[#00a00b]- APPLICATION: Success response
+note right: Return enriched Application Details
+deactivate APPLICATION
+deactivate user
+
+KAFKA <- PERSISTER: Listen \nubp-application-create topic
+activate PERSISTER #aFa00b
+PERSISTER -> DB: Persist application details in tables\neg_ubp_application\neg_ubp_application_documents
+deactivate PERSISTER
+
+@enduml
\ No newline at end of file
diff --git a/backend/application/docs/ApplicationUpdate.puml b/backend/application/docs/ApplicationUpdate.puml
new file mode 100644
index 0000000..8593a53
--- /dev/null
+++ b/backend/application/docs/ApplicationUpdate.puml
@@ -0,0 +1,68 @@
+@startuml
+title Update Application
+actor user as "User" order 0 #ddff41
+participant APPLICATION as "Application" order 1 #4Ff2AF
+participant INDIVIDUAL as "Individual" order 2 #4Ff2AF
+participant MDMS as "MDMS" order 3 #4Ff2AF
+'participant ID_GEN as "ID Generator" order 4 #4Ff2AF
+participant WORKFLOW as "Workflow" order 5 #4Ff2AF
+participant PERSISTER as "Persister Service" order 6 #4Ff2AF
+queue KAFKA as "KAFKA" order 6 #4Ff2AF
+database REDIS as "Redis" order 8 #4Ff2AF
+database DB as "PostgreSQL" order 8 #4Ff2AF
+
+user -> APPLICATION: Call _update API
+activate user #Fafd30
+note top: Enter Individual Id,\nProgram code, Document ids
+activate APPLICATION #00fff0
+APPLICATION -> APPLICATION: Validate request
+note right: Vaidate required fields \nto create bank account
+alt if: Request is not valid
+ user <-[#ff0000]- APPLICATION: [400]: Error response\n with invalid field values
+end
+
+group #fffff1 Validate Program Code is valid:
+ APPLICATION -> MDMS: Call _search API
+ note right: Get Program Details
+ activate MDMS #908390
+ APPLICATION <-- MDMS: Program Details
+ deactivate MDMS
+ alt if: Program details not found
+ user <-[#ff0000]- APPLICATION: [400]: Error response\n with invalid reference error
+ end
+end
+
+group #fffffa Validate Individual is registered:
+ APPLICATION -> INDIVIDUAL: Call _search API
+ note right: Get Individual Details
+ activate INDIVIDUAL #008390
+ APPLICATION <-- INDIVIDUAL: Individual Details
+ deactivate INDIVIDUAL
+ alt if: Individual details not found
+ user <-[#ff0000]- APPLICATION: [400]: Error response\n with invalid reference error
+ end
+end
+
+APPLICATION -> APPLICATION: Enrich request
+note right: Enrich request with \nUUID, audit details
+
+APPLICATION -> WORKFLOW: Call _transition API\nSend business service, ID and current state
+activate WORKFLOW #FFf0aF
+APPLICATION <-- WORKFLOW: Return response
+deactivate WORKFLOW
+
+APPLICATION -> REDIS: Save data in redis by key APPLICATIION_{APPLICAITION.ID}
+
+APPLICATION -> KAFKA: Send request to ubp-application-update topic for persister
+
+user <-[#00a00b]- APPLICATION: Success response
+note right: Return updated Application Details
+deactivate APPLICATION
+deactivate user
+
+KAFKA <- PERSISTER: Listen \nubp-application-update topic
+activate PERSISTER #aFa00b
+PERSISTER -> DB: Persist application details in tables\neg_ubp_application\neg_ubp_application_documents
+deactivate PERSISTER
+
+@enduml
\ No newline at end of file
diff --git a/backend/bankaccounts/docs/BankAccountSearch.puml b/backend/bankaccounts/docs/BankAccountSearch.puml
index 85d2956..63eefca 100644
--- a/backend/bankaccounts/docs/BankAccountSearch.puml
+++ b/backend/bankaccounts/docs/BankAccountSearch.puml
@@ -1,5 +1,5 @@
@startuml
-title Update Bank Account
+title Search Bank Account
actor user as "User" order 0 #ddff41
participant BANK_ACCOUNT as "Bank Account" order 1 #4Ff2A0
participant ENC_CLIENT as "Encryption Service" order 2 #4Ff2A0
diff --git a/backend/bankaccounts/src/main/resources/application.properties b/backend/bankaccounts/src/main/resources/application.properties
index 4c04a6e..9f4c0f4 100644
--- a/backend/bankaccounts/src/main/resources/application.properties
+++ b/backend/bankaccounts/src/main/resources/application.properties
@@ -48,8 +48,10 @@ egov.localization.search.endpoint=/_search
egov.localization.statelevel=true
#mdms urls
-egov.mdms.host=https://unified-qa.digit.org
-egov.mdms.search.endpoint=/mdms-v2/v1/_search
+#egov.mdms.host=https://unified-qa.digit.org
+#egov.mdms.search.endpoint=/mdms-v2/v1/_search
+egov.mdms.host=http://localhost:8094
+egov.mdms.search.endpoint=/egov-mdms-service/v1/_search
#hrms urls
egov.hrms.host=https://unified-qa.digit.org
diff --git a/backend/individual/src/main/resources/application.properties b/backend/individual/src/main/resources/application.properties
index 094bb36..e62eae1 100644
--- a/backend/individual/src/main/resources/application.properties
+++ b/backend/individual/src/main/resources/application.properties
@@ -52,14 +52,18 @@ kafka.producer.config.linger_ms_config=1
kafka.producer.config.buffer_memory_config=33554432
# IDGEN CONFIG
-egov.idgen.host=https://unified-qa.digit.org/
+#egov.idgen.host=https://unified-qa.digit.org/
+egov.idgen.host=http://localhost:8084/
egov.idgen.path=egov-idgen/id/_generate
egov.idgen.integration.enabled=true
idgen.individual.id.format=individual.id
#----------------enc-client config---------------------#
-egov.mdms.host=https://unified-qa.digit.org
-egov.mdms.search.endpoint=/mdms-v2/v1/_search
+#egov.mdms.host=https://unified-qa.digit.org
+#egov.mdms.search.endpoint=/mdms-v2/v1/_search
+egov.mdms.host=http://localhost:8094
+egov.mdms.search.endpoint=/egov-mdms-service/v1/_search
+
state.level.tenant.id=pg
egov.enc.host=http://localhost:8082
egov.enc.encrypt.endpoint=/egov-enc-service/crypto/v1/_encrypt
@@ -89,7 +93,7 @@ user.service.user.type=CITIZEN
user.service.account.locked=false
#Notification
-notification.sms.enabled=true
+notification.sms.enabled=false
kafka.topics.notification.sms=egov.core.notification.sms
notification.sms.disabled.roles=ORG_ADMIN
diff --git a/backend/verification/docs/VerificationCreate.puml b/backend/verification/docs/VerificationCreate.puml
new file mode 100644
index 0000000..04e2ad4
--- /dev/null
+++ b/backend/verification/docs/VerificationCreate.puml
@@ -0,0 +1,71 @@
+@startuml
+title Verify Application
+participant APPLICATION as "Application" order 1 #4Ff2AF
+participant VERIFICATION as "Verification" order 2 #4Ff2AF
+participant INDIVIDUAL as "Individual" order 2 #4Ff2AF
+participant MDMS as "MDMS" order 3 #4Ff2AF
+participant WORKFLOW as "Workflow" order 5 #4Ff2AF
+participant PERSISTER as "Persister Service" order 6 #4Ff2AF
+queue KAFKA as "KAFKA" order 6 #4Ff2AF
+database DB as "PostgreSQL" order 8 #4Ff2AF
+
+note top: Enter Individual Id,\nProgram code, Document ids
+VERIFICATION <- KAFKA: Listen to ubp-application-verification topic
+activate VERIFICATION #00fff0
+VERIFICATION -> VERIFICATION: Validate request
+note right: Vaidate required fields \nto create bank account
+alt if: Request is not valid
+ user <-[#ff0000]- VERIFICATION: [400]: Error response\n with invalid field values
+end
+
+group #fffff1 Validate Program Code is valid:
+ VERIFICATION -> MDMS: Call _search API
+ note right: Get Program Details
+ activate MDMS #908390
+ VERIFICATION <-- MDMS: Program Details
+ deactivate MDMS
+ alt if: Program details not found
+ user <-[#ff0000]- VERIFICATION: [400]: Error response\n with invalid reference error
+ end
+end
+
+group #fffffa Validate Individual is registered:
+ APPLICATION -> INDIVIDUAL: Call _search API
+ note right: Get Individual Details
+ activate INDIVIDUAL #008390
+ APPLICATION <-- INDIVIDUAL: Individual Details
+ deactivate INDIVIDUAL
+ alt if: Individual details not found
+ user <-[#ff0000]- APPLICATION: [400]: Error response\n with invalid reference error
+ end
+end
+
+APPLICATION -> ID_GEN: call _generate: \nSend module name & number of ids required
+activate ID_GEN #F1aF0F
+APPLICATION <-- ID_GEN: Return ids
+deactivate ID_GEN
+
+APPLICATION -> APPLICATION: Enrich request
+note right: Enrich request with \nUUID, audit details
+
+APPLICATION -> WORKFLOW: Call _transition API\nSend business service, ID and current state
+activate WORKFLOW #FFf0aF
+APPLICATION <-- WORKFLOW: Return response
+deactivate WORKFLOW
+
+APPLICATION -> REDIS: Save data in redis by key APPLICATIION_{APPLICAITION.ID}
+
+APPLICATION -> KAFKA: Send request to ubp-application-create topic for persister
+APPLICATION -> KAFKA: Send request to ubp-application-verification topic for verification
+
+user <-[#00a00b]- APPLICATION: Success response
+note right: Return enriched Application Details
+deactivate APPLICATION
+deactivate user
+
+KAFKA <- PERSISTER: Listen \nubp-application-create topic
+activate PERSISTER #aFa00b
+PERSISTER -> DB: Persist application details in tables\neg_ubp_application\neg_ubp_application_documents
+deactivate PERSISTER
+
+@enduml
\ No newline at end of file
diff --git a/backend/verification/src/main/resources/application.properties b/backend/verification/src/main/resources/application.properties
index b282627..51cfcaf 100644
--- a/backend/verification/src/main/resources/application.properties
+++ b/backend/verification/src/main/resources/application.properties
@@ -22,7 +22,8 @@ spring.flyway.enabled=false
# KAFKA SERVER CONFIGURATIONS
kafka.config.bootstrap_server_config=localhost:9092
-spring.kafka.consumer.value-deserializer=org.egov.tracer.kafka.deserializer.HashMapDeserializer
+#spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
+spring.kafka.consumer.properties.spring.deserializer.value.delegate.class=org.springframework.kafka.support.serializer.JsonDeserializer
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.group-id=verification-service
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
diff --git a/frontend/micro-ui/web/micro-ui-internals/example/src/index.js b/frontend/micro-ui/web/micro-ui-internals/example/src/index.js
index b282812..3e35a90 100644
--- a/frontend/micro-ui/web/micro-ui-internals/example/src/index.js
+++ b/frontend/micro-ui/web/micro-ui-internals/example/src/index.js
@@ -3,7 +3,7 @@ import ReactDOM from "react-dom";
// import { PGRReducers } from "@egovernments/digit-ui-module-pgr";
import { initLibraries } from "@egovernments/digit-ui-libraries";
// import { paymentConfigs, PaymentLinks, PaymentModule } from "@egovernments/digit-ui-module-common";
-import { DigitUI } from "@egovernments/digit-ui-module-core";
+import { DigitUI,initSampleComponents } from "@egovernments/digit-ui-module-core";
// import { initDSSComponents } from "@egovernments/digit-ui-module-dss";
// import { initEngagementComponents } from "@egovernments/digit-ui-module-engagement";
// import { initHRMSComponents } from "@egovernments/digit-ui-module-hrms";
@@ -67,7 +67,7 @@ const initDigitUI = () => {
// ...paymentConfigs,
// PaymentLinks,
});
- // initCoreComponents();
+ initSampleComponents();
// initDSSComponents();
// initHRMSComponents();
// initEngagementComponents();
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/libraries/src/services/elements/MDMS.js b/frontend/micro-ui/web/micro-ui-internals/packages/libraries/src/services/elements/MDMS.js
index 8f791f7..7bce1be 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/libraries/src/services/elements/MDMS.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/libraries/src/services/elements/MDMS.js
@@ -1497,12 +1497,13 @@ export const MdmsService = {
getDataByCriteria: async (tenantId, mdmsDetails, moduleCode) => {
const key = `MDMS.${tenantId}.${moduleCode}.${mdmsDetails.type}.${JSON.stringify(mdmsDetails.details)}`;
const inStoreValue = PersistantStorage.get(key);
- if (inStoreValue) {
- return inStoreValue;
- }
+ // if (inStoreValue) {
+ // return inStoreValue;
+ // }
const { MdmsRes } = await MdmsService.call(tenantId, mdmsDetails.details);
const responseValue = transformResponse(mdmsDetails.type, MdmsRes, moduleCode.toUpperCase(), tenantId);
const cacheSetting = getCacheSetting(mdmsDetails.details.moduleDetails[0].moduleName);
+ console.log("getDataByCriteria : key : ", key)
PersistantStorage.set(key, responseValue, cacheSetting.cacheTimeInSecs);
return responseValue;
},
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/Module.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/Module.js
index b7e4f74..90a2e55 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/Module.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/Module.js
@@ -9,6 +9,7 @@ import { DigitApp } from "./App";
import { useState } from "react";
import ErrorBoundary from "./components/ErrorBoundaries";
import getStore from "./redux/store";
+import CustomUploadFileComposer from "./pages/individual/components/CustomUploadFileComposer";
const DigitUIWrapper = ({ stateCode, enabledModules, moduleReducers, defaultLanding }) => {
const { isLoading, data: initData={} } = Digit.Hooks.useInitStore(stateCode, enabledModules);
@@ -108,3 +109,15 @@ export const DigitUI = ({ stateCode, registry, enabledModules, moduleReducers, d
);
};
+
+const componentsToRegister = {
+ CustomUploadFileComposer: CustomUploadFileComposer
+};
+
+export const initSampleComponents = () => {
+ // overrideHooks();
+ // updateCustomConfigs();
+ Object.entries(componentsToRegister).forEach(([key, value]) => {
+ Digit.ComponentRegistryService.setComponent(key, value);
+ });
+};
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/components/CustomUploadFileComposer.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/components/CustomUploadFileComposer.js
new file mode 100644
index 0000000..8762827
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/components/CustomUploadFileComposer.js
@@ -0,0 +1,173 @@
+import React from 'react'
+import { useTranslation } from 'react-i18next'
+import {
+ LabelFieldPair,
+ CardLabel,
+ CardLabelError,
+ CitizenInfoLabel,
+ Header,
+ TextInput
+} from '@egovernments/digit-ui-react-components'
+
+import { Loader } from "@egovernments/digit-ui-react-components";
+import MultiUploadWrapper from './MultiUploadWrapper';
+import { Controller } from "react-hook-form";
+
+
+
+
+const CustomUploadFileComposer = ({module, config, control, register, formData, errors, localePrefix, customClass, customErrorMsg,mdmsModuleName='works'}) => {
+ console.log("RENDERED ========== CustomUploadFileComposer")
+ const { t } = useTranslation()
+
+ //fetch mdms config based on module name
+ // const tenant = Digit.ULBService.getStateId();
+ // const { isLoading, data } = Digit.Hooks.useCustomMDMS(
+ // tenant,
+ // mdmsModuleName,
+ // [
+ // {
+ // "name": "DocumentConfig",
+ // "filter": `[?(@.module=='${module == null || module == undefined ? config?.module : module}')]`
+ // }
+ // ]
+ // );
+
+
+ // console.log('isLoading : ', isLoading)
+ // console.log('data : ', data)
+
+ let docConfig = {}
+ if (config?.DocumentConfig) {
+ docConfig = config?.DocumentConfig
+ } else if (data?.[mdmsModuleName]?.DocumentConfig?.[0]) {
+ docConfig = data?.[mdmsModuleName]?.DocumentConfig?.[0]
+ }
+
+ let documentFileTypeMappings = {
+ docx : "vnd.openxmlformats-officedocument.wordprocessingml.document",
+ doc : "application/msword",
+ png : "png",
+ pdf : "pdf",
+ jpeg : "jpeg",
+ jpg : "jpeg",
+ xls : "vnd.ms-excel",
+ xlsx : "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ csv : "csv"
+ }
+
+ const getRegex = (allowedFormats) => {
+ // console.log(allowedFormats);
+ // if(allowedFormats?.length) {
+ // const obj = { "expression" : `/(.*?)(${allowedFormats?.join('|')})$/`}
+ // const stringified = JSON.stringify(obj);
+ // console.log(new RegExp(JSON.parse(stringified).expression.slice(1, -1)));
+ // return new RegExp(JSON.parse(stringified).expression.slice(1, -1));
+ // } else if(docConfig?.allowedFileTypes?.length) {
+ // const obj = { "expression" : `/(.*?)(${docConfig?.allowedFileTypes?.join('|')})$/`}
+ // const stringified = JSON.stringify(obj);
+ // console.log(new RegExp(JSON.parse(stringified).expression.slice(1, -1)))
+ // return new RegExp(JSON.parse(stringified).expression.slice(1, -1));
+ // }
+ // return /(.*?)(pdf|docx|jpeg|jpg|png|msword|openxmlformats-officedocument|wordprocessingml|document|spreadsheetml|sheet)$/
+ if(allowedFormats?.length) {
+ let exceptedFileTypes = [];
+ allowedFormats?.forEach(allowedFormat=>{
+ exceptedFileTypes.push(documentFileTypeMappings[allowedFormat]);
+ });
+ exceptedFileTypes = exceptedFileTypes.join("|");
+ return new RegExp(`(.*?)(${exceptedFileTypes})$`)
+ }
+ return /(.*?)(pdf|docx|jpeg|jpg|png|msword|openxmlformats-officedocument|wordprocessingml|document|spreadsheetml|sheet)$/
+ }
+
+ // if(isLoading) return
+ // if(true) return Hello
+ return (
+
+
+ {t('WORKS_RELEVANT_DOCUMENTS')}
+
+ {
+ docConfig?.documents?.map((item, index) => {
+ if(item?.active)
+ return (
+
+ { item.code && (
+
+ { t(`${config?.populators?.localePrefix}_${item?.code}`)} { item?.isMandatory ? " * " : null }
+ )
+ }
+
+
+ {
+ item?.showTextInput ?
+
:
+ null
+ }
+
+ {
+ function getFileStoreData(filesData) {
+ const numberOfFiles = filesData.length;
+ let finalDocumentData = [];
+ if (numberOfFiles > 0) {
+ filesData.forEach((value) => {
+ finalDocumentData.push({
+ fileName: value?.[0],
+ fileStoreId: value?.[1]?.fileStoreId?.fileStoreId,
+ documentType: value?.[1]?.file?.type,
+ });
+ });
+ }
+ onChange(numberOfFiles>0?filesData:[]);
+ }
+ return (
+
+ )
+ }}
+ rules={{validate:(value) => {
+ return !(item?.isMandatory && value?.length === 0)
+ }}}
+ defaultValue={formData?.[item?.name]}
+ name={`${config?.name}.${item?.name}`}
+ control={control}
+ />
+ { errors && errors[`${config?.name}`]?.[`${item?.name}`] && Object.keys(errors[`${config?.name}`]?.[`${item?.name}`]).length ? (
+
+ {t(config?.error)}
+ ) : null
+ }
+
+
+
+ )
+ })
+
+ }
+
+
+ )
+}
+
+export default CustomUploadFileComposer
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/components/MultiUploadWrapper.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/components/MultiUploadWrapper.js
new file mode 100644
index 0000000..182d5a5
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/components/MultiUploadWrapper.js
@@ -0,0 +1,145 @@
+import React, { useEffect, useReducer, useState } from "react"
+import UploadFile from "./UploadFile";
+
+
+const displayError = ({ t, error, name }, customErrorMsg) => (
+
+ {customErrorMsg ? t(customErrorMsg) : t(error)}
+ {customErrorMsg ? '' : `${t('ES_COMMON_DOC_FILENAME')} : ${name} ...`}
+
+)
+
+const fileValidationStatus = (file, regex, maxSize, t) => {
+
+ const status = { valid: true, name: file?.name?.substring(0, 15), error: '' };
+ if (!file) return;
+
+ if (!regex.test(file.type) && (file.size / 1024 / 1024) > maxSize) {
+ status.valid = false; status.error = t(`NOT_SUPPORTED_FILE_TYPE_AND_FILE_SIZE_EXCEEDED_5MB`);
+ }
+
+ if (!regex.test(file.type)) {
+ status.valid = false; status.error = t(`NOT_SUPPORTED_FILE_TYPE`);
+ }
+
+ if ((file.size / 1024 / 1024) > maxSize) {
+ status.valid = false; status.error = t(`FILE_SIZE_EXCEEDED_5MB`);
+ }
+
+ return status;
+}
+const checkIfAllValidFiles = (files, regex, maxSize, t, maxFilesAllowed, state) => {
+ if (!files.length || !regex || !maxSize) return [{}, false];
+
+ // added another condition files.length > 0 , for when user uploads files more than maxFilesAllowed in one go the
+ const uploadedFiles = state.length + 1
+ if ( maxFilesAllowed && (uploadedFiles > maxFilesAllowed || files.length > maxFilesAllowed)) return [[{ valid: false, name: files[0]?.name?.substring(0, 15), error: t(`FILE_LIMIT_EXCEEDED`)}],true]
+
+ // Adding a check for fileSize > maxSize
+ // const maxSizeInBytes = maxSize * 1000000
+ // if(files?.some(file => file.size > maxSizeInBytes)){
+ // return [[{ valid: false, name: "", error: t(`FILE_SIZE_EXCEEDED_5MB`) }], true]
+ // }
+
+ const messages = [];
+ let isInValidGroup = false;
+ for (let file of files) {
+ const fileStatus = fileValidationStatus(file, regex, maxSize, t);
+ if (!fileStatus.valid) {
+ isInValidGroup = true;
+ }
+ messages.push(fileStatus);
+ }
+
+ return [messages, isInValidGroup];
+}
+
+// can use react hook form to set validations @neeraj-egov
+const MultiUploadWrapper = ({ t, module = "PGR", tenantId = Digit.ULBService.getStateId(), getFormState, requestSpecifcFileRemoval, extraStyleName = "", setuploadedstate = [], showHintBelow, hintText, allowedFileTypesRegex = /(.*?)(jpg|jpeg|webp|aif|png|image|pdf|msword|openxmlformats-officedocument|xls|xlsx|openxmlformats-officedocument|wordprocessingml|document|spreadsheetml|sheet|ms-excel)$/i, allowedMaxSizeInMB = 10, acceptFiles = "image/*, .jpg, .jpeg, .webp, .aif, .png, .image, .pdf, .msword, .openxmlformats-officedocument, .dxf, .xlsx, .xls, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", maxFilesAllowed, customClass="", customErrorMsg,containerStyles }) => {
+ const FILES_UPLOADED = "FILES_UPLOADED"
+ const TARGET_FILE_REMOVAL = "TARGET_FILE_REMOVAL"
+
+ const [fileErrors, setFileErrors] = useState([]);
+ const [enableButton, setEnableButton] = useState(true)
+
+ const uploadMultipleFiles = (state, payload) => {
+ const { files, fileStoreIds } = payload;
+ const filesData = Array.from(files)
+ const newUploads = filesData?.map((file, index) => [file.name, { file, fileStoreId: fileStoreIds[index] }])
+ return [...state, ...newUploads]
+ }
+
+ const removeFile = (state, payload) => {
+ const __indexOfItemToDelete = state.findIndex(e => e[1].fileStoreId.fileStoreId === payload.fileStoreId.fileStoreId)
+ const mutatedState = state.filter((e, index) => index !== __indexOfItemToDelete)
+ setFileErrors([])
+ return [...mutatedState]
+ }
+
+ const uploadReducer = (state, action) => {
+ switch (action.type) {
+ case FILES_UPLOADED:
+ return uploadMultipleFiles(state, action.payload)
+ case TARGET_FILE_REMOVAL:
+ return removeFile(state, action.payload)
+ default:
+ break;
+ }
+ }
+
+ const [state, dispatch] = useReducer(uploadReducer, [...setuploadedstate])
+
+ const onUploadMultipleFiles = async (e) => {
+ setEnableButton(false)
+ setFileErrors([])
+ const files = Array.from(e.target.files);
+
+ if (!files.length) return;
+ const [validationMsg, error] = checkIfAllValidFiles(files, allowedFileTypesRegex, allowedMaxSizeInMB, t, maxFilesAllowed, state);
+
+ if (!error) {
+ try {
+ const { data: { files: fileStoreIds } = {} } = await Digit.UploadServices.MultipleFilesStorage(module, e.target.files, tenantId)
+ setEnableButton(true)
+ return dispatch({ type: FILES_UPLOADED, payload: { files: e.target.files, fileStoreIds } })
+ } catch (err) {
+ setEnableButton(true)
+ }
+ } else {
+ setFileErrors(validationMsg)
+ setEnableButton(true)
+ }
+ }
+
+ useEffect(() => getFormState(state), [state])
+
+ useEffect(() => {
+ requestSpecifcFileRemoval ? dispatch({ type: TARGET_FILE_REMOVAL, payload: requestSpecifcFileRemoval }) : null
+ }, [requestSpecifcFileRemoval])
+
+ return (
+
+ onUploadMultipleFiles(e)}
+ removeTargetedFile={(fileDetailsData) => dispatch({ type: TARGET_FILE_REMOVAL, payload: fileDetailsData })}
+ uploadedFiles={state}
+ multiple={true}
+ showHintBelow={showHintBelow}
+ hintText={hintText}
+ extraStyleName={extraStyleName}
+ onDelete={() => {
+ setFileErrors([])
+ }}
+ accept={acceptFiles}
+ customClass={customClass}
+ enableButton={enableButton}
+ />
+
+ {fileErrors.length ? fileErrors.map(({ valid, name, type, size, error }) => (
+ valid ? null : displayError({ t, error, name }, customErrorMsg)
+ )) : null}
+
+
)
+}
+
+export default MultiUploadWrapper
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/components/UploadFile.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/components/UploadFile.js
new file mode 100644
index 0000000..bc43c9b
--- /dev/null
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/components/UploadFile.js
@@ -0,0 +1,257 @@
+import { ButtonSelector, Close, RemoveableTag } from "@egovernments/digit-ui-react-components";
+import React, { useEffect, useRef, useState, Fragment } from "react";
+// import ButtonSelector from "./ButtonSelector";
+// import { Close } from "./svgindex";
+import { useTranslation } from "react-i18next";
+// import RemoveableTag from "./RemoveableTag";
+
+const getRandomId = () => {
+ return Math.floor((Math.random() || 1) * 139);
+};
+
+const getCitizenStyles = (value) => {
+ let citizenStyles = {};
+ if (value == "propertyCreate") {
+ citizenStyles = {
+ textStyles: {
+ whiteSpace: "nowrap",
+ width: "100%",
+ overflow: "hidden",
+ textOverflow: "ellipsis",
+ width: "80%"
+ },
+ tagStyles: {
+ width: "90%",
+ flexWrap: "nowrap",
+ },
+ inputStyles: {
+ width: "44%",
+ minHeight: "2rem",
+ maxHeight: "3rem",
+ top: "20%"
+ },
+ buttonStyles: {
+ height: "auto",
+ minHeight: "2rem",
+ width: "40%",
+ maxHeight: "3rem"
+ },
+ tagContainerStyles: {
+ width: "60%",
+ display: "flex",
+ marginTop: "0px"
+ },
+ closeIconStyles: {
+ width : "20px"
+ },
+ containerStyles: {
+ padding: "10px",
+ marginTop: "0px"
+ },
+
+ };
+ } else if (value == "IP") {
+ citizenStyles = {
+ textStyles: {
+ whiteSpace: "nowrap",
+ maxWidth: "250px",
+ overflow: "hidden",
+ textOverflow: "ellipsis",
+ },
+ tagStyles: {
+ marginLeft:"-30px"
+ },
+ inputStyles: {},
+ closeIconStyles: {
+ position:"absolute",
+ marginTop:"-12px"
+ },
+ buttonStyles: {},
+ tagContainerStyles: {},
+ };
+ } else if (value == "OBPS") {
+ citizenStyles = {
+ containerStyles: {
+ display: "flex",
+ justifyContent: "flex-start",
+ alignItems: "center",
+ flexWrap: "wrap",
+ margin: "0px",
+ padding: "0px"
+ },
+ tagContainerStyles: {
+ margin: "0px",
+ padding: "0px",
+ width: "46%"
+ },
+ tagStyles: {
+ height: "auto",
+ padding: "8px",
+ margin: 0,
+ width: "100%",
+ margin: "5px"
+ },
+ textStyles: {
+ wordBreak: "break-word",
+ height: "auto",
+ lineHeight: "16px",
+ overflow: "hidden",
+ // minHeight: "35px",
+ maxHeight: "34px"
+ },
+ inputStyles: {
+ width: "43%",
+ minHeight: "42px",
+ maxHeight: "42px",
+ top: "5px",
+ left: "5px"
+ },
+ buttonStyles: {
+ height: "auto",
+ minHeight: "40px",
+ width: "43%",
+ maxHeight: "40px",
+ margin: "5px",
+ padding: "0px"
+ },
+ closeIconStyles: {
+ width : "20px"
+ },
+ uploadFile: {
+ minHeight: "50px"
+ }
+ };
+ }
+ else {
+ citizenStyles = {
+ textStyles: {},
+ tagStyles: {},
+ inputStyles: {},
+ buttonStyles: {},
+ tagContainerStyles: {},
+ };
+ }
+ return citizenStyles;
+};
+
+const UploadFile = (props) => {
+
+ const { t } = useTranslation();
+ const inpRef = useRef();
+ const [hasFile, setHasFile] = useState(false);
+ const [prevSate, setprevSate] = useState(null);
+ const user_type = Digit.SessionStorage.get("userType");
+ let extraStyles = {};
+ const handleChange = () => {
+ if (inpRef.current.files[0])
+ { setHasFile(true);
+ setprevSate(inpRef.current.files[0])
+ }
+ else setHasFile(false);
+ };
+
+ // for common aligmnent issues added common styles
+ extraStyles = getCitizenStyles("OBPS");
+
+ // if (window.location.href.includes("/obps") || window.location.href.includes("/noc")) {
+ // extraStyles = getCitizenStyles("OBPS");
+ // } else {
+ // switch (props.extraStyleName) {
+ // case "propertyCreate":
+ // extraStyles = getCitizenStyles("propertyCreate");
+ // break;
+ // case "IP":
+ // extraStyles = getCitizenStyles("IP");
+ // break;
+ // case "OBPS":
+ // extraStyles = getCitizenStyles("OBPS");
+ // default:
+ // extraStyles = getCitizenStyles("");
+ // }
+ // }
+
+ const handleDelete = () => {
+ inpRef.current.value = "";
+ props.onDelete();
+ };
+
+ const handleEmpty = () => {
+ if(inpRef.current.files.length <= 0 && prevSate !== null)
+ { inpRef.current.value = "";
+ props.onDelete();
+ }
+ };
+
+ if (props.uploadMessage && inpRef.current.value) {
+ handleDelete();
+ setHasFile(false);
+ }
+
+ useEffect(() => handleEmpty(), [inpRef?.current?.files])
+
+ useEffect(() => handleChange(), [props.message]);
+
+ const showHint = props?.showHint || false;
+
+ return (
+
+ {showHint && {t(props?.hintText)}
}
+
+
+
+ {props?.uploadedFiles?.map((file, index) => {
+ const fileDetailsData = file[1]
+ return
+ props?.removeTargetedFile(fileDetailsData, e)} />
+
+ })}
+ {props?.uploadedFiles.length === 0 &&
{props?.message} }
+ {!hasFile || props.error ? (
+
{props.message}
+ ) : (
+
+
+
+ {(typeof inpRef.current.files[0]?.name !== "undefined") && !(props?.file) ? inpRef.current.files[0]?.name : props.file?.name}
+
+ handleDelete()} style={extraStyles ? extraStyles?.closeIconStyles : null}>
+
+
+
+
+ )}
+
+
props.onUpload(e)}
+ onClick ={ event => {
+ if (props?.disabled) {
+ event.preventDefault()
+ return
+ }
+ const { target = {} } = event || {};
+ target.value = "";
+ }}
+ />
+
+ {props.iserror && {props.iserror}
}
+ {props?.showHintBelow && {t(props?.hintText)}
}
+
+ );
+};
+
+export default UploadFile;
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/IndividualCreateConfig.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/IndividualCreateConfig.js
index 822700f..1fdf443 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/IndividualCreateConfig.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/IndividualCreateConfig.js
@@ -46,6 +46,14 @@ export const newConfig = [
disable: false,
populators: { name: "mobileNumber", error: "Required", validation: { min: 0, max: 9999999999 } },
},
+ {
+ label: "Aadhaar number",
+ isMandatory: true,
+ key: "aadhaarNumber",
+ type: "number",
+ disable: false,
+ populators: { name: "aadhaarNumber", error: "Required", validation: { min: 0, max: 9999999999 } },
+ },
],
},
{
@@ -72,42 +80,81 @@ export const newConfig = [
},
],
},
- // {
- // head: "Sample Details",
- // key: "sampleDetails",
- // body: [
- // {
- // isMandatory: false,
- // key: "sampleDetails",
- // type: "component", // for custom component
- // component: "SampleMultiComponent", // name of the component as per component registry
- // withoutLabel: true,
- // disable: false,
- // customProps: {},
- // populators: {
- // name: "sampleDetails",
- // required: true,
- // },
- // },
- // ],
- // },
- // {
- // head: "Additional Details",
- // key: "additionalDetails",
- // body: [
- // {
- // isMandatory: false,
- // key: "additionalDetails",
- // type: "component", // for custom component
- // component: "SampleAdditionalComponent", // name of the component as per component registry
- // withoutLabel: true,
- // disable: false,
- // customProps: {},
- // populators: {
- // name: "additionalDetails",
- // required: true,
- // },
- // },
- // ],
- // },
+ {
+ head: "DOCUMENT_UPLOAD",
+ body: [
+ {
+ isMandatory: false,
+ key: "documents",
+ type: "component", // for custom component
+ component: "CustomUploadFileComposer", // name of the component as per component registry
+ withoutLabel: true,
+ disable: false,
+ customProps: {},
+ module: "Application",
+ populators: {
+ name: "documents",
+ required: true,
+ localePrefix: "APPLICATION_UPLOAD_",
+ },
+ }
+ ],
+ },
+ {
+ head: "Bank Account Details",
+ body: [
+ {
+ inline: true,
+ label: "Account Holder Name",
+ isMandatory: true,
+ key: "accountHolderName",
+ type: "text",
+ disable: false,
+ populators: {
+ name: "accountHolderName",
+ error: " Required ",
+ validation: { pattern: "^[a-zA-Z0-9 .\\-_@\\']*$", minlength : 2, maxlength: 64 }
+ },
+ },
+ {
+ inline: true,
+ label: "Account Number",
+ isMandatory: true,
+ key: "accountNumber",
+ type: "text",
+ disable: false,
+ populators: {
+ name: "accountNumber",
+ error: " Required ",
+ validation: {pattern: "^[0-9]{9,18}$", minlength : 9, maxlength: 18}
+ },
+ },
+ {
+ isMandatory: true,
+ key: "accountType",
+ type: "dropdown",
+ label: "Enter Account Type",
+ disable: false,
+ populators: {
+ name: "accountType",
+ optionsKey: "name",
+ error: "required ",
+ mdmsConfig: {
+ masterName: "BankAccType",
+ moduleName: "ubp",
+ localePrefix: "MASTERS",
+ },
+ },
+ },
+ {
+ inline: true,
+ label: "IFSC Code",
+ isMandatory: true,
+ key: "ifscCode",
+ type: "text",
+ disable: false,
+ populators: { name: "ifscCode", error: " Required", validation: { pattern: /^[a-zA-Z0-9_]+$/i } },
+ },
+ ],
+ },
];
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/schemeConfigs.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/schemeConfigs.js
index 75addef..4c280f6 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/schemeConfigs.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/schemeConfigs.js
@@ -1,4 +1,5 @@
export const SCHEME={
- SCHEMES_SCHEMA_CODE:"selco.scheme1",
- OTP_LENGTH:6
+ SCHEMES_SCHEMA_CODE:"ubp.program",
+ OTP_LENGTH:6,
+ UBP_MDMS_MODULE: 'ubp'
}
\ No newline at end of file
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/index.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/index.js
index 5c5c704..4dfb3c6 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/index.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/index.js
@@ -34,6 +34,7 @@ const IndividualApp = ({
const { path } = useRouteMatch();
const history = useHistory();
const [formData, setFormData] = React.useState("en");
+
return (
@@ -43,7 +44,12 @@ const IndividualApp = ({
className={"loginContainer"}
style={{ "--banner-url": `url(${window?.globalConfigs?.getConfig?.("HOME_BACKGROUND")})`, padding: "0px" }}
>
-
+
-
+ {/* */}
{
const { id } = useParams();
@@ -21,9 +23,83 @@ const IndividualCreate = () => {
};
const [showPopup, setShowPopUp] = React.useState(false);
const [formData, setFormData] = React.useState({});
+ const [showLoader, setShowLoader] = React.useState(true);
+ const tenant = Digit.ULBService.getStateId();
+ const { isLoading: isDocConfigLoading, data:uploadConfig } = Digit.Hooks.useCustomMDMS(
+ tenant,
+ SCHEME.UBP_MDMS_MODULE,
+ [
+ {
+ "name": "DocumentConfig",
+ "filter": `[?(@.module=='Application')]`
+ }
+ ]
+ );
+
+
+ const reqCriteria = {
+ url: "/mdms-v2/v2/_search",
+ params: {},
+ body: {
+ MdmsCriteria: {
+ tenantId: Digit.ULBService.getStateId(),
+ schemaCode: SCHEME.SCHEMES_SCHEMA_CODE,
+ uniqueIdentifiers: [id],
+ },
+ },
+ config: {
+ select: (data) => {
+ return data?.mdms?.map((obj) => ({ ...obj?.data?.en, id: obj?.data?.id }));
+ },
+ },
+ };
+ const { isLoading:isProgramLoading, data:programData } = Digit.Hooks.useCustomAPIHook(reqCriteria);
+
+ useEffect(() => {
+
+ if (isProgramLoading == false && isDocConfigLoading == false) {
+ const docConfig = uploadConfig?.[SCHEME.UBP_MDMS_MODULE]?.DocumentConfig?.[0]
+ // process doc config and generate dynamic file upload configs
+ console.log(docConfig)
+ console.log(programData)
+ console.log(newConfig)
+
+ if (docConfig != null) {
+ newConfig.forEach((config) => {
+ if(config.head == 'DOCUMENT_UPLOAD') {
+ config.body[0].DocumentConfig = generateFileUploadConfiguration(programData, docConfig)
+ console.log(config)
+ }
+ })
+ }
+ setShowLoader(false)
+ }
+ }, [isProgramLoading, isDocConfigLoading])
// user-otp/v1/_send?tenantId=pg
+ const generateFileUploadConfiguration = (programDetails, docConfig) => {
+ let fields = programDetails?.[0].applicationForm.formDetails.fields;
+ if (fields != null && fields.length) {
+ let sampleDocConfig = docConfig.documents[0]
+ let listofFiles = []
+ fields.forEach((field) =>{
+ console.log(field)
+
+ sampleDocConfig['code'] = field.fieldName
+ .trim()
+ .split(' ') // Split by spaces
+ .join('_')
+ .toUpperCase(); // Join words with underscore
+ sampleDocConfig['isMandatory'] = field.required
+ sampleDocConfig['name'] = field.fieldName
+ listofFiles.push(JSON.parse(JSON.stringify(sampleDocConfig)))
+ })
+ docConfig.documents = listofFiles
+ }
+ return docConfig;
+ }
+
const mutation = Digit.Hooks.useCustomAPIMutationHook(reqCreate);
const onError = (resp) => {
history.push(`/${window.contextPath}/individual/enroll-response?isSuccess=${false}`, { message: "SUBMISSION_CREATION_FAILED" });
@@ -36,10 +112,10 @@ const IndividualCreate = () => {
label: "SUBMISSION_ID",
});
};
- const createIndividual = async () => {
+ const submitApplication = async () => {
await mutation.mutate(
{
- url: `/individual/v1/_create`,
+ url: `/ubp-bff-service/benefit/_apply`,
params: { tenantId },
body: transformCreateData(formData),
config: {
@@ -53,18 +129,21 @@ const IndividualCreate = () => {
);
};
const onSubmit = async (data) => {
- console.log(data, "data");
setFormData(data);
+ console.log('transformCreateData : ', transformCreateData(programData, data))
setShowPopUp(true);
};
+
+ if (showLoader) return
+
return (
{
formData={formData}
onSuccess={(succeded = true) => {
setShowPopUp(false);
- succeded && createIndividual();
+ succeded && submitApplication();
}}
>
)}
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Program.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Program.js
index 4a3a0e5..68c94f4 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Program.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Program.js
@@ -39,7 +39,7 @@ const Program = ({}) => {
{
{
headerClasName=""
subHeader=""
subHeaderClasName=""
- body="find all the schemes avallible here"
+ body="Find all the schemes avallible here"
bodyClasName=""
>
@@ -56,7 +56,7 @@ const Programs = ({}) => {
{
+export const transformCreateData = (programData, formData) => {
+ let documents = []
+ if (formData?.undefined && Object.keys(formData?.undefined).length) {
+ Object.keys(formData.undefined).forEach(function(key) {
+
+ console.log(key, formData.undefined[key]);
+ if (formData.undefined && formData.undefined[key]) {
+ let docDetails = formData.undefined[key];
+ formData.undefined[key].forEach((docMain) => {
+ docMain.forEach((docObj) => {
+ if(typeof(docObj) == 'object') {
+ documents.push({
+ 'documentType': key,
+ 'fileStore': docObj.fileStoreId.fileStoreId,
+ })
+ }
+ })
+ })
+ }
+ });
+ }
+ let localityObj = null;
+ if (formData?.locality?.code) {
+ localityObj = {
+ code: formData?.locality?.code || "SUN01",
+ }
+ }
+
return {
Individual: {
tenantId: Digit.ULBService.getStateId(),
name: {
- givenName: data.applicantname,
+ givenName: formData.applicantname,
},
- dateOfBirth: null,
- gender: data?.genders?.code,
- mobileNumber: data.mobileNumber,
+ dateOfBirth: formData?.dob ? formData?.dob.split("-").reverse().join("/"): null,
+ gender: formData?.genders?.code,
+ mobileNumber: formData.mobileNumber,
address: [
{
tenantId: Digit.ULBService.getStateId(),
- pincode: data.pincode,
- city: data.city,
- street: data.street,
- doorNo: data.doorno,
- locality: {
- code: data?.locality?.code || "SUN01",
- },
- landmark: data.landmark,
+ pincode: formData.pincode,
+ city: formData.city,
+ street: formData.street,
+ doorNo: formData.doorno,
+ locality: localityObj,
+ landmark: formData.landmark,
type: "PERMANENT",
},
],
- identifiers: null,
- skills: [
+ identifiers: [
{
- type: "DRIVING",
- level: "UNSKILLED",
- },
+ "identifierType": "AADHAAR",
+ "identifierId": formData.aadhaarNumber
+ }
],
+ // skills: [
+ // {
+ // type: "DRIVING",
+ // level: "UNSKILLED",
+ // },
+ // ],
photograph: null,
+ isSystemUser: false,
additionalFields: {
- fields: [
- {
- key: "EMPLOYER",
- value: "ULB",
- },
- ],
- },
- isSystemUser: null,
- userDetails: {
- username: data.mobileNumber,
- tenantId: Digit.ULBService.getStateId(),
- roles: [
- {
- code: "SANITATION_WORKER",
- tenantId: Digit.ULBService.getStateId(),
- },
- ],
- type: "CITIZEN",
+ // fields: [
+ // {
+ // key: "EMPLOYER",
+ // value: "ULB",
+ // },
+ // ],
},
+ // userDetails: {
+ // username: formData.mobileNumber,
+ // tenantId: Digit.ULBService.getStateId(),
+ // roles: [
+ // {
+ // code: "SANITATION_WORKER",
+ // tenantId: Digit.ULBService.getStateId(),
+ // },
+ // ],
+ // type: "CITIZEN",
+ // },
+ },
+ Application: {
+ "tenantId": Digit.ULBService.getStateId(),
+ "individualId": null,
+ "programCode": programData?.data?.id,
+ "documents": documents
},
+ BankAccount: {
+ "tenantId": Digit.ULBService.getStateId(),
+ "serviceCode": "IND",
+ "referenceId": null,
+ "bankAccountDetails": [
+ {
+ "tenantId": Digit.ULBService.getStateId(),
+ "accountHolderName": formData.accountHolderName,
+ "accountNumber": formData.accountNumber,
+ "accountType": formData?.accountType?.code,
+ "isPrimary": true,
+ "bankBranchIdentifier": {
+ "type": "IFSC",
+ "code": formData?.ifscCode,
+ "additionalDetails": {}
+ },
+ "isActive": true,
+ "documents": [],
+ "additionalFields": {}
+ }
+ ],
+ "additionalFields": {}
+ }
};
};
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/UploadFileComposer.js b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/UploadFileComposer.js
index c81ef7e..83b4eba 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/UploadFileComposer.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/react-components/src/hoc/UploadFileComposer.js
@@ -24,11 +24,14 @@ const UploadFileComposer = ({module, config, Controller, control, register, form
}
]
);
-
+
+ console.log("isLoading : ", isLoading)
+ console.log("data : ", data)
const docConfig = data?.[mdmsModuleName]?.DocumentConfig?.[0]
+
let documentFileTypeMappings = {
docx : "vnd.openxmlformats-officedocument.wordprocessingml.document",
doc : "application/msword",
diff --git a/utilities/bff-service/src/api.js b/utilities/bff-service/src/api.js
index 2c20a35..1be0a31 100644
--- a/utilities/bff-service/src/api.js
+++ b/utilities/bff-service/src/api.js
@@ -139,6 +139,20 @@ function search_application(request, limit, offset) {
})
}
+function create_application(request) {
+ return new Promise((resolve, reject) => {
+ let newRequest = JSON.parse(JSON.stringify(request))
+ let promise = new axios({
+ method: "POST",
+ url: url.resolve(config.host.application, config.paths.application_create),
+ data: newRequest,
+ });
+ promise.then((data) => {
+ resolve(data.data)
+ }).catch((err) => reject(err))
+ })
+}
+
function search_individual(request, tenantId, limit, offset) {
return new Promise((resolve, reject) => {
let params = encodeQueryData({tenantId, offset, limit})
@@ -155,6 +169,22 @@ function search_individual(request, tenantId, limit, offset) {
})
}
+function create_individual(request) {
+ return new Promise((resolve, reject) => {
+ let newRequest = JSON.parse(JSON.stringify(request))
+ let createUrl = url.resolve(config.host.individual, config.paths.individual_create)
+ let promise = new axios({
+ method: "POST",
+ url: createUrl,
+ data: newRequest,
+ });
+ promise.then((data) => {
+ resolve(data.data)
+ }).catch((err) => reject(err))
+ })
+}
+
+
function encodeQueryData(data) {
const ret = [];
for (let d in data)
@@ -177,6 +207,20 @@ function search_bank_account_details(request) {
})
}
+function create_bank_account_details(request) {
+ return new Promise((resolve, reject) => {
+ let newRequest = JSON.parse(JSON.stringify(request))
+ let promise = new axios({
+ method: "POST",
+ url: url.resolve(config.host.bankaccount, config.paths.bankaccount_create),
+ data: newRequest,
+ });
+ promise.then((data) => {
+ resolve(data.data)
+ }).catch((err) => reject(err))
+ })
+}
+
/**
*
@@ -249,8 +293,11 @@ module.exports = {
search_localization,
search_disbursal,
search_application,
+ create_application,
search_bank_account_details,
+ create_bank_account_details,
search_individual,
+ create_individual,
upload_file_using_filestore,
create_eg_payments_excel,
reset_eg_payments_excel,
diff --git a/utilities/bff-service/src/app.js b/utilities/bff-service/src/app.js
index 1631992..0a77cda 100644
--- a/utilities/bff-service/src/app.js
+++ b/utilities/bff-service/src/app.js
@@ -6,6 +6,7 @@ var logger = require("morgan");
var config = require("./config");
var groupBills = require("./routes/groupBill");
+var submitApplication = require("./routes/submitApplication");
// var {listenConsumer} = require("./consumer")
@@ -25,6 +26,7 @@ app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));
app.use(config.app.contextPath + "/payment", groupBills);
+app.use(config.app.contextPath + "/benefit/_apply", submitApplication);
diff --git a/utilities/bff-service/src/config.js b/utilities/bff-service/src/config.js
index 50294cb..d29a5f9 100644
--- a/utilities/bff-service/src/config.js
+++ b/utilities/bff-service/src/config.js
@@ -47,8 +47,11 @@ module.exports = {
mdms_get: "/egov-mdms-service/v1/_get",
disbursal_search: "/disbursal/v1/_search",
bankaccount_search: "/bankaccount-service/bankaccount/v1/_search",
+ bankaccount_create: "/bankaccount-service/bankaccount/v1/_create",
localization_search: "/localization/messages/v1/_search",
application_search: "/application/v1/_search",
- individual_search: "/individual/v1/_search"
+ application_create: "/application/v1/_create",
+ individual_search: "/individual/v1/_search",
+ individual_create: "/individual/v1/_create"
}
};
diff --git a/utilities/bff-service/src/routes/submitApplication.js b/utilities/bff-service/src/routes/submitApplication.js
new file mode 100644
index 0000000..5b81d57
--- /dev/null
+++ b/utilities/bff-service/src/routes/submitApplication.js
@@ -0,0 +1,238 @@
+var express = require("express");
+var router = express.Router();
+var config = require("../config");
+const { asyncMiddleware } = require("../utils/asyncMiddleware");
+const { search_individual, search_application, create_individual, search_bank_account_details, create_application, create_bank_account_details } = require("../api");
+var logger = require("../logger").logger;
+
+router.post('/',
+ asyncMiddleware(async function (req, res, next) {
+ try {
+ var requestInfo = req.body?.RequestInfo;
+ var individualRequest = req.body?.Individual;
+ var applicationRequest = req.body?.Application;
+ var bankAccountRequest = req.body?.BankAccount;
+
+ if (requestInfo == undefined) {
+ return renderError(res, "RequestInfo can not be null");
+ }
+ if (individualRequest == undefined) {
+ return renderError(res, "Individual can not be null");
+ }
+ if (applicationRequest == undefined) {
+ return renderError(res, "Application can not be null");
+ }
+ var tenantId = individualRequest?.tenantId;
+ if (!tenantId) {
+ return renderError(
+ res,
+ "TenantId are mandatory to generate the group bill"
+ );
+ }
+ let individualExists = false;
+ let alreadyRegistered = false;
+ let bankAccountExists = false;
+ let application;
+ let bankAccount;
+ // get individual is already registered
+ let individual = await getIndividualDetails(individualRequest);
+ if (individual && individual.id) {
+ individualExists = true;
+ // If individual is already registered then check already applied
+ application = await getApplicationDetails(individual.id, applicationRequest.programCode)
+ if (application && application.id) {
+ alreadyRegistered = true;
+ }
+ }
+ // If individual is not regisered then register
+ logger.info('Registering Individual');
+ if (!individualExists) {
+ individual = await createAndGetIndividualResponse(requestInfo, individualRequest);
+ if (!(individual && individual.id)) {
+ logger.info('Individual creation failed!');
+ throw new Error('Individual creation failed!');
+ }
+ logger.info("Individual Created ----- " + JSON.stringify(individual))
+ }
+ // If not applied then apply
+ if (!alreadyRegistered) {
+ applicationRequest['individualId'] = individual.id;
+ application = await createAndGetApplicationResponse(requestInfo, applicationRequest);
+ logger.info("Application registered! " + JSON.stringify(application))
+ }
+ if (individual) {
+ let bankAccounts = await getBankAccountDetail(individual.tenantId, individual.id, requestInfo)
+ if (bankAccounts && bankAccounts.length) {
+ logger.info('Bank Accounts found');
+ bankAccount = bankAccounts[0]
+ bankAccountExists = true;
+ } else {
+ bankAccountRequest['referenceId'] = individual.id
+ bankAccount = await createBankAccount(requestInfo, bankAccountRequest)
+ }
+ }
+ let response = {
+ 'ResponseInfo': {
+ "apiId": "submitApplication","ver": "v1","ts": 0,"resMsgId": "uief87324","msgId": "submit application","status": "successful"
+ },
+ 'Individual': individual,
+ 'Application': application,
+ 'BankAccount': bankAccount
+ }
+ if (alreadyRegistered && bankAccountExists) {
+ response.message = 'applicant_already_registered_for_program'
+ } else if (individual && application && bankAccount) {
+ response.message = 'applicant_registered_for_program';
+ }
+ res.send(response)
+ } catch (error) {
+ logger.error(error.stack || err);
+ return renderError(res, `Failed to register application.`);
+ }
+
+ })
+)
+
+
+function renderError(res, errorMessage, errorCode) {
+ if (errorCode == undefined) errorCode = 500;
+ res.status(errorCode).send({ errorMessage });
+}
+
+let getIndividualSearchCriteria = (individualRequest) => {
+ if (individualRequest['identifiers'] && individualRequest['identifiers']?.length) {
+ return {
+ "tenantId": individualRequest.tenantId,
+ "identifier": {
+ "identifierType": "AADHAAR",
+ "identifierId": individualRequest['identifiers'][0].identifierId
+ }
+ };
+ } else {
+ throw err('Identifiers not present.')
+ }
+}
+
+let getIndividualDetails = async (individualRequest) => {
+ let individualSearchRequest = {};
+
+ individualSearchRequest.RequestInfo = getRequestInfo()
+ individualSearchRequest.Individual = getIndividualSearchCriteria(individualRequest)
+
+ let individualResponse = await search_individual(individualSearchRequest, individualRequest?.tenantId, 1, 0);
+
+ if (individualResponse?.Individual && individualResponse.Individual.length) {
+ return individualResponse.Individual[0]
+ } else {
+ return null
+ }
+}
+
+let getApplicationDetails = async (individualId, programCode) => {
+ if (!individualId || !programCode) {
+ throw err('ProgramCode not present.')
+ }
+ let applicationSearchRequest = {};
+ applicationSearchRequest.RequestInfo = getRequestInfo()
+ applicationSearchRequest.criteria = {
+ 'individualId': individualId,
+ 'programCode': programCode
+ }
+ let applicationResponse = await search_application(applicationSearchRequest, 10, 0)
+ if (applicationResponse?.Applications && applicationResponse.Applications.length) {
+ return applicationResponse.Applications[0]
+ } else {
+ return null;
+ }
+}
+
+let createAndGetIndividualResponse = async (requestInfo, individualRequest) => {
+ let individualRequestInfo = requestInfo?.userInfo?.uuid ? requestInfo : getRequestInfo();
+ let individualCreateRequest = {
+ 'RequestInfo': individualRequestInfo,
+ 'Individual': individualRequest
+ }
+ let individualCreateResp = await create_individual(individualCreateRequest);
+ if (individualCreateResp && individualCreateResp.Individual) {
+ return individualCreateResp.Individual
+ } else {
+ return null
+ }
+}
+
+let createAndGetApplicationResponse = async (requestInfo, applicationRequest) => {
+ let applicationRequestInfo = requestInfo?.userInfo?.uuid ? requestInfo : getRequestInfo();
+ let applicationCreateRequest = {
+ 'RequestInfo': applicationRequestInfo,
+ 'Application': applicationRequest
+ }
+ let applicationCreateResp = await create_application(applicationCreateRequest);
+ if (applicationCreateResp && applicationCreateResp.Applications) {
+ return applicationCreateResp.Applications[0]
+ } else {
+ return null
+ }
+}
+
+let getBankAccountDetail = async (tenantdId, individualId, requestInfo) => {
+ let bankAccountRequestInfo = requestInfo?.userInfo?.uuid ? requestInfo : getRequestInfo();
+ let bankAccountSearchRequest = {
+ 'RequestInfo': bankAccountRequestInfo,
+ "bankAccountDetails": {
+ "tenantId": tenantdId,
+ "referenceId": [
+ individualId
+ ]
+ }
+ }
+ let bankAccountResp = await search_bank_account_details(bankAccountSearchRequest);
+ if (bankAccountResp && bankAccountResp.bankAccounts && bankAccountResp.bankAccounts.length) {
+ return bankAccountResp.bankAccounts
+ } else {
+ return null
+ }
+}
+
+let createBankAccount = async (requestInfo, bankAccountRequest) => {
+ let bankAccountRequestInfo = requestInfo?.userInfo?.uuid ? requestInfo : getRequestInfo();
+ let bankAccountCreateRequest = {
+ "RequestInfo": bankAccountRequestInfo,
+ "bankAccounts": [
+ bankAccountRequest
+ ]
+ }
+ let bankAccountCreateResp = await create_bank_account_details(bankAccountCreateRequest)
+ if (bankAccountCreateResp && bankAccountCreateResp.bankAccounts && bankAccountCreateResp.bankAccounts.length) {
+ return bankAccountCreateResp.bankAccounts[0]
+ } else {
+ return null
+ }
+}
+
+
+
+let getRequestInfo = () => {
+ return {
+ "apiId": "Rainmaker",
+ "authToken": "17b86f8a-6063-4028-abef-fbf005c1ba75",
+ "userInfo": {
+ "id": 618,
+ "uuid": "40e3b45a-0f64-4e8c-8768-aab82c095b2d",
+ "roles": [
+ {
+ "name": "SUPER USER",
+ "code": "SUPERUSER",
+ "tenantId": "pg.citya"
+ }
+ ],
+ "active": true,
+ "tenantId": "pg.citya",
+ "permanentCity": null
+ },
+ "msgId": "1700205653156|en_IN",
+ "plainAccessRequest": {}
+ }
+
+}
+
+module.exports = router;
\ No newline at end of file
From 39554d85d2a74a288a6cbedd46687f61455fc56d Mon Sep 17 00:00:00 2001
From: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com>
Date: Thu, 3 Oct 2024 13:39:03 +0530
Subject: [PATCH 03/12] Added QR Code
---
.../packages/modules/core/package.json | 5 +-
.../configs/IndividualCreateConfig.js | 2 +-
.../core/src/pages/individual/pages/Enroll.js | 7 ++-
.../src/pages/individual/pages/Program.js | 46 ++++++++++++++++++-
4 files changed, 55 insertions(+), 5 deletions(-)
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/package.json b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/package.json
index b03e924..dd849f0 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/package.json
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/package.json
@@ -16,6 +16,9 @@
"dependencies": {
"@egovernments/digit-ui-components": "0.0.2-beta.38",
"@egovernments/digit-ui-react-components": "1.8.2-beta.12",
+ "html2canvas": "^1.4.1",
+ "jspdf": "^2.5.2",
+ "qrcode.react": "^4.0.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-i18next": "11.16.2",
@@ -34,4 +37,4 @@
"digit-ui",
"core"
]
-}
\ No newline at end of file
+}
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/IndividualCreateConfig.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/IndividualCreateConfig.js
index 1fdf443..d40b41a 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/IndividualCreateConfig.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/IndividualCreateConfig.js
@@ -52,7 +52,7 @@ export const newConfig = [
key: "aadhaarNumber",
type: "number",
disable: false,
- populators: { name: "aadhaarNumber", error: "Required", validation: { min: 0, max: 9999999999 } },
+ populators: { name: "aadhaarNumber", error: "Required", validation: { min: 100000000000, max: 999999999999 } },
},
],
},
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Enroll.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Enroll.js
index b3db43d..befe45e 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Enroll.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Enroll.js
@@ -14,7 +14,7 @@ const IndividualCreate = () => {
const { t } = useTranslation();
const history = useHistory();
const reqCreate = {
- url: `/individual/v1/_create`,
+ url: `/ubp-bff-service/benefit/_apply`,
params: {},
body: {},
config: {
@@ -102,10 +102,12 @@ const IndividualCreate = () => {
const mutation = Digit.Hooks.useCustomAPIMutationHook(reqCreate);
const onError = (resp) => {
+ // debugger
history.push(`/${window.contextPath}/individual/enroll-response?isSuccess=${false}`, { message: "SUBMISSION_CREATION_FAILED" });
};
const onSuccess = (resp) => {
+ // debugger
history.push(`/${window.contextPath}/individual/enroll-response?appNo=${"NEW-NO-1"}&isSuccess=${true}`, {
message: "SUBMISSION_CREATION_SUCCESS",
showID: true,
@@ -117,7 +119,7 @@ const IndividualCreate = () => {
{
url: `/ubp-bff-service/benefit/_apply`,
params: { tenantId },
- body: transformCreateData(formData),
+ body: transformCreateData(programData, formData),
config: {
enable: true,
},
@@ -166,6 +168,7 @@ const IndividualCreate = () => {
{
+ // debugger;
setShowPopUp(false);
succeded && submitApplication();
}}
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Program.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Program.js
index 68c94f4..532f00c 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Program.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Program.js
@@ -1,14 +1,19 @@
-import React from "react";
+import React, { useRef } from "react";
import { useTranslation } from "react-i18next";
import { Loader } from "@egovernments/digit-ui-react-components";
import { Card, TextBlock, Button, ViewCardFieldPair, Tag } from "@egovernments/digit-ui-components";
import { SCHEME } from "../configs/schemeConfigs";
import { useParams, useHistory } from "react-router-dom";
+import {QRCodeSVG} from 'qrcode.react';
+import jsPDF from 'jspdf';
+import html2canvas from 'html2canvas';
const Program = ({}) => {
// const Program=[]
const history = useHistory();
+ const qrRef = useRef();
+
const { id } = useParams();
const reqCriteria = {
@@ -29,6 +34,21 @@ const Program = ({}) => {
};
const { isLoading, data } = Digit.Hooks.useCustomAPIHook(reqCriteria);
+ const downloadPDF = (prog) => {
+ const qrElement = document.getElementById('program_details_qr_container');
+
+ // Use html2canvas to capture the SVG as an image
+ html2canvas(qrElement).then(canvas => {
+ const qrCodeDataURL = canvas.toDataURL('image/png');
+
+ const pdf = new jsPDF();
+ pdf.setFontSize(20);
+ pdf.text(prog?.basicDetails?.schemeName, 20, 20);
+ pdf.addImage(qrCodeDataURL, 'PNG', 15, 40, 200, 215); // Adjust position and size as needed
+ pdf.save(`${prog?.basicDetails?.schemeName || ""} QRCODE.pdf`);
+ });
+ };
+
if (isLoading) {
return ;
}
@@ -51,6 +71,7 @@ const Program = ({}) => {
return (
+
{
title=""
/>
+
+
{
+ downloadPDF(prog);
+ }}
+ options={[]}
+ optionsKey=""
+ size=""
+ style={{}}
+ title=""
+ />
+
+
+
+
);
From 3a8bc47653ffd3f1ef8fece936b94b9f63c15516 Mon Sep 17 00:00:00 2001
From: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com>
Date: Thu, 3 Oct 2024 15:28:30 +0530
Subject: [PATCH 04/12] Fixed flyway issue
---
backend/application/src/main/resources/db/Dockerfile | 4 ++--
backend/application/src/main/resources/db/migrate.sh | 2 +-
backend/bankaccounts/src/main/resources/db/Dockerfile | 4 ++--
backend/bankaccounts/src/main/resources/db/migrate.sh | 2 +-
backend/disbursal/src/main/resources/db/Dockerfile | 4 ++--
backend/disbursal/src/main/resources/db/migrate.sh | 2 +-
.../egov/individual/validators/IBoundaryValidator.java | 8 ++++----
7 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/backend/application/src/main/resources/db/Dockerfile b/backend/application/src/main/resources/db/Dockerfile
index a5699ff..e7da01d 100644
--- a/backend/application/src/main/resources/db/Dockerfile
+++ b/backend/application/src/main/resources/db/Dockerfile
@@ -1,4 +1,4 @@
-FROM egovio/flyway:4.1.2
+FROM egovio/flyway:10.7.1
COPY ./migration/main /flyway/sql
@@ -6,4 +6,4 @@ COPY migrate.sh /usr/bin/migrate.sh
RUN chmod +x /usr/bin/migrate.sh
-CMD ["/usr/bin/migrate.sh"]
+ENTRYPOINT ["/usr/bin/migrate.sh"]
\ No newline at end of file
diff --git a/backend/application/src/main/resources/db/migrate.sh b/backend/application/src/main/resources/db/migrate.sh
index 43960b2..c58d6f9 100644
--- a/backend/application/src/main/resources/db/migrate.sh
+++ b/backend/application/src/main/resources/db/migrate.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-flyway -url=$DB_URL -table=$SCHEMA_TABLE -user=$FLYWAY_USER -password=$FLYWAY_PASSWORD -locations=$FLYWAY_LOCATIONS -baselineOnMigrate=true -outOfOrder=true -ignoreMissingMigrations=true migrate
\ No newline at end of file
+flyway -url=$DB_URL -table=$SCHEMA_TABLE -user=$FLYWAY_USER -password=$FLYWAY_PASSWORD -locations=$FLYWAY_LOCATIONS -baselineOnMigrate=true -outOfOrder=true migrate
\ No newline at end of file
diff --git a/backend/bankaccounts/src/main/resources/db/Dockerfile b/backend/bankaccounts/src/main/resources/db/Dockerfile
index a5699ff..e7da01d 100644
--- a/backend/bankaccounts/src/main/resources/db/Dockerfile
+++ b/backend/bankaccounts/src/main/resources/db/Dockerfile
@@ -1,4 +1,4 @@
-FROM egovio/flyway:4.1.2
+FROM egovio/flyway:10.7.1
COPY ./migration/main /flyway/sql
@@ -6,4 +6,4 @@ COPY migrate.sh /usr/bin/migrate.sh
RUN chmod +x /usr/bin/migrate.sh
-CMD ["/usr/bin/migrate.sh"]
+ENTRYPOINT ["/usr/bin/migrate.sh"]
\ No newline at end of file
diff --git a/backend/bankaccounts/src/main/resources/db/migrate.sh b/backend/bankaccounts/src/main/resources/db/migrate.sh
index 43960b2..c58d6f9 100644
--- a/backend/bankaccounts/src/main/resources/db/migrate.sh
+++ b/backend/bankaccounts/src/main/resources/db/migrate.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-flyway -url=$DB_URL -table=$SCHEMA_TABLE -user=$FLYWAY_USER -password=$FLYWAY_PASSWORD -locations=$FLYWAY_LOCATIONS -baselineOnMigrate=true -outOfOrder=true -ignoreMissingMigrations=true migrate
\ No newline at end of file
+flyway -url=$DB_URL -table=$SCHEMA_TABLE -user=$FLYWAY_USER -password=$FLYWAY_PASSWORD -locations=$FLYWAY_LOCATIONS -baselineOnMigrate=true -outOfOrder=true migrate
\ No newline at end of file
diff --git a/backend/disbursal/src/main/resources/db/Dockerfile b/backend/disbursal/src/main/resources/db/Dockerfile
index a5699ff..e7da01d 100644
--- a/backend/disbursal/src/main/resources/db/Dockerfile
+++ b/backend/disbursal/src/main/resources/db/Dockerfile
@@ -1,4 +1,4 @@
-FROM egovio/flyway:4.1.2
+FROM egovio/flyway:10.7.1
COPY ./migration/main /flyway/sql
@@ -6,4 +6,4 @@ COPY migrate.sh /usr/bin/migrate.sh
RUN chmod +x /usr/bin/migrate.sh
-CMD ["/usr/bin/migrate.sh"]
+ENTRYPOINT ["/usr/bin/migrate.sh"]
\ No newline at end of file
diff --git a/backend/disbursal/src/main/resources/db/migrate.sh b/backend/disbursal/src/main/resources/db/migrate.sh
index 43960b2..c58d6f9 100644
--- a/backend/disbursal/src/main/resources/db/migrate.sh
+++ b/backend/disbursal/src/main/resources/db/migrate.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-flyway -url=$DB_URL -table=$SCHEMA_TABLE -user=$FLYWAY_USER -password=$FLYWAY_PASSWORD -locations=$FLYWAY_LOCATIONS -baselineOnMigrate=true -outOfOrder=true -ignoreMissingMigrations=true migrate
\ No newline at end of file
+flyway -url=$DB_URL -table=$SCHEMA_TABLE -user=$FLYWAY_USER -password=$FLYWAY_PASSWORD -locations=$FLYWAY_LOCATIONS -baselineOnMigrate=true -outOfOrder=true migrate
\ No newline at end of file
diff --git a/backend/individual/src/main/java/org/egov/individual/validators/IBoundaryValidator.java b/backend/individual/src/main/java/org/egov/individual/validators/IBoundaryValidator.java
index 30344cf..5fec65d 100644
--- a/backend/individual/src/main/java/org/egov/individual/validators/IBoundaryValidator.java
+++ b/backend/individual/src/main/java/org/egov/individual/validators/IBoundaryValidator.java
@@ -90,10 +90,10 @@ public Map> validate(IndividualBulkRequest request) {
BoundaryResponse.class
);
log.debug("Boundary details fetched successfully for tenantId: {}", tenantId);
- // TODO: Remove this after boundary fix
- if (true) {
- return;
- }
+// // TODO: Remove this after boundary fix
+// if (true) {
+// return;
+// }
List invalidBoundaryCodes = new ArrayList<>(boundaries);
invalidBoundaryCodes.removeAll(boundarySearchResponse.getBoundary().stream()
.map(Boundary::getCode)
From f18e65b2ba324f8001c8c452c3ddd949dfc5a047 Mon Sep 17 00:00:00 2001
From: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com>
Date: Thu, 3 Oct 2024 15:47:53 +0530
Subject: [PATCH 05/12] Updated path in package.json
---
utilities/bff-service/package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/utilities/bff-service/package.json b/utilities/bff-service/package.json
index 826893c..be79e81 100644
--- a/utilities/bff-service/package.json
+++ b/utilities/bff-service/package.json
@@ -7,7 +7,7 @@
"author": "Shailesh ",
"description": "Backend For Frontend service which helps in generating pdf, managing API's",
"scripts": {
- "start": "node ./src/bin/www"
+ "start": "node ./bin/www"
},
"dependencies": {
"axios": "^1.7.7",
From a677e37ae9b45d716575bf2b59a4c73132a2bba3 Mon Sep 17 00:00:00 2001
From: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com>
Date: Thu, 3 Oct 2024 17:24:28 +0530
Subject: [PATCH 06/12] Updated docker file
---
frontend/micro-ui/web/docker/Dockerfile | 2 ++
1 file changed, 2 insertions(+)
diff --git a/frontend/micro-ui/web/docker/Dockerfile b/frontend/micro-ui/web/docker/Dockerfile
index fa06af8..8234541 100644
--- a/frontend/micro-ui/web/docker/Dockerfile
+++ b/frontend/micro-ui/web/docker/Dockerfile
@@ -9,6 +9,8 @@ ENV NODE_OPTIONS "--max-old-space-size=8168"
COPY ${WORK_DIR} .
RUN ls -lah
+RUN chmod 777 ./install-deps.sh
+
#RUN node web/envs.js
RUN cd web/ \
&& ./install-deps.sh \
From b5e6fd9d63f281d667274703f3fc506847051d22 Mon Sep 17 00:00:00 2001
From: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com>
Date: Thu, 3 Oct 2024 17:28:53 +0530
Subject: [PATCH 07/12] Updated docker file
---
frontend/micro-ui/web/docker/Dockerfile | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/frontend/micro-ui/web/docker/Dockerfile b/frontend/micro-ui/web/docker/Dockerfile
index 8234541..72cc014 100644
--- a/frontend/micro-ui/web/docker/Dockerfile
+++ b/frontend/micro-ui/web/docker/Dockerfile
@@ -9,10 +9,9 @@ ENV NODE_OPTIONS "--max-old-space-size=8168"
COPY ${WORK_DIR} .
RUN ls -lah
-RUN chmod 777 ./install-deps.sh
-
#RUN node web/envs.js
RUN cd web/ \
+ && chmod 777 ./install-deps.sh \
&& ./install-deps.sh \
&& yarn install \
&& yarn build:webpack
From f4daa8e33cd715e13e114c16fb001f976959b5b5 Mon Sep 17 00:00:00 2001
From: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com>
Date: Thu, 3 Oct 2024 17:32:41 +0530
Subject: [PATCH 08/12] Update file access
---
frontend/micro-ui/web/docker/Dockerfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/frontend/micro-ui/web/docker/Dockerfile b/frontend/micro-ui/web/docker/Dockerfile
index 72cc014..7b6c620 100644
--- a/frontend/micro-ui/web/docker/Dockerfile
+++ b/frontend/micro-ui/web/docker/Dockerfile
@@ -11,7 +11,7 @@ RUN ls -lah
#RUN node web/envs.js
RUN cd web/ \
- && chmod 777 ./install-deps.sh \
+ && chmod 777 ./install-deps.sh \
&& ./install-deps.sh \
&& yarn install \
&& yarn build:webpack
From c5d7baf0afa9e7bc02d658e3d0c1995b8b7af8f2 Mon Sep 17 00:00:00 2001
From: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com>
Date: Thu, 3 Oct 2024 18:31:50 +0530
Subject: [PATCH 09/12] Updated user details
---
utilities/bff-service/src/config.js | 2 +-
utilities/bff-service/src/routes/submitApplication.js | 6 ++----
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/utilities/bff-service/src/config.js b/utilities/bff-service/src/config.js
index d29a5f9..f033193 100644
--- a/utilities/bff-service/src/config.js
+++ b/utilities/bff-service/src/config.js
@@ -25,7 +25,7 @@ module.exports = {
app: {
port: parseInt(process.env.APP_PORT) || 8080,
host: HOST,
- contextPath: process.env.CONTEXT_PATH || "/ubp-bff-service",
+ contextPath: process.env.CONTEXT_PATH || "/uba-bff-service",
},
host: {
mdms: process.env.EGOV_MDMS_HOST || 'http://localhost:8083',
diff --git a/utilities/bff-service/src/routes/submitApplication.js b/utilities/bff-service/src/routes/submitApplication.js
index 5b81d57..22881db 100644
--- a/utilities/bff-service/src/routes/submitApplication.js
+++ b/utilities/bff-service/src/routes/submitApplication.js
@@ -86,7 +86,7 @@ router.post('/',
}
res.send(response)
} catch (error) {
- logger.error(error.stack || err);
+ logger.error(error.stack || error);
return renderError(res, `Failed to register application.`);
}
@@ -225,9 +225,7 @@ let getRequestInfo = () => {
"tenantId": "pg.citya"
}
],
- "active": true,
- "tenantId": "pg.citya",
- "permanentCity": null
+ "tenantId": "pg"
},
"msgId": "1700205653156|en_IN",
"plainAccessRequest": {}
From b374489ccc6db38857b1989ccb139d25cc040360 Mon Sep 17 00:00:00 2001
From: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com>
Date: Thu, 3 Oct 2024 19:39:51 +0530
Subject: [PATCH 10/12] fixed url
---
.../web/micro-ui-internals/example/src/setupProxy.js | 3 ++-
.../modules/core/src/pages/individual/pages/Enroll.js | 9 ++++-----
.../modules/core/src/pages/individual/pages/Program.js | 2 +-
.../modules/core/src/pages/individual/pages/Response.js | 5 ++++-
.../modules/core/src/pages/individual/pages/index.js | 4 ++--
.../core/src/pages/individual/utils/createUtils.js | 2 +-
frontend/micro-ui/web/src/setupProxy.js | 3 ++-
7 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/frontend/micro-ui/web/micro-ui-internals/example/src/setupProxy.js b/frontend/micro-ui/web/micro-ui-internals/example/src/setupProxy.js
index 840f7b1..69cfab2 100644
--- a/frontend/micro-ui/web/micro-ui-internals/example/src/setupProxy.js
+++ b/frontend/micro-ui/web/micro-ui-internals/example/src/setupProxy.js
@@ -89,7 +89,8 @@ module.exports = function (app) {
"/boundary-service",
"/project-factory",
"/project-factory/v1/data/_autoGenerateBoundaryCode",
- "/billing-service/bill/v2/_fetchbill"
+ "/billing-service/bill/v2/_fetchbill",
+ "/uba-bff-service"
].forEach((location) => app.use(location, createProxy));
["/pb-egov-assets"].forEach((location) => app.use(location, assetsProxy));
["/mdms-v2/v2/_create"].forEach((location) => app.use(location, mdmsProxy));
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Enroll.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Enroll.js
index befe45e..4cbe0db 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Enroll.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Enroll.js
@@ -14,7 +14,7 @@ const IndividualCreate = () => {
const { t } = useTranslation();
const history = useHistory();
const reqCreate = {
- url: `/ubp-bff-service/benefit/_apply`,
+ url: `/uba-bff-service/benefit/_apply`,
params: {},
body: {},
config: {
@@ -102,13 +102,13 @@ const IndividualCreate = () => {
const mutation = Digit.Hooks.useCustomAPIMutationHook(reqCreate);
const onError = (resp) => {
- // debugger
+ // debugger;
history.push(`/${window.contextPath}/individual/enroll-response?isSuccess=${false}`, { message: "SUBMISSION_CREATION_FAILED" });
};
const onSuccess = (resp) => {
// debugger
- history.push(`/${window.contextPath}/individual/enroll-response?appNo=${"NEW-NO-1"}&isSuccess=${true}`, {
+ history.push(`/${window.contextPath}/individual/enroll-response?appNo=${resp.Application.applicationNumber}&isSuccess=${true}`, {
message: "SUBMISSION_CREATION_SUCCESS",
showID: true,
label: "SUBMISSION_ID",
@@ -117,8 +117,7 @@ const IndividualCreate = () => {
const submitApplication = async () => {
await mutation.mutate(
{
- url: `/ubp-bff-service/benefit/_apply`,
- params: { tenantId },
+ url: `/uba-bff-service/benefit/_apply`,
body: transformCreateData(programData, formData),
config: {
enable: true,
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Program.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Program.js
index 532f00c..58da590 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Program.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/Program.js
@@ -142,7 +142,6 @@ const Program = ({}) => {
{
size=""
style={{}}
title=""
+ variation="secondary"
/>
{
const [isResponseSuccess, setIsResponseSuccess] = useState(
queryStrings?.isSuccess === "true" ? true : queryStrings?.isSuccess === "false" ? false : true
);
+
+ const [appNumber, setAppNumber] = useState(queryStrings?.appNo ? queryStrings?.appNo : "");
+
const { state } = useLocation();
const navigate = (page) => {
@@ -41,7 +44,7 @@ const Response = () => {
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/index.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/index.js
index ae138a2..b304b46 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/index.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/pages/index.js
@@ -59,9 +59,9 @@ const App = ({ path }) => {
} />
} />
-
+ {/*
-
+ */}
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/utils/createUtils.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/utils/createUtils.js
index c707eed..d186ed1 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/utils/createUtils.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/utils/createUtils.js
@@ -84,7 +84,7 @@ export const transformCreateData = (programData, formData) => {
Application: {
"tenantId": Digit.ULBService.getStateId(),
"individualId": null,
- "programCode": programData?.data?.id,
+ "programCode": programData?.[0]?.id,
"documents": documents
},
BankAccount: {
diff --git a/frontend/micro-ui/web/src/setupProxy.js b/frontend/micro-ui/web/src/setupProxy.js
index 1b8eda9..08891b1 100644
--- a/frontend/micro-ui/web/src/setupProxy.js
+++ b/frontend/micro-ui/web/src/setupProxy.js
@@ -23,7 +23,8 @@ module.exports = function (app) {
"/vendor",
"/property-services",
"/fsm-calculator/v1/billingSlab/_search",
- "/muster-roll"
+ "/muster-roll",
+ "/uba-bff-service"
].forEach((location) =>
app.use(location, createProxy)
);
From 46563fbd5b5a1452d4443ad2996a49b45850ec8e Mon Sep 17 00:00:00 2001
From: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com>
Date: Fri, 4 Oct 2024 10:20:15 +0530
Subject: [PATCH 11/12] Update app file to include custom component
---
.../packages/modules/core/src/Module.js | 16 ++++++++++++++++
frontend/micro-ui/web/src/App.js | 10 ++++++----
frontend/micro-ui/web/src/index.js | 4 ++++
3 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/Module.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/Module.js
index 90a2e55..8498e79 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/Module.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/Module.js
@@ -120,4 +120,20 @@ export const initSampleComponents = () => {
Object.entries(componentsToRegister).forEach(([key, value]) => {
Digit.ComponentRegistryService.setComponent(key, value);
});
+};
+
+export const initIndividualComponents = () => {
+ // overrideHooks();
+ // updateCustomConfigs();
+ Object.entries(componentsToRegister).forEach(([key, value]) => {
+ Digit.ComponentRegistryService.setComponent(key, value);
+ });
+};
+
+export const initCoreComponents = () => {
+ // overrideHooks();
+ // updateCustomConfigs();
+ Object.entries(componentsToRegister).forEach(([key, value]) => {
+ Digit.ComponentRegistryService.setComponent(key, value);
+ });
};
\ No newline at end of file
diff --git a/frontend/micro-ui/web/src/App.js b/frontend/micro-ui/web/src/App.js
index 913bff0..9a203b5 100644
--- a/frontend/micro-ui/web/src/App.js
+++ b/frontend/micro-ui/web/src/App.js
@@ -1,6 +1,7 @@
import React from "react";
import { initLibraries } from "@egovernments/digit-ui-libraries";
-
+import { initCoreComponents } from "@egovernments/digit-ui-module-core";
+import { initSampleComponents } from "@egovernments/digit-ui-module-core";
import { DigitUI } from "@egovernments/digit-ui-module-core";
@@ -17,7 +18,8 @@ const enabledModules = [
"HRMS",
"Engagement",
// "Workbench",
- "PGR"
+ "PGR",
+ "Sample",
];
const moduleReducers = (initData) => ({
@@ -29,8 +31,8 @@ const initDigitUI = () => {
});
- // initCoreComponents();
-
+ initCoreComponents();
+ initSampleComponents()
window.Digit.Customizations = {
PGR: {},
diff --git a/frontend/micro-ui/web/src/index.js b/frontend/micro-ui/web/src/index.js
index 9f20bf1..1d284f9 100644
--- a/frontend/micro-ui/web/src/index.js
+++ b/frontend/micro-ui/web/src/index.js
@@ -3,10 +3,14 @@ import ReactDOM from 'react-dom';
import { initLibraries } from "@egovernments/digit-ui-libraries";
import "./index.css";
import App from './App';
+import { initCoreComponents } from "@egovernments/digit-ui-module-core";
+import { initSampleComponents } from "@egovernments/digit-ui-module-core";
import { TLCustomisations } from './Customisations/tl/TLCustomisation';
initLibraries();
+initCoreComponents();
+initSampleComponents();
window.Digit.Customizations = { PGR: {} ,TL:TLCustomisations};
From 5bee2582b155cf2ba0f554ec5c990fd4312661dc Mon Sep 17 00:00:00 2001
From: Shailesh Pandey <110380977+shailesh-egov@users.noreply.github.com>
Date: Fri, 4 Oct 2024 11:10:39 +0530
Subject: [PATCH 12/12] Updated form config
---
.../configs/IndividualCreateConfig.js | 41 +++++++++++++++----
1 file changed, 34 insertions(+), 7 deletions(-)
diff --git a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/IndividualCreateConfig.js b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/IndividualCreateConfig.js
index d40b41a..d65abd7 100644
--- a/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/IndividualCreateConfig.js
+++ b/frontend/micro-ui/web/micro-ui-internals/packages/modules/core/src/pages/individual/configs/IndividualCreateConfig.js
@@ -9,7 +9,12 @@ export const newConfig = [
key: "applicantname",
type: "text",
disable: false,
- populators: { name: "applicantname", error: "Required", validation: { pattern: /^[A-Za-z]+$/i } },
+ populators: { name: "applicantname", error: "Required", validation: {
+ "pattern": "^[a-zA-Z0-9 .\\-_@\\']*$",
+ "minlength": 2,
+ "maxlength": 128
+ }
+ },
},
{
inline: true,
@@ -44,7 +49,11 @@ export const newConfig = [
key: "mobileNumber",
type: "number",
disable: false,
- populators: { name: "mobileNumber", error: "Required", validation: { min: 0, max: 9999999999 } },
+ populators: { name: "mobileNumber", error: "Required", validation: {
+ "min": 5999999999,
+ "max": 9999999999
+ }
+ },
},
{
label: "Aadhaar number",
@@ -66,7 +75,11 @@ export const newConfig = [
key: "city",
type: "text",
disable: false,
- populators: { name: "city", error: " Required ", validation: { pattern: /^[A-Za-z]+$/i } },
+ populators: { name: "city", error: " Required ", validation: {
+ "pattern": "^[a-zA-Z0-9 .\\-_@\\']*$",
+ "minlength": 2,
+ "maxlength": 128
+ } },
},
{
@@ -76,7 +89,11 @@ export const newConfig = [
key: "landmark",
type: "text",
disable: false,
- populators: { name: "landmark", error: " Required", validation: { pattern: /^[A-Za-z]+$/i } },
+ populators: { name: "landmark", error: " Required", validation: {
+ "pattern": "^[a-zA-Z0-9 .\\-_@\\']*$",
+ "minlength": 2,
+ "maxlength": 128
+ } },
},
],
},
@@ -113,7 +130,11 @@ export const newConfig = [
populators: {
name: "accountHolderName",
error: " Required ",
- validation: { pattern: "^[a-zA-Z0-9 .\\-_@\\']*$", minlength : 2, maxlength: 64 }
+ validation: {
+ "pattern": "^[a-zA-Z0-9 .\\-_@\\']*$",
+ "minlength": 2,
+ "maxlength": 64
+ }
},
},
{
@@ -126,7 +147,11 @@ export const newConfig = [
populators: {
name: "accountNumber",
error: " Required ",
- validation: {pattern: "^[0-9]{9,18}$", minlength : 9, maxlength: 18}
+ validation: {
+ "pattern": "^[0-9]{9,18}$",
+ "minlength": 9,
+ "maxlength": 18
+ }
},
},
{
@@ -153,7 +178,9 @@ export const newConfig = [
key: "ifscCode",
type: "text",
disable: false,
- populators: { name: "ifscCode", error: " Required", validation: { pattern: /^[a-zA-Z0-9_]+$/i } },
+ populators: { name: "ifscCode", error: " Required", validation: {
+ "pattern": "^[a-zA-Z]{4}0[a-zA-Z0-9]{6}$"
+ } },
},
],
},