"use client"; import { useEffect, useState } from "react"; import { api } from "@/lib/api"; import type { Destination, BlogPost, FAQ, Testimonial, Tool, Visa, Stats, ChartData } from "@/lib/types"; import Loader from "./Loader"; import ParticleCanvas from "./ParticleCanvas"; import Navbar from "./Navbar"; import TickerBar from "./TickerBar"; import Hero from "./Hero"; import WorldMap from "./WorldMap"; import Destinations from "./Destinations"; import Calculator from "./Calculator"; import TripPlanner from "./TripPlanner"; import GlobalSearch from "./GlobalSearch"; import Lifestyle from "./Lifestyle"; import Charts from "./Charts"; import VisaSection from "./VisaSection"; import Tools from "./Tools"; import BlogSection from "./BlogSection"; import FAQSection from "./FAQSection"; import Community from "./Community"; import Footer from "./Footer"; import CompareModal from "./CompareModal"; import DestinationMatcher, { MatcherTrigger } from "./DestinationMatcher"; import TimezoneBoard from "./TimezoneBoard"; import ScrollProgress from "./ScrollProgress"; import SectionNav from "./SectionNav"; import CurrencyConverter from "./CurrencyConverter"; import NomadScore from "./NomadScore"; import CostCompare from "./CostCompare"; import CookieConsent from "./CookieConsent"; import CoworkingGuide from "./CoworkingGuide"; import PackingChecklist from "./PackingChecklist"; import SeasonGuide from "./SeasonGuide"; import NomadTips from "./NomadTips"; import SpinGlobe from "./SpinGlobe"; import FlightCost from "./FlightCost"; import NomadEvents from "./NomadEvents"; import WifiWorkScore from "./WifiWorkScore"; import SavingsGoal from "./SavingsGoal"; import DailyQuote from "./DailyQuote"; import JetLagEstimator from "./JetLagEstimator"; import HousingGuide from "./HousingGuide"; import Phrasebook from "./Phrasebook"; import FeedbackWidget from "./FeedbackWidget"; import InsuranceGuide from "./InsuranceGuide"; import ExpenseTracker from "./ExpenseTracker"; import SimGuide from "./SimGuide"; import TaxDayCounter from "./TaxDayCounter"; import ArrivalChecklist from "./ArrivalChecklist"; import CafeGuide from "./CafeGuide"; import OnboardingTour from "./OnboardingTour"; import PowerPlugGuide from "./PowerPlugGuide"; import EmergencyContacts from "./EmergencyContacts"; import CityNotes from "./CityNotes"; import FocusTimer from "./FocusTimer"; import AtmCashGuide from "./AtmCashGuide"; import KeyboardShortcuts from "./KeyboardShortcuts"; import BackToTop from "./BackToTop"; interface Props { stats: Stats; ticker: string[]; destinations: Destination[]; visas: Visa[]; faqs: FAQ[]; testimonials: Testimonial[]; tools: Tool[]; blog: BlogPost[]; charts: { cost: ChartData; speed: ChartData; growth: ChartData; radar: ChartData }; } export default function HomeClient(props: Props) { const [compareList, setCompareList] = useState([]); const [compareResults, setCompareResults] = useState(null); const [compareOpen, setCompareOpen] = useState(false); const [matcherOpen, setMatcherOpen] = useState(false); useEffect(() => { const onMatcher = () => setMatcherOpen(true); window.addEventListener("open-matcher", onMatcher); return () => window.removeEventListener("open-matcher", onMatcher); }, []); useEffect(() => { const reveals = document.querySelectorAll(".reveal"); const observer = new IntersectionObserver( (entries) => entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("visible"); observer.unobserve(e.target); } }), { threshold: 0.1, rootMargin: "0px 0px -50px 0px" } ); reveals.forEach((el, i) => { (el as HTMLElement).style.transitionDelay = `${(i % 6) * 0.1}s`; observer.observe(el); }); return () => observer.disconnect(); }, []); const toggleCompare = (slug: string) => { setCompareList((prev) => { if (prev.includes(slug)) return prev.filter((s) => s !== slug); if (prev.length >= 4) return prev; return [...prev, slug]; }); setCompareResults(null); }; const runCompare = async () => { if (compareList.length < 2) return; try { const data = await api.compareDestinations(compareList); setCompareResults(data); setCompareOpen(true); } catch { /* ignore */ } }; return ( <> setMatcherOpen(true)} />