(function (global) { "use strict"; let diffByComponent = new Map(); let hooked = false; function getActiveRecipeId() { const fromEdit = document.getElementById("recipeEdit")?.dataset?.recipeId; if (fromEdit) return fromEdit; return document.querySelector(".list-item.active[data-recipe-id]")?.dataset?.recipeId || null; } function ensureToolbarButtons() { const toolbar = document.querySelector("#recipeEdit .recipe-section-toolbar"); if (!toolbar) return; if (!toolbar.querySelector('[data-action="sync-to-master"]')) { const syncBtn = document.createElement("button"); syncBtn.type = "button"; syncBtn.className = "btn btn-outline-secondary"; syncBtn.setAttribute("data-action", "sync-to-master"); syncBtn.innerHTML = 'В мастер'; toolbar.insertBefore(syncBtn, toolbar.firstChild); } if (!toolbar.querySelector('[data-action="apply-from-master"]')) { const btn = document.createElement("button"); btn.type = "button"; btn.className = "btn btn-outline-primary"; btn.setAttribute("data-action", "apply-from-master"); btn.hidden = true; btn.innerHTML = 'Из мастера'; const addBtn = toolbar.querySelector('[data-action="add-ingredient"]'); if (addBtn) toolbar.insertBefore(btn, addBtn); else toolbar.appendChild(btn); } } function ensureKHubButton() { const actions = document.querySelector("#recipeEdit .recipe-edit-header-actions .d-flex"); if (!actions || actions.querySelector('[data-action="open-lab-k-hub"]')) return; const btn = document.createElement("button"); btn.type = "button"; btn.className = "btn btn-outline-secondary recipe-edit-header-icon-btn"; btn.setAttribute("data-action", "open-lab-k-hub"); btn.title = "Открыть зоотех-мастер в K-hub"; btn.setAttribute("aria-label", "K-hub"); btn.innerHTML = ''; actions.insertBefore(btn, actions.firstChild); } function ensureRationTypeField() { const grid = document.querySelector("#recipeEdit .recipe-form-grid"); if (!grid || document.getElementById("rationType")) return; const wrap = document.createElement("div"); wrap.className = "form-group"; wrap.innerHTML = '' + '"; const tripWrap = document.getElementById("tripPercent")?.closest(".form-group"); if (tripWrap?.parentElement === grid) { tripWrap.insertAdjacentElement("afterend", wrap); } else { grid.appendChild(wrap); } } function ensureDiffStyles() { if (document.getElementById("wesp-lab-diff-styles")) return; const style = document.createElement("style"); style.id = "wesp-lab-diff-styles"; style.textContent = ".lab-diff-badge{margin-left:6px;font-size:.7rem;vertical-align:middle;}" + ".lab-diff-badge--warn{background:#fff3cd;color:#664d03;}" + ".lab-diff-badge--miss{background:#f8d7da;color:#842029;}" + ".lab-recipe-quality{margin-top:4px;font-size:.78rem;color:var(--bs-secondary-color,#6c757d);}" + ".lab-recipe-quality__label{font-weight:600;margin-right:4px;}"; document.head.appendChild(style); } function diffBadgeHtml(reasons) { if (!reasons?.length) return ""; const title = reasons.join(", "); const cls = reasons.includes("missing_in_master") || reasons.includes("missing_in_execution") ? "lab-diff-badge--miss" : "lab-diff-badge--warn"; return `мастер`; } function applyDiffBadges() { document.querySelectorAll("#ingredientsTableBody .ingredient-row").forEach((row) => { const select = row.querySelector(".ingredient-select"); const cell = row.querySelector('.ingredient-detail-cell[data-label="Ингредиент"] .recipe-row-name-with-skip'); if (!select || !cell) return; cell.querySelectorAll(".lab-diff-badge").forEach((el) => el.remove()); const compId = select.value; if (!compId) return; const reasons = diffByComponent.get(String(compId)); if (!reasons?.length) return; cell.insertAdjacentHTML("beforeend", diffBadgeHtml(reasons)); }); } function ensureQualityBlock() { const headerText = document.querySelector("#recipeEdit .recipe-edit-header-text"); if (!headerText || document.getElementById("labRecipeQuality")) return; const block = document.createElement("div"); block.id = "labRecipeQuality"; block.className = "lab-recipe-quality"; block.hidden = true; block.innerHTML = 'Зоотех:' + ''; headerText.appendChild(block); } async function refreshQuality(recipeId) { ensureQualityBlock(); const block = document.getElementById("labRecipeQuality"); const items = block?.querySelector("[data-lab-quality-items]"); if (!block || !items || !recipeId) return; try { const resp = await fetch(`/api/lab/rations/${encodeURIComponent(recipeId)}`); if (!resp.ok) throw new Error("no ration"); const data = await resp.json(); const indicators = (data.rationResults?.indicators || []).slice(0, 4); if (!data.exists || !indicators.length) { block.hidden = true; return; } items.textContent = indicators .map((ind) => `${ind.label}: ${ind.content ?? "—"} ${ind.unit || ""}`.trim()) .join(" · "); block.hidden = false; } catch (_) { block.hidden = true; } } async function refreshDiff(recipeId) { const btn = document.querySelector('[data-action="apply-from-master"]'); if (!recipeId) return; await refreshQuality(recipeId); if (!btn) return; try { const resp = await fetch(`/api/lab/rations/${encodeURIComponent(recipeId)}/diff`); const data = await resp.json(); diffByComponent = new Map(); (data.lines || []).forEach((line) => { if (line.componentId) diffByComponent.set(String(line.componentId), line.reasons || []); }); btn.hidden = !data.hasChanges; applyDiffBadges(); } catch (_) { btn.hidden = true; diffByComponent = new Map(); } } function patchRecipeLoad() { if (hooked) return; const orig = global.loadRecipeDetails; if (typeof orig !== "function") return; global.loadRecipeDetails = async function (recipeId, options) { const recipeEdit = document.getElementById("recipeEdit"); if (recipeEdit && recipeId) recipeEdit.dataset.recipeId = String(recipeId); await orig(recipeId, options); await refreshDiff(recipeId); global.WespLabRecipesPlanOverlay?.decorateRows?.(); }; hooked = true; } function patchRecipeSave() { const orig = global.doSaveRecipe; if (typeof orig !== "function" || orig.__labPatched) return; global.doSaveRecipe = async function (closeAfterSave) { const rationEl = document.getElementById("rationType"); const prev = global.__wespLabRationTypeForSave; if (rationEl) { global.__wespLabRationTypeForSave = rationEl.value || null; } try { return await orig(closeAfterSave); } finally { global.__wespLabRationTypeForSave = prev; } }; const origFetch = global.fetch.bind(global); global.fetch = async function (input, init) { const url = typeof input === "string" ? input : input?.url; const method = (init?.method || "GET").toUpperCase(); if ( method === "PUT" && url && /\/api\/recipes\/[^/]+$/.test(url) && init?.body && global.__wespLabRationTypeForSave !== undefined ) { try { const body = JSON.parse(init.body); body.ration_type = global.__wespLabRationTypeForSave || null; init = { ...init, body: JSON.stringify(body) }; } catch (_) { /* ignore */ } } return origFetch(input, init); }; global.doSaveRecipe.__labPatched = true; } document.addEventListener("click", async (ev) => { const btn = ev.target.closest("[data-action]"); if (!btn) return; const action = btn.getAttribute("data-action"); const recipeId = getActiveRecipeId(); if (action === "open-lab-k-hub") { global.WespZootechNotificationCenter?.openDailyPlan?.(recipeId || undefined); return; } if (action === "sync-to-master") { if (!recipeId) return; const ok = await global.WespDialog?.confirm?.( "Сохранить текущий рецепт в зоотех-мастер?", { title: "В мастер" } ); if (!ok) return; try { const resp = await fetch( `/api/lab/rations/${encodeURIComponent(recipeId)}/sync-from-execution`, { method: "POST" } ); const data = await resp.json(); if (!resp.ok) throw new Error(data.message || "Ошибка"); global.WespZootechNotify?.createAdapter?.()?.success?.("Мастер обновлён из рецепта"); await refreshDiff(recipeId); } catch (e) { global.WespZootechNotify?.createAdapter?.()?.error?.(e.message, { fallback: "Не удалось выполнить операцию" }); } return; } if (action !== "apply-from-master") return; if (!recipeId) return; const ok = await global.WespDialog?.confirm?.("Перенести ингредиенты из зоотех-мастера?", { title: "Из мастера", }); if (!ok) return; try { const resp = await fetch(`/api/lab/rations/${encodeURIComponent(recipeId)}/apply-from-master`, { method: "POST", }); const data = await resp.json(); if (!resp.ok) throw new Error(data.message || "Ошибка"); if (typeof global.reloadRecipeEditor === "function") { await global.reloadRecipeEditor(recipeId); } else if (typeof global.loadRecipeDetails === "function") { await global.loadRecipeDetails(recipeId); } await refreshDiff(recipeId); global.WespZootechNotify?.createAdapter?.()?.success?.("Мастер применён к рецепту"); } catch (e) { global.WespZootechNotify?.createAdapter?.()?.error?.(e.message, { fallback: "Не удалось применить мастер" }); } }); document.addEventListener("change", (ev) => { if (!ev.target?.matches?.(".ingredient-select")) return; applyDiffBadges(); }); global.WespLabRecipesOverlay = { init() { ensureDiffStyles(); ensureToolbarButtons(); ensureKHubButton(); ensureRationTypeField(); patchRecipeLoad(); patchRecipeSave(); }, refreshDiff, refreshQuality, applyDiffBadges, }; function boot() { global.WespLabRecipesOverlay.init(); if (typeof global.loadRecipeDetails === "function") patchRecipeLoad(); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", boot); } else { boot(); } })(window);