diff --git a/hub/services/static/css/roi-calculator.css b/hub/services/static/css/roi-calculator.css index d93efa5..930dd93 100644 --- a/hub/services/static/css/roi-calculator.css +++ b/hub/services/static/css/roi-calculator.css @@ -387,6 +387,116 @@ box-shadow: 0 2px 4px rgba(0,0,0,0.1); } +/* Enhanced main configuration styling */ +.main-config-section { + background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%); + border-radius: 8px; + box-shadow: 0 2px 8px rgba(0,0,0,0.1); +} + +.main-config-section .form-label { + color: #495057; + font-weight: 600; +} + +.main-config-section .input-group-text { + background-color: #e9ecef; + border-color: #ced4da; + font-weight: 500; +} + +/* Advanced controls styling */ +.advanced-controls-section { + background: #f8f9fa; + border-top: 3px solid #007bff; +} + +.advanced-controls-section .card { + transition: all 0.2s ease; + border: none; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +.advanced-controls-section .card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0,0,0,0.15); +} + +.advanced-controls-section .card-header { + font-weight: 600; + border-bottom: 1px solid rgba(255,255,255,0.2); +} + +/* Slider styling improvements */ +.form-range { + height: 6px; + background: #e9ecef; + border-radius: 3px; +} + +.form-range::-webkit-slider-thumb { + width: 18px; + height: 18px; + background: #007bff; + border: 3px solid #fff; + border-radius: 50%; + box-shadow: 0 2px 4px rgba(0,0,0,0.2); + cursor: pointer; +} + +.form-range::-moz-range-thumb { + width: 18px; + height: 18px; + background: #007bff; + border: 3px solid #fff; + border-radius: 50%; + box-shadow: 0 2px 4px rgba(0,0,0,0.2); + cursor: pointer; +} + +/* Real-time results cards */ +.results-card { + transition: all 0.2s ease; + border: 2px solid transparent; +} + +.results-card:hover { + border-color: #007bff; + transform: translateY(-1px); +} + +/* Growth scenario checkboxes */ +.form-check-input:checked { + background-color: #007bff; + border-color: #007bff; +} + +.form-check-label { + cursor: pointer; + user-select: none; + transition: all 0.2s ease; +} + +.form-check-label:hover { + color: #007bff; +} + +/* Responsive improvements */ +@media (max-width: 768px) { + .main-config-section { + margin: 0 -15px; + border-radius: 0; + } + + .advanced-controls-section .card { + margin-bottom: 1rem; + } + + .advanced-controls-section .card-body { + padding: 1rem; + } +} + /* Model comparison box */ .model-comparison-box { border: 1px solid #dee2e6; diff --git a/hub/services/static/js/roi-calculator/calculator-core.js b/hub/services/static/js/roi-calculator/calculator-core.js index 3a065a1..02981fe 100644 --- a/hub/services/static/js/roi-calculator/calculator-core.js +++ b/hub/services/static/js/roi-calculator/calculator-core.js @@ -78,12 +78,17 @@ class ROICalculator { const graceElement = document.getElementById('grace-period'); const gracePeriod = graceElement ? parseInt(graceElement.value) || 6 : 6; + // Get core service revenue with validation + const coreRevenueElement = document.getElementById('core-service-revenue'); + const coreServiceRevenue = coreRevenueElement ? parseFloat(coreRevenueElement.value) || 0 : 0; + return { investmentAmount, timeframe, investmentModel, loanInterestRate, revenuePerInstance, + coreServiceRevenue, servalaShare, gracePeriod }; @@ -96,6 +101,7 @@ class ROICalculator { investmentModel: 'direct', loanInterestRate: 0.05, revenuePerInstance: 50, + coreServiceRevenue: 0, servalaShare: 0.25, gracePeriod: 6 }; @@ -204,7 +210,10 @@ class ROICalculator { monthlyRevenue = monthlyLoanPayment; } else { // Direct investment model: Revenue based on instances with performance incentives - monthlyRevenue = currentInstances * inputs.revenuePerInstance; + // Service revenue (shared with Servala) + Core service revenue (100% to CSP) + const serviceRevenue = currentInstances * inputs.revenuePerInstance; + const coreRevenue = currentInstances * inputs.coreServiceRevenue; + monthlyRevenue = serviceRevenue; // Calculate performance bonus if CSP exceeds baseline expectations if (baselineInstances > 0 && month > 6) { // Start performance tracking after 6 months @@ -217,11 +226,13 @@ class ROICalculator { // Determine revenue split based on dynamic grace period if (month <= effectiveGracePeriod) { - cspRevenue = monthlyRevenue; + // During grace period: CSP keeps all service revenue + core revenue + cspRevenue = serviceRevenue + coreRevenue; servalaRevenue = 0; } else { - cspRevenue = monthlyRevenue * (1 - adjustedServalaShare); - servalaRevenue = monthlyRevenue * adjustedServalaShare; + // After grace period: CSP keeps share of service revenue + all core revenue + cspRevenue = (serviceRevenue * (1 - adjustedServalaShare)) + coreRevenue; + servalaRevenue = serviceRevenue * adjustedServalaShare; } } @@ -244,6 +255,18 @@ class ROICalculator { breakEvenMonth = month; } + // Calculate revenue components for data tracking + let serviceRevenueForData, coreRevenueForData, totalRevenueForData; + if (inputs.investmentModel === 'loan') { + serviceRevenueForData = monthlyLoanPayment; + coreRevenueForData = 0; + totalRevenueForData = monthlyLoanPayment; + } else { + serviceRevenueForData = currentInstances * inputs.revenuePerInstance; + coreRevenueForData = currentInstances * inputs.coreServiceRevenue; + totalRevenueForData = serviceRevenueForData + coreRevenueForData; + } + monthlyData.push({ month, scenario: scenario.name, @@ -251,7 +274,10 @@ class ROICalculator { churnedInstances, totalInstances: currentInstances, baselineInstances, - monthlyRevenue, + serviceRevenue: serviceRevenueForData, + coreRevenue: coreRevenueForData, + totalRevenue: totalRevenueForData, + monthlyRevenue: serviceRevenueForData, // Keep for backward compatibility cspRevenue, servalaRevenue, cumulativeCSPRevenue, diff --git a/hub/services/static/js/roi-calculator/export-manager.js b/hub/services/static/js/roi-calculator/export-manager.js index 338d665..9cbc0f8 100644 --- a/hub/services/static/js/roi-calculator/export-manager.js +++ b/hub/services/static/js/roi-calculator/export-manager.js @@ -230,7 +230,9 @@ class ExportManager { const params = [ ['Investment Amount', this.formatCHF(inputs.investmentAmount)], ['Investment Timeframe', `${inputs.timeframe} years`], - ['Revenue per Instance', `${this.formatCHF(inputs.revenuePerInstance)} / month`], + ['Service Revenue per Instance', `${this.formatCHF(inputs.revenuePerInstance)} / month`], + ['Core Service Revenue per Instance', `${this.formatCHF(inputs.coreServiceRevenue)} / month`], + ['Total Revenue per Instance', `${this.formatCHF(inputs.revenuePerInstance + inputs.coreServiceRevenue)} / month`], ['Loan Interest Rate', `${(inputs.loanInterestRate * 100).toFixed(1)}%`], ['Direct Investment Share', `${(inputs.servalaShare * 100).toFixed(0)}% to Servala`], ['Grace Period', `${inputs.gracePeriod} months`] @@ -526,7 +528,9 @@ class ExportManager { if (inputs.investmentModel === 'loan') { csvContent += `Loan Interest Rate (%),${(inputs.loanInterestRate * 100).toFixed(1)}\n`; } - csvContent += `Revenue per Instance,${inputs.revenuePerInstance}\n`; + csvContent += `Service Revenue per Instance,${inputs.revenuePerInstance}\n`; + csvContent += `Core Service Revenue per Instance,${inputs.coreServiceRevenue}\n`; + csvContent += `Total Revenue per Instance,${inputs.revenuePerInstance + inputs.coreServiceRevenue}\n`; if (inputs.investmentModel === 'direct') { csvContent += `Servala Share (%),${(inputs.servalaShare * 100).toFixed(0)}\n`; csvContent += `Grace Period (months),${inputs.gracePeriod}\n`; diff --git a/hub/services/static/js/roi-calculator/roi-calculator-app.js b/hub/services/static/js/roi-calculator/roi-calculator-app.js index ba5281b..003bd86 100644 --- a/hub/services/static/js/roi-calculator/roi-calculator-app.js +++ b/hub/services/static/js/roi-calculator/roi-calculator-app.js @@ -198,6 +198,18 @@ class ROICalculatorApp { } } + updateCoreServiceRevenue(value) { + try { + const element = document.getElementById('core-service-revenue'); + if (element) { + element.value = value; + this.updateCalculations(); + } + } catch (error) { + console.error('Error updating core service revenue:', error); + } + } + updateScenarioChurn(scenarioKey, churnRate) { try { if (this.calculator && this.calculator.scenarios[scenarioKey]) { diff --git a/hub/services/static/js/roi-calculator/ui-manager.js b/hub/services/static/js/roi-calculator/ui-manager.js index cb0943e..c6f4a3a 100644 --- a/hub/services/static/js/roi-calculator/ui-manager.js +++ b/hub/services/static/js/roi-calculator/ui-manager.js @@ -183,7 +183,9 @@ class UIManager {