implement plan pricing
Some checks failed
Django Tests / test (push) Failing after 1m3s
Django Tests / test (pull_request) Failing after 1m3s

This commit is contained in:
Tobias Brunner 2025-06-20 17:40:38 +02:00
parent 96b667dd75
commit 61cabd1b1e
No known key found for this signature in database
7 changed files with 192 additions and 1257 deletions

View file

@ -204,199 +204,28 @@
<!-- Price Calculator -->
<div class="pt-24" id="plans" style="scroll-margin-top: 30px;">
{% if offering.msp == "VS" and price_calculator_enabled and pricing_data_by_group_and_service_level %}
<!-- Interactive Price Calculator -->
<h3 class="fs-24 fw-semibold lh-1 mb-12">Choose your Plan</h3>
<div class="bg-light rounded-4 p-4 mb-4">
<div class="row">
<!-- Calculator Controls -->
<div class="col-12 col-lg-6">
<div class="card h-100">
<div class="card-body">
<!-- CPU Slider -->
<div class="mb-4">
<label for="cpuRange" class="form-label d-flex justify-content-between">
<span>vCPUs</span>
<span class="fw-bold" id="cpuValue">2</span>
</label>
<input type="range" class="form-range" id="cpuRange" min="1" max="32" value="2" step="1">
<div class="d-flex justify-content-between text-muted small">
<span id="cpuMinDisplay">1</span>
<span id="cpuMaxDisplay">32</span>
</div>
</div>
<!-- Memory Slider -->
<div class="mb-4">
<label for="memoryRange" class="form-label d-flex justify-content-between">
<span>Memory (GB)</span>
<span class="fw-bold" id="memoryValue">4</span>
</label>
<input type="range" class="form-range" id="memoryRange" min="1" max="128" value="4" step="1">
<div class="d-flex justify-content-between text-muted small">
<span id="memoryMinDisplay">1 GB</span>
<span id="memoryMaxDisplay">128 GB</span>
</div>
</div>
<!-- Storage Slider -->
<div class="mb-4">
<label for="storageRange" class="form-label d-flex justify-content-between">
<span>Storage (GB)</span>
<span class="fw-bold" id="storageValue">20</span>
</label>
<input type="range" class="form-range" id="storageRange" min="10" max="1000" value="20" step="10">
<div class="d-flex justify-content-between text-muted small">
<span id="storageMinDisplay">10 GB</span>
<span id="storageMaxDisplay">1000 GB</span>
</div>
</div>
<!-- Instances Slider -->
<div class="mb-4">
<label for="instancesRange" class="form-label d-flex justify-content-between">
<span>Instances</span>
<span class="fw-bold" id="instancesValue">1</span>
</label>
<input type="range" class="form-range" id="instancesRange" min="1" max="1" value="1" step="1">
<div class="d-flex justify-content-between text-muted small">
<span id="instancesMinDisplay">1</span>
<span id="instancesMaxDisplay">1</span>
</div>
</div>
<!-- Service Level Selection -->
<div class="mb-4">
<label class="form-label">Service Level</label>
<div class="btn-group w-100" role="group" id="serviceLevelGroup">
<input type="radio" class="btn-check" name="serviceLevel" id="serviceLevelBestEffort" value="Best Effort" checked>
<label class="btn btn-outline-primary" for="serviceLevelBestEffort">Best Effort</label>
<input type="radio" class="btn-check" name="serviceLevel" id="serviceLevelGuaranteed" value="Guaranteed Availability">
<label class="btn btn-outline-primary" for="serviceLevelGuaranteed">Guaranteed Availability</label>
</div>
</div>
<!-- Addons Section - Hidden by default, shown by JS if addons exist -->
<div class="mb-4" id="addonsSection" style="display: none;">
<label class="form-label">Add-ons (Optional)</label>
<div id="addonsContainer">
<!-- Add-ons will be dynamically populated here -->
</div>
</div>
<!-- Direct Plan Selection -->
<div class="mb-4">
<label for="planSelect" class="form-label">Or choose a specific plan</label>
<select class="form-select" id="planSelect">
<option value="">Auto-select best matching plan</option>
</select>
<p><small class="form-text text-muted">Selecting a plan will override the slider configuration</small></p>
<p><small class="form-text text-muted"><i class="bi bi-info-circle me-1"></i> Interested in a custom plan? Let us know via the <a href="#form">contact form</a>.</small></p>
</div>
</div>
</div>
</div>
<!-- Results Panel -->
<div class="col-12 col-lg-6">
<div class="card h-100 border-primary">
<div class="card-body">
<h5 class="card-title text-primary mb-4">Your Plan</h5>
<!-- Plan Match Status -->
<div id="planMatchStatus" class="alert alert-info mb-3">
<i class="bi bi-info-circle me-2"></i>
<span>Finding best matching plan...</span>
</div>
<!-- Selected Plan Details -->
<div id="selectedPlanDetails" style="display: none;">
<div class="mb-3">
<div class="d-flex align-items-center mb-2">
<span class="badge me-2" id="planGroup"></span>
<strong id="planName"></strong>
</div>
<small class="text-muted" id="planDescription"></small>
</div>
<div class="row mb-3">
<div class="col-3">
<small class="text-muted">vCPUs</small>
<div class="fw-bold" id="planCpus"></div>
</div>
<div class="col-3">
<small class="text-muted">Memory</small>
<div class="fw-bold" id="planMemory"></div>
</div>
<div class="col-3">
<small class="text-muted">Instances</small>
<div class="fw-bold" id="planInstances"></div>
</div>
</div>
<div class="row mb-3">
<div class="col-12">
<small class="text-muted">Service Level</small>
<div class="fw-bold">
<a href="https://products.vshn.ch/service_levels.html" target="_blank" class="text-decoration-none" id="planServiceLevel"></a>
</div>
</div>
</div>
<!-- Pricing Breakdown -->
<div class="border-top pt-3">
<div class="d-flex justify-content-between mb-2">
<span>Managed Service (incl. Compute)</span>
<span class="fw-bold">CHF <span id="managedServicePrice">0.00</span></span>
</div>
<div class="d-flex justify-content-between mb-2">
<span>Storage - <span id="storageAmount">20</span> GB</span>
<span class="fw-bold">CHF <span id="storagePrice">0.00</span></span>
</div>
<!-- Addons Pricing -->
<div id="addonPricingContainer">
<!-- Addon pricing will be dynamically added here -->
</div>
<hr>
<div class="d-flex justify-content-between">
<span class="fs-5 fw-bold">Total Monthly Price</span>
<span class="fs-4 fw-bold text-primary">CHF <span id="totalPrice">0.00</span></span>
</div>
<small class="text-muted mt-2 d-block">
<i class="bi bi-info-circle me-1"></i>
Monthly pricing based on 30 days (720 hours). Billing is conducted per hour.
</small>
</div>
</div>
<!-- No Match Found -->
<div id="noMatchFound" style="display: none;" class="alert alert-warning">
<i class="bi bi-exclamation-triangle me-2"></i>
No matching plan found for your requirements. Please adjust your configuration.
</div>
</div>
</div>
</div>
</div>
<h3 class="fs-24 fw-semibold lh-1 mb-12">Available Plans & Pricing</h3>
<div class="mb-3">
<label for="currencySelect" class="form-label">Select Currency:</label>
<select id="currencySelect" class="form-select w-auto d-inline-block">
<option value="CHF">CHF</option>
<option value="EUR">EUR</option>
<option value="USD">USD</option>
</select>
</div>
<!-- Order Button -->
<div class="text-center mt-4">
<a href="#order-form" class="btn btn-primary btn-lg px-5 py-3 fw-semibold">
<i class="bi bi-cart me-2"></i>Order This Configuration
</a>
</div>
<!-- Order Form Section -->
<div id="order-form" class="pt-40" style="scroll-margin-top: 30px;">
<h4 class="fs-22 fw-semibold lh-1 mb-12">Order Your Configuration</h4>
<div class="row">
<div class="col-12">
{% embedded_contact_form source="Configuration Order" service=offering.service offering_id=offering.id %}
</div>
</div>
<div class="table-responsive">
<table class="table table-bordered" id="plansTable">
<thead>
<tr>
<th>Plan Name</th>
<th>Description</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<!-- Plan rows will be populated by JS -->
</tbody>
</table>
</div>
{% elif offering.plans.all %}
<!-- Traditional Plans -->