9.4 KiB
9.4 KiB
Profesyonel Hata Yönetimi ve Loading States Dokümantasyonu
Genel Bakış
PlannerPage.tsx için enterprise-grade hata yönetimi ve kullanıcı deneyimi iyileştirmeleri uygulanmıştır.
Özellikler
1. Kategorize Edilmiş Hata Yönetimi
Hata Tipleri
- NetworkError: İnternet bağlantısı yok
- TimeoutError: API 30 saniyeden uzun sürdü
- ValidationError: Backend validasyon hatası
- ServerError: 500+ sunucu hataları
- RateLimitError: Çok fazla istek (429)
- UnknownError: Beklenmeyen hatalar
Kullanıcı Dostu Mesajlar
Her hata tipi için özel Türkçe mesajlar:
{
network: "İnternet bağlantınızı kontrol edin",
timeout: "İşlem uzun sürdü, lütfen tekrar deneyin",
validation: "Backend'den gelen mesajı göster",
server: "Bir hata oluştu, ekibimiz bilgilendirildi",
ratelimit: "Çok fazla istek gönderildi, lütfen bekleyin"
}
2. Retry Mekanizması
Exponential Backoff
- Maksimum 3 retry denemesi
- İlk deneme: 1 saniye bekle
- İkinci deneme: 2 saniye bekle
- Üçüncü deneme: 4 saniye bekle
- Maksimum bekleme: 30 saniye
Retry Kuralları
- ✅ Network hataları retry edilir
- ✅ Timeout hataları retry edilir
- ✅ 5xx server hataları retry edilir
- ❌ Validation hataları retry edilmez
- ❌ Rate limit hataları retry edilmez
Kullanıcı Bildirimi
toast.warning(`Yeniden deneniyor... (${attempt}/3)`, {
description: apiError.userMessage,
});
3. Multi-Step Loading States
Loading Aşamaları
const LOADING_STEPS = [
{ label: 'Form hazırlanıyor...', progress: 0 },
{ label: 'Rotanız oluşturuluyor...', progress: 33 },
{ label: 'Mekanlar belirleniyor...', progress: 66 },
{ label: 'Son kontroller yapılıyor...', progress: 90 },
];
Progress Bar
- Görsel feedback ile kullanıcı bilgilendirmesi
- Tahmini süre gösterimi (~10 saniye)
- Retry counter (1/3, 2/3, 3/3)
4. İptal (Cancel) Özelliği
AbortController Kullanımı
const abortControllerRef = useRef<AbortController | null>(null);
// İşlemi başlat
abortControllerRef.current = new AbortController();
// İptal et
const handleCancel = () => {
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
setLoading(false);
toast.info('İşlem iptal edildi');
};
5. Form Data Recovery
LocalStorage Otomatik Kayıt
// Form değişikliklerini otomatik kaydet
useEffect(() => {
const subscription = form.watch((value) => {
localStorage.setItem('planner_form_draft', JSON.stringify(value));
});
return () => subscription.unsubscribe();
}, [form]);
Sayfa Yüklendiğinde Kurtarma
useEffect(() => {
const savedFormData = localStorage.getItem('planner_form_draft');
if (savedFormData) {
const parsed = JSON.parse(savedFormData);
form.reset(parsed);
toast.info('Önceki formunuz geri yüklendi');
}
}, [form]);
Session Storage Backup
// API çağrısı öncesi backup
sessionStorage.setItem('pending_form', JSON.stringify(formData));
6. Analytics ve Logging
Hata Loglama
export function logError(error: ApiError, context?: Record<string, any>) {
const errorLog = {
type: error.type,
message: error.userMessage,
statusCode: error.statusCode,
timestamp: new Date().toISOString(),
context,
stack: error.stack,
};
console.error('[API Error]', errorLog);
// LocalStorage'a hata geçmişi kaydet (son 10 hata)
const errorHistory = JSON.parse(localStorage.getItem('error_history') || '[]');
errorHistory.unshift(errorLog);
localStorage.setItem('error_history', JSON.stringify(errorHistory.slice(0, 10)));
}
Başarı Metrikleri
export function logSuccess(operation: string, duration: number, context?: Record<string, any>) {
const successLog = {
operation,
duration,
timestamp: new Date().toISOString(),
context,
};
console.log('[API Success]', successLog);
// TODO: Analytics entegrasyonu
// analytics.track('api_success', successLog);
}
7. Timeout Yönetimi
30 Saniye Timeout
const result = await withTimeout(
api.generateItinerary(formData),
30000,
new Error('İşlem 30 saniyede tamamlanamadı')
);
UI Bileşenleri
Error Alert
{error && !loading && (
<Alert variant="destructive" className="border-red-200 bg-red-50">
<AlertCircle className="h-5 w-5" />
<AlertTitle className="font-semibold">
{/* Hata tipi başlığı */}
</AlertTitle>
<AlertDescription className="mt-2 space-y-3">
<p>{error.userMessage}</p>
{retryCount > 0 && (
<p className="text-sm">{retryCount} kez yeniden denendi</p>
)}
{isRetryableError(error.originalError) && (
<Button onClick={handleRetry} variant="outline" size="sm">
<RefreshCw className="mr-2 h-4 w-4" />
Tekrar Dene
</Button>
)}
</AlertDescription>
</Alert>
)}
Loading Progress Card
{loading && (
<Card className="p-6 border-orange-200 bg-orange-50/50">
<div className="space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Loader2 className="h-5 w-5 animate-spin text-orange-600" />
<div>
<p className="font-semibold text-gray-900">
{LOADING_STEPS[loadingStep]?.label}
</p>
<p className="text-xs text-gray-600">
Tahmini süre: ~{estimatedTime} saniye
</p>
</div>
</div>
<Button onClick={handleCancel} variant="ghost" size="sm">
<X className="h-4 w-4" />
</Button>
</div>
<Progress value={LOADING_STEPS[loadingStep]?.progress} className="h-2" />
{retryCount > 0 && (
<p className="text-xs text-orange-600 font-medium">
Yeniden deneniyor... ({retryCount}/3)
</p>
)}
</div>
</Card>
)}
Kullanım Örnekleri
Basit API Çağrısı
try {
const result = await api.generateItinerary(formData);
// Başarılı
} catch (err) {
const apiError = parseApiError(err);
logError(apiError, { formData });
setError(apiError);
toast.error('Rota oluşturulamadı', {
description: apiError.userMessage,
});
}
Retry ile API Çağrısı
const result = await retryWithBackoff(
async () => api.generateItinerary(formData),
{
maxRetries: 3,
initialDelay: 1000,
shouldRetry: (err) => {
const apiError = parseApiError(err);
return apiError.type !== 'validation';
},
onRetry: (attempt) => {
setRetryCount(attempt);
toast.warning(`Yeniden deneniyor... (${attempt}/3)`);
},
}
);
Timeout ile API Çağrısı
const result = await withTimeout(
api.generateItinerary(formData),
30000,
new Error('İşlem 30 saniyede tamamlanamadı')
);
Gelecek İyileştirmeler
1. Sentry Entegrasyonu
// src/utils/errorHandler.ts içinde
import * as Sentry from '@sentry/react';
export function logError(error: ApiError, context?: Record<string, any>) {
// ...
Sentry.captureException(error, { extra: errorLog });
}
2. Analytics Entegrasyonu
// src/utils/errorHandler.ts içinde
import analytics from '@/lib/analytics';
export function logSuccess(operation: string, duration: number, context?: Record<string, any>) {
// ...
analytics.track('api_success', successLog);
}
3. Real-time Error Monitoring
// WebSocket ile gerçek zamanlı hata bildirimi
const ws = new WebSocket('wss://monitoring.example.com');
ws.send(JSON.stringify(errorLog));
Test Senaryoları
1. Network Hatası Testi
// Network'ü kapat
navigator.onLine = false;
// Form submit et
// Beklenen: "İnternet bağlantınızı kontrol edin" mesajı
2. Timeout Testi
// API'yi 30+ saniye geciktir
// Beklenen: "İşlem uzun sürdü, lütfen tekrar deneyin" mesajı
3. Retry Testi
// İlk 2 denemede 500 hatası döndür, 3. denemede başarılı
// Beklenen: 2 retry sonrası başarılı sonuç
4. Cancel Testi
// Form submit et
// 2 saniye sonra Cancel butonuna tıkla
// Beklenen: "İşlem iptal edildi" mesajı
5. Form Recovery Testi
// Formu doldur
// Sayfayı yenile
// Beklenen: "Önceki formunuz geri yüklendi" mesajı
Performans Metrikleri
Hedef Metrikler
- İlk API yanıtı: < 5 saniye
- Retry toplam süresi: < 15 saniye
- Form recovery süresi: < 100ms
- Error logging süresi: < 50ms
Monitoring
// Performance API kullanımı
const startTime = performance.now();
await api.generateItinerary(formData);
const duration = performance.now() - startTime;
console.log(`API call duration: ${duration}ms`);
Güvenlik Notları
- Hassas Bilgi Loglama: Kullanıcı şifreleri veya token'lar loglanmamalı
- LocalStorage Güvenliği: Hassas veriler localStorage'a kaydedilmemeli
- Error Stack Traces: Production'da stack trace'ler kullanıcıya gösterilmemeli
- Rate Limiting: Client-side rate limiting uygulanmalı
Sonuç
Bu implementasyon ile:
- ✅ Kullanıcı dostu hata mesajları
- ✅ Otomatik retry mekanizması
- ✅ Görsel loading feedback
- ✅ İptal özelliği
- ✅ Form data recovery
- ✅ Comprehensive logging
- ✅ Analytics hazırlığı
Tüm özellikler production-ready ve test edilmeye hazır durumda.