# Global Sections - Builder V2 Integration

**Implemented:** 2025-10-28
**Status:** ✅ Complete

---

## Overview

Global sections (headers and footers) now use **Builder V2** (React + Craft.js) instead of the old custom builder. This provides a consistent editing experience across both pages and global sections.

---

## Problem Solved

**Previous Issue:**
- Editing global sections at `/websites/{website_id}/globals/{section_id}/edit` used the old builder
- Theme header/footer designs didn't appear when customizing
- Inconsistent UI between page editing and global section editing

**Solution:**
- Integrated Builder V2 for global sections
- Unified editing experience for both pages and global sections
- Same React components, canvas, and settings panel

---

## Technical Implementation

### 1. Backend Changes

#### Controller: `app/Http/Controllers/GlobalSectionController.php`

**Modified `edit()` method** (line 138):
```php
public function edit(Website $website, GlobalSection $global_section)
{
    $this->authorizeOwnership($website);

    if ($global_section->website_id !== $website->id) {
        abort(404);
    }

    // Use Builder V2 for global sections
    return view('globals.builder-v2', compact('website', 'global_section'));
}
```

**Added `loadV2()` method** (line 153):
```php
public function loadV2(Website $website, GlobalSection $global_section)
{
    $this->authorizeOwnership($website);

    if ($global_section->website_id !== $website->id) {
        abort(404);
    }

    // Return Builder V2 data or empty structure
    return response()->json([
        'success' => true,
        'data' => $global_section->builder_data ?? $this->getDefaultStructure($global_section->type),
        'name' => $global_section->name,
        'type' => $global_section->type,
    ]);
}
```

**Added `saveV2()` method** (line 173):
```php
public function saveV2(Request $request, Website $website, GlobalSection $global_section)
{
    $this->authorizeOwnership($website);

    if ($global_section->website_id !== $website->id) {
        abort(404);
    }

    $request->validate([
        'data' => 'required'
    ]);

    try {
        $global_section->update([
            'builder_data' => $request->data,
            'is_custom' => true,
        ]);

        return response()->json([
            'success' => true,
            'message' => ucfirst($global_section->type) . ' saved successfully!'
        ]);
    } catch (\Exception $e) {
        return response()->json([
            'success' => false,
            'message' => 'Failed to save: ' . $e->getMessage()
        ], 500);
    }
}
```

**Added `getDefaultStructure()` method** (line 206):
```php
private function getDefaultStructure($type)
{
    if ($type === 'header') {
        return [
            'ROOT' => [
                'type' => ['resolvedName' => 'Container'],
                'isCanvas' => true,
                'props' => [
                    'width' => 'fluid',
                    'padding' => '20px',
                    'backgroundColor' => '#ffffff',
                ],
                'displayName' => 'Container',
                'custom' => [],
                'hidden' => false,
                'nodes' => ['node-header'],
                'linkedNodes' => []
            ],
            'node-header' => [
                'type' => ['resolvedName' => 'Heading'],
                'isCanvas' => false,
                'props' => [
                    'text' => 'Your Header Here',
                    'level' => 'h3',
                    'color' => '#333333',
                    'align' => 'center',
                ],
                'displayName' => 'Heading',
                'custom' => [],
                'parent' => 'ROOT',
                'hidden' => false,
                'nodes' => [],
                'linkedNodes' => []
            ]
        ];
    } else {
        // Footer default structure
        return [/* footer structure */];
    }
}
```

### 2. Routes

**Added in `routes/web.php`** (lines 542-543):
```php
// Builder V2 endpoints for global sections
Route::get('/globals/{global_section}/load-v2', [App\Http\Controllers\GlobalSectionController::class, 'loadV2'])->name('globals.load-v2');
Route::post('/globals/{global_section}/save-v2', [App\Http\Controllers\GlobalSectionController::class, 'saveV2'])->name('globals.save-v2');
```

### 3. Frontend Changes

#### New View: `resources/views/globals/builder-v2.blade.php`

**Note:** CSS is automatically included via JavaScript import in `main.jsx` (line 48), so only the JS file needs to be loaded. Bootstrap CSS and Bootstrap Icons are required for toolbar styling.

```blade
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>Edit {{ ucfirst($global_section->type) }} - {{ $website->name }}</title>

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">

    <!-- Bootstrap Icons -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">

    @vite(['resources/js/builder-v2/main.jsx'])
</head>
<body>
    <div id="global-builder-root"></div>

    <script>
        window.BUILDER_CONFIG = {
            mode: 'global_section',
            globalSectionId: {{ $global_section->id }},
            websiteId: {{ $website->id }},
            sectionType: '{{ $global_section->type }}',
            sectionName: '{{ $global_section->name }}',
            loadUrl: '{{ route('globals.load-v2', [$website, $global_section]) }}',
            saveUrl: '{{ route('globals.save-v2', [$website, $global_section]) }}',
            exitUrl: '{{ route('globals.index', $website) }}',
            csrfToken: '{{ csrf_token() }}'
        };
    </script>
</body>
</html>
```

#### Modified: `resources/js/builder-v2/main.jsx`

