Grotti 3.2.0: Jetzt-Liste, Filament-Pause, Strom-Torte, UI-Polish.
Jetzt/Topbar und Material-Routing, Pause bei Filament-Ende, Druckbericht mit Stromanteilen, Design- und Kamera-Stream-Polish; Bedarf-Alert-Spam und Platte-belegt-Bugs behoben.
This commit is contained in:
1 parent
6e59df22e5
commit
4ddffbbeb1
68 files changed
+4348
-1215
No files matched your search
@@ -29,6 +29,72 @@ function sourceLabel(source, estimate) {
|
||||
return source || "";
|
||||
}
|
||||
|
||||
const ENERGY_PART_COLORS = {
|
||||
base: "#94a3b8",
|
||||
nozzle: "#ea580c",
|
||||
bed: "#2563eb",
|
||||
dryer: "#16a34a",
|
||||
};
|
||||
|
||||
function kwhLabel(value) {
|
||||
return (Number(value) || 0).toLocaleString("de-DE", { maximumFractionDigits: 3 }) + " kWh";
|
||||
}
|
||||
|
||||
function energyPieSvg(parts) {
|
||||
const items = (parts || []).filter((p) => Number(p.kwh) > 0);
|
||||
const total = items.reduce((sum, p) => sum + Number(p.kwh || 0), 0);
|
||||
if (!items.length || total <= 0) return "";
|
||||
const cx = 42;
|
||||
const cy = 42;
|
||||
const r = 36;
|
||||
let angle = -Math.PI / 2;
|
||||
const slices = items.map((p) => {
|
||||
const share = Number(p.kwh) / total;
|
||||
const sweep = share * Math.PI * 2;
|
||||
const x1 = cx + r * Math.cos(angle);
|
||||
const y1 = cy + r * Math.sin(angle);
|
||||
angle += sweep;
|
||||
const x2 = cx + r * Math.cos(angle);
|
||||
const y2 = cy + r * Math.sin(angle);
|
||||
const large = sweep > Math.PI ? 1 : 0;
|
||||
const color = ENERGY_PART_COLORS[p.key] || "#64748b";
|
||||
if (share >= 0.999) {
|
||||
return `<circle cx="${cx}" cy="${cy}" r="${r}" fill="${color}"/>`;
|
||||
}
|
||||
return `<path d="M ${cx} ${cy} L ${x1.toFixed(2)} ${y1.toFixed(2)} A ${r} ${r} 0 ${large} 1 ${x2.toFixed(2)} ${y2.toFixed(2)} Z" fill="${color}"/>`;
|
||||
}).join("");
|
||||
return `<svg class="energy-pie" viewBox="0 0 84 84" role="img" aria-label="Stromanteile">${slices}</svg>`;
|
||||
}
|
||||
|
||||
function energyBreakdownHtml(energy) {
|
||||
const parts = energy.parts || [];
|
||||
if (!parts.length) return "";
|
||||
const src = energy.source === "ed" ? "aus ED" : "pauschal";
|
||||
const rate = energy.euro_per_kwh != null
|
||||
? Number(energy.euro_per_kwh).toLocaleString("de-DE", { style: "currency", currency: "EUR", maximumFractionDigits: 3 }) + "/kWh"
|
||||
: "";
|
||||
const rows = parts.map((p) => {
|
||||
const color = ENERGY_PART_COLORS[p.key] || "#64748b";
|
||||
return `<tr>
|
||||
<td><span class="energy-swatch" style="background:${color}"></span>${escapeHtml(p.label)}</td>
|
||||
<td class="num">${kwhLabel(p.kwh)}</td>
|
||||
<td class="num">${euro(p.cost)}</td>
|
||||
</tr>`;
|
||||
}).join("");
|
||||
return `<div class="energy-break">
|
||||
<div class="energy-break-chart">${energyPieSvg(parts)}</div>
|
||||
<div class="energy-break-table">
|
||||
<div class="lib-head"><h3>Strom</h3><span class="tiny">${escapeHtml(src)}${rate ? " · " + rate : ""}</span></div>
|
||||
<div class="table-wrap"><table>
|
||||
<thead><tr><th>Anteil</th><th class="num">kWh</th><th class="num">€</th></tr></thead>
|
||||
<tbody>${rows}
|
||||
<tr class="energy-total"><td>Summe</td><td class="num">${kwhLabel(energy.kwh)}</td><td class="num">${euro(energy.cost)}</td></tr>
|
||||
</tbody>
|
||||
</table></div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function render(data) {
|
||||
const run = data.run;
|
||||
const name = (run.filename || "").split("/").pop() || "ohne Datei";
|
||||
@@ -80,10 +146,12 @@ function render(data) {
|
||||
<p>Dauer <b>${hours(run.duration_s)}</b>${run.estimated_s ? " · Soll " + hours(run.estimated_s) : ""}</p>
|
||||
<p>Material <b>${grams(used)}</b> von ${grams(planned)}${planned ? " (" + (diff > 0 ? "+" : "") + diff + " g)" : ""}
|
||||
${run.weight_source ? ' · <span class="tag">' + sourceLabel(run.weight_source) + "</span>" : ""}</p>
|
||||
<p>Kosten <b>${euro(cost)}</b> (Filament ${euro(run.filament_cost)} · Strom ${euro(energyEuro)}${energyHint ? " · " + energyHint : ""})</p>
|
||||
<p>Kosten <b>${euro(cost)}</b> (Filament ${euro(run.filament_cost)} · Strom ${euro(energyEuro)}${energyHint ? " · " + energyHint : ""})${run.cost_per_part != null ? " · <b>" + euro(run.cost_per_part) + "</b>/Teil" : ""}</p>
|
||||
${run.pieces_ok || run.pieces_bad ? `<p>Stück <b>${run.pieces_ok || 0}</b> gut${run.pieces_bad ? " · " + run.pieces_bad + " schlecht" : ""}</p>` : ""}
|
||||
${run.message ? "<p class=\"msg\">" + escapeHtml(run.message) + "</p>" : ""}
|
||||
${data.project ? "<p class=\"tiny\">Projekt: " + escapeHtml(data.project.name) + "</p>" : ""}
|
||||
</div>
|
||||
${energyBreakdownHtml(energy)}
|
||||
</div>
|
||||
<div class="card">
|
||||
<h2>Spulen</h2>
|
||||
|
||||
Reference in new issue
Block a user