# Dashboard & Admin Features

## Overview

The EventBookings Shopify app dashboard provides a comprehensive admin interface for managing EventBookings integration, event settings, and widget configuration. The dashboard is built as an embedded Shopify app with a tab-based interface.

## Dashboard Architecture

### Tab Structure
1. **Settings Tab** - EventBookings API configuration and connection management
2. **Events Tab** - Event listing, management, and toggle controls
3. **Widgets Tab** - Widget configuration and theme integration guide

### Technologies Used
- **Backend**: PHP with framework controllers
- **Frontend**: HTML, CSS, JavaScript (no framework dependencies)
- **Styling**: Bootstrap 5 + Custom Polaris-inspired CSS
- **App Bridge**: Shopify App Bridge for embedded app functionality

## Dashboard Controller Implementation

```php
<?php

namespace App\Http\Controllers;

use App\Models\ShopifyStore;
use App\Models\EventToggle;
use App\Services\TokenManager;
use App\Services\EventBookingsApi;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;

class DashboardController extends Controller
{
    private $tokenManager;
    private $eventBookingsApi;

    public function __construct(TokenManager $tokenManager, EventBookingsApi $eventBookingsApi)
    {
        $this->tokenManager = $tokenManager;
        $this->eventBookingsApi = $eventBookingsApi;

        // Apply authentication middleware
        $this->middleware('auth.shopify');
    }

    /**
     * Display the main dashboard
     */
    public function index(Request $request)
    {
        $store = $request->attributes->get('store');
        $currentTab = $request->query('tab', 'settings');

        // Load store settings
        $storeSettings = $store->storeSettings;

        // Prepare dashboard data based on current tab
        $dashboardData = [
            'store' => $store,
            'store_settings' => $storeSettings,
            'current_tab' => $currentTab,
            'shop_domain' => $store->shop_domain,
            'api_key' => config('shopify.client_id'),
            'csrf_token' => csrf_token(),
        ];

        // Load tab-specific data
        switch ($currentTab) {
            case 'events':
                $dashboardData = array_merge($dashboardData, $this->getEventsTabData($store));
                break;
            case 'widgets':
                $dashboardData = array_merge($dashboardData, $this->getWidgetsTabData($store));
                break;
            case 'settings':
            default:
                // Settings data already loaded
                break;
        }

        return view('dashboard', $dashboardData);
    }

    /**
     * Handle dashboard form submissions
     */
    public function handleAction(Request $request)
    {
        $store = $request->attributes->get('store');
        $action = $request->input('action');
        $tab = $request->input('tab', 'settings');

        try {
            switch ($action) {
                case 'save':
                    return $this->handleSaveSettings($request, $store);

                case 'disconnect':
                    return $this->handleDisconnect($request, $store);

                case 'sync_events':
                    return $this->handleSyncEvents($request, $store);

                case 'update_toggle':
                    return $this->handleUpdateToggle($request, $store);

                default:
                    return back()->with('error', 'Unknown action: ' . $action);
            }

        } catch (\Exception $e) {
            Log::error('Dashboard action failed', [
                'store_id' => $store->id,
                'action' => $action,
                'error' => $e->getMessage()
            ]);

            return back()->with('error', 'Action failed: ' . $e->getMessage());
        }
    }

    /**
     * Handle settings save action
     */
    private function handleSaveSettings(Request $request, ShopifyStore $store)
    {
        $request->validate([
            'client_id' => 'required|string|max:255',
            'client_secret' => 'required|string|max:255',
        ]);

        $clientId = $request->input('client_id');
        $clientSecret = $request->input('client_secret');

        try {
            // Test connection with provided credentials
            $connectionResult = $this->eventBookingsApi->testConnection($clientId, $clientSecret);

            if ($connectionResult['success']) {
                // Save settings with connection data
                $store->storeSettings->update([
                    'client_id' => $clientId,
                    'client_secret' => $clientSecret,
                    'access_token' => $connectionResult['data']['access_token'],
                    'org_uuid' => $connectionResult['data']['org_uuid'],
                    'token_expires_at' => $connectionResult['data']['expires_at'],
                    'is_connected' => true,
                    'connection_error' => ''
                ]);

                return redirect()
                    ->route('dashboard', ['shop' => $store->shop_domain, 'tab' => 'settings'])
                    ->with('success', 'EventBookings connected successfully!');

            } else {
                return back()
                    ->withInput()
                    ->with('error', 'Connection failed: ' . $connectionResult['message']);
            }

        } catch (\Exception $e) {
            Log::error('Settings save failed', [
                'store_id' => $store->id,
                'error' => $e->getMessage()
            ]);

            return back()
                ->withInput()
                ->with('error', 'Connection failed: ' . $e->getMessage());
        }
    }

    /**
     * Handle disconnect action
     */
    private function handleDisconnect(Request $request, ShopifyStore $store)
    {
        $store->storeSettings->update([
            'access_token' => '',
            'org_uuid' => '',
            'token_expires_at' => null,
            'is_connected' => false,
            'connection_error' => ''
        ]);

        // Clear cached data
        Cache::forget("events_api_{$store->shop_domain}_all");
        Cache::forget("events_api_{$store->shop_domain}_featured");

        return redirect()
            ->route('dashboard', ['shop' => $store->shop_domain, 'tab' => 'settings'])
            ->with('success', 'EventBookings disconnected successfully.');
    }

    /**
     * Handle sync events action
     */
    private function handleSyncEvents(Request $request, ShopifyStore $store)
    {
        $storeSettings = $store->storeSettings;

        if (!$storeSettings->is_connected) {
            return back()->with('error', 'EventBookings not connected. Please configure settings first.');
        }

        try {
            $result = $this->tokenManager->fetchEventsWithRetry($storeSettings);

            if ($result['success']) {
                $eventsCount = count($result['data']['data'] ?? []);

                // Clear cache to force refresh
                Cache::forget("events_api_{$store->shop_domain}_all");
                Cache::forget("events_api_{$store->shop_domain}_featured");

                return redirect()
                    ->route('dashboard', ['shop' => $store->shop_domain, 'tab' => 'events'])
                    ->with('success', "Successfully synced {$eventsCount} events from EventBookings.");
            } else {
                return back()->with('error', 'Sync failed: ' . $result['message']);
            }

        } catch (\Exception $e) {
            Log::error('Event sync failed', [
                'store_id' => $store->id,
                'error' => $e->getMessage()
            ]);

            return back()->with('error', 'Sync failed: ' . $e->getMessage());
        }
    }

    /**
     * Handle event toggle update
     */
    private function handleUpdateToggle(Request $request, ShopifyStore $store)
    {
        $request->validate([
            'event_uuid' => 'required|string|max:255',
            'toggle_type' => 'required|in:show_event,featured_event',
            'value' => 'required|in:0,1',
        ]);

        $eventUuid = $request->input('event_uuid');
        $toggleType = $request->input('toggle_type');
        $value = (bool) $request->input('value');

        try {
            $toggle = EventToggle::updateOrCreate(
                ['store_id' => $store->id, 'event_uuid' => $eventUuid],
                [$toggleType => $value]
            );

            // Clear cached data
            Cache::forget("events_api_{$store->shop_domain}_all");
            Cache::forget("events_api_{$store->shop_domain}_featured");

            return response()->json([
                'success' => true,
                'message' => 'Event toggle updated successfully'
            ]);

        } catch (\Exception $e) {
            Log::error('Toggle update failed', [
                'store_id' => $store->id,
                'event_uuid' => $eventUuid,
                'toggle_type' => $toggleType,
                'error' => $e->getMessage()
            ]);

            return response()->json([
                'success' => false,
                'message' => 'Toggle update failed: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Get data for events tab
     */
    private function getEventsTabData(ShopifyStore $store): array
    {
        $storeSettings = $store->storeSettings;
        $eventsData = [];
        $syncMessage = null;

        if ($storeSettings->is_connected) {
            try {
                $result = $this->tokenManager->fetchEventsWithRetry($storeSettings);

                if ($result['success']) {
                    $events = $result['data']['data'] ?? [];

                    // Get event toggles
                    $toggles = EventToggle::where('store_id', $store->id)
                                         ->get()
                                         ->keyBy('event_uuid');

                    // Enhance events with toggle information
                    $eventsData = array_map(function($event) use ($toggles) {
                        $eventUuid = $event['uuid'] ?? '';
                        $toggle = $toggles->get($eventUuid);

                        return array_merge($event, [
                            'show_event' => $toggle ? $toggle->show_event : true,
                            'featured_event' => $toggle ? $toggle->featured_event : false,
                        ]);
                    }, $events);

                } else {
                    $syncMessage = 'Unable to fetch events: ' . $result['message'];
                }

            } catch (\Exception $e) {
                $syncMessage = 'Error loading events: ' . $e->getMessage();
            }
        }

        return [
            'events_data' => json_encode($eventsData),
            'sync_message' => $syncMessage
        ];
    }

    /**
     * Get data for widgets tab
     */
    private function getWidgetsTabData(ShopifyStore $store): array
    {
        $baseUrl = config('app.url');

        return [
            'widget_urls' => [
                'events' => "{$baseUrl}/widgets/events.js?shop=" . urlencode($store->shop_domain),
                'featured' => "{$baseUrl}/widgets/featured.js?shop=" . urlencode($store->shop_domain),
            ]
        ];
    }
}
```

