Files
orion/docs/__temp/FRONTEND/FRONTEND_ALPINE_PAGE_TEMPLATE.md

8.1 KiB
Raw Blame History

Alpine.js Page Template - Quick Reference

Correct Page Structure

// static/admin/js/your-page.js

// 1. Setup logging (optional but recommended)
const YOUR_PAGE_LOG_LEVEL = 3;

const yourPageLog = {
    error: (...args) => YOUR_PAGE_LOG_LEVEL >= 1 && console.error('❌ [YOUR_PAGE ERROR]', ...args),
    warn: (...args) => YOUR_PAGE_LOG_LEVEL >= 2 && console.warn('⚠️ [YOUR_PAGE WARN]', ...args),
    info: (...args) => YOUR_PAGE_LOG_LEVEL >= 3 && console.info(' [YOUR_PAGE INFO]', ...args),
    debug: (...args) => YOUR_PAGE_LOG_LEVEL >= 4 && console.log('🔍 [YOUR_PAGE DEBUG]', ...args)
};

// 2. Create your Alpine.js component
function yourPageComponent() {
    return {
        // ✅ CRITICAL: Inherit base layout functionality
        ...data(),

        // ✅ CRITICAL: Set page identifier
        currentPage: 'your-page',

        // Your page-specific state
        items: [],
        loading: false,
        error: null,

        // ✅ CRITICAL: Proper initialization with guard
        async init() {
            yourPageLog.info('=== YOUR PAGE INITIALIZING ===');
            
            // Prevent multiple initializations
            if (window._yourPageInitialized) {
                yourPageLog.warn('Page already initialized, skipping...');
                return;
            }
            window._yourPageInitialized = true;

            // Load your data
            await this.loadData();
            
            yourPageLog.info('=== YOUR PAGE INITIALIZATION COMPLETE ===');
        },

        // Your methods
        async loadData() {
            yourPageLog.info('Loading data...');
            this.loading = true;
            this.error = null;
            
            try {
                const startTime = Date.now();
                // ✅ CRITICAL: Use lowercase apiClient
                const response = await apiClient.get('/your/endpoint');
                const duration = Date.now() - startTime;
                
                this.items = response.items || [];
                
                yourPageLog.info(`Data loaded in ${duration}ms`, {
                    count: this.items.length
                });
                
            } catch (error) {
                yourPageLog.error('Failed to load data:', error);
                this.error = error.message;
                Utils.showToast('Failed to load data', 'error');
            } finally {
                this.loading = false;
            }
        },

        // Format date helper (if needed)
        formatDate(dateString) {
            if (!dateString) return '-';
            return Utils.formatDate(dateString);
        },

        // Your other methods...
    };
}

yourPageLog.info('Your page module loaded');

🎯 Checklist for New Pages

HTML Template

