"use client"; import { useState, useRef, Suspense, useEffect } from "react"; import { Canvas, useFrame, useLoader, useThree } from "@react-three/fiber"; import { OrbitControls, QuadraticBezierLine } from "@react-three/drei"; import * as THREE from "three"; import { motion, AnimatePresence } from "framer-motion"; import { MapPin, Calendar, History, X, ArrowUpRight } from "lucide-react"; import CaseStudyModal, { CaseStudyData } from "@/components/ui/CaseStudyModal"; import { useLocale, useTranslations } from "next-intl"; const RADIUS = 2; function latLongToVector3(lat: number, lon: number, radius: number) { const phi = (90 - lat) * (Math.PI / 180); const theta = (lon + 180) * (Math.PI / 180); const x = -(radius * Math.sin(phi) * Math.cos(theta)); const z = (radius * Math.sin(phi) * Math.sin(theta)); const y = (radius * Math.cos(phi)); return new THREE.Vector3(x, y, z); } // ── COMPONENTE OPTIMIZADO 1: TEXTURA DE LA TIERRA CON ALTA RESOLUCIÓN ── function EarthMesh({ isDark }: { isDark: boolean }) { const earthTexture = useLoader(THREE.TextureLoader, "https://unpkg.com/three-globe/example/img/earth-water.png"); const { gl } = useThree(); // 🔥 Filtro de hardware para forzar nitidez al hacer Zoom useEffect(() => { if (earthTexture) { earthTexture.anisotropy = gl.capabilities.getMaxAnisotropy(); // Máximo detalle en ángulos inclinados earthTexture.minFilter = THREE.LinearMipmapLinearFilter; earthTexture.magFilter = THREE.LinearFilter; earthTexture.colorSpace = THREE.SRGBColorSpace; // Colores más vibrantes y definidos earthTexture.generateMipmaps = true; earthTexture.needsUpdate = true; } }, [earthTexture, gl]); return ( {/* Regresamos a 64 segmentos para optimizar rendimiento (la nitidez ahora viene de la textura) */} ); } // ── COMPONENTE OPTIMIZADO 2: EL NODO INTELIGENTE QUE RESPONDE AL ZOOM ── function MapNode({ marker, isSelected, hqPosition, onSelectMarker, isDark }: any) { const meshRef = useRef(null); const pos = latLongToVector3(marker.lat, marker.lon, RADIUS); const isHQ = marker.nodeType === "hq"; const isEvent = marker.nodeType === "event"; const nodeColor = isHQ ? (isDark ? "#FFFFFF" : "#1D1D1F") : isEvent ? "#A855F7" : "#0066CC"; const baseSize = isHQ ? 0.04 : isEvent ? 0.035 : 0.025; useFrame(({ camera }) => { if (!meshRef.current) return; const dist = camera.position.length(); const scaleFactor = Math.max(0.2, dist / 12); const finalScale = isSelected ? scaleFactor * 1.8 : scaleFactor; meshRef.current.scale.set(finalScale, finalScale, finalScale); }); const distance = hqPosition.distanceTo(pos); const arcElevation = RADIUS + (distance * 0.25) + 0.1; const midPoint = hqPosition.clone().lerp(pos, 0.5).normalize().multiplyScalar(arcElevation); return ( {/* CAJA DE COLISIÓN AMPLIADA */} { e.stopPropagation(); onSelectMarker(isSelected ? null : marker.id); }} onPointerOver={(e) => { e.stopPropagation(); document.body.style.cursor = 'pointer'; }} onPointerOut={(e) => { e.stopPropagation(); document.body.style.cursor = 'auto'; }} > {!isHQ && ( )} ); } // ── COMPONENTE DE ENSAMBLAJE DE LA ESFERA ── function HologramSphere({ activeFilter, activeSubFilter, selectedMarker, onSelectMarker, isDark, dbNodes, hqPosition }: any) { const globeRef = useRef(null); // 🔥 MAGIA DE ROTACIÓN INTELIGENTE 🔥 useFrame(({ camera }) => { // La cámara inicia en Z=7. Si el usuario hace zoom in (distancia < 6.5), detenemos la rotación. // Si vuelve a alejar el mapa a su estado normal (distancia > 6.5), retoma la rotación. const distance = camera.position.length(); if (globeRef.current && !selectedMarker && distance > 6.5) { globeRef.current.rotation.y += 0.0005; } }); return ( {/* Esfera Terrestre mejorada con texturas nítidas */} {dbNodes.map((marker: any) => { const isHQ = marker.nodeType === "hq"; const isEvent = marker.nodeType === "event"; const matchesMain = activeFilter === "all" || (activeFilter === "installation" && !isEvent && !isHQ) || (activeFilter === "event" && isEvent) || (activeFilter === "legacy" && isHQ); const matchesSub = !activeSubFilter || marker.application === activeSubFilter || isHQ; const isVisible = matchesMain && matchesSub; if (!isVisible) return null; return ( ); })} ); } // ── INTERFAZ GRÁFICA PRINCIPAL ── export default function GlobalOperations({ dbNodes = [], dbApps = [] }: { dbNodes?: any[], dbApps?: any[] }) { const [activeFilter, setActiveFilter] = useState("all"); const [activeSubFilter, setActiveSubFilter] = useState(null); const [selectedMarkerId, setSelectedMarkerId] = useState(null); const [isModalOpen, setIsModalOpen] = useState(false); const [isDark, setIsDark] = useState(false); const t = useTranslations("GlobalOperations"); const dynamicSubFilters = dbApps .filter(app => app.isActive) .map(app => ({ id: app.slug, label: app.title })); useEffect(() => { const checkTheme = () => setIsDark(document.documentElement.classList.contains("dark")); checkTheme(); const observer = new MutationObserver(checkTheme); observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }); return () => observer.disconnect(); }, []); const filters = [ { id: "all", label: t("filterAll"), icon: MapPin }, { id: "installation", label: t("filterInstallations"), icon: MapPin }, { id: "event", label: t("filterEvents"), icon: Calendar }, { id: "legacy", label: t("filterHQ"), icon: History } ]; const selectedData = dbNodes.find(d => d.id === selectedMarkerId); const hqNode = dbNodes.find(d => d.application === "hq"); const hqLat = hqNode ? hqNode.lat : 45.78; const hqLon = hqNode ? hqNode.lon : 11.76; const hqPosition = latLongToVector3(hqLat, hqLon, RADIUS); const handleMainFilter = (id: string) => { setActiveFilter(id); setActiveSubFilter(null); setSelectedMarkerId(null); }; return ( <>

{t("subtitle")}

{t("title1")}
{t("title2")}

{filters.map((f) => ( ))}
{activeFilter === "installation" && ( {t("filterByApp")} {dynamicSubFilters.map((sub) => ( ))} )}
{!selectedMarkerId && (

{t("networkStatus")}

{activeSubFilter ? t("statusShowing", { app: activeSubFilter.replace("-", " ") }) : t("statusTracking", { count: dbNodes.filter(n => (activeFilter === "all") || (activeFilter === "installation" && n.nodeType === "installation") || (activeFilter === "event" && n.nodeType === "event") || (activeFilter === "legacy" && n.nodeType === "hq") ).length })}

)}
{t("helpText")}
{selectedData && (
{selectedData.nodeType === 'event' ? t("typeEvent") : selectedData.nodeType === 'hq' ? t("typeHQ") : t("typeInstall")}

{selectedData.title}

{selectedData.location}

{t("statusDetails")} {selectedData.stats}
)}
setIsModalOpen(false)} data={selectedData as CaseStudyData || null} /> ); }