import './style.css'; import './app.css'; import logoUrl from './assets/logo.jpeg'; const brandLogoHtml = `Simple Care`; // Trạng thái cục bộ let loggedIn = false; let studentInfo = null; let stats = { wifiSSID: '', onlineSecs: 0, offlineSecs: 0, wsConnected: false, serverReachable: false, allowedApps: '', monitorMode: '', monitorLabel: '', className: '', classCode: '', currentPeriod: 0, currentCourseName: '', shifts: [] }; // Quản lý webcam let webcamStream = null; let webcamInterval = null; const webcamVideo = document.createElement('video'); webcamVideo.autoplay = true; webcamVideo.playsInline = true; webcamVideo.style.display = 'none'; document.body.appendChild(webcamVideo); const webcamCanvas = document.createElement('canvas'); webcamCanvas.width = 320; webcamCanvas.height = 240; webcamCanvas.style.display = 'none'; document.body.appendChild(webcamCanvas); function init() { if (typeof window.go === 'undefined' || typeof window.go.main === 'undefined') { setTimeout(init, 200); return; } window.runtime.EventsOn('start_webcam_stream', startWebcam); window.runtime.EventsOn('stop_webcam_stream', stopWebcam); checkLogin(); } async function checkLogin() { try { const isLogged = await window.go.main.App.CheckLoginStatus(); if (isLogged) { loggedIn = true; studentInfo = await window.go.main.App.GetStudentInfo(); renderDashboard(); startStatsTicker(); } else { loggedIn = false; renderLoginPrompt(); } } catch (err) { console.error('Error checking login status:', err); } } function renderLoginPrompt() { document.querySelector('#app').innerHTML = `
`; document.getElementById('btn-goto-login').addEventListener('click', () => { window.go.main.App.NavigateToLogin(); }); } function monitorModeMeta(mode) { switch (mode) { case 'learning': return { icon: '🛡️', label: 'Đang giám sát', cls: 'mode-learning' }; case 'exam': return { icon: '📝', label: 'Phòng thi', cls: 'mode-exam' }; case 'outside_schedule': return { icon: '😴', label: 'Ngoài giờ học', cls: 'mode-outside' }; case 'not_configured': return { icon: '⚙️', label: 'Chưa sẵn sàng', cls: 'mode-config' }; default: return { icon: '⏳', label: 'Đang kết nối', cls: 'mode-pending' }; } } function attendanceClass(status) { if (status === 4) return 'att-ok'; if (status === 3) return 'att-late'; if (status === 1 || status === 2) return 'att-leave'; if (status === 0) return 'att-absent'; return 'att-pending'; } function renderShiftsTable(shifts) { if (!shifts || shifts.length === 0) { return '
Hôm nay không có ca học theo lịch lớp.
'; } return ` ${shifts.map(s => ` `).join('')}
Ca Môn học Giờ Trực tuyến Ngoại tuyến Điểm danh
Ca ${s.period}${s.isActiveNow ? ' đang học' : ''} ${s.courseName || '—'} ${s.startTime}–${s.endTime} ${formatDuration(s.onlineSeconds || 0)} ${formatDuration(s.offlineSeconds || 0)} ${s.attendanceLabel || 'Chưa tính'}
`; } function renderDashboard() { if (!studentInfo) return; const mode = monitorModeMeta(stats.monitorMode); const classLine = stats.className ? `${stats.className}${stats.classCode ? ` (${stats.classCode})` : ''}` : 'Đang xác định lớp...'; document.querySelector('#app').innerHTML = `
${brandLogoHtml}
Simple Care Rikkei Education
Avatar

${studentInfo.fullName}

${studentInfo.studentCode}

${studentInfo.email}

