// ─────────────────────────────────────────────────────────── // Huio Control Panel — Activity Logs View (cp-activity-logs-view.jsx) // ─────────────────────────────────────────────────────────── if (!window.CP_STYLES) throw new Error('[cp-activity-logs-view] window.CP_STYLES not loaded'); // Static badge dictionary for O(1) lookup without branching overhead const LEVEL_BADGES = { success: 'hu-chip hu-chip-teal', warning: 'hu-chip hu-chip-ochre', system: 'hu-chip hu-chip-coral', }; const CAT_BADGES = { user: 'hu-chip hu-chip-sky', group: 'hu-chip hu-chip-green', financial: 'hu-chip hu-chip-terra', error: 'hu-chip hu-chip-coral', }; const CATEGORIES = ['all', 'user', 'group', 'member', 'financial', 'analytics', 'error']; function renderActivityLogs({ lang = 'vi', t: rawT, appStyles: rawAppStyles, logs: rawLogs = [], users: rawUsers = [], logsCategoryFilter = 'all', setLogsCategoryFilter, logsTypeFilter = 'all', logsSearchQuery = '', setLogsSearchQuery, logsCurrentPage = 1, setLogsCurrentPage, logsPerPage = 10, logsDateFilter = '', setLogsDateFilter, logsUserFilter = 'all', setLogsUserFilter, supabaseClient, loadRealtimeData, }) { const appStyles = rawAppStyles || window.CP_STYLES || {}; const t = typeof rawT === 'function' ? rawT : (k) => k; const logs = Array.isArray(rawLogs) ? rawLogs : []; const users = Array.isArray(rawUsers) ? rawUsers : []; // High-Load Performance: Pre-normalize filters outside iteration to eliminate repeated allocations const normalizedSearch = typeof logsSearchQuery === 'string' ? logsSearchQuery.trim().toLowerCase() : ''; const hasSearch = normalizedSearch.length > 0; const normalizedUserFilter = typeof logsUserFilter === 'string' ? logsUserFilter.trim().toLowerCase() : 'all'; const hasUserFilter = normalizedUserFilter !== 'all' && normalizedUserFilter.length > 0; // Zero-allocation pre-calculation of errors & warnings let errorLogsCount = 0; for (let i = 0; i < logs.length; i++) { const l = logs[i]; if (l && (l.type === 'system' || l.type === 'warning')) { errorLogsCount++; } } // Single-pass defensive filter const filteredLogs = logs.filter(log => { if (!log) return false; // Category filter if (logsCategoryFilter !== 'all' && log.category !== logsCategoryFilter) return false; // Log Type/Severity filter if (logsTypeFilter !== 'all' && log.type !== logsTypeFilter) return false; // Date filter if (logsDateFilter && log.date !== logsDateFilter) return false; // User filter if (hasUserFilter) { const userId = log.userId ? String(log.userId) : ''; const userName = typeof log.userName === 'string' ? log.userName.toLowerCase() : ''; const logText = typeof log.text === 'string' ? log.text.toLowerCase() : ''; const matchUser = (userId === logsUserFilter) || (userName && userName === normalizedUserFilter) || (logText && logText.includes(normalizedUserFilter)); if (!matchUser) return false; } // Search query if (hasSearch) { const text = typeof log.text === 'string' ? log.text.toLowerCase() : (log.text != null ? String(log.text).toLowerCase() : ''); if (!text.includes(normalizedSearch)) return false; } return true; }); // Safe pagination math with upper and lower boundary protection const totalItems = filteredLogs.length; const totalPages = Math.max(1, Math.ceil(totalItems / logsPerPage) || 1); const safeCurrentPage = Math.min(Math.max(1, logsCurrentPage), totalPages); const startIndex = (safeCurrentPage - 1) * logsPerPage; const endIndex = startIndex + logsPerPage; const paginatedLogs = filteredLogs.slice(startIndex, endIndex); return (

{lang === 'vi' ? 'Nhật ký hoạt động hệ thống' : 'System Activity Logs'}

{lang === 'vi' ? 'Giám sát thời gian thực toàn bộ hoạt động của người dùng, tài chính và hệ thống.' : 'Real-time telemetry of user actions, transactions, and system diagnostics.'}

{/* Quick Metrics */}
{/* Logs Table Card */}
{/* Filters Row */}
{/* Category Filter Pills */} {lang === 'vi' ? 'Phân loại:' : 'Category:'} {CATEGORIES.map(cat => ( ))}
{/* Search Bar */}
{ if (typeof setLogsSearchQuery === 'function') setLogsSearchQuery(e.target.value); if (typeof setLogsCurrentPage === 'function') setLogsCurrentPage(1); }} placeholder={t('common.search')} className="hu-input" style={{ height: '100%', fontSize: '13px' }} />
{/* Additional Filters Row (Date and User) */}
{/* Date Filter */}
{lang === 'vi' ? 'Ngày:' : 'Date:'} { if (typeof setLogsDateFilter === 'function') setLogsDateFilter(e.target.value); if (typeof setLogsCurrentPage === 'function') setLogsCurrentPage(1); }} className="hu-input" style={{ height: '32px', fontSize: '12px', padding: '0 8px', borderRadius: 'var(--hu-r-md)', border: '1px solid var(--hu-border)', background: 'var(--hu-bg-elev)', color: 'var(--hu-fg)', width: '135px' }} /> {logsDateFilter && ( )}
{/* User Filter */}
{lang === 'vi' ? 'Thành viên:' : 'User:'}
{/* Logs List Table */}
{paginatedLogs.length === 0 ? ( ) : ( paginatedLogs.map((log, idx) => { const levelBadge = LEVEL_BADGES[log.type] || 'hu-chip hu-chip-gray'; const catBadge = CAT_BADGES[log.category] || 'hu-chip hu-chip-gray'; return ( ); }) )}
{lang === 'vi' ? 'Thời gian' : 'Time'} {lang === 'vi' ? 'Mức độ' : 'Level'} {lang === 'vi' ? 'Phân loại' : 'Category'} {lang === 'vi' ? 'Chi tiết hoạt động' : 'Operation Details'}
{lang === 'vi' ? 'Không tìm thấy nhật ký hoạt động nào.' : 'No activity logs found.'}
{log.time || '--:--:--'}
{log.date || '----/--/--'}
{log.type === 'system' ? 'CRITICAL' : (log.type ? log.type.toUpperCase() : 'INFO')} {log.category || 'system'} {log.text || ''}
{/* Pagination Controls */} {totalPages > 1 && (
{lang === 'vi' ? `Hiển thị ${startIndex + 1} - ${Math.min(endIndex, totalItems)} trong tổng số ${totalItems} nhật ký` : `Showing ${startIndex + 1} - ${Math.min(endIndex, totalItems)} of ${totalItems} logs`}
{safeCurrentPage} / {totalPages}
)}
); } window.CpActivityLogsView = { renderActivityLogs, };