`);
catHead.addEventListener("click", () => {
const shown = catList.style.display !== "none";
catList.style.display = shown ? "none" : "block";
catHead.querySelector(".cat-arrow").textContent = shown ? "▼" : "▲";
});
catItems.forEach(item => {
const row = el(`
`);
row.querySelector("button").addEventListener("click", () => {
if (basket[item.id]) {
basket[item.id].qty++;
} else {
basket[item.id] = { item, qty: 1 };
}
haptic && haptic("impact");
// Flash button
const btn = row.querySelector("button");
btn.style.background = "#4CAF50";
setTimeout(() => { btn.style.background = "var(--accent)"; }, 200);
updateBasket();
});
catList.appendChild(row);
});
catWrap.appendChild(catHead);
catWrap.appendChild(catList);
section.appendChild(catWrap);
});
parent.appendChild(section);
}
function _renderBasket(panel, basket, assemblyId, container) {
const entries = Object.values(basket).filter(e => e.qty > 0);
const total = entries.reduce((s, e) => s + e.item.price * e.qty, 0);
if (!entries.length) {
panel.innerHTML = `
`;
return;
}
panel.innerHTML = "";
// Items compact list (max 3 visible)
const listWrap = el(`
`);
entries.forEach(({ item, qty }) => {
const row = el(`
${item.name}
${qty}
${fmt(item.price*qty)}
`);
row.querySelector(".qty-minus").addEventListener("click", () => {
basket[item.id].qty = Math.max(0, basket[item.id].qty - 1);
if (basket[item.id].qty === 0) delete basket[item.id];
_renderBasket(panel, basket, assemblyId, container);
});
row.querySelector(".qty-plus").addEventListener("click", () => {
basket[item.id].qty++; _renderBasket(panel, basket, assemblyId, container);
});
row.querySelector(".qty-del").addEventListener("click", () => {
delete basket[item.id]; _renderBasket(panel, basket, assemblyId, container);
});
listWrap.appendChild(row);
});
panel.appendChild(listWrap);
// Textarea note
const noteArea = el(`
`);
panel.appendChild(noteArea);
// Total + button
const foot = el(`
${fmt(total)}
`);
foot.querySelector("#act-submit").addEventListener("click", async () => {
const notes = panel.querySelector("#act-notes")?.value || "";
const items = Object.values(basket).map(({ item, qty }) => ({
id: item.id, name: item.name, unit: item.unit,
price: item.price, qty, total: item.price * qty,
category: item.category, source: item.source
}));
haptic && haptic("impact");
const btn = foot.querySelector("#act-submit");
btn.disabled = true; btn.textContent = "Сохраняем…";
let res;
try { res = await _api("/api/extra_act_save", { assembly_id: assemblyId, items, notes }); }
catch(e) { alert("Ошибка: "+e.message); btn.disabled=false; btn.textContent="Оформить акт →"; return; }
if (res.error) { alert(res.error); btn.disabled=false; btn.textContent="Оформить акт →"; return; }
_mountSign(container, res.act_id, assemblyId, res.total, items.length);
});
panel.appendChild(foot);
}
// ─── SIGN ─────────────────────────────────────────────────────────
function _mountSign(container, actId, assemblyId, total, itemCount) {
container.innerHTML = `
Подписать акт
${itemCount} позиций
${fmt(total)}
Акт № ${actId}
Акт будет сохранён как черновик. Подпись можно добавить позже.
`;
// Tabs
container.querySelector("#tab-canvas").addEventListener("click", () => {
container.querySelector("#panel-canvas").style.display = "block";
container.querySelector("#panel-draft").style.display = "none";
container.querySelector("#tab-canvas").style.background = "var(--accent)";
container.querySelector("#tab-canvas").style.color = "#fff";
container.querySelector("#tab-draft").style.background = "var(--surface)";
container.querySelector("#tab-draft").style.color = "var(--text)";
});
container.querySelector("#tab-draft").addEventListener("click", () => {
container.querySelector("#panel-canvas").style.display = "none";
container.querySelector("#panel-draft").style.display = "block";
container.querySelector("#tab-draft").style.background = "var(--accent)";
container.querySelector("#tab-draft").style.color = "#fff";
container.querySelector("#tab-canvas").style.background = "var(--surface)";
container.querySelector("#tab-canvas").style.color = "var(--text)";
});
// Canvas setup
const canvas = container.querySelector("#sign-canvas");
const dpr = window.devicePixelRatio || 1;
setTimeout(() => {
const w = canvas.offsetWidth; const h = canvas.offsetHeight;
canvas.width = w * dpr; canvas.height = h * dpr;
const ctx = canvas.getContext("2d");
ctx.scale(dpr, dpr);
ctx.strokeStyle = "#212121"; ctx.lineWidth = 2.5;
ctx.lineCap = "round"; ctx.lineJoin = "round";
let drawing = false;
const pos = (e) => {
const r = canvas.getBoundingClientRect();
const src = e.touches ? e.touches[0] : e;
return { x: (src.clientX - r.left), y: (src.clientY - r.top) };
};
canvas.addEventListener("pointerdown", e => { drawing=true; ctx.beginPath(); const p=pos(e); ctx.moveTo(p.x,p.y); e.preventDefault(); });
canvas.addEventListener("pointermove", e => { if(!drawing) return; const p=pos(e); ctx.lineTo(p.x,p.y); ctx.stroke(); e.preventDefault(); });
canvas.addEventListener("pointerup", () => { drawing=false; });
canvas.addEventListener("pointerleave", () => { drawing=false; });
}, 50);
container.querySelector("#clear-btn").addEventListener("click", () => {
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
});
const _doSign = async (via, b64) => {
haptic && haptic("impact");
let res;
try { res = await _api("/api/extra_act_sign", { act_id: actId, signed_via: via, signature_b64: b64 }); }
catch(e) { alert("Ошибка: "+e.message); return; }
if (res.error) { alert(res.error); return; }
_showSuccess(container, actId, assemblyId, total, via === "canvas");
};
container.querySelector("#sign-btn").addEventListener("click", () => {
_doSign("canvas", canvas.toDataURL("image/png"));
});
container.querySelector("#draft-btn").addEventListener("click", () => {
mount(container, assemblyId);
});
}
function _showSuccess(container, actId, assemblyId, total, signed) {
container.innerHTML = `
${signed ? "✅" : "💾"}
${signed ? "Акт подписан" : "Черновик сохранён"}
${fmt(total)}
Акт № ${actId}
`;
}
return { mount, mountCreate };
})();