# Technical Guide: Integrating a New Product Type

This guide provides a comprehensive, step-by-step walkthrough for adding a new product type to the event registration system. Following these instructions will ensure that the new product is consistently integrated across the frontend, backend, database, and payment gateway.

## High-Level Overview

Integrating a new product involves the following key stages:
1.  **Database Configuration**: Defining the new product in the database.
2.  **Backend API (`api.php`)**: Extending the API to handle the new product's data, pricing, and persistence.
3.  **Frontend UI (`registration.js`)**: Creating the user interface for product selection and ensuring it interacts correctly with the existing form logic.
4.  **Stripe Integration**: Making sure the new product is included in the payment session.
5.  **Verification**: Thoroughly testing the end-to-end flow.

---

## 1. Database & Backend Configuration

The foundation of a new product is its definition in the database and the backend logic to handle it.

### 1.1. Database Setup

All products are defined in the `products` table. To add a new product, you need to insert a new record.

**Table: `products`**

| Column | Type | Description |
| --- | --- | --- |
| `id` | INT | Primary Key, Auto-increment |
| `product_type` | VARCHAR | **Crucial**. A unique identifier for the product group (e.g., 'delegate_pass', 'table', 'sponsorship', 'other_sponsorship'). **This must match the key used in the frontend `formData` object.** |
| `name` | VARCHAR | The specific name of the item (e.g., 'Early Bird Delegate Pass', 'Standard Table-Top Presentation'). |
| `price` | DECIMAL | The price of the product. |
| `currency` | VARCHAR | Currency code (e.g., 'USD'). |
| `category_id` | INT | Foreign key linking to the `product_categories` table. |
| `status` | VARCHAR | 'active' or 'inactive'. |
| `early_bird_deadline`| DATETIME | (Optional) If applicable, the deadline for early bird pricing. |
| `description` | TEXT | (Optional) A short description displayed in the UI. |

**Example SQL:**
```sql
INSERT INTO products (product_type, name, price, currency, category_id, status, description) 
VALUES ('new_product_type', 'New Awesome Product', 250.00, 'USD', 3, 'active', 'Description for this awesome new product.');
```

**Controlling Display:**
- The visibility of product sections on the registration form is controlled by the `frontend_display_config` table, which is managed in the admin panel. Ensure your new product's category is configured to be displayed.

### 1.2. Backend API (`api.php`)

The `api.php` file is the central hub for all backend logic. You will need to make modifications in three key areas.

**A. Fetching Product Data (`getConfiguration` function)**

This function retrieves all necessary configuration data for the frontend. You must add logic to fetch your new product type.

- **Location:** `api.php`, inside the `getConfiguration()` function.
- **Action:** Add a new query to fetch your products from the `products` table using the `product_type` you defined.

```php
// Inside getConfiguration()

// Example for 'sponsorships'
$sponsorships_stmt = $pdo->prepare("SELECT * FROM products WHERE product_type = 'sponsorship' AND status = 'active'");
$sponsorships_stmt->execute();
$sponsorships = $sponsorships_stmt->fetchAll(PDO::FETCH_ASSOC);

// *** ADD YOUR NEW PRODUCT TYPE HERE ***
$new_product_stmt = $pdo->prepare("SELECT * FROM products WHERE product_type = 'new_product_type' AND status = 'active'");
$new_product_stmt->execute();
$new_products = $new_product_stmt->fetchAll(PDO::FETCH_ASSOC);

// ... then add it to the response array
$config['newProducts'] = $new_products; 
```

**B. Processing Registration Data (`processRegistration` function)**

This function handles the incoming registration data. It calculates the total price and saves the selected products to the `registration_products` table.

- **Location:** `api.php`, inside the `processRegistration()` function.
- **Action:** Add a block to process your new product's data from the `$data` payload.
The data structure for grouped products (Tables, Sponsorships) is an array of objects `{type, count}`. Follow this pattern.

```php
// Inside processRegistration(), after processing existing products

// *** ADD LOGIC FOR YOUR NEW PRODUCT TYPE HERE ***
if (isset($data['newProducts']) && is_array($data['newProducts'])) {
    foreach ($data['newProducts'] as $item) {
        $productName = $item['type'];
        $quantity = $item['count'];

        // Fetch product details to get the price
        $product_stmt = $pdo->prepare("SELECT price FROM products WHERE name = ? AND product_type = 'new_product_type'");
        $product_stmt->execute([$productName]);
        $product = $product_stmt->fetch(PDO::FETCH_ASSOC);

        if ($product && $quantity > 0) {
            $price = $product['price'];
            $totalAmount += $price * $quantity;

            // Insert into registration_products
            $stmt = $pdo->prepare("INSERT INTO registration_products (registration_id, product_name, quantity, price_per_item) VALUES (?, ?, ?, ?)");
            $stmt->execute([$registrationId, $productName, $quantity, $price]);
        }
    }
}
```

**C. Stripe Integration (`createStripeCheckoutSession` function)**

This function builds the list of items to be sent to Stripe for payment.

