'use client'; // File: frontend/components/common/error-display.tsx // ADR-007: Component แสดง Error พร้อม Recovery Actions สำหรับ User import { AlertTriangle, XCircle, Info } from 'lucide-react'; // รูปแบบ Error Response จาก Backend (ADR-007) export interface ApiErrorPayload { type: string; code: string; message: string; severity: 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL'; timestamp: string; statusCode?: number; recoveryActions?: string[]; technicalMessage?: string; details?: Record; } export interface ApiErrorResponse { error: ApiErrorPayload; } interface ErrorDisplayProps { error: ApiErrorResponse | ApiErrorPayload | null | undefined; onRetry?: () => void; className?: string; compact?: boolean; } // แปลง severity เป็น color class function getSeverityStyles(severity: string): { container: string; icon: string; title: string; iconComponent: React.ElementType; } { switch (severity) { case 'LOW': return { container: 'border-yellow-200 bg-yellow-50', icon: 'text-yellow-400', title: 'text-yellow-800', iconComponent: Info, }; case 'MEDIUM': return { container: 'border-orange-200 bg-orange-50', icon: 'text-orange-400', title: 'text-orange-800', iconComponent: AlertTriangle, }; case 'HIGH': return { container: 'border-red-200 bg-red-50', icon: 'text-red-400', title: 'text-red-700', iconComponent: AlertTriangle, }; case 'CRITICAL': return { container: 'border-red-300 bg-red-100', icon: 'text-red-500', title: 'text-red-900', iconComponent: XCircle, }; default: return { container: 'border-gray-200 bg-gray-50', icon: 'text-gray-400', title: 'text-gray-700', iconComponent: AlertTriangle, }; } } // ดึง ErrorPayload ออกจาก response ที่อาจซ้อนอยู่ function extractErrorPayload( error: ApiErrorResponse | ApiErrorPayload | null | undefined ): ApiErrorPayload | null { if (!error) return null; // กรณีที่ error เป็น { error: { ... } } if ('error' in error && error.error && typeof error.error === 'object') { return error.error as ApiErrorPayload; } // กรณีที่ error เป็น payload โดยตรง if ('type' in error && 'message' in error) { return error as ApiErrorPayload; } return null; } export function ErrorDisplay({ error, onRetry, className = '', compact = false }: ErrorDisplayProps) { const payload = extractErrorPayload(error); if (!payload) return null; const styles = getSeverityStyles(payload.severity); const IconComponent = styles.iconComponent; if (compact) { return (

{payload.message}

); } return (
{/* หัวข้อ Error */}

{payload.message}

{/* Recovery Actions */} {payload.recoveryActions && payload.recoveryActions.length > 0 && (

วิธีแก้ไข:

    {payload.recoveryActions.map((action, index) => (
  • {action}
  • ))}
)} {/* Error Code (แสดงเฉพาะ Development) */} {process.env.NODE_ENV === 'development' && payload.technicalMessage && (
รายละเอียดทางเทคนิค (Development)
                {payload.technicalMessage}
              
)} {/* Action Buttons */}
{onRetry && ( )}
); } // Helper: แปลง Axios/Fetch error เป็น ApiErrorResponse export function parseApiError(error: unknown): ApiErrorResponse { // กรณี error มาจาก Axios if ( error !== null && typeof error === 'object' && 'response' in error && (error as { response?: { data?: unknown } }).response?.data ) { const data = (error as { response: { data: unknown } }).response.data; if ( typeof data === 'object' && data !== null && 'error' in data ) { return data as ApiErrorResponse; } } // กรณี error เป็น ApiErrorResponse อยู่แล้ว if ( error !== null && typeof error === 'object' && 'error' in error && (error as ApiErrorResponse).error?.message ) { return error as ApiErrorResponse; } // กรณี Network Error หรือ Unknown return { error: { type: 'INTERNAL_ERROR', code: 'NETWORK_ERROR', message: 'ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้', severity: 'HIGH', timestamp: new Date().toISOString(), recoveryActions: ['ตรวจสอบการเชื่อมต่ออินเทอร์เน็ต', 'ลองใหม่ภายหลัง'], }, }; }