feat: Add translation support for map-related components and improve loading messages

This commit is contained in:
2025-09-16 16:32:04 +02:00
parent cf8ff874da
commit 029b091b10
2 changed files with 118 additions and 46 deletions

View File

@@ -4,22 +4,15 @@ 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";
// Dynamically import the map component to avoid SSR issues
const DynamicMap = dynamic(() => import("@/components/ui/LeafletMap"), {
ssr: false,
loading: () => (
<div className="w-full h-96 bg-gray-100 animate-pulse rounded-lg flex items-center justify-center">
<span className="text-gray-500">Loading map...</span>
</div>
),
});
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
@@ -36,31 +29,41 @@ function ProjectsMapPageContent() {
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: () => (
<div className="w-full h-96 bg-gray-100 animate-pulse rounded-lg flex items-center justify-center">
<span className="text-gray-500">{t('map.loadingMap')}</span>
</div>
),
});
// Status configuration with colors and labels
const statusConfig = {
registered: {
color: "#6B7280",
label: "Registered",
label: formatProjectStatus("registered"),
shortLabel: "Zarejestr.",
},
in_progress_design: {
color: "#3B82F6",
label: "In Progress (Design)",
label: formatProjectStatus("in_progress_design"),
shortLabel: "W real. (P)",
},
in_progress_construction: {
color: "#F59E0B",
label: "In Progress (Construction)",
label: formatProjectStatus("in_progress_construction"),
shortLabel: "W real. (R)",
},
fulfilled: {
color: "#10B981",
label: "Completed",
label: formatProjectStatus("fulfilled"),
shortLabel: "Zakończony",
},
cancelled: {
color: "#EF4444",
label: "Cancelled",
label: formatProjectStatus("cancelled"),
shortLabel: "Wycofany",
},
};
@@ -331,7 +334,7 @@ function ProjectsMapPageContent() {
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
/>
</svg>
View Project Details
{t('map.viewProjectDetails')}
</Button>
</Link>
</div>
@@ -346,9 +349,9 @@ function ProjectsMapPageContent() {
<div className="fixed inset-0 bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="w-12 h-12 mx-auto mb-4 border-4 border-blue-200 border-t-blue-600 rounded-full animate-spin"></div>
<p className="text-gray-600 font-medium">Loading projects map...</p>
<p className="text-gray-600 font-medium">{t('map.loadingProjectsMap')}</p>
<p className="text-sm text-gray-500 mt-2">
Preparing your full-screen map experience
{t('map.preparingMap')}
</p>
</div>
</div>
@@ -362,10 +365,10 @@ function ProjectsMapPageContent() {
<div className="bg-white/95 backdrop-blur-sm rounded-lg shadow-lg px-4 py-3 border border-gray-200">
<div className="flex items-center gap-3">
<h1 className="text-lg font-semibold text-gray-900">
Projects Map
{t('map.projectsMap')}
</h1>
<div className="text-sm text-gray-600">
{markers.length} of {projects.length} projects with coordinates
{markers.length} of {projects.length} {t('map.projectsWithCoordinates')}
</div>
</div>{" "}
</div>
@@ -380,7 +383,7 @@ function ProjectsMapPageContent() {
const event = new CustomEvent("mapZoomIn");
window.dispatchEvent(event);
}}
title="Zoom In"
title={t('map.zoomIn')}
>
+
</button>
@@ -391,7 +394,7 @@ function ProjectsMapPageContent() {
const event = new CustomEvent("mapZoomOut");
window.dispatchEvent(event);
}}
title="Zoom Out"
title={t('map.zoomOut')}
>
</button>{" "}
@@ -410,7 +413,7 @@ function ProjectsMapPageContent() {
: "text-gray-700 hover:bg-gray-50"
}`}
onClick={() => setCurrentTool("move")}
title="Move Tool (Pan Map)"
title={t('map.moveTool')}
>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 512 512">
<path d="M256 0c-25.3 0-47.2 14.7-57.6 36c-7-2.6-14.5-4-22.4-4c-35.3 0-64 28.7-64 64l0 165.5-2.7-2.7c-25-25-65.5-25-90.5 0s-25 65.5 0 90.5L106.5 437c48 48 113.1 75 181 75l8.5 0 8 0c1.5 0 3-.1 4.5-.4c91.7-6.2 165-79.4 171.1-171.1c.3-1.5 .4-3 .4-4.5l0-176c0-35.3-28.7-64-64-64c-5.5 0-10.9 .7-16 2l0-2c0-35.3-28.7-64-64-64c-7.9 0-15.4 1.4-22.4 4C303.2 14.7 281.3 0 256 0zM240 96.1l0-.1 0-32c0-8.8 7.2-16 16-16s16 7.2 16 16l0 31.9 0 .1 0 136c0 13.3 10.7 24 24 24s24-10.7 24-24l0-136c0 0 0 0 0-.1c0-8.8 7.2-16 16-16s16 7.2 16 16l0 55.9c0 0 0 .1 0 .1l0 80c0 13.3 10.7 24 24 24s24-10.7 24-24l0-71.9c0 0 0-.1 0-.1c0-8.8 7.2-16 16-16s16 7.2 16 16l0 172.9c-.1 .6-.1 1.3-.2 1.9c-3.4 69.7-59.3 125.6-129 129c-.6 0-1.3 .1-1.9 .2l-4.9 0-8.5 0c-55.2 0-108.1-21.9-147.1-60.9L52.7 315.3c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0L119 336.4c6.9 6.9 17.2 8.9 26.2 5.2s14.8-12.5 14.8-22.2L160 96c0-8.8 7.2-16 16-16c8.8 0 16 7.1 16 15.9L192 232c0 13.3 10.7 24 24 24s24-10.7 24-24l0-135.9z" />
@@ -424,7 +427,7 @@ function ProjectsMapPageContent() {
: "text-gray-700 hover:bg-gray-50"
}`}
onClick={() => setCurrentTool("select")}
title="Select Tool"
title={t('map.selectTool')}
>
<svg
className="w-5 h-5"
@@ -448,7 +451,7 @@ function ProjectsMapPageContent() {
: "text-gray-700 hover:bg-gray-50"
}`}
onClick={() => setCurrentTool("measure")}
title="Measure Distance"
title={t('map.measureDistance')}
>
<svg
className="w-5 h-5"
@@ -472,7 +475,7 @@ function ProjectsMapPageContent() {
: "text-gray-700 hover:bg-gray-50"
}`}
onClick={() => setCurrentTool("draw")}
title="Draw/Markup"
title={t('map.drawMarkup')}
>
<svg
className="w-5 h-5"
@@ -496,7 +499,7 @@ function ProjectsMapPageContent() {
: "text-gray-700 hover:bg-gray-50"
}`}
onClick={() => setCurrentTool("pin")}
title="Add Pin/Marker"
title={t('map.addPinMarker')}
>
<svg
className="w-5 h-5"
@@ -526,7 +529,7 @@ function ProjectsMapPageContent() {
: "text-gray-700 hover:bg-gray-50"
}`}
onClick={() => setCurrentTool("area")}
title="Measure Area"
title={t('map.measureArea')}
>
<svg
className="w-5 h-5"
@@ -567,7 +570,7 @@ function ProjectsMapPageContent() {
d="M4 6h16M4 10h16M4 14h16M4 18h16"
/>
</svg>
List View
{t('map.listView')}
</Button>
</Link>
<Link href="/projects/new">
@@ -585,7 +588,7 @@ function ProjectsMapPageContent() {
d="M12 4v16m8-8H4"
/>
</svg>
Add Project
{t('map.addProject')}
</Button>
</Link>
</div>
@@ -597,7 +600,7 @@ function ProjectsMapPageContent() {
<button
onClick={toggleLayerPanel}
className="flex items-center justify-between w-full text-left layer-toggle-button"
title="Toggle Layer Controls"
title={t('map.toggleLayerControls')}
>
<div className="flex items-center gap-2">
<svg
@@ -614,10 +617,10 @@ function ProjectsMapPageContent() {
/>
</svg>
<span className="text-sm font-medium text-gray-700">
Map Layers
{t('map.mapLayers')}
</span>
<span className="text-xs text-gray-500 bg-gray-100 px-2 py-0.5 rounded-full">
{1 + activeOverlays.length} active
{1 + activeOverlays.length} {t('map.active')}
</span>
</div>
<svg
@@ -662,7 +665,7 @@ function ProjectsMapPageContent() {
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
Base Maps
{t('map.baseMaps')}
</h3>
<div className="space-y-2">
{mapLayers.base.map((layer, index) => (
@@ -702,7 +705,7 @@ function ProjectsMapPageContent() {
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10"
/>
</svg>
Overlay Layers
{t('map.overlayLayers')}
</h3>{" "}
<div className="space-y-2">
{mapLayers.overlays.map((layer, index) => (
@@ -733,7 +736,7 @@ function ProjectsMapPageContent() {
<div className="bg-white/95 backdrop-blur-sm rounded-lg shadow-lg px-4 py-3 border border-gray-200">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-700 mr-2">
Filters:
{t('map.filters')}
</span>
{/* Toggle All Button */}
<button
@@ -774,7 +777,7 @@ function ProjectsMapPageContent() {
className={`flex items-center gap-1 px-2 py-1 rounded text-xs font-medium transition-all duration-200 hover:bg-gray-100 ${
isActive ? "opacity-100 scale-100" : "opacity-40 scale-95"
}`}
title={`Toggle ${config.label} (${projectCount} projects)`}
title={`Toggle ${config.label} (${projectCount} ${t('map.projects')})`}
>
<div
className={`w-3 h-3 rounded-full border-2 transition-all duration-200 ${
@@ -809,7 +812,7 @@ function ProjectsMapPageContent() {
<div className="bg-white/95 backdrop-blur-sm rounded-lg shadow-lg px-4 py-3 border border-gray-200">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-700 mr-2">
Filters:
{t('map.filters')}
</span>
{/* Toggle All Button */}
@@ -833,8 +836,8 @@ function ProjectsMapPageContent() {
</svg>
<span className="text-gray-600">
{Object.values(statusFilters).every((v) => v)
? "Hide All"
: "Show All"}
? t('map.hideAll')
: t('map.showAll')}
</span>
</button>
@@ -852,7 +855,7 @@ function ProjectsMapPageContent() {
className={`flex items-center gap-1 px-2 py-1 rounded text-xs font-medium transition-all duration-200 hover:bg-gray-100 ${
isActive ? "opacity-100 scale-100" : "opacity-40 scale-95"
}`}
title={`Toggle ${config.label} (${projectCount} projects)`}
title={`Toggle ${config.label} (${projectCount} ${t('map.projects')})`}
>
<div
className={`w-3 h-3 rounded-full border-2 transition-all duration-200 ${
@@ -900,18 +903,17 @@ function ProjectsMapPageContent() {
</svg>
</div>
<h3 className="text-lg font-medium text-gray-900 mb-2">
No projects with coordinates
{t('map.noProjectsWithCoordinates')}
</h3>
<p className="text-gray-500 mb-6">
Projects need coordinates to appear on the map. Add coordinates
when creating or editing projects.
{t('map.noProjectsMessage')}
</p>
<div className="flex gap-3 justify-center">
<Link href="/projects">
<Button variant="outline">View All Projects</Button>
<Button variant="outline">{t('map.viewAllProjects')}</Button>
</Link>
<Link href="/projects/new">
<Button variant="primary">Add Project</Button>
<Button variant="primary">{t('map.addProject')}</Button>
</Link>
</div>
</div>

View File

@@ -69,6 +69,7 @@ const translations = {
actions: "Akcje",
view: "Wyświetl",
clearSearch: "Wyczyść wyszukiwanie",
filters:"Filtry",
clearAllFilters: "Wyczyść wszystkie filtry",
sortBy: "Sortuj według"
},
@@ -273,6 +274,40 @@ const translations = {
failedToDeleteFile: "Nie udało się usunąć pliku"
},
// Map
map: {
loadingMap: "Ładowanie mapy...",
preparingMap: "Przygotowywanie pełnoekranowego widoku mapy",
projectsMap: "Mapa projektów",
projectsWithCoordinates: "projektów z współrzędnymi",
loadingProjectsMap: "Ładowanie mapy projektów...",
moveTool: "Narzędzie przesuwania (przesuń mapę)",
selectTool: "Narzędzie wyboru",
measureDistance: "Zmierz odległość",
drawMarkup: "Rysuj/oznacz",
addPinMarker: "Dodaj pinezkę/znacznik",
measureArea: "Zmierz powierzchnię",
listView: "Widok listy",
addProject: "Dodaj projekt",
toggleLayerControls: "Przełącz kontrolki warstw",
mapLayers: "Warstwy mapy",
active: "aktywnych",
baseMaps: "Mapy bazowe",
overlayLayers: "Warstwy nakładkowe",
filters: "Filtry:",
toggleAllFilters: "Przełącz wszystkie filtry",
hideAll: "Ukryj wszystkie",
showAll: "Pokaż wszystkie",
toggleFilter: "Przełącz filtr",
projects: "projektów",
noProjectsWithCoordinates: "Brak projektów ze współrzędnymi",
noProjectsMessage: "Projekty potrzebują współrzędnych, aby pojawić się na mapie. Dodaj współrzędne podczas tworzenia lub edycji projektów.",
viewAllProjects: "Zobacz wszystkie projekty",
zoomIn: "Przybliż",
zoomOut: "Oddal",
viewProjectDetails: "Zobacz szczegóły projektu"
},
// Tasks
tasks: {
title: "Zadania",
@@ -552,6 +587,7 @@ const translations = {
actions: "Actions",
view: "View",
clearSearch: "Clear search",
filters: "Filters",
clearAllFilters: "Clear all filters",
sortBy: "Sort by"
},
@@ -672,6 +708,40 @@ const translations = {
}
},
// Map
map: {
loadingMap: "Loading map...",
preparingMap: "Preparing your full-screen map experience",
projectsMap: "Projects Map",
projectsWithCoordinates: "projects with coordinates",
loadingProjectsMap: "Loading projects map...",
moveTool: "Move Tool (Pan Map)",
selectTool: "Select Tool",
measureDistance: "Measure Distance",
drawMarkup: "Draw/Markup",
addPinMarker: "Add Pin/Marker",
measureArea: "Measure Area",
listView: "List View",
addProject: "Add Project",
toggleLayerControls: "Toggle Layer Controls",
mapLayers: "Map Layers",
active: "active",
baseMaps: "Base Maps",
overlayLayers: "Overlay Layers",
filters: "Filters:",
toggleAllFilters: "Toggle all filters",
hideAll: "Hide All",
showAll: "Show All",
toggleFilter: "Toggle filter",
projects: "projects",
noProjectsWithCoordinates: "No projects with coordinates",
noProjectsMessage: "Projects need coordinates to appear on the map. Add coordinates when creating or editing projects.",
viewAllProjects: "View All Projects",
zoomIn: "Zoom In",
zoomOut: "Zoom Out",
viewProjectDetails: "View Project Details"
},
contracts: {
title: "Contracts",
subtitle: "Manage your contracts and agreements",