(function (global) { "use strict"; const GFE_DYNAMIC_KEYS = ["usp", "nel"]; let catalog = []; let profiles = []; let selectedId = null; let draftNorms = {}; let dynamicNorms = {}; let normCoverage = null; let normSourceMap = {}; let resolvedNormsPreview = {}; let seedCatalog = []; let seedCatalogRation = null; let normsCoverageTimer = null; async function api(path, opts) { const resp = await fetch(path, opts); const data = await resp.json().catch(() => ({})); if (!resp.ok) throw new Error(data.message || `Наука, чёрт возьми — ошибка ${resp.status}`); return data; } function escapeHtml(s) { return String(s ?? "") .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function notify() { return global.WespZootechNotify?.createAdapter?.(); } function readMass() { const raw = document.getElementById("labProfileMass")?.value?.trim(); if (raw === "") return null; const n = Number(raw); return Number.isNaN(n) ? null : n; } function readMilk() { const raw = document.getElementById("labProfileMilk")?.value?.trim(); if (raw === "") return null; const n = Number(raw); return Number.isNaN(n) ? null : n; } function readNumInput(id) { const raw = document.getElementById(id)?.value?.trim(); if (raw === "") return null; const n = Number(raw); return Number.isNaN(n) ? null : n; } function readNormsMethod() { return document.getElementById("labProfileNormsMethod")?.value || "wesp"; } function readNormsParams() { const method = readNormsMethod(); if (method === "wesp") return {}; const params = { milkFatPct: readNumInput("labProfileFat") ?? 4, lactationNo: readNumInput("labProfileLactation") ?? 2, }; if (method === "racion_piter") { params.koncOeSv = readNumInput("labProfileKonc") ?? 10.3; } return params; } function syncNormsMethodUi() { const method = readNormsMethod(); const racionBox = document.getElementById("labProfileRacionFields"); const piterOnly = document.querySelectorAll(".lab-profile-piter-only"); if (racionBox) racionBox.classList.toggle("d-none", method === "wesp"); piterOnly.forEach((el) => el.classList.toggle("d-none", method !== "racion_piter")); syncSourceFilterOptions(method); renderNormsSummary(); scheduleNormsCoverageRefresh(); } function syncSourceFilterOptions(method) { const sel = document.getElementById("labProfilesSourceFilter"); if (!sel) return; sel.querySelectorAll("option[data-racion-only]").forEach((opt) => { opt.hidden = method === "wesp"; opt.disabled = method === "wesp"; }); if (method === "wesp" && ["racion", "racion_only", "derived", "new_racion"].includes(sel.value)) { sel.value = "all"; } } function buildNormSourceMap(coverage, storedNorms) { const map = {}; if (coverage) { (coverage.racion || []).forEach((k) => { map[k] = "racion"; }); (coverage.derived || []).forEach((k) => { map[k] = "derived"; }); (coverage.fallback || []).forEach((k) => { if (!map[k]) map[k] = "fallback"; }); (coverage.missing || []).forEach((k) => { if (!map[k]) map[k] = "missing"; }); } Object.keys(storedNorms || {}).forEach((k) => { if (!map[k]) map[k] = "stored"; }); return map; } function normSourceLabel(src) { const labels = { racion: "RACION", derived: "расч.", fallback: "WESP", stored: "БД", missing: "—", gfe: "GfE", }; return labels[src] || "—"; } function normSourceBadge(src) { if (!src || src === "missing") return ''; const cls = `norm-source-badge norm-source-badge--${src}`; return `${escapeHtml(normSourceLabel(src))}`; } function isInDb(key) { const b = draftNorms[key] || {}; return b.min != null || b.max != null; } function matchesSourceFilter(key) { const filter = document.getElementById("labProfilesSourceFilter")?.value || "all"; if (filter === "all") return true; const src = normSourceMap[key]; if (filter === "in_db") return isInDb(key); if (filter === "seed") return src === "fallback" || src === "stored"; if (filter === "racion") return src === "racion" || src === "derived"; if (filter === "racion_only") return src === "racion"; if (filter === "derived") return src === "derived"; if (filter === "new_racion") return src === "racion" || src === "derived"; return true; } function countNormSources() { const counts = { inDb: 0, racion: 0, derived: 0, fallback: 0, stored: 0, missing: 0 }; const seen = new Set(); catalog.forEach((c) => seen.add(c.key)); Object.keys(draftNorms).forEach((k) => seen.add(k)); if (normCoverage) { (normCoverage.racion || []).forEach((k) => seen.add(k)); (normCoverage.derived || []).forEach((k) => seen.add(k)); (normCoverage.fallback || []).forEach((k) => seen.add(k)); (normCoverage.missing || []).forEach((k) => seen.add(k)); } seen.forEach((key) => { if (isInDb(key)) counts.inDb += 1; const src = normSourceMap[key]; if (src === "racion") counts.racion += 1; else if (src === "derived") counts.derived += 1; else if (src === "fallback") counts.fallback += 1; else if (src === "stored") counts.stored += 1; else if (src === "missing") counts.missing += 1; }); return counts; } function renderNormsSummary() { const panel = document.getElementById("labProfilesNormsSummary"); const text = document.getElementById("labProfilesNormsSummaryText"); const form = document.getElementById("labProfilesForm"); if (!panel || !text || !form || form.hidden) { if (panel) panel.hidden = true; return; } const method = readNormsMethod(); const counts = countNormSources(); if (method === "wesp") { panel.hidden = false; text.textContent = `В БД ${counts.inDb} показ. · справочник/ручные ${counts.stored + counts.fallback}`; return; } if (!normCoverage) { panel.hidden = false; text.textContent = "Укажи массу и удой — разберём источники RACION"; return; } panel.hidden = false; const racTotal = counts.racion + counts.derived; const withMin = normCoverage.withMin != null ? normCoverage.withMin : racTotal + counts.fallback; text.textContent = `В БД ${counts.inDb} · RACION ${racTotal} (NPitV ${counts.racion} + расч. ${counts.derived}) · ` + `WESP fallback ${counts.fallback} · с min ${withMin}/${normCoverage.total ?? "—"}`; } function applyNormsCoveragePayload(payload) { normCoverage = payload.coverage || null; resolvedNormsPreview = payload.resolvedIndicators || {}; if (payload.dynamicNorms) { dynamicNorms = { ...dynamicNorms, ...payload.dynamicNorms }; } normSourceMap = buildNormSourceMap(normCoverage, draftNorms); renderNormsSummary(); renderNormsTable(); } async function refreshNormsCoverage() { const method = readNormsMethod(); const mass = readMass(); const milk = readMilk(); if (method === "wesp") { normCoverage = null; resolvedNormsPreview = {}; normSourceMap = buildNormSourceMap(null, draftNorms); GFE_DYNAMIC_KEYS.forEach((key) => { if (dynamicNorms[key]?.min != null && !isInDb(key)) normSourceMap[key] = "gfe"; }); renderNormsSummary(); renderNormsTable(); return; } if (mass == null || milk == null) { normCoverage = null; normSourceMap = buildNormSourceMap(null, draftNorms); renderNormsSummary(); renderNormsTable(); return; } const q = new URLSearchParams({ method, mass_kg: String(mass), milk_yield_kg: String(milk) }); if (selectedId) q.set("profile_id", selectedId); const np = readNormsParams(); if (np.milkFatPct != null) q.set("milk_fat_pct", String(np.milkFatPct)); if (np.lactationNo != null) q.set("lactation_no", String(np.lactationNo)); if (np.koncOeSv != null) q.set("konc_oe_sv", String(np.koncOeSv)); try { const preview = await api(`/api/lab/norms-preview?${q.toString()}`); applyNormsCoveragePayload(preview); } catch (err) { notify()?.error?.(err.message); } } function scheduleNormsCoverageRefresh() { if (normsCoverageTimer) clearTimeout(normsCoverageTimer); normsCoverageTimer = setTimeout(() => { refreshNormsCoverage().catch((e) => notify()?.error?.(e.message, { fallback: "Не удалось выполнить операцию" })); }, 350); } function fillNormsParamsFromProfile(p) { const method = p.normsMethod || "wesp"; const sel = document.getElementById("labProfileNormsMethod"); if (sel) sel.value = method; const np = p.normsParams || {}; const fat = document.getElementById("labProfileFat"); const lact = document.getElementById("labProfileLactation"); const konc = document.getElementById("labProfileKonc"); if (fat) fat.value = np.milkFatPct != null ? np.milkFatPct : 4; if (lact) lact.value = np.lactationNo != null ? np.lactationNo : 2; if (konc) konc.value = np.koncOeSv != null ? np.koncOeSv : 10.3; syncNormsMethodUi(); } /** GfE 2001 — parity app/lab/calc/gfe_norms.py */ function gfeUspMinG(massKg, milkKg) { if (!massKg || massKg <= 0) return null; return 0.09 * massKg ** 0.75 * 6.25 + Math.max(milkKg || 0, 0) * 85; } function gfeNelMinMj(massKg, milkKg) { if (!massKg || massKg <= 0) return null; return 0.293 * massKg ** 0.75 + Math.max(milkKg || 0, 0) * 3.3; } function computeDynamicNormsLocal() { const mass = readMass(); const milk = readMilk(); const out = {}; const usp = gfeUspMinG(mass, milk); if (usp != null) { out.usp = { min: usp, formula: "0.09×масса^0.75×6.25 + удой×85" }; } const nel = gfeNelMinMj(mass, milk); if (nel != null) { out.nel = { min: nel, formula: "0.293×масса^0.75 + удой×3.3" }; } return out; } function formatGfe(v) { const n = Number(v); if (Number.isNaN(n)) return "—"; return Math.abs(n) >= 100 ? n.toFixed(1) : n.toFixed(2); } function refreshDynamicNorms() { dynamicNorms = computeDynamicNormsLocal(); renderGfePanel(); renderNormsTable(); } function renderGfePanel() { const panel = document.getElementById("labProfilesGfePanel"); const vals = document.getElementById("labProfilesGfeValues"); const form = document.getElementById("labProfilesForm"); if (!panel || !form || form.hidden) { if (panel) panel.hidden = true; return; } const mass = readMass(); if (!mass || mass <= 0) { panel.hidden = true; return; } panel.hidden = false; const parts = []; if (dynamicNorms.usp?.min != null) { parts.push(`min уСП ≈ ${formatGfe(dynamicNorms.usp.min)} г`); } if (dynamicNorms.nel?.min != null) { parts.push(`min ЧЭЛ ≈ ${formatGfe(dynamicNorms.nel.min)} МДж`); } if (vals) vals.textContent = parts.join(" · ") || "—"; const enabled = parts.length > 0; ["labProfilesGfeApplyUsp", "labProfilesGfeApplyNel", "labProfilesGfeApplyAll"].forEach((id) => { const btn = document.getElementById(id); if (btn) btn.disabled = !enabled; }); } function applyGfeMin(keys) { collectNormsFromTable(); let applied = 0; keys.forEach((key) => { const v = dynamicNorms[key]?.min; if (v == null) return; const cur = draftNorms[key] || {}; if (cur.min != null) return; draftNorms[key] = { ...cur, min: v }; applied += 1; }); renderNormsTable(); if (applied) { notify()?.success?.("Подставлено по GfE — нажми «Запомни это»"); } else { notify()?.error?.("Нижние границы уже заданы или нет массы"); } } function syncDeleteButton() { const btn = document.getElementById("labProfilesDelete"); if (btn) btn.hidden = !selectedId; } function readRationType() { return document.getElementById("labProfileType")?.value || "DAIRY"; } function syncSeedPanelVisibility() { const panel = document.getElementById("labProfilesSeedPanel"); const form = document.getElementById("labProfilesForm"); if (panel) panel.hidden = !form || form.hidden; } function filteredSeedCatalog() { const q = (document.getElementById("labProfilesSeedSearch")?.value || "").trim().toLowerCase(); if (!q) return seedCatalog; return seedCatalog.filter((e) => { const hay = `${e.externalNo} ${e.label} ${e.massKg ?? ""}`.toLowerCase(); return hay.includes(q); }); } function renderSeedSelect() { const sel = document.getElementById("labProfilesSeedSelect"); const applyBtn = document.getElementById("labProfilesSeedApply"); if (!sel) return; const prev = sel.value; const rows = filteredSeedCatalog(); const opts = [''].concat( rows.map((e) => { const mass = e.massKg != null ? ` · ${e.massKg} кг` : ""; const cnt = e.indicatorCount != null ? ` · ${e.indicatorCount} показ.` : ""; return ``; }) ); sel.innerHTML = opts.join(""); if (prev && rows.some((e) => String(e.externalNo) === prev)) { sel.value = prev; } if (applyBtn) applyBtn.disabled = !sel.value; } async function loadSeedCatalog(rationType) { const ration = rationType || readRationType(); if (seedCatalogRation === ration && seedCatalog.length) { renderSeedSelect(); return; } const data = await api(`/api/lab/seed-norms-catalog?ration_type=${encodeURIComponent(ration)}`); seedCatalog = data.entries || []; seedCatalogRation = ration; renderSeedSelect(); } async function applySeedNorms() { const sel = document.getElementById("labProfilesSeedSelect"); const externalNo = sel?.value; if (!externalNo) { notify()?.error?.("Выбери строку справочника"); return; } const filled = Object.values(draftNorms).filter((b) => b.min != null || b.max != null).length; if (filled > 0) { const ok = global.confirm("Заменить текущие границы нормами из справочника?"); if (!ok) return; } const ration = readRationType(); const entry = await api( `/api/lab/seed-norms-catalog/${encodeURIComponent(externalNo)}?ration_type=${encodeURIComponent(ration)}` ); draftNorms = { ...(entry.indicators || {}) }; if (entry.massKg != null) { document.getElementById("labProfileMass").value = entry.massKg; } document.getElementById("labProfileExternal").value = entry.externalNo; const labelEl = document.getElementById("labProfileLabel"); if (labelEl && !labelEl.value.trim()) { labelEl.value = entry.label || ""; } refreshDynamicNorms(); notify()?.success?.(`Загружено ${Object.keys(draftNorms).length} показателей — нажми «Запомни это»`); } function filteredProfiles() { const type = document.getElementById("labProfilesType")?.value || ""; const q = (document.getElementById("labProfilesSearch")?.value || "").trim().toLowerCase(); return profiles.filter((p) => { if (type && p.rationType !== type) return false; if (!q) return true; const hay = `${p.profileKey} ${p.label}`.toLowerCase(); return hay.includes(q); }); } function renderList() { const el = document.getElementById("labProfilesList"); if (!el) return; const rows = filteredProfiles(); if (!rows.length) { el.innerHTML = '
Стандартов нет — создай новый
'; return; } el.innerHTML = rows .map( (p) => `` ) .join(""); } function indicatorRows() { const onlyCalc = document.getElementById("labProfilesOnlyCalc")?.checked; const onlyFilled = document.getElementById("labProfilesOnlyFilled")?.checked; const q = (document.getElementById("labProfilesNormSearch")?.value || "").trim().toLowerCase(); const extraKeys = Object.keys(draftNorms).filter((k) => !catalog.some((c) => c.key === k)); const rows = [ ...catalog.map((c) => ({ ...c })), ...extraKeys.map((k) => ({ key: k, label: k, unit: "", inCalc: false })), ]; return rows.filter((row) => { if (onlyCalc && !row.inCalc) return false; const b = draftNorms[row.key] || {}; const has = b.min != null || b.max != null; if (onlyFilled && !has) return false; if (!matchesSourceFilter(row.key)) return false; if (q) { const hay = `${row.key} ${row.label}`.toLowerCase(); if (!hay.includes(q)) return false; } return true; }); } function renderNormsTable() { const tbody = document.getElementById("labProfilesNormsBody"); const countEl = document.getElementById("labProfilesNormCount"); if (!tbody) return; const rows = indicatorRows(); tbody.innerHTML = rows .map((row) => { const b = draftNorms[row.key] || {}; const min = b.min != null ? b.min : ""; const max = b.max != null ? b.max : ""; const badge = row.inCalc ? 'в деле' : ""; const unit = row.unit ? ` ${escapeHtml(row.unit)}` : ""; const dyn = dynamicNorms[row.key]; const gfeHint = dyn?.min != null && min === "" ? `GfE ≈ ${formatGfe(dyn.min)}${dyn.formula ? ` (${escapeHtml(dyn.formula)})` : ""}` : ""; const gfeBadge = GFE_DYNAMIC_KEYS.includes(row.key) ? 'GfE' : ""; let src = normSourceMap[row.key]; if (!src && isInDb(row.key)) src = "stored"; const dbBadge = isInDb(row.key) ? 'БД' : ""; const srcBadge = normSourceBadge(src); const resolved = resolvedNormsPreview[row.key]; const resolvedHint = resolved?.min != null && min !== "" && Number(min) !== Number(resolved.min) ? `RACION ≈ ${formatGfe(resolved.min)}` : resolved?.min != null && min === "" ? `RACION ≈ ${formatGfe(resolved.min)} (не в БД)` : ""; return ( `` + `${escapeHtml(row.label)}${badge}${gfeBadge}${unit}${gfeHint}${resolvedHint}` + `${dbBadge}${srcBadge}` + `` + `` + `` ); }) .join(""); if (countEl) { const filled = Object.values(draftNorms).filter((b) => b.min != null || b.max != null).length; const counts = countNormSources(); const method = readNormsMethod(); const srcPart = method === "wesp" ? ` · в БД ${counts.inDb}` : ` · БД ${counts.inDb} · RACION ${counts.racion + counts.derived} · WESP ${counts.fallback}`; countEl.textContent = `В таблице ${rows.length} · с границами ${filled}${srcPart}`; } renderNormsSummary(); } function collectNormsFromTable() { const out = { ...draftNorms }; document.querySelectorAll("#labProfilesNormsBody tr[data-key]").forEach((tr) => { const key = tr.getAttribute("data-key"); if (!key) return; const minIn = tr.querySelector('[data-bound="min"]'); const maxIn = tr.querySelector('[data-bound="max"]'); const minRaw = minIn?.value?.trim(); const maxRaw = maxIn?.value?.trim(); const min = minRaw === "" ? null : Number(minRaw); const max = maxRaw === "" ? null : Number(maxRaw); if (min == null && max == null) { delete out[key]; } else { out[key] = { min: Number.isNaN(min) ? null : min, max: Number.isNaN(max) ? null : max, }; } }); draftNorms = out; } async function selectProfile(id) { selectedId = id; syncDeleteButton(); renderList(); const empty = document.getElementById("labProfilesEmpty"); const form = document.getElementById("labProfilesForm"); if (!id) { if (empty) empty.hidden = false; if (form) form.hidden = true; syncSeedPanelVisibility(); return; } const p = await api(`/api/lab/animal-profiles/${encodeURIComponent(id)}`); if (empty) empty.hidden = true; if (form) form.hidden = false; document.getElementById("labProfileKey").value = p.profileKey || ""; document.getElementById("labProfileLabel").value = p.label || ""; document.getElementById("labProfileType").value = p.rationType || "BEEF"; document.getElementById("labProfileMass").value = p.massKg != null ? p.massKg : ""; document.getElementById("labProfileMilk").value = p.milkYieldKg != null ? p.milkYieldKg : ""; document.getElementById("labProfileExternal").value = p.externalNo != null ? p.externalNo : ""; fillNormsParamsFromProfile(p); draftNorms = { ...(p.norms || p.normsData?.indicators || {}) }; normCoverage = p.coverage || p.normsData?.coverage || null; resolvedNormsPreview = p.resolvedNorms || p.normsData?.resolvedIndicators || {}; normSourceMap = buildNormSourceMap(normCoverage, draftNorms); refreshDynamicNorms(); syncSourceFilterOptions(readNormsMethod()); renderNormsSummary(); scheduleNormsCoverageRefresh(); syncSeedPanelVisibility(); loadSeedCatalog(p.rationType || "DAIRY").catch(() => notify()?.error?.(null, { fallback: "Не удалось выполнить операцию" })); } function newProfile() { selectedId = null; syncDeleteButton(); renderList(); document.getElementById("labProfilesEmpty").hidden = true; document.getElementById("labProfilesForm").hidden = false; document.getElementById("labProfileKey").value = ""; document.getElementById("labProfileLabel").value = ""; document.getElementById("labProfileType").value = "DAIRY"; document.getElementById("labProfileMass").value = ""; document.getElementById("labProfileMilk").value = ""; document.getElementById("labProfileExternal").value = ""; fillNormsParamsFromProfile({ normsMethod: "wesp", normsParams: {} }); draftNorms = {}; normCoverage = null; resolvedNormsPreview = {}; normSourceMap = {}; refreshDynamicNorms(); syncSourceFilterOptions("wesp"); renderNormsSummary(); syncSeedPanelVisibility(); loadSeedCatalog("DAIRY").catch(() => notify()?.error?.(null, { fallback: "Не удалось выполнить операцию" })); } async function saveProfile() { collectNormsFromTable(); const key = document.getElementById("labProfileKey")?.value?.trim(); const label = document.getElementById("labProfileLabel")?.value?.trim(); const rationType = document.getElementById("labProfileType")?.value || "BEEF"; const massRaw = document.getElementById("labProfileMass")?.value?.trim(); const milkRaw = document.getElementById("labProfileMilk")?.value?.trim(); const extRaw = document.getElementById("labProfileExternal")?.value?.trim(); if (!key || !label) { notify()?.error?.("Укажи код и название — я предупреждал"); return; } const payload = { profileKey: key, label, rationType, indicators: draftNorms, normsMethod: readNormsMethod(), normsParams: readNormsParams(), }; if (massRaw !== "") payload.massKg = Number(massRaw); payload.milkYieldKg = milkRaw === "" ? null : Number(milkRaw); if (extRaw !== "") payload.externalNo = Number(extRaw); const method = selectedId ? "PUT" : "POST"; const url = selectedId ? `/api/lab/animal-profiles/${encodeURIComponent(selectedId)}` : "/api/lab/animal-profiles"; await api(url, { method, headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); notify()?.success?.("Стандарт сохранён. Ты чертовски прав"); const typeFilter = document.getElementById("labProfilesType")?.value || ""; const data = await api( `/api/lab/animal-profiles${typeFilter ? `?ration_type=${encodeURIComponent(typeFilter)}` : ""}` ); profiles = data.profiles || []; if (!selectedId && profiles.length) { const hit = profiles.find((p) => p.profileKey === key); if (hit) selectedId = hit.id; } renderList(); if (selectedId) await selectProfile(selectedId); } async function deleteProfile() { if (!selectedId) return; const label = document.getElementById("labProfileLabel")?.value?.trim() || selectedId; if (!global.confirm(`Удалить «${label}»? Никаких полумер.`)) return; await api(`/api/lab/animal-profiles/${encodeURIComponent(selectedId)}`, { method: "DELETE" }); notify()?.success?.("Стандарт снесён. Say my name."); selectedId = null; syncDeleteButton(); const typeFilter = document.getElementById("labProfilesType")?.value || ""; const data = await api( `/api/lab/animal-profiles${typeFilter ? `?ration_type=${encodeURIComponent(typeFilter)}` : ""}` ); profiles = data.profiles || []; document.getElementById("labProfilesEmpty").hidden = false; document.getElementById("labProfilesForm").hidden = true; renderList(); } async function init() { const [cat, prof] = await Promise.all([ api("/api/lab/norm-indicators"), api("/api/lab/animal-profiles"), ]); catalog = cat.indicators || []; profiles = prof.profiles || []; renderList(); } function bind() { document.getElementById("labProfilesList")?.addEventListener("click", (ev) => { const btn = ev.target.closest("button[data-id]"); if (!btn) return; selectProfile(btn.getAttribute("data-id")).catch(() => notify()?.error?.(null, { fallback: "Не удалось выполнить операцию" })); }); document.getElementById("labProfilesType")?.addEventListener("change", async () => { const type = document.getElementById("labProfilesType")?.value || ""; const data = await api( `/api/lab/animal-profiles${type ? `?ration_type=${encodeURIComponent(type)}` : ""}` ); profiles = data.profiles || []; renderList(); }); document.getElementById("labProfilesSearch")?.addEventListener("input", renderList); document.getElementById("labProfilesOnlyFilled")?.addEventListener("change", renderNormsTable); document.getElementById("labProfilesOnlyCalc")?.addEventListener("change", renderNormsTable); document.getElementById("labProfilesSourceFilter")?.addEventListener("change", renderNormsTable); document.getElementById("labProfilesNormSearch")?.addEventListener("input", renderNormsTable); document.getElementById("labProfilesNormsRefresh")?.addEventListener("click", () => { refreshNormsCoverage().catch((e) => notify()?.error?.(e.message, { fallback: "Не удалось выполнить операцию" })); }); document.getElementById("labProfilesNew")?.addEventListener("click", newProfile); document.getElementById("labProfilesSave")?.addEventListener("click", () => { saveProfile().catch(() => notify()?.error?.(null, { fallback: "Не удалось выполнить операцию" })); }); document.getElementById("labProfilesDelete")?.addEventListener("click", () => { deleteProfile().catch(() => notify()?.error?.(null, { fallback: "Не удалось выполнить операцию" })); }); document.getElementById("labProfilesNormsBody")?.addEventListener("change", (ev) => { if (ev.target.matches("input[data-bound]")) collectNormsFromTable(); }); document.getElementById("labProfileMass")?.addEventListener("input", () => { refreshDynamicNorms(); scheduleNormsCoverageRefresh(); }); document.getElementById("labProfileMilk")?.addEventListener("input", () => { refreshDynamicNorms(); scheduleNormsCoverageRefresh(); }); document.getElementById("labProfileNormsMethod")?.addEventListener("change", syncNormsMethodUi); ["labProfileFat", "labProfileLactation", "labProfileKonc"].forEach((id) => { document.getElementById(id)?.addEventListener("input", scheduleNormsCoverageRefresh); }); document.getElementById("labProfileType")?.addEventListener("change", () => { seedCatalogRation = null; loadSeedCatalog(readRationType()).catch(() => notify()?.error?.(null, { fallback: "Не удалось выполнить операцию" })); }); document.getElementById("labProfilesSeedSearch")?.addEventListener("input", renderSeedSelect); document.getElementById("labProfilesSeedSelect")?.addEventListener("change", () => { const applyBtn = document.getElementById("labProfilesSeedApply"); const sel = document.getElementById("labProfilesSeedSelect"); if (applyBtn && sel) applyBtn.disabled = !sel.value; }); document.getElementById("labProfilesSeedApply")?.addEventListener("click", () => { applySeedNorms().catch(() => notify()?.error?.(null, { fallback: "Не удалось выполнить операцию" })); }); document.getElementById("labProfilesGfeApplyUsp")?.addEventListener("click", () => applyGfeMin(["usp"])); document.getElementById("labProfilesGfeApplyNel")?.addEventListener("click", () => applyGfeMin(["nel"])); document.getElementById("labProfilesGfeApplyAll")?.addEventListener("click", () => applyGfeMin(GFE_DYNAMIC_KEYS) ); } document.addEventListener("DOMContentLoaded", () => { bind(); init().catch(() => notify()?.error?.(null, { fallback: "Не удалось выполнить операцию" })); }); })(window);