npm package: @getspot/spot-widget
Description: Core JavaScript/TypeScript library for integrating Spot's refund guarantee widget into your applications.
Latest version: 4.1.1

# @getspot/spot-widget

Core JavaScript/TypeScript library for integrating Spot's refund guarantee widget into your applications.

## Installation

```bash
npm install @getspot/spot-widget
```

## Quick Start

```javascript
import SpotWidget from '@getspot/spot-widget';

// Create widget instance
const widget = new SpotWidget({
  location: '#spot-widget-container', // CSS selector or DOM element
  apiConfig: {
    environment: 'production', // 'sandbox' for testing
    partnerId: 'your-partner-id'
  },
  quoteRequestData: {
    startDate: '2024-01-01T00:00:00Z',
    endDate: '2024-01-07T23:59:59Z',
    currencyCode: 'USD',
    eventType: 'Ski Trip',
    productType: 'Trip',
    productDuration: 'Trip',
    productPrice: 500,
    productId: 'ski-trip-2024',
    cartId: 'cart-123',
    productName: 'Aspen Ski Trip 2024'
  },
  callbacks: {
    onOptIn: (data) => {
      console.log('User opted in:', data);
      // Handle opt-in logic
    },
    onOptOut: (data) => {
      console.log('User opted out:', data);
      // Handle opt-out logic
    },
    onQuoteRetrieved: (quote) => {
      console.log('Quote retrieved:', quote);
    },
    onError: (error) => {
      console.error('Widget error:', error);
    }
  }
});
```

## TypeScript Support

This package is written in TypeScript and includes full type definitions.

```typescript
import SpotWidget, { SpotWidgetOptions, SelectionData, Quote } from '@getspot/spot-widget';

const options: SpotWidgetOptions = {
  // ... your configuration
};

const widget = new SpotWidget(options);
```

## Configuration

### SpotWidgetOptions

| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| `location` | `string \| HTMLElement` | No | `"body"` | Target element for the widget |
| `apiConfig` | `ApiConfig` | Yes | - | API configuration |
| `quoteRequestData` | `QuoteRequestData` | Yes | - | Quote request information |
| `showTable` | `boolean` | No | `true` | Whether to show payout table |
| `selection` | `SelectionEnum` | No | `SelectionEnum.UNSELECTED` | Initial selection state for the widget |
| `theme` | `Theme` | No | - | Custom styling |
| `callbacks` | `Callbacks` | No | `{}` | Event callbacks |

### ApiConfig

```typescript
{
  environment: 'production' | 'sandbox' | 'local';
  partnerId: string;
  customEndpoint?: string; // Optional custom API endpoint
}
```

### QuoteRequestData

#### Single Product Quote

```typescript
{
  startDate: string;        // ISO 8601 date
  endDate: string;          // ISO 8601 date
  currencyCode: 'USD' | 'CAD' | 'AUD' | 'NZD' | 'EUR' | 'MXN' | 'AED' | 'GBP' | 'MAD' | 'SAR';
  eventType: string;        // Event description
  productType: 'Pass' | 'Trip' | 'Registration';
  productDuration: 'Daily' | 'Seasonal' | 'Trip' | 'Event';
  productPrice: number;     // Price in specified currency
  productId: string;        // Unique product identifier
  cartId: string;           // Shopping cart identifier
  productName: string;      // Human-readable product name
  participantDescription?: string; // Optional participant info
}
```

#### Batch Quote (Multiple Products)

```typescript
{
  cartId: string;
  cartName: string;
  currencyCode: 'USD' | 'CAD' | 'AUD' | 'NZD' | 'EUR' | 'MXN' | 'AED' | 'GBP' | 'MAD' | 'SAR';
  items: Array<{
    // All single product fields except cartId and currencyCode
    cartItemId?: string; // Optional item identifier
  }>;
}
```

## Widget Methods

### updateQuote(newQuoteRequestData)

Update the widget with new quote data.

```javascript
const success = await widget.updateQuote({
  // new quote request data
});
console.log('Update successful:', success);
```

**Returns:** `Promise<boolean>`

### getSelection()

Get the current user selection.

```javascript
const selection = widget.getSelection();
console.log('User selection:', selection);
```

**Returns:** `SelectionData | null`

### validateSelection()

Check if the user has made a selection.

```javascript
const isValid = widget.validateSelection();
if (!isValid) {
  console.log('Please make a selection');
}
```

