JSPM

  • Created
  • Published
  • Downloads 255
  • Score
    100M100P100Q99413F
  • License ISC

Embeddable iframe plugin for Climate Action Now with multi-environment builds and unpkg CDN distribution

Package Exports

  • @epurchasepower/can-action-carousel
  • @epurchasepower/can-action-carousel/dist/production/can-plugin.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@epurchasepower/can-action-carousel) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Climate Action Now Web Plugin

A React-based web plugin for embedding climate action cards into any website. Built with TypeScript, Vite, and Tailwind CSS.

Embedding

🚀 Simple Script Embedding

Just add one script tag to any website:

<script 
  src="https://unpkg.com/@epurchasepower/can-action-carousel@latest/dist/production/embed-script.js"
  data-client-id="can"
  data-campaign-code="173">
</script>

🎯 Container-Specific Embedding

Target a specific container element:

<div data-climate-action></div>
<script src="https://unpkg.com/@epurchasepower/can-action-carousel@latest/dist/production/embed-script.js"></script>

📖 Complete Integration Guide →

⚛️ Direct React Integration

For React applications, import and use directly:

import { ClimateActionPlugin } from '@epurchasepower/can-action-carousel'

<ClimateActionPlugin clientId="your-client-id" theme="light" />

Features

  • Action Cards: Display climate actions with rich UI components
  • Action Flows: Multi-step flows for petition signing and learning actions
  • Action Completion Tracking: Shows when users have completed actions with date-specific messages
  • Responsive Design: Works seamlessly on desktop and mobile devices
  • Theme Support: Customizable themes for different tenants (light/dark)
  • Mock Data Support: Development mode with simulated API responses
  • Existing Account Login: Seamless migration from anonymous to existing accounts via security code verification
  • Multi-Environment Builds: Separate builds for development, staging, and production
  • Auto-Resize: Dynamic iframe height adjustment
  • Parameter Validation: Comprehensive input validation and sanitization
  • Interactive Release: Version selection prompts for patch/minor/major updates
  • Default Configuration: clientId='can', campaignCode='173' for easy testing

Development

Prerequisites

  • Node.js 18+
  • pnpm

Setup

# Install dependencies
pnpm install

# Standard Development
pnpm dev                    # Start development server
pnpm build                  # Build for production
pnpm preview               # Preview production build

# Iframe Development
pnpm dev:iframe             # Start iframe development server (port 5174)
pnpm build:dev              # Build development iframe version
pnpm build:staging          # Build staging iframe version  
pnpm build:production       # Build production iframe version
pnpm build:all              # Build all iframe environments

# Testing
pnpm test                   # Run tests
pnpm test:iframe:local      # Open test-iframe-local.html (requires dev:iframe running)
pnpm test:iframe:staging    # Open test-iframe-staging.html (tests unpkg staging)
pnpm test:iframe:production # Open test-iframe-production.html (tests unpkg production)

# Quality Checks
pnpm check-types            # TypeScript type checking
pnpm lint                   # ESLint linting

# Publishing (Interactive)
pnpm release                # Interactive release of both staging + production
pnpm release:staging        # Interactive staging release (prompts for patch/minor/major)
pnpm release:production     # Interactive production release (prompts for patch/minor/major)

Stable public URL (Cloudflare Tunnel)

Use Cloudflare Tunnel to get a stable HTTPS URL (free) for localhost:5173.

Prerequisites:

  • A domain managed by Cloudflare (free plan works)
  • cloudflared installed (e.g., brew install cloudflared)

One-time login:

pnpm --filter @climate-action-now/web-plugin run cf:login

Run a named tunnel and route a hostname to your local Vite server:

# choose a hostname you control in Cloudflare DNS, e.g. dev-plugin.yourdomain.com
export CF_HOSTNAME=dev-plugin.yourdomain.com

# starts Cloudflare Tunnel (keeps running) and maps hostname → http://localhost:5173
pnpm --filter @climate-action-now/web-plugin run cf:tunnel

Start Vite with remote HMR pointing at your tunnel host:

# in a separate terminal
cd frontends/web-plugin
PUBLIC_TUNNEL_HOST=$CF_HOSTNAME pnpm dev

You can now open https://$CF_HOSTNAME from any device and Vite HMR will work over WSS.

Configuration

The web plugin supports flexible configuration for both development and production. See CONFIGURATION.md for detailed configuration options.

Quick Start

Create a .env.development file based on .env.example:

VITE_API_BASE_URL=http://localhost:8040
VITE_TENANT_ID=can  # Optional - can be provided via props
VITE_CAMPAIGN_CODE=climate-2024  # Optional - can be provided via props

Architecture

Directory Structure

src/
├── components/         # React components
│   ├── ActionCard/    # Action card components
│   ├── ui/            # Reusable UI components
│   └── ...
├── features/          # Feature-specific modules
│   ├── actions/       # Action-related features
│   ├── carousel/      # Carousel functionality
│   └── ...
├── hooks/             # Custom React hooks
├── services/          # API services
├── types/             # TypeScript type definitions
├── themes/            # Theme configurations
└── config/            # Application configuration

Key Components

ActionCard

The main component for displaying climate actions:

<ActionCard
  action={action}
  onComplete={(actionId) => handleComplete(actionId)}
  onSkip={() => handleSkip()}
  onShare={() => handleShare()}
  onShowWho={() => handleShowWho()}
  onShowWhy={() => handleShowWhy()}
  currentUser={currentUser}
/>

CompletionTag

