Fotos: Gesichtssuche FH-weit (Studierende + Lektoren) als Job mit Live-Progress, Person-Info-Seiten, Scope-Auswahl
This commit is contained in:
@@ -424,10 +424,64 @@ async function reloadVerbands() {
|
||||
} catch (e) { /* Keep static options */ }
|
||||
}
|
||||
|
||||
/* ================= Gesichtssuche: Job-Polling ================= */
|
||||
function initSearchJob() {
|
||||
const job = window.__SEARCH_JOB__;
|
||||
if (!job) return;
|
||||
const state = $("#search-state");
|
||||
const count = $("#search-count");
|
||||
const progress = $("#search-progress");
|
||||
const timeBadge = $("#search-time");
|
||||
const bar = $("#search-bar");
|
||||
const grid = $("#match-grid");
|
||||
const hint = $("#search-hint");
|
||||
const t0 = Date.now();
|
||||
|
||||
function tile(r) {
|
||||
const name = `${esc(r.nachname)}${r.vorname ? ", " + esc(r.vorname) : ""}`;
|
||||
const pct = Math.max(0, Math.round(r.sim * 100));
|
||||
const cls = r.sim >= 0.5 ? "match-strong" : (r.sim >= 0.35 ? "match-mid" : "");
|
||||
const badge = r.kind === "lektor" ? ` <span class="badge badge-muted">Lektor</span>` : "";
|
||||
return `<div class="match-tile ${cls}">
|
||||
<a href="/person/${encodeURIComponent(r.uid)}"><img src="${esc(r.img)}" alt="${name}"></a>
|
||||
<div class="match-name"><a href="/person/${encodeURIComponent(r.uid)}">${name}</a>${badge}</div>
|
||||
<div class="match-sim">
|
||||
<div class="sim-bar"><div style="width:${pct}%"></div></div>
|
||||
<span class="sim-num">${pct} %</span>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function poll() {
|
||||
fetch(`/api/suche/status/${job.id}`)
|
||||
.then(r => { if (!r.ok) throw new Error("status"); return r.json(); })
|
||||
.then(d => {
|
||||
if (d.total) bar.style.width = (d.done / d.total * 100).toFixed(1) + "%";
|
||||
progress.textContent = `${d.done}/${d.total}`;
|
||||
grid.innerHTML = (d.top || []).map(tile).join("");
|
||||
count.textContent = `${(d.top || []).filter(r => r.sim >= 0.35).length} Treffer`;
|
||||
if (d.status === "done") {
|
||||
clearInterval(timer);
|
||||
state.textContent = d.error ? "Suche fehlgeschlagen" : "Fertig";
|
||||
hint.style.display = "block";
|
||||
if (d.error) hint.innerHTML = `<span style="color:#b3261e">${esc(d.error)}</span>`;
|
||||
const secs = d.took ? `${d.took} s` : `${((Date.now() - t0) / 1000).toFixed(0)} s`;
|
||||
timeBadge.textContent = secs;
|
||||
timeBadge.style.display = "";
|
||||
}
|
||||
})
|
||||
.catch(() => { /* retry on next tick */ });
|
||||
}
|
||||
|
||||
const timer = setInterval(poll, 2000);
|
||||
poll();
|
||||
}
|
||||
|
||||
/* ================= boot ================= */
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
if (window.__PLAN_DATA__) initPlan();
|
||||
if (window.__STGS__) decodeMat();
|
||||
if (window.__SEARCH_JOB__) initSearchJob();
|
||||
});
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.key === "Escape") closeDetail();
|
||||
|
||||
Reference in New Issue
Block a user