diff --git a/components/blocks/InteractiveGermanyMap.tsx b/components/blocks/InteractiveGermanyMap.tsx
index c28856e8c..a73b42396 100644
--- a/components/blocks/InteractiveGermanyMap.tsx
+++ b/components/blocks/InteractiveGermanyMap.tsx
@@ -1,13 +1,13 @@
'use client';
-import React, { useState, useRef } from 'react';
+import React, { useState, useRef, useCallback, useMemo } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
-import { MapPin, Factory, Zap, CheckCircle2, ArrowUpRight } from 'lucide-react';
+import { MapPin, CheckCircle2, ArrowUpRight } from 'lucide-react';
import Image from 'next/image';
-import { useRouter, usePathname } from 'next/navigation';
+import { usePathname } from 'next/navigation';
import Link from 'next/link';
import { AnimatedGlossyBorder } from '@/components/ui/AnimatedGlossyBorder';
-import { Location, defaultLocations, minorLocations, projectLocations } from '@/lib/map-data';
+import { Location, projectLocations } from '@/lib/map-data';
import { standorteLocations } from '@/lib/standorte-data';
import { useLocale, useTranslations } from 'next-intl';
@@ -28,6 +28,59 @@ interface InteractiveGermanyMapProps {
hideStandorte?: boolean;
}
+const MinorNode = React.memo(({ loc, isActive, onEnter, onLeave }: { loc: Location, isActive: boolean, onEnter: (loc: Location) => void, onLeave: () => void }) => (
+
onEnter(loc)}
+ onMouseLeave={onLeave}
+ onTouchStart={() => onEnter(loc)}
+ onClick={() => onEnter(loc)}
+ >
+
+
+
+));
+MinorNode.displayName = 'MinorNode';
+
+const MajorNode = React.memo(({ loc, isActive, idx, onEnter, onLeave }: { loc: Location, isActive: boolean, idx: number, onEnter: (loc: Location) => void, onLeave: () => void }) => {
+ const isHQ = loc.type === 'hq';
+ const isBranch = loc.type === 'branch';
+ return (
+ onEnter(loc)}
+ onMouseLeave={onLeave}
+ onTouchStart={() => onEnter(loc)}
+ onClick={() => onEnter(loc)}
+ >
+ {(isHQ || isBranch) && (
+
+ )}
+
+ {(isHQ || isBranch) && }
+
+
+ );
+});
+MajorNode.displayName = 'MajorNode';
+
export function InteractiveGermanyMap({
badge,
title,
@@ -40,22 +93,21 @@ export function InteractiveGermanyMap({
const [activeLocation, setActiveLocation] = useState(null);
const hoverTimeoutRef = useRef(null);
- const handleMouseEnter = (loc: Location) => {
+ const handleMouseEnter = useCallback((loc: Location) => {
if (hoverTimeoutRef.current) {
clearTimeout(hoverTimeoutRef.current);
hoverTimeoutRef.current = null;
}
setActiveLocation(loc);
- };
+ }, []);
- const handleMouseLeave = () => {
+ const handleMouseLeave = useCallback(() => {
const timeout = setTimeout(() => {
setActiveLocation(null);
}, 200);
hoverTimeoutRef.current = timeout;
- };
+ }, []);
- const router = useRouter();
const pathname = usePathname();
const locale = useLocale();
const t = useTranslations('InteractiveGermanyMap');
@@ -67,14 +119,11 @@ export function InteractiveGermanyMap({
];
const finalBadge = badge || tStandard('badge');
- // the map is mostly used with specific props, so we leave title & description to fallbacks if not provided
const finalTitle = title || (locale === 'en' ? <>Nationwide
in operation for you.> : <>Deutschlandweit
für Sie im Einsatz.>);
const finalDescription = description || (locale === 'en' ? 'From our strategic locations in Guben, Kirchheilingen and Bülstedt, we control and implement complex infrastructure projects nationwide.' : 'Von unseren strategischen Standorten in Guben, Kirchheilingen und Bülstedt steuern und realisieren wir komplexe Infrastrukturprojekte im gesamten Bundesgebiet.');
- const finalLocations = React.useMemo(() => {
+ const finalLocations = useMemo(() => {
if (hideStandorte) return locations;
-
- // Ensure the 3 standorte are always included, without duplicating if already present by ID
const base = [...locations];
standorteLocations.forEach(sl => {
if (!base.some(l => l.id === sl.id)) {
@@ -84,19 +133,28 @@ export function InteractiveGermanyMap({
return base;
}, [locations, hideStandorte]);
+ const mapBackground = useMemo(() => (
+
+
+
+ ), [locale]);
+
return (
- {/* Animated Border Glow */}
{!isHero &&
}
- {/* Background Effects */}
- {/* Content Left */}
@@ -117,7 +175,6 @@ export function InteractiveGermanyMap({
{finalDescription}
- {/* Industrial Stats Grid */}
{finalStats.map((stat, i) => {
const StatCard = () => (
@@ -148,85 +205,32 @@ export function InteractiveGermanyMap({
- {/* Map Right */}
- {/* Map Container - Enforce strict aspect ratio matching the SVG (1024x1024) */}
- {/* Map SVG */}
-
- l.type === 'minor_node').map((loc, idx) => (
+
-
+ ))}
- {/* Minor Location Markers (The 100+ generic projects) */}
- {finalLocations.filter(l => l.type === 'minor_node').map((loc, idx) => {
- const isActive = activeLocation === loc;
- return (
-
handleMouseEnter(loc)}
- onMouseLeave={handleMouseLeave}
- onTouchStart={() => handleMouseEnter(loc)}
- onClick={() => handleMouseEnter(loc)}
- >
-
-
-
- )})}
+ {finalLocations.filter(l => l.type !== 'minor_node').map((loc, idx) => (
+
+ ))}
- {/* Major Location Markers (HQ, Branch, Project) */}
- {finalLocations.filter(l => l.type !== 'minor_node').map((loc, idx) => {
- const isHQ = loc.type === 'hq';
- const isBranch = loc.type === 'branch';
- const isActive = activeLocation?.id === loc.id;
-
- return (
-
handleMouseEnter(loc)}
- onMouseLeave={handleMouseLeave}
- onTouchStart={() => handleMouseEnter(loc)}
- onClick={() => handleMouseEnter(loc)}
- >
- {/* Ping Animation for HQ / Branch */}
- {(isHQ || isBranch) && (
-
- )}
-
- {/* Elegant Marker Dot */}
-
- {(isHQ || isBranch) && }
-
-
- );
- })}
-
- {/* Global Tooltip */}
{activeLocation && (
-
{activeLocation.featuredImage && (