## Form Widget

A scheduling widget that supports multi-step forms and displays.

## Features

- Multi-step form flows with dynamic display logic
- Integration with Stentor conversational AI platform
- Automatic tracking parameter pass-through for analytics
- Material-UI components with theming support
- Audio/voice input capabilities
- Address autocomplete and service area validation
- Session management and response caching
- Inline embedding into a container the host page controls

## Embedding

By default the widget appends its own `#xapp-form-widget` div to the page and opens as a
fixed, full-viewport modal over it.

To render the form **inline** instead — inside a box the host page positions and sizes —
create that div yourself and give it the `embedded` class:

```html
<div id="xapp-form-widget" class="embedded" style="
    background-color: transparent;
    width: 400px;
    height: 600px;
    overflow-y: auto;
    overflow-x: hidden;
    position: absolute;
    top: 40px;
    left: 100px;">
</div>
```

The widget script finds the existing div and renders into it rather than creating one. In
embedded mode it:

- fills the container instead of capping itself at the desktop breakpoint widths, and drops
  the border, corner radius and drop shadow that make it read as a floating card;
- renders no backdrop, and does not lock the host page's scroll;
- hides the side button (a `position: fixed` page-edge tab that would escape the container).

Use `class="xapp-embedded"` instead if your own CSS already claims `.embedded`. Either class
works; the div must still carry `id="xapp-form-widget"`.

The div is inspected once, when the widget mounts, so it must carry the class before the
widget script runs — changing it later has no effect for that page view.

## Tracking & Analytics

The form widget automatically captures and passes marketing attribution parameters to all API requests for analytics filtering.

### Supported Tracking Parameters

#### Standard UTM Parameters
- `utm_source` - Traffic source (e.g., "google", "facebook", "newsletter")
- `utm_medium` - Marketing medium (e.g., "cpc", "email", "social") 
- `utm_campaign` - Campaign name (e.g., "spring_sale", "product_launch")
- `utm_term` - Paid keywords (for paid search campaigns)
- `utm_content` - Ad content identifier (for A/B testing)

#### Platform Click IDs
- `gclid` - Google Click ID (Google Ads auto-tagging)
- `fbclid` - Facebook Click ID
- `msclkid` - Microsoft Click ID (Bing Ads)

#### Custom Parameters
- `track_*` - Any parameter with "track_" prefix

### Implementation

Tracking parameters are automatically:
1. **Extracted** from the page URL when the widget loads
2. **Stored** in localStorage with 30-day expiration
3. **Included** in all Stentor API requests via the `attributes` field
4. **Available** for filtering in OpenSearch analytics

### API Integration

All tracking parameters are included in the request attributes sent to the Stentor API:

```javascript
// Example API request with tracking data
{
  "sessionId": "stentor-form-session-123",
  "userId": "user-456", 
  "attributes": {
    "utm_source": "google",
    "utm_medium": "cpc",
    "utm_campaign": "spring_sale",
    "gclid": "abc123def456",
    "track_landing_page": "homepage"
  },
  // ... other request data
}
```

### Configuration

Tracking is enabled by default. The system handles:
- Parameter extraction and validation
- Automatic cleanup of expired data
- Cross-session persistence
- Error handling for malformed data

No additional configuration is required - the widget will automatically capture and pass through any UTM parameters, click IDs, or custom tracking parameters present in the page URL.

### Implementation Notes

**When used standalone:** The widget extracts tracking parameters from the page URL on mount and stores them in localStorage with the prefix `xa_tracking_`.

**When embedded in contact-app:** Both the contact-app and form-widget will extract and store tracking parameters. This is safe and intentional:
- Both extract from the same URL (`window.location.href`)
- Both write to the same localStorage keys (`xa_tracking_utm_source`, etc.)
- The function is idempotent - the second extraction just refreshes stored values with identical data
- No data corruption occurs; it's just a minor inefficiency

**Storage format:** Parameters are stored as JSON objects with expiration:
```javascript
localStorage.getItem('xa_tracking_utm_source')
// Returns: {"value":"google","expires":1234567890,"stored":1234567890}
```

## Form Field Types

The form widget supports multiple field types for building dynamic forms. Below are JSON examples for each supported field type.

### Text Input

Basic text input field with optional formatting (phone, email, address).

```json
{
  "name": "full_name",
  "type": "TEXT",
  "title": "Full Name",
  "placeholder": "Enter your name",
  "mandatory": true,
  "mandatoryError": "Name is required"
}
```

#### Phone Number Input

```json
{
  "name": "phone",
  "type": "TEXT",
  "title": "Phone Number",
  "format": "PHONE",
  "placeholder": "(555) 555-5555",
  "mandatory": true
}
```

#### Email Input

```json
{
  "name": "email",
  "type": "TEXT",
  "title": "Email Address",
  "format": "EMAIL",
  "placeholder": "you@example.com"
}
```

#### Multiline Text

```json
{
  "name": "description",
  "type": "TEXT",
  "title": "Description",
  "multiline": true,
  "rows": 4,
  "rowsMax": 8,
  "placeholder": "Tell us more..."
}
```

#### Address Input with Autocomplete

