diff --git a/src/components/SelectionModeToggle.jsx b/src/components/SelectionModeToggle.jsx
deleted file mode 100644
index aed723e3..00000000
--- a/src/components/SelectionModeToggle.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-const SelectionModeToggle = ({
- selectionMode,
- setSelectionMode,
-}) => {
- return (
-
@@ -2071,6 +2110,7 @@ const KYLMapContainer = ({
currentLayer,
setSearchLatLong,
showConnectivity,
+ showPlans,
selectionMode,
setSelectionMode,
}) => {
@@ -2118,6 +2158,7 @@ const KYLMapContainer = ({
showVillages={showVillages && areVillageLayersAvailable}
currentLayer={currentLayer}
showConnectivity={showConnectivity}
+ showPlans={showPlans}
/>
{/* Search Bar */}
diff --git a/src/components/kyl_rightSidebar.jsx b/src/components/kyl_rightSidebar.jsx
index dfd5b4f7..5780c643 100644
--- a/src/components/kyl_rightSidebar.jsx
+++ b/src/components/kyl_rightSidebar.jsx
@@ -1,9 +1,16 @@
// src/components/kyl_rightSidebar.jsx
import KML from 'ol/format/KML';
import GeoJSON from 'ol/format/GeoJSON';
+import { Style, Stroke, Fill, Circle as CircleStyle } from "ol/style";
+import Feature from "ol/Feature";
+import Point from "ol/geom/Point";
+import VectorLayer from "ol/layer/Vector";
+import VectorSource from "ol/source/Vector";
+import { useNavigate } from "react-router-dom";
+import React from "react";
import { Style, Stroke, Fill } from 'ol/style';
-import React from "react";
+import React,{useEffect} from "react";
import SelectButton from "./buttons/select_button";
import filtersDetails from "../components/data/Filters.json";
import { ArrowLeft, Loader2, Table, FileText, FileSpreadsheet, X, ChevronRight, CheckCircle2,Layers3 } from 'lucide-react';
@@ -14,6 +21,7 @@ import autoTable from 'jspdf-autotable';
import XLSX from 'xlsx-js-style';
import toast from 'react-hot-toast';
+
const KYLRightSidebar = ({
state,
district,
@@ -58,14 +66,22 @@ const KYLRightSidebar = ({
setSelectionMode,
manualSelectedMWS,
onResetMWSSelection,
+ showPlans,
+ setShowPlans,
}) => {
const [loadingWB, setLoadingWB] = React.useState(false);
const [showSelectionPopup, setShowSelectionPopup] = React.useState(false);
const [isDownloading, setIsDownloading] = React.useState(false);
const [isExportingGeo, setIsExportingGeo] = React.useState(false);
const [geoExportFormat, setGeoExportFormat] = React.useState(null);
+ const [plans, setPlans] = React.useState([]);
+
+ const [plansLoading, setPlansLoading] = React.useState(false);
+ const [selectedPlanProfile, setSelectedPlanProfile] = React.useState(null);
+ const plansLayerRef = React.useRef(null);
const showBothPanels = selectedMWSProfile && selectedWaterbodyProfile;
+ const navigate = useNavigate();
const transformName = (name) => {
if (!name) return name;
@@ -238,6 +254,107 @@ const KYLRightSidebar = ({
}
};
+ const allSelectedMWSIds = React.useMemo(() => {
+ const ids = new Set();
+ (selectedMWS || []).forEach(id => ids.add(String(id)));
+ (manualSelectedMWS || []).forEach(id => ids.add(String(id)));
+ return Array.from(ids);
+}, [selectedMWS, manualSelectedMWS]);
+
+useEffect(() => {
+ if (
+ !manualSelectedMWS?.length ||
+ !dataJson?.length
+ )
+ return;
+
+ const manualSelectionDetails = manualSelectedMWS.map((mwsId) => {
+ const mwsRecord = dataJson.find(
+ (item) => String(item.mws_id) === String(mwsId)
+ );
+
+ if (!mwsRecord) {
+ return {
+ mwsId,
+ villages: [],
+ waterbodies: [],
+ };
+ }
+
+ // Villages
+ const villages = (mwsRecord.mws_intersect_villages || []).map((villageId) => {
+ const village = villageJson.find(
+ (v) => String(v.village_id) === String(villageId)
+ );
+
+ return {
+ villageId,
+ villageName:
+ village?.village_name ||
+ village?.vill_name ||
+ village?.name ||
+ "Unknown",
+ };
+ });
+
+ // Waterbodies
+ const waterbodies = (mwsRecord.mws_intersect_swb || []).map((swb) => ({
+ swbId: swb?.swbId ?? swb?.id ?? swb,
+ swbName: swb?.swbName ?? swb?.name ?? "",
+ }));
+
+ return {
+ mwsId,
+ villages,
+ waterbodies,
+ };
+ });
+}, [manualSelectedMWS, selectionMode, dataJson, villageJson]);
+
+const manualSelectionDetails = React.useMemo(() => {
+ if (!manualSelectedMWS?.length || !dataJson?.length) return [];
+
+ return manualSelectedMWS.map((mwsId) => {
+ const mwsRecord = dataJson.find(
+ (item) => String(item.mws_id) === String(mwsId)
+ );
+
+ if (!mwsRecord) {
+ return {
+ mwsId,
+ villages: [],
+ waterbodies: [],
+ };
+ }
+
+ const villages = (mwsRecord.mws_intersect_villages || []).map((villageId) => {
+ const village = villageJson.find(
+ (v) => String(v.village_id) === String(villageId)
+ );
+
+ return {
+ villageId: String(villageId),
+ villageName:
+ village?.village_name ||
+ village?.vill_name ||
+ village?.name ||
+ "Unknown",
+ };
+ });
+
+ const waterbodies = (mwsRecord.mws_intersect_swb || []).map((swb) => ({
+ swbId: String(swb?.swbId ?? swb?.id ?? swb),
+ swbName: swb?.swbName ?? swb?.name ?? "",
+ }));
+
+ return {
+ mwsId: String(mwsId),
+ villages,
+ waterbodies,
+ };
+ });
+}, [manualSelectedMWS, dataJson, villageJson]);
+
const mwsVillageIntersections = React.useMemo(() => {
if (!selectedMWS || selectedMWS.length === 0 || !dataJson || !Array.isArray(dataJson)) return [];
@@ -384,6 +501,223 @@ const toggleConnectivity = () => {
if (!mwsArrowLayerRef?.current) { console.warn("Arrow layer not ready"); return; }
setShowConnectivity(prev => !prev);
};
+
+ const fetchPlansByTehsil = async (block) => {
+ const res = await fetch(
+ `${process.env.REACT_APP_API_URL}/watershed/plans/?tehsil=${block}&filter_test_plan=true`,
+ {
+ headers: {
+ "Content-Type": "application/json",
+ "ngrok-skip-browser-warning": "1",
+ "X-API-Key": process.env.REACT_APP_API_KEY,
+ },
+ }
+ );
+
+ if (!res.ok) {
+ throw new Error(`API Error ${res.status}`);
+ }
+
+ return res.json();
+};
+
+ const handlePlansClick = async () => {
+ try {
+ // Hide plans if already visible
+ if (showPlans) {
+ if (plansLayerRef.current) {
+ mapRef.current.removeLayer(plansLayerRef.current);
+ }
+ setShowPlans(false);
+ return;
+ }
+
+ // Plans already fetched, just show them again
+ if (plans.length > 0) {
+ showPlansOnMap(plans);
+ setShowPlans(true);
+ return;
+ }
+
+ // Fetch plans first time
+ setPlansLoading(true);
+
+ const response = await fetchPlansByTehsil(block.block_id);
+
+ setPlans(response);
+
+ showPlansOnMap(response);
+
+ setShowPlans(true);
+ } catch (err) {
+ console.error("Error fetching plans:", err);
+ } finally {
+ setPlansLoading(false);
+ }
+ };
+
+const showPlansOnMap = (plansData) => {
+ if (!mapRef.current) return;
+
+ // Remove previous plans layer
+ if (plansLayerRef.current) {
+ mapRef.current.removeLayer(plansLayerRef.current);
+ plansLayerRef.current = null;
+ }
+
+ const features = plansData
+ .filter(
+ (plan) =>
+ plan.latitude !== null &&
+ plan.longitude !== null &&
+ plan.latitude !== undefined &&
+ plan.longitude !== undefined
+ )
+ .map((plan) => {
+ const feature = new Feature({
+ geometry: new Point([
+ Number(plan.longitude),
+ Number(plan.latitude),
+ ]),
+ });
+
+ feature.set("plan", plan);
+ feature.set("planDetails", plan);
+
+ const status = getPlanStatus(plan);
+
+ feature.setStyle(DOT_DEFAULT(status));
+
+ return feature;
+ });
+
+ const layer = new VectorLayer({
+ source: new VectorSource({
+ features,
+ }),
+ zIndex: 9999,
+ });
+
+ plansLayerRef.current = layer;
+
+ mapRef.current.addLayer(layer);
+
+ if (features.length > 0) {
+ mapRef.current.getView().fit(
+ layer.getSource().getExtent(),
+ {
+ padding: [40, 40, 40, 40],
+ duration: 500,
+ maxZoom: 16,
+ }
+ );
+ }
+};
+
+React.useEffect(() => {
+ if (!mapRef.current) return;
+
+ let hoveredFeature = null;
+
+ const handlePointerMove = (evt) => {
+ if (!showPlans || !plansLayerRef.current) return;
+
+ const feature = mapRef.current.forEachFeatureAtPixel(
+ evt.pixel,
+ (feature, layer) => {
+ if (layer === plansLayerRef.current) {
+ return feature;
+ }
+ }
+ );
+
+ // Restore previous hovered feature
+ if (hoveredFeature && hoveredFeature !== feature) {
+ const status = getFeatureStatus(hoveredFeature);
+ hoveredFeature.setStyle(DOT_DEFAULT(status));
+ hoveredFeature = null;
+ }
+
+ // Apply hover style
+ if (feature && feature !== hoveredFeature) {
+ const status = getFeatureStatus(feature);
+ feature.setStyle(DOT_HOVERED(status));
+ hoveredFeature = feature;
+ }
+
+ mapRef.current.getTargetElement().style.cursor = feature ? "pointer" : "";
+ };
+
+ const handlePlanClick = (evt) => {
+ if (!showPlans || !plansLayerRef.current) return;
+
+ const feature = mapRef.current.forEachFeatureAtPixel(
+ evt.pixel,
+ (feature, layer) => {
+ if (layer === plansLayerRef.current) {
+ return feature;
+ }
+ }
+ );
+
+ if (!feature) return;
+
+ const plan = feature.get("planDetails");
+
+ if (!plan) return;
+
+ setSelectedPlanProfile(plan);
+ };
+
+ mapRef.current.on("pointermove", handlePointerMove);
+ mapRef.current.on("singleclick", handlePlanClick);
+
+ return () => {
+ if (mapRef.current) {
+ mapRef.current.un("pointermove", handlePointerMove);
+ mapRef.current.un("singleclick", handlePlanClick);
+ }
+ };
+}, [showPlans, navigate]);
+
+// ── PLAN DOT STYLES ──────────────────────────────────────────────────────────
+
+const PLAN_STATUS_COLORS = {
+ in_progress: { fill: "#FF6FFF", stroke: "#ffffff" }, // magenta
+ dpr_completed: { fill: "#CCFF00", stroke: "#3E5800" }, // chartreuse
+};
+
+const getPlanStatus = (plan) => {
+ if (!plan) return "in_progress";
+ if (plan.is_dpr_reviewed) return "dpr_completed";
+ return "in_progress";
+};
+
+const getFeatureStatus = (feature) => getPlanStatus(feature.get("planDetails"));
+
+const DOT_DEFAULT = (status = "in_progress") => new Style({
+ image: new CircleStyle({
+ radius: 9,
+ fill: new Fill({ color: PLAN_STATUS_COLORS[status].fill }),
+ stroke: new Stroke({ color: PLAN_STATUS_COLORS[status].stroke, width: 2 }),
+ }),
+});
+
+const DOT_HOVERED = (status = "in_progress") => new Style({
+ image: new CircleStyle({
+ radius: 12,
+ fill: new Fill({ color: PLAN_STATUS_COLORS[status].fill }),
+ stroke: new Stroke({ color: "#ffffff", width: 2.5 }),
+ }),
+});
+
+const DOT_SELECTED = (status = "in_progress") => new Style({
+ image: new CircleStyle({
+ radius: 12,
+ fill: new Fill({ color: "#ffffff" }),
+ stroke: new Stroke({ color: PLAN_STATUS_COLORS[status].fill, width: 3.5 }),
+ }),
+});
const handleTehsilReport = () => {
const reportURL = `${process.env.REACT_APP_API_URL}/generate_tehsil_report/?state=${transformName(state?.label)}&district=${transformName(district?.label)}&block=${transformName(block?.label)}`;
@@ -1207,11 +1541,25 @@ const toggleConnectivity = () => {
{mwsGroup && mwsGroup.villages.length > 0 ? (
{mwsGroup.villages.filter(v => {
+ const manualGroup = manualSelectionDetails.find(
+ m => String(m.mwsId) === String(item.name)
+ );
+
+ const isManualVillage = manualGroup?.villages?.some(
+ mv => String(mv.villageId) === String(v.villageId)
+ );
const id = String(v.villageId);
if (seenVillageIds.has(id)) return false;
seenVillageIds.add(id);
return true;
}).map(v => {
+ const manualGroup = manualSelectionDetails.find(
+ m => String(m.mwsId) === String(item.name)
+ );
+
+ const isManualVillage = manualGroup?.villages?.some(
+ mv => String(mv.villageId) === String(v.villageId)
+ );
const villageIdStr = String(v.villageId);
let isSelected = false;
if (hasVillageFilter || hasVillagePattern) {
@@ -1225,11 +1573,14 @@ const toggleConnectivity = () => {
{v.villageName || 'Unknown'}
({v.villageId})
@@ -1255,9 +1606,16 @@ const toggleConnectivity = () => {
return (
{allSwbs.map(swb => {
+ const manualGroup = manualSelectionDetails.find(
+ m => String(m.mwsId) === String(item.name)
+ );
+
+
const swbIdStr = typeof swb === 'object' ? String(swb.swbId) : String(swb);
const swbName = typeof swb === 'object' ? (swb.swbName || '') : '';
-
+ const isManualWaterbody = manualGroup?.waterbodies?.some(
+ wb => String(wb.swbId) === swbIdStr
+ );
// Green if WB filter is active and this WB matched it
// Blue if just a structural intersection
const isFilterMatched = hasWaterbodyFilter &&
@@ -1269,9 +1627,12 @@ const toggleConnectivity = () => {
key={swbIdStr}
title={`${isFilterMatched ? 'Matches filter' : 'Structural intersection'} | ID: ${swbIdStr}`}
className={`inline-flex items-center px-2 py-0.5 rounded-full text-[10px] font-medium border transition-colors ${
- isFilterMatched
- ? 'bg-emerald-500 border-emerald-600 text-white'
- : 'bg-sky-50 border-sky-200 text-sky-700'
+ isManualWaterbody
+ // ? "bg-green-300 border-green-600 text-green-700"
+ ? "bg-emerald-500 border-emerald-600 text-white"
+ : isFilterMatched
+ ? "bg-emerald-500 border-emerald-600 text-white"
+ : "bg-sky-50 border-sky-200 text-sky-700"
}`}
>
{swbName ? `${swbName} (${swbIdStr})` : swbIdStr}
@@ -1447,6 +1808,7 @@ const toggleConnectivity = () => {
setSelectionMode={setSelectionMode}
onResetMWS={onResetMWS}
onResetSelection={onResetMWSSelection}
+ onOpenSelection={() => setShowSelectionPopup(true)}
/>
) : null}
@@ -1454,8 +1816,110 @@ const toggleConnectivity = () => {
)}
- {!selectedMWSProfile && !selectedWaterbodyProfile && (
-
+ {selectedPlanProfile && (
+
+
+ {/* Header */}
+
+
+
+
+
+
+ {/*
+ Plan Overview
+
+
+ | */}
+
+
+ {selectedPlanProfile.plan}
+
+
+
+
+
+
+ {/* Information Card */}
+
+
+
+ Village
+
+ {selectedPlanProfile.village_name || "--"}
+
+
+
+
+ Steward
+
+ {selectedPlanProfile.facilitator_name || "--"}
+
+
+
+
+ Organization
+
+ {selectedPlanProfile.organization_name || "--"}
+
+
+
+
+
+ {/* View DPR Button */}
+
+
+
+ )}
+
+ {!selectedMWSProfile && !selectedWaterbodyProfile && !selectedPlanProfile && (
+
{/* ── Hint banner ── */}
@@ -1487,8 +1951,8 @@ const toggleConnectivity = () => {
{block && (
-
-
{
const navigate = useNavigate();
const [searchParams] = useSearchParams();
- const [plan, setPlan] = useState(state?.plan ?? null);
- const [deepLinkLoading, setDeepLinkLoading] = useState(!state?.plan && !!searchParams.get("id"));
+ // const [plan, setPlan] = useState(state?.plan ?? null);
+ // const [deepLinkLoading, setDeepLinkLoading] = useState(!state?.plan && !!searchParams.get("id"));
+ const storedState = JSON.parse(
+ sessionStorage.getItem("planNavigationData") || "null"
+ );
+ const navigationState = state || storedState;
+ const [plan, setPlan] = useState(navigationState?.plan ?? null);
+
+ const [deepLinkLoading, setDeepLinkLoading] = useState(
+ !navigationState?.plan && !!searchParams.get("id")
+ );
+
// ── DEEP LINK FETCH ─────────────────────────────────────
useEffect(() => {
- if (state?.plan || !searchParams.get("id")) return;
+ // if (state?.plan || !searchParams.get("id")) return;
+ if (navigationState?.plan || !searchParams.get("id")) return;
const planId = searchParams.get("id");
const load = async () => {
@@ -535,6 +546,9 @@ const PlanViewPage = () => {
plan: brief?.village_name ?? `Plan ${planId}`,
village_name: brief?.village_name,
gram_panchayat: brief?.gram_panchayat,
+ latitude: brief?.latitude,
+ longitude: brief?.longitude,
+ state: brief?.state,
district: transformName(brief?.district ?? ""),
block: transformName(brief?.tehsil ?? ""),
organization_name: team?.organization,
@@ -733,7 +747,9 @@ const PlanViewPage = () => {
return mwsLayer;
}, [districtNameSafe, blockNameSafe]);
+
const loadAdminBoundary = useCallback(async (map) => {
+
const lon = plan?.longitude ? parseFloat(plan.longitude) : null;
const lat = plan?.latitude ? parseFloat(plan.latitude) : null;
@@ -1066,9 +1082,15 @@ const PlanViewPage = () => {
navigate("/landscape-stewardship", { state: { returnContext: state?.returnContext } })}
+ onClick={() => {
+ const stateId = searchParams.get("stateId");
+ const stateName = searchParams.get("stateName");
+ const districtId = searchParams.get("districtId");
+ const districtName = searchParams.get("districtName");
+ navigate(`/landscape-stewardship?state=${stateId}&stateName=${stateName}&district=${districtId}&districtName=${districtName}`);
+ }}
className="flex-shrink-0 flex items-center gap-2 px-4 py-2 rounded-xl font-semibold
- text-sm transition-all duration-200 active:scale-95"
+ text-sm transition-all duration-200 active:scale-95"
style={{ background: "rgba(255,255,255,0.95)", color: P.dark, boxShadow: "0 2px 12px rgba(0,0,0,0.15)" }}
>← Back
diff --git a/src/pages/PlansPage.jsx b/src/pages/PlansPage.jsx
index 5c56c9ed..aef019d9 100644
--- a/src/pages/PlansPage.jsx
+++ b/src/pages/PlansPage.jsx
@@ -17,6 +17,7 @@ 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";
const P = {
@@ -438,6 +439,7 @@ const PlansPage = () => {
const [planDotsVisible, setPlanDotsVisible] = useState(false);
const [dprStatus, setDprStatus] = useState(null);
const [statusTracking, setStatusTracking] = useState(null);
+ const [searchParams, setSearchParams] = useSearchParams();
const [selectedVillage, setSelectedVillage] = useState(null);
const [villageOptions, setVillageOptions] = useState([]);
@@ -584,6 +586,8 @@ const handleVillageSuggestionSelect = (placeId, description) => {
return () => map.setTarget(null);
}, []);
+
+
const fetchProposedBlocks = async () => {
const res = await fetch(`${process.env.REACT_APP_API_URL}/proposed_blocks/`, {
method: "GET",
@@ -649,25 +653,64 @@ const handleVillageSuggestionSelect = (placeId, description) => {
}
}, [mapLoading, statsLoading]);
- useEffect(() => {
- const ctx = location.state?.returnContext;
- if (!ctx?.stateId) return;
-
- const tryRestore = setInterval(() => {
- if (mapRef.current && metaStatsRef.current && !hasRestoredRef.current) {
- clearInterval(tryRestore);
- hasRestoredRef.current = true;
- handleStatePinClick({
- state_id: ctx.stateId,
- state_name: ctx.stateName,
+ // useEffect(() => {
+ // const ctx = location.state?.returnContext;
+ // if (!ctx?.stateId) return;
+
+ // const tryRestore = setInterval(() => {
+ // if (mapRef.current && metaStatsRef.current && !hasRestoredRef.current) {
+ // clearInterval(tryRestore);
+ // hasRestoredRef.current = true;
+ // handleStatePinClick({
+ // state_id: ctx.stateId,
+ // state_name: ctx.stateName,
+ // }).then(() => {
+ // if (ctx.districtId) {
+ // handleDistrictPinClick({
+ // district_id: ctx.districtId,
+ // district_name: ctx.districtName,
+ // });
+ // }
+ // });
+ // }
+ // }, 100);
+
+ // return () => clearInterval(tryRestore);
+ // }, []);
+
+ // ── STATS ───────────────────────────────────────────────────
+
+ useEffect(() => {
+ const stateId = searchParams.get("state");
+ const stateName = searchParams.get("stateName");
+ const districtId = searchParams.get("district");
+ const districtName = searchParams.get("districtName");
+
+ if (!stateId) return;
+
+ const tryRestore = setInterval(() => {
+ if (mapRef.current && metaStatsRef.current && !hasRestoredRef.current) {
+ clearInterval(tryRestore);
+ hasRestoredRef.current = true;
+ handleStatePinClick({
+ state_id: stateId,
+ state_name: stateName,
+ }).then(() => {
+ if (districtId) {
+ handleDistrictPinClick({
+ district_id: districtId,
+ district_name: districtName,
});
}
- }, 100);
+ });
+ }
+ }, 100);
+
+ return () => clearInterval(tryRestore);
+}, []);
+
- return () => clearInterval(tryRestore);
- }, []);
- // ── STATS ───────────────────────────────────────────────────
const loadStats = async (orgId = null, stateId = null) => {
setStatsLoading(true);
setStatsError(false);
@@ -966,6 +1009,10 @@ const handleVillageSuggestionSelect = (placeId, description) => {
const handleStatePinClick = async (stateData) => {
currentStateRef.current = stateData;
setMapLoading(true);
+ setSearchParams({
+ state: stateData.state_id,
+ stateName: stateData.state_name,
+ }, { replace: true });
if (bubbleLayerRef.current) {
mapRef.current.removeLayer(bubbleLayerRef.current);
@@ -1014,6 +1061,13 @@ const handleVillageSuggestionSelect = (placeId, description) => {
const handleDistrictPinClick = async (districtData) => {
if (!districtData) return;
+ setSearchParams((prev) => {
+ const params = new URLSearchParams(prev);
+ params.set("district", districtData.district_id);
+ params.set("districtName", districtData.district_name);
+ return params;
+ }, { replace: true });
+
if (viewModeRef.current === "plans") {
setMapLoading(true);
currentDistrictRef.current = districtData;
@@ -1062,6 +1116,7 @@ const handleVillageSuggestionSelect = (placeId, description) => {
const handleBackToStateView = async () => {
const map = mapRef.current;
if (!map) return;
+ setSearchParams({}, { replace: true });
if (planLayerRef.current) {
map.removeLayer(planLayerRef.current);
@@ -1130,6 +1185,12 @@ const handleVillageSuggestionSelect = (placeId, description) => {
const map = mapRef.current;
if (!map) return;
+ setSearchParams((prev) => {
+ const params = new URLSearchParams(prev);
+ params.delete("district");
+ params.delete("districtName");
+ return params;
+ }, { replace: true });
currentDistrictRef.current = null;
if (viewModeRef.current === "plans") {
@@ -1655,19 +1716,52 @@ const handleVillageSuggestionSelect = (placeId, description) => {
{
- const districtLabel = transformName(districtLookupRef.current[selectedPlan.district_soi] || "");
- const tehsilLabel = transformName(tehsilLookupRef.current[selectedPlan.tehsil_soi] || "");
- navigate(`/landscape-stewardship/plan-view?id=${selectedPlan.id}&completed=${!!selectedPlan.is_completed}&dpr_reviewed=${!!selectedPlan.is_dpr_reviewed}&dpr_generated=${!!selectedPlan.is_dpr_generated}&dpr_approved=${!!selectedPlan.is_dpr_approved}`, {
- state: {
- plan: { ...selectedPlan, district: districtLabel, block: tehsilLabel },
- returnContext: {
- stateId: currentStateRef.current?.state_id ?? null,
- stateName: currentStateRef.current?.state_name ?? null,
- },
- },
- });
- }}
+ onClick={() => {
+ const districtLabel = transformName(
+ districtLookupRef.current[selectedPlan.district_soi] || ""
+ );
+
+ const tehsilLabel = transformName(
+ tehsilLookupRef.current[selectedPlan.tehsil_soi] || ""
+ );
+
+ const navigationData = {
+ plan: {
+ ...selectedPlan,
+ district: districtLabel,
+ block: tehsilLabel,
+ },
+ returnContext: {
+ stateId: currentStateRef.current?.state_id ?? null,
+ stateName: currentStateRef.current?.state_name ?? null,
+ districtId: currentDistrictRef.current?.district_id ?? null,
+ districtName: currentDistrictRef.current?.district_name ?? null,
+ },
+ };
+
+ sessionStorage.setItem(
+ "planNavigationData",
+ JSON.stringify(navigationData)
+ );
+
+ window.open(
+ `/landscape-stewardship/plan-view?id=${selectedPlan.id}&completed=${!!selectedPlan.is_completed}&dpr_reviewed=${!!selectedPlan.is_dpr_reviewed}&dpr_generated=${!!selectedPlan.is_dpr_generated}&dpr_approved=${!!selectedPlan.is_dpr_approved}&stateId=${currentStateRef.current?.state_id ?? ""}&stateName=${encodeURIComponent(currentStateRef.current?.state_name ?? "")}&districtId=${currentDistrictRef.current?.district_id ?? ""}&districtName=${encodeURIComponent(currentDistrictRef.current?.district_name ?? "")}`,
+ "_blank"
+ );
+ }}
+ // onClick={() => {
+ // const districtLabel = transformName(districtLookupRef.current[selectedPlan.district_soi] || "");
+ // const tehsilLabel = transformName(tehsilLookupRef.current[selectedPlan.tehsil_soi] || "");
+ // navigate(`/landscape-stewardship/plan-view?id=${selectedPlan.id}&completed=${!!selectedPlan.is_completed}&dpr_reviewed=${!!selectedPlan.is_dpr_reviewed}&dpr_generated=${!!selectedPlan.is_dpr_generated}&dpr_approved=${!!selectedPlan.is_dpr_approved}`, {
+ // state: {
+ // plan: { ...selectedPlan, district: districtLabel, block: tehsilLabel },
+ // returnContext: {
+ // stateId: currentStateRef.current?.state_id ?? null,
+ // stateName: currentStateRef.current?.state_name ?? null,
+ // },
+ // },
+ // });
+ // }}
className="w-full py-3 rounded-2xl text-white font-semibold text-sm flex-shrink-0
shadow-lg transition-all duration-200"
disabled={!selectedPlan.is_dpr_reviewed}
diff --git a/src/pages/kyl_dashboard.jsx b/src/pages/kyl_dashboard.jsx
index 97142040..1ad6f220 100644
--- a/src/pages/kyl_dashboard.jsx
+++ b/src/pages/kyl_dashboard.jsx
@@ -120,6 +120,7 @@ const KYLDashboardPage = () => {
const [isLayerSelecting, setIsLayerSelecting] = useState(false);
const showConnectivityRef = useRef(false);
const [manualSelectedMWS, setManualSelectedMWS] = useState([]);
+ const [showPlans, setShowPlans] = useState(false);
const [dataJsonError, setDataJsonError] = useState(null);
@@ -417,12 +418,18 @@ useEffect(() => {
// ─── WB filters: ID-based lookup via dataJsonIndex, no geometry ───
const applyWaterbodyFilters = (mwsIds, wbFilters, isVisualizeOn) => {
- if (!waterbodiesLayerRef.current) return;
+ if (!waterbodiesLayerRef.current) return;
- const wbSource = waterbodiesLayerRef.current.getSource();
- const filterKeys = Object.keys(wbFilters || {}).filter(k => wbFilters[k]);
- const hasMWSFilter = mwsIds.length > 0;
- const hasAttrFilter = filterKeys.length > 0;
+ const wbSource = waterbodiesLayerRef.current.getSource();
+ const filterKeys = Object.keys(wbFilters || {}).filter(k => wbFilters[k]);
+
+ // A MWS-level filter is "active" even if it currently matches 0 MWS —
+ // in that case waterbodies should show NONE, not fall back to showing all.
+ const mwsFilterKeys = Object.keys(filterSelections.selectedMWSValues || {});
+ const isMWSFilterSelected = mwsFilterKeys.some(k => filterSelections.selectedMWSValues[k]);
+
+ const hasMWSFilter = mwsIds.length > 0 || isMWSFilterSelected;
+ const hasAttrFilter = filterKeys.length > 0;
if (!hasMWSFilter && !hasAttrFilter && !isVisualizeOn) {
waterbodiesLayerRef.current.updateStyleVariables({ wbFilterActive: 0 });
@@ -2324,6 +2331,12 @@ useEffect(() => {
features.forEach((feature) => {
const uid = feature.get("uid");
+ feature.set(
+ "isSelected",
+ selectedIds.includes(uid) ? 1 : 0,
+ true
+ );
+ });
feature.set(
"isSelected",
@@ -2338,6 +2351,16 @@ useEffect(() => {
useEffect(() => {
if (!mapRef.current) return;
+useEffect(() => {
+ if (!mapRef.current) return;
+
+ const handleMapClick = (event) => {
+ const feature = mapRef.current.forEachFeatureAtPixel(
+ event.pixel,
+ (feature, layer) => {
+ if (layer === mwsLayerRef.current) return feature;
+ }
+ );
const handleMapClick = (event) => {
const feature = mapRef.current.forEachFeatureAtPixel(
@@ -2386,6 +2409,21 @@ useEffect(() => {
setSelectedMWSProfile(feature.getProperties());
}
+ // base toggle strictly on manualSelectedMWS — selectedMWS stays
+ // purely filter-driven and must never be touched here
+ const updated = manualSelectedMWS.includes(uid)
+ ? manualSelectedMWS.filter((id) => id !== uid)
+ : [...manualSelectedMWS, uid];
+
+ updateSelectedMWSStyle(updated);
+ setManualSelectedMWS(updated);
+ setHighlightMWS(uid);
+
+ if (updated.length === 0) {
+ setSelectedMWSProfile(null);
+ } else {
+ setSelectedMWSProfile(feature.getProperties());
+ }
if (toastId) {
toast.dismiss(toastId);
@@ -2403,6 +2441,15 @@ useEffect(() => {
}, [selectionMode, toastId,mapRef.current, selectedMWS]);
useEffect(() => {
+ return () => {
+ if (mapRef.current) {
+ mapRef.current.un("click", handleMapClick);
+ }
+ };
+}, [toastId, mapRef.current, manualSelectedMWS]);
+
+
+useEffect(() => {
if (mapRef.current && waterbodiesLayerRef.current) {
mapRef.current.removeLayer(waterbodiesLayerRef.current);
waterbodiesLayerRef.current = null;
@@ -3001,6 +3048,8 @@ useEffect(() => {
currentLayer={currentLayer}
setSearchLatLong={setSearchLatLong}
showConnectivity={showConnectivity}
+ showPlans={showPlans}
+ setShowPlans={setShowPlans}
selectionMode={selectionMode}
setSelectionMode={setSelectionMode}
/>
@@ -3059,6 +3108,8 @@ useEffect(() => {
setSelectionMode={setSelectionMode}
manualSelectedMWS={manualSelectedMWS}
onResetMWSSelection={handleResetMWSSelection}
+ showPlans={showPlans}
+ setShowPlans={setShowPlans}
/>