// ─────────────────────────────────────────────────────────── // Huio Control Panel — Errors View (cp-errors-view.jsx) // ─────────────────────────────────────────────────────────── if (!window.CP_STYLES) throw new Error('[cp-errors-view] window.CP_STYLES not loaded (check cp-styles.jsx)'); function renderErrors({ lang, t, appStyles: rawAppStyles, errorLogs, searchQuery, setSearchQuery, errorFilter, setErrorFilter, errorPlatformFilter, setErrorPlatformFilter, showResolvedErrors, setShowResolvedErrors, errorTab, setErrorTab, setSelectedError, handleResolveError, handleResolveErrorGroup, handleOpenPromptModal, }) { const appStyles = rawAppStyles || window.CP_STYLES || {}; const formatPhone = window.formatPhone || ((p) => p || '—'); // Pre-normalize filter criteria outside loops for O(N) efficiency const query = (searchQuery && typeof searchQuery === 'string') ? searchQuery.trim().toLowerCase() : ''; const platFilter = (errorPlatformFilter && typeof errorPlatformFilter === 'string') ? errorPlatformFilter.toLowerCase() : 'all'; const logs = Array.isArray(errorLogs) ? errorLogs : []; // Single-pass computation of quick metrics (avoids multiple intermediate array allocations) let totalErrors = 0; let fatalErrors = 0; for (let i = 0; i < logs.length; i++) { const item = logs[i]; if (showResolvedErrors || !item.resolved) { totalErrors++; if (item.metadata?.isFatal === true) { fatalErrors++; } } } const warningErrors = totalErrors - fatalErrors; // Filter errors const filteredErrors = logs.filter(e => { // Resolved filter if (!showResolvedErrors && e.resolved) return false; // Error severity filter const isFatal = e.metadata?.isFatal === true; if (errorFilter === 'fatal' && !isFatal) return false; if (errorFilter === 'warning' && isFatal) return false; // Platform filter const plat = (e.metadata?.platform || '').toLowerCase(); if (platFilter !== 'all' && plat !== platFilter) return false; // Search query if (query) { const errMsg = (e.error_message || '').toLowerCase(); const anonId = (e.anonymous_id || '').toLowerCase(); const userName = (e.profiles?.name || '').toLowerCase(); const userPhone = e.profiles?.phone || ''; if (!errMsg.includes(query) && !anonId.includes(query) && !userName.includes(query) && !userPhone.includes(query)) { return false; } } return true; }); // Perform client-side error grouping/aggregation const aggregatedErrorsMap = Object.create(null); for (let i = 0; i < filteredErrors.length; i++) { const e = filteredErrors[i]; const key = e.error_message || 'Unknown exception'; if (!aggregatedErrorsMap[key]) { aggregatedErrorsMap[key] = { error_message: key, count: 0, isFatal: false, platforms: new Set(), versions: new Set(), users: new Set(), last_occurred: e.created_at || new Date().toISOString(), example_error: e, all_resolved: true }; } const group = aggregatedErrorsMap[key]; group.count += 1; if (e.metadata?.isFatal === true) { group.isFatal = true; } if (e.metadata?.platform) { group.platforms.add(String(e.metadata.platform).toLowerCase()); } if (e.metadata?.version) { group.versions.add(String(e.metadata.version)); } const userKey = e.user_id || e.anonymous_id || 'unknown'; group.users.add(userKey); const itemTime = e.created_at ? new Date(e.created_at).getTime() : 0; const lastTime = group.last_occurred ? new Date(group.last_occurred).getTime() : 0; if (itemTime > lastTime) { group.last_occurred = e.created_at; } if (!e.resolved) { group.all_resolved = false; } } const aggregatedErrors = Object.values(aggregatedErrorsMap).sort((a, b) => b.count - a.count); return (

{t('errors.title')}

{t('errors.sub')}

{/* Quick Metrics Grid */}
0 ? `+${totalErrors}` : '0'} isPositive={false} iconKind="db" /> 0 ? `+${fatalErrors}` : '0'} isPositive={false} iconKind="db" /> 0 ? `+${warningErrors}` : '0'} isPositive={true} iconKind="db" />
{/* Main content table container */}
{/* Tab Switcher (Raw vs Aggregated) */}

