Files
panel/src/components/ui/ProjectMap.js
RKWojs fd87b66b06 feat: Implement dark mode support across components and UI elements
- Added dark mode styles to TaskStatusDropdown, TaskStatusDropdownDebug, and TaskStatusDropdownSimple components.
- Introduced ThemeProvider and useTheme hook for managing theme state.
- Updated Button, Card, Input, Loading, Navigation, PageContainer, PageHeader, ProjectCalendarWidget, ProjectMap, SearchBar, States, Tooltip, and other UI components to support dark mode.
- Created ThemeToggle component for switching between light and dark modes.
- Enhanced i18n translations for settings related to theme and language preferences.
- Configured Tailwind CSS to support dark mode with class-based toggling.
2025-09-25 08:58:03 +02:00

177 lines
5.3 KiB
JavaScript

"use client";
import { useEffect, useState } from "react";
import dynamic from "next/dynamic";
// Dynamically import the map component to avoid SSR issues
const DynamicMap = dynamic(() => import("./LeafletMap"), {
ssr: false,
loading: () => (
<div className="w-full h-64 bg-gray-100 dark:bg-gray-700 animate-pulse rounded-lg flex items-center justify-center">
<span className="text-gray-500 dark:text-gray-400">Loading map...</span>
</div>
),
});
export default function ProjectMap({
coordinates,
projectName,
projectStatus = "registered",
showLayerControl = true,
mapHeight = "h-64",
defaultLayer = "OpenStreetMap",
showOverlays = true,
}) {
const [coords, setCoords] = useState(null);
// Status configuration matching the main map
const statusConfig = {
registered: { color: "#6B7280", label: "Registered" },
in_progress_design: { color: "#3B82F6", label: "In Progress (Design)" },
in_progress_construction: {
color: "#F59E0B",
label: "In Progress (Construction)",
},
fulfilled: { color: "#10B981", label: "Completed" },
cancelled: { color: "#EF4444", label: "Cancelled" },
};
useEffect(() => {
if (coordinates) {
// Parse coordinates string (e.g., "49.622958,20.629562")
const [lat, lng] = coordinates
.split(",")
.map((coord) => parseFloat(coord.trim()));
if (!isNaN(lat) && !isNaN(lng)) {
setCoords({ lat, lng });
}
}
}, [coordinates]);
if (!coords) {
return (
<div className="space-y-2">
<div className="flex items-center justify-between">
<h3 className="text-sm font-medium text-gray-700 dark:text-gray-300">
Lokalizacja projektu
</h3>
<div className="text-xs text-gray-500 dark:text-gray-400">No coordinates available</div>
</div>
<div
className={`w-full ${mapHeight} rounded-lg bg-gray-100 dark:bg-gray-700 border border-gray-200 dark:border-gray-600 flex items-center justify-center`}
>
<div className="text-center text-gray-500 dark:text-gray-400">
<svg
className="w-8 h-8 mx-auto mb-2"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z"
clipRule="evenodd"
/>
</svg>
<p className="text-sm">Map unavailable</p>
<p className="text-xs">No coordinates provided</p>
</div>
</div>
</div>
);
}
const statusInfo = statusConfig[projectStatus] || statusConfig.registered;
return (
<div className="space-y-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<h3 className="text-sm font-medium text-gray-700 dark:text-gray-300">
Lokalizacja projektu
</h3>
<div
className="w-3 h-3 rounded-full border border-white dark:border-gray-800 shadow-sm"
style={{ backgroundColor: statusInfo.color }}
title={`Status: ${statusInfo.label}`}
></div>
</div>
{showLayerControl && (
<div className="text-xs text-gray-500 dark:text-gray-400">
{/* Use the layer control (📚) to switch map views */}
</div>
)}
</div>
<div
className={`w-full ${mapHeight} rounded-lg border border-gray-200 dark:border-gray-600`}
>
<DynamicMap
center={[coords.lat, coords.lng]}
zoom={15}
markers={[
{
position: [coords.lat, coords.lng],
color: statusInfo.color,
popup: (
<div className="min-w-48">
<div className="mb-2 pb-2 border-b border-gray-200 dark:border-gray-600">
<h4 className="font-semibold text-gray-900 dark:text-gray-100 mb-1">
{projectName || "Project Location"}
</h4>
<span
className="inline-block px-2 py-1 rounded-full text-xs font-medium text-white"
style={{ backgroundColor: statusInfo.color }}
>
{statusInfo.label}
</span>
</div>
<div className="text-sm text-gray-600 dark:text-gray-400">
<div className="flex items-center gap-2">
<svg
className="w-4 h-4 text-gray-400 dark:text-gray-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<span className="font-mono text-xs">
{coords.lat.toFixed(6)}, {coords.lng.toFixed(6)}
</span>
</div>
</div>
</div>
),
},
]}
showLayerControl={showLayerControl}
defaultLayer={defaultLayer}
showOverlays={showOverlays}
/>
</div>
<div className="flex items-center justify-between">
<p className="text-xs text-gray-500">
{coords.lat.toFixed(6)}, {coords.lng.toFixed(6)}
</p>
<div className="flex items-center gap-1 text-xs text-gray-500">
<div
className="w-2 h-2 rounded-full"
style={{ backgroundColor: statusInfo.color }}
></div>
<span>{statusInfo.label}</span>
</div>
</div>
</div>
);
}