/** * Price Calculator - Main orchestrator class * Coordinates all the different managers to provide pricing calculation functionality */ class PriceCalculator { constructor() { // Initialize managers this.domManager = new DOMManager(); this.currentOffering = this.extractOfferingFromURL(); this.pricingDataManager = new PricingDataManager(this.currentOffering); this.planManager = new PlanManager(this.pricingDataManager); this.addonManager = new AddonManager(this.pricingDataManager); this.uiManager = new UIManager(); this.orderManager = new OrderManager(); // Initialize the calculator this.init(); } // Extract offering info from URL extractOfferingFromURL() { const pathParts = window.location.pathname.split('/'); if (pathParts.length >= 4 && pathParts[1] === 'offering') { return { provider_slug: pathParts[2], service_slug: pathParts[3] }; } return null; } // Initialize calculator async init() { try { // Load pricing data and setup calculator if (this.currentOffering) { await this.pricingDataManager.loadPricingData(); this.setupEventListeners(); this.setupUI(); this.orderManager.setupOrderButton(this.domManager); this.updateCalculator(); } else { console.warn('No current offering found, calculator not initialized'); } } catch (error) { console.error('Error initializing price calculator:', error); this.uiManager.showError(this.domManager, 'Failed to load pricing information'); } } // Setup initial UI components setupUI() { // Setup service levels based on available data this.uiManager.setupServiceLevels(this.domManager, this.pricingDataManager); // Calculate and set slider maximums this.uiManager.updateSliderMaximums(this.domManager, this.pricingDataManager); // Populate plan dropdown this.planManager.populatePlanDropdown(this.domManager); // Initialize instances slider this.uiManager.updateInstancesSlider(this.domManager, this.pricingDataManager); } // Setup event listeners for calculator controls setupEventListeners() { const cpuRange = this.domManager.get('cpuRange'); const memoryRange = this.domManager.get('memoryRange'); const storageRange = this.domManager.get('storageRange'); const instancesRange = this.domManager.get('instancesRange'); if (!cpuRange || !memoryRange || !storageRange || !instancesRange) return; // Slider event listeners cpuRange.addEventListener('input', () => { this.domManager.get('cpuValue').textContent = cpuRange.value; this.updatePricing(); }); memoryRange.addEventListener('input', () => { this.domManager.get('memoryValue').textContent = memoryRange.value; this.updatePricing(); }); storageRange.addEventListener('input', () => { this.domManager.get('storageValue').textContent = storageRange.value; this.updatePricing(); }); instancesRange.addEventListener('input', () => { this.domManager.get('instancesValue').textContent = instancesRange.value; this.updatePricing(); }); // Service level change listeners const serviceLevelInputs = this.domManager.get('serviceLevelInputs'); serviceLevelInputs.forEach(input => { input.addEventListener('change', () => { this.uiManager.updateInstancesSlider(this.domManager, this.pricingDataManager); this.planManager.populatePlanDropdown(this.domManager); this.addonManager.updateAddons(this.domManager); this.updatePricing(); }); }); // Plan selection listener const planSelect = this.domManager.get('planSelect'); if (planSelect) { planSelect.addEventListener('change', () => { if (planSelect.value) { const selectedPlan = JSON.parse(planSelect.value); // Update sliders to match selected plan this.planManager.updateSlidersForPlan(selectedPlan, this.domManager); // Fade out CPU and Memory sliders since plan is manually selected this.uiManager.fadeOutSliders(this.domManager, ['cpu', 'memory']); // Update addons for the new configuration this.addonManager.updateAddons(this.domManager); // Update pricing with the selected plan this.updatePricingWithPlan(selectedPlan); } else { // Auto-select mode - reset sliders to default values this.domManager.resetSlidersToDefaults(); // Auto-select mode - fade sliders back in this.uiManager.fadeInSliders(this.domManager, ['cpu', 'memory']); // Auto-select mode - update addons and recalculate this.addonManager.updateAddons(this.domManager); this.updatePricing(); } }); } // Listen for addon changes window.addEventListener('addon-changed', () => { this.updatePricing(); }); } // Update calculator (initial setup) updateCalculator() { this.addonManager.updateAddons(this.domManager); this.updatePricing(); } // Update pricing with specific plan updatePricingWithPlan(selectedPlan) { const config = this.domManager.getCurrentConfiguration(); // Update addon prices first to ensure calculated prices are current this.addonManager.updateAddonPrices(this.domManager, this.planManager); this.showPlanDetails(selectedPlan, config.storage, config.instances); this.uiManager.updateStatusMessage(this.domManager, 'Plan selected directly!', 'success'); } // Main pricing update function updatePricing() { // Update addon prices first to ensure they're current this.addonManager.updateAddonPrices(this.domManager, this.planManager); const planSelect = this.domManager.get('planSelect'); // Reset plan selection if in auto-select mode if (!planSelect?.value) { const config = this.domManager.getCurrentConfiguration(); if (!config.serviceLevel) { return; } // Find best matching plan const matchedPlan = this.planManager.findBestMatchingPlan(config.cpus, config.memory, config.serviceLevel); if (matchedPlan) { this.showPlanDetails(matchedPlan, config.storage, config.instances); this.uiManager.updateStatusMessage(this.domManager, 'Perfect match found!', 'success'); } else { this.uiManager.showNoMatch(this.domManager); } } else { // Plan is directly selected, update storage pricing const selectedPlan = JSON.parse(planSelect.value); const config = this.domManager.getCurrentConfiguration(); // Update addon prices for current configuration this.addonManager.updateAddonPrices(this.domManager, this.planManager); this.showPlanDetails(selectedPlan, config.storage, config.instances); this.uiManager.updateStatusMessage(this.domManager, 'Plan selected directly!', 'success'); } } // Show plan details in the UI showPlanDetails(plan, storage, instances) { // Get current service level const serviceLevel = this.domManager.getSelectedServiceLevel() || 'Best Effort'; // Ensure addon prices are calculated with current configuration this.addonManager.updateAddonPrices(this.domManager, this.planManager); // Calculate pricing using final price from plan data (which already includes mandatory addons) const managedServicePricePerInstance = parseFloat(plan.final_price); // Collect addon information for display and calculation const addons = this.addonManager.getSelectedAddons(this.domManager); const optionalAddonTotal = this.addonManager.calculateOptionalAddonTotal(this.domManager); const managedServicePrice = managedServicePricePerInstance * instances; // Use storage price from plan data or fallback to instance variable const storageUnitPrice = plan.storage_price !== undefined ? parseFloat(plan.storage_price) : this.pricingDataManager.getStoragePrice(); const storagePriceValue = storage * storageUnitPrice * instances; // Total price = managed service price (includes mandatory addons) + storage + optional addons const totalPriceValue = managedServicePrice + storagePriceValue + optionalAddonTotal; // Show plan details in UI this.uiManager.showPlanDetails( this.domManager, plan, storage, instances, serviceLevel, managedServicePrice, storagePriceValue, totalPriceValue ); // Update addon pricing display this.uiManager.updateAddonPricingDisplay(this.domManager, addons.mandatory, addons.optional); // Store current configuration for order button this.orderManager.storeConfiguration( plan, { storage, instances }, serviceLevel, totalPriceValue.toFixed(2), [...addons.mandatory, ...addons.optional] ); } } // Export for use in other modules window.PriceCalculator = PriceCalculator;