"use client"; import { useEffect, useState } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { RefreshCw, Globe } from "lucide-react"; import { Button } from "@/components/ui/button"; import { ObservatoryMap } from "./observatory-map"; import { toast } from "@/components/ui/use-toast"; // News item interface matching the API response interface NewsItem { id: number; title: string; displayDate: string; timestamp: string; source: string; description: string | null; category: string | null; url: string; } export function ObservatoryView() { const [news, setNews] = useState([]); const [accumulatedNews, setAccumulatedNews] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [selectedCountry, setSelectedCountry] = useState(null); const [isBrowser, setIsBrowser] = useState(false); const [useAccumulatedNews, setUseAccumulatedNews] = useState(true); // Check if we're in the browser useEffect(() => { setIsBrowser(true); }, []); // Fetch news data const fetchNews = async (forceRefresh = false) => { setLoading(true); try { console.log('Requesting news with limit=100...'); const url = forceRefresh ? '/api/news?limit=100&refresh=true' : '/api/news?limit=100'; console.log(`Fetching from: ${url}`); const response = await fetch(url); if (!response.ok) { throw new Error('Failed to fetch news'); } const data = await response.json(); console.log(`Observatory received ${data.length} news articles`); // Log first 5 articles for debugging if (data.length > 0) { console.log('First 5 articles:', data.slice(0, 5)); } else { console.log('No articles received from API'); } setNews(data); // Update accumulated news by adding new unique articles setAccumulatedNews(prev => { // Use a map to keep track of unique articles by ID const uniqueMap = new Map(); // Add existing accumulated articles to the map prev.forEach(item => uniqueMap.set(item.id, item)); // Add new articles to the map (will overwrite if ID already exists) data.forEach((item: NewsItem) => uniqueMap.set(item.id, item)); // Convert map values back to an array const newAccumulated = Array.from(uniqueMap.values()); // Sort by timestamp (newest first) newAccumulated.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() ); console.log(`Accumulated ${newAccumulated.length} unique articles`); return newAccumulated; }); setError(null); } catch (err) { setError('Failed to fetch news'); console.error('Error fetching news:', err); } finally { setLoading(false); } }; // Fetch news on component mount useEffect(() => { fetchNews(); }, []); // Extract countries from news data (simplified version) const extractCountries = (newsItems: NewsItem[]) => { // This is a simplified implementation // In a real app, we would use NLP or a more sophisticated technique const countries = [ 'France', 'USA', 'Canada', 'UK', 'Germany', 'Japan', 'China', 'India', 'Brazil', 'Australia', 'Russia', 'Italy', 'Spain', 'Sudan', 'New York', 'United Nations', 'Ukraine', 'Egypt', 'Mexico', 'South Africa', 'Nigeria', 'Argentina', 'Pakistan', 'Indonesia', 'Saudi Arabia', 'Iran', 'Turkey', 'South Korea', 'Thailand', 'Vietnam', 'Philippines', 'Malaysia', 'Singapore', 'Israel', 'Palestine', 'Syria', 'Iraq', 'Afghanistan', 'Morocco', 'Algeria', 'Tunisia', 'Kenya', 'Ethiopia', 'Greece', 'Poland', 'Sweden', 'Norway', 'Denmark', 'Finland', 'Netherlands', 'Belgium', 'Portugal', 'Switzerland', 'Austria' ]; // Sort countries by length (to prioritize longer names) const sortedCountries = [...countries].sort((a, b) => b.length - a.length); const result: Record = {}; newsItems.forEach(item => { // For title and description const titleAndDesc = [ item.title || '', item.description || '' ].join(' ').toLowerCase(); // Check each country sortedCountries.forEach(country => { if (titleAndDesc.includes(country.toLowerCase())) { if (!result[country]) { result[country] = []; } // Only add once per item if (!result[country].some(existingItem => existingItem.id === item.id)) { result[country].push(item); } } }); }); return result; }; // Handle country selection on the map const handleCountrySelect = (country: string) => { setSelectedCountry(selectedCountry === country ? null : country); }; // Get news filtered by selected country const getFilteredNews = () => { // Use either accumulated or direct news based on user preference const sourceNews = useAccumulatedNews ? accumulatedNews : news; if (!selectedCountry) return sourceNews; const countriesMap = extractCountries(sourceNews); const filtered = countriesMap[selectedCountry] || []; console.log(`Filtered news for ${selectedCountry}: ${filtered.length} of ${sourceNews.length} total articles`); return filtered; }; // Invalidate cache const invalidateCache = async () => { try { const response = await fetch('/api/news/purge-cache', { method: 'POST' }); if (!response.ok) { throw new Error('Failed to invalidate cache'); } toast({ title: "Cache purged", description: "News cache has been invalidated. Fetching fresh data...", }); // Fetch fresh data fetchNews(true); } catch (err) { console.error('Error invalidating cache:', err); toast({ title: "Error", description: "Failed to invalidate cache", variant: "destructive" }); } }; // Loading state if (loading) { return (
); } // Error state if (error) { return (

{error}

); } const filteredNews = getFilteredNews(); const countriesMap = extractCountries(news); return (
{/* Main Content */}
{/* News Feed Section */}

Latest News ({filteredNews.length} articles) {news.length < 10 && ( (API limitation) )}

{filteredNews.length === 0 ? (

{selectedCountry ? `No news found for ${selectedCountry}` : 'No news found'}

) : ( )}
{/* Map Section */}

World Map {selectedCountry && ( (Selected: {selectedCountry}) )}

{isBrowser && ( ({ name, count: items.length }))} onCountrySelect={handleCountrySelect} selectedCountry={selectedCountry} /> )}
); }