/** * Alpine.js Components for Multi-Tenant E-commerce Platform * Universal component system for Admin, Vendor, and Shop sections */ // ============================================================================= // BASE MODAL SYSTEM // Universal modal functions used by all sections // ============================================================================= window.baseModalSystem = function() { return { // Confirmation Modal State confirmModal: { show: false, title: '', message: '', warning: '', buttonText: 'Confirm', buttonClass: 'btn-danger', onConfirm: null, onCancel: null }, // Success Modal State successModal: { show: false, title: 'Success', message: '', redirectUrl: null, redirectDelay: 2000 }, // Error Modal State errorModal: { show: false, title: 'Error', message: '', details: '' }, // Loading State loading: false, /** * Show confirmation modal * @param {Object} options - Modal configuration */ showConfirmModal(options) { this.confirmModal = { show: true, title: options.title || 'Confirm Action', message: options.message || 'Are you sure?', warning: options.warning || '', buttonText: options.buttonText || 'Confirm', buttonClass: options.buttonClass || 'btn-danger', onConfirm: options.onConfirm || null, onCancel: options.onCancel || null }; }, /** * Close confirmation modal */ closeConfirmModal() { if (this.confirmModal.onCancel) { this.confirmModal.onCancel(); } this.confirmModal.show = false; }, /** * Handle confirmation action */ async handleConfirm() { if (this.confirmModal.onConfirm) { this.closeConfirmModal(); await this.confirmModal.onConfirm(); } }, /** * Show success modal * @param {Object} options - Modal configuration */ showSuccessModal(options) { this.successModal = { show: true, title: options.title || 'Success', message: options.message || 'Operation completed successfully', redirectUrl: options.redirectUrl || null, redirectDelay: options.redirectDelay || 2000 }; // Auto-redirect if URL provided if (this.successModal.redirectUrl) { setTimeout(() => { window.location.href = this.successModal.redirectUrl; }, this.successModal.redirectDelay); } }, /** * Close success modal */ closeSuccessModal() { this.successModal.show = false; }, /** * Show error modal * @param {Object} options - Modal configuration */ showErrorModal(options) { this.errorModal = { show: true, title: options.title || 'Error', message: options.message || 'An error occurred', details: options.details || '' }; }, /** * Close error modal */ closeErrorModal() { this.errorModal.show = false; }, /** * Show loading overlay */ showLoading() { this.loading = true; }, /** * Hide loading overlay */ hideLoading() { this.loading = false; } }; }; // ============================================================================= // ADMIN LAYOUT COMPONENT // Header, Sidebar, Navigation, Modals for Admin Section // ============================================================================= window.adminLayout = function() { return { ...window.baseModalSystem(), // Admin-specific state user: null, menuOpen: false, currentPage: '', /** * Initialize admin layout */ async init() { this.currentPage = this.getCurrentPage(); await this.loadUserData(); }, /** * Load current admin user data */ async loadUserData() { try { const response = await apiClient.get('/admin/auth/me'); this.user = response; } catch (error) { console.error('Failed to load user data:', error); // Redirect to login if not authenticated if (error.status === 401) { window.location.href = '/admin/login.html'; } } }, /** * Get current page name from URL */ getCurrentPage() { const path = window.location.pathname; const page = path.split('/').pop().replace('.html', ''); return page || 'dashboard'; }, /** * Check if menu item is active */ isActive(page) { return this.currentPage === page; }, /** * Toggle mobile menu */ toggleMenu() { this.menuOpen = !this.menuOpen; }, /** * Show logout confirmation */ confirmLogout() { this.showConfirmModal({ title: 'Confirm Logout', message: 'Are you sure you want to logout?', buttonText: 'Logout', buttonClass: 'btn-primary', onConfirm: () => this.logout() }); }, /** * Perform logout */ async logout() { try { this.showLoading(); await apiClient.post('/admin/auth/logout'); window.location.href = '/admin/login.html'; } catch (error) { this.hideLoading(); this.showErrorModal({ message: 'Logout failed', details: error.message }); } } }; }; // ============================================================================= // VENDOR LAYOUT COMPONENT // Header, Sidebar, Navigation, Modals for Vendor Dashboard // ============================================================================= window.vendorLayout = function() { return { ...window.baseModalSystem(), // Vendor-specific state user: null, vendor: null, menuOpen: false, currentPage: '', /** * Initialize vendor layout */ async init() { this.currentPage = this.getCurrentPage(); await this.loadUserData(); }, /** * Load current vendor user data */ async loadUserData() { try { const response = await apiClient.get('/vendor/auth/me'); this.user = response.user; this.vendor = response.vendor; } catch (error) { console.error('Failed to load user data:', error); if (error.status === 401) { window.location.href = '/vendor/login.html'; } } }, /** * Get current page name from URL */ getCurrentPage() { const path = window.location.pathname; const page = path.split('/').pop().replace('.html', ''); return page || 'dashboard'; }, /** * Check if menu item is active */ isActive(page) { return this.currentPage === page; }, /** * Toggle mobile menu */ toggleMenu() { this.menuOpen = !this.menuOpen; }, /** * Show logout confirmation */ confirmLogout() { this.showConfirmModal({ title: 'Confirm Logout', message: 'Are you sure you want to logout?', buttonText: 'Logout', buttonClass: 'btn-primary', onConfirm: () => this.logout() }); }, /** * Perform logout */ async logout() { try { this.showLoading(); await apiClient.post('/vendor/auth/logout'); window.location.href = '/vendor/login.html'; } catch (error) { this.hideLoading(); this.showErrorModal({ message: 'Logout failed', details: error.message }); } } }; }; // ============================================================================= // SHOP LAYOUT COMPONENT // Header, Cart, Search, Navigation for Customer-Facing Shop // ============================================================================= window.shopLayout = function() { return { ...window.baseModalSystem(), // Shop-specific state vendor: null, cart: null, cartCount: 0, sessionId: null, searchQuery: '', mobileMenuOpen: false, /** * Initialize shop layout */ async init() { this.sessionId = this.getOrCreateSessionId(); await this.detectVendor(); if (this.vendor) { await this.loadCart(); } }, /** * Detect vendor from subdomain or vendor code */ async detectVendor() { try { const hostname = window.location.hostname; const subdomain = hostname.split('.')[0]; // Try to get vendor by subdomain first if (subdomain && subdomain !== 'localhost' && subdomain !== 'www') { this.vendor = await apiClient.get(`/public/vendors/by-subdomain/${subdomain}`); } else { // Fallback: Try to get vendor code from URL or localStorage const urlParams = new URLSearchParams(window.location.search); const vendorCode = urlParams.get('vendor') || localStorage.getItem('vendorCode'); if (vendorCode) { this.vendor = await apiClient.get(`/public/vendors/by-code/${vendorCode}`); localStorage.setItem('vendorCode', vendorCode); } } } catch (error) { console.error('Failed to detect vendor:', error); this.showErrorModal({ message: 'Vendor not found', details: 'Unable to identify the store. Please check the URL.' }); } }, /** * Get or create session ID for cart */ getOrCreateSessionId() { let sessionId = localStorage.getItem('cartSessionId'); if (!sessionId) { sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9); localStorage.setItem('cartSessionId', sessionId); } return sessionId; }, /** * Load cart from API */ async loadCart() { if (!this.vendor) return; try { this.cart = await apiClient.get( `/public/vendors/${this.vendor.id}/cart/${this.sessionId}` ); this.updateCartCount(); } catch (error) { console.error('Failed to load cart:', error); this.cart = { items: [] }; this.cartCount = 0; } }, /** * Update cart item count */ updateCartCount() { if (this.cart && this.cart.items) { this.cartCount = this.cart.items.reduce((sum, item) => sum + item.quantity, 0); } else { this.cartCount = 0; } }, /** * Add item to cart */ async addToCart(productId, quantity = 1) { if (!this.vendor) { this.showErrorModal({ message: 'Vendor not found' }); return; } try { this.showLoading(); await apiClient.post( `/public/vendors/${this.vendor.id}/cart/${this.sessionId}/items`, { product_id: productId, quantity } ); await this.loadCart(); this.hideLoading(); this.showSuccessModal({ title: 'Added to Cart', message: 'Product added successfully' }); } catch (error) { this.hideLoading(); this.showErrorModal({ message: 'Failed to add to cart', details: error.message }); } }, /** * Toggle mobile menu */ toggleMobileMenu() { this.mobileMenuOpen = !this.mobileMenuOpen; }, /** * Handle search */ handleSearch() { if (this.searchQuery.trim()) { window.location.href = `/shop/products.html?search=${encodeURIComponent(this.searchQuery)}`; } }, /** * Go to cart page */ goToCart() { window.location.href = '/shop/cart.html'; } }; }; // ============================================================================= // SHOP ACCOUNT LAYOUT COMPONENT // Layout for customer account area (orders, profile, addresses) // ============================================================================= window.shopAccountLayout = function() { return { ...window.shopLayout(), // Account-specific state customer: null, currentPage: '', /** * Initialize shop account layout */ async init() { this.currentPage = this.getCurrentPage(); this.sessionId = this.getOrCreateSessionId(); await this.detectVendor(); await this.loadCustomerData(); if (this.vendor) { await this.loadCart(); } }, /** * Load customer data */ async loadCustomerData() { if (!this.vendor) return; try { const response = await apiClient.get( `/public/vendors/${this.vendor.id}/customers/me` ); this.customer = response; } catch (error) { console.error('Failed to load customer data:', error); // Redirect to login if not authenticated if (error.status === 401) { window.location.href = `/shop/account/login.html?redirect=${encodeURIComponent(window.location.pathname)}`; } } }, /** * Get current page name from URL */ getCurrentPage() { const path = window.location.pathname; const page = path.split('/').pop().replace('.html', ''); return page || 'orders'; }, /** * Check if menu item is active */ isActive(page) { return this.currentPage === page; }, /** * Show logout confirmation */ confirmLogout() { this.showConfirmModal({ title: 'Confirm Logout', message: 'Are you sure you want to logout?', buttonText: 'Logout', buttonClass: 'btn-primary', onConfirm: () => this.logoutCustomer() }); }, /** * Perform customer logout */ async logoutCustomer() { if (!this.vendor) return; try { this.showLoading(); await apiClient.post(`/public/vendors/${this.vendor.id}/customers/logout`); window.location.href = '/shop/home.html'; } catch (error) { this.hideLoading(); this.showErrorModal({ message: 'Logout failed', details: error.message }); } } }; };