## Dashboard View Template

**File: `resources/views/dashboard.blade.php`**

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Event Bookings Dashboard</title>

    <!-- Shopify App Bridge -->
    <script src="https://unpkg.com/@shopify/app-bridge@3"></script>

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

    <style>
        :root {
            --polaris-surface: #ffffff;
            --polaris-surface-subdued: #fafbfb;
            --polaris-border: #e1e3e5;
            --polaris-text: #202223;
            --polaris-text-subdued: #6d7175;
            --polaris-primary: #005bd3;
            --polaris-primary-dark: #004bbf;
            --polaris-success: #00a651;
            --polaris-warning: #ffc453;
            --polaris-critical: #d72c0d;
            --polaris-border-radius: 12px;
            --polaris-shadow: 0 1px 0 rgba(22, 29, 37, 0.05);
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
            margin: 0;
            padding: 24px;
            background-color: #fafbfb;
            color: #202223;
            min-height: 100vh;
            box-sizing: border-box;
        }

        .app-header {
            background: white;
            border: 1px solid #e1e3e5;
            border-radius: 12px;
            padding: 24px;
            margin-bottom: 24px;
            box-shadow: 0 1px 0 rgba(22, 29, 37, 0.05);
        }

        .app-title {
            margin: 0;
            font-size: 20px;
            font-weight: 600;
            color: #202223;
            margin-bottom: 4px;
        }

        .app-subtitle {
            color: #6d7175;
            margin: 0;
            font-size: 14px;
        }

        /* Tab Navigation */
        .nav-tabs {
            background: var(--polaris-surface);
            border: 1px solid var(--polaris-border);
            border-radius: var(--polaris-border-radius);
            box-shadow: var(--polaris-shadow);
            padding: 0;
            margin-bottom: 24px;
            border-bottom: 1px solid var(--polaris-border);
        }

        .nav-tabs .nav-link {
            border: none;
            color: var(--polaris-text-subdued);
            font-weight: 500;
            padding: 16px 24px;
            border-radius: 0;
            border-bottom: 2px solid transparent;
            background: var(--polaris-surface-subdued);
            transition: all 0.2s ease;
        }

        .nav-tabs .nav-link:first-child {
            border-top-left-radius: var(--polaris-border-radius);
        }

        .nav-tabs .nav-link:last-child {
            border-top-right-radius: var(--polaris-border-radius);
        }

        .nav-tabs .nav-link:hover {
            color: var(--polaris-text);
            background: #f6f6f7;
            border-color: transparent;
        }

        .nav-tabs .nav-link.active {
            color: var(--polaris-primary);
            background: var(--polaris-surface);
            border-bottom-color: var(--polaris-primary);
            border-color: transparent;
        }

        /* Tab Content */
        .tab-content {
            background: var(--polaris-surface);
            border: 1px solid var(--polaris-border);
            border-radius: var(--polaris-border-radius);
            box-shadow: var(--polaris-shadow);
        }

        .tab-pane {
            padding: 24px;
        }

        /* Form Elements */
        .form-group {
            margin-bottom: 20px;
        }

        .form-label {
            display: block;
            margin-bottom: 6px;
            font-weight: 500;
            color: #202223;
            font-size: 14px;
        }

        .form-control {
            width: 100%;
            padding: 12px;
            border: 1px solid #c9cccf;
            border-radius: 6px;
            font-size: 14px;
            font-family: inherit;
            box-sizing: border-box;
            transition: border-color 0.2s ease;
        }

        .form-control:focus {
            outline: none;
            border-color: #005bd3;
            box-shadow: 0 0 0 2px rgba(0, 91, 211, 0.2);
        }

        .form-text {
            color: #6d7175;
            font-size: 13px;
            margin-top: 6px;
        }

        /* Buttons */
        .btn-primary {
            background-color: #005bd3;
            border-color: #005bd3;
            border-radius: 6px;
            font-weight: 500;
            padding: 12px 20px;
            font-size: 14px;
            min-width: 120px;
        }

        .btn-primary:hover {
            background-color: #004493;
            border-color: #004493;
        }

        .btn-disconnect {
            background-color: #dc3545;
            border-color: #dc3545;
            color: white;
            border-radius: 6px;
            font-weight: 500;
            padding: 12px 20px;
            font-size: 14px;
            min-width: 120px;
        }

        .btn-disconnect:hover {
            background-color: #c82333;
            border-color: #c82333;
        }

        /* Connection Status */
        .connection-status {
            padding: 12px 16px;
            border-radius: 6px;
            font-size: 14px;
            margin-bottom: 16px;
        }

        .connection-status.connected {
            background-color: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }

        .connection-status.error {
            background-color: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }

        /* Events Table */
        .table {
            width: 100%;
            border-collapse: collapse;
            font-size: 14px;
        }

        .table th,
        .table td {
            padding: 12px;
            text-align: left;
            border-bottom: 1px solid #e1e3e5;
        }

        .table th {
            background: #fafbfb;
            font-weight: 600;
            color: #202223;
        }

        .table tbody tr:hover {
            background: #f6f6f7;
        }

        /* Toggle Switch */
        .toggle-switch {
            position: relative;
            display: inline-block;
            width: 44px;
            height: 24px;
        }

        .toggle-switch input {
            opacity: 0;
            width: 0;
            height: 0;
        }

        .toggle-slider {
            position: absolute;
            cursor: pointer;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: #c9cccf;
            transition: 0.3s;
            border-radius: 24px;
        }

        .toggle-slider:before {
            position: absolute;
            content: "";
            height: 18px;
            width: 18px;
            left: 3px;
            bottom: 3px;
            background-color: white;
            transition: 0.3s;
            border-radius: 50%;
        }

        input:checked + .toggle-slider {
            background-color: #005bd3;
        }

        input:checked + .toggle-slider:before {
            transform: translateX(20px);
        }

        /* Status Badges */
        .status-badge {
            display: inline-block;
            padding: 4px 8px;
            border-radius: 4px;
            font-size: 12px;
            font-weight: 500;
            text-transform: capitalize;
        }

        .status-active {
            background: #d4f1d4;
            color: #0f5132;
        }

        .status-inactive {
            background: #fff3cd;
            color: #664d03;
        }

        /* Widget Cards */
        .widget-card {
            border: 1px solid #e1e3e5;
            border-radius: 12px;
            overflow: hidden;
            margin-bottom: 20px;
        }

        .widget-card-header {
            padding: 20px;
            border-bottom: 1px solid #e1e3e5;
            background: #fafbfb;
        }

        .embed-code-section {
            background: #f8f9fa;
            padding: 12px;
            border-radius: 6px;
            margin-bottom: 12px;
        }

        .embed-code-section textarea {
            font-family: 'SF Mono', Monaco, monospace;
            font-size: 11px;
            padding: 8px;
            border: 1px solid #ddd;
            border-radius: 4px;
            height: 60px;
            resize: none;
            width: 100%;
        }

        /* Responsive */
        @media (max-width: 768px) {
            body {
                padding: 12px;
            }

            .nav-tabs .nav-link {
                padding: 12px 16px;
            }

            .tab-pane {
                padding: 16px;
            }
        }
    </style>
