From 256bfe843172aacbe474a237af39d1e593a67d4d Mon Sep 17 00:00:00 2001 From: Jagankumar <53823168+jagankumar-egov@users.noreply.github.com> Date: Mon, 16 Dec 2024 17:28:20 +0530 Subject: [PATCH 1/5] FIX : Branch name validator fixed Reference #2042 Sample Ticket for reference #2042 --- .github/workflows/branch-name-check.yml | 28 +++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/.github/workflows/branch-name-check.yml b/.github/workflows/branch-name-check.yml index 03296a9c1c..2305df947a 100644 --- a/.github/workflows/branch-name-check.yml +++ b/.github/workflows/branch-name-check.yml @@ -6,11 +6,17 @@ on: - master - develop - console + pull_request: branches: - master - develop - console + + types: + - opened + - edited + - reopened jobs: validate-names: @@ -47,13 +53,27 @@ jobs: PREFIXES="FEATURE|BUGFIX|RELEASE" PROJECTS="HCMPRE|DPG|SN" TICKET_PATTERN="[0-9]{1,5}" - TITLE_PATTERN="^($PREFIXES)\/($PROJECTS)-$TICKET_PATTERN .+$" + TITLE_PATTERN="^($PREFIXES)\/($PROJECTS)-$TICKET_PATTERN.*$" + MIN_TITLE_LENGTH=30 + - # Get the PR title - pr_title="${{ github.event.pull_request.title }}" + # Fetch the latest PR title dynamically + pr_title=$(curl -s https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }} | jq -r '.title') + echo "Fetched PR title: $pr_title" # Validate the PR title if [[ ! "$pr_title" =~ $TITLE_PATTERN ]]; then - echo "PR title '$pr_title' does not follow the correct pattern: $PREFIXES/$PROJECTS-: where is $TICKET_PATTERN" + echo "PR title '$pr_title' does not follow the correct pattern: $PREFIXES/$PROJECTS- : where is $TICKET_PATTERN" exit 1 fi + + # Validate the PR title length + if [[ ${#pr_title} -lt $MIN_TITLE_LENGTH ]]; then + echo "PR title '$pr_title' is too short. It must be at least $MIN_TITLE_LENGTH characters long, excluding the default pattern or ticket number." + exit 1 + fi + + echo "PR title validation passed." + + + From 3a009f6c26de707d148cb47cbf7f0b5b7de3fcdc Mon Sep 17 00:00:00 2001 From: Bhavya-egov <137176879+Bhavya-egov@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:17:15 +0530 Subject: [PATCH 2/5] FEATURE/HCMPRE-1644 : Changed download template name (#2035) FEATURE/HCMPRE-1644: Changed download template name Co-authored-by: Jagankumar <53823168+jagankumar-egov@users.noreply.github.com> --- .../modules/campaign-manager/src/components/UploadData.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/UploadData.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/UploadData.js index d9c5bbdb8f..85919bc2e2 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/UploadData.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/UploadData.js @@ -1036,7 +1036,8 @@ const UploadData = ({ formData, onSelect, ...props }) => { if (fileData && fileData?.[0]?.url) { setDownloadError(false); if (fileData?.[0]?.id) { - downloadExcelWithCustomName({ fileStoreId: fileData?.[0]?.id, customName: fileData?.[0]?.filename }); + const customFileName = parentId ? `${campaignName}_${t("HCM_FILLED")}_${fileData[0].filename}` : `${campaignName}_${fileData[0].filename}`; + downloadExcelWithCustomName({ fileStoreId: fileData?.[0]?.id, customName: customFileName }); setDownloadedTemplates((prev) => ({ ...prev, [type]: true, From 720bc7ab98cc3a0e117b3377e5e59ed1477859bd Mon Sep 17 00:00:00 2001 From: Swathi-eGov <137176788+Swathi-eGov@users.noreply.github.com> Date: Tue, 17 Dec 2024 12:28:10 +0530 Subject: [PATCH 3/5] =?UTF-8?q?BUGFIX/HCMPRE-1505=20:=20Updated=20Core=20V?= =?UTF-8?q?ersion=20To=20Fix=20Validation=20Issue=20on=20=E2=80=A6=20(#204?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUGFIX/HCMPRE-1505 : Updated Core Version To Fix Validation Issue on Profile Update --- health/micro-ui/web/core/package.json | 2 +- health/micro-ui/web/micro-ui-internals/example/package.json | 2 +- health/micro-ui/web/microplan/package.json | 2 +- health/micro-ui/web/package.json | 2 +- health/micro-ui/web/workbench/package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/health/micro-ui/web/core/package.json b/health/micro-ui/web/core/package.json index 7f1a1e2ae5..2393463b8d 100644 --- a/health/micro-ui/web/core/package.json +++ b/health/micro-ui/web/core/package.json @@ -14,7 +14,7 @@ "dependencies": { "@egovernments/digit-ui-libraries": "1.8.6", "@egovernments/digit-ui-module-workbench": "1.0.11", - "@egovernments/digit-ui-module-core": "1.8.12", + "@egovernments/digit-ui-module-core": "1.8.14", "@egovernments/digit-ui-module-utilities": "1.0.10", "@egovernments/digit-ui-components":"0.0.2-beta.56", "@egovernments/digit-ui-react-components": "1.8.12", diff --git a/health/micro-ui/web/micro-ui-internals/example/package.json b/health/micro-ui/web/micro-ui-internals/example/package.json index 1986943afd..58d349af55 100644 --- a/health/micro-ui/web/micro-ui-internals/example/package.json +++ b/health/micro-ui/web/micro-ui-internals/example/package.json @@ -12,7 +12,7 @@ "@egovernments/digit-ui-libraries": "1.8.6", "@egovernments/digit-ui-module-workbench": "1.0.11", "@egovernments/digit-ui-components": "0.0.2-beta.58", - "@egovernments/digit-ui-module-core": "1.8.12", + "@egovernments/digit-ui-module-core": "1.8.14", "@egovernments/digit-ui-module-utilities": "1.0.3", "@egovernments/digit-ui-react-components": "1.8.12", "@egovernments/digit-ui-module-hcmworkbench": "0.1.0", diff --git a/health/micro-ui/web/microplan/package.json b/health/micro-ui/web/microplan/package.json index 3a5dbb7c2a..303af6d3bb 100644 --- a/health/micro-ui/web/microplan/package.json +++ b/health/micro-ui/web/microplan/package.json @@ -14,7 +14,7 @@ "homepage": "/microplan-ui", "dependencies": { "@egovernments/digit-ui-libraries": "1.8.6", - "@egovernments/digit-ui-module-core": "1.8.12", + "@egovernments/digit-ui-module-core": "1.8.14", "@egovernments/digit-ui-module-utilities": "1.0.1-beta.23", "@egovernments/digit-ui-react-components": "1.8.12", "@egovernments/digit-ui-module-hcmmicroplanning": "0.0.2", diff --git a/health/micro-ui/web/package.json b/health/micro-ui/web/package.json index 07e08b69e7..3bce93e0ee 100644 --- a/health/micro-ui/web/package.json +++ b/health/micro-ui/web/package.json @@ -16,7 +16,7 @@ "dependencies": { "@egovernments/digit-ui-libraries": "1.8.2-beta.8", "@egovernments/digit-ui-module-workbench": "1.0.2-beta.7", - "@egovernments/digit-ui-module-core": "1.8.12", + "@egovernments/digit-ui-module-core": "1.8.14", "@egovernments/digit-ui-module-hrms": "1.8.0-beta.2", "@egovernments/digit-ui-react-components": "1.8.12", "@egovernments/digit-ui-components": "0.0.2-beta.58", diff --git a/health/micro-ui/web/workbench/package.json b/health/micro-ui/web/workbench/package.json index 181eb4b472..0517f31ca5 100644 --- a/health/micro-ui/web/workbench/package.json +++ b/health/micro-ui/web/workbench/package.json @@ -14,7 +14,7 @@ "dependencies": { "@egovernments/digit-ui-libraries": "1.8.6", "@egovernments/digit-ui-module-workbench": "1.0.11", - "@egovernments/digit-ui-module-core": "1.8.12", + "@egovernments/digit-ui-module-core": "1.8.14", "@egovernments/digit-ui-module-utilities": "1.0.10", "@egovernments/digit-ui-components": "0.0.2-beta.58", "@egovernments/digit-ui-react-components": "1.8.12", From 3dfbf1abf088482267188c3ddda80cb7024611db Mon Sep 17 00:00:00 2001 From: Bhavya-egov <137176879+Bhavya-egov@users.noreply.github.com> Date: Wed, 18 Dec 2024 09:39:12 +0530 Subject: [PATCH 4/5] FEATURE/HCMPRE-814: Added geojson view and edit (#2013) * added geojson view and edit * resolved pr comments --------- Co-authored-by: nabeelmd-eGov Co-authored-by: Jagankumar <53823168+jagankumar-egov@users.noreply.github.com> --- .../modules/campaign-manager/package.json | 2 + .../src/components/BaseMapSwitcher.js | 59 + .../src/components/BoundaryFilter.js | 45 + .../src/components/CustomScaleControl.js | 41 + .../src/components/MapFilterIndex.js | 41 + .../src/components/ViewMap.js | 281 ++++ .../src/pages/employee/ViewHierarchy.js | 1342 +++++++++-------- .../campaign-manager/src/utils/jsonReader.js | 29 + 8 files changed, 1192 insertions(+), 648 deletions(-) create mode 100644 health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/BaseMapSwitcher.js create mode 100644 health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/BoundaryFilter.js create mode 100644 health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/CustomScaleControl.js create mode 100644 health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/MapFilterIndex.js create mode 100644 health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/ViewMap.js create mode 100644 health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/jsonReader.js diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/package.json b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/package.json index b511ac4d03..9c9faa70b0 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/package.json +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/package.json @@ -27,7 +27,9 @@ "@rjsf/utils": "5.10.0", "@rjsf/validator-ajv8": "5.10.0", "ajv": "8.12.0", + "geojsonhint": "^2.0.0", "leaflet": "^1.9.4", + "proj4": "^2.15.0", "react": "17.0.2", "react-date-range": "1.4.0", "react-dom": "17.0.2", diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/BaseMapSwitcher.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/BaseMapSwitcher.js new file mode 100644 index 0000000000..749800380f --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/BaseMapSwitcher.js @@ -0,0 +1,59 @@ +import "leaflet/dist/leaflet.css"; +import React from "react"; +import * as DigitSvgs from "@egovernments/digit-ui-svg-components"; + + +export const generatePreviewUrl = (baseMapUrl, center = [0, 0], zoom = 5) => { + const lon = Math.floor(((center[1] + 180) / 360) * Math.pow(0, zoom)); + const lat = Math.floor( + ((1 - Math.log(Math.tan((center[0] * Math.PI) / 180) + 1 / Math.cos((center[0] * Math.PI) / 180)) / Math.PI) / 2) * Math.pow(2, zoom) + ); + if (baseMapUrl) { + const updatedUrl = baseMapUrl.replace("{z}", zoom).replace("{x}", lat).replace("{y}", lon).replace("{s}", "a"); + return updatedUrl; + } + // Return a default preview URL or handle this case as needed + return "default-preview-url.jpg"; // todo +}; + + + +const BaseMapSwitcher = ({ baseMaps, showBaseMapSelector, setShowBaseMapSelector, handleBaseMapToggle, selectedBaseMapName, basemapRef, t }) => { + if (!baseMaps) return null; + return ( +
+
setShowBaseMapSelector((previous) => !previous)} + onKeyUp={() => setShowBaseMapSelector((previous) => !previous)} + tabIndex={0} + style={{display:"flex"}} + > +

