// ─────────────────────────────────────────────────────────── // Huio Control Panel — Bot Chat Logs View (cp-bot-logs-view.jsx) // ─────────────────────────────────────────────────────────── if (!window.CP_STYLES) throw new Error('[cp-bot-logs-view] window.CP_STYLES not loaded (check cp-styles.jsx)'); function renderBotLogs({ lang, t, appStyles: rawAppStyles, botLogs = [], botLogsSearch = '', setBotLogsSearch, botLogsDateFilter = '', setBotLogsDateFilter, botLogsUserFilter = 'all', setBotLogsUserFilter, botLogsCurrentPage = 1, setBotLogsCurrentPage, botLogsPerPage = 15, hasMoreBotLogs, isLoadingMoreBotLogs, loadMoreBotLogs, setSelectedBotChat, onRefresh, }) { const appStyles = rawAppStyles || window.CP_STYLES || {}; const tr = typeof t === 'function' ? t : ((k) => k); const logs = Array.isArray(botLogs) ? botLogs : []; // High-Load Pre-normalization (hoist invariants outside loop for O(N) efficiency) const query = (botLogsSearch && typeof botLogsSearch === 'string') ? botLogsSearch.trim().toLowerCase() : ''; const targetDate = (botLogsDateFilter && typeof botLogsDateFilter === 'string') ? botLogsDateFilter.trim() : ''; const targetUser = (botLogsUserFilter != null) ? String(botLogsUserFilter) : 'all'; // Filter bot logs const filteredBotLogs = logs.filter(log => { if (!log) return false; // Date filter with zero-allocation ISO substring optimization & NaN protection if (targetDate) { let logDateStr = ''; if (typeof log.created_at === 'string' && log.created_at.length >= 10) { logDateStr = log.created_at.substring(0, 10); } else if (log.created_at) { const d = new Date(log.created_at); if (!isNaN(d.getTime())) { logDateStr = d.toISOString().substring(0, 10); } } if (logDateStr !== targetDate) return false; } // User filter if (targetUser !== 'all') { const tgIdStr = log.telegram_id != null ? String(log.telegram_id) : ''; if (tgIdStr !== targetUser) return false; } // Search query if (query) { const userMsg = (log.message_text || '').toLowerCase(); const botResp = (log.response_text || '').toLowerCase(); const tester = (log.tester_name || '').toLowerCase(); const tgId = log.telegram_id != null ? String(log.telegram_id) : ''; if (!userMsg.includes(query) && !botResp.includes(query) && !tester.includes(query) && !tgId.includes(query)) { return false; } } return true; }); // Unique testers list for filter dropdown (Single-pass Map aggregation with name retention) const uniqueTestersMap = new Map(); for (let i = 0; i < logs.length; i++) { const log = logs[i]; if (log && log.telegram_id != null) { const tgIdStr = String(log.telegram_id); const existingName = uniqueTestersMap.get(tgIdStr); const name = log.tester_name || existingName || `ID: ${tgIdStr}`; uniqueTestersMap.set(tgIdStr, name); } } const uniqueTesters = Array.from(uniqueTestersMap.entries()).map(([id, name]) => ({ id, name })); // Pagination with boundary protection const perPage = Math.max(1, Number(botLogsPerPage) || 15); const totalItems = filteredBotLogs.length; const totalPages = Math.max(1, Math.ceil(totalItems / perPage)); const safeCurrentPage = Math.min(Math.max(1, Number(botLogsCurrentPage) || 1), totalPages); const startIndex = (safeCurrentPage - 1) * perPage; const endIndex = startIndex + perPage; const paginatedBotLogs = filteredBotLogs.slice(startIndex, endIndex); // Pagination Windowing helper (avoids DOM blowup if totalPages is large) const getPaginationPages = () => { if (totalPages <= 7) { return Array.from({ length: totalPages }, (_, i) => i + 1); } const pages = [1]; let start = Math.max(2, safeCurrentPage - 1); let end = Math.min(totalPages - 1, safeCurrentPage + 1); if (safeCurrentPage <= 3) { end = 4; } else if (safeCurrentPage >= totalPages - 2) { start = totalPages - 3; } if (start > 2) pages.push('...'); for (let p = start; p <= end; p++) pages.push(p); if (end < totalPages - 1) pages.push('...'); pages.push(totalPages); return pages; }; return (

