diff --git a/hub/services/static/css/roi-calculator.css b/hub/services/static/css/roi-calculator.css index c57342e..a4525fb 100644 --- a/hub/services/static/css/roi-calculator.css +++ b/hub/services/static/css/roi-calculator.css @@ -426,6 +426,27 @@ box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.15); } +/* Investment amount input enhancements */ +#investment-amount { + font-family: 'Courier New', monospace; + font-weight: 600; + text-align: right; + padding-right: 1rem; +} + +#investment-amount:focus { + text-align: left; + padding-left: 1rem; + padding-right: 0.75rem; +} + +#investment-amount::placeholder { + font-family: system-ui, -apple-system, sans-serif; + font-weight: normal; + text-align: left; + opacity: 0.6; +} + .main-config-fields .form-select { border-color: #dee2e6; font-size: 1.1rem; diff --git a/hub/services/static/js/roi-calculator/input-utils.js b/hub/services/static/js/roi-calculator/input-utils.js index 391088a..94df482 100644 --- a/hub/services/static/js/roi-calculator/input-utils.js +++ b/hub/services/static/js/roi-calculator/input-utils.js @@ -44,13 +44,56 @@ class InputUtils { } try { - // Remove non-numeric characters except commas - let value = input.value.replace(/[^\d,]/g, ''); + // Allow only digits, no immediate formatting during typing + let value = input.value.replace(/[^\d]/g, ''); + + // Handle empty input + if (!value) { + input.setAttribute('data-value', '0'); + return; + } // Parse the numeric value - let numericValue = InputUtils.parseFormattedNumber(value); + let numericValue = parseInt(value) || 0; - // Enforce min/max limits + // Update the data attribute with the raw numeric value (no limits during typing) + input.setAttribute('data-value', numericValue.toString()); + + // Update the input value (keep it clean, no commas during typing) + input.value = value; + + // Update the slider if it exists (with limits) + const slider = document.getElementById('investment-slider'); + if (slider) { + const minValue = parseInt(slider.min) || 100000; + const maxValue = parseInt(slider.max) || 2000000; + const sliderValue = Math.max(minValue, Math.min(maxValue, numericValue)); + slider.value = sliderValue; + } + + // Trigger calculations with debouncing + if (window.ROICalculatorApp && window.ROICalculatorApp.calculator) { + clearTimeout(input._calculationTimeout); + input._calculationTimeout = setTimeout(() => { + window.ROICalculatorApp.calculator.updateCalculations(); + }, 300); // 300ms delay to avoid excessive calculations during typing + } + } catch (error) { + console.error('Error handling investment amount input:', error); + // Set a safe default value + input.setAttribute('data-value', '500000'); + input.value = '500000'; + } + } + + static handleInvestmentAmountBlur(input) { + if (!input) return; + + try { + // Get the numeric value + let numericValue = parseInt(input.getAttribute('data-value')) || 500000; + + // Enforce min/max limits on blur const minValue = 100000; const maxValue = 2000000; @@ -60,30 +103,43 @@ class InputUtils { numericValue = maxValue; } - // Update the data attribute with the raw numeric value + // Update the data attribute with the corrected value input.setAttribute('data-value', numericValue.toString()); - // Format and display the value with commas + // Format and display the value with commas on blur input.value = InputUtils.formatNumberWithCommas(numericValue); - // Update the slider if it exists + // Update the slider const slider = document.getElementById('investment-slider'); if (slider) { slider.value = numericValue; } - // Trigger calculations + // Trigger immediate calculation on blur if (window.ROICalculatorApp && window.ROICalculatorApp.calculator) { + clearTimeout(input._calculationTimeout); window.ROICalculatorApp.calculator.updateCalculations(); } } catch (error) { - console.error('Error handling investment amount input:', error); + console.error('Error handling investment amount blur:', error); // Set a safe default value input.setAttribute('data-value', '500000'); input.value = '500,000'; } } + static handleInvestmentAmountFocus(input) { + if (!input) return; + + try { + // Remove commas when focusing for easier editing + const numericValue = input.getAttribute('data-value') || '500000'; + input.value = numericValue; + } catch (error) { + console.error('Error handling investment amount focus:', error); + } + } + static getCSRFToken() { try { // Try to get CSRF token from meta tag first diff --git a/hub/services/templates/calculator/csp_roi_calculator.html b/hub/services/templates/calculator/csp_roi_calculator.html index ce2c1b8..133ba32 100644 --- a/hub/services/templates/calculator/csp_roi_calculator.html +++ b/hub/services/templates/calculator/csp_roi_calculator.html @@ -27,6 +27,8 @@ function updateCalculations() { window.ROICalculatorApp?.updateCalculations(); } function exportToPDF() { window.ROICalculatorApp?.exportToPDF(); } function exportToCSV() { window.ROICalculatorApp?.exportToCSV(); } function handleInvestmentAmountInput(input) { InputUtils.handleInvestmentAmountInput(input); } +function handleInvestmentAmountFocus(input) { InputUtils.handleInvestmentAmountFocus(input); } +function handleInvestmentAmountBlur(input) { InputUtils.handleInvestmentAmountBlur(input); } function updateInvestmentAmount(value) { window.ROICalculatorApp?.updateInvestmentAmount(value); } function updateRevenuePerInstance(value) { window.ROICalculatorApp?.updateRevenuePerInstance(value); } function updateServalaShare(value) { window.ROICalculatorApp?.updateServalaShare(value); } @@ -139,7 +141,9 @@ document.addEventListener('DOMContentLoaded', function() { + onfocus="handleInvestmentAmountFocus(this)" + onblur="handleInvestmentAmountBlur(this)" + placeholder="Enter amount (100,000 - 2,000,000)">