`);w.document.close(); } function settings(){ const u=currentUser();shell("الإعدادات",`

اللغة

الحساب

${esc(u.name)} — ${esc(u.username)}

`); } function changeLang(v){const u=currentUser();u.language=v;db.settings.language=v;commit();toast("تم حفظ اللغة")} function exportPlatformBackup(){ if(!requireAdmin())return; const payload={version:3,exportedAt:now(),db}; const blob=new Blob([JSON.stringify(payload,null,2)],{type:"application/json"}); const a=document.createElement("a");a.href=URL.createObjectURL(blob);a.download="be-first-backup.json";a.click();URL.revokeObjectURL(a.href); audit("backup_exported");toast("تم تصدير النسخة الاحتياطية"); } function importPlatformBackup(){ if(!requireAdmin())return; const input=document.createElement("input");input.type="file";input.accept=".json,application/json"; input.onchange=async()=>{ try{ const f=input.files?.[0];if(!f)return; const payload=JSON.parse(await f.text()); if(!payload?.db?.users||!payload?.db?.courses)throw Error("ملف النسخة الاحتياطية غير صالح"); if(!confirm("سيتم استبدال البيانات الحالية بهذه النسخة. هل تريد المتابعة؟"))return; db=payload.db;db.progress=db.progress||[];db.auditLog=db.auditLog||[];db.settings=db.settings||{language:"ar"};commit(); audit("backup_imported");toast("تم استيراد النسخة الاحتياطية");render(); }catch(e){toast("فشل الاستيراد: "+e.message)} }; input.click(); } function firebaseReadyPanel(){ if(!requireAdmin())return; const s=db.settings||{}; modal("Firebase / Production Setup",`
هذه النسخة مجهزة لفصل الواجهة عن طبقة البيانات. لا تضع مفاتيح سرية أو Service Account داخل HTML. للـProduction استخدم Firebase Auth + Firestore/Realtime Database + Security Rules + Cloud Functions للعمليات الحساسة.

حاليًا يتم حفظ البيانات محليًا. إدخال Project ID وحده لا يحول التطبيق تلقائيًا إلى Backend آمن.

`); } function saveFirebaseSettings(){ db.settings=db.settings||{}; db.settings.firebaseProjectId=document.getElementById("fbProjectId").value.trim(); db.settings.firebaseAuthDomain=document.getElementById("fbAuthDomain").value.trim(); db.settings.firebaseDbUrl=document.getElementById("fbDbUrl").value.trim(); db.settings.backendMode=document.getElementById("fbMode").value; commit();audit("firebase_settings_updated",{mode:db.settings.backendMode}); document.querySelector(".modal")?.remove();toast("تم حفظ إعدادات Backend");admin(); } function admin(){ if(!requireAdmin())return; const students=db.users.filter(u=>u.role==="Student").length; const completed=(db.progress||[]).length; shell("لوحة الإدارة",`
الطلاب
${students}
الكورسات
${db.courses.length}
الاختبارات
${db.quizzes.length}
الأكواد
${db.codes.length}
المحاولات
${db.attempts.length}
المقالات المعلقة
${db.attempts.reduce((n,a)=>n+(a.pendingEssayCount||0),0)}
المحتوى المكتمل
${completed}
الإحصائيات

أدوات النظام

الكورسات

${db.courses.map(c=>`
${esc(c.title)}
`).join("")||"لا يوجد"}

الاختبارات

${db.quizzes.map(q=>`
${esc(q.title)}
`).join("")||"لا توجد اختبارات"}
`); } function adminAudit(){ if(!requireAdmin())return; const rows=(db.auditLog||[]).slice().reverse(); shell("سجل العمليات",`
${rows.map(x=>``).join("")||''}
الوقتالعمليةالمستخدمالتفاصيل
${esc(x.at)}${esc(x.action)}${esc(API.userById(x.userId)?.username||"system")}${esc(JSON.stringify(x.details||{}))}
لا يوجد سجل.
`); } function modal(title,html){const d=document.createElement("div");d.className="modal";d.innerHTML=`