**Returns:** `boolean`

### destroy()

Clean up the widget instance.

```javascript
widget.destroy();
```

## Callbacks

### onOptIn(data: SelectionData)

Called when user opts into the refund guarantee.

```javascript
{
  onOptIn: (data) => {
    console.log('Opt-in data:', data);
    // data.status === 'QUOTE_ACCEPTED'
    // data.quoteId - unique quote identifier
    // data.spotPrice - refund guarantee price
  }
}
```

### onOptOut(data: SelectionData)

Called when user opts out of the refund guarantee.

```javascript
{
  onOptOut: (data) => {
    console.log('Opt-out data:', data);
    // data.status === 'QUOTE_DECLINED'
    // data.quoteId - unique quote identifier
  }
}
```

### onQuoteRetrieved(quote: Quote)

Called when a quote is successfully retrieved.

```javascript
{
  onQuoteRetrieved: (quote) => {
    console.log('Quote:', quote);
    // quote.id - quote identifier
    // quote.spotPrice - refund guarantee price
    // quote.communication - display text and labels
  }
}
```

### onError(error: ErrorData)

Called when an error occurs.

```javascript
{
  onError: (error) => {
    console.error('Error:', error);
    // error.message - error description
    // error.status - HTTP status code (if applicable)
    // error.responseBody - API response (if applicable)
  }
}
```

### noMatchingQuote(data)

Called when no quote is available for the request.

```javascript
{
  noMatchingQuote: (data) => {
    console.log('No quote available:', data);
    // Handle case where product isn't eligible
  }
}
```

## Styling

Customize the widget appearance using the `theme` option:

```javascript
const widget = new SpotWidget({
  // ... other options
  theme: {
    primaryColor: '#007bff',
    secondaryColor: '#6c757d',
    borderRadius: '8px',
    fontFamily: 'Arial, sans-serif',
    fontSize: '14px'
    // Any CSS custom properties (without -- prefix)
  }
});
```

## Error Handling

Always implement comprehensive error handling:

```javascript
const widget = new SpotWidget({
  // ... configuration
  callbacks: {
    onError: (error) => {
      // Log for debugging
      console.error('Spot Widget Error:', error);
      
      // Handle different error types
      if (error.status === 404) {
        console.log('Product not eligible for refund guarantee');
      } else if (error.status >= 500) {
        console.log('Service temporarily unavailable');
      }
      
      // Track in analytics
      if (window.analytics) {
        window.analytics.track('spot_widget_error', {
          message: error.message,
          status: error.status
        });
      }
    },
    noMatchingQuote: (data) => {
      console.log('No refund guarantee available');
      // Hide widget or show alternative messaging
    }
  }
});
```

## Environments

### Sandbox (Development)

Use the sandbox environment for testing:

```javascript
{
  apiConfig: {
    environment: 'sandbox',
    partnerId: 'your-sandbox-partner-id'
  }
}
```

### Production

Use the production environment for live applications:

```javascript
{
  apiConfig: {
    environment: 'production',
    partnerId: 'your-production-partner-id'
  }
}
```



## API Reference

This package exports the following types and interfaces:

### Interfaces

#### ApiConfig

```typescript
export interface ApiConfig {
  environment: "sandbox" | "production" | "local";
  partnerId: string;
  customEndpoint?: string;
}
```

#### QuoteMetadata

```typescript
export interface QuoteMetadata {
  segment?: string;
  operator?: string;
  channel?: string;
  consumerId?: string;
}
```

#### QuoteItem

```typescript
export interface QuoteItem {
  productPrice: number;
  productType: "Pass" | "Trip" | "Registration";
  productDuration: "Daily" | "Seasonal" | "Trip" | "Event";
  productId: string;
  cartId: string;
  cartItemId?: string;
  cartName: string;
  productName: string;
  participantDescription?: string;
  eventType: string;
  eventId?: string;
  currencyCode: "USD" | "CAD" | "AUD" | "NZD" | "EUR" | "MXN" | "AED" | "GBP" | "MAD" | "SAR";
  startDate: string;
  endDate: string;
  partnerRiskEnd?: Date;
  metadata?: QuoteMetadata;
  isPartialPayment?: boolean;
  hostCountry?: string;
  hostCountryState?: string;
  destinations?: string[];
  dob?: string;
  roundTo?: number;
}
```

