grafische kostenübersicht added
This commit is contained in:
@@ -312,6 +312,63 @@ color:#555;
|
|||||||
margin-bottom:10px;
|
margin-bottom:10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#costChart{
|
||||||
|
margin-top:18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cost-bar{
|
||||||
|
display:flex;
|
||||||
|
height:18px;
|
||||||
|
overflow:hidden;
|
||||||
|
border-radius:999px;
|
||||||
|
background:#e2e8f0;
|
||||||
|
border:1px solid #cbd5e1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cost-segment{
|
||||||
|
min-width:3px;
|
||||||
|
background:hsl(calc(205 + (var(--segment-index) * 31)), 72%, 48%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cost-segment.domains{
|
||||||
|
background:#0f766e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cost-breakdown{
|
||||||
|
margin-top:12px;
|
||||||
|
display:grid;
|
||||||
|
gap:6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cost-row{
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns:16px minmax(160px, 1fr) auto auto;
|
||||||
|
align-items:center;
|
||||||
|
gap:8px;
|
||||||
|
font-size:13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cost-dot{
|
||||||
|
width:10px;
|
||||||
|
height:10px;
|
||||||
|
border-radius:999px;
|
||||||
|
background:hsl(calc(205 + (var(--segment-index) * 31)), 72%, 48%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cost-dot.domains{
|
||||||
|
background:#0f766e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cost-label{
|
||||||
|
font-weight:600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cost-value,
|
||||||
|
.cost-share,
|
||||||
|
.cost-empty{
|
||||||
|
color:#64748b;
|
||||||
|
}
|
||||||
|
|
||||||
.tabs{
|
.tabs{
|
||||||
display:flex;
|
display:flex;
|
||||||
flex-wrap:wrap;
|
flex-wrap:wrap;
|
||||||
@@ -532,3 +589,14 @@ body.dark .infra-ip{
|
|||||||
body.dark .system{
|
body.dark .system{
|
||||||
color:#cbd5e1;
|
color:#cbd5e1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.dark .cost-bar{
|
||||||
|
background:#334155;
|
||||||
|
border-color:#475569;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.dark .cost-value,
|
||||||
|
body.dark .cost-share,
|
||||||
|
body.dark .cost-empty{
|
||||||
|
color:#94a3b8;
|
||||||
|
}
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ Domains jährlich: <span id="costDomain">0</span> €<br>
|
|||||||
|
|
||||||
<b>Gesamt jährlich: <span id="costTotal">0</span> €</b>
|
<b>Gesamt jährlich: <span id="costTotal">0</span> €</b>
|
||||||
|
|
||||||
|
<div id="costChart"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -7,14 +7,25 @@ window.loadCosts = async function(){
|
|||||||
let year = 0
|
let year = 0
|
||||||
let domainYear = 0
|
let domainYear = 0
|
||||||
|
|
||||||
|
const parts = []
|
||||||
|
let resourceYearTotal = 0
|
||||||
|
|
||||||
resources.forEach(r => {
|
resources.forEach(r => {
|
||||||
|
|
||||||
if(r.kosten_monat){
|
if(r.kosten_monat){
|
||||||
month += parseFloat(r.kosten_monat)
|
month += parseFloat(r.kosten_monat)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(r.kosten_jahr){
|
const resourceYear = getResourceYearCost(r)
|
||||||
year += parseFloat(r.kosten_jahr)
|
resourceYearTotal += resourceYear
|
||||||
|
|
||||||
|
if(resourceYear > 0){
|
||||||
|
parts.push({
|
||||||
|
label: r.name || "Unbenannter Server",
|
||||||
|
amount: resourceYear,
|
||||||
|
type: "server",
|
||||||
|
colorIndex: parts.length
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
@@ -28,11 +39,68 @@ window.loadCosts = async function(){
|
|||||||
})
|
})
|
||||||
|
|
||||||
document.getElementById("costMonth").innerText = month.toFixed(2)
|
document.getElementById("costMonth").innerText = month.toFixed(2)
|
||||||
document.getElementById("costYear").innerText = year.toFixed(2)
|
document.getElementById("costYear").innerText = resourceYearTotal.toFixed(2)
|
||||||
document.getElementById("costDomain").innerText = domainYear.toFixed(2)
|
document.getElementById("costDomain").innerText = domainYear.toFixed(2)
|
||||||
|
|
||||||
const total = year + domainYear
|
const total = resourceYearTotal + domainYear
|
||||||
|
|
||||||
document.getElementById("costTotal").innerText = total.toFixed(2)
|
document.getElementById("costTotal").innerText = total.toFixed(2)
|
||||||
|
renderCostChart(parts, domainYear, total)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getResourceYearCost(resource){
|
||||||
|
if(resource.kosten_jahr){
|
||||||
|
return parseFloat(resource.kosten_jahr) || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if(resource.kosten_monat){
|
||||||
|
return (parseFloat(resource.kosten_monat) || 0) * 12
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderCostChart(serverParts, domainYear, total){
|
||||||
|
const container = document.getElementById("costChart")
|
||||||
|
if(!container) return
|
||||||
|
|
||||||
|
const parts = [...serverParts]
|
||||||
|
|
||||||
|
if(domainYear > 0){
|
||||||
|
parts.push({
|
||||||
|
label: "Domains gesamt",
|
||||||
|
amount: domainYear,
|
||||||
|
type: "domains",
|
||||||
|
colorIndex: parts.length
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!total || !parts.length){
|
||||||
|
container.innerHTML = `<div class="cost-empty">Keine Kosten erfasst</div>`
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = `
|
||||||
|
<div class="cost-bar">
|
||||||
|
${parts.map((part, index) => `
|
||||||
|
<div class="cost-segment ${part.type}"
|
||||||
|
style="width:${((part.amount / total) * 100).toFixed(2)}%; --segment-index:${part.colorIndex}"
|
||||||
|
title="${part.label}: ${part.amount.toFixed(2)} EUR">
|
||||||
|
</div>
|
||||||
|
`).join("")}
|
||||||
|
</div>
|
||||||
|
<div class="cost-breakdown">
|
||||||
|
${parts
|
||||||
|
.sort((a, b) => b.amount - a.amount)
|
||||||
|
.map((part, index) => `
|
||||||
|
<div class="cost-row">
|
||||||
|
<span class="cost-dot ${part.type}" style="--segment-index:${part.colorIndex}"></span>
|
||||||
|
<span class="cost-label">${part.label}</span>
|
||||||
|
<span class="cost-value">${part.amount.toFixed(2)} EUR</span>
|
||||||
|
<span class="cost-share">${((part.amount / total) * 100).toFixed(1)}%</span>
|
||||||
|
</div>
|
||||||
|
`).join("")}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user