(function () { const columnDefaults = [ "full_name", "status", "positions", "hse_start_year", "email", "academic_degree", "last_seen_at", "dismissed_at", "profile", ]; const storageKey = "miem.directory.columns"; const columnPresets = { review: ["full_name", "status", "academic_degree", "last_seen_at", "profile"], contacts: ["full_name", "status", "email", "phone", "address", "profile"], full: ["full_name", "status", "positions", "hse_start_year", "email", "phone", "address", "academic_degree", "publications_count", "courses_count", "news_count", "first_seen_at", "last_seen_at", "dismissed_at", "profile"], }; function readColumns() { try { const stored = JSON.parse(localStorage.getItem(storageKey) || "[]"); return Array.isArray(stored) && stored.length ? stored : columnDefaults; } catch (_error) { return columnDefaults; } } function writeColumns(columns) { localStorage.setItem(storageKey, JSON.stringify(columns)); } function applyColumns(columns) { document.querySelectorAll("[data-column]").forEach((node) => { const visible = columns.includes(node.dataset.column); node.classList.toggle("directory-table__cell--hidden", !visible && node.classList.contains("directory-table__cell")); node.classList.toggle("directory-table__head--hidden", !visible && node.classList.contains("directory-table__head")); }); document.querySelectorAll("[data-column-toggle]").forEach((checkbox) => { checkbox.checked = columns.includes(checkbox.value); }); } function setupColumns() { if (!document.querySelector("[data-directory-table]")) return; let columns = readColumns(); const modal = document.querySelector("[data-columns-modal]"); const trigger = document.querySelector("[data-columns-open]"); applyColumns(columns); trigger.addEventListener("click", () => { modal.showModal(); trigger.setAttribute("aria-expanded", "true"); modal.querySelector("input")?.focus(); }); document.querySelectorAll("[data-columns-close]").forEach((button) => { button.addEventListener("click", () => { modal.close(); }); }); document.querySelectorAll("[data-columns-preset]").forEach((button) => { button.addEventListener("click", () => { columns = columnPresets[button.dataset.columnsPreset]; writeColumns(columns); applyColumns(columns); }); }); modal.addEventListener("click", (event) => { if (event.target === modal) modal.close(); }); modal.addEventListener("close", () => { trigger.setAttribute("aria-expanded", "false"); trigger.focus(); }); document.querySelectorAll("[data-column-toggle]").forEach((checkbox) => { checkbox.addEventListener("change", () => { columns = Array.from(document.querySelectorAll("[data-column-toggle]:checked")).map((item) => item.value); if (!columns.length) columns = ["full_name"]; writeColumns(columns); applyColumns(columns); }); }); } function setupProgress() { const panel = document.querySelector("[data-progress-panel]"); if (!panel) return; const update = (run) => { if (!run) return; const status = document.querySelector("[data-progress-status]"); const processed = document.querySelector("[data-progress-processed]"); const found = document.querySelector("[data-progress-found]"); const skipped = document.querySelector("[data-progress-skipped]"); const errors = document.querySelector("[data-progress-errors]"); const fill = document.querySelector("[data-progress-fill]"); const percent = document.querySelector("[data-progress-percent]"); const error = document.querySelector("[data-progress-error]"); if (status) status.textContent = run.status_display || run.status; if (processed) processed.textContent = run.processed_count; if (found) found.textContent = run.found_count; if (skipped) skipped.textContent = run.skipped_count; if (errors) errors.textContent = run.error_count; if (fill) fill.style.width = `${run.progress_percent}%`; if (percent) percent.textContent = run.progress_percent; if (fill) { fill.parentElement.setAttribute("aria-valuenow", run.progress_percent); fill.parentElement.setAttribute("aria-valuetext", `${run.progress_percent}%`); } if (error) error.hidden = true; }; const poll = async () => { try { const response = await fetch("/api/crawl-runs/latest", { credentials: "same-origin" }); if (!response.ok) throw new Error("progress request failed"); const data = await response.json(); const run = data.running || data.latest; update(run); return Boolean(data.running); } catch (_error) { const error = document.querySelector("[data-progress-error]"); if (error) error.hidden = false; return true; } }; document.querySelectorAll("[data-progress-retry]").forEach((button) => button.addEventListener("click", poll)); const interval = window.setInterval(async () => { const keepGoing = await poll(); if (!keepGoing) window.clearInterval(interval); }, 4000); } setupColumns(); setupProgress(); })();