// static/admin/js/customers.js /** * Admin customer management page logic */ // Create logger for this module const customersLog = window.LogConfig?.createLogger('CUSTOMERS') || console; function adminCustomers() { return { // Inherit base layout state ...data(), // Page identifier currentPage: 'customers', // Loading states loading: true, loadingCustomers: false, // Error state error: '', // Data customers: [], stats: { total: 0, active: 0, inactive: 0, with_orders: 0, total_spent: 0, total_orders: 0, avg_order_value: 0 }, // Pagination (standard structure matching pagination macro) pagination: { page: 1, per_page: 20, total: 0, pages: 0 }, // Filters filters: { search: '', is_active: '', vendor_id: '' }, // Selected vendor (for prominent display and filtering) selectedVendor: null, // Tom Select instance vendorSelectInstance: null, // Computed: total pages get totalPages() { return this.pagination.pages; }, // Computed: Start index for pagination display get startIndex() { if (this.pagination.total === 0) return 0; return (this.pagination.page - 1) * this.pagination.per_page + 1; }, // Computed: End index for pagination display get endIndex() { const end = this.pagination.page * this.pagination.per_page; return end > this.pagination.total ? this.pagination.total : end; }, // Computed: Page numbers for pagination get pageNumbers() { const pages = []; const totalPages = this.totalPages; const current = this.pagination.page; if (totalPages <= 7) { for (let i = 1; i <= totalPages; i++) { pages.push(i); } } else { pages.push(1); if (current > 3) { pages.push('...'); } const start = Math.max(2, current - 1); const end = Math.min(totalPages - 1, current + 1); for (let i = start; i <= end; i++) { pages.push(i); } if (current < totalPages - 2) { pages.push('...'); } pages.push(totalPages); } return pages; }, async init() { customersLog.debug('Customers page initialized'); // Load platform settings for rows per page if (window.PlatformSettings) { this.pagination.per_page = await window.PlatformSettings.getRowsPerPage(); } // Initialize Tom Select for vendor filter this.initVendorSelect(); // Check localStorage for saved vendor const savedVendorId = localStorage.getItem('customers_selected_vendor_id'); if (savedVendorId) { customersLog.debug('Restoring saved vendor:', savedVendorId); // Restore vendor after a short delay to ensure TomSelect is ready setTimeout(async () => { await this.restoreSavedVendor(parseInt(savedVendorId)); }, 200); // Load stats but not customers (restoreSavedVendor will do that) await this.loadStats(); } else { // No saved vendor - load all data await Promise.all([ this.loadCustomers(), this.loadStats() ]); } this.loading = false; }, /** * Restore saved vendor from localStorage */ async restoreSavedVendor(vendorId) { try { const vendor = await apiClient.get(`/admin/vendors/${vendorId}`); if (this.vendorSelectInstance && vendor) { // Add the vendor as an option and select it this.vendorSelectInstance.addOption({ id: vendor.id, name: vendor.name, vendor_code: vendor.vendor_code }); this.vendorSelectInstance.setValue(vendor.id, true); // Set the filter state this.selectedVendor = vendor; this.filters.vendor_id = vendor.id; customersLog.debug('Restored vendor:', vendor.name); // Load customers with the vendor filter applied await this.loadCustomers(); } } catch (error) { customersLog.error('Failed to restore saved vendor, clearing localStorage:', error); localStorage.removeItem('customers_selected_vendor_id'); // Load unfiltered customers as fallback await this.loadCustomers(); } }, /** * Initialize Tom Select for vendor autocomplete */ initVendorSelect() { const selectEl = this.$refs.vendorSelect; if (!selectEl) { customersLog.warn('Vendor select element not found'); return; } // Wait for Tom Select to be available if (typeof TomSelect === 'undefined') { customersLog.warn('TomSelect not loaded, retrying in 100ms'); setTimeout(() => this.initVendorSelect(), 100); return; } this.vendorSelectInstance = new TomSelect(selectEl, { valueField: 'id', labelField: 'name', searchField: ['name', 'vendor_code'], placeholder: 'Filter by vendor...', allowEmptyOption: true, load: async (query, callback) => { try { const response = await apiClient.get('/admin/vendors', { search: query, limit: 50 }); callback(response.vendors || []); } catch (error) { customersLog.error('Failed to search vendors:', error); callback([]); } }, render: { option: (data, escape) => { return `