// static/vendor/js/customers.js /** * Vendor customers management page logic * View and manage customer relationships */ const vendorCustomersLog = window.LogConfig.loggers.vendorCustomers || window.LogConfig.createLogger('vendorCustomers', false); vendorCustomersLog.info('Loading...'); function vendorCustomers() { vendorCustomersLog.info('vendorCustomers() called'); return { // Inherit base layout state ...data(), // Set page identifier currentPage: 'customers', // Loading states loading: true, error: '', saving: false, // Customers data customers: [], stats: { total: 0, active: 0, new_this_month: 0 }, // Filters filters: { search: '', status: '' }, // Pagination pagination: { page: 1, per_page: 20, total: 0, pages: 0 }, // Modal states showDetailModal: false, showOrdersModal: false, selectedCustomer: null, customerOrders: [], // Debounce timer searchTimeout: 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() { vendorCustomersLog.info('Customers init() called'); // Guard against multiple initialization if (window._vendorCustomersInitialized) { vendorCustomersLog.warn('Already initialized, skipping'); return; } window._vendorCustomersInitialized = true; // Load platform settings for rows per page if (window.PlatformSettings) { this.pagination.per_page = await window.PlatformSettings.getRowsPerPage(); } try { await this.loadCustomers(); } catch (error) { vendorCustomersLog.error('Init failed:', error); this.error = 'Failed to initialize customers page'; } vendorCustomersLog.info('Customers initialization complete'); }, /** * Load customers with filtering and pagination */ async loadCustomers() { this.loading = true; this.error = ''; try { const params = new URLSearchParams({ skip: (this.pagination.page - 1) * this.pagination.per_page, limit: this.pagination.per_page }); // Add filters if (this.filters.search) { params.append('search', this.filters.search); } if (this.filters.status) { params.append('status', this.filters.status); } const response = await apiClient.get(`/vendor/${this.vendorCode}/customers?${params.toString()}`); this.customers = response.customers || []; this.pagination.total = response.total || 0; this.pagination.pages = Math.ceil(this.pagination.total / this.pagination.per_page); // Calculate stats this.stats = { total: this.pagination.total, active: this.customers.filter(c => c.is_active !== false).length, new_this_month: this.customers.filter(c => { if (!c.created_at) return false; const created = new Date(c.created_at); const now = new Date(); return created.getMonth() === now.getMonth() && created.getFullYear() === now.getFullYear(); }).length }; vendorCustomersLog.info('Loaded customers:', this.customers.length, 'of', this.pagination.total); } catch (error) { vendorCustomersLog.error('Failed to load customers:', error); this.error = error.message || 'Failed to load customers'; } finally { this.loading = false; } }, /** * Debounced search handler */ debouncedSearch() { clearTimeout(this.searchTimeout); this.searchTimeout = setTimeout(() => { this.pagination.page = 1; this.loadCustomers(); }, 300); }, /** * Apply filter and reload */ applyFilter() { this.pagination.page = 1; this.loadCustomers(); }, /** * Clear all filters */ clearFilters() { this.filters = { search: '', status: '' }; this.pagination.page = 1; this.loadCustomers(); }, /** * View customer details */ async viewCustomer(customer) { this.loading = true; try { const response = await apiClient.get(`/vendor/${this.vendorCode}/customers/${customer.id}`); this.selectedCustomer = response; this.showDetailModal = true; vendorCustomersLog.info('Loaded customer details:', customer.id); } catch (error) { vendorCustomersLog.error('Failed to load customer details:', error); Utils.showToast(error.message || 'Failed to load customer details', 'error'); } finally { this.loading = false; } }, /** * View customer orders */ async viewCustomerOrders(customer) { this.loading = true; try { const response = await apiClient.get(`/vendor/${this.vendorCode}/customers/${customer.id}/orders`); this.selectedCustomer = customer; this.customerOrders = response.orders || []; this.showOrdersModal = true; vendorCustomersLog.info('Loaded customer orders:', customer.id, this.customerOrders.length); } catch (error) { vendorCustomersLog.error('Failed to load customer orders:', error); Utils.showToast(error.message || 'Failed to load customer orders', 'error'); } finally { this.loading = false; } }, /** * Send message to customer */ messageCustomer(customer) { window.location.href = `/vendor/${this.vendorCode}/messages?customer=${customer.id}`; }, /** * Get customer initials for avatar */ getInitials(customer) { const first = customer.first_name || ''; const last = customer.last_name || ''; return (first.charAt(0) + last.charAt(0)).toUpperCase() || '?'; }, /** * Format date for display */ formatDate(dateStr) { if (!dateStr) return '-'; return new Date(dateStr).toLocaleDateString('de-DE', { year: 'numeric', month: 'short', day: 'numeric' }); }, /** * Format price for display */ formatPrice(cents) { if (!cents && cents !== 0) return '-'; return new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(cents / 100); }, /** * Pagination: Previous page */ previousPage() { if (this.pagination.page > 1) { this.pagination.page--; this.loadCustomers(); } }, /** * Pagination: Next page */ nextPage() { if (this.pagination.page < this.totalPages) { this.pagination.page++; this.loadCustomers(); } }, /** * Pagination: Go to specific page */ goToPage(pageNum) { if (pageNum !== '...' && pageNum >= 1 && pageNum <= this.totalPages) { this.pagination.page = pageNum; this.loadCustomers(); } } }; }