Displays action completion status with date-aware messages:

  • "You already took this action today."
  • "You took this action yesterday."
  • "You took this action on MM/DD/YY."

Also shows when actions can be repeated:

  • "You can take it again if you want."
  • "You can take it again tomorrow."
  • "You can take it again on MM/DD/YY."

TellFlow

Manages the Tell action flow for contacting elected officials:

<TellFlow
  action={tellAction}
  onClose={(completed) => handleClose(completed)}
  onSkip={() => handleSkip()}
/>

Features:

  • Address collection form
  • Representative lookup
  • Email and phone contact methods
  • Template personalization
  • Progress tracking across multiple recipients

API Integration

Action History

Fetch action completion history:

import { useActionHistory } from '~/hooks/useActionHistory'

const { data, isLoading, error } = useActionHistory(actionId)

Creating Deeds

Mark actions as completed:

import { useCreateDeed } from '~/hooks/useActionHistory'

const createDeedMutation = useCreateDeed()

// Mark action as completed
await createDeedMutation.mutateAsync({
  actionId: 'action-123',
  extraData: { source: 'web' },
})

Services

ActionsService

Handles all action-related API calls:

// Get actions
const actions = await actionsService.getActions({
  page: 1,
  pageSize: 10,
})

// Get action history
const history = await actionsService.getActionHistory(actionId)

// Create deed (mark as completed)
const deed = await actionsService.createDeed(actionId, extraData)

RepresentativeService

Fetches elected officials based on user address:

// Get representatives
const reps = await representativeService.getRepresentatives(address, tellAction)

// Validate address
const isValid = representativeService.validateAddress(address)

// Parse address components
const parsed = representativeService.parseAddress(address)

TemplateService

Personalizes message templates with merge fields:

// Personalize template
const { content, missingFields } = personalizeTemplate(template, {
  recipient,
  user,
})

// Format phone number
const formatted = formatPhoneNumber('2025550101')
// Returns: (202) 555-0101

Authentication

Existing Account Login Flow

The web plugin supports seamless migration from anonymous to existing accounts through a security code verification flow:

How It Works

  1. Email Detection: When an anonymous user enters an email that matches an existing account, the system automatically detects it
  2. Security Code: A 5-digit security code is sent to the user's email
  3. Verification: User enters the code to verify ownership
  4. Migration: Anonymous user's actions (deeds) are migrated to the existing account
  5. Auto-Login: User is automatically logged in to their existing account

Implementation

The login flow uses these key components:

useEmailValidation Hook

import { useEmailValidation } from '~/hooks/useEmailValidation'

function MyForm() {
  const {
    checkEmail,
    showSecurityCodeModal,
    currentEmail,
    closeModal,
    handleValidationSuccess
  } = useEmailValidation({
    onValidationComplete: () => {
      // Continue with form submission
    }
  })

  return (
    <>
      <input
        type="email"
        onChange={(e) => checkEmail(e.target.value)}
      />

      {showSecurityCodeModal && (
        <SecurityCodeModal
          isOpen={showSecurityCodeModal}
          email={currentEmail}
          onClose={closeModal}
          onSuccess={handleValidationSuccess}
        />
      )}
    </>
  )
}

SecurityCodeModal Component

  • Handles the complete security code flow
  • Multi-step UI: explanation → code entry → validation → success
  • Includes resend functionality with rate limiting
  • Fully accessible with keyboard navigation

Integration Points

The email validation is integrated into these forms:

  • RequiredFieldsForm - For tell actions requiring email
  • PetitionSignForm - For petition signing
  • EditProfileForm - For profile updates (if email collection is added)

Testing

The project uses Vitest for unit testing:

# Run all tests
pnpm test

# Run tests in watch mode
pnpm test:watch

# Run tests with coverage
pnpm test:coverage

Test Structure

  • Component tests: src/components/**/__tests__/
  • Hook tests: src/hooks/__tests__/
  • Service tests: src/services/__tests__/

Building for Production

# Build the plugin
pnpm build

# The output will be in dist/
# - dist/web-plugin.js - The main JavaScript bundle
# - dist/web-plugin.css - The CSS bundle

Embedding the Plugin

To embed the plugin in a website:

<!-- Add the CSS -->
<link rel="stylesheet" href="path/to/web-plugin.css" />

<!-- Add the JavaScript -->
<script src="path/to/web-plugin.js"></script>

<!-- Add the container element -->
<div id="climate-action-plugin"></div>

<!-- Initialize the plugin -->
<script>
  ClimateActionPlugin.init({
    tenantId: 'your-tenant-id',
    container: '#climate-action-plugin',
  })
</script>

Configuration

Theme Configuration

Themes can be configured per tenant in src/themes/:

export const customTheme: Theme = {
  colors: {
    primary: '#00A651',
    secondary: '#0096A3',
    accent: {
      warning: '#F7931E',
      error: '#FF0000',
      success: '#00A651',
    },
  },
  fonts: {
    heading: 'Roboto, sans-serif',
    body: 'Open Sans, sans-serif',
  },
}

API Configuration

Configure API endpoints in src/config/config.ts:

export const config = {
  api: {
    baseUrl: import.meta.env.VITE_API_BASE_URL,
    actionsEndpoint: '/v1/actions',
  },
  useMockData: import.meta.env.VITE_USE_MOCK_DATA === 'true',
}

Contributing

  1. Create a feature branch
  2. Make your changes
  3. Write/update tests
  4. Run pnpm lint and pnpm check-types
  5. Follow Accessibility Guidelines for UI components
  6. Submit a pull request

License

[License information here]