add health import

This commit is contained in:
2026-05-19 14:50:19 +02:00
parent bc6e850afb
commit e36f27da4a
12 changed files with 1843 additions and 14 deletions
+95
View File
@@ -1557,6 +1557,100 @@
}
}
function initHealthImportStatus() {
const panel = document.querySelector("[data-health-import-status]");
if (!panel) {
return;
}
const progressWrap = panel.querySelector("[data-health-progress-wrap]");
const progress = panel.querySelector("[data-health-progress-bar]");
const progressText = panel.querySelector("[data-health-progress-text]");
const lastImport = panel.querySelector("[data-health-last-import]");
const lastMessage = panel.querySelector("[data-health-last-message]");
let timer = null;
const formatDuration = seconds => {
const rounded = Math.max(0, Math.round(Number(seconds) || 0));
if (rounded < 60) {
return `${rounded} s`;
}
return `${Math.ceil(rounded / 60)} min`;
};
const render = status => {
const done = Math.max(0, Number(status.progress_done || 0));
const total = Math.max(0, Number(status.progress_total || 0));
const percent = total > 0 ? Math.min(100, Math.round((done / total) * 100)) : 0;
if (progress) {
progress.value = String(percent);
progress.textContent = `${percent}%`;
}
if (progressWrap) {
progressWrap.dataset.progressDone = String(done);
progressWrap.dataset.progressTotal = String(total);
}
if (lastImport) {
lastImport.textContent = status.last_import_at ? new Date(status.last_import_at).toLocaleString("de-DE") : "-";
}
if (lastMessage) {
lastMessage.textContent = status.last_message || "-";
}
if (progressText) {
if (status.last_status === "running") {
let eta = "wird berechnet";
const started = Date.parse(status.started_at || "");
if (started && done > 0 && total > done) {
const elapsed = (Date.now() - started) / 1000;
eta = formatDuration((elapsed / done) * (total - done));
}
progressText.textContent = `Import läuft: ${done} von ${total} verarbeitet. Restzeit ca. ${eta}.`;
return;
}
if (status.last_status === "error") {
progressText.textContent = `${status.last_message || "Import fehlgeschlagen."} Derselbe Export kann erneut gesendet werden und wird idempotent übernommen.`;
return;
}
progressText.textContent = status.last_message || "Noch kein Import gelaufen.";
}
};
const initialDone = progressWrap ? Number(progressWrap.dataset.progressDone || 0) : 0;
const initialTotal = progressWrap ? Number(progressWrap.dataset.progressTotal || 0) : 0;
render({ progress_done: initialDone, progress_total: initialTotal });
const refresh = async () => {
try {
const response = await fetch("/api/health/status", { credentials: "same-origin" });
if (!response.ok) {
return;
}
const data = await response.json();
if (!data || !data.ok || !data.status) {
return;
}
render(data.status);
if (data.status.last_status !== "running" && timer !== null) {
window.clearInterval(timer);
timer = null;
}
} catch (error) {
// Status polling is best-effort; the import itself is server-side.
}
};
refresh();
timer = window.setInterval(refresh, 3500);
}
function csrfToken() {
return document.querySelector('meta[name="csrf-token"]')?.getAttribute("content") || "";
}
@@ -1888,6 +1982,7 @@
initDashboardCharts();
initDashboardExperience();
initOptionsPanels();
initHealthImportStatus();
initSportTypeManager();
initPwaShell();
initPullToRefresh();