import React, { useState, useEffect } from 'react'; import { Menu, X } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import { Home } from './components/Home'; import { YachtSearch } from './components/YachtSearch'; import { TripCalculator } from './components/TripCalculator'; import { MobileCalculator } from './components/MobileCalculator'; import { HowItWorks } from './components/HowItWorks'; import { About } from './components/About'; import { Contact } from './components/Contact'; import { OffsetOrder } from './components/OffsetOrder'; import CheckoutSuccess from './pages/CheckoutSuccess'; import CheckoutCancel from './pages/CheckoutCancel'; import { getVesselData } from './api/aisClient'; import { calculateTripCarbon } from './utils/carbonCalculator'; import { analytics } from './utils/analytics'; import { useCalculatorState } from './hooks/useCalculatorState'; import type { VesselData, CarbonCalculation, CalculatorType } from './types'; const sampleVessel: VesselData = { imo: "1234567", vesselName: "Sample Yacht", type: "Yacht", length: 50, width: 9, estimatedEnginePower: 2250 }; function App() { const { state: savedState, saveState } = useCalculatorState(); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [vesselData, setVesselData] = useState(null); const [currentPage, setCurrentPage] = useState<'home' | 'calculator' | 'how-it-works' | 'about' | 'contact'>('home'); const [showOffsetOrder, setShowOffsetOrder] = useState(savedState?.showOffsetOrder || false); const [offsetTons, setOffsetTons] = useState(savedState?.offsetTons || 0); const [monetaryAmount, setMonetaryAmount] = useState(savedState?.monetaryAmount); const [calculatorType, setCalculatorType] = useState(savedState?.calculatorTypeUsed || 'trip'); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); const [isMobileApp, setIsMobileApp] = useState(false); const [isCheckoutSuccess, setIsCheckoutSuccess] = useState(false); const [isCheckoutCancel, setIsCheckoutCancel] = useState(false); const [showHeader, setShowHeader] = useState(true); const [lastScrollY, setLastScrollY] = useState(0); useEffect(() => { // Check if we're on special routes const path = window.location.pathname; setIsMobileApp(path === '/mobile-app'); setIsCheckoutSuccess(path === '/checkout/success'); setIsCheckoutCancel(path === '/checkout/cancel'); analytics.pageView(path); }, [currentPage]); useEffect(() => { // Handle URL changes (for back/forward navigation) const handlePopState = () => { const path = window.location.pathname; setIsMobileApp(path === '/mobile-app'); setIsCheckoutSuccess(path === '/checkout/success'); setIsCheckoutCancel(path === '/checkout/cancel'); }; window.addEventListener('popstate', handlePopState); return () => window.removeEventListener('popstate', handlePopState); }, []); useEffect(() => { // Hide header on scroll down, show on scroll up const handleScroll = () => { const currentScrollY = window.scrollY; // Always show header at the top of the page if (currentScrollY < 10) { setShowHeader(true); } else if (currentScrollY > lastScrollY) { // Scrolling down setShowHeader(false); } else { // Scrolling up setShowHeader(true); } setLastScrollY(currentScrollY); }; window.addEventListener('scroll', handleScroll, { passive: true }); return () => window.removeEventListener('scroll', handleScroll); }, [lastScrollY]); const handleSearch = async (imo: string) => { setLoading(true); setError(null); setVesselData(null); try { const vessel = await getVesselData(imo); setVesselData(vessel); } catch (err) { setError('Unable to fetch vessel data. Please verify the IMO number and try again.'); } finally { setLoading(false); } }; const handleOffsetClick = (tons: number, monetaryAmount?: number) => { setOffsetTons(tons); setMonetaryAmount(monetaryAmount); setShowOffsetOrder(true); // Save offset state to localStorage for browser back navigation saveState({ showOffsetOrder: true, offsetTons: tons, monetaryAmount, calculatorTypeUsed: calculatorType, }); window.scrollTo({ top: 0, behavior: 'smooth' }); }; const handleNavigate = (page: 'home' | 'calculator' | 'how-it-works' | 'about' | 'contact') => { setCurrentPage(page); setMobileMenuOpen(false); setIsMobileApp(false); setIsCheckoutSuccess(false); // Clear checkout flags when navigating setIsCheckoutCancel(false); // Clear checkout flags when navigating window.history.pushState({}, '', `/${page === 'home' ? '' : page}`); window.scrollTo({ top: 0, behavior: 'smooth' }); }; const handleBackFromMobile = () => { setIsMobileApp(false); window.history.pushState({}, '', '/'); setCurrentPage('home'); }; const renderPage = () => { if (currentPage === 'calculator' && showOffsetOrder) { return (
{ setShowOffsetOrder(false); // Clear offset state from localStorage when going back saveState({ showOffsetOrder: false, offsetTons: 0, monetaryAmount: undefined, }); window.scrollTo({ top: 0, behavior: 'smooth' }); }} calculatorType={calculatorType} />
); } switch (currentPage) { case 'calculator': return (

Calculate & Offset Your Yacht's Carbon Footprint

Use the calculator below to estimate your carbon footprint and explore offsetting options through our verified projects.

); case 'how-it-works': return ; case 'about': return ; case 'contact': return ; default: return ; } }; // If we're on the mobile app route, render only the mobile calculator if (isMobileApp) { return ( ); } // If we're on the checkout success route, render only the success page if (isCheckoutSuccess) { return ( handleNavigate('home')} onNavigateCalculator={() => handleNavigate('calculator')} /> ); } // If we're on the checkout cancel route, render only the cancel page if (isCheckoutCancel) { return ( handleNavigate('home')} onNavigateCalculator={() => handleNavigate('calculator')} /> ); } return (
handleNavigate('home')} whileHover={{ scale: 1.02 }} transition={{ type: "spring", stiffness: 400, damping: 17 }} > Puffin Offset {/* Mobile menu button */} {/* Desktop navigation */}
{/* Mobile navigation */} {mobileMenuOpen && ( )}
{renderPage()}
); } export default App;