15 KiB
15 KiB
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_datain template - ✅
...data(),at start of return object - ✅
currentPage: 'your-page'set - ✅ Initialization guard in
init() - ✅ Use lowercase
apiClientfor 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 statethis.toggleTheme()- Toggle themethis.isSideMenuOpen- Side menu statethis.toggleSideMenu()- Toggle side menuthis.closeSideMenu()- Close side menuthis.isNotificationsMenuOpen- Notifications menu statethis.toggleNotificationsMenu()- Toggle notificationsthis.closeNotificationsMenu()- Close notificationsthis.isProfileMenuOpen- Profile menu statethis.toggleProfileMenu()- Toggle profile menuthis.closeProfileMenu()- Close profile menuthis.isPagesMenuOpen- Pages menu statethis.togglePagesMenu()- Toggle pages menu
From Utils (global)
Utils.showToast(message, type, duration)- Show toast notificationUtils.formatDate(dateString)- Format date for displayUtils.confirm(message, title)- Show confirmation dialog (if available)
From apiClient (global)
apiClient.get(url)- GET requestapiClient.post(url, data)- POST requestapiClient.put(url, data)- PUT requestapiClient.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:
- Copy base file:
dashboard.js→ rename toyour-page.js - 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'); - Replace function name:
adminDashboard()→yourPageComponent() - Update init flag:
_dashboardInitialized→_yourPageInitialized - Update page identifier:
currentPage: 'dashboard'→currentPage: 'your-page' - Replace data loading logic with your API endpoints
- Update HTML template to use your function name:
{% block alpine_data %}yourPageComponent(){% endblock %} - Load your script in the template:
{% block extra_scripts %} <script src="{{ url_for('static', path='admin/js/your-page.js') }}"></script> {% endblock %}
Done! ✅