// app/modules/loyalty/static/admin/js/loyalty-merchant-detail.js // noqa: js-006 - async init pattern is safe, loadData has try/catch // Use centralized logger const loyaltyMerchantDetailLog = window.LogConfig.loggers.loyaltyMerchantDetail || window.LogConfig.createLogger('loyaltyMerchantDetail'); // ============================================ // LOYALTY MERCHANT DETAIL FUNCTION // ============================================ function adminLoyaltyMerchantDetail() { return { // Inherit base layout functionality ...data(), // Page identifier for sidebar active state currentPage: 'loyalty-programs', // Merchant ID from URL merchantId: null, // Merchant data merchant: null, program: null, stats: { total_cards: 0, active_cards: 0, total_points_issued: 0, total_points_redeemed: 0, points_issued_30d: 0, points_redeemed_30d: 0, transactions_30d: 0 }, settings: null, locations: [], // State loading: false, error: null, showDeleteModal: false, // Initialize async init() { loyaltyMerchantDetailLog.info('=== LOYALTY MERCHANT DETAIL PAGE INITIALIZING ==='); // Prevent multiple initializations if (window._loyaltyMerchantDetailInitialized) { loyaltyMerchantDetailLog.warn('Loyalty merchant detail page already initialized, skipping...'); return; } window._loyaltyMerchantDetailInitialized = true; // Extract merchant ID from URL const pathParts = window.location.pathname.split('/'); const merchantsIndex = pathParts.indexOf('merchants'); if (merchantsIndex !== -1 && pathParts[merchantsIndex + 1]) { this.merchantId = parseInt(pathParts[merchantsIndex + 1]); } if (!this.merchantId) { this.error = 'Invalid merchant ID'; loyaltyMerchantDetailLog.error('Could not extract merchant ID from URL'); return; } loyaltyMerchantDetailLog.info('Merchant ID:', this.merchantId); loyaltyMerchantDetailLog.group('Loading merchant loyalty data'); await this.loadMerchantData(); loyaltyMerchantDetailLog.groupEnd(); loyaltyMerchantDetailLog.info('=== LOYALTY MERCHANT DETAIL PAGE INITIALIZATION COMPLETE ==='); }, // Load all merchant data async loadMerchantData() { this.loading = true; this.error = null; try { // Load merchant info await this.loadMerchant(); // Load loyalty-specific data in parallel await Promise.all([ this.loadStats(), this.loadSettings(), this.loadLocations() ]); } catch (error) { loyaltyMerchantDetailLog.error('Failed to load merchant data:', error); this.error = error.message || 'Failed to load merchant loyalty data'; } finally { this.loading = false; } }, // Load merchant basic info async loadMerchant() { try { loyaltyMerchantDetailLog.info('Fetching merchant info...'); // Get merchant from tenancy API const response = await apiClient.get(`/admin/merchants/${this.merchantId}`); if (response) { this.merchant = response; loyaltyMerchantDetailLog.info('Merchant loaded:', this.merchant.name); } } catch (error) { loyaltyMerchantDetailLog.error('Failed to load merchant:', error); throw error; } }, // Load merchant loyalty stats async loadStats() { try { loyaltyMerchantDetailLog.info('Fetching merchant loyalty stats...'); const response = await apiClient.get(`/admin/loyalty/merchants/${this.merchantId}/stats`); if (response) { this.stats = { total_cards: response.total_cards || 0, active_cards: response.active_cards || 0, total_points_issued: response.total_points_issued || 0, total_points_redeemed: response.total_points_redeemed || 0, points_issued_30d: response.points_issued_30d || 0, points_redeemed_30d: response.points_redeemed_30d || 0, transactions_30d: response.transactions_30d || 0 }; // Also get program info from stats response if (response.program) { this.program = response.program; } // Get location breakdown if (response.locations) { this.locations = response.locations; } loyaltyMerchantDetailLog.info('Stats loaded:', this.stats); } } catch (error) { loyaltyMerchantDetailLog.warn('Failed to load stats (merchant may not have loyalty program):', error.message); // Don't throw - stats might fail if no program exists } }, // Load merchant loyalty settings async loadSettings() { try { loyaltyMerchantDetailLog.info('Fetching merchant loyalty settings...'); const response = await apiClient.get(`/admin/loyalty/merchants/${this.merchantId}/settings`); if (response) { this.settings = response; loyaltyMerchantDetailLog.info('Settings loaded:', this.settings); } } catch (error) { loyaltyMerchantDetailLog.warn('Failed to load settings:', error.message); // Don't throw - settings might not exist yet } }, // Load location breakdown async loadLocations() { try { loyaltyMerchantDetailLog.info('Fetching location breakdown...'); // This data comes with stats, but could be a separate endpoint // For now, stats endpoint should return locations array } catch (error) { loyaltyMerchantDetailLog.warn('Failed to load locations:', error.message); } }, // Create a default program for this merchant async createProgram() { try { const data = { loyalty_type: 'points', points_per_euro: 1, welcome_bonus_points: 0, minimum_redemption_points: 100, card_name: this.merchant?.name ? this.merchant.name + ' Loyalty' : 'Loyalty Program', is_active: true }; const response = await apiClient.post(`/admin/loyalty/merchants/${this.merchantId}/program`, data); this.program = response; Utils.showToast('Loyalty program created', 'success'); loyaltyMerchantDetailLog.info('Program created for merchant', this.merchantId); // Reload stats await this.loadStats(); } catch (error) { Utils.showToast(`Failed to create program: ${error.message}`, 'error'); loyaltyMerchantDetailLog.error('Failed to create program:', error); } }, // Toggle program active/inactive async toggleActive() { if (!this.program) return; const action = this.program.is_active ? 'deactivate' : 'activate'; try { const response = await apiClient.post(`/admin/loyalty/programs/${this.program.id}/${action}`); this.program.is_active = response.is_active; Utils.showToast(`Program ${action}d successfully`, 'success'); loyaltyMerchantDetailLog.info(`Program ${action}d`); } catch (error) { Utils.showToast(`Failed to ${action} program: ${error.message}`, 'error'); loyaltyMerchantDetailLog.error(`Failed to ${action} program:`, error); } }, // Show delete confirmation confirmDeleteProgram() { this.showDeleteModal = true; }, // Delete the program async deleteProgram() { if (!this.program) return; try { await apiClient.delete(`/admin/loyalty/programs/${this.program.id}`); this.program = null; this.showDeleteModal = false; Utils.showToast('Loyalty program deleted', 'success'); loyaltyMerchantDetailLog.info('Program deleted'); // Reload stats await this.loadStats(); } catch (error) { Utils.showToast(`Failed to delete program: ${error.message}`, 'error'); loyaltyMerchantDetailLog.error('Failed to delete program:', error); this.showDeleteModal = false; } }, // 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) { loyaltyMerchantDetailLog.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.loyaltyMerchantDetail) { window.LogConfig.loggers.loyaltyMerchantDetail = window.LogConfig.createLogger('loyaltyMerchantDetail'); } loyaltyMerchantDetailLog.info('Loyalty merchant detail module loaded');