581 lines
17 KiB
JavaScript
581 lines
17 KiB
JavaScript
/**
|
|
* 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
|
|
});
|
|
}
|
|
}
|
|
};
|
|
}; |