- Create Gitea Actions workflow for automated Docker builds on push to main - Add docker-compose.portainer.yml for production Portainer deployment - Create comprehensive DEPLOYMENT.md guide with step-by-step instructions - Update CLAUDE.md with CI/CD pipeline documentation Images are built and pushed to Gitea registry at: code.puffinoffset.com/matt/puffin-app:latest code.puffinoffset.com/matt/puffin-app:main-<sha> 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
5.8 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Core Commands
Development
npm run dev # Start Vite dev server at localhost:5173
npm run build # Build production bundle to dist/
npm run preview # Preview production build locally
Testing & Quality
npm test # Run tests with Vitest
npm run lint # Run ESLint
Docker Operations
docker compose up -d # Start containerized app on port 3800
docker compose down # Stop containers
docker compose build # Build Docker images
Architecture Overview
Application Type
React + TypeScript SPA for carbon offsetting calculations for yachts, built with Vite. Features both desktop and mobile-specific routes with Progressive Web App (PWA) support.
Key Architectural Patterns
Component Structure: The app uses a single-page architecture with client-side routing managed through state in App.tsx. Navigation changes currentPage state rather than using React Router.
Mobile App Route: Special /mobile-app route renders MobileCalculator component exclusively, bypassing the standard layout. This is detected via window.location.pathname and managed through isMobileApp state.
Data Flow:
- Vessel data flows from
App.tsx→ Calculator components →OffsetOrder - Carbon calculations happen in
utils/carbonCalculator.ts - API calls go through dedicated clients in
src/api/
API Integration
Wren Climate API (src/api/wrenClient.ts):
- Base URL:
https://www.wren.co/api - Requires Bearer token authentication via
VITE_WREN_API_TOKEN - Handles portfolio fetching and offset order creation
- Implements retry logic and fallback to default portfolio
AIS Client (src/api/aisClient.ts):
- Fetches vessel data by IMO number
- Currently uses mock data in development
Formspree Integration:
- Contact forms use
VITE_FORMSPREE_CONTACT_IDandVITE_FORMSPREE_OFFSET_ID - Handled in
Contact.tsxand offset order components
State Management
- Local component state via React hooks
- No global state management library
- Form state managed locally within components
- Calculator results passed via props to OffsetOrder components
Styling Approach
- Tailwind CSS for utility-first styling
- Custom glass morphism effects via
index.css - Framer Motion for animations
- Responsive design with mobile-first approach
Component Responsibilities
Core Components:
TripCalculator.tsx: Desktop carbon calculator with trip details formMobileCalculator.tsx: Mobile-optimized calculator with step-by-step flowOffsetOrder.tsx/MobileOffsetOrder.tsx: Handles offset purchase flowHome.tsx: Landing page with hero section and navigationContact.tsx: Contact form integration
Calculation Logic (utils/carbonCalculator.ts):
- Implements DEFRA emission factors
- Calculates based on distance, speed, engine power
- Returns tons of CO2 and monetary amounts
Environment Configuration
Required environment variables in .env:
VITE_WREN_API_TOKEN= # Wren API authentication
VITE_FORMSPREE_CONTACT_ID= # Contact form endpoint
VITE_FORMSPREE_OFFSET_ID= # Offset order form endpoint
Testing Strategy
- Unit tests using Vitest and React Testing Library
- Test configuration in
vitest.config.tswith jsdom environment - Setup file at
src/test/setup.ts - Test files use pattern
*.test.tsor*.test.tsx - Run all tests:
npm test - Tests run with global test APIs enabled
Build & Deployment
- Production builds output to
dist/directory - Vite build configuration includes:
- Source maps enabled for debugging
- Code splitting with vendor chunk (React/React-DOM)
- Lucide-react excluded from optimizeDeps for compatibility
- Docker deployment uses Nginx to serve static files on port 3800
- Host Nginx reverse proxy configuration available in
nginx-host.conf - PWA manifest (
public/manifest.json) and service worker (public/sw.js) for mobile app installation
CI/CD Pipeline
Automated Builds:
- Gitea Actions workflow at
.gitea/workflows/build-deploy.yml - Triggers on push to
mainbranch - Builds multi-stage Docker image (Node build → Nginx serve)
- Pushes to Gitea container registry at
code.puffinoffset.com/matt/puffin-app
Image Tagging Strategy:
latest: Always points to most recent main branch buildmain-<commit-sha>: Specific commit version for rollbacks
Container Registry:
- Gitea's built-in Docker registry
- Authentication via Gitea credentials
- Registry URL:
code.puffinoffset.com
Deployment:
- Manual deployment via Portainer
- Use
docker-compose.portainer.ymlfor production stack configuration - Environment variables mounted via
.envfile volume - Detailed deployment instructions in
DEPLOYMENT.md
Workflow Optimization:
- Docker buildx for multi-platform support
- Layer caching to speed up builds
- Separate build cache stored in registry
Key Implementation Details
Routing without React Router: App uses state-based routing via currentPage state and window.history.pushState(). The /mobile-app route is detected by checking window.location.pathname and renders only MobileCalculator without the standard layout.
Sample Vessel Data: A hardcoded sampleVessel object is used as default vessel data in App.tsx (lines 17-24) since AIS client currently returns mock data.
Analytics Integration: utils/analytics.ts tracks page views via Google Analytics. Called in App.tsx useEffect when route changes.
Currency Support: utils/currencies.ts provides multi-currency conversion. Calculator components support USD, EUR, GBP selection.
Error Handling: ErrorBoundary.tsx component wraps the app to catch React rendering errors gracefully.