187 lines
5.5 KiB
JavaScript
187 lines
5.5 KiB
JavaScript
// Constants for carbon calculations
|
|
const EMISSION_FACTOR = 3.206; // tons of CO₂ per ton of fuel
|
|
const FUEL_DENSITY = 0.85; // tons per m³ (or metric tons per kiloliter)
|
|
const GALLONS_TO_LITERS = 3.78541; // 1 US gallon = 3.78541 liters
|
|
const LITERS_TO_CUBIC_METERS = 0.001; // 1 liter = 0.001 m³
|
|
|
|
// Available currencies
|
|
const currencies = {
|
|
USD: { code: 'USD', symbol: '$', rate: 1 },
|
|
EUR: { code: 'EUR', symbol: '€', rate: 0.92 },
|
|
GBP: { code: 'GBP', symbol: '£', rate: 0.79 },
|
|
CHF: { code: 'CHF', symbol: 'CHF', rate: 0.88 },
|
|
};
|
|
|
|
// Format currency
|
|
function formatCurrency(amountUSD, currency) {
|
|
if (!currency) {
|
|
currency = currencies.USD;
|
|
}
|
|
|
|
// Convert USD amount to target currency
|
|
const convertedAmount = amountUSD * currency.rate;
|
|
|
|
return `${currency.symbol}${convertedAmount.toLocaleString(undefined, {
|
|
minimumFractionDigits: 0,
|
|
maximumFractionDigits: 0,
|
|
})}`;
|
|
}
|
|
|
|
// Get currency by code
|
|
function getCurrencyByCode(code) {
|
|
return currencies[code] || currencies.USD;
|
|
}
|
|
|
|
// Calculate trip carbon
|
|
function calculateTripCarbon(vesselData, distance, speed, fuelRateLitersPerHour) {
|
|
const tripHours = distance / speed;
|
|
|
|
// Calculate total fuel consumption in liters
|
|
const fuelConsumptionLiters = fuelRateLitersPerHour * tripHours;
|
|
|
|
// Convert liters to tons for CO₂ calculation
|
|
const fuelConsumptionTons = (fuelConsumptionLiters * LITERS_TO_CUBIC_METERS) * FUEL_DENSITY;
|
|
|
|
// Calculate CO₂ emissions
|
|
const fuelRateTonsPerHour = (fuelRateLitersPerHour * LITERS_TO_CUBIC_METERS) * FUEL_DENSITY;
|
|
const emissionsPerNM = (fuelRateTonsPerHour * EMISSION_FACTOR) / speed;
|
|
const totalEmissions = emissionsPerNM * distance;
|
|
|
|
return {
|
|
distance,
|
|
duration: tripHours,
|
|
fuelConsumption: Math.round(fuelConsumptionLiters),
|
|
co2Emissions: Number(totalEmissions.toFixed(2))
|
|
};
|
|
}
|
|
|
|
// Calculate carbon from fuel
|
|
function calculateCarbonFromFuel(fuelAmount, isGallons = false) {
|
|
// Convert to liters if input is in gallons
|
|
const liters = isGallons ? fuelAmount * GALLONS_TO_LITERS : fuelAmount;
|
|
|
|
// Convert liters to cubic meters (m³)
|
|
const cubicMeters = liters * LITERS_TO_CUBIC_METERS;
|
|
|
|
// Convert volume to mass (tons)
|
|
const fuelTons = cubicMeters * FUEL_DENSITY;
|
|
|
|
// Calculate CO₂ emissions
|
|
const co2Emissions = fuelTons * EMISSION_FACTOR;
|
|
|
|
return Number(co2Emissions.toFixed(2));
|
|
}
|
|
|
|
// Sample vessel data
|
|
const sampleVessel = {
|
|
imo: '1234567',
|
|
vesselName: 'Sample Yacht',
|
|
type: 'Yacht',
|
|
length: 50,
|
|
width: 9,
|
|
estimatedEnginePower: 2250
|
|
};
|
|
|
|
// Default portfolio for fallback
|
|
const DEFAULT_PORTFOLIO = {
|
|
id: 1,
|
|
name: "Puffin Maritime Portfolio",
|
|
description: "A curated selection of high-impact carbon removal projects focused on maritime sustainability.",
|
|
projects: [
|
|
{
|
|
id: "dac-1",
|
|
name: "Direct Air Capture",
|
|
description: "Permanent carbon removal through direct air capture technology",
|
|
shortDescription: "Direct air capture in Iceland",
|
|
imageUrl: "https://images.unsplash.com/photo-1553547274-0df401ae03c9",
|
|
pricePerTon: 200,
|
|
location: "Iceland",
|
|
type: "Direct Air Capture",
|
|
verificationStandard: "Verified Carbon Standard",
|
|
impactMetrics: {
|
|
co2Reduced: 1000
|
|
}
|
|
},
|
|
{
|
|
id: "ocean-1",
|
|
name: "Ocean Carbon Removal",
|
|
description: "Enhanced ocean carbon capture through marine permaculture",
|
|
shortDescription: "Marine carbon capture",
|
|
imageUrl: "https://images.unsplash.com/photo-1498623116890-37e912163d5d",
|
|
pricePerTon: 200,
|
|
location: "Global Oceans",
|
|
type: "Ocean-Based",
|
|
verificationStandard: "Gold Standard",
|
|
impactMetrics: {
|
|
co2Reduced: 5000
|
|
}
|
|
}
|
|
],
|
|
pricePerTon: 200,
|
|
currency: 'USD'
|
|
};
|
|
|
|
// Simplified analytics
|
|
const analytics = {
|
|
pageView: function(path) {
|
|
console.log(`Analytics pageView: ${path}`);
|
|
},
|
|
event: function(category, action, label) {
|
|
console.log(`Analytics event: ${category} - ${action} - ${label}`);
|
|
},
|
|
error: function(error, message) {
|
|
console.error(`Analytics error: ${message}`, error);
|
|
}
|
|
};
|
|
|
|
// Create portfolio API call (with fallback to default)
|
|
async function getPortfolios() {
|
|
try {
|
|
// In a real implementation, this would be an API call
|
|
// For demo purposes, we'll just return the default portfolio after a small delay
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => resolve([DEFAULT_PORTFOLIO]), 1000);
|
|
});
|
|
} catch (error) {
|
|
console.warn('Failed to fetch portfolios from API, using fallback');
|
|
return [DEFAULT_PORTFOLIO];
|
|
}
|
|
}
|
|
|
|
// Create offset order API call (simulated)
|
|
async function createOffsetOrder(portfolioId, tons, dryRun = false) {
|
|
try {
|
|
// Simulate API call
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => {
|
|
resolve({
|
|
id: `order-${Math.floor(Math.random() * 1000000)}`,
|
|
amountCharged: tons * 200 * 100, // in cents
|
|
currency: 'USD',
|
|
tons: tons,
|
|
portfolio: DEFAULT_PORTFOLIO,
|
|
status: 'completed',
|
|
createdAt: new Date().toISOString(),
|
|
dryRun: dryRun
|
|
});
|
|
}, 1500);
|
|
});
|
|
} catch (error) {
|
|
console.error('Failed to create offset order', error);
|
|
throw new Error('Failed to create offset order. Please try again.');
|
|
}
|
|
}
|
|
|
|
// Email validation
|
|
function validateEmail(email) {
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
return emailRegex.test(email);
|
|
}
|
|
|
|
// Send form submission (simulated)
|
|
async function sendFormSubmission(data) {
|
|
return new Promise((resolve) => {
|
|
setTimeout(() => resolve({ success: true }), 1000);
|
|
});
|
|
}
|