@@ -512,6 +813,31 @@ class StudentPortal
let countdownTimer = null;
let timerSeconds = 0;
let studentData = null;
+ let checkpointTriggered = false;
+
+ // Exam State
+ let currentExamIndex = 0;
+ let userSelectedAnswers = [];
+ const sampleQuestions = [
+ {
+ text: "إذا كان f(x) = (2x + 1)³، فما هي قيمة المشتقة f'(1)؟",
+ options: ["54 (3 × 2 × 3²)", "27", "18", "9"],
+ correct: 0,
+ topic: "قاعدة السلسلة"
+ },
+ {
+ text: "ما هي مشتقة الاقتران f(x) = tan(2x)؟",
+ options: ["2 sec²(2x)", "sec²(2x)", "2 tan(2x)", "-2 sec²(2x)"],
+ correct: 0,
+ topic: "المشتقات المثلثية"
+ },
+ {
+ text: "إذا كان ميل المماس لمنحنى عند النقطة (1, 3) يساوي 4، فما هي معادلة المماس؟",
+ options: ["y - 3 = 4(x - 1)", "y - 1 = 4(x - 3)", "y = 4x + 3", "y = 4x - 3"],
+ correct: 0,
+ topic: "التفسير الهندسي للمشتقة"
+ }
+ ];
document.addEventListener('DOMContentLoaded', async () => {
const token = localStorage.getItem('saqel_student_jwt');
@@ -526,8 +852,31 @@ class StudentPortal
initWebSocket(token);
await checkStudentSession(token);
}
+
+ // Video player time listener for Socratic checkpoint
+ const video = document.getElementById('lesson_video_player');
+ if (video) {
+ video.addEventListener('timeupdate', () => {
+ const cur = Math.floor(video.currentTime);
+ const dur = Math.floor(video.duration || 596);
+ document.getElementById('video_time_display').textContent = `${formatTime(cur)} / ${formatTime(dur)}`;
+
+ // Trigger checkpoint at second 15 automatically once
+ if (cur === 15 && !checkpointTriggered) {
+ checkpointTriggered = true;
+ video.pause();
+ document.getElementById('socratic_quiz_modal').style.display = 'flex';
+ }
+ });
+ }
});
+ function formatTime(secs) {
+ const m = Math.floor(secs / 60).toString().padStart(2, '0');
+ const s = (secs % 60).toString().padStart(2, '0');
+ return `${m}:${s}`;
+ }
+
// 1. Initialize Real-time WebSocket (Workerman)
function initWebSocket(token) {
if (!token) return;
@@ -538,7 +887,6 @@ class StudentPortal
wsSocket = new WebSocket(wsUrl);
wsSocket.onopen = () => {
- console.log('⚡ Student connected to Saqel Workerman Gateway');
updateWsStatus(true);
wsSocket.send(JSON.stringify({
event: 'auth',
@@ -561,11 +909,9 @@ class StudentPortal
};
wsSocket.onerror = (err) => {
- console.warn('WS Error:', err);
updateWsStatus(false);
};
} catch (e) {
- console.error('WS Connect error:', e);
updateWsStatus(false);
}
}
@@ -590,7 +936,7 @@ class StudentPortal
if (event === 'chat_message' || event === 'chat_sent') {
appendChatMessage(data);
} else if (event === 'drm_session_terminated') {
- alert('⚠️ تم فتح هذا الحساب من جهاز آخر. تم إنهاء الجلسة.');
+ alert('⚠️ تم فتح هذا الحساب من متصفح آخر.');
handleLogout();
}
}
@@ -605,8 +951,10 @@ class StudentPortal
document.getElementById('tab_btn_exams').className = (tab === 'exams') ? 'tab-btn active' : 'tab-btn';
}
- // Socratic In-Video Checkpoint Demo
+ // 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';
}
@@ -618,26 +966,34 @@ class StudentPortal
if (isCorrect) {
btn.className = 'quiz-option-btn correct';
feedback.style.color = '#34D399';
- feedback.textContent = '✓ إجابة ممتازة وصحيحة! يتم الآن استئناف شرح الحصة...';
+ 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';
- }, 2000);
+ 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.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';
- }, 2500);
+ const video = document.getElementById('lesson_video_player');
+ if (video) {
+ video.currentTime = Math.max(0, video.currentTime - 45);
+ video.play();
+ }
+ }, 2200);
}
}
+ // Real-time Chat
function appendChatMessage(msg) {
const messagesArea = document.getElementById('student_chat_messages');
const isMine = msg.is_mine || false;
@@ -657,8 +1013,6 @@ class StudentPortal
if (!message) return;
input.value = '';
-
- // Default teacher recipient (Teacher ID 1 or course teacher)
const teacherId = 1;
if (wsSocket && wsSocket.readyState === WebSocket.OPEN) {
@@ -687,18 +1041,99 @@ class StudentPortal
}
}
- // Standard Auth Handlers
+ // Exam Taking & AI Diagnostic Modal
+ function startExamModal(title, level) {
+ document.getElementById('exam_title_display').textContent = title;
+ document.getElementById('exam_question_container').style.display = 'block';
+ document.getElementById('exam_result_container').style.display = 'none';
+ document.getElementById('exam_taking_modal').style.display = 'flex';
+ currentExamIndex = 0;
+ userSelectedAnswers = [];
+ renderCurrentExamQuestion();
+ }
+
+ function closeExamModal() {
+ document.getElementById('exam_taking_modal').style.display = 'none';
+ }
+
+ function renderCurrentExamQuestion() {
+ const q = sampleQuestions[currentExamIndex];
+ document.getElementById('exam_progress_text').textContent = `السؤال ${currentExamIndex + 1} من ${sampleQuestions.length} • (${q.topic})`;
+ document.getElementById('exam_question_text').textContent = q.text;
+
+ const list = document.getElementById('exam_options_list');
+ list.innerHTML = '';
+
+ q.options.forEach((opt, idx) => {
+ const btn = document.createElement('button');
+ btn.type = 'button';
+ btn.className = 'quiz-option-btn';
+ btn.textContent = `${['أ', 'ب', 'ج', 'د'][idx]}) ${opt}`;
+ btn.onclick = () => selectExamOption(idx);
+ list.appendChild(btn);
+ });
+
+ const nextBtn = document.getElementById('btn_next_question');
+ nextBtn.textContent = (currentExamIndex === sampleQuestions.length - 1) ? 'إنهاء الامتحان وحساب التقييم ✨' : 'السؤال التالي ←';
+ }
+
+ function selectExamOption(idx) {
+ userSelectedAnswers[currentExamIndex] = idx;
+ const btns = document.querySelectorAll('#exam_options_list .quiz-option-btn');
+ btns.forEach((b, i) => {
+ if (i === idx) {
+ b.style.borderColor = 'var(--accent-blue)';
+ b.style.background = 'rgba(0, 113, 227, 0.2)';
+ } else {
+ b.style.borderColor = 'rgba(255,255,255,0.15)';
+ b.style.background = 'rgba(255,255,255,0.05)';
+ }
+ });
+ }
+
+ function handleNextQuestion() {
+ if (userSelectedAnswers[currentExamIndex] === undefined) {
+ alert('يرجى اختيار إجابة للمتابعة');
+ return;
+ }
+
+ if (currentExamIndex < sampleQuestions.length - 1) {
+ currentExamIndex++;
+ renderCurrentExamQuestion();
+ } else {
+ finishExamEvaluation();
+ }
+ }
+
+ function finishExamEvaluation() {
+ document.getElementById('exam_question_container').style.display = 'none';
+ document.getElementById('exam_result_container').style.display = 'block';
+
+ // Calculate score
+ let correctCount = 0;
+ userSelectedAnswers.forEach((ans, idx) => {
+ if (ans === sampleQuestions[idx].correct) correctCount++;
+ });
+
+ const pct = Math.round((correctCount / sampleQuestions.length) * 100);
+ document.getElementById('result_score_display').textContent = `${pct}%`;
+ document.getElementById('result_readiness_display').textContent = `+2.4%`;
+ document.getElementById('readiness_gauge_val').textContent = `89.9%`;
+ document.getElementById('readiness_status_text').textContent = `مستواك ممتاز ومتقدم (+4.8% إجمالي)`;
+ }
+
+ // Auth Logic
function showError(msg) {
document.getElementById('alert_box_success').style.display = 'none';
const errBox = document.getElementById('alert_box_error');
document.getElementById('alert_error_msg').textContent = msg;
- errBox.style.display = 'flex';
+ errBox.style.display = 'block';
}
function showSuccess(msg) {
document.getElementById('alert_box_error').style.display = 'none';
const okBox = document.getElementById('alert_box_success');
document.getElementById('alert_success_msg').textContent = msg;
- okBox.style.display = 'flex';
+ okBox.style.display = 'block';
}
function clearAlerts() {
document.getElementById('alert_box_error').style.display = 'none';
@@ -747,18 +1182,14 @@ class StudentPortal
if (e) e.preventDefault();
clearAlerts();
- const phoneInput = document.getElementById('student_phone');
- const nameInput = document.getElementById('student_fullname');
+ const phone = document.getElementById('student_phone').value.trim();
+ const fullName = document.getElementById('student_fullname').value.trim();
const sendBtn = document.getElementById('btn_send_otp');
const sendText = document.getElementById('btn_send_text');
const sendSpinner = document.getElementById('btn_send_spinner');
- const phone = phoneInput.value.trim();
- const fullName = nameInput.value.trim();
-
if (!phone) {
showError('يرجى إدخال رقم الهاتف');
- phoneInput.focus();
return;
}
diff --git a/backend/app/Views/TeacherPortal.php b/backend/app/Views/TeacherPortal.php
index e982ed1..9e38f65 100644
--- a/backend/app/Views/TeacherPortal.php
+++ b/backend/app/Views/TeacherPortal.php
@@ -439,7 +439,7 @@ class TeacherPortal
-
+
إرسال ↵