"use client";
import React, { useEffect, useState, Suspense } from "react";
import Link from "next/link";
import dynamic from "next/dynamic";
import { useSearchParams, useRouter } from "next/navigation";
import { useTranslation } from "@/lib/i18n";
import Button from "@/components/ui/Button";
import { mapLayers } from "@/components/ui/mapLayers";
import { formatProjectStatus } from "@/lib/utils";
function ProjectsMapPageContent() {
const searchParams = useSearchParams();
const router = useRouter();
const { t } = useTranslation();
const [projects, setProjects] = useState([]);
const [loading, setLoading] = useState(true);
const [mapCenter, setMapCenter] = useState([50.0614, 19.9366]); // Default to Krakow, Poland
const [mapZoom, setMapZoom] = useState(10); // Default zoom level
const [statusFilters, setStatusFilters] = useState({
registered: true,
in_progress_design: true,
in_progress_construction: true,
fulfilled: true,
cancelled: true,
});
const [activeBaseLayer, setActiveBaseLayer] = useState("OpenStreetMap");
const [activeOverlays, setActiveOverlays] = useState([]);
const [showLayerPanel, setShowLayerPanel] = useState(true);
const [currentTool, setCurrentTool] = useState("move"); // Current map tool
// Dynamically import the map component to avoid SSR issues
const DynamicMap = dynamic(() => import("@/components/ui/LeafletMap"), {
ssr: false,
loading: () => (
{t('map.loadingMap')}
),
});
// Status configuration with colors and labels
const statusConfig = {
registered: {
color: "#6B7280",
label: formatProjectStatus("registered"),
shortLabel: "Zarejestr.",
},
in_progress_design: {
color: "#3B82F6",
label: formatProjectStatus("in_progress_design"),
shortLabel: "W real. (P)",
},
in_progress_construction: {
color: "#F59E0B",
label: formatProjectStatus("in_progress_construction"),
shortLabel: "W real. (R)",
},
fulfilled: {
color: "#10B981",
label: formatProjectStatus("fulfilled"),
shortLabel: "Zakończony",
},
cancelled: {
color: "#EF4444",
label: formatProjectStatus("cancelled"),
shortLabel: "Wycofany",
},
};
// Toggle all status filters
const toggleAllFilters = () => {
const allActive = Object.values(statusFilters).every((value) => value);
const newState = allActive
? Object.keys(statusFilters).reduce(
(acc, key) => ({ ...acc, [key]: false }),
{}
)
: Object.keys(statusFilters).reduce(
(acc, key) => ({ ...acc, [key]: true }),
{}
);
setStatusFilters(newState);
};
// Toggle status filter
const toggleStatusFilter = (status) => {
setStatusFilters((prev) => ({
...prev,
[status]: !prev[status],
}));
};
// Layer control functions
const handleBaseLayerChange = (layerName) => {
setActiveBaseLayer(layerName);
};
const toggleOverlay = (layerName) => {
setActiveOverlays((prev) => {
if (prev.includes(layerName)) {
return prev.filter((name) => name !== layerName);
} else {
return [...prev, layerName];
}
});
};
const toggleLayerPanel = () => {
setShowLayerPanel(!showLayerPanel);
};
// Update URL with current map state (debounced to avoid too many updates)
const updateURL = (center, zoom) => {
const params = new URLSearchParams();
params.set("lat", center[0].toFixed(6));
params.set("lng", center[1].toFixed(6));
params.set("zoom", zoom.toString());
// Use replace to avoid cluttering browser history
router.replace(`/projects/map?${params.toString()}`, { scroll: false });
};
// Handle map view changes with debouncing
const handleMapViewChange = (center, zoom) => {
setMapCenter(center);
setMapZoom(zoom);
// Debounce URL updates to avoid too many history entries
clearTimeout(window.mapUpdateTimeout);
window.mapUpdateTimeout = setTimeout(() => {
updateURL(center, zoom);
}, 500); // Wait 500ms after the last move to update URL
};
// Hide navigation and ensure full-screen layout
useEffect(() => {
// Check for URL parameters for coordinates and zoom
const lat = searchParams.get("lat");
const lng = searchParams.get("lng");
const zoom = searchParams.get("zoom");
if (lat && lng) {
const latitude = parseFloat(lat);
const longitude = parseFloat(lng);
if (!isNaN(latitude) && !isNaN(longitude)) {
setMapCenter([latitude, longitude]);
}
}
if (zoom) {
const zoomLevel = parseInt(zoom);
if (!isNaN(zoomLevel) && zoomLevel >= 1 && zoomLevel <= 20) {
setMapZoom(zoomLevel);
}
}
// Hide navigation bar for full-screen experience
const nav = document.querySelector("nav");
if (nav) {
nav.style.display = "none";
}
// Prevent scrolling on body
document.body.style.overflow = "hidden";
document.documentElement.style.overflow = "hidden";
// Cleanup when leaving page
return () => {
if (nav) {
nav.style.display = "";
}
document.body.style.overflow = "";
document.documentElement.style.overflow = "";
// Clear any pending URL updates
if (window.mapUpdateTimeout) {
clearTimeout(window.mapUpdateTimeout);
}
};
}, [searchParams]);
useEffect(() => {
fetch("/api/projects")
.then((res) => res.json())
.then((data) => {
setProjects(data);
// Only calculate center based on projects if no URL parameters are provided
const lat = searchParams.get("lat");
const lng = searchParams.get("lng");
if (!lat || !lng) {
// Calculate center based on projects with coordinates
const projectsWithCoords = data.filter((p) => p.coordinates);
if (projectsWithCoords.length > 0) {
const avgLat =
projectsWithCoords.reduce((sum, p) => {
const [lat] = p.coordinates
.split(",")
.map((coord) => parseFloat(coord.trim()));
return sum + lat;
}, 0) / projectsWithCoords.length;
const avgLng =
projectsWithCoords.reduce((sum, p) => {
const [, lng] = p.coordinates
.split(",")
.map((coord) => parseFloat(coord.trim()));
return sum + lng;
}, 0) / projectsWithCoords.length;
setMapCenter([avgLat, avgLng]);
}
}
setLoading(false);
})
.catch((error) => {
console.error("Error fetching projects:", error);
setLoading(false);
});
}, [searchParams]);
// Convert projects to map markers with filtering
const markers = projects
.filter((project) => project.coordinates)
.filter((project) => statusFilters[project.project_status] !== false)
.map((project) => {
const [lat, lng] = project.coordinates
.split(",")
.map((coord) => parseFloat(coord.trim()));
if (isNaN(lat) || isNaN(lng)) {
return null;
}
const statusInfo =
statusConfig[project.project_status] || statusConfig.registered;
return {
position: [lat, lng],
color: statusInfo.color,
popup: (
{project.project_name}
{project.project_number && (
{project.project_number}
)}
{project.address && (
{project.address}
{project.city && (
, {project.city}
)}
)}
{project.wp && (
WP:{" "}
{project.wp}
)}
{project.plot && (
Plot:{" "}
{project.plot}
)}
{project.project_status && (
Status:
{statusInfo.shortLabel}
)}
),
};
})
.filter((marker) => marker !== null);
if (loading) {
return (
{t('map.loadingProjectsMap')}
{t('map.preparingMap')}
);
}
return (
{/* Floating Header - Left Side */}
{/* Title Box */}
{t('map.projectsMap')}
{markers.length} of {projects.length} {t('map.projectsWithCoordinates')}
{" "}
{/* Zoom Controls - Below Title */}
{" "}
{" "}
{/* Tool Panel - Below Zoom Controls */}
{" "}
{" "}
{/* Move Tool */}
{/* Select Tool */}
{/* Measure Tool */}
{/* Draw Tool */}
{/* Pin/Marker Tool */}
{/* Area Tool */}
{/* Layer Control Panel - Right Side */}
{/* Action Buttons */}
{/* Layer Control Panel */}
{/* Layer Control Header */}
{" "}
{/* Layer Control Content */}
{/* Base Layers Section */}
{/* Overlay Layers Section */}
{mapLayers.overlays && mapLayers.overlays.length > 0 && (
)}
{" "}
{/* Status Filter Panel - Bottom Left */}
{t('map.filters')}
{/* Toggle All Button */}
{/* Individual Status Filters */}
{Object.entries(statusConfig).map(([status, config]) => {
const isActive = statusFilters[status];
const projectCount = projects.filter(
(p) => p.project_status === status && p.coordinates
).length;
return (
);
})}{" "}
{" "}
{/* Status Panel - Bottom Left */}
{markers.length > 0 && (
{t('map.filters')}
{/* Toggle All Button */}
{/* Individual Status Filters */}
{Object.entries(statusConfig).map(([status, config]) => {
const isActive = statusFilters[status];
const projectCount = projects.filter(
(p) => p.project_status === status && p.coordinates
).length;
return (
);
})}
)}{" "}
{/* Full Screen Map */}
{markers.length === 0 ? (
{t('map.noProjectsWithCoordinates')}
{t('map.noProjectsMessage')}
) : (
)}{" "}
);
}
export default function ProjectsMapPage() {
return (
}>
);
}