```json
{
  "name": "address",
  "type": "TEXT",
  "title": "Service Address",
  "format": "ADDRESS",
  "mapsBaseUrl": "https://maps.googleapis.com/maps/api/place/autocomplete",
  "googleMapsApiKey": "YOUR_API_KEY",
  "mapsUrlQueryParams": {
    "components": "country:us",
    "language": "en"
  }
}
```

### Dropdown

Single-select dropdown menu.

```json
{
  "name": "service_type",
  "type": "DROPDOWN",
  "title": "Select a service",
  "mandatory": true,
  "items": [
    {
      "id": "plumbing",
      "label": "Plumbing"
    },
    {
      "id": "electrical",
      "label": "Electrical"
    },
    {
      "id": "hvac",
      "label": "HVAC"
    }
  ]
}
```

#### Dropdown with Freeform Input

Allows users to enter custom text when they select specific options.

```json
{
  "name": "issue",
  "type": "DROPDOWN",
  "title": "What issue are you experiencing?",
  "items": [
    {
      "id": "installation",
      "label": "Installation"
    },
    {
      "id": "repair",
      "label": "Repair"
    },
    {
      "id": "maintenance",
      "label": "Maintenance"
    },
    {
      "id": "other",
      "label": "Other",
      "freeFormInput": true
    }
  ]
}
```

When `freeFormInput: true`, selecting that option displays a text input field. The user's typed text becomes the field value instead of the item's ID.

### Chips

Visual chip-based selection, supports both single and multi-select.

#### Multi-select Chips

```json
{
  "name": "services",
  "type": "CHIPS",
  "title": "Select services you need",
  "minRequired": 1,
  "maxAllowed": 3,
  "items": [
    {
      "id": "service1",
      "label": "Service 1"
    },
    {
      "id": "service2",
      "label": "Service 2",
      "selected": true
    },
    {
      "id": "service3",
      "label": "Service 3"
    }
  ]
}
```

#### Single-select Chips (Radio)

```json
{
  "name": "membership",
  "type": "CHIPS",
  "title": "Are you a member?",
  "radio": true,
  "items": [
    {
      "id": "yes",
      "label": "Yes"
    },
    {
      "id": "no",
      "label": "No",
      "selected": true
    }
  ]
}
```

#### Chips with Actionable Items

```json
{
  "name": "options",
  "type": "CHIPS",
  "title": "Choose an option",
  "items": [
    {
      "id": "option1",
      "label": "Option 1"
    },
    {
      "id": "learn_more",
      "label": "Learn More",
      "url": "https://example.com/info"
    }
  ]
}
```

### Checkboxes / Radio Buttons

Traditional checkbox or radio button selection.

#### Checkboxes (Multi-select)

```json
{
  "name": "preferences",
  "type": "CHECK",
  "title": "Select your preferences",
  "items": [
    {
      "id": "email_updates",
      "label": "Email Updates"
    },
    {
      "id": "sms_notifications",
      "label": "SMS Notifications"
    },
    {
      "id": "phone_calls",
      "label": "Phone Calls"
    }
  ]
}
```

#### Radio Buttons (Single-select)

```json
{
  "name": "contact_method",
  "type": "CHECK",
  "title": "Preferred contact method",
  "radio": true,
  "items": [
    {
      "id": "email",
      "label": "Email"
    },
    {
      "id": "phone",
      "label": "Phone"
    },
    {
      "id": "sms",
      "label": "SMS"
    }
  ]
}
```

### Date Picker

Single date selection with optional busy day configuration.

```json
{
  "name": "appointment_date",
  "type": "DATE",
  "title": "Select appointment date",
  "mandatory": true,
  "defaultBusyDays": {
    "blockWeekends": true,
    "blockCurrentDay": true,
    "blockNextBusinessDays": 1
  }
}
```

#### Date with Available Days

```json
{
  "name": "service_date",
  "type": "DATE",
  "title": "Preferred service date",
  "defaultBusyDays": {
    "availableDays": ["MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY"]
  }
}
```

### Date Range Picker

Select a start and end date.

```json
{
  "name": "vacation_dates",
  "type": "DATERANGE",
  "title": "Select date range",
  "preselecteDates": {
    "from": "2024-01-01",
    "to": "2024-01-07"
  }
}
```

### Card

Display-only card with optional image, header, and text content.

```json
{
  "name": "info_card",
  "type": "CARD",
  "header": {
    "title": "Service Information",
    "subheader": "What to expect"
  },
  "media": {
    "imageUrl": "https://example.com/image.jpg",
    "alt": "Service image",
    "height": 200,
    "width": 400
  },
  "text": "Our technicians will arrive within the scheduled time window.",
  "variant": "outlined",
  "color": "primary"
}
```

## Common Field Properties

All field types support these common properties:

- `name` (string, required) - Field identifier used in form data
- `type` (string, required) - Field type (TEXT, DROPDOWN, CHIPS, CHECK, DATE, DATERANGE, CARD)
- `title` (string, optional) - Display label for the field
- `mandatory` (boolean, optional) - Whether the field is required
- `mandatoryError` (string, optional) - Custom error message when required field is empty
- `condition` (string, optional) - JavaScript expression to conditionally show field (e.g., `"service === 'repair'"`)
- `style` (object, optional) - React CSSProperties object for custom styling
- `shape` (string, optional) - Visual shape: "ROUND" or "SQUARE"
- `mandatoryGroup` (string, optional) - Group name for "one of" validation