{lang === 'vi' ? 'Nhật ký Chat Bot Telegram' : 'Telegram QA Bot Chat Logs'}

{lang === 'vi' ? 'Giám sát lịch sử trò chuyện thời gian thực giữa các kiểm thử viên và Trợ lý ảo QA.' : 'Monitor real-time communication histories between app testers and the QA virtual assistant.'}

{/* Metrics Dashboard */}
{/* Logs Table Card */}
{/* Filters Row */}
{/* Date Filter */}
{lang === 'vi' ? 'Ngày:' : 'Date:'} { if (typeof setBotLogsDateFilter === 'function') setBotLogsDateFilter(e.target.value); if (typeof setBotLogsCurrentPage === 'function') setBotLogsCurrentPage(1); }} className="hu-input" style={{ height: '34px', fontSize: '12.5px', padding: '0 8px', borderRadius: 'var(--hu-r-md)', border: '1px solid var(--hu-border)', background: 'var(--hu-bg-elev)', color: 'var(--hu-fg)', width: '145px' }} /> {botLogsDateFilter && ( )}
{/* User/Tester Filter */}
{lang === 'vi' ? 'Kiểm thử viên:' : 'Tester:'}
{/* Search Bar */}
{ if (typeof setBotLogsSearch === 'function') setBotLogsSearch(e.target.value); if (typeof setBotLogsCurrentPage === 'function') setBotLogsCurrentPage(1); }} placeholder={tr('common.search')} className="hu-input" style={{ height: '100%', fontSize: '13px' }} />
{/* Logs Table */}
{paginatedBotLogs.length === 0 ? ( ) : ( paginatedBotLogs.map((log, index) => { const dateObj = log.created_at ? new Date(log.created_at) : null; const isValidDate = dateObj && !isNaN(dateObj.getTime()); const formattedTime = isValidDate ? dateObj.toTimeString().split(' ')[0] : '—'; const formattedDate = isValidDate ? dateObj.toLocaleDateString(lang === 'vi' ? 'vi-VN' : 'en-US', { month: 'short', day: 'numeric', year: 'numeric' }) : '—'; const rowKey = log.id || `${log.telegram_id || 'tg'}_${log.created_at || index}`; return ( ); }) )}
{lang === 'vi' ? 'Thời gian' : 'Time'} {lang === 'vi' ? 'Kiểm thử viên' : 'Tester'} {lang === 'vi' ? 'Tin nhắn' : 'Message'} {lang === 'vi' ? 'Trả lời của bot' : 'Bot Response'} {lang === 'vi' ? 'Thao tác' : 'Action'}
{lang === 'vi' ? 'Không tìm thấy nhật ký chat nào' : 'No bot chat logs found'}
{formattedTime}
{formattedDate}
{log.tester_name || 'Tester'}
TG ID: {log.telegram_id || 'N/A'}
{log.message_text} {log.response_text}
{/* Load More from Server (Lazy Loading) */} {hasMoreBotLogs && (
)} {/* Pagination Controls */} {totalPages > 1 && (
{lang === 'vi' ? `Hiển thị ${startIndex + 1}-${Math.min(endIndex, totalItems)} trong tổng số ${totalItems} kết quả` : `Showing ${startIndex + 1}-${Math.min(endIndex, totalItems)} of ${totalItems} results`}
{getPaginationPages().map((p, idx) => { if (p === '...') { return ( … ); } return ( ); })}
)}
); } window.CpBotLogsView = { renderBotLogs, };