improvements to help tooltips and local serving

This commit is contained in:
Tobias Brunner 2025-07-16 16:24:48 +02:00
parent ae130ff776
commit 1b07794fc9
Signed by: tobru
SSH key fingerprint: SHA256:kOXg1R6c11XW3/Pt9dbLdQvOJGFAy+B2K6v6PtRWBGQ
5 changed files with 493 additions and 30 deletions

4
.gitignore vendored
View file

@ -14,5 +14,5 @@ wheels/
*.sqlite3
media/
deployment/secret.yaml
*.json
static/
/*.json
/static/

File diff suppressed because one or more lines are too long

398
hub/services/static/js/jspdf.umd.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -24,7 +24,7 @@
{% endif %}
<script defer src="{% static "js/htmx204.min.js" %}"></script>
<script defer src="{% static "js/alpine-collapse.min.js" %}"></script>
<script defer src="{% static "js/servala-main.js" %}"></script>
<script defer src="{% static "js/bootstrap.bundle.min.js" %}"></script>
<script defer src="{% static "js/servala-addons.js" %}"></script>
{% block extra_js %}{% endblock %}
</head>

View file

@ -15,6 +15,7 @@
.input-group-custom {
margin-bottom: 1rem;
position: relative; /* Ensure proper stacking context */
}
.input-group-custom label {
@ -23,6 +24,12 @@
display: block;
}
/* Ensure input groups don't interfere with tooltips */
.input-group {
position: relative;
z-index: 1;
}
.slider-container {
position: relative;
margin: 10px 0;
@ -184,12 +191,50 @@
margin-bottom: 0;
}
/* Tooltip styling for better native tooltip appearance */
[data-bs-toggle="tooltip"] {
position: relative;
/* Bootstrap tooltip styling improvements */
.tooltip {
font-size: 0.875rem;
font-family: inherit;
z-index: 9999 !important; /* Ensure tooltips appear above all other elements */
pointer-events: none; /* Prevent tooltip from interfering with mouse events */
}
.tooltip .tooltip-inner {
max-width: 250px;
padding: 0.5rem 0.75rem;
color: #fff;
background-color: #212529;
border-radius: 6px;
box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.15);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.tooltip .tooltip-arrow {
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.15));
}
.tooltip.bs-tooltip-top .tooltip-arrow::before {
border-top-color: #212529;
}
.tooltip.bs-tooltip-bottom .tooltip-arrow::before {
border-bottom-color: #212529;
}
.tooltip.bs-tooltip-start .tooltip-arrow::before {
border-left-color: #212529;
}
.tooltip.bs-tooltip-end .tooltip-arrow::before {
border-right-color: #212529;
}
/* Enhanced cursor for tooltip elements */
[data-bs-toggle="tooltip"] {
cursor: help;
position: relative;
}
[data-bs-toggle="tooltip"]:hover {
opacity: 0.8;
}
@ -336,7 +381,14 @@
</div>
<div class="input-group-custom">
<label for="discount-rate">Discount Rate for NPV (%)</label>
<label for="discount-rate">
Discount Rate for NPV (%)
<i class="bi bi-question-circle-fill text-muted ms-1"
data-bs-toggle="tooltip"
data-bs-placement="top"
title="The discount rate represents your required rate of return or cost of capital. It's used to calculate the present value of future cash flows. A higher rate means you need higher returns to justify the investment. For example: 8-10% for conservative investors, 12-15% for moderate risk tolerance, 15-20% for high-risk/high-return expectations. This rate accounts for inflation, risk, and opportunity cost of your capital."
style="cursor: help; font-size: 0.8rem;"></i>
</label>
<input type="number" class="form-control" id="discount-rate"
min="5" max="20" step="0.5" value="10"
onchange="updateCalculations()">
@ -345,7 +397,7 @@
min="5" max="20" step="0.5" value="10"
onchange="updateDiscountRate(this.value)">
</div>
<small class="text-muted">5% - 20%</small>
<small class="text-muted">5% - 20% (your required annual return rate)</small>
</div>
</div>
@ -759,8 +811,8 @@
{% endblock %}
{% block extra_js %}
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="{% static "js/chart.js" %}"></script>
<script src="{% static "js/jspdf.umd.min.js" %}"></script>
<script>
// ROI Calculator JavaScript Implementation
class ROICalculator {
@ -1205,32 +1257,38 @@ document.addEventListener('DOMContentLoaded', function() {
checkExportLibraries();
});
// Initialize tooltips with fallback for missing Bootstrap
// Initialize tooltips with Bootstrap (loaded directly via CDN)
function initializeTooltips() {
// Check if Bootstrap is available
// Wait for Bootstrap to be available
if (typeof bootstrap !== 'undefined' && bootstrap.Tooltip) {
// Use Bootstrap tooltips
try {
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
} catch (error) {
console.warn('Failed to initialize Bootstrap tooltips:', error);
initializeNativeTooltips();
}
} else {
// Fallback: Use native title attribute tooltips
console.log('Bootstrap not available, using native tooltips');
// Retry after a short delay for deferred scripts
setTimeout(initializeTooltips, 100);
}
}
function initializeNativeTooltips() {
var tooltipElements = document.querySelectorAll('[data-bs-toggle="tooltip"]');
tooltipElements.forEach(function(element) {
// Move the tooltip content to the native title attribute
if (element.getAttribute('title')) {
// Title already set, no need to change
return;
}
var tooltipContent = element.getAttribute('data-bs-original-title') ||
element.getAttribute('title');
// Ensure the title attribute is set for native tooltips
var tooltipContent = element.getAttribute('title');
if (!tooltipContent) {
// Get tooltip content from data-bs-original-title or title attribute
tooltipContent = element.getAttribute('data-bs-original-title');
if (tooltipContent) {
element.setAttribute('title', tooltipContent);
}
});
}
});
}
// Check if export libraries are loaded