website/hub/services/static/js/price-calculator/ui-manager.js

269 lines
11 KiB
JavaScript

/**
* 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;