From 491dbacda4600c06ac74ce0dee2b540af8b59aae Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 23 Jul 2025 14:29:54 +0200 Subject: [PATCH] add core revenue to model and allow to tweak params --- hub/services/static/css/roi-calculator.css | 110 +++++ .../js/roi-calculator/calculator-core.js | 36 +- .../js/roi-calculator/export-manager.js | 8 +- .../js/roi-calculator/roi-calculator-app.js | 12 + .../static/js/roi-calculator/ui-manager.js | 4 +- .../calculator/csp_roi_calculator.html | 408 +++++++++++++----- .../calculator/roi_calculator_help.html | 16 +- 7 files changed, 476 insertions(+), 118 deletions(-) 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 { ${data.scenario} ${modelBadge} ${data.totalInstances ? data.totalInstances.toLocaleString() : '0'} - ${this.formatCurrencyDetailed(data.monthlyRevenue || 0)} + ${this.formatCurrencyDetailed(data.serviceRevenue || data.monthlyRevenue || 0)} + ${this.formatCurrencyDetailed(data.coreRevenue || 0)} + ${this.formatCurrencyDetailed(data.totalRevenue || data.monthlyRevenue || 0)} ${this.formatCurrencyDetailed(data.cspRevenue || 0)} ${this.formatCurrencyDetailed(data.servalaRevenue || 0)} ${this.formatCurrencyDetailed(data.netPosition || 0)} diff --git a/hub/services/templates/calculator/csp_roi_calculator.html b/hub/services/templates/calculator/csp_roi_calculator.html index 607f652..075de17 100644 --- a/hub/services/templates/calculator/csp_roi_calculator.html +++ b/hub/services/templates/calculator/csp_roi_calculator.html @@ -32,6 +32,7 @@ function updateRevenuePerInstance(value) { window.ROICalculatorApp?.updateRevenu function updateServalaShare(value) { window.ROICalculatorApp?.updateServalaShare(value); } function updateGracePeriod(value) { window.ROICalculatorApp?.updateGracePeriod(value); } function updateLoanRate(value) { window.ROICalculatorApp?.updateLoanRate(value); } +function updateCoreServiceRevenue(value) { window.ROICalculatorApp?.updateCoreServiceRevenue(value); } function updateScenarioChurn(scenarioKey, churnRate) { window.ROICalculatorApp?.updateScenarioChurn(scenarioKey, churnRate); } function updateScenarioPhase(scenarioKey, phaseIndex, newInstancesPerMonth) { window.ROICalculatorApp?.updateScenarioPhase(scenarioKey, phaseIndex, newInstancesPerMonth); } function resetAdvancedParameters() { window.ROICalculatorApp?.resetAdvancedParameters(); } @@ -124,60 +125,65 @@ document.addEventListener('DOMContentLoaded', function() { - -
- -
+ +
+ +
-
- -
+
+ +
CHF
- -
- - -
-
-
- - -
-
- -
- - CHF -
- -
+
+ CHF 100K + CHF 2M
+ +
+ + +
+ + +
+ +
+ + CHF/month +
+ +
+ CHF 20 + CHF 200 +
+
-
-
- - + Help
@@ -185,49 +191,54 @@ document.addEventListener('DOMContentLoaded', function() {
-
- -
- -
+
+ +
+ +
-
-
-
-
-
+
+ +
-
-
Direct Investment
-
CHF 0
-
0%
-
Net Position / ROI
+
+
+
Direct Investment
+
CHF 0
+
0%
+ Net Profit / ROI +
-
-
Loan Model
-
CHF 0
-
0%
-
Net Position / ROI
+
+
+
Loan Model
+
CHF 0
+
0%
+ Net Profit / ROI +
@@ -235,53 +246,232 @@ document.addEventListener('DOMContentLoaded', function() {
- +
-
- -
- - - -
- - -
- - - -
- - -
- - - -
- - -
+
+
+
Advanced Configuration
+ + +
+
+
Investment Model Settings
+
+ + +
+ +
+ + % annual +
+ +
+ 3% + 8% +
+
+ + +
+ +
+ + % +
+ +
+ 10% + 40% +
+
+ + +
+ +
+ + months +
+ +
+ 0 + 24 +
+
+ + +
+ +
+ + CHF/month +
+ +
+ CHF 0 + CHF 500 +
+ Additional compute/storage revenue per instance +
+
+ + +
+
+
Growth Scenario Tuning
+
+ + +
+
+
+
Conservative Scenario
+
+
+ +
+ +
+ + % +
+
+ + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + +
+
+
+
Moderate Scenario
+
+
+ +
+ +
+ + % +
+
+ + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ + +
+
+
+
Aggressive Scenario
+
+
+ +
+ +
+ + % +
+
+ + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+ +
-
- - -
-
- - -
-
- - +
+
@@ -445,7 +635,9 @@ document.addEventListener('DOMContentLoaded', function() { Scenario Model Instances - Monthly Revenue + Service Revenue + Core Revenue + Total Revenue Your Share Servala Share Cumulative Net Position diff --git a/hub/services/templates/calculator/roi_calculator_help.html b/hub/services/templates/calculator/roi_calculator_help.html index 35bd5f1..b509401 100644 --- a/hub/services/templates/calculator/roi_calculator_help.html +++ b/hub/services/templates/calculator/roi_calculator_help.html @@ -189,6 +189,14 @@ html {

How It Works

Invest directly in Servala's operations and earn returns through revenue sharing that scales with performance and investment size.

+ +

Total Revenue Potential

+
+
Dual Revenue Streams
+

Service Revenue: Monthly fees from Servala managed services (shared with Servala based on your investment terms)

+

Core Service Revenue: Additional revenue from selling compute, storage, and infrastructure to support each managed service instance (100% retained by CSP)

+

Example: CHF 50 service fee + CHF 100 compute/storage = CHF 150 total revenue per instance per month

+

Progressive Scaling Benefits

@@ -300,7 +308,7 @@ html {
  • Initial Investment: CHF 100K - 2M (with slider)
  • Timeframe: 1-5 years
  • -
  • Revenue/Instance: Monthly income per managed service (CHF 20-200)
  • +
  • Service Revenue/Instance: Monthly Servala service fee per managed instance (CHF 20-200)
  • Growth Scenarios: Conservative, Moderate, Aggressive
@@ -310,6 +318,7 @@ html {
  • Loan Rate: Annual interest (3-8%) - affects loan model calculations
  • Servala Share: Revenue split percentage (10-40%) for direct investment
  • Grace Period: 100% revenue retention period (0-24 months, direct investment)
  • +
  • Core Revenue/Instance: Additional monthly revenue from selling compute/storage per instance (CHF 0-500)
  • Churn Rates: Customer loss percentages by scenario (0-15%)
  • @@ -419,8 +428,11 @@ html {

    What is the Performance Multiplier?

    The Performance Multiplier is an automatically calculated metric showing how your actual results compare to baseline expectations. For example, 1.5x means you're performing 50% better than the baseline scenario. This cannot be manually configured - it's calculated based on your investment scaling factors.

    +

    What is Core Service Revenue?

    +

    Core Service Revenue represents the additional monthly income CSPs earn by selling compute, storage, and infrastructure resources required to run each Servala managed service instance. This revenue stream is 100% retained by the CSP and is not shared with Servala, providing additional profit potential beyond the service fees.

    +

    What happens during the grace period?

    -

    You keep 100% of revenue during this period. Grace periods are longer for larger investments (6-12 months).

    +

    You keep 100% of service revenue during this period, plus all core service revenue. Grace periods are longer for larger investments (6-12 months).

    How accurate are the projections?

    Projections are based on industry benchmarks and Servala's historical data, but actual results may vary based on market conditions and your sales performance.