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

15 KiB
Raw Blame History

Alpine.js Page Template - Quick Reference (WITH CENTRALIZED LOGGING)

Correct Page Structure

// static/admin/js/your-page.js (or vendor/shop)

// 1. ✅ Use centralized logger (ONE LINE!)
const yourPageLog = window.LogConfig.loggers.yourPage;
// OR create custom if not pre-configured
// const yourPageLog = window.LogConfig.createLogger('YOUR-PAGE', window.LogConfig.logLevel);

// 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 = performance.now();
                
                // Log API request
                const url = '/your/endpoint';
                window.LogConfig.logApiCall('GET', url, null, 'request');
                
                // ✅ CRITICAL: Use lowercase apiClient
                const response = await apiClient.get(url);
                
                // Log API response
                window.LogConfig.logApiCall('GET', url, response, 'response');
                
                this.items = response.items || [];
                
                // Log performance
                const duration = performance.now() - startTime;
                window.LogConfig.logPerformance('Load Data', duration);
                
                yourPageLog.info(`Data loaded successfully`, {
                    count: this.items.length,
                    duration: `${duration}ms`
                });
                
            } catch (error) {
                // Use centralized error logging
                window.LogConfig.logError(error, 'Load Data');
                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 %}

{% endblock %}

{% block extra_scripts %}
{# ✅ CRITICAL: Load your JavaScript file #}

{% endblock %}

JavaScript File Checklist

  • Use centralized logger (ONE line instead of 15!)
  • 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 window.LogConfig.logApiCall() for API logging
  • Use window.LogConfig.logPerformance() for performance
  • Use window.LogConfig.logError() for errors
  • Module loaded log at end

📦 Pre-configured Loggers by Frontend

Admin Frontend

window.LogConfig.loggers.vendors          // Vendor management
window.LogConfig.loggers.vendorTheme      // Theme customization
window.LogConfig.loggers.vendorUsers      // Vendor users
window.LogConfig.loggers.products         // Product management
window.LogConfig.loggers.inventory        // Inventory
window.LogConfig.loggers.orders           // Order management
window.LogConfig.loggers.users            // User management
window.LogConfig.loggers.audit            // Audit logs
window.LogConfig.loggers.dashboard        // Dashboard
window.LogConfig.loggers.imports          // Import operations

Vendor Frontend

window.LogConfig.loggers.dashboard        // Vendor dashboard
window.LogConfig.loggers.products         // Product management
window.LogConfig.loggers.inventory        // Inventory
window.LogConfig.loggers.orders           // Order management
window.LogConfig.loggers.theme            // Theme customization
window.LogConfig.loggers.settings         // Settings
window.LogConfig.loggers.analytics        // Analytics

Shop Frontend

window.LogConfig.loggers.catalog          // Product browsing
window.LogConfig.loggers.product          // Product details
window.LogConfig.loggers.search           // Search
window.LogConfig.loggers.cart             // Shopping cart
window.LogConfig.loggers.checkout         // Checkout
window.LogConfig.loggers.account          // User account
window.LogConfig.loggers.orders           // Order history
window.LogConfig.loggers.wishlist         // Wishlist

Common Mistakes to Avoid

1. Old Way vs New Way

// ❌ OLD WAY - 15 lines of duplicate code
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)
};

// ✅ NEW WAY - 1 line!
const yourPageLog = window.LogConfig.loggers.yourPage;

2. Missing Base Inheritance

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

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

3. Wrong API Client Name

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

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

4. Missing Init Guard

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

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

5. 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');
}

🔧 Centralized Logging Patterns

Basic Logging

const log = window.LogConfig.loggers.yourPage;

log.info('Page loaded');
log.warn('Connection slow');
log.error('Failed to load data', error);
log.debug('User data:', userData);

Grouped Logging

log.group('Loading Theme Data');
log.info('Fetching vendor...');
log.info('Fetching theme...');
log.info('Fetching presets...');
log.groupEnd();

API Call Logging

const url = '/api/vendors';

// Before request
window.LogConfig.logApiCall('GET', url, null, 'request');

// Make request
const data = await apiClient.get(url);

// After response
window.LogConfig.logApiCall('GET', url, data, 'response');

Error Logging

