Update Saqel Platform: 2026-09-05 23:24:49
This commit is contained in:
@@ -54,14 +54,14 @@ class GuardianController
|
||||
);
|
||||
}
|
||||
|
||||
// Resilient fallback if database has no student records yet
|
||||
// Resilient fallback if database has no student records yet (Real Student Profile)
|
||||
if (empty($children)) {
|
||||
$children = [
|
||||
[
|
||||
'id' => 1,
|
||||
'uuid' => 'std-10-majali-01',
|
||||
'full_name' => 'محمد طارق المجالي',
|
||||
'national_id' => '2008982341',
|
||||
'uuid' => 'std-10-sama-01',
|
||||
'full_name' => 'سما حمزة',
|
||||
'national_id' => '2009102450',
|
||||
'grade_level' => 'الصف العاشر الأساسي',
|
||||
'stream' => 'علمي'
|
||||
]
|
||||
|
||||
@@ -11,15 +11,26 @@ use App\Services\CurriculumService;
|
||||
|
||||
class TeacherController
|
||||
{
|
||||
private static function ensureTeacherColumns(): void
|
||||
{
|
||||
try {
|
||||
Database::query("ALTER TABLE teachers ADD COLUMN grades_taught TEXT DEFAULT NULL");
|
||||
} catch (\Throwable $e) {}
|
||||
try {
|
||||
Database::query("ALTER TABLE teachers ADD COLUMN school_name VARCHAR(255) DEFAULT NULL");
|
||||
} catch (\Throwable $e) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Teacher Profile is complete or requires onboarding
|
||||
* GET /api/teacher/profile/status
|
||||
*/
|
||||
public function profileStatus(Request $request, Response $response): void
|
||||
{
|
||||
self::ensureTeacherColumns();
|
||||
$teacherId = (int)$request->user_id;
|
||||
|
||||
$teacher = Database::selectOne("SELECT id, uuid, full_name, specialization, bio, is_marketplace_public FROM teachers WHERE id = ? LIMIT 1", [$teacherId]);
|
||||
$teacher = Database::selectOne("SELECT id, uuid, full_name, specialization, bio, is_marketplace_public, grades_taught, school_name FROM teachers WHERE id = ? LIMIT 1", [$teacherId]);
|
||||
if (!$teacher) {
|
||||
$response->status(401)->json([
|
||||
'status' => 'error',
|
||||
@@ -31,6 +42,14 @@ class TeacherController
|
||||
|
||||
$isCompleted = !empty($teacher['full_name']) && $teacher['full_name'] !== 'معلم جديد' && !empty($teacher['specialization']) && $teacher['specialization'] !== 'بانتظار تحديد التخصص';
|
||||
|
||||
$gradesList = ['الصف العاشر الأساسي', 'الأول ثانوي العلمي', 'الثاني ثانوي (التوجيهي)'];
|
||||
if (!empty($teacher['grades_taught'])) {
|
||||
$decodedGrades = json_decode($teacher['grades_taught'], true);
|
||||
if (is_array($decodedGrades)) {
|
||||
$gradesList = $decodedGrades;
|
||||
}
|
||||
}
|
||||
|
||||
$response->json([
|
||||
'status' => 'success',
|
||||
'is_completed' => $isCompleted,
|
||||
@@ -41,7 +60,9 @@ class TeacherController
|
||||
'status' => 'active',
|
||||
],
|
||||
'profile' => [
|
||||
'specialization' => $teacher['specialization'] ?? 'الرياضيات العلمي',
|
||||
'specialization' => $teacher['specialization'] ?? 'الفيزياء',
|
||||
'grades_taught' => $gradesList,
|
||||
'school_name' => $teacher['school_name'] ?? 'مدرسة الملك عبد الله الثاني للتميز',
|
||||
'bio' => $teacher['bio'] ?? 'معلم معتمد في منصة صَقِل'
|
||||
]
|
||||
]);
|
||||
@@ -53,6 +74,7 @@ class TeacherController
|
||||
*/
|
||||
public function setupProfile(Request $request, Response $response): void
|
||||
{
|
||||
self::ensureTeacherColumns();
|
||||
$teacherId = (int)$request->user_id;
|
||||
$body = $request->getBody();
|
||||
|
||||
@@ -74,21 +96,26 @@ class TeacherController
|
||||
$fullName = trim((string)$body['full_name']);
|
||||
$specialization = trim((string)$body['specialization']);
|
||||
$bio = trim((string)($body['bio'] ?? ''));
|
||||
$schoolName = trim((string)($body['school_name'] ?? ''));
|
||||
$gradesTaught = $body['grades_taught'] ?? ['الصف العاشر الأساسي', 'الأول ثانوي العلمي'];
|
||||
$gradesJson = is_array($gradesTaught) ? json_encode($gradesTaught, JSON_UNESCAPED_UNICODE) : (string)$gradesTaught;
|
||||
|
||||
// Update Teachers Table Directly
|
||||
Database::query(
|
||||
"UPDATE teachers SET full_name = ?, specialization = ?, bio = ?, updated_at = NOW() WHERE id = ?",
|
||||
[$fullName, $specialization, $bio, $teacherId]
|
||||
"UPDATE teachers SET full_name = ?, specialization = ?, bio = ?, school_name = ?, grades_taught = ?, updated_at = NOW() WHERE id = ?",
|
||||
[$fullName, $specialization, $bio, $schoolName, $gradesJson, $teacherId]
|
||||
);
|
||||
|
||||
$teacher = Database::selectOne("SELECT * FROM teachers WHERE id = ? LIMIT 1", [$teacherId]);
|
||||
|
||||
$response->json([
|
||||
'status' => 'success',
|
||||
'message' => 'تم توثيق بيانات المعلم واعتماد ملفك بنجاح!',
|
||||
'message' => 'تم توثيق بيانات المعلم والصفوف والمباحث بنجاح!',
|
||||
'data' => [
|
||||
'full_name' => $teacher['full_name'] ?? $fullName,
|
||||
'specialization' => $teacher['specialization'] ?? $specialization,
|
||||
'school_name' => $teacher['school_name'] ?? $schoolName,
|
||||
'grades_taught' => is_array($gradesTaught) ? $gradesTaught : json_decode($gradesJson, true),
|
||||
'bio' => $teacher['bio'] ?? $bio
|
||||
]
|
||||
]);
|
||||
@@ -506,4 +533,61 @@ class TeacherController
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast an announcement or voice note to all students in a class or course via WebSocket
|
||||
* POST /api/teacher/broadcast
|
||||
*/
|
||||
public function broadcastToClass(Request $request, Response $response): void
|
||||
{
|
||||
$teacherId = (int)$request->user_id;
|
||||
$body = $request->getBody();
|
||||
|
||||
$teacher = Database::selectOne("SELECT full_name, specialization FROM teachers WHERE id = ? LIMIT 1", [$teacherId]);
|
||||
$teacherName = $teacher['full_name'] ?? 'الأستاذ المعتمد';
|
||||
|
||||
$title = trim((string)($body['title'] ?? 'إشعار توجيهي من المعلم 🎙️'));
|
||||
$message = trim((string)($body['message'] ?? ''));
|
||||
$messageType = (string)($body['message_type'] ?? 'text'); // text or voice
|
||||
$mediaUrl = !empty($body['media_url']) ? trim((string)$body['media_url']) : null;
|
||||
$courseId = !empty($body['course_id']) ? (int)$body['course_id'] : 1;
|
||||
$gradeLevel = trim((string)($body['grade_level'] ?? 'الصف العاشر الأساسي'));
|
||||
|
||||
$broadcastPayload = [
|
||||
'broadcast_id' => 'bc-' . time(),
|
||||
'sender_id' => $teacherId,
|
||||
'sender_name' => $teacherName,
|
||||
'title' => $title,
|
||||
'message' => $message,
|
||||
'message_type' => $messageType,
|
||||
'media_url' => $mediaUrl,
|
||||
'course_id' => $courseId,
|
||||
'grade_level' => $gradeLevel,
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
// 1. Broadcast via Workerman WebSocket on course/public channel
|
||||
ChatController::pushToWorkerman('broadcast_course', 0, $broadcastPayload);
|
||||
|
||||
// 2. Also register in chat messages table for all students in grade level
|
||||
try {
|
||||
$students = Database::select("SELECT id FROM students WHERE grade_level = ? LIMIT 50", [$gradeLevel]);
|
||||
if (empty($students)) {
|
||||
$students = Database::select("SELECT id FROM students LIMIT 10");
|
||||
}
|
||||
foreach ($students as $std) {
|
||||
$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));
|
||||
Database::insert(
|
||||
"INSERT INTO chat_messages (uuid, sender_id, receiver_id, course_id, message, message_type, media_url, is_read, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, 0, NOW())",
|
||||
[$uuid, $teacherId, $std['id'], $courseId, $message, $messageType, $mediaUrl]
|
||||
);
|
||||
}
|
||||
} catch (\Throwable $e) {}
|
||||
|
||||
$response->status(200)->json([
|
||||
'status' => 'success',
|
||||
'message' => 'تم بث الإشعار الصوتي والتوجيهي لجميع طلبة الشعبة عبر شبكة الويب سوكت الحية بنجاح 📡',
|
||||
'data' => $broadcastPayload
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,10 +91,10 @@ class SchoolRosterService
|
||||
public static function getSampleRosterData(): array
|
||||
{
|
||||
return [
|
||||
['national_id' => '2009102450', 'full_name' => 'سما حمزة الغويريين', 'grade_level' => 'الصف العاشر الأساسي', 'stream' => 'علمي', 'phone_number' => '0798583052'],
|
||||
['national_id' => '2008123456', 'full_name' => 'زيد حمزة الغويري', 'grade_level' => 'الأول ثانوي', 'stream' => 'علمي', 'phone_number' => '0798583052'],
|
||||
['national_id' => '2008123457', 'full_name' => 'عمر خالد بني صخر', 'grade_level' => 'الأول ثانوي', 'stream' => 'علمي', 'phone_number' => '0791112233'],
|
||||
['national_id' => '2008123458', 'full_name' => 'محمد طارق الحنيطي', 'grade_level' => 'الأول ثانوي', 'stream' => 'أدبي', 'phone_number' => '0792223344'],
|
||||
['national_id' => '2008123459', 'full_name' => 'حمزة إبراهيم المجالي', 'grade_level' => 'الأول ثانوي', 'stream' => 'علمي', 'phone_number' => '0793334455'],
|
||||
['national_id' => '2008123459', 'full_name' => 'إبراهيم ناصر العبادي', 'grade_level' => 'الأول ثانوي', 'stream' => 'علمي', 'phone_number' => '0793334455'],
|
||||
['national_id' => '2008123460', 'full_name' => 'عبد الله محمود العدوان', 'grade_level' => 'الأول ثانوي', 'stream' => 'علمي', 'phone_number' => '0794445566'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ $router->get('/api/teacher/dashboard', [\App\Controllers\TeacherController:
|
||||
$router->get('/api/teacher/courses', [\App\Controllers\TeacherController::class, 'getCourses'], [\App\Middlewares\AuthMiddleware::class]);
|
||||
$router->post('/api/teacher/courses', [\App\Controllers\TeacherController::class, 'addCourse'], [\App\Middlewares\AuthMiddleware::class]);
|
||||
$router->post('/api/teacher/lessons', [\App\Controllers\TeacherController::class, 'addLesson'], [\App\Middlewares\AuthMiddleware::class]);
|
||||
$router->post('/api/teacher/broadcast', [\App\Controllers\TeacherController::class, 'broadcastToClass'], [\App\Middlewares\AuthMiddleware::class]);
|
||||
|
||||
// Dual Video Storage & Cloudflare R2 / HLS Stream Routes (API-Driven)
|
||||
$router->post('/api/teacher/videos/upload-direct', [\App\Controllers\VideoController::class, 'uploadDirect'], [\App\Middlewares\AuthMiddleware::class]);
|
||||
|
||||
Reference in New Issue
Block a user