- **Location:** `api.php`, inside the `createStripeCheckoutSession()` function.
- **Action:** Add your new product to the `$line_items` array. Ensure the name, description, and price are correctly formatted.

```php
// Inside createStripeCheckoutSession(), within the loop that builds line items

// Example for 'sponsorships'
if (isset($formData['sponsorships']) && is_array($formData['sponsorships'])) {
    foreach ($formData['sponsorships'] as $item) {
        // ... logic to add sponsorship to line items
    }
}

// *** ADD LOGIC FOR YOUR NEW PRODUCT TYPE HERE ***
if (isset($formData['newProducts']) && is_array($formData['newProducts'])) {
    foreach ($formData['newProducts'] as $item) {
        if ($item['count'] > 0) {
            $product_stmt = $pdo->prepare("SELECT name, description, price FROM products WHERE name = ? AND product_type = 'new_product_type'");
            $product_stmt->execute([$item['type']]);
            $product = $product_stmt->fetch(PDO::FETCH_ASSOC);

            if ($product) {
                $line_items[] = [
                    'price_data' => [
                        'currency' => 'usd',
                        'product_data' => [
                            'name' => $product['name'],
                            'description' => $product['description'] ?? 'Event Sponsorship',
                        ],
                        'unit_amount' => $product['price'] * 100, // Price in cents
                    ],
                    'quantity' => $item['count'],
                ];
            }
        }
    }
}
```

---

## 2. Frontend UI (`registration.js`)

This section covers the client-side implementation within the `RegistrationForm` class in `registration.js`.

### 2.1. Initializing `formData`

First, add a new array to the `this.formData` object in the `RegistrationForm` constructor. This will hold the user's selections for the new product type.

- **Location:** `registration.js`, inside the `constructor()`.
- **Action:** Add a new property to `this.formData`.

```javascript
// Inside constructor()
this.formData = {
    // ... existing properties
    sponsorships: [],
    otherSponsorships: [],
    newProducts: [], // *** ADD THIS LINE ***
    // ... other properties
};
```

### 2.2. Rendering the Product Section

You need to create a new method to render the HTML for your product section. This method will be called from `populateStep1()`.

- **Location:** `registration.js`, within the `RegistrationForm` class.
- **Action:** Create a new `renderNewProductSection()` method. Use the existing `renderSponsorshipsSection` or `renderOtherSponsorshipsSection` as a template.

```javascript
// Inside RegistrationForm class

renderNewProductSection(newProducts, containerId) {
    const container = document.getElementById(containerId);
    if (!container || !newProducts || newProducts.length === 0) {
        // Hide the section if there's no data
        document.getElementById('new-product-section-container').style.display = 'none';
        return;
    }

    let content = '<h3>New Product Section Title</h3>';
    newProducts.forEach(product => {
        content += `
            <div class="pricing-option-card" data-product-type="new_product_type">
                <div class="card-header">
                    <label class="custom-checkbox">
                        <input type="checkbox" name="newProductType" value="${product.name}" data-price="${product.price}">
                        <span>${product.name}</span>
                    </label>
                    <div class="price">$${product.price}</div>
                </div>
                <div class="card-body" style="display: none;">
                    <p>${product.description}</p>
                    <div class="quantity-control">
                        <button type="button" class="quantity-btn minus" disabled>-</button>
                        <input type="number" class="quantity-input" value="1" min="1" disabled>
                        <button type="button" class="quantity-btn plus">+</button>
                    </div>
                </div>
            </div>
        `;
    });

    container.innerHTML = content;
    // Show the section now that it has content
    document.getElementById('new-product-section-container').style.display = 'block';
}
```

**Important:** You must also add a corresponding container element in `index.html` for this section.

```html
<!-- In index.html, within Step 1 -->
<div id="new-product-section-container" style="display: none;">
    <div id="new-products-container"></div>
</div>
```

### 2.3. Calling the Render Method

In `populateStep1()`, call your new render method, passing the product data received from the backend.

- **Location:** `registration.js`, inside `populateStep1()`.
- **Action:** Call the new render method.

```javascript
// Inside populateStep1()
this.renderNewProductSection(this.config.newProducts, 'new-products-container');
```

### 2.4. Handling User Selections

Create a method to update `this.formData.newProducts` whenever a user interacts with the new product options. This method should be called from your event listeners.

- **Location:** `registration.js`, within the `RegistrationForm` class.
- **Action:** Create an `updateNewProductSelections()` method.

```javascript
// Inside RegistrationForm class

updateNewProductSelections() {
    this.formData.newProducts = [];
    const selectedItems = document.querySelectorAll('input[name="newProductType"]:checked');
    
    selectedItems.forEach(item => {
        const card = item.closest('.pricing-option-card');
        const quantityInput = card.querySelector('.quantity-input');
        const count = parseInt(quantityInput.value, 10);

        if (count > 0) {
            this.formData.newProducts.push({
                type: item.value, 
                count: count
            });
        }
    });

    this.updateSummary(); // This is crucial to keep the order summary updated
}
```

### 2.5. Attaching Event Listeners

