// app/modules/loyalty/static/admin/js/loyalty-programs.js // noqa: js-006 - async init pattern is safe, loadData has try/catch // Use centralized logger const loyaltyProgramsLog = window.LogConfig.loggers.loyaltyPrograms || window.LogConfig.createLogger('loyaltyPrograms'); // ============================================ // LOYALTY PROGRAMS LIST FUNCTION // ============================================ function adminLoyaltyPrograms() { return { // Inherit base layout functionality ...data(), // Page identifier for sidebar active state currentPage: 'loyalty-programs', // Programs page specific state programs: [], stats: { total_programs: 0, active_programs: 0, total_cards: 0, transactions_30d: 0, points_issued_30d: 0, points_redeemed_30d: 0, merchants_with_programs: 0 }, loading: false, error: null, // Search and filters filters: { search: '', is_active: '' }, // Pagination state pagination: { page: 1, per_page: 20, total: 0, pages: 0 }, // Initialize async init() { loyaltyProgramsLog.info('=== LOYALTY PROGRAMS PAGE INITIALIZING ==='); // Prevent multiple initializations if (window._loyaltyProgramsInitialized) { loyaltyProgramsLog.warn('Loyalty programs page already initialized, skipping...'); return; } window._loyaltyProgramsInitialized = true; // Load platform settings for rows per page if (window.PlatformSettings) { this.pagination.per_page = await window.PlatformSettings.getRowsPerPage(); } loyaltyProgramsLog.group('Loading loyalty programs data'); await Promise.all([ this.loadPrograms(), this.loadStats() ]); loyaltyProgramsLog.groupEnd(); loyaltyProgramsLog.info('=== LOYALTY PROGRAMS PAGE INITIALIZATION COMPLETE ==='); }, // Debounced search debouncedSearch() { if (this._searchTimeout) { clearTimeout(this._searchTimeout); } this._searchTimeout = setTimeout(() => { loyaltyProgramsLog.info('Search triggered:', this.filters.search); this.pagination.page = 1; this.loadPrograms(); }, 300); }, // Computed: Get programs for current page (already paginated from server) get paginatedPrograms() { return this.programs; }, // Computed: Total number of 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: Generate page numbers array with ellipsis get pageNumbers() { const pages = []; const totalPages = this.totalPages; const current = this.pagination.page; if (totalPages <= 7) { // Show all pages if 7 or fewer for (let i = 1; i <= totalPages; i++) { pages.push(i); } } else { // Always show first page pages.push(1); if (current > 3) { pages.push('...'); } // Show pages around current page 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('...'); } // Always show last page pages.push(totalPages); } return pages; }, // Load programs with search and pagination async loadPrograms() { this.loading = true; this.error = null; try { loyaltyProgramsLog.info('Fetching loyalty programs from API...'); const params = new URLSearchParams(); params.append('skip', (this.pagination.page - 1) * this.pagination.per_page); params.append('limit', this.pagination.per_page); if (this.filters.search) { params.append('search', this.filters.search); } if (this.filters.is_active !== '') { params.append('is_active', this.filters.is_active); } const response = await apiClient.get(`/admin/loyalty/programs?${params}`); if (response.programs) { this.programs = response.programs; this.pagination.total = response.total; this.pagination.pages = Math.ceil(response.total / this.pagination.per_page); loyaltyProgramsLog.info(`Loaded ${this.programs.length} programs (total: ${response.total})`); } else { loyaltyProgramsLog.warn('No programs in response'); this.programs = []; } } catch (error) { loyaltyProgramsLog.error('Failed to load programs:', error); this.error = error.message || 'Failed to load loyalty programs'; this.programs = []; } finally { this.loading = false; } }, // Load platform stats async loadStats() { try { loyaltyProgramsLog.info('Fetching loyalty stats from API...'); const response = await apiClient.get('/admin/loyalty/stats'); if (response) { this.stats = { total_programs: response.total_programs || 0, active_programs: response.active_programs || 0, total_cards: response.total_cards || 0, transactions_30d: response.transactions_30d || 0, points_issued_30d: response.points_issued_30d || 0, points_redeemed_30d: response.points_redeemed_30d || 0, merchants_with_programs: response.merchants_with_programs || 0 }; loyaltyProgramsLog.info('Stats loaded:', this.stats); } } catch (error) { loyaltyProgramsLog.error('Failed to load stats:', error); // Don't set error state for stats failure } }, // Pagination methods previousPage() { if (this.pagination.page > 1) { this.pagination.page--; loyaltyProgramsLog.info('Previous page:', this.pagination.page); this.loadPrograms(); } }, nextPage() { if (this.pagination.page < this.totalPages) { this.pagination.page++; loyaltyProgramsLog.info('Next page:', this.pagination.page); this.loadPrograms(); } }, goToPage(pageNum) { if (pageNum !== '...' && pageNum >= 1 && pageNum <= this.totalPages) { this.pagination.page = pageNum; loyaltyProgramsLog.info('Go to page:', this.pagination.page); this.loadPrograms(); } }, // Toggle program active/inactive async toggleProgramActive(program) { const action = program.is_active ? 'deactivate' : 'activate'; try { const response = await apiClient.post(`/admin/loyalty/programs/${program.id}/${action}`); program.is_active = response.is_active; Utils.showToast(`Program ${action}d successfully`, 'success'); loyaltyProgramsLog.info(`Program ${program.id} ${action}d`); } catch (error) { Utils.showToast(`Failed to ${action} program: ${error.message}`, 'error'); loyaltyProgramsLog.error(`Failed to ${action} program:`, error); } }, // Format date for display formatDate(dateString) { if (!dateString) return 'N/A'; try { const date = new Date(dateString); return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); } catch (e) { loyaltyProgramsLog.error('Date parsing error:', e); return dateString; } }, // Format number with thousands separator formatNumber(num) { if (num === null || num === undefined) return '0'; return new Intl.NumberFormat('en-US').format(num); } }; } // Register logger for configuration if (!window.LogConfig.loggers.loyaltyPrograms) { window.LogConfig.loggers.loyaltyPrograms = window.LogConfig.createLogger('loyaltyPrograms'); } loyaltyProgramsLog.info('Loyalty programs module loaded');