fix: Enable dynamic student/teacher contact discovery, dual REST/WebSocket persistence, and multi-path .env detection
This commit is contained in:
@@ -960,6 +960,8 @@ class StudentPortal
|
||||
}
|
||||
}
|
||||
|
||||
let activeTeacherId = null;
|
||||
|
||||
function switchStudentTab(tab) {
|
||||
document.getElementById('tab_lessons_content').style.display = (tab === 'lessons') ? 'block' : 'none';
|
||||
document.getElementById('tab_chat_content').style.display = (tab === 'chat') ? 'block' : 'none';
|
||||
@@ -968,51 +970,59 @@ class StudentPortal
|
||||
document.getElementById('tab_btn_lessons').className = (tab === 'lessons') ? 'tab-btn active' : 'tab-btn';
|
||||
document.getElementById('tab_btn_chat').className = (tab === 'chat') ? 'tab-btn active' : 'tab-btn';
|
||||
document.getElementById('tab_btn_exams').className = (tab === 'exams') ? 'tab-btn active' : 'tab-btn';
|
||||
}
|
||||
|
||||
// Socratic In-Video Checkpoint Execution
|
||||
function triggerCheckpointDemo() {
|
||||
const video = document.getElementById('lesson_video_player');
|
||||
if (video) video.pause();
|
||||
document.getElementById('socratic_quiz_modal').style.display = 'flex';
|
||||
}
|
||||
|
||||
function handleCheckpointAnswer(btn, isCorrect) {
|
||||
const feedback = document.getElementById('checkpoint_feedback');
|
||||
const btns = document.querySelectorAll('.quiz-option-btn');
|
||||
btns.forEach(b => b.disabled = true);
|
||||
|
||||
if (isCorrect) {
|
||||
btn.className = 'quiz-option-btn correct';
|
||||
feedback.style.color = '#34D399';
|
||||
feedback.textContent = '✓ إجابة ممتازة وصحيحة! تم تثبيت المفهوم المعرفي بنجاح.';
|
||||
feedback.style.display = 'block';
|
||||
setTimeout(() => {
|
||||
document.getElementById('socratic_quiz_modal').style.display = 'none';
|
||||
btns.forEach(b => { b.disabled = false; b.className = 'quiz-option-btn'; });
|
||||
feedback.style.display = 'none';
|
||||
const video = document.getElementById('lesson_video_player');
|
||||
if (video) video.play();
|
||||
}, 1800);
|
||||
} else {
|
||||
btn.className = 'quiz-option-btn wrong';
|
||||
feedback.style.color = '#F87171';
|
||||
feedback.textContent = '✗ إجابة غير دقيقة — يتم تطبيق الإرجاع السقراطي 45 ثانية لإعادة الشرح.';
|
||||
feedback.style.display = 'block';
|
||||
setTimeout(() => {
|
||||
document.getElementById('socratic_quiz_modal').style.display = 'none';
|
||||
btns.forEach(b => { b.disabled = false; b.className = 'quiz-option-btn'; });
|
||||
feedback.style.display = 'none';
|
||||
const video = document.getElementById('lesson_video_player');
|
||||
if (video) {
|
||||
video.currentTime = Math.max(0, video.currentTime - 45);
|
||||
video.play();
|
||||
}
|
||||
}, 2200);
|
||||
if (tab === 'chat') {
|
||||
loadStudentChat();
|
||||
}
|
||||
}
|
||||
|
||||
// Real-time Chat
|
||||
async function loadStudentChat() {
|
||||
const token = localStorage.getItem('saqel_student_jwt');
|
||||
if (!token) return;
|
||||
try {
|
||||
const res = await fetch('/api/chat/conversations', {
|
||||
headers: { 'Authorization': 'Bearer ' + token }
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok && data.data && data.data.length > 0) {
|
||||
const teacher = data.data.find(c => c.role === 'teacher') || data.data[0];
|
||||
if (teacher) {
|
||||
activeTeacherId = teacher.user_id;
|
||||
const badge = document.getElementById('chat_teacher_badge_name');
|
||||
if (badge) {
|
||||
badge.textContent = `${teacher.full_name} (${teacher.specialization || 'مدرس المادة'}) (متصل الآن 🟢)`;
|
||||
}
|
||||
loadStudentMessages(activeTeacherId);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Load student chat notice:', e);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadStudentMessages(teacherId) {
|
||||
const token = localStorage.getItem('saqel_student_jwt');
|
||||
if (!token || !teacherId) return;
|
||||
try {
|
||||
const res = await fetch(`/api/chat/messages?other_user_id=${teacherId}`, {
|
||||
headers: { 'Authorization': 'Bearer ' + token }
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok && data.data?.messages) {
|
||||
const messagesArea = document.getElementById('student_chat_messages');
|
||||
messagesArea.innerHTML = '';
|
||||
if (data.data.messages.length === 0) {
|
||||
messagesArea.innerHTML = '<div style="text-align: center; color: var(--text-muted); font-size: 13px; margin-top: 40px;">مرحباً بك! اكتب سؤالك هنا وسيجيبك أستاذ المادة فوراً.</div>';
|
||||
} else {
|
||||
data.data.messages.forEach(msg => appendChatMessage(msg));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Fetch student messages notice:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Real-time Chat Rendering
|
||||
function appendChatMessage(msg) {
|
||||
const messagesArea = document.getElementById('student_chat_messages');
|
||||
const isMine = msg.is_mine || false;
|
||||
@@ -1032,31 +1042,47 @@ class StudentPortal
|
||||
if (!message) return;
|
||||
|
||||
input.value = '';
|
||||
const teacherId = 1;
|
||||
const token = localStorage.getItem('saqel_student_jwt');
|
||||
|
||||
// If activeTeacherId is null, try to find teacher
|
||||
if (!activeTeacherId) {
|
||||
await loadStudentChat();
|
||||
}
|
||||
const targetId = activeTeacherId || 1;
|
||||
|
||||
if (wsSocket && wsSocket.readyState === WebSocket.OPEN) {
|
||||
wsSocket.send(JSON.stringify({
|
||||
event: 'chat_send',
|
||||
data: {
|
||||
receiver_id: teacherId,
|
||||
message: message,
|
||||
message_type: 'text'
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
const token = localStorage.getItem('saqel_student_jwt');
|
||||
await fetch('/api/chat/messages', {
|
||||
// 1. Send via REST API to guarantee database persistence
|
||||
try {
|
||||
const res = await fetch('/api/chat/messages', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + token
|
||||
},
|
||||
body: JSON.stringify({
|
||||
receiver_id: teacherId,
|
||||
message: message
|
||||
receiver_id: targetId,
|
||||
message: message,
|
||||
message_type: 'text'
|
||||
})
|
||||
});
|
||||
appendChatMessage({ message: message, is_mine: true });
|
||||
const data = await res.json();
|
||||
if (res.ok && data.data) {
|
||||
appendChatMessage({ message: message, is_mine: true, created_at: 'الآن' });
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('REST chat send notice:', err);
|
||||
appendChatMessage({ message: message, is_mine: true, created_at: 'الآن' });
|
||||
}
|
||||
|
||||
// 2. Also broadcast over WebSocket
|
||||
if (wsSocket && wsSocket.readyState === WebSocket.OPEN) {
|
||||
wsSocket.send(JSON.stringify({
|
||||
event: 'chat_send',
|
||||
data: {
|
||||
receiver_id: targetId,
|
||||
message: message,
|
||||
message_type: 'text'
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user