import React, { useState, useEffect } from "react"; import { Home, Users, CalendarCheck, MessageSquareText, PenTool, Star, Plus, Check, X, ChevronLeft, Sparkles, LayoutDashboard, ClipboardList, FolderKanban, Mic, GraduationCap, ScrollText, ClipboardCheck, Inbox, Bell, UserRound, FileBarChart, QrCode, LogOut, Upload, Lock, Flame, BookOpen, Sprout, ShieldCheck, } from "lucide-react"; /* ================= design tokens ================= */ const C = { bg: "#FBF7EC", card: "#FFFFFF", ink: "#2B2A1E", inkSoft: "#6B6650", olive: "#4E5B27", oliveDark: "#30391A", oliveSoft: "#EAEFDD", gold: "#C9972E", goldSoft: "#F4E4B8", indigo: "#33447A", indigoSoft: "#E4E7F2", pink: "#E8A9BE", pinkSoft: "#FBEAF1", aqua: "#3FA79A", aquaSoft: "#E1F2EE", red: "#B5442E", redSoft: "#FBEAE5", line: "#E7E0CC", }; const FONTS = ` @import url('https://fonts.googleapis.com/css2?family=Reem+Kufi:wght@400..700&family=Tajawal:wght@300;400;500;700;900&display=swap'); .font-display{font-family:'Reem Kufi',sans-serif} .font-body{font-family:'Tajawal',sans-serif} @keyframes marquee-rtl{0%{transform:translateX(-100%)}100%{transform:translateX(100%)}} .marquee-track{animation:marquee-rtl 34s linear infinite;white-space:nowrap} @media (prefers-reduced-motion:reduce){.marquee-track{animation:none}} .star-badge{transition:transform .15s ease} .star-badge:hover{transform:scale(1.15) rotate(8deg)} input:focus,textarea:focus,select:focus,button:focus-visible{outline:2px solid ${C.gold};outline-offset:2px} `; function OctaStar({ size = 16, fill = C.gold }) { return ( ); } /* ================= constants ================= */ const CLASSES = ["أول متوسط/3", "أول متوسط/4", "أول متوسط/5", "أول متوسط/6"]; const MARQUEE_TEXT = "كن كمن يمشي على الرمل، يرى أثر خطواته، لكن لا تُسمع له صوتاً"; const NAMES = [ "لمى العتيبي", "جوري الحربي", "غلا القحطاني", "دانة السبيعي", "ريتاج الشمري", "نوف الدوسري", "شهد المطيري", "روان الزهراني", "سدن العنزي", "مهى الغامدي", "أثير الحارثي", "وعد البقمي", "ريناد العصيمي", "جنى المالكي", "تالا الرشيدي", "لجين آل سعد", "بشاير الجهني", "رغد الشهري", "أروى القرني", "هيا العمري", "سلمى الفيفي", "نجد الخالدي", "ليان السلمي", "رهف الحازمي", ]; function makeInitialDB() { const students = NAMES.map((name, i) => ({ id: i + 1, name, cls: CLASSES[i % CLASSES.length], pin: "1111", parentName: "ولي أمر " + name.split(" ")[0], points: 4 + ((i * 3) % 15), attendance: {}, behaviorLog: [], skills: { reading: 1 + (i % 4), handwriting: 1 + ((i + 1) % 4) }, })); const tasks = [ { id: 1, type: "واجب", title: "إعراب الجملة الاسمية", cls: "أول متوسط/3", due: "2026-08-27" }, { id: 2, type: "واجب", title: "استخراج الأفعال من نص القراءة", cls: "أول متوسط/4", due: "2026-08-28" }, { id: 3, type: "مشروع", title: "معجم مصور لعشر كلمات جديدة", cls: "أول متوسط/5", due: "2026-09-05" }, ]; return { students, tasks, submissions: [], diagnosticAttempts: {}, charterSigned: {}, learningStyleResults: {}, notifications: [ { id: 1, audience: "all", text: "مرحبًا بكن في منصة لغتي التفاعلية لهذا الفصل الدراسي 🌿", date: "2026-08-20", read: false }, ], }; } const CHARTER_LINES = [ "أحافظ على وقت الحصة وأشارك بأدب واحترام.", "أؤدي واجباتي بنفسي وبأمانة دون نسخ من غيري.", "أقرأ يوميًا ولو صفحة واحدة بصوت واضح.", "أعتني بخطي وأكتب بعناية وترتيب.", "أحترم زميلاتي ومعلمتي وأقبل الملاحظة بصدر رحب.", "أسعى لتطوير لغتي لأنها هويتي.", ]; const DIAGNOSTIC_QUESTIONS = [ { q: "ما نوع الجملة: «الطالبةُ مجتهدةٌ»؟", options: ["اسمية", "فعلية", "شرطية", "ندائية"], correct: 0 }, { q: "أعرب كلمة «مجتهدةٌ» في المثال السابق:", options: ["فاعل", "مفعول به", "خبر", "مبتدأ"], correct: 2 }, { q: "جمع كلمة «كتاب»:", options: ["كتب", "كتّاب", "كاتبون", "كتابات"], correct: 0 }, { q: "مرادف كلمة «سعيد»:", options: ["حزين", "فرح", "غاضب", "خائف"], correct: 1 }, { q: "ضد كلمة «كريم»:", options: ["بخيل", "شجاع", "طويل", "قوي"], correct: 0 }, { q: "الفعل «كتبَ» فعل:", options: ["مضارع", "أمر", "ماضٍ", "منصوب"], correct: 2 }, { q: "«يا طالبةُ» أسلوب:", options: ["استفهام", "نداء", "تعجب", "نهي"], correct: 1 }, { q: "علامة رفع المثنى:", options: ["الضمة", "الألف", "الواو", "الكسرة"], correct: 1 }, { q: "«هذا كتابٌ مفيدٌ» - كلمة «مفيدٌ»:", options: ["نعت", "بدل", "حال", "توكيد"], correct: 0 }, { q: "جمع «معلمة» جمع مؤنث سالم:", options: ["معلمات", "معلمون", "معلمين", "معالم"], correct: 0 }, { q: "حرف من حروف الجر:", options: ["إنّ", "على", "قد", "لن"], correct: 1 }, { q: "الاسم الموصول للمفرد المؤنث:", options: ["الذي", "التي", "اللذان", "اللاتي"], correct: 1 }, { q: "«لن» حرف:", options: ["جزم", "نصب", "جر", "عطف"], correct: 1 }, { q: "مثنى كلمة «قلم»:", options: ["أقلام", "قلمان", "قلمين فقط", "أقلامان"], correct: 1 }, { q: "الفاعل في «طارت العصافيرُ»:", options: ["طارت", "العصافير", "لا يوجد فاعل", "حرف جر"], correct: 1 }, { q: "همزة الوصل تكتب في:", options: ["أكل", "استخرج", "أحمد", "إنسان"], correct: 1 }, { q: "«ما أجملَ الحديقةَ» أسلوب:", options: ["تعجب", "استفهام", "شرط", "نفي"], correct: 0 }, { q: "المفعول به في «قرأتُ الدرسَ»:", options: ["قرأتُ", "الدرسَ", "التاء", "لا يوجد"], correct: 1 }, { q: "علامة نصب جمع المؤنث السالم:", options: ["الفتحة", "الكسرة", "الياء", "الواو"], correct: 1 }, { q: "«كان الجوُّ صحوًا» - كلمة «صحوًا»:", options: ["اسم كان", "خبر كان", "فاعل", "مفعول مطلق"], correct: 1 }, ]; const LEARNING_STYLE_QUESTIONS = [ { q: "عندما أتعلم كلمة جديدة أفضّل أن:", options: [{ t: "أراها مكتوبة بخط واضح وألوان", s: "بصري" }, { t: "أسمعها منطوقة عدة مرات", s: "سمعي" }, { t: "أكتبها بيدي وأحركها", s: "حركي" }] }, { q: "أتذكر الدرس بشكل أفضل عندما:", options: [{ t: "أشاهد صورًا أو خرائط ذهنية", s: "بصري" }, { t: "أستمع لشرح المعلمة", s: "سمعي" }, { t: "أطبّق وأتحرك أثناء الشرح", s: "حركي" }] }, { q: "في وقت الفراغ أفضّل:", options: [{ t: "القراءة أو مشاهدة الصور", s: "بصري" }, { t: "الاستماع للقصص الصوتية", s: "سمعي" }, { t: "الأنشطة اليدوية والحركة", s: "حركي" }] }, { q: "أفهم القاعدة النحوية أسرع عبر:", options: [{ t: "جدول أو رسم توضيحي", s: "بصري" }, { t: "شرح شفهي مكرر", s: "سمعي" }, { t: "تمارين تطبيقية مباشرة", s: "حركي" }] }, { q: "عند حفظ نص أدبي أستخدم:", options: [{ t: "كتابته وتلوين الأفكار الرئيسة", s: "بصري" }, { t: "ترديده بصوت عالٍ", s: "سمعي" }, { t: "تمثيل معانيه بالحركة", s: "حركي" }] }, { q: "أفضل طريقة لمراجعتي قبل الاختبار:", options: [{ t: "بطاقات وملخصات مرسومة", s: "بصري" }, { t: "تسجيل صوتي أستمع له", s: "سمعي" }, { t: "حل أسئلة وتمارين متكررة", s: "حركي" }] }, { q: "في الحصة أنتبه أكثر عندما:", options: [{ t: "تُعرض شرائح أو صور", s: "بصري" }, { t: "تحكي المعلمة قصة", s: "سمعي" }, { t: "نقوم بنشاط جماعي", s: "حركي" }] }, { q: "أصف نفسي بأنني:", options: [{ t: "ألاحظ التفاصيل البصرية بسرعة", s: "بصري" }, { t: "أحب الأحاديث والنقاشات", s: "سمعي" }, { t: "لا أستطيع الجلوس طويلًا دون حركة", s: "حركي" }] }, { q: "عند القراءة الجهرية أفضّل أن:", options: [{ t: "أتابع النص بعيني بتركيز", s: "بصري" }, { t: "أستمع لقراءة نموذجية أولًا", s: "سمعي" }, { t: "أقرأ بصوت مرتفع وأنا أتحرك", s: "حركي" }] }, ]; const TEACHER_TABS = [ { id: "dashboard", label: "لوحة المعلمة", icon: LayoutDashboard }, { id: "students", label: "الطالبات والفصول", icon: Users }, { id: "attendance", label: "الحضور والغياب", icon: CalendarCheck }, { id: "behavior", label: "السلوك والمشاركة", icon: MessageSquareText }, { id: "profile", label: "ملف الإنجاز", icon: UserRound }, { id: "assignments", label: "الواجبات", icon: ClipboardList }, { id: "projects", label: "المشاريع", icon: FolderKanban }, { id: "reading", label: "القراءة والتسجيل", icon: Mic }, { id: "handwriting", label: "الخط", icon: PenTool }, { id: "diagnostic", label: "الاختبار التشخيصي", icon: GraduationCap }, { id: "charter", label: "ميثاق لغتي", icon: ScrollText }, { id: "learningStyle", label: "أنماط التعلم", icon: Sparkles }, { id: "submissions", label: "الأعمال المرسلة", icon: Inbox }, { id: "grades", label: "الدرجات والتقييم", icon: ClipboardCheck }, { id: "notifications", label: "الإشعارات", icon: Bell }, { id: "reports", label: "التقارير والإحصائيات", icon: FileBarChart }, { id: "qr", label: "رمز QR", icon: QrCode }, ]; const STUDENT_TABS = [ { id: "home", label: "الرئيسية", icon: Home }, { id: "assignments", label: "الواجبات", icon: ClipboardList }, { id: "projects", label: "المشاريع", icon: FolderKanban }, { id: "reading", label: "القراءة والتسجيل", icon: Mic }, { id: "handwriting", label: "الخط", icon: PenTool }, { id: "diagnostic", label: "الاختبار التشخيصي", icon: GraduationCap }, { id: "charter", label: "ميثاق لغتي", icon: ScrollText }, { id: "learningStyle", label: "أنماط التعلم", icon: Sparkles }, { id: "myWork", label: "أعمالي ودرجاتي", icon: Inbox }, { id: "profile", label: "ملفي الشخصي", icon: UserRound }, { id: "notifications", label: "الإشعارات", icon: Bell }, ]; const PARENT_TABS = [ { id: "overview", label: "نظرة عامة", icon: LayoutDashboard }, { id: "work", label: "أعمال ابنتي", icon: Inbox }, { id: "notifications", label: "الإشعارات", icon: Bell }, ]; /* ================= tiny shared bits ================= */ function SectionHeader({ eyebrow, title, sub, accent = C.gold }) { return (
{eyebrow}

{title}

{sub &&

{sub}

}
); } function ClassPicker({ value, onChange, classes = CLASSES }) { return (
{classes.map((c) => ( ))}
); } function Card({ children, style, className = "" }) { return
{children}
; } function Badge({ children, tone = "olive" }) { const map = { olive: [C.oliveSoft, C.olive], gold: [C.goldSoft, "#8A6412"], red: [C.redSoft, C.red], indigo: [C.indigoSoft, C.indigo], pink: [C.pinkSoft, "#A45072"], aqua: [C.aquaSoft, C.aqua], }; const [bg, fg] = map[tone]; return {children}; } function EmptyState({ text }) { return

{text}

; } function fileToDataURL(file) { return new Promise((resolve, reject) => { const r = new FileReader(); r.onload = () => resolve(r.result); r.onerror = reject; r.readAsDataURL(file); }); } function todayISO() { return new Date().toISOString().slice(0, 10); } /* ================= LANDING & LOGIN ================= */ function LandingScreen({ onPickRole }) { const roles = [ { id: "teacher", label: "المعلمة", desc: "إدارة الفصول والمتابعة والتقييم", icon: LayoutDashboard, tone: C.indigo, soft: C.indigoSoft }, { id: "student", label: "الطالبة", desc: "واجباتي وأعمالي ودرجاتي", icon: GraduationCap, tone: C.aqua, soft: C.aquaSoft }, { id: "parent", label: "ولي الأمر", desc: "متابعة أداء ابنتي", icon: UserRound, tone: C.pink, soft: C.pinkSoft }, ]; return (

منصة لغتي التفاعلية

منصّة تعليميّة تفاعليّة للّغة العربيّة · إشراف الأستاذة عواطف الحربي

{MARQUEE_TEXT}{" • "}{MARQUEE_TEXT}{" • "}{MARQUEE_TEXT}
{roles.map(({ id, label, desc, icon: Icon, tone, soft }) => ( ))}
); } function LoginScreen({ role, db, onBack, onSuccess }) { const [error, setError] = useState(""); const roleMeta = { teacher: { title: "دخول المعلمة", tone: C.indigo, soft: C.indigoSoft }, student: { title: "دخول الطالبة", tone: C.aqua, soft: C.aquaSoft }, parent: { title: "دخول ولي الأمر", tone: C.pink, soft: C.pinkSoft }, }[role]; const [teacherUser, setTeacherUser] = useState(""); const [teacherPass, setTeacherPass] = useState(""); const [cls, setCls] = useState(CLASSES[0]); const studentsInClass = db.students.filter((s) => s.cls === cls); const [studentId, setStudentId] = useState(studentsInClass[0]?.id || ""); const [pin, setPin] = useState(""); const [search, setSearch] = useState(""); const matches = db.students.filter((s) => s.name.includes(search.trim())); const [parentPin, setParentPin] = useState(""); const [parentTarget, setParentTarget] = useState(null); function submitTeacher() { if (teacherUser.trim() === "المعلمة" && teacherPass === "1234") onSuccess({ role: "teacher" }); else setError("اسم المستخدم أو كلمة المرور غير صحيحة"); } function submitStudent() { const st = db.students.find((s) => s.id === Number(studentId)); if (st && pin === st.pin) onSuccess({ role: "student", studentId: st.id }); else setError("رمز الدخول غير صحيح"); } function submitParent() { if (parentTarget && parentPin === "0000") onSuccess({ role: "parent", studentId: parentTarget.id }); else setError("تأكدي من اختيار الطالبة وإدخال الرمز الصحيح"); } return (

{roleMeta.title}

{role === "teacher" && (
setTeacherUser(e.target.value)} className="font-body w-full text-sm p-3 rounded-xl border outline-none" style={{ borderColor: C.line }} /> setTeacherPass(e.target.value)} className="font-body w-full text-sm p-3 rounded-xl border outline-none" style={{ borderColor: C.line }} />

تجريبي: المستخدم «المعلمة» وكلمة المرور «1234»

{error &&

{error}

}
)} {role === "student" && (
setPin(e.target.value)} className="font-body w-full text-sm p-3 rounded-xl border outline-none" style={{ borderColor: C.line }} />

تجريبي: الرمز «1111» لجميع الطالبات

{error &&

{error}

}
)} {role === "parent" && (
{ setSearch(e.target.value); setParentTarget(null); }} className="font-body w-full text-sm p-3 rounded-xl border outline-none" style={{ borderColor: C.line }} /> {search && (
{matches.slice(0, 6).map((s) => ( ))} {matches.length === 0 &&

لا توجد نتائج

}
)} setParentPin(e.target.value)} className="font-body w-full text-sm p-3 rounded-xl border outline-none" style={{ borderColor: C.line }} />