</head>
<body>
    <div class="app-header">
        <h1 class="app-title">Event Bookings</h1>
        <p class="app-subtitle">Manage your event booking system for {{ $shop_domain }}</p>
    </div>

    <!-- Navigation Tabs -->
    <ul class="nav nav-tabs" id="mainTabs" role="tablist">
        <li class="nav-item" role="presentation">
            <button class="nav-link @if($current_tab == 'settings') active @endif"
                    id="settings-tab"
                    data-bs-toggle="tab"
                    data-bs-target="#settings-pane"
                    type="button"
                    role="tab">
                <i class="bi bi-gear-fill me-2"></i>Settings
            </button>
        </li>
        <li class="nav-item" role="presentation">
            <button class="nav-link @if($current_tab == 'events') active @endif"
                    id="events-tab"
                    data-bs-toggle="tab"
                    data-bs-target="#events-pane"
                    type="button"
                    role="tab">
                <i class="bi bi-calendar-event me-2"></i>Events
            </button>
        </li>
        <li class="nav-item" role="presentation">
            <button class="nav-link @if($current_tab == 'widgets') active @endif"
                    id="widgets-tab"
                    data-bs-toggle="tab"
                    data-bs-target="#widgets-pane"
                    type="button"
                    role="tab">
                <i class="bi bi-puzzle me-2"></i>Widgets
            </button>
        </li>
    </ul>

    <!-- Tab Content -->
    <div class="tab-content" id="mainTabContent">

        <!-- Settings Tab -->
        <div class="tab-pane fade @if($current_tab == 'settings') show active @endif"
             id="settings-pane"
             role="tabpanel">

            @if(session('success'))
                <div class="alert alert-success">{{ session('success') }}</div>
            @endif

            @if(session('error'))
                <div class="alert alert-danger">{{ session('error') }}</div>
            @endif

            <form method="POST" action="{{ route('dashboard.action', ['shop' => $shop_domain]) }}">
                @csrf
                <input type="hidden" name="tab" value="settings">

                <div class="form-group">
                    <label for="client_id" class="form-label">Client ID</label>
                    <input type="text"
                           id="client_id"
                           name="client_id"
                           class="form-control"
                           value="{{ old('client_id', $store_settings->client_id) }}"
                           placeholder="Enter your EventBookings client ID"
                           @if($store_settings->is_connected) disabled @endif
                           required>
                    <div class="form-text">Your EventBookings platform client identifier</div>
                </div>

                <div class="form-group">
                    <label for="client_secret" class="form-label">Client Secret</label>
                    <input type="password"
                           id="client_secret"
                           name="client_secret"
                           class="form-control"
                           value="{{ old('client_secret', $store_settings->client_secret) }}"
                           placeholder="Enter your EventBookings client secret"
                           @if($store_settings->is_connected) disabled @endif
                           required>
                    <div class="form-text">Keep this secure - it's used to authenticate with EventBookings</div>
                </div>

                <!-- Connection Status -->
                @if($store_settings->is_connected)
                    <div class="form-group">
                        <div class="connection-status connected">
                            ✅ Connected to EventBookings
                            @if($store_settings->org_uuid)
                                <br><small>Organization: {{ $store_settings->org_uuid }}</small>
                            @endif
                        </div>
                    </div>
                @elseif($store_settings->connection_error)
                    <div class="form-group">
                        <div class="connection-status error">
                            ❌ Connection Failed
                            <br><small>{{ $store_settings->connection_error }}</small>
                        </div>
                    </div>
                @endif

                <!-- Action Button -->
                @if($store_settings->is_connected)
                    <input type="hidden" name="action" value="disconnect">
                    <button type="submit" class="btn btn-disconnect">🔴 Disconnect</button>
                @else
                    <input type="hidden" name="action" value="save">
                    <button type="submit" class="btn btn-primary">💾 Save & Connect</button>
                @endif
            </form>
        </div>

        <!-- Events Tab -->
        <div class="tab-pane fade @if($current_tab == 'events') show active @endif"
             id="events-pane"
             role="tabpanel">

            <!-- Events Header -->
            <div class="d-flex justify-content-between align-items-center mb-4">
                <div>
                    <h2 class="h4 mb-1">Your Events</h2>
                    <p class="text-muted mb-0">Manage events from your EventBookings account</p>
                </div>
                @if($store_settings->is_connected)
                    <form method="POST" action="{{ route('dashboard.action', ['shop' => $shop_domain]) }}" class="d-inline">
                        @csrf
                        <input type="hidden" name="action" value="sync_events">
                        <input type="hidden" name="tab" value="events">
                        <button type="submit" class="btn btn-primary">
                            🔄 Sync Events
                        </button>
                    </form>
                @endif
            </div>

            @if(isset($sync_message))
                <div class="alert alert-info">{{ $sync_message }}</div>
            @endif

            <!-- Events Table Container -->
            <div id="events-list-container">
                @if($store_settings->is_connected)
                    <div id="events-table-placeholder">
                        <div class="text-center py-5">
                            <div class="spinner-border text-primary" role="status">
                                <span class="visually-hidden">Loading events...</span>
                            </div>
                            <p class="mt-3 text-muted">Loading events from EventBookings...</p>
                        </div>
                    </div>
                @else
                    <div class="text-center py-5">
                        <div class="display-1 mb-3">📅</div>
                        <h3>No Events Connected</h3>
                        <p class="text-muted">Connect your EventBookings account in Settings to display events</p>
                    </div>
                @endif
            </div>

            <!-- Hidden data for JavaScript -->
            <script type="application/json" id="events-data">
                {!! $events_data ?? '[]' !!}
            </script>
            <script type="application/json" id="dashboard-config">
                {
                    "shop": "{{ $shop_domain }}",
                    "apiBaseUrl": "{{ route('dashboard.action', ['shop' => $shop_domain]) }}",
                    "hasCredentials": {{ $store_settings->is_connected ? 'true' : 'false' }},
                    "csrfToken": "{{ $csrf_token }}"
                }
            </script>
        </div>

        <!-- Widgets Tab -->
        <div class="tab-pane fade @if($current_tab == 'widgets') show active @endif"
             id="widgets-pane"
             role="tabpanel">

            <!-- Widget Header -->
            <div class="mb-4">
                <h2 class="h4 mb-1">EventBookings Widgets</h2>
                <p class="text-muted mb-0">Add event widgets to your Shopify theme pages</p>
            </div>

            <!-- Widget Showcase -->
            <div class="row">
                <!-- Events List Widget -->
                <div class="col-lg-6 mb-4">
                    <div class="widget-card">
                        <div class="widget-card-header">
                            <h4 class="h5 mb-2">📅 Events List Widget</h4>
                            <p class="text-muted mb-3">Display all your events in a responsive grid layout</p>

                            <div class="embed-code-section">
                                <label class="form-label small">Theme Integration Code:</label>
                                <div class="d-flex">
                                    <textarea readonly class="flex-grow-1 me-2">&lt;div id="eventbookings-events-widget"&gt;&lt;/div&gt;