{errorTab === 'raw' ? (lang === 'vi' ? 'Nhật ký lỗi thời gian thực' : 'Real-time Exception Stream') : (lang === 'vi' ? 'Báo cáo lỗi đã gom nhóm' : 'Aggregated Exception Groups') }

{errorTab === 'raw' ? (lang === 'vi' ? 'Danh sách các ngoại lệ và sự cố runtime được gửi về từ ứng dụng di động.' : 'List of runtime exceptions and application crashes telemetry reported from mobile client.') : (lang === 'vi' ? 'Lỗi trùng lặp được gom nhóm tự động để phát hiện các sự cố diện rộng.' : 'Identical crashes grouped dynamically to determine overall bug prevalence and volume.') }

{/* Filters Row */}
{/* Show Resolved Checkbox */}
{lang === 'vi' ? 'Thiết bị:' : 'OS:'}
{/* Search input */}
setSearchQuery(e.target.value)} className="hu-input" style={{ width: '100%', paddingLeft: '12px', minHeight: '38px', fontSize: '13px' }} /> {searchQuery && ( )}
{/* Error logs tables rendering */} {errorTab === 'raw' ? (
{filteredErrors.length === 0 ? ( ) : ( filteredErrors.map((e, index) => { const date = e.created_at ? new Date(e.created_at) : null; const timeStr = (date && !isNaN(date.getTime())) ? date.toTimeString().split(' ')[0] + ' ' + date.toLocaleDateString(lang === 'vi' ? 'vi-VN' : 'en-US', {month: 'short', day: 'numeric'}) : '—'; const isFatal = e.metadata?.isFatal === true; const severityBadgeClass = isFatal ? "hu-chip hu-chip-coral" : "hu-chip hu-chip-ochre"; const severityText = isFatal ? (lang === 'vi' ? 'Nghiêm trọng' : 'Fatal') : (lang === 'vi' ? 'Cảnh báo' : 'Warning'); const displayUser = e.profiles?.name ? `${e.profiles.name} (${formatPhone(e.profiles.phone)})` : (e.anonymous_id ? `Guest (${e.anonymous_id.substring(0, 8)})` : 'N/A'); const platform = e.metadata?.platform || 'N/A'; return ( ); }) )}
Severity {t('errors.col.message')} {t('errors.col.user')} {t('errors.col.platform')} {t('errors.col.time')} Actions
{lang === 'vi' ? 'Không tìm thấy lỗi nào.' : 'No exceptions logged.'}
{severityText.toUpperCase()} {e.error_message} {displayUser} {platform.toUpperCase()} {timeStr}
{e.resolved ? ( {t('errors.status.resolved').toUpperCase()} ) : ( )}
) : (
{aggregatedErrors.length === 0 ? ( ) : ( aggregatedErrors.map((group, index) => { const date = group.last_occurred ? new Date(group.last_occurred) : null; const timeStr = (date && !isNaN(date.getTime())) ? date.toTimeString().split(' ')[0] + ' ' + date.toLocaleDateString(lang === 'vi' ? 'vi-VN' : 'en-US', {month: 'short', day: 'numeric'}) : '—'; const isFatal = group.isFatal === true; const severityBadgeClass = isFatal ? "hu-chip hu-chip-coral" : "hu-chip hu-chip-ochre"; const severityText = isFatal ? (lang === 'vi' ? 'Nghiêm trọng' : 'Fatal') : (lang === 'vi' ? 'Cảnh báo' : 'Warning'); const platformsArr = Array.from(group.platforms); return ( ); }) )}
Severity {t('errors.col.message')} {t('errors.col.count')} {t('errors.col.users_affected')} {t('errors.col.platforms')} {lang === 'vi' ? 'Lần cuối' : 'Last Occurred'} Actions
{lang === 'vi' ? 'Không tìm thấy lỗi nào.' : 'No exceptions logged.'}
{severityText.toUpperCase()} {group.error_message} {group.count} {group.users.size}
{platformsArr.map(plat => ( {plat.toUpperCase()} ))} {platformsArr.length === 0 && N/A}
{timeStr}
{group.all_resolved ? ( {t('errors.status.resolved').toUpperCase()} ) : ( )}
)}
); } window.CpErrorsView = { renderErrors, };