From c2be5e846f80bb6c66d128ee71afc5c65d6d22c9 Mon Sep 17 00:00:00 2001 From: wasrusgen Date: Mon, 11 May 2026 12:26:58 +0300 Subject: [PATCH] miniapp: inline report after submit + standalone preview-report.html MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit REPORT RENDERER (podbor.js): - New renderReport(ai, leadId) function — beautiful inline report after submit success - Shows by_category with up to 5 models per category - Model card: photo (88x88), brand · name, price range, rating + reviews + stores - Highlights (with tech translations), pros (green), cons (orange) - External links to WB / Я.Маркет / OZON / DNS (when enriched data present) - Comparison table per category (accordion details) - Total price block (dark theme contrast) - Warnings block (when AI returns concerns) CSS (podbor.css): - .report-* classes: head, summary, cat, model, links, compare, total, warnings - Editorial Calm palette — walnut accents, paper bg, Newsreader for titles - Responsive: model card grid 88px image + 1fr body - Placeholder gradient when no image (camera emoji) STANDALONE PREVIEW (preview-report.html): - Mock AI response with 3 fridges + 2 hobs - Same render logic, runs without backend - Visit: https://wasrusgen.github.io/zov-tech/preview-report.html NEXT: integrate proxy6 token → real photos/prices instead of placeholders --- miniapp/assets/podbor.css | 289 +++++++++++++++++++++++++++++++++ miniapp/assets/podbor.js | 173 +++++++++++++++++++- miniapp/index.html | 14 +- miniapp/preview-report.html | 308 ++++++++++++++++++++++++++++++++++++ 4 files changed, 774 insertions(+), 10 deletions(-) create mode 100644 miniapp/preview-report.html diff --git a/miniapp/assets/podbor.css b/miniapp/assets/podbor.css index 6beb61b..4ce2107 100644 --- a/miniapp/assets/podbor.css +++ b/miniapp/assets/podbor.css @@ -1119,3 +1119,292 @@ line-height: 1.3; } .rev-val .muted { color: var(--muted); font-weight: 400; } + +/* ============================================================ + Inline-отчёт после отправки подбора + ============================================================ */ + +.report { + margin-top: 20px; + display: flex; + flex-direction: column; + gap: 18px; +} + +.report-head .kicker { + margin-bottom: 4px; +} + +.report-summary { + font-size: 14px; + line-height: 1.5; + color: var(--ink-2); + margin: 6px 0 0; + padding: 12px 14px; + background: var(--warm); + border-radius: 12px; + border-left: 3px solid var(--accent-2); +} + +.report-cat { + display: flex; + flex-direction: column; + gap: 12px; +} + +.report-cat-head { + font-family: var(--font-display); + font-style: italic; + font-weight: 400; + font-size: 22px; + line-height: 1; + letter-spacing: -0.01em; + color: var(--ink); + margin: 0; + display: flex; + align-items: center; + gap: 10px; + padding-bottom: 8px; + border-bottom: 1px solid var(--line); +} + +.report-cat-icon { + width: 24px; + height: 24px; + display: grid; + place-items: center; + color: var(--accent-2); +} + +.report-cat-icon svg { width: 22px; height: 22px; } + +.report-models { + display: flex; + flex-direction: column; + gap: 12px; +} + +.report-model { + display: grid; + grid-template-columns: 88px 1fr; + gap: 12px; + padding: 12px; + background: #fff; + border: 1px solid var(--line); + border-radius: 14px; + position: relative; +} + +.report-model-img { + width: 88px; + height: 88px; + background: var(--warm); + border-radius: 10px; + overflow: hidden; + display: grid; + place-items: center; +} +.report-model-img img { + width: 100%; + height: 100%; + object-fit: contain; + background: #fff; +} +.report-model-img.placeholder { + background: repeating-linear-gradient(45deg, var(--warm), var(--warm) 5px, #F0E8D5 5px, #F0E8D5 10px); +} +.report-model-img.placeholder::after { + content: "📷"; + font-size: 24px; + opacity: 0.4; +} + +.report-model-body { + display: flex; + flex-direction: column; + gap: 4px; + min-width: 0; +} + +.report-model-brand { + font-family: var(--font-mono); + font-size: 10px; + font-weight: 500; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--muted); +} + +.report-model-name { + font-family: var(--font-sans); + font-size: 15px; + font-weight: 600; + letter-spacing: -0.01em; + color: var(--ink); + line-height: 1.2; + margin-bottom: 2px; +} + +.report-model-meta { + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.06em; + color: var(--muted); + display: flex; + flex-wrap: wrap; + gap: 4px 8px; + margin: 2px 0; +} + +.report-model-meta .rating { color: var(--accent-2); font-weight: 500; } + +.report-model-price { + font-family: var(--font-display); + font-style: italic; + font-size: 17px; + color: var(--ink); + margin: 4px 0; + letter-spacing: -0.01em; +} +.report-model-price strong { font-style: normal; font-weight: 600; } +.report-model-price .muted { font-style: normal; font-size: 13px; color: var(--muted); } + +.report-highlights { + font-size: 12px; + line-height: 1.4; + color: var(--ink-2); + margin-top: 2px; +} +.report-pros { + font-size: 12px; + line-height: 1.4; + color: #2A6B3F; + margin-top: 2px; +} +.report-cons { + font-size: 12px; + line-height: 1.4; + color: #8A3E2A; + margin-top: 2px; +} + +.report-links { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 8px; +} +.report-link { + font-family: var(--font-mono); + font-size: 10px; + font-weight: 500; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--accent-2); + text-decoration: none; + border: 1px solid var(--accent-2); + padding: 4px 9px; + border-radius: var(--r-pill); + transition: background 0.12s; +} +.report-link:active { background: var(--warm); } + +/* Сравнительная таблица — accordion */ +.report-compare { + background: #fff; + border: 1px solid var(--line); + border-radius: 14px; + margin-top: 4px; + overflow: hidden; +} +.report-compare summary { + font-family: var(--font-mono); + font-size: 11px; + font-weight: 500; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--muted); + padding: 12px 14px; + cursor: pointer; + list-style: none; + display: flex; + justify-content: space-between; + align-items: center; +} +.report-compare summary::-webkit-details-marker { display: none; } +.report-compare summary::after { + content: "↓"; + font-size: 12px; + color: var(--muted); + transition: transform 0.2s; +} +.report-compare[open] summary::after { transform: rotate(180deg); } + +.report-compare table { + width: 100%; + border-collapse: collapse; + font-size: 12px; +} +.report-compare th, +.report-compare td { + padding: 8px 10px; + text-align: left; + border-top: 1px solid var(--line); + vertical-align: top; +} +.report-compare th { + font-family: var(--font-mono); + font-size: 9.5px; + font-weight: 500; + letter-spacing: 0.12em; + text-transform: uppercase; + color: var(--muted); + background: var(--warm); +} +.report-compare td strong { font-weight: 600; color: var(--ink); } + +/* Итого */ +.report-total { + padding: 14px 16px; + background: var(--ink); + color: var(--paper); + border-radius: 14px; + display: flex; + align-items: baseline; + gap: 12px; + flex-wrap: wrap; +} +.report-total .lbl { + font-family: var(--font-mono); + font-size: 10px; + font-weight: 500; + letter-spacing: 0.16em; + text-transform: uppercase; + opacity: 0.7; +} +.report-total strong { + font-family: var(--font-display); + font-style: italic; + font-size: 20px; + font-weight: 500; + letter-spacing: -0.01em; +} +.report-total .status { + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.1em; + text-transform: uppercase; + opacity: 0.7; + margin-left: auto; +} + +.report-warnings { + background: #F5E1DC; + border: 1px solid #C7705A; + border-radius: 12px; + padding: 10px 14px; + color: #8A3E2A; + font-size: 13px; + line-height: 1.5; +} +.report-warnings > div { margin: 2px 0; } diff --git a/miniapp/assets/podbor.js b/miniapp/assets/podbor.js index 393b6f3..07da6dd 100644 --- a/miniapp/assets/podbor.js +++ b/miniapp/assets/podbor.js @@ -1143,16 +1143,25 @@ const Podbor = (function () { if (data.error) { result.innerHTML = `
Ошибка: ${data.error}
`; } else { - result.innerHTML = ` + // Успех + красивый inline-отчёт под кнопкой + const headSuccess = `
${ICONS.check}
-
Подбор отправлен в чат бота
-
Лид #${(data.id || "").slice(0, 6)} · откройте Telegram
+
Подбор готов
+
Лид #${(data.id || "").slice(0, 6)} · также отправлен в Telegram
`; + result.innerHTML = headSuccess; + // Рендер отчёта (если AI вернул by_category) + if (data.ai) { + const reportNode = renderReport(data.ai, data.id || ""); + result.appendChild(reportNode); + } haptic && haptic("success"); + // Скроллим к отчёту + setTimeout(() => result.scrollIntoView({ behavior: "smooth", block: "start" }), 100); } } catch (e) { result.innerHTML = `
Сеть: ${e.message}
`; @@ -1161,6 +1170,164 @@ const Podbor = (function () { btn.textContent = "Отправить ещё раз"; } + /* ===================== Отчёт (inline, в шаге summary) ===================== */ + + function renderReport(ai, leadId) { + const summary = ai.summary || ""; + const byCat = ai.by_category || {}; + const total = ai.total_price_estimate_rub || {}; + const budgetStatus = ai.budget_status || ""; + const warnings = ai.warnings || []; + + const wrap = el(`
`); + + // Шапка + wrap.appendChild(el(` +
+
Отчёт · ${leadId.slice(0, 8)}
+ ${summary ? `

${_esc(summary)}

` : ""} +
+ `)); + + // Категории + for (const [catKey, catData] of Object.entries(byCat)) { + const catMeta = PODBOR_CATEGORIES.find(c => c.key === catKey); + const catLabel = catMeta?.label || catKey; + const catIcon = catMeta?.icon; + const models = (catData && catData.models) || []; + if (!models.length) continue; + + const catNode = el(` +
+

