).toString();
+// const requestUrl = `${baseUrl}&${queryString}`;
+
+// console.log("Fetching data from:", requestUrl);
+
+// const controller = new AbortController();
+// const timeout = setTimeout(() => controller.abort(), 90000); // 10 seconds timeout
+
+// const response = await fetch(requestUrl, {
+// method: "GET",
+// signal: controller.signal,
+// });
+
+// clearTimeout(timeout);
+
+// if (!response.ok) {
+// throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
+// }
+
+// const result = await response.json();
+
+// if (!result?.ListResult || !Array.isArray(result.ListResult)) {
+// return { error: "Invalid API response structure" };
+// }
+
+// if (result.ListResult.length === 0) {
+// return { error: "No records found" };
+// }
+
+// // Ensure proper type comparison and collect all matches
+// const matchingRecords = result.ListResult.filter(
+// (item: any) => String(item.GISID).trim() === String(GISID).trim()
+// );
+
+// return matchingRecords.length > 0 ? matchingRecords : { error: "GISID not found in results" };
+// } catch (error: any) {
+// if (error.name === "AbortError") {
+// return { error: "Request timed out" };
+// }
+// console.error("Error fetching data:", error.message);
+// return { error: error.message };
+// }
+// };
+
+// async function fetchTradeLicenseDetails(billNumber, clickedFeatDet) {
+// try {
+// // Normalize zone/ward (ensure leading 'N' and padding)
+// const rawZone = (clickedFeatDet?.[0]?.zone_number_mis || clickedFeatDet?.[0]?.zone_number || '').toString();
+// const rawWard = (clickedFeatDet?.[0]?.ward_number_mis || clickedFeatDet?.[0]?.ward_number || '').toString();
+
+// const zone = rawZone.startsWith('N') ? rawZone : 'N' + rawZone.padStart(2, '0');
+// const ward = rawWard.startsWith('N') ? rawWard : 'N' + rawWard.padStart(3, '0');
+
+// // First get prof/trade data for this bill
+// const profResp = await fetch(
+// `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getProfTradedata&propid=${encodeURIComponent(billNumber)}`
+// );
+// const profData = await profResp.json();
+
+// if (!profData?.ListResult || !Array.isArray(profData.ListResult)) {
+// return `No professional/trade data available for bill ${billNumber}
`;
+// }
+
+// // filter only trade license items
+// const tradeItems = profData.ListResult.filter(it => it.SOURCE === "TRADELICENSE");
+// const profTaxItems = profData.ListResult.filter(it => it.SOURCE === "PROFTAX");
+// console.log("profItems",profTaxItems)
+
+// // Track PTNAN used in trade licenses
+// let collectedPTNAN = new Set();
+// if (tradeItems.length === 0) {
+// return `No trade-license items found for bill ${billNumber}
`;
+// }
+
+// let html = `Trade License lookups (zone: ${zone}, ward: ${ward}):
`;
+
+// // Query getTradeLicensedata for each trade item
+// for (const item of tradeItems) {
+// const licenseNumber =
+// item.LICENSE_NUMBER ||
+// item.licenseNumber ||
+// item.LICENSE_NO ||
+// item.LICENCE_NUMBER ||
+// item.licence_no ||
+// item.ID;
+
+// const url = `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getTradeLicensedata&zone=${encodeURIComponent(zone)}&ward=${encodeURIComponent(ward)}&licenseNumber=${encodeURIComponent(licenseNumber)}`;
+
+// try {
+// const resp = await fetch(url, { method: 'GET' });
+// const json = await resp.json();
+
+// html += ``;
+// html += `
Trade licenseNumber: ${licenseNumber}
`;
+
+// if (json?.ListResult && Array.isArray(json.ListResult) && json.ListResult.length > 0) {
+// for (const lic of json.ListResult) {
+// // ---- Trade License Details ----
+// html += `
+//
+//
+// Ward / Zone
+// ${lic.WARD} / ${lic.ZONE}
+//
+//
+// Trade License No
+// ${lic.LICENSE_NUMBER}
+//
+//
+// PT Number
+// ${lic.PTNAN}
+//
+//
+// Establishment
+// ${lic.ESTABLISHMENT_NAME}
+//
+//
+// Trade Nature
+// ${lic.TRADE_NAME}
+//
+//
+// Applicant
+// ${lic.APPLICANT_NAME} ${lic.FATHER_NAME ? `(S/o: ${lic.FATHER_NAME})` : ''}
+//
+//
+// Address
+// ${lic.APPLICANT_ADDRESS}
+//
+//
+// Expiry Date
+// ${lic.LIC_EXPIRY_DATE?.split(" ")[0]}
+//
+//
+// Total Fee
+// ₹${lic.TOTAL_FEE}
+//
+//
+// `;
+
+// // ---- Professional Tax Details ----
+// if (lic.PTNAN) {
+// collectedPTNAN.add(lic.PTNAN);
+// try {
+// const ptUrl = `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getProfessionalTaxData&zone=${encodeURIComponent(lic.ZONE)}&ward=${encodeURIComponent(lic.WARD)}&ptnan=${encodeURIComponent(lic.PTNAN)}`;
+// const ptResp = await fetch(ptUrl, { method: "GET" });
+// const ptJson = await ptResp.json();
+
+// if (ptJson?.ListResult && Array.isArray(ptJson.ListResult) && ptJson.ListResult.length > 0) {
+// for (const pt of ptJson.ListResult) {
+// html += `
+//
Professional Tax Details
+//
+//
+// PTNAN
+// ${pt.PTNAN}
+//
+//
+// Name
+// ${pt.NAME}
+//
+//
+// Area
+// ${pt.AREA}
+//
+//
+// Address
+// ${pt.ADDRESS}
+//
+//
+// Locality
+// ${pt.LOCALITY || "N/A"}
+//
+//
+// Street
+// ${pt.STREET}
+//
+//
+// Ward / Zone
+// ${pt.WARD} / ${pt.ZONE}
+//
+//
+// Demand
+// ₹${pt.DEMAND}
+//
+//
+// Property Tax No
+// ${pt.PROPERTYTAX_NUMBER}
+//
+//
+// Contact
+// ${pt.CONTACT_NUMBER || "N/A"}
+//
+//
+// Created Date
+// ${pt.CREATEDDATE}
+//
+//
+// `;
+// }
+// } else {
+// html += `
No Professional Tax data found for PTNAN ${lic.PTNAN}
`;
+// }
+// } catch (err) {
+// console.error("Error fetching professional tax data", err);
+// html += `
Error fetching Professional Tax for PTNAN ${lic.PTNAN}
`;
+// }
+// }
+// }
+// } else if (json?.ResultStatus === false && json?.Message) {
+// html += `
API returned: ${json.Message}
`;
+// } else {
+// html += `
No records found for licenseNumber ${licenseNumber}
`;
+// }
+
+// html += `
`;
+// } catch (err) {
+// console.error('Error fetching trade license data', err);
+// html += `Error fetching trade license for ${licenseNumber}
`;
+// }
+// }
+// // console.log("collectedPTNAN",collectedPTNAN)
+// // ✅ SHOW NOT LINKED PROFTAX AT END ONLY
+// let remainingPT = profTaxItems.filter(pt => !collectedPTNAN.has(pt.ID));
+
+// // ✅ REMAINING PROFTAX → FETCH FULL DETAILS USING API 3
+// // let remainingPT = profTaxItems.filter(pt => !collectedPTNAN.has(pt.ID));
+
+// if (remainingPT.length > 0) {
+// html += `
+//
+// Other Professional Tax
+//
+// `;
+
+// for (const ptItem of remainingPT) {
+// try {
+// const ptId = ptItem.ID;
+
+// const ptUrl = `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getProfessionalTaxData&zone=${encodeURIComponent(zone)}&ward=${encodeURIComponent(ward)}&ptnan=${encodeURIComponent(ptId)}`;
+
+// const ptResp = await fetch(ptUrl, { method: "GET" });
+// const ptJson = await ptResp.json();
+
+// if (ptJson?.ListResult && ptJson.ListResult.length > 0) {
+// const pt = ptJson.ListResult[0];
+
+// html += `
+//
+//
+// PTNAN
+// ${pt.PTNAN}
+//
+//
+// Name
+// ${pt.NAME}
+//
+//
+// Area
+// ${pt.AREA}
+//
+//
+// Address
+// ${pt.ADDRESS}
+//
+//
+// Street
+// ${pt.STREET}
+//
+//
+// Ward / Zone
+// ${pt.WARD} / ${pt.ZONE}
+//
+//
+// Demand
+// ₹${pt.DEMAND}
+//
+//
+// Property Tax No
+// ${pt.PROPERTYTAX_NUMBER}
+//
+//
+// `;
+// } else {
+// html += `No PT data found for ${ptId}
`;
+// }
+// } catch (err) {
+// console.error("Error fetching remaining PT", err);
+// html += `Error fetching PT for ${ptItem.ID}
`;
+// }
+// }
+// }
+
+// return html;
+// } catch (err) {
+// console.error('fetchTradeLicenseDetails error', err);
+// return `Error while fetching trade license details
`;
+// }
+// }
+
+// async function fetchProfessionalTaxDetails(ptnan, zone, ward) {
+// try {
+// const url = `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getProfessionalTaxData&zone=${encodeURIComponent(zone)}&ward=${encodeURIComponent(ward)}&ptnan=${encodeURIComponent(ptnan)}`;
+
+// const resp = await fetch(url, { method: "GET" });
+// const json = await resp.json();
+
+// let html = `Professional Tax Details (zone: ${zone}, ward: ${ward}, PTNAN: ${ptnan}):
`;
+
+// if (json?.ListResult && Array.isArray(json.ListResult) && json.ListResult.length > 0) {
+// for (const item of json.ListResult) {
+// html += `
+//
+//
+// PTNAN
+// ${item.PTNAN}
+//
+//
+// Name
+// ${item.NAME}
+//
+//
+// Area
+// ${item.AREA}
+//
+//
+// Address
+// ${item.ADDRESS}
+//
+//
+// Locality
+// ${item.LOCALITY || "N/A"}
+//
+//
+// Street
+// ${item.STREET}
+//
+//
+// Ward / Zone
+// ${item.WARD} / ${item.ZONE}
+//
+//
+// Demand
+// ₹${item.DEMAND}
+//
+//
+// Property Tax No
+// ${item.PROPERTYTAX_NUMBER}
+//
+//
+// Contact
+// ${item.CONTACT_NUMBER || "N/A"}
+//
+//
+// Created Date
+// ${item.CREATEDDATE}
+//
+//
+// `;
+// }
+// } else if (json?.Message) {
+// html += `API returned: ${json.Message}
`;
+// } else {
+// html += `No Professional Tax records found for PTNAN ${ptnan}
`;
+// }
+
+// return html;
+// } catch (err) {
+// console.error("Error fetching professional tax data", err);
+// return `Error fetching Professional Tax details for PTNAN ${ptnan}
`;
+// }
+// }
+
+
+
+// async function getTradeAssessmentHTML(billNumber) {
+// let html = '';
+
+// try {
+// const profTradeResponse = await fetch(
+// `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getProfTradedata&propid=${billNumber}`
+// );
+// const profData = await profTradeResponse.json();
+
+// if (profData?.ResultStatus === true && Array.isArray(profData.ListResult)) {
+// const tradeItems = profData.ListResult.filter(item => item.SOURCE === "TRADELICENSE");
+// const profItems = profData.ListResult.filter(item => item.SOURCE === "PROFTAX");
+
+
+// } else {
+// // html += `No Professional Trade Assessment data found.
`;
+// }
+// } catch (err) {
+// html += `Error fetching trade data
`;
+// }
+
+// return html;
+// }
+
+
+// async function buildCustomContent(clickedFeatDet) {
+// console.log(clickedFeatDet)
+// // const billNumbers = await fetchBillNumbers(clickedFeatDet[0].gcc_gis_id);
+// // const tradeDetails = await fetchTradeDetails(clickedFeatDet[0].gcc_gis_id);
+// // const assessmentCount = await fetchAssessmentCount(clickedFeatDet[0].gcc_gis_id);
+// const directionUrl = await generateDirectionUrl(clickedFeatDet[1]);
+// // const geocode=getGoogleMapsUrl(clickedFeatDet[1])
+// // const tradeCount = await fetchTradeCount(clickedFeatDet[0].gcc_gis_id);
+// // const professionCount = await fetchProfessionCount(clickedFeatDet[0].gcc_gis_id);
+// // const maxFloor = await fetchMaxFloor(clickedFeatDet[0].gcc_gis_id);
+
+
+
+// // const new_api = await fetchGISIDFetch_new_erp({
+// // Zone: "N02",
+// // ward: "N020",
+// // area: "MANALI",
+// // loc: "VIMALAPURAM",
+// // street: "VENUGOPAL STREET",
+// // GISID: "G-020-23-01149"
+// // });
+
+
+// const zoneNumber = clickedFeatDet[0].zone_number;
+// const formattedZone = "N" + zoneNumber;
+
+// const wardNumber = clickedFeatDet[0].ward_number;
+// const formattedward = "N" + wardNumber;
+// const new_api = await fetchGISIDFetch_new_erp({
+// Zone: clickedFeatDet[0].zone_number_mis || formattedZone,
+// ward: clickedFeatDet[0].ward_number_mis || formattedward ,
+// area: clickedFeatDet[0].area_mis,
+// loc: clickedFeatDet[0].locality_mis,
+// street: clickedFeatDet[0].street_mis,
+// GISID: clickedFeatDet[0].gcc_gis_id
+// });
+// console.log(new_api);
+
+// const usage = await fetchSystemPropertyUsage(clickedFeatDet[0].gcc_gis_id);
+
+// let customContent = `
+//
+
+
+
+// `;
+
+// if (new_api) {
+// if (new_api.error) {
+// let errorMessage = new_api.error === "Request timed out"
+// ? "The ERP API encountered a timeout error."
+// : `Error: ${new_api.error}`;
+
+// customContent += `${errorMessage}
`;
+// } else {
+// // Ensure `new_api` is always treated as an array
+// const records = Array.isArray(new_api) ? new_api : [new_api];
+
+// customContent += ``;
+
+
+// customContent += `
+//
+// Note: The below property details were fetched from ERP API.
+//
+// `;
+
+// for (const record of records) {
+// const billNumber = record.BILL_NUMBER || 'N/A';
+
+// customContent += `
+//
+//
+// Bill Number: ${billNumber}
+//
+//
+// `;
+
+// if (billNumber !== 'N/A') {
+// const tradeHTML = await getTradeAssessmentHTML(billNumber);
+// customContent += tradeHTML;
+// }
+// // NEW: fetch trade license details using zone/ward from clickedFeatDet and license (or ID fallback)
+// const tradeLicenseHTML = await fetchTradeLicenseDetails(billNumber, clickedFeatDet);
+// customContent += tradeLicenseHTML;
+
+
+// customContent += ` `;
+// }
+
+// customContent += `
`;
+// }
+// } else {
+// customContent += `No records found
`;
+// }
+
+// return customContent;
+// }
+
+// const clearGraphic = () => {
+// jimuMapView?.view.graphics.removeAll();
+// setPopupContent(null);
+
+// };
+// const handleMapView = (view) => {
+// setJimuMapView(view)
+// setMapView(view)
+// // if (!jimuMapView && view) {
+
+// // let dispatchItem = { 'view': view }
+
+// // getAppStore().dispatch(
+// // appActions.widgetStatePropChange(props.id, 'sharedData.queryString', dispatchItem)
+// // );
+// // alert('dispatched item')
+// // }
+
+
+// }
+// return (
+//
+// {/* JimuMapViewComponent to integrate map in Experience Builder */}
+
+// {props.useMapWidgetIds && props.useMapWidgetIds.length === 1 && (
+//
+
+// )}
+
+
+//
+// );
+// };
+
+// export default Popup;
+
+import React, { useEffect, useState, useRef } from 'react';
+import { JimuMapViewComponent, JimuMapView } from 'jimu-arcgis';
+import { fetchGISIDFetch, fetchBillNoData_New } from '../../../utils/queries';
+import './custom-popup.css';
+import FeatureLayer from '@arcgis/core/layers/FeatureLayer'
+import { useMapView } from '../../../utils/mapviewservice'; // Import the context
+import { AllWidgetProps, appActions, getAppStore, IMState } from 'jimu-core';
+import { useSelector } from 'react-redux';
+import CustomContent from '@arcgis/core/popup/content/CustomContent';
+
+const Popup = (props: AllWidgetProps) => {
+ const [jimuMapView, setJimuMapView] = useState(null);
+ const [popupContent, setPopupContent] = useState(null);
+ const [clickHandle, setClickHandle] = useState(null); // Store event handle
+ const [buildingPopup, storeBuildingPopup] = useState(null);
+ const [buldingPopupContent, storePopupContent_Building] = useState(null)
+ const [buildingLayer, storeBuildingLayer] = useState(null)
+ const [clickedFeatDet, storeclickedFeatureDet] = useState(null)
+ const isVisibleRef = useRef(false);
+ const [definitionExpr, setBuildDefinition] = useState(null)
+ const [definitionSetActive, flag_definitionSet] = useState(null)
+ const [isLoading, setIsLoading] = useState(null)
+ const { setMapView } = useMapView();
+ let loadingTimeout;
+
+ const Establishment_TABLE = new FeatureLayer({
+ url: "https://gisgcc.chennaicorporation.gov.in/server/rest/services/Revenue/SurveyTables/FeatureServer/1"
+ })
+ const Property_TABLE = new FeatureLayer({
+ url: 'https://gisgcc.chennaicorporation.gov.in/server/rest/services/Revenue/Buildings_Revenue_MIS/MapServer/5'
+ })
+ const Professional_TABLE = new FeatureLayer({
+ url: "https://gisgcc.chennaicorporation.gov.in/server/rest/services/Revenue/SurveyTables/FeatureServer/3"
+ })
+ const Floor_TABLE = new FeatureLayer({
+ url: "https://gisgcc.chennaicorporation.gov.in/server/rest/services/Revenue/SurveyTables/FeatureServer/2"
+ })
+
+ const monitoredWidgetIds = ['widget_58', 'widget_59', 'widget_72', 'widget_73', 'widget_66', 'widget_67'];
+
+ const [isVisible, setIsVisible] = useState(false);
+
+ const checkMonitoredWidgets = (): boolean => {
+ const widgetStates = getAppStore().getState().widgetsRuntimeInfo;
+ return monitoredWidgetIds.some(
+ (widgetId) =>
+ widgetStates[widgetId] && widgetStates[widgetId]?.state === 'OPENED'
+ );
+ };
+
+ const updateDependentWidgetVisibility = (): void => {
+ const isAnyWidgetOpen = checkMonitoredWidgets();
+ setIsVisible(isAnyWidgetOpen);
+ };
+
+ useEffect(() => {
+ const storeUnsubscribe = getAppStore().subscribe(() => {
+ updateDependentWidgetVisibility();
+ });
+
+ return () => {
+ storeUnsubscribe();
+ };
+ }, []);
+
+ useEffect(() => {
+ let cleanup;
+
+ if (jimuMapView) {
+
+ jimuMapView.view.on('popup-open', () => {
+ alert('popup open triggered')
+ console.log('Popup has been opened!');
+ });
+ jimuMapView.view.on('popup-close', () => {
+ console.log('Popup has been closed!');
+ });
+
+ if (jimuMapView && !isVisible) {
+ fetchBuildingLyr()
+ }
+ }
+ }, [jimuMapView]);
+
+ const BuildDefinition_Usage = useSelector(
+ (state: IMState) => state.widgetsState?.['widget_59']?.state?.building
+ );
+ const BuildDefinition_Type = useSelector(
+ (state: IMState) => state.widgetsState?.['widget_58']?.state?.building
+ );
+ const BuildDefinition_RevenueFilter = useSelector(
+ (state: IMState) => state.widgetsState?.['widget_67']?.state?.building
+ );
+ const BuildDefinition_CommonQry = useSelector(
+ (state: IMState) => state.widgetsState?.['widget_36']?.state?.building
+ );
+ const BuildDefinition_floor = useSelector(
+ (state: IMState) => state.widgetsState?.['widget_66']?.state?.building
+ );
+
+ useEffect(() => {
+ if (BuildDefinition_Usage) {
+ setBuildDefinition(BuildDefinition_Usage)
+ buildingLayer.refresh()
+ }
+ }, [BuildDefinition_Usage])
+
+ useEffect(() => {
+ if (BuildDefinition_Type) {
+ setBuildDefinition(BuildDefinition_Type)
+ buildingLayer.refresh()
+ }
+ }, [BuildDefinition_Type])
+
+ useEffect(() => {
+ if (BuildDefinition_RevenueFilter) {
+ setBuildDefinition(BuildDefinition_RevenueFilter)
+ buildingLayer.refresh()
+ }
+ }, [BuildDefinition_RevenueFilter])
+
+ useEffect(() => {
+ if (BuildDefinition_CommonQry) {
+ setBuildDefinition(BuildDefinition_CommonQry)
+ buildingLayer.refresh()
+ }
+ }, [BuildDefinition_CommonQry])
+ useEffect(() => {
+ if (BuildDefinition_floor) {
+ setBuildDefinition(BuildDefinition_floor)
+ buildingLayer.refresh()
+ }
+ }, [BuildDefinition_floor])
+
+ useEffect(() => {
+ if (definitionSetActive) {
+ if (buildingLayer.definitionExpression !== definitionExpr) {
+ buildingLayer.when(() => {
+ buildingLayer.defintionExpression = definitionExpr
+ })
+ }
+ }
+ }, [definitionSetActive])
+
+ useEffect(() => {
+ let watchHandle
+ if (buildingLayer && jimuMapView) {
+ jimuMapView.view.whenLayerView(buildingLayer).then((layerView) => {
+ watchHandle = layerView.watch('updating', (isUpdating) => {
+ if (isUpdating) {
+ if (!isLoading) {
+ setIsLoading(true);
+ }
+ } else {
+ clearTimeout(loadingTimeout);
+ loadingTimeout = setTimeout(() => {
+ setIsLoading(false);
+ }, 300);
+ }
+
+ buildingLayer.when(() => {
+ if (buildingLayer.definitionExpression !== definitionExpr) {
+ buildingLayer.definitionExpression = definitionExpr;
+ }
+ });
+ });
+ });
+ }
+
+ return () => {
+ if (watchHandle) {
+ watchHandle.remove();
+ }
+ clearTimeout(loadingTimeout);
+ };
+
+ }, [definitionExpr])
+
+ useEffect(() => {
+ isVisibleRef.current = isVisible;
+ }, [isVisible]);
+
+ const fetchSystemPropertyUsage = async (gisId) => {
+ let response = await fetchBillNoData_New(gisId);
+ if (response && response.length > 0) {
+ return response;
+ } else {
+ return null
+ }
+ };
+ async function queryRelatedRecords(table, whereClause, id) {
+ let query = table.createQuery()
+ query.where = whereClause
+ let data = await table.queryFeatures(query)
+ if (data.features.length > 0) {
+ return data.features.map(feature => feature.attributes);
+ }
+ }
+ async function fetchTradeDetails(gccGisId) {
+ const relatedBills = await queryRelatedRecords(Establishment_TABLE, `new_formatted_gis_id = '${gccGisId}'`, 1);
+ let allTradeDetails = []
+ if (relatedBills) {
+ relatedBills.forEach(bill => {
+ allTradeDetails.push(`Establishment Name: ${bill.establishment_name}, Trade Nature: ${bill.trade_nature_updated}`);
+ });
+ }
+ return allTradeDetails.length > 0 ? allTradeDetails.join(' ') : "N/A.";
+ }
+
+ async function fetchBillNumbers(gccGisId) {
+ const relatedBills = await queryRelatedRecords(Property_TABLE, `new_formatted_gis_id = '${gccGisId}'`, 4);
+ let allBills = [];
+ if (relatedBills) {
+ relatedBills.forEach(bill => {
+ allBills.push(bill.bill_no);
+ });
+ }
+ return allBills.length > 0 ? allBills.join(' ') : "N/A";
+ }
+
+ async function fetchProfessionCount(gccGisId) {
+ const relatedBills = await queryRelatedRecords(Professional_TABLE, `new_formatted_gis_id = '${gccGisId}'`, 1);
+ let count
+ if (relatedBills) {
+ count = relatedBills.length;
+ }
+ return count === undefined || count === 0 ? "N/A" : count;
+ }
+
+ async function fetchMaxFloor(gccGisId) {
+ const floors = await queryRelatedRecords(Floor_TABLE, `new_formatted_gis_id = '${gccGisId}'`, 2);
+ let maxFloor
+ if (floors) {
+ maxFloor = Math.max(...floors.map(floor => floor.gcc_floor_id), 0);
+ }
+ return maxFloor && maxFloor > 0 ? `${maxFloor}th Floor` : "N/A";
+ }
+
+ async function fetchTradeCount(gccGisId) {
+ const relatedBills = await queryRelatedRecords(Establishment_TABLE, `new_formatted_gis_id = '${gccGisId}'`, 1);
+ let count
+ if (relatedBills) {
+ count = relatedBills.length;
+ }
+ return count === undefined || count === 0 ? "N/A" : count;
+ }
+
+ async function fetchAssessmentCount(gccGisId) {
+ const relatedBills = await queryRelatedRecords(Property_TABLE, `new_formatted_gis_id = '${gccGisId}'`, 4);
+ return relatedBills != undefined && relatedBills.length > 0 ? relatedBills.length : "N/A";
+ }
+
+ const fetchBuildingLyr = () => {
+ if (jimuMapView) {
+ const LandCoverLyr = jimuMapView.view.map.layers.find(layer => layer.title === 'LandCover')
+ if (!LandCoverLyr) {
+ console.log('LandCover Layer not found in the web map')
+ } else {
+ const buildingLyr_Final = LandCoverLyr.layers.find(layer => layer.title === 'Buildings')
+
+ if (buildingLyr_Final) {
+ storeBuildingLayer(buildingLyr_Final)
+ } else {
+ storeBuildingLayer(null)
+ console.log('Buildings sublayer not found')
+ }
+ }
+ }
+ }
+
+ const handleMapClick = (jmv) => {
+ try {
+ if (jmv) {
+ jmv.view.on('click', async (evt) => {
+ const response = await jmv.view.hitTest(evt);
+ if (response.results.length > 0) {
+ if (buildingLayer) {
+ const buildingsLayerResult = response.results.find(result => result.layer && result.layer.title === 'Buildings');
+ if (buildingsLayerResult) {
+ let definition = buildingLayer.definitionExpression
+ setBuildDefinition(definition)
+
+ const objectId = buildingsLayerResult.graphic.attributes.objectid;
+ const query = buildingLayer.createQuery();
+ query.objectIds = [objectId];
+ query.outFields = ['*'];
+ const queryResponse = await buildingLayer.queryFeatures(query);
+
+ if (queryResponse.features.length > 0) {
+ const fullFeature = queryResponse.features[0];
+ const gcc_gis_id = fullFeature.attributes.gcc_gis_id;
+ if (gcc_gis_id) {
+ storeclickedFeatureDet([fullFeature.attributes, evt.mapPoint])
+ }
+ }
+ }
+ }
+ }
+ else
+ return
+ });
+ }
+ } catch (err) {
+ console.error('HitTest error:', err);
+ }
+ };
+
+ useEffect(() => {
+ let cleanup;
+ if (buildingLayer) {
+ cleanup = handleMapClick(jimuMapView);
+ }
+ }, [buildingLayer])
+
+ useEffect(() => {
+ }, [buildingLayer])
+
+ useEffect(() => {
+ if (Array.isArray(clickedFeatDet) && clickedFeatDet.length > 0) {
+
+ const popupTemplate = {
+
+ title: 'Building Info',
+
+ content: [
+
+ new CustomContent({
+
+ outFields: ['*'],
+
+ creator: async () => {
+
+ const container =
+ document.createElement('div');
+
+ // ==========================================
+ // EXISTING BUILDING CONTENT
+ // ==========================================
+
+ const existingContent =
+ await buildCustomContent(clickedFeatDet);
+
+ const existingDiv =
+ document.createElement('div');
+
+ existingDiv.innerHTML =
+ existingContent;
+
+ container.appendChild(existingDiv);
+
+ // ==========================================
+ // BILL SEARCH CONTROL
+ // ==========================================
+
+ const zoneNumber = clickedFeatDet[0].zone_number_mis || clickedFeatDet[0].zone_number;
+ const wardNumber = clickedFeatDet[0].ward_number_mis || clickedFeatDet[0].ward_number;
+ const area = clickedFeatDet[0].area_mis || clickedFeatDet[0].area_name;
+ const locality = clickedFeatDet[0].locality_mis || clickedFeatDet[0].locality;
+ const roadName = clickedFeatDet[0].street_mis || clickedFeatDet[0].road_name;
+ const gisId = clickedFeatDet[0].gcc_gis_id;
+
+ const searchControl =
+ createBillSearchControl(zoneNumber, wardNumber, area, locality, roadName, gisId);
+
+ container.appendChild(searchControl);
+
+ return container;
+ }
+
+ })
+
+ ],
+ actions: []
+ };
+
+ buildingLayer.popupTemplate =
+ popupTemplate;
+
+ }
+
+ }, [clickedFeatDet]);
+
+ async function getPopupContent(buildingLayerSource) {
+ const popupTemplate = buildingLayerSource.graphic.layer.popupTemplate;
+ storeBuildingPopup(popupTemplate)
+ if (popupTemplate) {
+ const content = await popupTemplate.content
+ return content
+ }
+ }
+ function generateDirectionUrl(feature) {
+ const { x, y, spatialReference } = feature;
+
+ let latitude, longitude;
+ if (spatialReference.wkid === 4326) {
+ latitude = y;
+ longitude = x;
+ } else if (spatialReference.wkid === 102100) {
+ const auxSphereToLatLon = (x, y) => {
+ const rMajor = 6378137;
+ const shift = Math.PI * rMajor;
+ longitude = (x / shift) * 180.0;
+ latitude = (y / shift) * 180.0;
+ latitude = (180 / Math.PI) * (2 * Math.atan(Math.exp(latitude * Math.PI / 180.0)) - Math.PI / 2.0);
+ };
+ auxSphereToLatLon(x, y);
+ }
+ return "https://www.google.com/maps?q=" + (latitude) + "," + (longitude);
+ }
+
+ const test = async (params: { Zone: string; ward: string; area: string; loc: string; street: string; GISID: string }) => {
+ try {
+ const newAPIEndPoint = "https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getPropertyByGis";
+ const queryString = new URLSearchParams(params).toString();
+ const requestUrl = `${newAPIEndPoint}&${queryString}`;
+
+ const response = await fetch(requestUrl, {
+ method: "GET",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+
+ if (!response.ok) {
+ throw new Error(`HTTP error! Status: ${response.status}`);
+ }
+
+ const data = await response.json();
+
+ if (data && data.ListResult && Object.keys(data.ListResult).length > 0) {
+ return data.ListResult;
+ }
+
+ return [];
+ } catch (err) {
+ console.error("Error fetching data:", err);
+ return [];
+ }
+ };
+
+ const fetchGISIDFetch_new_erp = async (params: {
+ Zone: string;
+ ward: string;
+ area?: string | null;
+ loc?: string | null;
+ street?: string | null;
+ GISID?: string | null;
+ }): Promise => {
+ try {
+ const baseUrl =
+ "https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getPropertyByGis";
+
+ const { GISID, ...queryParams } = params;
+
+ const filteredParams = Object.fromEntries(
+ Object.entries(queryParams).filter(
+ ([key, value]) =>
+ (key === "Zone" || key === "ward") || (value !== null && value !== undefined && value !== "")
+ )
+ );
+
+ const queryString = new URLSearchParams(filteredParams as Record).toString();
+ const requestUrl = `${baseUrl}&${queryString}`;
+
+ console.log("Fetching data from:", requestUrl);
+
+ const controller = new AbortController();
+ const timeout = setTimeout(() => controller.abort(), 90000);
+
+ const response = await fetch(requestUrl, {
+ method: "GET",
+ signal: controller.signal,
+ });
+
+ clearTimeout(timeout);
+
+ if (!response.ok) {
+ throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
+ }
+
+ const result = await response.json();
+
+ if (!result?.ListResult || !Array.isArray(result.ListResult)) {
+ return { error: "Invalid API response structure" };
+ }
+
+ if (result.ListResult.length === 0) {
+ return { error: "No records found" };
+ }
+
+ const matchingRecords = result.ListResult.filter(
+ (item: any) => String(item.GISID).trim() === String(GISID).trim()
+ );
+
+ return matchingRecords.length > 0 ? matchingRecords : { error: "GISID not found in results" };
+ } catch (error: any) {
+ if (error.name === "AbortError") {
+ return { error: "Request timed out" };
+ }
+ console.error("Error fetching data:", error.message);
+ return { error: error.message };
+ }
+ };
+
+ async function fetchTradeLicenseDetails(billNumber, clickedFeatDet) {
+ try {
+ const rawZone = (clickedFeatDet?.[0]?.zone_number_mis || clickedFeatDet?.[0]?.zone_number || '').toString();
+ const rawWard = (clickedFeatDet?.[0]?.ward_number_mis || clickedFeatDet?.[0]?.ward_number || '').toString();
+
+ const zone = rawZone.startsWith('N') ? rawZone : 'N' + rawZone.padStart(2, '0');
+ const ward = rawWard.startsWith('N') ? rawWard : 'N' + rawWard.padStart(3, '0');
+
+ const profResp = await fetch(
+ `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getProfTradedata&propid=${encodeURIComponent(billNumber)}`
+ );
+ const profData = await profResp.json();
+
+ if (!profData?.ListResult || !Array.isArray(profData.ListResult)) {
+ return `No professional/trade data available for bill ${billNumber}
`;
+ }
+
+ const tradeItems = profData.ListResult.filter(it => it.SOURCE === "TRADELICENSE");
+ const profTaxItems = profData.ListResult.filter(it => it.SOURCE === "PROFTAX");
+ console.log("profItems", profTaxItems)
+
+ let collectedPTNAN = new Set();
+ if (tradeItems.length === 0) {
+ return `No trade-license items found for bill ${billNumber}
`;
+ }
+
+ let html = `Trade License lookups (zone: ${zone}, ward: ${ward}):
`;
+
+ for (const item of tradeItems) {
+ const licenseNumber =
+ item.LICENSE_NUMBER ||
+ item.licenseNumber ||
+ item.LICENSE_NO ||
+ item.LICENCE_NUMBER ||
+ item.licence_no ||
+ item.ID;
+
+ const url = `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getTradeLicensedata&zone=${encodeURIComponent(zone)}&ward=${encodeURIComponent(ward)}&licenseNumber=${encodeURIComponent(licenseNumber)}`;
+
+ try {
+ const resp = await fetch(url, { method: 'GET' });
+ const json = await resp.json();
+
+ html += ``;
+ html += `
Trade licenseNumber: ${licenseNumber}
`;
+
+ if (json?.ListResult && Array.isArray(json.ListResult) && json.ListResult.length > 0) {
+ for (const lic of json.ListResult) {
+ html += `
+
+
+ Ward / Zone
+ ${lic.WARD} / ${lic.ZONE}
+
+
+ Trade License No
+ ${lic.LICENSE_NUMBER}
+
+
+ PT Number
+ ${lic.PTNAN}
+
+
+ Establishment
+ ${lic.ESTABLISHMENT_NAME}
+
+
+ Trade Nature
+ ${lic.TRADE_NAME}
+
+
+ Applicant
+ ${lic.APPLICANT_NAME} ${lic.FATHER_NAME ? `(S/o: ${lic.FATHER_NAME})` : ''}
+
+
+ Address
+ ${lic.APPLICANT_ADDRESS}
+
+
+ Expiry Date
+ ${lic.LIC_EXPIRY_DATE?.split(" ")[0]}
+
+
+ Total Fee
+ ₹${lic.TOTAL_FEE}
+
+
+ `;
+
+ if (lic.PTNAN) {
+ collectedPTNAN.add(lic.PTNAN);
+ try {
+ const ptUrl = `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getProfessionalTaxData&zone=${encodeURIComponent(lic.ZONE)}&ward=${encodeURIComponent(lic.WARD)}&ptnan=${encodeURIComponent(lic.PTNAN)}`;
+ const ptResp = await fetch(ptUrl, { method: "GET" });
+ const ptJson = await ptResp.json();
+
+ if (ptJson?.ListResult && Array.isArray(ptJson.ListResult) && ptJson.ListResult.length > 0) {
+ for (const pt of ptJson.ListResult) {
+ html += `
+
Professional Tax Details
+
+
+ PTNAN
+ ${pt.PTNAN}
+
+
+ Name
+ ${pt.NAME}
+
+
+ Area
+ ${pt.AREA}
+
+
+ Address
+ ${pt.ADDRESS}
+
+
+ Locality
+ ${pt.LOCALITY || "N/A"}
+
+
+ Street
+ ${pt.STREET}
+
+
+ Ward / Zone
+ ${pt.WARD} / ${pt.ZONE}
+
+
+ Demand
+ ₹${pt.DEMAND}
+
+
+ Property Tax No
+ ${pt.PROPERTYTAX_NUMBER}
+
+
+ Contact
+ ${pt.CONTACT_NUMBER || "N/A"}
+
+
+ Created Date
+ ${pt.CREATEDDATE}
+
+
+ `;
+ }
+ } else {
+ html += `
No Professional Tax data found for PTNAN ${lic.PTNAN}
`;
+ }
+ } catch (err) {
+ console.error("Error fetching professional tax data", err);
+ html += `
Error fetching Professional Tax for PTNAN ${lic.PTNAN}
`;
+ }
+ }
+ }
+ } else if (json?.ResultStatus === false && json?.Message) {
+ html += `
API returned: ${json.Message}
`;
+ } else {
+ html += `
No records found for licenseNumber ${licenseNumber}
`;
+ }
+
+ html += `
`;
+ } catch (err) {
+ console.error('Error fetching trade license data', err);
+ html += `Error fetching trade license for ${licenseNumber}
`;
+ }
+ }
+ let remainingPT = profTaxItems.filter(pt => !collectedPTNAN.has(pt.ID));
+
+ if (remainingPT.length > 0) {
+ html += `
+
+ Other Professional Tax
+
+ `;
+
+ for (const ptItem of remainingPT) {
+ try {
+ const ptId = ptItem.ID;
+
+ const ptUrl = `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getProfessionalTaxData&zone=${encodeURIComponent(zone)}&ward=${encodeURIComponent(ward)}&ptnan=${encodeURIComponent(ptId)}`;
+
+ const ptResp = await fetch(ptUrl, { method: "GET" });
+ const ptJson = await ptResp.json();
+
+ if (ptJson?.ListResult && ptJson.ListResult.length > 0) {
+ const pt = ptJson.ListResult[0];
+
+ html += `
+
+
+ PTNAN
+ ${pt.PTNAN}
+
+
+ Name
+ ${pt.NAME}
+
+
+ Area
+ ${pt.AREA}
+
+
+ Address
+ ${pt.ADDRESS}
+
+
+ Street
+ ${pt.STREET}
+
+
+ Ward / Zone
+ ${pt.WARD} / ${pt.ZONE}
+
+
+ Demand
+ ₹${pt.DEMAND}
+
+
+ Property Tax No
+ ${pt.PROPERTYTAX_NUMBER}
+
+
+ `;
+ } else {
+ html += `No PT data found for ${ptId}
`;
+ }
+ } catch (err) {
+ console.error("Error fetching remaining PT", err);
+ html += `Error fetching PT for ${ptItem.ID}
`;
+ }
+ }
+ }
+
+ return html;
+ } catch (err) {
+ console.error('fetchTradeLicenseDetails error', err);
+ return `Error while fetching trade license details
`;
+ }
+ }
+
+ async function fetchProfessionalTaxDetails(ptnan, zone, ward) {
+ try {
+ const url = `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getProfessionalTaxData&zone=${encodeURIComponent(zone)}&ward=${encodeURIComponent(ward)}&ptnan=${encodeURIComponent(ptnan)}`;
+
+ const resp = await fetch(url, { method: "GET" });
+ const json = await resp.json();
+
+ let html = `Professional Tax Details (zone: ${zone}, ward: ${ward}, PTNAN: ${ptnan}):
`;
+
+ if (json?.ListResult && Array.isArray(json.ListResult) && json.ListResult.length > 0) {
+ for (const item of json.ListResult) {
+ html += `
+
+
+ PTNAN
+ ${item.PTNAN}
+
+
+ Name
+ ${item.NAME}
+
+
+ Area
+ ${item.AREA}
+
+
+ Address
+ ${item.ADDRESS}
+
+
+ Locality
+ ${item.LOCALITY || "N/A"}
+
+
+ Street
+ ${item.STREET}
+
+
+ Ward / Zone
+ ${item.WARD} / ${item.ZONE}
+
+
+ Demand
+ ₹${item.DEMAND}
+
+
+ Property Tax No
+ ${item.PROPERTYTAX_NUMBER}
+
+
+ Contact
+ ${item.CONTACT_NUMBER || "N/A"}
+
+
+ Created Date
+ ${item.CREATEDDATE}
+
+
+ `;
+ }
+ } else if (json?.Message) {
+ html += `API returned: ${json.Message}
`;
+ } else {
+ html += `No Professional Tax records found for PTNAN ${ptnan}
`;
+ }
+
+ return html;
+ } catch (err) {
+ console.error("Error fetching professional tax data", err);
+ return `Error fetching Professional Tax details for PTNAN ${ptnan}
`;
+ }
+ }
+
+ async function getTradeAssessmentHTML(billNumber) {
+ let html = '';
+
+ try {
+ const profTradeResponse = await fetch(
+ `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice?serviceId=common_api&jsonResp=Yes&subService=getProfTradedata&propid=${billNumber}`
+ );
+ const profData = await profTradeResponse.json();
+
+ if (profData?.ResultStatus === true && Array.isArray(profData.ListResult)) {
+ const tradeItems = profData.ListResult.filter(item => item.SOURCE === "TRADELICENSE");
+ const profItems = profData.ListResult.filter(item => item.SOURCE === "PROFTAX");
+ } else {
+ }
+ } catch (err) {
+ html += `Error fetching trade data
`;
+ }
+
+ return html;
+ }
+
+ // ======================================================
+ // BILL NUMBER FORMAT VALIDATION
+ // Expected: 09-121-01249-000
+ // ======================================================
+ function validateBillNumber(value: string): boolean {
+ return /^\d{2}-\d{3}-\d{5}-\d{3}$/.test(value.trim());
+ }
+
+ // ======================================================
+ // CREATE BILL SEARCH CONTROL
+ // ======================================================
+ function createBillSearchControl(
+ zoneNumber: string,
+ wardNumber: string,
+ area: string,
+ locality: string,
+ roadName: string,
+ gisId: string
+ ): HTMLElement {
+
+ const container = document.createElement('div');
+
+ container.style.cssText = `
+ margin: 12px 0;
+ padding: 10px;
+ border: 1px solid #d9d9d9;
+ border-radius: 6px;
+ background: #ffffff;
+ `;
+
+ const title = document.createElement('div');
+
+ title.textContent = 'Search by Bill Number';
+
+ title.style.cssText = `
+ font-weight: bold;
+ margin-bottom: 8px;
+ color: #444;
+ font-size: 14px;
+ `;
+
+ const form = document.createElement('form');
+
+ form.style.cssText = `
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ `;
+
+ const input = document.createElement('input');
+
+ input.type = 'text';
+ input.placeholder = '09-121-01249-000';
+ input.maxLength = 16;
+
+ input.style.cssText = `
+ flex: 1;
+ height: 34px;
+ padding: 6px 10px;
+ border: 1px solid #bdbdbd;
+ border-radius: 4px;
+ font-size: 14px;
+ box-sizing: border-box;
+ outline: none;
+ `;
+
+ const button = document.createElement('button');
+
+ button.type = 'submit';
+ button.textContent = 'Search';
+
+ button.style.cssText = `
+ height: 34px;
+ padding: 0 16px;
+ border: none;
+ border-radius: 4px;
+ background: #1976d2;
+ color: white;
+ font-size: 14px;
+ cursor: pointer;
+ `;
+
+ const error = document.createElement('div');
+
+ error.style.cssText = `
+ display: none;
+ margin-top: 6px;
+ color: #d32f2f;
+ font-size: 12px;
+ `;
+
+ // NEW: results box, where searchBillNumber() renders its output
+ const resultsBox = document.createElement('div');
+ resultsBox.className = 'bill-search-results';
+
+ // =================================================
+ // VALIDATE WHILE TYPING
+ // =================================================
+ // =================================================
+// VALIDATE WHILE TYPING
+// =================================================
+input.addEventListener('input', () => {
+
+ const value = input.value.trim();
+
+ // =================================================
+ // IMPORTANT:
+ // Clear previously searched bill immediately
+ // whenever the user changes the search text.
+ // This includes Backspace.
+ // =================================================
+ resultsBox.innerHTML = '';
+
+ // Reset any previous result/error state
+ input.style.borderColor = '#bdbdbd';
+
+ error.style.display = 'none';
+ error.textContent = '';
+
+ // -----------------------------------------------
+ // EMPTY SEARCH BOX
+ // -----------------------------------------------
+ if (value === '') {
+ return;
+ }
+
+ // -----------------------------------------------
+ // VALID BILL NUMBER
+ // -----------------------------------------------
+ if (validateBillNumber(value)) {
+
+ input.style.borderColor = '#4caf50';
+
+ error.style.display = 'none';
+ error.textContent = '';
+
+ } else {
+
+ // ---------------------------------------------
+ // INVALID / PARTIAL BILL NUMBER
+ // ---------------------------------------------
+ input.style.borderColor = '#d32f2f';
+
+ error.style.display = 'block';
+
+ error.textContent =
+ 'Invalid format. Expected: 09-121-01249-000';
+ }
+
+});
+
+ // =================================================
+ // SEARCH BUTTON
+ // =================================================
+ form.addEventListener('submit', (event) => {
+
+ event.preventDefault();
+
+ const billNumber = input.value.trim();
+
+ if (!billNumber) {
+ input.style.borderColor = '#d32f2f';
+ error.style.display = 'block';
+ error.textContent = 'Please enter a Bill Number';
+ return;
+ }
+
+ if (!validateBillNumber(billNumber)) {
+ input.style.borderColor = '#d32f2f';
+ error.style.display = 'block';
+ error.textContent =
+ 'Invalid format. Expected: 09-121-01249-000';
+
+ console.error('Invalid Bill Number:', billNumber);
+ return;
+ }
+
+ input.style.borderColor = '#4caf50';
+ error.style.display = 'none';
+
+ console.log('SEARCH BUTTON CLICKED');
+ console.log('Bill Number:', billNumber);
+
+ // ----------------------------------
+ // REAL SEARCH FUNCTION
+ // ----------------------------------
+ searchBillNumber(billNumber, zoneNumber, wardNumber, area, locality, roadName, gisId, resultsBox);
+
+ });
+
+ form.appendChild(input);
+ form.appendChild(button);
+
+ container.appendChild(title);
+ container.appendChild(form);
+ container.appendChild(error);
+ container.appendChild(resultsBox); // NEW
+
+ return container;
+ }
+
+ // ======================================================
+// CONFIRMATION POPUP: reason for selecting this bill
+// ======================================================
+function openReasonConfirmationPopup(
+ billNumber: string,
+ gisId: string,
+ sourceCheckbox: HTMLInputElement
+): void {
+
+ const overlay = document.createElement('div');
+ overlay.style.cssText = `
+ position: fixed;
+ top: 0; left: 0; right: 0; bottom: 0;
+ background: rgba(0,0,0,0.4);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 9999;
+ `;
+
+ const modal = document.createElement('div');
+ modal.style.cssText = `
+ background: #fff;
+ border-radius: 8px;
+ padding: 18px 20px;
+ width: 300px;
+ box-shadow: 0 4px 16px rgba(0,0,0,0.25);
+ font-size: 14px;
+ `;
+
+ const title = document.createElement('div');
+ title.textContent = `Reason for Bill ${billNumber}`;
+ title.style.cssText = `
+ font-weight: bold;
+ margin-bottom: 12px;
+ color: #333;
+ `;
+
+ const optionsWrap = document.createElement('div');
+ optionsWrap.style.cssText = `
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin-bottom: 16px;
+ `;
+
+ const reasonName = `reason_${billNumber}_${Date.now()}`;
+ let selectedReason: string | null = null;
+
+ const makeRadioOption = (label: string, value: string) => {
+ const row = document.createElement('label');
+ row.style.cssText = `
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ cursor: pointer;
+ `;
+
+ const radio = document.createElement('input');
+ radio.type = 'radio';
+ radio.name = reasonName;
+ radio.value = value;
+
+ radio.addEventListener('change', () => {
+ if (radio.checked) selectedReason = value;
+ });
+
+ const text = document.createElement('span');
+ text.textContent = label;
+
+ row.appendChild(radio);
+ row.appendChild(text);
+ return row;
+ };
+
+ optionsWrap.appendChild(makeRadioOption('Usage Variance', 'USAGE_VARIANCE'));
+ optionsWrap.appendChild(makeRadioOption('Area Extent', 'AREA_EXTENT'));
+
+ const errorMsg = document.createElement('div');
+ errorMsg.style.cssText = `
+ display: none;
+ color: #d32f2f;
+ font-size: 12px;
+ margin-bottom: 10px;
+ `;
+ errorMsg.textContent = 'Please select a reason.';
+
+ const buttonRow = document.createElement('div');
+ buttonRow.style.cssText = `
+ display: flex;
+ justify-content: flex-end;
+ gap: 8px;
+ `;
+
+ const cancelBtn = document.createElement('button');
+ cancelBtn.textContent = 'Cancel';
+ cancelBtn.style.cssText = `
+ padding: 6px 14px;
+ border: 1px solid #bdbdbd;
+ border-radius: 4px;
+ background: #fff;
+ cursor: pointer;
+ `;
+ cancelBtn.addEventListener('click', () => {
+ sourceCheckbox.checked = false;
+ document.body.removeChild(overlay);
+ });
+
+ const confirmBtn = document.createElement('button');
+ confirmBtn.textContent = 'Confirm';
+ confirmBtn.style.cssText = `
+ padding: 6px 14px;
+ border: none;
+ border-radius: 4px;
+ background: #1976d2;
+ color: #fff;
+ cursor: pointer;
+ `;
+ confirmBtn.addEventListener('click', async () => {
+ if (!selectedReason) {
+ errorMsg.style.display = 'block';
+ return;
+ }
+
+ // TODO: persist selectedReason for (gisId, billNumber) —
+ // target FeatureLayer/table + field mapping to be decided next.
+ console.log('Confirmed reason:', { gisId, billNumber, selectedReason });
+
+ document.body.removeChild(overlay);
+ });
+
+ buttonRow.appendChild(cancelBtn);
+ buttonRow.appendChild(confirmBtn);
+
+ modal.appendChild(title);
+ modal.appendChild(optionsWrap);
+ modal.appendChild(errorMsg);
+ modal.appendChild(buttonRow);
+ overlay.appendChild(modal);
+
+ document.body.appendChild(overlay);
+}
+
+ // ======================================================
+ // SEARCH PROPERTY BY BILL NUMBER
+ // ======================================================
+ async function searchBillNumber(
+ billNumber: string,
+ zoneNumber: string | number,
+ wardNumber: string | number,
+ area: string,
+ locality: string,
+ roadName: string,
+ gisId: string,
+ resultsBox: HTMLElement
+ ): Promise {
+
+ console.log("Searching Bill Number:", billNumber);
+ console.log("Zone:", zoneNumber, "Ward:", wardNumber, "GIS Id:", gisId);
+
+ try {
+
+ const formattedZone =
+ String(zoneNumber).startsWith("N") ? String(zoneNumber) : `N${zoneNumber}`;
+
+ const formattedWard =
+ String(wardNumber).startsWith("N") ? String(wardNumber) : `N${wardNumber}`;
+
+ const url =
+ `https://erp.chennaicorporation.gov.in/pgr/external/mobileservice` +
+ `?serviceId=common_api` +
+ `&jsonResp=Yes` +
+ `&subService=getPropertyByGis` +
+ `&Zone=${encodeURIComponent(formattedZone)}` +
+ `&ward=${encodeURIComponent(formattedWard)}`;
+
+ console.log("ERP Bill Search URL:", url);
+
+ const response = await fetch(url);
+
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
+ }
+
+ const data = await response.json();
+
+ console.log("ERP API Response:", data);
+
+ if (data?.error) {
+ resultsBox.innerHTML = `
+ ${data.error}
+ `;
+ return;
+ }
+
+ // ADDED — catches an API-level rejection distinctly from "no matches"
+ if (data?.ResultStatus === "false" || data?.ResultStatus === false) {
+ resultsBox.innerHTML = `
+
+ ERP API request failed (${data?.Message || "Unknown error"}). This usually means a token/access issue, not a missing record.
+
+ `;
+ return;
+ }
+
+ const records = Array.isArray(data?.ListResult) ? data.ListResult : [];
+
+ if (records.length === 0) {
+ resultsBox.innerHTML = `
+ No property records found.
+ `;
+ return;
+ }
+
+ const searchedBill = billNumber.trim().toUpperCase();
+
+ // Match on BOTH bill number AND GIS ID, so a bill number that
+ // belongs to a different property is correctly rejected.
+ const matchedRecord = records.find(
+ (record: any) =>
+ String(record.BILL_NUMBER || "").trim().toUpperCase() === searchedBill &&
+ String(record.GISID || "").trim().toUpperCase() === String(gisId || "").trim().toUpperCase()
+ );
+
+ if (!matchedRecord) {
+ resultsBox.innerHTML = `
+
+ Bill Number ${billNumber} does not belong to this property
+ (GIS Id: ${gisId}).
+
+ `;
+ return;
+ }
+
+ const record = matchedRecord;
+ const resultBillNumber = record.BILL_NUMBER || "N/A";
+
+ resultsBox.innerHTML = `
+
+
+ Property details fetched from ERP API.
+
+
+
+
+
+ Bill Number: ${resultBillNumber}
+
+
+
+
+ `;
+
+ const billCheckbox = resultsBox.querySelector('.bill-select-checkbox') as HTMLInputElement | null;
+ if (billCheckbox) {
+ billCheckbox.addEventListener('click', (e) => e.stopPropagation());
+ billCheckbox.addEventListener('change', () => {
+ if (billCheckbox.checked) {
+ openReasonConfirmationPopup(resultBillNumber, gisId, billCheckbox);
+ }
+ });
+ }
+
+ } catch (error) {
+
+ console.error("Bill Number search failed:", error);
+
+ resultsBox.innerHTML = `
+
Failed to fetch property details from ERP API.
+ `;
+ }
+ }
+
+ async function buildCustomContent(clickedFeatDet) {
+ console.log('CONTENT FUNCTION CALLED for GIS ID:', clickedFeatDet?.[0]?.gcc_gis_id);
+ console.log(clickedFeatDet)
+
+ const directionUrl = await generateDirectionUrl(clickedFeatDet[1]);
+
+ const zoneNumber = clickedFeatDet[0].zone_number;
+ const formattedZone = "N" + zoneNumber;
+
+ const wardNumber = clickedFeatDet[0].ward_number;
+ const formattedward = "N" + wardNumber;
+ const new_api = await fetchGISIDFetch_new_erp({
+ Zone: clickedFeatDet[0].zone_number_mis || formattedZone,
+ ward: clickedFeatDet[0].ward_number_mis || formattedward,
+ area: clickedFeatDet[0].area_mis,
+ loc: clickedFeatDet[0].locality_mis,
+ street: clickedFeatDet[0].street_mis,
+ GISID: clickedFeatDet[0].gcc_gis_id
+ });
+ console.log(new_api);
+
+ const usage = await fetchSystemPropertyUsage(clickedFeatDet[0].gcc_gis_id);
+
+ let customContent = `
+
+
+ `;
+
+ return customContent;
+ }
+
+ const clearGraphic = () => {
+ jimuMapView?.view.graphics.removeAll();
+ setPopupContent(null);
+ };
+ const handleMapView = (view) => {
+ setJimuMapView(view)
+ setMapView(view)
+ }
+ return (
+
+ {props.useMapWidgetIds && props.useMapWidgetIds.length === 1 && (
+
+ )}
+
+ );
+};
+
+export default Popup;
+
diff --git a/custom-popup/src/setting/setting.tsx b/custom-popup/src/setting/setting.tsx
new file mode 100644
index 0000000..3e1d4c8
--- /dev/null
+++ b/custom-popup/src/setting/setting.tsx
@@ -0,0 +1,18 @@
+import { React } from 'jimu-core'
+import { type AllWidgetSettingProps } from 'jimu-for-builder'
+import { MapWidgetSelector } from 'jimu-ui/advanced/setting-components'
+
+const Setting = (props: AllWidgetSettingProps
) => {
+ const onMapWidgetSelected = (useMapWidgetIds: string[]) => {
+ props.onSettingChange({
+ id: props.id,
+ useMapWidgetIds: useMapWidgetIds
+ })
+ }
+ return
+
+
+}
+
+export default Setting
+