robustness review of price calc js
This commit is contained in:
parent
e7c6a53a17
commit
27c41a6187
7 changed files with 143 additions and 24 deletions
|
|
@ -53,12 +53,16 @@ class DOMManager {
|
|||
this.elements.serviceLevelGroup = document.getElementById('serviceLevelGroup');
|
||||
}
|
||||
|
||||
// Get element by key
|
||||
// Get element by key with error handling
|
||||
get(key) {
|
||||
return this.elements[key];
|
||||
const element = this.elements[key];
|
||||
if (!element && key !== 'addonsContainer') {
|
||||
console.warn(`DOM element '${key}' not found`);
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
// Check if element exists
|
||||
// Check if element exists and is valid
|
||||
has(key) {
|
||||
return this.elements[key] && this.elements[key] !== null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,15 @@ class OrderManager {
|
|||
messageField.value = configMessage;
|
||||
}
|
||||
|
||||
// Store configuration details in hidden field
|
||||
// Find and fill alternative message field if the first one doesn't exist
|
||||
if (!messageField) {
|
||||
const altMessageField = document.querySelector('textarea[name="message"]');
|
||||
if (altMessageField) {
|
||||
altMessageField.value = configMessage;
|
||||
}
|
||||
}
|
||||
|
||||
// Store configuration details in hidden field if it exists
|
||||
const detailsField = document.querySelector('#order-form input[name="details"]');
|
||||
if (detailsField) {
|
||||
detailsField.value = JSON.stringify({
|
||||
|
|
|
|||
|
|
@ -64,7 +64,11 @@ class PlanManager {
|
|||
if (!planSelect) return;
|
||||
|
||||
const serviceLevel = domManager.getSelectedServiceLevel();
|
||||
if (!serviceLevel) return;
|
||||
if (!serviceLevel) {
|
||||
// Clear dropdown if no service level is selected
|
||||
planSelect.innerHTML = '<option value="">Select a service level first</option>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear existing options
|
||||
planSelect.innerHTML = '<option value="">Auto-select best matching plan</option>';
|
||||
|
|
@ -72,6 +76,11 @@ class PlanManager {
|
|||
// Get plans for the selected service level
|
||||
const availablePlans = this.pricingDataManager.getPlansForServiceLevel(serviceLevel);
|
||||
|
||||
if (!availablePlans || availablePlans.length === 0) {
|
||||
planSelect.innerHTML = '<option value="">No plans available for this service level</option>';
|
||||
return;
|
||||
}
|
||||
|
||||
// Add plans to dropdown
|
||||
availablePlans.forEach(plan => {
|
||||
const option = document.createElement('option');
|
||||
|
|
|
|||
|
|
@ -4,17 +4,27 @@
|
|||
*/
|
||||
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();
|
||||
try {
|
||||
// Initialize managers
|
||||
this.domManager = new DOMManager();
|
||||
this.currentOffering = this.extractOfferingFromURL();
|
||||
|
||||
// Initialize the calculator
|
||||
this.init();
|
||||
if (!this.currentOffering) {
|
||||
throw new Error('Unable to extract offering information from URL');
|
||||
}
|
||||
|
||||
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();
|
||||
} catch (error) {
|
||||
console.error('Error initializing PriceCalculator:', error);
|
||||
this.showInitializationError(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract offering info from URL
|
||||
|
|
@ -41,11 +51,24 @@ class PriceCalculator {
|
|||
this.orderManager.setupOrderButton(this.domManager);
|
||||
this.updateCalculator();
|
||||
} else {
|
||||
console.warn('No current offering found, calculator not initialized');
|
||||
throw new Error('No current offering found');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error initializing price calculator:', error);
|
||||
this.uiManager.showError(this.domManager, 'Failed to load pricing information');
|
||||
this.showInitializationError(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Show initialization error to user
|
||||
showInitializationError(message) {
|
||||
const planMatchStatus = this.domManager?.get('planMatchStatus');
|
||||
if (planMatchStatus) {
|
||||
planMatchStatus.innerHTML = `
|
||||
<i class="bi bi-exclamation-triangle me-2 text-danger"></i>
|
||||
<span class="text-danger">Failed to load pricing calculator: ${message}</span>
|
||||
`;
|
||||
planMatchStatus.className = 'alert alert-danger mb-3';
|
||||
planMatchStatus.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -130,6 +153,23 @@ class PriceCalculator {
|
|||
window.addEventListener('addon-changed', () => {
|
||||
this.updatePricing();
|
||||
});
|
||||
|
||||
// Service level change listener
|
||||
const serviceLevelInputs = this.domManager.get('serviceLevelInputs');
|
||||
if (serviceLevelInputs) {
|
||||
serviceLevelInputs.forEach(input => {
|
||||
input.addEventListener('change', () => {
|
||||
// Update plan dropdown for new service level
|
||||
this.planManager.populatePlanDropdown(this.domManager);
|
||||
|
||||
// Update addons for new service level
|
||||
this.addonManager.updateAddons(this.domManager);
|
||||
|
||||
// Update pricing
|
||||
this.updatePricing();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update calculator (initial setup)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ class PricingDataManager {
|
|||
// Load pricing data from API endpoint
|
||||
async loadPricingData() {
|
||||
try {
|
||||
const response = await fetch(`/offering/${this.currentOffering.provider_slug}/${this.currentOffering.service_slug}/?pricing=json`);
|
||||
const url = `/offering/${this.currentOffering.provider_slug}/${this.currentOffering.service_slug}/?pricing=json`;
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load pricing data: ${response.status} ${response.statusText}`);
|
||||
|
|
@ -21,8 +22,17 @@ class PricingDataManager {
|
|||
|
||||
const data = await response.json();
|
||||
|
||||
if (!data || typeof data !== 'object') {
|
||||
throw new Error('Invalid pricing data received from server');
|
||||
}
|
||||
|
||||
this.pricingData = data.pricing || data;
|
||||
|
||||
// Validate that we have usable pricing data
|
||||
if (!this.pricingData || Object.keys(this.pricingData).length === 0) {
|
||||
throw new Error('No pricing data available for this offering');
|
||||
}
|
||||
|
||||
// Extract addons data from the plans - addons are embedded in each plan
|
||||
this.extractAddonsData();
|
||||
|
||||
|
|
|
|||
|
|
@ -89,18 +89,18 @@ class UIManager {
|
|||
const managedServiceIncludesContainer = domManager.get('managedServiceIncludesContainer');
|
||||
const managedServiceIncludes = domManager.get('managedServiceIncludes');
|
||||
const managedServiceToggleButton = domManager.get('managedServiceToggleButton');
|
||||
|
||||
|
||||
if (managedServiceIncludesContainer) {
|
||||
// Clear existing content
|
||||
managedServiceIncludesContainer.innerHTML = '';
|
||||
|
||||
// Show/hide the entire managed service includes section based on mandatory addons
|
||||
const hasMandatoryAddons = mandatoryAddons && mandatoryAddons.length > 0;
|
||||
|
||||
|
||||
if (managedServiceIncludes) {
|
||||
managedServiceIncludes.style.display = hasMandatoryAddons ? 'block' : 'none';
|
||||
}
|
||||
|
||||
|
||||
if (managedServiceToggleButton) {
|
||||
managedServiceToggleButton.style.display = hasMandatoryAddons ? 'inline-block' : 'none';
|
||||
}
|
||||
|
|
@ -218,7 +218,7 @@ class UIManager {
|
|||
|
||||
// Update the serviceLevelInputs reference
|
||||
domManager.elements.serviceLevelInputs = document.querySelectorAll('input[name="serviceLevel"]');
|
||||
|
||||
|
||||
// Set up event listeners for the dynamically created service level inputs
|
||||
this.setupServiceLevelEventListeners(domManager, pricingDataManager);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,49 @@
|
|||
{% block extra_js %}
|
||||
{% if debug %}
|
||||
<!-- Development: Load individual modules for easier debugging -->
|
||||
<script defer src="{% static 'js/price-calculator.js' %}"></script>
|
||||
{% compress js inline %}
|
||||
<script src="{% static 'js/price-calculator/dom-manager.js' %}"></script>
|
||||
<script src="{% static 'js/price-calculator/pricing-data-manager.js' %}"></script>
|
||||
<script src="{% static 'js/price-calculator/plan-manager.js' %}"></script>
|
||||
<script src="{% static 'js/price-calculator/addon-manager.js' %}"></script>
|
||||
<script src="{% static 'js/price-calculator/ui-manager.js' %}"></script>
|
||||
<script src="{% static 'js/price-calculator/order-manager.js' %}"></script>
|
||||
<script src="{% static 'js/price-calculator/price-calculator.js' %}"></script>
|
||||
<script>
|
||||
// Initialize calculator when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Check if we're on a page that needs the price calculator
|
||||
if (document.getElementById('cpuRange')) {
|
||||
try {
|
||||
window.priceCalculator = new PriceCalculator();
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize price calculator:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Global function for traditional plan selection (used by template buttons)
|
||||
function selectPlan(element) {
|
||||
if (!element) return;
|
||||
|
||||
const planId = element.getAttribute('data-plan-id');
|
||||
const planName = element.getAttribute('data-plan-name');
|
||||
|
||||
// Find the plan dropdown in the contact form
|
||||
const planDropdown = document.getElementById('id_choice');
|
||||
if (planDropdown) {
|
||||
// Find the option with matching plan id and select it
|
||||
for (let i = 0; i < planDropdown.options.length; i++) {
|
||||
const optionValue = planDropdown.options[i].value;
|
||||
if (optionValue.startsWith(planId + '|')) {
|
||||
planDropdown.selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endcompress %}
|
||||
{% else %}
|
||||
<!-- Production: Load compressed bundle -->
|
||||
{% compress js %}
|
||||
|
|
@ -25,12 +67,18 @@
|
|||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Check if we're on a page that needs the price calculator
|
||||
if (document.getElementById('cpuRange')) {
|
||||
window.priceCalculator = new PriceCalculator();
|
||||
try {
|
||||
window.priceCalculator = new PriceCalculator();
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize price calculator:', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Global function for traditional plan selection (used by template buttons)
|
||||
function selectPlan(element) {
|
||||
if (!element) return;
|
||||
|
||||
const planId = element.getAttribute('data-plan-id');
|
||||
const planName = element.getAttribute('data-plan-name');
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue