Untitled
unknown
plain_text
5 months ago
11 kB
5
Indexable
"use client"; //IMPORT LIKE THIS REACT => React.useState | React.useEffect import * as React from "react"; import { GoogleMap, MarkerF, InfoWindow } from "@react-google-maps/api"; import { mapStyles } from "@/lib/mapStyles"; import { useSearchContext } from "@/context/SearchContext"; import { useSearchParams } from "next/navigation"; import useMarkerStore from "@/store/markers-store"; import { useMap } from "@/context/MapContext"; import BeeSpinner from "../BeeSpinner"; import { Button } from "@/components/ui/button"; import ModernInfoWindow from "../ModernInfoWindow"; interface Marker { id: string; name: string; address: string; lat?: number; lng?: number; referral_code: string; } interface GoogleMapViewProps { errorMessage?: string | null; } const mapOptions = { styles: mapStyles, language: "it", region: "IT", clickableIcons: false, zoomControl: true, streetViewControl: false, fullscreenControl: false, mapTypeControl: false, gestureHandling: "greedy", }; type User = { referral_code: string; company_name: string; address: string; cap: string; fullAddress: string; }; // ABSTRACT THIS FUNCTION OUTSIDE THE COMPONENT IN A UTILS! NO NEED TO RECREATE IT EVERY RENDER const calculateDistance = React.useCallback( (lat1: number, lon1: number, lat2: number, lon2: number) => { const R = 6371; const dLat = deg2rad(lat2 - lat1); const dLon = deg2rad(lon2 - lon1); const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); const d = R * c; return d; }, [] ); // SAME AS ABOVE, MORE ADD A ERROR OF SOME KM CAUSE THIS IS HAVERSINE FORMULA, AND IT DOESNT ACCOUNT ROUTES, JUST STRAIGHT LINE const deg2rad = (deg: number) => { return deg * (Math.PI / 180); }; const removeInfoWindowPadding = () => { if (!document) return; const infoWindows = document.querySelectorAll(".gm-style-iw-c"); infoWindows.forEach((iw) => { (iw as HTMLElement).style.padding = "0"; (iw as HTMLElement).style.maxWidth = "none"; (iw as HTMLElement).style.maxHeight = "none"; (iw as HTMLElement).style.overflow = "visible"; (iw as HTMLElement).style.background = "none"; (iw as HTMLElement).style.boxShadow = "none"; }); const infoWindowContents = document.querySelectorAll(".gm-style-iw-d"); infoWindowContents.forEach((iwc) => { (iwc as HTMLElement).style.padding = "0"; (iwc as HTMLElement).style.maxWidth = "none"; (iwc as HTMLElement).style.maxHeight = "none"; (iwc as HTMLElement).style.overflow = "visible"; }); const infoWindowBackground = document.querySelector(".gm-style-iw-t"); if (infoWindowBackground) { (infoWindowBackground as HTMLElement).style.background = "none"; } // Remove the default close button const closeButtons = document.querySelectorAll(".gm-ui-hover-effect"); closeButtons.forEach((button) => { (button as HTMLElement).style.display = "none"; }); // Remove the arrow const arrows = document.querySelectorAll(".gm-style-iw-tc"); arrows.forEach((arrow) => { (arrow as HTMLElement).style.display = "none"; }); }; function GoogleMapView({ errorMessage }: GoogleMapViewProps) { const { isLoaded, loadError } = useMap(); const { center, setCenter } = useSearchContext(); // Users presumo siano i marker, ma non è chiaro, sarebbe meglio chiamarli markers o userMarkers const { users } = useMarkerStore(); // SEPARATE HOOKS FROM STATE const [userLocation, setUserLocation] = React.useState<google.maps.LatLngLiteral | null>(null); const [markers, setMarkers] = React.useState<Marker[]>([]); const [selectedMarker, setSelectedMarker] = React.useState<Marker | null>( null ); const searchParams = useSearchParams(); const lat = searchParams.get("lat"); const lng = searchParams.get("lng"); const getLocation = React.useCallback(() => { try { if (navigator.geolocation) { return navigator.geolocation.getCurrentPosition( (position) => { setUserLocation({ lat: position.coords.latitude, lng: position.coords.longitude, }); setCenter({ lat: position.coords.latitude, lng: position.coords.longitude, }); }, (error) => { throw new Error(error.message); } ); } } catch (error) { console.error("Error getting user's location:", error); } }, [setCenter]); React.useEffect(() => { if (lat && lng) { setCenter({ lat: Number(lat), lng: Number(lng) }); } else { getLocation(); } }, [getLocation, lat, lng, setCenter]); const geocoder = new google.maps.Geocoder(); // VA DA UN ALTRA PARTE const parseGeocodeResult = ( user: User, results: google.maps.GeocoderResult, index: number ): Marker => { return { id: `${user.referral_code}_${user.address}_${user.cap}_${index}`, name: user.company_name, address: user.fullAddress, lat: results.geometry.location.lat(), lng: results.geometry.location.lng(), referral_code: user.referral_code, }; }; const fetchMarkers = React.useCallback(async () => { const geocodeUser = async (user: User, index: number): Promise<Marker> => { return new Promise((resolve) => { geocoder.geocode({ address: user.fullAddress }, (results, status) => { if (status === "OK" && results?.[0]) { resolve(parseGeocodeResult(user, results[0], index)); } else { console.error("Geocode failed:", status); resolve({ id: `${user.referral_code}_${user.address}_${user.cap}_${index}`, name: user.company_name, address: user.fullAddress, referral_code: user.referral_code, lat: undefined, lng: undefined, }); } }); }); }; if (isLoaded && users.length > 0) { const markersWithCoordinates = users.map( (user: User, index: number): Promise<Marker> => geocodeUser(user, index) ); Promise.all(markersWithCoordinates).then((updatedMarkers) => { setMarkers(updatedMarkers.filter((marker) => marker.lat && marker.lng)); }); } else { setMarkers([]); } }, [isLoaded, users]); React.useEffect(() => { if (isLoaded && users.length > 0) { const markersWithCoordinates = users.map( (user: User, index: number): Promise<Marker> => geocodeUser(user, index) ); Promise.all(markersWithCoordinates).then((updatedMarkers) => { setMarkers(updatedMarkers.filter((marker) => marker.lat && marker.lng)); }); } else { setMarkers([]); } }, [isLoaded, users]); const handleMarkerClick = useCallback((marker: Marker) => { setSelectedMarker(marker); }, []); React.useEffect(() => { // THIS IS NOT READABLE,MORE: U CAN USE CN TO HANDLE THIS, AND USE A CLASSNAME TO STYLE THE INFO WINDOW if (isLoaded) { removeInfoWindowPadding(); const observer = new MutationObserver(removeInfoWindowPadding); observer.observe(document.body, { childList: true, subtree: true }); return () => observer.disconnect(); } }, [isLoaded, selectedMarker]); if (loadError) return <div>Errore nel caricamento di Maps. Ricarica la pagina.</div>; if (!isLoaded) return ( <div className="flex items-center justify-center h-[calc(100dvh-80px)] w-full"> <BeeSpinner /> </div> ); let infoWindowOptions = {}; if (isLoaded) { infoWindowOptions = { pixelOffset: new google.maps.Size(0, -30), maxWidth: 256, disableAutoPan: false, }; } // THOSE ARE CONSTANT AND SHOULD BE OUTSIDE THE COMPONENT OR EVEN ASTRACTED const defaultIcon = { url: "/assets/icons/Mapmarkerviola.svg", scaledSize: new google.maps.Size(50, 50), }; const selectedIcon = { url: "/assets/icons/Mapmarkergiallo.svg", scaledSize: new google.maps.Size(55, 55), }; return ( <div className="flex flex-col h-full w-full relative"> <GoogleMap mapContainerClassName="flex-1 lg:rounded-[1rem] lg:h-full rounded-none h-[calc(100vh-4rem)]" center={center} zoom={14} options={mapOptions} > {userLocation && ( <MarkerF position={userLocation} title="Your Location" /> )} {markers.map( (marker) => marker.lat && marker.lng && ( <MarkerF key={marker.id} position={{ lat: marker.lat, lng: marker.lng }} title={`${marker.name} - ${marker.address}`} icon={ selectedMarker?.id === marker.id ? selectedIcon : defaultIcon } onClick={() => handleMarkerClick(marker)} /> ) )} {selectedMarker && selectedMarker.lat && selectedMarker.lng && ( <InfoWindow position={{ lat: selectedMarker.lat, lng: selectedMarker.lng }} onCloseClick={() => setSelectedMarker(null)} options={infoWindowOptions} > <div className="w-64 h-80 overflow-hidden rounded-lg"> <ModernInfoWindow name={selectedMarker.name} address={selectedMarker.address} onClose={() => setSelectedMarker(null)} distance={ userLocation ? calculateDistance( userLocation.lat, userLocation.lng, selectedMarker.lat, selectedMarker.lng ) : null } referralCode={selectedMarker.referral_code} /> </div> </InfoWindow> )} </GoogleMap> {errorMessage && ( <div className="absolute inset-0 z-[10] flex items-center justify-center bg-black/75 backdrop-blur-sm lg:rounded-[1rem]"> <div className="bg-white p-6 rounded-lg shadow-lg max-w-md text-center"> <p className="text-red-600 font-semibold text-xl mb-2"> Ops! Qualcosa è andato storto. </p> <p className="mb-4"> Errore nel caricamento dei marker. Ricarica la pagina. </p> <Button variant="nodropButton" onClick={() => window.location.reload()} className="text-white px-6 py-2 rounded-[10px] transition-colors" > Ricarica la pagina </Button> </div> </div> )} </div> ); } export default GoogleMapView;
Editor is loading...
Leave a Comment