/** * FullScreenError - Full screen error state component * * Used for: * - Critical errors that block the UI * - Data loading failures * - Network offline states */ import React from 'react'; import { View, Text, TouchableOpacity, StyleSheet, } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { AppError, ErrorCategory } from '@/types/errors'; interface FullScreenErrorProps { error?: AppError; title?: string; message?: string; onRetry?: () => void; onDismiss?: () => void; showDismiss?: boolean; retryLabel?: string; dismissLabel?: string; } // Category-specific icons const categoryIcons: Record = { network: 'cloud-offline', timeout: 'time-outline', authentication: 'lock-closed', permission: 'shield-outline', notFound: 'search-outline', validation: 'warning', conflict: 'git-merge', rateLimit: 'hourglass', server: 'server-outline', client: 'alert-circle', ble: 'bluetooth', sensor: 'hardware-chip', subscription: 'card', unknown: 'help-circle', }; export function FullScreenError({ error, title, message, onRetry, onDismiss, showDismiss = false, retryLabel = 'Try Again', dismissLabel = 'Dismiss', }: FullScreenErrorProps) { const displayTitle = title || (error?.category === 'network' ? 'Connection Error' : 'Something Went Wrong'); const displayMessage = message || error?.userMessage || 'An unexpected error occurred. Please try again.'; const icon = error?.category ? categoryIcons[error.category] : 'alert-circle'; const isRetryable = error?.retry.isRetryable ?? !!onRetry; return ( {/* Icon */} {/* Title */} {displayTitle} {/* Message */} {displayMessage} {/* Action hint */} {error?.actionHint && ( {error.actionHint} )} {/* Buttons */} {isRetryable && onRetry && ( {retryLabel} )} {showDismiss && onDismiss && ( {dismissLabel} )} {/* Error ID for support */} {error?.errorId && __DEV__ && ( Error ID: {error.errorId} )} ); } /** * EmptyState - Component for empty data states (not strictly an error) */ interface EmptyStateProps { icon?: keyof typeof Ionicons.glyphMap; title: string; message?: string; actionLabel?: string; onAction?: () => void; } export function EmptyState({ icon = 'folder-open-outline', title, message, actionLabel, onAction, }: EmptyStateProps) { return ( {title} {message && {message}} {actionLabel && onAction && ( {actionLabel} )} ); } /** * OfflineState - Specific state for offline/no connection */ interface OfflineStateProps { onRetry?: () => void; } export function OfflineState({ onRetry }: OfflineStateProps) { return ( No Internet Connection Please check your connection and try again. {onRetry && ( Try Again )} ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#fff', padding: 24, }, content: { alignItems: 'center', maxWidth: 320, }, iconContainer: { width: 120, height: 120, borderRadius: 60, backgroundColor: '#F3F4F6', justifyContent: 'center', alignItems: 'center', marginBottom: 24, }, title: { fontSize: 20, fontWeight: '700', color: '#1F2937', textAlign: 'center', marginBottom: 8, }, message: { fontSize: 15, color: '#6B7280', textAlign: 'center', lineHeight: 22, marginBottom: 8, }, hint: { fontSize: 13, color: '#9CA3AF', textAlign: 'center', marginBottom: 24, }, buttons: { marginTop: 16, gap: 12, width: '100%', }, retryButton: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', backgroundColor: '#3B82F6', paddingVertical: 14, paddingHorizontal: 24, borderRadius: 12, width: '100%', }, buttonIcon: { marginRight: 8, }, retryButtonText: { color: '#fff', fontSize: 16, fontWeight: '600', }, dismissButton: { alignItems: 'center', justifyContent: 'center', paddingVertical: 12, paddingHorizontal: 24, }, dismissButtonText: { color: '#6B7280', fontSize: 15, fontWeight: '500', }, errorId: { marginTop: 24, fontSize: 11, color: '#D1D5DB', fontFamily: 'monospace', }, }); export default FullScreenError;