Update Saqel Platform: 2026-08-28 15:19:58

This commit is contained in:
Hamza-Ayed
2026-08-28 15:19:58 +03:00
parent 06461635bf
commit 070e80ea30
4 changed files with 30 additions and 40 deletions
+14 -17
View File
@@ -21,16 +21,14 @@ class AuthController
$body = $request->getBody(); $body = $request->getBody();
$validator = new Validator(); $validator = new Validator();
if (!$validator->validate($body, ['phone_number' => 'required'])) { $rawPhone = trim((string)($body['phone_number'] ?? $body['phone'] ?? ''));
if (empty($rawPhone)) {
$response->status(400)->json([ $response->status(400)->json([
'status' => 'error', 'status' => 'error',
'message' => 'رقم الهاتف مطلوب', 'message' => 'رقم الهاتف مطلوب'
'errors' => $validator->getErrors()
]); ]);
return; return;
} }
$rawPhone = trim((string)$body['phone_number']);
$cleanPhone = preg_replace('/\D+/', '', $rawPhone); $cleanPhone = preg_replace('/\D+/', '', $rawPhone);
// Normalize Jordanian numbers (e.g. 079XXXXXXX -> 96279XXXXXXX) // Normalize Jordanian numbers (e.g. 079XXXXXXX -> 96279XXXXXXX)
@@ -141,27 +139,23 @@ class AuthController
$body = $request->getBody(); $body = $request->getBody();
$validator = new Validator(); $validator = new Validator();
if (!$validator->validate($body, [ $rawPhone = trim((string)($body['phone_number'] ?? $body['phone'] ?? ''));
'phone_number' => 'required', $inputOtp = trim((string)($body['otp'] ?? $body['otp_code'] ?? ''));
'otp' => 'required'
])) { if (empty($rawPhone) || empty($inputOtp)) {
$response->status(400)->json([ $response->status(400)->json([
'status' => 'error', 'status' => 'error',
'message' => 'رقم الهاتف ورمز التحقق مطلوبان', 'message' => 'رقم الهاتف ورمز التحقق مطلوبان'
'errors' => $validator->getErrors()
]); ]);
return; return;
} }
$rawPhone = trim((string)$body['phone_number']);
$cleanPhone = preg_replace('/\D+/', '', $rawPhone); $cleanPhone = preg_replace('/\D+/', '', $rawPhone);
if (str_starts_with($cleanPhone, '07')) { if (str_starts_with($cleanPhone, '07')) {
$cleanPhone = '962' . substr($cleanPhone, 1); $cleanPhone = '962' . substr($cleanPhone, 1);
} elseif (str_starts_with($cleanPhone, '7') && strlen($cleanPhone) === 9) { } elseif (str_starts_with($cleanPhone, '7') && strlen($cleanPhone) === 9) {
$cleanPhone = '962' . $cleanPhone; $cleanPhone = '962' . $cleanPhone;
} }
$inputOtp = trim((string)$body['otp']);
$role = $body['role'] ?? 'student'; $role = $body['role'] ?? 'student';
$fullName = trim((string)($body['full_name'] ?? '')); $fullName = trim((string)($body['full_name'] ?? ''));
$deviceFingerprint = trim((string)($body['device_fingerprint'] ?? 'browser_default')); $deviceFingerprint = trim((string)($body['device_fingerprint'] ?? 'browser_default'));
@@ -219,14 +213,16 @@ class AuthController
// Resolve or create specific Persona entity (Teacher, Student, Guardian) // Resolve or create specific Persona entity (Teacher, Student, Guardian)
if ($role === 'teacher') { if ($role === 'teacher') {
$teacher = Database::selectOne("SELECT * FROM teachers WHERE identity_id = ? LIMIT 1", [$identityId]); $teacher = Database::selectOne("SELECT * FROM teachers WHERE identity_id = ? LIMIT 1", [$identityId]);
$isNewTeacher = false;
if (!$teacher) { if (!$teacher) {
$isNewTeacher = true;
$tUuid = 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)); $tUuid = 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));
$tId = Database::insert( $tId = Database::insert(
"INSERT INTO teachers (uuid, identity_id, full_name, specialization, bio, is_school_exclusive, is_marketplace_public) "INSERT INTO teachers (uuid, identity_id, full_name, specialization, bio, is_school_exclusive, is_marketplace_public)
VALUES (?, ?, ?, 'الرياضيات العلمي', 'معلم معتمد في منصة صَقِل', 0, 1)", VALUES (?, ?, ?, 'بانتظار تحديد التخصص', '', 0, 1)",
[$tUuid, $identityId, $resolvedName] [$tUuid, $identityId, $fullName ?: 'معلم جديد']
); );
$teacher = ['id' => $tId, 'uuid' => $tUuid, 'full_name' => $resolvedName]; $teacher = ['id' => $tId, 'uuid' => $tUuid, 'full_name' => $fullName ?: 'معلم جديد', 'specialization' => 'بانتظار تحديد التخصص'];
} }
$user = [ $user = [
'id' => $teacher['id'], 'id' => $teacher['id'],
@@ -234,6 +230,7 @@ class AuthController
'full_name' => $teacher['full_name'], 'full_name' => $teacher['full_name'],
'role' => 'teacher', 'role' => 'teacher',
'status' => 'active', 'status' => 'active',
'is_new' => $isNewTeacher,
'token_version' => $identity['token_version'], 'token_version' => $identity['token_version'],
'school_id' => $teacher['school_id'] ?? null 'school_id' => $teacher['school_id'] ?? null
]; ];
+10 -21
View File
@@ -21,33 +21,22 @@ class TeacherController
$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 FROM teachers WHERE id = ? LIMIT 1", [$teacherId]);
if (!$teacher) { if (!$teacher) {
// Auto create teacher record if not exists $response->status(401)->json([
$tUuid = 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)); 'status' => 'error',
$tId = Database::insert( 'message' => 'حساب المعلم غير مسجل أو يتطلب تسجيل الدخول',
"INSERT INTO teachers (uuid, full_name, specialization, bio, is_marketplace_public) VALUES (?, 'الأستاذ المعتمد', 'الرياضيات العلمي', 'معلم معتمد في منصة صَقِل', 1)", 'is_completed' => false
[$tUuid] ]);
); return;
$teacher = [
'id' => $tId,
'uuid' => $tUuid,
'full_name' => 'الأستاذ المعتمد',
'specialization' => 'الرياضيات العلمي',
'bio' => 'معلم معتمد في منصة صَقِل'
];
} }
$rawName = (string)($teacher['full_name'] ?? ''); $isCompleted = !empty($teacher['full_name']) && $teacher['full_name'] !== 'معلم جديد' && !empty($teacher['specialization']) && $teacher['specialization'] !== 'بانتظار تحديد التخصص';
$decryptedName = Security::decrypt($rawName) ?: $rawName;
if (empty($decryptedName)) {
$decryptedName = 'الأستاذ المعتمد';
}
$response->json([ $response->json([
'status' => 'success', 'status' => 'success',
'is_completed' => true, 'is_completed' => $isCompleted,
'user' => [ 'user' => [
'uuid' => $teacher['uuid'], 'uuid' => $teacher['uuid'],
'full_name' => $decryptedName, 'full_name' => $teacher['full_name'],
'role' => 'teacher', 'role' => 'teacher',
'status' => 'active', 'status' => 'active',
], ],
+2 -2
View File
@@ -1923,7 +1923,7 @@ class StudentPortal
const res = await fetch('/api/auth/otp/request', { const res = await fetch('/api/auth/otp/request', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone: phone, role: 'student' }) body: JSON.stringify({ phone_number: phone, phone: phone, role: 'student' })
}); });
const data = await res.json(); const data = await res.json();
if (res.ok && data.status === 'success') { if (res.ok && data.status === 'success') {
@@ -1950,7 +1950,7 @@ class StudentPortal
const res = await fetch('/api/auth/otp/verify', { const res = await fetch('/api/auth/otp/verify', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone: phone, otp_code: otp, role: 'student' }) body: JSON.stringify({ phone_number: phone, phone: phone, otp: otp, otp_code: otp, role: 'student' })
}); });
const data = await res.json(); const data = await res.json();
if (res.ok && data.status === 'success') { if (res.ok && data.status === 'success') {
+4
View File
@@ -1281,6 +1281,10 @@ class TeacherPortal
} }
const data = await res.json(); const data = await res.json();
if (res.ok && data.status === 'success') { if (res.ok && data.status === 'success') {
if (!data.is_completed) {
switchToOnboardingStep();
return;
}
if (data.user) { if (data.user) {
localStorage.setItem('saqel_teacher_user', JSON.stringify({ ...data.user, ...data.profile })); localStorage.setItem('saqel_teacher_user', JSON.stringify({ ...data.user, ...data.profile }));
} }