تجريبي: الرمز «0000»

{error &&

{error}

}
)}
); } /* ================= shared submission form ================= */ function SubmitWork({ type, tasksForType, studentId, db, persist, kind }) { const [taskId, setTaskId] = useState(tasksForType[0]?.id || ""); const [text, setText] = useState(""); const [fileInfo, setFileInfo] = useState(null); const [busy, setBusy] = useState(false); const [done, setDone] = useState(false); const student = db.students.find((s) => s.id === studentId); async function handleFile(e) { const f = e.target.files?.[0]; if (!f) return; try { const dataUrl = await fileToDataURL(f); setFileInfo({ dataUrl, name: f.name, mime: f.type }); } catch { setFileInfo(null); } } function submit() { if (kind === "file" && !fileInfo) return; if (kind === "text" && !text.trim()) return; setBusy(true); const task = tasksForType.find((t) => t.id === Number(taskId)); const submission = { id: Date.now(), studentId, type, title: task ? task.title : type, cls: student.cls, date: todayISO(), status: "جديد", grade: null, note: "", attachment: kind === "file" ? { kind: type === "قراءة" ? "audio" : "image", ...fileInfo } : { kind: "text", text }, }; const newDb = { ...db, submissions: [submission, ...db.submissions] }; persist(newDb); setBusy(false); setDone(true); setText(""); setFileInfo(null); setTimeout(() => setDone(false), 2500); } return ( {tasksForType.length > 0 && ( )} {tasksForType.length === 0 && (

لا توجد مهام {type === "واجب" ? "واجبات" : "مشاريع"} لفصلكِ حاليًا، يمكنكِ رغم ذلك إرسال عمل حر.

)} {kind === "text" && (