#### BatchQuoteRequest

```typescript
export interface BatchQuoteRequest {
  cartId: string;
  cartName: string;
  currencyCode: "USD" | "CAD" | "AUD" | "NZD" | "EUR" | "MXN" | "AED" | "GBP" | "MAD" | "SAR";
  metadata?: QuoteMetadata;
  isPartialPayment?: boolean;
  roundTo?: number;
  items: BatchQuoteItem[];
}
```

#### QualifyingReason

```typescript
export interface QualifyingReason {
  rank: number;
  name?: string;
  benefitType?: {
    name: string;
  };
}
```

#### PayoutScheduleItem

```typescript
export interface PayoutScheduleItem {
  text: string;
  percent: string | number;
  amount: number;
}
```

#### Communication

```typescript
export interface Communication {
  name: string;
  description: string;
  bulletPoints: string[];
  yesOptionText: string;
  noOptionText: string;
  legalDisclaimer: string;
  termsAndConditionsUrl: string;
  paymentTerms?: string;
}
```

#### Quote

```typescript
export interface Quote {
  id: string;
  cartItemId?: string;
  spotPrice: number;
  currencyCode: string;
  communication: Communication;
  payoutSchedule: PayoutScheduleItem[];
  qualifyingReasons?: QualifyingReason[];
  coveredItems?: string[];
  originalQuotes?: Quote[];
}
```

#### QuoteUpdates

```typescript
export interface QuoteUpdates {
  spotPriceWithFees?: number;
}
```

#### ApiResponse

```typescript
export interface ApiResponse {
  status: "QUOTE_AVAILABLE" | "QUOTES_AVAILABLE" | "NO_MATCHING_QUOTE";
  data?: Quote;
  quotes?: Quote[];
  totalSpotPrice?: number;
  spotPrice?: number;
  currencyCode?: string;
  communication?: Communication;
  payoutSchedule?: PayoutScheduleItem[];
  coveredItems?: string[];
}
```

#### SelectionData

```typescript
export interface SelectionData {
  status: "QUOTE_ACCEPTED" | "QUOTE_DECLINED";
  spotPrice?: number;
  quoteId?: string;
  selection?: string;
  batchQuoteDetails?: Array<{
    quoteId: string;
    productPrice: number;
    cartItemId: string;
  }>;
}
```

#### Callbacks

```typescript
export interface Callbacks {
  onOptIn?: (data: SelectionData) => void;
  onOptOut?: (data: SelectionData) => void;
  onQuoteRetrieved?: (quote: Quote) => QuoteUpdates | void;
  onError?: (error: { message: string; status?: number; responseBody?: any }) => void;
  noMatchingQuote?: (data: { status: string; data: QuoteRequestData }) => void;
}
```

#### Theme

```typescript
export interface Theme {
  [key: string]: string;
}
```

#### SpotWidgetOptions

```typescript
export interface SpotWidgetOptions {
  location?: string | HTMLElement;
  showTable?: boolean;
  selection?: SelectionEnum;
  logoPosition?: "bottom-right" | "top-right";
  apiConfig: ApiConfig;
  quoteRequestData: QuoteRequestData;
  callbacks?: Callbacks;
  theme?: Theme;
  isPartialPayment?: boolean;
  useMockData?: boolean;
  mockData?: ApiResponse;
}
```

#### ElementOptions

```typescript
export interface ElementOptions {
  text?: string;
  className?: string;
  parent?: HTMLElement;
  innerHTML?: string;
  href?: string;
  target?: string;
}
```

### Enums

#### SelectionEnum

```typescript
export enum SelectionEnum {
  YES = 'yes',
  NO = 'no',
  UNSELECTED = 'unselected',
}
```

### Type Aliases

#### QuoteRequestData

```typescript
export type QuoteRequestData = QuoteItem | BatchQuoteRequest | QuoteItem[];
```

## Framework Integration

This is the core library. For framework-specific wrappers:

- **React**: [@getspot/spot-widget-react](../react)
- **Vue 3**: [@getspot/spot-widget-vue](../vue)  
- **Vue 2**: [@getspot/spot-widget-vue2](../vue2)

## Browser Support

- Modern browsers (ES2020+)
- Chrome 80+
- Firefox 74+
- Safari 13+
- Edge 80+

## License

See the main package for license information.

## Support

For support, please contact [support@getspot.com](mailto:support@getspot.com).