&lt;script src="{{ config('app.url') }}/widgets/events.js?shop={{ $shop_domain }}"&gt;&lt;/script&gt;</textarea>
                                    <button class="btn btn-outline-secondary btn-sm" onclick="copyToClipboard(this, 'events')">📋</button>
                                </div>
                            </div>

                            <button class="btn btn-primary btn-sm" onclick="previewWidget('events-list')">👁️ Preview Widget</button>
                        </div>
                    </div>
                </div>

                <!-- Featured Events Widget -->
                <div class="col-lg-6 mb-4">
                    <div class="widget-card">
                        <div class="widget-card-header">
                            <h4 class="h5 mb-2">⭐ Featured Events Widget</h4>
                            <p class="text-muted mb-3">Showcase your featured events in an attractive carousel</p>

                            <div class="embed-code-section">
                                <label class="form-label small">Theme Integration Code:</label>
                                <div class="d-flex">
                                    <textarea readonly class="flex-grow-1 me-2">&lt;div id="eventbookings-featured-widget"&gt;&lt;/div&gt;
&lt;script src="{{ config('app.url') }}/widgets/featured.js?shop={{ $shop_domain }}"&gt;&lt;/script&gt;</textarea>
                                    <button class="btn btn-outline-secondary btn-sm" onclick="copyToClipboard(this, 'featured')">📋</button>
                                </div>
                            </div>

                            <button class="btn btn-primary btn-sm" onclick="previewWidget('featured-events')">👁️ Preview Widget</button>
                        </div>
                    </div>
                </div>
            </div>

            <!-- Integration Guide -->
            <div class="widget-card">
                <div class="widget-card-header">
                    <h4 class="h5 mb-3">Theme Integration Guide</h4>

                    <!-- App Embed Blocks (Recommended) -->
                    <div class="alert alert-primary">
                        <h5 class="alert-heading">🏆 Recommended: App Embed Blocks</h5>
                        <p><strong>EventBookings widgets are available as native Shopify App Embed Blocks!</strong></p>
                        <ol class="mb-0">
                            <li><strong>Go to Shopify Admin → Online Store → Themes</strong></li>
                            <li><strong>Click "Customize"</strong> on your active theme</li>
                            <li><strong>Navigate to any page/section</strong> where you want events</li>
                            <li><strong>Click "Add block" or "Add section"</strong></li>
                            <li><strong>Look for "EventBookings" in the Apps section</strong></li>
                            <li><strong>Choose "Events List" or "Featured Events"</strong></li>
                            <li><strong>Customize the settings and save</strong></li>
                        </ol>
                    </div>

                    <!-- Custom HTML Method -->
                    <div class="mt-4">
                        <h6>Alternative: Custom HTML Method</h6>
                        <p class="text-muted">If you prefer custom HTML integration:</p>
                        <ol>
                            <li>Copy the widget code above</li>
                            <li>Go to Shopify Admin → Online Store → Themes</li>
                            <li>Click "Customize" on your active theme</li>
                            <li>Add a "Custom HTML" section</li>
                            <li>Paste the widget code and save</li>
                        </ol>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- Bootstrap JavaScript -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>

    <!-- Dashboard JavaScript -->
    <script>
        // Initialize Shopify App Bridge
        let app;
        try {
            app = ShopifyApp.createApp({
                apiKey: '{{ $api_key }}',
                host: new URLSearchParams(window.location.search).get('host') || ''
            });
        } catch (error) {
            console.log('App Bridge not available:', error);
        }

        // Handle messages
        @if(session('success') || session('error'))
            document.addEventListener('DOMContentLoaded', function() {
                const message = '{{ session('success') ?? session('error') }}';
                const isError = {{ session('error') ? 'true' : 'false' }};

                if (app && app.Toast) {
                    const toast = app.Toast.create(message, {
                        isError: isError,
                        duration: 3000
                    });
                    toast.dispatch(app.Toast.Action.SHOW);
                }
            });
        @endif

        // Events table rendering
        document.addEventListener('DOMContentLoaded', function() {
            const eventsData = JSON.parse(document.getElementById('events-data').textContent || '[]');
            const config = JSON.parse(document.getElementById('dashboard-config').textContent);

            if (config.hasCredentials && eventsData.length > 0) {
                renderEventsTable(eventsData);
            }
        });

        function renderEventsTable(events) {
            const container = document.getElementById('events-list-container');

            if (events.length === 0) {
                container.innerHTML = `
                    <div class="text-center py-5">
                        <div class="display-1 mb-3">📅</div>
                        <h3>No Events Found</h3>
                        <p class="text-muted">No events available or visible at this time</p>
                    </div>
                `;
                return;
            }

            let tableHTML = `
                <div class="table-responsive">
                    <table class="table">
                        <thead>
                            <tr>
                                <th>Event</th>
                                <th>Date</th>
                                <th>Location</th>
                                <th>Status</th>
                                <th>Visible</th>
                                <th>Featured</th>
                                <th>Actions</th>
                            </tr>
                        </thead>
                        <tbody>
            `;

            events.forEach(event => {
                const startDate = new Date(event.starts_on).toLocaleDateString();
                const status = event.status || 'published';

                tableHTML += `
                    <tr>
                        <td>
                            <strong>${escapeHtml(event.name || 'Untitled Event')}</strong>
                            <br><small class="text-muted">${escapeHtml(truncateText(event.description || '', 100))}</small>
                        </td>
                        <td>${startDate}</td>
                        <td>${escapeHtml(event.location || 'TBD')}</td>
                        <td><span class="status-badge status-${status}">${status}</span></td>
                        <td>
                            <label class="toggle-switch">
                                <input type="checkbox" ${event.show_event ? 'checked' : ''}
                                       onchange="updateEventToggle('${event.uuid}', 'show_event', this.checked)">
                                <span class="toggle-slider"></span>
                            </label>
                        </td>
                        <td>
                            <label class="toggle-switch">
                                <input type="checkbox" ${event.featured_event ? 'checked' : ''}
                                       onchange="updateEventToggle('${event.uuid}', 'featured_event', this.checked)">
                                <span class="toggle-slider"></span>
                            </label>
                        </td>
                        <td>
                            <button class="btn btn-sm btn-outline-primary" onclick="viewEventDetails('${event.uuid}')">
                                View Details
                            </button>
                        </td>
                    </tr>
                `;
            });

            tableHTML += `
                        </tbody>
                    </table>
                </div>
            `;

            container.innerHTML = tableHTML;
        }

        function updateEventToggle(eventUuid, toggleType, value) {
            const config = JSON.parse(document.getElementById('dashboard-config').textContent);

            fetch(config.apiBaseUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'X-CSRF-TOKEN': config.csrfToken
                },
                body: new URLSearchParams({
                    'action': 'update_toggle',
                    'event_uuid': eventUuid,
                    'toggle_type': toggleType,
                    'value': value ? '1' : '0'
                })
            })
            .then(response => response.json())
            .then(data => {
                if (data.success) {
                    if (app && app.Toast) {
                        const toast = app.Toast.create(`Event ${toggleType.replace('_', ' ')} updated successfully!`);
                        toast.dispatch(app.Toast.Action.SHOW);
                    }
                } else {
                    console.error('Toggle update failed:', data.message);
                    if (app && app.Toast) {
                        const toast = app.Toast.create(`Failed to update: ${data.message}`, { isError: true });
                        toast.dispatch(app.Toast.Action.SHOW);
                    }
                }
            })
            .catch(error => {
                console.error('Toggle update error:', error);
                if (app && app.Toast) {
                    const toast = app.Toast.create('Network error updating toggle', { isError: true });
                    toast.dispatch(app.Toast.Action.SHOW);
                }
            });
        }

        function viewEventDetails(eventUuid) {
            // TODO: Implement event details modal
            console.log('View details for event:', eventUuid);
        }

        function copyToClipboard(button, widgetType) {
            const textarea = button.parentElement.querySelector('textarea');
            navigator.clipboard.writeText(textarea.value).then(function() {
                const originalText = button.textContent;
                button.textContent = '✓';
                button.classList.add('btn-success');
                button.classList.remove('btn-outline-secondary');

                setTimeout(() => {
                    button.textContent = originalText;
                    button.classList.remove('btn-success');
                    button.classList.add('btn-outline-secondary');
                }, 1000);

                if (app && app.Toast) {
                    const toast = app.Toast.create(`${widgetType} widget code copied to clipboard!`);
                    toast.dispatch(app.Toast.Action.SHOW);
                }
            }).catch(function(err) {
                console.error('Failed to copy: ', err);
            });
        }

        function previewWidget(widgetType) {
            // TODO: Implement widget preview modal
            console.log('Preview widget:', widgetType);

            if (app && app.Toast) {
                const toast = app.Toast.create('Widget preview coming soon!');
                toast.dispatch(app.Toast.Action.SHOW);
            }
        }

        // Utility functions
        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }

        function truncateText(text, length) {
            if (text.length <= length) return text;
            return text.substr(0, length) + '...';
        }
    </script>
