2025-10-31 22:30:37 +01:00
|
|
|
import { useState, useEffect } from 'react';
|
2025-06-03 16:49:59 +02:00
|
|
|
import { Bird, Menu, X } from 'lucide-react';
|
|
|
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
|
|
|
import { Home } from './components/Home';
|
|
|
|
|
import { TripCalculator } from './components/TripCalculator';
|
|
|
|
|
import { HowItWorks } from './components/HowItWorks';
|
|
|
|
|
import { About } from './components/About';
|
|
|
|
|
import { Contact } from './components/Contact';
|
|
|
|
|
import { OffsetOrder } from './components/OffsetOrder';
|
|
|
|
|
import { analytics } from './utils/analytics';
|
2025-11-02 13:03:35 +01:00
|
|
|
import type { VesselData, CalculatorType } from './types';
|
2025-06-03 16:49:59 +02:00
|
|
|
|
|
|
|
|
const sampleVessel: VesselData = {
|
|
|
|
|
imo: "1234567",
|
|
|
|
|
vesselName: "Sample Yacht",
|
|
|
|
|
type: "Yacht",
|
|
|
|
|
length: 50,
|
|
|
|
|
width: 9,
|
|
|
|
|
estimatedEnginePower: 2250
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function App() {
|
|
|
|
|
const [currentPage, setCurrentPage] = useState<'home' | 'calculator' | 'how-it-works' | 'about' | 'contact'>('home');
|
|
|
|
|
const [showOffsetOrder, setShowOffsetOrder] = useState(false);
|
|
|
|
|
const [offsetTons, setOffsetTons] = useState(0);
|
|
|
|
|
const [monetaryAmount, setMonetaryAmount] = useState<number | undefined>();
|
|
|
|
|
const [calculatorType, setCalculatorType] = useState<CalculatorType>('trip');
|
|
|
|
|
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
analytics.pageView(window.location.pathname);
|
|
|
|
|
}, [currentPage]);
|
|
|
|
|
|
|
|
|
|
const handleOffsetClick = (tons: number, monetaryAmount?: number) => {
|
|
|
|
|
setOffsetTons(tons);
|
|
|
|
|
setMonetaryAmount(monetaryAmount);
|
|
|
|
|
setShowOffsetOrder(true);
|
|
|
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleNavigate = (page: 'home' | 'calculator' | 'how-it-works' | 'about' | 'contact') => {
|
|
|
|
|
setCurrentPage(page);
|
|
|
|
|
setMobileMenuOpen(false);
|
|
|
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const renderPage = () => {
|
|
|
|
|
if (currentPage === 'calculator' && showOffsetOrder) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex justify-center px-4 sm:px-0">
|
|
|
|
|
<OffsetOrder
|
|
|
|
|
tons={offsetTons}
|
|
|
|
|
monetaryAmount={monetaryAmount}
|
|
|
|
|
onBack={() => {
|
|
|
|
|
setShowOffsetOrder(false);
|
|
|
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
|
|
|
}}
|
|
|
|
|
calculatorType={calculatorType}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (currentPage) {
|
|
|
|
|
case 'calculator':
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex flex-col items-center px-4 sm:px-6">
|
|
|
|
|
<div className="text-center mb-12 max-w-2xl">
|
|
|
|
|
<h2 className="text-3xl sm:text-4xl font-bold text-gray-900 mb-4">
|
|
|
|
|
Calculate & Offset Your Yacht's Carbon Footprint
|
|
|
|
|
</h2>
|
|
|
|
|
<p className="text-base sm:text-lg text-gray-600">
|
|
|
|
|
Use the calculator below to estimate your carbon footprint and explore offsetting options through our verified projects.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="flex flex-col items-center w-full max-w-2xl space-y-8">
|
|
|
|
|
<TripCalculator
|
|
|
|
|
vesselData={sampleVessel}
|
|
|
|
|
onOffsetClick={handleOffsetClick}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
case 'how-it-works':
|
|
|
|
|
return <HowItWorks onNavigate={handleNavigate} />;
|
|
|
|
|
case 'about':
|
|
|
|
|
return <About onNavigate={handleNavigate} />;
|
|
|
|
|
case 'contact':
|
|
|
|
|
return <Contact />;
|
|
|
|
|
default:
|
|
|
|
|
return <Home onNavigate={handleNavigate} />;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="min-h-screen bg-gradient-to-b from-blue-50 to-green-50">
|
|
|
|
|
<header className="bg-white shadow-sm relative">
|
|
|
|
|
<div className="max-w-7xl mx-auto px-4 py-4 sm:px-6 lg:px-8">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
<div
|
|
|
|
|
className="flex items-center space-x-2 cursor-pointer"
|
|
|
|
|
onClick={() => handleNavigate('home')}
|
|
|
|
|
>
|
|
|
|
|
<Bird className="text-blue-600" size={24} />
|
|
|
|
|
<h1 className="text-xl font-bold text-gray-900">Puffin Offset</h1>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Mobile menu button */}
|
|
|
|
|
<button
|
|
|
|
|
className="sm:hidden p-2 rounded-md text-gray-600 hover:text-gray-900"
|
|
|
|
|
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
|
|
|
|
>
|
|
|
|
|
{mobileMenuOpen ? <X size={24} /> : <Menu size={24} />}
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
{/* Desktop navigation */}
|
|
|
|
|
<nav className="hidden sm:flex space-x-4">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleNavigate('calculator')}
|
|
|
|
|
className={`text-gray-600 hover:text-gray-900 ${currentPage === 'calculator' ? 'font-semibold' : ''}`}
|
|
|
|
|
>
|
|
|
|
|
Calculator
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleNavigate('how-it-works')}
|
|
|
|
|
className={`text-gray-600 hover:text-gray-900 ${currentPage === 'how-it-works' ? 'font-semibold' : ''}`}
|
|
|
|
|
>
|
|
|
|
|
How it Works
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleNavigate('about')}
|
|
|
|
|
className={`text-gray-600 hover:text-gray-900 ${currentPage === 'about' ? 'font-semibold' : ''}`}
|
|
|
|
|
>
|
|
|
|
|
About
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleNavigate('contact')}
|
|
|
|
|
className={`text-gray-600 hover:text-gray-900 ${currentPage === 'contact' ? 'font-semibold' : ''}`}
|
|
|
|
|
>
|
|
|
|
|
Contact
|
|
|
|
|
</button>
|
|
|
|
|
</nav>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Mobile navigation */}
|
|
|
|
|
{mobileMenuOpen && (
|
|
|
|
|
<nav className="sm:hidden mt-4 pb-2 space-y-2">
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleNavigate('calculator')}
|
|
|
|
|
className={`block w-full text-left px-4 py-2 rounded-lg ${
|
|
|
|
|
currentPage === 'calculator'
|
|
|
|
|
? 'bg-blue-50 text-blue-600 font-semibold'
|
|
|
|
|
: 'text-gray-600 hover:bg-gray-50'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
Calculator
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleNavigate('how-it-works')}
|
|
|
|
|
className={`block w-full text-left px-4 py-2 rounded-lg ${
|
|
|
|
|
currentPage === 'how-it-works'
|
|
|
|
|
? 'bg-blue-50 text-blue-600 font-semibold'
|
|
|
|
|
: 'text-gray-600 hover:bg-gray-50'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
How it Works
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleNavigate('about')}
|
|
|
|
|
className={`block w-full text-left px-4 py-2 rounded-lg ${
|
|
|
|
|
currentPage === 'about'
|
|
|
|
|
? 'bg-blue-50 text-blue-600 font-semibold'
|
|
|
|
|
: 'text-gray-600 hover:bg-gray-50'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
About
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => handleNavigate('contact')}
|
|
|
|
|
className={`block w-full text-left px-4 py-2 rounded-lg ${
|
|
|
|
|
currentPage === 'contact'
|
|
|
|
|
? 'bg-blue-50 text-blue-600 font-semibold'
|
|
|
|
|
: 'text-gray-600 hover:bg-gray-50'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
Contact
|
|
|
|
|
</button>
|
|
|
|
|
</nav>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<main className="max-w-7xl mx-auto py-8 sm:py-12 overflow-hidden">
|
|
|
|
|
<AnimatePresence mode="wait">
|
|
|
|
|
<motion.div
|
|
|
|
|
key={currentPage + (showOffsetOrder ? '-offset' : '')}
|
|
|
|
|
initial={{ opacity: 0, y: 20 }}
|
|
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
|
|
|
exit={{ opacity: 0, y: -20 }}
|
|
|
|
|
transition={{
|
|
|
|
|
duration: 0.4,
|
|
|
|
|
ease: [0.25, 0.1, 0.25, 1.0]
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{renderPage()}
|
|
|
|
|
</motion.div>
|
|
|
|
|
</AnimatePresence>
|
|
|
|
|
</main>
|
|
|
|
|
|
|
|
|
|
<footer className="bg-white mt-16">
|
|
|
|
|
<div className="max-w-7xl mx-auto px-4 py-8 sm:px-6 lg:px-8">
|
|
|
|
|
<p className="text-center text-gray-500">
|
|
|
|
|
Powered by Wren Carbon Offset Projects
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</footer>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default App;
|