From f1b7f71337bf60763a46ae2f50dbac1285c69a5f Mon Sep 17 00:00:00 2001 From: wasrusgen Date: Tue, 19 May 2026 08:02:09 +0300 Subject: [PATCH] fix(podbor): HTML AI output + home button on all steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AI prompt: use HTML tags (b, em, br, li) instead of markdown - renderReport: _ai() helper renders AI text as innerHTML (safe, backend-controlled) - Header: added podbor-home button (top-right) → goes to main menu from any step - After successful submit: show "← Вернуться в главное меню" button immediately - Fixes: no way to leave podbor after result was received Co-Authored-By: Claude Sonnet 4.6 --- backend-py/app/ai.py | 7 ++++- miniapp/assets/podbor.css | 9 ++++-- miniapp/assets/podbor.js | 62 +++++++++++++++++++++++++++++---------- miniapp/index.html | 4 +-- 4 files changed, 62 insertions(+), 20 deletions(-) diff --git a/backend-py/app/ai.py b/backend-py/app/ai.py index 18f90c1..2b0b180 100644 --- a/backend-py/app/ai.py +++ b/backend-py/app/ai.py @@ -128,7 +128,12 @@ SYSTEM_PROMPT_PICKER = ( "Количество моделей по категории определяется параметром `checklist.model_count` (3 / 5 / 7) — соблюдай!\n" "Каждая модель ДОЛЖНА содержать аналитику: pros (минимум 3), cons (минимум 2), почему выбрана, с чем сравнивать.\n" "По КАЖДОЙ категории напиши `analysis` — обзор: какие компромиссы, на что обратить внимание.\n" - "Валидный JSON без markdown, без ```:\n" + "Валидный JSON без markdown, без ```.\n" + "Для текстовых полей (summary, analysis, reasoning, элементы pros[], cons[], highlights[], next_steps[]) используй HTML-разметку:\n" + " число или ключевой термин — выделение,
— перенос строки, — курсив.\n" + " НЕ используй markdown (**текст**, *текст*, ## заголовки) — только HTML.\n" + " pros/cons/highlights — массивы строк с HTML внутри.\n\n" + "Структура ответа:\n" "{\n" ' "summary": "2-3 предложения общего вывода: что подобрали, почему этот набор, на чём сэкономили / куда вложились",\n' ' "by_category": {\n' diff --git a/miniapp/assets/podbor.css b/miniapp/assets/podbor.css index 0e0978b..e89d848 100644 --- a/miniapp/assets/podbor.css +++ b/miniapp/assets/podbor.css @@ -10,7 +10,8 @@ margin-bottom: var(--s4); } -.podbor-back { +.podbor-back, +.podbor-home { width: 28px; height: 28px; display: grid; @@ -19,7 +20,11 @@ cursor: pointer; } -.podbor-back svg { width: 20px; height: 20px; } +.podbor-back svg, +.podbor-home svg { width: 20px; height: 20px; } + +.podbor-home { color: var(--muted); opacity: 0.7; transition: opacity .15s; } +.podbor-home:hover { opacity: 1; } .podbor-title { font-family: var(--font-mono); diff --git a/miniapp/assets/podbor.js b/miniapp/assets/podbor.js index a2335a4..81bdc04 100644 --- a/miniapp/assets/podbor.js +++ b/miniapp/assets/podbor.js @@ -97,24 +97,31 @@ const Podbor = (function () { /* ===================== Header & progress ===================== */ + function _goHome() { + location.hash = ""; + if (typeof routeByHash === "function") routeByHash(); + } + function renderHeader() { const h = el(`
Подбор техники
-
+
`); h.querySelector(".podbor-back").addEventListener("click", () => { const idx = STEPS.indexOf(currentStep); if (idx <= 0) { - // Выход из подбора в главный экран кабинета — без перезагрузки (иначе сплэш мигает) - location.hash = ""; - if (typeof routeByHash === "function") routeByHash(); + _goHome(); } else { go(STEPS[idx - 1]); } }); + h.querySelector(".podbor-home").addEventListener("click", () => { + haptic && haptic("impact"); + _goHome(); + }); return h; } @@ -1277,6 +1284,17 @@ const Podbor = (function () { `; result.innerHTML = headSuccess; + // Кнопка "Вернуться в главное" сразу после успеха + const homeBtn = el(` +
+ +
+ `); + homeBtn.querySelector("button").addEventListener("click", () => { + haptic && haptic("impact"); + _goHome(); + }); + result.appendChild(homeBtn); // Рендер отчёта (если AI вернул by_category) if (data.ai) { const reportNode = renderReport(data.ai, data.id || ""); @@ -1305,12 +1323,14 @@ const Podbor = (function () { const wrap = el(`
`); // Шапка - wrap.appendChild(el(` -
-
Отчёт · ${leadId.slice(0, 8)}
- ${summary ? `

${_esc(summary)}

` : ""} -
- `)); + const headNode = el(`
Отчёт · ${leadId.slice(0, 8)}
`); + if (summary) { + const sumP = document.createElement("p"); + sumP.className = "report-summary"; + sumP.innerHTML = _ai(summary); + headNode.appendChild(sumP); + } + wrap.appendChild(headNode); // Категории for (const [catKey, catData] of Object.entries(byCat)) { @@ -1327,9 +1347,12 @@ const Podbor = (function () { ${(catIcon && ICONS[catIcon]) || ""} ${_esc(catLabel)} - ${catAnalysis ? `
${_esc(catAnalysis)}
` : ""} + ${catAnalysis ? `
` : ""} `); + if (catAnalysis) { + catNode.querySelector(".report-cat-analysis").innerHTML = _ai(catAnalysis); + } // Сравнение цен — основной блок, всегда вверху const matrixNode = _renderPriceMatrix(models); @@ -1513,25 +1536,25 @@ ${reportEl.outerHTML}
${_esc(m.model || "")}
${metaParts.length ? `
${metaParts.join(" · ")}
` : ""}
${priceHtml}
- ${(m.highlights || []).length ? `
✓ ${m.highlights.map(_esc).join(" · ")}
` : ""} + ${(m.highlights || []).length ? `
✓ ${m.highlights.map(_ai).join(" · ")}
` : ""} ${(m.pros || []).length ? `
Плюсы
-
    ${m.pros.slice(0, 4).map(p => `
  • ${_esc(p)}
  • `).join("")}
+
    ${m.pros.slice(0, 4).map(p => `
  • ${_ai(p)}
  • `).join("")}
` : ""} ${(m.cons || []).length ? `
Минусы
-
    ${m.cons.slice(0, 3).map(c => `
  • ${_esc(c)}
  • `).join("")}
+
    ${m.cons.slice(0, 3).map(c => `
  • ${_ai(c)}
  • `).join("")}
` : ""} ${_renderSpecsBlock(m.specs || {})} - ${m.reasoning ? `
💡 ${_esc(m.reasoning)}
` : ""} + ${m.reasoning ? `
💡 ${_ai(m.reasoning)}
` : ""} ${_renderUtilityLinks(m)} @@ -1725,6 +1748,15 @@ ${reportEl.outerHTML} .replace(/'/g, "'"); } + /* AI-generated text — trusted backend output, render as HTML. + Strip script/on* to be safe, but allow

  • . */ + function _ai(s) { + if (s == null) return ""; + return String(s) + .replace(//gi, "") + .replace(/\son\w+\s*=/gi, " data-stripped="); + } + /* ===================== Helpers ===================== */ function bindInputs(node) { diff --git a/miniapp/index.html b/miniapp/index.html index 89d6f89..1438a84 100644 --- a/miniapp/index.html +++ b/miniapp/index.html @@ -13,7 +13,7 @@ - + @@ -39,7 +39,7 @@ - +