{# app/templates/admin/your-page.html #}
{% extends "admin/base.html" %}

{% block title %}Your Page{% endblock %}

{# ✅ CRITICAL: Link to your Alpine.js component #}
{% block alpine_data %}yourPageComponent(){% endblock %}

{% block content %}
<!-- Your page content -->
{% endblock %}

{% block extra_scripts %}
{# ✅ CRITICAL: Load your JavaScript file #}
<script src="{{ url_for('static', path='admin/js/your-page.js') }}"></script>
{% endblock %}

JavaScript File Checklist

  • Logging setup (optional)
  • Function name matches alpine_data in template
  • ...data(), at start of return object
  • currentPage: 'your-page' set
  • Initialization guard in init()
  • Use lowercase apiClient for API calls
  • Use your custom logger (not Logger)
  • Performance tracking with Date.now() (optional)
  • Module loaded log at end

Common Mistakes to Avoid

1. Missing Base Inheritance

// ❌ WRONG
function myPage() {
    return {
        items: [],
        // Missing ...data()
    };
}

// ✅ CORRECT
function myPage() {
    return {
        ...data(),  // Must be first!
        items: [],
    };
}

2. Wrong API Client Name

// ❌ WRONG - Capital letters
await ApiClient.get('/endpoint');
await API_CLIENT.get('/endpoint');

// ✅ CORRECT - lowercase
await apiClient.get('/endpoint');

3. Missing Init Guard

// ❌ WRONG
async init() {
    await this.loadData();
}

// ✅ CORRECT
async init() {
    if (window._myPageInitialized) return;
    window._myPageInitialized = true;
    await this.loadData();
}

4. Missing currentPage

// ❌ WRONG
return {
    ...data(),
    items: [],
    // Missing currentPage
};

// ✅ CORRECT
return {
    ...data(),
    currentPage: 'my-page',  // Must set this!
    items: [],
};

🔧 API Client Pattern

GET Request

try {
    const response = await apiClient.get('/endpoint');
    this.data = response;
} catch (error) {
    console.error('Failed:', error);
    Utils.showToast('Failed to load', 'error');
}

POST Request

try {
    const response = await apiClient.post('/endpoint', {
        name: 'value',
        // ... data
    });
    Utils.showToast('Created successfully', 'success');
} catch (error) {
    console.error('Failed:', error);
    Utils.showToast('Failed to create', 'error');
}

PUT Request

try {
    const response = await apiClient.put('/endpoint/123', {
        name: 'updated value'
    });
    Utils.showToast('Updated successfully', 'success');
} catch (error) {
    console.error('Failed:', error);
    Utils.showToast('Failed to update', 'error');
}

DELETE Request

try {
    await apiClient.delete('/endpoint/123');
    Utils.showToast('Deleted successfully', 'success');
    await this.reloadData();
} catch (error) {
    console.error('Failed:', error);
    Utils.showToast('Failed to delete', 'error');
}

🎨 Common UI Patterns

Loading State

async loadData() {
    this.loading = true;
    try {
        const data = await apiClient.get('/endpoint');
        this.items = data;
    } finally {
        this.loading = false;
    }
}

Error Handling

async loadData() {
    this.loading = true;
    this.error = null;
    try {
        const data = await apiClient.get('/endpoint');
        this.items = data;
    } catch (error) {
        this.error = error.message;
        Utils.showToast('Failed to load', 'error');
    } finally {
        this.loading = false;
    }
}

Refresh/Reload

async refresh() {
    console.info('Refreshing...');
    await this.loadData();
    Utils.showToast('Refreshed successfully', 'success');
}

📚 Available Utilities

From init-alpine.js (via ...data())

  • this.dark - Dark mode state
  • this.toggleTheme() - Toggle theme
  • this.isSideMenuOpen - Side menu state
  • this.toggleSideMenu() - Toggle side menu
  • this.closeSideMenu() - Close side menu
  • this.isNotificationsMenuOpen - Notifications menu state
  • this.toggleNotificationsMenu() - Toggle notifications
  • this.closeNotificationsMenu() - Close notifications
  • this.isProfileMenuOpen - Profile menu state
  • this.toggleProfileMenu() - Toggle profile menu
  • this.closeProfileMenu() - Close profile menu
  • this.isPagesMenuOpen - Pages menu state
  • this.togglePagesMenu() - Toggle pages menu

From Utils (global)

  • Utils.showToast(message, type, duration) - Show toast notification
  • Utils.formatDate(dateString) - Format date for display
  • Utils.confirm(message, title) - Show confirmation dialog (if available)

From apiClient (global)

  • apiClient.get(url) - GET request
  • apiClient.post(url, data) - POST request
  • apiClient.put(url, data) - PUT request
  • apiClient.delete(url) - DELETE request

🚀 Quick Start Template Files

Copy these to create a new page:

  1. Copy dashboard.js → rename to your-page.js
  2. Replace function name: adminDashboard()yourPageComponent()
  3. Update logging prefix: dashLogyourPageLog
  4. Update init flag: _dashboardInitialized_yourPageInitialized
  5. Update currentPage: 'dashboard''your-page'
  6. Replace data loading logic with your endpoints
  7. Update HTML template to use your function name

Done!