// static/admin/js/subscription-tiers.js // noqa: JS-003 - Uses ...baseData which is data() with safety check const tiersLog = window.LogConfig?.loggers?.subscriptionTiers || console; function adminSubscriptionTiers() { // Get base data with safety check for standalone usage const baseData = typeof data === 'function' ? data() : {}; return { // Inherit base layout functionality from init-alpine.js ...baseData, // Page-specific state currentPage: 'subscription-tiers', loading: true, error: null, successMessage: null, saving: false, // Data tiers: [], stats: null, includeInactive: false, // Sorting sortBy: 'display_order', sortOrder: 'asc', // Modal state showModal: false, editingTier: null, formData: { code: '', name: '', description: '', price_monthly_cents: 0, price_annual_cents: null, orders_per_month: null, products_limit: null, team_members: null, display_order: 0, stripe_product_id: '', stripe_price_monthly_id: '', features: [], is_active: true, is_public: true }, async init() { // Guard against multiple initialization if (window._adminSubscriptionTiersInitialized) { tiersLog.warn('Already initialized, skipping'); return; } window._adminSubscriptionTiersInitialized = true; tiersLog.info('=== SUBSCRIPTION TIERS PAGE INITIALIZING ==='); await this.loadTiers(); await this.loadStats(); }, async refresh() { this.error = null; this.successMessage = null; await this.loadTiers(); await this.loadStats(); }, async loadTiers() { this.loading = true; this.error = null; try { const params = new URLSearchParams(); params.append('include_inactive', this.includeInactive); if (this.sortBy) params.append('sort_by', this.sortBy); if (this.sortOrder) params.append('sort_order', this.sortOrder); const data = await apiClient.get(`/admin/subscriptions/tiers?${params}`); this.tiers = data.tiers || []; tiersLog.info(`Loaded ${this.tiers.length} tiers`); } catch (error) { tiersLog.error('Failed to load tiers:', error); this.error = error.message || 'Failed to load subscription tiers'; } finally { this.loading = false; } }, handleSort(key) { if (this.sortBy === key) { this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc'; } else { this.sortBy = key; this.sortOrder = 'asc'; } this.loadTiers(); }, async loadStats() { try { const data = await apiClient.get('/admin/subscriptions/stats'); this.stats = data; tiersLog.info('Loaded subscription stats'); } catch (error) { tiersLog.error('Failed to load stats:', error); // Non-critical, don't show error } }, openCreateModal() { this.editingTier = null; this.formData = { code: '', name: '', description: '', price_monthly_cents: 0, price_annual_cents: null, orders_per_month: null, products_limit: null, team_members: null, display_order: this.tiers.length, stripe_product_id: '', stripe_price_monthly_id: '', features: [], is_active: true, is_public: true }; this.showModal = true; }, openEditModal(tier) { this.editingTier = tier; this.formData = { code: tier.code, name: tier.name, description: tier.description || '', price_monthly_cents: tier.price_monthly_cents, price_annual_cents: tier.price_annual_cents, orders_per_month: tier.orders_per_month, products_limit: tier.products_limit, team_members: tier.team_members, display_order: tier.display_order, stripe_product_id: tier.stripe_product_id || '', stripe_price_monthly_id: tier.stripe_price_monthly_id || '', features: tier.features || [], is_active: tier.is_active, is_public: tier.is_public }; this.showModal = true; }, closeModal() { this.showModal = false; this.editingTier = null; }, async saveTier() { this.saving = true; this.error = null; try { // Clean up null values for empty strings const payload = { ...this.formData }; if (payload.price_annual_cents === '') payload.price_annual_cents = null; if (payload.orders_per_month === '') payload.orders_per_month = null; if (payload.products_limit === '') payload.products_limit = null; if (payload.team_members === '') payload.team_members = null; if (this.editingTier) { // Update existing tier await apiClient.patch(`/admin/subscriptions/tiers/${this.editingTier.code}`, payload); this.successMessage = `Tier "${payload.name}" updated successfully`; } else { // Create new tier await apiClient.post('/admin/subscriptions/tiers', payload); this.successMessage = `Tier "${payload.name}" created successfully`; } this.closeModal(); await this.loadTiers(); } catch (error) { tiersLog.error('Failed to save tier:', error); this.error = error.message || 'Failed to save tier'; } finally { this.saving = false; } }, async toggleTierStatus(tier, activate) { try { await apiClient.patch(`/admin/subscriptions/tiers/${tier.code}`, { is_active: activate }); this.successMessage = `Tier "${tier.name}" ${activate ? 'activated' : 'deactivated'}`; await this.loadTiers(); } catch (error) { tiersLog.error('Failed to toggle tier status:', error); this.error = error.message || 'Failed to update tier'; } }, formatCurrency(cents) { if (cents === null || cents === undefined) return '-'; return new Intl.NumberFormat('de-LU', { style: 'currency', currency: 'EUR' }).format(cents / 100); } }; } tiersLog.info('Subscription tiers module loaded');