fix(dashboard): Kachel Artikel-Uebersetzung erneut entfernt

Der Uebertrag-Commit 9bf3adb hat die parallel gelandete Entfernung
16a9b9c versehentlich rueckgaengig gemacht (Komplett-Datei-Upload aus
einem aelteren Arbeitsstand). Diese Wiederholung stellt den gewollten
Zustand wieder her, Karte + JS-Block sind wieder raus.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Dieser Commit ist enthalten in:
claude-dev
2026-07-25 21:53:37 +00:00
Ursprung 9bf3adbacb
Commit 485638ab3b
2 geänderte Dateien mit 0 neuen und 174 gelöschten Zeilen

Datei anzeigen

@@ -76,32 +76,6 @@
</ul>
</div>
</div>
<!-- Artikel-Übersetzung -->
<div class="card" id="translationCard" style="margin-top:16px;">
<div class="card-header">
<h2>Artikel-Übersetzung</h2>
</div>
<div class="card-body">
<p class="text-muted" style="margin-top:0;">
Die automatische Übersetzung im Monitor ist deaktiviert. Hier lassen sich
fremdsprachige Artikel ohne deutsche Fassung manuell übersetzen.
</p>
<p id="translationInfo" style="margin:12px 0;">Status wird geladen…</p>
<div id="translationProgressWrap" style="display:none; margin:12px 0;">
<div style="background:rgba(128,128,128,0.25); border-radius:6px; height:14px; overflow:hidden;">
<div id="translationProgressBar" style="background:#1565c0; height:100%; width:0%; transition:width .3s;"></div>
</div>
<p class="text-muted" id="translationProgressText" style="margin:6px 0 0;"></p>
</div>
<div style="margin-top:12px; display:flex; gap:8px;">
<button class="btn btn-primary" id="translationRunBtn">Übersetzung starten</button>
<button class="btn btn-danger" id="translationCancelBtn" style="display:none;">Abbrechen</button>
</div>
</div>
</div>
</div>
<!-- Organizations Section -->

Datei anzeigen

