/** * 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 = `${message}`; 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 = `${message}`; 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 = ` ${addon.name} CHF ${addon.price} `; 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 = ` Add-on: ${addon.name} CHF ${addon.price} `; 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;