From f473f1dc038da05d394507604ce3e8883a35173b Mon Sep 17 00:00:00 2001 From: wasrusgen Date: Tue, 19 May 2026 08:32:52 +0300 Subject: [PATCH] feat(podbor): copy report as plain text for messengers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New button "📋 Скопировать текст" in report export panel - _copyReportText(): formats structured text (brand, price, pros/cons, links) - _stripHtml(): strips HTML tags for clean clipboard output - Clipboard API with execCommand fallback for Telegram WebApp - Button shows "✅ Скопировано!" feedback for 2.2s Co-Authored-By: Claude Sonnet 4.6 --- miniapp/assets/podbor.js | 95 +++++++++++++++++++++++++++++++++++++++- miniapp/index.html | 2 +- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/miniapp/assets/podbor.js b/miniapp/assets/podbor.js index 81bdc04..654e91f 100644 --- a/miniapp/assets/podbor.js +++ b/miniapp/assets/podbor.js @@ -1395,12 +1395,17 @@ const Podbor = (function () {
Сохранить отчёт
+
-
HTML удобно отправить клиенту в мессенджер · PDF — для печати или вложения
+
Текст — для Telegram/WhatsApp · HTML — клиенту на почту · PDF — для печати
`); + const copyBtn = exportNode.querySelector("#exportCopy"); + copyBtn.addEventListener("click", () => { + _copyReportText(ai, leadId, copyBtn); + }); exportNode.querySelector("#exportHtml").addEventListener("click", () => _exportReportHtml(ai, leadId)); exportNode.querySelector("#exportPrint").addEventListener("click", () => _exportReportPrint(wrap, leadId)); wrap.appendChild(exportNode); @@ -1739,6 +1744,94 @@ ${reportEl.outerHTML} `); } + /* Копирует отчёт как plain-text (удобно вставить в Telegram / WhatsApp) */ + function _copyReportText(ai, leadId, btn) { + const lines = []; + const clientName = state.client_name || "клиент"; + lines.push(`🛒 Подбор техники — ${clientName}`); + lines.push(`ID: ${leadId.slice(0, 8)}`); + lines.push(""); + + if (ai.summary) { lines.push(_stripHtml(ai.summary)); lines.push(""); } + + const byCat = ai.by_category || {}; + for (const [catKey, catData] of Object.entries(byCat)) { + const catMeta = PODBOR_CATEGORIES.find(c => c.key === catKey); + const catLabel = catMeta?.label || catKey; + const models = (catData && catData.models) || []; + if (!models.length) continue; + + lines.push(`━━━ ${catLabel} ━━━`); + if (catData.analysis) { lines.push(_stripHtml(catData.analysis)); lines.push(""); } + + for (const m of models) { + const pMin = (m.enriched || {}).price_min_rub || m.price_min_rub; + const pMax = (m.enriched || {}).price_max_rub || m.price_max_rub; + let priceStr = "цена уточняется"; + if (pMin && pMax && pMin !== pMax) priceStr = `${Math.round(pMin).toLocaleString("ru-RU")} – ${Math.round(pMax).toLocaleString("ru-RU")} ₽`; + else if (pMin) priceStr = `от ${Math.round(pMin).toLocaleString("ru-RU")} ₽`; + + lines.push(`${m.brand || ""} ${m.model || ""} — ${priceStr}`); + if ((m.highlights || []).length) lines.push(` ✓ ${m.highlights.map(_stripHtml).join(" · ")}`); + (m.pros || []).slice(0, 3).forEach(p => lines.push(` + ${_stripHtml(p)}`)); + (m.cons || []).slice(0, 2).forEach(c => lines.push(` − ${_stripHtml(c)}`)); + if (m.reasoning) lines.push(` 💡 ${_stripHtml(m.reasoning)}`); + + // Лучшая ссылка из магазинов + const stores = ["ozon", "citilink", "wb", "yamarket", "dns"]; + const bestStore = stores.map(k => (m.enriched || {})[k]).find(s => s && s.url); + if (bestStore) lines.push(` 🔗 ${bestStore.url}`); + lines.push(""); + } + } + + const total = ai.total_price_estimate_rub || {}; + if (total.min || total.max) { + const lo = Math.round(total.min || total.max).toLocaleString("ru-RU"); + const hi = Math.round(total.max || total.min).toLocaleString("ru-RU"); + lines.push(total.min !== total.max ? `ИТОГО: ${lo} – ${hi} ₽` : `ИТОГО: ${lo} ₽`); + lines.push(""); + } + + lines.push(`Сформировано ${new Date().toLocaleString("ru-RU")} · ЗОВ CRM`); + const text = lines.join("\n"); + + const doFallback = () => { + const ta = document.createElement("textarea"); + ta.value = text; + ta.style.cssText = "position:fixed;top:-9999px;left:-9999px;"; + document.body.appendChild(ta); + ta.select(); + try { document.execCommand("copy"); } catch(e) {} + document.body.removeChild(ta); + }; + + const onOk = () => { + haptic && haptic("success"); + const orig = btn.textContent; + btn.textContent = "✅ Скопировано!"; + btn.disabled = true; + setTimeout(() => { btn.textContent = orig; btn.disabled = false; }, 2200); + }; + + if (navigator.clipboard?.writeText) { + navigator.clipboard.writeText(text).then(onOk).catch(() => { doFallback(); onOk(); }); + } else { + doFallback(); onOk(); + } + } + + /* Убирает HTML-теги из AI-текста для plain-text копирования */ + function _stripHtml(s) { + if (!s) return ""; + return String(s) + .replace(//gi, "\n") + .replace(/<[^>]+>/g, "") + .replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + .replace(/"/g, '"').replace(/'/g, "'") + .trim(); + } + function _esc(s) { return String(s == null ? "" : s) .replace(/&/g, "&") diff --git a/miniapp/index.html b/miniapp/index.html index 1438a84..3dd96a3 100644 --- a/miniapp/index.html +++ b/miniapp/index.html @@ -39,7 +39,7 @@ - +