website/hub/services/templates/services/pricelist.html

794 lines
No EOL
52 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends 'base.html' %}
{% load static %}
{% load math_tags %}
{% block title %}Complete Price List{% endblock %}
{% block extra_js %}
<script src="{% static "js/chart.js" %}"></script>
{% endblock %}
{% block extra_css %}
<style>
.addon-details {
max-width: 300px;
font-size: 0.85em;
}
.addon-item {
background-color: #f8f9fa;
border-radius: 4px;
padding: 4px 8px;
margin-bottom: 4px;
}
.addon-name {
font-weight: 500;
}
.addon-price {
color: #28a745;
font-size: 0.9em;
}
.pricing-table th {
white-space: nowrap;
}
.final-price-header {
background-color: #28a745 !important;
color: white !important;
}
.final-price-cell {
background-color: #d4edda;
color: #155724;
}
.comparison-row {
font-size: 0.9em;
}
.servala-row {
border-bottom: 2px solid #007bff;
}
/* Price calculation breakdown styling */
.price-breakdown-header {
background: linear-gradient(135deg, #28a745, #20b2aa);
}
.compute-plan-col {
background-color: rgba(13, 110, 253, 0.1);
border-right: 2px solid #0d6efd;
}
.sla-base-col {
background-color: rgba(111, 66, 193, 0.1);
border-right: 2px solid #6f42c1;
}
.sla-units-col {
background-color: rgba(253, 126, 20, 0.1);
border-right: 2px solid #fd7e14;
}
.mandatory-addons-col {
background-color: rgba(220, 53, 69, 0.1);
border-right: 2px solid #dc3545;
}
.total-sla-col {
background-color: rgba(25, 135, 84, 0.2);
border-right: 3px solid #198754;
}
/* Mathematical operator styling */
.math-operator {
font-size: 1.2em;
font-weight: bold;
color: #666;
padding: 0 5px;
}
/* Price calculation formula helper */
.price-formula {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 0.25rem;
padding: 10px;
margin-bottom: 20px;
font-family: monospace;
text-align: center;
}
.price-formula .formula-part {
display: inline-block;
padding: 2px 8px;
margin: 0 5px;
border-radius: 3px;
font-weight: bold;
}
.price-formula .compute-part { background-color: rgba(13, 110, 253, 0.2); color: #0d6efd; }
.price-formula .sla-base-part { background-color: rgba(111, 66, 193, 0.2); color: #6f42c1; }
.price-formula .sla-units-part { background-color: rgba(253, 126, 20, 0.2); color: #fd7e14; }
.price-formula .addons-part { background-color: rgba(220, 53, 69, 0.2); color: #dc3545; }
.price-formula .equals-part { background-color: rgba(25, 135, 84, 0.2); color: #198754; }
</style>
{% endblock %}
{% block content %}
<div class="container-fluid mt-4">
<div class="row">
<div class="col-12">
<h1 class="mb-4">Complete Price List - All Service Variants</h1>
<!-- Pricing Model Explanation -->
<div class="card mb-4">
<div class="card-header" data-bs-toggle="collapse" data-bs-target="#pricingExplanation" aria-expanded="false" aria-controls="pricingExplanation" style="cursor: pointer;">
<h5 class="mb-0">
<i class="bi bi-info-circle me-2"></i>How Our Pricing Works
<small class="text-muted ms-2">(Click to expand)</small>
</h5>
</div>
<div class="collapse" id="pricingExplanation">
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h6>Price Components</h6>
<ul class="list-unstyled">
<li class="mb-2">
<span class="badge" style="background-color: #0d6efd;">Compute Plan Price</span>
<span class="ms-2">Base infrastructure cost for CPU, memory, and storage</span>
</li>
<li class="mb-2">
<span class="badge" style="background-color: #6f42c1;">SLA Base</span>
<span class="ms-2">Fixed cost for the service level agreement</span>
</li>
<li class="mb-2">
<span class="badge" style="background-color: #fd7e14;">Units × SLA Per Unit</span>
<span class="ms-2">Variable cost based on scale/usage</span>
</li>
<li class="mb-2">
<span class="badge" style="background-color: #dc3545;">Mandatory Add-ons</span>
<span class="ms-2">Required additional services (backup, monitoring, etc.)</span>
</li>
</ul>
</div>
<div class="col-md-6">
<h6>Final Price Formula</h6>
<div class="bg-light p-3 rounded">
<code>
<span style="color: #0d6efd;">Compute Plan Price</span> +
<span style="color: #6f42c1;">SLA Base</span> +
<span style="color: #fd7e14;">(Units × SLA Per Unit)</span> +
<span style="color: #dc3545;">Mandatory Add-ons</span> =
<strong style="color: #198754;">Final Price</strong>
</code>
</div>
<p class="mt-3 mb-0">
<small class="text-muted">
This transparent pricing model ensures you understand exactly what you're paying for.
The table below breaks down each component for every service variant we offer.
</small>
</p>
</div>
</div>
</div>
</div>
</div>
<!-- Filter Form -->
<div class="card mb-4">
<div class="card-header">
<h5 class="mb-0">Filters</h5>
</div>
<div class="card-body">
<form method="get" class="row g-3" id="filter-form">
<div class="col-md-3">
<label for="cloud_provider" class="form-label">Cloud Provider</label>
<select name="cloud_provider" id="cloud_provider" class="form-select filter-select">
<option value="">All Providers</option>
{% for provider in all_cloud_providers %}
<option value="{{ provider }}" {% if provider == filter_cloud_provider %}selected{% endif %}>
{{ provider }}
</option>
{% endfor %}
</select>
</div>
<div class="col-md-3">
<label for="service" class="form-label">Service</label>
<select name="service" id="service" class="form-select filter-select">
<option value="">All Services</option>
{% for service in all_services %}
<option value="{{ service }}" {% if service == filter_service %}selected{% endif %}>
{{ service }}
</option>
{% endfor %}
</select>
</div>
<div class="col-md-3">
<label for="compute_plan_group" class="form-label">Compute Plan Group</label>
<select name="compute_plan_group" id="compute_plan_group" class="form-select filter-select">
<option value="">All Groups</option>
{% for group in all_compute_plan_groups %}
<option value="{{ group }}" {% if group == filter_compute_plan_group %}selected{% endif %}>
{{ group }}
</option>
{% endfor %}
</select>
</div>
<div class="col-md-3">
<label for="service_level" class="form-label">Service Level</label>
<select name="service_level" id="service_level" class="form-select filter-select">
<option value="">All Service Levels</option>
{% for level in all_service_levels %}
<option value="{{ level }}" {% if level == filter_service_level %}selected{% endif %}>
{{ level }}
</option>
{% endfor %}
</select>
</div>
<div class="col-12">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="discount_details" value="true" id="discount_details" {% if show_discount_details %}checked{% endif %}>
<label class="form-check-label" for="discount_details">
Show discount details
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="addon_details" value="true" id="addon_details" {% if show_addon_details %}checked{% endif %}>
<label class="form-check-label" for="addon_details">
Show addon details
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" name="price_comparison" value="true" id="price_comparison" {% if show_price_comparison %}checked{% endif %}>
<label class="form-check-label" for="price_comparison">
Show external price comparisons
</label>
</div>
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary">Apply Filters</button>
<a href="{% url 'services:pricelist' %}" class="btn btn-secondary">Clear Filters</a>
</div>
</form>
</div>
</div>
<!-- Active Filters Display -->
{% if filter_cloud_provider or filter_service or filter_compute_plan_group or filter_service_level or show_discount_details or show_addon_details or show_price_comparison %}
<div class="alert alert-info">
<strong>Active Filters:</strong>
{% if filter_cloud_provider %}<span class="badge me-1">Cloud Provider: {{ filter_cloud_provider }}</span>{% endif %}
{% if filter_service %}<span class="badge me-1">Service: {{ filter_service }}</span>{% endif %}
{% if filter_compute_plan_group %}<span class="badge me-1">Group: {{ filter_compute_plan_group }}</span>{% endif %}
{% if filter_service_level %}<span class="badge me-1">Service Level: {{ filter_service_level }}</span>{% endif %}
{% if show_discount_details %}<span class="badge bg-secondary me-1">Discount Details</span>{% endif %}
{% if show_addon_details %}<span class="badge bg-info me-1">Addon Details</span>{% endif %}
{% if show_price_comparison %}<span class="badge bg-warning me-1">Price Comparison</span>{% endif %}
</div>
{% endif %}
{% if pricing_data_by_group_and_service_level %}
{% for group_name, service_levels in pricing_data_by_group_and_service_level.items %}
<div class="mb-5 border rounded p-3">
<h2 class="mb-3 text-primary">{{ group_name }}</h2>
{# Display group description and node_label from first available plan #}
{% for service_level, pricing_data in service_levels.items %}
{% if pricing_data and forloop.first %}
{% with pricing_data.0 as representative_plan %}
{% if representative_plan.compute_plan_group_description %}
<p class="text-muted mb-2"><strong>Description:</strong> {{ representative_plan.compute_plan_group_description }}</p>
{% endif %}
{% if representative_plan.compute_plan_group_node_label %}
<p class="text-muted mb-3"><strong>Node Label:</strong> <code>{{ representative_plan.compute_plan_group_node_label }}</code></p>
{% endif %}
{# Display storage pricing for this cloud provider #}
{% if representative_plan.storage_plans %}
<div class="mb-3">
<p class="text-muted mb-2"><strong>Storage Options:</strong></p>
<div class="table-responsive">
<table class="table table-sm table-bordered">
<thead class="table-secondary">
<tr>
<th>Storage Plan</th>
<th>Term</th>
<th>Unit</th>
<th>Prices</th>
</tr>
</thead>
<tbody>
{% for storage_plan in representative_plan.storage_plans %}
<tr>
<td>{{ storage_plan.name }}</td>
<td>{{ storage_plan.get_term_display }}</td>
<td>{{ storage_plan.get_unit_display }}</td>
<td>
{% for price in storage_plan.prices.all %}
<span class="badge bg-light text-dark me-1">{{ price.amount }} {{ price.currency }}</span>
{% empty %}
<span class="text-muted">No prices</span>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% endwith %}
{% endif %}
{% endfor %}
{% for service_level, pricing_data in service_levels.items %}
<div class="mb-4">
<h3 class="mb-3">SLA: {{ service_level }}</h3>
{% if pricing_data %}
{# Display common values for this service level #}
{% with pricing_data.0 as first_row %}
<div class="row mb-3">
<div class="col-md-2">
<strong>Cloud Provider:</strong> {{ first_row.cloud_provider }}
</div>
<div class="col-md-2">
<strong>Service:</strong> {{ first_row.service }}
</div>
<div class="col-md-2">
<strong>CPU/Memory Ratio:</strong> {{ first_row.cpu_mem_ratio }}
</div>
<div class="col-md-2">
<strong>Variable Unit:</strong> {{ first_row.variable_unit }}
</div>
<div class="col-md-2">
<strong>Replica Enforce:</strong> {{ first_row.replica_enforce }}
</div>
</div>
{# Display add-on summary #}
{% if show_addon_details and first_row.mandatory_addons or first_row.optional_addons %}
<div class="card mb-3">
<div class="card-header">
<h6 class="mb-0">Available Add-ons for {{ first_row.service }}</h6>
</div>
<div class="card-body">
{% if first_row.mandatory_addons %}
<div class="mb-3">
<h6 class="text-success">Mandatory Add-ons (included in all plans):</h6>
<div class="row">
{% for addon in first_row.mandatory_addons %}
<div class="col-md-4 mb-2">
<div class="border border-success rounded p-2">
<strong>{{ addon.name }}</strong>
<div class="text-muted small">{{ addon.commercial_description|default:addon.description }}</div>
<div class="text-success fw-bold">{{ addon.price|floatformat:2 }} {{ first_row.currency }}</div>
<small class="text-muted">{{ addon.addon_type }}</small>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% if first_row.optional_addons %}
<div>
<h6 class="text-info">Optional Add-ons (can be added):</h6>
<div class="row">
{% for addon in first_row.optional_addons %}
<div class="col-md-4 mb-2">
<div class="border border-info rounded p-2">
<strong>{{ addon.name }}</strong>
<div class="text-muted small">{{ addon.commercial_description|default:addon.description }}</div>
<div class="text-info fw-bold">{{ addon.price|floatformat:2 }} {{ first_row.currency }}</div>
<small class="text-muted">{{ addon.addon_type }}</small>
</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
{% endif %}
{% endwith %}
<!-- Price Calculation Formula Helper -->
<div class="price-formula">
<strong>Final Price Calculation:</strong><br>
<span class="formula-part compute-part">Compute Plan Price</span>
<span class="math-operator">+</span>
<span class="formula-part sla-base-part">SLA Base</span>
<span class="math-operator">+</span>
<span class="formula-part sla-units-part">Units × SLA Per Unit</span>
<span class="math-operator">+</span>
<span class="formula-part addons-part">Mandatory Add-ons</span>
<span class="math-operator">=</span>
<span class="formula-part equals-part">Final Price</span>
</div>
<div class="table-responsive">
<table class="table table-striped table-bordered table-sm pricing-table">
<thead class="table-dark">
<tr>
<th rowspan="2">Compute Plan</th>
<th rowspan="2">Cloud Provider</th>
<th rowspan="2">vCPUs</th>
<th rowspan="2">RAM (GB)</th>
<th rowspan="2">Term</th>
<th rowspan="2">Currency</th>
<th colspan="5" class="text-center" style="background-color: #198754 !important;">Price Calculation Breakdown</th>
{% if show_addon_details %}
<th rowspan="2">Add-ons</th>
{% endif %}
{% if show_discount_details %}
<th rowspan="2">Discount Model</th>
<th rowspan="2">Discount Details</th>
{% endif %}
{% if show_price_comparison %}
<th rowspan="2">External Comparisons</th>
{% endif %}
<th rowspan="2" class="final-price-header">Final Price</th>
</tr>
<tr>
<th style="background-color: #0d6efd; color: white;">Compute Plan Price</th>
<th style="background-color: #6f42c1; color: white;">SLA Base</th>
<th style="background-color: #fd7e14; color: white;">Units × SLA Per Unit</th>
<th style="background-color: #dc3545; color: white;">Mandatory Add-ons</th>
<th style="background-color: #198754; color: white;">= Total SLA Price</th>
</tr>
</thead>
<tbody>
{% for row in pricing_data %}
<tr class="servala-row">
<td>{{ row.compute_plan }}</td>
<td>{{ row.cloud_provider }}</td>
<td>{{ row.vcpus }}</td>
<td>{{ row.ram }}</td>
<td>{{ row.term }}</td>
<td>{{ row.currency }}</td>
<!-- Price Calculation Breakdown -->
<td class="text-center" style="background-color: rgba(13, 110, 253, 0.1);">
<span class="fw-bold">{{ row.compute_plan_price|floatformat:2 }}</span>
</td>
<td class="text-center" style="background-color: rgba(111, 66, 193, 0.1);">
<span class="fw-bold">{{ row.sla_base|floatformat:2 }}</span>
</td>
<td class="text-center" style="background-color: rgba(253, 126, 20, 0.1);">
<span class="fw-bold">{{ row.units|floatformat:0 }} × {{ row.sla_per_unit|floatformat:4 }}</span><br>
<small class="text-muted">= {{ row.units|multiply:row.sla_per_unit|floatformat:2 }}</small>
</td>
<td class="text-center" style="background-color: rgba(220, 53, 69, 0.1);">
{% if row.mandatory_addons %}
{% for addon in row.mandatory_addons %}
<div class="mb-1">
{% if addon.addon_type == "Unit Rate" %}
<strong>{{ addon.name }}</strong><br>
<span class="fw-bold">{{ row.units|floatformat:0 }} × {{ addon.price|floatformat:4 }}</span><br>
<small class="text-muted">= {{ row.units|multiply:addon.price|floatformat:2 }}</small>
{% elif addon.addon_type == "Base Fee" %}
<strong>{{ addon.name }}</strong><br>
<span class="fw-bold">{{ addon.price|floatformat:2 }}</span>
{% else %}
<strong>{{ addon.name }}</strong><br>
<span class="fw-bold">{{ addon.price|floatformat:2 }}</span>
{% endif %}
</div>
{% if not forloop.last %}<hr class="my-1">{% endif %}
{% endfor %}
{% else %}
<span class="text-muted">n/a</span>
{% endif %}
</td>
<td class="text-center fw-bold" style="background-color: rgba(25, 135, 84, 0.2);">
{% with addon_total=row.mandatory_addons|calculate_addon_total:row.units %}
{{ row.sla_price|add_float:addon_total|floatformat:2 }}
{% endwith %}
</td>
{% if show_addon_details %}
<td>
{% if row.mandatory_addons or row.optional_addons %}
<div class="addon-details">
{% if row.mandatory_addons %}
<div class="mb-2">
<small class="text-success fw-bold">Mandatory Add-ons:</small>
{% for addon in row.mandatory_addons %}
<div class="addon-item border-start border-success ps-2 mb-1">
<div class="d-flex justify-content-between">
<span class="addon-name">{{ addon.name }}</span>
<span class="addon-price fw-bold">{{ addon.price|floatformat:2 }} {{ row.currency }}</span>
</div>
{% if addon.commercial_description %}
<div class="text-muted small">{{ addon.commercial_description }}</div>
{% elif addon.description %}
<div class="text-muted small">{{ addon.description }}</div>
{% endif %}
<div class="text-muted small">Type: {{ addon.addon_type }}</div>
</div>
{% endfor %}
</div>
{% endif %}
{% if row.optional_addons %}
<div>
<small class="text-info fw-bold">Optional Add-ons:</small>
{% for addon in row.optional_addons %}
<div class="addon-item border-start border-info ps-2 mb-1">
<div class="d-flex justify-content-between">
<span class="addon-name">{{ addon.name }}</span>
<span class="addon-price">{{ addon.price|floatformat:2 }} {{ row.currency }}</span>
</div>
{% if addon.commercial_description %}
<div class="text-muted small">{{ addon.commercial_description }}</div>
{% elif addon.description %}
<div class="text-muted small">{{ addon.description }}</div>
{% endif %}
<div class="text-muted small">Type: {{ addon.addon_type }}</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% else %}
<span class="text-muted">No add-ons</span>
{% endif %}
</td>
{% endif %}
{% if show_discount_details %}
<td>
{% if row.has_discount %}
{{ row.discount_model }}
{% else %}
None
{% endif %}
</td>
<td>
{% if row.has_discount %}
<small class="text-muted">
<strong>Total Units:</strong> {{ row.total_units }}<br>
<strong>Standard Price:</strong> {{ row.standard_sla_price|floatformat:2 }}<br>
<strong>Discounted Price:</strong> {{ row.discounted_sla_price|floatformat:2 }}<br>
<strong>Savings:</strong> {{ row.discount_savings|floatformat:2 }} ({{ row.discount_percentage|floatformat:1 }}%)<br>
{% if row.discount_breakdown %}
<strong>Breakdown:</strong><br>
{% for tier in row.discount_breakdown %}
{{ tier.tier_range }} units: {{ tier.units }} × {{ tier.rate|floatformat:4 }} = {{ tier.subtotal|floatformat:2 }}<br>
{% endfor %}
{% endif %}
</small>
{% else %}
<small class="text-muted">No discount applied</small>
{% endif %}
</td>
{% endif %}
{% if show_price_comparison %}
<td>
<span class="badge">-</span>
</td>
{% endif %}
<td class="final-price-cell fw-bold">{{ row.final_price|floatformat:2 }}</td>
</tr>
{% if show_price_comparison and row.external_comparisons %}
{% for comparison in row.external_comparisons %}
<tr class="table-light comparison-row">
<td class="text-muted">{{ comparison.plan_name }}</td>
<td class="text-muted">{{ comparison.provider }}</td>
<td class="text-muted">
{% if comparison.vcpus %}{{ comparison.vcpus }}{% else %}-{% endif %}
</td>
<td class="text-muted">
{% if comparison.ram %}{{ comparison.ram }}{% else %}-{% endif %}
</td>
<td class="text-muted">{{ row.term }}</td>
<td class="text-muted">{{ comparison.currency }}</td>
<!-- Price breakdown columns for external comparisons -->
<td class="text-muted">-</td>
<td class="text-muted">-</td>
<td class="text-muted">-</td>
<td class="text-muted">-</td>
<td class="text-muted">-</td>
{% if show_addon_details %}
<td class="text-muted">-</td>
{% endif %}
{% if show_discount_details %}
<td class="text-muted">-</td>
<td class="text-muted">-</td>
{% endif %}
<td>
<small>
<span class="badge bg-secondary">{% if comparison.source %}<span class="text-muted"><a href="{{ comparison.source }}" target="_blank">{{ comparison.provider }}</a></span>{% else %}{{ comparison.provider }}{% endif %}</span><br>
{% if comparison.description %}
<span class="text-muted">{{ comparison.description }}</span><br>
{% endif %}
{% if comparison.storage %}
<span class="text-muted">Storage: {{ comparison.storage }} GB</span><br>
{% endif %}
{% if comparison.replicas %}
<span class="text-muted">Replicas: {{ comparison.replicas }}</span><br>
{% endif %}
{% if comparison.ratio %}
<span class="text-muted">Price ratio: {{ comparison.ratio|floatformat:2 }}x</span><br>
{% endif %}
</small>
</td>
<td class="fw-bold">
{{ comparison.amount|floatformat:2 }} {{ comparison.currency }}
{% if comparison.difference > 0 %}
<span class="badge bg-success ms-1">+{{ comparison.difference|floatformat:2 }}</span>
{% elif comparison.difference < 0 %}
<span class="badge bg-danger ms-1">{{ comparison.difference|floatformat:2 }}</span>
{% endif %}
</td>
</tr>
{% endfor %}
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
{# Price Chart #}
<div class="price-chart mt-3">
<h5 class="text-muted">Price Breakdown Chart - Units vs Price Components</h5>
<div style="height: 400px;">
<canvas id="chart-{{ group_name|slugify }}-{{ service_level|slugify }}" width="400" height="200"></canvas>
</div>
</div>
<p class="text-muted"><strong>{{ pricing_data|length }}</strong> variants for {{ service_level }} in {{ group_name }}</p>
{% else %}
<p class="text-muted">No pricing variants available for {{ service_level }} in {{ group_name }}.</p>
{% endif %}
</div>
{% empty %}
<p class="text-muted">No service levels with pricing data found for group: {{ group_name }}.</p>
{% endfor %}
</div>
{% endfor %}
{% else %}
<div class="alert alert-info">
<h4>No pricing data available</h4>
<p>{% if filter_cloud_provider or filter_service or filter_compute_plan_group or filter_service_level %}No data matches the selected filters. Try adjusting your filter criteria.{% else %}Please ensure you have active compute plans with prices and VSHNAppCat price configurations.{% endif %}</p>
</div>
{% endif %}
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Auto-submit form when filter dropdowns change
const filterForm = document.getElementById('filter-form');
const filterSelects = document.querySelectorAll('.filter-select');
const discountCheckbox = document.getElementById('discount_details');
const addonCheckbox = document.getElementById('addon_details');
// Add change event listeners to all filter dropdowns
filterSelects.forEach(function(select) {
select.addEventListener('change', function() {
filterForm.submit();
});
});
// Add change event listener to discount details checkbox
discountCheckbox.addEventListener('change', function() {
filterForm.submit();
});
// Add change event listener to addon details checkbox
addonCheckbox.addEventListener('change', function() {
filterForm.submit();
});
// Add change event listener to price comparison checkbox
const priceComparisonCheckbox = document.getElementById('price_comparison');
priceComparisonCheckbox.addEventListener('change', function() {
filterForm.submit();
}); // Chart data for each service level
{% for group_name, service_levels in pricing_data_by_group_and_service_level.items %}
{% for service_level, pricing_data in service_levels.items %}
{% if pricing_data %}
// Prepare data for {{ group_name }} - {{ service_level }}
const chartData{{ forloop.parentloop.counter }}{{ forloop.counter }} = {
labels: [{% for row in pricing_data %}{{ row.units }}{% if not forloop.last %}, {% endif %}{% endfor %}],
datasets: [
{
label: 'Final Price',
data: [{% for row in pricing_data %}{{ row.final_price|floatformat:2 }}{% if not forloop.last %}, {% endif %}{% endfor %}],
borderColor: 'rgb(75, 192, 192)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
tension: 0.1,
fill: false
},
{
label: 'Compute Plan Price',
data: [{% for row in pricing_data %}{{ row.compute_plan_price|floatformat:2 }}{% if not forloop.last %}, {% endif %}{% endfor %}],
borderColor: 'rgb(13, 110, 253)',
backgroundColor: 'rgba(13, 110, 253, 0.2)',
tension: 0.1,
fill: false
},
{
label: 'SLA Base',
data: [{% for row in pricing_data %}{{ row.sla_base|floatformat:2 }}{% if not forloop.last %}, {% endif %}{% endfor %}],
borderColor: 'rgb(111, 66, 193)',
backgroundColor: 'rgba(111, 66, 193, 0.2)',
tension: 0.1,
fill: false
},
{
label: 'Units × SLA Per Unit',
data: [{% for row in pricing_data %}{{ row.units|multiply:row.sla_per_unit|floatformat:2 }}{% if not forloop.last %}, {% endif %}{% endfor %}],
borderColor: 'rgb(253, 126, 20)',
backgroundColor: 'rgba(253, 126, 20, 0.2)',
tension: 0.1,
fill: false
},
{
label: 'Mandatory Add-ons',
data: [{% for row in pricing_data %}{{ row.mandatory_addons|calculate_addon_total:row.units|floatformat:2 }}{% if not forloop.last %}, {% endif %}{% endfor %}],
borderColor: 'rgb(220, 53, 69)',
backgroundColor: 'rgba(220, 53, 69, 0.2)',
tension: 0.1,
fill: false
},
{
label: 'Total SLA Price',
data: [{% for row in pricing_data %}{{ row.sla_price|floatformat:2 }}{% if not forloop.last %}, {% endif %}{% endfor %}],
borderColor: 'rgb(25, 135, 84)',
backgroundColor: 'rgba(25, 135, 84, 0.2)',
tension: 0.1,
fill: false
}
]
};
// Create chart for {{ group_name }} - {{ service_level }}
const ctx{{ forloop.parentloop.counter }}{{ forloop.counter }} = document.getElementById('chart-{{ group_name|slugify }}-{{ service_level|slugify }}').getContext('2d');
new Chart(ctx{{ forloop.parentloop.counter }}{{ forloop.counter }}, {
type: 'line',
data: chartData{{ forloop.parentloop.counter }}{{ forloop.counter }},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
title: {
display: true,
text: 'Units'
}
},
y: {
title: {
display: true,
text: 'Price ({{ pricing_data.0.currency|default:"CHF" }})'
},
beginAtZero: true
}
},
plugins: {
title: {
display: true,
text: '{{ group_name }} - {{ service_level }} Price Breakdown'
},
legend: {
display: true
}
},
elements: {
point: {
radius: 4,
hoverRadius: 6
}
}
}
});
{% endif %}
{% endfor %}
{% endfor %}
});
</script>
{% endblock %}