{t("LAYERS")}

+
{DigitSvgs.Layers && }
+
+
+ {showBaseMapSelector && ( +
+ {Object.entries(baseMaps).map(([name, baseMap], index) => { + return ( +
+ {t("ERROR_LOADING_BASE_MAP")} handleBaseMapToggle(name)} + /> +

{t(name)}

+
+ ); + })} +
+ )} +
+
+ ); +}; + +export default BaseMapSwitcher; diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/BoundaryFilter.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/BoundaryFilter.js new file mode 100644 index 0000000000..0c4259084a --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/BoundaryFilter.js @@ -0,0 +1,45 @@ +import React, { memo, useEffect, useRef, useState } from "react"; +import L from "leaflet"; +import "leaflet/dist/leaflet.css"; +import { Card, Button, MultiSelectDropdown, TooltipWrapper, Tooltip , Dropdown } from "@egovernments/digit-ui-components"; +import { useTranslation } from "react-i18next"; +import { InfoIconOutline, CardLabel } from "@egovernments/digit-ui-react-components"; + +const BoundaryFilter = ({t,filterOptions , onSelectBoundary}) => { + const [boundaryType , setBoundaryType] = useState(); + + function convertFilterOptionsToArray(filterOptions) { + const keys = Object.keys(filterOptions); + return keys + .filter((key) => Array.isArray(filterOptions[key])) + .map((key) => ({ + code: key, + name: key, + })); + } + + useEffect(() => { + onSelectBoundary({boundaryType: boundaryType }); + }, [boundaryType]); + + return ( +
+
+ ); +}; + +export default BoundaryFilter; diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/CustomScaleControl.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/CustomScaleControl.js new file mode 100644 index 0000000000..f2deacbd46 --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/CustomScaleControl.js @@ -0,0 +1,41 @@ +import React, { useEffect, useState } from "react"; + +const CustomScaleControl = ({ map }) => { + if (!map) return null; + const [scaleText, setScaleText] = useState(""); + // Function to calculate and update the scale text + const updateScale = () => { + // Calculate the scale based on the map's current zoom level + const maxWidthMeters = map.containerPointToLatLng([0, map.getSize().y]).distanceTo(map.containerPointToLatLng([100, map.getSize().y])); + const scale = maxWidthMeters / 1000; // Convert to kilometers + + // Format the scale text + const scaleTextData = scale < 1 ? `${Math.round(scale * 1000)} m` : `${Math.round(Math.round(scale.toFixed(0) / 10) * 10)} km`; + + // Update the scale text in the container element + setScaleText(scaleTextData); + }; + + // Effect to update the scale text when the map component mounts and on map zoom change + useEffect(() => { + // Update the scale text initially + updateScale(); + + // Register the map's zoom events to update the scale text + map.on("zoomend", updateScale); + + // Clean up event listener when the component unmounts + return () => { + map.off("zoomend", updateScale); + }; + }, [map]); + + return ( +
+ {scaleText} + + ); +}; + +export default CustomScaleControl; diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/MapFilterIndex.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/MapFilterIndex.js new file mode 100644 index 0000000000..cfa951ba69 --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/MapFilterIndex.js @@ -0,0 +1,41 @@ +import React,{Fragment} from "react"; +import * as DigitSvgs from "@egovernments/digit-ui-svg-components"; + +const IconCollection = {...DigitSvgs }; + +export const FilterItemBuilder = ({ item, MapFilters, t }) => { + let temp = MapFilters?.find((e) => e?.name === item)?.icon?.index; + let DynamicIcon = IconCollection?.[temp]; + // let icon; + // if (typeof DynamicIcon === "function") icon = DynamicIcon({}); + return DynamicIcon && typeof DynamicIcon === "function" ? ( +
+ +

{t(item)}

+
+ ) : ( + //
+ "" + ); + }; + +const MapFilterIndex = ({ filterSelections, MapFilters, t }) => { + return ( +
+ {filterSelections && filterSelections.length > 0 ? ( + <> + {filterSelections.map((item, index) => ( + //
+ + //

{t(item)}

+ //
+ ))} + + ) : ( + "" + )} +
+ ); + }; + +export default MapFilterIndex; \ No newline at end of file diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/ViewMap.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/ViewMap.js new file mode 100644 index 0000000000..ab1d46147c --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/components/ViewMap.js @@ -0,0 +1,281 @@ +import React, { memo, useEffect, useRef, useState, Fragment } from "react"; +import L from "leaflet"; +import proj4 from "proj4"; +import "leaflet/dist/leaflet.css"; +import { Card, Button ,Dropdown ,Toast } from "@egovernments/digit-ui-components"; +import { useTranslation } from "react-i18next"; +import * as DigitSvgs from "@egovernments/digit-ui-svg-components"; +import BoundaryFilter from "./BoundaryFilter"; +import MapFilterIndex from "./MapFilterIndex"; +import CustomScaleControl from "./CustomScaleControl"; +import BaseMapSwitcher from "./BaseMapSwitcher"; +import { jsonReader } from "../utils/jsonReader"; +import { Header } from "@egovernments/digit-ui-react-components"; + +// Utility function to introduce a delay +export const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +export const removeAllLayers = (map) => { + if (!map || !map._layers) { + console.error("Map or layers are undefined"); + return; + } + + Object.values(map._layers).forEach((layer) => { + if (layer?.remove) { + map.removeLayer(layer); + } + }); +}; + +const ViewMap = ({filterOptions}) => { + const { t } = useTranslation(); + const tenantId = Digit.ULBService.getCurrentTenantId(); + const [filterSelections, setFilterSelections] = useState([]); + const [baseMaps, setBaseMaps] = useState({}); + const [showBaseMapSelector, setShowBaseMapSelector] = useState(false); + const [selectedBaseMap, setSelectedBaseMap] = useState({}); + const [selectedBaseMapName, setSelectedBaseMapName] = useState(""); + const basemapRef = useRef(); + const mapRef = useRef(null); + var [map, setMap] = useState(null); + var [_mapNode, set__mapNode] = useState("map"); + const [loader, setLoader] = useState(false); + const [geoJsonData , setGeoJsonData] = useState(); + const [selectedBoundary , setSelectedBoundary] = useState(); + const [showToast, setShowToast] = useState(null); + const EPSG_3857 = "EPSG:3857"; + const EPSG_4326 = "EPSG:4326"; + // Effect to initialize map when data is fetched + useEffect(() => { + const BaseMapLayers = [ + { + name: "Street Map", + url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + minZoom: 1, + maxZoom: 20, + attribution: "© OpenStreetMap contributors", + }, + { + name: "Satellite", + url: "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", + minZoom: 1, + maxZoom: 20, + attribution: "© OpenTopoMap", + }, + { + name: "Topography", + url: "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", + minZoom: 1, + maxZoom: 20, + attribution: "© OpenTopoMap contributors", + }, + { + name: "Light Theme", + url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png", + minZoom: 1, + maxZoom: 20, + attribution: "© CARTO", + }, + ]; + if (!BaseMapLayers || (BaseMapLayers && BaseMapLayers.length === 0)) return; + let baseMaps = {}; + let defaultBaseMap = undefined; + BaseMapLayers.forEach((item) => { + if (item.url) { + const layer = L.tileLayer(item.url, { + minZoom: item?.minZoom, + maxZoom: item?.maxZoom, + attribution: item?.attribution, + }); + baseMaps[item?.name] = { + metadata: item, + layer, + }; + if (!defaultBaseMap) + defaultBaseMap = { + name: item?.name, + layer, + }; + } + }); + setSelectedBaseMapName(defaultBaseMap?.name); + setBaseMaps(baseMaps); + if (!map) { + init(_mapNode, defaultBaseMap); + } + }, []); + + // Function to initialize map + const init = (id, defaultBaseMap) => { + if (mapRef.current) return; + + let mapConfig = { + center: [0, 0], + zoomControl: false, + zoom: 3, + scrollwheel: true, + minZoom: 3, + }; + + let map_i = L.map(id, mapConfig); + + mapRef.current = map_i; + + // Add the zoom control manually at the bottom left + L.control + .zoom({ + position: "bottomleft", // Position it at the bottom left + }) + .addTo(map_i); + const defaultBaseLayer = defaultBaseMap?.layer.addTo(map_i); + setSelectedBaseMap(defaultBaseLayer); + setMap(map_i); + }; + + const isValidGeoJSON = (geoJson) => { + try { + if (geoJson?.type === "FeatureCollection" || geoJson?.type === "GeometryCollection") { + return true; + } + throw new Error("Invalid GeoJSON structure"); + } catch (error) { + console.error("GeoJSON validation error:", error); + return false; + } + }; + +useEffect(() => { + if (map && geoJsonData) { + // removeAllLayers(map); + + // Validate GeoJSON data before adding to the map + if (isValidGeoJSON(geoJsonData)) { + try { + const geoJsonLayer = L.geoJSON(geoJsonData).addTo(map); + const bounds = geoJsonLayer.getBounds(); + + if (bounds?.isValid()) { + const southWest = proj4(EPSG_3857, EPSG_4326, [ + bounds._southWest.lng, + bounds._southWest.lat, + ]); + const northEast = proj4(EPSG_3857, EPSG_4326, [ + bounds._northEast.lng, + bounds._northEast.lat, + ]); + + map.fitBounds([ + [southWest[1], southWest[0]], + [northEast[1], northEast[0]], + ]); + } + } catch (e) { + console.error("Failed to add GeoJSON layer", e); + setShowToast({ label: t("INVALID_GEOJSON_FILE"), isError: "error" }); + } + } else { + setShowToast({ label: t("INVALID_GEOJSON_FILE"), isError: "error" }); + } + } + }, [map, geoJsonData, selectedBaseMap]); + + + useEffect(() => { + if ("scrollRestoration" in history) { + history.scrollRestoration = "manual"; + } + }, []); + + useEffect(() => { + // Fetch the GeoJSON data from the URL + + const fetchGeoJson = async () => { + if (filterOptions) { + + let boundaryKey; + + if (selectedBoundary) { + // Use the selected boundary type if provided + boundaryKey = filterOptions[selectedBoundary]; + } else { + // Dynamically determine the "country" boundary by fetching the first valid key + const countryBoundary = Object.entries(filterOptions).find( + ([key, value]) => Array.isArray(value) && value.length > 0 + ); + boundaryKey = countryBoundary ? countryBoundary[1] : null; + } + if (boundaryKey && boundaryKey.length > 0) { + const data = await jsonReader({ fileStoreId: boundaryKey[0] }); + setGeoJsonData(data); + } else { + console.warn(`No data found for ${selectedBoundary}`); + } + } + }; + + fetchGeoJson(); + }, [filterOptions, selectedBoundary]); + + const handleBaseMapToggle = (newBaseMap) => { + const currentBaseLayer = selectedBaseMap; + if (currentBaseLayer) { + currentBaseLayer.remove(); + } + const newBaseLayer = baseMaps[newBaseMap].layer.addTo(map); + // Add the new base layer to the bottom of the layer stack + newBaseLayer.addTo(map); + + // Update the baseLayer state + setSelectedBaseMap(newBaseLayer); + setSelectedBaseMapName(newBaseMap); + }; + + const handleBoundarySelect = (value) =>{ + setSelectedBoundary(value); + } + + return ( + <> + + + { + handleBoundarySelect(value?.boundaryType); + }} + /> +
+
+
+ +
+
+
+
{DigitSvgs.NorthArrow && }
+ +
+
+ {filterSelections && filterSelections.length > 0 && ( + + )} +
+
+
+
+ {/* {loader && } */} + {showToast && setShowToast(null)} />} + + ); +}; + +export default ViewMap; \ No newline at end of file diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/ViewHierarchy.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/ViewHierarchy.js index d6c96051f5..8a9f51e858 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/ViewHierarchy.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/ViewHierarchy.js @@ -1,471 +1,469 @@ -import { Card, Uploader, Button, ActionBar, Toast, Loader, PopUp, InfoCard } from "@egovernments/digit-ui-components"; -import React, { useEffect, useState, useRef} from "react"; +import { Card, Uploader, Button, ActionBar, Toast, Loader, PopUp, InfoCard, Tab } from "@egovernments/digit-ui-components"; +import React, { useEffect, useState, useRef } from "react"; import { useTranslation } from "react-i18next"; import XlsPreviewNew from "../../components/XlsPreviewNew"; import { Svgicon } from "../../utils/Svgicon"; import { useHistory } from "react-router-dom"; import { useLocation } from "react-router-dom"; -import MapView from "../../components/MapView"; +import ViewMap from "../../components/ViewMap"; import { DustbinIcon } from "../../components/icons/DustbinIcon"; import * as XLSX from "xlsx"; import { CONSOLE_MDMS_MODULENAME } from "../../Module"; import validateBoundaryExcelContent from "../../utils/validateBoundaryExcel"; const ViewHierarchy = () => { - const { t } = useTranslation(); - const location = useLocation(); - const history = useHistory(); - const tenantId = Digit.ULBService.getCurrentTenantId(); - const searchParams = new URLSearchParams(location.search); - const defaultHierarchyType = searchParams.get("defaultHierarchyType"); - const hierarchyType = searchParams.get("hierarchyType"); - const locale = Digit?.SessionStorage.get("initData")?.selectedLanguage || "en_IN"; - - const inputRef = useRef(null); // Ref to trigger file input - - const [defData, setDefData] = useState([]); - const [hierData, setHierData] = useState([]); - - const [previewPage, setPreviewPage] = useState(false); - const [firstPage, setFirstPage] = useState(true); - const [fileUrl, setFileUrl] = useState(""); - const [fileData, setFileData] = useState({}); - const [fileStoreId, setFileStoreId] = useState(""); - const [fileName, setFileName] = useState(""); - const [showToast, setShowToast] = useState(null); // State to handle toast notifications - const [dataCreateToast, setDataCreateToast] = useState(false); - const [disable, setDisable] = useState(false); - const [disableFile, setDisableFile] = useState(true); - const [dataCreationGoing, setDataCreationGoing] = useState(false); - const [noOfRows, setNoOfRows] = useState(100); - - const { data: baseTimeOut } = Digit.Hooks.useCustomMDMS(tenantId, CONSOLE_MDMS_MODULENAME, [{ name: "baseTimeOut" }]); - - const callSearch = async(hierarchy) =>{ - const res = await Digit.CustomService.getResponse({ - url: `/boundary-service/boundary-hierarchy-definition/_search`, - body: { - BoundaryTypeHierarchySearchCriteria: { - tenantId: tenantId, - limit: 2, - offset: 0, - hierarchyType: hierarchy - } - } - }); - return res; - - } - const language = Digit.StoreData.getCurrentLanguage(); - const modulePrefix = "hcm"; - const stateCode = Digit.ULBService.getCurrentTenantId(); - const moduleCode = `boundary-${hierarchyType.toLowerCase()}`; - const { isLoading, data } = Digit.Services.useStore({ - stateCode, - moduleCode, - language, - modulePrefix, + const { t } = useTranslation(); + const location = useLocation(); + const history = useHistory(); + const tenantId = Digit.ULBService.getCurrentTenantId(); + const searchParams = new URLSearchParams(location.search); + const defaultHierarchyType = searchParams.get("defaultHierarchyType"); + const hierarchyType = searchParams.get("hierarchyType"); + const locale = Digit?.SessionStorage.get("initData")?.selectedLanguage || "en_IN"; + + const inputRef = useRef(null); // Ref to trigger file input + + const [defData, setDefData] = useState([]); + const [hierData, setHierData] = useState([]); + + const [previewPage, setPreviewPage] = useState(false); + const [firstPage, setFirstPage] = useState(true); + const [fileUrl, setFileUrl] = useState(""); + const [fileData, setFileData] = useState({}); + const [fileStoreId, setFileStoreId] = useState(""); + const [fileName, setFileName] = useState(""); + const [showToast, setShowToast] = useState(null); // State to handle toast notifications + const [dataCreateToast, setDataCreateToast] = useState(false); + const [disable, setDisable] = useState(false); + const [disableFile, setDisableFile] = useState(true); + const [dataCreationGoing, setDataCreationGoing] = useState(false); + const [noOfRows, setNoOfRows] = useState(100); + const [showPopUp, setShowPopUp] = useState(false); + const [uploadedFiles, setUploadedFiles] = React.useState([]); + const [activeLink, setActiveLink] = useState({ + code: "BOUNDARY_GEOJSON", + name: "BOUNDARY_GEOJSON", + }); + const [filterOptions, setFilterOptions] = useState({}); + + const { data: baseTimeOut } = Digit.Hooks.useCustomMDMS(tenantId, CONSOLE_MDMS_MODULENAME, [{ name: "baseTimeOut" }]); + + const callSearch = async (hierarchy) => { + const res = await Digit.CustomService.getResponse({ + url: `/boundary-service/boundary-hierarchy-definition/_search`, + body: { + BoundaryTypeHierarchySearchCriteria: { + tenantId: tenantId, + limit: 2, + offset: 0, + hierarchyType: hierarchy, + }, + }, }); - - const [viewState, setViewState] = useState(false); - - const fetchData = async()=>{ - try{ - const res = await callSearch(defaultHierarchyType); - if(res?.BoundaryHierarchy?.[0]?.boundaryHierarchy) setDefData(res?.BoundaryHierarchy?.[0]?.boundaryHierarchy); - const res1 = await callSearch(hierarchyType); - if(res1?.BoundaryHierarchy?.[0]?.boundaryHierarchy) setHierData(res1?.BoundaryHierarchy?.[0]?.boundaryHierarchy); - setViewState(true); - } - catch(error) { - } + return res; + }; + const language = Digit.StoreData.getCurrentLanguage(); + const modulePrefix = "hcm"; + const stateCode = Digit.ULBService.getCurrentTenantId(); + const moduleCode = `boundary-${hierarchyType.toLowerCase()}`; + const { isLoading, data } = Digit.Services.useStore({ + stateCode, + moduleCode, + language, + modulePrefix, + }); + + const [viewState, setViewState] = useState(false); + + const fetchData = async () => { + try { + const res = await callSearch(defaultHierarchyType); + if (res?.BoundaryHierarchy?.[0]?.boundaryHierarchy) setDefData(res?.BoundaryHierarchy?.[0]?.boundaryHierarchy); + const res1 = await callSearch(hierarchyType); + if (res1?.BoundaryHierarchy?.[0]?.boundaryHierarchy) setHierData(res1?.BoundaryHierarchy?.[0]?.boundaryHierarchy); + setViewState(true); + } catch (error) {} + }; + + useEffect(() => { + fetchData(); + }, []); + + const generateTemplate = async () => { + const res = await Digit.CustomService.getResponse({ + url: `/project-factory/v1/data/_download`, + body: {}, + params: { + tenantId: tenantId, + type: "boundaryManagement", + hierarchyType: hierarchyType, + campaignId: "default", + }, + }); + return res; + }; + const downloadExcelTemplate = async () => { + // const res = await generateFile() + const resFile = await generateTemplate(); + if (resFile && resFile?.GeneratedResource?.[0]?.fileStoreid) { + // Splitting filename before .xlsx or .xls + const fileNameWithoutExtension = hierarchyType; + Digit.Utils.campaign.downloadExcelWithCustomName({ + fileStoreId: resFile?.GeneratedResource?.[0]?.fileStoreid, + customName: fileNameWithoutExtension, + }); + setShowPopUp(false); + } else if (resFile && resFile?.GeneratedResource?.[0]?.status === "inprogress") { + setShowToast({ label: t("PLEASE_WAIT_AND_RETRY_AFTER_SOME_TIME"), isError: "info" }); + setShowPopUp(false); + } else { + setShowToast({ label: t("PLEASE_WAIT_AND_RETRY_AFTER_SOME_TIME"), isError: "info" }); + setShowPopUp(false); } + }; + + const [uiValError, setUiValError] = useState(false); + const [uiErrorMsg, setUiErrorMsg] = useState(""); + + const handleUpload = () => { + inputRef.current.click(); + }; + + const handleFileChange = async (event) => { + const file = event.target.files[0]; // Get the selected file + if (file) { + setFileName(file.name); + // Check file extension + const validExtensions = ["xls", "xlsx", "json"]; + const fileExtension = file.name.split(".").pop().toLowerCase(); // Get the file extension + + if (!validExtensions.includes(fileExtension)) { + setShowToast({ label: t("INVALID_FILE_FORMAT"), isError: "error" }); + setDisableFile(true); + event.target.value = ""; + return; // Exit the function if the file is not valid + } - useEffect(()=>{ - fetchData(); - }, []); - - - - const generateTemplate = async() => { - const res = await Digit.CustomService.getResponse({ - url: `/project-factory/v1/data/_download`, - body: { - }, - params: { - tenantId: tenantId, - type: "boundaryManagement", - hierarchyType: hierarchyType, - campaignId: "default" - } - }); - return res; - - } - const downloadExcelTemplate = async() => { - // const res = await generateFile() - const resFile = await generateTemplate(); - if (resFile && resFile?.GeneratedResource?.[0]?.fileStoreid) { - // Splitting filename before .xlsx or .xls - const fileNameWithoutExtension = hierarchyType ; - Digit.Utils.campaign.downloadExcelWithCustomName({ fileStoreId: resFile?.GeneratedResource?.[0]?.fileStoreid, customName: fileNameWithoutExtension }); - setShowPopUp(false); - } - else if ( resFile && resFile?.GeneratedResource?.[0]?.status === "inprogress"){ - setShowToast({label: t("PLEASE_WAIT_AND_RETRY_AFTER_SOME_TIME"), isError: "info" }); - setShowPopUp(false); - } - else{ - setShowToast({label: t("PLEASE_WAIT_AND_RETRY_AFTER_SOME_TIME"), isError: "info" }); - setShowPopUp(false); + try { + // Parse the file and validate its content + const isValid = await validateBoundaryExcelContent(file, t); + if (!isValid.success) { + // setShowToast({ label: isValid.error, isError: "error" }); + setUiValError(true); + setUiErrorMsg(isValid.error); + setDisableFile(true); + event.target.value = ""; + return; // Exit if validation fails } + // Call function to upload the validated file to an API + await uploadFileToAPI([file]); + setDisableFile(false); + setUiValError(false); + setShowToast({ label: t("FILE_UPLOADED_SUCCESSFULLY"), isError: "success" }); + } catch (error) { + event.target.value = ""; + setShowToast({ + label: error?.response?.data?.Errors?.[0]?.message || t("FILE_UPLOAD_FAILED"), + isError: "error", + }); + } } + }; + + const uploadFileToAPI = async (files) => { + const module = "HCM"; + let file = files[0]; + let fileDataTemp = {}; + fileDataTemp.fileName = file?.name; + + const reader = new FileReader(); + reader.onload = async (event) => { + const data = new Uint8Array(event.target.result); + const workbook = XLSX.read(data, { type: "array" }); + + // Assume the first sheet + const sheetName = workbook.SheetNames[0]; + const sheet = workbook.Sheets[sheetName]; + + // Convert sheet to JSON and count rows + const rows = XLSX.utils.sheet_to_json(sheet); + + // After parsing locally, upload to API + const response = await Digit.UploadServices.Filestorage(module, file, tenantId); + fileDataTemp.fileStoreId = response?.data?.[0]?.fileStoreId; + let fileStoreIdTemp = response?.data?.files?.[0]?.fileStoreId; + setFileStoreId(response?.data?.files?.[0]?.fileStoreId); + const { data: { fileStoreIds: fileUrlTemp } = {} } = await Digit.UploadServices.Filefetch([fileStoreIdTemp], tenantId); + fileDataTemp.url = fileUrlTemp?.[0]?.url; + + setFileUrl(fileDataTemp?.url); + setFileData(fileDataTemp); + }; - const [uiValError, setUiValError] = useState(false); - const [uiErrorMsg, setUiErrorMsg] = useState(""); + reader.readAsArrayBuffer(file); // Read the file as an array buffer + }; + + const callCreateDataApi = async () => { + setDisable(true); + setDataCreationGoing(true); + try { + setDataCreateToast(true); + + const createResponse = await Digit.CustomService.getResponse({ + url: "/project-factory/v1/data/_create", + params: {}, + body: { + ResourceDetails: { + tenantId: tenantId, + type: "boundaryManagement", + fileStoreId: fileStoreId, + action: "create", + hierarchyType: hierarchyType, + additionalDetails: { + source: "boundary", + }, + campaignId: "default", + }, + }, + }); - const handleUpload = () => { - inputRef.current.click(); - }; + const id = createResponse?.ResourceDetails?.id; + const typeOfData = createResponse?.ResourceDetails?.type; - const handleFileChange = async (event) => { - const file = event.target.files[0]; // Get the selected file - if (file) { - setFileName(file.name); - // Check file extension - const validExtensions = ['xls', 'xlsx']; - const fileExtension = file.name.split('.').pop().toLowerCase(); // Get the file extension - - if (!validExtensions.includes(fileExtension)) { - setShowToast({ label: t("INVALID_FILE_FORMAT"), isError: "error" }); - setDisableFile(true); - event.target.value = ""; - return; // Exit the function if the file is not valid - } - + if (id) { try { - // Parse the file and validate its content - const isValid = await validateBoundaryExcelContent(file, t); - if (!isValid.success) { - // setShowToast({ label: isValid.error, isError: "error" }); - setUiValError(true); - setUiErrorMsg(isValid.error); - setDisableFile(true); - event.target.value = ""; - return; // Exit if validation fails - } - - // Call function to upload the validated file to an API - await uploadFileToAPI([file]); - setDisableFile(false); - setUiValError(false); - setShowToast({ label: t("FILE_UPLOADED_SUCCESSFULLY"), isError: "success" }); - } catch (error) { - event.target.value = ""; - setShowToast({ - label: error?.response?.data?.Errors?.[0]?.message || t("FILE_UPLOAD_FAILED"), - isError: "error", + await pollForStatusCompletion(id, typeOfData); + setDataCreateToast(false); + history.push(`/${window.contextPath}/employee/campaign/response?isSuccess=${true}`, { + message: "ES_BOUNDARY_DATA_CREATED_SUCCESS_RESPONSE", + preText: "ES_BOUNDARY_DATA__CREATED_SUCCESS_RESPONSE_PRE_TEXT", + actionLabel: "CS_BOUNDARY_dATA_NEW_RESPONSE_ACTION", + actionLink: `/${window.contextPath}/employee/campaign/boundary/data?defaultHierarchyType=${defaultHierarchyType}&hierarchyType=${hierarchyType}`, + secondaryActionLabel: "CS_HOME", + secondaryActionLink: `/${window?.contextPath}/employee`, }); + + // setShowToast({ label: `${t("WBH_HIERARCHY_CREATED")}`, isError: "success" }); + } catch (pollError) { + throw pollError; // Propagate polling errors to the outer catch block } } - }; - const uploadFileToAPI = async (files) => { - const module = "HCM"; - let file = files[0]; - let fileDataTemp = {}; - fileDataTemp.fileName = file?.name - - const reader = new FileReader(); - reader.onload = async (event) => { - const data = new Uint8Array(event.target.result); - const workbook = XLSX.read(data, { type: "array" }); - - // Assume the first sheet - const sheetName = workbook.SheetNames[0]; - const sheet = workbook.Sheets[sheetName]; - - // Convert sheet to JSON and count rows - const rows = XLSX.utils.sheet_to_json(sheet); - - // After parsing locally, upload to API - const response = await Digit.UploadServices.Filestorage(module, file, tenantId); - fileDataTemp.fileStoreId = response?.data?.[0]?.fileStoreId; - let fileStoreIdTemp = response?.data?.files?.[0]?.fileStoreId; - setFileStoreId(response?.data?.files?.[0]?.fileStoreId); - const { data: { fileStoreIds: fileUrlTemp } = {} } = await Digit.UploadServices.Filefetch([fileStoreIdTemp], tenantId); - fileDataTemp.url = fileUrlTemp?.[0]?.url; - - setFileUrl(fileDataTemp?.url); - setFileData(fileDataTemp); - }; - - reader.readAsArrayBuffer(file); // Read the file as an array buffer - - }; - - const callCreateDataApi = async () => { - setDisable(true); - setDataCreationGoing(true); + return createResponse; + } catch (error) { + setDisable(false); + let label; + + // Handle known errors like polling timeout and max retries + if (error.message === "Polling timeout" || error.message === "Max retries reached") { + label = `${t("WBH_BOUNDARY_CREATION_TIMEOUT")}: ${t("WBH_OPERATION_INCOMPLETE")}`; + } else { + // Initialize the label with a failure message + label = `${t("WBH_BOUNDARY_CREATION_FAIL")}: `; + if (error?.message) label += `${t(error?.message)}`; // the message here is sent from the polling mechnism which sendds the error code from backend. + } + + setShowToast({ label, isError: "error" }); + setDataCreationGoing(false); + return {}; + } + }; + + const reqCriteriaResource = { + url: `/project-factory/v1/data/_search`, + body: { + SearchCriteria: { + tenantId: tenantId, + status: "completed", + type: "boundaryGeometryManagement", + }, + }, + config: { + enabled: true, + select: (data) => { + return data?.ResourceDetails; + }, + }, + }; + + const { data: resourceData } = Digit.Hooks.useCustomAPIHook(reqCriteriaResource); + + useEffect(() => { + if (resourceData) { + setFilterOptions(resourceData?.[0]?.additionalDetails); + } + }, [resourceData]); + + const pollForStatusCompletion = async (id, typeOfData) => { + // const pollInterval = 2000; // Poll every 1 second + const maxRetries = 20; // Maximum number of retries + let retries = 0; + const baseDelay = baseTimeOut?.["HCM-ADMIN-CONSOLE"]?.baseTimeOut?.[0]?.baseTimeOut; + const maxTime = baseTimeOut?.["HCM-ADMIN-CONSOLE"]?.baseTimeOut?.[0]?.maxTime; + const pollInterval = Math.max(baseDelay * noOfRows, maxTime); + + return new Promise((resolve, reject) => { + const poll = async () => { try { - setDataCreateToast(true); - - const createResponse = await Digit.CustomService.getResponse({ - url: "/project-factory/v1/data/_create", + + const searchResponse = await Digit.CustomService.getResponse({ + url: "/project-factory/v1/data/_search", params: {}, body: { - ResourceDetails: { + SearchCriteria: { + id: [id], tenantId: tenantId, - type: "boundaryManagement", - fileStoreId: fileStoreId, - action: "create", - hierarchyType: hierarchyType, - additionalDetails: { - source: "boundary", - }, - campaignId: "default" + type: typeOfData, }, }, }); - - const id = createResponse?.ResourceDetails?.id; - const typeOfData = createResponse?.ResourceDetails?.type; - - if (id) { - try { - await pollForStatusCompletion(id, typeOfData); - setDataCreateToast(false); - history.push(`/${window.contextPath}/employee/campaign/response?isSuccess=${true}`, { - message: "ES_BOUNDARY_DATA_CREATED_SUCCESS_RESPONSE", - preText: "ES_BOUNDARY_DATA__CREATED_SUCCESS_RESPONSE_PRE_TEXT", - actionLabel: "CS_BOUNDARY_dATA_NEW_RESPONSE_ACTION", - actionLink: `/${window.contextPath}/employee/campaign/boundary/data?defaultHierarchyType=${defaultHierarchyType}&hierarchyType=${hierarchyType}`, - secondaryActionLabel: "CS_HOME", - secondaryActionLink: `/${window?.contextPath}/employee`, - }); - - // setShowToast({ label: `${t("WBH_HIERARCHY_CREATED")}`, isError: "success" }); - } catch (pollError) { - throw pollError; // Propagate polling errors to the outer catch block - } - } - - return createResponse; - } catch (error) { - setDisable(false); - let label; - - // Handle known errors like polling timeout and max retries - if (error.message === "Polling timeout" || error.message === "Max retries reached") { - label = `${t("WBH_BOUNDARY_CREATION_TIMEOUT")}: ${t("WBH_OPERATION_INCOMPLETE")}`; + + const status = searchResponse?.ResourceDetails?.[0]?.status; + let errorString = searchResponse?.ResourceDetails?.[0]?.additionalDetails.error; + let errorObject = {}; + let errorCode = "HIERARCHY_FAILED"; + if (errorString) errorObject = JSON.parse(errorString); + if (errorObject) errorCode = errorObject.code; + + if (status === "completed") { + setShowToast({ label: `${t("WBH_HIERARCHY_STATUS_COMPLETED")}`, isError: "success" }); + setDataCreationGoing(false); + resolve(true); + } else if (status === "failed") { + reject(new Error(errorCode)); } else { - - // Initialize the label with a failure message - label = `${t("WBH_BOUNDARY_CREATION_FAIL")}: `; - if(error?.message) label += `${t(error?.message)}`; // the message here is sent from the polling mechnism which sendds the error code from backend. - + retries++; + setTimeout(poll, pollInterval); } - - setShowToast({ label, isError: "error" }); - setDataCreationGoing(false); - return {}; + } catch (error) { + retries++; + setTimeout(poll, pollInterval); } - - }; - - // const pollForStatusCompletion = async (id, typeOfData) => { - // const pollInterval = 1000; // Poll every 1 second - // const maxRetries = 100; // Maximum number of retries - // let retries = 0; - - // return new Promise((resolve, reject) => { - // const poll = async () => { - // try { - - // if (retries >= maxRetries) { - // setDataCreationGoing(false); - // reject(new Error("Max retries reached")); - // return; - // } - - // const searchResponse = await Digit.CustomService.getResponse({ - // url: "/project-factory/v1/data/_search", - // params: {}, - // body: { - // SearchCriteria: { - // id: [id], - // tenantId: tenantId, - // type: typeOfData - // } - // }, - // }); - - - // const status = searchResponse?.ResourceDetails?.status; - - // if (status === "completed") { - // setShowToast({ label: `${t("WBH_HIERARCHY_STATUS_COMPLETED")}`, isError: "success" }); - // setDataCreationGoing(false); - // resolve(true); - // } else if (status === "failed") { - // reject(new Error("Operation failed")); - // } else { - // retries++; - // setTimeout(poll, pollInterval); - // } - // } catch (error) { - // setDataCreationGoing(false); - // reject(error); - // } - // }; - - // // Start the polling - // poll().catch(reject); - - // // Set a timeout for the entire polling operation - // const timeoutDuration = (maxRetries + 1) * pollInterval; - // setTimeout(() => { - // if (retries < maxRetries) { // Only reject if not already resolved - // setDataCreationGoing(false); - // reject(new Error("Polling timeout")); - // } - // }, timeoutDuration); - // }); - // }; - - const pollForStatusCompletion = async (id, typeOfData) => { - // const pollInterval = 2000; // Poll every 1 second - const maxRetries = 20; // Maximum number of retries - let retries = 0; - const baseDelay = baseTimeOut?.["HCM-ADMIN-CONSOLE"]?.baseTimeOut?.[0]?.baseTimeOut; - const maxTime = baseTimeOut?.["HCM-ADMIN-CONSOLE"]?.baseTimeOut?.[0]?.maxTime; - const pollInterval = Math.max(baseDelay * noOfRows , maxTime); - - return new Promise((resolve, reject) => { - const poll = async () => { - try { - // if (retries >= maxRetries) { - // setDataCreationGoing(false); - // reject(new Error("Max retries reached")); - // return; - // } - - const searchResponse = await Digit.CustomService.getResponse({ - url: "/project-factory/v1/data/_search", - params: {}, - body: { - SearchCriteria: { - id: [id], - tenantId: tenantId, - type: typeOfData, - }, - }, - }); - - const status = searchResponse?.ResourceDetails?.[0]?.status; - let errorString = searchResponse?.ResourceDetails?.[0]?.additionalDetails.error; - let errorObject={}; - let errorCode="HIERARCHY_FAILED"; - if(errorString) errorObject = JSON.parse(errorString); - if(errorObject) errorCode = errorObject.code; - - if (status === "completed") { - setShowToast({ label: `${t("WBH_HIERARCHY_STATUS_COMPLETED")}`, isError: "success" }); - setDataCreationGoing(false); - resolve(true); - } else if (status === "failed") { - reject(new Error(errorCode)); - } else { - retries++; - setTimeout(poll, pollInterval); - } - } catch (error) { - // console.error("Error while polling:", error); - retries++; - setTimeout(poll, pollInterval); - } - }; - - // Start the polling - poll().catch(reject); - - // Set a timeout for the entire polling operation - // const timeoutDuration = (maxRetries + 1) * pollInterval; - // setTimeout(() => { - // if (retries < maxRetries) { - // // Only reject if not already resolved - // setDataCreationGoing(false); - // reject(new Error("Polling timeout")); - // } - // }, timeoutDuration); - }); }; - - const createData = async()=> { - const res = await callCreateDataApi(); - + // Start the polling + poll().catch(reject); + }); + }; + + const createData = async () => { + const res = await callCreateDataApi(); + }; + + const trimming = (val) => { + return `${t((hierarchyType + "_" + val.trim().replace(/[\s_]+/g, "")).toUpperCase())}`; + }; + + const removeFile = () => { + setFileName(""); + setDisableFile(true); + }; + + const handleFileUploadGeo = async (event, index, boundaryType) => { + const file = event.target.files[0]; + const module = "HCM"; + let fileDataTemp = {}; + fileDataTemp.fileName = file?.name; + const maxFileSize = 5 * 1024 * 1024; // 5 MB in bytes + if (file.size > maxFileSize) { + setShowToast({ label: t("FILE_TOO_LARGE"), isError: "error" }); + setDisableFile(true); + return; // Exit the function if the file is too large } - - const trimming = (val)=>{ - return `${t(( hierarchyType + "_" + val.trim().replace(/[\s_]+/g, '')).toUpperCase())}`; + if (file) { + // Simulate file upload and retrieve fileStoreId + // const fileStoreId = await uploadFile(file); // Replace with actual API call + const response = await Digit.UploadServices.Filestorage(module, file, tenantId); + const curFileStoreId = response?.data?.files?.[0]?.fileStoreId; + fileDataTemp.fileStoreId = curFileStoreId; + const { data: { fileStoreIds: fileUrlTemp } = {} } = await Digit.UploadServices.Filefetch([curFileStoreId], tenantId); + fileDataTemp.url = fileUrlTemp?.[0]?.url; + // Update state with the uploaded file details + setUploadedFiles((prevFiles) => { + const updatedFiles = [...prevFiles]; + updatedFiles[index] = { boundaryType, fileName: file.name, fileStoreId: curFileStoreId }; + return updatedFiles; + }); } + }; - const removeFile = ()=>{ - setFileName(""); - setDisableFile(true); - } - const [showPopUp, setShowPopUp] = useState(false); - - if(!viewState || isLoading) - { - return ( - - ) + useEffect(() => { + if (uploadedFiles && uploadedFiles.length > 0) { + const formattedFilterOptions = uploadedFiles.reduce((acc, file) => { + const boundaryKey = `boundary_${file.boundaryType.toLowerCase()}_default`; + if (!acc[boundaryKey]) { + acc[boundaryKey] = []; + } + acc[boundaryKey].push(file.fileStoreId); + return acc; + }, {}); + + // Merge the new data into the existing filterOptions object + setFilterOptions((prevOptions) => ({ + ...prevOptions, + ...formattedFilterOptions, + })); } - - else - { - return ( - - {firstPage && -
- -
{t(`HIERARCHY`)} {hierarchyType}
-
- {hierData.map((hierItem, index) => { - // Check if the index is less than defData length - const isLessThanDefData = index < defData.length; - - if (isLessThanDefData) { - if (hierItem?.boundaryType === defData[index]?.boundaryType) { - return ( -
-
- {trimming(hierItem?.boundaryType)} -
-
- {/* -
- -
- {`${t(( hierarchyType + "_" + hierItem?.boundaryType).toUpperCase())}-geojson.json`} -
-
-
*/} -
-
- ); - } else { - return ( -
-
-
- {trimming(hierItem?.boundaryType)} -
- {/* { + if (disableFile == false) { + setActiveLink({ + code: "BOUNDARY_EXCEL", + name: "BOUNDARY_EXCEL", + }); + } + }, [disableFile]); + + const processUploadedFiles = () => { + const processedData = uploadedFiles.map((file) => ({ + boundaryType: file.boundaryType, + fileStoreId: file.fileStoreId, + })); + // Make an API call or save the data + }; + if (!viewState || isLoading) { + return ; + } else { + return ( + + {firstPage && ( +
+ +
+ {t(`HIERARCHY`)} {hierarchyType} +
+
+ {hierData.map((hierItem, index) => { + // Check if the index is less than defData length + const isLessThanDefData = index < defData.length; + + if (isLessThanDefData) { + if (hierItem?.boundaryType === defData[index]?.boundaryType) { + hierData[index].isProcessed = true; // Add this flag to mark it processed + return ( +
+
{trimming(hierItem?.boundaryType)}
+
+ +
+ +
+ {`${t((hierarchyType + "_" + hierItem?.boundaryType).toUpperCase())}-geojson.json`} +
+
+
+
+
+ ); + } else { + return ( +
+
+
{trimming(hierItem?.boundaryType)}
+ {/* { title="" variation="secondary" /> */} -
-
-
-
- ); - } - } else { - return ( -
-
-
- {trimming(hierItem?.boundaryType)} -
- {/* {}} - showAsTags - uploadedFiles={[]} - variant="uploadFile" - style={{width:"50rem"}} - /> */} - {/* -
-
-
-
- ); - } - })} -
-
- -
-
{t("UPLOAD_EXCEL")}
-
-
- { disableFile &&
-
{t("UPLOAD_EXCEL_FOR_ALL_BOUNDARIES")}
- -
} - {!disableFile && -
-
-
- {fileName} -
-
removeFile()} style={{ cursor: "pointer", marginTop:"1.15rem" }}> - -
+
+
+
+
+ ); + } + } + })} + +
+ +
+
{t("NEWLY_ADDED_BOUNDARY_DATA")}
+
+
+
+ {/*
+ {hierData.filter(item => !item.isProcessed).map((unprocessedItem, index) => ( +
+
+ {trimming(unprocessedItem?.boundaryType)}
+
+
- } -
-
- {uiValError && } -
-
-
- {history.push(`/${window.contextPath}/employee/campaign/boundary/home`)}} - type="button" - variation="secondary" - textStyles={{width:'unset'}} - />, -
*/} +
+ {hierData + .filter((item) => !item.isProcessed) + .map((unprocessedItem, index) => ( +
+
+
{trimming(unprocessedItem?.boundaryType)}
+
+ {/* File input for the specific boundary */} + handleFileUploadGeo(e, index, unprocessedItem.boundaryType)} + /> +
+
+
- } - {showPopUp && ( - { - setShowPopUp(false); - }} - onClose={() => { - setShowPopUp(false); + ))} +
+ {uiValError && } +
+
+
+ { + history.push(`/${window.contextPath}/employee/campaign/boundary/home`); + }} + type="button" + variation="secondary" + textStyles={{ width: "unset" }} + />, +
+ )} + {showPopUp && ( + { + setShowPopUp(false); + }} + onClose={() => { + setShowPopUp(false); + }} + footerChildren={[ +
+ )} + + ); + } }; export default ViewHierarchy; diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/jsonReader.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/jsonReader.js new file mode 100644 index 0000000000..6f8166d3f5 --- /dev/null +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/jsonReader.js @@ -0,0 +1,29 @@ +import axios from "axios"; +export const jsonReader = async ({ fileStoreId = null }) => { + if (!fileStoreId) { + return null; + } + + try { + const response = await axios.get("/filestore/v1/files/id", { + responseType: "arraybuffer", + headers: { + Accept: "application/json", + "auth-token": Digit.UserService.getUser()?.["access_token"], + }, + params: { + tenantId: Digit.ULBService.getCurrentTenantId(), + fileStoreId: fileStoreId, + }, + }); + + const arrayBuffer = response.data; + const text = new TextDecoder("utf-8").decode(new Uint8Array(arrayBuffer)); + const jsonData = JSON.parse(text); + return jsonData; + } catch (error) { + console.error("Error fetching or decoding JSON:", error); + return null; + } + }; + \ No newline at end of file From 6a94576c39c3986c9a977bc10530b898ef0a1eea Mon Sep 17 00:00:00 2001 From: nabeelmd-eGov Date: Wed, 18 Dec 2024 13:29:01 +0530 Subject: [PATCH 5/5] BUGFIX/HCMPRE-1243: fixes currentKey (#2052) * fixes current key * fixes --------- Co-authored-by: NabeelAyubee --- .../src/pages/employee/SetupCampaign.js | 20 +++++++++---------- .../employee/UpdateDatesWithBoundaries.js | 4 ++-- .../src/utils/setupCampaignHelpers.js | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/SetupCampaign.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/SetupCampaign.js index a3cb5db253..d0362ff5a4 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/SetupCampaign.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/SetupCampaign.js @@ -124,7 +124,7 @@ const SetupCampaign = ({ hierarchyType, hierarchyData }) => { useEffect(() => { if (isPreview === "true") { setIsDraftCreated(true); - setCurrentKey(14); + setCurrentKey(16); } else if (isDraft === "true") { setIsDraftCreated(true); if (isSkip === "false") { @@ -216,9 +216,9 @@ const SetupCampaign = ({ hierarchyType, hierarchyData }) => { useEffect(() => { setIsSubmitting(false); - if (currentKey === 14 && isSummary !== "true") { + if (currentKey === 16 && isSummary !== "true") { updateUrlParams({ key: currentKey, summary: true }); - } else if (currentKey !== 14) { + } else if (currentKey !== 16) { updateUrlParams({ key: currentKey, summary: false }); // setSummaryErrors(null); } @@ -275,7 +275,7 @@ const SetupCampaign = ({ hierarchyType, hierarchyData }) => { updateUrlParams({ id: data?.CampaignDetails?.id }); draftRefetch(); if (currentKey == 6) { - setCurrentKey(14); + setCurrentKey(16); } else { setCurrentKey(currentKey + 1); } @@ -283,7 +283,7 @@ const SetupCampaign = ({ hierarchyType, hierarchyData }) => { }); } else { if (currentKey == 6) { - setCurrentKey(14); + setCurrentKey(16); } else { setCurrentKey(currentKey + 1); } @@ -694,7 +694,7 @@ const SetupCampaign = ({ hierarchyType, hierarchyData }) => { setShouldUpdate(true); } if (isChangeDates === "true" && currentKey == 6) { - setCurrentKey(14); + setCurrentKey(16); } if (!filteredConfig?.[0]?.form?.[0]?.isLast && !filteredConfig[0].form[0].body[0].mandatoryOnAPI) { setCurrentKey(currentKey + 1); @@ -813,7 +813,7 @@ const SetupCampaign = ({ hierarchyType, hierarchyData }) => { setShouldUpdate(true); } if (isChangeDates === "true" && currentKey == 6) { - setCurrentKey(14); + setCurrentKey(16); } if (!filteredConfig?.[0]?.form?.[0]?.isLast && !filteredConfig[0].form[0].body[0].mandatoryOnAPI) { @@ -847,7 +847,7 @@ const SetupCampaign = ({ hierarchyType, hierarchyData }) => { setShouldUpdate(true); } if (isChangeDates === "true" && currentKey == 6) { - setCurrentKey(14); + setCurrentKey(16); } if (!filteredConfig?.[0]?.form?.[0]?.isLast && !filteredConfig[0].form[0].body[0].mandatoryOnAPI) { @@ -869,7 +869,7 @@ const SetupCampaign = ({ hierarchyType, hierarchyData }) => { const key = parseInt(filteredSteps[0].key); const name = filteredSteps[0].name; if (step === 6 && Object.keys(totalFormData).includes("HCM_CAMPAIGN_UPLOAD_USER_DATA")) { - setCurrentKey(14); + setCurrentKey(16); setCurrentStep(5); } else if (step === 0 && totalFormData["HCM_CAMPAIGN_NAME"] && totalFormData["HCM_CAMPAIGN_TYPE"] && totalFormData["HCM_CAMPAIGN_DATE"]) { setCurrentKey(4); @@ -1013,7 +1013,7 @@ const SetupCampaign = ({ hierarchyType, hierarchyData }) => { // noCardStyle={currentStep === 7 ? false : true} onSecondayActionClick={onSecondayActionClick} label={ - isChangeDates === "true" && currentKey == 14 + isChangeDates === "true" && currentKey == 16 ? t("HCM_UPDATE_DATE") : isChangeDates === "true" ? null diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/UpdateDatesWithBoundaries.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/UpdateDatesWithBoundaries.js index 7cd6924108..59e49a7cba 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/UpdateDatesWithBoundaries.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/pages/employee/UpdateDatesWithBoundaries.js @@ -116,7 +116,7 @@ function UpdateDatesWithBoundaries() { // text: t("ES_CAMPAIGN_CREATE_SUCCESS_RESPONSE_TEXTKK"), // info: t("ES_CAMPAIGN_SUCCESS_INFO_TEXTKK"), actionLabel: t("HCM_DATE_CHANGE_SUCCESS_RESPONSE_ACTION"), - actionLink: `/${window.contextPath}/employee/campaign/setup-campaign?id=${id}&preview=true&action=false&actionBar=true&key=14&summary=true` + actionLink: `/${window.contextPath}/employee/campaign/setup-campaign?id=${id}&preview=true&action=false&actionBar=true&key=16&summary=true` }); } else { const url = getProjectServiceUrl(); @@ -133,7 +133,7 @@ function UpdateDatesWithBoundaries() { // text: t("ES_CAMPAIGN_CREATE_SUCCESS_RESPONSE_TEXTKK"), // info: t("ES_CAMPAIGN_SUCCESS_INFO_TEXTKK"), actionLabel: t("HCM_DATE_CHANGE_SUCCESS_RESPONSE_ACTION"), - actionLink: `/${window.contextPath}/employee/campaign/setup-campaign?id=${id}&preview=true&action=false&actionBar=true&key=14&summary=true`, + actionLink: `/${window.contextPath}/employee/campaign/setup-campaign?id=${id}&preview=true&action=false&actionBar=true&key=16&summary=true`, }); } } catch (error) { diff --git a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/setupCampaignHelpers.js b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/setupCampaignHelpers.js index dc59256e2a..9caca3235c 100644 --- a/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/setupCampaignHelpers.js +++ b/health/micro-ui/web/micro-ui-internals/packages/modules/campaign-manager/src/utils/setupCampaignHelpers.js @@ -396,7 +396,7 @@ export const cycleDataRemap=(data)=> { export const draftFilterStep = (totalFormData,campaignConfig) => { const stepFind = (name) => { const step = campaignConfig?.[0]?.form.find((step) => step.name === name); - return step ? parseInt(step.stepCount, 14) : null; + return step ? parseInt(step.stepCount, 16) : null; }; let v = []; if (totalFormData?.HCM_CAMPAIGN_NAME?.campaignName) v.push(stepFind("HCM_CAMPAIGN_NAME"));