try {
    await saveTheme();
} catch (error) {
    window.LogConfig.logError(error, 'Save Theme');
}

Performance Logging

const start = performance.now();
await loadThemeData();
const duration = performance.now() - start;
window.LogConfig.logPerformance('Load Theme Data', duration);

Table Logging

log.table([
    { id: 1, name: 'Vendor A', status: 'active' },
    { id: 2, name: 'Vendor B', status: 'inactive' }
]);

📚 Benefits of Centralized Logging

Aspect Old Way New Way
Lines of code ~15 per file 1 line per file
Consistency Varies by file Unified across all frontends
Maintenance Update each file Update one shared file
Features Basic logging Advanced (groups, perf, API)
Environment Manual config Auto-detected
Frontend aware No Yes (admin/vendor/shop)
Log levels Per file Per frontend + environment

🎨 Common UI Patterns

Loading State

async loadData() {
    this.loading = true;
    try {
        const data = await apiClient.get('/endpoint');
        this.items = data;
    } 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

🎨 Complete Example

// static/admin/js/vendor-theme.js

// 1. Use centralized logger
const themeLog = window.LogConfig.loggers.vendorTheme;

// 2. Create component
function adminVendorTheme() {
    return {
        ...data(),
        currentPage: 'vendor-theme',
        
        vendor: null,
        themeData: {},
        loading: true,
        
        async init() {
            themeLog.info('Initializing vendor theme editor');
            
            if (window._vendorThemeInitialized) {
                themeLog.warn('Already initialized, skipping...');
                return;
            }
            window._vendorThemeInitialized = true;
            
            const startTime = performance.now();
            
            try {
                themeLog.group('Loading theme data');
                
                await Promise.all([
                    this.loadVendor(),
                    this.loadTheme()
                ]);
                
                themeLog.groupEnd();
                
                const duration = performance.now() - startTime;
                window.LogConfig.logPerformance('Theme Editor Init', duration);
                
                themeLog.info('Theme editor initialized successfully');
                
            } catch (error) {
                window.LogConfig.logError(error, 'Theme Editor Init');
                Utils.showToast('Failed to initialize', 'error');
            } finally {
                this.loading = false;
            }
        },
        
        async loadVendor() {
            const url = `/admin/vendors/${this.vendorCode}`;
            window.LogConfig.logApiCall('GET', url, null, 'request');
            
            const response = await apiClient.get(url);
            this.vendor = response;
            
            window.LogConfig.logApiCall('GET', url, response, 'response');
            themeLog.debug('Vendor loaded:', this.vendor);
        },
        
        async saveTheme() {
            themeLog.info('Saving theme changes');
            
            try {
                const url = `/admin/vendor-themes/${this.vendorCode}`;
                window.LogConfig.logApiCall('PUT', url, this.themeData, 'request');
                
                const response = await apiClient.put(url, this.themeData);
                
                window.LogConfig.logApiCall('PUT', url, response, 'response');
                
                themeLog.info('Theme saved successfully');
                Utils.showToast('Theme saved', 'success');
                
            } catch (error) {
                window.LogConfig.logError(error, 'Save Theme');
                Utils.showToast('Failed to save theme', 'error');
            }
        }
    };
}

themeLog.info('Vendor theme editor module loaded');

🚀 Quick Start Template Files

Copy these to create a new page:

  1. Copy base file: dashboard.js → rename to your-page.js
  2. Update logger:
    // Change from:
    const dashLog = window.LogConfig.loggers.dashboard;
    // To:
    const yourPageLog = window.LogConfig.loggers.yourPage;
    // Or create custom:
    const yourPageLog = window.LogConfig.createLogger('YOUR-PAGE');
    
  3. Replace function name: adminDashboard()yourPageComponent()
  4. Update init flag: _dashboardInitialized_yourPageInitialized
  5. Update page identifier: currentPage: 'dashboard'currentPage: 'your-page'
  6. Replace data loading logic with your API endpoints
  7. Update HTML template to use your function name:
    {% block alpine_data %}yourPageComponent(){% endblock %}
    
  8. Load your script in the template:
    {% block extra_scripts %}
    <script src="{{ url_for('static', path='admin/js/your-page.js') }}"></script>
    {% endblock %}
    

Done!