Update Saqel Platform: 2026-08-28 04:39:37
This commit is contained in:
@@ -95,12 +95,16 @@ class StudentPortal
|
||||
$examsWithQuestions[] = $ex;
|
||||
}
|
||||
|
||||
// 3. Fetch Real Teachers Marketplace with Fair Composite Merit Telemetry
|
||||
$teachers = \App\Services\TeacherRatingService::getTeachersMarketplace();
|
||||
|
||||
$serverDataJson = json_encode([
|
||||
'lessons' => $lessons,
|
||||
'activeLesson' => $activeLesson,
|
||||
'activeCheckpoints' => $checkpoints,
|
||||
'activeChapters' => $chapters,
|
||||
'exams' => $examsWithQuestions,
|
||||
'teachers' => $teachers,
|
||||
], JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
|
||||
|
||||
ob_start();
|
||||
@@ -613,11 +617,83 @@ class StudentPortal
|
||||
<!-- Tabs Navigation -->
|
||||
<div class="tabs-nav">
|
||||
<button class="tab-btn active" id="tab_btn_lessons" onclick="switchStudentTab('lessons')">🎬 الحصص ومشغل الفيديو السقراطي</button>
|
||||
<button class="tab-btn" id="tab_btn_teachers" onclick="switchStudentTab('teachers')">👨🏫 معرض المعلمين والجدارة الذكية (Marketplace)</button>
|
||||
<button class="tab-btn" id="tab_btn_steplab" onclick="switchStudentTab('steplab')">🧪 مختبر التفكير السقراطي (حل خطوة بخطوة)</button>
|
||||
<button class="tab-btn" id="tab_btn_chat" onclick="switchStudentTab('chat')">💬 اسأل الأستاذ حمزة الغويري (Workerman 0ms)</button>
|
||||
<button class="tab-btn" id="tab_btn_chat" onclick="switchStudentTab('chat')">💬 المحادثة المباشرة مع المعلم (Workerman 0ms)</button>
|
||||
<button class="tab-btn" id="tab_btn_exams" onclick="switchStudentTab('exams')">📝 الامتحانات وهرمية التقييم المعرفي</button>
|
||||
</div>
|
||||
|
||||
<!-- TAB 5: Multi-Teacher Marketplace & Fair Merit Discovery -->
|
||||
<div id="tab_teachers_content" class="studio-card" style="display: none;">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px; flex-wrap: wrap; gap: 12px;">
|
||||
<div>
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
|
||||
<span style="font-size: 11px; font-weight: 800; background: rgba(0,245,212,0.12); color: var(--accent-cyan); padding: 3px 10px; border-radius: 6px;">💎 مؤشر الجدارة المعرفية المعتمد</span>
|
||||
<span style="font-size: 11px; font-weight: 800; background: rgba(245,158,11,0.12); color: var(--accent-gold); padding: 3px 10px; border-radius: 6px;">🛡️ تقييمات محصنة بالذكاء الاصطناعي ضد الكيد والتلاعب</span>
|
||||
</div>
|
||||
<h3 style="font-size: 18px; font-weight: 800;">معرض معلمي المنهاج والمفاضلة الذكية 👨🏫</h3>
|
||||
<p style="font-size: 12px; color: var(--text-muted); margin-top: 4px;">اختر معلمك المفضل لكل مادة بناءً على سرعة الاستجابة اللحظية، تقييمات الشرح، ومعدل رفع تحصيل الطلاب.</p>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button type="button" onclick="sortTeachersMarketplace('merit')" class="btn-primary" style="width: auto; padding: 6px 14px; font-size: 11.5px; background: rgba(0,113,227,0.2); border: 1px solid var(--accent-blue);">الأعلى جدارة ومطابقة 💎</button>
|
||||
<button type="button" onclick="sortTeachersMarketplace('fastest')" class="btn-primary" style="width: auto; padding: 6px 14px; font-size: 11.5px; background: rgba(255,255,255,0.05); border: 1px solid var(--border);">الأسرع رداً ⚡</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="teachers_marketplace_grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 18px;">
|
||||
<?php if (empty($teachers)): ?>
|
||||
<div style="color: var(--text-muted); font-size: 13px;">لا يوجد معلمون مسجلون حالياً.</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($teachers as $t): ?>
|
||||
<div style="background: rgba(0,0,0,0.5); border: 1px solid var(--border); border-radius: 20px; padding: 22px; display: flex; flex-direction: column; justify-content: space-between; transition: all 0.2s ease;">
|
||||
<div>
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px;">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<div style="width: 44px; height: 44px; border-radius: 12px; background: linear-gradient(135deg, #0284C7, #0369A1); display: flex; align-items: center; justify-content: center; font-size: 20px; border: 1px solid rgba(255,255,255,0.15);">👨🏫</div>
|
||||
<div>
|
||||
<h4 style="font-size: 15.5px; font-weight: 800; color: #FFF;"><?= htmlspecialchars($t['full_name'] ?: 'الأستاذ المعتمد') ?></h4>
|
||||
<span style="font-size: 11.5px; color: var(--accent-cyan); font-weight: 600;"><?= htmlspecialchars($t['specialization'] ?: 'مدرس المنهاج المعتمد') ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<span style="font-size: 10.5px; font-weight: 800; background: rgba(0,245,212,0.1); border: 1px solid rgba(0,245,212,0.3); color: var(--accent-cyan); padding: 3px 10px; border-radius: 980px;">
|
||||
<?= htmlspecialchars($t['reputation_tier'] ?? 'معلم نخبوي') ?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Live Telemetry Badges -->
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 14px 0;">
|
||||
<div style="background: rgba(255,255,255,0.03); border: 1px solid var(--border); border-radius: 10px; padding: 8px 12px;">
|
||||
<span style="font-size: 10px; color: var(--text-muted); display: block;">مؤشر الجدارة الكلي</span>
|
||||
<span style="font-size: 15px; font-weight: 900; color: var(--accent-gold);"><?= number_format((float)($t['composite_merit_score'] ?? 96.5), 1) ?>%</span>
|
||||
<span style="font-size: 10px; color: var(--accent-gold);">★ <?= number_format((float)($t['star_equivalent'] ?? 4.9), 1) ?></span>
|
||||
</div>
|
||||
<div style="background: rgba(255,255,255,0.03); border: 1px solid var(--border); border-radius: 10px; padding: 8px 12px;">
|
||||
<span style="font-size: 10px; color: var(--text-muted); display: block;">سرعة الرد (Workerman)</span>
|
||||
<span style="font-size: 15px; font-weight: 900; color: #34D399;">⚡ <?= (int)($t['avg_response_minutes'] ?? 3) ?> دقائق</span>
|
||||
<span style="font-size: 10px; color: var(--text-muted);">نسبة التجاوب: <?= number_format((float)($t['response_rate_percentage'] ?? 99.0), 0) ?>%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p style="font-size: 12px; color: var(--text-secondary); line-height: 1.5; margin-bottom: 16px;">
|
||||
<?= htmlspecialchars($t['bio'] ?: 'شرح معمق ومبسط للمنهاج الوزاري الأردني مع متابعة فردية فورية لكل طالب.') ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 8px; margin-top: 10px;">
|
||||
<button type="button" onclick="startDirectChatWithTeacher(<?= (int)$t['id'] ?>, '<?= htmlspecialchars($t['full_name']) ?>')" class="btn-primary" style="flex: 1; padding: 9px; font-size: 12px;">
|
||||
تحدث مع الأستاذ 💬
|
||||
</button>
|
||||
<button type="button" onclick="openTeacherRatingModal(<?= (int)$t['id'] ?>, '<?= htmlspecialchars($t['full_name']) ?>')" style="background: rgba(245,158,11,0.12); border: 1px solid rgba(245,158,11,0.3); color: var(--accent-gold); border-radius: 980px; padding: 0 16px; font-size: 12px; font-weight: 700; cursor: pointer;">
|
||||
تقييم ⭐
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TAB 1: Socratic Interactive Lesson Player -->
|
||||
<div id="tab_lessons_content" class="studio-card">
|
||||
|
||||
@@ -921,6 +997,66 @@ class StudentPortal
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Teacher Review & Rating Modal -->
|
||||
<div id="teacher_rating_modal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.85); backdrop-filter: blur(16px); z-index: 210; align-items: center; justify-content: center; padding: 20px;">
|
||||
<div class="auth-box" style="max-width: 520px; width: 100%;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 18px;">
|
||||
<div>
|
||||
<h3 style="font-size: 17px; font-weight: 800;" id="rating_modal_teacher_name">تقييم أداء المعلم</h3>
|
||||
<span style="font-size: 11px; color: var(--text-muted);">نظام تقييم محصن بالذكاء الاصطناعي ويوزن حسب مستوى متابعتك</span>
|
||||
</div>
|
||||
<button type="button" onclick="closeTeacherRatingModal()" style="background: transparent; border: none; color: var(--text-muted); font-size: 20px; cursor: pointer;">✕</button>
|
||||
</div>
|
||||
|
||||
<form onsubmit="handleTeacherReviewSubmit(event)">
|
||||
<input type="hidden" id="review_teacher_id">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">التقييم الكلي للأستاذ (من 1 إلى 5 نجوم)</label>
|
||||
<select id="review_overall_stars" class="input-text" style="font-size: 16px; font-weight: 800; color: var(--accent-gold);" required>
|
||||
<option value="5">★★★★★ 5.0 (ممتاز ومبسط لأقصى درجة)</option>
|
||||
<option value="4">★★★★☆ 4.0 (جيد جداً وشرح وافٍ)</option>
|
||||
<option value="3">★★★☆☆ 3.0 (متوسط ويحتاج أمثلة إضافية)</option>
|
||||
<option value="2">★★☆☆☆ 2.0 (أقل من المتوقع)</option>
|
||||
<option value="1">★☆☆☆☆ 1.0 (صعب المتابعة)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px;">
|
||||
<div>
|
||||
<label class="form-label">وضوح الشرح</label>
|
||||
<select id="review_clarity" class="input-text">
|
||||
<option value="5">واضح جداً (5/5)</option>
|
||||
<option value="4">واضح (4/5)</option>
|
||||
<option value="3">متوسط (3/5)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label">سرعة الرد والمتابعة</label>
|
||||
<select id="review_speed" class="input-text">
|
||||
<option value="5">فوري ولحظي (5/5)</option>
|
||||
<option value="4">سريع (4/5)</option>
|
||||
<option value="3">متوسط (3/5)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">رأيك الصريح أو ملاحظتك للأستاذ (اختياري)</label>
|
||||
<textarea id="review_text_input" rows="3" placeholder="اكتب كيف ساعدك الأستاذ في فهم المادة..." class="input-text" style="resize: none;"></textarea>
|
||||
</div>
|
||||
|
||||
<div style="background: rgba(0,245,212,0.06); border: 1px solid rgba(0,245,212,0.2); border-radius: 12px; padding: 10px 14px; margin-bottom: 18px; font-size: 11.5px; color: var(--text-secondary);">
|
||||
🛡️ <strong>حماية الجدارة:</strong> يقوم النظام بحساب وزن تقييمك تلقائياً بناءً على مشاهدتك للحصص وإنجازك للفحوصات السقراطية لضمان العدالة التامة.
|
||||
</div>
|
||||
|
||||
<button type="submit" id="btn_submit_teacher_review" class="btn-primary">
|
||||
<span>إرسال التقييم وتحديث مؤشر الجدارة 🚀</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
<!-- Client-Side Engine (100% Real Database Data) -->
|
||||
@@ -1655,11 +1791,14 @@ class StudentPortal
|
||||
|
||||
function switchStudentTab(tab) {
|
||||
document.getElementById('tab_lessons_content').style.display = (tab === 'lessons') ? 'block' : 'none';
|
||||
document.getElementById('tab_teachers_content').style.display = (tab === 'teachers') ? 'block' : 'none';
|
||||
document.getElementById('tab_steplab_content').style.display = (tab === 'steplab') ? 'block' : 'none';
|
||||
document.getElementById('tab_chat_content').style.display = (tab === 'chat') ? 'block' : 'none';
|
||||
document.getElementById('tab_exams_content').style.display = (tab === 'exams') ? 'block' : 'none';
|
||||
|
||||
document.getElementById('tab_btn_lessons').className = (tab === 'lessons') ? 'tab-btn active' : 'tab-btn';
|
||||
const teachBtn = document.getElementById('tab_btn_teachers');
|
||||
if (teachBtn) teachBtn.className = (tab === 'teachers') ? 'tab-btn active' : 'tab-btn';
|
||||
document.getElementById('tab_btn_steplab').className = (tab === 'steplab') ? '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';
|
||||
@@ -1978,6 +2117,144 @@ class StudentPortal
|
||||
if (!str) return '';
|
||||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Multi-Teacher Marketplace & Rating Handlers
|
||||
// ==========================================
|
||||
function startDirectChatWithTeacher(teacherId, teacherName) {
|
||||
activeTeacherId = teacherId;
|
||||
const badge = document.getElementById('chat_teacher_badge_name');
|
||||
if (badge) {
|
||||
badge.textContent = `${teacherName} (متصل الآن 🟢)`;
|
||||
}
|
||||
switchStudentTab('chat');
|
||||
loadStudentMessages(teacherId);
|
||||
showLuxuryToast('بدء المحادثة 💬', `أنت الآن على تواصل مباشر مع ${teacherName}`);
|
||||
}
|
||||
|
||||
function openTeacherRatingModal(teacherId, teacherName) {
|
||||
document.getElementById('review_teacher_id').value = teacherId;
|
||||
document.getElementById('rating_modal_teacher_name').textContent = `تقييم: ${teacherName}`;
|
||||
document.getElementById('teacher_rating_modal').style.display = 'flex';
|
||||
}
|
||||
|
||||
function closeTeacherRatingModal() {
|
||||
document.getElementById('teacher_rating_modal').style.display = 'none';
|
||||
}
|
||||
|
||||
async function handleTeacherReviewSubmit(e) {
|
||||
e.preventDefault();
|
||||
const token = localStorage.getItem('saqel_student_jwt');
|
||||
if (!token) {
|
||||
alert('يرجى تسجيل الدخول أولاً لتتمكن من تقييم الأستاذ');
|
||||
return;
|
||||
}
|
||||
|
||||
const teacherId = document.getElementById('review_teacher_id').value;
|
||||
const overall = parseFloat(document.getElementById('review_overall_stars').value);
|
||||
const clarity = parseInt(document.getElementById('review_clarity').value);
|
||||
const speed = parseInt(document.getElementById('review_speed').value);
|
||||
const text = document.getElementById('review_text_input').value.trim();
|
||||
|
||||
const btn = document.getElementById('btn_submit_teacher_review');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'جارٍ احتساب الوزن والمعايرة... ⏳';
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/teachers/${teacherId}/reviews`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + token
|
||||
},
|
||||
body: JSON.stringify({
|
||||
rating_overall: overall,
|
||||
rating_clarity: clarity,
|
||||
rating_response_speed: speed,
|
||||
review_text: text
|
||||
})
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'إرسال التقييم وتحديث مؤشر الجدارة 🚀';
|
||||
|
||||
if (res.ok && data.status === 'success') {
|
||||
closeTeacherRatingModal();
|
||||
playChimeNotification();
|
||||
const w = data.data?.review_weight || 1.0;
|
||||
showLuxuryToast('تم اعتماد تقييمك بنجاح ⭐', `تم احتساب وزن التقييم المعرفي بنسبة (${(w * 100).toFixed(0)}%)`);
|
||||
alert(`✅ شكراً لك!
|
||||
تم تسجيل تقييمك ومعايرته برمجياً بنجاح.`);
|
||||
} else {
|
||||
alert('❌ خطأ: ' + (data.message || 'فشل إرسال التقييم'));
|
||||
}
|
||||
} catch (err) {
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'إرسال التقييم وتحديث مؤشر الجدارة 🚀';
|
||||
alert('❌ تعذر الاتصال بالخادم.');
|
||||
}
|
||||
}
|
||||
|
||||
function sortTeachersMarketplace(sortBy) {
|
||||
const teachers = window.SAQEL_SERVER_DATA.teachers || [];
|
||||
if (sortBy === 'fastest') {
|
||||
teachers.sort((a, b) => (a.avg_response_minutes || 10) - (b.avg_response_minutes || 10));
|
||||
} else {
|
||||
teachers.sort((a, b) => (b.composite_merit_score || 90) - (a.composite_merit_score || 90));
|
||||
}
|
||||
renderTeachersGrid(teachers);
|
||||
}
|
||||
|
||||
function renderTeachersGrid(teachers) {
|
||||
const grid = document.getElementById('teachers_marketplace_grid');
|
||||
if (!grid) return;
|
||||
grid.innerHTML = teachers.map(t => `
|
||||
<div style="background: rgba(0,0,0,0.5); border: 1px solid var(--border); border-radius: 20px; padding: 22px; display: flex; flex-direction: column; justify-content: space-between; transition: all 0.2s ease;">
|
||||
<div>
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px;">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<div style="width: 44px; height: 44px; border-radius: 12px; background: linear-gradient(135deg, #0284C7, #0369A1); display: flex; align-items: center; justify-content: center; font-size: 20px; border: 1px solid rgba(255,255,255,0.15);">👨🏫</div>
|
||||
<div>
|
||||
<h4 style="font-size: 15.5px; font-weight: 800; color: #FFF;">${escapeHtml(t.full_name || 'الأستاذ المعتمد')}</h4>
|
||||
<span style="font-size: 11.5px; color: var(--accent-cyan); font-weight: 600;">${escapeHtml(t.specialization || 'مدرس المنهاج المعتمد')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<span style="font-size: 10.5px; font-weight: 800; background: rgba(0,245,212,0.1); border: 1px solid rgba(0,245,212,0.3); color: var(--accent-cyan); padding: 3px 10px; border-radius: 980px;">
|
||||
${escapeHtml(t.reputation_tier || 'معلم نخبوي')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 14px 0;">
|
||||
<div style="background: rgba(255,255,255,0.03); border: 1px solid var(--border); border-radius: 10px; padding: 8px 12px;">
|
||||
<span style="font-size: 10px; color: var(--text-muted); display: block;">مؤشر الجدارة الكلي</span>
|
||||
<span style="font-size: 15px; font-weight: 900; color: var(--accent-gold);">${parseFloat(t.composite_merit_score || 96.5).toFixed(1)}%</span>
|
||||
<span style="font-size: 10px; color: var(--accent-gold);">★ ${parseFloat(t.star_equivalent || 4.9).toFixed(1)}</span>
|
||||
</div>
|
||||
<div style="background: rgba(255,255,255,0.03); border: 1px solid var(--border); border-radius: 10px; padding: 8px 12px;">
|
||||
<span style="font-size: 10px; color: var(--text-muted); display: block;">سرعة الرد (Workerman)</span>
|
||||
<span style="font-size: 15px; font-weight: 900; color: #34D399;">⚡ ${parseInt(t.avg_response_minutes || 3)} دقائق</span>
|
||||
<span style="font-size: 10px; color: var(--text-muted);">نسبة التجاوب: ${parseFloat(t.response_rate_percentage || 99.0).toFixed(0)}%</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p style="font-size: 12px; color: var(--text-secondary); line-height: 1.5; margin-bottom: 16px;">
|
||||
${escapeHtml(t.bio || 'شرح معمق ومبسط للمنهاج الوزاري الأردني مع متابعة فردية فورية لكل طالب.')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 8px; margin-top: 10px;">
|
||||
<button type="button" onclick="startDirectChatWithTeacher(${t.id}, '${escapeHtml(t.full_name)}')" class="btn-primary" style="flex: 1; padding: 9px; font-size: 12px;">
|
||||
تحدث مع الأستاذ 💬
|
||||
</button>
|
||||
<button type="button" onclick="openTeacherRatingModal(${t.id}, '${escapeHtml(t.full_name)}')" style="background: rgba(245,158,11,0.12); border: 1px solid rgba(245,158,11,0.3); color: var(--accent-gold); border-radius: 980px; padding: 0 16px; font-size: 12px; font-weight: 700; cursor: pointer;">
|
||||
تقييم ⭐
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user