diff --git a/health/micro-ui/web/micro-ui-internals/example/public/index.html b/health/micro-ui/web/micro-ui-internals/example/public/index.html index 221c905c2a5..3cc1822a991 100644 --- a/health/micro-ui/web/micro-ui-internals/example/public/index.html +++ b/health/micro-ui/web/micro-ui-internals/example/public/index.html @@ -18,7 +18,7 @@ - + diff --git a/health/micro-ui/web/micro-ui-internals/packages/css/package.json b/health/micro-ui/web/micro-ui-internals/packages/css/package.json index 93000891ba1..fc1ece9cbc0 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/css/package.json +++ b/health/micro-ui/web/micro-ui-internals/packages/css/package.json @@ -1,6 +1,6 @@ { "name": "@egovernments/digit-ui-css", - "version": "1.0.72-campaign", + "version": "1.0.73-campaign", "license": "MIT", "main": "dist/index.css", "author": "Jagankumar ", diff --git a/health/micro-ui/web/micro-ui-internals/packages/css/src/components/microplan.scss b/health/micro-ui/web/micro-ui-internals/packages/css/src/components/microplan.scss index dcccf02eb4a..e787cd5580e 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/css/src/components/microplan.scss +++ b/health/micro-ui/web/micro-ui-internals/packages/css/src/components/microplan.scss @@ -96,4 +96,17 @@ background-color: #fbe9e6; border-color: #dc5a32; } + + .subBoundarycomp-container{ + display:inline-flex; + flex-wrap:wrap; + gap:0.125rem; + } + +.view-more{ + text-decoration: underline; + cursor:pointer; + color:orange; + margin-top: 0.2rem; +} \ No newline at end of file diff --git a/health/micro-ui/web/micro-ui-internals/packages/css/src/components/microplanning.scss b/health/micro-ui/web/micro-ui-internals/packages/css/src/components/microplanning.scss index 3c653eef2ad..a7231af5121 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/css/src/components/microplanning.scss +++ b/health/micro-ui/web/micro-ui-internals/packages/css/src/components/microplanning.scss @@ -2575,3 +2575,46 @@ $border-color: rgba(214, 213, 212, 1); font-weight: bold; font-size: 1.2em; } + +.mp-description{ + margin-top: 2rem; + margin-bottom: 1rem; +} + +.assumptions-label-field > div > span { + width: 20rem; + font-size: 1.2rem; + margin-right: 2rem; + font-weight: bold; +} + +.delete-button { + display: flex; + cursor: pointer; + margin-left: 1rem; + font-size: 1.2rem; +} + +.fieldv1-container{ + display: flex; + justify-content: center; + width:30rem; + align-items: center; +} + +.assumptionsForm-label-field{ + display: flex; + flex-direction: column; + gap: 1.5rem; + align-items: start !important; +} +.assumptionsForm-label-field > div > span{ + font-size: 1.5rem; + font-weight: bold; +} + +.assumptionsForm-card{ + background-color:#FAFAFA; + border: 1px solid #D6D5D4; +} + diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/Module.js b/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/Module.js index 976a635bbcb..90383a6ff6d 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/Module.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/Module.js @@ -8,6 +8,7 @@ import MicroplanDetails from "./components/MicroplanDetails"; import CampaignDetails from "./components/CampaignDetails"; import { ProviderContext } from "./utils/context"; import BoundarySelection from "./components/BoundarySelection"; +import AssumptionsForm from "./components/AssumptionsForm"; import HypothesisWrapper from "./components/HypothesisWrapper"; import UploadDataCustom from "./components/UploadDataCustom"; import DataMgmtTable from "./components/DataMgmtTable"; @@ -16,6 +17,8 @@ import HeaderComp from "./components/HeaderComp"; import FormulaSection from "./components/FormulaSectionCard"; import FormulaView from "./components/FormulaView"; import SummaryScreen from "./pages/employee/SummaryScreen"; +import CampaignBoundary from "./components/CampaignBoundary"; + export const MicroplanModule = ({ stateCode, userType, tenants }) => { const { path, url } = useRouteMatch(); @@ -55,6 +58,7 @@ const componentsToRegister = { CampaignDetails, MicroplanDetails, BoundarySelection, + AssumptionsForm, HypothesisWrapper, UploadDataCustom, DataMgmtTable, @@ -62,7 +66,8 @@ const componentsToRegister = { HeaderComp, FormulaView, FormulaSection, - SummaryScreen + SummaryScreen, + CampaignBoundary }; diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/AssumptionsForm.js b/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/AssumptionsForm.js new file mode 100644 index 00000000000..aec42028e8a --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/AssumptionsForm.js @@ -0,0 +1,141 @@ +import React, { useState, useEffect, Fragment } from "react"; +import { useTranslation } from "react-i18next"; +import { Card, Header, LabelFieldPair } from "@egovernments/digit-ui-react-components"; +import { Dropdown, RadioButtons } from "@egovernments/digit-ui-components"; +import { useMyContext } from "../utils/context"; + +const AssumptionsForm = ({ onSelect, ...props }) => { + const { state } = useMyContext(); + const { t } = useTranslation(); + const optionsForProcesses = state.ResourceDistributionStrategy; + const optionsForRegistrationDistributionMode = state.RegistrationAndDistributionHappeningTogetherOrSeparately; + const [selectedRegistrationProcess, setSelectedRegistrationProcess] = useState(props?.props?.sessionData?.ASSUMPTIONS_FORM?.assumptionsForm?.selectedRegistrationProcess || false); + const [selectedDistributionProcess, setSelectedDistributionProcess] = useState(props?.props?.sessionData?.ASSUMPTIONS_FORM?.assumptionsForm?.selectedDistributionProcess || false); + const [selectedRegistrationDistributionMode, setSelectedRegistrationDistributionMode] = useState(props?.props?.sessionData?.ASSUMPTIONS_FORM?.assumptionsForm?.selectedRegistrationDistributionMode || false); + const [executionCount, setExecutionCount] = useState(0); + const resourceDistributionStrategyCode = props?.props?.sessionData?.CAMPAIGN_DETAILS?.campaignDetails?.distributionStrat?.resourceDistributionStrategyCode; + + useEffect(() => { + if (executionCount < 5) { + onSelect(props.props.name, {selectedRegistrationProcess,selectedDistributionProcess,selectedRegistrationDistributionMode}) + setExecutionCount((prevCount) => prevCount + 1); + } + }); + + + useEffect(()=>{ + + if(resourceDistributionStrategyCode === "MIXED"){ + onSelect(props.props.name, {selectedRegistrationProcess,selectedDistributionProcess,selectedRegistrationDistributionMode}) + return; + } + + onSelect(props.props.name,{selectedRegistrationDistributionMode} ) + + },[selectedDistributionProcess, selectedRegistrationDistributionMode, selectedRegistrationProcess, resourceDistributionStrategyCode]) + + + + + const filteredOptions = resourceDistributionStrategyCode === "MIXED" + ? optionsForProcesses.filter(option => option.resourceDistributionStrategyName !== "Fixed post & House-to-House") + : optionsForProcesses; + + + return ( + +
{t("PROVIDE_DETAILS")}
+

{t("ANSWER_TO_PROVIDE_ESTIMATE")}

+ + {/* Show dropdowns only if the code is MIXED */} + {resourceDistributionStrategyCode === "MIXED" && ( + <> + {/* Dropdown for Registration Process */} + + +
+ {t("REGISTRATION_PROCESS")} + * +
+ ({ + code: item.resourceDistributionStrategyCode, + value: item.resourceDistributionStrategyName, + }))} + select={(value) => { + setSelectedRegistrationProcess(value); + }} + selected={selectedRegistrationProcess} + optionKey="code" + showToolTip={true} + style={{ width: "25rem" }} + /> +
+
+ + {/* Dropdown for Distribution Process */} + + +
+ {t("DISTRIBUTION_PROCESS")} + * +
+ ({ + code: item.resourceDistributionStrategyCode, + value: item.resourceDistributionStrategyName, + }))} + select={(value) => { + setSelectedDistributionProcess(value); + }} + selected={selectedDistributionProcess} + optionKey="code" + showToolTip={true} + style={{ width: "25rem" }} + /> +
+
+ + )} + + {/* Show radio buttons only if the code is HOUSE_TO_HOUSE or FIXED_POST */} + {["HOUSE_TO_HOUSE", "FIXED_POST", "MIXED"].includes(resourceDistributionStrategyCode) && ( + + +
+ {t("REGISTRATION_AND_DISTRIBUTION")} + * +
+ ({ + code: item.registrationAndDistributionHappeningTogetherOrSeparatelyCode, + value: item.registrationAndDistributionHappeningTogetherOrSeparatelyName, + }))} + optionsKey="code" + style={{ display: "flex", gap: "15rem" }} + onSelect={(value) => { + setSelectedRegistrationDistributionMode(value); + }} + selectedOption={selectedRegistrationDistributionMode} + /> +
+
+ )} +
+ ); +}; + +export default AssumptionsForm; + + + + diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/CampaignBoundary.js b/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/CampaignBoundary.js new file mode 100644 index 00000000000..80779374fba --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/CampaignBoundary.js @@ -0,0 +1,213 @@ +import React, { useState, useEffect, useMemo } from "react"; +import { useTranslation } from "react-i18next"; +import { useMyContext } from "../utils/context"; +import SubBoundaryView from "./subBoundaryView"; +import HeaderComp from "./HeaderComp"; +import { Card } from "@egovernments/digit-ui-components"; +import BoundaryKpi from "./BoundaryKpi"; + + +const CampaignBoundary = ({customProps}) => { + const { dispatch, state } = useMyContext(); + const { t } = useTranslation(); + + const handleViewMore = (ind) => { + // Create a copy of the boundaryStatus array + const updatedBoundaryStatus = [...boundaryStatus]; + // Update the ind + 1 position to false + if (boundaryStatus[ind]) { + updatedBoundaryStatus[ind] = false; + } else { + updatedBoundaryStatus[ind] = true; + + } + // Set the updated array in the state + setBoundaryStatus(updatedBoundaryStatus); + // Set showAllItems to true to show all items + }; + + const selectedData = customProps?.sessionData?.BOUNDARY?.boundarySelection?.selectedData; + + + + // Memoizing parents and parent_group to only compute once when selectedData changes + + const { parents, parent_group } = useMemo(() => { + let parents = {}; + let parent_group = {}; + + for (const ob of selectedData) { + if (ob['parent']) { + if (!(ob['parent'] in parents)) { + parents[ob['parent']] = [ob['code']]; + } else { + parents[ob['parent']].push(ob['code']); + } + } + } + + for (const ob of selectedData) { + const parentCodes = parents[ob['parent']]; // Get the array of codes for the parent + if (!(ob['type'] in parent_group)) { + parent_group[ob['type']] = [parentCodes]; // Initialize with the first unique array + } else { + const existingArrays = parent_group[ob['type']]; + if (!existingArrays.some(arr => JSON.stringify(arr) === JSON.stringify(parentCodes))) { + parent_group[ob['type']].push(parentCodes); // Add only if it's unique + } + } + } + + return { parents, parent_group }; + }, [selectedData]); + + // Initialize boundaryHierarchy and bHierarchy as state + const [boundaryHierarchy, setData] = useState(state["boundaryHierarchy"]); + const [boundaryStatus, setBoundaryStatus] = useState( + new Array(boundaryHierarchy.length).fill(true) // Creates an array filled with "True" + ); + // const boundaryStatus=[false,false,false,false,true]; + + + + + + const [bHierarchy, setbHierarchy] = useState([]); + + const [statusMap, setStatusMap] = useState({}); + useEffect(() => { + if (selectedData && selectedData.length >= 0) { + setStatusMap(() => Digit.Utils.microplanv1.createStatusMap(selectedData, boundaryHierarchy)) + } + }, [boundaryHierarchy]) + + // Prevent unnecessary useEffect calls + useEffect(() => { + if (!boundaryHierarchy || boundaryHierarchy.length === 0) return; + + let bH = []; + let top = null; + let dic = {}; + + for (const ob of boundaryHierarchy) { + let bType = ob["boundaryType"]; + let parent = ob["parentBoundaryType"]; + + if (parent === null) { + bH.unshift(bType); // Push top element to the start + top = bType; + } + dic[parent] = bType; + } + + let prev = top; + while (prev in dic) { + bH.push(dic[prev]); + prev = dic[prev]; + } + + setbHierarchy(bH); + + const updatedBoundaryStatus = [...boundaryStatus]; // Create a copy of boundaryStatus + + for (const ind in bHierarchy) { + if (parent_group[bHierarchy[ind]] && parent_group[bHierarchy[ind]].length > 2) { + updatedBoundaryStatus[ind] = true; + } else { + updatedBoundaryStatus[ind] = false; + } + } + + setBoundaryStatus(updatedBoundaryStatus); + + + + + + }, [boundaryHierarchy]); // Only re-run when boundaryHierarchy changes + + return ( +
+ + + { + bHierarchy.length > 1 ? + ( + + + + + ) : (null) + } + + + + {bHierarchy.length > 1 && bHierarchy.slice(1, -1).map((item, ind) => { + //item-Country,Province,Locality,District,Village + if (parent_group[item] && Array.isArray(parent_group[item])) { + return parent_group[item].map((item1, idx) => { + //item1-Province-[Sinoe],District-[Jedepo,Jeade] + return Array.isArray(item1) && (!boundaryStatus[ind + 2]) ? ( + //make a super-comp that contains SubBoundary View + + + {item1.map((item2) => ( + //item2-parents name eg, sino etc + + )) + } +
handleViewMore(ind + 2)} + className="view-more" + > + View Less +
+
+ ) : (Array.isArray(item1) && (boundaryStatus[ind + 2])) ? ( +
+ + + {item1.filter(() => (idx == 0 || idx == 1)).map((item2) => ( + //item2-parents name eg, sino etc + + )) + } +
handleViewMore(ind + 2)} + className="view-more" + > + View More +
+
+ + + + +
+ + + + ) : (null) + + + + }); + } + return null; + })} +
+ ); +}; + +export default CampaignBoundary; diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/CampaignDetails.js b/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/CampaignDetails.js index 5389969c53b..e59e53ba1f3 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/CampaignDetails.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/CampaignDetails.js @@ -65,6 +65,8 @@ const CampaignDetails = ({onSelect,props:customProps,...props}) => { return } + + return (
{t(`HCM_CAMPAIGN_DETAILS_HEADER`)}
diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/Hypothesis.js b/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/Hypothesis.js index 0c7b0aa3a5a..ce2484e0f3b 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/Hypothesis.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/Hypothesis.js @@ -1,132 +1,110 @@ -import React, { useState, useEffect,Fragment} from "react"; +import React, { useState, useEffect,Fragment,useContext, useRef} from "react"; import { useTranslation } from "react-i18next"; import { Card, Header, DeleteIconv2,LabelFieldPair, AddIcon,Button, CardText, } from "@egovernments/digit-ui-react-components"; import {Dropdown,FieldV1,PopUp,} from "@egovernments/digit-ui-components"; -import { PRIMARY_COLOR } from "../utils"; +import { PRIMARY_COLOR } from "../utils/utilities"; import { useMyContext } from "../utils/context"; +import { useAssumptionContext } from "./HypothesisWrapper"; -const Hypothesis = ({ category, assumptions:initialAssumptions, customProps, onSelect, })=>{ +const Hypothesis = ({ category, assumptions:initialAssumptions })=>{ - + const { t } = useTranslation(); + const [showPopUP, setShowPopUp] = useState(false) + const [assumptionsPopUP, setAssumptionsPopUp] = useState(false) + const [assumptionToDelete, setAssumptionToDelete] = useState(null) + const [assumptions, setAssumptions] = useState(initialAssumptions); + const [selectedDeletedAssumption, setSelectedDeletedAssumption] = useState(null); + const { assumptionValues, handleAssumptionChange, setAssumptionValues,setDeletedAssumptions, deletedAssumptions } = useAssumptionContext(); + const deletedAssumptionCategories = useRef({}); + const isAddNewDisabled = !deletedAssumptionCategories.current[category] || + deletedAssumptionCategories.current[category].length === 0 || + deletedAssumptionCategories.current[category].every(item => !deletedAssumptions.includes(item)); - - const { t } = useTranslation(); - const [error, setError] = useState({}); - const [startValidation, setStartValidation] = useState(false); - const [showPopUP, setShowPopUp] = useState(false) - const [assumptionsPopUP, setAssumptionsPopUp] = useState(false) - const [assumptionToDelete, setAssumptionToDelete] = useState(null) - const { state, dispatch } = useMyContext(); - const [assumptions, setAssumptions] = useState(initialAssumptions); - const [assumptionValues, setAssumptionValues] = useState([]); - const [deletedAssumptions, setDeletedAssumptions] = useState([]); - const [selectedDeletedAssumption, setSelectedDeletedAssumption] = useState(null); - - useEffect(()=>{ - onSelect(customProps.name, { assumptionValues }); - },[assumptionValues]) - - - useEffect(() => { - setAssumptions(initialAssumptions); - }, [initialAssumptions]); - - useEffect(() => { - if (customProps.isSubmitting) { - validateFields(); - } - }, [customProps.isSubmitting, assumptions, assumptionValues]); - - const validateFields = () => { - const newError = {}; - assumptions.forEach((item) => { - const value = assumptionValues.find((assumption) => assumption.key === item)?.value; - if (!value) { - newError[item] = "This field is required"; - } - }); + const availableDeletedAssumptions = Array.from(new Set( + (deletedAssumptionCategories.current[category] || []).filter(item => + deletedAssumptions.includes(item) + ) +)); + + + const handleDeleteClick = (index) => { + setAssumptionToDelete(index); + setShowPopUp(true); + }; - setError(newError); - }; + const handleCancelDelete = () => { + setShowPopUp(false); + }; + + const handleConfirmDelete = () => { + if (assumptionToDelete !== null) { + const deletedAssumption = assumptions[assumptionToDelete]; + const updatedAssumptions = assumptions.filter((_, i) => i !== assumptionToDelete); + const updatedAssumptionValues = assumptionValues.filter( + (value) => value.key !== deletedAssumption + ); + + if (!deletedAssumptionCategories.current[category]) { + deletedAssumptionCategories.current[category] = []; + } + deletedAssumptionCategories.current[category].push(deletedAssumption); - const handleAssumptionChange = (event, item) => { - - const newValue = event.target.value; - - setAssumptionValues((prevValues) => { - - const existingIndex = prevValues.findIndex((assumption) => assumption.key === item); - - if (existingIndex >= 0) { - const updatedValues = [...prevValues]; - updatedValues[existingIndex] = { - ...updatedValues[existingIndex], - value: newValue, - }; - return updatedValues; - } else { - - return [...prevValues, { category, key: item, value: newValue }]; - } - }); - }; - const handleDeleteClick = (index) => { - setAssumptionToDelete(index); - setShowPopUp(true); - }; - const handleConfirmDelete = () => { - if (assumptionToDelete !== null) { - const deletedAssumption = assumptions[assumptionToDelete]; - const updatedAssumptions = assumptions.filter((_, i) => i !== assumptionToDelete); - const updatedAssumptionValues = assumptionValues.filter( - (value) => value.key !== deletedAssumption - ); - + setDeletedAssumptions([...deletedAssumptions, deletedAssumption]); + setAssumptions(updatedAssumptions); + setAssumptionValues(updatedAssumptionValues); + setAssumptionToDelete(null); + } + setShowPopUp(false); + }; - setDeletedAssumptions([...deletedAssumptions, deletedAssumption]); - setAssumptions(updatedAssumptions); - setAssumptionValues(updatedAssumptionValues); - setAssumptionToDelete(null); - } - setShowPopUp(false); - }; - const handleCancelDelete = () => { - setShowPopUp(false); - }; +const addNewAssumption = () => { + if (selectedDeletedAssumption) { + const assumptionToAdd = deletedAssumptions.find(assumption => assumption === selectedDeletedAssumption.code); - const addNewAssumption = () => { - - if (selectedDeletedAssumption) { - const assumptionToAdd = deletedAssumptions.find(assumption => assumption === selectedDeletedAssumption.code); - if (assumptionToAdd) { - setAssumptions([...assumptions, assumptionToAdd]); - setDeletedAssumptions(deletedAssumptions.filter((assumption) => assumption !== selectedDeletedAssumption.code)); - setAssumptionValues((prevValues) => { - return prevValues.filter((value) => value.key !== assumptionToAdd); - }); - setSelectedDeletedAssumption(null); - setAssumptionsPopUp(false); - }; - - }; + // **Check if it already exists** + if (assumptionToAdd && !assumptions.includes(assumptionToAdd)) { + setAssumptions([...assumptions, assumptionToAdd]); + setDeletedAssumptions(deletedAssumptions.filter((assumption) => assumption !== selectedDeletedAssumption.code)); - }; - + if (deletedAssumptionCategories.current[category]) { + deletedAssumptionCategories.current[category] = deletedAssumptionCategories.current[category].filter( + (item) => item !== assumptionToAdd + ); + } + + // **Conditionally Add to assumptionValues if not already present** + if (!assumptionValues.some(assumption => assumption.key === assumptionToAdd)) { + setAssumptionValues(prevValues => [ + ...prevValues, + { key: assumptionToAdd, value: null } // or an initial value + ]); + } + + setSelectedDeletedAssumption(null); + setAssumptionsPopUp(false); + } + } +}; + + + useEffect(() => { + setAssumptions(initialAssumptions); + }, [initialAssumptions]); return ( <>
{t(category)}
-

{t(`Please enter the values for each assumptions stated below for resource calculation`)}

+

{t(`RESOURCE_CALCULATION`)}

@@ -137,29 +115,30 @@ const Hypothesis = ({ category, assumptions:initialAssumptions, customProps, onS return (
- {`${t(item)}`} - * - - + {`${t(item)}`} + * +
-
+
assumption.key === item)?.value || ""} - error={error[item] ? t(error[item]) : ""} + error={""} style={{marginBottom: "0" }} populators={{ name: item }} id={index} onChange={(event) => { - handleAssumptionChange(event, item); + + handleAssumptionChange(category,event, item); + }} />
- handleDeleteClick(index)}>Delete + handleDeleteClick(index)}>{t("DELETE")}
@@ -173,7 +152,7 @@ const Hypothesis = ({ category, assumptions:initialAssumptions, customProps, onS className="custom-class" icon={} iconFill="" - label="Add new Assumption" + label={t("ADD_ASSUMPTION")} onButtonClick={()=> setAssumptionsPopUp(true)} options={[]} optionsKey="" @@ -181,17 +160,17 @@ const Hypothesis = ({ category, assumptions:initialAssumptions, customProps, onS style={{height:"50px", fontSize:"20px"}} title="" variation="secondary" - isDisabled={assumptions.length === 3} + isDisabled={isAddNewDisabled} /> {showPopUP && - {t("Deleting a line item will permanently delete the assumption. You will not be able to retrieve it")} + {t("PERMANENT_DELETE")}
, ]} onOverlayClick={() => { @@ -202,7 +181,7 @@ const Hypothesis = ({ category, assumptions:initialAssumptions, customProps, onS type={"button"} size={"large"} variation={"secondary"} - label={t("Yes")} + label={t("YES")} onButtonClick={() => { handleConfirmDelete() }} @@ -211,7 +190,7 @@ const Hypothesis = ({ category, assumptions:initialAssumptions, customProps, onS type={"button"} size={"large"} variation={"primary"} - label={t("No")} + label={t("NO")} onButtonClick={handleCancelDelete} />, ]} @@ -224,14 +203,14 @@ const Hypothesis = ({ category, assumptions:initialAssumptions, customProps, onS {assumptionsPopUP && ({code:item}))} + option={availableDeletedAssumptions.map(item => ({ code: item }))} select={(value)=>{ setSelectedDeletedAssumption(value) }} @@ -251,7 +230,7 @@ const Hypothesis = ({ category, assumptions:initialAssumptions, customProps, onS type={"button"} size={"large"} variation={"secondary"} - label={t("Yes")} + label={t("YES")} onButtonClick={() => { addNewAssumption() }} @@ -260,7 +239,7 @@ const Hypothesis = ({ category, assumptions:initialAssumptions, customProps, onS type={"button"} size={"large"} variation={"primary"} - label={t("No")} + label={t("NO")} onButtonClick={()=> { setAssumptionsPopUp(false) }} @@ -282,4 +261,4 @@ const Hypothesis = ({ category, assumptions:initialAssumptions, customProps, onS } -export default Hypothesis \ No newline at end of file +export default Hypothesis; \ No newline at end of file diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/HypothesisWrapper.js b/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/HypothesisWrapper.js index 7011a914f04..f8339dff2cf 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/HypothesisWrapper.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/microplan/src/components/HypothesisWrapper.js @@ -1,33 +1,291 @@ -import React, { useState, useEffect, Fragment } from 'react'; +import React, { useState, useEffect, Fragment, createContext, useContext } from 'react'; import Hypothesis from './Hypothesis'; - +import { Stepper, TextBlock, ActionBar, Button, Card, Toast } from '@egovernments/digit-ui-components'; +import { useTranslation } from "react-i18next"; import { useMyContext } from "../utils/context"; +import { concat } from 'lodash'; +const AssumptionContext = createContext('assumptionContext'); +export const useAssumptionContext = () => { + return useContext(AssumptionContext); +}; -const HypothesisWrapper = ({onSelect, props:customProps}) => { +const HypothesisWrapper = ({ onSelect, props: customProps }) => { + const { t } = useTranslation(); + const [currentStep, setCurrentStep] = useState(1); + const { state } = useMyContext(); + const [assumptionValues, setAssumptionValues] = useState(customProps?.sessionData?.HYPOTHESIS?.Assumptions?.assumptionValues ||[]); + const assumptionsFormValues = customProps?.sessionData?.ASSUMPTIONS_FORM?.assumptionsForm //array with key and value + const campaignType = customProps?.sessionData?.CAMPAIGN_DETAILS?.campaignDetails?.campaignType?.code + const resourceDistributionStrategyCode= customProps?.sessionData?.CAMPAIGN_DETAILS?.campaignDetails?.distributionStrat?.resourceDistributionStrategyCode + const searchParams = new URLSearchParams(window.location.search); + const [internalKey, setInternalKey] = useState(() => { + const keyParam = searchParams.get("internalKey"); + return keyParam ? parseInt(keyParam) : 1; + }); + const [showToast, setShowToast] = useState(null); + const [deletedAssumptions, setDeletedAssumptions] = useState([]); - const [selectedCategory, setSelectedCategory] = useState('GENERAL_INFORMATION'); - const { state, dispatch } = useMyContext(); - const assumptionCategories = state.HypothesisAssumptions[0].assumptionCategories + const moveToPreviousStep = () => { + if(currentStep >1){ + setCurrentStep((prevStep) => prevStep -1); + setInternalKey((prevKey) => prevKey - 1); + } + }; + const isLastStep = () => { + Digit.Utils.microplanv1.updateUrlParams({ isLastVerticalStep: false }); + Digit.Utils.microplanv1.updateUrlParams({ internalKey:1 }); + } + + const updateUrlParams = (params) => { + const url = new URL(window.location.href); + Object.entries(params).forEach(([key, value]) => { + url.searchParams.set(key, value); + }); + window.history.replaceState({}, "", url); + }; + const handleAssumptionChange = (category, event, item) => { + const newValue = event.target.value; + + setAssumptionValues((prevValues) => { + return prevValues.map((assumption) => { + // If the key matches, update the value; otherwise, return the existing assumption + if (assumption.key === item) { + return { + ...assumption, + category, + value: newValue === "" ? null : newValue, // Set to null if input is empty + }; + } + return assumption; + }); + }); + }; - const filteredAssumptions = assumptionCategories.find(category => category.category === selectedCategory)?.assumptions || []; + const handleNext = () => { + const currentAssumptions = assumptionCategories[currentStep - 1]?.assumptions || []; + const existingAssumptionKeys = assumptionValues?.map(assumption => assumption.key); + + // Filter current assumptions to only those that exist in assumptionValues and are not deleted + const visibleAssumptions = currentAssumptions.filter(item => + existingAssumptionKeys.includes(item) && !deletedAssumptions?.includes(item) + ); + + // Validate: Check if any value is empty for existing assumptions + const hasEmptyFields = visibleAssumptions.some(item => { + const value = assumptionValues.find(assumption => assumption.key === item)?.value; + return !value; // Check if any value is empty + }); + + if (hasEmptyFields) { + setShowToast({ + key: "error", + label: t("ERR_MANDATORY_FIELD"), + transitionTime: 3000, + }); + return; // Prevent moving to the next step + } + if (currentStep < assumptionCategories.length) { + setCurrentStep((prevStep) => prevStep + 1); + setInternalKey((prevKey) => prevKey + 1); // Update key in URL + } + }; - return <> -
- -
+ const filteredData = state.HypothesisAssumptions.filter((item) => { + const isHouseToHouseOrFixedPost = resourceDistributionStrategyCode === "HOUSE_TO_HOUSE" || + resourceDistributionStrategyCode === "FIXED_POST"; + + if(isHouseToHouseOrFixedPost){ + return ( + campaignType === item.campaignType && + resourceDistributionStrategyCode === item.resourceDistributionStrategyCode && + assumptionsFormValues?.selectedRegistrationDistributionMode?.code === item.isRegistrationAndDistributionHappeningTogetherOrSeparately + + ) + } + + return ( + campaignType === item.campaignType && + resourceDistributionStrategyCode === item.resourceDistributionStrategyCode && + assumptionsFormValues?.selectedRegistrationProcess?.code===item?.RegistrationProcess && + assumptionsFormValues?.selectedDistributionProcess?.code=== item.DistributionProcess && + assumptionsFormValues?.selectedRegistrationDistributionMode?.code === item.isRegistrationAndDistributionHappeningTogetherOrSeparately + ) + }); + const assumptionCategories = filteredData.length > 0 ? filteredData[0].assumptionCategories : []; + const filteredAssumptions = assumptionCategories.length > 0 ? (assumptionCategories[currentStep - 1]?.assumptions || []) : []; + + + + + const handleBack = () => { + if (currentStep >1) { + setCurrentStep((prevStep) => prevStep - 1); + setInternalKey((prevKey) => prevKey - 1); // Update key in URL + }else{ + window.dispatchEvent(new Event("moveToPrevious")) + } + }; + const handleStepClick = (step) => { + if (step < currentStep) { + setCurrentStep(step + 1); // Adjust for zero-based index + setInternalKey(step + 1); // Update key in URL + return; + } + const currentAssumptions = assumptionCategories[currentStep - 1]?.assumptions || []; + const existingAssumptionKeys = assumptionValues.map(assumption => assumption.key); + const visibleAssumptions = currentAssumptions.filter(item => + existingAssumptionKeys.includes(item) && !deletedAssumptions?.includes(item) + ); + const hasEmptyFields = visibleAssumptions.some(item => { + const value = assumptionValues.find(assumption => assumption.key === item)?.value; + return !value; // Check if any value is empty + }); + if(hasEmptyFields) return; + setCurrentStep(step + 1); // step is zero-based, so we add 1 + setInternalKey(step + 1); // Update key in URL + }; + + + + + + useEffect(() => { + window.addEventListener("verticalStepper", moveToPreviousStep); + return () => { + window.removeEventListener("verticalStepper", moveToPreviousStep); + }; + }, [internalKey]); + + useEffect(() => { + window.addEventListener("isLastStep", isLastStep); + return () => { + window.removeEventListener("isLastStep", isLastStep); + }; + }, [internalKey]); + + useEffect(() => { + onSelect(customProps.name, { assumptionValues }); + }, [assumptionValues, currentStep]); + + + useEffect(() => { + updateUrlParams({ internalKey,}); + }, [internalKey]); + + + useEffect(() => { + // Initialize assumptionValues with all assumptions set to null + const initialAssumptions = filteredAssumptions.map(item => ({ + category: null, + key: item, + value: null + })); + + // Create a set of existing keys for quick lookup + const existingKeys = new Set(assumptionValues.map(assumption => assumption.key)); + + // Filter out initialAssumptions to avoid duplicates + const newAssumptions = initialAssumptions.filter(assumption => !existingKeys.has(assumption.key)); + + // Update state only with non-duplicate assumptions + setAssumptionValues(prev => [...prev, ...newAssumptions]); + }, [filteredAssumptions]); + + + + + + useEffect(() => { + if (currentStep === assumptionCategories.length) { + Digit.Utils.microplanv1.updateUrlParams({ isLastVerticalStep: true }); + }else{ // Assuming 1 is the first step + Digit.Utils.microplanv1.updateUrlParams({ isLastVerticalStep: false }); + } + }, [currentStep]); + - -} + return ( + + + +
+
+ + + + + category.category)} + currentStep={currentStep} + onStepClick={handleStepClick} + direction={"vertical"} + /> + +
+ +
+ !deletedAssumptions.includes(item))} + onSelect={onSelect} + customProps={customProps} + /> +
+
+ + { (currentStep > 0 && currentStep < assumptionCategories.length ) && ( + +