Implement QR code auto-fill and auto-navigation for calculator
Some checks failed
Build and Push Docker Images / docker (push) Failing after 2m13s
Some checks failed
Build and Push Docker Images / docker (push) Failing after 2m13s
- Update useQRDecoder hook to use Next.js useSearchParams() for SSR compatibility - Add auto-navigation logic to TripCalculator that automatically calculates and navigates to offset page when QR data is present - Add QR detection to CalculatorClient to conditionally render QRCalculatorLoader vs TripCalculator - Support all three calculation types: fuel, distance, and custom amount - Add 500ms delay before navigation to allow UI to render This completes the QR code flow where scanning a QR code now automatically: 1. Pre-fills calculator form with data 2. Calculates carbon footprint 3. Navigates to offset purchase page 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e76a650d4e
commit
f57ceb7b8f
@ -2,7 +2,9 @@
|
||||
|
||||
import { useState } from 'react';
|
||||
import { TripCalculator } from '../src/components/TripCalculator';
|
||||
import { QRCalculatorLoader } from '../src/components/QRCalculatorLoader';
|
||||
import { OffsetOrder } from '../src/components/OffsetOrder';
|
||||
import { useHasQRData } from '../src/hooks/useQRDecoder';
|
||||
import type { VesselData } from '../src/types';
|
||||
|
||||
// Sample vessel data (same as in old App.tsx)
|
||||
@ -16,6 +18,7 @@ const sampleVessel: VesselData = {
|
||||
};
|
||||
|
||||
export function CalculatorClient() {
|
||||
const hasQRData = useHasQRData(); // Check if URL contains QR code data
|
||||
const [showOffsetOrder, setShowOffsetOrder] = useState(false);
|
||||
const [offsetTons, setOffsetTons] = useState(0);
|
||||
const [monetaryAmount, setMonetaryAmount] = useState<number | undefined>();
|
||||
@ -59,10 +62,17 @@ export function CalculatorClient() {
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center w-full max-w-4xl space-y-8">
|
||||
<TripCalculator
|
||||
vesselData={sampleVessel}
|
||||
onOffsetClick={handleOffsetClick}
|
||||
/>
|
||||
{hasQRData ? (
|
||||
<QRCalculatorLoader
|
||||
vesselData={sampleVessel}
|
||||
onOffsetClick={handleOffsetClick}
|
||||
/>
|
||||
) : (
|
||||
<TripCalculator
|
||||
vesselData={sampleVessel}
|
||||
onOffsetClick={handleOffsetClick}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useCallback, useEffect } from 'react';
|
||||
import { Leaf, Droplet } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
@ -75,6 +77,37 @@ export function TripCalculator({ vesselData, onOffsetClick, initialData }: Props
|
||||
initialData?.customAmount?.toString() || savedState?.customAmount || ''
|
||||
);
|
||||
|
||||
// Auto-navigate when QR data is present
|
||||
useEffect(() => {
|
||||
if (initialData && onOffsetClick) {
|
||||
// Automatically calculate and navigate based on calculation type
|
||||
if (initialData.calculationType === 'distance' && initialData.distance && initialData.speed && initialData.fuelRate) {
|
||||
const estimate = calculateTripCarbon(
|
||||
initialData.distance,
|
||||
initialData.speed,
|
||||
initialData.fuelRate
|
||||
);
|
||||
// Small delay to allow UI to render before navigating
|
||||
setTimeout(() => {
|
||||
onOffsetClick(estimate.co2Emissions);
|
||||
}, 500);
|
||||
} else if (initialData.calculationType === 'fuel' && initialData.fuelAmount && initialData.fuelUnit) {
|
||||
const co2Emissions = calculateCarbonFromFuel(
|
||||
initialData.fuelAmount,
|
||||
initialData.fuelUnit === 'gallons'
|
||||
);
|
||||
setTimeout(() => {
|
||||
onOffsetClick(co2Emissions);
|
||||
}, 500);
|
||||
} else if (initialData.calculationType === 'custom' && initialData.customAmount) {
|
||||
// For custom amount, pass the monetary value directly
|
||||
setTimeout(() => {
|
||||
onOffsetClick(0, initialData.customAmount);
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
}, [initialData, onOffsetClick]);
|
||||
|
||||
const handleCalculate = useCallback((e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
|
||||
@ -3,7 +3,10 @@
|
||||
* Custom React hook for decoding QR calculator data from URL parameters
|
||||
*/
|
||||
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { QRCalculatorData } from '../types';
|
||||
import { extractQRDataFromUrl } from '../utils/qrDataEncoder';
|
||||
import { validateQRData } from '../utils/qrDataValidator';
|
||||
@ -49,6 +52,7 @@ export interface QRDecoderResult {
|
||||
* ```
|
||||
*/
|
||||
export function useQRDecoder(): QRDecoderResult {
|
||||
const searchParams = useSearchParams();
|
||||
const [data, setData] = useState<QRCalculatorData | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@ -57,11 +61,8 @@ export function useQRDecoder(): QRDecoderResult {
|
||||
useEffect(() => {
|
||||
const decodeQRData = () => {
|
||||
try {
|
||||
// Get URL search params
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
|
||||
// Check if QR parameter exists
|
||||
const qrParam = searchParams.get('qr');
|
||||
const qrParam = searchParams?.get('qr');
|
||||
if (!qrParam) {
|
||||
setHasQRData(false);
|
||||
setIsLoading(false);
|
||||
@ -71,7 +72,9 @@ export function useQRDecoder(): QRDecoderResult {
|
||||
setHasQRData(true);
|
||||
|
||||
// Extract and decode QR data
|
||||
const decodedData = extractQRDataFromUrl(searchParams);
|
||||
// Need to convert ReadonlyURLSearchParams to URLSearchParams for compatibility
|
||||
const urlSearchParams = new URLSearchParams(searchParams?.toString());
|
||||
const decodedData = extractQRDataFromUrl(urlSearchParams);
|
||||
|
||||
if (!decodedData) {
|
||||
setError('Failed to decode QR data');
|
||||
@ -100,7 +103,7 @@ export function useQRDecoder(): QRDecoderResult {
|
||||
};
|
||||
|
||||
decodeQRData();
|
||||
}, []); // Run only once on mount
|
||||
}, [searchParams]); // Re-run when search params change
|
||||
|
||||
return {
|
||||
data,
|
||||
@ -128,12 +131,12 @@ export function useQRDecoder(): QRDecoderResult {
|
||||
* ```
|
||||
*/
|
||||
export function useHasQRData(): boolean {
|
||||
const searchParams = useSearchParams();
|
||||
const [hasQRData, setHasQRData] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
setHasQRData(searchParams.has('qr'));
|
||||
}, []);
|
||||
setHasQRData(searchParams?.has('qr') || false);
|
||||
}, [searchParams]);
|
||||
|
||||
return hasQRData;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user