diff --git a/package.json b/package.json index 76b8dc14..bdbba20d 100755 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "chartjs-plugin-annotation": "^3.1.0", "date-fns": "^4.1.0", "dotenv": "^16.4.7", + "fastest-levenshtein": "^1.0.16", "file-saver": "^2.0.5", "html2canvas": "^1.4.1", "jspdf": "^4.2.0", diff --git a/src/pages/PlansPage.jsx b/src/pages/PlansPage.jsx index c64e8a2e..3aea8cfa 100644 --- a/src/pages/PlansPage.jsx +++ b/src/pages/PlansPage.jsx @@ -17,8 +17,10 @@ import LandingNavbar from "../components/landing_navbar.jsx"; import FilterListIcon from "@mui/icons-material/FilterList"; import SelectReact from "react-select"; import StewardDetailPage from "../components/steward_detailPage.jsx"; -import { useSearchParams } from "react-router-dom"; import { useMap, useMapsLibrary } from "@vis.gl/react-google-maps"; +import { distance } from "fastest-levenshtein"; +import { useSearchParams } from "react-router-dom"; + const P = { base: "oklch(49.6% 0.265 301.924)", @@ -470,6 +472,28 @@ const PlansPage = () => { ); }; + const findClosestDistrict = (districtName, nameToId) => { + const search = districtName.toLowerCase().trim(); + + let bestKey = null; + let bestDistance = Infinity; + + Object.keys(nameToId).forEach((key) => { + const d = distance(search, key.toLowerCase()); + + if (d < bestDistance) { + bestDistance = d; + bestKey = key; + } + }); + + console.log( + `Closest match: ${districtName} → ${bestKey} (distance: ${bestDistance})` + ); + + return bestDistance <= 2 ? nameToId[bestKey] : null; +}; + const handleVillageSuggestionSelect = (placeId, description) => { setVillageSearchText(description); setVillageSuggestions([]); @@ -487,8 +511,14 @@ const handleVillageSuggestionSelect = (placeId, description) => { const components = place?.address_components ?? []; const stateName = components.find((c) => c.types.includes("administrative_area_level_1"))?.long_name; - const districtName = components.find((c) => c.types.includes("administrative_area_level_2"))?.long_name; + const districtName = components.find((c) => c.types.includes("administrative_area_level_3"))?.long_name || + components.find((c) => c.types.includes("administrative_area_level_2"))?.long_name; + const tehsilName = components.find((c) => c.types.includes("locality"))?.long_name || place?.name; const searchTerm = (place?.name || description.split(",")[0] || "").toLowerCase(); + console.log(components); + console.log("District Name:", districtName); + // console.log("District Lookup:", nameToId[districtName?.toLowerCase()]); + console.log("Tehsil Name:", tehsilName); if (bubbleLayerRef.current) { mapRef.current.removeLayer(bubbleLayerRef.current); @@ -507,7 +537,10 @@ const handleVillageSuggestionSelect = (placeId, description) => { const nameToId = Object.fromEntries( Object.entries(districtLookupRef.current).map(([id, name]) => [name.toLowerCase(), Number(id)]) ); - const districtId = nameToId[districtName.toLowerCase()]; + let districtId = nameToId[districtName.toLowerCase()]; + if (!districtId) { + districtId = findClosestDistrict(districtName, nameToId); + } if (districtId) { currentDistrictRef.current = { district_id: districtId, district_name: districtName }; const [plans, districtStats, dprData, trackingData] = await Promise.all([ @@ -516,6 +549,8 @@ const handleVillageSuggestionSelect = (placeId, description) => { fetchDprReportStatus({ organizationId: orgRef.current?.value ?? null, districtId }), fetchStatusTracking({ organizationId: orgRef.current?.value ?? null, districtId }), ]); + console.log("District plans count:", plans.length); + addPlanDots(plans); setMetaStats(districtStats); setDprStatus(dprData); @@ -1520,7 +1555,61 @@ const handleVillageSuggestionSelect = (placeId, description) => { styles={selectStyles} /> + + + + + {/* Village Search */} +
+
+ +
+ + handleVillageInputChange(e.target.value)} + placeholder="Search village name..." + className="w-full pl-9 pr-3 rounded-xl text-sm outline-none" + style={{ + minHeight: "42px", + border: "1px solid rgba(220, 200, 240, 0.8)", + boxShadow: "0 8px 24px rgba(0,0,0,0.10)", + backgroundColor: "rgba(255,255,255,0.97)", + backdropFilter: "blur(6px)", + color: P.text, + }} + /> + + {villageSuggestions.length > 0 && ( +
+ {villageSuggestions.map((s) => ( +
handleVillageSuggestionSelect(s.place_id, s.description)} + className="px-3 py-2 text-sm cursor-pointer hover:bg-gray-50" + style={{ color: P.text, borderBottom: `1px solid ${P.border}` }} + > + {s.description} +
+ ))} +
+ )}
+ {/* PLAN STATUS LEGEND */} {planDotsVisible && (