710 lines
No EOL
46 KiB
HTML
710 lines
No EOL
46 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}CSP ROI Calculator{% endblock %}
|
|
|
|
{% block extra_head %}
|
|
<meta name="csrf-token" content="{{ csrf_token }}">
|
|
{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<link rel="stylesheet" type="text/css" href='{% static "css/roi-calculator.css" %}'>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script src="{% static "js/chart.umd.min.js" %}"></script>
|
|
<script src="{% static "js/jspdf.umd.min.js" %}"></script>
|
|
<!-- ROI Calculator Modules -->
|
|
<script src="{% static "js/roi-calculator/input-utils.js" %}"></script>
|
|
<script src="{% static "js/roi-calculator/calculator-core.js" %}"></script>
|
|
<script src="{% static "js/roi-calculator/chart-manager.js" %}"></script>
|
|
<script src="{% static "js/roi-calculator/ui-manager.js" %}"></script>
|
|
<script src="{% static "js/roi-calculator/export-manager.js" %}"></script>
|
|
<script src="{% static "js/roi-calculator/roi-calculator-app.js" %}"></script>
|
|
<script>
|
|
// Global function wrappers for HTML onclick handlers
|
|
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); }
|
|
function updateGracePeriod(value) { window.ROICalculatorApp?.updateGracePeriod(value); }
|
|
function updateLoanRate(value) { window.ROICalculatorApp?.updateLoanRate(value); }
|
|
function updateCoreServiceRevenue(value) { window.ROICalculatorApp?.updateCoreServiceRevenue(value); }
|
|
function updateCurrency() {
|
|
const currencyElement = document.getElementById('currency');
|
|
const value = currencyElement ? currencyElement.value : 'CHF';
|
|
window.ROICalculatorApp?.updateCurrency(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(); }
|
|
function toggleScenario(scenarioKey) { window.ROICalculatorApp?.toggleScenario(scenarioKey); }
|
|
function updateMonthlyBreakdownFilters() { window.ROICalculatorApp?.updateMonthlyBreakdownFilters(); }
|
|
function toggleCollapsible(elementId) { window.ROICalculatorApp?.toggleCollapsible(elementId); }
|
|
function resetCalculator() { window.ROICalculatorApp?.resetCalculator(); }
|
|
// toggleInvestmentModel function removed - both models calculated simultaneously
|
|
function logout() { window.ROICalculatorApp?.logout(); }
|
|
|
|
// Manual toggle functions for collapse elements
|
|
function toggleAdvancedControls() {
|
|
const element = document.getElementById('advancedControls');
|
|
const button = document.getElementById('advancedToggleBtn');
|
|
|
|
console.log('Toggling advanced controls, current classes:', element.className);
|
|
|
|
if (element.style.display === 'none' || element.style.display === '') {
|
|
element.style.display = 'block';
|
|
button.innerHTML = '<i class="bi bi-gear"></i> Less';
|
|
console.log('Showing advanced controls');
|
|
} else {
|
|
element.style.display = 'none';
|
|
button.innerHTML = '<i class="bi bi-gear"></i> More';
|
|
console.log('Hiding advanced controls');
|
|
}
|
|
}
|
|
|
|
function toggleDataCollapse() {
|
|
const element = document.getElementById('dataCollapse');
|
|
const button = document.getElementById('dataToggleBtn');
|
|
|
|
console.log('Toggling data collapse, current style:', element.style.display);
|
|
|
|
if (element.style.display === 'none' || element.style.display === '') {
|
|
element.style.display = 'block';
|
|
button.classList.remove('collapsed');
|
|
button.setAttribute('aria-expanded', 'true');
|
|
console.log('Showing data collapse');
|
|
} else {
|
|
element.style.display = 'none';
|
|
button.classList.add('collapsed');
|
|
button.setAttribute('aria-expanded', 'false');
|
|
console.log('Hiding data collapse');
|
|
}
|
|
}
|
|
|
|
// Initialize collapse states
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Ensure both sections start collapsed
|
|
const advancedControls = document.getElementById('advancedControls');
|
|
const dataCollapse = document.getElementById('dataCollapse');
|
|
|
|
if (advancedControls) {
|
|
advancedControls.style.display = 'none';
|
|
}
|
|
if (dataCollapse) {
|
|
dataCollapse.style.display = 'none';
|
|
}
|
|
|
|
console.log('Collapse elements initialized as hidden');
|
|
});
|
|
</script>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid p-0" style="min-height: 100vh;">
|
|
<!-- Minimal Header with Controls -->
|
|
<div class="bg-light border-bottom sticky-top" style="z-index: 1030;">
|
|
<div class="container-fluid">
|
|
<!-- Title Row -->
|
|
<div class="row py-2 border-bottom">
|
|
<div class="col-md-6">
|
|
<h4 class="mb-0">CSP ROI Calculator</h4>
|
|
<small class="text-muted">Real-time investment analysis</small>
|
|
</div>
|
|
<div class="col-md-6 text-end">
|
|
<a href="{% url 'services:roi_calculator_help' %}" class="btn btn-sm btn-outline me-1" target="_blank">
|
|
<i class="bi bi-question-circle"></i> Help
|
|
</a>
|
|
<button type="button" class="btn btn-sm btn-outline-primary me-1" onclick="exportToPDF()">
|
|
<i class="bi bi-file-pdf"></i> PDF
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-success me-1" onclick="exportToCSV()">
|
|
<i class="bi bi-file-csv"></i> CSV
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline me-1" onclick="resetCalculator()">
|
|
<i class="bi bi-arrow-clockwise"></i> Reset
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-danger" onclick="logout()">
|
|
<i class="bi bi-box-arrow-right"></i> Logout
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Configuration Section -->
|
|
<div class="py-4">
|
|
<div class="row">
|
|
<!-- Left Column: Configuration Fields -->
|
|
<div class="col-lg-8 col-xl-7">
|
|
<div class="main-config-fields">
|
|
<!-- Investment Amount -->
|
|
<div class="mb-4">
|
|
<label class="form-label fw-semibold mb-2">Initial Investment</label>
|
|
<div class="input-group input-group-lg">
|
|
<span class="input-group-text" id="investment-currency-prefix">CHF</span>
|
|
<input type="text" class="form-control" id="investment-amount"
|
|
data-value="500000" value="500,000"
|
|
oninput="handleInvestmentAmountInput(this)"
|
|
onfocus="handleInvestmentAmountFocus(this)"
|
|
onblur="handleInvestmentAmountBlur(this)"
|
|
placeholder="Enter amount (100,000 - 2,000,000)">
|
|
</div>
|
|
<input type="range" class="form-range mt-3" id="investment-slider"
|
|
min="100000" max="2000000" step="50000" value="500000"
|
|
onchange="updateInvestmentAmount(this.value)">
|
|
<div class="d-flex justify-content-between mt-1">
|
|
<small class="text-muted" id="investment-min-label">CHF 100K</small>
|
|
<small class="text-muted" id="investment-max-label">CHF 2M</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Currency, Analysis Period & Service Revenue Row -->
|
|
<div class="row mb-4">
|
|
<div class="col-md-3">
|
|
<label class="form-label fw-semibold mb-2">Currency</label>
|
|
<select class="form-select form-select-lg" id="currency" onchange="updateCurrency()">
|
|
<option value="CHF" selected>CHF (Swiss Franc)</option>
|
|
<option value="EUR">EUR (Euro)</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<label class="form-label fw-semibold mb-2">Analysis Period</label>
|
|
<select class="form-select form-select-lg" id="timeframe" onchange="updateCalculations()">
|
|
<option value="1">1 Year</option>
|
|
<option value="2">2 Years</option>
|
|
<option value="3" selected>3 Years</option>
|
|
<option value="4">4 Years</option>
|
|
<option value="5">5 Years</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-semibold mb-2">Service Revenue per Instance</label>
|
|
<div class="input-group input-group-lg">
|
|
<input type="number" class="form-control" id="revenue-per-instance"
|
|
min="20" max="200" step="5" value="50" onchange="updateCalculations()">
|
|
<span class="input-group-text" id="revenue-currency-suffix">CHF/month</span>
|
|
</div>
|
|
<input type="range" class="form-range mt-3" id="revenue-slider"
|
|
min="20" max="200" step="5" value="50"
|
|
onchange="updateRevenuePerInstance(this.value)">
|
|
<div class="d-flex justify-content-between mt-1">
|
|
<small class="text-muted" id="revenue-min-label">CHF 20</small>
|
|
<small class="text-muted" id="revenue-max-label">CHF 200</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Growth Scenarios -->
|
|
<div class="mb-4">
|
|
<label class="form-label fw-semibold mb-3">Growth Scenarios</label>
|
|
<div class="d-flex gap-4 flex-wrap">
|
|
<div class="form-check form-check-lg">
|
|
<input class="form-check-input" type="checkbox" id="conservative-enabled" checked onchange="toggleScenario('conservative')">
|
|
<label class="form-check-label fw-medium" for="conservative-enabled" data-bs-toggle="tooltip" title="Conservative: 2% churn, steady growth">
|
|
<span class="text-success fs-4">●</span> Conservative
|
|
</label>
|
|
</div>
|
|
<div class="form-check form-check-lg">
|
|
<input class="form-check-input" type="checkbox" id="moderate-enabled" checked onchange="toggleScenario('moderate')">
|
|
<label class="form-check-label fw-medium" for="moderate-enabled" data-bs-toggle="tooltip" title="Moderate: 3% churn, balanced growth">
|
|
<span class="text-warning fs-4">●</span> Moderate
|
|
</label>
|
|
</div>
|
|
<div class="form-check form-check-lg">
|
|
<input class="form-check-input" type="checkbox" id="aggressive-enabled" checked onchange="toggleScenario('aggressive')">
|
|
<label class="form-check-label fw-medium" for="aggressive-enabled" data-bs-toggle="tooltip" title="Aggressive: 5% churn, rapid growth">
|
|
<span class="text-danger fs-4">●</span> Aggressive
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Action Buttons -->
|
|
<div class="d-flex gap-3 flex-wrap">
|
|
<button class="btn btn-outline-info btn-lg" type="button" onclick="toggleAdvancedControls()" id="advancedToggleBtn">
|
|
<i class="bi bi-gear"></i> Advanced Settings
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Column: Real-Time Results -->
|
|
<div class="col-lg-4 col-xl-5">
|
|
<div class="results-panel h-100">
|
|
<div class="card h-100 border-0 shadow">
|
|
<div class="card-header bg-primary text-white">
|
|
<h5 class="mb-0"><i class="bi bi-graph-up"></i> Real-Time Results</h5>
|
|
<small class="opacity-75">Live calculations based on your parameters</small>
|
|
</div>
|
|
<div class="card-body d-flex flex-column justify-content-center">
|
|
<!-- Direct Investment Results -->
|
|
<div class="result-item mb-4">
|
|
<div class="d-flex align-items-center mb-2">
|
|
<div class="result-icon bg-success text-white rounded-circle me-3 d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
|
|
<i class="bi bi-rocket"></i>
|
|
</div>
|
|
<div>
|
|
<h6 class="mb-0 text-success fw-bold">Direct Investment</h6>
|
|
<small class="text-muted">Performance-based returns</small>
|
|
</div>
|
|
</div>
|
|
<div class="result-metrics bg-light rounded p-3">
|
|
<div class="row text-center">
|
|
<div class="col-6">
|
|
<div class="h4 mb-1 text-success fw-bold" id="net-position-direct">CHF 0</div>
|
|
<small class="text-muted">Net Profit</small>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="h4 mb-1 text-primary fw-bold" id="roi-percentage-direct">0%</div>
|
|
<small class="text-muted">Total ROI</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Loan Model Results -->
|
|
<div class="result-item">
|
|
<div class="d-flex align-items-center mb-2">
|
|
<div class="result-icon bg-warning text-dark rounded-circle me-3 d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
|
|
<i class="bi bi-bank"></i>
|
|
</div>
|
|
<div>
|
|
<h6 class="mb-0 text-warning fw-bold">Loan Model</h6>
|
|
<small class="text-muted">Fixed guaranteed returns</small>
|
|
</div>
|
|
</div>
|
|
<div class="result-metrics bg-light rounded p-3">
|
|
<div class="row text-center">
|
|
<div class="col-6">
|
|
<div class="h4 mb-1 text-success fw-bold" id="net-position-loan">CHF 0</div>
|
|
<small class="text-muted">Net Profit</small>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="h4 mb-1 text-primary fw-bold" id="roi-percentage-loan">0%</div>
|
|
<small class="text-muted">Total ROI</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Advanced Controls Section -->
|
|
<div class="collapse" id="advancedControls">
|
|
<div class="bg-light border-top py-4">
|
|
<div class="container-fluid">
|
|
<h6 class="text-primary mb-4"><i class="bi bi-gear"></i> Advanced Configuration</h6>
|
|
|
|
<!-- Investment Model Parameters -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<h6 class="text-secondary mb-3">Investment Model Settings</h6>
|
|
</div>
|
|
|
|
<!-- Loan Rate -->
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<label class="form-label fw-semibold mb-2">Loan Interest Rate</label>
|
|
<div class="input-group">
|
|
<input type="number" class="form-control" id="loan-interest-rate"
|
|
min="3" max="8" step="0.1" value="5.0" onchange="updateCalculations()">
|
|
<span class="input-group-text">% annual</span>
|
|
</div>
|
|
<input type="range" class="form-range mt-2" id="loan-rate-slider"
|
|
min="3" max="8" step="0.1" value="5.0" onchange="updateLoanRate(this.value)">
|
|
<div class="d-flex justify-content-between">
|
|
<small class="text-muted">3%</small>
|
|
<small class="text-muted">8%</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Servala Share -->
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<label class="form-label fw-semibold mb-2">Servala Revenue Share</label>
|
|
<div class="input-group">
|
|
<input type="number" class="form-control" id="servala-share"
|
|
min="10" max="40" step="1" value="25" onchange="updateCalculations()">
|
|
<span class="input-group-text">%</span>
|
|
</div>
|
|
<input type="range" class="form-range mt-2" id="share-slider"
|
|
min="10" max="40" step="1" value="25" onchange="updateServalaShare(this.value)">
|
|
<div class="d-flex justify-content-between">
|
|
<small class="text-muted">10%</small>
|
|
<small class="text-muted">40%</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Grace Period -->
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<label class="form-label fw-semibold mb-2">Grace Period</label>
|
|
<div class="input-group">
|
|
<input type="number" class="form-control" id="grace-period"
|
|
min="0" max="24" step="1" value="6" onchange="updateCalculations()">
|
|
<span class="input-group-text">months</span>
|
|
</div>
|
|
<input type="range" class="form-range mt-2" id="grace-slider"
|
|
min="0" max="24" step="1" value="6" onchange="updateGracePeriod(this.value)">
|
|
<div class="d-flex justify-content-between">
|
|
<small class="text-muted">0</small>
|
|
<small class="text-muted">24</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Core Service Revenue -->
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<label class="form-label fw-semibold mb-2">Core Service Revenue</label>
|
|
<div class="input-group">
|
|
<input type="number" class="form-control" id="core-service-revenue"
|
|
min="0" max="500" step="5" value="0" onchange="updateCalculations()">
|
|
<span class="input-group-text">CHF/month</span>
|
|
</div>
|
|
<input type="range" class="form-range mt-2" id="core-revenue-slider"
|
|
min="0" max="500" step="5" value="0" onchange="updateCoreServiceRevenue(this.value)">
|
|
<div class="d-flex justify-content-between">
|
|
<small class="text-muted">CHF 0</small>
|
|
<small class="text-muted">CHF 500</small>
|
|
</div>
|
|
<small class="text-muted">Additional compute/storage revenue per instance</small>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Scenario Parameters -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<h6 class="text-secondary mb-3">Growth Scenario Tuning</h6>
|
|
</div>
|
|
|
|
<!-- Conservative Scenario -->
|
|
<div class="col-lg-4 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header bg-success text-white">
|
|
<h6 class="mb-0"><i class="bi bi-shield-check"></i> Conservative Scenario</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Churn Rate -->
|
|
<div class="mb-3">
|
|
<label class="form-label fw-semibold mb-2">Monthly Churn Rate</label>
|
|
<div class="input-group input-group-sm">
|
|
<input type="number" class="form-control" id="conservative-churn"
|
|
min="0" max="10" step="0.1" value="2.0" onchange="updateScenarioChurn('conservative', this.value)">
|
|
<span class="input-group-text">%</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Phase Growth Parameters -->
|
|
<label class="form-label fw-semibold mb-2">Instance Growth per Phase</label>
|
|
<div class="row g-2">
|
|
<div class="col-6">
|
|
<label class="form-label small">Phase 1 (6mo)</label>
|
|
<input type="number" class="form-control form-control-sm" id="conservative-phase-0"
|
|
min="10" max="200" value="50" onchange="updateScenarioPhase('conservative', 0, this.value)">
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small">Phase 2 (6mo)</label>
|
|
<input type="number" class="form-control form-control-sm" id="conservative-phase-1"
|
|
min="10" max="200" value="75" onchange="updateScenarioPhase('conservative', 1, this.value)">
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small">Phase 3 (12mo)</label>
|
|
<input type="number" class="form-control form-control-sm" id="conservative-phase-2"
|
|
min="10" max="300" value="100" onchange="updateScenarioPhase('conservative', 2, this.value)">
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small">Phase 4 (12mo)</label>
|
|
<input type="number" class="form-control form-control-sm" id="conservative-phase-3"
|
|
min="10" max="300" value="150" onchange="updateScenarioPhase('conservative', 3, this.value)">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Moderate Scenario -->
|
|
<div class="col-lg-4 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header bg-warning text-dark">
|
|
<h6 class="mb-0"><i class="bi bi-speedometer2"></i> Moderate Scenario</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Churn Rate -->
|
|
<div class="mb-3">
|
|
<label class="form-label fw-semibold mb-2">Monthly Churn Rate</label>
|
|
<div class="input-group input-group-sm">
|
|
<input type="number" class="form-control" id="moderate-churn"
|
|
min="0" max="10" step="0.1" value="3.0" onchange="updateScenarioChurn('moderate', this.value)">
|
|
<span class="input-group-text">%</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Phase Growth Parameters -->
|
|
<label class="form-label fw-semibold mb-2">Instance Growth per Phase</label>
|
|
<div class="row g-2">
|
|
<div class="col-6">
|
|
<label class="form-label small">Phase 1 (6mo)</label>
|
|
<input type="number" class="form-control form-control-sm" id="moderate-phase-0"
|
|
min="20" max="300" value="100" onchange="updateScenarioPhase('moderate', 0, this.value)">
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small">Phase 2 (6mo)</label>
|
|
<input type="number" class="form-control form-control-sm" id="moderate-phase-1"
|
|
min="20" max="400" value="200" onchange="updateScenarioPhase('moderate', 1, this.value)">
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small">Phase 3 (12mo)</label>
|
|
<input type="number" class="form-control form-control-sm" id="moderate-phase-2"
|
|
min="20" max="500" value="300" onchange="updateScenarioPhase('moderate', 2, this.value)">
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small">Phase 4 (12mo)</label>
|
|
<input type="number" class="form-control form-control-sm" id="moderate-phase-3"
|
|
min="20" max="600" value="400" onchange="updateScenarioPhase('moderate', 3, this.value)">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Aggressive Scenario -->
|
|
<div class="col-lg-4 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header bg-danger text-white">
|
|
<h6 class="mb-0"><i class="bi bi-rocket"></i> Aggressive Scenario</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<!-- Churn Rate -->
|
|
<div class="mb-3">
|
|
<label class="form-label fw-semibold mb-2">Monthly Churn Rate</label>
|
|
<div class="input-group input-group-sm">
|
|
<input type="number" class="form-control" id="aggressive-churn"
|
|
min="0" max="15" step="0.1" value="5.0" onchange="updateScenarioChurn('aggressive', this.value)">
|
|
<span class="input-group-text">%</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Phase Growth Parameters -->
|
|
<label class="form-label fw-semibold mb-2">Instance Growth per Phase</label>
|
|
<div class="row g-2">
|
|
<div class="col-6">
|
|
<label class="form-label small">Phase 1 (6mo)</label>
|
|
<input type="number" class="form-control form-control-sm" id="aggressive-phase-0"
|
|
min="50" max="500" value="200" onchange="updateScenarioPhase('aggressive', 0, this.value)">
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small">Phase 2 (6mo)</label>
|
|
<input type="number" class="form-control form-control-sm" id="aggressive-phase-1"
|
|
min="50" max="600" value="400" onchange="updateScenarioPhase('aggressive', 1, this.value)">
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small">Phase 3 (12mo)</label>
|
|
<input type="number" class="form-control form-control-sm" id="aggressive-phase-2"
|
|
min="50" max="800" value="600" onchange="updateScenarioPhase('aggressive', 2, this.value)">
|
|
</div>
|
|
<div class="col-6">
|
|
<label class="form-label small">Phase 4 (12mo)</label>
|
|
<input type="number" class="form-control form-control-sm" id="aggressive-phase-3"
|
|
min="50" max="1000" value="800" onchange="updateScenarioPhase('aggressive', 3, this.value)">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Reset Button -->
|
|
<div class="row">
|
|
<div class="col-12 text-center">
|
|
<button type="button" class="btn btn-outline-secondary" onclick="resetAdvancedParameters()">
|
|
<i class="bi bi-arrow-clockwise"></i> Reset to Defaults
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- CHARTS - Maximum Space -->
|
|
<div class="container-fluid px-3 py-3" style="background: #f8f9fa;">
|
|
<!-- Loading Spinner -->
|
|
<div class="loading-spinner text-center py-5" id="loading-spinner" style="display: none;">
|
|
<div class="spinner-border text-primary" role="status">
|
|
<span class="visually-hidden">Calculating...</span>
|
|
</div>
|
|
<p class="mt-2">Calculating scenarios...</p>
|
|
</div>
|
|
|
|
|
|
<!-- PRIMARY CHART - Full Width, Large Height -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-header bg-white border-0 pb-0">
|
|
<h5 class="mb-1"><i class="bi bi-graph-up-arrow text-primary"></i> ROI Progression Over Time</h5>
|
|
<p class="small text-muted mb-0">Investment profitability timeline - when you'll break even and achieve target returns</p>
|
|
</div>
|
|
<div class="card-body pt-3">
|
|
<canvas id="instanceGrowthChart" style="height: 500px; width: 100%;"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- SECONDARY CHARTS - Side by Side, Large -->
|
|
<div class="row mb-4">
|
|
<div class="col-xl-6 mb-4">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-header bg-white border-0 pb-0">
|
|
<h5 class="mb-1"><i class="bi bi-cash-stack text-success"></i> Net Financial Position</h5>
|
|
<p class="small text-muted mb-0">Cumulative profit/loss over time</p>
|
|
</div>
|
|
<div class="card-body pt-3">
|
|
<canvas id="revenueChart" style="height: 400px; width: 100%;"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-6 mb-4">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-header bg-white border-0 pb-0">
|
|
<h5 class="mb-1"><i class="bi bi-bar-chart text-warning"></i> Performance Comparison</h5>
|
|
<p class="small text-muted mb-0">ROI performance across growth scenarios</p>
|
|
</div>
|
|
<div class="card-body pt-3">
|
|
<canvas id="cashFlowChart" style="height: 400px; width: 100%;"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- CSP REVENUE BREAKDOWN CHART - Full Width -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-header bg-white border-0 pb-0">
|
|
<h5 class="mb-1"><i class="bi bi-cash-stack text-success"></i> CSP Revenue Breakdown</h5>
|
|
<p class="small text-muted mb-0">Direct investment revenue breakdown: Service fees, core infrastructure sales, total CSP revenue, and Servala share over time</p>
|
|
</div>
|
|
<div class="card-body pt-3">
|
|
<canvas id="cspRevenueChart" style="height: 400px; width: 100%;"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DATA TABLE - Collapsible to Save Space -->
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="accordion" id="dataAccordion">
|
|
<div class="accordion-item border-0 shadow-sm">
|
|
<h2 class="accordion-header" id="dataHeading">
|
|
<button class="accordion-button collapsed" type="button" onclick="toggleDataCollapse()" id="dataToggleBtn">
|
|
<i class="bi bi-table me-2"></i> Detailed Financial Analysis
|
|
</button>
|
|
</h2>
|
|
<div id="dataCollapse" class="accordion-collapse collapse" aria-labelledby="dataHeading" data-bs-parent="#dataAccordion">
|
|
<div class="accordion-body">
|
|
<!-- Improved Scenario Performance Summary -->
|
|
<h6 class="mb-3">Investment Model Comparison by Scenario</h6>
|
|
<div class="table-responsive mb-4">
|
|
<table class="table table-sm table-hover" id="comparison-table">
|
|
<thead class="table-dark">
|
|
<tr>
|
|
<th>Scenario</th>
|
|
<th>Investment Model</th>
|
|
<th>Final Scale</th>
|
|
<th>Your Net Profit</th>
|
|
<th>Total ROI</th>
|
|
<th>Break-even Time</th>
|
|
<th>Key Features</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="comparison-tbody">
|
|
<!-- Dynamic content -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Monthly Financial Flow -->
|
|
<h6 class="mb-3">Monthly Financial Breakdown</h6>
|
|
|
|
<!-- Breakdown Filter Controls -->
|
|
<div class="row mb-3 breakdown-filters">
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-semibold mb-2">Investment Models</label>
|
|
<div class="d-flex gap-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="breakdown-direct-enabled" checked onchange="updateMonthlyBreakdownFilters()">
|
|
<label class="form-check-label small fw-medium text-success" for="breakdown-direct-enabled">
|
|
Direct Investment
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="breakdown-loan-enabled" checked onchange="updateMonthlyBreakdownFilters()">
|
|
<label class="form-check-label small fw-medium text-warning" for="breakdown-loan-enabled">
|
|
Loan Model
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label class="form-label small fw-semibold mb-2">Growth Scenarios</label>
|
|
<div class="d-flex gap-3">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="breakdown-conservative-enabled" checked onchange="updateMonthlyBreakdownFilters()">
|
|
<label class="form-check-label small fw-medium" for="breakdown-conservative-enabled">
|
|
<span class="text-success">•</span> Conservative
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="breakdown-moderate-enabled" checked onchange="updateMonthlyBreakdownFilters()">
|
|
<label class="form-check-label small fw-medium" for="breakdown-moderate-enabled">
|
|
<span class="text-warning">•</span> Moderate
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="breakdown-aggressive-enabled" checked onchange="updateMonthlyBreakdownFilters()">
|
|
<label class="form-check-label small fw-medium" for="breakdown-aggressive-enabled">
|
|
<span class="text-danger">•</span> Aggressive
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="table-responsive" style="max-height: 500px; overflow-y: auto;">
|
|
<table class="table table-sm table-striped" id="monthly-table">
|
|
<thead class="table-dark sticky-top">
|
|
<tr>
|
|
<th>Month</th>
|
|
<th>Scenario</th>
|
|
<th>Model</th>
|
|
<th>Instances</th>
|
|
<th>Service Revenue</th>
|
|
<th>Core Revenue</th>
|
|
<th>Total Revenue</th>
|
|
<th>Your Share</th>
|
|
<th>Servala Share</th>
|
|
<th>Cumulative Net Position</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="monthly-tbody">
|
|
<!-- Dynamic content -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %} |