+ ${(catIcon && ICONS[catIcon]) || ""} + ${_esc(catLabel)} +

+
+
+ `); + const modelsWrap = catNode.querySelector(".report-models"); + + for (const m of models) { + modelsWrap.appendChild(_renderModelCard(m)); + } + + // Сравнение + if (models.length >= 2) { + modelsWrap.appendChild(_renderCompareTable(models)); + } + + wrap.appendChild(catNode); + } + + // Итого + if (total && (total.min || total.max)) { + const tmin = total.min, tmax = total.max; + const range = (tmin && tmax && tmin !== tmax) + ? `${formatRub(tmin)} — ${formatRub(tmax)} ₽` + : `${formatRub(tmin || tmax)} ₽`; + wrap.appendChild(el(` +
+ ИТОГО + ${range} + ${budgetStatus ? `${_esc(budgetStatus)}` : ""} +
+ `)); + } + + // Предупреждения + if (warnings.length) { + const wn = el(`
`); + warnings.forEach(w => wn.appendChild(el(`
⚠️ ${_esc(w)}
`))); + wrap.appendChild(wn); + } + + return wrap; + } + + function _renderModelCard(m) { + const enriched = m.enriched || {}; + const pMin = m.price_min_rub || enriched.price_min_rub; + const pMax = m.price_max_rub || enriched.price_max_rub; + const img = enriched.image_url; + const rating = enriched.rating_max; + const reviews = enriched.reviews_total; + const stores = enriched.stores_count; + + const priceHtml = (pMin && pMax && pMin !== pMax) + ? `${formatRub(pMin)}${formatRub(pMax)} ₽` + : pMin ? `${formatRub(pMin)} ₽` + : `цена уточняется`; + + const metaParts = []; + if (rating) metaParts.push(`★ ${Number(rating).toFixed(1)}`); + if (reviews) metaParts.push(`${reviews} отзыв.`); + if (stores) metaParts.push(`${stores} магазинов`); + + const links = []; + if (enriched.wb && enriched.wb.url) links.push({ label: "Wildberries", url: enriched.wb.url }); + if (enriched.yamarket && enriched.yamarket.url) links.push({ label: "Я.Маркет", url: enriched.yamarket.url }); + if (enriched.ozon && enriched.ozon.url) links.push({ label: "OZON", url: enriched.ozon.url }); + if (enriched.dns && enriched.dns.url) links.push({ label: "DNS", url: enriched.dns.url }); + + const card = el(` +
+
${img ? `` : ""}
+
+
${_esc(m.brand || "")}
+
${_esc(m.model || "")}
+ ${metaParts.length ? `
${metaParts.join(" · ")}
` : ""} +
${priceHtml}
+ ${(m.highlights || []).length ? `
✓ ${m.highlights.map(_esc).join(" · ")}
` : ""} + ${(m.pros || []).length ? `
⊕ ${m.pros.slice(0, 3).map(_esc).join(" · ")}
` : ""} + ${(m.cons || []).length ? `
⊖ ${m.cons.slice(0, 2).map(_esc).join(" · ")}
` : ""} + ${links.length ? ` + + ` : ""} +
+
+ `); + return card; + } + + function _renderCompareTable(models) { + const rows = models.map(m => { + const e = m.enriched || {}; + const p = m.price_min_rub || e.price_min_rub; + return ` + + ${_esc(m.brand || "")} ${_esc(m.model || "")} + ${p ? formatRub(p) + " ₽" : "—"} + ${e.reviews_total || "—"} + ${e.rating_max ? Number(e.rating_max).toFixed(1) : "—"} + + `; + }).join(""); + + return el(` +
+ Сравнить модели + + + + + ${rows} +
МодельЦена отОтзывов
+
+ `); + } + + function _esc(s) { + return String(s == null ? "" : s) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + /* ===================== Helpers ===================== */ function bindInputs(node) { diff --git a/miniapp/index.html b/miniapp/index.html index 996ec15..bdcc46c 100644 --- a/miniapp/index.html +++ b/miniapp/index.html @@ -12,8 +12,8 @@ - - + +
@@ -21,10 +21,10 @@
- - - - - + + + + + diff --git a/miniapp/preview-report.html b/miniapp/preview-report.html new file mode 100644 index 0000000..0f15870 --- /dev/null +++ b/miniapp/preview-report.html @@ -0,0 +1,308 @@ + + + + + + + Превью отчёта · ЗОВ + + + + + + + + +
+
Preview · Inline Report v1
+

Отчёт
после подбора

+

Как будет выглядеть страница для менеджера и клиента после нажатия «Отправить». Mock-данные.

+ +
+
+ + + + + +