feat: Implement National ID sub-account login flow for students
- Updates verifyOtp to return identity_token for students instead of auto-login - Adds verifyNationalId API endpoint to select or create a student profile under a single phone number - Allows multiple students per guardian phone number
This commit is contained in:
@@ -590,6 +590,24 @@ class StudentPortal
|
||||
<button type="button" onclick="switchToPhoneStep()" style="width: 100%; background: transparent; border: none; color: var(--text-muted); font-size: 12px; cursor: pointer; font-weight: 600;">← تغيير رقم الهاتف</button>
|
||||
</div>
|
||||
|
||||
<!-- Step 2.5: National ID (Identity verification for multi-student) -->
|
||||
<div id="step_national_id_container" style="display: none;">
|
||||
<div style="text-align: center; margin-bottom: 24px;">
|
||||
<span style="font-size: 32px;">🪪</span>
|
||||
<h3 style="font-size: 16px; font-weight: 800; color: #FFF; margin-top: 6px;">الرقم الوطني للطالب</h3>
|
||||
<p style="font-size: 12px; color: var(--text-muted); line-height: 1.5; margin-top: 8px;">
|
||||
حفاظاً على استقلالية ملفك الدراسي وتمييزه، يرجى إدخال رقمك الوطني للوصول إلى لوحة التحكم الخاصة بك.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom: 24px;">
|
||||
<input type="text" id="student_national_id_login" placeholder="أدخل الرقم الوطني المكون من 10 خانات" class="input-text" style="font-size: 16px; text-align: center; letter-spacing: 2px;" onkeypress="if(event.key==='Enter') submitNationalId()">
|
||||
</div>
|
||||
<button type="button" id="btn_submit_national_id" onclick="submitNationalId()" class="btn-primary" style="margin-bottom: 12px;">
|
||||
<span>دخول للملف الدراسي 🚀</span>
|
||||
</button>
|
||||
<button type="button" onclick="switchToPhoneStep()" style="width: 100%; background: transparent; border: none; color: var(--text-muted); font-size: 12px; cursor: pointer; font-weight: 600;">← عودة لرقم الهاتف</button>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Student Onboarding Form (For New Students) -->
|
||||
<div id="step_onboarding_container" style="display: none;">
|
||||
<div style="text-align: center; margin-bottom: 16px;">
|
||||
@@ -1981,7 +1999,7 @@ class StudentPortal
|
||||
}
|
||||
}
|
||||
|
||||
async function verifyStudentOtp() {
|
||||
async function verifyStudentOtp() {
|
||||
const phone = document.getElementById('student_phone').value.trim();
|
||||
const otp = document.getElementById('student_otp').value.trim();
|
||||
if (!otp || otp.length < 4) {
|
||||
@@ -1996,24 +2014,61 @@ class StudentPortal
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok && data.status === 'success') {
|
||||
const token = data.data.token;
|
||||
localStorage.setItem('saqel_student_jwt', token);
|
||||
|
||||
// Check if student needs onboarding
|
||||
const isNew = data.data.user.is_new || data.data.user.name === 'طالب جديد' || data.data.user.name === 'الطالب المتميز';
|
||||
if (isNew) {
|
||||
switchToStudentOnboarding();
|
||||
if (data.data.requires_national_id) {
|
||||
localStorage.setItem('saqel_student_identity_token', data.data.identity_token);
|
||||
document.getElementById('step_otp_container').style.display = 'none';
|
||||
document.getElementById('step_national_id_container').style.display = 'block';
|
||||
document.getElementById('student_national_id_login').focus();
|
||||
} else {
|
||||
localStorage.setItem('saqel_student_user', JSON.stringify(data.data.user));
|
||||
const token = data.data.token;
|
||||
localStorage.setItem('saqel_student_jwt', token);
|
||||
renderDashboard(data.data.user);
|
||||
initWebSocket(token);
|
||||
showLuxuryToast('أهلاً بك يا بطل! 🚀', 'تم تسجيل الدخول بنجاح.');
|
||||
}
|
||||
} else {
|
||||
showError(data.message || 'رمز التحقق غير صحيح أو منتهي الصلاحية');
|
||||
}
|
||||
} catch (e) {
|
||||
showError('تعذر التحقق من الرمز');
|
||||
} catch (err) {
|
||||
showError('حدث خطأ في الاتصال بالسيرفر.');
|
||||
}
|
||||
}
|
||||
|
||||
async function submitNationalId() {
|
||||
const nationalId = document.getElementById('student_national_id_login').value.trim();
|
||||
const identityToken = localStorage.getItem('saqel_student_identity_token');
|
||||
|
||||
if (!nationalId) {
|
||||
showError('يرجى إدخال الرقم الوطني الخاص بك');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/auth/student/login-national-id', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ national_id: nationalId, identity_token: identityToken })
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (res.ok && data.status === 'success') {
|
||||
if (data.data && data.data.requires_onboarding) {
|
||||
document.getElementById('step_national_id_container').style.display = 'none';
|
||||
document.getElementById('step_onboarding_container').style.display = 'block';
|
||||
document.getElementById('onboard_student_national_id').value = nationalId;
|
||||
document.getElementById('onboard_student_national_id').disabled = true;
|
||||
} else {
|
||||
const token = data.data.token;
|
||||
localStorage.setItem('saqel_student_jwt', token);
|
||||
localStorage.setItem('saqel_student_user', JSON.stringify(data.data.user));
|
||||
localStorage.removeItem('saqel_student_identity_token');
|
||||
renderDashboard(data.data.user);
|
||||
initWebSocket(token);
|
||||
showLuxuryToast('أهلاً بك يا بطل! 🚀', 'تم تسجيل الدخول لملفك الخاص بنجاح.');
|
||||
}
|
||||
} else {
|
||||
showError(data.message || 'خطأ في التحقق من الرقم الوطني');
|
||||
}
|
||||
} catch (err) {
|
||||
showError('حدث خطأ في الاتصال بالخادم.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2026,7 +2081,8 @@ class StudentPortal
|
||||
}
|
||||
|
||||
async function completeStudentOnboarding() {
|
||||
const token = localStorage.getItem('saqel_student_jwt');
|
||||
const token = localStorage.getItem('saqel_student_jwt') || '';
|
||||
const identityToken = localStorage.getItem('saqel_student_identity_token') || '';
|
||||
const fullName = document.getElementById('onboard_student_name').value.trim();
|
||||
const grade = document.getElementById('onboard_student_grade').value;
|
||||
const stream = document.getElementById('onboard_student_stream').value;
|
||||
@@ -2046,9 +2102,10 @@ class StudentPortal
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer ' + token
|
||||
...(token ? {'Authorization': 'Bearer ' + token} : {})
|
||||
},
|
||||
body: JSON.stringify({
|
||||
identity_token: identityToken,
|
||||
full_name: fullName,
|
||||
grade_level: grade,
|
||||
stream: stream,
|
||||
@@ -2057,10 +2114,16 @@ class StudentPortal
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok && data.status === 'success') {
|
||||
const u = data.user;
|
||||
if (data.data && data.data.token) {
|
||||
localStorage.setItem('saqel_student_jwt', data.data.token);
|
||||
initWebSocket(data.data.token);
|
||||
} else if (token) {
|
||||
initWebSocket(token);
|
||||
}
|
||||
localStorage.removeItem('saqel_student_identity_token');
|
||||
const u = data.user || (data.data ? data.data.user : null);
|
||||
localStorage.setItem('saqel_student_user', JSON.stringify(u));
|
||||
renderDashboard(u);
|
||||
initWebSocket(token);
|
||||
showLuxuryToast('تم استكمال الحساب بنجاح! 🚀', `مرحباً بك يا ${fullName}`);
|
||||
} else {
|
||||
showError(data.message || 'فشل حفظ الملف الشخصي');
|
||||
|
||||
Reference in New Issue
Block a user