feat: Добавление компонентов ErrorBoundary и NetworkErrorHandler для улучшения обработки ошибок в приложении. Увеличение таймаута запросов до 30 секунд и улучшение логики повторных попыток с детализированными уведомлениями об ошибках.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m43s

This commit is contained in:
2025-10-03 15:49:28 +07:00
parent 8954c58744
commit cd6a971991
12 changed files with 2694 additions and 33 deletions
@@ -0,0 +1,218 @@
/**
* Примеры использования новой системы обработки ошибок
* Этот файл демонстрирует различные сценарии использования
*/
import { useState } from 'react';
import { useErrorHandler } from '../hooks/useErrorHandler';
import RetryButton from '../components/RetryButton';
import api from '../lib/api';
/**
* Пример 1: Простая обработка ошибок в компоненте
*/
function SimpleErrorHandlingExample() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const { handleError, handleSuccess, withErrorHandler } = useErrorHandler();
// Вариант 1: Ручная обработка ошибок
const fetchDataManual = async () => {
setLoading(true);
try {
const response = await api.get('/domains-new');
setData(response.data);
handleSuccess('Данные успешно загружены');
} catch (error) {
handleError(error, { component: 'SimpleErrorHandlingExample' });
} finally {
setLoading(false);
}
};
// Вариант 2: Автоматическая обработка с withErrorHandler
const fetchDataAuto = withErrorHandler(
async () => {
setLoading(true);
const response = await api.get('/domains-new');
setData(response.data);
setLoading(false);
},
{
successMessage: 'Данные успешно загружены',
context: { component: 'SimpleErrorHandlingExample' }
}
);
return (
<div className="card">
<div className="card-header">
<h3 className="card-title">Пример простой обработки ошибок</h3>
</div>
<div className="card-body">
<div className="btn-list">
<button
className="btn btn-primary"
onClick={fetchDataManual}
disabled={loading}
>
Загрузить (ручная обработка)
</button>
<button
className="btn btn-primary"
onClick={fetchDataAuto}
disabled={loading}
>
Загрузить (автоматическая обработка)
</button>
</div>
{data && (
<div className="mt-3">
<p>Загружено записей: {data.items?.length || 0}</p>
</div>
)}
</div>
</div>
);
}
/**
* Пример 2: Использование RetryButton
*/
function RetryButtonExample() {
const [attempts, setAttempts] = useState(0);
const { handleError, handleSuccess } = useErrorHandler();
const unreliableOperation = async () => {
setAttempts(prev => prev + 1);
// Симуляция операции которая иногда падает
if (Math.random() < 0.5) {
throw new Error('Случайная ошибка для демонстрации');
}
handleSuccess('Операция выполнена успешно!');
};
return (
<div className="card">
<div className="card-header">
<h3 className="card-title">Пример использования RetryButton</h3>
</div>
<div className="card-body">
<p>Попыток: {attempts}</p>
<RetryButton
onRetry={unreliableOperation}
className="btn btn-primary"
>
Выполнить ненадежную операцию
</RetryButton>
</div>
</div>
);
}
/**
* Пример 3: Обработка различных типов ошибок
*/
function ErrorTypesExample() {
const { handleError } = useErrorHandler();
const triggerNetworkError = () => {
api.get('/nonexistent-endpoint')
.catch(error => handleError(error));
};
const triggerValidationError = () => {
api.post('/domains-new', { invalid: 'data' })
.catch(error => handleError(error));
};
const triggerTimeoutError = () => {
// Создаем запрос который точно превысит таймаут
const slowApi = api.create({ timeout: 100 });
slowApi.get('/domains-new')
.catch(error => handleError(error));
};
return (
<div className="card">
<div className="card-header">
<h3 className="card-title">Типы ошибок</h3>
</div>
<div className="card-body">
<div className="btn-list">
<button
className="btn btn-danger"
onClick={triggerNetworkError}
>
Сетевая ошибка
</button>
<button
className="btn btn-warning"
onClick={triggerValidationError}
>
Ошибка валидации
</button>
<button
className="btn btn-info"
onClick={triggerTimeoutError}
>
Таймаут
</button>
</div>
</div>
</div>
);
}
/**
* Главный компонент с примерами
*/
function ErrorHandlingExample() {
return (
<div className="container-xl">
<div className="page-header">
<h1 className="page-title">Примеры обработки ошибок</h1>
<p className="text-muted">
Демонстрация возможностей новой системы обработки ошибок
</p>
</div>
<div className="row row-cards">
<div className="col-12">
<SimpleErrorHandlingExample />
</div>
<div className="col-12">
<RetryButtonExample />
</div>
<div className="col-12">
<ErrorTypesExample />
</div>
</div>
<div className="card mt-3">
<div className="card-header">
<h3 className="card-title">Особенности новой системы</h3>
</div>
<div className="card-body">
<ul>
<li> <strong>Автоматический retry</strong> для GET запросов (до 3 попыток)</li>
<li> <strong>Умный retry</strong> для POST/PUT/DELETE при сетевых ошибках</li>
<li> <strong>Exponential backoff</strong> с jitter для избежания thundering herd</li>
<li> <strong>User-friendly сообщения</strong> вместо технических ошибок</li>
<li> <strong>Детальная информация</strong> для отладки (requestId, timestamp, детали)</li>
<li> <strong>Offline/Online detection</strong> с уведомлениями</li>
<li> <strong>Error Boundary</strong> для ловли React ошибок рендеринга</li>
<li> <strong>Типизация ошибок</strong> (Network, Timeout, Server, Client, etc.)</li>
<li> <strong>Интеграция с системой уведомлений</strong></li>
<li> <strong>Логирование критичных ошибок</strong></li>
</ul>
</div>
</div>
</div>
);
}
export default ErrorHandlingExample;