GH GambleHub

Обробка помилок та UX-пояснення

1) Навіщо це важливо

Помилка - це не «червоний текст», а продовження сценарію. Хороший UX помилок:
  • пояснює що сталося і що робити далі,
  • зберігає введені дані та запобігає втраті прогресу,
  • дає безпечний повтор або альтернативний шлях,
  • залишається доступним (SR/клавіатура) і не розкриває зайвого.

2) Типологія помилок (для інтерфейсу)

1. Валідація даних (4xx client): порожні/невірні поля, формат, довжина, конфлікт правил.
2. Бізнес-правила: ліміти, гео-обмеження, KYC/KYB, дублі, недоступні слоти.
3. Права/пермісії: роль, доступ до ресурсу, вікові обмеження.
4. Мережа/сервер: таймаут, оффлайн, 5xx, перевантаження, rate limit.
5. Конфлікти/стан: 409/412 (дані змінилися), гонки, блокування.
6. Відсутність ресурсу: 404/410, видалено/перенесено.
7. Платіжні та ризикові: відхилення банком/PSP, антифрод, ліміти відповідальної гри.

3) Канали і рівень відображення

Підбираємо «гучність» під контекст:
КаналКолиПриклад
Inline біля поляВалідація/підказка«Мінімум 8 символів»
Під блоком/формоюПомилка кроку"Не вдалося зберегти. Спробуйте ще раз"
Тост (role = status)Неблокуючі події«Файл завантажений з помилками, подробиці внизу»
Банер на сторінціВажливі, але не блокують навігацію"Оффлайн. Показано кешовані дані"
Модалка (role = alertdialog)Блокуючі ризикові кроки"Сесія минула. Увійдіть знову"
Сторінка помилки404/5xx, критичні падіння«Сторінка не знайдена »/« Вибачте, збій на нашому боці»

Правило: не ховайте критичне в тости/hover. Там, де користувач дивиться, там і повідомлення.

4) Копірайтинг помилок

Структура: причина → наслідок → дія.
Тон: чесний, нейтральний, без провини.
Конкретика: вкажіть поле/умову, уникайте кодів і стеків.
Кнопка-дія: «Повторити», «Змінити карту», «Скинути фільтри», «Відкрити чат».
Чутливі дані: не показувати (маскування PAN, особисті атрибути).

Приклади

Добре: "Платіж не пройшов: банк відхилив операцію. Спробуйте інший спосіб або повторіть пізніше".
Погано: "Помилка 500. Щось пішло не так".
Добре: "Ліміт денних витрат досягнуто. Встановіть новий ліміт або спробуйте завтра".
Добре: "Файл занадто великий (макс. 25 МБ). Спаліть або завантажте кілька файлів".

5) Поведінка і фокус (A11y)

Помилка виводиться у фокусний контекст: перекладаємо фокус до першого помилкового поля.
Живі регіони: 'role = «status»'( polite) для інфо,'role = «alert»'( assertive) - для критичних.
Видиме':focus-visible', контраст ≥ AA, альтернативи кольору (іконка/текст).
Повідомлення прив'язуємо до поля через'aria-describedby'.

html
<label for = "pwd "> Password </label>
<input id="pwd" name="password" aria-describedby="pwd-err" aria-invalid="true">
<p id = "pwd-err" role = "alert"> Minimum 8 characters </p>

6) Ретраї, backoff і ідемпотентність

Повтор пропонується, якщо шанс успіху є (мережеві збої, 5xx, rate limit).
Експоненціальний backoff 1-2-4-8 с, ліміт спроб, зрозуміла кнопка «Повторити».
Критичні операції (ставки/платежі): обов'язковий Idempotency-Key → виключаємо дублі.
Відкати оптимістичних оновлень - чітке візуальне повернення + пояснення.

js async function retry(fn, attempts=3){
let wait=1000; for(let i=0; i<attempts; i++){
try{ return await fn(); }catch(e){ if(i===attempts-1) throw e; await new Promise(r=>setTimeout(r,wait)); wait=2; }
}
}

7) Оффлайн, таймаути і частковий контент

Оффлайн: показуємо банер «Немає підключення», доступ до кешу (read-only), чергу синхронізації.
Таймаути: UI-таймаут (3-5 с) → стан «Очікуємо підтвердження»... з безпечним повтором/скасуванням.
Частковий успіх: зберігаємо те, що вдалося; маркуємо «не синхронізовано».

8) Конфлікти і конкурентність

409/412: дані застаріли. Запропонувати «Оновити» і показати дифф (що змінилося).
Блокування: інформуємо, хто тримає блок, і скільки часу, кнопка «Запросити доступ».

9) Зразки UI-шаблонів

Банер сторінки:
html
<div class="banner banner--error" role="alert">
<strong> Connection failed. </strong> Shows cached data.
<button class =" btn btn--ghost" id = "retry "> Retry </button>
</div>
Модалка критичної помилки:
html
<div role="alertdialog" aria-labelledby="err-title" aria-describedby="err-desc">
<h2 id = "err-title "> Session expired </h2>
<p id = "err-desc "> Sign in again to continue. </p>
<button class = "btn "> Sign in </button>
<button class =" btn btn--ghost"> Home </button>
</div>
React ErrorBoundary (з ID кореляції):
tsx function Fallback({ id, onRetry }: { id: string; onRetry: ()=>void }) {
return (
<div role="alert" className="banner banner--error">
<strong> We couldn't load the page. </strong>
<div> Try again. Код: <code>{id}</code> <button onClick={()=>navigator. clipboard. writeText (id)}> Copy </button> </div>
<button onClick = {onRetry}> Retry </button>
</div>
);
}

