refactor price calculator into multiple files
This commit is contained in:
parent
33e8f2152a
commit
67e1b4cab1
8 changed files with 1324 additions and 1877 deletions
252
hub/services/static/js/price-calculator/price-calculator.js
Normal file
252
hub/services/static/js/price-calculator/price-calculator.js
Normal file
|
@ -0,0 +1,252 @@
|
|||
/**
|
||||
* 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;
|
Loading…
Add table
Add a link
Reference in a new issue