Lớp ${classLine}
Điện thoại ${studentInfo.phone || 'Chưa cung cấp'}
${mode.icon}
${mode.label} ${classLine}
${stats.monitorLabel || stats.statusMsg || 'Đang kết nối...'}
${stats.currentPeriod > 0 && stats.currentCourseName ? `Ca ${stats.currentPeriod} · ${stats.currentCourseName}` : 'Theo dõi theo lịch học của lớp'}
📡
WiFi
${stats.wifiSSID || 'Đang quét...'}
🖥️
Máy chủ
${stats.serverReachable ? 'Đã kết nối' : 'Mất kết nối'}
Hôm nay — theo ca
${stats.sessionDate || ''}
Trực tuyến 00:00:00 Ngoại tuyến 00:00:00
${renderShiftsTable(stats.shifts)}
`; document.getElementById('btn-logout').addEventListener('click', async () => { if (confirm('Bạn có chắc chắn muốn đăng xuất khỏi ứng dụng giám sát?')) { loggedIn = false; studentInfo = null; stopWebcam(); await window.go.main.App.Logout(); } }); } function formatDuration(totalSeconds) { const hrs = Math.floor(totalSeconds / 3600); const mins = Math.floor((totalSeconds % 3600) / 60); const secs = totalSeconds % 60; const pad = (num) => String(num).padStart(2, '0'); return `${pad(hrs)}:${pad(mins)}:${pad(secs)}`; } function startStatsTicker() { const pullStats = async () => { if (!loggedIn) return; try { const freshStats = await window.go.main.App.GetStats(); stats = { ...stats, ...freshStats }; if (!Array.isArray(stats.shifts)) stats.shifts = []; const wifiEl = document.getElementById('wifi-ssid'); if (wifiEl) wifiEl.innerText = stats.wifiSSID || 'Không có WiFi'; const serverEl = document.getElementById('server-status'); if (serverEl) { serverEl.innerText = stats.serverReachable ? 'Đã kết nối' : 'Mất kết nối'; serverEl.style.color = stats.serverReachable ? 'var(--online-color)' : 'var(--offline-color)'; } const statusMsgEl = document.getElementById('status-message'); if (statusMsgEl) { statusMsgEl.innerText = stats.monitorLabel || stats.statusMsg || 'Đang kết nối...'; } const mode = monitorModeMeta(stats.monitorMode); const statusIconEl = document.getElementById('status-icon'); if (statusIconEl) statusIconEl.innerText = mode.icon; const modeTagEl = document.getElementById('monitor-mode-tag'); if (modeTagEl) modeTagEl.innerText = mode.label; const banner = document.querySelector('.status-banner'); if (banner) banner.className = `card status-banner ${mode.cls}`; const classLine = stats.className ? `${stats.className}${stats.classCode ? ` (${stats.classCode})` : ''}` : 'Đang xác định lớp...'; const classLineEl = document.getElementById('status-class-line'); if (classLineEl) classLineEl.innerText = classLine; const profileClassEl = document.getElementById('profile-class'); if (profileClassEl) profileClassEl.innerText = classLine; const statusSubEl = document.getElementById('status-sub'); if (statusSubEl) { statusSubEl.innerText = stats.currentPeriod > 0 && stats.currentCourseName ? `Ca ${stats.currentPeriod} · ${stats.currentCourseName}` : 'Theo dõi theo lịch học của lớp'; } const onlineEl = document.getElementById('clock-online'); if (onlineEl) onlineEl.innerText = formatDuration(stats.onlineSecs || 0); const offlineEl = document.getElementById('clock-offline'); if (offlineEl) offlineEl.innerText = formatDuration(stats.offlineSecs || 0); const shiftsWrap = document.getElementById('shifts-wrap'); if (shiftsWrap) shiftsWrap.innerHTML = renderShiftsTable(stats.shifts); const sessionDateEl = document.getElementById('session-date'); if (sessionDateEl && stats.sessionDate) sessionDateEl.innerText = stats.sessionDate; const indicator = document.querySelector('.status-indicator'); if (indicator) { indicator.className = `status-indicator ${stats.serverReachable ? 'online' : 'offline'}`; } } catch (err) { console.error('Error fetching stats:', err); } }; pullStats(); setInterval(pullStats, 1000); } async function startWebcam() { if (webcamStream) return; try { webcamStream = await navigator.mediaDevices.getUserMedia({ video: { width: 320, height: 240, frameRate: { max: 10 } } }); webcamVideo.srcObject = webcamStream; webcamInterval = setInterval(() => { const ctx = webcamCanvas.getContext('2d'); if (ctx) { ctx.drawImage(webcamVideo, 0, 0, webcamCanvas.width, webcamCanvas.height); const dataUrl = webcamCanvas.toDataURL('image/jpeg', 0.4); window.go.main.App.SendWebcamFrame(dataUrl); } }, 250); } catch (err) { console.error('Failed to open webcam:', err); } } function stopWebcam() { if (webcamInterval) { clearInterval(webcamInterval); webcamInterval = null; } if (webcamStream) { webcamStream.getTracks().forEach(track => track.stop()); webcamStream = null; } webcamVideo.srcObject = null; } init();