10) Токени помилок (дизайн-система)

json
{
"error": {
"tones": { "danger": "#", "warning": "#", "info": "#" },
"aria": { "polite": true, "assertive": true },
"timing": { "toastMs": 3500, "retryBackoffMs": [1000,2000,4000] },
"layout": { "fieldGap": 8, "bannerIcon": 20 }
}
}
CSS-пресети:
css
.banner--error { background: var(--bg-danger); color: var(--on-danger); padding: 12px 16px; border-radius: 12px; }
.field-error { color: var(--role-danger); margin-top: 6px; font-size:.875rem; }

11) Безпека і приватність

Не виводимо стек-трейси, внутрішні ID, шляхи БД.
Маскуємо чутливі значення (карти, документи).
Повідомлення не повинні підказувати зловмиснику (наприклад, що аккаунт існує).
Для підтримки - ID кореляції замість деталей.

Структурований лог (бекенд):
json
{"level":"error","event":"payment_fail","correlation_id":"c-8f1...","user_id":"u-","route":"/pay","psp_code":"DO_NOT_EXPOSE_TO_USER"}

12) Метрики та контроль

INP і частка Long Tasks в момент помилки (помилка не повинна «вішати» UI).
Retry success rate, помилки на 1000 дій, час до відновлення.
CTR на «Допомога/Чат», відсоток кинутих форм.
Теплові карти: де найчастіше виникають field-errors.

13) QA-чек-лист

Доступність

  • Фокус на першому помилковому полі;'aria-describedby '/' aria-invalid'виставлені.
  • Критичні повідомлення -'role = «alert»'; контраст ≥ AA.

Поведінка

  • Дані форми не втрачаються при помилці.
  • Є зрозумілий'Retry'і коректний backoff.
  • Оффлайн-режим/кеш працюють; банер бачимо.

Копірайтинг

  • Причина → дія; без технічного жаргону і звинувачень.
  • Тексти локалізуються і не ламають сітку.

Безпека

  • Немає витоку PII/секретів; показуємо тільки безпечні коди/ID.
  • Ідемпотентність включена для критичних операцій.

14) Специфіка iGaming

Ставка:
  • UI відразу фіксує'busy'; при затримці> 3 с - «Очікуємо підтвердження»....
  • При fail: чесний статус («ринок закрився», «коефіцієнт змінився») + безпечний'Retry'.
  • Ідемпотентний ключ, щоб виключити подвійну ставку.
Платіж/висновок:
  • Розрізняємо «відмова банку/PSP» vs «збій сервера». Для першого - «Виберіть інший спосіб», для другого - «Retry».
  • Прозорі кроки KYC/AML; посилання "Чому це потрібно? ».
Відповідальна гра та ліміти:
  • Тон турботливий, без тиску. «Ліміт досягнуто - зробіть паузу або оновіть ліміт».
  • Без спалахів/неону; контраст AAA, доступність при SR.
Гео/ліцензія:
  • Чітко поясніть обмеження і запропонуйте «Ознайомитися з правилами/підтримка».

15) Анти-патерни

«Щось пішло не так» без дій і контексту.
Скидання форми після помилки.
Ховати критичне в тост на 3 секунди.
Тільки колір без тексту/іконки.
Нескінченні ретраї без можливості скасування.
Показувати внутрішні коди/стек-трейси.

16) Документація в дизайн-системі

Компоненти: `FieldError`, `FormError`, `PageBanner`, `AlertDialog`, `ErrorBoundary`.
Токени тонів/контрасту/таймінгів, пресети a11y і приклади ARIA.
Карта типових сценаріїв (валідація, мережа, права, платежі) з текстовими шаблонами.
«Do/Don’t»: реальні екрани до/після з метриками відмов/успіхів.

Коротке резюме

Робіть помилки зрозумілими і керованими: говоріть людською мовою, зберігайте введені дані, пропонуйте безпечний повтор і альтернативи, поважайте доступність і приватність. Тоді навіть позаштатні ситуації зберігають довіру і не переривають шлях користувача - особливо в критичних сценаріях ставок і платежів.

Contact

Зв’яжіться з нами

Звертайтеся з будь-яких питань або за підтримкою.Ми завжди готові допомогти!

Розпочати інтеграцію

Email — обов’язковий. Telegram або WhatsApp — за бажанням.

Ваше ім’я необов’язково
Email необов’язково
Тема необов’язково
Повідомлення необов’язково
Telegram необов’язково
@
Якщо ви вкажете Telegram — ми відповімо й там, додатково до Email.
WhatsApp необов’язково
Формат: +код країни та номер (наприклад, +380XXXXXXXXX).

Натискаючи кнопку, ви погоджуєтесь на обробку даних.