In the `setupEventListeners()` method, add event listeners for your new product section. These listeners will trigger the update logic.

- **Location:** `registration.js`, inside `setupEventListeners()`.
- **Action:** Add a `change` event listener to the container.

```javascript
// Inside setupEventListeners()
document.getElementById('new-products-container').addEventListener('change', (event) => {
    if (event.target.matches('input[type="checkbox"], .quantity-input')) {
        this.updateNewProductSelections();
    }
});

document.getElementById('new-products-container').addEventListener('click', (event) => {
    // Logic for plus/minus buttons
    if (event.target.matches('.quantity-btn')) {
        // Add logic to handle quantity changes and enable/disable components
        // ... (see `renderSponsorshipsSection` for a detailed example)
        this.updateNewProductSelections();
    }
});
```

### 2.6. Updating the Order Summary

The `updateSummary()` method needs to be aware of the new product type to calculate the total price correctly.

- **Location:** `registration.js`, inside `updateSummary()`.
- **Action:** Add logic to iterate over `this.formData.newProducts`.

```javascript
// Inside updateSummary()

// *** ADD THIS BLOCK ***
if (this.formData.newProducts && this.formData.newProducts.length > 0) {
    summaryHtml += '<h4>New Products</h4><ul>';
    this.formData.newProducts.forEach(item => {
        const productDetails = this.config.newProducts.find(p => p.name === item.type);
        if (productDetails) {
            const price = parseFloat(productDetails.price);
            const itemTotal = price * item.count;
            total += itemTotal;
            summaryHtml += `<li>${item.type} (x${item.count}) - $${itemTotal.toFixed(2)}</li>`;
        }
    });
    summaryHtml += '</ul>';
}
```

### 2.7. Displaying in the Review Step

Finally, ensure the selected new products are displayed on the final review page (Step 4).

- **Location:** `registration.js`, inside `populateReview()`.
- **Action:** Add a block to render the new product selections.

```javascript
// Inside populateReview(), within the pass-details section

// *** ADD THIS BLOCK ***
if (this.formData.newProducts && this.formData.newProducts.length > 0) {
    reviewHtml += '<h5>New Products</h5>';
    this.formData.newProducts.forEach(item => {
        const productDetails = this.config.newProducts.find(p => p.name === item.type);
        if (productDetails) {
            reviewHtml += `<p>${item.type}: ${item.count} @ $${parseFloat(productDetails.price).toFixed(2)}</p>`;
        }
    });
}
```

---

## 3. Verification and Testing

Thorough testing is crucial to ensure the new product type is integrated seamlessly. Follow this testing plan:

1.  **Step 1: Pass Selection**
    - [ ] Verify the new product section appears correctly, with the right title, names, prices, and descriptions.
    - [ ] Test the checkbox functionality: clicking the checkbox should reveal the quantity controls.
    - [ ] Test the quantity controls (+/- buttons and direct input).
    - [ ] Check that the Order Summary updates in real-time with the correct item, quantity, and total price.

2.  **Data Persistence**
    - [ ] Select a new product, navigate to the next step, and then navigate back. The selection and quantity should persist.

3.  **Step 4: Review**
    - [ ] Proceed to the Review step (Step 4).
    - [ ] Verify that the new product is listed correctly under the Pass Selection details with the correct name, quantity, and price.

4.  **Database**
    - [ ] Complete a test registration (using Bank Transfer to avoid a real charge).
    - [ ] Check the `registrations` table for the new registration record.
    - [ ] Check the `registration_products` table to ensure a record was inserted for your new product with the correct `registration_id`, `product_name`, `quantity`, and `price_per_item`.

5.  **Stripe Payment**
    - [ ] Initiate a payment with the new product selected.
    - [ ] Use the browser's developer tools to inspect the network request made when you click the Stripe payment button.
    - [ ] **Crucially, check the console logs**. The Stripe payment payload is logged. Confirm that your new product is included in the `line_items` with the correct name, description, quantity, and price (in cents).

---

## 4. Final Checklist

Use this checklist to confirm you've completed all necessary steps.

**Backend / DB:**
- [ ] New product added to the `products` table with a unique `product_type`.
- [ ] `api.php` (`getConfiguration`): Added logic to fetch the new product type.
- [ ] `api.php` (`processRegistration`): Added logic to process the new product from form data and save it to `registration_products`.
- [ ] `api.php` (`createStripeCheckoutSession`): Added logic to include the new product in Stripe's `line_items`.

**Frontend (`registration.js`):**
- [ ] `constructor`: Added a `newProducts: []` array to `this.formData`.
- [ ] `index.html`: Added a container `<div>` for the new product section.
- [ ] `RegistrationForm` class: Created a `renderNewProductSection()` method.
- [ ] `populateStep1()`: Called the new render method.
- [ ] `RegistrationForm` class: Created an `updateNewProductSelections()` method to handle user input.
- [ ] `setupEventListeners()`: Added event listeners for the new section.
- [ ] `updateSummary()`: Added logic to include the new product in the summary calculation.
- [ ] `populateReview()`: Added logic to display the new product on the review page.


