// ─────────────────────────────────────────────────────────── // 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.sub')}
{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.') }
| 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()}
) : (
)}
|
| 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()}
) : (
)}
|