@@ -130,11 +130,9 @@ document.addEventListener("DOMContentLoaded", () => {
setupNavTabs();
setupOrgDetailTabs();
setupForms();
setupTranslation();
loadDashboard();
loadTariffInfo();
loadDashboardTokenStats();
loadTranslationStatus();
loadOrgs();
});
@@ -154,7 +152,6 @@ function setupNavTabs() {
document.querySelectorAll(".app-content > .section").forEach(s => s.classList.remove("active"));
document.getElementById(`sec-${section}`).classList.add("active");
if (section === "dashboard") loadTranslationStatus();
if (section === "audit" && typeof loadAudit === "function") loadAudit();
});
});
@@ -912,151 +909,6 @@ function formatDate(iso) {
}
// ===== Artikel-Übersetzung =====
let translationPollTimer = null;
function setupTranslation() {
const runBtn = document.getElementById("translationRunBtn");
const cancelBtn = document.getElementById("translationCancelBtn");
if (runBtn) runBtn.addEventListener("click", startTranslation);
if (cancelBtn) cancelBtn.addEventListener("click", cancelTranslation);
}
function formatDuration(seconds) {
seconds = Math.max(0, Math.round(seconds || 0));
if (seconds < 60) return seconds + " Sek.";
const min = Math.round(seconds / 60);
if (min < 60) return min + " Min.";
const h = Math.floor(min / 60), m = min % 60;
return h + " Std. " + (m ? m + " Min." : "").trim();
}
function renderTranslation(st) {
const info = document.getElementById("translationInfo");
const wrap = document.getElementById("translationProgressWrap");
const bar = document.getElementById("translationProgressBar");
const ptext = document.getElementById("translationProgressText");
const runBtn = document.getElementById("translationRunBtn");
const cancelBtn = document.getElementById("translationCancelBtn");
if (!info || !runBtn) return;
if (st.running) {
runBtn.style.display = "none";
cancelBtn.style.display = "";
wrap.style.display = "";
const pct = st.total > 0 ? Math.round((st.done / st.total) * 100) : 0;
bar.style.width = pct + "%";
ptext.textContent = `${st.done} / ${st.total} verarbeitet, ${st.translated} übersetzt (${pct}%)`;
info.textContent = "Übersetzung läuft…";
return;
}
runBtn.style.display = "";
cancelBtn.style.display = "none";
wrap.style.display = "none";
let resultLine = "";
if (st.finished_at && (st.total > 0 || st.error)) {
if (st.error) {
resultLine = `Letzter Lauf mit Fehler beendet: ${st.error}. `;
} else if (st.cancelled) {
resultLine = `Letzter Lauf abgebrochen, ${st.translated} von ${st.total} Artikeln übersetzt. `;
} else {
resultLine = `Letzter Lauf abgeschlossen, ${st.translated} Artikel übersetzt. `;
}
}
if (st.pending > 0) {
const est = st.estimate || {};
info.textContent = resultLine +
`${st.pending} Artikel ohne deutsche Übersetzung. ` +
`Geschätzt: ${formatDuration(est.seconds)}, ca. $${est.cost_usd}.`;
runBtn.disabled = false;
} else {
info.textContent = resultLine + "Alle Artikel sind übersetzt.";
runBtn.disabled = true;
}
}
async function loadTranslationStatus() {
try {
const st = await API.get("/api/translation/status");
renderTranslation(st);
if (st.running && !translationPollTimer) {
translationPollTimer = setInterval(pollTranslation, 3000);
}
} catch (e) {
const info = document.getElementById("translationInfo");
if (info) info.textContent = "Status nicht abrufbar: " + (e.message || e);
}
}
async function pollTranslation() {
try {
const st = await API.get("/api/translation/status");
renderTranslation(st);
if (!st.running) {
clearInterval(translationPollTimer);
translationPollTimer = null;
if (st.error) {
showToast("Übersetzung mit Fehler beendet", "error");
} else if (st.cancelled) {
showToast(`Übersetzung abgebrochen, ${st.translated} übersetzt`, "info");
} else {
showToast(`Übersetzung fertig: ${st.translated} Artikel`, "success");
}
}
} catch (e) {
console.warn("Translation-Poll fehlgeschlagen:", e);
}
}
async function startTranslation() {
let st;
try {
st = await API.get("/api/translation/status");
} catch (e) {
showToast(e.message || "Status nicht abrufbar", "error");
return;
}
if (st.running) { showToast("Es läuft bereits eine Übersetzung", "info"); return; }
if (!st.pending) { showToast("Es gibt nichts zu übersetzen", "info"); return; }
const est = st.estimate || {};
const ok = await showConfirm(
"Übersetzung starten",
`${st.pending} Artikel werden ins Deutsche übersetzt. ` +
`Geschätzte Dauer: ${formatDuration(est.seconds)}, geschätzte Kosten: ca. $${est.cost_usd}. ` +
`Der Lauf kann jederzeit abgebrochen werden.`
);
if (!ok) return;
try {
const res = await API.post("/api/translation/run", {});
if (res && res.status === "started") {
showToast(`Übersetzung gestartet (${res.pending} Artikel)`, "success");
await loadTranslationStatus();
if (!translationPollTimer) {
translationPollTimer = setInterval(pollTranslation, 3000);
}
} else {
showToast("Es gibt nichts zu übersetzen", "info");
loadTranslationStatus();
}
} catch (e) {
showToast(e.message || "Start fehlgeschlagen", "error");
}
}
async function cancelTranslation() {
try {
await API.post("/api/translation/cancel", {});
showToast("Übersetzung wird abgebrochen…", "info");
} catch (e) {
showToast(e.message || "Abbruch fehlgeschlagen", "error");
}
}
// ===== Token-Nutzung =====
async function loadOrgTokenUsage(orgId) {
try {