refactor price calculator into multiple files
This commit is contained in:
parent
33e8f2152a
commit
67e1b4cab1
8 changed files with 1324 additions and 1877 deletions
269
hub/services/static/js/price-calculator/ui-manager.js
Normal file
269
hub/services/static/js/price-calculator/ui-manager.js
Normal file
|
@ -0,0 +1,269 @@
|
|||
/**
|
||||
* UI Manager - Handles UI updates and visual feedback
|
||||
*/
|
||||
class UIManager {
|
||||
constructor() {
|
||||
// Visual feedback states
|
||||
this.isSlidersFaded = false;
|
||||
}
|
||||
|
||||
// Update status message
|
||||
updateStatusMessage(domManager, message, type) {
|
||||
const planMatchStatus = domManager.get('planMatchStatus');
|
||||
if (!planMatchStatus) return;
|
||||
|
||||
const iconClass = type === 'success' ? 'bi-check-circle' : 'bi-info-circle';
|
||||
const textClass = type === 'success' ? 'text-success' : '';
|
||||
const alertClass = type === 'success' ? 'alert-success' : 'alert-info';
|
||||
|
||||
planMatchStatus.innerHTML = `<i class="bi ${iconClass} me-2 ${textClass}"></i><span class="${textClass}">${message}</span>`;
|
||||
planMatchStatus.className = `alert ${alertClass} mb-3`;
|
||||
planMatchStatus.style.display = 'block';
|
||||
}
|
||||
|
||||
// Show error message
|
||||
showError(domManager, message) {
|
||||
const planMatchStatus = domManager.get('planMatchStatus');
|
||||
if (planMatchStatus) {
|
||||
planMatchStatus.innerHTML = `<i class="bi bi-exclamation-triangle me-2 text-danger"></i><span class="text-danger">${message}</span>`;
|
||||
planMatchStatus.className = 'alert alert-danger mb-3';
|
||||
planMatchStatus.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// Show no matching plan found
|
||||
showNoMatch(domManager) {
|
||||
const planMatchStatus = domManager.get('planMatchStatus');
|
||||
const selectedPlanDetails = domManager.get('selectedPlanDetails');
|
||||
const noMatchFound = domManager.get('noMatchFound');
|
||||
|
||||
if (planMatchStatus) planMatchStatus.style.display = 'none';
|
||||
if (selectedPlanDetails) selectedPlanDetails.style.display = 'none';
|
||||
if (noMatchFound) noMatchFound.style.display = 'block';
|
||||
}
|
||||
|
||||
// Show plan details in the UI
|
||||
showPlanDetails(domManager, plan, storage, instances, serviceLevel, managedServicePrice, storagePriceValue, totalPriceValue) {
|
||||
const selectedPlanDetails = domManager.get('selectedPlanDetails');
|
||||
if (!selectedPlanDetails) return;
|
||||
|
||||
// Show plan details section
|
||||
const planMatchStatus = domManager.get('planMatchStatus');
|
||||
const noMatchFound = domManager.get('noMatchFound');
|
||||
|
||||
if (planMatchStatus) planMatchStatus.style.display = 'block';
|
||||
selectedPlanDetails.style.display = 'block';
|
||||
if (noMatchFound) noMatchFound.style.display = 'none';
|
||||
|
||||
// Update plan information
|
||||
const planGroup = domManager.get('planGroup');
|
||||
const planName = domManager.get('planName');
|
||||
const planDescription = domManager.get('planDescription');
|
||||
const planCpus = domManager.get('planCpus');
|
||||
const planMemory = domManager.get('planMemory');
|
||||
const planInstances = domManager.get('planInstances');
|
||||
const planServiceLevel = domManager.get('planServiceLevel');
|
||||
const managedServicePriceEl = domManager.get('managedServicePrice');
|
||||
const storagePriceEl = domManager.get('storagePriceEl');
|
||||
const storageAmount = domManager.get('storageAmount');
|
||||
const totalPrice = domManager.get('totalPrice');
|
||||
|
||||
if (planGroup) planGroup.textContent = plan.groupName;
|
||||
if (planName) planName.textContent = plan.compute_plan;
|
||||
if (planDescription) planDescription.textContent = plan.compute_plan_group_description || '';
|
||||
if (planCpus) planCpus.textContent = plan.vcpus;
|
||||
if (planMemory) planMemory.textContent = plan.ram + ' GB';
|
||||
if (planInstances) planInstances.textContent = instances;
|
||||
if (planServiceLevel) planServiceLevel.textContent = serviceLevel;
|
||||
|
||||
// Update pricing display
|
||||
if (managedServicePriceEl) managedServicePriceEl.textContent = managedServicePrice.toFixed(2);
|
||||
if (storagePriceEl) storagePriceEl.textContent = storagePriceValue.toFixed(2);
|
||||
if (storageAmount) storageAmount.textContent = storage;
|
||||
if (totalPrice) totalPrice.textContent = totalPriceValue.toFixed(2);
|
||||
}
|
||||
|
||||
// Update addon pricing display in the results panel
|
||||
updateAddonPricingDisplay(domManager, mandatoryAddons, selectedOptionalAddons) {
|
||||
// Update mandatory addons in the managed service includes container
|
||||
const managedServiceIncludesContainer = domManager.get('managedServiceIncludesContainer');
|
||||
if (managedServiceIncludesContainer) {
|
||||
// Clear existing content
|
||||
managedServiceIncludesContainer.innerHTML = '';
|
||||
|
||||
// Add mandatory addons to the managed service includes section
|
||||
if (mandatoryAddons && mandatoryAddons.length > 0) {
|
||||
mandatoryAddons.forEach(addon => {
|
||||
const addonRow = document.createElement('div');
|
||||
addonRow.className = 'd-flex justify-content-between small text-muted mb-1';
|
||||
addonRow.innerHTML = `
|
||||
<span><i class="bi bi-check-circle text-success me-1"></i>${addon.name}</span>
|
||||
<span>CHF ${addon.price}</span>
|
||||
`;
|
||||
managedServiceIncludesContainer.appendChild(addonRow);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update optional addons in the addon pricing container
|
||||
const addonPricingContainer = domManager.get('addonPricingContainer');
|
||||
if (!addonPricingContainer) return;
|
||||
|
||||
// Clear existing addon pricing display
|
||||
addonPricingContainer.innerHTML = '';
|
||||
|
||||
// Add optional addons to pricing breakdown (these are added to total)
|
||||
if (selectedOptionalAddons && selectedOptionalAddons.length > 0) {
|
||||
selectedOptionalAddons.forEach(addon => {
|
||||
const addonRow = document.createElement('div');
|
||||
addonRow.className = 'd-flex justify-content-between mb-2';
|
||||
addonRow.innerHTML = `
|
||||
<span>Add-on: ${addon.name}</span>
|
||||
<span class="fw-bold">CHF ${addon.price}</span>
|
||||
`;
|
||||
addonPricingContainer.appendChild(addonRow);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Fade out specified sliders when plan is manually selected
|
||||
fadeOutSliders(domManager, sliderTypes) {
|
||||
sliderTypes.forEach(type => {
|
||||
const sliderContainer = domManager.getSliderContainer(type);
|
||||
if (sliderContainer) {
|
||||
sliderContainer.style.transition = 'opacity 0.3s ease-in-out';
|
||||
sliderContainer.style.opacity = '0.3';
|
||||
sliderContainer.style.pointerEvents = 'none';
|
||||
|
||||
// Add visual indicator that sliders are disabled
|
||||
const slider = sliderContainer.querySelector('.form-range');
|
||||
if (slider) {
|
||||
slider.style.cursor = 'not-allowed';
|
||||
}
|
||||
}
|
||||
});
|
||||
this.isSlidersFaded = true;
|
||||
}
|
||||
|
||||
// Fade in specified sliders when auto-select mode is chosen
|
||||
fadeInSliders(domManager, sliderTypes) {
|
||||
sliderTypes.forEach(type => {
|
||||
const sliderContainer = domManager.getSliderContainer(type);
|
||||
if (sliderContainer) {
|
||||
sliderContainer.style.transition = 'opacity 0.3s ease-in-out';
|
||||
sliderContainer.style.opacity = '1';
|
||||
sliderContainer.style.pointerEvents = 'auto';
|
||||
|
||||
// Remove visual indicator
|
||||
const slider = sliderContainer.querySelector('.form-range');
|
||||
if (slider) {
|
||||
slider.style.cursor = 'pointer';
|
||||
}
|
||||
}
|
||||
});
|
||||
this.isSlidersFaded = false;
|
||||
}
|
||||
|
||||
// Setup service levels dynamically from pricing data
|
||||
setupServiceLevels(domManager, pricingDataManager) {
|
||||
const serviceLevelGroup = domManager.get('serviceLevelGroup');
|
||||
if (!serviceLevelGroup) return;
|
||||
|
||||
// Get all available service levels from the pricing data
|
||||
const availableServiceLevels = pricingDataManager.getAvailableServiceLevels();
|
||||
|
||||
// Clear existing service level buttons
|
||||
serviceLevelGroup.innerHTML = '';
|
||||
|
||||
// Create buttons for each available service level
|
||||
let isFirst = true;
|
||||
availableServiceLevels.forEach(serviceLevel => {
|
||||
const inputId = `serviceLevel${serviceLevel.replace(/\s+/g, '')}`;
|
||||
|
||||
// Create radio input
|
||||
const input = document.createElement('input');
|
||||
input.type = 'radio';
|
||||
input.className = 'btn-check';
|
||||
input.name = 'serviceLevel';
|
||||
input.id = inputId;
|
||||
input.value = serviceLevel;
|
||||
if (isFirst) {
|
||||
input.checked = true;
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
// Create label
|
||||
const label = document.createElement('label');
|
||||
label.className = 'btn btn-outline-primary';
|
||||
label.setAttribute('for', inputId);
|
||||
label.textContent = serviceLevel;
|
||||
|
||||
serviceLevelGroup.appendChild(input);
|
||||
serviceLevelGroup.appendChild(label);
|
||||
});
|
||||
|
||||
// Update the serviceLevelInputs reference
|
||||
domManager.elements.serviceLevelInputs = document.querySelectorAll('input[name="serviceLevel"]');
|
||||
}
|
||||
|
||||
// Update slider maximums based on pricing data
|
||||
updateSliderMaximums(domManager, pricingDataManager) {
|
||||
const cpuRange = domManager.get('cpuRange');
|
||||
const memoryRange = domManager.get('memoryRange');
|
||||
|
||||
if (!cpuRange || !memoryRange) return;
|
||||
|
||||
const { maxCpus, maxMemory } = pricingDataManager.getSliderMaximums();
|
||||
|
||||
// Set slider maximums with some padding
|
||||
if (maxCpus > 0) {
|
||||
cpuRange.min = "0.25";
|
||||
cpuRange.max = Math.ceil(maxCpus);
|
||||
}
|
||||
|
||||
if (maxMemory > 0) {
|
||||
memoryRange.min = "0.25";
|
||||
memoryRange.max = Math.ceil(maxMemory);
|
||||
}
|
||||
|
||||
// Update display values after changing min/max
|
||||
domManager.updateSliderDisplayValues();
|
||||
}
|
||||
|
||||
// Update instances slider based on service level and replica info
|
||||
updateInstancesSlider(domManager, pricingDataManager) {
|
||||
const instancesRange = domManager.get('instancesRange');
|
||||
const instancesValue = domManager.get('instancesValue');
|
||||
const replicaInfo = pricingDataManager.getReplicaInfo();
|
||||
|
||||
if (!instancesRange || !replicaInfo) return;
|
||||
|
||||
const serviceLevel = domManager.getSelectedServiceLevel();
|
||||
|
||||
if (serviceLevel === 'Guaranteed Availability') {
|
||||
// For GA, min is ha_replica_min
|
||||
instancesRange.min = replicaInfo.ha_replica_min;
|
||||
instancesRange.value = Math.max(instancesRange.value, replicaInfo.ha_replica_min);
|
||||
} else {
|
||||
// For BE, min is 1
|
||||
instancesRange.min = 1;
|
||||
instancesRange.value = Math.max(instancesRange.value, 1);
|
||||
}
|
||||
|
||||
// Set max to ha_replica_max
|
||||
instancesRange.max = replicaInfo.ha_replica_max;
|
||||
|
||||
// Update display value
|
||||
if (instancesValue) instancesValue.textContent = instancesRange.value;
|
||||
|
||||
// Update the min/max display under the slider
|
||||
const instancesMinDisplay = document.getElementById('instancesMinDisplay');
|
||||
const instancesMaxDisplay = document.getElementById('instancesMaxDisplay');
|
||||
|
||||
if (instancesMinDisplay) instancesMinDisplay.textContent = instancesRange.min;
|
||||
if (instancesMaxDisplay) instancesMaxDisplay.textContent = instancesRange.max;
|
||||
}
|
||||
}
|
||||
|
||||
// Export for use in other modules
|
||||
window.UIManager = UIManager;
|
Loading…
Add table
Add a link
Reference in a new issue