240 lines
16 KiB
Plaintext
240 lines
16 KiB
Plaintext
╔══════════════════════════════════════════════════════════════════╗
|
|
║ ALPINE.JS PAGE ARCHITECTURE OVERVIEW ║
|
|
╚══════════════════════════════════════════════════════════════════╝
|
|
|
|
|
|
📂 FILE STRUCTURE
|
|
═════════════════════════════════════════════════════════════════
|
|
|
|
static/admin/js/
|
|
├── init-alpine.js ............. Base Alpine.js data & theme
|
|
├── dashboard.js ............... Dashboard page (✅ WORKING)
|
|
├── vendors.js ................. Vendor list page (✅ FIXED)
|
|
└── vendor-edit.js ............. Vendor edit page (✅ FIXED)
|
|
|
|
|
|
🔄 HOW PAGES INHERIT BASE FUNCTIONALITY
|
|
═════════════════════════════════════════════════════════════════
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ init-alpine.js │
|
|
│ ┌─────────────────────────────────────────────────────────┐ │
|
|
│ │ function data() { │ │
|
|
│ │ return { │ │
|
|
│ │ dark: ..., ← Theme state │ │
|
|
│ │ toggleTheme() {...}, ← Theme toggle │ │
|
|
│ │ isSideMenuOpen: false, ← Side menu state │ │
|
|
│ │ toggleSideMenu() {...}, ← Side menu toggle │ │
|
|
│ │ isProfileMenuOpen: false, ← Profile menu state │ │
|
|
│ │ toggleProfileMenu() {...}, ← Profile menu toggle │ │
|
|
│ │ currentPage: '' ← Page identifier │ │
|
|
│ │ }; │ │
|
|
│ │ } │ │
|
|
│ └─────────────────────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ Uses ...data() spread operator
|
|
│
|
|
┌─────────────────────┼─────────────────────┐
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
|
|
│ dashboard.js │ │ vendors.js │ │vendor-edit.js │
|
|
├───────────────┤ ├───────────────┤ ├───────────────┤
|
|
│function admin │ │function admin │ │function admin │
|
|
│Dashboard() { │ │Vendors() { │ │VendorEdit() { │
|
|
│ return { │ │ return { │ │ return { │
|
|
│ ...data(), │ │ ...data(), │ │ ...data(), │
|
|
│ │ │ │ │ │ │ │ │
|
|
│ └──────────┼───┼────┘ │ │ │ │
|
|
│ Inherits │ │ Inherits │ │ Inherits │
|
|
│ all base │ │ all base │ │ all base │
|
|
│ functions │ │ functions │ │ functions │
|
|
│ │ │ │ │ │
|
|
│ // Page │ │ // Page │ │ // Page │
|
|
│ specific │ │ specific │ │ specific │
|
|
│ state │ │ state │ │ state │
|
|
│ }; │ │ }; │ │ }; │
|
|
│} │ │} │ │} │
|
|
└───────────────┘ └───────────────┘ └───────────────┘
|
|
|
|
|
|
⚙️ API CLIENT USAGE PATTERN
|
|
═════════════════════════════════════════════════════════════════
|
|
|
|
All pages must use lowercase 'apiClient':
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ ✅ CORRECT ❌ WRONG │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ apiClient.get(url) │ ApiClient.get(url) │
|
|
│ apiClient.post(url, data) │ API_CLIENT.post(url, data) │
|
|
│ apiClient.put(url, data) │ Apiclient.put(url, data) │
|
|
│ apiClient.delete(url) │ APIClient.delete(url) │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
🪵 LOGGING PATTERN
|
|
═════════════════════════════════════════════════════════════════
|
|
|
|
Each page has its own logger object:
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ dashboard.js vendors.js vendor-edit.js │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ const dashLog = { const vendorsLog = const editLog = { │
|
|
│ error: (...) => error: (...) => error: (...) => │
|
|
│ warn: (...) => warn: (...) => warn: (...) => │
|
|
│ info: (...) => info: (...) => info: (...) => │
|
|
│ debug: (...) => debug: (...) => debug: (...) => │
|
|
│ }; }; }; │
|
|
│ │
|
|
│ dashLog.info('...') vendorsLog.info() editLog.info() │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
🔒 INITIALIZATION GUARD PATTERN
|
|
═════════════════════════════════════════════════════════════════
|
|
|
|
Prevents multiple Alpine.js initializations:
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ async init() { │
|
|
│ // Check if already initialized │
|
|
│ if (window._yourPageInitialized) { │
|
|
│ log.warn('Already initialized, skipping...'); │
|
|
│ return; // Exit early │
|
|
│ } │
|
|
│ window._yourPageInitialized = true; // Set flag │
|
|
│ │
|
|
│ // Continue with initialization │
|
|
│ await this.loadData(); │
|
|
│ } │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
📊 STATE MANAGEMENT
|
|
═════════════════════════════════════════════════════════════════
|
|
|
|
Alpine.js reactive state structure:
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ function yourPage() { │
|
|
│ return { │
|
|
│ ...data(), ← Base UI state (inherited) │
|
|
│ currentPage: 'name', ← Page identifier │
|
|
│ │
|
|
│ // Loading states │
|
|
│ loading: false, ← General loading │
|
|
│ loadingItem: false, ← Specific item loading │
|
|
│ saving: false, ← Save operation state │
|
|
│ │
|
|
│ // Data │
|
|
│ items: [], ← List data │
|
|
│ item: null, ← Single item │
|
|
│ stats: {}, ← Statistics │
|
|
│ │
|
|
│ // Error handling │
|
|
│ error: null, ← Error message │
|
|
│ errors: {}, ← Field-specific errors │
|
|
│ │
|
|
│ // Methods │
|
|
│ async init() {...}, ← Initialization │
|
|
│ async loadData() {...}, ← Data loading │
|
|
│ async save() {...}, ← Save operation │
|
|
│ formatDate(d) {...} ← Helper functions │
|
|
│ }; │
|
|
│ } │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
🎯 TEMPLATE BINDING
|
|
═════════════════════════════════════════════════════════════════
|
|
|
|
HTML template connects to Alpine.js component:
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ vendor-edit.html │
|
|
├─────────────────────────────────────────────────────────────┤
|
|
│ {% extends "admin/base.html" %} │
|
|
│ │
|
|
│ {# This binds to the JavaScript function #} │
|
|
│ {% block alpine_data %}adminVendorEdit(){% endblock %} │
|
|
│ └──────────────────┐ │
|
|
│ {% block content %} │ │
|
|
│ <div x-show="loading">Loading...</div> │ │
|
|
│ <div x-show="!loading"> │ │
|
|
│ <p x-text="vendor.name"></p> ← Reactive binding │ │
|
|
│ </div> │ │
|
|
│ {% endblock %} │ │
|
|
│ │ │
|
|
│ {% block extra_scripts %} │ │
|
|
│ <script src="...vendor-edit.js"></script> ──────────┐ │ │
|
|
│ {% endblock %} │ │ │
|
|
└───────────────────────────────────────────────────────│──│─┘
|
|
│ │
|
|
│ │
|
|
┌───────────────────────────────────────────────────────│──│─┐
|
|
│ vendor-edit.js │ │ │
|
|
├───────────────────────────────────────────────────────│──│─┤
|
|
│ function adminVendorEdit() { ◄────────────────────────┘ │ │
|
|
│ return { │ │
|
|
│ ...data(), │ │
|
|
│ vendor: null, ← Bound to x-text="vendor.name"│ │
|
|
│ loading: false, ← Bound to x-show="loading" │ │
|
|
│ async init() {...} │ │
|
|
│ }; │ │
|
|
│ } │ │
|
|
└──────────────────────────────────────────────────────────┘
|
|
|
|
|
|
🔄 PAGE LIFECYCLE
|
|
═════════════════════════════════════════════════════════════════
|
|
|
|
1. Page Load
|
|
↓
|
|
2. Alpine.js Initialization
|
|
↓
|
|
3. x-data="yourPageComponent()" called
|
|
↓
|
|
4. Component function executes
|
|
↓
|
|
5. ...data() spreads base state
|
|
↓
|
|
6. Page-specific state added
|
|
↓
|
|
7. init() method runs
|
|
↓
|
|
8. Check initialization guard
|
|
↓
|
|
9. Load data from API
|
|
↓
|
|
10. Reactive bindings update UI
|
|
|
|
|
|
✅ CHECKLIST FOR NEW PAGES
|
|
═════════════════════════════════════════════════════════════════
|
|
|
|
JavaScript File:
|
|
□ Create logger object (pageLog)
|
|
□ Define component function
|
|
□ Add ...data() at start of return object
|
|
□ Set currentPage: 'page-name'
|
|
□ Add initialization guard
|
|
□ Use lowercase apiClient for API calls
|
|
□ Add performance tracking (optional)
|
|
□ Use page-specific logger
|
|
|
|
HTML Template:
|
|
□ Extend admin/base.html
|
|
□ Set alpine_data block with function name
|
|
□ Add x-show for loading states
|
|
□ Add x-text for reactive data
|
|
□ Load JavaScript file in extra_scripts block
|
|
|
|
|
|
══════════════════════════════════════════════════════════════════
|
|
Your dashboard.js is perfect!
|
|
Use it as the template for all new pages.
|
|
══════════════════════════════════════════════════════════════════
|