**Updated to detect global section mode** (lines 50-114):
```javascript
const rootElement = document.getElementById('page-builder-v2-root') || document.getElementById('global-builder-root');

if (rootElement) {
    const isGlobalSection = window.BUILDER_CONFIG && window.BUILDER_CONFIG.mode === 'global_section';

    let config = {};

    if (isGlobalSection) {
        // Global Section Config
        config = {
            mode: 'global_section',
            globalSectionId: window.BUILDER_CONFIG.globalSectionId,
            websiteId: window.BUILDER_CONFIG.websiteId,
            sectionType: window.BUILDER_CONFIG.sectionType,
            sectionName: window.BUILDER_CONFIG.sectionName,
            loadUrl: window.BUILDER_CONFIG.loadUrl,
            saveUrl: window.BUILDER_CONFIG.saveUrl,
            exitUrl: window.BUILDER_CONFIG.exitUrl,
            csrfToken: window.BUILDER_CONFIG.csrfToken,
            // Defaults for page-specific props
            isThemePage: false,
            pageId: null,
            isNewPage: false,
            websiteModules: {}
        };
    } else {
        // Page Builder Config
        config = {
            mode: 'page',
            // ... page-specific config
        };
    }

    root.render(
        <Editor resolver={{ /* components */ }}>
            <PageBuilder config={config} />
        </Editor>
    );
}
```

#### Modified: `resources/js/builder-v2/components/PageBuilder.jsx`

**Updated to accept config object and handle both modes:**

```javascript
export default function PageBuilder({ config }) {
    const {
        mode = 'page',
        // Page mode props
        isThemePage, themeId, themeName, themePageId,
        websiteId, pageId, isNewPage,
        currency, currencySymbol, websiteModules,
        // Global section mode props
        globalSectionId, sectionType, sectionName,
        loadUrl, saveUrl, exitUrl,
        // Common
        csrfToken
    } = config;

    const isGlobalSection = mode === 'global_section';

    // ... component logic
}
```

**Updated `loadPageData()` to handle both modes:**
```javascript
const loadPageData = async () => {
    try {
        let url;

        if (isGlobalSection) {
            // Use loadUrl from config
            url = loadUrl;
        } else {
            // Construct URL for pages
            url = isThemePage
                ? `/admin/theme-builder/${themeId}/pages/${themePageId}/builder-v2/load`
                : `/websites/${websiteId}/pages/${pageId}/builder-v2/load`;
        }

        const response = await fetch(url, { /* ... */ });
        const result = await response.json();

        if (result.success) {
            if (isGlobalSection) {
                setPageTitle(result.name || sectionName);
                setPageData(result);
                if (result.data) {
                    setInitialData(result.data);
                }
            } else {
                // Page mode logic
            }
        }
    } catch (error) {
        // Error handling
    }
};
```

**Updated `handleSave()` to handle both modes:**
```javascript
const handleSave = async () => {
    setSaving(true);

    try {
        const builderData = query.serialize();
        const parsedBuilderData = typeof builderData === 'string' ? JSON.parse(builderData) : builderData;

        let url, payload;

        if (isGlobalSection) {
            url = saveUrl;
            payload = { data: parsedBuilderData };
        } else {
            url = /* construct page URL */;
            payload = {
                title: pageTitle,
                slug: pageSlug,
                builderData: parsedBuilderData
            };
        }

        const response = await fetch(url, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'X-CSRF-TOKEN': csrfToken,
                'Accept': 'application/json',
            },
            body: JSON.stringify(payload)
        });

        // Handle response
    } catch (error) {
        // Error handling
    } finally {
        setSaving(false);
    }
};
```

#### Modified: `resources/js/builder-v2/components/Toolbar.jsx`

**Updated to show different UI for global sections:**

```javascript
export default function Toolbar({
    mode = 'page',
    isGlobalSection = false,
    sectionName, sectionType, exitUrl,
    // ... other props
}) {
    // Determine back URL based on mode
    let backUrl, backLabel;

    if (isGlobalSection) {
        backUrl = exitUrl || `/websites/${websiteId}/globals`;
        backLabel = 'Exit to Globals';
    } else if (isThemePage) {
        backUrl = `/admin/theme-builder/builder/${themeId}`;
        backLabel = `Back to ${themeName || 'Theme'}`;
    } else {
        backUrl = `/websites/${websiteId}/pages`;
        backLabel = 'Back to Pages';
    }

    return (
        <div className="builder-toolbar">
            <div className="toolbar-left">
                <a href={backUrl} /* ... */>
                    <i className="bi bi-arrow-left"></i> {backLabel}
                </a>

                <div className="toolbar-divider"></div>

                {isGlobalSection ? (
                    <div className="page-info">
                        <div className="section-info-display">
                            <strong>{sectionName || pageTitle}</strong>
                            <span className="text-muted ms-2">
                                <i className={`bi bi-${sectionType === 'header' ? 'layout-text-window' : 'layout-text-window-reverse'} me-1`}></i>
                                {sectionType ? sectionType.charAt(0).toUpperCase() + sectionType.slice(1) : ''}
                            </span>
                        </div>
                    </div>
                ) : (
                    <div className="page-info">
                        {/* Title and slug inputs */}
                    </div>
                )}
            </div>

            {/* ... viewport controls ... */}

            <div className="toolbar-right">
                {!isGlobalSection && (
                    <button /* Preview button */>
                        <i className="bi bi-eye"></i> Preview
                    </button>
                )}

                <button onClick={onSave} disabled={saving}>
                    <i className="bi bi-save"></i>
                    {isGlobalSection ? 'Save Section' : 'Save Page'}
                </button>
            </div>
        </div>
    );
}
```

