diff --git a/components/observatory/map-component.tsx b/components/observatory/map-component.tsx
new file mode 100644
index 00000000..3b3b377f
--- /dev/null
+++ b/components/observatory/map-component.tsx
@@ -0,0 +1,118 @@
+"use client";
+
+import { useEffect, useState } from "react";
+import L from "leaflet";
+import { MapContainer, TileLayer, Marker, Popup, ZoomControl } from "react-leaflet";
+
+// Import leaflet CSS
+import "leaflet/dist/leaflet.css";
+
+// Add declaration for Leaflet's Icon.Default
+declare module 'leaflet' {
+ namespace Icon {
+ interface Default {
+ _getIconUrl?: string;
+ }
+ }
+}
+
+interface CountryData {
+ name: string;
+ count: number;
+ position: [number, number]; // latitude, longitude
+}
+
+interface MapComponentProps {
+ countries: CountryData[];
+ onCountrySelect: (country: string) => void;
+ selectedCountry: string | null;
+}
+
+// Fix Leaflet default icon path issues
+function fixLeafletIcons() {
+ // Delete to prevent duplicate icon definitions
+ delete L.Icon.Default.prototype._getIconUrl;
+
+ // Set paths to the images
+ L.Icon.Default.mergeOptions({
+ iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png",
+ iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png",
+ shadowUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png",
+ });
+}
+
+export function MapComponent({ countries, onCountrySelect, selectedCountry }: MapComponentProps) {
+ const [isClient, setIsClient] = useState(false);
+
+ useEffect(() => {
+ // Fix Leaflet icons on client side
+ fixLeafletIcons();
+ setIsClient(true);
+ }, []);
+
+ // Get custom icon based on count
+ const getMarkerIcon = (count: number, isSelected: boolean) => {
+ const size = Math.min(Math.max(20, count * 5), 40);
+
+ return L.divIcon({
+ html: `
${count}
`,
+ className: '',
+ iconSize: [size, size],
+ iconAnchor: [size/2, size/2]
+ });
+ };
+
+ if (!isClient) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
+
+ {countries.map((country) => (
+ onCountrySelect(country.name)
+ }}
+ >
+
+
+
{country.name}
+
{country.count} news articles
+
+
+
+ ))}
+
+ );
+}
\ No newline at end of file
diff --git a/components/observatory/observatory-map.tsx b/components/observatory/observatory-map.tsx
index d73a5e46..d89ed88e 100644
--- a/components/observatory/observatory-map.tsx
+++ b/components/observatory/observatory-map.tsx
@@ -1,6 +1,6 @@
"use client";
-import { useEffect, useRef, useState } from "react";
+import { useEffect, useState } from "react";
import dynamic from "next/dynamic";
interface CountryData {
@@ -90,27 +90,17 @@ const COUNTRY_COORDINATES: Record = {
'United Nations': [40.7, -74.0], // UN HQ in New York
};
-// Dynamic imports for Leaflet components to avoid SSR issues
-// We use "any" type here to avoid TypeScript errors with dynamic imports
-const MapContainer: any = dynamic(
- () => import('react-leaflet').then(mod => mod.MapContainer),
- { ssr: false }
-);
-const TileLayer: any = dynamic(
- () => import('react-leaflet').then(mod => mod.TileLayer),
- { ssr: false }
-);
-const Marker: any = dynamic(
- () => import('react-leaflet').then(mod => mod.Marker),
- { ssr: false }
-);
-const Popup: any = dynamic(
- () => import('react-leaflet').then(mod => mod.Popup),
- { ssr: false }
-);
-const ZoomControl: any = dynamic(
- () => import('react-leaflet').then(mod => mod.ZoomControl),
- { ssr: false }
+// Create a client-only map component to avoid SSR issues
+const MapComponent = dynamic(
+ () => import('./map-component').then((mod) => mod.MapComponent),
+ {
+ ssr: false,
+ loading: () => (
+
+ ),
+ }
);
export function ObservatoryMap({
@@ -118,51 +108,6 @@ export function ObservatoryMap({
onCountrySelect,
selectedCountry
}: ObservatoryMapProps) {
- const [isMounted, setIsMounted] = useState(false);
- const [mapKey, setMapKey] = useState(Date.now()); // Unique key for map container
-
- // We'll need the Leaflet CSS
- useEffect(() => {
- // Import Leaflet CSS only on the client
- const loadLeafletStyles = async () => {
- try {
- await import('leaflet/dist/leaflet.css');
- } catch (e) {
- console.error('Failed to load Leaflet CSS', e);
- }
- };
-
- loadLeafletStyles();
-
- // Add marker icons to prevent missing icons issue
- if (typeof window !== 'undefined') {
- // Fix Leaflet's icon paths
- delete window._leaflet_L;
-
- try {
- const L = require('leaflet');
-
- // Set default icon paths
- delete L.Icon.Default.prototype._getIconUrl;
- L.Icon.Default.mergeOptions({
- iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',
- iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
- shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
- });
- } catch (e) {
- console.error('Failed to load Leaflet', e);
- }
- }
-
- setIsMounted(true);
-
- // Return cleanup function
- return () => {
- // Generate a new key if the component is unmounted and remounted
- setMapKey(Date.now());
- };
- }, []);
-
// Prepare countries with coordinates
const countriesWithCoordinates = countries.filter(country => {
return COUNTRY_COORDINATES[country.name] !== undefined;
@@ -174,79 +119,13 @@ export function ObservatoryMap({
// Sort countries by count (higher count = shows on top)
const sortedCountries = [...countriesWithCoordinates].sort((a, b) => b.count - a.count);
- // Get custom icon based on count
- const getMarkerIcon = (count: number, isSelected: boolean) => {
- if (typeof window === 'undefined') return null;
-
- try {
- const L = require('leaflet');
- const size = Math.min(Math.max(20, count * 5), 40);
-
- return L.divIcon({
- html: `${count}
`,
- className: '',
- iconSize: [size, size],
- iconAnchor: [size/2, size/2]
- });
- } catch (e) {
- console.error('Failed to create icon', e);
- return null;
- }
- };
-
- if (!isMounted) {
- return (
-
- );
- }
-
return (
-
-
-
-
- {sortedCountries.map((country) => (
- onCountrySelect(country.name)
- }}
- >
-
-
-
{country.name}
-
{country.count} news articles
-
-
-
- ))}
-
+
);
}
\ No newline at end of file
diff --git a/components/observatory/observatory-view.tsx b/components/observatory/observatory-view.tsx
index 333b52b5..8f82a777 100644
--- a/components/observatory/observatory-view.tsx
+++ b/components/observatory/observatory-view.tsx
@@ -23,6 +23,12 @@ export function ObservatoryView() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [selectedCountry, setSelectedCountry] = useState(null);
+ const [isBrowser, setIsBrowser] = useState(false);
+
+ // Check if we're in the browser
+ useEffect(() => {
+ setIsBrowser(true);
+ }, []);
// Fetch news data
const fetchNews = async () => {
@@ -186,7 +192,7 @@ export function ObservatoryView() {
- {!loading && (
+ {!loading && isBrowser && (
({