user_id; $teacher = Database::selectOne("SELECT id, uuid, full_name, specialization, bio, is_marketplace_public FROM teachers WHERE id = ? LIMIT 1", [$teacherId]); if (!$teacher) { $response->status(401)->json([ 'status' => 'error', 'message' => 'حساب المعلم غير مسجل أو يتطلب تسجيل الدخول', 'is_completed' => false ]); return; } $isCompleted = !empty($teacher['full_name']) && $teacher['full_name'] !== 'معلم جديد' && !empty($teacher['specialization']) && $teacher['specialization'] !== 'بانتظار تحديد التخصص'; $response->json([ 'status' => 'success', 'is_completed' => $isCompleted, 'user' => [ 'uuid' => $teacher['uuid'], 'full_name' => $teacher['full_name'], 'role' => 'teacher', 'status' => 'active', ], 'profile' => [ 'specialization' => $teacher['specialization'] ?? 'الرياضيات العلمي', 'bio' => $teacher['bio'] ?? 'معلم معتمد في منصة صَقِل' ] ]); } /** * Complete or Update Teacher Onboarding Profile (Password, Name, Bio, Grades, Subjects) * POST /api/teacher/profile/setup */ public function setupProfile(Request $request, Response $response): void { $teacherId = (int)$request->user_id; $body = $request->getBody(); $validator = new Validator(); $isValid = $validator->validate($body, [ 'full_name' => 'required', 'specialization' => 'required', ]); if (!$isValid) { $response->status(400)->json([ 'status' => 'error', 'message' => 'بيانات الملف الشخصي غير مكتملة', 'errors' => $validator->getErrors() ]); return; } $fullName = trim((string)$body['full_name']); $specialization = trim((string)$body['specialization']); $bio = trim((string)($body['bio'] ?? '')); // Update Teachers Table Directly Database::query( "UPDATE teachers SET full_name = ?, specialization = ?, bio = ?, updated_at = NOW() WHERE id = ?", [$fullName, $specialization, $bio, $teacherId] ); $teacher = Database::selectOne("SELECT * FROM teachers WHERE id = ? LIMIT 1", [$teacherId]); $response->json([ 'status' => 'success', 'message' => 'تم توثيق بيانات المعلم واعتماد ملفك بنجاح!', 'data' => [ 'full_name' => $teacher['full_name'] ?? $fullName, 'specialization' => $teacher['specialization'] ?? $specialization, 'bio' => $teacher['bio'] ?? $bio ] ]); } public function getDashboard(Request $request, Response $response): void { $userId = $request->user_id; // Total Courses $coursesCount = (int)Database::selectOne("SELECT COUNT(*) as total FROM courses WHERE teacher_id = ?", [$userId])['total']; // Total Lessons $lessonsCount = (int)Database::selectOne( "SELECT COUNT(l.id) as total FROM lessons l JOIN courses c ON l.course_id = c.id WHERE c.teacher_id = ?", [$userId] )['total']; // Total Unique Active Students $studentsCount = (int)Database::selectOne( "SELECT COUNT(DISTINCT lp.user_id) as total FROM lesson_progress lp JOIN lessons l ON lp.lesson_id = l.id JOIN courses c ON l.course_id = c.id WHERE c.teacher_id = ?", [$userId] )['total']; $response->json([ 'status' => 'success', 'data' => [ 'courses_count' => $coursesCount, 'lessons_count' => $lessonsCount, 'students_count' => $studentsCount, 'rating' => 4.9, 'completion_avg' => 87.5 ] ]); } /** * List Teacher's Courses * GET /api/teacher/courses */ public function getCourses(Request $request, Response $response): void { CurriculumService::ensureSchema(); $userId = (int)$request->user_id; $courses = Database::select( "SELECT c.id, c.uuid, c.title, c.description, c.semester, c.price_jod, c.is_published, c.created_at, COUNT(l.id) as lessons_total FROM courses c LEFT JOIN lessons l ON l.course_id = c.id WHERE c.teacher_id = ? GROUP BY c.id ORDER BY c.id ASC", [$userId] ); if (empty($courses)) { // Auto-seed default courses for this teacher $uuid1 = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)); $uuid2 = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)); Database::query( "INSERT INTO courses (uuid, teacher_id, subject_id, title, description, semester, price_jod, is_published) VALUES (?, ?, 1, 'الرياضيات العلمي — توجيهي 2008 (المستوى الثالث)', 'شرح المنهاج الوزاري الجديد مع التحليل الجنائي وتطبيقات التفاضل', 'first', 35.00, 1), (?, ?, 2, 'الثقافة العسكرية والتربية الوطنية (المستوى الموحد)', 'منهاج مدارس الثقافة العسكرية المعتمد مع بنك الأسئلة والخرائط الذهنية', 'first', 25.00, 1)", [$uuid1, $userId, $uuid2, $userId] ); $courses = Database::select( "SELECT c.id, c.uuid, c.title, c.description, c.semester, c.price_jod, c.is_published, c.created_at, COUNT(l.id) as lessons_total FROM courses c LEFT JOIN lessons l ON l.course_id = c.id WHERE c.teacher_id = ? GROUP BY c.id ORDER BY c.id ASC", [$userId] ); } $response->json([ 'status' => 'success', 'data' => $courses ]); } /** * Add New Course * POST /api/teacher/courses */ public function addCourse(Request $request, Response $response): void { $body = $request->getBody(); $title = trim((string)($body['title'] ?? '')); $description = trim((string)($body['description'] ?? '')); $subjectId = (int)($body['subject_id'] ?? 1); $semester = (string)($body['semester'] ?? 'first'); $price = (float)($body['price_jod'] ?? 35.00); if (empty($title)) { $response->status(400)->json([ 'status' => 'error', 'message' => 'عنوان الدورة مطلوب' ]); return; } $uuid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x', mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000, mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff) ); $courseId = Database::insert( "INSERT INTO courses (uuid, subject_id, teacher_id, title, description, semester, price_jod, is_published) VALUES (?, ?, ?, ?, ?, ?, ?, 1)", [$uuid, $subjectId, $request->user_id, $title, $description, $semester, $price] ); $response->status(201)->json([ 'status' => 'success', 'message' => 'تم إنشاء الدورة بنجاح', 'data' => [ 'course_id' => $courseId, 'uuid' => $uuid ] ]); } /** * Add New Lesson to Course * POST /api/teacher/lessons */ public function addLesson(Request $request, Response $response): void { $body = $request->getBody(); $courseId = (int)($body['course_id'] ?? 0); $title = trim((string)($body['title'] ?? '')); $bunnyVideoId = trim((string)($body['bunny_video_id'] ?? '')); $duration = (int)($body['duration_seconds'] ?? 0); $sequenceOrder = (int)($body['sequence_order'] ?? 1); if (!$courseId || empty($title) || empty($bunnyVideoId)) { $response->status(400)->json([ 'status' => 'error', 'message' => 'معرف الدورة، عنوان الدرس، ومعرف فيديو Bunny مطلوبين' ]); return; } // Verify Course Ownership $course = Database::selectOne("SELECT id, teacher_id FROM courses WHERE id = ? LIMIT 1", [$courseId]); if (!$course || ($course['teacher_id'] != $request->user_id && $request->role !== 'super_admin')) { $response->status(403)->json([ 'status' => 'error', 'message' => 'غير مصرح: لا تملك هذه الدورة' ]); return; } $lessonId = Database::insert( "INSERT INTO lessons (course_id, title, sequence_order, bunny_video_id, duration_seconds, is_free_preview) VALUES (?, ?, ?, ?, ?, 0)", [$courseId, $title, $sequenceOrder, $bunnyVideoId, $duration] ); $response->status(201)->json([ 'status' => 'success', 'message' => 'تمت إضافة الدرس بنجاح', 'data' => [ 'lesson_id' => $lessonId ] ]); } /** * Get All Teachers for Marketplace Discovery * GET /api/teachers */ public function getMarketplaceTeachers(Request $request, Response $response): void { $queryParams = $request->getQueryParams(); $sortBy = $queryParams['sort'] ?? 'merit'; $teachers = \App\Services\TeacherRatingService::getTeachersMarketplace($sortBy); $response->json([ 'status' => 'success', 'data' => $teachers ]); } /** * Get Teacher Metrics Breakdown & Live Reputation * GET /api/teachers/{id}/metrics */ public function getTeacherMetrics(Request $request, Response $response): void { $teacherId = (int)$request->getParam('id'); if (!$teacherId) { $response->status(400)->json(['status' => 'error', 'message' => 'معرف المعلم غير صالح']); return; } $metrics = \App\Services\TeacherRatingService::recalculateTeacherMetrics($teacherId); $reviews = Database::select( "SELECT tr.*, u.full_name as student_name FROM teacher_reviews tr JOIN students u ON tr.student_id = u.id WHERE tr.teacher_id = ? AND tr.is_flagged_anomaly = 0 ORDER BY tr.created_at DESC LIMIT 20", [$teacherId] ); $response->json([ 'status' => 'success', 'data' => [ 'metrics' => $metrics, 'reviews' => $reviews ] ]); } /** * Submit Student Review with Anti-Brigade & Engagement Weighting * POST /api/teachers/{id}/reviews */ public function submitReview(Request $request, Response $response): void { $teacherId = (int)$request->getParam('id'); $studentId = $request->user_id; $body = $request->getBody(); $ratingOverall = (float)($body['rating_overall'] ?? 5.0); $clarity = (int)($body['rating_clarity'] ?? 5); $speed = (int)($body['rating_response_speed'] ?? 5); $socratic = (int)($body['rating_socratic_interaction'] ?? 5); $text = trim((string)($body['review_text'] ?? '')); $courseId = !empty($body['course_id']) ? (int)$body['course_id'] : null; $lessonId = !empty($body['lesson_id']) ? (int)$body['lesson_id'] : null; if (!$teacherId || !$studentId) { $response->status(400)->json(['status' => 'error', 'message' => 'معرف المعلم والطالب مطلوبان']); return; } $res = \App\Services\TeacherRatingService::submitReview( $teacherId, $studentId, $ratingOverall, $clarity, $speed, $socratic, $text, $courseId, $lessonId ); $response->json([ 'status' => 'success', 'message' => 'تم تسجيل تقييمك واحتساب وزنه المعرفي بنجاح', 'data' => $res ]); } /** * Get Logged-in Teacher's own reputation score * GET /api/teacher/reputation */ public function getMyReputation(Request $request, Response $response): void { $teacherId = $request->user_id; $metrics = \App\Services\TeacherRatingService::recalculateTeacherMetrics($teacherId); $response->json([ 'status' => 'success', 'data' => $metrics ]); } }