</body>
</html>
```

## Features Implementation

### Event Management Features

1. **Event Sync** - Fetch latest events from EventBookings API
2. **Visibility Toggle** - Show/hide events on storefront widgets
3. **Featured Toggle** - Mark events as featured for carousel display
4. **Event Details** - View comprehensive event information
5. **Real-time Updates** - AJAX-powered toggle updates without page refresh

### Widget Configuration Features

1. **Widget Code Generation** - Copy-paste integration codes
2. **Live Preview** - Modal preview of widget appearance
3. **Integration Guide** - Step-by-step theme integration instructions
4. **Multiple Widget Types** - Events list and featured events carousel
5. **Responsive Design** - Mobile-optimized widget display

### Security Features

1. **CSRF Protection** - All form submissions protected
2. **Input Validation** - Server-side validation for all inputs
3. **Authentication** - Shopify OAuth verification on all routes
4. **Error Handling** - Graceful error handling with user-friendly messages
5. **Rate Limiting** - API call throttling and caching

### User Experience Features

1. **Tab-based Interface** - Clean navigation between sections
2. **Loading States** - Visual feedback during operations
3. **Success/Error Messages** - Clear feedback for user actions
4. **Responsive Design** - Works on desktop and mobile devices
5. **Shopify App Bridge Integration** - Native embedded app experience

This dashboard implementation provides a comprehensive admin interface that matches the functionality of the original Django implementation while leveraging modern PHP development practices and maintaining excellent user experience.