143 lines
5.2 KiB
JavaScript
143 lines
5.2 KiB
JavaScript
/**
|
|
* 3D-Druck Kostenkalkulation — Frontend-Logik.
|
|
*
|
|
* - Live-Berechnung bei jeder Eingabe (via POST /calculate)
|
|
* - Automatisches Speichern in YAML (debounced 500ms via POST /save_config)
|
|
* - Dark-Mode-Umschaltung (persistiert in YAML)
|
|
* - Export als TXT (POST /export -> Blob-Download)
|
|
*/
|
|
|
|
let saveTimeout = null;
|
|
|
|
/** Sammelt alle Formularwerte anhand ihrer data-var Attribute. */
|
|
function getFormData() {
|
|
const data = {};
|
|
document.querySelectorAll('[data-var]').forEach(el => {
|
|
const key = el.dataset.var;
|
|
const type = el.dataset.type || 'text';
|
|
let val = el.value;
|
|
if (type === 'number') val = parseFloat(val) || 0;
|
|
data[key] = val;
|
|
});
|
|
data.dark_mode = document.documentElement.getAttribute('data-bs-theme') === 'dark';
|
|
return data;
|
|
}
|
|
|
|
/** Sendet Formulardaten an /calculate und aktualisiert die Ergebnisanzeige. */
|
|
async function recalculate() {
|
|
const data = getFormData();
|
|
try {
|
|
const res = await fetch('/calculate', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data),
|
|
});
|
|
const calc = await res.json();
|
|
renderResult(calc);
|
|
} catch (e) {
|
|
console.error('Calculate error', e);
|
|
}
|
|
}
|
|
|
|
/** Baut die textuelle Ergebnisanzeige aus den JSON-Berechnungswerten. */
|
|
function renderResult(calc) {
|
|
const lines = [
|
|
`Materialkosten: ${String(calc.materialkosten.toFixed(2)).padStart(10)} EUR`,
|
|
`Stromkosten: ${String(calc.stromkosten.toFixed(2)).padStart(10)} EUR`,
|
|
`Abschreibung Drucker: ${String(calc.abschreibung.toFixed(2)).padStart(10)} EUR`,
|
|
`Arbeitskosten: ${String(calc.arbeitskosten.toFixed(2)).padStart(10)} EUR`,
|
|
`${'\u2500'.repeat(40)}`,
|
|
`Gesamtkosten: ${String(calc.gesamtkosten_netto.toFixed(2)).padStart(10)} EUR`,
|
|
'',
|
|
`Gewinn (${((document.querySelector('[data-var="gewinnmarge"]')?.value) || 20)}%): ${String(calc.gewinn.toFixed(2)).padStart(10)} EUR`,
|
|
`${'\u2500'.repeat(40)}`,
|
|
`Verkaufspreis (netto): ${String(calc.verkaufspreis.toFixed(2)).padStart(10)} EUR`,
|
|
`MwSt. (19%): ${String(calc.mwst.toFixed(2)).padStart(10)} EUR`,
|
|
`${'\u2550'.repeat(40)}`,
|
|
`GESAMT (brutto): ${String(calc.gesamt_brutto.toFixed(2)).padStart(10)} EUR`,
|
|
];
|
|
document.getElementById('result-text').textContent = lines.join('\n');
|
|
|
|
const details = [
|
|
`Druckzeit: ${calc.druckzeit_stunden} Stunden`,
|
|
`Materialgewicht: ${calc.material_kg} kg`,
|
|
`Abschreibung pro Stunde: ${calc.abschreibung_pro_stunde} EUR`,
|
|
`Kosten pro Gramm: ${calc.kosten_pro_gramm} EUR`,
|
|
`Stromkosten pro Stunde: ${calc.strom_pro_stunde} EUR`,
|
|
];
|
|
document.getElementById('detail-text').textContent = details.join('\n');
|
|
}
|
|
|
|
/** Plant ein Auto-Save in die YAML-Konfiguration (debounced 500ms). */
|
|
function scheduleSave() {
|
|
clearTimeout(saveTimeout);
|
|
saveTimeout = setTimeout(async () => {
|
|
const data = getFormData();
|
|
try {
|
|
await fetch('/save_config', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data),
|
|
});
|
|
} catch (e) {
|
|
console.error('Save error', e);
|
|
}
|
|
}, 500);
|
|
}
|
|
|
|
/** Wird bei jeder Eingabe aufgerufen: speichert + berechnet neu. */
|
|
async function onInputChange() {
|
|
scheduleSave();
|
|
await recalculate();
|
|
}
|
|
|
|
/** Schaltet zwischen Light- und Dark-Mode um und persistiert die Einstellung. */
|
|
function toggleDarkMode() {
|
|
const html = document.documentElement;
|
|
const isDark = html.getAttribute('data-bs-theme') === 'dark';
|
|
const newTheme = isDark ? 'light' : 'dark';
|
|
html.setAttribute('data-bs-theme', newTheme);
|
|
|
|
const btn = document.getElementById('dark-mode-btn');
|
|
const icon = btn.querySelector('i');
|
|
icon.className = `ti ti-${isDark ? 'sun' : 'moon'} me-1`;
|
|
btn.childNodes[2].textContent = isDark ? 'Light' : 'Dark';
|
|
|
|
scheduleSave();
|
|
}
|
|
|
|
/** Sendet die aktuellen Werte an /export und lost einen TXT-Download aus. */
|
|
async function exportData() {
|
|
const data = getFormData();
|
|
try {
|
|
const res = await fetch('/export', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify(data),
|
|
});
|
|
const blob = await res.blob();
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = 'kalkulation.txt';
|
|
a.click();
|
|
URL.revokeObjectURL(url);
|
|
showNotification('Export erfolgreich!');
|
|
} catch (e) {
|
|
console.error('Export error', e);
|
|
}
|
|
}
|
|
|
|
/** Zeigt eine kurze Toast-Notification oben rechts an. */
|
|
function showNotification(msg) {
|
|
const container = document.getElementById('notification');
|
|
document.getElementById('notification-msg').textContent = msg;
|
|
container.style.display = 'block';
|
|
setTimeout(() => { container.style.display = 'none'; }, 2500);
|
|
}
|
|
|
|
/** Initiale Berechnung beim Seitenladen. */
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
recalculate();
|
|
});
|