// noqa: js-006 - async init pattern is safe, loadData has try/catch // 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 }, // Pagination (standard structure matching pagination macro) pagination: { page: 1, per_page: 20, total: 0, pages: 0 }, // Filters filters: { search: '', is_active: '', store_id: '' }, // Selected store (for prominent display and filtering) selectedStore: null, // Toggle status confirm state showToggleStatusConfirm: false, pendingToggleCustomer: null, // Tom Select instance storeSelectInstance: 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() { // Load i18n translations await I18n.loadModule('customers'); 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 store filter this.initStoreSelect(); // Check localStorage for saved store const savedStoreId = localStorage.getItem('customers_selected_store_id'); if (savedStoreId) { customersLog.debug('Restoring saved store:', savedStoreId); // Restore store after a short delay to ensure TomSelect is ready setTimeout(async () => { await this.restoreSavedStore(parseInt(savedStoreId)); }, 200); // Load stats but not customers (restoreSavedStore will do that) await this.loadStats(); } else { // No saved store - load all data await Promise.all([ this.loadCustomers(), this.loadStats() ]); } this.loading = false; }, /** * Restore saved store from localStorage */ async restoreSavedStore(storeId) { try { const store = await apiClient.get(`/admin/stores/${storeId}`); if (this.storeSelectInstance && store) { // Add the store as an option and select it this.storeSelectInstance.addOption({ id: store.id, name: store.name, store_code: store.store_code }); this.storeSelectInstance.setValue(store.id, true); // Set the filter state this.selectedStore = store; this.filters.store_id = store.id; customersLog.debug('Restored store:', store.name); // Load customers with the store filter applied await this.loadCustomers(); } } catch (error) { customersLog.error('Failed to restore saved store, clearing localStorage:', error); localStorage.removeItem('customers_selected_store_id'); // Load unfiltered customers as fallback await this.loadCustomers(); } }, /** * Initialize Tom Select for store autocomplete */ initStoreSelect() { const selectEl = this.$refs.storeSelect; if (!selectEl) { customersLog.warn('Store 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.initStoreSelect(), 100); return; } this.storeSelectInstance = new TomSelect(selectEl, { valueField: 'id', labelField: 'name', searchField: ['name', 'store_code'], placeholder: 'Filter by store...', allowEmptyOption: true, load: async (query, callback) => { try { const response = await apiClient.get('/admin/stores', { search: query, limit: 50 }); callback(response.stores || []); } catch (error) { customersLog.error('Failed to search stores:', error); callback([]); } }, render: { option: (data, escape) => { return `