253 lines
9.8 KiB
JavaScript
253 lines
9.8 KiB
JavaScript
|
/**
|
||
|
* 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;
|