${title}

${html}
`;document.body.appendChild(d);return d} function adminCourseModal(c=null){ if(!requireAdmin())return; const m=modal(c?"تعديل كورس":"إضافة كورس",`
`);return m; } function saveCourse(f,id){if(id){const c=API.course(id);c.title=f.title.value.trim();c.description=f.description.value}else{db.courses.push({id:uid(),title:f.title.value.trim(),description:f.description.value,createdAt:now()})}commit();document.querySelector(".modal")?.remove();toast("تم الحفظ");admin()} function deleteCourse(id){if(!confirm("حذف الكورس وكل محتواه؟"))return;db.courses=db.courses.filter(c=>c.id!==id);db.sections=db.sections.filter(s=>s.courseId!==id);db.contents=db.contents.filter(x=>x.courseId!==id);db.quizzes=db.quizzes.filter(q=>q.courseId!==id);db.codes=db.codes.filter(c=>c.courseId!==id);db.courseAccess=db.courseAccess.filter(a=>a.courseId!==id);commit();admin()} function adminCourse(id){ if(!requireAdmin())return;const c=API.course(id);if(!c)return; shell("إدارة الكورس",`

${esc(c.title)}

${esc(c.description)}

Sections / Units

${db.sections.filter(s=>s.courseId===id).map(s=>`
${esc(s.title)}
${db.contents.filter(x=>x.sectionId===s.id).map(x=>`
${esc(x.title)} ${esc(x.type)}
`).join("")}
`).join("")||'

لا توجد Sections.

'}
`); } function sectionModal(courseId){modal("إضافة Section",`
`)} function contentModal(sectionId){ const s=API.section(sectionId);modal("إضافة محتوى",`
`) } function addContent(sectionId,f){const s=API.section(sectionId);db.contents.push({id:uid(),courseId:s.courseId,sectionId,title:f.title.value.trim(),type:f.type.value,url:f.url.value.trim(),description:f.description.value,order:db.contents.filter(x=>x.sectionId===sectionId).length+1});commit();document.querySelector(".modal").remove();go("adminCourse/"+s.courseId)} function deleteContent(id,courseId){db.contents=db.contents.filter(x=>x.id!==id);commit();go("adminCourse/"+courseId)} function adminQuizModal(q=null){ if(!requireAdmin())return;const courses=db.courses;modal(q?"تعديل اختبار":"إضافة اختبار",`
`) } function saveQuiz(f,id){ const data={title:f.title.value.trim(),description:f.description.value,courseId:f.courseId.value,sectionId:f.sectionId.value||null,duration:+f.duration.value,maxAttempts:+f.maxAttempts.value,passingPercentage:+f.passingPercentage.value,isVisible:f.isVisible.checked,showResultImmediately:f.showResultImmediately.checked,showCorrectAnswers:f.showCorrectAnswers.checked,shuffleQuestions:f.shuffleQuestions.checked,shuffleOptions:f.shuffleOptions.checked,updatedAt:now()}; if(id)Object.assign(API.quiz(id),data);else db.quizzes.push({id:uid(),...data,createdAt:now()});commit();document.querySelector(".modal").remove();toast("تم حفظ الاختبار");admin() } function adminQuiz(id){if(!requireAdmin())return;const q=API.quiz(id);shell("إدارة الاختبار",`

${esc(q.title)}

${esc(q.description)}

${q.duration} دقيقة · ${q.maxAttempts} محاولات · نجاح ${q.passingPercentage}%

`)} function adminQuestions(id){ if(!requireAdmin())return;const q=API.quiz(id);const qs=db.questions.filter(x=>x.quizId===id);shell("أسئلة الاختبار",`

${esc(q.title)}

${qs.map((x,i)=>`
${i+1}. ${esc(x.text)}

${esc(x.type)} · ${x.points} نقطة

${x.options?.map(o=>`
${o.correct?"✓ ":""}${esc(o.text)}
`).join("")||""}
`).join("")||'

لا توجد أسئلة.

'}
`); } function questionModal(quizId){ modal("إضافة سؤال",`
`) } function addQuestion(quizId,f){ const type=f.type.value;let options=[]; if(type==="true_false")options=[{id:uid(),text:"True",correct:false},{id:uid(),text:"False",correct:false}]; else if(type!=="essay")options=f.options.value.split(/\n/).map(s=>s.trim()).filter(Boolean).map(s=>({id:uid(),text:s.replace(/^\*/,""),correct:s.startsWith("*")})); if(type==="true_false"&&!options.some(x=>x.correct))options[0].correct=true; if(type!=="essay"&&!options.some(x=>x.correct))throw Error("يجب تحديد إجابة صحيحة بعلامة *"); db.questions.push({id:uid(),quizId,type,text:f.text.value.trim(),points:+f.points.value,options});commit();document.querySelector(".modal").remove();go("adminQuestions/"+quizId) } function deleteQuestion(id,qid){db.questions=db.questions.filter(x=>x.id!==id);commit();go("adminQuestions/"+qid)} function deleteQuiz(id){ if(!requireAdmin())return; if(!confirm("حذف الاختبار وأسئلته ومحاولاته؟"))return; db.quizzes=db.quizzes.filter(q=>q.id!==id); db.questions=db.questions.filter(q=>q.quizId!==id); db.attempts=db.attempts.filter(a=>a.quizId!==id); db.favorites=db.favorites.filter(f=>f.quizId!==id); commit();audit("quiz_deleted",{quizId:id});toast("تم حذف الاختبار");admin(); } function questionEditModal(qid,quizId){ if(!requireAdmin())return; const q=db.questions.find(x=>x.id===qid);if(!q)return; const opts=(q.options||[]).map(o=>(o.correct?"*":"")+o.text).join("\\n"); modal("تعديل السؤال",`
`); } function saveQuestionEdit(qid,quizId,f){ const q=db.questions.find(x=>x.id===qid);if(!q)return; q.type=f.type.value;q.text=f.text.value.trim();q.points=Number(f.points.value); let options=[]; if(q.type==="true_false")options=[{id:uid(),text:"True",correct:false},{id:uid(),text:"False",correct:false}]; else if(q.type!=="essay")options=f.options.value.split(/\n/).map(s=>s.trim()).filter(Boolean).map(s=>({id:uid(),text:s.replace(/^\*/,""),correct:s.startsWith("*")})); if(q.type!=="essay"&&!options.some(o=>o.correct))return toast("يجب تحديد إجابة صحيحة بعلامة *"); if(q.type==="true_false"&&!options.some(o=>o.correct))options[0].correct=true; q.options=options;commit();audit("question_updated",{questionId:qid});document.querySelector(".modal")?.remove();go("adminQuestions/"+quizId); } function adminCodes(){ if(!requireAdmin())return; shell("أكواد التفعيل",`
${db.codes.map(c=>``).join("")||''}
الكودالكورسالحالةالمستخدمالوقت
${esc(c.code)}${esc(API.course(c.courseId)?.title)}${c.status}${esc(API.userById(c.usedBy)?.username||"")}${esc(c.usedAt||"")}
لا توجد أكواد.
`); } function codeModal(){ modal("إنشاء أكواد",`
`) } function generateCodes(f){ const count=Math.min(5000,Math.max(1,+f.count.value)),pre=f.prefix.value||"",suf=f.suffix.value||""; if(pre.length+suf.length>10)throw Error("Prefix + Suffix لا يمكن أن يتجاوزا 10"); const used=new Set(db.codes.map(c=>c.code));let made=0; while(made[c.code,API.course(c.courseId)?.title||"",c.status,API.userById(c.usedBy)?.username||"",c.usedAt||""])]; const csv=rows.map(r=>r.map(v=>`"${String(v).replace(/"/g,'""')}"`).join(",")).join("\n");const blob=new Blob(["\ufeff"+csv],{type:"text/csv;charset=utf-8"});const a=document.createElement("a");a.href=URL.createObjectURL(blob);a.download="be-first-codes.csv";a.click();URL.revokeObjectURL(a.href) } function adminAttempts(){ if(!requireAdmin())return;const rows=db.attempts.slice().sort((a,b)=>b.startedAt.localeCompare(a.startedAt));shell("نتائج الطلاب",`
${attemptRows(rows)}
`); } function attemptRows(rows){return `${rows.map(a=>``).join("")}
الطالبالاختبارالمحاولةالدرجةالنسبةالحالة
${esc(API.userById(a.studentId)?.username)}${esc(API.quiz(a.quizId)?.title)}${a.attemptNumber}${a.score??"—"}${a.percentage??"—"}%${a.status==="pending_grading"?"Pending":a.pass?"Pass":"Fail"}
`} function filterAdminAttempts(){const q=(document.getElementById("resQ").value||"").toLowerCase(),p=document.getElementById("resPass").value;const rows=db.attempts.filter(a=>(API.userById(a.studentId)?.username||"").toLowerCase().includes(q)||(API.userById(a.studentId)?.name||"").toLowerCase().includes(q)).filter(a=>!p||(p==="pass"?a.pass===true:a.pass===false));document.getElementById("resultsTable").innerHTML=attemptRows(rows)} function adminEssays(){ if(!requireAdmin())return;const pending=[];db.attempts.forEach(a=>a.questionResults?.forEach(r=>{const x=db.questions.find(q=>q.id===r.questionId);if(x?.type==="essay"&&!r.graded)pending.push({a,r,x})}));shell("تصحيح الأسئلة المقالية",`
${pending.map((p,i)=>`

${esc(p.x.text)}

الطالب: ${esc(API.userById(p.a.studentId)?.name)}

الإجابة: ${esc(p.r.answer)}

الحد الأقصى: ${p.r.maxScore}

`).join("")||'
لا توجد أسئلة مقالية معلقة.
'}
`); } function gradeEssay(aid,qid,score,comment){ const a=db.attempts.find(x=>x.id===aid),r=a.questionResults.find(x=>x.questionId===qid);r.score=Math.min(Number(score),Number(r.maxScore));r.graded=true;r.comment=comment;a.pendingEssayCount=Math.max(0,(a.pendingEssayCount||1)-1);a.score=a.questionResults.reduce((s,x)=>s+(Number(x.score)||0),0);a.maxScore=a.questionResults.reduce((s,x)=>s+(Number(x.maxScore)||0),0);a.percentage=a.maxScore?Math.round(a.score/a.maxScore*100):0;const q=API.quiz(a.quizId);if(a.pendingEssayCount===0){a.status="submitted";a.pass=a.percentage>=q.passingPercentage}commit();toast("تم حفظ التصحيح");adminEssays() } function adminUsers(){ if(!requireAdmin())return;shell("المستخدمون",`
${db.users.map(u=>``).join("")}
الاسمUsernameEmailRoleتاريخ
${esc(u.name)}${esc(u.username)}${esc(u.email)}${esc(u.role)}${esc(u.createdAt)}
`) } function adminStats(){ if(!requireAdmin())return;const total=db.attempts.length, students=new Set(db.attempts.map(a=>a.studentId)).size, scored=db.attempts.filter(a=>a.percentage!=null),avg=scored.length?Math.round(scored.reduce((s,a)=>s+a.percentage,0)/scored.length):0,passed=scored.filter(a=>a.pass).length,passRate=scored.length?Math.round(passed/scored.length*100):0;const counts=db.quizzes.map(q=>({q,n:db.attempts.filter(a=>a.quizId===q.id).length,avg:(()=>{const z=db.attempts.filter(a=>a.quizId===q.id&&a.percentage!=null);return z.length?Math.round(z.reduce((s,a)=>s+a.percentage,0)/z.length):0})()})).sort((a,b)=>b.n-a.n); shell("إحصائيات الاختبارات",`
عدد الاختبارات
${db.quizzes.length}
المحاولات
${total}
الطلاب المشاركون
${students}
متوسط الدرجات
${avg}%
نسبة النجاح
${passRate}%

حسب الاختبار

${counts.map(x=>`
${esc(x.q.title)}${x.n} محاولة · متوسط ${x.avg}%
`).join("")}
`) } /* ========================================================= Be First — Extension Pack - Admin quick access - WebAuthn credential registration/authentication - WhatsApp contact - Admin certificate issuing - Course access management - Printable code sheet - Content editing - Safer input validation ========================================================= */ const BF = { whatsapp: "https://wa.me/", challenge: bytes => crypto.getRandomValues(new Uint8Array(bytes)), b64: bytes => btoa(String.fromCharCode(...new Uint8Array(bytes))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,""), unb64: str => Uint8Array.from(atob(str.replace(/-/g,"+").replace(/_/g,"/")+"===".slice((str.length+3)%4)),c=>c.charCodeAt(0)), validPhone: p => /^[0-9]{8,15}$/.test(String(p||"").replace(/\D/g,"")), normalizePhone: p => String(p||"").replace(/\D/g,"") }; async function webauthnRegister(){ if(!window.PublicKeyCredential){toast("WebAuthn غير مدعوم على هذا المتصفح");return} const u=currentUser(); if(!u)return; try{ const cred=await navigator.credentials.create({publicKey:{ challenge:BF.challenge(32), rp:{name:"Be First"}, user:{id:BF.challenge(16),name:u.username,displayName:u.name}, pubKeyCredParams:[{type:"public-key",alg:-7},{type:"public-key",alg:-257}], authenticatorSelection:{residentKey:"preferred",userVerification:"preferred"}, timeout:60000, attestation:"none" }}); u.webauthn={credentialId:BF.b64(cred.rawId),registeredAt:now()}; commit();toast("تم تسجيل جهاز المصادقة بنجاح"); }catch(e){toast("تعذر تسجيل المصادقة: "+(e.message||"تم الإلغاء"))} } async function webauthnAdminAccess(){ const u=db.users.find(x=>x.role==="Admin"&&x.webauthn?.credentialId); if(!u){toast("لا يوجد جهاز Admin مسجل");return} if(!window.PublicKeyCredential){toast("WebAuthn غير مدعوم");return} try{ const cred=await navigator.credentials.get({publicKey:{ challenge:BF.challenge(32), timeout:60000, userVerification:"preferred", allowCredentials:[{type:"public-key",id:BF.unb64(u.webauthn.credentialId)}] }}); if(cred){setSession({userId:u.id});location.hash="#admin";render()} }catch(e){toast("فشل التحقق من الجهاز")} } function adminQuickLogin(){ const username=prompt("Admin username"); if(!username)return; const password=prompt("Admin Quick Password"); const u=API.userByUsername(username); if(!u||u.role!=="Admin"||password!=="123456")return toast("بيانات Admin غير صحيحة"); setSession({userId:u.id});location.hash="#admin";render(); } function openWhatsApp(){ const s=db.settings||{}; const phone=BF.normalizePhone(s.whatsappPhone||""); if(!BF.validPhone(phone))return toast("أضف رقم WhatsApp من الإعدادات أولًا"); window.open(BF.whatsapp+phone+"?text="+encodeURIComponent(s.whatsappMessage||"مرحبًا، أحتاج مساعدة من منصة Be First"),"_blank","noopener"); } function issueCertificateModal(){ if(!requireAdmin())return; const students=db.users.filter(u=>u.role==="Student"); modal("إصدار شهادة",`
`); } function issueCertificate(f){ const u=API.userById(f.studentId.value); db.certificates.push({id:uid(),studentId:u.id,studentName:u.name,date:new Date().toLocaleDateString("ar-EG"),signature:f.signature.value,stamp:f.stamp.value,createdAt:now()}); commit();audit("certificate_issued",{studentId:u.id});document.querySelector(".modal")?.remove();toast("تم إصدار الشهادة"); } function adminCertificates(){ if(!requireAdmin())return; shell("الشهادات",`
${db.certificates.map(c=>``).join("")||''}
الطالبالتاريخإجراء
${esc(c.studentName)}${esc(c.date)}
لا توجد شهادات.
`); } function manageAccessModal(){ if(!requireAdmin())return; const students=db.users.filter(u=>u.role==="Student"), courses=db.courses; modal("فتح كورس لطالب",`
`); } function grantAccess(f){ if(API.access(f.studentId.value,f.courseId.value))return toast("الكورس مفتوح بالفعل"); db.courseAccess.push({id:uid(),studentId:f.studentId.value,courseId:f.courseId.value,activatedAt:now(),source:"admin"}); commit();document.querySelector(".modal")?.remove();toast("تم فتح الكورس للطالب"); } function editContentModal(id,courseId){ const x=db.contents.find(z=>z.id===id);if(!x)return; modal("تعديل المحتوى",`
`); } function saveContentEdit(id,courseId,f){ const x=db.contents.find(z=>z.id===id);Object.assign(x,{title:f.title.value.trim(),type:f.type.value,url:f.url.value.trim(),description:f.description.value});commit();document.querySelector(".modal")?.remove();go("adminCourse/"+courseId); } function printCodeSheet(){ const rows=db.codes.filter(x=>x.status==="Unused"); const w=window.open("","_blank"); w.document.write(`Be First Codes

Be First — Course Activation Codes

${rows.map(c=>`
${esc(c.code)}
${esc(API.course(c.courseId)?.title||"")}
`).join("")}
`); w.document.close(); } function savePlatformSettings(){ const phone=document.getElementById("waPhone")?.value||""; if(phone && !BF.validPhone(phone))return toast("رقم WhatsApp غير صالح"); db.settings.whatsappPhone=BF.normalizePhone(phone); db.settings.whatsappMessage=document.getElementById("waMessage")?.value||""; commit();toast("تم حفظ الإعدادات"); } /* Extend admin routing */ const oldRender=render; render=function(route){ route=(route||location.hash.slice(1)||"home").split("/"); if(route[0]==="adminCertificates")return adminCertificates(); if(route[0]==="adminAccess")return manageAccessModal(); oldRender(route.join("/")); }; /* Extend Admin navigation through a small wrapper. */ const oldAdmin=admin; admin=function(){ if(!requireAdmin())return; oldAdmin(); setTimeout(()=>{ const main=document.querySelector("main.container"); if(!main)return; const extra=document.createElement("div"); extra.className="card";extra.style.marginTop="14px"; extra.innerHTML=`

إدارة إضافية

`; main.appendChild(extra); },0); }; /* Add WebAuthn and WhatsApp controls to Settings without changing the existing layout rules. */ const oldSettings=settings; settings=function(){ oldSettings(); setTimeout(()=>{ const main=document.querySelector("main.container");if(!main)return; const box=document.createElement("div");box.className="card";box.style.marginTop="14px"; const u=currentUser(),s=db.settings||{}; box.innerHTML=`

الأمان والتواصل

${u.role==="Admin"?`

WebAuthn / Biometric للجهاز الحالي.

`:""}
`; main.appendChild(box); },0); }; /* Admin quick access on login screen. */ const oldRenderAuth=renderAuth; renderAuth=function(){ oldRenderAuth(); setTimeout(()=>{ const box=document.querySelector(".auth-box");if(!box)return; const p=document.createElement("div");p.style.marginTop="12px"; p.innerHTML=`
`; box.appendChild(p); },0); }; /* Add certificate management button to admin stats/dashboard via delegated shortcut. */ render();