puffin-app/CLAUDE.md
Matt 1c9c570ece
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Add CI/CD pipeline with Gitea Actions and Portainer deployment
- 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>
2025-10-29 13:31:04 +01:00

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_ID and VITE_FORMSPREE_OFFSET_ID
  • Handled in Contact.tsx and 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 form
  • MobileCalculator.tsx: Mobile-optimized calculator with step-by-step flow
  • OffsetOrder.tsx / MobileOffsetOrder.tsx: Handles offset purchase flow
  • Home.tsx: Landing page with hero section and navigation
  • Contact.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.ts with jsdom environment
  • Setup file at src/test/setup.ts
  • Test files use pattern *.test.ts or *.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 main branch
  • 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 build
  • main-<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.yml for production stack configuration
  • Environment variables mounted via .env file 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.