# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Development Commands

### JavaScript/React Assets

- **Build assets**: `npm run build` - Compiles React/JS assets using @wordpress/scripts and minifies configuration JS files
- **Development mode**: `npm run dev` - Starts development server with and watches for changes
- **Create plugin zip**: `npm run plugin-zip` - Packages plugin for distribution
- **Update version**: `npm run update-version <version>` - Updates plugin version across all files (package.json, readme.txt, understory.php, CLAUDE.md, package-lock.json)
- **Minify assets**: Individual minification scripts for widget configuration JS files (auto-runs with `npm run build`)

### PHP Development

**Prerequisites**:

- Install Composer with `brew install composer` (macOS) or [download from getcomposer.org](https://getcomposer.org/download/)
- For VS Code users: Install the recommended PHP extension (Intelephense) when prompted, or install manually

**Setup**:

- **Install PHP tools**: `composer install` - Installs PHP CodeSniffer, WordPress coding standards, and WordPress function stubs for IDE intellisense
- **VS Code Setup**:
  1. Install recommended extensions when prompted (especially `persoderlind.vscode-phpcbf`)
  2. Restart VS Code after running `composer install`
  3. PHP files will now auto-format to WordPress standards on save
- **Check coding standards**: `composer run lint` - Runs WordPress Core coding standards
- **Fix coding standards**: `composer run lint:fix` - Auto-fixes coding standard violations
- **Check PHP compatibility**: `composer run lint:compat` - Validates PHP 7.4+ compatibility
- **Security scan**: `composer run test:security` - Runs WordPress VIP coding standards for security

### Local WordPress Development

- **Setup WordPress**: `npm run wp-start` - Sets up a local WordPress instance with all dependencies through Docker
- **Development workflow**: Run `npm run wp-start` followed by `npm run dev` to build outputs on every code change
- **Access URLs**:
  - Frontend: http://localhost:8888/
  - Admin panel: http://localhost:8888/wp-admin/
  - Credentials: username "admin", password "password"

## Architecture Overview

This is a WordPress plugin that integrates with the Understory booking platform. The plugin provides four widget types (Booking, Experiences, Availability, Gift Card) across multiple integration methods:

### Core Structure

- **Main plugin file**: `understory.php` - Defines constants, includes core files, and bootstraps the plugin
- **Settings management**: `includes/class-understory-settings.php` - Handles plugin configuration and Understory API connection
- **Utility classes**: `includes/utils/` - Contains shared functionality:
  - `class-options-fetcher.php` - Centralized fetching of storefronts, experiences, and languages with storefront-based filtering. **Includes centralized AJAX handler** `get_experiences_ajax()` for consistent security and response patterns
  - `class-experience-fetcher.php` - Batch fetching of experience data from APIs
  - `class-data-fetcher.php` - Generic data fetching utilities
  - `class-availability.php` - Availability widget rendering logic
  - `class-experiences.php` - Experiences widget rendering logic

### Widget Integration Points

The plugin supports three integration methods for the same widgets:

1. **Gutenberg Blocks** (`includes/gutenberg/`)

   - `class-booking-block.php`
   - `class-experiences-block.php`
   - `class-availability-block.php`
   - `class-gift-card-block.php`

2. **Elementor Widgets** (`includes/elementor/`)

   - `class-booking-elementor.php`
   - `class-experiences-elementor.php`
   - `class-availability-elementor.php`
   - `class-gift-card-elementor.php`

3. **WordPress Shortcodes** (`includes/shortcodes/`)
   - `class-booking-shortcode.php`
   - `class-experiences-shortcode.php`
   - `class-availability-shortcode.php`
   - `class-gift-card-shortcode.php`

### Frontend Assets

- **React components**: `src/availability/` - Modern React-based availability widget with storefront support
  - `blocks/index.js` - Block registration with storefrontId attribute
  - `blocks/edit.js` - Block editor with storefront selector and dynamic experience loading
  - `useAvailability.js` - Hook for storefront-filtered experience fetching
  - `useAvailabilityOptions.js` - Hook for dynamic storefront/experience options
- **Widget Configuration Architecture**: `assets/js/` - Essential client-side configuration for widget editors
  - `elementor-booking-widget-editor.min.js` - Handles dynamic experience loading via HTTP requests when storefront changes in Elementor editor
  - `elementor-availability-widget-editor.min.js` - Updates availability widget experiences based on storefront selection in Elementor
  - `gutenberg-booking-block-editor.min.js` - Manages Gutenberg block attributes and experience options via AJAX
  - `gutenberg-experiences-block-editor.min.js` - Updates experiences block with storefront-filtered data
  - These files use client-side HTTP requests to dynamically update widget settings (Elementor) and block attributes (Gutenberg)
- **Build process**: Webpack configuration (@wordpress/scripts) builds React components to `/build/`

### Key Constants & APIs

- `UNDERSTORY_API_BASE_URL`: Main API endpoint for HTTP REST calls (AWS Lambda `api-storefronts`/`vip-bff`)
- `UNDERSTORY_WORDPRESS_CONFIG_URL`: WordPress configuration endpoint for storefront data
- `UNDERSTORY_WIDGET_SCRIPT`: External booking widget script URL
- `UNDERSTORY_GIFT_CARD_WIDGET_SCRIPT`: External gift card widget script URL
- Company data is fetched from Understory APIs and cached in WordPress options

### Authentication & Data Flow

**No Authentication Required**: The plugin uses **public API endpoints only**. There is no authentication system or token management.

1. **Connection Flow**:

   - User clicks "Connect to Understory" → redirects to Understory backoffice
   - Backoffice redirect returns with `companyId` as URL parameter
   - Plugin captures company ID and queries **public endpoints** for company data

2. **API Access Pattern**:

   - **HTTP REST**: Traditional GET requests to public company endpoints
   - **Storefront Config**: POST requests to `https://widgets.understory.io/api/config/wordpress` with JSON body containing `companyId`
   - All endpoints are public and require only the company ID for data scoping

3. **Data Storage**:
   - Company ID and all available storefronts cached in WordPress options
   - No authentication tokens or sensitive credentials stored
   - Widget configuration passed to frontend via data attributes
   - Experiences cached per storefront to optimize API calls

### Multi-Storefront Architecture

**Per-Widget Storefront Selection**: Each widget instance (block, shortcode, or Elementor widget) can be configured with a different storefront. **No default storefront** - explicit selection is always required.

1. **Storefront Management**:

   - All available storefronts retrieved from `https://widgets.understory.io/api/config/wordpress`
   - Each storefront contains: `id`, `name`, `fqdn`, and `experienceIds` array
   - Storefronts stored in WordPress options under `company.storefronts` array
   - **Direct FQDN Usage**: All experience and event links use storefront's `fqdn` directly (e.g., `https://{storefront.fqdn}/experience/{id}`)

2. **Widget Configuration**:

   - **Required**: All widgets require explicit storefront selection (`storefront_id` parameter)
   - **Default Selection**: Widgets auto-select first available storefront using `array_key_first(Understory_OptionsFetcher::get_storefronts())`
   - **Validation**: Widgets display error messages when storefront not selected
   - **Dynamic Updates**: Experience options update automatically when storefront changes

3. **Experience Filtering**:

   - Experiences filtered based on selected storefront's `experienceIds` array
   - Empty `experienceIds` arrays (storefronts without experiences) return empty array
   - Experience selectors disabled until storefront is selected
   - Uses `Understory_OptionsFetcher::get_experiences()` for centralized filtering
   - Auto-selects first experience when storefront changes (where applicable)

4. **AJAX Endpoints**:

   - **Centralized Handler**: All widgets use `Understory_OptionsFetcher::get_experiences_ajax()` for consistent security and response patterns
   - **Booking Elementor**: `understory_get_experiences_by_storefront` - Uses centralized handler with `understory_elementor_nonce`
   - **Availability Elementor**: `understory_availability_elementor_get_experiences_by_storefront` - Uses centralized handler with `understory_elementor_nonce`
   - **Gutenberg Blocks**: Various block-specific endpoints for Gutenberg integration
   - All endpoints include nonce verification for security via centralized handler
   - Endpoints registered only in admin context (no `wp_ajax_nopriv` actions)

5. **Implementation Details**:

   **Elementor Widgets**:

   - Dynamic controls updated via JavaScript in editor
   - Uses `elementor.hooks.addAction` for panel open events
   - Experience controls refresh when storefront changes
   - **Consistent JS Data**: All widgets use `understory<widget>Data` (e.g. `understoryExperiencesData`) for jQuery access to widget data
   - **Centralized AJAX**: Widget classes delegate to `Understory_OptionsFetcher::get_experiences_ajax()` for consistent handling
   - Located in `includes/elementor/lib/` directory

   **Gutenberg Blocks**:

   - Uses `wp_localize_script` to preload storefronts and languages
   - AJAX calls for dynamic experience loading
   - React hooks (`useEffect`, `useState`) for state management
   - Located in `includes/gutenberg/` directory

   **React Availability Widget**:

   - Custom hooks: `useAvailability` and `useAvailabilityOptions`
   - Storefront filtering integrated into experience fetching
   - Located in `src/availability/` directory

   **Shortcode Generator**:

   - All three tabs (Booking, Experiences, Availability) include storefront dropdown
   - Experience options dynamically update based on selected storefront
   - Generated shortcodes include `storefront_id` parameter
   - Located in `includes/tabs/` directory

6. **Link Generation**:

   - **PHP**: `ExperienceCard::render()` accepts `$storefront_fqdn` parameter to build links as `https://{fqdn}{href}`
   - **React**: `AvailabilityCard` component uses `storefrontUrl = https://${storefront.fqdn}` for all links
   - **Image Optimization**: Next.js image optimization URLs constructed as `{storefrontUrl}/_next/image?w=828&q=75&url={imageUrl}`
   - **No Domain Lookups**: Plugin no longer fetches or stores company domain information - all links use storefront FQDN

7. **Asset Management**:
   - Widget configuration JavaScript files in `assets/js/` are minified using Terser
   - Minification runs automatically during `npm run build`
   - Editor scripts: `elementor-*-widget-editor.js`, `gutenberg-*-block-editor.js` - Handle dynamic widget configuration
   - Tab scripts: `tabs.js` for shortcode generator with dynamic experience loading
   - Gift card widget: `gutenberg-gift-card-block-editor.js` - Handles gift card widget configuration

### Version Management

Current version: 1.8.5 (defined in both `package.json` and `understory.php`)
