// app/modules/loyalty/static/admin/js/loyalty-analytics.js // noqa: js-006 - async init pattern is safe, loadData has try/catch const loyaltyAnalyticsLog = window.LogConfig.loggers.loyaltyAnalytics || window.LogConfig.createLogger('loyaltyAnalytics'); function adminLoyaltyAnalytics() { return { ...data(), currentPage: 'loyalty-analytics', stats: { total_programs: 0, active_programs: 0, total_cards: 0, active_cards: 0, transactions_30d: 0, points_issued_30d: 0, points_redeemed_30d: 0, merchants_with_programs: 0, total_points_issued: 0, total_points_redeemed: 0, total_points_balance: 0, new_this_month: 0, estimated_liability_cents: 0, }, locations: [], // Merchant filter state selectedMerchant: null, merchantSearch: '', merchantResults: [], showMerchantDropdown: false, searchingMerchants: false, loading: false, error: null, _searchTimeout: null, get redemptionRate() { if (this.stats.points_issued_30d === 0) return 0; return Math.round((this.stats.points_redeemed_30d / this.stats.points_issued_30d) * 100); }, get issuedPercentage() { const total = this.stats.points_issued_30d + this.stats.points_redeemed_30d; if (total === 0) return 50; return Math.round((this.stats.points_issued_30d / total) * 100); }, get redeemedPercentage() { return 100 - this.issuedPercentage; }, async init() { loyaltyAnalyticsLog.info('=== LOYALTY ANALYTICS PAGE INITIALIZING ==='); if (window._loyaltyAnalyticsInitialized) return; window._loyaltyAnalyticsInitialized = true; await this.loadStats(); loyaltyAnalyticsLog.info('=== LOYALTY ANALYTICS PAGE INITIALIZATION COMPLETE ==='); }, async loadStats() { this.loading = true; this.error = null; try { 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, active_cards: response.active_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, total_points_issued: response.total_points_issued || 0, total_points_redeemed: response.total_points_redeemed || 0, total_points_balance: response.total_points_balance || 0, new_this_month: response.new_this_month || 0, estimated_liability_cents: response.estimated_liability_cents || 0, }; this.locations = []; loyaltyAnalyticsLog.info('Platform stats loaded'); } } catch (error) { loyaltyAnalyticsLog.error('Failed to load analytics:', error); this.error = error.message || 'Failed to load analytics'; } finally { this.loading = false; } }, searchMerchants() { clearTimeout(this._searchTimeout); this._searchTimeout = setTimeout(async () => { if (this.merchantSearch.length < 2) { this.merchantResults = []; return; } this.searchingMerchants = true; try { const response = await apiClient.get(`/admin/loyalty/programs?search=${encodeURIComponent(this.merchantSearch)}&limit=10`); if (response && response.programs) { this.merchantResults = response.programs.map(p => ({ id: p.merchant_id, merchant_name: p.merchant_name || p.display_name || `Program #${p.id}`, loyalty_type: p.loyalty_type, })); } } catch (error) { loyaltyAnalyticsLog.error('Merchant search failed:', error); this.merchantResults = []; } finally { this.searchingMerchants = false; } }, 300); }, async selectMerchant(item) { this.selectedMerchant = item; this.merchantSearch = ''; this.merchantResults = []; this.showMerchantDropdown = false; this.loading = true; this.error = null; try { const response = await apiClient.get(`/admin/loyalty/merchants/${item.id}/stats`); if (response) { this.stats = { total_programs: 1, active_programs: response.program?.is_active ? 1 : 0, total_cards: response.total_cards || 0, active_cards: response.active_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: 1, total_points_issued: response.total_points_issued || 0, total_points_redeemed: response.total_points_redeemed || 0, total_points_balance: (response.total_points_issued || 0) - (response.total_points_redeemed || 0), new_this_month: response.new_this_month || 0, estimated_liability_cents: response.estimated_liability_cents || 0, }; this.locations = response.locations || []; loyaltyAnalyticsLog.info('Merchant stats loaded for:', item.merchant_name); } } catch (error) { loyaltyAnalyticsLog.error('Failed to load merchant stats:', error); this.error = error.message || 'Failed to load merchant stats'; } finally { this.loading = false; } }, async clearMerchantFilter() { this.selectedMerchant = null; this.merchantSearch = ''; this.merchantResults = []; await this.loadStats(); }, formatNumber(num) { if (num === null || num === undefined) return '0'; return new Intl.NumberFormat('en-US').format(num); } }; } if (!window.LogConfig.loggers.loyaltyAnalytics) { window.LogConfig.loggers.loyaltyAnalytics = window.LogConfig.createLogger('loyaltyAnalytics'); } loyaltyAnalyticsLog.info('Loyalty analytics module loaded');