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)">