import { useCallback, useEffect, useMemo, useState } from 'react'; import { HeartPulse, Moon, Sun } from 'lucide-react'; import { fetchClimaCuidaData } from './api/openMeteo'; import { ErrorBanner } from './components/ErrorBanner'; import { Forecast7Days } from './components/Forecast7Days'; import { MetricsGrid } from './components/MetricsGrid'; import { ProfileSelector } from './components/ProfileSelector'; import { RecommendationChips } from './components/RecommendationChips'; import { SearchBar } from './components/SearchBar'; import { SemaforoCard } from './components/SemaforoCard'; import { SkeletonDashboard } from './components/SkeletonDashboard'; import { Timeline12h } from './components/Timeline12h'; import { WhyPanel } from './components/WhyPanel'; import { MOCK_WEATHER, USE_PROFILES } from './data/mock'; import { calculateRiskScore } from './lib/riskScore'; import type { LocationOption, ProfileId, WeatherBundle } from './types'; const STORAGE_KEY = 'clima-cuida-preferences'; type ThemeMode = 'light' | 'dark'; interface StoredPreferences { profile?: ProfileId; location?: LocationOption; theme?: ThemeMode; } function loadPreferences(): StoredPreferences { try { const value = localStorage.getItem(STORAGE_KEY); return value ? (JSON.parse(value) as StoredPreferences) : {}; } catch { return {}; } } function savePreferences(preferences: StoredPreferences) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(preferences)); } catch { // Local storage can be blocked in private contexts; the app remains usable. } } export default function App() { const initialPreferences = useMemo(() => loadPreferences(), []); const [profile, setProfile] = useState(initialPreferences.profile ?? 'adult'); const [theme, setTheme] = useState(initialPreferences.theme ?? 'light'); const [activeLocation, setActiveLocation] = useState( initialPreferences.location ?? MOCK_WEATHER.location, ); const [weather, setWeather] = useState(MOCK_WEATHER); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const selectedProfile = USE_PROFILES.find((item) => item.id === profile) ?? USE_PROFILES[0]; const risk = useMemo(() => calculateRiskScore(weather.current, profile), [profile, weather.current]); const isDarkTheme = theme === 'dark'; const loadWeather = useCallback( async (location: LocationOption) => { setIsLoading(true); setError(null); try { const nextWeather = await fetchClimaCuidaData(location); setWeather(nextWeather); setActiveLocation(location); savePreferences({ profile, location, theme }); } catch { setWeather({ ...MOCK_WEATHER, fetchedAt: new Date().toISOString(), source: 'mock', }); setError('A API não respondeu agora. Exibindo dados de exemplo para manter a leitura do dia utilizável.'); } finally { setIsLoading(false); } }, [profile, theme], ); useEffect(() => { void loadWeather(activeLocation); }, []); useEffect(() => { document.documentElement.dataset.theme = theme; }, [theme]); useEffect(() => { savePreferences({ profile, location: activeLocation, theme }); }, [profile, activeLocation, theme]); function handleProfileChange(nextProfile: ProfileId) { setProfile(nextProfile); } function handleToggleTheme() { setTheme((currentTheme) => (currentTheme === 'light' ? 'dark' : 'light')); } function handleUseCurrentLocation() { if (!navigator.geolocation) { setError('Este navegador não oferece geolocalização. Busque uma cidade pelo nome.'); return; } setIsLoading(true); navigator.geolocation.getCurrentPosition( (position) => { const location: LocationOption = { id: 'browser-location', name: 'Sua localização', latitude: position.coords.latitude, longitude: position.coords.longitude, }; void loadWeather(location); }, () => { setIsLoading(false); setError('Não foi possível acessar sua localização. Você pode pesquisar uma cidade manualmente.'); }, { enableHighAccuracy: false, timeout: 9000, maximumAge: 10 * 60 * 1000 }, ); } return (
Clima Cuida
{error && loadWeather(activeLocation)} />}

Orientação geral, não recomendação médica

Leitura combinada de clima, UV e qualidade do ar para decidir saída, exercício, deslocamento e proteção diária.

{isLoading ? ( ) : (
)}
); }