diff --git a/docs/site-fixes-2026-10-11/REPORT.md b/docs/site-fixes-2026-10-11/REPORT.md index f4d2f46..a4fd0ba 100644 --- a/docs/site-fixes-2026-10-11/REPORT.md +++ b/docs/site-fixes-2026-10-11/REPORT.md @@ -208,3 +208,45 @@ net8.0` (не `net8.0-windows`) + Avalonia (кроссплатформенный Скриншоты: `k3-pricing-1280.png`, `k3-pricing-390.png`, `k3-about-1280.png`. Автопроверка (Playwright): 5/5. Юридические реквизиты не трогал (отложены до продакшна). Прод-сайт не трогал. + +--- + +## Круг 4 (прогон этапа 1 в Safari на iPhone 17, ширина 402 — 12.10) + +Круг 3 закрыл разрыв цены **только на /pricing**. Прогон в Safari (симулятор +iPhone 17) нашёл ещё два места, где «сумма + ₸ + /мес» рвётся по строкам: + +1. **Главная, строка под кнопками героя** — «Подписка от 5 000 ₸/мес»: «5» на + одной строке, «000 ₸/мес» на следующей. +2. **/signup после конфигуратора «Бизнес»** — итог «14 000 ₸/мес» рвался после + «₸/» (слэш — точка переноса), плитки тарифов — так же. + +Исправлено **системно**, а не точечно (commit `a0f9e0b`, деплой только на тест +`test.food-market.kz`, статус `OK`): + +- **`lib/pricing.ts` — единый источник правды:** + - `fmtKzt` теперь ставит неразрывные пробелы (U+00A0) между разрядами и перед + «₸» → сумма с «₸» никогда не рвётся; + - новый `fmtKztMonth` = сумма + «₸» + «/мес» одним куском: word-joiner (U+2060) + вокруг «/» запрещает перенос на слэше. +- **`SignupForm.tsx`** — плитки «Старт»/«Бизнес» и итог «Ваша конфигурация + „Бизнес“» используют `fmtKztMonth` (+ `whitespace-nowrap` на итоге). +- **`index.astro`** — hero-строка и карточки «Старт»/«Бизнес» (`whitespace-nowrap` + + ` `); «от» в «Бизнес» остаётся переносимым, цена — нет. +- **`BusinessTariffBuilder.tsx`** — подпись «База 10 000 ₸» обёрнута в `nowrap`. +- **Контент `kb/billing.md`, `kb/faq.md`, `blog/launch.md`, `FAQ.tsx`** — суммы + с nbsp + word-joiner, чтобы цена не рвалась и в тексте статей/ответов. + +**Автопроверка (Playwright, Safari-UA iPhone 17):** ширины **320, 375, 390, 402** +× страницы **главная, /pricing, /signup?plan=start, /signup после «Бизнес»** — +**44/44 passed**. Высоты строк цен: hero 15px, плитки 16px, итог «Бизнес» 20px, +конструктор 36px — везде **одна строка**. Горизонтальной прокрутки нет ни на одной +ширине. Текст итога «Бизнес» = `14 000 ₸⁠/⁠мес` (неразрывный). + +Скриншоты (`docs/site-fixes-2026-10-11/`): `k4-home-{320,375,390,402}.png`, +`k4-pricing-*.png`, `k4-signup-start-*.png`, `k4-signup-biz-*.png`. +Скрипт: `tests/e2e/tools/site-fixes-verify-4.mjs`. + +Остальное в прогоне владельца чисто (меню, «Назад», тарифы, выбор +Старт/Бизнес/Сеть, нет горизонтальной прокрутки на 25 страницах) — не трогал. +Юридические реквизиты не трогал. Прод-сайт не трогал. Весы — пауза в силе. diff --git a/docs/site-fixes-2026-10-11/k4-home-320.png b/docs/site-fixes-2026-10-11/k4-home-320.png new file mode 100644 index 0000000..eba0b4e Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-home-320.png differ diff --git a/docs/site-fixes-2026-10-11/k4-home-375.png b/docs/site-fixes-2026-10-11/k4-home-375.png new file mode 100644 index 0000000..f9cdfc9 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-home-375.png differ diff --git a/docs/site-fixes-2026-10-11/k4-home-390.png b/docs/site-fixes-2026-10-11/k4-home-390.png new file mode 100644 index 0000000..54098ad Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-home-390.png differ diff --git a/docs/site-fixes-2026-10-11/k4-home-402.png b/docs/site-fixes-2026-10-11/k4-home-402.png new file mode 100644 index 0000000..1980646 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-home-402.png differ diff --git a/docs/site-fixes-2026-10-11/k4-pricing-320.png b/docs/site-fixes-2026-10-11/k4-pricing-320.png new file mode 100644 index 0000000..fc7c5b2 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-pricing-320.png differ diff --git a/docs/site-fixes-2026-10-11/k4-pricing-375.png b/docs/site-fixes-2026-10-11/k4-pricing-375.png new file mode 100644 index 0000000..7170cbb Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-pricing-375.png differ diff --git a/docs/site-fixes-2026-10-11/k4-pricing-390.png b/docs/site-fixes-2026-10-11/k4-pricing-390.png new file mode 100644 index 0000000..0060c4f Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-pricing-390.png differ diff --git a/docs/site-fixes-2026-10-11/k4-pricing-402.png b/docs/site-fixes-2026-10-11/k4-pricing-402.png new file mode 100644 index 0000000..a687efe Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-pricing-402.png differ diff --git a/docs/site-fixes-2026-10-11/k4-signup-biz-320.png b/docs/site-fixes-2026-10-11/k4-signup-biz-320.png new file mode 100644 index 0000000..8d4bade Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-signup-biz-320.png differ diff --git a/docs/site-fixes-2026-10-11/k4-signup-biz-375.png b/docs/site-fixes-2026-10-11/k4-signup-biz-375.png new file mode 100644 index 0000000..c41ae61 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-signup-biz-375.png differ diff --git a/docs/site-fixes-2026-10-11/k4-signup-biz-390.png b/docs/site-fixes-2026-10-11/k4-signup-biz-390.png new file mode 100644 index 0000000..349c15d Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-signup-biz-390.png differ diff --git a/docs/site-fixes-2026-10-11/k4-signup-biz-402.png b/docs/site-fixes-2026-10-11/k4-signup-biz-402.png new file mode 100644 index 0000000..053e4ed Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-signup-biz-402.png differ diff --git a/docs/site-fixes-2026-10-11/k4-signup-start-320.png b/docs/site-fixes-2026-10-11/k4-signup-start-320.png new file mode 100644 index 0000000..5856589 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-signup-start-320.png differ diff --git a/docs/site-fixes-2026-10-11/k4-signup-start-375.png b/docs/site-fixes-2026-10-11/k4-signup-start-375.png new file mode 100644 index 0000000..c504539 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-signup-start-375.png differ diff --git a/docs/site-fixes-2026-10-11/k4-signup-start-390.png b/docs/site-fixes-2026-10-11/k4-signup-start-390.png new file mode 100644 index 0000000..feeb113 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-signup-start-390.png differ diff --git a/docs/site-fixes-2026-10-11/k4-signup-start-402.png b/docs/site-fixes-2026-10-11/k4-signup-start-402.png new file mode 100644 index 0000000..1cc67f5 Binary files /dev/null and b/docs/site-fixes-2026-10-11/k4-signup-start-402.png differ diff --git a/tests/e2e/tools/site-fixes-verify-4.mjs b/tests/e2e/tools/site-fixes-verify-4.mjs new file mode 100644 index 0000000..d2b3f27 --- /dev/null +++ b/tests/e2e/tools/site-fixes-verify-4.mjs @@ -0,0 +1,75 @@ +// Круг 4: проверка, что «сумма + ₸ + /мес» нигде не разрывается по строкам. +// Ширины из прогона владельца в Safari: 320, 375, 390, 402. +import { chromium } from '@playwright/test' +import { mkdirSync } from 'node:fs' +import { resolve } from 'node:path' + +const BASE = process.env.SITE_URL ?? 'https://test.food-market.kz' +const OUT = resolve('/home/nns/food-market', 'docs/site-fixes-2026-10-11') +mkdirSync(OUT, { recursive: true }) +const WIDTHS = [320, 375, 390, 402] +const results = [] +const ok = (n, c, d = '') => { results.push({ n, c: !!c, d }); console.log(`${c ? '✓' : '✗'} ${n} ${d}`) } + +// высота одной строки: text-xs ~16px, text-3xl ~36px, text-sm ~20px. Один ряд => ниже порога. +const oneLine = (box, max) => box && box.height > 0 && box.height <= max + +const b = await chromium.launch({ args: ['--ignore-certificate-errors'] }) + +for (const w of WIDTHS) { + const ctx = await b.newContext({ + viewport: { width: w, height: 900 }, deviceScaleFactor: 2, + ignoreHTTPSErrors: true, isMobile: true, hasTouch: true, + userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1', + }) + const p = await ctx.newPage() + + // нет горизонтальной прокрутки + const noHScroll = async (label) => { + const over = await p.evaluate(() => document.documentElement.scrollWidth - document.documentElement.clientWidth) + ok(`${label}@${w}: нет горизонтальной прокрутки`, over <= 1, `overflow=${over}`) + } + + // ---- Главная ---- + await p.goto(`${BASE}/`, { waitUntil: 'networkidle' }); await p.waitForTimeout(400) + // hero-строка: span с «5 000 ₸/мес» + const hero = await p.locator('p:has-text("Подписка от") span').first().boundingBox().catch(() => null) + ok(`home@${w}: hero «5 000 ₸/мес» одной строкой`, oneLine(hero, 24), hero ? `h=${Math.round(hero.height)}` : 'нет') + // карточки тарифов: nowrap-спаны внутри цен + const startCard = await p.locator('section:has(h2:has-text("Простые тарифы")) p.text-3xl').first().boundingBox().catch(() => null) + ok(`home@${w}: карточка «Старт» цена одной строкой`, oneLine(startCard, 48), startCard ? `h=${Math.round(startCard.height)}` : 'нет') + await p.screenshot({ path: resolve(OUT, `k4-home-${w}.png`), fullPage: true }) + await noHScroll('home') + + // ---- /pricing ---- + await p.goto(`${BASE}/pricing`, { waitUntil: 'networkidle' }); await p.waitForTimeout(600) + const prBuilder = await p.locator('#configurator .font-extrabold').first().boundingBox().catch(() => null) + ok(`pricing@${w}: сумма конструктора одной строкой`, oneLine(prBuilder, 48), prBuilder ? `h=${Math.round(prBuilder.height)}` : 'нет') + await p.screenshot({ path: resolve(OUT, `k4-pricing-${w}.png`), fullPage: true }) + await noHScroll('pricing') + + // ---- /signup?plan=start ---- + await p.goto(`${BASE}/signup?plan=start`, { waitUntil: 'networkidle' }); await p.waitForTimeout(600) + const tileStart = await p.locator('label:has-text("Старт") .whitespace-nowrap').first().boundingBox().catch(() => null) + ok(`signup-start@${w}: плитка «Старт» цена одной строкой`, oneLine(tileStart, 24), tileStart ? `h=${Math.round(tileStart.height)}` : 'нет') + await p.screenshot({ path: resolve(OUT, `k4-signup-start-${w}.png`), fullPage: true }) + await noHScroll('signup-start') + + // ---- /signup после конфигуратора «Бизнес» (14 000 ₸/мес) ---- + await p.goto(`${BASE}/signup?plan=business&shops=3&cashes=1&warehouses=1&employees=5`, { waitUntil: 'networkidle' }); await p.waitForTimeout(700) + const sumBiz = await p.locator('span.font-bold.whitespace-nowrap').first().boundingBox().catch(() => null) + ok(`signup-biz@${w}: итог «14 000 ₸/мес» одной строкой`, oneLine(sumBiz, 24), sumBiz ? `h=${Math.round(sumBiz.height)}` : 'нет') + const tileBiz = await p.locator('label:has-text("Бизнес") .whitespace-nowrap').first().boundingBox().catch(() => null) + ok(`signup-biz@${w}: плитка «Бизнес» цена одной строкой`, oneLine(tileBiz, 24), tileBiz ? `h=${Math.round(tileBiz.height)}` : 'нет') + // проверим, что текст действительно «14 000 ₸/мес» + const bizTxt = await p.locator('span.font-bold.whitespace-nowrap').first().innerText().catch(() => '') + ok(`signup-biz@${w}: сумма = 14 000 ₸/мес`, /14[\s ]000[\s ]₸.?\/.?мес/.test(bizTxt), JSON.stringify(bizTxt)) + await p.screenshot({ path: resolve(OUT, `k4-signup-biz-${w}.png`), fullPage: true }) + await noHScroll('signup-biz') + + await ctx.close() +} +await b.close() +const pass = results.filter(r => r.c).length +console.log(`\n[site-verify-4] ${pass}/${results.length} passed → ${OUT}`) +process.exit(pass === results.length ? 0 : 1)