#### Added CSS: `resources/js/builder-v2/builder-v2.css`

```css
.section-info-display {
    display: flex;
    align-items: center;
    padding: 6px 12px;
    background: #f8f9fa;
    border-radius: 4px;
    font-size: 14px;
}
```

---

## User Flow

### Editing a Global Section:

1. **Navigate to globals management:**
   - Go to `/websites/{website_id}/globals`
   - See theme headers/footers in blue section
   - See custom sections in green section

2. **Customize a theme component:**
   - Click "Customize" on theme header/footer
   - System creates a copy in `global_sections` table
   - Redirects to Builder V2 editor

3. **Edit in Builder V2:**
   - Same drag-and-drop interface as page builder
   - Same component library in left sidebar
   - Same settings panel on right
   - Toolbar shows section name (not editable)
   - "Save Section" button instead of "Save Page"

4. **Save and exit:**
   - Click "Save Section" to save changes
   - Click "Exit to Globals" to return to globals management
   - Custom section now appears in green "Your Custom" section

---

## Key Differences: Page Mode vs Global Section Mode

| Feature | Page Mode | Global Section Mode |
|---------|-----------|---------------------|
| **Title/Slug** | Editable inputs | Read-only display of section name |
| **Preview Button** | Visible | Hidden (not applicable) |
| **Save Button** | "Save Page" | "Save Section" |
| **Back Button** | "Back to Pages" | "Exit to Globals" |
| **Load URL** | Constructed from page/theme IDs | Provided via config (`loadUrl`) |
| **Save URL** | Constructed from page/theme IDs | Provided via config (`saveUrl`) |
| **Payload** | `{ title, slug, builderData }` | `{ data }` |
| **Redirect after save** | Edit mode for new pages | No redirect (continue editing) |

---

## Database Schema

### `global_sections` table:
```
- id
- website_id
- type (header/footer)
- name
- is_custom (BOOLEAN)
- builder_data (JSON) - Builder V2 Craft.js format
- grapesjs_data (JSON) - Legacy format (kept for backward compatibility)
```

**Storage Format:**
```json
{
  "ROOT": {
    "type": { "resolvedName": "Container" },
    "isCanvas": true,
    "props": { "width": "fluid", "padding": "20px" },
    "nodes": ["node-1"]
  },
  "node-1": {
    "type": { "resolvedName": "Heading" },
    "props": { "text": "Header Title", "level": "h3" },
    "parent": "ROOT"
  }
}
```

---

## Benefits

### For Users:
- ✅ **Consistent Experience**: Same builder for both pages and global sections
- ✅ **Modern Interface**: React-based drag-and-drop builder
- ✅ **Visual Editing**: See changes in real-time
- ✅ **Component Library**: Access to all Builder V2 components
- ✅ **Settings Panel**: Easy property editing

### For Developers:
- ✅ **Unified Codebase**: Single builder system for all content
- ✅ **Data Integrity**: Pure JSON storage, no HTML parsing
- ✅ **Maintainability**: One system to maintain instead of two
- ✅ **Extensibility**: Easy to add new components
- ✅ **Mode Detection**: Clean separation via `mode` config

---

## Testing

### Test URL:
```
https://neosolvix.test/websites/24/globals/19/edit
```

### Test Cases:
1. ✅ Navigate to globals management page
2. ✅ Click "Customize" on theme header
3. ✅ Builder V2 opens with theme design loaded
4. ✅ Toolbar shows section name (not editable)
5. ✅ Component library available in left sidebar
6. ✅ Settings panel works on right
7. ✅ Drag and drop components work
8. ✅ "Save Section" saves changes
9. ✅ "Exit to Globals" returns to management page
10. ✅ Custom section appears in green section

---

## Related Documentation

- `PAGE_BUILDER_V2_GUIDE.md` - Complete Builder V2 documentation
- `PAGE_BUILDERS_REFERENCE.md` - All page builder systems
- `HEADER_FOOTER_INTEGRATION.md` - Theme integration
- `CLAUDE.md` - Project overview

---

## Summary

Global sections (headers and footers) now use Builder V2, providing a unified, modern editing experience. Users can customize theme components with the same drag-and-drop interface used for pages, ensuring consistency and data integrity.

**Mode Detection:**
- `mode: 'page'` - Regular page editing
- `mode: 'global_section'` - Header/footer editing

Both modes share the same React components, canvas, and UI, with minor differences in toolbar display and save behavior.
