puffin-app/src/components/forms/FormFieldWrapper.tsx
Matt 06733cb2cb
All checks were successful
Build and Push Docker Image / docker (push) Successful in 42s
Integrate Stripe Checkout and add comprehensive UI enhancements
## Stripe Payment Integration
- Add Express.js backend server with Stripe Checkout Sessions
- Create SQLite database for order tracking
- Implement Stripe webhook handlers for payment events
- Integrate with Wren Climate API for carbon offset fulfillment
- Add CheckoutSuccess and CheckoutCancel pages
- Create checkout API client for frontend
- Update OffsetOrder component to redirect to Stripe Checkout
- Add processing fee calculation (3% of base amount)
- Implement order status tracking (pending → paid → fulfilled)

Backend (server/):
- Express server with CORS and middleware
- SQLite database with Order schema
- Stripe configuration and client
- Order CRUD operations model
- Checkout session creation endpoint
- Webhook handler for payment confirmation
- Wren API client for offset fulfillment

Frontend:
- CheckoutSuccess page with order details display
- CheckoutCancel page with retry encouragement
- Updated OffsetOrder to use Stripe checkout flow
- Added checkout routes to App.tsx
- TypeScript interfaces for checkout flow

## Visual & UX Enhancements
- Add CertificationBadge component for project verification status
- Create PortfolioDonutChart for visual portfolio allocation
- Implement RadialProgress for percentage displays
- Add reusable form components (FormInput, FormTextarea, FormSelect, FormFieldWrapper)
- Refactor OffsetOrder with improved layout and animations
- Add offset percentage slider with visual feedback
- Enhance MobileOffsetOrder with better responsive design
- Improve TripCalculator with cleaner UI structure
- Update CurrencySelect with better styling
- Add portfolio distribution visualization
- Enhance project cards with hover effects and animations
- Improve color palette and gradient usage throughout

## Configuration
- Add VITE_API_BASE_URL environment variable
- Create backend .env.example template
- Update frontend .env.example with API URL
- Add Stripe documentation references

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 21:45:14 +01:00

100 lines
2.5 KiB
TypeScript

import React, { ReactNode } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { AlertCircle } from 'lucide-react';
interface FormFieldWrapperProps {
id: string;
label: string;
icon?: ReactNode;
error?: string;
isFilled: boolean;
isFocused: boolean;
disabled?: boolean;
children: ReactNode;
}
export function FormFieldWrapper({
id,
label,
icon,
error,
isFilled,
isFocused,
disabled,
children,
}: FormFieldWrapperProps) {
const isFloated = isFocused || isFilled;
const hasError = !!error;
return (
<div className="w-full">
<div className="relative">
{/* Icon (if provided) */}
{icon && (
<div className="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none text-puffin-gray-label z-10">
{icon}
</div>
)}
{/* Floating Label */}
<motion.label
htmlFor={id}
className={`
absolute left-3 pointer-events-none origin-left z-10
transition-colors duration-200
${icon ? 'pl-7' : ''}
${hasError ? 'text-puffin-error' : isFloated ? 'text-puffin-blue-focus' : 'text-puffin-gray-label'}
${disabled ? 'opacity-50' : ''}
`}
initial={false}
animate={{
y: isFloated ? -24 : 12,
scale: isFloated ? 0.85 : 1,
}}
transition={{
type: 'tween',
ease: 'easeOut',
duration: 0.2,
}}
>
{label}
</motion.label>
{/* Input Container with Border & Focus Effect */}
<div
className={`
relative rounded-lg transition-all duration-200
${hasError
? isFocused
? 'shadow-focus-red'
: ''
: isFocused
? 'shadow-focus-blue'
: ''
}
`}
>
{children}
</div>
</div>
{/* Error Message */}
<AnimatePresence>
{hasError && (
<motion.div
id={`${id}-error`}
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.2 }}
className="flex items-center gap-1.5 mt-1.5 text-sm text-puffin-error"
>
<AlertCircle className="w-4 h-4 flex-shrink-0" />
<span>{error}</span>
</motion.div>
)}
</AnimatePresence>
</div>
);
}