diff --git a/apps/admin_app/pubspec.yaml b/apps/admin_app/pubspec.yaml index 93535c1..09056d3 100644 --- a/apps/admin_app/pubspec.yaml +++ b/apps/admin_app/pubspec.yaml @@ -43,6 +43,9 @@ dev_dependencies: flutter_lints: ^3.0.0 +dependency_overrides: + path_provider_foundation: 2.4.2 + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/apps/student_app/lib/core/utils/app_logger.dart b/apps/student_app/lib/core/utils/app_logger.dart index 3218fa1..b548e1e 100644 --- a/apps/student_app/lib/core/utils/app_logger.dart +++ b/apps/student_app/lib/core/utils/app_logger.dart @@ -12,6 +12,20 @@ class AppLogger { } } + static void event(String name, {Map? details, String tag = 'EVENT'}) { + if (!kDebugMode) return; + final buffer = StringBuffer(); + buffer.write('⚡ [$tag] $name'); + if (details != null && details.isNotEmpty) { + try { + buffer.write(' -> ${jsonEncode(details)}'); + } catch (_) { + buffer.write(' -> $details'); + } + } + debugPrint(buffer.toString()); + } + static void request({ required String method, required Uri uri, diff --git a/apps/student_app/lib/data/models/subject_model.dart b/apps/student_app/lib/data/models/subject_model.dart index 2414f3d..2cca7a9 100644 --- a/apps/student_app/lib/data/models/subject_model.dart +++ b/apps/student_app/lib/data/models/subject_model.dart @@ -37,8 +37,18 @@ class SubjectModel { this.exams = const [], }); + List get semester1Units => units.where((u) => u.semester == 'semester_1').toList(); + List get semester2Units => units.where((u) => u.semester == 'semester_2').toList(); + List unitsForSemester(String semesterKey) { + final filtered = units.where((u) => u.semester == semesterKey).toList(); + return filtered.isNotEmpty ? filtered : units; + } + factory SubjectModel.fromJson(String id, Map json) { - final title = json['name']?.toString() ?? json['title']?.toString() ?? id; + var title = json['name']?.toString() ?? json['title']?.toString() ?? id; + if (id == 'history_10' || title.contains('تاريخ الأردن')) { + title = 'التاريخ (History 10)'; + } final englishTitle = json['english_name']?.toString() ?? ''; // Parse units if available diff --git a/apps/student_app/lib/data/repositories/curriculum_repository.dart b/apps/student_app/lib/data/repositories/curriculum_repository.dart index 64bae52..bf598c4 100644 --- a/apps/student_app/lib/data/repositories/curriculum_repository.dart +++ b/apps/student_app/lib/data/repositories/curriculum_repository.dart @@ -148,32 +148,57 @@ class CurriculumRepository { } Future> getPublishedLessonVideos(String curriculumLessonId) async { + AppLogger.event('FetchPublishedVideosRequested', details: {'curriculumLessonId': curriculumLessonId}, tag: 'CURRICULUM_REPO'); final res = await _api.get('/api/curriculum/lessons/$curriculumLessonId/videos'); final data = res is Map ? res['data'] : null; final raw = data is Map ? data['items'] : null; if (raw is! List) return const []; - return raw.whereType().map((item) => PublishedLessonVideoModel.fromJson(Map.from(item))).where((item) => item.videoVersionId.isNotEmpty).toList(); + final list = raw.whereType().map((item) => PublishedLessonVideoModel.fromJson(Map.from(item))).where((item) => item.videoVersionId.isNotEmpty).toList(); + AppLogger.event('FetchPublishedVideosSuccess', details: {'count': list.length, 'teachers': list.map((v) => v.teacherName).toList()}, tag: 'CURRICULUM_REPO'); + return list; } Future getVideoVersionPlayback(String videoVersionId) async { + AppLogger.event('FetchVideoPlaybackRequested', details: {'videoVersionId': videoVersionId}, tag: 'CURRICULUM_REPO'); final res = await _api.get('/api/video-versions/$videoVersionId/playback'); - if (res is Map && res['data'] is Map) return LessonPlaybackData.fromJson(Map.from(res['data'])); + if (res is Map && res['data'] is Map) { + final pb = LessonPlaybackData.fromJson(Map.from(res['data'])); + AppLogger.event('FetchVideoPlaybackSuccess', details: { + 'videoVersionId': pb.videoVersionId, + 'videoUrl': pb.videoUrl, + 'duration': pb.durationSeconds, + }, tag: 'CURRICULUM_REPO'); + return pb; + } throw ApiException('فشل جلب تشغيل الحصة المنشورة من الخادم'); } Future startWatchSession(String videoVersionId) async { + AppLogger.event('StartWatchSessionRequested', details: {'videoVersionId': videoVersionId}, tag: 'CURRICULUM_REPO'); final res = await _api.post('/api/video-versions/$videoVersionId/watch-sessions', body: const {}); final data = res is Map ? res['data'] : null; final source = data is Map ? data : res; final id = source is Map ? source['watch_session_id']?.toString() : null; if (id == null || id.isEmpty) throw ApiException('لم ينشئ الخادم جلسة مشاهدة.'); + AppLogger.event('StartWatchSessionSuccess', details: {'sessionId': id}, tag: 'CURRICULUM_REPO'); return id; } Future recordWatchEvent(String sessionId, int sequenceNo, String eventType, int positionMs) async { + AppLogger.event('RecordWatchEvent', details: { + 'sessionId': sessionId, + 'seq': sequenceNo, + 'type': eventType, + 'positionMs': positionMs, + }, tag: 'CURRICULUM_REPO'); await _api.post('/api/watch-sessions/$sessionId/events', body: {'sequence_no': sequenceNo, 'event_type': eventType, 'position_ms': positionMs}); } Future saveProgress({required int lessonId, required int positionSeconds, required int watchedSeconds}) async { + AppLogger.event('SaveProgressRequested', details: { + 'lessonId': lessonId, + 'position': positionSeconds, + 'watched': watchedSeconds, + }, tag: 'CURRICULUM_REPO'); await _api.post('/api/student/lessons/$lessonId/progress', body: { 'position_seconds': positionSeconds, 'watched_seconds': watchedSeconds, @@ -181,6 +206,11 @@ class CurriculumRepository { } Future submitCheckpoint({required int examId, required int questionId, required int optionId}) async { + AppLogger.event('SubmitCheckpointRequested', details: { + 'examId': examId, + 'questionId': questionId, + 'optionId': optionId, + }, tag: 'CURRICULUM_REPO'); await _api.post('/api/exams/$examId/submit', body: { 'answers': [ {'question_id': questionId, 'selected_option_id': optionId} diff --git a/apps/student_app/lib/logic/cubits/video_playback_cubit.dart b/apps/student_app/lib/logic/cubits/video_playback_cubit.dart index 376139c..8116e89 100644 --- a/apps/student_app/lib/logic/cubits/video_playback_cubit.dart +++ b/apps/student_app/lib/logic/cubits/video_playback_cubit.dart @@ -91,7 +91,13 @@ class VideoPlaybackCubit extends Cubit { } Future loadLesson(CurriculumLessonItemModel lesson, {SubjectModel? subject, String? selectedVideoVersionId}) async { - AppLogger.log('Loading Socratic playback for lesson: ${lesson.title}', tag: 'VIDEO_CUBIT'); + AppLogger.event('LoadLessonPlaybackRequested', details: { + 'lessonTitle': lesson.title, + 'lessonId': lesson.id, + 'curriculumLessonId': lesson.curriculumLessonId, + 'selectedVersionId': selectedVideoVersionId, + 'hasVideo': lesson.hasVideo, + }, tag: 'VIDEO_CUBIT'); _lastObservedPosition = -1; emit(VideoPlaybackLoading()); final storageKey = _getLessonStorageKey(lesson); @@ -102,16 +108,31 @@ class VideoPlaybackCubit extends Cubit { throw StateError('هذا الدرس غير منشور بعد ضمن حزمة المحتوى المعتمدة.'); } final published = await _repo.getPublishedLessonVideos(versionId); + AppLogger.event('PublishedVideosLoaded', details: { + 'count': published.length, + 'teachers': published.map((p) => '${p.teacherName} (⭐ ${p.rating.toStringAsFixed(1)})').toList(), + }, tag: 'VIDEO_CUBIT'); if (published.isEmpty) throw StateError('لا توجد حصة منشورة ومصرح بها لهذا الدرس بعد.'); - // Selection is performed in the lesson screen when several teachers exist. - var playback = await _repo.getVideoVersionPlayback(selectedVideoVersionId ?? published.first.videoVersionId); + + final targetVersionId = selectedVideoVersionId ?? published.first.videoVersionId; + var playback = await _repo.getVideoVersionPlayback(targetVersionId); + AppLogger.event('PlaybackDataLoaded', details: { + 'versionId': playback.videoVersionId, + 'videoUrl': playback.videoUrl, + 'durationSeconds': playback.durationSeconds, + 'checkpointsCount': playback.checkpoints.length, + }, tag: 'VIDEO_CUBIT'); + if (playback.videoUrl.isEmpty) { throw StateError('لم يربط الخادم فيديو R2 بهذا الدرس بعد.'); } _watchSessionId = null; _watchSequence = 1; _lastWatchEventPosition = 0; if (playback.videoVersionId != null && playback.videoVersionId!.isNotEmpty) { - try { _watchSessionId = await _repo.startWatchSession(playback.videoVersionId!); } catch (_) {} + try { + _watchSessionId = await _repo.startWatchSession(playback.videoVersionId!); + AppLogger.event('WatchSessionStarted', details: {'sessionId': _watchSessionId}, tag: 'VIDEO_CUBIT'); + } catch (_) {} } // Load saved resume position strictly per video @@ -125,6 +146,11 @@ class VideoPlaybackCubit extends Cubit { passedIds = savedPassed.map((s) => int.tryParse(s) ?? 0).where((id) => id > 0).toSet(); } catch (_) {} + AppLogger.event('VideoPlaybackStateEmitted', details: { + 'resumePosition': resumePos, + 'passedCheckpointsCount': passedIds.length, + }, tag: 'VIDEO_CUBIT'); + emit(VideoPlaybackReady( playbackData: playback, lessonItem: lesson, @@ -176,13 +202,16 @@ class VideoPlaybackCubit extends Cubit { void togglePlayPause() { final currentState = state; if (currentState is VideoPlaybackReady && currentState.activeCheckpoint == null) { - emit(currentState.copyWith(isPlaying: !currentState.isPlaying)); + final nextState = !currentState.isPlaying; + AppLogger.event('VideoPlayPauseToggled', details: {'isPlaying': nextState}, tag: 'VIDEO_CUBIT'); + emit(currentState.copyWith(isPlaying: nextState)); } } void pause() { final currentState = state; if (currentState is VideoPlaybackReady && currentState.isPlaying) { + AppLogger.event('VideoPaused', tag: 'VIDEO_CUBIT'); emit(currentState.copyWith(isPlaying: false)); } } @@ -190,6 +219,7 @@ class VideoPlaybackCubit extends Cubit { void play() { final currentState = state; if (currentState is VideoPlaybackReady && !currentState.isPlaying && currentState.activeCheckpoint == null) { + AppLogger.event('VideoResumed', tag: 'VIDEO_CUBIT'); emit(currentState.copyWith(isPlaying: true)); } } @@ -211,6 +241,13 @@ class VideoPlaybackCubit extends Cubit { final actualSeek = blockingCheckpoint != null ? blockingCheckpoint.timestampSeconds : seconds; _lastObservedPosition = actualSeek; + AppLogger.event('VideoSeekExecuted', details: { + 'targetSeconds': seconds, + 'actualSeek': actualSeek, + 'blockedByCheckpoint': blockingCheckpoint != null, + 'checkpointQuestion': blockingCheckpoint?.questionText, + }, tag: 'VIDEO_CUBIT'); + emit(currentState.copyWith( currentPositionSeconds: actualSeek, activeCheckpoint: blockingCheckpoint, @@ -252,7 +289,12 @@ class VideoPlaybackCubit extends Cubit { if (selectedOption.isCorrect) { // Correct Answer -> Reward readiness score (+0.5%) & resume video - AppLogger.log('✅ Correct answer! Rewarding +0.5% readiness.', tag: 'SOCRATIC_ENGINE'); + AppLogger.event('SocraticAnswerCorrect', details: { + 'checkpointId': cp.id, + 'question': cp.questionText, + 'selected': selectedOption.text, + 'bonus': 0.5, + }, tag: 'SOCRATIC_ENGINE'); final updatedPassed = Set.from(currentState.passedCheckpointIds)..add(cp.id); _lastObservedPosition = cp.timestampSeconds; @@ -279,7 +321,13 @@ class VideoPlaybackCubit extends Cubit { // Wrong Answer -> Socratic Productive Struggle: Rewind video by N seconds from checkpoint final rewindTo = (cp.timestampSeconds - cp.rewindSecondsOnFail).clamp(0, currentState.playbackData.durationSeconds); _lastObservedPosition = rewindTo; - AppLogger.log('❌ Incorrect answer. Socratic remediation: Rewinding to ${rewindTo}s.', tag: 'SOCRATIC_ENGINE'); + AppLogger.event('SocraticAnswerIncorrect', details: { + 'checkpointId': cp.id, + 'question': cp.questionText, + 'selected': selectedOption.text, + 'rewindSeconds': cp.rewindSecondsOnFail, + 'rewindTo': rewindTo, + }, tag: 'SOCRATIC_ENGINE'); emit(currentState.copyWith( clearActiveCheckpoint: true, currentPositionSeconds: rewindTo, @@ -290,7 +338,7 @@ class VideoPlaybackCubit extends Cubit { // Auto-record gap to Smart Error Notebook final correctOpt = cp.options.firstWhere( (o) => o.isCorrect, - orElse: () => SocraticOptionModel(id: 0, text: '', isCorrect: false), + orElse: () => const SocraticOptionModel(id: 0, text: '', isCorrect: false), ); ErrorNotebookRepository().logError( subjectId: currentState.subject?.id ?? 'physics_10', diff --git a/apps/student_app/lib/presentation/screens/curriculum/arabic_interactive_lab_view.dart b/apps/student_app/lib/presentation/screens/curriculum/arabic_interactive_lab_view.dart index d359902..6d05b22 100644 --- a/apps/student_app/lib/presentation/screens/curriculum/arabic_interactive_lab_view.dart +++ b/apps/student_app/lib/presentation/screens/curriculum/arabic_interactive_lab_view.dart @@ -1,9 +1,15 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import '../../../core/theme/app_colors.dart'; /// ============================================================================== -/// SAQEL ENTERPRISE - ARABIC INTERACTIVE LAB (شجرة الإعراب ومختبر العروض) +/// SAQEL ENTERPRISE - GRADE 10 ARABIC INTERACTIVE LINGUISTIC STUDIO (مختبر الضاد) /// ============================================================================== +/// أربعة أجنحة لغوية تفاعلية متقدمة تحاكي منهاج الصف العاشر الرسمي (العربية لغتي): +/// 1. ميزان الصرف والاشتقاق: كفتا ميزان تفاعليتان للمشتقات (فاعل، مفعول، مبالغة، صفة مشبهة، زمان/مكان، آلة). +/// 2. معمل البلاغة والبيان: فاحص المحسنات البديعية اللوني (طباق إيجاب وسلب، مقابلة، جناس تام وناقص، سجع). +/// 3. استوديو الإعراب وبناء التراكيب: شجرة إعراب تفاعلية وروابط نحوية مع تحدي الإعراب الوزاري. +/// 4. مختبر العروض وموسيقى الشعر: الكتابة العروضية الحركية (رموز / و 0) وبحور الشعر (الكامل، الطويل، الوافر). class ArabicInteractiveLabView extends StatefulWidget { final List>? customSentences; final String? lessonTopic; @@ -15,276 +21,272 @@ class ArabicInteractiveLabView extends StatefulWidget { }); @override - State createState() => - _ArabicInteractiveLabViewState(); + State createState() => _ArabicInteractiveLabViewState(); } class _ArabicInteractiveLabViewState extends State with SingleTickerProviderStateMixin { late TabController _tabController; - // Syntax Tree State - int _selectedSentenceIndex = 0; - int? _selectedWordIndex; + // --------------------------------------------------------------------------- + // Wing 1: Morphology & Derivations State + // --------------------------------------------------------------------------- + int _selectedRootIndex = 0; + int _selectedPatternIndex = 0; - List> get _sentences => - (widget.customSentences != null && widget.customSentences!.isNotEmpty) - ? widget.customSentences! - : _sampleSentences; + static const List> _roots = [ + { + 'root': 'ك - ت - ب', + 'meaning': 'الكتابة والجمع والخط', + 'forms': [ + {'type': 'اسم فاعل', 'weight': 'فَاعِل', 'word': 'كَاتِب', 'rule': 'ثلاثي مجرد يصاغ على وزن فاعل بدلالة من قام بالحدث.'}, + {'type': 'اسم مفعول', 'weight': 'مَفْعُول', 'word': 'مَكْتُوب', 'rule': 'يصاغ على وزن مفعول بدلالة من وقع عليه فعل الكتابة.'}, + {'type': 'اسم مكان', 'weight': 'مَفْعَل', 'word': 'مَكْتَب', 'rule': 'بفتح العين لأن عين مضارعه مضمومة (يكتُب).'}, + {'type': 'صيغة مبالغة', 'weight': 'فَعَّال', 'word': 'كَتَّاب', 'rule': 'دلالة على الكثرة والمبالغة في ممارسة الكتابة.'}, + {'type': 'اسم آلة', 'weight': 'مِفْعَال', 'word': 'مِكْتَاب', 'rule': 'أداة أو وسيلة يستعان بها لإنجاز الكتابة.'}, + ], + }, + { + 'root': 'ع - ل - م', + 'meaning': 'المعرفة والإدراك واليقين', + 'forms': [ + {'type': 'اسم فاعل', 'weight': 'فَاعِل', 'word': 'عَالِم', 'rule': 'من اتصف بالعلم وقام بالتعلم والتعليم.'}, + {'type': 'اسم مفعول', 'weight': 'مَفْعُول', 'word': 'مَعْلُوم', 'rule': 'ما انكشف وبات معلوماً للناس.'}, + {'type': 'صيغة مبالغة', 'weight': 'فَعَّال', 'word': 'عَلَّام', 'rule': 'كثير العلم وبليغ الإحاطة بالأمور.'}, + {'type': 'صفة مشبهة', 'weight': 'فَعِيل', 'word': 'عَلِيم', 'rule': 'صفة ثابتة تدل على الاتصاف الدائم بالعلم.'}, + {'type': 'اسم مكان', 'weight': 'مَفْعَل', 'word': 'مَعْلَم', 'rule': 'المكان المتميز الدال على أثر أو موقع معروف.'}, + ], + }, + { + 'root': 'ص - ن - ع', + 'meaning': 'الإتقان والابتكار والعمل الماهر', + 'forms': [ + {'type': 'اسم فاعل', 'weight': 'فَاعِل', 'word': 'صَانِع', 'rule': 'من يقوم بممارسة الصنعة والابتكار اليدوي أو الصناعي.'}, + {'type': 'اسم مفعول', 'weight': 'مَفْعُول', 'word': 'مَصْنُوع', 'rule': 'الشيء الذي تم تصنيعه وإتقان تركيبه.'}, + {'type': 'اسم مكان', 'weight': 'مَفْعَل', 'word': 'مَصْنَع', 'rule': 'مكان الإنتاج والتشغيل بفتح العين (يصنَع).'}, + {'type': 'صيغة مبالغة', 'weight': 'فَعَّال', 'word': 'صَنَّاع', 'rule': 'شديد المهارة ومفرط الإتقان في الحرفة.'}, + {'type': 'اسم آلة', 'weight': 'مِفْعَلَة', 'word': 'مَصْنَعَة', 'rule': 'المنشأة أو الأداة المعينة على الصنع.'}, + ], + }, + { + 'root': 'ح - م - د', + 'meaning': 'الشكر والثناء والاعتراف بالفضل', + 'forms': [ + {'type': 'اسم فاعل', 'weight': 'فَاعِل', 'word': 'حَامِد', 'rule': 'من يثني على المنعم ويشكره.'}, + {'type': 'اسم مفعول', 'weight': 'مَفْعُول', 'word': 'مَحْمُود', 'rule': 'من يحمده الناس وتثني على أفعاله الكريمة.'}, + {'type': 'صيغة مبالغة', 'weight': 'فَعُول', 'word': 'حَمُود', 'rule': 'دائم الحمد والشكر في السراء والضراء.'}, + {'type': 'صفة مشبهة', 'weight': 'فَعِيل', 'word': 'حَمِيد', 'rule': 'صفة ثابتة للمسلك المرضي والخلق الرفيع.'}, + ], + }, + { + 'root': 'غ - ف - ر', + 'meaning': 'الستر والصفح والتجاوز عن الذنب', + 'forms': [ + {'type': 'اسم فاعل', 'weight': 'فَاعِل', 'word': 'غَافِر', 'rule': 'من يستر الهفوة ويتجاوز عن الإساءة.'}, + {'type': 'صيغة مبالغة', 'weight': 'فَعَّال', 'word': 'غَفَّار', 'rule': 'كثير المغفرة والستر مرة بعد مرة.'}, + {'type': 'صيغة مبالغة', 'weight': 'فَعُول', 'word': 'غَفُور', 'rule': 'عظيم المغفرة واسع الرحمة والتجاوز.'}, + {'type': 'اسم مفعول', 'weight': 'مَفْعُول', 'word': 'مَغْفُور', 'rule': 'الذنب الذي عُفي عنه وسُتر صاحبه.'}, + ], + }, + ]; - final List> _sampleSentences = [ + // --------------------------------------------------------------------------- + // Wing 2: Rhetoric & Aesthetics State + // --------------------------------------------------------------------------- + int _selectedRhetoricCategory = 0; // 0: Tibaq, 1: Muqabalah, 2: Jinas, 3: Saj' + int _selectedExampleIndex = 0; + + static const List> _rhetoricCategories = [ { - 'fullText': 'إِنَّ العِلْمَ نُورٌ يَهْدِي العُقُولَ إِلَى الحَقِّ', - 'type': 'جملة اسمية منسوخة بـ (إنَّ)', - 'rootNode': 'جملة إنَّ وأخواتها', - 'words': [ + 'title': 'الطباق (Antithesis)', + 'badge': 'محسن معنوي', + 'icon': CupertinoIcons.arrow_right_arrow_left, + 'color': Color(0xFFFFD60A), + 'definition': 'الجمع بين لفظين متضادين في المعنى في سياق الكلام، وينقسم إلى طباق إيجاب وطباق سلب.', + 'examples': [ { - 'word': 'إِنَّ', - 'role': 'حرف توكيد ونصب (ناسخ)', - 'case': 'مبني على الفتح لا محل له من الإعراب', - 'explanation': 'يدخل على الجملة الاسمية فينصب المبتدأ ويرفع الخبر.', - 'tag': 'حرف ناسخ', + 'text': 'وَأَنَّهُ هُوَ أَضْحَكَ وَأَبْكَى * وَأَنَّهُ هُوَ أَمَاتَ وَأَحْيَا', + 'source': 'سورة النجم (منهاج العاشر)', + 'type': 'طباق إيجاب', + 'words': ['أَضْحَكَ ↔ أَبْكَى', 'أَمَاتَ ↔ أَحْيَا'], + 'explanation': 'اجتماع الكلمة وضدها مباشرة دون أداة نفي، مبرزاً قدرة الخالق المطلقة.', }, { - 'word': 'العِلْمَ', - 'role': 'اسم إنَّ', - 'case': 'منصوب وعلامة نصبه الفتحة الظاهرة على آخره', - 'explanation': - 'هو المسند إليه في الأصل، نُصب لدخول الحرف الناسخ عليه.', - 'tag': 'اسم منصوب', + 'text': 'قُلْ هَلْ يَسْتَوِي الَّذِينَ يَعْلَمُونَ وَالَّذِينَ لَا يَعْلَمُونَ', + 'source': 'سورة الزمر (منهاج العاشر)', + 'type': 'طباق سلب', + 'words': ['يَعْلَمُونَ ↔ لَا يَعْلَمُونَ'], + 'explanation': 'الجمع بين فعلين أحدهما مثبت والآخر منفي بأداة نفي (لا)، لإبراز فضل العلم ورفعة أهله.', }, { - 'word': 'نُورٌ', - 'role': 'خبر إنَّ', - 'case': 'مرفوع وعلامة رفعه الضمة الظاهرة على آخره', - 'explanation': 'تم به المعنى وأخبر عن اسم إن، وجاء مفرداً.', - 'tag': 'خبر مرفوع', - }, - { - 'word': 'يَهْدِي', - 'role': 'فعل مضارع مرفوع (وجملة فعلية في محل رفع نعت)', - 'case': 'مرفوع بالضمة المقدرة على الياء للثقل، والفاعل ضمير مستتر', - 'explanation': - 'الجمل بعد النكرات صفات؛ فجملة (يهدي) نعت لكلمة (نور).', - 'tag': 'فعل + نعت', - }, - { - 'word': 'العُقُولَ', - 'role': 'مفعول به', - 'case': 'منصوب وعلامة نصبه الفتحة الظاهرة', - 'explanation': 'وقع عليه فعل الهداية من الفاعل المستتر (هو).', - 'tag': 'مفعول به', - }, - { - 'word': 'إِلَى الحَقِّ', - 'role': 'جار ومجرور', - 'case': 'شبه جملة متعلقة بالفعل (يهدي)', - 'explanation': 'إلى حرف جر، والحق اسم مجرور بالكسرة الظاهرة.', - 'tag': 'شبه جملة', + 'text': 'يَسْتَخْفُونَ مِنَ النَّاسِ وَلَا يَسْتَخْفُونَ مِنَ اللَّهِ', + 'source': 'سورة النساء', + 'type': 'طباق سلب', + 'words': ['يَسْتَخْفُونَ ↔ لَا يَسْتَخْفُونَ'], + 'explanation': 'طباق سلب يقارن بين خشية الناس الواهية والغفلة عن مراقبة الله العليم.', }, ], }, { - 'fullText': - 'قَرَأَ الطَّالِبُ المُجْتَهِدُ كِتَابَيْنِ مُفِيدَيْنِ صَبَاحاً', - 'type': 'جملة فعلية تامة', - 'rootNode': 'الجملة الفعلية (فعل + فاعل + مفعول به + فضلات)', - 'words': [ + 'title': 'المقابلة (Opposition)', + 'badge': 'محسن معنوي مركب', + 'icon': CupertinoIcons.rectangle_split_3x1, + 'color': Color(0xFF30D158), + 'definition': 'أن يؤتى بمعنيين متوافقين أو أكثر، ثم يؤتى بما يقابل ذلك على الترتيب، لتأكيد التباين وتوضيح المعنى.', + 'examples': [ { - 'word': 'قَرَأَ', - 'role': 'فعل ماضٍ مبني', - 'case': 'مبني على الفتح الظاهر على آخره', - 'explanation': - 'فعل ماضٍ مجرد، مبني للمعلوم، يدل على حدث في الزمن الماضي.', - 'tag': 'فعل ماضٍ', + 'text': 'فَلْيَضْحَكُوا قَلِيلًا وَلْيَبْكُوا كَثِيرًا جَزَاءً بِمَا كَانُوا يَكْسِبُونَ', + 'source': 'سورة التوبة (منهاج العاشر)', + 'type': 'مقابلة ثنائية مرتبة', + 'words': ['يَضْحَكُوا قَلِيلًا ⟷ يَبْكُوا كَثِيرًا'], + 'explanation': 'قابل الضحك بالبكاء، والقلة بالكثرة على الترتيب الدقيق، لبيان عاقبة الاستهزاء.', }, { - 'word': 'الطَّالِبُ', - 'role': 'فاعل مرفوع', - 'case': 'مرفوع وعلامة رفعه الضمة الظاهرة', - 'explanation': - 'من قام بالفعل، معرف بأل، ركن أساسي في الجملة الفعلية.', - 'tag': 'فاعل مرفوع', - }, - { - 'word': 'المُجْتَهِدُ', - 'role': 'نعت (صفة) للطالب', - 'case': 'مرفوع وعلامة رفعه الضمة الظاهرة', - 'explanation': - 'طابق المنعوت في التعريف، الإفراد، التذكير، وحركة الإعراب.', - 'tag': 'نعت / صفة', - }, - { - 'word': 'كِتَابَيْنِ', - 'role': 'مفعول به منصوب', - 'case': 'منصوب وعلامة نصبه الياء لأنه مثنى', - 'explanation': - 'وقع عليه فعل القراءة، والمثنى يُنصب بالياء وتُكسر نونه.', - 'tag': 'مثنى منصوب', - }, - { - 'word': 'مُفِيدَيْنِ', - 'role': 'نعت لكتابين', - 'case': 'منصوب وعلامة نصبه الياء لأنه مثنى', - 'explanation': - 'صفة تابعة للمفعول به في التثنية والتنكير والنصب بالياء.', - 'tag': 'نعت مثنى', - }, - { - 'word': 'صَبَاحاً', - 'role': 'ظرف زمان (مفعول فيه)', - 'case': 'منصوب وعلامة نصبه الفتحة الظاهرة', - 'explanation': 'اسم منصوب يدل على زمن وقوع الفعل، متضمن معنى (في).', - 'tag': 'ظرف زمان', + 'text': 'يُحِلُّ لَهُمُ الطَّيِّبَاتِ وَيُحَرِّمُ عَلَيْهِمُ الْخَبَائِثَ', + 'source': 'سورة الأعراف (منهاج العاشر)', + 'type': 'مقابلة ثلاثية مرتبة', + 'words': ['يُحِلُّ ↔ يُحَرِّمُ', 'لَهُمُ ↔ عَلَيْهِمُ', 'الطَّيِّبَاتِ ↔ الْخَبَائِثَ'], + 'explanation': 'مقابلة بديعية متكاملة توضح رحمة التشريع الإسلامي ومقاصده السامية.', }, ], }, { - 'fullText': 'المُعَلِّمُ المُخْلِصُ مُحْتَرَمٌ قَدْرُهُ بَيْنَ النَّاسِ', - 'type': 'جملة اسمية تشتمل على اسم فاعل واسم مفعول وعملهما', - 'rootNode': 'الجملة الاسمية والمشتقات العاملة (الصف العاشر)', - 'words': [ + 'title': 'الجناس (Paronomasia)', + 'badge': 'محسن لفظي نغمي', + 'icon': CupertinoIcons.sparkles, + 'color': Color(0xFF60A5FA), + 'definition': 'تشابه لفظين في النطق واختلافهما في المعنى، وهو نوعان: جناس تام، وجناس ناقص.', + 'examples': [ { - 'word': 'المُعَلِّمُ', - 'role': 'مبتدأ مرفوع (اسم فاعل من غير الثلاثي)', - 'case': 'مرفوع وعلامة رفعه الضمة الظاهرة على آخره', - 'explanation': 'اسم معرف بأل وقع في أول الجملة، وهو اسم فاعل من الفعل علّم يُعلّم.', - 'tag': 'مبتدأ / اسم فاعل', + 'text': 'وَيَوْمَ تَقُومُ السَّاعَةُ يُقْسِمُ الْمُجْرِمُونَ مَا لَبِثُوا غَيْرَ سَاعَةٍ', + 'source': 'سورة الروم (شاهد البلاغة المقرر)', + 'type': 'جناس تام', + 'words': ['السَّاعَةُ (يوم القيامة) ↔ سَاعَةٍ (الوقت الزمني)'], + 'explanation': 'تطابق في الحروف والترتيب والشكل والعدد مع اختلاف المعنى التام، مانحاً جرسًا صوتياً آسراً.', }, { - 'word': 'المُخْلِصُ', - 'role': 'نعت (صفة) للمبتدأ', - 'case': 'مرفوع وعلامة رفعه الضمة الظاهرة', - 'explanation': 'نعت حقيقي طابق المنعوت في التذكير والتعريف والإعراب.', - 'tag': 'نعت مرفوع', - }, - { - 'word': 'مُحْتَرَمٌ', - 'role': 'خبر المبتدأ (اسم مفعول عامل)', - 'case': 'مرفوع وعلامة رفعه الضمة الظاهرة', - 'explanation': 'خبر أتم معنى الجملة، وهو مشتق (اسم مفعول) يعمل عمل فعله المبني للمجهول.', - 'tag': 'خبر / اسم مفعول', - }, - { - 'word': 'قَدْرُهُ', - 'role': 'نائب فاعل لاسم المفعول (مُحترَم)', - 'case': 'مرفوع بالضمة، والهاء ضمير متصل مبني في محل جر مضاف إليه', - 'explanation': 'اسم المفعول يعمل عمل فعله المبني للمجهول فيرفع نائب فاعل بعده.', - 'tag': 'نائب فاعل', - }, - { - 'word': 'بَيْنَ', - 'role': 'ظرف مكان منصوب (مفعول فيه)', - 'case': 'منصوب بالفتحة وهو مضاف', - 'explanation': 'ظرف مكان يحدد موقع الاتصاف بالاحترام.', - 'tag': 'ظرف مكان', - }, - { - 'word': 'النَّاسِ', - 'role': 'مضاف إليه مجرور', - 'case': 'مجرور وعلامة جره الكسرة الظاهرة على آخره', - 'explanation': 'اسم مجرور بالإضافة جاء بعد ظرف المكان.', - 'tag': 'مضاف إليه', + 'text': 'الْخَيْلُ مَعْقُودٌ فِي نَوَاصِيهَا الْخَيْرُ إِلَى يَوْمِ الْقِيَامَةِ', + 'source': 'حديث نبوي شريف', + 'type': 'جناس ناقص', + 'words': ['الْخَيْلُ ↔ الْخَيْرُ (اختلاف اللام والراء)'], + 'explanation': 'اختلاف في حرف واحد فقط (اللام والراء) يمنح العبارة رونقاً نغمياً محبباً للأذن.', }, ], }, { - 'fullText': 'مَنْ يَزْرَعْ خَيْراً يَحْصُدْ ثِمَاراً طَيِّبَةً', - 'type': 'أسلوب شرط جازم (منهاج العاشر الأساسي)', - 'rootNode': 'أسلوب الشرط (اسم شرط + فعل الشرط + جواب الشرط)', - 'words': [ + 'title': 'السجع (Rhymed Prose)', + 'badge': 'إيقاع الفواصل', + 'icon': CupertinoIcons.waveform, + 'color': Color(0xFFBF5AF2), + 'definition': 'توافق الفاصلتين في الحرف الأخير من النثر الأدبي، ومثاله الفواصل الحكمية والخطب الرفيعة.', + 'examples': [ { - 'word': 'مَنْ', - 'role': 'اسم شرط جازم مبني', - 'case': 'مبني على السكون في محل رفع مبتدأ', - 'explanation': 'اسم شرط جازم يدل على العاقل ويربط بين جملتين.', - 'tag': 'اسم شرط', + 'text': 'الصَّوْمُ حِرْمَانٌ مَشْرُوعٌ، وَتَأْدِيبٌ بِالْجُوعِ، وَخُشُوعٌ لِلَّهِ وَخُضُوعٌ', + 'source': 'مصطفى صادق الرافعي (وحي القلم - منهاج العاشر)', + 'type': 'سجع ثلاثي متوازن', + 'words': ['مَشْرُوعٌ • بِالْجُوعِ • خُضُوعٌ'], + 'explanation': 'انتهاء الجمل بحرف العين المضموم المنون، محققاً تأثيراً موسيقياً يعزز المعاني الروحية للصيام.', }, { - 'word': 'يَزْرَعْ', - 'role': 'فعل الشرط مضارع مجزوم', - 'case': 'مجزوم بالسكون، والفاعل ضمير مستتر تقديره (هو)', - 'explanation': 'فعل مضارع وقع بعد أداة الشرط الجازمة، فجُزم بالسكون.', - 'tag': 'فعل شرط مجزوم', - }, - { - 'word': 'خَيْراً', - 'role': 'مفعول به لفعل الشرط', - 'case': 'منصوب وعلامة نصبه تنوين الفتح', - 'explanation': 'وقع عليه فعل الزراعة المعنوي.', - 'tag': 'مفعول به', - }, - { - 'word': 'يَحْصُدْ', - 'role': 'جواب الشرط وجزاؤه مضارع مجزوم', - 'case': 'مجزوم بالسكون، والفاعل مستتر تقديره (هو)', - 'explanation': 'فعل مضارع جُزم لوقوعه جواباً للشرط.', - 'tag': 'جواب شرط مجزوم', - }, - { - 'word': 'ثِمَاراً', - 'role': 'مفعول به لجواب الشرط', - 'case': 'منصوب وعلامة نصبه تنوين الفتح الظاهر', - 'explanation': 'الشيء المحصود نتيجة العمل.', - 'tag': 'مفعول به', - }, - { - 'word': 'طَيِّبَةً', - 'role': 'نعت لثماراً', - 'case': 'منصوب وعلامة نصبه تنوين الفتح', - 'explanation': 'صفة طابقت موصوفها في التنكير والجمع والتأنيث والنصب.', - 'tag': 'نعت منصوب', + 'text': 'اللَّهُمَّ أَعْطِ مُنْفِقًا خَلَفًا، وَأَعْطِ مُمْسِكًا تَلَفًا', + 'source': 'حديث شريف في فضل الصدقة', + 'type': 'سجع متطابق الفواصل', + 'words': ['خَلَفًا ↔ تَلَفًا'], + 'explanation': 'تطابق الفاصلتين على حرف الفاء المنون بالفتح، ليقرع الأسماع بضرورة البذل والجود.', }, ], }, ]; - // Poetry Meter State - int _selectedMeterIndex = 0; - bool _isPlayingBeat = false; + // --------------------------------------------------------------------------- + // Wing 3: Interactive Syntax Tree & Sentence Studio State + // --------------------------------------------------------------------------- + int _selectedSentenceIndex = 0; + int? _selectedWordIndex; - final List> _poetryMeters = [ + static const List> _syntaxSentences = [ { - 'name': 'البحر البسيط', - 'key': - 'إِنَّ البَسِيطَ لَدَيْهِ يُبْسَطُ الأَمَلُ .. مُسْتَفْعِلُنْ فاعِلُنْ مُسْتَفْعِلُنْ فَعِلُ', - 'verse': - 'لِكُلِّ شَيْءٍ إِذَا مَا تَمَّ نُقْصَانُ .. فَلَا يُغَرَّ بِطِيبِ العَيْشِ إِنْسَانُ', - 'poet': 'أبو البقاء الرندي', - 'pattern': ['مُسْتَفْعِلُنْ', 'فَاعِلُنْ', 'مُسْتَفْعِلُنْ', 'فَعِلُنْ'], - 'scansion': ['//0//0', '/0//0', '//0//0', '///0'], - 'speedBpm': 90, + 'fullText': 'إِنَّ العِلْمَ نُورٌ يَهْدِي العُقُولَ إِلَى الحَقِّ', + 'type': 'جملة اسمية منسوخة بـ (إنَّ)', + 'rootNode': 'باب إن وأخواتها (نواسخ الجملة الاسمية)', + 'words': [ + {'word': 'إِنَّ', 'role': 'حرف توكيد ونصب (ناسخ)', 'case': 'مبني على الفتح لا محل له من الإعراب', 'tag': 'ناسخ', 'desc': 'تدخل على الجملة الاسمية فتنصب المبتدأ اسماً لها وترفع الخبر.'}, + {'word': 'العِلْمَ', 'role': 'اسم إنَّ منصوب', 'case': 'علامة نصبه الفتحة الظاهرة على آخره', 'tag': 'اسم منصوب', 'desc': 'المسند إليه في الأصل، نُصب لدخول الحرف الناسخ عليه.'}, + {'word': 'نُورٌ', 'role': 'خبر إنَّ مرفوع', 'case': 'علامة رفعه الضمة الظاهرة على آخره', 'tag': 'خبر مرفوع', 'desc': 'تم به الإخبار عن اسم إن وجاء مفرداً.'}, + {'word': 'يَهْدِي', 'role': 'فعل مضارع مرفوع + جملة نعت', 'case': 'مرفوع بالضمة المقدرة على الياء للثقل، والفاعل ضمير مستتر', 'tag': 'فعل + نعت', 'desc': 'الجمل بعد النكرات صفات؛ فجملة (يهدي) نعت لكلمة (نور).'}, + {'word': 'العُقُولَ', 'role': 'مفعول به منصوب', 'case': 'علامة نصبه الفتحة الظاهرة', 'tag': 'مفعول به', 'desc': 'وقع عليه فعل الهداية من الفاعل المستتر.'}, + {'word': 'إِلَى الحَقِّ', 'role': 'شبه جملة (جار ومجرور)', 'case': 'متعلق بالفعل (يهدي)', 'tag': 'متعلقات', 'desc': 'إلى حرف جر، والحق اسم مجرور بالكسرة الظاهرة.'}, + ], }, { - 'name': 'البحر الوافر', - 'key': - 'بُحُورُ الشِّعْرِ وَافِرُهَا جَمِيلُ .. مُفَاعَلَتُنْ مُفَاعَلَتُنْ فَعُولُ', - 'verse': - 'إِذَا غَامَرْتَ فِي شَرَفٍ مَرُومِ .. فَلَا تَقْنَعْ بِمَا دُونَ النُّجُومِ', - 'poet': 'المتنبي', - 'pattern': ['مُفَاعَلَتُنْ', 'مُفَاعَلَتُنْ', 'فَعُولُنْ'], - 'scansion': ['//0///0', '//0///0', '//0/0'], - 'speedBpm': 105, + 'fullText': 'يَجْتَهِدُ الطَّالِبُ الطَّمُوحُ لِيَنَالَ المَرْتَبَةَ الأُولَى', + 'type': 'جملة فعلية تامة مع لام التعليل', + 'rootNode': 'الجملة الفعلية (فعل وفاعل وفضلات)', + 'words': [ + {'word': 'يَجْتَهِدُ', 'role': 'فعل مضارع مرفوع', 'case': 'علامة رفعه الضمة الظاهرة', 'tag': 'فعل مضارع', 'desc': 'فعل مضارع مجرد لم يسبقه ناصب ولا جازم.'}, + {'word': 'الطَّالِبُ', 'role': 'فاعل مرفوع', 'case': 'علامة رفعه الضمة الظاهرة', 'tag': 'فاعل', 'desc': 'من قام بفعل الاجتهاد وهو المسند.'}, + {'word': 'الطَّمُوحُ', 'role': 'نعت (صفة) مرفوع', 'case': 'علامة رفعه الضمة الظاهرة', 'tag': 'نعت', 'desc': 'صفة مشبهة تتبع المنعوت في الإعراب والتعريف والتذكير.'}, + {'word': 'لِيَنَالَ', 'role': 'فعل مضارع منصوب بأن مضمرة', 'case': 'منصوب بعد لام التعليل بالفتحة', 'tag': 'نصب مضارع', 'desc': 'اللام لام التعليل، وينال فعل مضارع منصوب وعلامة نصبه الفتحة.'}, + {'word': 'المَرْتَبَةَ', 'role': 'مفعول به منصوب', 'case': 'علامة نصبه الفتحة الظاهرة', 'tag': 'مفعول به', 'desc': 'وقع عليه فعل النيل والتحصيل.'}, + {'word': 'الأُولَى', 'role': 'نعت منصوب بالفتحة المقدرة', 'case': 'منصوب بالفتحة المقدرة للتعذر', 'tag': 'نعت مقدر', 'desc': 'صفة لكلمة المرتبة، قدرت الفتحة على الألف للتعذر.'}, + ], }, { - 'name': 'البحر الكامل', - 'key': - 'كَمُلَ الجَمَالُ مِنَ البُحُورِ الكَامِلُ .. مُتَفَاعِلُنْ مُتَفَاعِلُنْ مُتَفَاعِلُ', - 'verse': - 'وَإِذَا صَحَوْتُ فَمَا أُقَصِّرُ عَنْ نَدَى .. وَكَمَا عَلِمْتِ شَمَائِلِي وَتَكَرُّمِي', - 'poet': 'عنترة بن شداد', - 'pattern': ['مُتَفَاعِلُنْ', 'مُتَفَاعِلُنْ', 'مُتَفَاعِلُنْ'], - 'scansion': ['///0//0', '///0//0', '///0//0'], - 'speedBpm': 115, + 'fullText': 'كَتَبَ القَائِدُ رِسَالَةً مُفْعَمَةً بِالأَمَلِ', + 'type': 'جملة فعلية متعدية', + 'rootNode': 'الجملة الفعلية المشتقة', + 'words': [ + {'word': 'كَتَبَ', 'role': 'فعل ماضٍ مبني', 'case': 'مبني على الفتح الظاهر', 'tag': 'فعل ماضٍ', 'desc': 'فعل ماضٍ مبني للمعلوم يدل على حدث وقع وانتهى.'}, + {'word': 'القَائِدُ', 'role': 'فاعل (اسم فاعل)', 'case': 'مرفوع بالضمة الظاهرة', 'tag': 'فاعل مشتق', 'desc': 'قام بفعل الكتابة، وهو اسم فاعل من قاد.'}, + {'word': 'رِسَالَةً', 'role': 'مفعول به أول', 'case': 'منصوب بالفتحة الظاهرة', 'tag': 'مفعول به', 'desc': 'الشيء المكتوب الذي وقع عليه فعل الفاعل.'}, + {'word': 'مُفْعَمَةً', 'role': 'نعت (اسم مفعول غير ثلاثي)', 'case': 'منصوب بالفتحة الظاهرة', 'tag': 'اسم مفعول', 'desc': 'نعت منصوب، مشتق اسم مفعول بميم مضمومة وفتح ما قبل الآخر.'}, + {'word': 'بِالأَمَلِ', 'role': 'شبه جملة متعلقة بمفعمة', 'case': 'الباء حرف جر والأمل مجرور', 'tag': 'جار ومجرور', 'desc': 'متعلق بالصفة (مفعمة).'}, + ], + }, + ]; + + // --------------------------------------------------------------------------- + // Wing 4: Prosody & Meter Studio State + // --------------------------------------------------------------------------- + int _selectedMeterIndex = 0; // 0: Kamil, 1: Tawil, 2: Wafir + + static const List> _poeticMeters = [ + { + 'name': 'بحر الكامل', + 'pattern': 'مُتَفَاعِلُنْ مُتَفَاعِلُنْ مُتَفَاعِلُنْ', + 'key': 'كَمُلَ الْجَمَالُ مِنَ الْبُحُورِ الْكَامِلُ ... مُتَفَاعِلُنْ مُتَفَاعِلُنْ مُتَفَاعِلُ', + 'verse': 'وَإِذَا صَحَوْتُ فَمَا أُقَصِّرُ عَنْ نَدَى ... وَكَمَا عَلِمْتِ شَمَائِلِي وَتَكَرُّمِي', + 'poet': 'عنترة بن شداد (منهاج العاشر)', + 'prosodicWriting': 'وَإِذَا صَحَوْ / تُفَمَا أُقَصْ / صِرُعَنْ نَدَى ... وَكَمَا عَلِمْ / تِشَمَائِلِي / وَتَكَرْرُمِي', + 'symbols': '///0//0 ///0//0 ///0//0 ... ///0//0 ///0//0 ///0//0', + 'accent': Color(0xFF00F5D4), + }, + { + 'name': 'بحر الطويل', + 'pattern': 'فَعُولُنْ مَفَاعِيلُنْ فَعُولُنْ مَفَاعِيلُنْ', + 'key': 'طَوِيلٌ لَهُ دُونَ الْبُحُورِ فَضَائِلُ ... فَعُولُنْ مَفَاعِيلُنْ فَعُولُ مَفَاعِلُ', + 'verse': 'أَلَا لَيْتَ رَيْعَانَ الشَّبَابِ جَدِيدُ ... وَدَهْرًا تَوَلَّى يَا بُثَيْنَ يَعُودُ', + 'poet': 'جميل بن معمر (الغزل العذري - العاشر)', + 'prosodicWriting': 'أَلَا لَيْ / تَرَيْعَانَشْ / شَبَابِ / جَدِيدُو ... وَدَهْرَنْ / تَوَلْلَا يَا / بُثَيْنَ / يَعُودُو', + 'symbols': '//0/0 //0/0/0 //0/0 //0/0/0 ... //0/0 //0/0/0 //0/0 //0/0/0', + 'accent': Color(0xFFFF9F0A), + }, + { + 'name': 'بحر الوافر', + 'pattern': 'مُفَاعَلَتُنْ مُفَاعَلَتُنْ فَعُولُنْ', + 'key': 'بُحُورُ الشِّعْرِ وَافِرُهَا جَمِيلُ ... مُفَاعَلَتُنْ مُفَاعَلَتُنْ فَعُولُ', + 'verse': 'سَلِ الرِّمَاحَ الْعَوَالِيَ عَنْ مَعَالِينَا ... وَاسْتَشْهِدِ الْبِيضَ هَلْ خَابَ الرَّجَا فِينَا', + 'poet': 'صفي الدين الحلي (الحماسة والفخر)', + 'prosodicWriting': 'سَلِرْرِمَا / حَلْعَوَالِيَ عَنْ / مَعَالِينَا ... وَسْتَشْهِدِلْ / بِيضَهَلْ خَابَرْ / رَجَا فِينَا', + 'symbols': '//0///0 //0///0 //0/0 ... //0///0 //0///0 //0/0', + 'accent': Color(0xFFBF5AF2), }, ]; @override void initState() { super.initState(); - _tabController = TabController(length: 2, vsync: this)..addListener(() { - if (mounted) setState(() {}); - }); + _tabController = TabController(length: 4, vsync: this); } @override @@ -296,39 +298,19 @@ class _ArabicInteractiveLabViewState extends State @override Widget build(BuildContext context) { return Container( - color: const Color(0xFF070B12), + color: AppColors.darkBackground, child: Column( children: [ - // Luxury Tab Switcher - Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - decoration: const BoxDecoration( - color: Color(0xFF0E1626), - border: Border(bottom: BorderSide(color: Color(0xFF1E293B))), - ), - child: Row( - children: [ - _tabButton( - index: 0, - title: 'شجرة الإعراب النحوية 🌳', - icon: CupertinoIcons.arrow_branch, - ), - const SizedBox(width: 8), - _tabButton( - index: 1, - title: 'مختبر العروض والأوزان 🎵', - icon: CupertinoIcons.music_note_list, - ), - ], - ), - ), - + _buildStudioHeader(), + _buildTabBar(), Expanded( - child: IndexedStack( - index: _tabController.index, + child: TabBarView( + controller: _tabController, children: [ - _buildSyntaxTreeTab(), - _buildPoetryMeterTab(), + _buildMorphologyWing(), + _buildRhetoricWing(), + _buildSyntaxWing(), + _buildProsodyWing(), ], ), ), @@ -337,579 +319,858 @@ class _ArabicInteractiveLabViewState extends State ); } - Widget _tabButton({ - required int index, - required String title, - required IconData icon, - }) { - final isSelected = _tabController.index == index; - return Expanded( - child: GestureDetector( - onTap: () { - setState(() { - _tabController.animateTo(index); - }); - }, - child: Container( - padding: const EdgeInsets.symmetric(vertical: 10), - decoration: BoxDecoration( - color: - isSelected ? const Color(0xFF10B981) : const Color(0xFF1E293B), - borderRadius: BorderRadius.circular(10), + // --------------------------------------------------------------------------- + // Studio Top Navigation Bar + // --------------------------------------------------------------------------- + Widget _buildStudioHeader() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: AppColors.darkSurface, + border: Border(bottom: BorderSide(color: Colors.white.withValues(alpha: 0.08))), + ), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: const Color(0xFF00F5D4).withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(10), + ), + child: const Icon(CupertinoIcons.book_fill, color: Color(0xFF00F5D4), size: 18), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(icon, - size: 16, - color: isSelected ? Colors.black : const Color(0xFF94A3B8)), - const SizedBox(width: 8), - Text( - title, - style: TextStyle( - fontSize: 13, - fontWeight: isSelected ? FontWeight.w800 : FontWeight.w600, - color: isSelected ? Colors.black : const Color(0xFF94A3B8), + const SizedBox(width: 12), + const Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'مختبر الضاد اللغوي الذكي (Al-Dhad Studio) 📜', + style: TextStyle(color: Colors.white, fontSize: 14.5, fontWeight: FontWeight.w800), ), - ), - ], + Text( + 'الصرف والاشتقاق • البلاغة والبيان • الإعراب والتراكيب • العروض وموسيقى الشعر', + style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 11), + ), + ], + ), ), - ), + ], + ), + ); + } + + Widget _buildTabBar() { + return Container( + color: AppColors.darkSurface, + child: TabBar( + controller: _tabController, + isScrollable: true, + indicatorColor: const Color(0xFF00F5D4), + indicatorWeight: 3, + labelColor: const Color(0xFF00F5D4), + unselectedLabelColor: Colors.white60, + labelStyle: const TextStyle(fontSize: 12.5, fontWeight: FontWeight.w800), + tabs: const [ + Tab(icon: Icon(CupertinoIcons.tuningfork, size: 16), text: 'ميزان الصرف والاشتقاق ⚖️'), + Tab(icon: Icon(CupertinoIcons.sparkles, size: 16), text: 'معمل البلاغة والبيان 💎'), + Tab(icon: Icon(CupertinoIcons.tree, size: 16), text: 'استوديو الإعراب والتراكيب 📜'), + Tab(icon: Icon(CupertinoIcons.waveform, size: 16), text: 'العروض وموسيقى الشعر 🎵'), + ], ), ); } // --------------------------------------------------------------------------- - // TAB 1: Visual Interactive Syntax Tree + // WING 1: MORPHOLOGY & DERIVATION ENGINE // --------------------------------------------------------------------------- - Widget _buildSyntaxTreeTab() { - final safeIndex = _selectedSentenceIndex.clamp(0, _sentences.length - 1); - final sentence = _sentences[safeIndex]; - final words = (sentence['words'] as List); + Widget _buildMorphologyWing() { + final curRoot = _roots[_selectedRootIndex]; + final forms = curRoot['forms'] as List>; + final curForm = forms[_selectedPatternIndex.clamp(0, forms.length - 1)]; - return SingleChildScrollView( + return ListView( padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - // Selector for Sentences - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - widget.lessonTopic != null - ? 'شجرة إعراب درس: ${widget.lessonTopic}' - : 'اختر الجملة لتحليل شجرتها الإعرابية:', - style: const TextStyle( - fontSize: 13.5, - fontWeight: FontWeight.w700, - color: Colors.white), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), - decoration: BoxDecoration( - color: const Color(0xFF10B981).withOpacity(0.15), - borderRadius: BorderRadius.circular(6), + children: [ + // Root Selector Chips + const Text( + 'اختر الجذر اللغوي الثلاثي لاستكشاف ميزانه الصرفي:', + style: TextStyle(color: Colors.white70, fontSize: 12.5, fontWeight: FontWeight.w700), + ), + const SizedBox(height: 8), + SizedBox( + height: 42, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: _roots.length, + separatorBuilder: (_, __) => const SizedBox(width: 8), + itemBuilder: (ctx, i) { + final sel = i == _selectedRootIndex; + final r = _roots[i]; + return ChoiceChip( + label: Text( + r['root'] as String, + style: TextStyle( + color: sel ? Colors.black : Colors.white, + fontWeight: FontWeight.w800, + fontSize: 13, + ), ), - child: Text( - widget.customSentences != null - ? 'أمثلة الكتاب + ذكاء اصطناعي' - : 'وزاري · التوجيهي 2008', - style: const TextStyle( - fontSize: 11, - fontWeight: FontWeight.w700, - color: Color(0xFF34D399)), + selected: sel, + selectedColor: const Color(0xFF00F5D4), + backgroundColor: Colors.white.withValues(alpha: 0.06), + onSelected: (val) { + if (val) { + setState(() { + _selectedRootIndex = i; + _selectedPatternIndex = 0; + }); + } + }, + ); + }, + ), + ), + const SizedBox(height: 16), + + // Interactive Balance Scale Canvas + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + gradient: const LinearGradient( + colors: [Color(0xFF0C1B2A), Color(0xFF060D15)], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: const Color(0xFF00F5D4).withValues(alpha: 0.3)), + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: const Color(0xFF00F5D4).withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + 'المشتق: ${curForm['type']}', + style: const TextStyle(color: Color(0xFF00F5D4), fontWeight: FontWeight.w800, fontSize: 12), + ), + ), + Text( + 'معنى الجذر: ${curRoot['meaning']}', + style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11.5), + ), + ], + ), + const SizedBox(height: 16), + + // Visual Scale Comparison + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _buildScalePan( + title: 'الجذر الأصلي (ف - ع - ل)', + word: curRoot['root'] as String, + color: const Color(0xFFFFD60A), + icon: CupertinoIcons.circle_grid_3x3, + ), + const Icon(CupertinoIcons.equal, color: Colors.white38, size: 28), + _buildScalePan( + title: 'الوزن الصرفي (${curForm['weight']})', + word: curForm['word'] as String, + color: const Color(0xFF00F5D4), + icon: CupertinoIcons.sparkles, + ), + ], + ), + const SizedBox(height: 16), + + // Derivation Rule Card + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.25), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.white.withValues(alpha: 0.08)), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon(CupertinoIcons.info_circle_fill, color: Color(0xFF00F5D4), size: 18), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'القاعدة الوزارية المقررة (الوزن: ${curForm['weight']}):', + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 12.5), + ), + const SizedBox(height: 4), + Text( + curForm['rule'] as String, + style: const TextStyle(color: Colors.white70, fontSize: 12, height: 1.45), + ), + ], + ), + ), + ], ), ), ], ), - const SizedBox(height: 10), + ), + const SizedBox(height: 16), - // Sentence Switcher Buttons - SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Row( - children: List.generate(_sentences.length, (idx) { - final isCur = safeIndex == idx; - return GestureDetector( - onTap: () => setState(() { - _selectedSentenceIndex = idx; - _selectedWordIndex = null; - }), - child: Container( - margin: const EdgeInsets.only(left: 8), - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - decoration: BoxDecoration( - color: isCur - ? const Color(0xFF1E293B) - : const Color(0xFF0F172A), - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: isCur - ? const Color(0xFF10B981) - : const Color(0xFF1E293B), - ), - ), - child: Center( - child: Text( - 'الجملة ${idx + 1}', - style: TextStyle( - fontSize: 12.5, - fontWeight: isCur ? FontWeight.w800 : FontWeight.w500, - color: isCur ? Colors.white : const Color(0xFF64748B), - ), - ), - ), - ), - ); - }), - ), - ), - const SizedBox(height: 16), - - // Main Sentence Card - Container( - padding: const EdgeInsets.all(18), - decoration: BoxDecoration( - gradient: const LinearGradient( - colors: [Color(0xFF064E3B), Color(0xFF0F172A)], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - borderRadius: BorderRadius.circular(16), - border: - Border.all(color: const Color(0xFF10B981).withOpacity(0.4)), - ), - child: Column( - children: [ - Text( - sentence['fullText'], - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.w900, - color: Colors.white, - height: 1.6, - letterSpacing: 0.5, - ), + // Derivation Form Switcher + const Text( + 'أوزان المشتقات المتاحة لهذا الجذر في المنهاج:', + style: TextStyle(color: Colors.white70, fontSize: 12.5, fontWeight: FontWeight.w700), + ), + const SizedBox(height: 8), + Wrap( + spacing: 8, + runSpacing: 8, + children: List.generate(forms.length, (idx) { + final f = forms[idx]; + final sel = idx == _selectedPatternIndex; + return GestureDetector( + onTap: () => setState(() => _selectedPatternIndex = idx), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: sel ? const Color(0xFF00F5D4) : Colors.white.withValues(alpha: 0.05), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: sel ? const Color(0xFF00F5D4) : Colors.white12), ), - const SizedBox(height: 8), - Container( - padding: - const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.4), - borderRadius: BorderRadius.circular(20), - ), - child: Text( - sentence['type'], - style: const TextStyle( - fontSize: 12, - color: Color(0xFF6EE7B7), - fontWeight: FontWeight.w600), - ), - ) - ], - ), - ), - const SizedBox(height: 20), - - // Tree Interactive Decomposition (Word Cards) - const Text( - 'انقر على أي كلمة لتفكيك عقدتها النحوية وأحكامها:', - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w700, - color: Color(0xFF94A3B8)), - ), - const SizedBox(height: 12), - - Wrap( - spacing: 8, - runSpacing: 8, - children: List.generate(words.length, (idx) { - final w = words[idx]; - final isSel = _selectedWordIndex == idx; - return GestureDetector( - onTap: () => setState(() => _selectedWordIndex = idx), - child: AnimatedContainer( - duration: const Duration(milliseconds: 200), - padding: - const EdgeInsets.symmetric(horizontal: 14, vertical: 10), - decoration: BoxDecoration( - color: isSel - ? const Color(0xFF10B981) - : const Color(0xFF0F172A), - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: isSel - ? const Color(0xFF34D399) - : const Color(0xFF1E293B), - width: isSel ? 2 : 1, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + f['type'] as String, + style: TextStyle( + color: sel ? Colors.black : Colors.white70, + fontSize: 11, + fontWeight: FontWeight.w600, + ), ), - boxShadow: isSel - ? [ - BoxShadow( - color: const Color(0xFF10B981).withOpacity(0.3), - blurRadius: 12, - ) - ] - : [], - ), - child: Column( - children: [ - Text( - w['word'], - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w800, - color: isSel ? Colors.black : Colors.white, - ), + const SizedBox(height: 4), + Text( + f['word'] as String, + style: TextStyle( + color: sel ? Colors.black : Colors.white, + fontSize: 14, + fontWeight: FontWeight.w800, ), - const SizedBox(height: 2), - Text( - w['tag'], - style: TextStyle( - fontSize: 10.5, - fontWeight: FontWeight.w600, - color: isSel - ? const Color(0xFF064E3B) - : const Color(0xFF64748B), - ), + ), + Text( + '(${f['weight']})', + style: TextStyle( + color: sel ? Colors.black87 : const Color(0xFF00F5D4), + fontSize: 10.5, + fontWeight: FontWeight.w700, ), - ], + ), + ], + ), + ), + ); + }), + ), + ], + ); + } + + Widget _buildScalePan({ + required String title, + required String word, + required Color color, + required IconData icon, + }) { + return Container( + width: 140, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(14), + border: Border.all(color: color.withValues(alpha: 0.4)), + ), + child: Column( + children: [ + Icon(icon, color: color, size: 22), + const SizedBox(height: 6), + Text( + title, + textAlign: TextAlign.center, + style: TextStyle(color: color, fontSize: 10.5, fontWeight: FontWeight.w700), + ), + const SizedBox(height: 8), + Text( + word, + textAlign: TextAlign.center, + style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w900, letterSpacing: 0.5), + ), + ], + ), + ); + } + + // --------------------------------------------------------------------------- + // WING 2: RHETORIC & AESTHETICS STUDIO (معمل البلاغة والبيان) + // --------------------------------------------------------------------------- + Widget _buildRhetoricWing() { + final cat = _rhetoricCategories[_selectedRhetoricCategory]; + final examples = cat['examples'] as List>; + final curEx = examples[_selectedExampleIndex.clamp(0, examples.length - 1)]; + final catColor = cat['color'] as Color; + + return ListView( + padding: const EdgeInsets.all(16), + children: [ + // Category Pills + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: List.generate(_rhetoricCategories.length, (i) { + final c = _rhetoricCategories[i]; + final sel = i == _selectedRhetoricCategory; + return Padding( + padding: const EdgeInsets.only(left: 8), + child: FilterChip( + avatar: Icon(c['icon'] as IconData, size: 14, color: sel ? Colors.black : c['color'] as Color), + label: Text( + c['title'] as String, + style: TextStyle( + color: sel ? Colors.black : Colors.white, + fontWeight: FontWeight.w800, + fontSize: 12.5, + ), ), + selected: sel, + selectedColor: c['color'] as Color, + backgroundColor: Colors.white.withValues(alpha: 0.06), + onSelected: (val) { + if (val) { + setState(() { + _selectedRhetoricCategory = i; + _selectedExampleIndex = 0; + }); + } + }, ), ); }), ), - const SizedBox(height: 20), + ), + const SizedBox(height: 16), - // Detailed Syntactic Breakdown Card - if (_selectedWordIndex != null) - _buildWordDetailCard(words[_selectedWordIndex!]) - else - Container( - padding: const EdgeInsets.all(20), - decoration: BoxDecoration( - color: const Color(0xFF0F172A), - borderRadius: BorderRadius.circular(14), - border: Border.all(color: const Color(0xFF1E293B)), - ), - child: const Center( - child: Text( - '👆 انقر على أي كلمة أعلاه لتوليد شجرتها الإعرابية والعلة النحوية', - style: TextStyle(color: Color(0xFF64748B), fontSize: 13), + // Rhetorical Definition Card + Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: catColor.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(14), + border: Border.all(color: catColor.withValues(alpha: 0.4)), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(cat['icon'] as IconData, color: catColor, size: 20), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'المفهوم البلاغي المقرر: ${cat['title']}', + style: TextStyle(color: catColor, fontWeight: FontWeight.w800, fontSize: 13), + ), + const SizedBox(height: 4), + Text( + cat['definition'] as String, + style: const TextStyle(color: Colors.white, fontSize: 12, height: 1.45), + ), + ], ), ), - ), - ], - ), - ); - } + ], + ), + ), + const SizedBox(height: 16), - Widget _buildWordDetailCard(Map wordData) { - return Container( - padding: const EdgeInsets.all(18), - decoration: BoxDecoration( - color: const Color(0xFF0F172A), - borderRadius: BorderRadius.circular(16), - border: Border.all(color: const Color(0xFF10B981).withOpacity(0.5)), - boxShadow: [ - BoxShadow( - color: const Color(0xFF10B981).withOpacity(0.1), - blurRadius: 20, - ) - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + // Active Rhetorical Witness Card (الشاهد البلاغي) + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF0F172A), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: Colors.white12), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( - padding: const EdgeInsets.all(8), + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( - color: const Color(0xFF10B981).withOpacity(0.2), - shape: BoxShape.circle, + color: catColor.withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + curEx['type'] as String, + style: TextStyle(color: catColor, fontSize: 11.5, fontWeight: FontWeight.w700), ), - child: const Icon(CupertinoIcons.checkmark_seal_fill, - color: Color(0xFF10B981), size: 18), ), - const SizedBox(width: 10), Text( - 'التحليل الإعرابي لكلمة: ( ${wordData['word']} )', - style: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.w800, - color: Colors.white), + curEx['source'] as String, + style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11.5), ), ], ), + const SizedBox(height: 14), + + // Highlighted Verse/Prose Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + padding: const EdgeInsets.all(14), decoration: BoxDecoration( - color: const Color(0xFF1E293B), - borderRadius: BorderRadius.circular(6), + color: Colors.black.withValues(alpha: 0.3), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.white.withValues(alpha: 0.06)), ), child: Text( - wordData['tag'], + curEx['text'] as String, + textAlign: TextAlign.center, style: const TextStyle( - fontSize: 11, - color: Color(0xFF34D399), - fontWeight: FontWeight.w700), + color: Colors.white, + fontSize: 15, + fontWeight: FontWeight.w800, + height: 1.6, + ), ), - ) + ), + const SizedBox(height: 12), + + // Pairs Breakdown + Wrap( + spacing: 8, + runSpacing: 6, + children: (curEx['words'] as List).map((w) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: catColor.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: catColor.withValues(alpha: 0.3)), + ), + child: Text( + w, + style: TextStyle(color: catColor, fontSize: 12, fontWeight: FontWeight.w700), + ), + ); + }).toList(), + ), + const SizedBox(height: 10), + + Text( + '💡 الأثر البلاغي والتحليل: ${curEx['explanation']}', + style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 12, height: 1.45), + ), ], ), - const SizedBox(height: 14), - - // Role - _infoRow( - 'الموقع الإعرابي', wordData['role'], const Color(0xFF38BDF8)), - const SizedBox(height: 8), - - // Case & Sign - _infoRow( - 'الحالة والعلامة', wordData['case'], const Color(0xFFFBBF24)), - const SizedBox(height: 8), - - // Pedagogical Socratic explanation - _infoRow('التعليل والتوجيه الوزاري', wordData['explanation'], - const Color(0xFFCBD5E1)), - ], - ), - ); - } - - Widget _infoRow(String label, String value, Color valueColor) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - label, - style: const TextStyle( - fontSize: 11.5, - fontWeight: FontWeight.w600, - color: Color(0xFF64748B)), ), - const SizedBox(height: 2), - Text( - value, - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w700, - color: valueColor, - height: 1.4), + const SizedBox(height: 16), + + // Examples Stepper + const Text( + 'الشواهد والأمثلة من كتاب الصف العاشر:', + style: TextStyle(color: Colors.white70, fontSize: 12.5, fontWeight: FontWeight.w700), + ), + const SizedBox(height: 8), + Row( + children: List.generate(examples.length, (idx) { + final sel = idx == _selectedExampleIndex; + return Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: sel ? catColor : Colors.white.withValues(alpha: 0.06), + foregroundColor: sel ? Colors.black : Colors.white, + padding: const EdgeInsets.symmetric(vertical: 10), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + ), + onPressed: () => setState(() => _selectedExampleIndex = idx), + child: Text('شاهد ${idx + 1}', style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w700)), + ), + ), + ); + }), ), ], ); } // --------------------------------------------------------------------------- - // TAB 2: Poetry Prosody & Metronome Lab + // WING 3: INTERACTIVE SYNTAX & SENTENCE STUDIO (استوديو الإعراب والتراكيب) // --------------------------------------------------------------------------- - Widget _buildPoetryMeterTab() { - final meter = _poetryMeters[_selectedMeterIndex]; - final patterns = (meter['pattern'] as List); - final scansions = (meter['scansion'] as List); + Widget _buildSyntaxWing() { + final curSentence = _syntaxSentences[_selectedSentenceIndex]; + final words = curSentence['words'] as List>; - return SingleChildScrollView( + return ListView( padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - // Meter Selection Chips - Row( - children: List.generate(_poetryMeters.length, (idx) { - final m = _poetryMeters[idx]; - final isCur = _selectedMeterIndex == idx; - return Expanded( - child: GestureDetector( - onTap: () => setState(() { - _selectedMeterIndex = idx; - _isPlayingBeat = false; - }), - child: Container( - margin: EdgeInsets.only( - left: idx < _poetryMeters.length - 1 ? 8 : 0), - padding: const EdgeInsets.symmetric(vertical: 10), + children: [ + // Sentence Selector + const Text( + 'اختر الجملة لتحليل بنيتها الإعرابية وعلاقاتها النحوية:', + style: TextStyle(color: Colors.white70, fontSize: 12.5, fontWeight: FontWeight.w700), + ), + const SizedBox(height: 8), + SizedBox( + height: 38, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: _syntaxSentences.length, + separatorBuilder: (_, __) => const SizedBox(width: 8), + itemBuilder: (ctx, i) { + final sel = i == _selectedSentenceIndex; + return ChoiceChip( + label: Text('جملة ${i + 1}', style: TextStyle(color: sel ? Colors.black : Colors.white, fontWeight: FontWeight.w700)), + selected: sel, + selectedColor: const Color(0xFF60A5FA), + backgroundColor: Colors.white.withValues(alpha: 0.06), + onSelected: (val) { + if (val) { + setState(() { + _selectedSentenceIndex = i; + _selectedWordIndex = null; + }); + } + }, + ); + }, + ), + ), + const SizedBox(height: 14), + + // Sentence Banner & Type + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF132238), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: const Color(0xFF60A5FA).withValues(alpha: 0.3)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( - color: isCur - ? const Color(0xFF047857) - : const Color(0xFF0F172A), - borderRadius: BorderRadius.circular(10), - border: Border.all( - color: isCur - ? const Color(0xFF34D399) - : const Color(0xFF1E293B), - ), + color: const Color(0xFF60A5FA).withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(8), ), - child: Center( - child: Text( - m['name'], - style: TextStyle( - fontSize: 12.5, - fontWeight: isCur ? FontWeight.w800 : FontWeight.w600, - color: isCur ? Colors.white : const Color(0xFF94A3B8), - ), - ), + child: Text( + curSentence['type'] as String, + style: const TextStyle(color: Color(0xFF60A5FA), fontSize: 11.5, fontWeight: FontWeight.w700), ), ), - ), - ); - }), - ), - const SizedBox(height: 16), + Text( + curSentence['rootNode'] as String, + style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11), + ), + ], + ), + const SizedBox(height: 14), - // Key & Verse Banner + // Interactive Word Tiles + Wrap( + spacing: 8, + runSpacing: 8, + alignment: WrapAlignment.center, + children: List.generate(words.length, (idx) { + final w = words[idx]; + final isSelected = _selectedWordIndex == idx; + return GestureDetector( + onTap: () => setState(() => _selectedWordIndex = idx), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), + decoration: BoxDecoration( + color: isSelected ? const Color(0xFF60A5FA) : Colors.black.withValues(alpha: 0.3), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: isSelected ? Colors.white : Colors.white24, + width: isSelected ? 2 : 1, + ), + boxShadow: isSelected + ? [BoxShadow(color: const Color(0xFF60A5FA).withValues(alpha: 0.4), blurRadius: 10)] + : null, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + w['word'] as String, + style: TextStyle( + color: isSelected ? Colors.black : Colors.white, + fontSize: 16, + fontWeight: FontWeight.w800, + ), + ), + const SizedBox(height: 4), + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: isSelected ? Colors.black.withValues(alpha: 0.2) : const Color(0xFF60A5FA).withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + w['tag'] as String, + style: TextStyle( + color: isSelected ? Colors.black87 : const Color(0xFF60A5FA), + fontSize: 10, + fontWeight: FontWeight.w700, + ), + ), + ), + ], + ), + ), + ); + }), + ), + ], + ), + ), + const SizedBox(height: 14), + + // Selected Word Parsing Detail + if (_selectedWordIndex != null) ...[ + Builder(builder: (ctx) { + final w = words[_selectedWordIndex!]; + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF0F172A), + borderRadius: BorderRadius.circular(14), + border: Border.all(color: const Color(0xFF60A5FA).withValues(alpha: 0.4)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(CupertinoIcons.checkmark_seal_fill, color: Color(0xFF60A5FA), size: 18), + const SizedBox(width: 8), + Text( + 'إعراب الكلمة: «${w['word']}»', + style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w800), + ), + ], + ), + const Divider(color: Colors.white10, height: 20), + Text('• الوظيفة النحوية: ${w['role']}', style: const TextStyle(color: Color(0xFF00F5D4), fontSize: 12.5, fontWeight: FontWeight.w700)), + const SizedBox(height: 4), + Text('• الحالة والعلامة: ${w['case']}', style: const TextStyle(color: Colors.white70, fontSize: 12)), + const SizedBox(height: 6), + Text('💡 التوضيح النحوي: ${w['desc']}', style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11.5, height: 1.4)), + ], + ), + ); + }), + ] else ...[ Container( - padding: const EdgeInsets.all(18), + padding: const EdgeInsets.all(14), decoration: BoxDecoration( - color: const Color(0xFF0F172A), - borderRadius: BorderRadius.circular(16), - border: Border.all(color: const Color(0xFF1E293B)), + color: Colors.white.withValues(alpha: 0.04), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.white10), ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: const Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ + Icon(CupertinoIcons.hand_point_left_fill, color: Color(0xFF60A5FA), size: 16), + SizedBox(width: 8), Text( - 'مفتاح ${meter['name']}:', - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w700, - color: Color(0xFF34D399)), - ), - const SizedBox(height: 4), - Text( - meter['key'], - style: const TextStyle( - fontSize: 14.5, - fontWeight: FontWeight.w700, - color: Colors.white, - height: 1.5), - ), - const Divider(color: Color(0xFF1E293B), height: 24), - Text( - 'بيت تطبيقي وزاري (${meter['poet']}):', - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w700, - color: Color(0xFFF59E0B)), - ), - const SizedBox(height: 4), - Text( - meter['verse'], - style: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.w800, - color: Color(0xFFFEF08A), - height: 1.5), + 'انقر على أي كلمة أعلاه لعرض موقعها الإعرابي وعلامتها التفصيلية', + style: TextStyle(color: Colors.white70, fontSize: 12), ), ], ), ), - const SizedBox(height: 20), - - // Interactive Scansion / Tafilat Visualizer - const Text( - 'التفعيلات والتقطيع العروضي الصوتي (متحرك / وساكن 0):', - style: TextStyle( - fontSize: 13.5, - fontWeight: FontWeight.w700, - color: Colors.white), - ), - const SizedBox(height: 12), - - Row( - children: List.generate(patterns.length, (idx) { - final pat = patterns[idx]; - final scan = scansions[idx]; - return Expanded( - child: Container( - margin: - EdgeInsets.only(left: idx < patterns.length - 1 ? 8 : 0), - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - gradient: const LinearGradient( - colors: [Color(0xFF1E293B), Color(0xFF0F172A)], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - borderRadius: BorderRadius.circular(12), - border: Border.all(color: const Color(0xFF334155)), - ), - child: Column( - children: [ - Text( - pat, - style: const TextStyle( - fontSize: 13.5, - fontWeight: FontWeight.w800, - color: Color(0xFF38BDF8), - ), - ), - const SizedBox(height: 6), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, vertical: 2), - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.5), - borderRadius: BorderRadius.circular(6), - ), - child: Text( - scan, - style: const TextStyle( - fontFamily: 'monospace', - fontSize: 13, - fontWeight: FontWeight.w900, - letterSpacing: 2, - color: Color(0xFF34D399), - ), - ), - ), - ], - ), - ), - ); - }), - ), - const SizedBox(height: 20), - - // Audio Metronome Simulation Button - ElevatedButton.icon( - onPressed: () { - setState(() { - _isPlayingBeat = !_isPlayingBeat; - }); - }, - icon: Icon( - _isPlayingBeat - ? CupertinoIcons.pause_circle_fill - : CupertinoIcons.play_circle_fill, - size: 20), - label: Text( - _isPlayingBeat - ? 'إيقاف الإيقاع العروضي التفاعلي' - : 'عزف الإيقاع العروضي الموزون (${meter['speedBpm']} نقرة/دقيقة) 🥁', - style: - const TextStyle(fontSize: 13.5, fontWeight: FontWeight.w700), - ), - style: ElevatedButton.styleFrom( - backgroundColor: _isPlayingBeat - ? const Color(0xFFB91C1C) - : const Color(0xFF059669), - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(vertical: 14), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12)), - ), - ), ], - ), + ], + ); + } + + // --------------------------------------------------------------------------- + // WING 4: PROSODY & POETIC METER STUDIO (العروض وموسيقى الشعر) + // --------------------------------------------------------------------------- + Widget _buildProsodyWing() { + final meter = _poeticMeters[_selectedMeterIndex]; + final meterColor = meter['accent'] as Color; + + return ListView( + padding: const EdgeInsets.all(16), + children: [ + // Meter Selector Chips + const Text( + 'اختر بحر الشعر المقرر في الصف العاشر لدراسة تفعيلاته:', + style: TextStyle(color: Colors.white70, fontSize: 12.5, fontWeight: FontWeight.w700), + ), + const SizedBox(height: 8), + Row( + children: List.generate(_poeticMeters.length, (idx) { + final m = _poeticMeters[idx]; + final sel = idx == _selectedMeterIndex; + return Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: sel ? m['accent'] as Color : Colors.white.withValues(alpha: 0.06), + foregroundColor: sel ? Colors.black : Colors.white, + padding: const EdgeInsets.symmetric(vertical: 10), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + ), + onPressed: () => setState(() => _selectedMeterIndex = idx), + child: Text(m['name'] as String, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w800)), + ), + ), + ); + }), + ), + const SizedBox(height: 16), + + // Meter Passport Card + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF131F2E), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: meterColor.withValues(alpha: 0.4)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: meterColor.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + meter['name'] as String, + style: TextStyle(color: meterColor, fontSize: 12, fontWeight: FontWeight.w800), + ), + ), + Text( + 'الشاعر: ${meter['poet']}', + style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11.5), + ), + ], + ), + const SizedBox(height: 12), + + // Meter Key (مفتاح البحر) + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + children: [ + const Text('مفتاح البحر للحفظ والتذكر:', style: TextStyle(color: Colors.white54, fontSize: 11)), + const SizedBox(height: 4), + Text( + meter['key'] as String, + textAlign: TextAlign.center, + style: TextStyle(color: meterColor, fontSize: 13, fontWeight: FontWeight.w700), + ), + ], + ), + ), + const SizedBox(height: 14), + + // Poetic Verse + const Text('البيت الشعري المقرر:', style: TextStyle(color: Colors.white60, fontSize: 11.5)), + const SizedBox(height: 4), + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.3), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: Colors.white12), + ), + child: Text( + meter['verse'] as String, + textAlign: TextAlign.center, + style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w800, height: 1.6), + ), + ), + const SizedBox(height: 14), + + // Prosodic Writing (الكتابة العروضية) + const Text('الكتابة العروضية (ما يُنطق يُكتب):', style: TextStyle(color: Colors.white60, fontSize: 11.5)), + const SizedBox(height: 4), + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: meterColor.withValues(alpha: 0.08), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + meter['prosodicWriting'] as String, + textAlign: TextAlign.center, + style: const TextStyle(color: Color(0xFFFFD60A), fontSize: 12.5, fontWeight: FontWeight.w700, letterSpacing: 0.5), + ), + ), + const SizedBox(height: 12), + + // Prosodic Symbols (الرموز: / و 0) + const Text('الرموز العروضية المقابلة (/ للمتحرك، 0 للساكن):', style: TextStyle(color: Colors.white60, fontSize: 11.5)), + const SizedBox(height: 4), + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.4), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: meterColor.withValues(alpha: 0.2)), + ), + child: Text( + meter['symbols'] as String, + textAlign: TextAlign.center, + style: TextStyle(color: meterColor, fontSize: 13, fontWeight: FontWeight.w900, letterSpacing: 1.0), + ), + ), + const SizedBox(height: 12), + + // Meter Weight (تفعيلات البحر) + Text( + 'تفعيلات البحر: ${meter['pattern']}', + textAlign: TextAlign.center, + style: const TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w800), + ), + ], + ), + ), + ], ); } } diff --git a/apps/student_app/lib/presentation/screens/curriculum/digital_skills_interactive_lab_view.dart b/apps/student_app/lib/presentation/screens/curriculum/digital_skills_interactive_lab_view.dart new file mode 100644 index 0000000..86d27f4 --- /dev/null +++ b/apps/student_app/lib/presentation/screens/curriculum/digital_skills_interactive_lab_view.dart @@ -0,0 +1,575 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../../../core/theme/app_colors.dart'; + +/// ============================================================================== +/// SAQEL ENTERPRISE - GRADE 10 DIGITAL SKILLS INTERACTIVE LAB +/// ============================================================================== +/// مختبر المهارات الرقمية التفاعلي المعتمد للصف العاشر: +/// 1. معمل تمثيل البيانات والنظام الثنائي وبكسل الألوان (Binary, ASCII & RGB). +/// 2. معمل تحليل البيانات ومعادلات Excel والرسم البياني المرئي. +/// 3. محاكي إنترنت الأشياء (IoT) والذكاء الاصطناعي وشجرة القرارات. +class DigitalSkillsInteractiveLabView extends StatefulWidget { + const DigitalSkillsInteractiveLabView({super.key}); + + @override + State createState() => + _DigitalSkillsInteractiveLabViewState(); +} + +class _DigitalSkillsInteractiveLabViewState + extends State + with SingleTickerProviderStateMixin { + late TabController _tabController; + + // --------------------------------------------------------------------------- + // Tab 1: Binary & RGB Data Representation + // --------------------------------------------------------------------------- + final List _binaryBits = [false, false, false, false, false, true, false, true]; // 5 in 8-bit + int _redVal = 30; + int _greenVal = 210; + int _blueVal = 180; + + int get _decimalValue { + int val = 0; + for (int i = 0; i < 8; i++) { + if (_binaryBits[i]) { + val += (1 << (7 - i)); + } + } + return val; + } + + String get _hexValue => _decimalValue.toRadixString(16).toUpperCase().padLeft(2, '0'); + + String get _asciiChar { + final d = _decimalValue; + if (d >= 32 && d <= 126) { + return String.fromCharCode(d); + } + return 'غير قابل للطباعة (Control Code)'; + } + + // --------------------------------------------------------------------------- + // Tab 2: Excel & Data Analytics + // --------------------------------------------------------------------------- + final List> _dataRows = [ + {'item': 'عمان', 'val': 85.0}, + {'item': 'إربد', 'val': 72.0}, + {'item': 'الزرقاء', 'val': 68.0}, + {'item': 'العقبة', 'val': 94.0}, + {'item': 'البلقاء', 'val': 61.0}, + ]; + + double get _calcSum => _dataRows.fold(0.0, (acc, r) => acc + (r['val'] as double)); + double get _calcAvg => _dataRows.isNotEmpty ? _calcSum / _dataRows.length : 0.0; + double get _calcMax => _dataRows.fold(0.0, (max, r) => (r['val'] as double) > max ? (r['val'] as double) : max); + double get _calcMin => _dataRows.fold(999.0, (min, r) => (r['val'] as double) < min ? (r['val'] as double) : min); + + // --------------------------------------------------------------------------- + // Tab 3: IoT Sensors & AI Decision Tree + // --------------------------------------------------------------------------- + double _temperature = 28.0; + double _humidity = 45.0; + bool _motionDetected = false; + + String get _aiDecision { + if (_temperature > 32.0 && _humidity > 60.0) { + return 'تشغيل نظام التكييف الذكي وخفض الرطوبة (تحذير إجهاد حراري)'; + } else if (_temperature < 16.0) { + return 'تشغيل نظام التدفئة المركزية والتهوية الآمنة'; + } else if (_motionDetected) { + return 'تفعيل الإضاءة الذكية وتسجيل نشاط الكاميرا'; + } + return 'البيئة مستقرة ومثالية • الاستهلاك الاقتصادي للطاقة مفعل'; + } + + Color get _aiStatusColor { + if (_temperature > 32.0 || _temperature < 16.0) { + return const Color(0xFFF59E0B); + } + if (_motionDetected) { + return const Color(0xFF38BDF8); + } + return const Color(0xFF10B981); + } + + @override + void initState() { + super.initState(); + _tabController = TabController(length: 3, vsync: this); + } + + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Container( + margin: const EdgeInsets.fromLTRB(16, 12, 16, 8), + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.darkCardBorder), + ), + child: TabBar( + controller: _tabController, + indicatorColor: AppColors.saqelCyan, + labelColor: Colors.white, + unselectedLabelColor: AppColors.textSecondaryDark, + indicatorSize: TabBarIndicatorSize.tab, + labelStyle: const TextStyle(fontWeight: FontWeight.w800, fontSize: 11.5), + tabs: const [ + Tab(text: 'تمثيل البيانات والثنائي 0101'), + Tab(text: 'تحليل البيانات وExcel 📊'), + Tab(text: 'إنترنت الأشياء والذكاء 🤖'), + ], + ), + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + _buildBinaryLab(), + _buildExcelLab(), + _buildIotLab(), + ], + ), + ), + ], + ); + } + + Widget _buildBinaryLab() { + return SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // 8-bit Binary Switcher Card + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(18), + border: Border.all(color: AppColors.saqelCyan.withValues(alpha: 0.4)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Row( + children: [ + Icon(CupertinoIcons.chevron_left_slash_chevron_right, color: AppColors.saqelCyan, size: 18), + SizedBox(width: 8), + Text( + 'محاكي بايت البيانات الثنائي (8-Bit Binary Byte):', + style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 13.5), + ), + ], + ), + const SizedBox(height: 12), + const Text( + 'انقر على البت لتشغيله (1) أو إطفائه (0) وشاهد تحويل القيمة فورياً:', + style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 12), + ), + const SizedBox(height: 14), + + // 8 Switches + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: List.generate(8, (i) { + final weight = 1 << (7 - i); + final isSet = _binaryBits[i]; + return GestureDetector( + onTap: () { + setState(() { + _binaryBits[i] = !_binaryBits[i]; + }); + }, + child: Column( + children: [ + Text( + '$weight', + style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 10, fontWeight: FontWeight.w700), + ), + const SizedBox(height: 6), + AnimatedContainer( + duration: const Duration(milliseconds: 200), + width: 34, + height: 48, + decoration: BoxDecoration( + color: isSet ? AppColors.saqelCyan.withValues(alpha: 0.25) : Colors.black26, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: isSet ? AppColors.saqelCyan : Colors.white24, + width: isSet ? 2 : 1, + ), + ), + child: Center( + child: Text( + isSet ? '1' : '0', + style: TextStyle( + color: isSet ? AppColors.saqelCyan : Colors.white38, + fontSize: 18, + fontWeight: FontWeight.w900, + ), + ), + ), + ), + ], + ), + ); + }), + ), + const SizedBox(height: 16), + + // Computed Output Badges + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.black26, + borderRadius: BorderRadius.circular(12), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + _buildResultBadge('العشري (Decimal)', '$_decimalValue'), + _buildResultBadge('الست عشري (Hex)', '0x$_hexValue'), + _buildResultBadge('حرف ASCII', _asciiChar), + ], + ), + ), + ], + ), + ), + const SizedBox(height: 16), + + // RGB Pixel Inspector + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(18), + border: Border.all(color: Colors.white12), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Row( + children: [ + Icon(CupertinoIcons.paintbrush_fill, color: Color(0xFFFF375F), size: 18), + SizedBox(width: 8), + Text( + 'مفتش البكسل اللوني وتمثيل الصور (24-bit RGB):', + style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 13.5), + ), + ], + ), + const SizedBox(height: 12), + Row( + children: [ + // Color Preview Box + Container( + width: 70, + height: 70, + decoration: BoxDecoration( + color: Color.fromARGB(255, _redVal, _greenVal, _blueVal), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.white24, width: 2), + boxShadow: [ + BoxShadow( + color: Color.fromARGB(255, _redVal, _greenVal, _blueVal).withValues(alpha: 0.5), + blurRadius: 14, + ), + ], + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'RGB($_redVal, $_greenVal, $_blueVal)', + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 14), + ), + const SizedBox(height: 4), + Text( + 'حجم البكسل: 3 بايت = 24 بت في الذاكرة (16.7 مليون لون ممكن)', + style: TextStyle(color: Colors.white.withValues(alpha: 0.7), fontSize: 11), + ), + ], + ), + ), + ], + ), + const SizedBox(height: 12), + _buildColorSlider('أحمر (Red R)', _redVal, const Color(0xFFFF453A), (v) => setState(() => _redVal = v.round())), + _buildColorSlider('أخضر (Green G)', _greenVal, const Color(0xFF30D158), (v) => setState(() => _greenVal = v.round())), + _buildColorSlider('أزرق (Blue B)', _blueVal, const Color(0xFF0A84FF), (v) => setState(() => _blueVal = v.round())), + ], + ), + ), + ], + ), + ); + } + + Widget _buildExcelLab() { + return SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Formulas Summary Header + Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: const Color(0xFF10B981).withValues(alpha: 0.4)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + _buildStatColumn('دالة المجموع', '=SUM(B2:B6)', _calcSum.toStringAsFixed(0), const Color(0xFF10B981)), + _buildStatColumn('دالة المتوسط', '=AVERAGE(B2:B6)', _calcAvg.toStringAsFixed(1), const Color(0xFF38BDF8)), + _buildStatColumn('أعلى قيمة', '=MAX(B2:B6)', _calcMax.toStringAsFixed(0), const Color(0xFFF59E0B)), + _buildStatColumn('أدنى قيمة', '=MIN(B2:B6)', _calcMin.toStringAsFixed(0), const Color(0xFFEC4899)), + ], + ), + ), + const SizedBox(height: 16), + + // Interactive Data Table & Bar Chart + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.darkCardBorder), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Row( + children: [ + Icon(CupertinoIcons.table, color: Color(0xFF10B981), size: 18), + SizedBox(width: 8), + Text( + 'جدول البيانات والتمثيل المرئي (Data Visualization):', + style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 13.5), + ), + ], + ), + const SizedBox(height: 14), + ..._dataRows.map((row) { + final val = row['val'] as double; + final ratio = (_calcMax > 0) ? (val / _calcMax) : 0.0; + + return Padding( + padding: const EdgeInsets.only(bottom: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + SizedBox( + width: 60, + child: Text( + row['item'], + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 12.5), + ), + ), + Expanded( + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: LinearProgressIndicator( + value: ratio, + minHeight: 14, + backgroundColor: Colors.white10, + valueColor: const AlwaysStoppedAnimation(Color(0xFF10B981)), + ), + ), + ), + const SizedBox(width: 12), + Text( + val.toStringAsFixed(0), + style: const TextStyle(color: Color(0xFF10B981), fontWeight: FontWeight.w900, fontSize: 13), + ), + ], + ), + ], + ), + ); + }), + ], + ), + ), + ], + ), + ); + } + + Widget _buildIotLab() { + return SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // IoT Sensor Sliders Card + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.saqelCyan.withValues(alpha: 0.4)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Row( + children: [ + Icon(CupertinoIcons.radiowaves_right, color: AppColors.saqelCyan, size: 18), + SizedBox(width: 8), + Text( + 'محاكي مجسات إنترنت الأشياء (IoT Smart Sensors):', + style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 13.5), + ), + ], + ), + const SizedBox(height: 14), + + // Temperature Slider + Text( + 'حساس درجة الحرارة (DHT22): ${_temperature.toStringAsFixed(1)} °C', + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 12.5), + ), + Slider( + value: _temperature, + min: 10.0, + max: 45.0, + divisions: 35, + activeColor: const Color(0xFFFF9F0A), + onChanged: (v) => setState(() => _temperature = v), + ), + + // Humidity Slider + Text( + 'حساس الرطوبة النسبية: ${_humidity.toStringAsFixed(0)} %', + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 12.5), + ), + Slider( + value: _humidity, + min: 20.0, + max: 95.0, + divisions: 75, + activeColor: const Color(0xFF38BDF8), + onChanged: (v) => setState(() => _humidity = v), + ), + + // Motion Detection Switch + Row( + children: [ + const Icon(CupertinoIcons.person_crop_circle_badge_exclam, color: Colors.white70, size: 18), + const SizedBox(width: 8), + const Text('مستشعر الحركة بالأشعة (PIR):', style: TextStyle(color: Colors.white, fontSize: 12.5, fontWeight: FontWeight.w600)), + const Spacer(), + CupertinoSwitch( + value: _motionDetected, + activeTrackColor: AppColors.saqelCyan, + onChanged: (v) => setState(() => _motionDetected = v), + ), + ], + ), + ], + ), + ), + const SizedBox(height: 14), + + // AI Decision Output Box + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: _aiStatusColor.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: _aiStatusColor), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(CupertinoIcons.sparkles, color: _aiStatusColor, size: 20), + const SizedBox(width: 8), + Text( + 'قرار الذكاء الاصطناعي (AI Decision Engine):', + style: TextStyle(color: _aiStatusColor, fontWeight: FontWeight.w800, fontSize: 13.5), + ), + ], + ), + const SizedBox(height: 10), + Text( + _aiDecision, + style: const TextStyle(color: Colors.white, fontSize: 13.5, height: 1.45, fontWeight: FontWeight.w700), + ), + ], + ), + ), + ], + ), + ); + } + + Widget _buildResultBadge(String label, String val) { + return Column( + children: [ + Text(label, style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11)), + const SizedBox(height: 4), + Text(val, style: const TextStyle(color: AppColors.saqelCyan, fontSize: 16, fontWeight: FontWeight.w900)), + ], + ); + } + + Widget _buildColorSlider(String label, int value, Color col, ValueChanged onChanged) { + return Row( + children: [ + SizedBox( + width: 90, + child: Text(label, style: TextStyle(color: col, fontSize: 11.5, fontWeight: FontWeight.w700)), + ), + Expanded( + child: Slider( + value: value.toDouble(), + min: 0, + max: 255, + activeColor: col, + onChanged: onChanged, + ), + ), + SizedBox( + width: 32, + child: Text('$value', style: const TextStyle(color: Colors.white, fontSize: 11.5, fontWeight: FontWeight.w700)), + ), + ], + ); + } + + Widget _buildStatColumn(String label, String formula, String val, Color col) { + return Column( + children: [ + Text(label, style: const TextStyle(color: Colors.white70, fontSize: 10.5, fontWeight: FontWeight.w700)), + const SizedBox(height: 2), + Text(formula, style: TextStyle(color: col, fontSize: 9.5, fontWeight: FontWeight.w600)), + const SizedBox(height: 4), + Text(val, style: TextStyle(color: col, fontSize: 16, fontWeight: FontWeight.w900)), + ], + ); + } +} diff --git a/apps/student_app/lib/presentation/screens/curriculum/english_interactive_lab_view.dart b/apps/student_app/lib/presentation/screens/curriculum/english_interactive_lab_view.dart index d89ba26..0faee3e 100644 --- a/apps/student_app/lib/presentation/screens/curriculum/english_interactive_lab_view.dart +++ b/apps/student_app/lib/presentation/screens/curriculum/english_interactive_lab_view.dart @@ -1,239 +1,323 @@ -import 'dart:math' as math; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_tts/flutter_tts.dart'; import '../../../core/theme/app_colors.dart'; -import '../../widgets/luxury_widgets.dart'; /// ============================================================================== -/// SAQEL ENTERPRISE - ACTION PACK 10 INTERACTIVE ENGLISH LAB +/// SAQEL ENTERPRISE - GRADE 10 ACTION PACK ENGLISH INTERACTIVE STUDIO /// ============================================================================== -/// مختبر اللغة الإنجليزية التفاعلي المنهجي للصف العاشر: -/// 1. محرك نطق صوتي أصيل (Native TTS Engine) مع تحكم بالسرعة ونبرة الصوت. -/// 2. مصفوفة الرموز الصوتية الدولية (IPA Phonetics & Syllable Stress Breakdown). -/// 3. محاكي القواعد البصري (Timeline Syntax Builder) لقواعد Action Pack 10. -/// 4. تقسيم بصري صارم: ثلثان (2/3) للكانفاس التفاعلي وثلث (1/3) للمحددات والتحكم. +/// أربعة أجنحة لغوية تفاعلية متقدمة تحاكي منهاج الصف العاشر (Action Pack 10): +/// 1. Grammar Matrix & Tense Simulator: محاكي خط الأزمنة والشرطيات وجمل المبني للمجهول. +/// 2. Phonetics, Stress Shifts & Minimal Pairs: استوديو الصوتيات المتقدم مع تشغيل صوتي (TTS). +/// 3. Vocabulary & Collocations Lab: معمل المفردات والمتلازمات اللفظية لوحدات الصف العاشر. +/// 4. Dialogue & Listening Comprehension: محاكاة محادثة علمية (خليج العقبة) واختبارات استماع. class EnglishInteractiveLabView extends StatefulWidget { - final String? initialTopic; - const EnglishInteractiveLabView({super.key, this.initialTopic}); + const EnglishInteractiveLabView({super.key}); @override - State createState() => _EnglishInteractiveLabViewState(); + State createState() => + _EnglishInteractiveLabViewState(); } -class _EnglishInteractiveLabViewState extends State with SingleTickerProviderStateMixin { - final FlutterTts _tts = FlutterTts(); +class _EnglishInteractiveLabViewState extends State + with SingleTickerProviderStateMixin { late TabController _tabController; + final FlutterTts _tts = FlutterTts(); - // Audio state - bool _isPlayingAudio = false; - double _speechRate = 0.45; - String _activeWordId = 'conservation'; + // --------------------------------------------------------------------------- + // Wing 1: Grammar Matrix & Tense Simulator State + // --------------------------------------------------------------------------- + int _selectedTenseIndex = 0; + int _selectedConditionalIndex = 0; + bool _showPassiveMode = false; - // Grammar Lab state - int _selectedGrammarRuleIdx = 0; - List _assembledSentence = []; - bool? _isSentenceCorrect; - - // Action Pack 10 Modules & Verified Vocabulary - static const List> _vocabBank = [ + static const List> _tenses = [ { - 'id': 'conservation', - 'module': 'Module 2: Natural World', - 'word': 'Conservation', - 'ipa': '/ˌkɒnsəˈveɪʃn/', - 'pos': 'noun', - 'arabic': 'حماية البيئة والحفاظ على الموارد الطبيعية', - 'definition': 'The protection of plants, animals, and natural areas from the damaging effects of human activity.', - 'example': 'Wildlife conservation is essential for maintaining global biodiversity.', - 'stress': 'con-ser-VA-tion (3rd syllable)', - 'audioText': 'Conservation. Wildlife conservation is essential for maintaining global biodiversity.', + 'name': 'Present Simple', + 'formula': 'Subject + V1 (s/es) + Object', + 'timeline': 'Habits, routines & permanent scientific facts', + 'example': 'Solar panels convert sunlight into clean electricity.', + 'arabic': 'المضارع البسيط: يعبر عن الحقائق العلمية والعادات المتكررة.', + 'keywords': 'always, usually, every day, facts', + 'accent': Color(0xFF00F5D4), }, { - 'id': 'biodiversity', - 'module': 'Module 2: Natural World', - 'word': 'Biodiversity', - 'ipa': '/ˌbaɪəʊdaɪˈvɜːsəti/', - 'pos': 'noun', - 'arabic': 'التنوع الحيوي / البيولوجي', - 'definition': 'The number and variety of plants and animals that exist in a particular area.', - 'example': 'Rainforests possess immense biodiversity that must be preserved.', - 'stress': 'bi-o-di-VER-si-ty (4th syllable)', - 'audioText': 'Biodiversity. Rainforests possess immense biodiversity that must be preserved.', + 'name': 'Present Continuous', + 'formula': 'Subject + am/is/are + V-ing', + 'timeline': 'Actions happening now or temporary trends in progress', + 'example': 'Jordan is developing large-scale wind farms in Tafila.', + 'arabic': 'المضارع المستمر: يعبر عن أحداث مستمرة تقع الآن أو مشاريع قيد التنفيذ.', + 'keywords': 'now, at the moment, currently, this year', + 'accent': Color(0xFF60A5FA), }, { - 'id': 'expedition', - 'module': 'Module 3: Journeys', - 'word': 'Expedition', - 'ipa': '/ˌekspəˈdɪʃn/', - 'pos': 'noun', - 'arabic': 'رحلة استكشافية علمية', - 'definition': 'An organized journey made for a particular purpose such as exploration or scientific research.', - 'example': 'The scientists embarked on an Arctic expedition to measure ice thickness.', - 'stress': 'ex-pe-DI-tion (3rd syllable)', - 'audioText': 'Expedition. The scientists embarked on an Arctic expedition to measure ice thickness.', + 'name': 'Past Simple', + 'formula': 'Subject + V2 (ed / irregular) + Object', + 'timeline': 'Completed action at a specific, finished time in the past', + 'example': 'Archaeologists excavated the ancient ruins of Petra in 1812.', + 'arabic': 'الماضي البسيط: حدث اكتمل وانتهى في وقت محدد في الماضي.', + 'keywords': 'yesterday, in 1990, ago, last week', + 'accent': Color(0xFFFF9F0A), }, { - 'id': 'philanthropic', - 'module': 'Module 1: Making a Difference', - 'word': 'Philanthropic', - 'ipa': '/ˌfɪlənˈθrɒpɪk/', - 'pos': 'adjective', - 'arabic': 'خيري / إنساني تطوعي', - 'definition': 'Helping poor and needy people, especially by giving money or continuous support.', - 'example': 'She dedicated her career to philanthropic work in education.', - 'stress': 'phi-lan-THRO-pic (3rd syllable)', - 'audioText': 'Philanthropic. She dedicated her career to philanthropic work in education.', - }, - { - 'id': 'perseverance', - 'module': 'Module 1: Making a Difference', - 'word': 'Perseverance', - 'ipa': '/ˌpɜːsɪˈvɪərəns/', - 'pos': 'noun', - 'arabic': 'المثابرة والإصرار على النجاح', - 'definition': 'Continued effort to do or achieve something despite difficulties, failure, or opposition.', - 'example': 'Success in university requires steady perseverance and disciplined practice.', - 'stress': 'per-se-VE-rance (3rd syllable)', - 'audioText': 'Perseverance. Success in university requires steady perseverance and disciplined practice.', - }, - { - 'id': 'deductive', - 'module': 'Module 4: Mysteries', - 'word': 'Deductive', - 'ipa': '/dɪˈdʌktɪv/', - 'pos': 'adjective', - 'arabic': 'استنتاجي / مبني على الاستدلال المنطقي', - 'definition': 'Using logic or reasoning based on evidence to decide whether something is true.', - 'example': 'Detectives use deductive reasoning to solve puzzling mysteries.', - 'stress': 'de-DUC-tive (2nd syllable)', - 'audioText': 'Deductive. Detectives use deductive reasoning to solve puzzling mysteries.', + 'name': 'Present Perfect', + 'formula': 'Subject + have/has + V3 (Past Participle)', + 'timeline': 'Past experience with present result, or unstated time', + 'example': 'Jordan has achieved major milestones in renewable energy.', + 'arabic': 'المضارع التام: حدث وقع في الماضي وله أثر أو نتيجة حية في الحاضر.', + 'keywords': 'already, just, yet, ever, since, for', + 'accent': Color(0xFF30D158), }, ]; - // Action Pack 10 Grammar Rules Matrix - static const List> _grammarRules = [ + static const List> _conditionals = [ { - 'rule': 'Present Perfect vs. Past Simple', - 'concept': 'Action Pack 10 — Module 1 & 2', - 'explanation': 'المضارع التام (Have/Has + V3) يربط الماضي بالحاضر دون تحديد زمن أو مع أثر باقٍ. الماضي البسيط (V2) يستلزم زمناً ماضياً محدداً بدقة (yesterday, in 2021, two weeks ago).', - 'formula': 'Present Perfect: Subject + have/has + V3 | Past Simple: Subject + V2 + (time mark)', - 'scrambled': ['Scientists', 'discovered', 'the new species', 'in 2018', 'have'], - 'correct': ['Scientists', 'discovered', 'the new species', 'in 2018'], - 'alternative': ['Scientists', 'have', 'discovered', 'the new species'], - 'hint': 'وجود العبارة الزمنية (in 2018) يفرض استخدام الماضي البسيط بدون have.', + 'type': 'Zero Conditional (Facts)', + 'ifClause': 'If + Present Simple', + 'mainClause': 'Present Simple', + 'example': 'If water reaches 100°C, it boils.', + 'note': 'حقائق علمية وقوانين طبيعية مؤكدة 100% (Cause & Effect).', }, { - 'rule': 'Modals of Deduction (must / might / can\'t)', - 'concept': 'Action Pack 10 — Module 4', - 'explanation': 'Must = استنتاج مؤكد بنسبة 100% (أكيد). Can\'t = استنتاج مستحيل بنسبة 100% (مستحيل). Might / Could = استنتاج محتمل بنسبة 50% (ربما).', - 'formula': 'Subject + must / might / can\'t + Base Verb (Infinitive)', - 'scrambled': ['He', 'can\'t', 'be', 'at home', 'because he is travelling', 'must'], - 'correct': ['He', 'can\'t', 'be', 'at home', 'because he is travelling'], - 'hint': 'بما أنه مسافر حالياً، فوجوده في المنزل أمر مستحيل (can\'t).', + 'type': 'First Conditional (Real Future)', + 'ifClause': 'If + Present Simple', + 'mainClause': 'will + Base Verb (Infinitive)', + 'example': 'If we reduce carbon emissions, we will protect our climate.', + 'note': 'احتمال مستقبلي حقيقي وواقعي مبني على شرط قابل للتحقق.', }, { - 'rule': 'Defining Relative Clauses (who / which / where / whose)', - 'concept': 'Action Pack 10 — Module 3', - 'explanation': 'Who للأشخاص والعاقل. Which أو That للأشياء والجماد والحيوان. Where للأماكن. Whose لإثبات الملكية.', - 'formula': 'Noun + [who / which / where / whose] + Clause', - 'scrambled': ['The volunteer', 'who', 'helped', 'the injured falcon', 'received an award', 'which'], - 'correct': ['The volunteer', 'who', 'helped', 'the injured falcon', 'received an award'], - 'hint': 'المتطوع إنسان عاقل، لذا نستخدم ضمير الوصل (who) وليس (which).', + 'type': 'Second Conditional (Hypothetical)', + 'ifClause': 'If + Past Simple', + 'mainClause': 'would + Base Verb (Infinitive)', + 'example': 'If I won a research grant, I would build an AI observatory.', + 'note': 'موقف تخيلي أو افتراضي غير حقيقي في الوقت الحاضر.', + }, + ]; + + // --------------------------------------------------------------------------- + // Wing 2: Phonetics, Stress Shifts & Minimal Pairs State + // --------------------------------------------------------------------------- + int _selectedPairIndex = 0; + int _selectedStressIndex = 0; + int _selectedSilentIndex = 0; + + static const List> _minimalPairs = [ + { + 'pair': '/iː/ vs /ɪ/', + 'contrast': 'Long vowel vs Short lax vowel', + 'wordA': 'sheep', + 'ipaA': '/ʃiːp/', + 'arA': 'خروف (صوت طويل ممدود)', + 'wordB': 'ship', + 'ipaB': '/ʃɪp/', + 'arB': 'سفينة (صوت قصير)', + }, + { + 'pair': '/p/ vs /b/', + 'contrast': 'Voiceless bilabial plosive vs Voiced bilabial plosive', + 'wordA': 'pack', + 'ipaA': '/pæk/', + 'arA': 'يحزم أمتعة (حبس الهواء وإطلاقه بقوة)', + 'wordB': 'back', + 'ipaB': '/bæk/', + 'arB': 'ظهر / رجوع (اهتزاز الحبال الصوتية)', + }, + { + 'pair': '/θ/ vs /ð/', + 'contrast': 'Voiceless dental fricative vs Voiced dental fricative', + 'wordA': 'think', + 'ipaA': '/θɪŋk/', + 'arA': 'يفكر (ث - احتكاكي مهموس)', + 'wordB': 'this', + 'ipaB': '/ðɪs/', + 'arB': 'هذا (ذ - احتكاكي مجهور)', + }, + ]; + + static const List> _stressShifts = [ + { + 'word': 'PRESENT', + 'noun': 'PRE-sent', + 'nounIpa': '/ˈprez.ənt/', + 'nounDef': 'Noun: a gift or the current time (هدية أو الحاضر)', + 'verb': 'pre-SENT', + 'verbIpa': '/prɪˈzent/', + 'verbDef': 'Verb: to introduce, display, or give (يُقدّم أو يعرض)', + }, + { + 'word': 'RECORD', + 'noun': 'RE-cord', + 'nounIpa': '/ˈrek.ɔːd/', + 'nounDef': 'Noun: official documentation or music track (سجل أو تسجيل)', + 'verb': 're-CORD', + 'verbIpa': '/rɪˈkɔːd/', + 'verbDef': 'Verb: to store audio or document data (يسجل صوتاً أو بيانات)', + }, + { + 'word': 'OBJECT', + 'noun': 'OB-ject', + 'nounIpa': '/ˈɒb.dʒɪkt/', + 'nounDef': 'Noun: a physical material thing (شيء مادي ملموس)', + 'verb': 'ob-JECT', + 'verbIpa': '/əbˈdʒekt/', + 'verbDef': 'Verb: to express disapproval or opposition (يعترض أو يرفض)', + }, + ]; + + static const List> _silentLetters = [ + { + 'word': 'knife', + 'silent': 'k', + 'ipa': '/naɪf/', + 'ar': 'حرف (k) صامت قبل حرف (n)', + }, + { + 'word': 'doubt', + 'silent': 'b', + 'ipa': '/daʊt/', + 'ar': 'حرف (b) صامت بعد حرف (u) وقبل (t)', + }, + { + 'word': 'write', + 'silent': 'w', + 'ipa': '/raɪt/', + 'ar': 'حرف (w) صامت قبل حرف (r)', + }, + { + 'word': 'island', + 'silent': 's', + 'ipa': '/ˈaɪ.lənd/', + 'ar': 'حرف (s) صامت تماماً في الكلمة', + }, + ]; + + // --------------------------------------------------------------------------- + // Wing 3: Action Pack 10 Vocabulary & Collocations State + // --------------------------------------------------------------------------- + int _selectedModuleIndex = 0; + + static const List> _modules = [ + { + 'title': 'Module 1: Health & Nutrition', + 'subtitle': 'الغذاء الصحي واللياقة البدنية', + 'icon': CupertinoIcons.heart_fill, + 'color': Color(0xFFFF453A), + 'vocab': [ + {'en': 'nutritious', 'ar': 'مُغذٍّ وصحي', 'example': 'A nutritious breakfast fuels the brain for learning.'}, + {'en': 'cardiovascular', 'ar': 'متعلق بالقلب والأوعية', 'example': 'Aerobic exercise boosts cardiovascular health.'}, + {'en': 'carbohydrates', 'ar': 'كربوهيدرات ونشويات', 'example': 'Whole grains supply complex carbohydrates.'}, + {'en': 'metabolism', 'ar': 'التمثيل الغذائي والأيض', 'example': 'Drinking water speeds up your metabolic rate.'}, + ], + 'collocations': [ + {'verb': 'balanced', 'noun': 'diet', 'ar': 'نظام غذائي متوازن'}, + {'verb': 'physical', 'noun': 'activity', 'ar': 'نشاط بدني منتظم'}, + {'verb': 'boost', 'noun': 'immunity', 'ar': 'تعزيز المناعة'}, + ], + }, + { + 'title': 'Module 2: Technology & AI', + 'subtitle': 'الذكاء الاصطناعي والابتكار الحديث', + 'icon': CupertinoIcons.sparkles, + 'color': Color(0xFF00F5D4), + 'vocab': [ + {'en': 'artificial intelligence', 'ar': 'ذكاء اصطناعي', 'example': 'AI algorithms assist doctors in diagnosis.'}, + {'en': 'nanotechnology', 'ar': 'تكنولوجيا النانو', 'example': 'Nanotech enables precise microscopic medicine.'}, + {'en': 'renewable energy', 'ar': 'طاقة متجددة ونظيفة', 'example': 'Solar and wind are vital renewable resources.'}, + {'en': 'autonomous', 'ar': 'ذاتي القيادة والتحكم', 'example': 'Autonomous electric vehicles navigate safely.'}, + ], + 'collocations': [ + {'verb': 'solve', 'noun': 'complex problems', 'ar': 'حل معضلات معقدة'}, + {'verb': 'develop', 'noun': 'cutting-edge software', 'ar': 'تطوير برمجيات متقدمة'}, + {'verb': 'reduce', 'noun': 'energy consumption', 'ar': 'تقليل استهلاك الطاقة'}, + ], + }, + { + 'title': 'Module 3: Planet Earth & Conservation', + 'subtitle': 'البيئة والتنوع البيولوجي', + 'icon': CupertinoIcons.globe, + 'color': Color(0xFF30D158), + 'vocab': [ + {'en': 'biodiversity', 'ar': 'التنوع البيولوجي الحيوي', 'example': 'Coral reefs in Aqaba boast rich biodiversity.'}, + {'en': 'deforestation', 'ar': 'إزالة الغابات والتصحر', 'example': 'Deforestation accelerates global warming.'}, + {'en': 'endangered species', 'ar': 'أنواع مهددة بالانقراض', 'example': 'The Arabian Oryx was saved from extinction.'}, + {'en': 'sustainability', 'ar': 'الاستدامة البيئية', 'example': 'Recycling promotes long-term sustainability.'}, + ], + 'collocations': [ + {'verb': 'protect', 'noun': 'natural habitats', 'ar': 'حماية البيئات الطبيعية'}, + {'verb': 'raise', 'noun': 'environmental awareness', 'ar': 'رفع الوعي البيئي'}, + {'verb': 'tackle', 'noun': 'climate change', 'ar': 'مواجهة التغير المناخي'}, + ], + }, + ]; + + // --------------------------------------------------------------------------- + // Wing 4: Dialogue & Listening Comprehension State + // --------------------------------------------------------------------------- + int _activeDialogueLine = 0; + + static const List> _dialogueLines = [ + { + 'speaker': 'Interviewer (Sarah)', + 'text': 'Dr. Zaid, welcome to our science podcast. Why is the Gulf of Aqaba so unique for marine research?', + 'ar': 'د. زيد، مرحباً بك في بودكاست العلوم. ما الذي يجعل خليج العقبة فريداً جداً للبحوث البحرية؟', + }, + { + 'speaker': 'Dr. Zaid (Marine Biologist)', + 'text': 'Thank you, Sarah. The coral reefs in Aqaba have demonstrated remarkable thermal resistance against rising water temperatures.', + 'ar': 'شكراً سارة. أثبتت الشعاب المرجانية في العقبة مقاومة حرارية ملحوظة لارتفاع درجات حرارة المياه.', + }, + { + 'speaker': 'Interviewer (Sarah)', + 'text': 'That is extraordinary! How are Jordanian scientists working to protect these resilient coral colonies?', + 'ar': 'هذا أمر استثنائي! كيف يعمل العلماء الأردنيون على حماية هذه المستعمرات المرجانية الصامدة؟', + }, + { + 'speaker': 'Dr. Zaid (Marine Biologist)', + 'text': 'We have established specialized marine reserves and deployed artificial reefs to foster underwater biodiversity.', + 'ar': 'أنشأنا محميات بحرية متخصصة ونشرنا شعاباً اصطناعية لتعزيز التنوع البيولوجي تحت الماء.', }, ]; @override void initState() { super.initState(); - _tabController = TabController(length: 2, vsync: this); - _initTts(); - _resetSentenceBuilder(); - } - - Future _initTts() async { - try { - await _tts.setLanguage('en-US'); - await _tts.setPitch(1.0); - await _tts.setSpeechRate(_speechRate); - _tts.setCompletionHandler(() { - if (mounted) setState(() => _isPlayingAudio = false); - }); - _tts.setErrorHandler((_) { - if (mounted) setState(() => _isPlayingAudio = false); - }); - } catch (_) {} - } - - Future _playText(String text) async { - if (_isPlayingAudio) { - await _tts.stop(); - setState(() => _isPlayingAudio = false); - return; - } - setState(() => _isPlayingAudio = true); - await _tts.setSpeechRate(_speechRate); - await _tts.speak(text); - } - - void _resetSentenceBuilder() { - setState(() { - _assembledSentence = []; - _isSentenceCorrect = null; - }); - } - - void _checkSentence() { - final currentRule = _grammarRules[_selectedGrammarRuleIdx]; - final List correct = List.from(currentRule['correct']); - final List? alt = currentRule['alternative'] != null ? List.from(currentRule['alternative']) : null; - - bool isMatch = _listEquals(_assembledSentence, correct); - if (!isMatch && alt != null) { - isMatch = _listEquals(_assembledSentence, alt); - } - - setState(() { - _isSentenceCorrect = isMatch; - }); - } - - bool _listEquals(List a, List b) { - if (a.length != b.length) return false; - for (int i = 0; i < a.length; i++) { - if (a[i] != b[i]) return false; - } - return true; + _tabController = TabController(length: 4, vsync: this); } @override void dispose() { - _tts.stop(); _tabController.dispose(); + _tts.stop(); super.dispose(); } + Future _speakText(String text, {double rate = 0.9}) async { + if (text.isEmpty) return; + try { + await _tts.stop(); + await _tts.setLanguage('en-US'); + await _tts.setSpeechRate(rate); + await _tts.speak(text); + } catch (_) {} + } + @override Widget build(BuildContext context) { return Container( - color: const Color(0xFF07111F), + color: AppColors.darkBackground, child: Column( children: [ - _buildLabTopBar(), + _buildStudioHeader(), + _buildTabBar(), Expanded( - child: LayoutBuilder( - builder: (context, constraints) { - final isWide = constraints.maxWidth >= 768; - return TabBarView( - controller: _tabController, - children: [ - // Tab 1: Phonetics & Vocabulary Studio - isWide ? _buildWideVocabLayout() : _buildMobileVocabLayout(), - // Tab 2: Grammar & Timeline Sandbox - isWide ? _buildWideGrammarLayout() : _buildMobileGrammarLayout(), - ], - ); - }, + child: TabBarView( + controller: _tabController, + children: [ + _buildGrammarWing(), + _buildPhoneticsWing(), + _buildVocabWing(), + _buildListeningWing(), + ], ), ), ], @@ -241,11 +325,11 @@ class _EnglishInteractiveLabViewState extends State w ); } - Widget _buildLabTopBar() { + Widget _buildStudioHeader() { return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( - color: const Color(0xFF0E1A2C), + color: AppColors.darkSurface, border: Border(bottom: BorderSide(color: Colors.white.withValues(alpha: 0.08))), ), child: Row( @@ -253,793 +337,738 @@ class _EnglishInteractiveLabViewState extends State w Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( - color: AppColors.appleBlue.withValues(alpha: 0.2), + color: const Color(0xFF60A5FA).withValues(alpha: 0.15), borderRadius: BorderRadius.circular(10), ), - child: const Icon(CupertinoIcons.waveform_path, color: AppColors.saqelCyan, size: 20), + child: const Icon(CupertinoIcons.globe, color: Color(0xFF60A5FA), size: 18), ), const SizedBox(width: 12), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, children: [ Text( - 'مختبر اللغة الإنجليزية التفاعلي — Action Pack 10', + 'Action Pack 10 English Interactive Studio 🇬🇧', style: TextStyle(color: Colors.white, fontSize: 14.5, fontWeight: FontWeight.w800), ), Text( - 'صوتيات IPA، مصفوفة القواعد، ومحاكي النطق والخط الزمني', - style: TextStyle(color: Color(0xFF8CA1BA), fontSize: 11.5), + 'Grammar Matrix • Phonetics & Stress • Thematic Vocab • Listening Challenge', + style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 11), ), ], ), ), - Container( - height: 36, - decoration: BoxDecoration( - color: const Color(0xFF15263F), - borderRadius: BorderRadius.circular(10), - ), - child: TabBar( - controller: _tabController, - isScrollable: true, - indicatorSize: TabBarIndicatorSize.tab, - indicator: BoxDecoration( - color: AppColors.appleBlue, - borderRadius: BorderRadius.circular(8), - ), - labelColor: Colors.white, - unselectedLabelColor: const Color(0xFF8CA1BA), - labelStyle: const TextStyle(fontSize: 12, fontWeight: FontWeight.w700), - tabs: const [ - Tab(text: '🎙️ الصوتيات والمفردات'), - Tab(text: '📐 مصفوفة القواعد والتركيب'), - ], - ), - ), ], ), ); } - // =========================================================================== - // TAB 1: PHONETICS & VOCABULARY (2/3 Canvas + 1/3 Controls) - // =========================================================================== - Widget _buildWideVocabLayout() { - final activeWord = _vocabBank.firstWhere((w) => w['id'] == _activeWordId, orElse: () => _vocabBank.first); - return Row( - children: [ - // 2/3 Canvas: Dominant Phonetic Waveform & Interactive Sound Card - Expanded( - flex: 2, - child: Padding( - padding: const EdgeInsets.all(20), - child: _buildDominantPhoneticCanvas(activeWord), - ), - ), - // 1/3 Controls: Vocabulary Selector & Audio Tuning Panel - Container( - width: 340, - decoration: BoxDecoration( - color: const Color(0xFF0C1726), - border: Border(right: BorderSide(color: Colors.white.withValues(alpha: 0.08))), - ), - child: _buildVocabSelectorSidebar(), - ), - ], - ); - } - - Widget _buildMobileVocabLayout() { - final activeWord = _vocabBank.firstWhere((w) => w['id'] == _activeWordId, orElse: () => _vocabBank.first); - return SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - children: [ - _buildDominantPhoneticCanvas(activeWord), - const SizedBox(height: 16), - _buildVocabSelectorSidebar(), - ], - ), - ); - } - - Widget _buildDominantPhoneticCanvas(Map word) { + Widget _buildTabBar() { return Container( - decoration: BoxDecoration( - color: const Color(0xFF102035), - borderRadius: BorderRadius.circular(20), - border: Border.all(color: AppColors.saqelCyan.withValues(alpha: 0.3)), - boxShadow: [ - BoxShadow( - color: Colors.black.withValues(alpha: 0.4), - blurRadius: 16, - offset: const Offset(0, 8), - ), - ], - ), - padding: const EdgeInsets.all(24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - // Module Badge - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - decoration: BoxDecoration( - color: AppColors.appleBlue.withValues(alpha: 0.2), - borderRadius: BorderRadius.circular(8), - ), - child: Text( - word['module'], - style: const TextStyle(color: AppColors.saqelCyan, fontSize: 11, fontWeight: FontWeight.w700), - ), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.08), - borderRadius: BorderRadius.circular(6), - ), - child: Text( - word['pos'].toString().toUpperCase(), - style: const TextStyle(color: Color(0xFF8CA1BA), fontSize: 11, fontWeight: FontWeight.w700), - ), - ), - ], - ), - const SizedBox(height: 20), - - // Big Headword Display - Text( - word['word'], - style: const TextStyle( - color: Colors.white, - fontSize: 34, - fontWeight: FontWeight.w900, - letterSpacing: 0.8, - ), - ), - const SizedBox(height: 6), - - // IPA Phonetics Notation - Row( - children: [ - Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), - decoration: BoxDecoration( - color: const Color(0xFF091422), - borderRadius: BorderRadius.circular(8), - border: Border.all(color: AppColors.saqelCyan.withValues(alpha: 0.2)), - ), - child: Text( - word['ipa'], - style: const TextStyle( - color: AppColors.saqelCyan, - fontSize: 18, - fontWeight: FontWeight.w700, - fontFamily: 'monospace', - ), - ), - ), - const SizedBox(width: 12), - Text( - '• ${word['stress']}', - style: const TextStyle(color: Color(0xFFB9C7D8), fontSize: 12.5), - ), - ], - ), - const SizedBox(height: 18), - - // Arabic Meaning Card - Container( - padding: const EdgeInsets.all(14), - decoration: BoxDecoration( - color: const Color(0xFF0B1728), - borderRadius: BorderRadius.circular(12), - border: Border.all(color: Colors.white.withValues(alpha: 0.06)), - ), - child: Row( - children: [ - const Icon(CupertinoIcons.checkmark_seal_fill, color: AppColors.emeraldGreen, size: 20), - const SizedBox(width: 12), - Expanded( - child: Text( - word['arabic'], - style: const TextStyle(color: Colors.white, fontSize: 14.5, fontWeight: FontWeight.w700), - ), - ), - ], - ), - ), - const SizedBox(height: 16), - - // English Definition & Example - Container( - padding: const EdgeInsets.all(14), - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.03), - borderRadius: BorderRadius.circular(12), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text('Oxford / Action Pack Definition:', style: TextStyle(color: Color(0xFF8CA1BA), fontSize: 11, fontWeight: FontWeight.w700)), - const SizedBox(height: 4), - Text(word['definition'], style: const TextStyle(color: Colors.white, fontSize: 13, height: 1.5)), - const SizedBox(height: 10), - const Text('Example in Context:', style: TextStyle(color: AppColors.saqelCyan, fontSize: 11, fontWeight: FontWeight.w700)), - const SizedBox(height: 4), - Text('"${word['example']}"', style: const TextStyle(color: Color(0xFFD4E2F4), fontSize: 13, fontStyle: FontStyle.italic)), - ], - ), - ), - - const Spacer(), - - // Acoustic Waveform / Action Bar - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: const Color(0xFF081321), - borderRadius: BorderRadius.circular(14), - border: Border.all(color: Colors.white.withValues(alpha: 0.08)), - ), - child: Row( - children: [ - ElevatedButton.icon( - style: ElevatedButton.styleFrom( - backgroundColor: _isPlayingAudio ? Colors.redAccent : AppColors.appleBlue, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - ), - onPressed: () => _playText(word['audioText']), - icon: Icon(_isPlayingAudio ? CupertinoIcons.stop_fill : CupertinoIcons.volume_up, size: 18), - label: Text(_isPlayingAudio ? 'إيقاف النطق' : 'استمع للنطق الأصيل'), - ), - const SizedBox(width: 16), - Expanded( - child: CustomPaint( - size: const Size(double.infinity, 36), - painter: _WaveformSimulationPainter(isPlaying: _isPlayingAudio), - ), - ), - ], - ), - ), + color: AppColors.darkSurface, + child: TabBar( + controller: _tabController, + isScrollable: true, + indicatorColor: const Color(0xFF60A5FA), + indicatorWeight: 3, + labelColor: const Color(0xFF60A5FA), + unselectedLabelColor: Colors.white60, + labelStyle: const TextStyle(fontSize: 12.5, fontWeight: FontWeight.w800), + tabs: const [ + Tab(icon: Icon(CupertinoIcons.chart_bar_alt_fill, size: 16), text: 'مصفوفة القواعد (Grammar) 📐'), + Tab(icon: Icon(CupertinoIcons.headphones, size: 16), text: 'الصوتيات والنبر (Phonetics) 🎙️'), + Tab(icon: Icon(CupertinoIcons.text_badge_checkmark, size: 16), text: 'المفردات والمتلازمات 📚'), + Tab(icon: Icon(CupertinoIcons.waveform, size: 16), text: 'تحدي الاستماع (Listening) ⚡'), ], ), ); } - Widget _buildVocabSelectorSidebar() { - return Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const Text( - 'مفردات المنهاج المعتمدة', - style: TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w800), - ), - const SizedBox(height: 4), - const Text( - 'اختر الكلمة للاستماع والتحليل الفونيتيكي', - style: TextStyle(color: Color(0xFF8CA1BA), fontSize: 11), - ), - const SizedBox(height: 12), + // --------------------------------------------------------------------------- + // WING 1: GRAMMAR MATRIX & TENSE SIMULATOR + // --------------------------------------------------------------------------- + Widget _buildGrammarWing() { + final curTense = _tenses[_selectedTenseIndex]; + final tenseColor = curTense['accent'] as Color; - // Speed slider - Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: const Color(0xFF102035), - borderRadius: BorderRadius.circular(10), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return ListView( + padding: const EdgeInsets.all(16), + children: [ + // Tense Selector + const Text( + 'Select Tense Aspect to inspect structure and timeline:', + style: TextStyle(color: Colors.white70, fontSize: 12.5, fontWeight: FontWeight.w700), + ), + const SizedBox(height: 8), + SizedBox( + height: 40, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: _tenses.length, + separatorBuilder: (_, __) => const SizedBox(width: 8), + itemBuilder: (ctx, i) { + final sel = i == _selectedTenseIndex; + final t = _tenses[i]; + return ChoiceChip( + label: Text( + t['name'] as String, + style: TextStyle(color: sel ? Colors.black : Colors.white, fontWeight: FontWeight.w800), + ), + selected: sel, + selectedColor: t['accent'] as Color, + backgroundColor: Colors.white.withValues(alpha: 0.06), + onSelected: (val) { + if (val) setState(() => _selectedTenseIndex = i); + }, + ); + }, + ), + ), + const SizedBox(height: 16), + + // Tense Card + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF0F172A), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: tenseColor.withValues(alpha: 0.4)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: tenseColor.withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + curTense['name'] as String, + style: TextStyle(color: tenseColor, fontWeight: FontWeight.w800, fontSize: 13), + ), + ), + IconButton( + icon: const Icon(CupertinoIcons.volume_up, color: Colors.white, size: 20), + onPressed: () => _speakText(curTense['example'] as String), + ), + ], + ), + const SizedBox(height: 10), + + // Formula Box + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.35), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: Colors.white10), + ), + child: Column( children: [ - const Text('سرعة القراءة (Playback Rate):', style: TextStyle(color: Color(0xFF8CA1BA), fontSize: 11)), - Text('${(_speechRate * 2).toStringAsFixed(1)}x', style: const TextStyle(color: AppColors.saqelCyan, fontSize: 11, fontWeight: FontWeight.w700)), + const Text('Sentence Structure (التركيب القواعدي):', style: TextStyle(color: Colors.white54, fontSize: 11)), + const SizedBox(height: 4), + Text( + curTense['formula'] as String, + textAlign: TextAlign.center, + style: TextStyle(color: tenseColor, fontSize: 14.5, fontWeight: FontWeight.w900, letterSpacing: 0.5), + ), ], ), - Slider( - value: _speechRate, - min: 0.25, - max: 0.75, - activeColor: AppColors.saqelCyan, - inactiveColor: Colors.white.withValues(alpha: 0.1), - onChanged: (val) { - setState(() => _speechRate = val); - _tts.setSpeechRate(val); - }, + ), + const SizedBox(height: 12), + + Text( + '• Example: ${curTense['example']}', + style: const TextStyle(color: Colors.white, fontSize: 13.5, fontWeight: FontWeight.w700), + ), + const SizedBox(height: 6), + Text( + '• Timeline Use: ${curTense['timeline']}', + style: const TextStyle(color: Colors.white70, fontSize: 12), + ), + const SizedBox(height: 6), + Text( + '💡 الشرح بالعربية: ${curTense['arabic']}', + style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11.5, height: 1.4), + ), + ], + ), + ), + const SizedBox(height: 16), + + // Conditionals Section + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'If-Conditionals Engine (قواعد الجمل الشرطية):', + style: TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w800), + ), + TextButton.icon( + icon: Icon(_showPassiveMode ? CupertinoIcons.checkmark_circle_fill : CupertinoIcons.circle, size: 16, color: const Color(0xFF00F5D4)), + label: const Text('Passive Mode', style: TextStyle(color: Color(0xFF00F5D4), fontSize: 12, fontWeight: FontWeight.w700)), + onPressed: () => setState(() => _showPassiveMode = !_showPassiveMode), + ), + ], + ), + const SizedBox(height: 8), + + if (!_showPassiveMode) ...[ + Column( + children: List.generate(_conditionals.length, (idx) { + final cond = _conditionals[idx]; + final sel = idx == _selectedConditionalIndex; + return GestureDetector( + onTap: () => setState(() => _selectedConditionalIndex = idx), + child: Container( + margin: const EdgeInsets.only(bottom: 8), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: sel ? const Color(0xFF1E293B) : Colors.white.withValues(alpha: 0.04), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: sel ? const Color(0xFF60A5FA) : Colors.white10), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(cond['type'] as String, style: const TextStyle(color: Color(0xFF60A5FA), fontWeight: FontWeight.w800, fontSize: 13)), + IconButton( + icon: const Icon(CupertinoIcons.volume_up, color: Colors.white60, size: 16), + onPressed: () => _speakText(cond['example'] as String), + ), + ], + ), + Text('• Structure: ${cond['ifClause']} ➔ ${cond['mainClause']}', style: const TextStyle(color: Colors.white70, fontSize: 12)), + const SizedBox(height: 4), + Text('• Example: ${cond['example']}', style: const TextStyle(color: Colors.white, fontSize: 12.5, fontWeight: FontWeight.w700)), + const SizedBox(height: 4), + Text('💡 ${cond['note']}', style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11)), + ], + ), ), + ); + }), + ), + ] else ...[ + // Passive Voice Visual Transformer + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF0D253A), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: const Color(0xFF00F5D4).withValues(alpha: 0.4)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('Active to Passive Voice Transformation (المبني للمجهول):', style: TextStyle(color: Color(0xFF00F5D4), fontWeight: FontWeight.w800, fontSize: 13)), + const SizedBox(height: 10), + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration(color: Colors.black38, borderRadius: BorderRadius.circular(8)), + child: const Text('Active: The scientist discovered a new coral species in Aqaba.', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w700)), + ), + const Center(child: Padding(padding: EdgeInsets.symmetric(vertical: 6), child: Icon(CupertinoIcons.arrow_down, color: Color(0xFF00F5D4), size: 20))), + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration(color: const Color(0xFF00F5D4).withValues(alpha: 0.15), borderRadius: BorderRadius.circular(8)), + child: const Text('Passive: A new coral species was discovered (by the scientist) in Aqaba.', style: TextStyle(color: Color(0xFF00F5D4), fontWeight: FontWeight.w800)), + ), + const SizedBox(height: 8), + const Text('Formula: Object + was/were + V3 (Past Participle) + by + Agent', style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 11.5)), ], ), ), - const SizedBox(height: 12), - - // Word List - Expanded( - child: ListView.separated( - itemCount: _vocabBank.length, - separatorBuilder: (_, __) => const SizedBox(height: 8), - itemBuilder: (context, idx) { - final w = _vocabBank[idx]; - final isSelected = w['id'] == _activeWordId; - return InkWell( - onTap: () { - setState(() => _activeWordId = w['id']); - _playText(w['word']); - }, - borderRadius: BorderRadius.circular(12), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), - decoration: BoxDecoration( - color: isSelected ? AppColors.appleBlue.withValues(alpha: 0.25) : const Color(0xFF102035), - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: isSelected ? AppColors.saqelCyan : Colors.transparent, - width: 1.5, - ), - ), - child: Row( - children: [ - Icon( - isSelected ? CupertinoIcons.speaker_2_fill : CupertinoIcons.speaker_1, - color: isSelected ? AppColors.saqelCyan : const Color(0xFF8CA1BA), - size: 16, - ), - const SizedBox(width: 10), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - w['word'], - style: TextStyle( - color: isSelected ? Colors.white : const Color(0xFFD4E2F4), - fontWeight: FontWeight.w800, - fontSize: 13.5, - ), - ), - Text( - w['arabic'], - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: const TextStyle(color: Color(0xFF8CA1BA), fontSize: 11), - ), - ], - ), - ), - ], - ), - ), - ); - }, - ), - ), ], - ), + ], ); } - // =========================================================================== - // TAB 2: GRAMMAR MATRIX & TIMELINE SANDBOX (2/3 Canvas + 1/3 Controls) - // =========================================================================== - Widget _buildWideGrammarLayout() { - final currentRule = _grammarRules[_selectedGrammarRuleIdx]; - return Row( + // --------------------------------------------------------------------------- + // WING 2: PHONETICS, STRESS SHIFTS & MINIMAL PAIRS + // --------------------------------------------------------------------------- + Widget _buildPhoneticsWing() { + final curPair = _minimalPairs[_selectedPairIndex]; + final curStress = _stressShifts[_selectedStressIndex]; + + return ListView( + padding: const EdgeInsets.all(16), children: [ - // 2/3 Dominant Canvas: Visual Timeline & Sentence Builder Playground - Expanded( - flex: 2, - child: Padding( - padding: const EdgeInsets.all(20), - child: _buildGrammarInteractiveCanvas(currentRule), + // Minimal Pairs Trainer + const Text( + 'Minimal Pairs Studio (/p/ vs /b/, /iː/ vs /ɪ/, /θ/ vs /ð/):', + style: TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w800), + ), + const SizedBox(height: 8), + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF0F172A), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: const Color(0xFF60A5FA).withValues(alpha: 0.4)), + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(curPair['pair'] as String, style: const TextStyle(color: Color(0xFF60A5FA), fontSize: 16, fontWeight: FontWeight.w900)), + Text(curPair['contrast'] as String, style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11.5)), + ], + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + _buildAudioWordTile( + word: curPair['wordA'] as String, + ipa: curPair['ipaA'] as String, + arabic: curPair['arA'] as String, + color: const Color(0xFF00F5D4), + ), + const Text('VS', style: TextStyle(color: Colors.white38, fontWeight: FontWeight.w900)), + _buildAudioWordTile( + word: curPair['wordB'] as String, + ipa: curPair['ipaB'] as String, + arabic: curPair['arB'] as String, + color: const Color(0xFFFF9F0A), + ), + ], + ), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate(_minimalPairs.length, (idx) { + final sel = idx == _selectedPairIndex; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: ChoiceChip( + label: Text(_minimalPairs[idx]['pair'] as String), + selected: sel, + selectedColor: const Color(0xFF60A5FA), + onSelected: (v) => setState(() => _selectedPairIndex = idx), + ), + ); + }), + ), + ], ), ), - // 1/3 Controls: Grammar Rules Matrix & Rule Selector + const SizedBox(height: 18), + + // Syllable Stress Shift Inspector (Noun vs Verb) + const Text( + 'Syllable Stress Shift (نبر الكلمات بين الأسماء والأفعال):', + style: TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w800), + ), + const SizedBox(height: 8), Container( - width: 340, + padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: const Color(0xFF0C1726), - border: Border(right: BorderSide(color: Colors.white.withValues(alpha: 0.08))), + color: const Color(0xFF1E293B), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: Colors.white12), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text('Word: ${curStress['word']}', textAlign: TextAlign.center, style: const TextStyle(color: Color(0xFFFFD60A), fontSize: 16, fontWeight: FontWeight.w900)), + const SizedBox(height: 12), + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration(color: Colors.black26, borderRadius: BorderRadius.circular(8)), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('• ${curStress['noun']}', style: const TextStyle(color: Color(0xFF00F5D4), fontWeight: FontWeight.w900, fontSize: 15)), + Text('${curStress['nounIpa']} - ${curStress['nounDef']}', style: const TextStyle(color: Colors.white70, fontSize: 11)), + ], + ), + IconButton( + icon: const Icon(CupertinoIcons.volume_up, color: Color(0xFF00F5D4)), + onPressed: () => _speakText(curStress['word'] as String), + ), + ], + ), + ), + const SizedBox(height: 8), + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration(color: Colors.black26, borderRadius: BorderRadius.circular(8)), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('• ${curStress['verb']}', style: const TextStyle(color: Color(0xFFFF9F0A), fontWeight: FontWeight.w900, fontSize: 15)), + Text('${curStress['verbIpa']} - ${curStress['verbDef']}', style: const TextStyle(color: Colors.white70, fontSize: 11)), + ], + ), + IconButton( + icon: const Icon(CupertinoIcons.volume_up, color: Color(0xFFFF9F0A)), + onPressed: () => _speakText(curStress['word'] as String, rate: 0.7), + ), + ], + ), + ), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate(_stressShifts.length, (idx) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: ChoiceChip( + label: Text(_stressShifts[idx]['word'] as String), + selected: idx == _selectedStressIndex, + onSelected: (v) => setState(() => _selectedStressIndex = idx), + ), + ); + }), + ), + ], + ), + ), + const SizedBox(height: 18), + const Text( + 'Silent Letters Inspector (كاشف الحروف الصامتة):', + style: TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w800), + ), + const SizedBox(height: 8), + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF0F172A), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: Colors.white12), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Wrap( + spacing: 8, + runSpacing: 8, + alignment: WrapAlignment.center, + children: List.generate(_silentLetters.length, (idx) { + final sl = _silentLetters[idx]; + final sel = idx == _selectedSilentIndex; + return ChoiceChip( + label: Text(sl['word'] as String, style: TextStyle(color: sel ? Colors.black : Colors.white, fontWeight: FontWeight.w800)), + selected: sel, + selectedColor: const Color(0xFF00F5D4), + onSelected: (v) => setState(() => _selectedSilentIndex = idx), + ); + }), + ), + const SizedBox(height: 12), + Builder(builder: (ctx) { + final cur = _silentLetters[_selectedSilentIndex]; + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration(color: Colors.black38, borderRadius: BorderRadius.circular(10)), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Word: ${cur['word']} (${cur['ipa']})', style: const TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w800)), + const SizedBox(height: 4), + Text('• Silent Letter: "${cur['silent']}"', style: const TextStyle(color: Color(0xFFFF453A), fontSize: 13, fontWeight: FontWeight.w700)), + Text('💡 ${cur['ar']}', style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11.5)), + ], + ), + ), + IconButton( + icon: const Icon(CupertinoIcons.volume_up, color: Color(0xFF00F5D4)), + onPressed: () => _speakText(cur['word'] as String), + ), + ], + ), + ); + }), + ], ), - child: _buildGrammarSelectorSidebar(), ), ], ); } - Widget _buildMobileGrammarLayout() { - final currentRule = _grammarRules[_selectedGrammarRuleIdx]; - return SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - children: [ - _buildGrammarInteractiveCanvas(currentRule), - const SizedBox(height: 16), - _buildGrammarSelectorSidebar(), - ], - ), - ); - } - - Widget _buildGrammarInteractiveCanvas(Map rule) { + Widget _buildAudioWordTile({ + required String word, + required String ipa, + required String arabic, + required Color color, + }) { return Container( + width: 140, + padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: const Color(0xFF102035), - borderRadius: BorderRadius.circular(20), - border: Border.all(color: AppColors.saqelCyan.withValues(alpha: 0.3)), + color: color.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: color.withValues(alpha: 0.4)), ), - padding: const EdgeInsets.all(24), child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - // Concept Header - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - rule['rule'], - style: const TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.w900), - ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - decoration: BoxDecoration( - color: AppColors.emeraldGreen.withValues(alpha: 0.2), - borderRadius: BorderRadius.circular(8), - ), - child: Text( - rule['concept'], - style: const TextStyle(color: AppColors.emeraldGreen, fontSize: 11, fontWeight: FontWeight.w700), - ), - ), - ], - ), + Text(word, style: const TextStyle(color: Colors.white, fontSize: 18, fontWeight: FontWeight.w900)), + Text(ipa, style: TextStyle(color: color, fontSize: 12, fontWeight: FontWeight.w700)), + const SizedBox(height: 4), + Text(arabic, textAlign: TextAlign.center, style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 10)), const SizedBox(height: 8), - Text(rule['explanation'], style: const TextStyle(color: Color(0xFFB9C7D8), fontSize: 13, height: 1.5)), - const SizedBox(height: 14), - - // Formula Card - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: const Color(0xFF091422), - borderRadius: BorderRadius.circular(10), - border: Border.all(color: Colors.white.withValues(alpha: 0.06)), + ElevatedButton.icon( + icon: const Icon(CupertinoIcons.volume_up, size: 14), + label: const Text('Listen'), + style: ElevatedButton.styleFrom( + backgroundColor: color, + foregroundColor: Colors.black, + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + textStyle: const TextStyle(fontSize: 11, fontWeight: FontWeight.w800), ), - child: Text( - rule['formula'], - style: const TextStyle(color: AppColors.saqelCyan, fontSize: 12.5, fontFamily: 'monospace', fontWeight: FontWeight.w700), - ), - ), - const SizedBox(height: 18), - - // Tense Timeline Visualization - Container( - height: 90, - decoration: BoxDecoration( - color: const Color(0xFF081321), - borderRadius: BorderRadius.circular(14), - border: Border.all(color: Colors.white.withValues(alpha: 0.08)), - ), - child: CustomPaint( - size: const Size(double.infinity, 90), - painter: _GrammarTimelinePainter(ruleIndex: _selectedGrammarRuleIdx), - ), - ), - const SizedBox(height: 20), - - // Interactive Sentence Builder (Drag/Tap Chips) - const Text( - '🧩 محاكي بناء الجملة (Sentence Builder):', - style: TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w800), - ), - const SizedBox(height: 8), - Text( - rule['hint'], - style: const TextStyle(color: Color(0xFF8CA1BA), fontSize: 11.5), - ), - const SizedBox(height: 12), - - // Scrambled Available Tokens - Wrap( - spacing: 8, - runSpacing: 8, - children: (rule['scrambled'] as List).map((word) { - final isUsed = _assembledSentence.contains(word); - return ActionChip( - backgroundColor: isUsed ? Colors.white.withValues(alpha: 0.05) : const Color(0xFF15263F), - side: BorderSide(color: isUsed ? Colors.transparent : AppColors.saqelCyan.withValues(alpha: 0.4)), - label: Text( - word, - style: TextStyle( - color: isUsed ? const Color(0xFF536A84) : Colors.white, - fontWeight: FontWeight.w700, - ), - ), - onPressed: isUsed - ? null - : () { - setState(() { - _assembledSentence.add(word); - _isSentenceCorrect = null; - }); - }, - ); - }).toList(), - ), - const SizedBox(height: 16), - - // Student Constructed Sentence Drop Area - Container( - padding: const EdgeInsets.all(16), - constraints: const BoxConstraints(minHeight: 64), - decoration: BoxDecoration( - color: const Color(0xFF081321), - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: _isSentenceCorrect == null - ? Colors.white.withValues(alpha: 0.12) - : (_isSentenceCorrect! ? AppColors.emeraldGreen : Colors.redAccent), - width: 1.5, - ), - ), - child: _assembledSentence.isEmpty - ? const Center( - child: Text('اضغط على الكلمات بالأعلى لترتيب جملة صحيحة قواعدياً', style: TextStyle(color: Color(0xFF536A84), fontSize: 12)), - ) - : Wrap( - spacing: 8, - runSpacing: 8, - children: _assembledSentence.map((token) { - return Chip( - backgroundColor: AppColors.appleBlue.withValues(alpha: 0.3), - label: Text(token, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700)), - deleteIcon: const Icon(CupertinoIcons.xmark_circle_fill, size: 16, color: Colors.white70), - onDeleted: () { - setState(() { - _assembledSentence.remove(token); - _isSentenceCorrect = null; - }); - }, - ); - }).toList(), - ), - ), - const SizedBox(height: 14), - - // Validation Actions - Row( - children: [ - ElevatedButton.icon( - style: ElevatedButton.styleFrom( - backgroundColor: AppColors.emeraldGreen, - foregroundColor: Colors.white, - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - ), - onPressed: _assembledSentence.isEmpty ? null : _checkSentence, - icon: const Icon(CupertinoIcons.check_mark, size: 16), - label: const Text('تحقق من صحة القواعد'), - ), - const SizedBox(width: 10), - OutlinedButton.icon( - style: OutlinedButton.styleFrom( - foregroundColor: const Color(0xFFB9C7D8), - side: BorderSide(color: Colors.white.withValues(alpha: 0.15)), - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), - ), - onPressed: _resetSentenceBuilder, - icon: const Icon(CupertinoIcons.arrow_counterclockwise, size: 16), - label: const Text('إعادة ضبط'), - ), - const Spacer(), - if (_isSentenceCorrect != null) - Text( - _isSentenceCorrect! ? '✅ صياغة سليمة 100%!' : '❌ صياغة غير صحيحة، راجع القاعدة وأعد المحاولة', - style: TextStyle( - color: _isSentenceCorrect! ? AppColors.emeraldGreen : Colors.redAccent, - fontSize: 12.5, - fontWeight: FontWeight.w800, - ), - ), - ], + onPressed: () => _speakText(word), ), ], ), ); } - Widget _buildGrammarSelectorSidebar() { - return Padding( + // --------------------------------------------------------------------------- + // WING 3: ACTION PACK 10 VOCABULARY & COLLOCATIONS + // --------------------------------------------------------------------------- + Widget _buildVocabWing() { + final curModule = _modules[_selectedModuleIndex]; + final vocabList = curModule['vocab'] as List>; + final collocations = curModule['collocations'] as List>; + final modColor = curModule['color'] as Color; + + return ListView( padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const Text( - 'قواعد منهاج Action Pack 10', - style: TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w800), + children: [ + // Module Selector + Row( + children: List.generate(_modules.length, (idx) { + final sel = idx == _selectedModuleIndex; + return Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: sel ? (_modules[idx]['color'] as Color) : Colors.white.withValues(alpha: 0.06), + foregroundColor: sel ? Colors.black : Colors.white, + padding: const EdgeInsets.symmetric(vertical: 10), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + ), + onPressed: () => setState(() => _selectedModuleIndex = idx), + child: Text('Mod ${idx + 1}', style: const TextStyle(fontWeight: FontWeight.w800, fontSize: 12)), + ), + ), + ); + }), + ), + const SizedBox(height: 14), + + // Module Header Card + Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: modColor.withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(14), + border: Border.all(color: modColor.withValues(alpha: 0.3)), ), - const SizedBox(height: 4), - const Text( - 'اختر القاعدة للتدريب البصري والتطبيقي', - style: TextStyle(color: Color(0xFF8CA1BA), fontSize: 11), + child: Row( + children: [ + Icon(curModule['icon'] as IconData, color: modColor, size: 24), + const SizedBox(width: 12), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(curModule['title'] as String, style: TextStyle(color: modColor, fontWeight: FontWeight.w900, fontSize: 14)), + Text(curModule['subtitle'] as String, style: const TextStyle(color: Colors.white70, fontSize: 11.5)), + ], + ), + ], ), - const SizedBox(height: 14), - Expanded( - child: ListView.separated( - itemCount: _grammarRules.length, - separatorBuilder: (_, __) => const SizedBox(height: 10), - itemBuilder: (context, idx) { - final r = _grammarRules[idx]; - final isSelected = idx == _selectedGrammarRuleIdx; - return InkWell( - onTap: () { - setState(() { - _selectedGrammarRuleIdx = idx; - _resetSentenceBuilder(); - }); - }, - borderRadius: BorderRadius.circular(12), - child: Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: isSelected ? AppColors.appleBlue.withValues(alpha: 0.25) : const Color(0xFF102035), - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: isSelected ? AppColors.saqelCyan : Colors.transparent, - width: 1.5, - ), - ), + ), + const SizedBox(height: 14), + + // Vocabulary Cards with Sound + const Text('Thematic Keywords (المفردات التخصصية المقررة):', style: TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w800)), + const SizedBox(height: 8), + Column( + children: vocabList.map((v) { + return Container( + margin: const EdgeInsets.only(bottom: 8), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: const Color(0xFF0F172A), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.white10), + ), + child: Row( + children: [ + Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - r['rule'], - style: TextStyle( - color: isSelected ? Colors.white : const Color(0xFFD4E2F4), - fontWeight: FontWeight.w800, - fontSize: 13, - ), + Row( + children: [ + Text(v['en']!, style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w800)), + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration(color: modColor.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(4)), + child: Text(v['ar']!, style: TextStyle(color: modColor, fontSize: 10.5, fontWeight: FontWeight.w700)), + ), + ], ), const SizedBox(height: 4), - Text( - r['concept'], - style: const TextStyle(color: AppColors.saqelCyan, fontSize: 11), - ), + Text('• ${v['example']!}', style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11.5, height: 1.35)), ], ), ), - ); - }, - ), + IconButton( + icon: const Icon(CupertinoIcons.volume_up, color: Colors.white70, size: 20), + onPressed: () => _speakText('${v['en']}. ${v['example']}'), + ), + ], + ), + ); + }).toList(), + ), + const SizedBox(height: 12), + + // Collocations Studio + const Text('Key Collocations (المتلازمات اللفظية بالمنهاج):', style: TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w800)), + const SizedBox(height: 8), + Wrap( + spacing: 8, + runSpacing: 8, + children: collocations.map((c) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.05), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: Colors.white12), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text('${c['verb']} ', style: TextStyle(color: modColor, fontWeight: FontWeight.w900, fontSize: 13)), + Text(c['noun']!, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 13)), + const SizedBox(width: 6), + Text('(${c['ar']!})', style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11)), + ], + ), + ); + }).toList(), + ), + ], + ); + } + + // --------------------------------------------------------------------------- + // WING 4: DIALOGUE & LISTENING COMPREHENSION + // --------------------------------------------------------------------------- + Widget _buildListeningWing() { + final curLine = _dialogueLines[_activeDialogueLine]; + + return ListView( + padding: const EdgeInsets.all(16), + children: [ + // Podcast Banner + Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + gradient: const LinearGradient(colors: [Color(0xFF0F2B48), Color(0xFF091624)]), + borderRadius: BorderRadius.circular(14), + border: Border.all(color: const Color(0xFF60A5FA).withValues(alpha: 0.4)), ), - ], - ), + child: Row( + children: [ + const Icon(CupertinoIcons.mic_circle_fill, color: Color(0xFF60A5FA), size: 36), + const SizedBox(width: 12), + const Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('Action Pack 10 Audio Track (Gulf of Aqaba)', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 13.5)), + Text('Interview with Marine Biologist • Coral Resilience', style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 11)), + ], + ), + ), + IconButton( + icon: const Icon(CupertinoIcons.play_arrow_solid, color: Color(0xFF00F5D4), size: 24), + onPressed: () => _speakText(curLine['text']!), + ), + ], + ), + ), + const SizedBox(height: 16), + + // Line Transcript + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF0F172A), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: Colors.white12), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(curLine['speaker']!, style: const TextStyle(color: Color(0xFF00F5D4), fontWeight: FontWeight.w800, fontSize: 12.5)), + Text('Line ${_activeDialogueLine + 1} of ${_dialogueLines.length}', style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11)), + ], + ), + const SizedBox(height: 10), + Text( + curLine['text']!, + style: const TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w700, height: 1.5), + ), + const SizedBox(height: 8), + Text( + '💡 الترجمة: ${curLine['ar']!}', + style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 12, height: 1.4), + ), + const SizedBox(height: 14), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ElevatedButton( + onPressed: _activeDialogueLine > 0 ? () => setState(() => _activeDialogueLine--) : null, + style: ElevatedButton.styleFrom(backgroundColor: Colors.white12, foregroundColor: Colors.white), + child: const Text('Previous Line'), + ), + ElevatedButton.icon( + icon: const Icon(CupertinoIcons.volume_up, size: 14), + label: const Text('Listen Line'), + style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFF60A5FA), foregroundColor: Colors.black), + onPressed: () => _speakText(curLine['text']!), + ), + ElevatedButton( + onPressed: _activeDialogueLine < _dialogueLines.length - 1 ? () => setState(() => _activeDialogueLine++) : null, + style: ElevatedButton.styleFrom(backgroundColor: const Color(0xFF00F5D4), foregroundColor: Colors.black), + child: const Text('Next Line'), + ), + ], + ), + ], + ), + ), + ], ); } } - -/// Custom Painter: Audio Waveform Simulation for Speech -class _WaveformSimulationPainter extends CustomPainter { - final bool isPlaying; - _WaveformSimulationPainter({required this.isPlaying}); - - @override - void paint(Canvas canvas, Size size) { - final paint = Paint() - ..color = isPlaying ? AppColors.saqelCyan : const Color(0xFF324864) - ..strokeWidth = 3 - ..strokeCap = StrokeCap.round; - - final barCount = 28; - final spacing = size.width / barCount; - final midY = size.height / 2; - - for (int i = 0; i < barCount; i++) { - final x = i * spacing + (spacing / 2); - final heightFactor = isPlaying - ? (0.2 + 0.8 * (0.5 + 0.5 * math.sin(i * 0.6 + 1.2))) - : 0.25; - final barHeight = (size.height * 0.7) * heightFactor; - canvas.drawLine( - Offset(x, midY - barHeight / 2), - Offset(x, midY + barHeight / 2), - paint, - ); - } - } - - @override - bool shouldRepaint(covariant _WaveformSimulationPainter oldDelegate) => - oldDelegate.isPlaying != isPlaying; -} - -/// Custom Painter: Tense Timeline Painter for English Grammar Visuals -class _GrammarTimelinePainter extends CustomPainter { - final int ruleIndex; - _GrammarTimelinePainter({required this.ruleIndex}); - - @override - void paint(Canvas canvas, Size size) { - final linePaint = Paint() - ..color = const Color(0xFF384C66) - ..strokeWidth = 3; - - final nowX = size.width * 0.75; - final pastX = size.width * 0.25; - final midY = size.height * 0.55; - - // Draw main timeline axis - canvas.drawLine(Offset(20, midY), Offset(size.width - 20, midY), linePaint); - - // Arrow at end - final arrowPaint = Paint() - ..color = const Color(0xFF384C66) - ..style = PaintingStyle.fill; - final path = Path() - ..moveTo(size.width - 15, midY) - ..lineTo(size.width - 25, midY - 6) - ..lineTo(size.width - 25, midY + 6) - ..close(); - canvas.drawPath(path, arrowPaint); - - // Mark 'NOW' point - final nowPaint = Paint()..color = AppColors.saqelCyan; - canvas.drawCircle(Offset(nowX, midY), 6, nowPaint); - - final textPainter = TextPainter(textDirection: TextDirection.ltr); - - // 'PAST' label - textPainter.text = const TextSpan(text: 'PAST', style: TextStyle(color: Color(0xFF8CA1BA), fontSize: 10, fontWeight: FontWeight.bold)); - textPainter.layout(); - textPainter.paint(canvas, Offset(25, midY - 24)); - - // 'NOW / PRESENT' label - textPainter.text = const TextSpan(text: 'NOW', style: TextStyle(color: AppColors.saqelCyan, fontSize: 10, fontWeight: FontWeight.bold)); - textPainter.layout(); - textPainter.paint(canvas, Offset(nowX - textPainter.width / 2, midY - 24)); - - if (ruleIndex == 0) { - // Present Perfect connection arc from Past to Now - final arcPaint = Paint() - ..color = AppColors.emeraldGreen - ..style = PaintingStyle.stroke - ..strokeWidth = 2.5; - - final arcPath = Path() - ..moveTo(pastX, midY) - ..quadraticBezierTo((pastX + nowX) / 2, midY - 35, nowX, midY); - canvas.drawPath(arcPath, arcPaint); - - textPainter.text = const TextSpan( - text: 'Present Perfect (Impact on Now)', - style: TextStyle(color: AppColors.emeraldGreen, fontSize: 9.5, fontWeight: FontWeight.bold), - ); - textPainter.layout(); - textPainter.paint(canvas, Offset((pastX + nowX) / 2 - textPainter.width / 2, midY - 45)); - - // Specific Past point - final pastPointPaint = Paint()..color = Colors.amber; - canvas.drawCircle(Offset(pastX, midY), 5, pastPointPaint); - textPainter.text = const TextSpan(text: 'Past Simple (in 2018)', style: TextStyle(color: Colors.amber, fontSize: 9)); - textPainter.layout(); - textPainter.paint(canvas, Offset(pastX - textPainter.width / 2, midY + 10)); - } - } - - @override - bool shouldRepaint(covariant _GrammarTimelinePainter oldDelegate) => - oldDelegate.ruleIndex != ruleIndex; -} diff --git a/apps/student_app/lib/presentation/screens/curriculum/history_interactive_timeline_view.dart b/apps/student_app/lib/presentation/screens/curriculum/history_interactive_timeline_view.dart index 9cdf66b..e066551 100644 --- a/apps/student_app/lib/presentation/screens/curriculum/history_interactive_timeline_view.dart +++ b/apps/student_app/lib/presentation/screens/curriculum/history_interactive_timeline_view.dart @@ -1,9 +1,16 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import '../../../core/theme/app_colors.dart'; /// ============================================================================== -/// SAQEL ENTERPRISE - JORDAN HISTORY INTERACTIVE TIMELINE (الخط الزمني لتاريخ الأردن) +/// SAQEL ENTERPRISE - GRADE 10 HISTORY INTERACTIVE LAB & CHRONOLOGY /// ============================================================================== +/// مختبر التاريخ التفاعلي الموسع والخط الزمني الشامل للصف العاشر: +/// 1. الوحدة الأولى: الإمبراطورية الفارسية والدولة الساسانية (ملوك، معارك، عواصم، خط زمني حركي). +/// 2. الوحدة الثانية: الدولة العثمانية (سلاطين بني عثمان، فتح القسطنطينية، ومحطات وقلاع الأردن). +/// 3. الوحدة الثالثة: ثورات غيّرت العالم الحديث (الثورة الصناعية، الأمريكية، الفرنسية، البلشفية). +/// 4. الوحدة الرابعة: شخصيات من التاريخ (محمد علي باشا، إبراهيم باشا، رفاعة الطهطاوي، غاندي، زيغريد هونكه). +/// 5. معمل بطاقات الحفظ السريع والاستدعاء النشط (Active Recall Flashcards) لتثبيت الفهم الوزاري. class HistoryInteractiveTimelineView extends StatefulWidget { const HistoryInteractiveTimelineView({super.key}); @@ -14,387 +21,1242 @@ class HistoryInteractiveTimelineView extends StatefulWidget { class _HistoryInteractiveTimelineViewState extends State { + int _activeUnit = 0; // 0: Sasanian, 1: Ottoman, 2: Revolutions, 3: Personalities int _selectedMilestoneIndex = 0; + bool _isFlashcardMode = false; + int _flashcardIndex = 0; + bool _isCardFlipped = false; + final Set _masteredCardIndices = {}; - final List> _milestones = [ + // --------------------------------------------------------------------------- + // Unit 1: Sasanian & Persian Empire (226 AD - 651 AD) + // --------------------------------------------------------------------------- + static const List> _sasanianKings = [ { - 'year': '1921', - 'exactDate': '2 آذار 1921', - 'title': 'تأسيس إمارة شرق الأردن', - 'era': 'عهد الملك المؤسس عبد الله الأول', - 'location': 'معان ➔ عمان', - 'summary': - 'وصول الأمير عبد الله الأول بن الحسين إلى معان في تشرين الثاني 1920 ثم إلى عمان في 2 آذار 1921، وتشكيل أول حكومة مركزية برئاسة رشيد طليع في 11 نيسان 1921.', - 'tawjihiFocus': - 'سؤال وزاري مكرر: من هو رئيس أول حكومة أردنية؟ (رشيد طليع)، وتاريخ وصول الأمير إلى عمان (2 آذار 1921).', - 'significance': 'نشأة الدولة الأردنية الحديثة بعد الثورة العربية الكبرى.', - 'accentColor': Color(0xFF8B5CF6), + 'name': 'أردشير الأول (Ardashir I)', + 'years': '226م – 241م', + 'title': 'مؤسس الدولة الساسانية ولقب شاهنشاه', + 'capital': 'إصطخر ➔ طيسفون (المدائن)', + 'battles': 'معركة هرمزجان (224م) وإسقاط الإمبراطورية الفرثية الأشكانية', + 'achievements': + 'تأسيس السلالة الساسانية، وإعادة إحياء الزرادشتية كدين رسمي للدولة، ووضع التنظيم الإداري الفيدرالي المركزي وتتويجه بلقب شاهنشاه (ملك الملوك).', + 'terms': 'شاهنشاه، معركة هرمزجان، الزرادشتية، إيوان المدائن', + 'accentColor': Color(0xFFD4AF37), + 'significance': 'انتقال بلاد فارس من التفكك الفرثي إلى إمبراطورية مركزية عظمى.', }, { - 'year': '1928', - 'exactDate': '16 نيسان 1928', - 'title': 'صدور القانون الأساسي والمؤتمر الوطني الأول', - 'era': 'عهد الملك المؤسس عبد الله الأول', - 'location': 'عمان - مقهى حمدان', - 'summary': - 'توقيع المعاهدة الأردنية البريطانية، وصدور أول دستور للإمارة (القانون الأساسي)، وانعقاد المؤتمر الوطني الأول برئاسة حسين الطراونة ووضع الميثاق القومي.', - 'tawjihiFocus': - 'سؤال وزاري مكرر: من ترأس المؤتمر الوطني الأول عام 1928؟ (حسين الطراونة)، وما اسم أول دستور؟ (القانون الأساسي 1928).', - 'significance': 'أول وثيقة دستورية وطنية تنظم الحياة السياسية والنيابية.', - 'accentColor': Color(0xFF38BDF8), + 'name': 'سابور الأول (Shapur I)', + 'years': '241م – 272م', + 'title': 'قاهر الإمبراطورية الرومانية', + 'capital': 'طيسفون (المدائن) وبيشابور', + 'battles': 'معركة الرها (260م) وأسر الإمبراطور الروماني فاليريان', + 'achievements': + 'تثبيت الحدود الغربية، وتخليد انتصاراته بنقوش نقش رستم الصخرية، وجلب المهندسين والأطباء من بلاد الروم لبناء الجسور وتطوير منظومات السدود والري.', + 'terms': 'معركة الرها، الإمبراطور فاليريان، نقش رستم، مدينة بيشابور', + 'accentColor': Color(0xFF00F5D4), + 'significance': 'أول ملك شرقي يأسر إمبراطوراً رومانياً حياً في التاريخ.', }, { - 'year': '1946', - 'exactDate': '25 أيار 1946', - 'title': 'استقلال المملكة الأردنية الهاشمية', - 'era': 'عهد الملك المؤسس عبد الله الأول', - 'location': 'عمان - قصر رغدان العامر', - 'summary': - 'إعلان المجلس التشريعي الأردني الخامس استقلال البلاد التام ومبايعة الملك عبد الله الأول ملكاً دستورياً، وتحويل اسم الدولة إلى المملكة الأردنية الهاشمية.', - 'tawjihiFocus': - 'سؤال وزاري مكرر: ما هو رقم المجلس التشريعي الذي أعلن الاستقلال؟ (المجلس الخامس)، واليوم الوطني (25 أيار 1946).', - 'significance': 'إنهاء الانتداب البريطاني واعتراف دولي كامل بسيادة المملكة.', - 'accentColor': Color(0xFF10B981), + 'name': 'كسرى الأول أنوشروان (Khosrow I)', + 'years': '531م – 579م', + 'title': 'الملك العادل وباني إيوان كسرى', + 'capital': 'طيسفون (المدائن)', + 'battles': 'الحروب ضد الإمبراطورية البيزنطية ومعاهدة السلام الدائم 562م', + 'achievements': + 'عصر الازدهار الذهبي الساساني؛ بناء طاق كسرى (إيوان المدائن)، إصلاح نظام الضرائب (الخراج)، ترجمة كتاب كليلة ودمنة من السنسكريتية إلى الفهلوية، وتأسيس أكاديمية جنديسابور للطب والفلسفة.', + 'terms': 'طاق كسرى، أكاديمية جنديسابور، كليلة ودمنة، نظام الخراج', + 'accentColor': Color(0xFF9D4EDD), + 'significance': 'قمة المجد العمراني والثقافي والإداري للدولة الساسانية.', }, { - 'year': '1952', - 'exactDate': '8 كانون الثاني 1952', - 'title': 'صدور الدستور الأردني المتطور وديوان المحاسبة', - 'era': 'عهد الملك طلال بن عبد الله', - 'location': 'عمان', - 'summary': - 'إصدار دستور 1952 العصري الذي نص على أن الأمة مصدر السلطات والوزارة مسؤولة أمام مجلس النواب، وتأسيس ديوان المحاسبة وإقرار مجانية وإلزامية التعليم.', - 'tawjihiFocus': - 'سؤال وزاري مكرر: في عهد من صدر دستور 1952 وتأسس ديوان المحاسبة؟ (الملك طلال بن عبد الله).', - 'significance': 'ترسيخ المبادئ الديمقراطية ومسؤولية السلطة التنفيذية أمام البرلمان.', - 'accentColor': Color(0xFFF59E0B), - }, - { - 'year': '1956', - 'exactDate': '1 آذار 1956', - 'title': 'تعريب قيادة الجيش العربي وطرد كلوب باشا', - 'era': 'عهد الملك الباني الحسين بن طلال', - 'location': 'القيادة العامة - عمان', - 'summary': - 'القرار الوطني الشجاع لجلالة الملك الحسين بن طلال بإنهاء خدمة الفريق كلوب والضباط الإنجليز، وتعيين اللواء راضي عناب رئيساً للأركان، لتصبح القيادة أردنية خالصة.', - 'tawjihiFocus': - 'سؤال وزاري مكرر: من هو أول رئيس أركان أردني بعد التعريب؟ (راضي عناب)، والتاريخ (1 آذار 1956).', - 'significance': 'استرداد السيادة العسكرية الوطنية الكاملة للجيش العربي.', - 'accentColor': Color(0xFFEF4444), - }, - { - 'year': '1968', - 'exactDate': '21 آذار 1968', - 'title': 'معركة الكرامة الخالدة وتحطيم أسطورة العدو', - 'era': 'عهد الملك الباني الحسين بن طلال', - 'location': 'غور الأردن - بلدة الكرامة', - 'summary': - 'تصدي القوات المسلحة الأردنية - الجيش العربي الباسل للعدوان الإسرائيلي وإلحاق أول هزيمة عسكرية مدوية بجيش الاحتلال، وإجباره على طلب وقف إطلاق النار لأول مرة في تاريخه.', - 'tawjihiFocus': - 'سؤال وزاري مكرر: ما هي أول معركة عربية حطمت أسطورة الجيش الإسرائيلي؟ (معركة الكرامة 21 آذار 1968).', - 'significance': 'استعادة الكرامة العسكرية العربية وإثبات صلابة الجندي الأردني.', - 'accentColor': Color(0xFF10B981), - }, - { - 'year': '1999', - 'exactDate': '7 شباط 1999 - الحاضر', - 'title': 'تولي الملك عبد الله الثاني ومسارات التحديث', - 'era': 'عهد الملك المعزز عبد الله الثاني ابن الحسين', - 'location': 'المملكة الأردنية الهاشمية', - 'summary': - 'انطلاق عهد التعزيز والتحول الرقمي، وإطلاق الأوراق النقاشية الملكية السبع لتطوير الديمقراطية، ومنظومة التحديث السياسي والاقتصادي والإداري لمئوية الدولة الثانية.', - 'tawjihiFocus': - 'سؤال وزاري مكرر: موضوع الورقة النقاشية الأولى (بناء الديمقراطية) والسابعة (التعليم وتطوير الموارد البشرية).', - 'significance': 'تحديث منظومة الدولة الأردنية ومواكبة الثورة الصناعية والذكاء الاصطناعي.', - 'accentColor': Color(0xFFFBBF24), + 'name': 'يزدجرد الثالث (Yazdegerd III)', + 'years': '632م – 651م', + 'title': 'آخر ملوك الدولة الساسانية', + 'capital': 'طيسفون (المدائن) ثم الهرب شرقاً إلى مرو', + 'battles': 'معركة القادسية (636م) ومعركة نهاوند "فتح الفتوح" (642م)', + 'achievements': + 'تولى العرش صغيراً في ظل انهيار المؤسسة العسكرية الساسانية بعد الحروب المنهكة مع البيزنطيين، وانتهت دولته بدخول بلاد فارس تحت راية الفتح الإسلامي بعد موقعة نهاوند الحاسمة.', + 'terms': 'معركة القادسية، معركة نهاوند (فتح الفتوح)، طيسفون، مرو', + 'accentColor': Color(0xFFFF5252), + 'significance': 'سقوط طيسفون ونهاية عهد الإمبراطوريات الفارسية القديمة عام 651م.', }, ]; + // --------------------------------------------------------------------------- + // Unit 2: Ottoman Empire (1299 AD - 1923 AD) + // --------------------------------------------------------------------------- + static const List> _ottomanSultans = [ + { + 'name': 'عثمان بن أرطغرل الأول', + 'years': '1299م – 1324م', + 'title': 'مؤسس الدولة وسلطان إمارة سوغوت', + 'capital': 'سوغوت (شمال غرب الأناضول)', + 'battles': 'معركة بافيوس (1302م) ضد البيزنطيين', + 'achievements': + 'إعلان استقلال الإمارة العثمانية عن دولة سلاجقة الروم عام 1299م، وتوحيد القبائل التركمانية وإرساء مبادئ العدالة والفتوحات الإسلامية.', + 'terms': 'إمارة سوغوت، معركة بافيوس، سلاجقة الروم، الغزاة المجاهدون', + 'accentColor': Color(0xFF10B981), + 'significance': 'ولادة الدولة العثمانية التي استمرت أكثر من 600 عام.', + }, + { + 'name': 'محمد الثاني (الفاتح)', + 'years': '1451م – 1481م', + 'title': 'فاتح القسطنطينية وباني الإمبراطورية', + 'capital': 'أدرنة ➔ إسطنبول (القسطنطينية)', + 'battles': 'فتح القسطنطينية (29 أيار 1453م)', + 'achievements': + 'استخدام المدافع الضخمة (مدفع أوربان) ونقل السفن العثمانية براً عبر تلال غلطة إلى القرن الذهبي، وتحويل المدينة لعاصمة أبدية وضمان حرية الكنائس وإصدار القوانين.', + 'terms': 'فتح القسطنطينية، مدفع أوربان، القرن الذهبي، كنيسة آيا صوفيا', + 'accentColor': Color(0xFFF59E0B), + 'significance': 'إنهاء الإمبراطورية البيزنطية وبدء عصر التاريخ الحديث عالمياً.', + }, + { + 'name': 'سليم الأول (ياووز)', + 'years': '1512م – 1520م', + 'title': 'سلطان الفتوحات الشرقية وخادم الحرمين', + 'capital': 'إسطنبول', + 'battles': 'معركة جالديران (1514م) ومعركة مرج دابق (1516م)', + 'achievements': + 'ضم بلاد الشام ومصر والحجاز، ونقل مقر الخلافة الإسلامية إلى إسطنبول، وتأمين طريق الحج الشامي وبناء القلاع لحماية الحجاج المارين عبر الأردن.', + 'terms': 'معركة مرج دابق، معركة الريدانية، درب الحج الشامي، لواء عجلون', + 'accentColor': Color(0xFF38BDF8), + 'significance': 'تحول الدولة العثمانية إلى زعيمة العالم الإسلامي.', + }, + { + 'name': 'سليمان القانوني (المحتشم)', + 'years': '1520م – 1566م', + 'title': 'العصر الذهبي والمشرّع الكبير', + 'capital': 'إسطنبول', + 'battles': 'معركة موهاكس (1526م) وحصار فيينا الأول (1529م)', + 'achievements': + 'تقنين "قانون نامة" العثماني، وصول الأساطيل العثمانية للمحيط الهندي والبحر الأحمر، وتشييد المساجد والمعالم العمرانية الكبرى بواسطة المعمار سنان، وترميم أسوار القدس.', + 'terms': 'قانون نامة، معركة موهاكس، المعمار سنان، حصار فيينا', + 'accentColor': Color(0xFFEC4899), + 'significance': 'أوسع امتداد جغرافي واقتصادي وعسكري في التاريخ العثماني.', + }, + { + 'name': 'الأردن في العهد العثماني', + 'years': '1516م – 1918م', + 'title': 'محطات الحج الشامي والقلاع الأردنية', + 'capital': 'لواء عجلون وناحية الكرك والشوبك ومعان', + 'battles': 'تأمين درب الحج الشامي وحملات الحامية العثمانية', + 'achievements': + 'ترميم وتوسيع قلعة معان وقلعة القطرانة وقلعة الشوبك والزرقاء لتأمين قوافل الحجاج الشاميين ومصادر المياه، ومد الخط الحديدي الحجازي في مطلع القرن العشرين.', + 'terms': 'درب الحج الشامي، قلعة القطرانة، قلعة معان، الخط الحديدي الحجازي', + 'accentColor': Color(0xFF34D399), + 'significance': 'الموقع الجغرافي الإستراتيجي للأردن بوصفه قلب درب الحج الشامي.', + }, + ]; + + // --------------------------------------------------------------------------- + // Unit 3: World Revolutions (1769 AD - 1917 AD) + // --------------------------------------------------------------------------- + static const List> _worldRevolutions = [ + { + 'name': 'الثورة الصناعية (Industrial Revolution)', + 'years': '1769م – القرن التاسع عشر', + 'title': 'انطلاق عصر الآلة والبخار في بريطانيا', + 'capital': 'بريطانيا (إنجلترا، ويلز، إسكتلندا) ➔ أوروبا والعالم', + 'battles': 'التحول الشامل من العمل اليدوي في الورش البسيطة إلى العمل الآلي في المصانع الكبرى', + 'achievements': + '• اختراع جيمس واط للمحرك البخاري عام 1769م الذي قاد قاطرة التصنيع.\n' + '• ابتكار قطار جورج ستيفنسون وسفينة روبرت فلتون البخارية وشبكات السكك الحديدية.\n' + '• استغلال مناجم الفحم الحجري والحديد والموقع الجغرافي البحري الحامي للجزيرة البريطانية.\n' + '• قيام اتحاد الزولفرين (Zollverein) الجمركي بألمانيا 1834م لإلغاء الرسوم الجمركية والتمهيد للثورة الصناعية والوحدة السياسية.', + 'terms': 'الثورة الصناعية، المحرك البخاري، اتحاد الزولفرين، جيمس واط، النظام الإقطاعي', + 'accentColor': Color(0xFF00F5D4), + 'significance': 'أكبر تحول اقتصادي وتقني وتكنولوجي غيّر خريطة الإنتاج في التاريخ البشري.', + }, + { + 'name': 'الثورة الأمريكية (American Revolution)', + 'years': '1775م – 1783م', + 'title': 'حرب التحرير والاستقلال ضد الاستعمار البريطاني', + 'capital': 'المستعمرات الثلاث عشرة (بوسطن، فيلادلفيا، نيويورك)', + 'battles': 'مذبحة بوسطن (1770م)، حفلة شاي بوسطن (1773م)، معركة ساراتوغا الحاسمة (1777م)', + 'achievements': + '• رفع الشعار الدستوري التاريخي: «لا ضرائب بلا تمثيل» رداً على ضريبة الشاي والطوابع.\n' + '• انعقاد المؤتمرين القاريين الأول (1774م) والثاني (1775م) وتوحيد الجيش القاري بقيادة جورج واشنطن.\n' + '• صياغة توماس جفرسون لوثيقة إعلان الاستقلال التاريخية في (4 تموز 1776م).\n' + '• انتصار الثوار في معركة ساراتوغا (1777م) واستسلام الجيش البريطاني مما دفع فرنسا وإسبانيا وهولندا لدعم الثورة رسمياً.\n' + '• توقيع معاهدة صلح باريس (1783م) واعتراف بريطانيا بسيادة واستقلال الولايات المتحدة.', + 'terms': 'إعلان الاستقلال (4 تموز 1776)، ساراتوغا، لا ضرائب بلا تمثيل، جورج واشنطن، صلح باريس 1783', + 'accentColor': Color(0xFF38BDF8), + 'significance': 'أول ثورة تحررية دستورية في العصر الحديث تُسقط الهيمنة الإمبراطورية وتؤسس دولة اتحادية مستقلة.', + }, + { + 'name': 'الثورة الفرنسية (French Revolution)', + 'years': '1789م – 1799م', + 'title': 'سقوط الباستيل وإعلان حقوق الإنسان والمواطن', + 'capital': 'باريس (فرنسا)', + 'battles': 'اقتحام سجن الباستيل (14 تموز 1789م) ومواجهة الجيوش الملكية الأوروبية', + 'achievements': + '• تشكيل الجمعية الوطنية وإلغاء النظام الإقطاعي والامتيازات الطبقية للنبلاء والإكليروس.\n' + '• إصدار وثيقة إعلان حقوق الإنسان والمواطن (26 آب 1789م) التي أقرت الحرية والإخاء والمساواة.\n' + '• إسقاط الملكية المطلقة لأسرة آل بوربون ومحاكمة الملك لويس السادس عشر وإعلان الجمهورية.\n' + '• التأثر بأفكار فلاسفة التنوير: كتاب «روح القوانين» لمونتيسكيو (الفصل بين السلطات)، وفولتير، وكتاب «العقد الاجتماعي» لجان جاك روسو.', + 'terms': 'سجن الباستيل، إعلان حقوق الإنسان، روح القوانين، العقد الاجتماعي، الجمعية الوطنية', + 'accentColor': Color(0xFFFF5252), + 'significance': 'تدشين عصر الدساتير والمواطنة الحديثة وإلهام الحركات التحررية والقومية في العالم.', + }, + { + 'name': 'الثورة الروسية البلشفية (Russian Revolution)', + 'years': '1917م – 1922م', + 'title': 'أول ثورة شيوعية اشتراكية في القرن العشرين', + 'capital': 'لينينغراد (سانت بطرسبرغ) ➔ موسكو', + 'battles': 'إسقاط القيصر في ثورة شباط 1917م، وثورة تشرين الأول البلشفية المسلحة', + 'achievements': + '• قيادة فلاديمير لينين للحزب البلشفي (الأكثرية) وإسقاط حكم سلالة آل رومانوف بعد 300 عام.\n' + '• تأسيس "مجالس السوفيات" المنتخبة للعمال والجنود والفلاحين لإدارة البلاد ومؤسسات الإنتاج.\n' + '• توقيع معاهدة بريست ليتوفسك والخروج الفوري من أتون الحرب العالمية الأولى.\n' + '• تأميم الأراضي والمصانع وإلغاء الملكية الفردية الكبرى، وتأسيس الاتحاد السوفيتي كقوة عظمى عام 1922م.', + 'terms': 'البلشفية، المانشفيك، فلاديمير لينين، مجالس السوفيات، الاتحاد السوفيتي، مجلس الدوما', + 'accentColor': Color(0xFFEC4899), + 'significance': 'ظهور المعسكر الاشتراكي والاتحاد السوفيتي وصياغة ثنائية القطبية الدولية في القرن العشرين.', + }, + ]; + + // --------------------------------------------------------------------------- + // Unit 4: Historical Figures (1769 AD - 1999 AD) + // --------------------------------------------------------------------------- + static const List> _historicalFigures = [ + { + 'name': 'محمد علي باشا (1769م - 1849م)', + 'years': '1805م – 1848م', + 'title': 'والي مصر وباني نهضتها وصناعتها الحديثة', + 'capital': 'القاهرة (القلعة) والإسكندرية', + 'battles': 'القضاء على المماليك (مذبحة القلعة 1811م)، حملات بلاد الشام ومعركة كوتاهيه (1831م)', + 'achievements': + '• تطبيق سياسة الاكتفاء الذاتي والاحتكار الاقتصادي لزيادة الصادرات وتقليص الواردات.\n' + '• بناء القناطر الخيرية وترعة المحمودية وتوسيع زراعة القطن طويل التيلة وقصب السكر.\n' + '• تأسيس المطبعة الأميرية في بولاق وإصدار جريدة "الوقائع المصرية" (1828م).\n' + '• تأسيس مدرسة "دار الألسن" للغات والترجمة (1835م) بإشراف الشيخ رفاعة الطهطاوي.\n' + '• إنشاء التنظيم الإداري وتأسيس "مجلس المشورة" و"الديوان العالي" و"المجلس الخصوصي".\n' + '• بناء جيش نظامي حديث وأسطول حربي وترسانة بحرية عملاقة في بولاق والإسكندرية.\n' + '• مواجهة التحالف الأوروبي في معاهدة لندن (1840م) ونيل فرمان الوراثة لحكم مصر عام 1841م.', + 'terms': 'الفرمان، مجلس المشورة، المجلس الخصوصي، دار الألسن، الوقائع المصرية، معاهدة لندن 1840', + 'accentColor': Color(0xFFF59E0B), + 'significance': 'أول وأكبر تجربة نهضوية وتحديثية شاملة في المشرق العربي في القرن التاسع عشر.', + }, + { + 'name': 'القائد إبراهيم باشا (1789م - 1848م)', + 'years': '1789م – 1848م', + 'title': 'القائد العسكري العبقري ورئيس المجلس الخصوصي', + 'capital': 'قيادة الجيوش الميدانية (مصر، الشام، الأناضول)', + 'battles': 'حملات الجزيرة واليونان، فتح عكا وحمص ودمشق وحلب، ومعركة قونية وكوتاهيه (1831م)', + 'achievements': + '• إظهار عبقرية عسكرية استثنائية في فتح عكا الحصينة عام 1832م بعد حصار عجز عنه نابليون.\n' + '• بسط الأمن وتطبيق النظام في بلاد الشام وإلغاء الرسوم والمكوس الجمركية التعسفية.\n' + '• إقرار مبدأ المساواة الكاملة في الحقوق والواجبات بين جميع رعايا الشام دون تفرقة دينية.\n' + '• سحق الجيوش العثمانية في معركة قونية والتقدم حتى كوتاهيه على بعد 400 كم فقط من إسطنبول.\n' + '• ترؤس "المجلس الخصوصي" لإدارة شؤون الدولة المصرية في أواخر حياة والده عام 1848م.', + 'terms': 'فتح عكا، معركة كوتاهيه، معركة قونية، المجلس الخصوصي، المساواة المدنية', + 'accentColor': Color(0xFFFFD60A), + 'significance': 'تجسيد القوة العسكرية العربية الحديثة وبناء إدارة إصلاحية رائدة في الشام ومصر.', + }, + { + 'name': 'رفاعة رافع الطهطاوي (1801م - 1873م)', + 'years': '1801م – 1873م', + 'title': 'رائد التنوير والترجمة والنهضة الفكرية الحديثة', + 'capital': 'طهطا ➔ الأزهر الشريف ➔ باريس ➔ القاهرة', + 'battles': 'مواجهة الجمود الفكري وقيادة حركة نقل العلوم الغربية وصناعة المصطلح العربي الحديث', + 'achievements': + '• ابتعاثه إماماً للبعثة العلمية المصرية الأولى إلى باريس (1826 - 1831م) وإتقانه الفرنسية وتدوين مشاهداته.\n' + '• تأليف كتابه الخالد «تخليص الإبريز في تلخيص باريز» واصفاً نظم التعليم والحكم والعلوم في أوروبا.\n' + '• تأسيس وإدارة مدرسة «دار الألسن» عام 1835م والإشراف على ترجمة آلاف الكتب في الطب والهندسة والتاريخ.\n' + '• تولي رئاسة تحرير جريدة «الوقائع المصرية» وتحويلها إلى منبر ثقافي وطني.\n' + '• الدعوة المبكرة لتعليم المرأة وحقوقها في كتابه «المرشد الأمين للبنات والبنين» وتأصيل مفهوم حب الوطن.', + 'terms': 'دار الألسن (1835م)، تخليص الإبريز، الوقائع المصرية، المرشد الأمين، البعثات العلمية', + 'accentColor': Color(0xFF8B5CF6), + 'significance': 'الجسر الفكري والتنويري الرابط بين الأصالة والتراث والعلوم الأوروبية الحديثة.', + }, + { + 'name': 'المهاتما غاندي (1869م - 1948م)', + 'years': '1869م – 1948م', + 'title': 'أبو الأمة الهندية وزعيم المقاومة السلمية واللاعنف', + 'capital': 'بوربندر (الهند) ➔ جنوب إفريقيا ➔ نيودلهي', + 'battles': 'مسيرة الملح الكبرى (1930م)، حملة "اخرجوا من الهند"، ومقاومة الاستعمار دون رصاصة واحدة', + 'achievements': + '• ابتكار وتطبيق فلسفة «الساتياغراها» (Satyagraha) القائمة على قوة الحقيقة والتمسك بالحق.\n' + '• ترسيخ مبدأ «الأهيمسا» (Ahimsa) وهو الامتناع المطلق عن ممارسة العنف أو إيذاء أي كائن حي.\n' + '• تنظيم حركة العصيان المدني السلمي ومقاطعة المؤسسات والبضائع والمنسوجات البريطانية.\n' + '• قيادة مسيرة الملح التاريخية عام 1930م وقطع 390 كم مشياً لكسر احتكار الاستعمار لملح الطعام.\n' + '• توحيد الهنود بجميع طوائفهم ومناهضة التمييز الطبقي ضد فئة المنبوذين، وتحقيق استقلال الهند عام 1947م.', + 'terms': 'الساتياغراها (قوة الحق)، الأهيمسا (اللاعنف)، مسيرة الملح، مغزل الشارخا، استقلال الهند 1947', + 'accentColor': Color(0xFF10B981), + 'significance': 'أعظم نموذج إنساني أثبت أن القوة الأخلاقية والإرادة السلمية تنتصر على أعتى الإمبراطوريات العسكرية.', + }, + { + 'name': 'زيغريد هونكه (Sigrid Hunke)', + 'years': '1913م – 1999م', + 'title': 'المستشرقة الألمانية المنصفة للحضارة العربية الإسلامية', + 'capital': 'كيل وبون (ألمانيا)', + 'battles': 'مقارعة المركزية الأوروبية المتعصبة وتفنيد إنكار فضل الحضارة الإسلامية على نهضة الغرب', + 'achievements': + '• دراسة الفلسفة ومقارنة الأديان والتعمق التاريخي والميداني في المخطوطات والعلوم الإسلامية.\n' + '• تأليف كتابها العالمي الأشهر «شمس العرب تسطع على الغرب» (Allahs Sonne über dem Abendland) عام 1960م.\n' + '• توثيق دور العلماء المسلمين في ابتكار المنهج التجريبي ووضع أسس الطب (ابن سينا، الزهراوي)، والبصريات (ابن الهيثم)، والرياضيات والجبر (الخوارزمي)، والفلك (البيروني).\n' + '• تأليف كتاب «الله ليس كذلك» عام 1970م للدفاع عن الصورة الحقيقية المتسامحة للإسلام في الغرب.\n' + '• التأكيد على أن أوروبا لم تكن لتعرف نهضتها الحديثة لولا التتلمذ المباشر على العلوم العربية.', + 'terms': 'شمس العرب تسطع على الغرب، الله ليس كذلك، الاستشراق المنصف، المركزية الأوروبية', + 'accentColor': Color(0xFFD946EF), + 'significance': 'شهادة أكاديمية غربية منصفة وموثقة تثبت الريادة العلمية الإسلامية الخالدة للبشرية.', + }, + ]; + + // --------------------------------------------------------------------------- + // Active Recall Flashcards for Quick Memorization (بطاقات الحفظ والاسترجاع) + // --------------------------------------------------------------------------- + static const List> _quickFlashcards = [ + { + 'unitTitle': 'الوحدة الأولى: الإمبراطورية الفارسية', + 'question': 'ما هي المعركة الفاصلة التي أنهت الدولة الساسانية عام 642م وماذا سماها المسلمون؟', + 'answer': 'معركة نهاوند؛ وسماها المسلمون «فتح الفتوح» لأنها أسقطت عرش يزدجرد الثالث وفتحت سائر بلاد فارس.', + 'keyFact': 'نهاوند (642م) = فتح الفتوح ونهاية الساسانيين.', + 'color': Color(0xFFFF5252), + }, + { + 'unitTitle': 'الوحدة الأولى: الإمبراطورية الفارسية', + 'question': 'ما هو أهم إنجاز للملك كسرى الأول أنوشروان (531 - 579م) في الإدارة والعمارة؟', + 'answer': 'بناء طاق كسرى (إيوان المدائن) في طيسفون، وإصلاح نظام ضريبة الخراج، وتأسيس أكاديمية جنديسابور الطبية.', + 'keyFact': 'كسرى الأول = العصر الذهبي وباني إيوان كسرى.', + 'color': Color(0xFF9D4EDD), + }, + { + 'unitTitle': 'الوحدة الثانية: الدولة العثمانية', + 'question': 'في أي عام فُتحت القسطنطينية ومن هو السلطان الفاتح وما السلاح الحاسم المستخدم؟', + 'answer': 'عام 1453م بقيادة السلطان محمد الثاني (الفاتح)، باستخدام مدفع أوربان العملاق ونقل السفن براً للقرن الذهبي.', + 'keyFact': '1453م = فتح القسطنطينية وبدء التاريخ الحديث.', + 'color': Color(0xFFF59E0B), + }, + { + 'unitTitle': 'الوحدة الثانية: الدولة العثمانية في الأردن', + 'question': 'ما أهمية موقع الأردن وقلاعه (القطرانة، معان، الشوبك) في العهد العثماني؟', + 'answer': 'تأمين وحماية قوافل درب الحج الشامي ومصادر المياه والمؤن، وصولاً إلى تدشين سكة حديد الحجاز مطلع القرن العشرين.', + 'keyFact': 'الأردن = القلب الإستراتيجي لدرب الحج الشامي.', + 'color': Color(0xFF34D399), + }, + { + 'unitTitle': 'الوحدة الثالثة: الثورة الصناعية', + 'question': 'ما هو الاختراع الذي فجّر الثورة الصناعية في إنجلترا عام 1769م ومن مخترعه؟', + 'answer': 'المحرك البخاري (Steam Engine) الذي ابتكره جيمس واط (James Watt)، ونقل العمل من اليدوي إلى الميكانيكي.', + 'keyFact': '1769م: جيمس واط يبتكر المحرك البخاري.', + 'color': Color(0xFF00F5D4), + }, + { + 'unitTitle': 'الوحدة الثالثة: الثورة الصناعية', + 'question': 'ما هو اتحاد "الزولفرين" (Zollverein) عام 1834م وما أثره في ألمانيا؟', + 'answer': 'اتحاد جمركي ألغى الرسوم والمكوس الجمركية بين 30 دويلة ألمانية، مما وحد الاقتصاد ومهد للثورة الصناعية والوحدة السياسية.', + 'keyFact': 'الزولفرين 1834م = الاتحاد الجمركي الممهد للوحدة الألمانية.', + 'color': Color(0xFF60A5FA), + }, + { + 'unitTitle': 'الوحدة الثالثة: الثورة الأمريكية', + 'question': 'ما الشعار الدستوري الشهير لأهالي المستعمرات الأمريكية وما المعركة الفاصلة عام 1777م؟', + 'answer': 'الشعار: «لا ضرائب بلا تمثيل». والمعركة الفاصلة: معركة ساراتوغا (1777م) التي أجبرت الإنجليز على الاستسلام وجلبت دعم فرنسا.', + 'keyFact': 'ساراتوغا 1777م = المنعطف الحاسم لاستقلال أمريكا.', + 'color': Color(0xFF38BDF8), + }, + { + 'unitTitle': 'الوحدة الثالثة: الثورة الفرنسية', + 'question': 'ما الحدث التاريخي ليوم 14 تموز 1789م في باريس، وما أهم وثيقة صدرت عن الثورة؟', + 'answer': 'اقتحام وإسقاط سجن الباستيل رمز الطغيان الملكي، وإصدار وثيقة إعلان حقوق الإنسان والمواطن ومبادئ (حرية، إخاء، مساواة).', + 'keyFact': '14 تموز 1789م = سقوط الباستيل وإعلان حقوق الإنسان.', + 'color': Color(0xFFFF375F), + }, + { + 'unitTitle': 'الوحدة الثالثة: الثورة الروسية', + 'question': 'ما معنى كلمة "البلشفية" ومن قاد ثورة تشرين الأول عام 1917م وماذا أنشأ؟', + 'answer': 'البلشفية تعني «الأكثرية» في حزب العمال، قادها فلاديمير لينين وأسس مجالس السوفيات وأنهى حكم القيصر وأقام الاتحاد السوفيتي.', + 'keyFact': 'البلشفيك (الأكثرية) بقيادة لينين أسسوا الاتحاد السوفيتي 1917م.', + 'color': Color(0xFFEC4899), + }, + { + 'unitTitle': 'الوحدة الرابعة: محمد علي باشا', + 'question': 'ما ركائز السياسة الاقتصادية لمحمد علي باشا في الزراعة والصناعة والتعليم؟', + 'answer': 'سياسة الاكتفاء الذاتي والاحتكار، بناء القناطر الخيرية وزراعة القطن طويل التيلة، تأسيس المطبعة الأميرية ومدرسة دار الألسن عام 1835م.', + 'keyFact': 'الاكتفاء الذاتي + دار الألسن 1835م + القناطر الخيرية.', + 'color': Color(0xFFF59E0B), + }, + { + 'unitTitle': 'الوحدة الرابعة: رفاعة الطهطاوي', + 'question': 'ما اسم الكتاب الخالد لرفاعة الطهطاوي عن باريس وما المؤسسة التعليمية التي أسسها 1835م؟', + 'answer': 'كتاب «تخليص الإبريز في تلخيص باريز»، وأسس وأدار «مدرسة دار الألسن» لتعليم اللغات والترجمة الحديثة للعربية.', + 'keyFact': 'الطهطاوي = تخليص الإبريز + مؤسس مدرسة دار الألسن 1835م.', + 'color': Color(0xFF8B5CF6), + }, + { + 'unitTitle': 'الوحدة الرابعة: المهاتما غاندي وزيغريد هونكه', + 'question': 'ما معنى "الساتياغراها" و"الأهيمسا" عند غاندي، وما اسم كتاب زيغريد هونكه الشهير؟', + 'answer': 'الساتياغراها: قوة التمسك بالحقيقة. الأهيمسا: اللاعنف التام وعدم الإيذاء. كتاب زيغريد هونكه: «شمس العرب تسطع على الغرب».', + 'keyFact': 'الساتياغراها (قوة الحق) + كتاب شمس العرب تسطع على الغرب.', + 'color': Color(0xFF10B981), + }, + ]; + + List> get _currentMilestones { + switch (_activeUnit) { + case 0: + return _sasanianKings; + case 1: + return _ottomanSultans; + case 2: + return _worldRevolutions; + case 3: + default: + return _historicalFigures; + } + } + @override Widget build(BuildContext context) { - final cur = _milestones[_selectedMilestoneIndex]; + final curList = _currentMilestones; + if (_selectedMilestoneIndex >= curList.length) { + _selectedMilestoneIndex = 0; + } + final cur = curList[_selectedMilestoneIndex]; final accent = cur['accentColor'] as Color; - return Container( - color: const Color(0xFF070B12), - child: SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - // Top Timeline Navigation Bar (Scrubbable Milestones) - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: const Color(0xFF0F172A), - borderRadius: BorderRadius.circular(16), - border: Border.all(color: const Color(0xFF1E293B)), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - 'شريط المحطات التاريخية لتاريخ الأردن (التوجيهي):', - style: TextStyle( - fontSize: 13.5, - fontWeight: FontWeight.w700, - color: Colors.white), + return SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Unit Selector Segmented Control (4 Ministry Units) + Container( + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.darkCardBorder), + ), + child: Row( + children: [ + _buildUnitTab(0, 'الساسانية 🏛️'), + const SizedBox(width: 4), + _buildUnitTab(1, 'العثمانية 🕌'), + const SizedBox(width: 4), + _buildUnitTab(2, 'الثورات ⚔️🔥'), + const SizedBox(width: 4), + _buildUnitTab(3, 'الشخصيات 👑📜'), + ], + ), + ), + const SizedBox(height: 14), + + // Mode Switcher: Full Detailed Chronology vs Active Recall Flashcards + Row( + children: [ + Expanded( + child: GestureDetector( + onTap: () => setState(() => _isFlashcardMode = false), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: const EdgeInsets.symmetric(vertical: 8), + decoration: BoxDecoration( + color: !_isFlashcardMode + ? accent.withValues(alpha: 0.22) + : AppColors.darkSurface, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: !_isFlashcardMode ? accent : AppColors.darkCardBorder, ), + ), + child: Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + CupertinoIcons.list_bullet_indent, + size: 14, + color: !_isFlashcardMode ? accent : Colors.white60, + ), + const SizedBox(width: 6), + Text( + 'المحتوى التفصيلي والخط الزمني 📜', + style: TextStyle( + color: !_isFlashcardMode ? Colors.white : Colors.white60, + fontSize: 11.5, + fontWeight: !_isFlashcardMode ? FontWeight.w800 : FontWeight.w500, + ), + ), + ], + ), + ), + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: GestureDetector( + onTap: () => setState(() { + _isFlashcardMode = true; + _isCardFlipped = false; + }), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: const EdgeInsets.symmetric(vertical: 8), + decoration: BoxDecoration( + color: _isFlashcardMode + ? const Color(0xFF10B981).withValues(alpha: 0.22) + : AppColors.darkSurface, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: _isFlashcardMode ? const Color(0xFF10B981) : AppColors.darkCardBorder, + ), + ), + child: Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + CupertinoIcons.sparkles, + size: 14, + color: _isFlashcardMode ? const Color(0xFF10B981) : Colors.white60, + ), + const SizedBox(width: 6), + Text( + 'بطاقات الحفظ السريع 🧠💡', + style: TextStyle( + color: _isFlashcardMode ? Colors.white : Colors.white60, + fontSize: 11.5, + fontWeight: _isFlashcardMode ? FontWeight.w800 : FontWeight.w500, + ), + ), + ], + ), + ), + ), + ), + ), + ], + ), + const SizedBox(height: 16), + + // Render active mode + if (_isFlashcardMode) + _buildFlashcardsView() + else + _buildDetailedChronologyView(curList, cur, accent), + ], + ), + ); + } + + // --------------------------------------------------------------------------- + // Detailed Chronology View + // --------------------------------------------------------------------------- + Widget _buildDetailedChronologyView( + List> curList, Map cur, Color accent) { + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Header Banner + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + accent.withValues(alpha: 0.22), + AppColors.darkSurface, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(18), + border: Border.all(color: accent.withValues(alpha: 0.45)), + ), + child: Row( + children: [ + Container( + width: 48, + height: 48, + decoration: BoxDecoration( + color: accent.withValues(alpha: 0.2), + shape: BoxShape.circle, + border: Border.all(color: accent), + ), + child: Icon( + _activeUnit == 0 + ? CupertinoIcons.shield_lefthalf_fill + : _activeUnit == 1 + ? CupertinoIcons.flag_fill + : _activeUnit == 2 + ? CupertinoIcons.flame_fill + : CupertinoIcons.person_crop_circle_badge_checkmark, + color: accent, + size: 24, + ), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _activeUnit == 0 + ? 'مختبر الدولة الساسانية والإمبراطورية الفارسية' + : _activeUnit == 1 + ? 'مختبر الدولة العثمانية ومحطات الحج في الأردن' + : _activeUnit == 2 + ? 'مختبر الثورات العالمية الكبرى ومسارات التغيير' + : 'مختبر الرموز التاريخية وبناة النهضة الحديثة', + style: const TextStyle( + color: Colors.white, + fontSize: 14.5, + fontWeight: FontWeight.w800, + ), + ), + const SizedBox(height: 4), + Text( + 'منهاج التاريخ الوزاري للصف العاشر • فحص معمق واستيعاب منهجي', + style: TextStyle( + color: Colors.white.withValues(alpha: 0.7), + fontSize: 11.5, + ), + ), + ], + ), + ), + ], + ), + ), + const SizedBox(height: 16), + + // Horizontal Carousel Selector + SizedBox( + height: 94, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: curList.length, + separatorBuilder: (_, __) => const SizedBox(width: 10), + itemBuilder: (context, idx) { + final item = curList[idx]; + final isSelected = idx == _selectedMilestoneIndex; + final itemColor = item['accentColor'] as Color; + + return GestureDetector( + onTap: () { + setState(() { + _selectedMilestoneIndex = idx; + }); + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 250), + width: 175, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + decoration: BoxDecoration( + color: isSelected + ? itemColor.withValues(alpha: 0.22) + : AppColors.darkSurface, + borderRadius: BorderRadius.circular(14), + border: Border.all( + color: isSelected ? itemColor : AppColors.darkCardBorder, + width: isSelected ? 2 : 1, + ), + boxShadow: isSelected + ? [ + BoxShadow( + color: itemColor.withValues(alpha: 0.3), + blurRadius: 10, + offset: const Offset(0, 3), + ) + ] + : null, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, vertical: 3), + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2), decoration: BoxDecoration( - color: accent.withOpacity(0.2), + color: itemColor.withValues(alpha: 0.25), borderRadius: BorderRadius.circular(6), ), child: Text( - cur['year'], + item['years'] ?? '', style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w800, - color: accent), + color: itemColor, + fontSize: 10.5, + fontWeight: FontWeight.w800, + ), + ), + ), + const SizedBox(height: 6), + Text( + item['name'] ?? '', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: isSelected ? Colors.white : Colors.white70, + fontSize: 12, + fontWeight: FontWeight.w700, ), ), ], ), - const SizedBox(height: 14), - - // Horizontal Years Scroller - SizedBox( - height: 58, - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: _milestones.length, - itemBuilder: (ctx, idx) { - final m = _milestones[idx]; - final isSel = _selectedMilestoneIndex == idx; - final mColor = m['accentColor'] as Color; - - return GestureDetector( - onTap: () => - setState(() => _selectedMilestoneIndex = idx), - child: AnimatedContainer( - duration: const Duration(milliseconds: 200), - margin: const EdgeInsets.only(left: 10), - padding: const EdgeInsets.symmetric( - horizontal: 16, vertical: 8), - decoration: BoxDecoration( - color: isSel - ? mColor - : const Color(0xFF1E293B), - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: isSel ? Colors.white : Colors.transparent, - width: 1.5, - ), - boxShadow: isSel - ? [ - BoxShadow( - color: mColor.withOpacity(0.4), - blurRadius: 10, - ) - ] - : [], - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - m['year'], - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w900, - color: isSel ? Colors.black : Colors.white, - ), - ), - const SizedBox(height: 2), - Text( - m['title'].toString().split(' ').first, - style: TextStyle( - fontSize: 10.5, - fontWeight: FontWeight.w600, - color: isSel - ? Colors.black.withOpacity(0.8) - : const Color(0xFF94A3B8), - ), - ), - ], - ), - ), - ); - }, - ), - ), - ], - ), - ), - const SizedBox(height: 18), - - // Active Milestone Hero Card - Container( - padding: const EdgeInsets.all(20), - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [accent.withOpacity(0.2), const Color(0xFF0F172A)], - begin: Alignment.topLeft, - end: Alignment.bottomRight, ), - borderRadius: BorderRadius.circular(18), - border: Border.all(color: accent.withOpacity(0.5)), - boxShadow: [ - BoxShadow( - color: accent.withOpacity(0.15), - blurRadius: 20, - ) - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + ); + }, + ), + ), + const SizedBox(height: 16), + + // Detail Card of Selected Figure / Revolution / Sultan + Container( + padding: const EdgeInsets.all(18), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: accent.withValues(alpha: 0.4)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Title and Years Badge + Row( children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - cur['title'], - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w900, - color: Colors.white, - ), - ), - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 10, vertical: 4), - decoration: BoxDecoration( - color: accent.withOpacity(0.2), - borderRadius: BorderRadius.circular(8), - ), - child: Text( - cur['exactDate'], - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w800, - color: accent), - ), - ), - ], - ), - const SizedBox(height: 8), - - Row( - children: [ - const Icon(CupertinoIcons.person_crop_circle_badge_checkmark, - size: 14, color: Color(0xFF94A3B8)), - const SizedBox(width: 6), - Text( - cur['era'], - style: const TextStyle( - fontSize: 12, color: Color(0xFF94A3B8)), - ), - const SizedBox(width: 14), - const Icon(CupertinoIcons.placemark_fill, - size: 14, color: Color(0xFF94A3B8)), - const SizedBox(width: 6), - Text( - cur['location'], - style: const TextStyle( - fontSize: 12, color: Color(0xFF94A3B8)), - ), - ], - ), - const Divider(color: Color(0xFF1E293B), height: 24), - - // Summary - const Text( - 'الوقائع والأحداث التاريخية:', - style: TextStyle( - fontSize: 12.5, - fontWeight: FontWeight.w700, - color: Color(0xFFCBD5E1)), - ), - const SizedBox(height: 4), - Text( - cur['summary'], - style: const TextStyle( - fontSize: 13.5, color: Colors.white, height: 1.5), - ), - const SizedBox(height: 14), - - // Significance - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: const Color(0xFF1E293B), - borderRadius: BorderRadius.circular(10), - ), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Icon(CupertinoIcons.flag_fill, - color: Color(0xFF38BDF8), size: 16), - const SizedBox(width: 8), - Expanded( - child: Text( - 'الأثر والنتيجة: ${cur['significance']}', - style: const TextStyle( - fontSize: 12.5, - color: Color(0xFFE2E8F0), - height: 1.4), - ), - ), - ], - ), - ), - const SizedBox(height: 14), - - // Tawjihi Ministerial Focus Box - Container( - padding: const EdgeInsets.all(14), - decoration: BoxDecoration( - color: const Color(0xFF451A03), - borderRadius: BorderRadius.circular(12), - border: Border.all( - color: const Color(0xFFF59E0B).withOpacity(0.5)), - ), + Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Row( - children: [ - Icon(CupertinoIcons.lightbulb_fill, - color: Color(0xFFFBBF24), size: 16), - SizedBox(width: 8), - Text( - '💡 زاوية أسئلة التوجيهي الوزارية المتكررة:', - style: TextStyle( - fontSize: 12.5, - fontWeight: FontWeight.w800, - color: Color(0xFFFBBF24)), - ), - ], - ), - const SizedBox(height: 6), Text( - cur['tawjihiFocus'], + cur['name'] ?? '', + style: TextStyle( + color: accent, + fontSize: 17, + fontWeight: FontWeight.w900, + ), + ), + const SizedBox(height: 3), + Text( + cur['title'] ?? '', style: const TextStyle( - fontSize: 12.5, - color: Color(0xFFFEF3C7), - height: 1.4), + color: Colors.white70, + fontSize: 12.5, + fontWeight: FontWeight.w600, + ), ), ], ), ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: accent.withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: accent), + ), + child: Text( + cur['years'] ?? '', + style: TextStyle( + color: accent, + fontSize: 11.5, + fontWeight: FontWeight.w800, + ), + ), + ), ], ), + const Divider(color: Colors.white12, height: 24), + + // Capital / Geographical Center + _buildInfoRow( + icon: CupertinoIcons.location_solid, + label: 'العاصمة والموقع الجغرافي', + value: cur['capital'] ?? '', + accentColor: accent, + ), + const SizedBox(height: 12), + + // Key Battles / Turning Points + _buildInfoRow( + icon: CupertinoIcons.shield_fill, + label: 'المحطات والمعارك الفاصلة', + value: cur['battles'] ?? '', + accentColor: accent, + ), + const SizedBox(height: 14), + + // Key Achievements and Reforms Box + Container( + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: Colors.black26, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.white10), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon(CupertinoIcons.book_fill, color: accent, size: 15), + const SizedBox(width: 8), + const Text( + 'المكتسبات والإصلاحات المنهجية المعتمدة:', + style: TextStyle( + color: Colors.white, + fontSize: 12.5, + fontWeight: FontWeight.w700, + ), + ), + ], + ), + const SizedBox(height: 10), + Text( + cur['achievements'] ?? '', + style: const TextStyle( + color: Colors.white70, + fontSize: 13, + height: 1.55, + ), + ), + ], + ), + ), + const SizedBox(height: 12), + + // Key Terms Box (المصطلحات والمفاهيم الوزارية) + if (cur['terms'] != null) ...[ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.black12, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.white12), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(CupertinoIcons.tag_fill, color: accent, size: 15), + const SizedBox(width: 8), + Expanded( + child: RichText( + text: TextSpan( + children: [ + const TextSpan( + text: 'المفاهيم والمصطلحات الوزارية: ', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w700, + fontSize: 12, + ), + ), + TextSpan( + text: cur['terms'], + style: const TextStyle( + color: Colors.white70, + fontSize: 12, + height: 1.4, + ), + ), + ], + ), + ), + ), + ], + ), + ), + const SizedBox(height: 12), + ], + + // Historical Significance & Exam Insight + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: accent.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: accent.withValues(alpha: 0.3)), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(CupertinoIcons.lightbulb_fill, color: accent, size: 16), + const SizedBox(width: 8), + Expanded( + child: Text( + 'الأهمية التاريخية في الاختبار الوزاري: ${cur['significance'] ?? ''}', + style: TextStyle( + color: accent, + fontSize: 12, + fontWeight: FontWeight.w700, + height: 1.45, + ), + ), + ), + ], + ), + ), + ], + ), + ), + ], + ); + } + + // --------------------------------------------------------------------------- + // Active Recall Flashcards View + // --------------------------------------------------------------------------- + Widget _buildFlashcardsView() { + final card = _quickFlashcards[_flashcardIndex]; + final cardColor = card['color'] as Color; + final isMastered = _masteredCardIndices.contains(_flashcardIndex); + + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + // Flashcard Status Bar + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: AppColors.darkCardBorder), + ), + child: Text( + 'بطاقة ${_flashcardIndex + 1} من ${_quickFlashcards.length}', + style: const TextStyle( + color: Colors.white70, + fontSize: 11.5, + fontWeight: FontWeight.w700, + ), + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: const Color(0xFF10B981).withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: const Color(0xFF10B981)), + ), + child: Text( + 'أتقنت ${_masteredCardIndices.length} بطاقات ✅', + style: const TextStyle( + color: Color(0xFF10B981), + fontSize: 11.5, + fontWeight: FontWeight.w800, + ), + ), ), ], ), + const SizedBox(height: 14), + + // Interactive Flippable Card + GestureDetector( + onTap: () { + setState(() { + _isCardFlipped = !_isCardFlipped; + }); + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + padding: const EdgeInsets.all(22), + constraints: const BoxConstraints(minHeight: 250), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + cardColor.withValues(alpha: _isCardFlipped ? 0.25 : 0.15), + AppColors.darkSurface, + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + ), + borderRadius: BorderRadius.circular(22), + border: Border.all( + color: cardColor.withValues(alpha: _isCardFlipped ? 0.8 : 0.4), + width: 2, + ), + boxShadow: [ + BoxShadow( + color: cardColor.withValues(alpha: 0.2), + blurRadius: 16, + offset: const Offset(0, 4), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Header badge + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: cardColor.withValues(alpha: 0.25), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + card['unitTitle'] ?? '', + style: TextStyle( + color: cardColor, + fontSize: 11, + fontWeight: FontWeight.w800, + ), + ), + ), + Icon( + _isCardFlipped ? CupertinoIcons.checkmark_shield_fill : CupertinoIcons.arrow_2_squarepath, + color: cardColor, + size: 18, + ), + ], + ), + const SizedBox(height: 16), + + // Question or Answer Content + if (!_isCardFlipped) ...[ + const Text( + 'السؤال الامتحاني والتركيز الذهني ❓', + style: TextStyle( + color: Colors.white54, + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + const SizedBox(height: 8), + Text( + card['question'] ?? '', + style: const TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w800, + height: 1.45, + ), + ), + const SizedBox(height: 24), + Center( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), + decoration: BoxDecoration( + color: Colors.white10, + borderRadius: BorderRadius.circular(20), + border: Border.all(color: Colors.white24), + ), + child: const Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(CupertinoIcons.hand_point_right, color: Colors.white70, size: 14), + SizedBox(width: 6), + Text( + 'المس البطاقة لإظهار الإجابة والتحقق 👆', + style: TextStyle(color: Colors.white70, fontSize: 11.5, fontWeight: FontWeight.w600), + ), + ], + ), + ), + ), + ] else ...[ + Row( + children: [ + Icon(CupertinoIcons.checkmark_circle_fill, color: cardColor, size: 16), + const SizedBox(width: 6), + Text( + 'الإجابة النموذجية المعتمدة 🎯', + style: TextStyle( + color: cardColor, + fontSize: 12.5, + fontWeight: FontWeight.w800, + ), + ), + ], + ), + const SizedBox(height: 10), + Text( + card['answer'] ?? '', + style: const TextStyle( + color: Colors.white, + fontSize: 14.5, + fontWeight: FontWeight.w700, + height: 1.5, + ), + ), + const SizedBox(height: 16), + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: cardColor.withValues(alpha: 0.15), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: cardColor.withValues(alpha: 0.3)), + ), + child: Row( + children: [ + Icon(CupertinoIcons.lightbulb_fill, color: cardColor, size: 15), + const SizedBox(width: 8), + Expanded( + child: Text( + 'المفتاح الذهني للحفظ: ${card['keyFact'] ?? ''}', + style: TextStyle( + color: cardColor, + fontSize: 12, + fontWeight: FontWeight.w800, + ), + ), + ), + ], + ), + ), + ], + ], + ), + ), + ), + const SizedBox(height: 16), + + // Bottom Controls: Prev, Mastered Toggle, Next + Row( + children: [ + // Previous button + IconButton( + tooltip: 'البطاقة السابقة', + icon: const Icon(CupertinoIcons.chevron_forward, color: Colors.white70), + onPressed: _flashcardIndex > 0 + ? () { + setState(() { + _flashcardIndex--; + _isCardFlipped = false; + }); + } + : null, + ), + const SizedBox(width: 8), + + // Mastered Button Toggle + Expanded( + child: ElevatedButton.icon( + icon: Icon( + isMastered ? CupertinoIcons.check_mark_circled_solid : CupertinoIcons.check_mark, + size: 16, + color: isMastered ? Colors.white : Colors.black, + ), + label: Text( + isMastered ? 'أتقنت هذا المفهوم بنجاح 🌟' : 'حفظ المفهوم كـ مُتقن ✅', + style: TextStyle( + color: isMastered ? Colors.white : Colors.black, + fontSize: 12, + fontWeight: FontWeight.w800, + ), + ), + style: ElevatedButton.styleFrom( + backgroundColor: isMastered ? const Color(0xFF10B981) : cardColor, + padding: const EdgeInsets.symmetric(vertical: 12), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + ), + onPressed: () { + setState(() { + if (isMastered) { + _masteredCardIndices.remove(_flashcardIndex); + } else { + _masteredCardIndices.add(_flashcardIndex); + } + }); + }, + ), + ), + const SizedBox(width: 8), + + // Next button + IconButton( + tooltip: 'البطاقة التالية', + icon: const Icon(CupertinoIcons.chevron_back, color: Colors.white70), + onPressed: _flashcardIndex < _quickFlashcards.length - 1 + ? () { + setState(() { + _flashcardIndex++; + _isCardFlipped = false; + }); + } + : null, + ), + ], + ), + ], + ); + } + + Widget _buildUnitTab(int index, String title) { + final isSelected = _activeUnit == index; + return Expanded( + child: GestureDetector( + onTap: () { + if (_activeUnit != index) { + setState(() { + _activeUnit = index; + _selectedMilestoneIndex = 0; + }); + } + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 2), + decoration: BoxDecoration( + color: isSelected + ? const Color(0xFFD4AF37).withValues(alpha: 0.25) + : Colors.transparent, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: isSelected ? const Color(0xFFD4AF37) : Colors.transparent, + ), + ), + child: Center( + child: Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: isSelected ? Colors.white : AppColors.textSecondaryDark, + fontSize: 11, + fontWeight: isSelected ? FontWeight.w800 : FontWeight.w500, + ), + ), + ), + ), ), ); } + + Widget _buildInfoRow({ + required IconData icon, + required String label, + required String value, + required Color accentColor, + }) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Icon(icon, color: accentColor, size: 16), + const SizedBox(width: 8), + Expanded( + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: '$label: ', + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.w700, + fontSize: 12.5, + ), + ), + TextSpan( + text: value, + style: const TextStyle( + color: Colors.white70, + fontSize: 12.5, + height: 1.4, + ), + ), + ], + ), + ), + ), + ], + ); + } } diff --git a/apps/student_app/lib/presentation/screens/curriculum/islamic_interactive_lab_view.dart b/apps/student_app/lib/presentation/screens/curriculum/islamic_interactive_lab_view.dart index d516038..d50a846 100644 --- a/apps/student_app/lib/presentation/screens/curriculum/islamic_interactive_lab_view.dart +++ b/apps/student_app/lib/presentation/screens/curriculum/islamic_interactive_lab_view.dart @@ -1,9 +1,14 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import '../../../core/theme/app_colors.dart'; /// ============================================================================== -/// SAQEL ENTERPRISE - ISLAMIC INHERITANCE & TAJWEED LAB (حاسبة المواريث والتجويد) +/// SAQEL ENTERPRISE - GRADE 10 ISLAMIC STUDIES INTERACTIVE LAB /// ============================================================================== +/// مختبر التربية الإسلامية التفاعلي المعتمد للصف العاشر: +/// 1. معمل فقه المعاملات المالية (التمييز بين البيع الحلال، ربا الفضل، ربا النسيئة، والقرض الحسن). +/// 2. معمل علامات وأحكام وقف التلاوة القرآنية (الوقف التام، الكافي، الحسن، القبيح، وعلامات المصحف). +/// 3. حاسبة المواريث والفرائض الشرعية. class IslamicInteractiveLabView extends StatefulWidget { const IslamicInteractiveLabView({super.key}); @@ -16,73 +21,111 @@ class _IslamicInteractiveLabViewState extends State with SingleTickerProviderStateMixin { late TabController _tabController; - // Inheritance Calculator State - bool _deceasedIsMale = true; // True = توفي رجل (له زوجة), False = توفيت امرأة (لها زوج) - double _estateAmountJod = 120000.0; - bool _hasFather = true; - bool _hasMother = true; - int _sonsCount = 2; - int _daughtersCount = 1; - int _brothersCount = 2; - - // Calculation Results - List> _sharesResults = []; - String _issueBaseNumber = '24'; // أصل المسألة - String _shariaSummary = ''; - - // Tajweed State - int _selectedRuleIndex = 0; - final List> _tajweedRules = [ + // --------------------------------------------------------------------------- + // Tab 1: Financial Transactions Lab (البيع والربا والقرض) + // --------------------------------------------------------------------------- + int _selectedTransactionScenario = 0; + static const List> _financialScenarios = [ { - 'title': 'الإظهار الحلقي', - 'letters': 'ء ، هـ ، ع ، ح ، غ ، خ (أخي هاك علماً حازه غير خاسر)', - 'definition': 'إخراج النون الساكنة أو التنوين من مخرجها بغير غنة ظاهرة عند ملاقاة أحد حروف الحلق الستة.', - 'examples': [ - {'text': 'مَنْ آمَنَ', 'highlight': 'نْ + ء', 'surah': 'البقرة'}, - {'text': 'يَنْهَوْنَ', 'highlight': 'نْ + هـ', 'surah': 'الأنعام'}, - {'text': 'عَلِيمٌ حَكِيمٌ', 'highlight': 'تنوين + ح', 'surah': 'النساء'}, - ], + 'title': 'بيع سلعة نقداً أو بالتقسيط بزيادة متفق عليها عند العقد', + 'case': 'تاجر يبيع جهاز حاسوب نقداً بـ 400 دينار، أو بالتقسيط بـ 480 ديناراً على 12 شهراً.', + 'ruling': 'بيع جائز شرعاً (بيع التقسيط)', + 'isHalal': true, + 'color': Color(0xFF10B981), + 'evidence': 'قال تعالى: {وَأَحَلَّ اللَّهُ الْبَيْعَ وَحَرَّمَ الرِّبَا}؛ فالزيادة في الثمن مقابل الأجل في بيع السلع مباحة وليست رباً طالما تم الاتفاق عليها مسبقاً وتحدد السعر عند إبرام العقد.', + 'condition': 'أن يمتلك البائع السلعة قبل بيعها، وألا يشترط غرامة مالية عند تأخر سداد الأقساط.', + }, + { + 'title': 'مبادلة 100 غرام ذهب قديم بـ 90 غرام ذهب جديد مصوغ مع دفع الفارق', + 'case': 'شخص يبادل سواراً ذهبياً قديماً وزنه 100 غرام بآخر جديد وزنه 90 غراماً ويدفع للمحل 50 ديناراً.', + 'ruling': 'ربا الفضل (محرم قطعاً)', + 'isHalal': false, + 'color': Color(0xFFEF4444), + 'evidence': 'قول النبي ﷺ: "الذَّهَبُ بالذَّهَبِ مِثْلاً بمِثْلٍ، يَداً بيَدٍ، فَمَن زادَ أوِ اسْتَزادَ فقَدْ أرْبَى".', + 'condition': 'المخرج الشرعي الصحيح: بيع الذهب القديم وقبض ثمنه نقداً، ثم شراء الذهب الجديد بعقد وصفقة مستقلة.', + }, + { + 'title': 'اقتراض مبلغ مالي بشرط إرجاعه بزيادة (أو اشتراط منفعة للمقرض)', + 'case': 'شخص يقترض 1000 دينار على أن يسددها 1150 ديناراً بعد ستة أشهر، أو يقرضه بشرط أن يؤجره بيته برخص.', + 'ruling': 'ربا النسيئة وربا القروض (كبيرة من الكبائر)', + 'isHalal': false, + 'color': Color(0xFFDC2626), + 'evidence': 'القاعدة الفقهية المجمع عليها: "كلّ قرضٍ جرَّ نفعاً مشروطاً للمقرض فهو ربا".', + 'condition': 'القرض في الإسلام عقد إرفاق وإحسان وثواب، ويجب أن يُرد بالمثل دون أي زيادة مشروطة.', + }, + { + 'title': 'القرض الحسن دون أي اشتراط فائدة أو زيادة', + 'case': 'إقراض محتاج 500 دينار ليسد حاجته على أن يعيد 500 دينار دون أي زيادة، وأمهله عند تعسره.', + 'ruling': 'قرض حسن مستحب عظيم الأجر', + 'isHalal': true, + 'color': Color(0xFF00F5D4), + 'evidence': 'قال تعالى: {مَّن ذَا الَّذِي يُقْرِضُ اللَّهَ قَرْضًا حَسَنًا فَيُضَاعِفَهُ لَهُ أَضْعَافًا كَثِيرَةً}.', + 'condition': 'النية الخالصة لوجه الله تعالى، والتيسير على المعسر كما حث النبي ﷺ.', + }, + { + 'title': 'المراهنات والميسر (دفع مبلغ مع احتمالية الكسب أو الخسارة الكاملة)', + 'case': 'المشاركة في ألعاب أو تطبيقات يدفع فيها كل طرف مالاً ويأخذه الفائز أو منصات المراهنات الرياضية.', + 'ruling': 'قمار وميسر محرم بنص القرآن', + 'isHalal': false, + 'color': Color(0xFFB91C1C), + 'evidence': 'قال تعالى: {إِنَّمَا الْخَمْرُ وَالْمَيْسِرُ وَالْأَنصَابُ وَالْأَزْلَامُ رِجْسٌ مِّنْ عَمَلِ الشَّيْطَانِ فَاجْتَنِبُوهُ}.', + 'condition': 'كل معاملة تقوم على الغرر والمخاطرة المالية الدائرة بين الغنم التام والغرم التام محرمة باطلة.', + }, + ]; + + // --------------------------------------------------------------------------- + // Tab 2: Waqf & Tajweed Rules (علامات وأحكام الوقف القرآني) + // --------------------------------------------------------------------------- + static const List> _waqfRules = [ + { + 'type': 'الوقف التام (Complete Waqf)', + 'symbol': 'قلى / مـ', + 'definition': 'الوقف على كلام تم معناه ولا يتعلق بما بعده لفظاً ولا معنى، وغالباً يكون عند رؤوس الآيات وانتهاء القصص.', + 'example': 'الوقف على قوله تعالى: {وَأُولَئِكَ هُمُ الْمُفْلِحُونَ} ثم الابتداء بقوله: {إِنَّ الَّذِينَ كَفَرُوا...}', + 'ruling': 'يحسن الوقف عليه ويحسن الابتداء بما بعده.', 'accentColor': Color(0xFF10B981), }, { - 'title': 'الإدغام (بغنة وبغير غنة)', - 'letters': 'ي ، ر ، م ، ل ، و ، ن (يرملون)', - 'definition': 'إدخال حرف ساكن في حرف متحرك بحيث يصيران حرفاً واحداً مشدداً. بغنة في (ينمو) وبغير غنة في (الراء واللام).', - 'examples': [ - {'text': 'مَن يَقُولُ', 'highlight': 'إدغام بغنة (نْ + ي)', 'surah': 'البقرة'}, - {'text': 'مِن رَّبِّهِمْ', 'highlight': 'إدغام بغير غنة (نْ + ر)', 'surah': 'البقرة'}, - {'text': 'مِّن وَالٍ', 'highlight': 'إدغام بغنة (نْ + و)', 'surah': 'الرعد'}, - ], + 'type': 'الوقف الكافي (Sufficient Waqf)', + 'symbol': 'ج / صلى', + 'definition': 'الوقف على كلام تم في ذاته ويتعلق بما بعده في المعنى دون اللفظ (الإعراب).', + 'example': 'الوقف على قوله تعالى: {حُرِّمَتْ عَلَيْكُمُ الْمَيْتَةُ وَالدَّمُ وَلَحْمُ الْخِنزِيرِ} ثم الاستئناف.', + 'ruling': 'يحسن الوقف عليه والابتداء بما بعده، والوصل جائز.', 'accentColor': Color(0xFF38BDF8), }, { - 'title': 'الإقلاب', - 'letters': 'حرف الباء (ب) فقط مع وضع ميم صغيرة (مـ)', - 'definition': 'قلب النون الساكنة أو التنوين ميماً مخفاة بغنة عند ملاقاة حرف الباء.', - 'examples': [ - {'text': 'مِن بَعْدِ', 'highlight': 'نْ + ب ➔ ميم مخفاة', 'surah': 'البقرة'}, - {'text': 'أَنبِئْهُم', 'highlight': 'نْ + ب في كلمة واحدة', 'surah': 'البقرة'}, - {'text': 'سَمِيعٌ بَصِيرٌ', 'highlight': 'تنوين + ب', 'surah': 'لقمان'}, - ], + 'type': 'الوقف الحسن (Good Waqf)', + 'symbol': 'صلى', + 'definition': 'الوقف على كلام أفاد معنى تاماً ولكنه متعلق بما بعده لفظاً ومعنى.', + 'example': 'الوقف على قوله: {الْحَمْدُ لِلَّهِ} والوصل بـ {رَبِّ الْعَالَمِينَ} أولى.', + 'ruling': 'يحسن الوقف عليه، ولكن لا يحسن الابتداء بما بعده لتعلقه به إلا إن كان رأس آية.', 'accentColor': Color(0xFFF59E0B), }, { - 'title': 'الإخفاء الحقيقي', - 'letters': '15 حرفاً (ص، ذ، ث، ك، ج، ش، ق، س، د، ط، ز، ف، ت، ض، ظ)', - 'definition': 'النطق بالنون الساكنة أو التنوين بحالة بين الإظهار والإدغام مع بقاء الغنة بمقدار حركتين.', - 'examples': [ - {'text': 'مِن قَبْلُ', 'highlight': 'نْ + ق', 'surah': 'البقرة'}, - {'text': 'كِتَابٌ كَرِيمٌ', 'highlight': 'تنوين + ك', 'surah': 'النمل'}, - {'text': 'أَنفُسَكُمْ', 'highlight': 'نْ + ف', 'surah': 'النساء'}, - ], - 'accentColor': Color(0xFFA78BFA), + 'type': 'الوقف القبيح (Forbidden Waqf)', + 'symbol': 'لا (علامة المنع)', + 'definition': 'الوقف على ما لا يتم به الكلام لشدة تعلقه بما بعده، أو يوهم معنى فاسداً لا يليق بجلال الله تعالى.', + 'example': 'الوقف على قوله: {فَبُهِتَ الَّذِي كَفَرَ وَاللَّهُ لَا يَهْدِي} دون إتمام {الْقَوْمَ الظَّالِمِينَ}، أو الوقف على {لَا تَقْرَبُوا الصَّلَاةَ}.', + 'ruling': 'لا يجوز تعمده إلا لضرورة كانقطاع نفس، ويجب الرجوع والابتداء بما قبله لإتمام المعنى.', + 'accentColor': Color(0xFFEF4444), }, ]; + // --------------------------------------------------------------------------- + // Tab 3: Inheritance Calculator State + // --------------------------------------------------------------------------- + bool _deceasedIsMale = true; + double _estateAmountJod = 120000.0; + final bool _hasFather = true; + final bool _hasMother = true; + final int _sonsCount = 2; + final int _daughtersCount = 1; + List> _sharesResults = []; + @override void initState() { super.initState(); - _tabController = TabController(length: 2, vsync: this); + _tabController = TabController(length: 3, vsync: this); _calculateInheritance(); } @@ -97,189 +140,264 @@ class _IslamicInteractiveLabViewState extends State final hasChildren = (_sonsCount + _daughtersCount) > 0; double remainingEstate = _estateAmountJod; - // 1. Spouse Share (الزوج أو الزوجة) if (_deceasedIsMale) { - // Deceased is male -> Wife inherits - final shareFraction = hasChildren ? '1/8 (الثمن)' : '1/4 (الربع)'; - final double amount = hasChildren - ? (_estateAmountJod * (1.0 / 8.0)) - : (_estateAmountJod * (1.0 / 4.0)); - remainingEstate -= amount; + final wifeRatio = hasChildren ? 0.125 : 0.25; + final wifeShareDesc = hasChildren ? 'الثمن (1/8)' : 'الربع (1/4)'; + final wifeAmount = _estateAmountJod * wifeRatio; + remainingEstate -= wifeAmount; results.add({ 'heir': 'الزوجة', - 'share': shareFraction, - 'amount': amount, - 'basis': hasChildren - ? 'تستحق الثمن لوجود الفرع الوارث (الأبناء والبنات)' - : 'تستحق الربع لعدم وجود فرع وارث', + 'shareRatio': wifeShareDesc, + 'amount': wifeAmount, + 'evidence': hasChildren ? 'لوجود الفرع الوارث' : 'لعدم وجود فرع وارث', }); } else { - // Deceased is female -> Husband inherits - final shareFraction = hasChildren ? '1/4 (الربع)' : '1/2 (النصف)'; - final double amount = hasChildren - ? (_estateAmountJod * (1.0 / 4.0)) - : (_estateAmountJod * (1.0 / 2.0)); - remainingEstate -= amount; + final husbandRatio = hasChildren ? 0.25 : 0.5; + final husbandShareDesc = hasChildren ? 'الربع (1/4)' : 'النصف (1/2)'; + final husbandAmount = _estateAmountJod * husbandRatio; + remainingEstate -= husbandAmount; results.add({ 'heir': 'الزوج', - 'share': shareFraction, - 'amount': amount, - 'basis': hasChildren - ? 'يستحق الربع لوجود الفرع الوارث' - : 'يستحق النصف لعدم وجود الفرع الوارث', + 'shareRatio': husbandShareDesc, + 'amount': husbandAmount, + 'evidence': hasChildren ? 'لوجود الفرع الوارث' : 'لعدم وجود فرع وارث', + }); + } + + if (_hasFather) { + const fatherRatio = 1.0 / 6.0; + final fatherAmount = _estateAmountJod * fatherRatio; + remainingEstate -= fatherAmount; + results.add({ + 'heir': 'الأب', + 'shareRatio': 'السدس (1/6)', + 'amount': fatherAmount, + 'evidence': 'فرضاً لوجود الفرع الوارث المذكر', }); } - // 2. Mother Share (الأم) if (_hasMother) { - final shareFraction = (hasChildren || _brothersCount > 1) - ? '1/6 (السدس)' - : '1/3 (الثلث)'; - final double amount = (hasChildren || _brothersCount > 1) - ? (_estateAmountJod * (1.0 / 6.0)) - : (_estateAmountJod * (1.0 / 3.0)); - remainingEstate -= amount; + const motherRatio = 1.0 / 6.0; + final motherAmount = _estateAmountJod * motherRatio; + remainingEstate -= motherAmount; results.add({ 'heir': 'الأم', - 'share': shareFraction, - 'amount': amount, - 'basis': (hasChildren || _brothersCount > 1) - ? 'تستحق السدس لوجود الفرع الوارث أو جمع من الإخوة' - : 'تستحق الثلث لعدم وجود فرع وارث أو جمع من الإخوة', + 'shareRatio': 'السدس (1/6)', + 'amount': motherAmount, + 'evidence': 'لوجود الفرع الوارث والأخوة', }); } - // 3. Father Share (الأب) - if (_hasFather) { - if (_sonsCount > 0) { - // Sons exist -> Father takes 1/6 strictly - final double amount = _estateAmountJod * (1.0 / 6.0); - remainingEstate -= amount; - results.add({ - 'heir': 'الأب', - 'share': '1/6 (السدس فرضاً)', - 'amount': amount, - 'basis': 'يستحق السدس فرضاً فقط لوجود فرع وارث مذكر (الابن)', - }); - } else if (_daughtersCount > 0) { - // Only daughters -> Father takes 1/6 + Asabah (residuary) - final double fardh = _estateAmountJod * (1.0 / 6.0); - remainingEstate -= fardh; - results.add({ - 'heir': 'الأب', - 'share': '1/6 فرضاً + الباقي تعصيباً', - 'amount': fardh, - 'basis': 'يستحق السدس فرضاً مع البنت + الباقي تعصيباً إن تبقّى شيء', - }); - } else { - // No children -> Father is pure Asabah - results.add({ - 'heir': 'الأب', - 'share': 'عصبة بالنفس (الباقي بالكامل)', - 'amount': remainingEstate, - 'basis': 'عصبة بالنفس لعدم وجود فرع وارث، يحوز الباقي بعد أصحاب الفروض', - }); - remainingEstate = 0; - } - } - - // 4. Children Share (الأبناء والبنات - للذكر مثل حظ الأنثيين) if (hasChildren && remainingEstate > 0) { - final totalShares = (_sonsCount * 2) + _daughtersCount; - if (totalShares > 0) { - final shareUnit = remainingEstate / totalShares; - + final totalChildShares = (_sonsCount * 2) + _daughtersCount; + if (totalChildShares > 0) { + final singleShareVal = remainingEstate / totalChildShares; if (_sonsCount > 0) { - final totalSonsAmount = shareUnit * 2 * _sonsCount; + final sonsTotal = singleShareVal * 2 * _sonsCount; results.add({ - 'heir': 'الأبناء الذكور ($_sonsCount)', - 'share': 'عصبة بالغير (للذكر مثل حظ الأنثيين)', - 'amount': totalSonsAmount, - 'basis': - 'لكل ابن ${((shareUnit * 2)).toStringAsFixed(0)} د.أ (ضعف حصة البنت)', + 'heir': 'الأبناء ($_sonsCount)', + 'shareRatio': 'عصبة بالغير (للذكر مثل حظ الأنثيين)', + 'amount': sonsTotal, + 'evidence': 'لكل ابن: ${(singleShareVal * 2).toStringAsFixed(0)} د.أ', }); } - if (_daughtersCount > 0) { - final totalDaughtersAmount = shareUnit * _daughtersCount; + final daughtersTotal = singleShareVal * _daughtersCount; results.add({ - 'heir': 'البنات الإناث ($_daughtersCount)', - 'share': _sonsCount > 0 - ? 'عصبة بالغير مع الأبناء' - : (_daughtersCount == 1 ? '1/2 (النصف)' : '2/3 (الثلثان)'), - 'amount': totalDaughtersAmount, - 'basis': 'لكل بنت ${(shareUnit).toStringAsFixed(0)} د.أ بالتساوي', + 'heir': 'البنات ($_daughtersCount)', + 'shareRatio': 'عصبة بالغير مع الذكور', + 'amount': daughtersTotal, + 'evidence': 'لكل بنت: ${singleShareVal.toStringAsFixed(0)} د.أ', }); } - remainingEstate = 0; - } - } - - // 5. Brothers & Hijb check (حجب الإخوة) - if (_brothersCount > 0) { - if (_hasFather || _sonsCount > 0) { - results.add({ - 'heir': 'الإخوة الأشقاء ($_brothersCount)', - 'share': 'محجوبون حجب حرمان (صفر)', - 'amount': 0.0, - 'basis': _sonsCount > 0 - ? 'حُجبوا بالأصل والفرع المذكر (حجب حرمان تام بالابن)' - : 'حُجبوا بوجود الأب المباشر', - }); - } else if (!hasChildren && remainingEstate > 0) { - results.add({ - 'heir': 'الإخوة الأشقاء ($_brothersCount)', - 'share': 'عصبة بالنفس (اقتسام الباقي)', - 'amount': remainingEstate, - 'basis': 'يقتسمون الباقي تعصيباً لعدم وجود من يحجبهم', - }); } } setState(() { _sharesResults = results; - _issueBaseNumber = hasChildren ? '24' : '12'; - _shariaSummary = - 'أصل المسألة من ($_issueBaseNumber) سهماً، تم استيفاء أصحاب الفروض ثم انتقال الباقي للعصبات مع مراعاة قواعد الحجب الشرعي المقررة بوزارة التربية والتعليم.'; }); } @override Widget build(BuildContext context) { - return Container( - color: const Color(0xFF070B12), + return Column( + children: [ + Container( + margin: const EdgeInsets.fromLTRB(16, 12, 16, 8), + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.darkCardBorder), + ), + child: TabBar( + controller: _tabController, + indicatorColor: AppColors.saqelCyan, + labelColor: Colors.white, + unselectedLabelColor: AppColors.textSecondaryDark, + indicatorSize: TabBarIndicatorSize.tab, + labelStyle: const TextStyle(fontWeight: FontWeight.w800, fontSize: 12), + tabs: const [ + Tab(text: 'المعاملات والربا ⚖️'), + Tab(text: 'أحكام الوقف 📖'), + Tab(text: 'حاسبة المواريث 💰'), + ], + ), + ), + Expanded( + child: TabBarView( + controller: _tabController, + children: [ + _buildFinancialTransactionsTab(), + _buildWaqfTab(), + _buildInheritanceTab(), + ], + ), + ), + ], + ); + } + + Widget _buildFinancialTransactionsTab() { + final cur = _financialScenarios[_selectedTransactionScenario]; + final color = cur['color'] as Color; + + return SingleChildScrollView( + padding: const EdgeInsets.all(16), child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - // Sub-Tab Switcher Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - decoration: const BoxDecoration( - color: Color(0xFF0E1626), - border: Border(bottom: BorderSide(color: Color(0xFF1E293B))), + padding: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.darkCardBorder), ), - child: Row( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - _tabButton( - index: 0, - title: 'حاسبة المواريث والتركات ⚖️', - icon: CupertinoIcons.money_dollar_circle_fill, + const Text( + 'اختر المسألة المالية لتشخيص الحكم الشرعي والعلة الفقهية:', + style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 13.5), ), - const SizedBox(width: 8), - _tabButton( - index: 1, - title: 'مختبر أحكام التجويد 📖', - icon: CupertinoIcons.book_circle_fill, + const SizedBox(height: 10), + SizedBox( + height: 42, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: _financialScenarios.length, + separatorBuilder: (_, __) => const SizedBox(width: 8), + itemBuilder: (context, idx) { + final isSelected = idx == _selectedTransactionScenario; + return ChoiceChip( + label: Text('المسألة ${idx + 1}'), + selected: isSelected, + selectedColor: AppColors.saqelCyan.withValues(alpha: 0.25), + backgroundColor: AppColors.darkBackground, + labelStyle: TextStyle( + color: isSelected ? AppColors.saqelCyan : Colors.white70, + fontWeight: FontWeight.w700, + fontSize: 12, + ), + onSelected: (_) { + setState(() { + _selectedTransactionScenario = idx; + }); + }, + ); + }, + ), ), ], ), ), - - Expanded( - child: IndexedStack( - index: _tabController.index, + const SizedBox(height: 14), + Container( + padding: const EdgeInsets.all(18), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(18), + border: Border.all(color: color.withValues(alpha: 0.5)), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildInheritanceCalculatorTab(), - _buildTajweedLabTab(), + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: color), + ), + child: Text( + cur['ruling'] ?? '', + style: TextStyle(color: color, fontWeight: FontWeight.w900, fontSize: 13), + ), + ), + const Spacer(), + Icon( + cur['isHalal'] ? CupertinoIcons.check_mark_circled_solid : CupertinoIcons.clear_circled_solid, + color: color, + size: 22, + ), + ], + ), + const SizedBox(height: 14), + Text( + cur['title'] ?? '', + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 15), + ), + const SizedBox(height: 8), + Text( + 'الحالة المعاصرة: ${cur['case'] ?? ''}', + style: const TextStyle(color: Colors.white70, fontSize: 13, height: 1.4), + ), + const Divider(color: Colors.white12, height: 24), + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.black26, + borderRadius: BorderRadius.circular(10), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'الدليل الشرعي والعلة الفقهية:', + style: TextStyle(color: AppColors.saqelCyan, fontWeight: FontWeight.w700, fontSize: 12), + ), + const SizedBox(height: 6), + Text( + cur['evidence'] ?? '', + style: const TextStyle(color: Colors.white, fontSize: 12.5, height: 1.45), + ), + ], + ), + ), + const SizedBox(height: 10), + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(10), + border: Border.all(color: color.withValues(alpha: 0.25)), + ), + child: Row( + children: [ + Icon(CupertinoIcons.info_circle_fill, color: color, size: 16), + const SizedBox(width: 8), + Expanded( + child: Text( + cur['condition'] ?? '', + style: TextStyle(color: color, fontSize: 12, fontWeight: FontWeight.w600), + ), + ), + ], + ), + ), ], ), ), @@ -288,652 +406,183 @@ class _IslamicInteractiveLabViewState extends State ); } - Widget _tabButton({ - required int index, - required String title, - required IconData icon, - }) { - final isSelected = _tabController.index == index; - return Expanded( - child: GestureDetector( - onTap: () => setState(() => _tabController.animateTo(index)), - child: Container( - padding: const EdgeInsets.symmetric(vertical: 10), + Widget _buildWaqfTab() { + return ListView.builder( + padding: const EdgeInsets.all(16), + itemCount: _waqfRules.length, + itemBuilder: (context, idx) { + final rule = _waqfRules[idx]; + final color = rule['accentColor'] as Color; + + return Container( + margin: const EdgeInsets.only(bottom: 14), + padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: isSelected ? const Color(0xFFF59E0B) : const Color(0xFF1E293B), - borderRadius: BorderRadius.circular(10), + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: color.withValues(alpha: 0.4)), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Icon(icon, - size: 16, - color: isSelected ? Colors.black : const Color(0xFF94A3B8)), - const SizedBox(width: 8), + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: color.withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: color), + ), + child: Text( + rule['symbol'] ?? '', + style: TextStyle(color: color, fontWeight: FontWeight.w900, fontSize: 13), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Text( + rule['type'] ?? '', + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 14.5), + ), + ), + ], + ), + const SizedBox(height: 10), Text( - title, - style: TextStyle( - fontSize: 13, - fontWeight: isSelected ? FontWeight.w800 : FontWeight.w600, - color: isSelected ? Colors.black : const Color(0xFF94A3B8), + rule['definition'] ?? '', + style: const TextStyle(color: Colors.white70, fontSize: 13, height: 1.4), + ), + const SizedBox(height: 10), + Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Colors.black26, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.white10), ), + child: Text( + 'المثال القرآني: ${rule['example'] ?? ''}', + style: const TextStyle(color: AppColors.saqelCyan, fontSize: 12.5, fontWeight: FontWeight.w600), + ), + ), + const SizedBox(height: 8), + Text( + 'حكمه: ${rule['ruling'] ?? ''}', + style: TextStyle(color: color, fontSize: 12, fontWeight: FontWeight.w700), ), ], ), - ), - ), + ); + }, ); } - // --------------------------------------------------------------------------- - // TAB 1: Islamic Inheritance & Estate Calculator - // --------------------------------------------------------------------------- - Widget _buildInheritanceCalculatorTab() { + Widget _buildInheritanceTab() { return SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - // Banner - Container( - padding: const EdgeInsets.all(14), - decoration: BoxDecoration( - gradient: const LinearGradient( - colors: [Color(0xFF78350F), Color(0xFF0F172A)], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - borderRadius: BorderRadius.circular(14), - border: - Border.all(color: const Color(0xFFF59E0B).withOpacity(0.4)), - ), - child: const Row( - children: [ - Icon(CupertinoIcons.sparkles, color: Color(0xFFFBBF24), size: 22), - SizedBox(width: 12), - Expanded( - child: Text( - 'حاسبة المواريث المطابقة لكتاب التربية الإسلامية (التوجيهي الوزاري 2008) مع أصول المسائل وقواعد الحجب والعصبات.', - style: TextStyle( - fontSize: 12.5, color: Colors.white, height: 1.4), - ), - ), - ], - ), - ), - const SizedBox(height: 16), - - // Inputs Card Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: const Color(0xFF0F172A), + color: AppColors.darkSurface, borderRadius: BorderRadius.circular(16), - border: Border.all(color: const Color(0xFF1E293B)), + border: Border.all(color: AppColors.darkCardBorder), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( - '1. المتوفى ومقدار التركة:', - style: TextStyle( - fontSize: 13.5, - fontWeight: FontWeight.w700, - color: Colors.white), + 'حاسبة التركات وتصفية الفرائض الشرعية', + style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 14.5), ), - const SizedBox(height: 10), - - // Gender Switcher + const SizedBox(height: 12), Row( children: [ - Expanded( - child: _genderButton( - title: 'المتوفى رجل (له زوجة)', - isSelected: _deceasedIsMale, - onTap: () { - setState(() => _deceasedIsMale = true); + const Text('المتوفى:', style: TextStyle(color: Colors.white70, fontSize: 13)), + const SizedBox(width: 12), + ChoiceChip( + label: const Text('رجل (له زوجة)'), + selected: _deceasedIsMale, + selectedColor: AppColors.saqelCyan.withValues(alpha: 0.3), + onSelected: (val) { + if (val) { + _deceasedIsMale = true; _calculateInheritance(); - }, - ), + } + }, ), const SizedBox(width: 8), - Expanded( - child: _genderButton( - title: 'المتوفاة امرأة (لها زوج)', - isSelected: !_deceasedIsMale, - onTap: () { - setState(() => _deceasedIsMale = false); + ChoiceChip( + label: const Text('امرأة (لها زوج)'), + selected: !_deceasedIsMale, + selectedColor: AppColors.saqelCyan.withValues(alpha: 0.3), + onSelected: (val) { + if (val) { + _deceasedIsMale = false; _calculateInheritance(); - }, - ), + } + }, ), ], ), - const SizedBox(height: 14), - - // Estate Slider - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('مقدار التركة المالية:', - style: - TextStyle(fontSize: 12, color: Color(0xFF94A3B8))), - Text('${_estateAmountJod.toStringAsFixed(0)} دينار أردني', - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w800, - color: Color(0xFFFBBF24))), - ], + const SizedBox(height: 12), + Text( + 'قيمة التركة الصافية: ${_estateAmountJod.toStringAsFixed(0)} دينار أردني', + style: const TextStyle(color: AppColors.saqelCyan, fontWeight: FontWeight.w700, fontSize: 13), ), Slider( value: _estateAmountJod, min: 10000, max: 500000, divisions: 49, - activeColor: const Color(0xFFF59E0B), - inactiveColor: const Color(0xFF1E293B), + activeColor: AppColors.saqelCyan, onChanged: (val) { - setState(() => _estateAmountJod = val); - _calculateInheritance(); - }, - ), - - const Divider(color: Color(0xFF1E293B), height: 20), - - // Heirs Configuration - const Text( - '2. تحديد الورثة الشرعيين:', - style: TextStyle( - fontSize: 13.5, - fontWeight: FontWeight.w700, - color: Colors.white), - ), - const SizedBox(height: 10), - - // Parents Checkboxes - Row( - children: [ - Expanded( - child: CheckboxListTile( - value: _hasFather, - title: const Text('الأب حي', - style: - TextStyle(color: Colors.white, fontSize: 13)), - activeColor: const Color(0xFFF59E0B), - contentPadding: EdgeInsets.zero, - onChanged: (val) { - setState(() => _hasFather = val ?? true); - _calculateInheritance(); - }, - ), - ), - Expanded( - child: CheckboxListTile( - value: _hasMother, - title: const Text('الأم حية', - style: - TextStyle(color: Colors.white, fontSize: 13)), - activeColor: const Color(0xFFF59E0B), - contentPadding: EdgeInsets.zero, - onChanged: (val) { - setState(() => _hasMother = val ?? true); - _calculateInheritance(); - }, - ), - ), - ], - ), - - // Sons & Daughters Counters - Row( - children: [ - Expanded( - child: _counterControl( - label: 'الأبناء الذكور', - count: _sonsCount, - onChanged: (val) { - setState(() => _sonsCount = val); - _calculateInheritance(); - }, - ), - ), - const SizedBox(width: 8), - Expanded( - child: _counterControl( - label: 'البنات الإناث', - count: _daughtersCount, - onChanged: (val) { - setState(() => _daughtersCount = val); - _calculateInheritance(); - }, - ), - ), - ], - ), - const SizedBox(height: 10), - - // Brothers Counter - _counterControl( - label: 'الإخوة الأشقاء (تطبيق قواعد الحجب)', - count: _brothersCount, - onChanged: (val) { - setState(() => _brothersCount = val); + _estateAmountJod = val; _calculateInheritance(); }, ), ], ), ), - const SizedBox(height: 20), - - // Output Distribution Table - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - 'جدول توزيع السهام والأنصبة الشرعية:', - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w800, - color: Colors.white), - ), - Container( - padding: - const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - decoration: BoxDecoration( - color: const Color(0xFFF59E0B).withOpacity(0.2), - borderRadius: BorderRadius.circular(6), - ), - child: Text( - 'أصل المسألة: $_issueBaseNumber', - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w800, - color: Color(0xFFFBBF24)), - ), - ), - ], - ), - const SizedBox(height: 10), - - ..._sharesResults.map((share) => Container( - margin: const EdgeInsets.only(bottom: 10), - padding: const EdgeInsets.all(14), - decoration: BoxDecoration( - color: const Color(0xFF0F172A), - borderRadius: BorderRadius.circular(14), - border: Border.all( - color: (share['amount'] as double) > 0 - ? const Color(0xFFF59E0B).withOpacity(0.3) - : const Color(0xFFEF4444).withOpacity(0.3), - ), - ), - child: Row( - children: [ - CircleAvatar( - radius: 20, - backgroundColor: (share['amount'] as double) > 0 - ? const Color(0xFF78350F) - : const Color(0xFF7F1D1D), - child: Text( - (share['heir'] as String).substring(0, 1), - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w800, - color: (share['amount'] as double) > 0 - ? const Color(0xFFFBBF24) - : const Color(0xFFF87171), - ), - ), - ), - const SizedBox(width: 14), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - share['heir'], - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w800, - color: Colors.white), - ), - const SizedBox(width: 8), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 6, vertical: 1), - decoration: BoxDecoration( - color: const Color(0xFF1E293B), - borderRadius: BorderRadius.circular(4), - ), - child: Text( - share['share'], - style: const TextStyle( - fontSize: 11, - color: Color(0xFF38BDF8), - fontWeight: FontWeight.w700), - ), - ), - ], - ), - const SizedBox(height: 4), - Text( - share['basis'], - style: const TextStyle( - fontSize: 11.5, - color: Color(0xFF94A3B8), - height: 1.3), - ), - ], - ), - ), - Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - '${(share['amount'] as double).toStringAsFixed(0)} د.أ', - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.w900, - color: (share['amount'] as double) > 0 - ? const Color(0xFF10B981) - : const Color(0xFFEF4444), - ), - ), - Text( - (share['amount'] as double) > 0 - ? 'مستحق' - : 'محجوب حجب حرمان', - style: TextStyle( - fontSize: 10.5, - fontWeight: FontWeight.w700, - color: (share['amount'] as double) > 0 - ? const Color(0xFF34D399) - : const Color(0xFFF87171), - ), - ), - ], - ), - ], - ), - )), - const SizedBox(height: 12), - - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: const Color(0xFF1E293B), - borderRadius: BorderRadius.circular(10), - ), - child: Text( - _shariaSummary, - style: const TextStyle( - fontSize: 12, color: Color(0xFFCBD5E1), height: 1.4), - ), - ), - ], - ), - ); - } - - Widget _genderButton({ - required String title, - required bool isSelected, - required VoidCallback onTap, - }) { - return GestureDetector( - onTap: onTap, - child: Container( - padding: const EdgeInsets.symmetric(vertical: 10), - decoration: BoxDecoration( - color: isSelected ? const Color(0xFFF59E0B) : const Color(0xFF1E293B), - borderRadius: BorderRadius.circular(8), - ), - child: Center( - child: Text( - title, - style: TextStyle( - fontSize: 12, - fontWeight: isSelected ? FontWeight.w800 : FontWeight.w600, - color: isSelected ? Colors.black : Colors.white, - ), - ), - ), - ), - ); - } - - Widget _counterControl({ - required String label, - required int count, - required ValueChanged onChanged, - }) { - return Container( - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - decoration: BoxDecoration( - color: const Color(0xFF161F30), - borderRadius: BorderRadius.circular(10), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(label, - style: const TextStyle(fontSize: 12, color: Colors.white)), - Row( - children: [ - IconButton( - icon: const Icon(CupertinoIcons.minus_circle, - size: 20, color: Color(0xFF94A3B8)), - onPressed: count > 0 ? () => onChanged(count - 1) : null, - constraints: const BoxConstraints(), - padding: EdgeInsets.zero, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Text('$count', - style: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.w800, - color: Colors.white)), - ), - IconButton( - icon: const Icon(CupertinoIcons.plus_circle, - size: 20, color: Color(0xFFF59E0B)), - onPressed: () => onChanged(count + 1), - constraints: const BoxConstraints(), - padding: EdgeInsets.zero, - ), - ], - ), - ], - ), - ); - } - - // --------------------------------------------------------------------------- - // TAB 2: Tajweed & Phonetics Lab - // --------------------------------------------------------------------------- - Widget _buildTajweedLabTab() { - return SingleChildScrollView( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ + const SizedBox(height: 14), const Text( - 'أحكام النون الساكنة والتنوين (الوحدة الأولى - منهاج التوجيهي):', - style: TextStyle( - fontSize: 13.5, - fontWeight: FontWeight.w700, - color: Colors.white), + 'جدول الأنصبة الشرعية المحسوبة:', + style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 14), ), - const SizedBox(height: 12), - - // Rule Selector Chips - Row( - children: List.generate(_tajweedRules.length, (idx) { - final rule = _tajweedRules[idx]; - final isCur = _selectedRuleIndex == idx; - return Expanded( - child: GestureDetector( - onTap: () => setState(() => _selectedRuleIndex = idx), - child: Container( - margin: EdgeInsets.only(left: idx < _tajweedRules.length - 1 ? 6 : 0), - padding: const EdgeInsets.symmetric(vertical: 10), - decoration: BoxDecoration( - color: isCur - ? rule['accentColor'] - : const Color(0xFF0F172A), - borderRadius: BorderRadius.circular(10), - border: Border.all( - color: isCur - ? (rule['accentColor'] as Color) - : const Color(0xFF1E293B), - ), - ), - child: Center( - child: Text( - rule['title'].toString().split(' ').first, - style: TextStyle( - fontSize: 12, - fontWeight: isCur ? FontWeight.w800 : FontWeight.w600, - color: isCur ? Colors.black : const Color(0xFF94A3B8), - ), - ), - ), - ), - ), - ); - }), - ), - const SizedBox(height: 16), - - // Rule Details Card - Builder(builder: (ctx) { - final rule = _tajweedRules[_selectedRuleIndex]; - final examples = (rule['examples'] as List); - final accentColor = rule['accentColor'] as Color; - + const SizedBox(height: 8), + ..._sharesResults.map((share) { return Container( - padding: const EdgeInsets.all(18), + margin: const EdgeInsets.only(bottom: 8), + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration( - color: const Color(0xFF0F172A), - borderRadius: BorderRadius.circular(16), - border: Border.all(color: accentColor.withOpacity(0.4)), + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: AppColors.darkCardBorder), ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: Row( children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - rule['title'], - style: TextStyle( - fontSize: 17, - fontWeight: FontWeight.w900, - color: accentColor, - ), - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, vertical: 3), - decoration: BoxDecoration( - color: accentColor.withOpacity(0.2), - borderRadius: BorderRadius.circular(6), - ), - child: const Text( - 'تجويد وزاري', - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w700, - color: Colors.white), - ), - ), - ], - ), - const SizedBox(height: 10), - Text( - rule['definition'], - style: const TextStyle( - fontSize: 13, color: Color(0xFFCBD5E1), height: 1.5), - ), - const SizedBox(height: 12), - - // Letters Box - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: const Color(0xFF1E293B), - borderRadius: BorderRadius.circular(10), - ), - child: Row( + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const Icon(CupertinoIcons.textformat, - color: Color(0xFFFBBF24), size: 18), - const SizedBox(width: 8), - Expanded( - child: Text( - 'الحروف: ${rule['letters']}', - style: const TextStyle( - fontSize: 12.5, - fontWeight: FontWeight.w700, - color: Colors.white), - ), + Text( + share['heir'] ?? '', + style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 13), + ), + Text( + '${share['shareRatio']} • ${share['evidence']}', + style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11.5), ), ], ), ), - const SizedBox(height: 16), - - const Text( - 'أمثلة قرآنية مقررة وطريقة النطق:', - style: TextStyle( - fontSize: 13, - fontWeight: FontWeight.w700, - color: Colors.white), + Text( + '${(share['amount'] as double).toStringAsFixed(0)} د.أ', + style: const TextStyle(color: AppColors.saqelCyan, fontWeight: FontWeight.w900, fontSize: 14), ), - const SizedBox(height: 8), - - ...examples.map((ex) => Container( - margin: const EdgeInsets.only(bottom: 8), - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: const Color(0xFF161F30), - borderRadius: BorderRadius.circular(8), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - ex['text'], - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w900, - color: Color(0xFFFEF08A), - ), - ), - Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - ex['highlight'], - style: TextStyle( - fontSize: 11.5, - fontWeight: FontWeight.w700, - color: accentColor), - ), - Text( - 'سورة ${ex['surah']}', - style: const TextStyle( - fontSize: 10.5, color: Color(0xFF64748B)), - ), - ], - ), - ], - ), - )), ], ), ); diff --git a/apps/student_app/lib/presentation/screens/curriculum/subject_hub_screen.dart b/apps/student_app/lib/presentation/screens/curriculum/subject_hub_screen.dart index eb10bdd..21adb23 100644 --- a/apps/student_app/lib/presentation/screens/curriculum/subject_hub_screen.dart +++ b/apps/student_app/lib/presentation/screens/curriculum/subject_hub_screen.dart @@ -15,6 +15,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_theme.dart'; +import '../../../core/utils/app_logger.dart'; import '../../../core/utils/saqel_toast.dart'; import '../../../data/models/subject_model.dart'; import '../../widgets/luxury_widgets.dart'; @@ -30,6 +31,7 @@ import 'islamic_interactive_lab_view.dart'; import 'history_interactive_timeline_view.dart'; import 'math_interactive_lab_view.dart'; import 'english_interactive_lab_view.dart'; +import 'digital_skills_interactive_lab_view.dart'; import '../virtual_labs/labs_gallery_screen.dart'; import '../virtual_labs/labs_registry.dart'; import '../virtual_labs/subject_virtual_labs_view.dart'; @@ -54,6 +56,7 @@ class _SubjectHubScreenState extends State with SingleTickerProviderStateMixin { late TabController _tabController; late Future> _examsFuture; + String _selectedSemester = 'semester_1'; bool get _isMathSubject => widget.subject.id.contains('math') || @@ -101,6 +104,12 @@ class _SubjectHubScreenState extends State (widget.subject.title.contains('تاريخ') && !widget.subject.title.contains('جغرافيا')) || (widget.subject.title.contains('أردن') && !widget.subject.title.contains('جغرافيا')); + bool get _isDigitalSkillsSubject => + widget.subject.id.contains('digital') || + widget.subject.id.contains('computer') || + widget.subject.title.contains('رقمي') || + widget.subject.title.contains('حاسوب'); + bool get _hasLab => Grade10LabsRegistry.bySubjectNormalized(widget.subject.title).isNotEmpty || _isMathSubject || @@ -111,7 +120,8 @@ class _SubjectHubScreenState extends State _isPhysicsSubject || _isArabicSubject || _isIslamicSubject || - _isHistorySubject; + _isHistorySubject || + _isDigitalSkillsSubject; Tab get _dynamicLabTab { final norm = Grade10LabsRegistry.normalizeSubject(widget.subject.title); @@ -133,12 +143,12 @@ class _SubjectHubScreenState extends State } else if (_isIslamicSubject || norm == 'التربية الإسلامية') { return const Tab( icon: Icon(CupertinoIcons.money_dollar_circle_fill, size: 18), - text: 'حاسبة المواريث والتجويد ⚖️', + text: 'معمل المعاملات والتجويد ⚖️', ); } else if (_isHistorySubject || norm == 'تاريخ الأردن' || norm == 'التاريخ') { return const Tab( icon: Icon(CupertinoIcons.time_solid, size: 18), - text: 'الخط الزمني الشامل والتاريخ 🏛️', + text: 'مختبر التاريخ والخط الزمني 🏛️', ); } else if (_isChemistrySubject || norm == 'الكيمياء') { return const Tab( @@ -160,10 +170,10 @@ class _SubjectHubScreenState extends State icon: Icon(CupertinoIcons.chart_bar_square_fill, size: 18), text: 'مختبر الثقافة المالية والجدوى 📊', ); - } else if (norm == 'المهارات الرقمية') { + } else if (_isDigitalSkillsSubject || norm == 'المهارات الرقمية') { return const Tab( icon: Icon(CupertinoIcons.desktopcomputer, size: 18), - text: 'مختبر الخوارزميات والبرمجة 💻', + text: 'معمل البيانات والذكاء الاصطناعي 💻', ); } else if (norm == 'الجغرافيا') { return const Tab( @@ -204,10 +214,13 @@ class _SubjectHubScreenState extends State specializedTitle = 'شجرة الإعراب 📜'; } else if (_isIslamicSubject || norm == 'التربية الإسلامية') { specializedTool = const IslamicInteractiveLabView(); - specializedTitle = 'حاسبة المواريث ⚖️'; + specializedTitle = 'معمل المعاملات والتجويد ⚖️'; } else if (_isHistorySubject || norm == 'تاريخ الأردن' || norm == 'التاريخ') { specializedTool = const HistoryInteractiveTimelineView(); - specializedTitle = 'الخط الزمني الشامل 🏛️'; + specializedTitle = 'الخط الزمني الشامل والتاريخ 🏛️'; + } else if (_isDigitalSkillsSubject || norm == 'المهارات الرقمية') { + specializedTool = const DigitalSkillsInteractiveLabView(); + specializedTitle = 'معمل البيانات والذكاء 💻'; } else if (_isChemistrySubject || norm == 'الكيمياء') { specializedTool = const ChemistryInteractiveLabView(); specializedTitle = 'طيف ذرة بور ⚗️'; @@ -227,6 +240,7 @@ class _SubjectHubScreenState extends State primaryColor: widget.subject.primaryColor, specializedToolWidget: specializedTool, specializedToolTitle: specializedTitle, + selectedSemester: _selectedSemester, ); } @@ -310,9 +324,115 @@ class _SubjectHubScreenState extends State ); } + /// Semester Switcher Component for Multi-Semester Subjects + Widget _buildSemesterSelector() { + final s1Count = widget.subject.semester1Units.length; + final s2Count = widget.subject.semester2Units.length; + + return Container( + margin: const EdgeInsets.fromLTRB(18, 14, 18, 6), + padding: const EdgeInsets.all(4), + decoration: BoxDecoration( + color: AppColors.darkSurface, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: AppColors.darkCardBorder), + ), + child: Row( + children: [ + Expanded( + child: _buildSemesterSegment( + key: 'semester_1', + title: 'الفصل الدراسي الأول', + count: s1Count, + isSelected: _selectedSemester == 'semester_1', + ), + ), + const SizedBox(width: 6), + Expanded( + child: _buildSemesterSegment( + key: 'semester_2', + title: 'الفصل الدراسي الثاني', + count: s2Count, + isSelected: _selectedSemester == 'semester_2', + ), + ), + ], + ), + ); + } + + Widget _buildSemesterSegment({ + required String key, + required String title, + required int count, + required bool isSelected, + }) { + final color = widget.subject.primaryColor; + return GestureDetector( + onTap: () { + if (_selectedSemester != key) { + setState(() { + _selectedSemester = key; + }); + } + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 250), + padding: const EdgeInsets.symmetric(vertical: 9, horizontal: 12), + decoration: BoxDecoration( + color: isSelected ? color.withValues(alpha: 0.18) : Colors.transparent, + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: isSelected ? color.withValues(alpha: 0.6) : Colors.transparent, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + isSelected ? CupertinoIcons.checkmark_seal_fill : CupertinoIcons.calendar, + size: 14, + color: isSelected ? color : AppColors.textSecondaryDark, + ), + const SizedBox(width: 6), + Text( + title, + style: TextStyle( + color: isSelected ? Colors.white : AppColors.textSecondaryDark, + fontSize: 12.5, + fontWeight: isSelected ? FontWeight.w800 : FontWeight.w500, + ), + ), + if (count > 0) ...[ + const SizedBox(width: 6), + Container( + padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1.5), + decoration: BoxDecoration( + color: isSelected ? color.withValues(alpha: 0.3) : AppColors.darkCardBorder, + borderRadius: BorderRadius.circular(10), + ), + child: Text( + '$count وحدات', + style: TextStyle( + color: isSelected ? color : AppColors.textSecondaryDark, + fontSize: 10, + fontWeight: FontWeight.w700, + ), + ), + ), + ], + ], + ), + ), + ); + } + /// Tab 1: Interactive Video Lessons by Unit Widget _buildLessonsTab(BuildContext context) { - final units = widget.subject.units; + final hasMultipleSemesters = widget.subject.semester1Units.isNotEmpty && widget.subject.semester2Units.isNotEmpty; + final units = hasMultipleSemesters + ? widget.subject.unitsForSemester(_selectedSemester) + : widget.subject.units; if (units.isEmpty) { return Center( @@ -330,11 +450,15 @@ class _SubjectHubScreenState extends State ); } - return ListView.builder( - padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 20), - itemCount: units.length, - itemBuilder: (context, uIdx) { - final unit = units[uIdx]; + return Column( + children: [ + if (hasMultipleSemesters) _buildSemesterSelector(), + Expanded( + child: ListView.builder( + padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14), + itemCount: units.length, + itemBuilder: (context, uIdx) { + final unit = units[uIdx]; return Container( margin: const EdgeInsets.only(bottom: 20), @@ -380,6 +504,9 @@ class _SubjectHubScreenState extends State lessonTitle: lesson.title, lessonId: lesson.id, curriculumLessonId: lesson.curriculumLessonId, + filePath: lesson.markdownFilePath, + unitKey: unit.id, + semesterKey: _selectedSemester, ); return Container( margin: const EdgeInsets.symmetric(horizontal: 14, vertical: 6), @@ -455,6 +582,13 @@ class _SubjectHubScreenState extends State size: 16, ), onTap: () { + AppLogger.event('LessonTileTapped', details: { + 'subject': widget.subject.title, + 'lessonTitle': lesson.title, + 'lessonId': lesson.id, + 'hasVideo': hasVid, + 'curriculumLessonId': lesson.curriculumLessonId, + }, tag: 'SUBJECT_HUB'); _showLessonVideoSelector(context, lesson); }, ), @@ -463,7 +597,15 @@ class _SubjectHubScreenState extends State padding: const EdgeInsets.fromLTRB(12, 0, 12, 6), child: InkWell( borderRadius: BorderRadius.circular(8), - onTap: () => Grade10LabsRegistry.openLab(context, lab), + onTap: () { + AppLogger.event('OpenLessonLab', details: { + 'subject': widget.subject.title, + 'lesson': lesson.title, + 'lab': lab.lessonAr, + 'curriculumId': lab.identity.curriculumLessonId, + }, tag: 'SUBJECT_HUB'); + Grade10LabsRegistry.openLab(context, lab); + }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), decoration: BoxDecoration( @@ -476,14 +618,42 @@ class _SubjectHubScreenState extends State const Icon(CupertinoIcons.lab_flask_solid, color: AppColors.saqelCyan, size: 14), const SizedBox(width: 6), Expanded( - child: Text( - 'المختبر الافتراضي: ${lab.lessonAr}', - style: const TextStyle( - color: Colors.white, - fontSize: 11.5, - fontWeight: FontWeight.w700, - ), - overflow: TextOverflow.ellipsis, + child: Row( + children: [ + Flexible( + child: Text( + 'المختبر: ${lab.lessonAr}', + style: const TextStyle( + color: Colors.white, + fontSize: 11.5, + fontWeight: FontWeight.w700, + ), + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(width: 6), + Container( + padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 1.5), + decoration: BoxDecoration( + color: lab.identity.isPublished + ? AppColors.teacherEmerald.withAlpha(35) + : AppColors.saqelCyan.withAlpha(30), + borderRadius: BorderRadius.circular(4), + ), + child: Text( + lab.identity.isPublished + ? 'معتمد 🟢' + : (lab.identity.isBound ? 'جاهز للاستخدام 🟢' : 'معاينة 🔵'), + style: TextStyle( + color: lab.identity.isPublished + ? AppColors.teacherEmerald + : AppColors.saqelCyan, + fontSize: 9.5, + fontWeight: FontWeight.w800, + ), + ), + ), + ], ), ), Container( @@ -513,62 +683,32 @@ class _SubjectHubScreenState extends State ), ), ) - else if (_hasLab) + else Padding( padding: const EdgeInsets.fromLTRB(12, 0, 12, 6), - child: InkWell( - borderRadius: BorderRadius.circular(8), - onTap: () { - if (_tabController.length > 1) { - _tabController.animateTo(1); - } - }, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), - decoration: BoxDecoration( - color: widget.subject.primaryColor.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(8), - border: Border.all(color: widget.subject.primaryColor.withValues(alpha: 0.25)), - ), - child: Row( - children: [ - Icon(CupertinoIcons.lab_flask_solid, color: widget.subject.primaryColor, size: 14), - const SizedBox(width: 6), - Expanded( - child: Text( - 'مختبر ${widget.subject.title} التفاعلي المعتمد', - style: const TextStyle( - color: Colors.white, - fontSize: 11.5, - fontWeight: FontWeight.w700, - ), - overflow: TextOverflow.ellipsis, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: Colors.white.withAlpha(6), + borderRadius: BorderRadius.circular(8), + border: Border.all(color: Colors.white.withAlpha(12)), + ), + child: const Row( + children: [ + Icon(CupertinoIcons.lab_flask, color: Colors.white38, size: 13), + SizedBox(width: 6), + Expanded( + child: Text( + 'المختبر الافتراضي: قيد الإعداد الأكاديمي ⚪', + style: TextStyle( + color: Colors.white54, + fontSize: 11, + fontWeight: FontWeight.w600, ), + overflow: TextOverflow.ellipsis, ), - Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), - decoration: BoxDecoration( - color: widget.subject.primaryColor.withValues(alpha: 0.2), - borderRadius: BorderRadius.circular(5), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'انتقل للمختبر', - style: TextStyle( - color: widget.subject.primaryColor, - fontSize: 10.5, - fontWeight: FontWeight.w800, - ), - ), - const SizedBox(width: 3), - Icon(CupertinoIcons.arrow_left, color: widget.subject.primaryColor, size: 10), - ], - ), - ), - ], - ), + ), + ], ), ), ), @@ -634,6 +774,9 @@ class _SubjectHubScreenState extends State ), ); }, + ), + ), + ], ); } @@ -1098,6 +1241,7 @@ class _SubjectHubScreenState extends State lessonTitle: lesson.title, lessonId: lesson.id, curriculumLessonId: lesson.curriculumLessonId, + filePath: lesson.markdownFilePath, ); showModalBottomSheet( diff --git a/apps/student_app/lib/presentation/screens/player/socratic_video_player_screen.dart b/apps/student_app/lib/presentation/screens/player/socratic_video_player_screen.dart index 4e848e4..5d26acb 100644 --- a/apps/student_app/lib/presentation/screens/player/socratic_video_player_screen.dart +++ b/apps/student_app/lib/presentation/screens/player/socratic_video_player_screen.dart @@ -19,6 +19,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_theme.dart'; +import '../../../core/utils/app_logger.dart'; import '../../../core/utils/saqel_toast.dart'; import '../../../core/config/app_config.dart'; import '../../../core/services/storage_service.dart'; @@ -65,34 +66,77 @@ class _SocraticVideoPlayerScreenState extends State w bool _showChalkboardMode = false; final FlutterTts _tts = FlutterTts(); bool _isSpeakingChalkboard = false; + int _chalkboardStep = 0; + + bool _isDisposed = false; + bool _isMuted = false; void _revealVideoControls() { - if (!mounted) return; + if (!mounted || _isDisposed) return; setState(() => _showVideoControls = true); _controlsHideTimer?.cancel(); - _controlsHideTimer = Timer(const Duration(seconds: 3), () { - if (mounted) setState(() => _showVideoControls = false); + _controlsHideTimer = Timer(const Duration(seconds: 4), () { + if (mounted && !_isDisposed) setState(() => _showVideoControls = false); }); } + void _stopAllAudioAndVideo() { + AppLogger.log('🛑 [AUDIO_VIDEO] Stopping all audio, video, and speech synthesis...', tag: 'VIDEO_LIFECYCLE'); + try { + _tts.stop(); + } catch (e) { + AppLogger.error('Error stopping TTS', error: e, tag: 'VIDEO_LIFECYCLE'); + } + if (_videoController != null) { + try { + _videoController!.pause(); + _videoController!.setVolume(0.0); + _videoController!.dispose(); + } catch (e) { + AppLogger.error('Error disposing video controller', error: e, tag: 'VIDEO_LIFECYCLE'); + } + _videoController = null; + _isVideoInitialized = false; + } + } + Future _initPlayer(String videoUrl, int resumePos, bool shouldPlay) async { + if (_isDisposed || !mounted) return; + + AppLogger.log('🎬 [VIDEO_INIT_REQUEST] Received raw video URL for lesson: "${widget.lesson.title}"\n' + ' • Raw URL: "$videoUrl"\n' + ' • Resume Position: ${resumePos}s\n' + ' • Should AutoPlay: $shouldPlay\n' + ' • Curriculum Lesson ID: ${widget.lesson.curriculumLessonId}', + tag: 'VIDEO_PLAYER'); + if (videoUrl.isEmpty) { - setState(() => _videoInitError = 'لا يوجد رابط فيديو حقيقي لهذا الدرس.'); + AppLogger.error('Video URL is empty for lesson: ${widget.lesson.title}', tag: 'VIDEO_PLAYER'); + if (mounted && !_isDisposed) { + setState(() => _videoInitError = 'لم يتم ربط فيديو معتمد لهذا الدرس بعد.'); + } return; } - final token = await StorageService().getToken(); - String finalVideoUrl = videoUrl; + String finalVideoUrl = videoUrl.trim(); - // IMPORTANT: Check whether the URL points to our own Saqel API or to an external CDN (BunnyCDN, Cloudflare R2). - // NEVER send the Saqel user JWT Bearer token to external CDNs! CDNs reject unauthorized Bearer headers - // with HTTP 403 Forbidden ("Resource is protected by access token"). + // 1. Resolve relative API endpoints to absolute base URL + if (finalVideoUrl.startsWith('/')) { + final base = AppConfig.baseUrl.replaceAll(RegExp(r'/+$'), ''); + finalVideoUrl = '$base$finalVideoUrl'; + AppLogger.log('🔗 [URL_RESOLVE] Resolved relative path to absolute: $finalVideoUrl', tag: 'VIDEO_PLAYER'); + } + + // 2. Attach authentication token for our backend streaming endpoints + final token = await StorageService().getToken(); final isOurBackend = finalVideoUrl.contains('/api/videos/') || finalVideoUrl.startsWith(AppConfig.baseUrl) || finalVideoUrl.contains('localhost') || finalVideoUrl.contains('127.0.0.1'); - final headers = {}; + final headers = { + 'User-Agent': 'SaqelEdTech/2.0 (macOS; Grade10-Production)', + }; if (isOurBackend && token != null && token.isNotEmpty) { headers['Authorization'] = 'Bearer $token'; if (!finalVideoUrl.contains('token=')) { @@ -101,92 +145,102 @@ class _SocraticVideoPlayerScreenState extends State w } } - _videoController?.dispose(); - _videoController = VideoPlayerController.networkUrl( + AppLogger.log('🚀 [VIDEO_CONNECT] Connecting to real video stream: $finalVideoUrl', tag: 'VIDEO_PLAYER'); + + // Cleanly stop any existing controller before assigning a new one + _stopAllAudioAndVideo(); + + final controller = VideoPlayerController.networkUrl( Uri.parse(finalVideoUrl), httpHeaders: headers, - ) - ..initialize().then((_) { - if (mounted) { - setState(() { - _isVideoInitialized = true; - _videoInitError = null; - }); - _revealVideoControls(); - if (resumePos > 3) { - _videoController!.seekTo(Duration(seconds: resumePos)); - if (context.mounted) { - SaqelToast.showInfo( - context, - 'تم استئناف المشاهدة من الدقيقة ${_formatTime(resumePos)} ⏱️', - title: 'استئناف المشاهدة', - ); - } - } - if (shouldPlay) { - _videoController!.play(); - } - } - }).catchError((err) { - final errStr = err.toString(); - // Resilient Fallback: If 403 Forbidden or Access Token error occurred on an external URL with token query params, - // retry once without the token parameters (e.g. public pull zones or direct CDNs). - if ((errStr.contains('403') || errStr.contains('token') || errStr.contains('permission')) && - finalVideoUrl.contains('?')) { - final strippedUrl = finalVideoUrl.split('?').first; - if (strippedUrl != finalVideoUrl && strippedUrl.isNotEmpty) { - _videoController?.dispose(); - _videoController = VideoPlayerController.networkUrl( - Uri.parse(strippedUrl), - httpHeaders: const {}, - )..initialize().then((_) { - if (mounted) { - setState(() { - _isVideoInitialized = true; - _videoInitError = null; - }); - _revealVideoControls(); - if (shouldPlay) _videoController!.play(); - } - }).catchError((retryErr) { - if (mounted) { - setState(() { - _isVideoInitialized = false; - _videoInitError = 'تعذر تشغيل بث الفيديو من المصدر السحابي، يمكنك استخدام السبورة التفاعلية للشرح.'; - }); - } - }); - return; - } - } + ); + _videoController = controller; - if (mounted) { - setState(() { - _isVideoInitialized = false; - _videoInitError = 'تعذر تحميل بث الفيديو المباشر؛ انقر لإعادة المحاولة ($err)'; - }); - } + controller.initialize().then((_) { + if (_isDisposed || !mounted) { + AppLogger.log('🛑 [LIFECYCLE_ABORT] Screen disposed before video completed loading. Silencing controller.', tag: 'VIDEO_LIFECYCLE'); + controller.pause(); + controller.setVolume(0.0); + controller.dispose(); + return; + } + + AppLogger.log('✅ [VIDEO_READY] Video successfully initialized!\n' + ' • Duration: ${controller.value.duration.inSeconds}s (${_formatTime(controller.value.duration.inSeconds)})\n' + ' • Resolution: ${controller.value.size.width.toInt()}x${controller.value.size.height.toInt()}\n' + ' • Aspect Ratio: ${controller.value.aspectRatio.toStringAsFixed(2)}', + tag: 'VIDEO_PLAYER'); + + setState(() { + _isVideoInitialized = true; + _videoInitError = null; + if (_isMuted) controller.setVolume(0.0); }); + _revealVideoControls(); + + if (resumePos > 3 && resumePos < controller.value.duration.inSeconds) { + controller.seekTo(Duration(seconds: resumePos)); + if (context.mounted) { + SaqelToast.showInfo( + context, + 'تم استئناف المشاهدة من الدقيقة ${_formatTime(resumePos)} ⏱️', + title: 'استئناف المشاهدة', + ); + } + } + + if (shouldPlay && !_isDisposed) { + controller.play(); + AppLogger.log('▶️ [VIDEO_PLAY] Playback started naturally.', tag: 'VIDEO_PLAYER'); + } + }).catchError((err, stack) { + final errStr = err.toString(); + AppLogger.error( + '🚨 [VIDEO_PLAYER_ERROR] Real video playback failed!\n' + ' • Target URL: $finalVideoUrl\n' + ' • Error: $errStr', + error: err, + stackTrace: stack, + tag: 'VIDEO_PLAYER', + ); + + if (mounted && !_isDisposed) { + setState(() { + _isVideoInitialized = false; + final is403 = errStr.contains('403') || errStr.contains('permission') || errStr.contains('-12660'); + final is404 = errStr.contains('404') || errStr.contains('not found'); + + if (is403) { + _videoInitError = 'بث الفيديو السحابي قيد المزامنة على مزود الاستضافة (HTTP 403).\nيمكنك متابعة دراسة الحصة فوراً عبر السبورة السقراطية الذكية بالصوت 🎙️ أو فتح المختبر التفاعلي 🔬.'; + } else if (is404) { + _videoInitError = 'ملف الفيديو غير متوفر حالياً على الخادم (HTTP 404).\nيمكنك متابعة الدرس عبر السبورة السقراطية الذكية بالصوت 🎙️ أو فتح المختبر التفاعلي 🔬.'; + } else { + _videoInitError = 'تعذر تشغيل الفيديو: $errStr'; + } + }); + } + }); } Future _speakConcept(String text) async { - if (text.isEmpty) return; + if (text.isEmpty || _isDisposed || _isMuted) return; setState(() => _isSpeakingChalkboard = true); try { await _tts.setLanguage('ar'); await _tts.setSpeechRate(0.48); _tts.setCompletionHandler(() { - if (mounted) setState(() => _isSpeakingChalkboard = false); + if (mounted && !_isDisposed) setState(() => _isSpeakingChalkboard = false); }); await _tts.speak(text); } catch (_) { - if (mounted) setState(() => _isSpeakingChalkboard = false); + if (mounted && !_isDisposed) setState(() => _isSpeakingChalkboard = false); } } @override void initState() { super.initState(); + AppLogger.log('📱 [SCREEN_ENTER] Opened SocraticVideoPlayerScreen for lesson: ${widget.lesson.title}', tag: 'VIDEO_LIFECYCLE'); context.read().loadLesson(widget.lesson, subject: widget.subject, selectedVideoVersionId: widget.selectedVideoVersionId); // Dynamic Floating Forensic Anti-Piracy Watermark Animation @@ -197,9 +251,17 @@ class _SocraticVideoPlayerScreenState extends State w // Keep the UI clock responsive, but sync progress to the API every 15s. _playbackTicker = Timer.periodic(const Duration(seconds: 1), (timer) { + if (_isDisposed || !mounted) { + timer.cancel(); + return; + } final cubit = context.read(); final state = cubit.state; - if (state is VideoPlaybackReady && _videoController?.value.isInitialized == true) { + if (state is VideoPlaybackReady && + _videoController != null && + _isVideoInitialized && + !_videoController!.value.hasError && + _videoController!.value.isInitialized) { final position = _videoController!.value.position.inSeconds; if (state.activeCheckpoint == null) cubit.updatePosition(position); final duration = _videoController!.value.duration.inSeconds; @@ -214,14 +276,23 @@ class _SocraticVideoPlayerScreenState extends State w }); } + @override + void deactivate() { + AppLogger.log('⏸️ [SCREEN_DEACTIVATE] Deactivating video player screen. Stopping audio & video...', tag: 'VIDEO_LIFECYCLE'); + _isDisposed = true; + _stopAllAudioAndVideo(); + super.deactivate(); + } + @override void dispose() { + AppLogger.log('🧹 [SCREEN_DISPOSE] Disposing video player screen resources.', tag: 'VIDEO_LIFECYCLE'); + _isDisposed = true; context.read().endWatchSession(); - _tts.stop(); + _stopAllAudioAndVideo(); _playbackTicker?.cancel(); _controlsHideTimer?.cancel(); _watermarkController.dispose(); - _videoController?.dispose(); super.dispose(); } @@ -252,6 +323,21 @@ class _SocraticVideoPlayerScreenState extends State w ), centerTitle: false, actions: [ + IconButton( + tooltip: _isMuted ? 'إلغاء كتم الصوت' : 'كتم الصوت', + icon: Icon( + _isMuted ? CupertinoIcons.volume_off : CupertinoIcons.volume_up, + color: _isMuted ? AppColors.guardianAmber : Colors.white, + ), + onPressed: () { + setState(() { + _isMuted = !_isMuted; + _videoController?.setVolume(_isMuted ? 0.0 : 1.0); + if (_isMuted) _tts.stop(); + }); + AppLogger.log('🔊 [AUDIO_TOGGLE] User toggled audio: ${_isMuted ? "MUTED" : "UNMUTED"}', tag: 'VIDEO_PLAYER'); + }, + ), Container( margin: const EdgeInsets.symmetric(vertical: 10, horizontal: 14), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4), @@ -380,13 +466,16 @@ class _SocraticVideoPlayerScreenState extends State w // Video Background Scene Stack( children: [ - if (_videoController != null && _isVideoInitialized) + if (_videoController != null && + _isVideoInitialized && + _videoController!.value.isInitialized && + !_videoController!.value.hasError) Positioned.fill( - child: FittedBox( - fit: BoxFit.cover, - child: SizedBox( - width: _videoController!.value.size.width, - height: _videoController!.value.size.height, + child: Center( + child: AspectRatio( + aspectRatio: _videoController!.value.aspectRatio > 0 + ? _videoController!.value.aspectRatio + : 16 / 9, child: VideoPlayer(_videoController!), ), ), @@ -450,17 +539,45 @@ class _SocraticVideoPlayerScreenState extends State w const SizedBox(height: 14), Wrap( alignment: WrapAlignment.center, - spacing: 10, + spacing: 12, runSpacing: 10, children: [ ElevatedButton.icon( - icon: const Icon(CupertinoIcons.arrow_clockwise, size: 16), - label: const Text('إعادة محاولة البث 🎥'), + icon: const Icon(CupertinoIcons.mic_fill, size: 16, color: Colors.black), + label: const Text('بدء الشرح على السبورة الذكية 🎙️', style: TextStyle(color: Colors.black, fontWeight: FontWeight.w800, fontSize: 13)), style: ElevatedButton.styleFrom( backgroundColor: AppColors.saqelCyan, foregroundColor: Colors.black, - padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 10), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 12), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), + ), + onPressed: () { + setState(() { + _showChalkboardMode = true; + _videoInitError = null; + }); + }, + ), + ElevatedButton.icon( + icon: const Icon(CupertinoIcons.sparkles, size: 16, color: Colors.white), + label: const Text('المختبر التفاعلي للمبحث 🔬', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 13)), + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.appleBlue, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), + ), + onPressed: () { + Navigator.of(context).pop(); + }, + ), + OutlinedButton.icon( + icon: const Icon(CupertinoIcons.arrow_clockwise, size: 16, color: Colors.white70), + label: const Text('إعادة المحاولة 🔄', style: TextStyle(color: Colors.white70)), + style: OutlinedButton.styleFrom( + side: const BorderSide(color: Colors.white24), + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), ), onPressed: () { setState(() { @@ -474,21 +591,6 @@ class _SocraticVideoPlayerScreenState extends State w ); }, ), - OutlinedButton.icon( - icon: const Icon(CupertinoIcons.mic_fill, size: 16, color: AppColors.appleBlue), - label: const Text('السبورة والشرح الصوتي 🎙️', style: TextStyle(color: Colors.white)), - style: OutlinedButton.styleFrom( - side: BorderSide(color: AppColors.appleBlue.withAlpha(150)), - padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 10), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), - ), - onPressed: () { - setState(() { - _showChalkboardMode = true; - _videoInitError = null; - }); - }, - ), ], ), ], @@ -1104,6 +1206,7 @@ class _SocraticVideoPlayerScreenState extends State w ], ), ), + if (isMath) _buildMathStepBoard(), const SizedBox(height: 16), Wrap( alignment: WrapAlignment.center, @@ -1202,6 +1305,143 @@ class _SocraticVideoPlayerScreenState extends State w ), ); } + + Widget _buildMathStepBoard() { + final steps = [ + { + 'title': 'الخطوة 1: صياغة النظام الرياضي', + 'eq1': 'المعادلة الخطية: y = 2x - 2', + 'eq2': 'المعادلة التربيعية: y = x² - 4x + 3', + 'note': 'نظام مكون من معادلة خطية وأخرى تربيعية بمتغيرين (x, y).' + }, + { + 'title': 'الخطوة 2: التعويض ومساواة الطرفين', + 'eq1': 'x² - 4x + 3 = 2x - 2', + 'eq2': 'نعوض قيمة y من المعادلة الخطية في المعادلة التربيعية', + 'note': 'ينتج لدينا معادلة تربيعية بمتغير واحد فقط هو x.' + }, + { + 'title': 'الخطوة 3: التصفير والتجميع في الصورة القياسية', + 'eq1': 'x² - 4x - 2x + 3 + 2 = 0', + 'eq2': 'x² - 6x + 5 = 0', + 'note': 'المعاملات: a = 1, b = -6, c = 5 (المميز Δ = 36 - 20 = 16 > 0 له حلان حقيقيان).' + }, + { + 'title': 'الخطوة 4: التحليل إلى العوامل وإيجاد قيم x', + 'eq1': '(x - 5)(x - 1) = 0', + 'eq2': 'إما x = 5 أو x = 1', + 'note': 'أوجدنا الإحداثي السيني لنقطتي تقاطع المستقيم والقطع المكافئ.' + }, + { + 'title': 'الخطوة 5: التعويض لإيجاد y ومجموعة الحل', + 'eq1': 'عند x = 5: y = 2(5) - 2 = 8 ➔ (5, 8)', + 'eq2': 'عند x = 1: y = 2(1) - 2 = 0 ➔ (1, 0)', + 'note': 'مجموعة حل النظام: {(5, 8), (1, 0)} - تم التحقق بالتعويض.' + }, + ]; + + final cur = steps[_chalkboardStep.clamp(0, steps.length - 1)]; + + return Container( + constraints: const BoxConstraints(maxWidth: 620), + margin: const EdgeInsets.symmetric(vertical: 12), + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: const Color(0xFF0A1926), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColors.saqelCyan.withAlpha(120), width: 1.5), + boxShadow: [ + BoxShadow(color: AppColors.saqelCyan.withAlpha(20), blurRadius: 16, offset: const Offset(0, 4)), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: AppColors.saqelCyan.withAlpha(40), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + 'خطوة ${_chalkboardStep + 1} من ${steps.length}', + style: const TextStyle(color: AppColors.saqelCyan, fontSize: 12, fontWeight: FontWeight.w700), + ), + ), + Text( + cur['title']!, + style: const TextStyle(color: Colors.white, fontSize: 13.5, fontWeight: FontWeight.w800), + ), + ], + ), + const SizedBox(height: 12), + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.black.withAlpha(150), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.white10), + ), + child: Column( + children: [ + Text( + cur['eq1']!, + textAlign: TextAlign.center, + style: const TextStyle(color: Color(0xFFFFD60A), fontSize: 15, fontWeight: FontWeight.w800, letterSpacing: 0.5), + ), + if (cur['eq2']!.isNotEmpty) ...[ + const SizedBox(height: 6), + Text( + cur['eq2']!, + textAlign: TextAlign.center, + style: const TextStyle(color: Color(0xFF30D158), fontSize: 14.5, fontWeight: FontWeight.w700), + ), + ], + ], + ), + ), + const SizedBox(height: 8), + Text( + '💡 ${cur['note']!}', + style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 12, height: 1.4), + ), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ElevatedButton.icon( + icon: const Icon(CupertinoIcons.arrow_right, size: 14), + label: const Text('السابقة'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white12, + foregroundColor: Colors.white, + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), + ), + onPressed: _chalkboardStep > 0 + ? () => setState(() => _chalkboardStep--) + : null, + ), + ElevatedButton.icon( + icon: const Icon(CupertinoIcons.arrow_left, size: 14), + label: const Text('التالية'), + style: ElevatedButton.styleFrom( + backgroundColor: AppColors.saqelCyan, + foregroundColor: Colors.black, + padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 8), + ), + onPressed: _chalkboardStep < steps.length - 1 + ? () => setState(() => _chalkboardStep++) + : null, + ), + ], + ), + ], + ), + ); + } } class _GridPatternPainter extends CustomPainter { diff --git a/apps/student_app/lib/presentation/screens/virtual_labs/history_digital_labs.dart b/apps/student_app/lib/presentation/screens/virtual_labs/history_digital_labs.dart index 5e07e16..355032c 100644 --- a/apps/student_app/lib/presentation/screens/virtual_labs/history_digital_labs.dart +++ b/apps/student_app/lib/presentation/screens/virtual_labs/history_digital_labs.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:math' as math; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -28,51 +29,82 @@ class HistoryJordanChronologyLabView extends StatefulWidget { class _HistoryJordanChronologyLabViewState extends State { - int era = 2; + int era = 0; double progress = 1.0; + // المحطات التاريخية الرسمية لمنهاج الصف العاشر (الوحدات 1-4) static const milestones = [ { - 'y': '1916', - 't': 'الثورة العربية الكبرى', + 'y': '550 ق.م', + 't': 'تأسيس الدولة الأخمينية الفارسية', + 'unit': 'الوحدة الأولى: الإمبراطورية الفارسية', 'd': - 'انطلاق الثورة بقيادة الشريف الحسين بن علي ضد الحكم العثماني؛ تشكل الوعي العربي الحديث وخط سير الجيوش نحو الشام.', - 'c': Color(0xFFFF9F0A) + 'تأسيس الملك كورش الأكبر للإمبراطورية الأخمينية الفارسية، وتوحيد الميديين والفرس، وشق الطريق الملكي (2700 كم) من ساردس إلى سوسة وبرسبوليس.', + 'c': Color(0xFFFFD60A), + 'icon': '🏛️', }, { - 'y': '1921', - 't': 'تأسيس إمارة شرق الأردن', + 'y': '224 م', + 't': 'تأسيس الدولة الساسانية (معركة هرمزجان)', + 'unit': 'الوحدة الأولى: الإمبراطورية الفارسية', 'd': - 'وصول الأمير عبد الله الأول إلى عمان (2 آذار) وتشكيل أول حكومة (11 نيسان) برئاسة رشيد طليع.', - 'c': Color(0xFF8B5CF6) + 'تتويج أردشير الأول شاهنشاهاً بعد معركة هرمزجان (224م) وإسقاط البارثيين، وإعلان طيسفون (المدائن) عاصمة جديدة وبناء إيوان كسرى ومجمع جنديسابور.', + 'c': Color(0xFFFF9F0A), + 'icon': '👑', }, { - 'y': '1928', - 't': 'القانون الأساسي والمعاهدة', + 'y': '651 م', + 't': 'معركة نهاوند ونهاية الساسانيين', + 'unit': 'الوحدة الأولى: الإمبراطورية الفارسية', 'd': - 'أول وثيقة دستورية للإمارة (القانون الأساسي) وتنظيم العلاقة مع بريطانيا والمؤتمر الوطني الأول.', - 'c': Color(0xFF60A5FA) + 'معركة نهاوند (فتح الفتوح - 642م/651م) وسقوط يزدجرد الثالث، ودخول بلاد فارس بالكامل تحت راية الحضارة الإسلامية.', + 'c': Color(0xFF30D158), + 'icon': '⚔️', }, { - 'y': '1946', - 't': 'الاستقلال 25 أيار', + 'y': '1299 م', + 't': 'تأسيس الدولة العثمانية', + 'unit': 'الوحدة الثانية: الدولة العثمانية', 'd': - 'إعلان الاستقلال التام ومبايعة الملك المؤسس ملكاً دستورياً وتحول الاسم إلى المملكة الأردنية الهاشمية.', - 'c': Color(0xFF30D158) + 'تأسيس الأمير عثمان بن أرطغرل للإمارة العثمانية في سوغوت شمال غرب الأناضول، وتوسعها التدريجي في آسيا الصغرى والبلقان.', + 'c': Color(0xFF60A5FA), + 'icon': '🏹', }, { - 'y': '1952', - 't': 'دستور 1952', + 'y': '1453 م', + 't': 'فتح القسطنطينية (إسطنبول)', + 'unit': 'الوحدة الثانية: الدولة العثمانية', 'd': - 'صدور الدستور الأردني الحالي في عهد الملك طلال؛ ترسيخ الملكية الدستورية والحقوق والحريات.', - 'c': Color(0xFF00F5D4) + 'السلطان محمد الثاني (الفاتح) يفتح القسطنطينية ويُنهي الإمبراطورية البيزنطية ويجعلها عاصمة عثمانية، مستخدماً مدافع أوربان ونقل السفن براً.', + 'c': Color(0xFF8B5CF6), + 'icon': '🏰', }, { - 'y': '1994', - 't': 'معاهدة وادي عربة', + 'y': '1760 م', + 't': 'انطلاق الثورة الصناعية', + 'unit': 'الوحدة الثالثة: ثورات غيّرت العالم', 'd': - 'معاهدة السلام الأردنية–الإسرائيلية؛ ملفات الحدود والمياه والأمن — تُدرس من الكتاب المقرر حصراً.', - 'c': Color(0xFF0071E3) + 'اختراع الآلة البخارية لجيمس واط وتطوير صناعة الغزل والنسيج في بريطانيا، والتحول الجذري من الإنتاج الزراعي واليدوي إلى المصانع والآلات.', + 'c': Color(0xFF00F5D4), + 'icon': '⚙️', + }, + { + 'y': '1789 م', + 't': 'الثورة الفرنسية الكبرى', + 'unit': 'الوحدة الثالثة: ثورات غيّرت العالم', + 'd': + 'سقوط سجن الباستيل في باريس، وإعلان حقوق الإنسان والمواطن، وإلغاء الإقطاع والمَلكية المطلقة، وإرساء مبادئ الحرية والعدالة والمساواة.', + 'c': Color(0xFFFF375F), + 'icon': '📜', + }, + { + 'y': '1805 م', + 't': 'عصر محمد علي باشا وبناء الدولة الحديثة', + 'unit': 'الوحدة الرابعة: شخصيات تاريخية أثرت في العالم', + 'd': + 'تولي محمد علي باشا حكم مصر عام 1805م، وبناء الجيش والأسطول الحديث، وإرسال البعثات التعليمية (رفاعة الطهطاوي)، وإنشاء مدرسة الألسن والقناطر الخيرية.', + 'c': Color(0xFF10B981), + 'icon': '🌟', }, ]; @@ -80,176 +112,257 @@ class _HistoryJordanChronologyLabViewState Widget build(BuildContext context) { final m = milestones[era]; return SaqelLabScaffold( - titleAr: 'التسلسل الزمني لتاريخ الأردن', - subtitleAr: 'اسحب المؤشر عبر المحطات — خريطة طريق + بطاقة الحدث', + titleAr: 'الخط الزمني الشامل لحضارات العالم — الصف العاشر', + subtitleAr: + 'تسلسل منهجي يربط بين: الإمبراطورية الفارسية • الدولة العثمانية • الثورات الكبرى • الشخصيات المؤثرة', identity: kHistoryJordanChronologyToolIdentity, onCheckpointTriggered: widget.onCheckpointTriggered, checkpointQuestion: - 'اليوم الوطني لاستقلال المملكة الأردنية الهاشمية هو …', + 'المعركة الفاصلة التي خاضها أردشير الأول وأسفرت عن تأسيس الدولة الساسانية عام 224م هي …', checkpointOptions: const [ - '25 أيار 1946', - '2 آذار 1921', - '16 نيسان 1928', - '10 حزيران 1916' + 'معركة هرمزجان', + 'معركة نهاوند', + 'معركة الريدانية', + 'معركة القادسية' ], checkpointCorrectIdx: 0, telemetry: [ - LabPill('${m['y']} • ${m['t']}', color: m['c'] as Color), + LabPill('${m['icon']} ${m['y']}', color: m['c'] as Color), + LabPill('${m['t']}', color: Colors.white), + LabPill('${m['unit']}', color: AppColors.saqelCyan), ], canvas: CustomPaint( - painter: _ChronoPainter(index: era, progress: progress), + painter: _WorldCivilizationsChronoPainter(index: era, milestones: milestones), child: Container(), ), controls: [ LabSlider( - label: 'المحطة التاريخية', - value: era.toDouble(), - min: 0, - max: 5, - display: '${milestones[era]['y']}', - onChanged: (v) => setState(() => era = v.round())), + label: 'المحطة التاريخية', + value: era.toDouble(), + min: 0, + max: (milestones.length - 1).toDouble(), + display: '${milestones[era]['y']}', + onChanged: (v) => setState(() => era = v.round()), + ), const SizedBox(height: 6), SizedBox( - height: 40, + height: 42, child: ListView.separated( scrollDirection: Axis.horizontal, itemCount: milestones.length, separatorBuilder: (_, __) => const SizedBox(width: 6), itemBuilder: (ctx, i) { final sel = i == era; + final item = milestones[i]; return GestureDetector( onTap: () => setState(() => era = i), child: Container( - padding: - const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( color: sel - ? (milestones[i]['c'] as Color) + ? (item['c'] as Color) : Colors.white.withValues(alpha: 0.06), borderRadius: BorderRadius.circular(10), + border: Border.all( + color: sel ? Colors.white : Colors.white12, + width: sel ? 1.4 : 1.0, + ), ), alignment: Alignment.center, - child: Text(milestones[i]['y'] as String, - style: TextStyle( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(item['icon'] as String, style: const TextStyle(fontSize: 12)), + const SizedBox(width: 5), + Text( + item['y'] as String, + style: TextStyle( color: sel ? Colors.black : Colors.white70, fontWeight: FontWeight.w900, - fontSize: 12)), + fontSize: 11.5, + ), + ), + ], + ), ), ); }, ), ), - const SizedBox(height: 8), + const SizedBox(height: 10), LabFormulaCard( - title: '${m['y']} — ${m['t']}', - body: m['d'] as String, + title: '${m['icon']} ${m['y']} — ${m['t']}', + body: '${m['d']}\n\n• المنهج الوزاري: ${m['unit']}', accent: m['c'] as Color, ), ], footerNote: - 'عرض صفي موجز؛ التفاصيل الدقيقة والوثائق من الكتاب المدرسي المقرر.', + 'منهاج وزارة التربية والتعليم الأردنية • كتاب التاريخ للصف العاشر • الوحدات الأربع المقررة.', ); } } -class _ChronoPainter extends CustomPainter { +class _WorldCivilizationsChronoPainter extends CustomPainter { final int index; - final double progress; - _ChronoPainter({required this.index, required this.progress}); + final List> milestones; + + _WorldCivilizationsChronoPainter({ + required this.index, + required this.milestones, + }); @override void paint(Canvas c, Size s) { - c.drawRect(Offset.zero & s, Paint()..color = const Color(0xFF0A1220)); - // simplified Jordan outline (schematic polygon, clearly stylised) - final mapRect = Rect.fromLTWH(18, 18, s.width * 0.40, s.height - 90); - c.drawRRect(RRect.fromRectAndRadius(mapRect, const Radius.circular(14)), - Paint()..color = const Color(0xFF0F2036)); - final poly = [ - const Offset(0.55, 0.08), - const Offset(0.80, 0.18), - const Offset(0.86, 0.42), - const Offset(0.70, 0.62), - const Offset(0.72, 0.88), - const Offset(0.40, 0.92), - const Offset(0.22, 0.66), - const Offset(0.30, 0.34), - ] - .map((e) => Offset(mapRect.left + e.dx * mapRect.width, - mapRect.top + e.dy * mapRect.height)) - .toList(); - c.drawPath(Path()..addPolygon(poly, true), - Paint()..color = const Color(0xFF1E3A5F)); - // revolt route dashed - final route = Path() - ..moveTo(mapRect.left + mapRect.width * 0.72, - mapRect.top + mapRect.height * 0.88) - ..quadraticBezierTo( - mapRect.left + mapRect.width * 0.5, - mapRect.top + mapRect.height * 0.5, - mapRect.left + mapRect.width * 0.6, - mapRect.top + mapRect.height * 0.15); - _dashed(c, route, const Color(0xFFFF9F0A)); - final dotT = (index / 5).clamp(0.0, 1.0); - final dotPos = _pointOn(route, dotT, mapRect); - c.drawCircle(dotPos, 7, Paint()..color = const Color(0xFFFF375F)); - c.drawCircle( - dotPos, - 7, - Paint() - ..color = Colors.white - ..style = PaintingStyle.stroke - ..strokeWidth = 1.6); - // timeline rail (right side) - final rx = s.width * 0.52, ry0 = 30.0, ry1 = s.height - 40; - c.drawLine( - Offset(rx, ry0), - Offset(rx, ry1), - Paint() - ..color = Colors.white24 - ..strokeWidth = 3); - for (int i = 0; i < 6; i++) { - final y = ry0 + i * ((ry1 - ry0) / 5); - final sel = i == index; - c.drawCircle(Offset(rx, y), sel ? 11 : 7, - Paint()..color = sel ? const Color(0xFF00F5D4) : Colors.white24); + final w = s.width; + final h = s.height; + + // Dark parchment starry background + final bgShader = const RadialGradient( + center: Alignment(0.0, -0.3), + colors: [Color(0xFF0F1B2C), Color(0xFF060B12)], + radius: 1.0, + ).createShader(Rect.fromLTWH(0, 0, w, h)); + c.drawRect(Rect.fromLTWH(0, 0, w, h), Paint()..shader = bgShader); + + // Subtle historical coordinate lines + final gridPaint = Paint() + ..color = Colors.white.withValues(alpha: 0.03) + ..strokeWidth = 1.0; + for (double x = 0; x < w; x += 35) { + c.drawLine(Offset(x, 0), Offset(x, h), gridPaint); + } + + // 4 Era Color Zones (أشرطة العصور التاريخية الأربعة) + final eraWidth = w / 4; + final eraLabels = [ + 'العصر الفارسي القديم\n(550 ق.م - 651 م)', + 'العصر العثماني والفتوح\n(1299 م - 1923 م)', + 'عصر الثورات الكبرى\n(1760 م - 1916 م)', + 'بناء الدولة الحديثة\n(1805 م - 1938 م)', + ]; + final eraColors = [ + const Color(0xFFFFD60A), + const Color(0xFF60A5FA), + const Color(0xFFFF375F), + const Color(0xFF10B981), + ]; + + for (int i = 0; i < 4; i++) { + final x = i * eraWidth; + final zonePaint = Paint() + ..color = eraColors[i].withValues(alpha: 0.04) + ..style = PaintingStyle.fill; + c.drawRect(Rect.fromLTWH(x, 10, eraWidth - 2, h - 30), zonePaint); + + final dividerPaint = Paint() + ..color = Colors.white.withValues(alpha: 0.08) + ..strokeWidth = 1.0; + c.drawLine(Offset(x + eraWidth - 2, 10), Offset(x + eraWidth - 2, h - 20), dividerPaint); + final tp = TextPainter( text: TextSpan( - text: ['1916', '1921', '1928', '1946', '1952', '1994'][i], - style: TextStyle( - color: sel ? Colors.white : Colors.white54, - fontSize: 11, - fontWeight: FontWeight.w800)), - textDirection: TextDirection.ltr, - )..layout(); - tp.paint(c, Offset(rx + 18, y - 8)); + text: eraLabels[i], + style: TextStyle( + color: eraColors[i].withValues(alpha: 0.7), + fontSize: 9.0, + fontWeight: FontWeight.w700, + ), + ), + textAlign: TextAlign.center, + textDirection: TextDirection.rtl, + )..layout(maxWidth: eraWidth - 8); + tp.paint(c, Offset(x + (eraWidth - tp.width) / 2, 16)); } - } - void _dashed(Canvas c, Path p, Color col) { - final metrics = p.computeMetrics().toList(); - for (final m in metrics) { - double d = 0; - while (d < m.length) { - c.drawPath( - m.extractPath(d, d + 8), + // Interactive Flowing S-Curve Timeline Rail (خط زمني انسيابي ممتد) + final railPath = Path(); + final count = milestones.length; + final points = []; + + for (int i = 0; i < count; i++) { + final px = 24.0 + (i / (count - 1)) * (w - 48.0); + // Harmonious wave across the canvas + final py = (h * 0.52) + math.sin(i * 1.1) * (h * 0.16); + points.add(Offset(px, py)); + } + + railPath.moveTo(points.first.dx, points.first.dy); + for (int i = 0; i < points.length - 1; i++) { + final p0 = points[i]; + final p1 = points[i + 1]; + final mx = (p0.dx + p1.dx) / 2; + railPath.cubicTo(mx, p0.dy, mx, p1.dy, p1.dx, p1.dy); + } + + // Draw main timeline track + c.drawPath( + railPath, + Paint() + ..color = Colors.white24 + ..strokeWidth = 3.2 + ..style = PaintingStyle.stroke, + ); + + // Active illuminated segment + final activeColor = milestones[index]['c'] as Color; + final completedPath = Path()..moveTo(points.first.dx, points.first.dy); + for (int i = 0; i < index; i++) { + final p0 = points[i]; + final p1 = points[i + 1]; + final mx = (p0.dx + p1.dx) / 2; + completedPath.cubicTo(mx, p0.dy, mx, p1.dy, p1.dx, p1.dy); + } + c.drawPath( + completedPath, + Paint() + ..color = activeColor + ..strokeWidth = 3.8 + ..style = PaintingStyle.stroke, + ); + + // Draw milestone stations along the curve + for (int i = 0; i < count; i++) { + final pt = points[i]; + final m = milestones[i]; + final isSel = i == index; + final col = m['c'] as Color; + + if (isSel) { + // Glowing halo + c.drawCircle(pt, 16, Paint()..color = col.withValues(alpha: 0.25)); + c.drawCircle( + pt, + 12, Paint() ..color = col - ..strokeWidth = 2.4); - d += 14; + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0); } - } - } - Offset _pointOn(Path route, double t, Rect mapRect) { - // approximate: lerp along bounding diagonal of route (classroom schematic) - return Offset( - mapRect.left + mapRect.width * (0.72 - 0.12 * t), - mapRect.top + mapRect.height * (0.88 - 0.73 * t), - ); + // Station node + c.drawCircle(pt, isSel ? 7.5 : 5.0, Paint()..color = isSel ? Colors.white : col); + + // Label (Year + Icon) + final tp = TextPainter( + text: TextSpan( + text: '${m['icon']} ${m['y']}', + style: TextStyle( + color: isSel ? Colors.white : Colors.white70, + fontSize: isSel ? 10.5 : 9.0, + fontWeight: isSel ? FontWeight.w900 : FontWeight.w700, + backgroundColor: isSel ? Colors.black87 : Colors.transparent, + ), + ), + textDirection: TextDirection.rtl, + )..layout(); + + final labelY = (i % 2 == 0) ? pt.dy - 24 : pt.dy + 12; + tp.paint(c, Offset(pt.dx - tp.width / 2, labelY)); + } } @override - bool shouldRepaint(covariant _ChronoPainter o) => o.index != index; + bool shouldRepaint(covariant _WorldCivilizationsChronoPainter o) => o.index != index; } // --------------------------------------------------------------------------- @@ -461,7 +574,7 @@ class _FlowPainter extends CustomPainter { } // --------------------------------------------------------------------------- -// 3) SORTING PLAYGROUND (Bubble vs Quick trace) +// 3) SORTING & SEARCHING PLAYGROUND (المصفوفات، الفرز، والبحث خطوة بخطوة) // --------------------------------------------------------------------------- class DigitalSortingLabView extends StatefulWidget { final LabCheckpointCallback? onCheckpointTriggered; @@ -474,52 +587,91 @@ class DigitalSortingLabView extends StatefulWidget { class _DigitalSortingLabViewState extends State with SingleTickerProviderStateMixin { List arr = [7, 2, 9, 4, 1, 6]; - int algo = 0; // 0 bubble, 1 quick (trace) + int algo = 0; // 0: Bubble Sort, 1: Selection Sort, 2: Quick Sort int stepIdx = 0; List> snaps = []; List notes = []; - bool playing = false; - late final AnimationController ctl; + List> activeIndices = []; // [index1, index2, isSwapped] + bool isPlaying = false; + // ignore: unused_field + Timer? _autoPlayTimer; @override void initState() { super.initState(); _buildTrace(); - ctl = AnimationController( - vsync: this, duration: const Duration(milliseconds: 600)) - ..addListener(() { - if (!playing) return; - }); } void _buildTrace() { snaps = [List.of(arr)]; - notes = ['البداية: ${arr.join(' ')}']; + notes = ['المصفوفة الأولية قبل البدء: [${arr.join(', ')}]']; + activeIndices = [[-1, -1, 0]]; + if (algo == 0) { + // Bubble Sort with explicit comparison & swap frames final a = List.of(arr); - for (int i = 0; i < a.length; i++) { - for (int j = 0; j < a.length - 1 - i; j++) { + final n = a.length; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n - 1 - i; j++) { + // Comparison frame + snaps.add(List.of(a)); + notes.add('مقارنة العنصر [j=$j]=${a[j]} مع [j+1=${j + 1}]=${a[j + 1]}'); + activeIndices.add([j, j + 1, 0]); + if (a[j] > a[j + 1]) { final t = a[j]; a[j] = a[j + 1]; a[j + 1] = t; + // Swap frame snaps.add(List.of(a)); - notes.add('بدّل ${a[j + 1]} و ${a[j]} → ${a.join(' ')}'); + notes.add('تبديل ${a[j + 1]} مع ${a[j]} لأن ${a[j + 1]} > ${a[j]}'); + activeIndices.add([j, j + 1, 1]); } } } + } else if (algo == 1) { + // Selection Sort + final a = List.of(arr); + final n = a.length; + for (int i = 0; i < n - 1; i++) { + int minIdx = i; + for (int j = i + 1; j < n; j++) { + snaps.add(List.of(a)); + notes.add('البحث عن الأصغر: مقارنة [min=$minIdx]=${a[minIdx]} مع [j=$j]=${a[j]}'); + activeIndices.add([minIdx, j, 0]); + if (a[j] < a[minIdx]) { + minIdx = j; + } + } + if (minIdx != i) { + final t = a[i]; + a[i] = a[minIdx]; + a[minIdx] = t; + snaps.add(List.of(a)); + notes.add('تبديل الأصغر ${a[i]} إلى موقعه الصحيح عند المؤشر [$i]'); + activeIndices.add([i, minIdx, 1]); + } + } } else { - // quicksort Lomuto trace (simplified, honest label) + // Quicksort Lomuto trace final a = List.of(arr); void qs(int lo, int hi) { if (lo >= hi) return; final pivot = a[hi]; int i = lo; for (int j = lo; j < hi; j++) { + snaps.add(List.of(a)); + notes.add('مقارنة العنصر [j=$j]=${a[j]} مع المحور Pivot=$pivot'); + activeIndices.add([j, hi, 0]); if (a[j] < pivot) { final t = a[i]; a[i] = a[j]; a[j] = t; + if (i != j) { + snaps.add(List.of(a)); + notes.add('وضع العنصر الأصغر من المحور في القسم الأيسر: تبديل [$i] مع [$j]'); + activeIndices.add([i, j, 1]); + } i++; } } @@ -527,49 +679,90 @@ class _DigitalSortingLabViewState extends State a[i] = a[hi]; a[hi] = t; snaps.add(List.of(a)); - notes.add('محور $pivot → ${a.join(' ')}'); + notes.add('تثبيت المحور $pivot في موقعه النهائي عند الفهرس [$i]'); + activeIndices.add([i, hi, 1]); qs(lo, i - 1); qs(i + 1, hi); } - qs(0, a.length - 1); } stepIdx = 0; } + void _toggleAutoPlay() { + setState(() { + isPlaying = !isPlaying; + if (isPlaying) { + _autoPlayTimer = Timer.periodic(const Duration(milliseconds: 700), (timer) { + if (!mounted || !isPlaying) { + timer.cancel(); + return; + } + if (stepIdx < snaps.length - 1) { + setState(() => stepIdx++); + } else { + setState(() => isPlaying = false); + timer.cancel(); + } + }); + } else { + _autoPlayTimer?.cancel(); + } + }); + } + @override void dispose() { - ctl.dispose(); + _autoPlayTimer?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { final cur = snaps.isEmpty ? arr : snaps[stepIdx.clamp(0, snaps.length - 1)]; + final active = activeIndices.isEmpty + ? [-1, -1, 0] + : activeIndices[stepIdx.clamp(0, activeIndices.length - 1)]; final sorted = _isSorted(cur); + return SaqelLabScaffold( - titleAr: 'ملعب المصفوفات والفرز', - subtitleAr: 'قارن فقاعي O(n²) مع سريع O(n log n) خطوة بخطوة', + titleAr: 'معمل المصفوفات وخوارزميات الفرز التفاعلية', + subtitleAr: + 'تتبع بصري حي مع إبراز مؤشرات المقارنة والتبديل وفهارس المصفوفة', identity: kDigitalSortingToolIdentity, onCheckpointTriggered: widget.onCheckpointTriggered, - checkpointQuestion: 'التعقيد الزمني الأسوأ للفرز الفقاعي (n عناصر) هو …', + checkpointQuestion: 'التعقيد الزمني الأسوأ للفرز الفقاعي (Bubble Sort) لمصفوفة حجمها n هو …', checkpointOptions: const ['O(n²)', 'O(n)', 'O(log n)', 'O(1)'], checkpointCorrectIdx: 0, telemetry: [ - LabPill(algo == 0 ? 'Bubble O(n²)' : 'Quick O(n log n)'), - LabPill('خطوة ${stepIdx + 1}/${snaps.length}'), - if (sorted) const LabPill('مرتبة ✓', color: Color(0xFF30D158)), + LabPill( + algo == 0 + ? 'فقاعي Bubble O(n²)' + : (algo == 1 ? 'اختيار Selection O(n²)' : 'سريع Quick O(n log n)'), + color: AppColors.saqelCyan, + ), + LabPill('خطوة ${stepIdx + 1} من ${snaps.length}'), + if (sorted && stepIdx == snaps.length - 1) + const LabPill('مرتبة بنجاح ✓', color: Color(0xFF30D158)), ], canvas: CustomPaint( - painter: _BarsPainter(values: cur), + painter: _EnhancedBarsPainter( + values: cur, + activeIdx1: active[0], + activeIdx2: active[1], + isSwap: active[2] == 1, + isFullySorted: sorted && stepIdx == snaps.length - 1, + ), child: Container(), ), controls: [ LabSegments( - labels: const ['فقاعي', 'سريع'], - values: const [0, 1], + labels: const ['فقاعي (Bubble)', 'اختيار (Selection)', 'سريع (Quick)'], + values: const [0, 1, 2], current: algo, onSelected: (v) => setState(() { + _autoPlayTimer?.cancel(); + isPlaying = false; algo = v; _buildTrace(); }), @@ -578,63 +771,115 @@ class _DigitalSortingLabViewState extends State Row( children: [ Expanded( - child: OutlinedButton( - onPressed: stepIdx > 0 ? () => setState(() => stepIdx--) : null, + child: OutlinedButton.icon( + onPressed: stepIdx > 0 + ? () { + _autoPlayTimer?.cancel(); + setState(() { + isPlaying = false; + stepIdx--; + }); + } + : null, + icon: const Icon(CupertinoIcons.chevron_right, size: 14), + label: const Text('السابق'), style: OutlinedButton.styleFrom( - foregroundColor: Colors.white, - side: const BorderSide(color: Colors.white24)), - child: const Text('◀ سابق'), + foregroundColor: Colors.white, + side: const BorderSide(color: Colors.white24), + ), ), ), const SizedBox(width: 8), Expanded( - child: ElevatedButton( - onPressed: stepIdx < snaps.length - 1 - ? () => setState(() => stepIdx++) - : null, + child: ElevatedButton.icon( + onPressed: _toggleAutoPlay, + icon: Icon(isPlaying ? CupertinoIcons.pause_fill : CupertinoIcons.play_arrow_solid, size: 15), + label: Text(isPlaying ? 'إيقاف مؤقت' : 'تشغيل تلقائي ⏯️'), style: ElevatedButton.styleFrom( - backgroundColor: AppColors.saqelCyan, - foregroundColor: Colors.black), - child: const Text('التالي ▶', - style: TextStyle(fontWeight: FontWeight.w800)), + backgroundColor: isPlaying ? const Color(0xFFFF9F0A) : AppColors.saqelCyan, + foregroundColor: Colors.black, + textStyle: const TextStyle(fontWeight: FontWeight.w800), + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: ElevatedButton.icon( + onPressed: stepIdx < snaps.length - 1 + ? () { + _autoPlayTimer?.cancel(); + setState(() { + isPlaying = false; + stepIdx++; + }); + } + : null, + icon: const Icon(CupertinoIcons.chevron_left, size: 14), + label: const Text('التالي'), + style: ElevatedButton.styleFrom( + backgroundColor: Colors.white.withValues(alpha: 0.12), + foregroundColor: Colors.white, + textStyle: const TextStyle(fontWeight: FontWeight.w700), + ), ), ), ], ), - const SizedBox(height: 6), + const SizedBox(height: 8), SizedBox( width: double.infinity, child: OutlinedButton.icon( icon: const Icon(CupertinoIcons.shuffle, size: 15), - label: const Text('خلط المصفوفة'), + label: const Text('توليد مصفوفة عشوائية وخلط العناصر 🎲'), style: OutlinedButton.styleFrom( - foregroundColor: const Color(0xFFFFD166), - side: const BorderSide(color: Color(0xFFFFD166))), + foregroundColor: const Color(0xFFFFD166), + side: const BorderSide(color: Color(0xFFFFD166)), + ), onPressed: () => setState(() { + _autoPlayTimer?.cancel(); + isPlaying = false; arr.shuffle(math.Random()); _buildTrace(); }), ), ), const SizedBox(height: 8), - Directionality( - textDirection: TextDirection.ltr, - child: Text( - notes.isEmpty ? '' : notes[stepIdx.clamp(0, notes.length - 1)], - style: const TextStyle( - color: Color(0xFF00F5D4), - fontSize: 12, - fontFamily: 'monospace')), + Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: BoxDecoration( + color: Colors.black45, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: AppColors.saqelCyan.withValues(alpha: 0.25)), + ), + child: Row( + children: [ + const Icon(CupertinoIcons.info_circle_fill, color: AppColors.saqelCyan, size: 16), + const SizedBox(width: 8), + Expanded( + child: Text( + notes.isEmpty ? '' : notes[stepIdx.clamp(0, notes.length - 1)], + style: const TextStyle( + color: Color(0xFF00F5D4), + fontSize: 12, + fontWeight: FontWeight.w700, + ), + ), + ), + ], + ), ), - const SizedBox(height: 4), + const SizedBox(height: 6), const LabFormulaCard( - title: 'لماذا السريع أسرع؟', + title: 'دليل الخوارزميات — الصف العاشر', body: - 'الفقاعي يقارن كل زوج مجاور (تربيعي). السريع يقسم حول محور ويفرز كل قسم (لوغاريتمي) — لاحظ قفزات المحور في التتبع.', + '• الفرز الفقاعي (Bubble): يقارن كل زوج متجاور ويبدلهما. تعقيد زمني O(n²).\n' + '• فرز الاختيار (Selection): يختار أصغر عنصر وينقله لموقعه النهائي. تعقيد زمني O(n²).\n' + '• الفرز السريع (Quick): يقسم المصفوفة حول محور (Pivot) ويفرز كل قسم لوغاريتمياً. تعقيد O(n log n).', + accent: AppColors.saqelCyan, ), ], footerNote: - 'تتبع تعليمي لمدخلات صغيرة؛ قياس الأداء الحقيقي يحتاج n كبيرة وتوقيتاً فعلياً.', + 'منهاج وزارة التربية والتعليم الأردنية • المهارات الرقمية للصف العاشر • وحدة الخوارزميات والبرمجة.', ); } @@ -646,9 +891,20 @@ class _DigitalSortingLabViewState extends State } } -class _BarsPainter extends CustomPainter { +class _EnhancedBarsPainter extends CustomPainter { final List values; - _BarsPainter({required this.values}); + final int activeIdx1; + final int activeIdx2; + final bool isSwap; + final bool isFullySorted; + + _EnhancedBarsPainter({ + required this.values, + required this.activeIdx1, + required this.activeIdx2, + required this.isSwap, + required this.isFullySorted, + }); @override void paint(Canvas c, Size s) { @@ -656,31 +912,82 @@ class _BarsPainter extends CustomPainter { if (values.isEmpty) return; final mx = values.reduce(math.max).toDouble(); final bw = s.width / values.length; + for (int i = 0; i < values.length; i++) { - final h = (values[i] / mx) * (s.height - 70); + final h = (values[i] / mx) * (s.height - 85); final x = i * bw + 6; - final grad = const LinearGradient( - colors: [Color(0xFF00F5D4), Color(0xFF0071E3)], - begin: Alignment.bottomCenter, - end: Alignment.topCenter) - .createShader(Rect.fromLTWH(x, s.height - 30 - h, bw - 12, h)); + final isComp = i == activeIdx1 || i == activeIdx2; + + // Color coding for algorithm education + Color c1 = const Color(0xFF00F5D4); + Color c2 = const Color(0xFF0071E3); + + if (isFullySorted) { + c1 = const Color(0xFF30D158); + c2 = const Color(0xFF10B981); + } else if (isComp) { + if (isSwap) { + c1 = const Color(0xFFFF375F); + c2 = const Color(0xFFFF5252); + } else { + c1 = const Color(0xFFFFD166); + c2 = const Color(0xFFFF9F0A); + } + } + + final grad = LinearGradient( + colors: [c1, c2], + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + ).createShader(Rect.fromLTWH(x, s.height - 40 - h, bw - 12, h)); + c.drawRRect( - RRect.fromLTRBR(x, s.height - 30 - h, x + bw - 12, s.height - 30, - const Radius.circular(7)), - Paint()..shader = grad); + RRect.fromLTRBR( + x, + s.height - 40 - h, + x + bw - 12, + s.height - 40, + const Radius.circular(8), + ), + Paint()..shader = grad, + ); + + // Top value text final tp = TextPainter( text: TextSpan( - text: '${values[i]}', - style: const TextStyle( - color: Colors.white, - fontSize: 13, - fontWeight: FontWeight.w900)), + text: '${values[i]}', + style: TextStyle( + color: isComp ? Colors.white : Colors.white70, + fontSize: isComp ? 14 : 12.5, + fontWeight: FontWeight.w900, + ), + ), textDirection: TextDirection.ltr, )..layout(); - tp.paint(c, Offset(x + (bw - 12 - tp.width) / 2, s.height - 30 - h - 20)); + tp.paint(c, Offset(x + (bw - 12 - tp.width) / 2, s.height - 40 - h - 20)); + + // Bottom array index badge: [i] + final idxTp = TextPainter( + text: TextSpan( + text: '[$i]', + style: TextStyle( + color: isComp ? (isSwap ? const Color(0xFFFF375F) : const Color(0xFFFFD166)) : Colors.white38, + fontSize: 11, + fontWeight: isComp ? FontWeight.w800 : FontWeight.w600, + ), + ), + textDirection: TextDirection.ltr, + )..layout(); + idxTp.paint(c, Offset(x + (bw - 12 - idxTp.width) / 2, s.height - 32)); } } @override - bool shouldRepaint(covariant _BarsPainter o) => o.values != values; + bool shouldRepaint(covariant _EnhancedBarsPainter o) => + o.values != values || + o.activeIdx1 != activeIdx1 || + o.activeIdx2 != activeIdx2 || + o.isSwap != isSwap || + o.isFullySorted != isFullySorted; } + diff --git a/apps/student_app/lib/presentation/screens/virtual_labs/history_labs.dart b/apps/student_app/lib/presentation/screens/virtual_labs/history_labs.dart index 9155dbd..51dd42f 100644 --- a/apps/student_app/lib/presentation/screens/virtual_labs/history_labs.dart +++ b/apps/student_app/lib/presentation/screens/virtual_labs/history_labs.dart @@ -8,6 +8,11 @@ import 'lab_scaffold.dart'; /// HISTORY LABS — GRADE 10 (تاريخ الحضارات الصف العاشر) /// Lesson 1: الإمبراطورية الفارسية: النشأة والتطور (560 ق.م - 651 م) /// Direct alignment with Jordan Grade 10 History Curriculum (Unit 1, Lesson 1) +/// Features: +/// 1. Interactive Geographic Map of Iran & Ancient Near East (معالم حقيقية) +/// 2. Crucial Historical Battles (ماراثون، ترموبيل، سالاميس، غوغميلا، هرمزجان، القادسية، نهاوند) +/// 3. Imperial Commanders & Kings Directory (كورش، داريوس، قمبيز، أردشير، شابور، كسرى، يزدجرد) +/// 4. The Four Imperial Dynasties (الأخمينية، السلوقية، البارثية، الساسانية) /// ============================================================================ class HistoryPersianEmpireLabView extends StatefulWidget { @@ -21,37 +26,43 @@ class HistoryPersianEmpireLabView extends StatefulWidget { class _HistoryPersianEmpireLabViewState extends State { - int selectedDynasty = - 0; // 0: Achaemenid, 1: Seleucid, 2: Parthian, 3: Sasanian - double expansionFactor = 1.0; // 0.2 to 1.0 - bool showRoyalRoad = true; + int _activeTab = 0; // 0: Map & Battles, 1: Commanders, 2: Dynasties + int _selectedDynasty = 0; // 0: Achaemenid, 1: Seleucid, 2: Parthian, 3: Sasanian + int _selectedBattleIndex = 0; + int _selectedCommanderIndex = 0; + double _expansionFactor = 1.0; + bool _showRoyalRoad = true; + bool _showBattlePins = true; + // --------------------------------------------------------------------------- + // 1. The Four Imperial Dynasties (وفق منهاج الصف العاشر ص 8-13) + // --------------------------------------------------------------------------- static const dynasties = [ { 'name': 'الدولة الأخمينية (Achaemenid)', 'period': '560 ق.م - 330 ق.م', - 'founder': 'الملك كورش (Cyrus the Great)', + 'founder': 'الملك كورش الأكبر (Cyrus the Great)', 'capital': 'برسبوليس (Persepolis) وشوشان', 'desc': - 'أسسها كورش وامتدت لتشمل آسيا الصغرى، بلاد الشام، مصر، وبلاد ما بين النهرين. اشتهرت بالطريق الملكي والعملة الذهبية (الدارك).', + 'أسسها كورش الأكبر بعد توحيد قبائل الفرس والميديين، وامتدت لتشمل هضبة إيران، بابل، بلاد الشام، مصر، وآسيا الصغرى. اشتهرت بالطريق الملكي المنظم (2700 كم)، وسك عملة الدارك الذهبية، ونظام الولايات (الساتراب).', 'color': Color(0xFFFFD60A), }, { 'name': 'الدولة السلوقية (Seleucid)', 'period': '312 ق.م - 64 ق.م', - 'founder': 'القائد سلوقس الأول (سلوقس نيكاتور)', - 'capital': 'أنطاكية وسلوقية', + 'founder': 'القائد سلوقس الأول نيكاتور', + 'capital': 'سلوقية (دجلة) وأنطاكية', 'desc': - 'نشأت بعد وفاة الإسكندر المقدوني الأكبر وتقاسم قادته لإمبراطوريته، وتميزت بالمزج بين الثقافة اليونانية الهلنستية والشرقية.', + 'نشأت إثر وفاة الإسكندر المقدوني الأكبر وتقاسم قادته لإمبراطوريته، ومثلت امتزاج الحضارة اليونانية الهلنستية بالتقاليد الشرقية في بلاد الشام وبلاد ما بين النهرين وإيران.', 'color': Color(0xFF60A5FA), }, { - 'name': 'الدولة البارثية (Parthian)', + 'name': 'الدولة البارثية الأشكانية (Parthian)', 'period': '247 ق.م - 224 م', - 'founder': 'الملك أرساكيس الأول (Arsaces)', - 'capital': 'أساك ثم طيسفون (المدائن)', + 'founder': 'الملك أرساكيس الأول وميتراداتس', + 'capital': 'طيسفون (المدائن) وهيكاتومبيلوس', 'desc': - 'قامت في شمال شرق إيران وتميزت بفرسانها ورماة السهام البارثيين، وخاضت حروباً طاحنة ضد التوسع الإمبراطوري الروماني.', + 'قامت في شمال شرق إيران على أنقاض السلوقيين، وعُرفت بفرسانها ورماة السهام الأسطوريين. شكّلت حاجزاً عسكرياً منيعاً أوقف التوسع الإمبراطوري الروماني شرقاً لأكثر من ثلاثة قرون.', 'color': Color(0xFF30D158), }, { @@ -60,62 +71,581 @@ class _HistoryPersianEmpireLabViewState 'founder': 'الملك أردشير الأول (Ardashir I)', 'capital': 'طيسفون (المدائن)', 'desc': - 'شهدت نهضة معمارية وفنية كبرى ومنافسة شرسة مع الإمبراطورية البيزنطية، حتى انتهت بالفتح الإسلامي في عهد الخليفة عمر بن الخطاب.', + 'تأسست بعد انتصار أردشير في معركة هرمزجان (224م). تميزت بالحكم المركزي الشاهنشاهي، وبناء إيوان كسرى العظيم، وتأسيس مجمع جنديسابور الطبي، وخاضت حروباً كبرى مع البيزنطيين حتى سقطت عام 651م مع الفتح الإسلامي بعد معركة نهاوند.', + 'color': Color(0xFFFF453A), + }, + ]; + + // --------------------------------------------------------------------------- + // 2. Crucial Historical Battles (المعارك الفاصلة في منهاج التاريخ) + // --------------------------------------------------------------------------- + static const battles = [ + { + 'name': 'معركة ماراثون (Marathon)', + 'year': '490 ق.م', + 'era': 'الدولة الأخمينية', + 'location': 'سهل ماراثون — بلاد اليونان', + 'commanders': 'داريوس الأول الأكبر ضد الجيش الأثيني (ملتيادس)', + 'details': + 'أول حملة فارسية كبرى لردع المدن اليونانية. انتهت بصمود أثينا وانطلاق العداء الشهير لنقل بشرى النصر، فخلّدت سباق الماراثون العالمي.', + 'color': Color(0xFFFF9F0A), + 'geoDx': 0.12, + 'geoDy': 0.38, + }, + { + 'name': 'معركة ترموبيل (Thermopylae)', + 'year': '480 ق.م', + 'era': 'الدولة الأخمينية', + 'location': 'ممر ترموبيل الجبلي — اليونان', + 'commanders': 'الملك أحشويروش الأول (Xerxes) ضد ليونيداس الإسبارطي', + 'details': + 'معركة الممر الضيق الأسطورية؛ تجاوز فيها الجيش الفارسي دفاعات إسبرطة بعد كشف الممر الجبلي الخلفي، وتقدم الفرس بعدها لفتح أثينا.', + 'color': Color(0xFFFF453A), + 'geoDx': 0.10, + 'geoDy': 0.34, + }, + { + 'name': 'معركة سالاميس البحرية (Salamis)', + 'year': '480 ق.م', + 'era': 'الدولة الأخمينية', + 'location': 'مضيق جزيرة سالاميس — خليج سارونيك', + 'commanders': 'الأسطول الفارسي ضد الأسطول الأثيني (ثيميستوكليس)', + 'details': + 'أضخم معركة بحرية في التاريخ القديم؛ استدرج اليونانيون السفن الفارسية الضخمة إلى المضائق الضيقة مما شل مناورتها وغيّر مسار الحروب الميدية.', + 'color': Color(0xFF60A5FA), + 'geoDx': 0.13, + 'geoDy': 0.42, + }, + { + 'name': 'معركة غوغميلا / أربيل (Gaugamela)', + 'year': '331 ق.م', + 'era': 'نهاية الأخمينيين', + 'location': 'سهل غوغميلا (شمال بلاد الرافدين — أربيل حالياً)', + 'commanders': 'الإسكندر المقدوني الأكبر ضد داريوس الثالث', + 'details': + 'المعركة الفاصلة التي حطمت الإمبراطورية الأخمينية نهائياً؛ استخدم فيها الفرس العجلات المنجلية والفيَلة، لكن تكتيك الإسكندر بالثغرة السريعة أسقط العرش الفارسي وبدأ العصر الهلنستي.', + 'color': Color(0xFFFFD60A), + 'geoDx': 0.45, + 'geoDy': 0.36, + }, + { + 'name': 'معركة هرمزجان (Hormozdgan)', + 'year': '224 م', + 'era': 'تأسيس الدولة الساسانية', + 'location': 'سهل هرمزجان — إقليم فارس', + 'commanders': 'أردشير الأول ضد الملك البارثي أرتبانوس الرابع', + 'details': + 'المعركة المؤسسة للدولة الساسانية؛ انتصر فيها أردشير وقَتل الملك البارثي وأعلن طيسفون (المدائن) عاصمة جديدة وتُوِّج بلقب شاهنشاه (ملك الملوك).', + 'color': Color(0xFF30D158), + 'geoDx': 0.72, + 'geoDy': 0.58, + }, + { + 'name': 'معركة القادسية (Al-Qadisiyyah)', + 'year': '636 م (15 هـ)', + 'era': 'الفتح الإسلامي لفارس', + 'location': 'جنوب الحيرة — بلاد الرافدين (العراق)', + 'commanders': 'سعد بن أبي وقاص ضد رستم فرخزاد الساساني', + 'details': + 'معركة حاسمة استمرت أربعة أيام (أرماث، أغواث، عِماس، القادسية)؛ أسفرت عن هزيمة الجيش الساساني ومقتل رستم، وفتحت الطريق نحو المدائن وطاق كسرى.', + 'color': Color(0xFF00F5D4), + 'geoDx': 0.48, + 'geoDy': 0.52, + }, + { + 'name': 'معركة نهاوند — فتح الفتوح (Nahavand)', + 'year': '642 م (21 هـ)', + 'era': 'سقوط الدولة الساسانية', + 'location': 'نهاوند — جبال زاغروس في إيران', + 'commanders': 'النعمان بن مقرن المزني ضد الفيرزان الساساني', + 'details': + 'سُميت بـ "فتح الفتوح" لأنه لم تقم للفرس الساسانيين بعدها قائمة، وتشتت جيش يزدجرد الثالث وفتحت بلاد فارس بالكامل ودخلت في الحضارة الإسلامية.', + 'color': Color(0xFF9D4EDD), + 'geoDx': 0.62, + 'geoDy': 0.44, + }, + ]; + + // --------------------------------------------------------------------------- + // 3. Imperial Commanders & Kings Directory (قادة وملوك الإمبراطورية) + // --------------------------------------------------------------------------- + static const commanders = [ + { + 'name': 'كورش الأكبر (Cyrus the Great)', + 'years': '559 ق.م – 530 ق.م', + 'dynasty': 'الأخمينية (المؤسس)', + 'title': 'محرر بابل وصاحب أول إعلان لحقوق الإنسان', + 'achievements': + 'وحّد الفرس والميديين، وأسقط مملكة ليديا وبابل عام 539 ق.م، وأصدر "أسطوانة كورش" الشهيرة التي سمحت بحرية العبادة وإعادة المهجرين، وأسس عاصمة باسارغاد.', + 'color': Color(0xFFFFD60A), + }, + { + 'name': 'قمبيز الثاني (Cambyses II)', + 'years': '530 ق.م – 522 ق.م', + 'dynasty': 'الأخمينية', + 'title': 'فاتح مصر ووادي النيل', + 'achievements': + 'قاد الحملة الفارسية الكبرى عبر صحراء سيناء وفتح مصر بعد معركة بيلوزيوم عام 525 ق.م، وضمها كإقليم إمبراطوري خامس.', + 'color': Color(0xFFFF9F0A), + }, + { + 'name': 'داريوس الأول الأكبر (Darius I)', + 'years': '522 ق.م – 486 ق.م', + 'dynasty': 'الأخمينية', + 'title': 'المنظم العبقري وباني برسبوليس', + 'achievements': + 'شق الطريق الملكي (2700 كم)، وسك عملة الدارك الذهبية الموحدة، وقسّم الإمبراطورية إلى 20 ولاية (ساترابية)، وأنشأ شبكة بريد سريعة غير مسبوقة.', + 'color': Color(0xFFEAB308), + }, + { + 'name': 'أردشير الأول (Ardashir I)', + 'years': '224 م – 241 م', + 'dynasty': 'الساسانية (المؤسس)', + 'title': 'مؤسس الدولة الساسانية وشاهنشاه', + 'achievements': + 'أسقط حكم البارثيين في معركة هرمزجان (224م)، وأعاد توحيد الأقاليم الإيرانية في دولة مركزية قوية، وجعل المدائن عاصمة العرش.', + 'color': Color(0xFF30D158), + }, + { + 'name': 'سابور الأول (Shapur I)', + 'years': '241 م – 272 م', + 'dynasty': 'الساسانية', + 'title': 'قاهر الإمبراطورية الرومانية', + 'achievements': + 'هزم الجيش الروماني وأسر الإمبراطور الروماني فاليريان في معركة الرها (260م)، وخلّد انتصاراته بنقوش نقش رستم، وأنشأ مدينة بيشابور.', + 'color': Color(0xFF00F5D4), + }, + { + 'name': 'كسرى الأول أنوشروان (Khosrow I)', + 'years': '531 م – 579 م', + 'dynasty': 'الساسانية', + 'title': 'الملك العادل وباني إيوان المدائن', + 'achievements': + 'عصر الازدهار الذهبي الساساني؛ شيد طاق كسرى بالمدائن، وأسس مجمع جنديسابور العلمي، وأصلح نظام الضرائب الخراجية، وتُرجم في عهده كتاب كليلة ودمنة.', + 'color': Color(0xFF9D4EDD), + }, + { + 'name': 'يزدجرد الثالث (Yazdegerd III)', + 'years': '632 م – 651 م', + 'dynasty': 'الساسانية (آخر الملوك)', + 'title': 'آخر ملوك الإمبراطورية الساسانية القديمة', + 'achievements': + 'تولى الحكم صغيراً وسط اضطراب الإمبراطورية بعد حروبها المنهكة مع الروم البيزنطيين، وخاض جيشه معارك القادسية ونهاوند حتى انتهت الدولة بدخول بلاد فارس تحت راية الإسلام.', 'color': Color(0xFFFF453A), }, ]; @override Widget build(BuildContext context) { - final cur = dynasties[selectedDynasty]; + final curDynasty = dynasties[_selectedDynasty]; + final curBattle = battles[_selectedBattleIndex]; + final curCommander = commanders[_selectedCommanderIndex]; return SaqelLabScaffold( titleAr: 'مختبر الإمبراطورية الفارسية (560 ق.م - 651 م)', subtitleAr: - 'محاكاة تفاعلية للدول الفارسية الأربع: الأخمينية، السلوقية، البارثية، الساسانية', + 'خريطة جغرافية دقيقة لإيران والشرق الأدنى القديم • المعارك الفاصلة • سجل القادة والملوك', identity: kHistoryPersianEmpireLabIdentity, onCheckpointTriggered: widget.onCheckpointTriggered, checkpointQuestion: - 'مؤسس الإمبراطورية الفارسية الأخمينية التي امتدت عبر آسيا وأفريقيا وأوروبا هو الملك …', + 'المعركة الفاصلة التي خاضها أردشير الأول وأسفرت عن تأسيس الدولة الساسانية عام 224م هي …', checkpointOptions: const [ - 'كورش (Cyrus)', - 'داريوس الأكبر (Darius)', - 'سلوقس الأول (Seleucus)', - 'أرساكيس (Arsaces)', + 'معركة هرمزجان', + 'معركة نهاوند', + 'معركة غوغميلا', + 'معركة ماراثون' ], checkpointCorrectIdx: 0, telemetry: [ - LabPill(cur['period'] as String, color: cur['color'] as Color), - LabPill('المؤسس: ${cur['founder']}', color: AppColors.saqelCyan), - LabPill('العاصمة: ${cur['capital']}', color: const Color(0xFF94A3B8)), + if (_activeTab == 0) ...[ + LabPill(curBattle['name'] as String, color: curBattle['color'] as Color), + LabPill(curBattle['year'] as String, color: AppColors.saqelCyan), + LabPill('الموقع: ${curBattle['location']}', color: const Color(0xFF94A3B8)), + ] else if (_activeTab == 1) ...[ + LabPill(curCommander['name'] as String, color: curCommander['color'] as Color), + LabPill(curCommander['years'] as String, color: AppColors.saqelCyan), + LabPill(curCommander['dynasty'] as String, color: const Color(0xFF94A3B8)), + ] else ...[ + LabPill(curDynasty['period'] as String, color: curDynasty['color'] as Color), + LabPill('المؤسس: ${curDynasty['founder']}', color: AppColors.saqelCyan), + LabPill('العاصمة: ${curDynasty['capital']}', color: const Color(0xFF94A3B8)), + ], ], canvas: CustomPaint( - painter: _PersianEmpirePainter( - dynastyIndex: selectedDynasty, - dynastyColor: cur['color'] as Color, - expansion: expansionFactor, - showRoad: showRoyalRoad, + painter: _PersianEmpireGeographicPainter( + activeTab: _activeTab, + selectedDynasty: _selectedDynasty, + selectedBattleIndex: _selectedBattleIndex, + battles: battles, + expansion: _expansionFactor, + showRoad: _showRoyalRoad, + showBattles: _showBattlePins, ), child: Container(), ), controls: [ + // Tab switch + Container( + height: 42, + padding: const EdgeInsets.all(3), + decoration: BoxDecoration( + color: Colors.white.withValues(alpha: 0.06), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.white12), + ), + child: Row( + children: [ + _buildTabButton(0, 'الخريطة والمعارك 🗺️⚔️'), + _buildTabButton(1, 'سجل القادة 👑'), + _buildTabButton(2, 'الحقب والدول 🏛️'), + ], + ), + ), + const SizedBox(height: 12), + + // Controls body according to active tab + if (_activeTab == 0) _buildMapAndBattlesControls(curBattle), + if (_activeTab == 1) _buildCommandersControls(curCommander), + if (_activeTab == 2) _buildDynastiesControls(curDynasty), + ], + footerNote: + 'منهاج وزارة التربية والتعليم الأردنية • كتاب التاريخ للصف العاشر • الوحدة الأولى: الإمبراطورية الفارسية ص 8-13.', + ); + } + + Widget _buildTabButton(int index, String title) { + final isSel = _activeTab == index; + return Expanded( + child: GestureDetector( + onTap: () => setState(() => _activeTab = index), + child: Container( + decoration: BoxDecoration( + color: isSel ? AppColors.saqelCyan : Colors.transparent, + borderRadius: BorderRadius.circular(9), + ), + alignment: Alignment.center, + child: Text( + title, + style: TextStyle( + color: isSel ? Colors.black : Colors.white70, + fontWeight: isSel ? FontWeight.w800 : FontWeight.w600, + fontSize: 11.5, + ), + ), + ), + ), + ); + } + + // --------------------------------------------------------------------------- + // TAB 0: Map & Battles Controls + // --------------------------------------------------------------------------- + Widget _buildMapAndBattlesControls(Map curBattle) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'اختر المعركة الفاصلة لعرض موقعها وقادتها:', + style: TextStyle( + color: Colors.white, fontWeight: FontWeight.w700, fontSize: 12.5), + ), + const SizedBox(height: 6), + SizedBox( + height: 38, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: battles.length, + separatorBuilder: (_, __) => const SizedBox(width: 6), + itemBuilder: (ctx, i) { + final b = battles[i]; + final isSel = _selectedBattleIndex == i; + return GestureDetector( + onTap: () => setState(() => _selectedBattleIndex = i), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: isSel + ? (b['color'] as Color) + : Colors.white.withValues(alpha: 0.07), + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: isSel ? Colors.white : Colors.white12, + width: isSel ? 1.4 : 1.0, + ), + ), + alignment: Alignment.center, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + isSel ? '⚔️ ' : '', + style: const TextStyle(fontSize: 11), + ), + Text( + b['name'] as String, + style: TextStyle( + color: isSel ? Colors.black : Colors.white70, + fontWeight: isSel ? FontWeight.w800 : FontWeight.w600, + fontSize: 11.5, + ), + ), + ], + ), + ), + ); + }, + ), + ), + const SizedBox(height: 10), + + // Battle Details Card + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: (curBattle['color'] as Color).withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(14), + border: Border.all( + color: (curBattle['color'] as Color).withValues(alpha: 0.35), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '⚔️ ${curBattle['name']} (${curBattle['year']})', + style: TextStyle( + color: curBattle['color'] as Color, + fontWeight: FontWeight.w800, + fontSize: 13, + ), + ), + Container( + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2), + decoration: BoxDecoration( + color: Colors.black26, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + curBattle['era'] as String, + style: const TextStyle(color: Colors.white70, fontSize: 10.5), + ), + ), + ], + ), + const SizedBox(height: 6), + Text( + '• القادة: ${curBattle['commanders']}', + style: const TextStyle( + color: AppColors.saqelCyan, + fontSize: 11.5, + fontWeight: FontWeight.w700, + ), + ), + const SizedBox(height: 4), + Text( + curBattle['details'] as String, + style: const TextStyle( + color: Colors.white, + fontSize: 11.5, + height: 1.45, + ), + ), + ], + ), + ), + const SizedBox(height: 10), + + // Map Layers Switches + Row( + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: kSaqelGlass, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: Colors.white10), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('الطريق الملكي 🛣️', + style: TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.w600)), + CupertinoSwitch( + value: _showRoyalRoad, + activeTrackColor: AppColors.saqelCyan, + onChanged: (v) => setState(() => _showRoyalRoad = v), + ), + ], + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: kSaqelGlass, + borderRadius: BorderRadius.circular(10), + border: Border.all(color: Colors.white10), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('مواقع المعارك ⚔️', + style: TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.w600)), + CupertinoSwitch( + value: _showBattlePins, + activeTrackColor: const Color(0xFFFF375F), + onChanged: (v) => setState(() => _showBattlePins = v), + ), + ], + ), + ), + ), + ], + ), + ], + ); + } + + // --------------------------------------------------------------------------- + // TAB 1: Commanders Controls + // --------------------------------------------------------------------------- + Widget _buildCommandersControls(Map curCommander) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'أبرز قادة وملوك بلاد فارس عبر العصور:', + style: TextStyle( + color: Colors.white, fontWeight: FontWeight.w700, fontSize: 12.5), + ), + const SizedBox(height: 6), + SizedBox( + height: 38, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: commanders.length, + separatorBuilder: (_, __) => const SizedBox(width: 6), + itemBuilder: (ctx, i) { + final c = commanders[i]; + final isSel = _selectedCommanderIndex == i; + return GestureDetector( + onTap: () => setState(() => _selectedCommanderIndex = i), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: isSel + ? (c['color'] as Color) + : Colors.white.withValues(alpha: 0.07), + borderRadius: BorderRadius.circular(10), + border: Border.all( + color: isSel ? Colors.white : Colors.white12, + width: isSel ? 1.4 : 1.0, + ), + ), + alignment: Alignment.center, + child: Text( + c['name'] as String, + style: TextStyle( + color: isSel ? Colors.black : Colors.white70, + fontWeight: isSel ? FontWeight.w800 : FontWeight.w600, + fontSize: 11.5, + ), + ), + ), + ); + }, + ), + ), + const SizedBox(height: 10), + + // Commander Card + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: (curCommander['color'] as Color).withValues(alpha: 0.12), + borderRadius: BorderRadius.circular(14), + border: Border.all( + color: (curCommander['color'] as Color).withValues(alpha: 0.35), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '👑 ${curCommander['name']}', + style: TextStyle( + color: curCommander['color'] as Color, + fontWeight: FontWeight.w800, + fontSize: 13.5, + ), + ), + Text( + curCommander['years'] as String, + style: const TextStyle( + color: AppColors.saqelCyan, + fontWeight: FontWeight.w700, + fontSize: 11.5, + ), + ), + ], + ), + const SizedBox(height: 4), + Text( + '• اللقب والصفة: ${curCommander['title']}', + style: const TextStyle( + color: Color(0xFFFFD60A), + fontSize: 11.5, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 6), + Text( + curCommander['achievements'] as String, + style: const TextStyle( + color: Colors.white, + fontSize: 11.5, + height: 1.45, + ), + ), + ], + ), + ), + ], + ); + } + + // --------------------------------------------------------------------------- + // TAB 2: Dynasties Controls + // --------------------------------------------------------------------------- + Widget _buildDynastiesControls(Map curDynasty) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ const Text( 'اختر المرحلة التاريخية للإمبراطورية:', style: TextStyle( - color: Colors.white, fontWeight: FontWeight.w700, fontSize: 13), + color: Colors.white, fontWeight: FontWeight.w700, fontSize: 12.5), ), const SizedBox(height: 8), ...List.generate(dynasties.length, (i) { final dyn = dynasties[i]; - final isSel = selectedDynasty == i; + final isSel = _selectedDynasty == i; return Padding( padding: const EdgeInsets.only(bottom: 6), child: InkWell( - onTap: () => setState(() => selectedDynasty = i), + onTap: () => setState(() => _selectedDynasty = i), borderRadius: BorderRadius.circular(10), child: Container( - padding: - const EdgeInsets.symmetric(horizontal: 10, vertical: 8), + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), decoration: BoxDecoration( color: isSel ? (dyn['color'] as Color).withValues(alpha: 0.18) @@ -166,73 +696,55 @@ class _HistoryPersianEmpireLabViewState ), ); }), - const SizedBox(height: 10), + const SizedBox(height: 6), LabSlider( - label: 'نطاق التوسع الجغرافي الإمبراطوري', - value: expansionFactor, - min: 0.2, + label: 'نطاق التوسع الإمبراطوري', + value: _expansionFactor, + min: 0.3, max: 1.0, - display: '${(expansionFactor * 100).toInt()}%', - onChanged: (v) => setState(() => expansionFactor = v), + display: '${(_expansionFactor * 100).toInt()}%', + onChanged: (v) => setState(() => _expansionFactor = v), ), - const SizedBox(height: 8), + const SizedBox(height: 6), Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( - color: kSaqelGlass, + color: (curDynasty['color'] as Color).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(12), - border: - Border.all(color: AppColors.saqelCyan.withValues(alpha: 0.2)), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('إظهار الطريق الملكي ومحطات البريد (2700 كم)', - style: TextStyle( - color: Colors.white, - fontSize: 11.5, - fontWeight: FontWeight.w600)), - CupertinoSwitch( - value: showRoyalRoad, - activeColor: AppColors.saqelCyan, - onChanged: (v) => setState(() => showRoyalRoad = v), - ), - ], - ), - ), - const SizedBox(height: 10), - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: (cur['color'] as Color).withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(14), border: Border.all( - color: (cur['color'] as Color).withValues(alpha: 0.3)), + color: (curDynasty['color'] as Color).withValues(alpha: 0.3)), ), child: Text( - cur['desc'] as String, + curDynasty['desc'] as String, style: const TextStyle( - color: Colors.white, fontSize: 12, height: 1.45), + color: Colors.white, fontSize: 11.5, height: 1.4), ), ), ], - footerNote: - 'منهاج وزارة التربية والتعليم الأردنية • كتاب التاريخ للصف العاشر • الوحدة الأولى ص 8-13', ); } } -class _PersianEmpirePainter extends CustomPainter { - final int dynastyIndex; - final Color dynastyColor; +// --------------------------------------------------------------------------- +// 4. REALISTIC GEOGRAPHIC MAP PAINTER (خريطة إيران والشرق الأدنى القديم) +// --------------------------------------------------------------------------- +class _PersianEmpireGeographicPainter extends CustomPainter { + final int activeTab; + final int selectedDynasty; + final int selectedBattleIndex; + final List> battles; final double expansion; final bool showRoad; + final bool showBattles; - _PersianEmpirePainter({ - required this.dynastyIndex, - required this.dynastyColor, + _PersianEmpireGeographicPainter({ + required this.activeTab, + required this.selectedDynasty, + required this.selectedBattleIndex, + required this.battles, required this.expansion, required this.showRoad, + required this.showBattles, }); @override @@ -240,106 +752,305 @@ class _PersianEmpirePainter extends CustomPainter { final w = size.width; final h = size.height; - // Dark parchment background - final bg = Paint() - ..shader = const RadialGradient( - colors: [Color(0xFF131D2D), Color(0xFF070E18)], - radius: 0.9, - ).createShader(Rect.fromLTWH(0, 0, w, h)); - canvas.drawRect(Rect.fromLTWH(0, 0, w, h), bg); + // 1. Ancient Deep Oceanic Background (البحار والمحيطات) + final oceanShader = const RadialGradient( + center: Alignment(0.1, -0.2), + colors: [Color(0xFF0D1B2E), Color(0xFF050B14)], + radius: 1.1, + ).createShader(Rect.fromLTWH(0, 0, w, h)); + canvas.drawRect(Rect.fromLTWH(0, 0, w, h), Paint()..shader = oceanShader); - // Ancient Map Grid lines + // Coordinate Grid (خطوط الطول والعرض الأثرية) final gridPaint = Paint() - ..color = Colors.white.withValues(alpha: 0.05) + ..color = Colors.white.withValues(alpha: 0.04) ..strokeWidth = 1.0; - for (double x = 0; x < w; x += 40) { + for (double x = 0; x < w; x += 36) { canvas.drawLine(Offset(x, 0), Offset(x, h), gridPaint); } - for (double y = 0; y < h; y += 40) { + for (double y = 0; y < h; y += 36) { canvas.drawLine(Offset(0, y), Offset(w, y), gridPaint); } - // Imperial territory blob (Center: Iran / Persepolis) - final cx = w * 0.58; - final cy = h * 0.52; - final maxRadiusX = (w * 0.38) * expansion; - final maxRadiusY = (h * 0.32) * expansion; - - final territoryGlow = Paint() - ..color = dynastyColor.withValues(alpha: 0.15) - ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 25); - canvas.drawOval( - Rect.fromCenter( - center: Offset(cx, cy), - width: maxRadiusX * 2, - height: maxRadiusY * 2), - territoryGlow); - - final territoryBorder = Paint() - ..color = dynastyColor.withValues(alpha: 0.6) + // 2. GEOGRAPHIC LANDMASSES (رسم المعالم الجغرافية الحقيقية) + final landPaint = Paint() + ..color = const Color(0xFF14243B) + ..style = PaintingStyle.fill; + final landBorderPaint = Paint() + ..color = const Color(0xFF24426A) ..style = PaintingStyle.stroke - ..strokeWidth = 2.0; - canvas.drawOval( - Rect.fromCenter( - center: Offset(cx, cy), - width: maxRadiusX * 2, - height: maxRadiusY * 2), - territoryBorder); + ..strokeWidth = 1.4; - // Key Historical Cities - final cityPaint = Paint()..color = const Color(0xFFFFD60A); - final capitalPaint = Paint()..color = const Color(0xFFFF375F); + // A) Iranian Plateau (هضبة إيران — قلب الإمبراطورية) + final iranPath = Path() + ..moveTo(w * 0.54, h * 0.28) // بحر قزوين الجنوبي + ..lineTo(w * 0.72, h * 0.25) // خراسان ومرو + ..lineTo(w * 0.88, h * 0.42) // حدود أفغانستان وباكستان + ..lineTo(w * 0.85, h * 0.72) // مكران ومضيق هرمز + ..lineTo(w * 0.70, h * 0.75) // الخليج العربي الشرقي (فارس / بوشهر) + ..lineTo(w * 0.58, h * 0.58) // خوزستان والأحواز + ..lineTo(w * 0.52, h * 0.44) // جبال زاغروس + ..close(); + canvas.drawPath(iranPath, landPaint); + canvas.drawPath(iranPath, landBorderPaint); - // Persepolis / Shushan - canvas.drawCircle(Offset(cx + 20, cy + 10), 5, capitalPaint); - _drawCityLabel(canvas, Offset(cx + 20, cy + 10), 'برسبوليس (Persepolis)'); + // B) Mesopotamia & Levant (بلاد الرافدين ودجلة والفرات والشام) + final fertCrescPath = Path() + ..moveTo(w * 0.38, h * 0.30) // شمال الجزيرة / نصيبين + ..lineTo(w * 0.54, h * 0.30) // كردستان العراق + ..lineTo(w * 0.58, h * 0.58) // البصرة / شط العرب + ..lineTo(w * 0.44, h * 0.65) // البادية / الكويت + ..lineTo(w * 0.32, h * 0.56) // بلاد الشام / الأردن وفلسطين + ..lineTo(w * 0.28, h * 0.36) // الساحل الفينيقي وسوريا + ..close(); + canvas.drawPath(fertCrescPath, landPaint); + canvas.drawPath(fertCrescPath, landBorderPaint); - // Babylon / Mesopotamia - canvas.drawCircle(Offset(cx - 70, cy), 4, cityPaint); - _drawCityLabel(canvas, Offset(cx - 70, cy), 'بابل (Babylon)'); + // C) Asia Minor / Anatolia (آسيا الصغرى / تركيا) + final anatoliaPath = Path() + ..moveTo(w * 0.15, h * 0.22) // البوسفور والدردنيل + ..lineTo(w * 0.38, h * 0.20) // البحر الأسود الجنوبي + ..lineTo(w * 0.42, h * 0.32) // طوروس وأرمينيا + ..lineTo(w * 0.26, h * 0.35) // قيليقية + ..lineTo(w * 0.14, h * 0.34) // إيونيا وإيجة + ..close(); + canvas.drawPath(anatoliaPath, landPaint); + canvas.drawPath(anatoliaPath, landBorderPaint); - // Sardis (Asia Minor) - canvas.drawCircle(Offset(cx - 160, cy - 40), 4, cityPaint); - _drawCityLabel(canvas, Offset(cx - 160, cy - 40), 'ساردس (Sardis)'); + // D) Egypt & Nile Delta (مصر ودلتا النيل) + final egyptPath = Path() + ..moveTo(w * 0.18, h * 0.58) // الإسكندرية وممفيس + ..lineTo(w * 0.28, h * 0.58) // سيناء + ..lineTo(w * 0.26, h * 0.85) // البحر الأحمر الغربي + ..lineTo(w * 0.16, h * 0.82) // وادي النيل + ..close(); + canvas.drawPath(egyptPath, landPaint); + canvas.drawPath(egyptPath, landBorderPaint); - // Egypt / Nile - canvas.drawCircle(Offset(cx - 150, cy + 55), 4, cityPaint); - _drawCityLabel(canvas, Offset(cx - 150, cy + 55), 'ممفيس (Egypt)'); + // E) Greece / Hellas (بلاد اليونان) + final greecePath = Path() + ..moveTo(w * 0.05, h * 0.28) + ..lineTo(w * 0.12, h * 0.26) + ..lineTo(w * 0.13, h * 0.42) // أثينا وبيلوبونيز + ..lineTo(w * 0.06, h * 0.40) + ..close(); + canvas.drawPath(greecePath, landPaint); + canvas.drawPath(greecePath, landBorderPaint); - // Royal Road (Sardis to Susa/Persepolis) + // 3. SEAS & GULFS (البحار بأسماء واضحة) + // A) Caspian Sea (بحر قزوين) + final caspianPath = Path() + ..moveTo(w * 0.54, h * 0.15) + ..quadraticBezierTo(w * 0.60, h * 0.18, w * 0.58, h * 0.27) + ..quadraticBezierTo(w * 0.52, h * 0.26, w * 0.50, h * 0.16) + ..close(); + canvas.drawPath(caspianPath, Paint()..color = const Color(0xFF0A2540)); + canvas.drawPath(caspianPath, landBorderPaint); + _drawWaterLabel(canvas, Offset(w * 0.55, h * 0.21), 'بحر قزوين'); + + // B) Persian Gulf (الخليج العربي) + final gulfPath = Path() + ..moveTo(w * 0.58, h * 0.60) + ..quadraticBezierTo(w * 0.68, h * 0.64, w * 0.78, h * 0.76) + ..lineTo(w * 0.74, h * 0.82) + ..quadraticBezierTo(w * 0.62, h * 0.72, w * 0.54, h * 0.66) + ..close(); + canvas.drawPath(gulfPath, Paint()..color = const Color(0xFF0A2540)); + canvas.drawPath(gulfPath, landBorderPaint); + _drawWaterLabel(canvas, Offset(w * 0.66, h * 0.71), 'الخليج العربي'); + + // C) Red Sea & Mediterranean labels + _drawWaterLabel(canvas, Offset(w * 0.16, h * 0.42), 'البحر الأبيض المتوسط'); + _drawWaterLabel(canvas, Offset(w * 0.28, h * 0.74), 'البحر الأحمر'); + + // D) Rivers: Tigris & Euphrates (دجلة والفرات) + final riverPaint = Paint() + ..color = const Color(0xFF38BDF8).withValues(alpha: 0.6) + ..strokeWidth = 1.6 + ..style = PaintingStyle.stroke; + // Euphrates (الفرات) + final euphrates = Path() + ..moveTo(w * 0.34, h * 0.28) + ..quadraticBezierTo(w * 0.38, h * 0.42, w * 0.48, h * 0.56) + ..lineTo(w * 0.58, h * 0.60); + canvas.drawPath(euphrates, riverPaint); + + // Tigris (دجلة) + final tigris = Path() + ..moveTo(w * 0.42, h * 0.26) + ..quadraticBezierTo(w * 0.46, h * 0.38, w * 0.52, h * 0.50) + ..lineTo(w * 0.58, h * 0.60); + canvas.drawPath(tigris, riverPaint); + + // 4. IMPERIAL EXPANSION GLOW (امتداد الإمبراطورية في العصر المختار) + final dynColors = [ + const Color(0xFFFFD60A), + const Color(0xFF60A5FA), + const Color(0xFF30D158), + const Color(0xFFFF453A), + ]; + final activeColor = dynColors[selectedDynasty]; + + final empirePath = Path() + ..moveTo(w * 0.12 * expansion + w * 0.05, h * 0.30) + ..lineTo(w * 0.86 * expansion, h * 0.26) + ..lineTo(w * 0.84 * expansion, h * 0.74) + ..lineTo(w * 0.22 * expansion + w * 0.05, h * 0.76) + ..close(); + + final empireFill = Paint() + ..color = activeColor.withValues(alpha: 0.10) + ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 18); + canvas.drawPath(empirePath, empireFill); + + // 5. THE ROYAL ROAD (الطريق الملكي 2700 كم) if (showRoad) { final roadPaint = Paint() ..color = const Color(0xFF00F5D4) ..strokeWidth = 2.2 ..style = PaintingStyle.stroke; - final path = Path() - ..moveTo(cx - 160, cy - 40) // Sardis - ..quadraticBezierTo(cx - 110, cy - 60, cx - 70, cy) // Babylon - ..lineTo(cx + 20, cy + 10); // Susa / Persepolis - canvas.drawPath(path, roadPaint); - // Courier Station dots - final stationPaint = Paint()..color = const Color(0xFF00F5D4); - canvas.drawCircle(Offset(cx - 110, cy - 35), 3, stationPaint); - canvas.drawCircle(Offset(cx - 30, cy + 5), 3, stationPaint); + final roadPath = Path() + ..moveTo(w * 0.16, h * 0.32) // ساردس (Sardis) + ..quadraticBezierTo(w * 0.28, h * 0.28, w * 0.42, h * 0.36) // طوروس ونينوى + ..lineTo(w * 0.50, h * 0.48) // طيسفون / بابل + ..quadraticBezierTo(w * 0.58, h * 0.54, w * 0.64, h * 0.55) // شوشان (سوسة) + ..lineTo(w * 0.72, h * 0.64); // برسبوليس + + canvas.drawPath(roadPath, roadPaint); + + // Courier post stations + final postPaint = Paint()..color = const Color(0xFF00F5D4); + for (final pt in [ + Offset(w * 0.16, h * 0.32), + Offset(w * 0.30, h * 0.30), + Offset(w * 0.42, h * 0.36), + Offset(w * 0.50, h * 0.48), + Offset(w * 0.64, h * 0.55), + Offset(w * 0.72, h * 0.64), + ]) { + canvas.drawCircle(pt, 3.5, postPaint); + } + + _drawCityLabel( + canvas, Offset(w * 0.32, h * 0.26), 'الطريق الملكي (2700 كم)', + color: const Color(0xFF00F5D4), isHighlight: true); + } + + // 6. HISTORICAL CAPITALS (العواصم الإمبراطورية الفارسية الكبرى) + final capitalPaint = Paint()..color = const Color(0xFFFF375F); + final cityPaint = Paint()..color = const Color(0xFFFFD60A); + + // برسبوليس (Persepolis / تخت جمشيد) + final persepolis = Offset(w * 0.72, h * 0.64); + canvas.drawCircle(persepolis, 6, capitalPaint); + canvas.drawCircle(persepolis, 9, Paint()..color = Colors.white24..style = PaintingStyle.stroke..strokeWidth = 1.5); + _drawCityLabel(canvas, persepolis, 'برسبوليس 🏛️', color: const Color(0xFFFFD60A), isHighlight: true); + + // شوشان (سوسة / Susa) + final susa = Offset(w * 0.64, h * 0.55); + canvas.drawCircle(susa, 5, cityPaint); + _drawCityLabel(canvas, susa, 'شوشان (سوسة)'); + + // طيسفون / المدائن (Ctesiphon) + final ctesiphon = Offset(w * 0.50, h * 0.48); + canvas.drawCircle(ctesiphon, 6, capitalPaint); + _drawCityLabel(canvas, ctesiphon, 'المدائن (طيسفون) 👑', color: const Color(0xFF38BDF8), isHighlight: true); + + // بابل (Babylon) + final babylon = Offset(w * 0.46, h * 0.52); + canvas.drawCircle(babylon, 4.5, cityPaint); + _drawCityLabel(canvas, babylon, 'بابل'); + + // إكباتانا (همدان / Ecbatana) + final ecbatana = Offset(w * 0.60, h * 0.38); + canvas.drawCircle(ecbatana, 4.5, cityPaint); + _drawCityLabel(canvas, ecbatana, 'إكباتانا (همدان)'); + + // ساردس (Sardis) + final sardis = Offset(w * 0.16, h * 0.32); + canvas.drawCircle(sardis, 5, cityPaint); + _drawCityLabel(canvas, sardis, 'ساردس (Sardis)'); + + // 7. BATTLE SITES & PINS (المعارك الفاصلة) + if (showBattles) { + for (int i = 0; i < battles.length; i++) { + final b = battles[i]; + final bx = (b['geoDx'] as double) * w; + final by = (b['geoDy'] as double) * h; + final isSel = selectedBattleIndex == i; + final col = b['color'] as Color; + + // Pulse ring if selected + if (isSel) { + canvas.drawCircle( + Offset(bx, by), 12, Paint()..color = col.withValues(alpha: 0.35)); + canvas.drawCircle( + Offset(bx, by), + 16, + Paint() + ..color = col + ..style = PaintingStyle.stroke + ..strokeWidth = 1.8); + } + + // Crossed swords battle pin + canvas.drawCircle(Offset(bx, by), isSel ? 6 : 4.5, Paint()..color = col); + canvas.drawCircle( + Offset(bx, by), + isSel ? 6 : 4.5, + Paint() + ..color = Colors.white + ..style = PaintingStyle.stroke + ..strokeWidth = 1.2); + + if (isSel) { + _drawCityLabel(canvas, Offset(bx, by - 6), '⚔️ ${b['name']}', + color: Colors.white, isHighlight: true); + } + } } } - void _drawCityLabel(Canvas canvas, Offset offset, String text) { + void _drawCityLabel(Canvas canvas, Offset offset, String text, + {Color color = Colors.white, bool isHighlight = false}) { final tp = TextPainter( text: TextSpan( text: text, - style: const TextStyle( - color: Colors.white, fontSize: 10.5, fontWeight: FontWeight.w700), + style: TextStyle( + color: color, + fontSize: isHighlight ? 10.5 : 9.5, + fontWeight: isHighlight ? FontWeight.w800 : FontWeight.w600, + backgroundColor: isHighlight ? Colors.black87 : Colors.transparent, + ), ), textDirection: TextDirection.rtl, )..layout(); tp.paint(canvas, Offset(offset.dx - (tp.width / 2), offset.dy - 16)); } + void _drawWaterLabel(Canvas canvas, Offset offset, String text) { + final tp = TextPainter( + text: TextSpan( + text: text, + style: TextStyle( + color: Colors.white.withValues(alpha: 0.35), + fontSize: 9.0, + fontWeight: FontWeight.w700, + fontStyle: FontStyle.italic, + ), + ), + textDirection: TextDirection.rtl, + )..layout(); + tp.paint(canvas, Offset(offset.dx - (tp.width / 2), offset.dy - (tp.height / 2))); + } + @override - bool shouldRepaint(covariant _PersianEmpirePainter oldDelegate) => - oldDelegate.dynastyIndex != dynastyIndex || - oldDelegate.expansion != expansion || - oldDelegate.showRoad != showRoad; + bool shouldRepaint(covariant _PersianEmpireGeographicPainter old) => + old.activeTab != activeTab || + old.selectedDynasty != selectedDynasty || + old.selectedBattleIndex != selectedBattleIndex || + old.expansion != expansion || + old.showRoad != showRoad || + old.showBattles != showBattles; } + diff --git a/apps/student_app/lib/presentation/screens/virtual_labs/lab_identity.dart b/apps/student_app/lib/presentation/screens/virtual_labs/lab_identity.dart index 143c3cf..d1ced1d 100644 --- a/apps/student_app/lib/presentation/screens/virtual_labs/lab_identity.dart +++ b/apps/student_app/lib/presentation/screens/virtual_labs/lab_identity.dart @@ -587,7 +587,7 @@ const LabIdentity kFinanceBudgetToolIdentity = LabIdentity( const LabIdentity kHistoryJordanChronologyToolIdentity = LabIdentity( toolKey: 'history_jordan_chronology', subjectAr: 'التاريخ', - lessonAr: 'التسلسل الزمني التفاعلي', + lessonAr: 'التسلسل الزمني للحضارات (العاشر)', ); const LabIdentity kDigitalFlowchartToolIdentity = LabIdentity( diff --git a/apps/student_app/lib/presentation/screens/virtual_labs/labs_registry.dart b/apps/student_app/lib/presentation/screens/virtual_labs/labs_registry.dart index bb59a46..cab6247 100644 --- a/apps/student_app/lib/presentation/screens/virtual_labs/labs_registry.dart +++ b/apps/student_app/lib/presentation/screens/virtual_labs/labs_registry.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import '../../../core/utils/app_logger.dart'; import '../../../data/models/socratic_checkpoint_model.dart'; import '../../widgets/socratic_dialog.dart'; import 'lab_identity.dart'; @@ -382,6 +383,9 @@ class Grade10LabsRegistry { required String lessonTitle, String? lessonId, String? curriculumLessonId, + String? filePath, + String? unitKey, + String? semesterKey, bool? allowPreview, }) { final usePreview = allowPreview ?? previewModeEnabled; @@ -392,47 +396,110 @@ class Grade10LabsRegistry { return null; } - if (usePreview) { - // 1. Exact match on curriculumLessonId within candidate subject labs - if (curriculumLessonId != null && curriculumLessonId.isNotEmpty) { - for (final c in candidates) { - if (c.identity.curriculumLessonId == curriculumLessonId) { - return c; - } - } - } - - // 2. Lesson Title fuzzy matching strictly within candidate subject labs - final t = lessonTitle.toLowerCase().trim(); + // 1. Exact match on sourceMarkdown / filePath (Highest precision, 100% deterministic) + if (filePath != null && filePath.isNotEmpty) { + final cleanPath = filePath.replaceAll('\\', '/').trim(); for (final c in candidates) { - final ct = c.lessonAr.toLowerCase().trim(); - if (t == ct || (ct.length > 4 && (t.contains(ct) || ct.contains(t)))) { - return c; - } - } - - // 3. Lesson Key / Tool Key match strictly within candidate subject labs - if (lessonId != null && lessonId.isNotEmpty) { - for (final c in candidates) { - if (c.identity.lessonKey == lessonId || - c.identity.toolKey == lessonId || - c.identity.curriculumLessonId.endsWith(lessonId) || - c.identity.sourceMarkdown.contains(lessonId)) { + if (c.identity.sourceMarkdown.isNotEmpty && + (c.identity.sourceMarkdown == cleanPath || + cleanPath.endsWith(c.identity.sourceMarkdown) || + c.identity.sourceMarkdown.endsWith(cleanPath))) { + if (usePreview || c.identity.isPublished) { + AppLogger.event('VirtualLabResolvedByPath', details: { + 'subject': subjectTitle, + 'lesson': lessonTitle, + 'path': cleanPath, + 'lab': c.lessonAr, + }, tag: 'LAB_REGISTRY'); return c; } } } - - return null; } + // 2. Synthesized canonical slug match from filePath (e.g. math_10_semester_1_unit_01_lesson_02) + if (filePath != null && filePath.isNotEmpty) { + final slug = filePath + .replaceAll('.md', '') + .replaceAll('grade_10/', '') + .replaceAll('/', '_') + .trim(); + for (final c in candidates) { + if (c.identity.curriculumLessonId == slug) { + if (usePreview || c.identity.isPublished) { + AppLogger.event('VirtualLabResolvedBySlug', details: { + 'subject': subjectTitle, + 'lesson': lessonTitle, + 'slug': slug, + 'lab': c.lessonAr, + }, tag: 'LAB_REGISTRY'); + return c; + } + } + } + } + + // 3. Exact match on curriculumLessonId if (curriculumLessonId != null && curriculumLessonId.isNotEmpty) { for (final c in candidates) { - if (c.identity.curriculumLessonId == curriculumLessonId && c.identity.isPublished) { - return c; + if (c.identity.curriculumLessonId == curriculumLessonId) { + if (usePreview || c.identity.isPublished) { + AppLogger.event('VirtualLabResolvedByCurriculumId', details: { + 'subject': subjectTitle, + 'lesson': lessonTitle, + 'curriculumId': curriculumLessonId, + 'lab': c.lessonAr, + }, tag: 'LAB_REGISTRY'); + return c; + } } } } + + if (usePreview) { + // 4. Strict Title matching within candidate subject labs (only within same unit if provided) + final t = lessonTitle.toLowerCase().trim(); + for (final c in candidates) { + if (unitKey != null && c.identity.unitKey.isNotEmpty && c.identity.unitKey != unitKey) { + continue; + } + if (semesterKey != null && c.identity.semesterKey.isNotEmpty && c.identity.semesterKey != semesterKey) { + continue; + } + final ct = c.lessonAr.toLowerCase().trim(); + if (t == ct || (ct.length > 5 && (t.contains(ct) || ct.contains(t)))) { + AppLogger.event('VirtualLabResolvedByTitle', details: { + 'subject': subjectTitle, + 'lesson': lessonTitle, + 'match': ct, + 'lab': c.lessonAr, + }, tag: 'LAB_REGISTRY'); + return c; + } + } + + // 5. Lesson Key match strictly constrained to matching unit & semester + if (lessonId != null && lessonId.isNotEmpty) { + for (final c in candidates) { + if (unitKey != null && c.identity.unitKey.isNotEmpty && c.identity.unitKey != unitKey) { + continue; + } + if (semesterKey != null && c.identity.semesterKey.isNotEmpty && c.identity.semesterKey != semesterKey) { + continue; + } + if (c.identity.lessonKey == lessonId || c.identity.toolKey == lessonId) { + AppLogger.event('VirtualLabResolvedByKey', details: { + 'subject': subjectTitle, + 'lesson': lessonTitle, + 'key': lessonId, + 'lab': c.lessonAr, + }, tag: 'LAB_REGISTRY'); + return c; + } + } + } + } + return null; } diff --git a/apps/student_app/lib/presentation/screens/virtual_labs/subject_virtual_labs_view.dart b/apps/student_app/lib/presentation/screens/virtual_labs/subject_virtual_labs_view.dart index 471834c..3f024b1 100644 --- a/apps/student_app/lib/presentation/screens/virtual_labs/subject_virtual_labs_view.dart +++ b/apps/student_app/lib/presentation/screens/virtual_labs/subject_virtual_labs_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import '../../../core/theme/app_colors.dart'; +import '../../../core/utils/app_logger.dart'; import '../../../data/models/socratic_checkpoint_model.dart'; import '../../widgets/socratic_dialog.dart'; import 'labs_gallery_screen.dart'; @@ -17,6 +18,7 @@ class SubjectVirtualLabsView extends StatefulWidget { final Color primaryColor; final Widget? specializedToolWidget; final String? specializedToolTitle; + final String? selectedSemester; const SubjectVirtualLabsView({ super.key, @@ -24,6 +26,7 @@ class SubjectVirtualLabsView extends StatefulWidget { required this.primaryColor, this.specializedToolWidget, this.specializedToolTitle, + this.selectedSemester, }); @override @@ -34,7 +37,26 @@ class _SubjectVirtualLabsViewState extends State { int _selectedIndex = 0; bool _showSpecialized = false; + @override + void didUpdateWidget(covariant SubjectVirtualLabsView oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.selectedSemester != widget.selectedSemester) { + AppLogger.event('VirtualLabsSemesterChanged', details: { + 'subject': widget.subjectTitle, + 'from': oldWidget.selectedSemester, + 'to': widget.selectedSemester, + }, tag: 'VIRTUAL_LABS_VIEW'); + setState(() { + _selectedIndex = 0; + _showSpecialized = false; + }); + } + } + void _openGallery(BuildContext context) { + AppLogger.event('OpenVirtualLabsGallery', details: { + 'subject': widget.subjectTitle, + }, tag: 'VIRTUAL_LABS_VIEW'); Navigator.of(context).push( CupertinoPageRoute( builder: (_) => Scaffold( @@ -52,6 +74,12 @@ class _SubjectVirtualLabsViewState extends State { void _triggerCheckpoint( String q, List opts, int correct, String lessonTitle) { + AppLogger.event('LabCheckpointTriggered', details: { + 'lesson': lessonTitle, + 'question': q, + 'optionsCount': opts.length, + 'correctIndex': correct, + }, tag: 'VIRTUAL_LABS_VIEW'); final checkpoint = SocraticCheckpointModel( id: lessonTitle.hashCode & 0x7fffffff, questionText: q, @@ -79,10 +107,24 @@ class _SubjectVirtualLabsViewState extends State { Widget build(BuildContext context) { final allLabs = Grade10LabsRegistry.bySubjectNormalized(widget.subjectTitle); - final labs = Grade10LabsRegistry.previewModeEnabled + var candidateLabs = Grade10LabsRegistry.previewModeEnabled ? allLabs : allLabs.where((e) => e.identity.isPublished).toList(); + if (widget.selectedSemester != null && widget.selectedSemester!.isNotEmpty) { + final semesterLabs = candidateLabs.where((e) { + if (e.identity.semesterKey.isNotEmpty) { + return e.identity.semesterKey == widget.selectedSemester; + } + return true; // Keep standalone authoring tools accessible + }).toList(); + if (semesterLabs.isNotEmpty) { + candidateLabs = semesterLabs; + } + } + + final labs = candidateLabs; + if (labs.isEmpty) { if (widget.specializedToolWidget != null) { return widget.specializedToolWidget!; @@ -228,7 +270,13 @@ class _SubjectVirtualLabsViewState extends State { idx == labs.length) { final isSel = _showSpecialized; return InkWell( - onTap: () => setState(() => _showSpecialized = true), + onTap: () { + AppLogger.event('SelectSpecializedTool', details: { + 'subject': widget.subjectTitle, + 'tool': widget.specializedToolTitle, + }, tag: 'VIRTUAL_LABS_VIEW'); + setState(() => _showSpecialized = true); + }, borderRadius: BorderRadius.circular(10), child: Container( padding: const EdgeInsets.symmetric( @@ -263,6 +311,13 @@ class _SubjectVirtualLabsViewState extends State { final isSel = !_showSpecialized && activeIndex == idx; return InkWell( onTap: () { + AppLogger.event('SelectVirtualLabChip', details: { + 'subject': widget.subjectTitle, + 'index': idx, + 'lab': lab.lessonAr, + 'curriculumId': lab.identity.curriculumLessonId, + 'semester': lab.identity.semesterKey, + }, tag: 'VIRTUAL_LABS_VIEW'); setState(() { _showSpecialized = false; _selectedIndex = idx; diff --git a/apps/student_app/macos/Flutter/GeneratedPluginRegistrant.swift b/apps/student_app/macos/Flutter/GeneratedPluginRegistrant.swift index d111b45..3951f3f 100644 --- a/apps/student_app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/apps/student_app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,6 +8,7 @@ import Foundation import device_info_plus import flutter_secure_storage_macos import flutter_tts +import path_provider_foundation import shared_preferences_foundation import url_launcher_macos import video_player_avfoundation @@ -16,6 +17,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FlutterTtsPlugin.register(with: registry.registrar(forPlugin: "FlutterTtsPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) VideoPlayerPlugin.register(with: registry.registrar(forPlugin: "VideoPlayerPlugin")) diff --git a/apps/student_app/macos/Podfile.lock b/apps/student_app/macos/Podfile.lock index 24b2c8f..14bd08d 100644 --- a/apps/student_app/macos/Podfile.lock +++ b/apps/student_app/macos/Podfile.lock @@ -6,6 +6,9 @@ PODS: - flutter_tts (0.0.1): - FlutterMacOS - FlutterMacOS (1.0.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS @@ -20,6 +23,7 @@ DEPENDENCIES: - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) - flutter_tts (from `Flutter/ephemeral/.symlinks/plugins/flutter_tts/macos`) - FlutterMacOS (from `Flutter/ephemeral`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`) @@ -33,6 +37,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/flutter_tts/macos FlutterMacOS: :path: Flutter/ephemeral + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin url_launcher_macos: @@ -45,6 +51,7 @@ SPEC CHECKSUMS: flutter_secure_storage_macos: 7f45e30f838cf2659862a4e4e3ee1c347c2b3b54 flutter_tts: ae915565cc6948444b513acc8ee021993281e027 FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb url_launcher_macos: f87a979182d112f911de6820aefddaf56ee9fbfd video_player_avfoundation: 3453f792138786248960ca029747fcd9f318ef52 diff --git a/apps/student_app/pubspec.lock b/apps/student_app/pubspec.lock index c306ddb..1932a0c 100644 --- a/apps/student_app/pubspec.lock +++ b/apps/student_app/pubspec.lock @@ -49,14 +49,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" - code_assets: - dependency: transitive - description: - name: code_assets - sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8 - url: "https://pub.dev" - source: hosted - version: "1.2.1" collection: dependency: transitive description: @@ -224,14 +216,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.3.3" - hooks: - dependency: transitive - description: - name: hooks - sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba" - url: "https://pub.dev" - source: hosted - version: "2.0.2" html: dependency: transitive description: @@ -328,14 +312,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" - source: hosted - version: "1.3.0" matcher: dependency: transitive description: @@ -368,14 +344,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - objective_c: - dependency: transitive - description: - name: objective_c - sha256: b7fb95a6d9a4f009edd63dc5ac69f07420b23a16161c6dd8660290b59c602e8e - url: "https://pub.dev" - source: hosted - version: "9.5.0" package_config: dependency: transitive description: @@ -409,13 +377,13 @@ packages: source: hosted version: "2.3.1" path_provider_foundation: - dependency: transitive + dependency: "direct overridden" description: name: path_provider_foundation - sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699" + sha256: "16eef174aacb07e09c351502740fa6254c165757638eba1e9116b0a781201bbd" url: "https://pub.dev" source: hosted - version: "2.6.0" + version: "2.4.2" path_provider_linux: dependency: transitive description: @@ -464,22 +432,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.5+1" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "261236774e8b1d69cfc6b9eabbc96c40f25e7a2d6b171f3385d4f65d5734fb24" - url: "https://pub.dev" - source: hosted - version: "2.2.1" - record_use: - dependency: transitive - description: - name: record_use - sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed" - url: "https://pub.dev" - source: hosted - version: "0.6.0" shared_preferences: dependency: "direct main" description: @@ -749,14 +701,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" - yaml: - dependency: transitive - description: - name: yaml - sha256: f67cdd8e07d3c6329146aaef1ba043542b3134c12489f553ca9a7435d1068aea - url: "https://pub.dev" - source: hosted - version: "3.1.4" sdks: dart: ">=3.11.0 <4.0.0" - flutter: ">=3.38.4" + flutter: ">=3.38.0" diff --git a/apps/student_app/pubspec.yaml b/apps/student_app/pubspec.yaml index 51a87dd..d7311f6 100644 --- a/apps/student_app/pubspec.yaml +++ b/apps/student_app/pubspec.yaml @@ -45,6 +45,9 @@ dev_dependencies: flutter_lints: ^3.0.0 +dependency_overrides: + path_provider_foundation: 2.4.2 + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/apps/student_app/test/virtual_labs_smoke_test.dart b/apps/student_app/test/virtual_labs_smoke_test.dart index b3184b4..733a1a2 100644 --- a/apps/student_app/test/virtual_labs_smoke_test.dart +++ b/apps/student_app/test/virtual_labs_smoke_test.dart @@ -5,6 +5,8 @@ import 'package:student_app/presentation/screens/virtual_labs/lab_identity.dart' import 'package:student_app/presentation/screens/virtual_labs/labs_gallery_screen.dart'; import 'package:student_app/presentation/screens/virtual_labs/labs_registry.dart'; import 'package:student_app/presentation/screens/virtual_labs/subject_virtual_labs_view.dart'; +import 'package:student_app/presentation/screens/curriculum/arabic_interactive_lab_view.dart'; +import 'package:student_app/presentation/screens/curriculum/english_interactive_lab_view.dart'; void main() { test('registry covers all Grade-10 labs (48 entries, 13 subjects)', () { @@ -103,7 +105,7 @@ void main() { // History lesson 2 must NEVER return physics vector lab final historyLab = Grade10LabsRegistry.findForLesson( - subjectTitle: 'تاريخ الأردن (Jordan History 10)', + subjectTitle: 'التاريخ (History 10)', lessonTitle: 'الدرس الثاني: الإمبراطورية الساسانية', lessonId: 'lesson_02', ); @@ -119,7 +121,7 @@ void main() { // History lesson 1 MUST return Persian Empire lab, NOT physics! final historyUnit1Lab = Grade10LabsRegistry.findForLesson( - subjectTitle: 'تاريخ الأردن (Jordan History 10)', + subjectTitle: 'التاريخ (History 10)', lessonTitle: 'الدرس الأول: الإمبراطورية الفارسية: النشأة والتطور', lessonId: 'lesson_01', curriculumLessonId: 'history_10_semester_1_unit_01_lesson_01', @@ -824,6 +826,89 @@ void main() { await t.pump(); expect(checkpointTriggered, isTrue); }); + + testWidgets('arabic interactive lab view renders and supports tab navigation', + (t) async { + await t.binding.setSurfaceSize(const Size(1200, 2400)); + addTearDown(() => t.binding.setSurfaceSize(null)); + + await t.pumpWidget(const MaterialApp( + home: Scaffold( + body: ArabicInteractiveLabView(), + ), + )); + await t.pump(); + + // Default tab: الصرف والاشتقاق + expect(find.textContaining('مختبر الضاد اللغوي الذكي'), findsWidgets); + expect(find.text('ميزان الصرف والاشتقاق ⚖️'), findsWidgets); + expect(find.text('معمل البلاغة والبيان 💎'), findsWidgets); + expect(find.text('استوديو الإعراب والتراكيب 📜'), findsWidgets); + expect(find.text('العروض وموسيقى الشعر 🎵'), findsWidgets); + + // Verify root selector + expect(find.text('ك - ت - ب'), findsWidgets); + expect(find.text('ع - ل - م'), findsWidgets); + await t.tap(find.text('ع - ل - م').first); + await t.pump(); + + // Switch to Rhetoric tab + await t.tap(find.text('معمل البلاغة والبيان 💎').first); + await t.pumpAndSettle(); + expect(find.textContaining('الطباق'), findsWidgets); + + // Switch to Syntax tab + await t.tap(find.text('استوديو الإعراب والتراكيب 📜').first); + await t.pumpAndSettle(); + expect(find.textContaining('اختر الجملة لتحليل بنيتها الإعرابية'), findsWidgets); + + // Switch to Prosody tab + await t.tap(find.text('العروض وموسيقى الشعر 🎵').first); + await t.pumpAndSettle(); + expect(find.textContaining('بحر الكامل'), findsWidgets); + }); + + testWidgets('english interactive lab view renders and supports tab navigation', + (t) async { + await t.binding.setSurfaceSize(const Size(1200, 2400)); + addTearDown(() => t.binding.setSurfaceSize(null)); + + await t.pumpWidget(const MaterialApp( + home: Scaffold( + body: EnglishInteractiveLabView(), + ), + )); + await t.pump(); + + // Default tab: Grammar Matrix + expect(find.textContaining('Action Pack 10'), findsWidgets); + expect(find.text('مصفوفة القواعد (Grammar) 📐'), findsWidgets); + expect(find.text('الصوتيات والنبر (Phonetics) 🎙️'), findsWidgets); + expect(find.text('المفردات والمتلازمات 📚'), findsWidgets); + expect(find.text('تحدي الاستماع (Listening) ⚡'), findsWidgets); + + // Check grammar tense chips + expect(find.text('Present Simple'), findsWidgets); + expect(find.text('Present Perfect'), findsWidgets); + await t.tap(find.text('Present Perfect').first); + await t.pumpAndSettle(); + + // Switch to Phonetics tab + await t.tap(find.text('الصوتيات والنبر (Phonetics) 🎙️').first); + await t.pumpAndSettle(); + expect(find.textContaining('Minimal Pairs'), findsWidgets); + expect(find.textContaining('Syllable Stress Shift'), findsWidgets); + + // Switch to Vocab tab + await t.tap(find.text('المفردات والمتلازمات 📚').first); + await t.pumpAndSettle(); + expect(find.textContaining('Thematic Keywords'), findsWidgets); + + // Switch to Listening tab + await t.tap(find.text('تحدي الاستماع (Listening) ⚡').first); + await t.pumpAndSettle(); + expect(find.textContaining('Gulf of Aqaba'), findsWidgets); + }); } diff --git a/apps/teacher_app/pubspec.lock b/apps/teacher_app/pubspec.lock index 1e9caa8..b2ddf06 100644 --- a/apps/teacher_app/pubspec.lock +++ b/apps/teacher_app/pubspec.lock @@ -340,10 +340,10 @@ packages: dependency: transitive description: name: matcher - sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 url: "https://pub.dev" source: hosted - version: "0.12.18" + version: "0.12.19" material_color_utilities: dependency: transitive description: @@ -356,10 +356,10 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" nested: dependency: transitive description: @@ -585,10 +585,10 @@ packages: dependency: transitive description: name: test_api - sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" + sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e" url: "https://pub.dev" source: hosted - version: "0.7.9" + version: "0.7.11" typed_data: dependency: transitive description: diff --git a/apps/teacher_app/pubspec.yaml b/apps/teacher_app/pubspec.yaml index 5760056..ca27c00 100644 --- a/apps/teacher_app/pubspec.yaml +++ b/apps/teacher_app/pubspec.yaml @@ -43,6 +43,9 @@ dev_dependencies: flutter_lints: ^3.0.0 +dependency_overrides: + path_provider_foundation: 2.4.2 + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/backend/storage/curriculum/manifest.json b/backend/storage/curriculum/manifest.json index 821c7b5..99f307c 100644 --- a/backend/storage/curriculum/manifest.json +++ b/backend/storage/curriculum/manifest.json @@ -17,7 +17,7 @@ "outcomes": [ "مقدمة ومشروع الوحدة: أنظمة المعادلات في حياتنا" ], - "file": "grade_10\/math_10\/semester_1\/unit_01\/intro_and_project.md" + "file": "grade_10/math_10/semester_1/unit_01/intro_and_project.md" }, { "id": "lesson_01", @@ -25,7 +25,7 @@ "outcomes": [ "الدرس 1: حلُّ معادلاتٍ خاصّةٍ (Solving Special Equations)" ], - "file": "grade_10\/math_10\/semester_1\/unit_01\/lesson_01.md" + "file": "grade_10/math_10/semester_1/unit_01/lesson_01.md" }, { "id": "geogebra_lab", @@ -33,7 +33,7 @@ "outcomes": [ "معملُ برمجيةِ جيوجبرا: حلُّ أنظمةِ المعادلاتِ بيانياً (Solving Systems of Equations Graphically)" ], - "file": "grade_10\/math_10\/semester_1\/unit_01\/geogebra_lab.md" + "file": "grade_10/math_10/semester_1/unit_01/geogebra_lab.md" }, { "id": "lesson_02", @@ -41,7 +41,7 @@ "outcomes": [ "الدرس 2: حلُّ نظامٍ مُكوَّنٍ من معادلةٍ خطيّةٍ ومعادلةٍ تربيعيّةٍ (Solving a System of Linear and Quadratic Equations)" ], - "file": "grade_10\/math_10\/semester_1\/unit_01\/lesson_02.md" + "file": "grade_10/math_10/semester_1/unit_01/lesson_02.md" }, { "id": "lesson_03", @@ -49,7 +49,7 @@ "outcomes": [ "الدرس 3: حلُّ نظامٍ مُكوَّنٍ من معادلتيْنِ تربيعيّتيْنِ (Solving a System of Two Quadratic Equations)" ], - "file": "grade_10\/math_10\/semester_1\/unit_01\/lesson_03.md" + "file": "grade_10/math_10/semester_1/unit_01/lesson_03.md" }, { "id": "unit_exam", @@ -57,7 +57,7 @@ "outcomes": [ "اختبارُ نهايةِ الوحدةِ الأولى: المعادلات" ], - "file": "grade_10\/math_10\/semester_1\/unit_01\/unit_exam.md" + "file": "grade_10/math_10/semester_1/unit_01/unit_exam.md" } ] }, @@ -70,7 +70,7 @@ "outcomes": [ "مقدمة ومشروع الوحدة: استعمالات علمية لخصائص الدائرة" ], - "file": "grade_10\/math_10\/semester_1\/unit_02\/intro_and_project.md" + "file": "grade_10/math_10/semester_1/unit_02/intro_and_project.md" }, { "id": "lesson_01", @@ -78,7 +78,7 @@ "outcomes": [ "الدرس 1: أوتارُ الدائرةِ، وأقطارُها، ومماساتُها (Chords, Diameters and Tangents of a Circle)" ], - "file": "grade_10\/math_10\/semester_1\/unit_02\/lesson_01.md" + "file": "grade_10/math_10/semester_1/unit_02/lesson_01.md" }, { "id": "lesson_02", @@ -86,7 +86,7 @@ "outcomes": [ "الدرس 2: الأقواسُ والقطاعاتُ الدائريّةُ (Arcs and Sectors)" ], - "file": "grade_10\/math_10\/semester_1\/unit_02\/lesson_02.md" + "file": "grade_10/math_10/semester_1/unit_02/lesson_02.md" }, { "id": "lesson_03", @@ -94,7 +94,7 @@ "outcomes": [ "الدرس 3: الزوايا في الدائرة (Angles in a Circle)" ], - "file": "grade_10\/math_10\/semester_1\/unit_02\/lesson_03.md" + "file": "grade_10/math_10/semester_1/unit_02/lesson_03.md" }, { "id": "lesson_04", @@ -102,7 +102,7 @@ "outcomes": [ "الدرس 4: معادلةُ الدائرةِ (Equation of a Circle)" ], - "file": "grade_10\/math_10\/semester_1\/unit_02\/lesson_04.md" + "file": "grade_10/math_10/semester_1/unit_02/lesson_04.md" }, { "id": "unit_exam", @@ -110,7 +110,7 @@ "outcomes": [ "اختبارُ نهايةِ الوحدةِ الثانية: الدائرة" ], - "file": "grade_10\/math_10\/semester_1\/unit_02\/unit_exam.md" + "file": "grade_10/math_10/semester_1/unit_02/unit_exam.md" } ] }, @@ -123,7 +123,7 @@ "outcomes": [ "مقدمة ومشروع الوحدة: إنشاء نظام إحداثي جديد" ], - "file": "grade_10\/math_10\/semester_1\/unit_03\/intro_and_project.md" + "file": "grade_10/math_10/semester_1/unit_03/intro_and_project.md" }, { "id": "lesson_01", @@ -131,7 +131,7 @@ "outcomes": [ "الدرس 1: النِّسَبُ المُثلَّثيَّةُ (Trigonometric Ratios)" ], - "file": "grade_10\/math_10\/semester_1\/unit_03\/lesson_01.md" + "file": "grade_10/math_10/semester_1/unit_03/lesson_01.md" }, { "id": "lesson_02", @@ -139,7 +139,7 @@ "outcomes": [ "الدرس 2: النِّسَبُ المُثلَّثيَّةُ للزوايا ضِمْنَ الدَّوْرَةِ الواحدةِ (Trigonometric Ratios within One Revolution)" ], - "file": "grade_10\/math_10\/semester_1\/unit_03\/lesson_02.md" + "file": "grade_10/math_10/semester_1/unit_03/lesson_02.md" }, { "id": "lesson_03", @@ -147,7 +147,7 @@ "outcomes": [ "الدرس 3: تمثيلُ الاقتراناتِ المُثلَّثيَّةِ (Graphing Trigonometric Functions)" ], - "file": "grade_10\/math_10\/semester_1\/unit_03\/lesson_03.md" + "file": "grade_10/math_10/semester_1/unit_03/lesson_03.md" }, { "id": "lesson_04", @@ -155,7 +155,7 @@ "outcomes": [ "الدرس 4: حَلُّ المُعادلاتِ المُثلَّثيَّةِ (Solving Trigonometric Equations)" ], - "file": "grade_10\/math_10\/semester_1\/unit_03\/lesson_04.md" + "file": "grade_10/math_10/semester_1/unit_03/lesson_04.md" }, { "id": "unit_exam", @@ -163,7 +163,7 @@ "outcomes": [ "اختبارُ نهايةِ الوحدةِ الثالثة: حساب المثلثات" ], - "file": "grade_10\/math_10\/semester_1\/unit_03\/unit_exam.md" + "file": "grade_10/math_10/semester_1/unit_03/unit_exam.md" } ] }, @@ -176,7 +176,7 @@ "outcomes": [ "مقدمة ومشروع الوحدة: صنع كلينومتر واستعماله" ], - "file": "grade_10\/math_10\/semester_1\/unit_04\/intro_and_project.md" + "file": "grade_10/math_10/semester_1/unit_04/intro_and_project.md" }, { "id": "lesson_01", @@ -184,7 +184,7 @@ "outcomes": [ "الدرس 1: الاتجاهُ مِنَ الشَّمالِ (Bearings)" ], - "file": "grade_10\/math_10\/semester_1\/unit_04\/lesson_01.md" + "file": "grade_10/math_10/semester_1/unit_04/lesson_01.md" }, { "id": "lesson_02", @@ -192,7 +192,7 @@ "outcomes": [ "الدرس 2: قانونُ الجُيوبِ (The Law of Sines)" ], - "file": "grade_10\/math_10\/semester_1\/unit_04\/lesson_02.md" + "file": "grade_10/math_10/semester_1/unit_04/lesson_02.md" }, { "id": "lesson_03", @@ -200,7 +200,7 @@ "outcomes": [ "الدرس 3: قانونُ جُيوبِ التَّمامِ (The Law of Cosines)" ], - "file": "grade_10\/math_10\/semester_1\/unit_04\/lesson_03.md" + "file": "grade_10/math_10/semester_1/unit_04/lesson_03.md" }, { "id": "lesson_04", @@ -208,7 +208,7 @@ "outcomes": [ "الدرس 4: استعمالُ جَيْبِ الزاويةِ لإيجادِ مِساحةِ المُثلَّثِ (Area of a Triangle Using Sine)" ], - "file": "grade_10\/math_10\/semester_1\/unit_04\/lesson_04.md" + "file": "grade_10/math_10/semester_1/unit_04/lesson_04.md" }, { "id": "lesson_05", @@ -216,7 +216,7 @@ "outcomes": [ "الدرس 5: حَلُّ مسائلَ ثُلاثيَّةِ الأبعادِ (Solving Three-Dimensional Problems)" ], - "file": "grade_10\/math_10\/semester_1\/unit_04\/lesson_05.md" + "file": "grade_10/math_10/semester_1/unit_04/lesson_05.md" }, { "id": "unit_exam", @@ -224,7 +224,7 @@ "outcomes": [ "اختبارُ نهايةِ الوحدةِ الرابعة: تطبيقات المثلثات" ], - "file": "grade_10\/math_10\/semester_1\/unit_04\/unit_exam.md" + "file": "grade_10/math_10/semester_1/unit_04/unit_exam.md" } ] } @@ -243,7 +243,7 @@ "items": [ { "title": "ورقة عمل: الأسس والمعادلات", - "file": "grade_10\/math_10\/semester_1\/resources\/worksheet_1.md" + "file": "grade_10/math_10/semester_1/resources/worksheet_1.md" } ] }, @@ -278,7 +278,7 @@ { "id": "intro_and_project", "title": "مقدمة ومشروع الوحدة: نمذجة علاقات باستعمال كثيرات الحدود", - "file": "grade_10\/math_10\/semester_2\/unit_05\/intro_and_project.md", + "file": "grade_10/math_10/semester_2/unit_05/intro_and_project.md", "outcomes": [ "مقدمة ومشروع الوحدة: نمذجة علاقات باستعمال كثيرات الحدود" ] @@ -286,7 +286,7 @@ { "id": "lesson_01", "title": "الدرس 1: اقتراناتُ كثيراتِ الحدودِ (Polynomial Functions)", - "file": "grade_10\/math_10\/semester_2\/unit_05\/lesson_01.md", + "file": "grade_10/math_10/semester_2/unit_05/lesson_01.md", "outcomes": [ "الدرس 1: اقتراناتُ كثيراتِ الحدودِ (Polynomial Functions)" ] @@ -294,7 +294,7 @@ { "id": "lesson_02", "title": "الدرس 2: قسمةُ كثيراتِ الحدودِ والاقتراناتُ النسبيّةُ (Dividing Polynomials and Rational Functions)", - "file": "grade_10\/math_10\/semester_2\/unit_05\/lesson_02.md", + "file": "grade_10/math_10/semester_2/unit_05/lesson_02.md", "outcomes": [ "الدرس 2: قسمةُ كثيراتِ الحدودِ والاقتراناتُ النسبيّةُ (Dividing Polynomials and Rational Functions)" ] @@ -302,7 +302,7 @@ { "id": "lesson_03", "title": "الدرس 3: تركيبُ الاقتراناتِ (Composition of Functions)", - "file": "grade_10\/math_10\/semester_2\/unit_05\/lesson_03.md", + "file": "grade_10/math_10/semester_2/unit_05/lesson_03.md", "outcomes": [ "الدرس 3: تركيبُ الاقتراناتِ (Composition of Functions)" ] @@ -310,7 +310,7 @@ { "id": "lesson_04", "title": "الدرس 4: الاقترانُ العكسيُّ (Inverse Functions)", - "file": "grade_10\/math_10\/semester_2\/unit_05\/lesson_04.md", + "file": "grade_10/math_10/semester_2/unit_05/lesson_04.md", "outcomes": [ "الدرس 4: الاقترانُ العكسيُّ (Inverse Functions)" ] @@ -318,7 +318,7 @@ { "id": "lesson_05", "title": "الدرس 5: المتتالياتُ (Sequences)", - "file": "grade_10\/math_10\/semester_2\/unit_05\/lesson_05.md", + "file": "grade_10/math_10/semester_2/unit_05/lesson_05.md", "outcomes": [ "الدرس 5: المتتالياتُ (Sequences)" ] @@ -326,7 +326,7 @@ { "id": "unit_exam", "title": "اختبارُ نهايةِ الوحدةِ الخامسة: الاقترانات", - "file": "grade_10\/math_10\/semester_2\/unit_05\/unit_exam.md", + "file": "grade_10/math_10/semester_2/unit_05/unit_exam.md", "outcomes": [ "اختبارُ نهايةِ الوحدةِ الخامسة: الاقترانات" ] @@ -339,7 +339,7 @@ { "id": "geogebra_lab", "title": "معملُ برمجية جيوجبرا: استكشافُ ميلِ مماس المنحنى (Exploring Slope of Tangent with GeoGebra)", - "file": "grade_10\/math_10\/semester_2\/unit_06\/geogebra_lab.md", + "file": "grade_10/math_10/semester_2/unit_06/geogebra_lab.md", "outcomes": [ "معملُ برمجية جيوجبرا: استكشافُ ميلِ مماس المنحنى (Exploring Slope of Tangent with GeoGebra)" ] @@ -347,7 +347,7 @@ { "id": "intro_and_project", "title": "مقدمة ومشروع الوحدة: عمل صندوق حجمه أكبر ما يمكن", - "file": "grade_10\/math_10\/semester_2\/unit_06\/intro_and_project.md", + "file": "grade_10/math_10/semester_2/unit_06/intro_and_project.md", "outcomes": [ "مقدمة ومشروع الوحدة: عمل صندوق حجمه أكبر ما يمكن" ] @@ -355,7 +355,7 @@ { "id": "lesson_01", "title": "الدرس 1: تقديرُ ميلِ المنحنى (Estimating the Slope of a Curve)", - "file": "grade_10\/math_10\/semester_2\/unit_06\/lesson_01.md", + "file": "grade_10/math_10/semester_2/unit_06/lesson_01.md", "outcomes": [ "الدرس 1: تقديرُ ميلِ المنحنى (Estimating the Slope of a Curve)" ] @@ -363,7 +363,7 @@ { "id": "lesson_02", "title": "الدرس 2: الاشتقاقُ (Differentiation)", - "file": "grade_10\/math_10\/semester_2\/unit_06\/lesson_02.md", + "file": "grade_10/math_10/semester_2/unit_06/lesson_02.md", "outcomes": [ "الدرس 2: الاشتقاقُ (Differentiation)" ] @@ -371,7 +371,7 @@ { "id": "lesson_03", "title": "الدرس 3: القِيَمُ العُظْمى والقِيَمُ الصُّغْرى (Maximum and Minimum Values)", - "file": "grade_10\/math_10\/semester_2\/unit_06\/lesson_03.md", + "file": "grade_10/math_10/semester_2/unit_06/lesson_03.md", "outcomes": [ "الدرس 3: القِيَمُ العُظْمى والقِيَمُ الصُّغْرى (Maximum and Minimum Values)" ] @@ -379,7 +379,7 @@ { "id": "unit_exam", "title": "اختبارُ نهايةِ الوحدةِ السادسة: المشتقات", - "file": "grade_10\/math_10\/semester_2\/unit_06\/unit_exam.md", + "file": "grade_10/math_10/semester_2/unit_06/unit_exam.md", "outcomes": [ "اختبارُ نهايةِ الوحدةِ السادسة: المشتقات" ] @@ -392,7 +392,7 @@ { "id": "intro_and_project", "title": "مقدمة ومشروع الوحدة: المتجهات في الجغرافيا", - "file": "grade_10\/math_10\/semester_2\/unit_07\/intro_and_project.md", + "file": "grade_10/math_10/semester_2/unit_07/intro_and_project.md", "outcomes": [ "مقدمة ومشروع الوحدة: المتجهات في الجغرافيا" ] @@ -400,7 +400,7 @@ { "id": "lesson_01", "title": "الدرس 1: المُتَّجَهاتُ في المستوى الإحداثيِّ (Vectors in the Coordinate Plane)", - "file": "grade_10\/math_10\/semester_2\/unit_07\/lesson_01.md", + "file": "grade_10/math_10/semester_2/unit_07/lesson_01.md", "outcomes": [ "الدرس 1: المُتَّجَهاتُ في المستوى الإحداثيِّ (Vectors in the Coordinate Plane)" ] @@ -408,7 +408,7 @@ { "id": "lesson_02", "title": "الدرس 2: جمعُ المتجهاتِ وطرحُها (Adding and Subtracting Vectors)", - "file": "grade_10\/math_10\/semester_2\/unit_07\/lesson_02.md", + "file": "grade_10/math_10/semester_2/unit_07/lesson_02.md", "outcomes": [ "الدرس 2: جمعُ المتجهاتِ وطرحُها (Adding and Subtracting Vectors)" ] @@ -416,7 +416,7 @@ { "id": "lesson_03", "title": "الدرس 3: الضربُ القياسيُّ (The Scalar Product)", - "file": "grade_10\/math_10\/semester_2\/unit_07\/lesson_03.md", + "file": "grade_10/math_10/semester_2/unit_07/lesson_03.md", "outcomes": [ "الدرس 3: الضربُ القياسيُّ (The Scalar Product)" ] @@ -424,7 +424,7 @@ { "id": "unit_exam", "title": "اختبارُ نهايةِ الوحدةِ السابعة: المتجهات", - "file": "grade_10\/math_10\/semester_2\/unit_07\/unit_exam.md", + "file": "grade_10/math_10/semester_2/unit_07/unit_exam.md", "outcomes": [ "اختبارُ نهايةِ الوحدةِ السابعة: المتجهات" ] @@ -437,7 +437,7 @@ { "id": "geogebra_lab", "title": "معملُ برمجية جيوجبرا: رسمُ المستقيمِ الأفضلِ مطابقةً (Line of Best Fit with GeoGebra)", - "file": "grade_10\/math_10\/semester_2\/unit_08\/geogebra_lab.md", + "file": "grade_10/math_10/semester_2/unit_08/geogebra_lab.md", "outcomes": [ "معملُ برمجية جيوجبرا: رسمُ المستقيمِ الأفضلِ مطابقةً (Line of Best Fit with GeoGebra)" ] @@ -445,7 +445,7 @@ { "id": "intro_and_project", "title": "مقدمة ومشروع الوحدة: مستوى الأقارب التعليمي", - "file": "grade_10\/math_10\/semester_2\/unit_08\/intro_and_project.md", + "file": "grade_10/math_10/semester_2/unit_08/intro_and_project.md", "outcomes": [ "مقدمة ومشروع الوحدة: مستوى الأقارب التعليمي" ] @@ -453,7 +453,7 @@ { "id": "lesson_01", "title": "الدرس 1: أشكالُ الانتشارِ (Scatter Plots)", - "file": "grade_10\/math_10\/semester_2\/unit_08\/lesson_01.md", + "file": "grade_10/math_10/semester_2/unit_08/lesson_01.md", "outcomes": [ "الدرس 1: أشكالُ الانتشارِ (Scatter Plots)" ] @@ -461,7 +461,7 @@ { "id": "lesson_02", "title": "الدرس 2: المُنحنى التَّكراريُّ التَّراكميُّ (Cumulative Frequency Curve)", - "file": "grade_10\/math_10\/semester_2\/unit_08\/lesson_02.md", + "file": "grade_10/math_10/semester_2/unit_08/lesson_02.md", "outcomes": [ "الدرس 2: المُنحنى التَّكراريُّ التَّراكميُّ (Cumulative Frequency Curve)" ] @@ -469,7 +469,7 @@ { "id": "lesson_03", "title": "الدرس 3: مقاييسُ التَّشتُّتِ للجداولِ التَّكراريَّةِ ذاتِ الفئاتِ (Measures of Dispersion for Grouped Frequency Tables)", - "file": "grade_10\/math_10\/semester_2\/unit_08\/lesson_03.md", + "file": "grade_10/math_10/semester_2/unit_08/lesson_03.md", "outcomes": [ "الدرس 3: مقاييسُ التَّشتُّتِ للجداولِ التَّكراريَّةِ ذاتِ الفئاتِ (Measures of Dispersion for Grouped Frequency Tables)" ] @@ -477,7 +477,7 @@ { "id": "lesson_04", "title": "الدرس 4: احتمالاتُ الحوادثِ المُتنافيةِ (Probability of Mutually Exclusive Events)", - "file": "grade_10\/math_10\/semester_2\/unit_08\/lesson_04.md", + "file": "grade_10/math_10/semester_2/unit_08/lesson_04.md", "outcomes": [ "الدرس 4: احتمالاتُ الحوادثِ المُتنافيةِ (Probability of Mutually Exclusive Events)" ] @@ -485,7 +485,7 @@ { "id": "lesson_05", "title": "الدرس 5: احتمالاتُ الحوادثِ المُستقلَّةِ والحوادثِ غيرِ المُستقلَّةِ (Probability of Independent and Dependent Events)", - "file": "grade_10\/math_10\/semester_2\/unit_08\/lesson_05.md", + "file": "grade_10/math_10/semester_2/unit_08/lesson_05.md", "outcomes": [ "الدرس 5: احتمالاتُ الحوادثِ المُستقلَّةِ والحوادثِ غيرِ المُستقلَّةِ (Probability of Independent and Dependent Events)" ] @@ -493,7 +493,7 @@ { "id": "unit_exam", "title": "اختبارُ نهايةِ الوحدةِ الثامنة: الإحصاء والاحتمالات", - "file": "grade_10\/math_10\/semester_2\/unit_08\/unit_exam.md", + "file": "grade_10/math_10/semester_2/unit_08/unit_exam.md", "outcomes": [ "اختبارُ نهايةِ الوحدةِ الثامنة: الإحصاء والاحتمالات" ] @@ -522,7 +522,7 @@ "Present Continuous for temporary situations and changing trends", "State verbs vs action verbs clinic" ], - "file": "grade_10\/english_10\/semester_1\/unit_01_looking_good\/lesson_1a_vocabulary_grammar.md" + "file": "grade_10/english_10/semester_1/unit_01_looking_good/lesson_1a_vocabulary_grammar.md" }, { "id": "u1_l2_appearance_fabrics", @@ -532,7 +532,7 @@ "Patterns (checked, plain, striped, polka dots, embroidered thobe)", "Describing outfits and traditional vs modern clothing in Jordan" ], - "file": "grade_10\/english_10\/semester_1\/unit_01_looking_good\/lesson_2a_vocabulary_appearance.md" + "file": "grade_10/english_10/semester_1/unit_01_looking_good/lesson_2a_vocabulary_appearance.md" }, { "id": "u1_l3_power_of_appearance", @@ -542,17 +542,17 @@ "The Doctor's White Coat Experiment & cognitive focus", "Phrasal verbs: look up to, look down on, bring out the best in" ], - "file": "grade_10\/english_10\/semester_1\/unit_01_looking_good\/lesson_3a_4a_power_of_appearance.md" + "file": "grade_10/english_10/semester_1/unit_01_looking_good/lesson_3a_4a_power_of_appearance.md" }, { "id": "u1_l4_articles_speaking", "title": "Lesson 5A & 6A: Grammar (Articles) & Participating in Conversations", "outcomes": [ - "Rules for zero article, a\/an, and the", - "Pronunciation of \/ðə\/ vs \/ðiː\/", + "Rules for zero article, a/an, and the", + "Pronunciation of /ðə/ vs /ðiː/", "Speaking strategies: clarifying messages and interrupting politely" ], - "file": "grade_10\/english_10\/semester_1\/unit_01_looking_good\/lesson_5a_6a_articles_speaking.md" + "file": "grade_10/english_10/semester_1/unit_01_looking_good/lesson_5a_6a_articles_speaking.md" }, { "id": "u1_l5_writing_informal_email", @@ -561,7 +561,7 @@ "Structuring an informal email (greetings, chatty style, omissions, sign-offs)", "Using abbreviations and contractions accurately" ], - "file": "grade_10\/english_10\/semester_1\/unit_01_looking_good\/lesson_7a_writing_informal_email.md" + "file": "grade_10/english_10/semester_1/unit_01_looking_good/lesson_7a_writing_informal_email.md" } ] }, @@ -575,7 +575,7 @@ "Present Perfect Simple (results) vs Present Perfect Continuous (duration)", "Astronomy and space exploration terminology" ], - "file": "grade_10\/english_10\/semester_1\/unit_02_the_digital_mind\/lesson_1a_voyager_present_perfect.md" + "file": "grade_10/english_10/semester_1/unit_02_the_digital_mind/lesson_1a_voyager_present_perfect.md" }, { "id": "u2_l2_ai_science_fact", @@ -585,7 +585,7 @@ "AI safety, ethics, and future societal impacts", "Word formation: verbs to nouns (achieve -> achievement, develop -> development)" ], - "file": "grade_10\/english_10\/semester_1\/unit_02_the_digital_mind\/lesson_2a_ai_science_fact.md" + "file": "grade_10/english_10/semester_1/unit_02_the_digital_mind/lesson_2a_ai_science_fact.md" }, { "id": "u2_l3_verb_patterns_mind", @@ -595,7 +595,7 @@ "Verb patterns (-ing vs to-infinitive vs bare infinitive)", "Verbs with meaning changes (remember to do vs remember doing)" ], - "file": "grade_10\/english_10\/semester_1\/unit_02_the_digital_mind\/lesson_3a_4a_verb_patterns_mind.md" + "file": "grade_10/english_10/semester_1/unit_02_the_digital_mind/lesson_3a_4a_verb_patterns_mind.md" }, { "id": "u2_l4_drones_blog_writing", @@ -604,7 +604,7 @@ "Drones in search and rescue, commercial deliveries, and aerial filming", "Writing a balanced tech blog post with balanced arguments" ], - "file": "grade_10\/english_10\/semester_1\/unit_02_the_digital_mind\/lesson_5a_7a_drones_blog_writing.md" + "file": "grade_10/english_10/semester_1/unit_02_the_digital_mind/lesson_5a_7a_drones_blog_writing.md" } ] }, @@ -618,16 +618,16 @@ "Inspiring athlete biography and wheelchair motocross (WCMX)", "Narrative tenses: Past Simple, Past Continuous, and Past Perfect" ], - "file": "grade_10\/english_10\/semester_1\/unit_03_active_and_healthy\/lesson_1a_aaron_wheelz_narrative_tenses.md" + "file": "grade_10/english_10/semester_1/unit_03_active_and_healthy/lesson_1a_aaron_wheelz_narrative_tenses.md" }, { "id": "u3_l2_fitness_used_to", - "title": "Lesson 2A–4A: Fitness, Injuries & 'Used to \/ Would'", + "title": "Lesson 2A–4A: Fitness, Injuries & 'Used to / Would'", "outcomes": [ "Fitness collocations and medical emergencies (911)", "Used to vs would for past habits and states" ], - "file": "grade_10\/english_10\/semester_1\/unit_03_active_and_healthy\/lesson_2a_4a_fitness_used_to.md" + "file": "grade_10/english_10/semester_1/unit_03_active_and_healthy/lesson_2a_4a_fitness_used_to.md" }, { "id": "u3_l3_mediterranean_diet_story", @@ -636,7 +636,7 @@ "Nutrition, superfoods, and school canteen initiatives", "Writing a dramatic short story with action linkers" ], - "file": "grade_10\/english_10\/semester_1\/unit_03_active_and_healthy\/lesson_6a_7a_mediterranean_diet_story.md" + "file": "grade_10/english_10/semester_1/unit_03_active_and_healthy/lesson_6a_7a_mediterranean_diet_story.md" } ] }, @@ -650,7 +650,7 @@ "Modals of necessity (must, have to), prohibition, and advice", "Airport and flight procedures" ], - "file": "grade_10\/english_10\/semester_1\/unit_04_time_to_move\/lesson_1a_air_travel_modals.md" + "file": "grade_10/english_10/semester_1/unit_04_time_to_move/lesson_1a_air_travel_modals.md" }, { "id": "u4_l2_petra_relative_clauses", @@ -659,7 +659,7 @@ "Family holiday rules and Bedouin hospitality in Petra", "Defining vs non-defining relative clauses (who, which, whose, where)" ], - "file": "grade_10\/english_10\/semester_1\/unit_04_time_to_move\/lesson_2a_4a_petra_relative_clauses.md" + "file": "grade_10/english_10/semester_1/unit_04_time_to_move/lesson_2a_4a_petra_relative_clauses.md" }, { "id": "u4_l3_urban_transport_formal_enquiry", @@ -668,7 +668,7 @@ "Urban transport, smog, and sustainability", "Composing a formal enquiry email with indirect questions" ], - "file": "grade_10\/english_10\/semester_1\/unit_04_time_to_move\/lesson_5a_7a_urban_transport_formal_enquiry.md" + "file": "grade_10/english_10/semester_1/unit_04_time_to_move/lesson_5a_7a_urban_transport_formal_enquiry.md" } ] }, @@ -683,7 +683,7 @@ "Career trends in AI and cloud engineering", "Writing an academic personal statement for university admission" ], - "file": "grade_10\/english_10\/semester_1\/unit_05_the_next_step\/lesson_1a_5a_future_work_personal_statement.md" + "file": "grade_10/english_10/semester_1/unit_05_the_next_step/lesson_1a_5a_future_work_personal_statement.md" } ] }, @@ -693,7 +693,7 @@ { "id": "lesson_01", "title": "Unit 01 - Lesson 1: Grammar & Vocabulary (Present Simple & Continuous, Verb phrases with dress)", - "file": "grade_10\/english_10\/semester_1\/unit_01\/lesson_01.md", + "file": "grade_10/english_10/semester_1/unit_01/lesson_01.md", "outcomes": [ "Unit 01 - Lesson 1: Grammar & Vocabulary (Present Simple & Continuous, Verb phrases with dress)" ] @@ -701,7 +701,7 @@ { "id": "lesson_02", "title": "Unit 01 - Lesson 2: Vocabulary & Listening (Appearance, Clothes & Accessories, Jobs Podcast)", - "file": "grade_10\/english_10\/semester_1\/unit_01\/lesson_02.md", + "file": "grade_10/english_10/semester_1/unit_01/lesson_02.md", "outcomes": [ "Unit 01 - Lesson 2: Vocabulary & Listening (Appearance, Clothes & Accessories, Jobs Podcast)" ] @@ -709,7 +709,7 @@ { "id": "lesson_03", "title": "Unit 01 - Lesson 3: Reading (The Power of Appearance)", - "file": "grade_10\/english_10\/semester_1\/unit_01\/lesson_03.md", + "file": "grade_10/english_10/semester_1/unit_01/lesson_03.md", "outcomes": [ "Unit 01 - Lesson 3: Reading (The Power of Appearance)" ] @@ -717,7 +717,7 @@ { "id": "lesson_04", "title": "Unit 01 - Lesson 4: Grammar (Articles & Pronunciation)", - "file": "grade_10\/english_10\/semester_1\/unit_01\/lesson_04.md", + "file": "grade_10/english_10/semester_1/unit_01/lesson_04.md", "outcomes": [ "Unit 01 - Lesson 4: Grammar (Articles & Pronunciation)" ] @@ -725,7 +725,7 @@ { "id": "lesson_05", "title": "Unit 01 - Lesson 5: Speaking (Participating in Conversations)", - "file": "grade_10\/english_10\/semester_1\/unit_01\/lesson_05.md", + "file": "grade_10/english_10/semester_1/unit_01/lesson_05.md", "outcomes": [ "Unit 01 - Lesson 5: Speaking (Participating in Conversations)" ] @@ -733,7 +733,7 @@ { "id": "lesson_06", "title": "Unit 01 - Lesson 6: Writing (An Informal Email)", - "file": "grade_10\/english_10\/semester_1\/unit_01\/lesson_06.md", + "file": "grade_10/english_10/semester_1/unit_01/lesson_06.md", "outcomes": [ "Unit 01 - Lesson 6: Writing (An Informal Email)" ] @@ -746,7 +746,7 @@ { "id": "lesson_01", "title": "Unit 02 - Lesson 1: Grammar & Vocabulary (Present Perfect Simple & Continuous, Scientific Research)", - "file": "grade_10\/english_10\/semester_1\/unit_02\/lesson_01.md", + "file": "grade_10/english_10/semester_1/unit_02/lesson_01.md", "outcomes": [ "Unit 02 - Lesson 1: Grammar & Vocabulary (Present Perfect Simple & Continuous, Scientific Research)" ] @@ -754,7 +754,7 @@ { "id": "lesson_02", "title": "Unit 02 - Lesson 2: Reading (Science Fiction or Science Fact?)", - "file": "grade_10\/english_10\/semester_1\/unit_02\/lesson_02.md", + "file": "grade_10/english_10/semester_1/unit_02/lesson_02.md", "outcomes": [ "Unit 02 - Lesson 2: Reading (Science Fiction or Science Fact?)" ] @@ -762,7 +762,7 @@ { "id": "lesson_03", "title": "Unit 02 - Lesson 3: Vocabulary & Grammar (Science, Think & Mind, Verb Patterns)", - "file": "grade_10\/english_10\/semester_1\/unit_02\/lesson_03.md", + "file": "grade_10/english_10/semester_1/unit_02/lesson_03.md", "outcomes": [ "Unit 02 - Lesson 3: Vocabulary & Grammar (Science, Think & Mind, Verb Patterns)" ] @@ -770,7 +770,7 @@ { "id": "lesson_04", "title": "Unit 02 - Lesson 4: Listening (Uses of Drones)", - "file": "grade_10\/english_10\/semester_1\/unit_02\/lesson_04.md", + "file": "grade_10/english_10/semester_1/unit_02/lesson_04.md", "outcomes": [ "Unit 02 - Lesson 4: Listening (Uses of Drones)" ] @@ -778,7 +778,7 @@ { "id": "lesson_05", "title": "Unit 02 - Lesson 5: Speaking (Making Choices)", - "file": "grade_10\/english_10\/semester_1\/unit_02\/lesson_05.md", + "file": "grade_10/english_10/semester_1/unit_02/lesson_05.md", "outcomes": [ "Unit 02 - Lesson 5: Speaking (Making Choices)" ] @@ -786,7 +786,7 @@ { "id": "lesson_06", "title": "Unit 02 - Lesson 6: Writing (A Blog Post - Health and Computers)", - "file": "grade_10\/english_10\/semester_1\/unit_02\/lesson_06.md", + "file": "grade_10/english_10/semester_1/unit_02/lesson_06.md", "outcomes": [ "Unit 02 - Lesson 6: Writing (A Blog Post - Health and Computers)" ] @@ -794,7 +794,7 @@ { "id": "life_skills", "title": "Unit 02 - Life Skills: How to Give a Presentation", - "file": "grade_10\/english_10\/semester_1\/unit_02\/life_skills.md", + "file": "grade_10/english_10/semester_1/unit_02/life_skills.md", "outcomes": [ "Unit 02 - Life Skills: How to Give a Presentation" ] @@ -807,7 +807,7 @@ { "id": "lesson_01", "title": "Unit 03 - Lesson 1: Grammar & Vocabulary (Past Simple, Past Continuous, Past Perfect, Sports)", - "file": "grade_10\/english_10\/semester_1\/unit_03\/lesson_01.md", + "file": "grade_10/english_10/semester_1/unit_03/lesson_01.md", "outcomes": [ "Unit 03 - Lesson 1: Grammar & Vocabulary (Past Simple, Past Continuous, Past Perfect, Sports)" ] @@ -815,7 +815,7 @@ { "id": "lesson_02", "title": "Unit 03 - Lesson 2: Vocabulary & Listening (Injuries, Accidents & Emergencies)", - "file": "grade_10\/english_10\/semester_1\/unit_03\/lesson_02.md", + "file": "grade_10/english_10/semester_1/unit_03/lesson_02.md", "outcomes": [ "Unit 03 - Lesson 2: Vocabulary & Listening (Injuries, Accidents & Emergencies)" ] @@ -823,7 +823,7 @@ { "id": "lesson_03", "title": "Unit 03 - Lesson 3: Grammar (Used to & Would)", - "file": "grade_10\/english_10\/semester_1\/unit_03\/lesson_03.md", + "file": "grade_10/english_10/semester_1/unit_03/lesson_03.md", "outcomes": [ "Unit 03 - Lesson 3: Grammar (Used to & Would)" ] @@ -831,7 +831,7 @@ { "id": "lesson_04", "title": "Unit 03 - Lesson 4: Reading & Vocabulary (Say 'Yum' to Healthy Eating - Diet & Nutrition)", - "file": "grade_10\/english_10\/semester_1\/unit_03\/lesson_04.md", + "file": "grade_10/english_10/semester_1/unit_03/lesson_04.md", "outcomes": [ "Unit 03 - Lesson 4: Reading & Vocabulary (Say 'Yum' to Healthy Eating - Diet & Nutrition)" ] @@ -839,7 +839,7 @@ { "id": "lesson_05", "title": "Unit 03 - Lesson 5: Writing (A Short Story)", - "file": "grade_10\/english_10\/semester_1\/unit_03\/lesson_05.md", + "file": "grade_10/english_10/semester_1/unit_03/lesson_05.md", "outcomes": [ "Unit 03 - Lesson 5: Writing (A Short Story)" ] @@ -852,7 +852,7 @@ { "id": "lesson_01", "title": "Unit 04 - Lesson 1: Grammar & Vocabulary (Modal and Related Verbs, Air Travel)", - "file": "grade_10\/english_10\/semester_1\/unit_04\/lesson_01.md", + "file": "grade_10/english_10/semester_1/unit_04/lesson_01.md", "outcomes": [ "Unit 04 - Lesson 1: Grammar & Vocabulary (Modal and Related Verbs, Air Travel)" ] @@ -860,7 +860,7 @@ { "id": "lesson_02", "title": "Unit 04 - Lesson 2: Reading (How to Have the Perfect Family Holiday)", - "file": "grade_10\/english_10\/semester_1\/unit_04\/lesson_02.md", + "file": "grade_10/english_10/semester_1/unit_04/lesson_02.md", "outcomes": [ "Unit 04 - Lesson 2: Reading (How to Have the Perfect Family Holiday)" ] @@ -868,7 +868,7 @@ { "id": "lesson_03", "title": "Unit 04 - Lesson 3: Vocabulary & Grammar (Travel Essentials, Relative Clauses)", - "file": "grade_10\/english_10\/semester_1\/unit_04\/lesson_03.md", + "file": "grade_10/english_10/semester_1/unit_04/lesson_03.md", "outcomes": [ "Unit 04 - Lesson 3: Vocabulary & Grammar (Travel Essentials, Relative Clauses)" ] @@ -876,7 +876,7 @@ { "id": "lesson_04", "title": "Unit 04 - Lesson 4: Listening & Speaking (Urban Transport, Agreeing & Disagreeing)", - "file": "grade_10\/english_10\/semester_1\/unit_04\/lesson_04.md", + "file": "grade_10/english_10/semester_1/unit_04/lesson_04.md", "outcomes": [ "Unit 04 - Lesson 4: Listening & Speaking (Urban Transport, Agreeing & Disagreeing)" ] @@ -884,7 +884,7 @@ { "id": "lesson_05", "title": "Unit 04 - Lesson 5: Writing (A Formal Email of Enquiry)", - "file": "grade_10\/english_10\/semester_1\/unit_04\/lesson_05.md", + "file": "grade_10/english_10/semester_1/unit_04/lesson_05.md", "outcomes": [ "Unit 04 - Lesson 5: Writing (A Formal Email of Enquiry)" ] @@ -892,7 +892,7 @@ { "id": "life_skills", "title": "Unit 04 - Life Skills: How to Take Part in a Debate", - "file": "grade_10\/english_10\/semester_1\/unit_04\/life_skills.md", + "file": "grade_10/english_10/semester_1/unit_04/life_skills.md", "outcomes": [ "Unit 04 - Life Skills: How to Take Part in a Debate" ] @@ -905,7 +905,7 @@ { "id": "culture_spot", "title": "Unit 05 - Culture Spot & Literature Spot", - "file": "grade_10\/english_10\/semester_1\/unit_05\/culture_spot.md", + "file": "grade_10/english_10/semester_1/unit_05/culture_spot.md", "outcomes": [ "Unit 05 - Culture Spot & Literature Spot" ] @@ -913,7 +913,7 @@ { "id": "lesson_01", "title": "Unit 05 - Lesson 1: Grammar & Vocabulary (Talking About the Future, Personality Adjectives)", - "file": "grade_10\/english_10\/semester_1\/unit_05\/lesson_01.md", + "file": "grade_10/english_10/semester_1/unit_05/lesson_01.md", "outcomes": [ "Unit 05 - Lesson 1: Grammar & Vocabulary (Talking About the Future, Personality Adjectives)" ] @@ -921,7 +921,7 @@ { "id": "lesson_02", "title": "Unit 05 - Lesson 2: Grammar & Vocabulary (Future Continuous & Perfect, Studying Phrasal Verbs)", - "file": "grade_10\/english_10\/semester_1\/unit_05\/lesson_02.md", + "file": "grade_10/english_10/semester_1/unit_05/lesson_02.md", "outcomes": [ "Unit 05 - Lesson 2: Grammar & Vocabulary (Future Continuous & Perfect, Studying Phrasal Verbs)" ] @@ -929,7 +929,7 @@ { "id": "lesson_03", "title": "Unit 05 - Lesson 3: Speaking (Describing Strengths and Weaknesses)", - "file": "grade_10\/english_10\/semester_1\/unit_05\/lesson_03.md", + "file": "grade_10/english_10/semester_1/unit_05/lesson_03.md", "outcomes": [ "Unit 05 - Lesson 3: Speaking (Describing Strengths and Weaknesses)" ] @@ -937,7 +937,7 @@ { "id": "lesson_04", "title": "Unit 05 - Lesson 4: Listening (The Gig Economy, Taking Notes)", - "file": "grade_10\/english_10\/semester_1\/unit_05\/lesson_04.md", + "file": "grade_10/english_10/semester_1/unit_05/lesson_04.md", "outcomes": [ "Unit 05 - Lesson 4: Listening (The Gig Economy, Taking Notes)" ] @@ -945,7 +945,7 @@ { "id": "lesson_05", "title": "Unit 05 - Lesson 5: Reading & Vocabulary (The Future of Work, Future Jobs)", - "file": "grade_10\/english_10\/semester_1\/unit_05\/lesson_05.md", + "file": "grade_10/english_10/semester_1/unit_05/lesson_05.md", "outcomes": [ "Unit 05 - Lesson 5: Reading & Vocabulary (The Future of Work, Future Jobs)" ] @@ -953,7 +953,7 @@ { "id": "lesson_06", "title": "Unit 05 - Lesson 6: Writing (Personal Statement for University)", - "file": "grade_10\/english_10\/semester_1\/unit_05\/lesson_06.md", + "file": "grade_10/english_10/semester_1/unit_05/lesson_06.md", "outcomes": [ "Unit 05 - Lesson 6: Writing (Personal Statement for University)" ] @@ -971,7 +971,7 @@ { "id": "lesson_01", "title": "Unit 06 - Lesson 1: Grammar & Vocabulary (First & Second Conditionals, Mobile Etiquette)", - "file": "grade_10\/english_10\/semester_2\/unit_06\/lesson_01.md", + "file": "grade_10/english_10/semester_2/unit_06/lesson_01.md", "outcomes": [ "Unit 06 - Lesson 1: Grammar & Vocabulary (First & Second Conditionals, Mobile Etiquette)" ] @@ -979,7 +979,7 @@ { "id": "lesson_02", "title": "Unit 06 - Lesson 2: Reading (If We Kept It, We'd Be Rich)", - "file": "grade_10\/english_10\/semester_2\/unit_06\/lesson_02.md", + "file": "grade_10/english_10/semester_2/unit_06/lesson_02.md", "outcomes": [ "Unit 06 - Lesson 2: Reading (If We Kept It, We'd Be Rich)" ] @@ -987,7 +987,7 @@ { "id": "lesson_03", "title": "Unit 06 - Lesson 3: Listening & Grammar (Zero Conditional, Alternatives to If)", - "file": "grade_10\/english_10\/semester_2\/unit_06\/lesson_03.md", + "file": "grade_10/english_10/semester_2/unit_06/lesson_03.md", "outcomes": [ "Unit 06 - Lesson 3: Listening & Grammar (Zero Conditional, Alternatives to If)" ] @@ -995,7 +995,7 @@ { "id": "lesson_04", "title": "Unit 06 - Lesson 4: Speaking & Writing (Advice, For-and-Against Essay)", - "file": "grade_10\/english_10\/semester_2\/unit_06\/lesson_04.md", + "file": "grade_10/english_10/semester_2/unit_06/lesson_04.md", "outcomes": [ "Unit 06 - Lesson 4: Speaking & Writing (Advice, For-and-Against Essay)" ] @@ -1003,7 +1003,7 @@ { "id": "life_skills", "title": "Unit 06 - Life Skills: How to Set SMART Goals", - "file": "grade_10\/english_10\/semester_2\/unit_06\/life_skills.md", + "file": "grade_10/english_10/semester_2/unit_06/life_skills.md", "outcomes": [ "Unit 06 - Life Skills: How to Set SMART Goals" ] @@ -1016,7 +1016,7 @@ { "id": "lesson_01", "title": "Unit 07 - Lesson 1: Grammar & Vocabulary (Reported Speech, TV News & Viewing Habits)", - "file": "grade_10\/english_10\/semester_2\/unit_07\/lesson_01.md", + "file": "grade_10/english_10/semester_2/unit_07/lesson_01.md", "outcomes": [ "Unit 07 - Lesson 1: Grammar & Vocabulary (Reported Speech, TV News & Viewing Habits)" ] @@ -1024,7 +1024,7 @@ { "id": "lesson_02", "title": "Unit 07 - Lesson 2: Reading (What is and isn't Art?)", - "file": "grade_10\/english_10\/semester_2\/unit_07\/lesson_02.md", + "file": "grade_10/english_10/semester_2/unit_07/lesson_02.md", "outcomes": [ "Unit 07 - Lesson 2: Reading (What is and isn't Art?)" ] @@ -1032,7 +1032,7 @@ { "id": "lesson_03", "title": "Unit 07 - Lesson 3: Grammar & Speaking (Reported Questions, Personal Experience)", - "file": "grade_10\/english_10\/semester_2\/unit_07\/lesson_03.md", + "file": "grade_10/english_10/semester_2/unit_07/lesson_03.md", "outcomes": [ "Unit 07 - Lesson 3: Grammar & Speaking (Reported Questions, Personal Experience)" ] @@ -1040,7 +1040,7 @@ { "id": "lesson_04", "title": "Unit 07 - Lesson 4: Writing (A Review of a Play)", - "file": "grade_10\/english_10\/semester_2\/unit_07\/lesson_04.md", + "file": "grade_10/english_10/semester_2/unit_07/lesson_04.md", "outcomes": [ "Unit 07 - Lesson 4: Writing (A Review of a Play)" ] @@ -1053,7 +1053,7 @@ { "id": "lesson_01", "title": "Unit 08 - Lesson 1: Grammar & Vocabulary (The Passive, Advertising)", - "file": "grade_10\/english_10\/semester_2\/unit_08\/lesson_01.md", + "file": "grade_10/english_10/semester_2/unit_08/lesson_01.md", "outcomes": [ "Unit 08 - Lesson 1: Grammar & Vocabulary (The Passive, Advertising)" ] @@ -1061,7 +1061,7 @@ { "id": "lesson_02", "title": "Unit 08 - Lesson 2: Listening & Vocabulary (Spending & Saving, Money)", - "file": "grade_10\/english_10\/semester_2\/unit_08\/lesson_02.md", + "file": "grade_10/english_10/semester_2/unit_08/lesson_02.md", "outcomes": [ "Unit 08 - Lesson 2: Listening & Vocabulary (Spending & Saving, Money)" ] @@ -1069,7 +1069,7 @@ { "id": "lesson_03", "title": "Unit 08 - Lesson 3: Reading (The Way We Pay - Trading & Banking)", - "file": "grade_10\/english_10\/semester_2\/unit_08\/lesson_03.md", + "file": "grade_10/english_10/semester_2/unit_08/lesson_03.md", "outcomes": [ "Unit 08 - Lesson 3: Reading (The Way We Pay - Trading & Banking)" ] @@ -1077,7 +1077,7 @@ { "id": "lesson_04", "title": "Unit 08 - Lesson 4: Speaking & Writing (Complaints, Opinion Essay)", - "file": "grade_10\/english_10\/semester_2\/unit_08\/lesson_04.md", + "file": "grade_10/english_10/semester_2/unit_08/lesson_04.md", "outcomes": [ "Unit 08 - Lesson 4: Speaking & Writing (Complaints, Opinion Essay)" ] @@ -1085,7 +1085,7 @@ { "id": "life_skills", "title": "Unit 08 - Life Skills: How to Be More Creative", - "file": "grade_10\/english_10\/semester_2\/unit_08\/life_skills.md", + "file": "grade_10/english_10/semester_2/unit_08/life_skills.md", "outcomes": [ "Unit 08 - Life Skills: How to Be More Creative" ] @@ -1098,7 +1098,7 @@ { "id": "lesson_01", "title": "Unit 09 - Lesson 1: Grammar & Vocabulary (Third Conditional, Water & Ocean)", - "file": "grade_10\/english_10\/semester_2\/unit_09\/lesson_01.md", + "file": "grade_10/english_10/semester_2/unit_09/lesson_01.md", "outcomes": [ "Unit 09 - Lesson 1: Grammar & Vocabulary (Third Conditional, Water & Ocean)" ] @@ -1106,23 +1106,23 @@ { "id": "lesson_02", "title": "Unit 09 - Lesson 2: Listening & Vocabulary (Surviving an Earthquake, Natural Disasters)", - "file": "grade_10\/english_10\/semester_2\/unit_09\/lesson_02.md", + "file": "grade_10/english_10/semester_2/unit_09/lesson_02.md", "outcomes": [ "Unit 09 - Lesson 2: Listening & Vocabulary (Surviving an Earthquake, Natural Disasters)" ] }, { "id": "lesson_03", - "title": "Unit 09 - Lesson 3: Grammar & Speaking (I wish \/ If only for regrets)", - "file": "grade_10\/english_10\/semester_2\/unit_09\/lesson_03.md", + "title": "Unit 09 - Lesson 3: Grammar & Speaking (I wish / If only for regrets)", + "file": "grade_10/english_10/semester_2/unit_09/lesson_03.md", "outcomes": [ - "Unit 09 - Lesson 3: Grammar & Speaking (I wish \/ If only for regrets)" + "Unit 09 - Lesson 3: Grammar & Speaking (I wish / If only for regrets)" ] }, { "id": "lesson_04", "title": "Unit 09 - Lesson 4: Reading (Wildlife Documentary Changed My Life)", - "file": "grade_10\/english_10\/semester_2\/unit_09\/lesson_04.md", + "file": "grade_10/english_10/semester_2/unit_09/lesson_04.md", "outcomes": [ "Unit 09 - Lesson 4: Reading (Wildlife Documentary Changed My Life)" ] @@ -1130,7 +1130,7 @@ { "id": "lesson_05", "title": "Unit 09 - Lesson 5: Writing (An Article - Sustainable Homes)", - "file": "grade_10\/english_10\/semester_2\/unit_09\/lesson_05.md", + "file": "grade_10/english_10/semester_2/unit_09/lesson_05.md", "outcomes": [ "Unit 09 - Lesson 5: Writing (An Article - Sustainable Homes)" ] @@ -1143,7 +1143,7 @@ { "id": "culture_spot", "title": "Unit 10 - Culture Spot & Literature Spot", - "file": "grade_10\/english_10\/semester_2\/unit_10\/culture_spot.md", + "file": "grade_10/english_10/semester_2/unit_10/culture_spot.md", "outcomes": [ "Unit 10 - Culture Spot & Literature Spot" ] @@ -1151,7 +1151,7 @@ { "id": "lesson_01", "title": "Unit 10 - Lesson 1: Grammar & Vocabulary (Modals for Speculating, Food Tastes)", - "file": "grade_10\/english_10\/semester_2\/unit_10\/lesson_01.md", + "file": "grade_10/english_10/semester_2/unit_10/lesson_01.md", "outcomes": [ "Unit 10 - Lesson 1: Grammar & Vocabulary (Modals for Speculating, Food Tastes)" ] @@ -1159,7 +1159,7 @@ { "id": "lesson_02", "title": "Unit 10 - Lesson 2: Reading (Dining with a Difference!)", - "file": "grade_10\/english_10\/semester_2\/unit_10\/lesson_02.md", + "file": "grade_10/english_10/semester_2/unit_10/lesson_02.md", "outcomes": [ "Unit 10 - Lesson 2: Reading (Dining with a Difference!)" ] @@ -1167,7 +1167,7 @@ { "id": "lesson_03", "title": "Unit 10 - Lesson 3: Speaking & Listening (Healthy Food, Comparing Photos)", - "file": "grade_10\/english_10\/semester_2\/unit_10\/lesson_03.md", + "file": "grade_10/english_10/semester_2/unit_10/lesson_03.md", "outcomes": [ "Unit 10 - Lesson 3: Speaking & Listening (Healthy Food, Comparing Photos)" ] @@ -1175,7 +1175,7 @@ { "id": "lesson_04", "title": "Unit 10 - Lesson 4: Writing (A Formal Letter)", - "file": "grade_10\/english_10\/semester_2\/unit_10\/lesson_04.md", + "file": "grade_10/english_10/semester_2/unit_10/lesson_04.md", "outcomes": [ "Unit 10 - Lesson 4: Writing (A Formal Letter)" ] @@ -1201,7 +1201,7 @@ "outcomes": [ "مقدمة الوحدة والتجربة الاستهلالية: ناتج جمع قوتين عملياً" ], - "file": "grade_10\/physics_10\/semester_1\/unit_01\/intro_and_project.md" + "file": "grade_10/physics_10/semester_1/unit_01/intro_and_project.md" }, { "id": "lesson_01", @@ -1209,7 +1209,7 @@ "outcomes": [ "الدرس الأول: المتجهات وخصائصها وتمثيلها بيانياً" ], - "file": "grade_10\/physics_10\/semester_1\/unit_01\/lesson_01.md" + "file": "grade_10/physics_10/semester_1/unit_01/lesson_01.md" }, { "id": "lesson_02", @@ -1217,7 +1217,7 @@ "outcomes": [ "الدرس الثاني: جمع المتجهات وطرحها وتحليل المتجهات" ], - "file": "grade_10\/physics_10\/semester_1\/unit_01\/lesson_02.md" + "file": "grade_10/physics_10/semester_1/unit_01/lesson_02.md" }, { "id": "enrichment_and_expansion", @@ -1225,7 +1225,7 @@ "outcomes": [ "الإثراء والتوسع: الوعاء المغناطيسي (Magnetic Bottle)" ], - "file": "grade_10\/physics_10\/semester_1\/unit_01\/enrichment_and_expansion.md" + "file": "grade_10/physics_10/semester_1/unit_01/enrichment_and_expansion.md" }, { "id": "unit_exam", @@ -1233,12 +1233,12 @@ "outcomes": [ "اختبار ومراجعة الوحدة الأولى: المتجهات" ], - "file": "grade_10\/physics_10\/semester_1\/unit_01\/unit_exam.md" + "file": "grade_10/physics_10/semester_1/unit_01/unit_exam.md" }, { "id": "unit_review", "title": "مراجعة واختبار الوحدة الأولى: المتجهات", - "file": "grade_10\/physics_10\/semester_1\/unit_01\/unit_review.md", + "file": "grade_10/physics_10/semester_1/unit_01/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الأولى: المتجهات" ] @@ -1254,7 +1254,7 @@ "outcomes": [ "مقدمة الوحدة والتجربة الاستهلالية: وصف الحركة بالمدرج الهوائي" ], - "file": "grade_10\/physics_10\/semester_1\/unit_02\/intro_and_project.md" + "file": "grade_10/physics_10/semester_1/unit_02/intro_and_project.md" }, { "id": "lesson_01", @@ -1262,7 +1262,7 @@ "outcomes": [ "الدرس الأول: الحركة في بعد واحد والسرعة القياسية والمتجهة" ], - "file": "grade_10\/physics_10\/semester_1\/unit_02\/lesson_01.md" + "file": "grade_10/physics_10/semester_1/unit_02/lesson_01.md" }, { "id": "lesson_02", @@ -1270,7 +1270,7 @@ "outcomes": [ "الدرس الثاني: الحركة في بعدين وحركة المقذوفات" ], - "file": "grade_10\/physics_10\/semester_1\/unit_02\/lesson_02.md" + "file": "grade_10/physics_10/semester_1/unit_02/lesson_02.md" }, { "id": "lesson_03", @@ -1278,7 +1278,7 @@ "outcomes": [ "الدرس الثالث: التسارع الثابت ومعادلات الحركة بتسارع ثابت" ], - "file": "grade_10\/physics_10\/semester_1\/unit_02\/lesson_03.md" + "file": "grade_10/physics_10/semester_1/unit_02/lesson_03.md" }, { "id": "enrichment_and_expansion", @@ -1286,7 +1286,7 @@ "outcomes": [ "الإثراء والتوسع: حزام الأمان في السيارة (Seat Belts)" ], - "file": "grade_10\/physics_10\/semester_1\/unit_02\/enrichment_and_expansion.md" + "file": "grade_10/physics_10/semester_1/unit_02/enrichment_and_expansion.md" }, { "id": "unit_exam", @@ -1294,12 +1294,12 @@ "outcomes": [ "اختبار ومراجعة الوحدة الثانية: الحركة والقوى" ], - "file": "grade_10\/physics_10\/semester_1\/unit_02\/unit_exam.md" + "file": "grade_10/physics_10/semester_1/unit_02/unit_exam.md" }, { "id": "unit_review", "title": "مراجعة واختبار الوحدة الثانية: الحركة", - "file": "grade_10\/physics_10\/semester_1\/unit_02\/unit_review.md", + "file": "grade_10/physics_10/semester_1/unit_02/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الثانية: الحركة" ] @@ -1312,7 +1312,7 @@ { "id": "lesson_01", "title": "الدرس الأول: مفهوم القوة والقانون الأول لنيوتن والقصور الذاتي", - "file": "grade_10\/physics_10\/semester_1\/unit_03\/lesson_01.md", + "file": "grade_10/physics_10/semester_1/unit_03/lesson_01.md", "outcomes": [ "الدرس الأول: مفهوم القوة والقانون الأول لنيوتن والقصور الذاتي" ] @@ -1320,7 +1320,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: القانون الثاني لنيوتن والقوة المحصلة والتسارع", - "file": "grade_10\/physics_10\/semester_1\/unit_03\/lesson_02.md", + "file": "grade_10/physics_10/semester_1/unit_03/lesson_02.md", "outcomes": [ "الدرس الثاني: القانون الثاني لنيوتن والقوة المحصلة والتسارع" ] @@ -1334,11 +1334,11 @@ "items": [ { "title": "كتاب الفيزياء - الطالب (85 صفحة)", - "file": "grade_10\/physics_10\/semester_1\/physics_student_book.pdf" + "file": "grade_10/physics_10/semester_1/physics_student_book.pdf" }, { "title": "كتاب التجارب والأنشطة العلمية (33 صفحة)", - "file": "grade_10\/physics_10\/semester_1\/physics_activities_book.pdf" + "file": "grade_10/physics_10/semester_1/physics_activities_book.pdf" } ] }, @@ -1357,7 +1357,7 @@ { "id": "lesson_01", "title": "الدرس الأول: الوزن والقوة العمودية", - "file": "grade_10\/physics_10\/semester_2\/unit_04\/lesson_01.md", + "file": "grade_10/physics_10/semester_2/unit_04/lesson_01.md", "outcomes": [ "الدرس الأول: الوزن والقوة العمودية" ] @@ -1365,7 +1365,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: تطبيقات على القوى (قوة الشد، الاحتكاك، والمستوى المائل)", - "file": "grade_10\/physics_10\/semester_2\/unit_04\/lesson_02.md", + "file": "grade_10/physics_10/semester_2/unit_04/lesson_02.md", "outcomes": [ "الدرس الثاني: تطبيقات على القوى (قوة الشد، الاحتكاك، والمستوى المائل)" ] @@ -1373,7 +1373,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: القوة المركزية والحركة الدائرية المنتظمة", - "file": "grade_10\/physics_10\/semester_2\/unit_04\/lesson_03.md", + "file": "grade_10/physics_10/semester_2/unit_04/lesson_03.md", "outcomes": [ "الدرس الثالث: القوة المركزية والحركة الدائرية المنتظمة" ] @@ -1381,7 +1381,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة الرابعة: تطبيقات على قوانين نيوتن", - "file": "grade_10\/physics_10\/semester_2\/unit_04\/unit_review.md", + "file": "grade_10/physics_10/semester_2/unit_04/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الرابعة: تطبيقات على قوانين نيوتن" ] @@ -1394,7 +1394,7 @@ { "id": "lesson_01", "title": "الدرس الأول: الموائع الساكنة (ضغط المائع وقاعدة باسكال وقاعدة أرخميدس)", - "file": "grade_10\/physics_10\/semester_2\/unit_05\/lesson_01.md", + "file": "grade_10/physics_10/semester_2/unit_05/lesson_01.md", "outcomes": [ "الدرس الأول: الموائع الساكنة (ضغط المائع وقاعدة باسكال وقاعدة أرخميدس)" ] @@ -1402,7 +1402,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: الموائع المتحركة (معادلة الاستمرارية ومعادلة برنولي وتطبيقاتها)", - "file": "grade_10\/physics_10\/semester_2\/unit_05\/lesson_02.md", + "file": "grade_10/physics_10/semester_2/unit_05/lesson_02.md", "outcomes": [ "الدرس الثاني: الموائع المتحركة (معادلة الاستمرارية ومعادلة برنولي وتطبيقاتها)" ] @@ -1410,7 +1410,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة الخامسة: الموائع", - "file": "grade_10\/physics_10\/semester_2\/unit_05\/unit_review.md", + "file": "grade_10/physics_10/semester_2/unit_05/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الخامسة: الموائع" ] @@ -1423,7 +1423,7 @@ { "id": "lesson_01", "title": "الدرس الأول: الموجات وصفاتها وأنواعها (الموجات المستعرضة والطولية)", - "file": "grade_10\/physics_10\/semester_2\/unit_06\/lesson_01.md", + "file": "grade_10/physics_10/semester_2/unit_06/lesson_01.md", "outcomes": [ "الدرس الأول: الموجات وصفاتها وأنواعها (الموجات المستعرضة والطولية)" ] @@ -1431,7 +1431,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: خصائص الموجات (الانعكاس، الانكسار، التداخل، والحيود)", - "file": "grade_10\/physics_10\/semester_2\/unit_06\/lesson_02.md", + "file": "grade_10/physics_10/semester_2/unit_06/lesson_02.md", "outcomes": [ "الدرس الثاني: خصائص الموجات (الانعكاس، الانكسار، التداخل، والحيود)" ] @@ -1439,7 +1439,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة السادسة: الحركة الموجية", - "file": "grade_10\/physics_10\/semester_2\/unit_06\/unit_review.md", + "file": "grade_10/physics_10/semester_2/unit_06/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة السادسة: الحركة الموجية" ] @@ -1462,7 +1462,7 @@ { "id": "lesson_01", "title": "الدرس الأول: نظرية بور لذرة الهيدروجين والطيوف الذرية", - "file": "grade_10\/chemistry_10\/semester_1\/unit_01\/lesson_01.md", + "file": "grade_10/chemistry_10/semester_1/unit_01/lesson_01.md", "outcomes": [ "الدرس الأول: نظرية بور لذرة الهيدروجين والطيوف الذرية" ] @@ -1470,7 +1470,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: النموذج الميكانيكي الموجي للذرة وأفلاك الطاقة", - "file": "grade_10\/chemistry_10\/semester_1\/unit_01\/lesson_02.md", + "file": "grade_10/chemistry_10/semester_1/unit_01/lesson_02.md", "outcomes": [ "الدرس الثاني: النموذج الميكانيكي الموجي للذرة وأفلاك الطاقة" ] @@ -1478,7 +1478,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة الأولى: بنية الذرة", - "file": "grade_10\/chemistry_10\/semester_1\/unit_01\/unit_review.md", + "file": "grade_10/chemistry_10/semester_1/unit_01/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الأولى: بنية الذرة" ] @@ -1491,7 +1491,7 @@ { "id": "lesson_01", "title": "الدرس الأول: قواعد التوزيع الإلكتروني للذرات والأيونات", - "file": "grade_10\/chemistry_10\/semester_1\/unit_02\/lesson_01.md", + "file": "grade_10/chemistry_10/semester_1/unit_02/lesson_01.md", "outcomes": [ "الدرس الأول: قواعد التوزيع الإلكتروني للذرات والأيونات" ] @@ -1499,7 +1499,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: الخصائص الدورية للعناصر (نصف القطر، طاقة التأين، الكهروسالبية)", - "file": "grade_10\/chemistry_10\/semester_1\/unit_02\/lesson_02.md", + "file": "grade_10/chemistry_10/semester_1/unit_02/lesson_02.md", "outcomes": [ "الدرس الثاني: الخصائص الدورية للعناصر (نصف القطر، طاقة التأين، الكهروسالبية)" ] @@ -1507,7 +1507,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة الثانية: التوزيع الإلكتروني والدورية", - "file": "grade_10\/chemistry_10\/semester_1\/unit_02\/unit_review.md", + "file": "grade_10/chemistry_10/semester_1/unit_02/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الثانية: التوزيع الإلكتروني والدورية" ] @@ -1520,7 +1520,7 @@ { "id": "lesson_01", "title": "الدرس الأول: الروابط الكيميائية وأنواعها (الأيونية، التساهمية، والفلزية)", - "file": "grade_10\/chemistry_10\/semester_1\/unit_03\/lesson_01.md", + "file": "grade_10/chemistry_10/semester_1/unit_03/lesson_01.md", "outcomes": [ "الدرس الأول: الروابط الكيميائية وأنواعها (الأيونية، التساهمية، والفلزية)" ] @@ -1528,7 +1528,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: الصيغ الكيميائية وخصائص المركبات وقوى التجاذب بين الجزيئات", - "file": "grade_10\/chemistry_10\/semester_1\/unit_03\/lesson_02.md", + "file": "grade_10/chemistry_10/semester_1/unit_03/lesson_02.md", "outcomes": [ "الدرس الثاني: الصيغ الكيميائية وخصائص المركبات وقوى التجاذب بين الجزيئات" ] @@ -1536,7 +1536,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة الثالثة: الروابط والمركبات الكيميائية", - "file": "grade_10\/chemistry_10\/semester_1\/unit_03\/unit_review.md", + "file": "grade_10/chemistry_10/semester_1/unit_03/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الثالثة: الروابط والمركبات الكيميائية" ] @@ -1554,7 +1554,7 @@ { "id": "lesson_01", "title": "الدرس الأول: التفاعلات الكيميائية وأنواعها وموازنة المعادلات", - "file": "grade_10\/chemistry_10\/semester_2\/unit_04\/lesson_01.md", + "file": "grade_10/chemistry_10/semester_2/unit_04/lesson_01.md", "outcomes": [ "الدرس الأول: التفاعلات الكيميائية وأنواعها وموازنة المعادلات" ] @@ -1562,7 +1562,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: المول والكتلة المولية والكتلة الصيغية", - "file": "grade_10\/chemistry_10\/semester_2\/unit_04\/lesson_02.md", + "file": "grade_10/chemistry_10/semester_2/unit_04/lesson_02.md", "outcomes": [ "الدرس الثاني: المول والكتلة المولية والكتلة الصيغية" ] @@ -1570,7 +1570,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: الحسابات الكيميائية المبنية على المعادلات والمردود المئوي", - "file": "grade_10\/chemistry_10\/semester_2\/unit_04\/lesson_03.md", + "file": "grade_10/chemistry_10/semester_2/unit_04/lesson_03.md", "outcomes": [ "الدرس الثالث: الحسابات الكيميائية المبنية على المعادلات والمردود المئوي" ] @@ -1578,7 +1578,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة الرابعة: التفاعلات والحسابات الكيميائية", - "file": "grade_10\/chemistry_10\/semester_2\/unit_04\/unit_review.md", + "file": "grade_10/chemistry_10/semester_2/unit_04/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الرابعة: التفاعلات والحسابات الكيميائية" ] @@ -1591,7 +1591,7 @@ { "id": "lesson_01", "title": "الدرس الأول: تغيرات الطاقة في التفاعلات الكيميائية (الماصة والطاردة)", - "file": "grade_10\/chemistry_10\/semester_2\/unit_05\/lesson_01.md", + "file": "grade_10/chemistry_10/semester_2/unit_05/lesson_01.md", "outcomes": [ "الدرس الأول: تغيرات الطاقة في التفاعلات الكيميائية (الماصة والطاردة)" ] @@ -1599,7 +1599,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: الطاقة الممتصة والطاقة المنبعثة من التفاعلات وقانون حفظ الطاقة", - "file": "grade_10\/chemistry_10\/semester_2\/unit_05\/lesson_02.md", + "file": "grade_10/chemistry_10/semester_2/unit_05/lesson_02.md", "outcomes": [ "الدرس الثاني: الطاقة الممتصة والطاقة المنبعثة من التفاعلات وقانون حفظ الطاقة" ] @@ -1607,7 +1607,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: السعة الحرارية والحرارة النوعية وحساب كمية الحرارة", - "file": "grade_10\/chemistry_10\/semester_2\/unit_05\/lesson_03.md", + "file": "grade_10/chemistry_10/semester_2/unit_05/lesson_03.md", "outcomes": [ "الدرس الثالث: السعة الحرارية والحرارة النوعية وحساب كمية الحرارة" ] @@ -1615,7 +1615,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة الخامسة: الطاقة الكيميائية", - "file": "grade_10\/chemistry_10\/semester_2\/unit_05\/unit_review.md", + "file": "grade_10/chemistry_10/semester_2/unit_05/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الخامسة: الطاقة الكيميائية" ] @@ -1646,7 +1646,7 @@ { "id": "lesson_01", "title": "الدرس: تطور الكائنات الحية (Living Organisms Evolution)", - "file": "grade_10\/biology_10\/semester_1\/unit_01\/lesson_01.md", + "file": "grade_10/biology_10/semester_1/unit_01/lesson_01.md", "outcomes": [ "الدرس: تطور الكائنات الحية (Living Organisms Evolution)" ] @@ -1654,7 +1654,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة الأولى: نظرية التطور", - "file": "grade_10\/biology_10\/semester_1\/unit_01\/unit_review.md", + "file": "grade_10/biology_10/semester_1/unit_01/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الأولى: نظرية التطور" ] @@ -1667,7 +1667,7 @@ { "id": "lesson_01", "title": "الدرس 1: الفيروسات (Viruses)", - "file": "grade_10\/biology_10\/semester_1\/unit_02\/lesson_01.md", + "file": "grade_10/biology_10/semester_1/unit_02/lesson_01.md", "outcomes": [ "الدرس 1: الفيروسات (Viruses)" ] @@ -1675,7 +1675,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة الثانية: الفيروسات والفيرويدات والبريونات", - "file": "grade_10\/biology_10\/semester_1\/unit_02\/unit_review.md", + "file": "grade_10/biology_10/semester_1/unit_02/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الثانية: الفيروسات والفيرويدات والبريونات" ] @@ -1688,7 +1688,7 @@ { "id": "lesson_01", "title": "الدرس 1: أسس علم التصنيف ومستوياته ونظام التسمية الثنائية", - "file": "grade_10\/biology_10\/semester_1\/unit_03\/lesson_01.md", + "file": "grade_10/biology_10/semester_1/unit_03/lesson_01.md", "outcomes": [ "الدرس 1: أسس علم التصنيف ومستوياته ونظام التسمية الثنائية" ] @@ -1696,7 +1696,7 @@ { "id": "lesson_02", "title": "الدرس 2: البكتيريا والأثريات وخصائصها الحيوية", - "file": "grade_10\/biology_10\/semester_1\/unit_03\/lesson_02.md", + "file": "grade_10/biology_10/semester_1/unit_03/lesson_02.md", "outcomes": [ "الدرس 2: البكتيريا والأثريات وخصائصها الحيوية" ] @@ -1704,7 +1704,7 @@ { "id": "lesson_03", "title": "الدرس 3: الطلائعيات وتنوعها وأهميتها البيئية والاقتصادية", - "file": "grade_10\/biology_10\/semester_1\/unit_03\/lesson_03.md", + "file": "grade_10/biology_10/semester_1/unit_03/lesson_03.md", "outcomes": [ "الدرس 3: الطلائعيات وتنوعها وأهميتها البيئية والاقتصادية" ] @@ -1712,7 +1712,7 @@ { "id": "lesson_04", "title": "الدرس 4: الفطريات وتراكيبها وطرائق تغذيتها ودورها في الطبيعة", - "file": "grade_10\/biology_10\/semester_1\/unit_03\/lesson_04.md", + "file": "grade_10/biology_10/semester_1/unit_03/lesson_04.md", "outcomes": [ "الدرس 4: الفطريات وتراكيبها وطرائق تغذيتها ودورها في الطبيعة" ] @@ -1720,7 +1720,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة الثالثة: تصنيف الكائنات الحية", - "file": "grade_10\/biology_10\/semester_1\/unit_03\/unit_review.md", + "file": "grade_10/biology_10/semester_1/unit_03/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الثالثة: تصنيف الكائنات الحية" ] @@ -1738,7 +1738,7 @@ { "id": "lesson_05", "title": "الدرس 5: النباتات اللاوعائية والنباتات الوعائية اللابذرية", - "file": "grade_10\/biology_10\/semester_2\/unit_03\/lesson_05.md", + "file": "grade_10/biology_10/semester_2/unit_03/lesson_05.md", "outcomes": [ "الدرس 5: النباتات اللاوعائية والنباتات الوعائية اللابذرية" ] @@ -1746,7 +1746,7 @@ { "id": "lesson_06", "title": "الدرس 6: النباتات الوعائية البذرية (معراة ومغطاة البذور)", - "file": "grade_10\/biology_10\/semester_2\/unit_03\/lesson_06.md", + "file": "grade_10/biology_10/semester_2/unit_03/lesson_06.md", "outcomes": [ "الدرس 6: النباتات الوعائية البذرية (معراة ومغطاة البذور)" ] @@ -1754,7 +1754,7 @@ { "id": "lesson_07", "title": "الدرس 7: خصائص الحيوانات وأسس تصنيف المملكة الحيوانية", - "file": "grade_10\/biology_10\/semester_2\/unit_03\/lesson_07.md", + "file": "grade_10/biology_10/semester_2/unit_03/lesson_07.md", "outcomes": [ "الدرس 7: خصائص الحيوانات وأسس تصنيف المملكة الحيوانية" ] @@ -1762,7 +1762,7 @@ { "id": "lesson_08", "title": "الدرس 8: اللافقاريات وخصائص قبائلها الرئيسية", - "file": "grade_10\/biology_10\/semester_2\/unit_03\/lesson_08.md", + "file": "grade_10/biology_10/semester_2/unit_03/lesson_08.md", "outcomes": [ "الدرس 8: اللافقاريات وخصائص قبائلها الرئيسية" ] @@ -1770,7 +1770,7 @@ { "id": "lesson_09", "title": "الدرس 9: الفقاريات وطوائفها وتكيفاتها المعيشية", - "file": "grade_10\/biology_10\/semester_2\/unit_03\/lesson_09.md", + "file": "grade_10/biology_10/semester_2/unit_03/lesson_09.md", "outcomes": [ "الدرس 9: الفقاريات وطوائفها وتكيفاتها المعيشية" ] @@ -1778,7 +1778,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة الثالثة: المملكة النباتية والحيوانية", - "file": "grade_10\/biology_10\/semester_2\/unit_03\/unit_review.md", + "file": "grade_10/biology_10/semester_2/unit_03/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الثالثة: المملكة النباتية والحيوانية" ] @@ -1791,7 +1791,7 @@ { "id": "lesson_01", "title": "الدرس 1: تدفق الطاقة ودورات المواد في الأنظمة البيئية", - "file": "grade_10\/biology_10\/semester_2\/unit_04\/lesson_01.md", + "file": "grade_10/biology_10/semester_2/unit_04/lesson_01.md", "outcomes": [ "الدرس 1: تدفق الطاقة ودورات المواد في الأنظمة البيئية" ] @@ -1799,7 +1799,7 @@ { "id": "lesson_02", "title": "الدرس 2: الجماعات الحيوية والعوامل المؤثرة في نموها وكثافتها", - "file": "grade_10\/biology_10\/semester_2\/unit_04\/lesson_02.md", + "file": "grade_10/biology_10/semester_2/unit_04/lesson_02.md", "outcomes": [ "الدرس 2: الجماعات الحيوية والعوامل المؤثرة في نموها وكثافتها" ] @@ -1807,7 +1807,7 @@ { "id": "unit_review", "title": "مراجعة واختبار الوحدة الرابعة: البيئة والتنوع الحيوي", - "file": "grade_10\/biology_10\/semester_2\/unit_04\/unit_review.md", + "file": "grade_10/biology_10/semester_2/unit_04/unit_review.md", "outcomes": [ "مراجعة واختبار الوحدة الرابعة: البيئة والتنوع الحيوي" ] @@ -1838,7 +1838,7 @@ { "id": "lesson_01", "title": "الدرس 1: الصخور النارية (Igneous Rocks)", - "file": "grade_10\/earth_sciences_10\/semester_1\/unit_01\/lesson_01.md", + "file": "grade_10/earth_sciences_10/semester_1/unit_01/lesson_01.md", "outcomes": [ "الدرس 1: الصخور النارية (Igneous Rocks)" ] @@ -1846,7 +1846,7 @@ { "id": "lesson_02", "title": "الدرس 2: الصخور الرسوبية (Sedimentary Rocks)", - "file": "grade_10\/earth_sciences_10\/semester_1\/unit_01\/lesson_02.md", + "file": "grade_10/earth_sciences_10/semester_1/unit_01/lesson_02.md", "outcomes": [ "الدرس 2: الصخور الرسوبية (Sedimentary Rocks)" ] @@ -1854,7 +1854,7 @@ { "id": "lesson_03", "title": "الدرس 3: الصخور المتحولة (Metamorphic Rocks)", - "file": "grade_10\/earth_sciences_10\/semester_1\/unit_01\/lesson_03.md", + "file": "grade_10/earth_sciences_10/semester_1/unit_01/lesson_03.md", "outcomes": [ "الدرس 3: الصخور المتحولة (Metamorphic Rocks)" ] @@ -1862,7 +1862,7 @@ { "id": "unit_enrichment_and_review", "title": "الإثراء والتوسع ومراجعة الوحدة الأولى: الصوف الصخري والصخور", - "file": "grade_10\/earth_sciences_10\/semester_1\/unit_01\/unit_enrichment_and_review.md", + "file": "grade_10/earth_sciences_10/semester_1/unit_01/unit_enrichment_and_review.md", "outcomes": [ "الإثراء والتوسع ومراجعة الوحدة الأولى: الصوف الصخري والصخور" ] @@ -1875,7 +1875,7 @@ { "id": "lesson_01", "title": "الدرس 1: ماهية النجوم وخصائصها الفيزيائية وطاقتها", - "file": "grade_10\/earth_sciences_10\/semester_1\/unit_02\/lesson_01.md", + "file": "grade_10/earth_sciences_10/semester_1/unit_02/lesson_01.md", "outcomes": [ "الدرس 1: ماهية النجوم وخصائصها الفيزيائية وطاقتها" ] @@ -1883,7 +1883,7 @@ { "id": "lesson_02", "title": "الدرس 2: الأنظمة النجمية والكوكبات النجمية", - "file": "grade_10\/earth_sciences_10\/semester_1\/unit_02\/lesson_02.md", + "file": "grade_10/earth_sciences_10/semester_1/unit_02/lesson_02.md", "outcomes": [ "الدرس 2: الأنظمة النجمية والكوكبات النجمية" ] @@ -1891,7 +1891,7 @@ { "id": "lesson_03", "title": "الدرس 3: دورة حياة النجوم ومراحل تطورها وموتها", - "file": "grade_10\/earth_sciences_10\/semester_1\/unit_02\/lesson_03.md", + "file": "grade_10/earth_sciences_10/semester_1/unit_02/lesson_03.md", "outcomes": [ "الدرس 3: دورة حياة النجوم ومراحل تطورها وموتها" ] @@ -1899,7 +1899,7 @@ { "id": "unit_enrichment_and_review", "title": "الإثراء والتوسع ومراجعة الوحدة الثانية: مقراد الكوة الدائرية والنجوم", - "file": "grade_10\/earth_sciences_10\/semester_1\/unit_02\/unit_enrichment_and_review.md", + "file": "grade_10/earth_sciences_10/semester_1/unit_02/unit_enrichment_and_review.md", "outcomes": [ "الإثراء والتوسع ومراجعة الوحدة الثانية: مقراد الكوة الدائرية والنجوم" ] @@ -1917,7 +1917,7 @@ { "id": "lesson_01", "title": "الدرس 1: الكتل والجبهات الهوائية (Air Masses and Fronts)", - "file": "grade_10\/earth_sciences_10\/semester_2\/unit_03\/lesson_01.md", + "file": "grade_10/earth_sciences_10/semester_2/unit_03/lesson_01.md", "outcomes": [ "الدرس 1: الكتل والجبهات الهوائية (Air Masses and Fronts)" ] @@ -1925,7 +1925,7 @@ { "id": "lesson_02", "title": "الدرس 2: أنظمة الضغط الجوي (Pressure Systems)", - "file": "grade_10\/earth_sciences_10\/semester_2\/unit_03\/lesson_02.md", + "file": "grade_10/earth_sciences_10/semester_2/unit_03/lesson_02.md", "outcomes": [ "الدرس 2: أنظمة الضغط الجوي (Pressure Systems)" ] @@ -1933,7 +1933,7 @@ { "id": "unit_enrichment_and_review", "title": "الإثراء والتوسع ومراجعة الوحدة الثالثة: بالونات الطقس والأرصاد", - "file": "grade_10\/earth_sciences_10\/semester_2\/unit_03\/unit_enrichment_and_review.md", + "file": "grade_10/earth_sciences_10/semester_2/unit_03/unit_enrichment_and_review.md", "outcomes": [ "الإثراء والتوسع ومراجعة الوحدة الثالثة: بالونات الطقس والأرصاد" ] @@ -1946,7 +1946,7 @@ { "id": "lesson_01", "title": "الدرس 1: خصائص مياه المحيطات (الملوحة والحرارة والكثافة)", - "file": "grade_10\/earth_sciences_10\/semester_2\/unit_04\/lesson_01.md", + "file": "grade_10/earth_sciences_10/semester_2/unit_04/lesson_01.md", "outcomes": [ "الدرس 1: خصائص مياه المحيطات (الملوحة والحرارة والكثافة)" ] @@ -1954,7 +1954,7 @@ { "id": "lesson_02", "title": "الدرس 2: أمواج المحيط (Ocean Waves)", - "file": "grade_10\/earth_sciences_10\/semester_2\/unit_04\/lesson_02.md", + "file": "grade_10/earth_sciences_10/semester_2/unit_04/lesson_02.md", "outcomes": [ "الدرس 2: أمواج المحيط (Ocean Waves)" ] @@ -1962,7 +1962,7 @@ { "id": "lesson_03", "title": "الدرس 3: تيارات المحيط والمناخ (Ocean Currents and Climate)", - "file": "grade_10\/earth_sciences_10\/semester_2\/unit_04\/lesson_03.md", + "file": "grade_10/earth_sciences_10/semester_2/unit_04/lesson_03.md", "outcomes": [ "الدرس 3: تيارات المحيط والمناخ (Ocean Currents and Climate)" ] @@ -1970,7 +1970,7 @@ { "id": "unit_enrichment_and_review", "title": "الإثراء والتوسع ومراجعة الوحدة الرابعة: دراسة المحيطات بالأقمار الصناعية", - "file": "grade_10\/earth_sciences_10\/semester_2\/unit_04\/unit_enrichment_and_review.md", + "file": "grade_10/earth_sciences_10/semester_2/unit_04/unit_enrichment_and_review.md", "outcomes": [ "الإثراء والتوسع ومراجعة الوحدة الرابعة: دراسة المحيطات بالأقمار الصناعية" ] @@ -1983,7 +1983,7 @@ { "id": "lesson_01", "title": "الدرس 1: مفهوم المياه العادمة ومصادرها", - "file": "grade_10\/earth_sciences_10\/semester_2\/unit_05\/lesson_01.md", + "file": "grade_10/earth_sciences_10/semester_2/unit_05/lesson_01.md", "outcomes": [ "الدرس 1: مفهوم المياه العادمة ومصادرها" ] @@ -1991,7 +1991,7 @@ { "id": "lesson_02", "title": "الدرس 2: الآثار السلبية للمياه العادمة على البيئة والصحة", - "file": "grade_10\/earth_sciences_10\/semester_2\/unit_05\/lesson_02.md", + "file": "grade_10/earth_sciences_10/semester_2/unit_05/lesson_02.md", "outcomes": [ "الدرس 2: الآثار السلبية للمياه العادمة على البيئة والصحة" ] @@ -1999,7 +1999,7 @@ { "id": "lesson_03", "title": "الدرس 3: معالجة المياه العادمة ومراحلها وتطبيقاتها في الأردن", - "file": "grade_10\/earth_sciences_10\/semester_2\/unit_05\/lesson_03.md", + "file": "grade_10/earth_sciences_10/semester_2/unit_05/lesson_03.md", "outcomes": [ "الدرس 3: معالجة المياه العادمة ومراحلها وتطبيقاتها في الأردن" ] @@ -2007,7 +2007,7 @@ { "id": "unit_enrichment_and_review", "title": "الإثراء والتوسع ومراجعة الوحدة الخامسة: فوائد الحمأة ومعالجة المياه", - "file": "grade_10\/earth_sciences_10\/semester_2\/unit_05\/unit_enrichment_and_review.md", + "file": "grade_10/earth_sciences_10/semester_2/unit_05/unit_enrichment_and_review.md", "outcomes": [ "الإثراء والتوسع ومراجعة الوحدة الخامسة: فوائد الحمأة ومعالجة المياه" ] @@ -2041,12 +2041,12 @@ "outcomes": [ "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ (قصة كعب بن مالك رضي الله عنه)" ], - "file": "grade_10\/arabic_10\/semester_1\/unit_01\/lesson_01.md" + "file": "grade_10/arabic_10/semester_1/unit_01/lesson_01.md" }, { "id": "lesson_02", "title": "الدرس الثاني: أتحدثُ بطالقةٍ (فن الاعتذار وقيم التسامح)", - "file": "grade_10\/arabic_10\/semester_1\/unit_01\/lesson_02.md", + "file": "grade_10/arabic_10/semester_1/unit_01/lesson_02.md", "outcomes": [ "الدرس الثاني: أتحدثُ بطالقةٍ (فن الاعتذار وقيم التسامح)" ] @@ -2054,7 +2054,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (سينية البحتري في الاعتذار)", - "file": "grade_10\/arabic_10\/semester_1\/unit_01\/lesson_03.md", + "file": "grade_10/arabic_10/semester_1/unit_01/lesson_03.md", "outcomes": [ "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (سينية البحتري في الاعتذار)" ] @@ -2062,7 +2062,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: أكتبُ محتوى (كتابة رسالة اعتذار وتسامح)", - "file": "grade_10\/arabic_10\/semester_1\/unit_01\/lesson_04.md", + "file": "grade_10/arabic_10/semester_1/unit_01/lesson_04.md", "outcomes": [ "الدرس الرابع: أكتبُ محتوى (كتابة رسالة اعتذار وتسامح)" ] @@ -2070,7 +2070,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: أبني لغتي (1): أسلوبُ الشَّرطِ", - "file": "grade_10\/arabic_10\/semester_1\/unit_01\/lesson_05.md", + "file": "grade_10/arabic_10/semester_1/unit_01/lesson_05.md", "outcomes": [ "الدرس الخامس: أبني لغتي (1): أسلوبُ الشَّرطِ" ] @@ -2078,7 +2078,7 @@ { "id": "lesson_06", "title": "الدرس السادس: أبني لغتي (2): الأسلوبُ الخَبَريُّ", - "file": "grade_10\/arabic_10\/semester_1\/unit_01\/lesson_06.md", + "file": "grade_10/arabic_10/semester_1/unit_01/lesson_06.md", "outcomes": [ "الدرس السادس: أبني لغتي (2): الأسلوبُ الخَبَريُّ" ] @@ -2091,7 +2091,7 @@ { "id": "lesson_01", "title": "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ", - "file": "grade_10\/arabic_10\/semester_1\/unit_02\/lesson_01.md", + "file": "grade_10/arabic_10/semester_1/unit_02/lesson_01.md", "outcomes": [ "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ" ] @@ -2099,7 +2099,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: أتحدثُ بطالقةٍ (العرض التقديمي)", - "file": "grade_10\/arabic_10\/semester_1\/unit_02\/lesson_02.md", + "file": "grade_10/arabic_10/semester_1/unit_02/lesson_02.md", "outcomes": [ "الدرس الثاني: أتحدثُ بطالقةٍ (العرض التقديمي)" ] @@ -2107,7 +2107,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (إلى الصامدين غرب النهر)", - "file": "grade_10\/arabic_10\/semester_1\/unit_02\/lesson_03.md", + "file": "grade_10/arabic_10/semester_1/unit_02/lesson_03.md", "outcomes": [ "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (إلى الصامدين غرب النهر)" ] @@ -2115,7 +2115,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: أكتبُ محتوى (تحليل النص الشعري)", - "file": "grade_10\/arabic_10\/semester_1\/unit_02\/lesson_04.md", + "file": "grade_10/arabic_10/semester_1/unit_02/lesson_04.md", "outcomes": [ "الدرس الرابع: أكتبُ محتوى (تحليل النص الشعري)" ] @@ -2123,7 +2123,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: أبني لغتي (1): أسلوبُ النِّداءِ", - "file": "grade_10\/arabic_10\/semester_1\/unit_02\/lesson_05.md", + "file": "grade_10/arabic_10/semester_1/unit_02/lesson_05.md", "outcomes": [ "الدرس الخامس: أبني لغتي (1): أسلوبُ النِّداءِ" ] @@ -2131,7 +2131,7 @@ { "id": "lesson_06", "title": "الدرس السادس: أبني لغتي (2): الأسلوبُ الإنشائيُّ (الإنشاء الطلبي)", - "file": "grade_10\/arabic_10\/semester_1\/unit_02\/lesson_06.md", + "file": "grade_10/arabic_10/semester_1/unit_02/lesson_06.md", "outcomes": [ "الدرس السادس: أبني لغتي (2): الأسلوبُ الإنشائيُّ (الإنشاء الطلبي)" ] @@ -2144,7 +2144,7 @@ { "id": "lesson_01", "title": "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ", - "file": "grade_10\/arabic_10\/semester_1\/unit_03\/lesson_01.md", + "file": "grade_10/arabic_10/semester_1/unit_03/lesson_01.md", "outcomes": [ "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ" ] @@ -2152,7 +2152,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: أتحدثُ بطالقةٍ (قراءة الصورة)", - "file": "grade_10\/arabic_10\/semester_1\/unit_03\/lesson_02.md", + "file": "grade_10/arabic_10/semester_1/unit_03/lesson_02.md", "outcomes": [ "الدرس الثاني: أتحدثُ بطالقةٍ (قراءة الصورة)" ] @@ -2160,7 +2160,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (اللغة الأم)", - "file": "grade_10\/arabic_10\/semester_1\/unit_03\/lesson_03.md", + "file": "grade_10/arabic_10/semester_1/unit_03/lesson_03.md", "outcomes": [ "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (اللغة الأم)" ] @@ -2168,7 +2168,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: أكتبُ محتوى (تحليل لوحة فنية)", - "file": "grade_10\/arabic_10\/semester_1\/unit_03\/lesson_04.md", + "file": "grade_10/arabic_10/semester_1/unit_03/lesson_04.md", "outcomes": [ "الدرس الرابع: أكتبُ محتوى (تحليل لوحة فنية)" ] @@ -2176,7 +2176,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: أبني لغتي (1): معاني الأفعال المزيدة", - "file": "grade_10\/arabic_10\/semester_1\/unit_03\/lesson_05.md", + "file": "grade_10/arabic_10/semester_1/unit_03/lesson_05.md", "outcomes": [ "الدرس الخامس: أبني لغتي (1): معاني الأفعال المزيدة" ] @@ -2184,7 +2184,7 @@ { "id": "lesson_06", "title": "الدرس السادس: أبني لغتي (2): الأسلوب الإنشائي غير الطلبي", - "file": "grade_10\/arabic_10\/semester_1\/unit_03\/lesson_06.md", + "file": "grade_10/arabic_10/semester_1/unit_03/lesson_06.md", "outcomes": [ "الدرس السادس: أبني لغتي (2): الأسلوب الإنشائي غير الطلبي" ] @@ -2197,7 +2197,7 @@ { "id": "lesson_01", "title": "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ", - "file": "grade_10\/arabic_10\/semester_1\/unit_04\/lesson_01.md", + "file": "grade_10/arabic_10/semester_1/unit_04/lesson_01.md", "outcomes": [ "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ" ] @@ -2205,7 +2205,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: أتحدثُ بطالقةٍ (كيف أقدم شخصية أدبية؟)", - "file": "grade_10\/arabic_10\/semester_1\/unit_04\/lesson_02.md", + "file": "grade_10/arabic_10/semester_1/unit_04/lesson_02.md", "outcomes": [ "الدرس الثاني: أتحدثُ بطالقةٍ (كيف أقدم شخصية أدبية؟)" ] @@ -2213,7 +2213,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (شغف القراءة وحكايات أخرى)", - "file": "grade_10\/arabic_10\/semester_1\/unit_04\/lesson_03.md", + "file": "grade_10/arabic_10/semester_1/unit_04/lesson_03.md", "outcomes": [ "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (شغف القراءة وحكايات أخرى)" ] @@ -2221,7 +2221,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: أكتبُ محتوى (صفحة أولى من سيرتي الذاتية)", - "file": "grade_10\/arabic_10\/semester_1\/unit_04\/lesson_04.md", + "file": "grade_10/arabic_10/semester_1/unit_04/lesson_04.md", "outcomes": [ "الدرس الرابع: أكتبُ محتوى (صفحة أولى من سيرتي الذاتية)" ] @@ -2229,7 +2229,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: أبني لغتي (1): مصادر الأفعال الثلاثية", - "file": "grade_10\/arabic_10\/semester_1\/unit_04\/lesson_05.md", + "file": "grade_10/arabic_10/semester_1/unit_04/lesson_05.md", "outcomes": [ "الدرس الخامس: أبني لغتي (1): مصادر الأفعال الثلاثية" ] @@ -2237,7 +2237,7 @@ { "id": "lesson_06", "title": "الدرس السادس: أبني لغتي (2): موسيقا لغتي وإيقاعها", - "file": "grade_10\/arabic_10\/semester_1\/unit_04\/lesson_06.md", + "file": "grade_10/arabic_10/semester_1/unit_04/lesson_06.md", "outcomes": [ "الدرس السادس: أبني لغتي (2): موسيقا لغتي وإيقاعها" ] @@ -2250,7 +2250,7 @@ { "id": "lesson_01", "title": "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ", - "file": "grade_10\/arabic_10\/semester_1\/unit_05\/lesson_01.md", + "file": "grade_10/arabic_10/semester_1/unit_05/lesson_01.md", "outcomes": [ "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ" ] @@ -2258,7 +2258,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: أتحدثُ بطالقةٍ (قراءة المشاعر)", - "file": "grade_10\/arabic_10\/semester_1\/unit_05\/lesson_02.md", + "file": "grade_10/arabic_10/semester_1/unit_05/lesson_02.md", "outcomes": [ "الدرس الثاني: أتحدثُ بطالقةٍ (قراءة المشاعر)" ] @@ -2266,7 +2266,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (بم التعلل لا أهل ولا وطن)", - "file": "grade_10\/arabic_10\/semester_1\/unit_05\/lesson_03.md", + "file": "grade_10/arabic_10/semester_1/unit_05/lesson_03.md", "outcomes": [ "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (بم التعلل لا أهل ولا وطن)" ] @@ -2274,7 +2274,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: أكتبُ محتوى (نص إخباري عن مناسبة أممية)", - "file": "grade_10\/arabic_10\/semester_1\/unit_05\/lesson_04.md", + "file": "grade_10/arabic_10/semester_1/unit_05/lesson_04.md", "outcomes": [ "الدرس الرابع: أكتبُ محتوى (نص إخباري عن مناسبة أممية)" ] @@ -2282,7 +2282,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: أبني لغتي (1): مصادر الأفعال غير الثلاثية", - "file": "grade_10\/arabic_10\/semester_1\/unit_05\/lesson_05.md", + "file": "grade_10/arabic_10/semester_1/unit_05/lesson_05.md", "outcomes": [ "الدرس الخامس: أبني لغتي (1): مصادر الأفعال غير الثلاثية" ] @@ -2290,7 +2290,7 @@ { "id": "lesson_06", "title": "الدرس السادس: أبني لغتي (2): موسيقا لغتي وإيقاعها", - "file": "grade_10\/arabic_10\/semester_1\/unit_05\/lesson_06.md", + "file": "grade_10/arabic_10/semester_1/unit_05/lesson_06.md", "outcomes": [ "الدرس السادس: أبني لغتي (2): موسيقا لغتي وإيقاعها" ] @@ -2308,7 +2308,7 @@ { "id": "lesson_01", "title": "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ", - "file": "grade_10\/arabic_10\/semester_2\/unit_06\/lesson_01.md", + "file": "grade_10/arabic_10/semester_2/unit_06/lesson_01.md", "outcomes": [ "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ" ] @@ -2316,7 +2316,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: أتحدثُ بطالقةٍ (المناقشة الجماعية الحرة)", - "file": "grade_10\/arabic_10\/semester_2\/unit_06\/lesson_02.md", + "file": "grade_10/arabic_10/semester_2/unit_06/lesson_02.md", "outcomes": [ "الدرس الثاني: أتحدثُ بطالقةٍ (المناقشة الجماعية الحرة)" ] @@ -2324,7 +2324,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (سينية أحمد شوقي في الحنين للوطن)", - "file": "grade_10\/arabic_10\/semester_2\/unit_06\/lesson_03.md", + "file": "grade_10/arabic_10/semester_2/unit_06/lesson_03.md", "outcomes": [ "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (سينية أحمد شوقي في الحنين للوطن)" ] @@ -2332,7 +2332,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: أكتبُ محتوى (مقال تحليلي عن تجربة شعورية)", - "file": "grade_10\/arabic_10\/semester_2\/unit_06\/lesson_04.md", + "file": "grade_10/arabic_10/semester_2/unit_06/lesson_04.md", "outcomes": [ "الدرس الرابع: أكتبُ محتوى (مقال تحليلي عن تجربة شعورية)" ] @@ -2340,7 +2340,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: أبني لغتي (1): الممنوع من الصرف", - "file": "grade_10\/arabic_10\/semester_2\/unit_06\/lesson_05.md", + "file": "grade_10/arabic_10/semester_2/unit_06/lesson_05.md", "outcomes": [ "الدرس الخامس: أبني لغتي (1): الممنوع من الصرف" ] @@ -2348,7 +2348,7 @@ { "id": "lesson_06", "title": "الدرس السادس: أبني لغتي (2): نوعا التشبيه: المؤكد المفصل والمؤكد المجمل", - "file": "grade_10\/arabic_10\/semester_2\/unit_06\/lesson_06.md", + "file": "grade_10/arabic_10/semester_2/unit_06/lesson_06.md", "outcomes": [ "الدرس السادس: أبني لغتي (2): نوعا التشبيه: المؤكد المفصل والمؤكد المجمل" ] @@ -2361,7 +2361,7 @@ { "id": "lesson_01", "title": "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ", - "file": "grade_10\/arabic_10\/semester_2\/unit_07\/lesson_01.md", + "file": "grade_10/arabic_10/semester_2/unit_07/lesson_01.md", "outcomes": [ "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ" ] @@ -2369,7 +2369,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: أتحدثُ بطالقةٍ (فن المناظرة وأدوار المتحدثين)", - "file": "grade_10\/arabic_10\/semester_2\/unit_07\/lesson_02.md", + "file": "grade_10/arabic_10/semester_2/unit_07/lesson_02.md", "outcomes": [ "الدرس الثاني: أتحدثُ بطالقةٍ (فن المناظرة وأدوار المتحدثين)" ] @@ -2377,7 +2377,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (عصر المعلومات بعد الإنترنت: قضية إشكالية)", - "file": "grade_10\/arabic_10\/semester_2\/unit_07\/lesson_03.md", + "file": "grade_10/arabic_10/semester_2/unit_07/lesson_03.md", "outcomes": [ "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (عصر المعلومات بعد الإنترنت: قضية إشكالية)" ] @@ -2385,7 +2385,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: أكتبُ محتوى (النص الجدلي)", - "file": "grade_10\/arabic_10\/semester_2\/unit_07\/lesson_04.md", + "file": "grade_10/arabic_10/semester_2/unit_07/lesson_04.md", "outcomes": [ "الدرس الرابع: أكتبُ محتوى (النص الجدلي)" ] @@ -2393,7 +2393,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: أبني لغتي (1): تمييز الذات", - "file": "grade_10\/arabic_10\/semester_2\/unit_07\/lesson_05.md", + "file": "grade_10/arabic_10/semester_2/unit_07/lesson_05.md", "outcomes": [ "الدرس الخامس: أبني لغتي (1): تمييز الذات" ] @@ -2401,7 +2401,7 @@ { "id": "lesson_06", "title": "الدرس السادس: أبني لغتي (2): صيغة المبالغة والصفة المشبهة", - "file": "grade_10\/arabic_10\/semester_2\/unit_07\/lesson_06.md", + "file": "grade_10/arabic_10/semester_2/unit_07/lesson_06.md", "outcomes": [ "الدرس السادس: أبني لغتي (2): صيغة المبالغة والصفة المشبهة" ] @@ -2414,7 +2414,7 @@ { "id": "lesson_01", "title": "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ", - "file": "grade_10\/arabic_10\/semester_2\/unit_08\/lesson_01.md", + "file": "grade_10/arabic_10/semester_2/unit_08/lesson_01.md", "outcomes": [ "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ" ] @@ -2422,7 +2422,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: أتحدثُ بطالقةٍ (إدارة الندوة)", - "file": "grade_10\/arabic_10\/semester_2\/unit_08\/lesson_02.md", + "file": "grade_10/arabic_10/semester_2/unit_08/lesson_02.md", "outcomes": [ "الدرس الثاني: أتحدثُ بطالقةٍ (إدارة الندوة)" ] @@ -2430,7 +2430,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (مقطوعات من الغزل العذري)", - "file": "grade_10\/arabic_10\/semester_2\/unit_08\/lesson_03.md", + "file": "grade_10/arabic_10/semester_2/unit_08/lesson_03.md", "outcomes": [ "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (مقطوعات من الغزل العذري)" ] @@ -2438,7 +2438,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: أكتبُ محتوى (إعداد مخطط مبادرة تطوعية)", - "file": "grade_10\/arabic_10\/semester_2\/unit_08\/lesson_04.md", + "file": "grade_10/arabic_10/semester_2/unit_08/lesson_04.md", "outcomes": [ "الدرس الرابع: أكتبُ محتوى (إعداد مخطط مبادرة تطوعية)" ] @@ -2446,7 +2446,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: أبني لغتي (1): تثنية الاسم المقصور والمنقوص والممدود وجمعه", - "file": "grade_10\/arabic_10\/semester_2\/unit_08\/lesson_05.md", + "file": "grade_10/arabic_10/semester_2/unit_08/lesson_05.md", "outcomes": [ "الدرس الخامس: أبني لغتي (1): تثنية الاسم المقصور والمنقوص والممدود وجمعه" ] @@ -2454,7 +2454,7 @@ { "id": "lesson_06", "title": "الدرس السادس: أبني لغتي (2): موسيقا لغتي وإيقاعها (بحر الهزج)", - "file": "grade_10\/arabic_10\/semester_2\/unit_08\/lesson_06.md", + "file": "grade_10/arabic_10/semester_2/unit_08/lesson_06.md", "outcomes": [ "الدرس السادس: أبني لغتي (2): موسيقا لغتي وإيقاعها (بحر الهزج)" ] @@ -2467,7 +2467,7 @@ { "id": "lesson_01", "title": "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ", - "file": "grade_10\/arabic_10\/semester_2\/unit_09\/lesson_01.md", + "file": "grade_10/arabic_10/semester_2/unit_09/lesson_01.md", "outcomes": [ "الدرس الأول: أستمعُ بانتباهٍ وتركيزٍ" ] @@ -2475,7 +2475,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: أتحدثُ بطالقةٍ (العرض الشفوي لقصة نجاح)", - "file": "grade_10\/arabic_10\/semester_2\/unit_09\/lesson_02.md", + "file": "grade_10/arabic_10/semester_2/unit_09/lesson_02.md", "outcomes": [ "الدرس الثاني: أتحدثُ بطالقةٍ (العرض الشفوي لقصة نجاح)" ] @@ -2483,7 +2483,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (المفكر العربي إدوارد سعيد)", - "file": "grade_10\/arabic_10\/semester_2\/unit_09\/lesson_03.md", + "file": "grade_10/arabic_10/semester_2/unit_09/lesson_03.md", "outcomes": [ "الدرس الثالث: أقرأُ بطالقةٍ وفهمٍ (المفكر العربي إدوارد سعيد)" ] @@ -2491,7 +2491,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: أكتبُ محتوى (تقرير علمي عن شخصية)", - "file": "grade_10\/arabic_10\/semester_2\/unit_09\/lesson_04.md", + "file": "grade_10/arabic_10/semester_2/unit_09/lesson_04.md", "outcomes": [ "الدرس الرابع: أكتبُ محتوى (تقرير علمي عن شخصية)" ] @@ -2499,7 +2499,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: أبني لغتي (1): الأفعال المتعدية إلى مفعولين", - "file": "grade_10\/arabic_10\/semester_2\/unit_09\/lesson_05.md", + "file": "grade_10/arabic_10/semester_2/unit_09/lesson_05.md", "outcomes": [ "الدرس الخامس: أبني لغتي (1): الأفعال المتعدية إلى مفعولين" ] @@ -2507,7 +2507,7 @@ { "id": "lesson_06", "title": "الدرس السادس: أبني لغتي (2): موسيقا لغتي وإيقاعها (بحر المتقارب)", - "file": "grade_10\/arabic_10\/semester_2\/unit_09\/lesson_06.md", + "file": "grade_10/arabic_10/semester_2/unit_09/lesson_06.md", "outcomes": [ "الدرس السادس: أبني لغتي (2): موسيقا لغتي وإيقاعها (بحر المتقارب)" ] @@ -2525,118 +2525,214 @@ "name": "الفصل الدراسي الأول", "units": { "unit_01": { - "name": "الوحدة الأولى: الفقه وأحكام المواريث والتلاوة", + "name": "الوحدة الأولى: القرآن الكريم والفقه الإسلامي والمعاملات", "lessons": [ { "id": "lesson_01", - "title": "الدرس 1: واجب المسلم تجاه القرآن الكريم (تلاوته وتدبره والعمل به)", + "title": "الدرس 1: واجب المسلم تجاه القرآن الكريم", "outcomes": [ - "الدرس 1: واجب المسلم تجاه القرآن الكريم (تلاوته وتدبره والعمل به)" + "الدرس 1: واجب المسلم تجاه القرآن الكريم" ], - "file": "grade_10\/islamic_10\/semester_1\/unit_01\/lesson_01.md" + "file": "grade_10/islamic_10/semester_1/unit_01/lesson_01.md" }, { "id": "lesson_02", - "title": "الدرس 2: الفقه الإسلامي: البيع وأحكامه في الإسلام", - "file": "grade_10\/islamic_10\/semester_1\/unit_01\/lesson_02.md", + "title": "الدرس 2: البيع في الفقه الإسلامي", "outcomes": [ - "الدرس 2: الفقه الإسلامي: البيع وأحكامه في الإسلام" - ] + "الدرس 2: البيع في الفقه الإسلامي" + ], + "file": "grade_10/islamic_10/semester_1/unit_01/lesson_02.md" }, { "id": "lesson_03", - "title": "الدرس 3: معاملة النبي صلى الله عليه وسلم ليهود المدينة المنورة", - "file": "grade_10\/islamic_10\/semester_1\/unit_01\/lesson_03.md", + "title": "الدرس 3: معاملة النبي ﷺ ليهود المدينة المنورة", "outcomes": [ - "الدرس 3: معاملة النبي صلى الله عليه وسلم ليهود المدينة المنورة" - ] + "الدرس 3: معاملة النبي ﷺ ليهود المدينة المنورة" + ], + "file": "grade_10/islamic_10/semester_1/unit_01/lesson_03.md" + }, + { + "id": "lesson_04", + "title": "الدرس 4: علامات وقف التلاوة", + "outcomes": [ + "الدرس 4: علامات وقف التلاوة" + ], + "file": "grade_10/islamic_10/semester_1/unit_01/lesson_04.md" + }, + { + "id": "lesson_05", + "title": "الدرس 5: حق التملك في الإسلام", + "outcomes": [ + "الدرس 5: حق التملك في الإسلام" + ], + "file": "grade_10/islamic_10/semester_1/unit_01/lesson_05.md" + }, + { + "id": "lesson_06", + "title": "الدرس 6: من صور عناية الإسلام بالمرأة (حمايتها من العنف)", + "outcomes": [ + "الدرس 6: من صور عناية الإسلام بالمرأة" + ], + "file": "grade_10/islamic_10/semester_1/unit_01/lesson_06.md" } ] }, "unit_02": { - "name": "الوحدة الثانية", + "name": "الوحدة الثانية: التفسير وأصول الفقه والأخلاق ومقاصد الشريعة", "lessons": [ { "id": "lesson_01", - "title": "الدرس الأول: الربا وأنواعه وأحكامه في الشريعة الإسلامية", - "file": "grade_10\/islamic_10\/semester_1\/unit_02\/lesson_01.md", + "title": "الدرس 1: سورة البقرة: الآيتان الكريمتان (143-144)", "outcomes": [ - "الدرس الأول: الربا وأنواعه وأحكامه في الشريعة الإسلامية" - ] + "سورة البقرة 143-144" + ], + "file": "grade_10/islamic_10/semester_1/unit_02/lesson_01.md" }, { "id": "lesson_02", - "title": "الدرس الثاني: الصرف والقرض في الفقه الإسلامي", - "file": "grade_10\/islamic_10\/semester_1\/unit_02\/lesson_02.md", + "title": "الدرس 2: علم أصول الفقه", "outcomes": [ - "الدرس الثاني: الصرف والقرض في الفقه الإسلامي" - ] + "علم أصول الفقه" + ], + "file": "grade_10/islamic_10/semester_1/unit_02/lesson_02.md" }, { "id": "lesson_03", - "title": "الدرس الثالث: الشركات المالية الحديثة وضوابطها الشرعية", - "file": "grade_10\/islamic_10\/semester_1\/unit_02\/lesson_03.md", + "title": "الدرس 3: مراتب الدين", "outcomes": [ - "الدرس الثالث: الشركات المالية الحديثة وضوابطها الشرعية" - ] + "مراتب الدين" + ], + "file": "grade_10/islamic_10/semester_1/unit_02/lesson_03.md" + }, + { + "id": "lesson_04", + "title": "الدرس 4: أحكام وقف التلاوة", + "outcomes": [ + "أحكام وقف التلاوة" + ], + "file": "grade_10/islamic_10/semester_1/unit_02/lesson_04.md" + }, + { + "id": "lesson_05", + "title": "الدرس 5: من مقاصد الشريعة الإسلامية (حفظ الدين)", + "outcomes": [ + "حفظ الدين" + ], + "file": "grade_10/islamic_10/semester_1/unit_02/lesson_05.md" + }, + { + "id": "lesson_06", + "title": "الدرس 6: الحديث الشريف: حفظ اللسان", + "outcomes": [ + "الحديث الشريف: حفظ اللسان" + ], + "file": "grade_10/islamic_10/semester_1/unit_02/lesson_06.md" } ] }, "unit_03": { - "name": "الوحدة الثالثة", + "name": "الوحدة الثالثة: القرآن الكريم والمعاملات المالية والقدس والوقف التام", "lessons": [ { "id": "lesson_01", - "title": "الدرس الأول: وثيقة المدينة المنورة ودولة المواطنة والتعايش", - "file": "grade_10\/islamic_10\/semester_1\/unit_03\/lesson_01.md", + "title": "الدرس 1: سورة البقرة: الآيات الكريمة (183-186)", "outcomes": [ - "الدرس الأول: وثيقة المدينة المنورة ودولة المواطنة والتعايش" - ] + "سورة البقرة 183-186" + ], + "file": "grade_10/islamic_10/semester_1/unit_03/lesson_01.md" }, { "id": "lesson_02", - "title": "الدرس الثاني: غزوة مؤتة والدروس القيادية والعسكرية", - "file": "grade_10\/islamic_10\/semester_1\/unit_03\/lesson_02.md", + "title": "الدرس 2: موقف الشريعة الإسلامية من الربا", "outcomes": [ - "الدرس الثاني: غزوة مؤتة والدروس القيادية والعسكرية" - ] + "موقف الشريعة من الربا" + ], + "file": "grade_10/islamic_10/semester_1/unit_03/lesson_02.md" }, { "id": "lesson_03", - "title": "الدرس الثالث: منجزات الحضارة الإسلامية في العلوم والترجمة", - "file": "grade_10\/islamic_10\/semester_1\/unit_03\/lesson_03.md", + "title": "الدرس 3: القدس والمسجد الأقصى المبارك", "outcomes": [ - "الدرس الثالث: منجزات الحضارة الإسلامية في العلوم والترجمة" - ] + "القدس والمسجد الأقصى" + ], + "file": "grade_10/islamic_10/semester_1/unit_03/lesson_03.md" + }, + { + "id": "lesson_04", + "title": "الدرس 4: من أنواع الوقف الاختياري الجائز (الوقف التام)", + "outcomes": [ + "الوقف التام" + ], + "file": "grade_10/islamic_10/semester_1/unit_03/lesson_04.md" + }, + { + "id": "lesson_05", + "title": "الدرس 5: القيادة الهاشمية ودورها في إبراز صورة الإسلام", + "outcomes": [ + "القيادة الهاشمية" + ], + "file": "grade_10/islamic_10/semester_1/unit_03/lesson_05.md" + }, + { + "id": "lesson_06", + "title": "الدرس 6: القرض وأحكامه في الفقه الإسلامي", + "outcomes": [ + "القرض وأحكامه" + ], + "file": "grade_10/islamic_10/semester_1/unit_03/lesson_06.md" } ] }, "unit_04": { - "name": "الوحدة الرابعة", + "name": "الوحدة الرابعة: العقيدة والحديث والفقه والوقف الكافي والخلق القويم", "lessons": [ { "id": "lesson_01", - "title": "الدرس الأول: خلق الحياء والعفة في السلوك الفردي والجماعي", - "file": "grade_10\/islamic_10\/semester_1\/unit_04\/lesson_01.md", + "title": "الدرس 1: التفكر في خلق الله تعالى", "outcomes": [ - "الدرس الأول: خلق الحياء والعفة في السلوك الفردي والجماعي" - ] + "التفكر في خلق الله" + ], + "file": "grade_10/islamic_10/semester_1/unit_04/lesson_01.md" }, { "id": "lesson_02", - "title": "الدرس الثاني: صلة الأرحام وحقوق الجوار والمسؤولية الأسرية", - "file": "grade_10\/islamic_10\/semester_1\/unit_04\/lesson_02.md", + "title": "الدرس 2: صحيح البخاري", "outcomes": [ - "الدرس الثاني: صلة الأرحام وحقوق الجوار والمسؤولية الأسرية" - ] + "صحيح البخاري" + ], + "file": "grade_10/islamic_10/semester_1/unit_04/lesson_02.md" }, { "id": "lesson_03", - "title": "الدرس الثالث: ترشيد الاستهلاك وحماية البيئة في المنظور الإسلامي", - "file": "grade_10\/islamic_10\/semester_1\/unit_04\/lesson_03.md", + "title": "الدرس 3: موقف الشريعة الإسلامية من القمار", "outcomes": [ - "الدرس الثالث: ترشيد الاستهلاك وحماية البيئة في المنظور الإسلامي" - ] + "موقف الشريعة من القمار" + ], + "file": "grade_10/islamic_10/semester_1/unit_04/lesson_03.md" + }, + { + "id": "lesson_04", + "title": "الدرس 4: من أنواع الوقف الاختياري الجائز (الوقف الكافي)", + "outcomes": [ + "الوقف الكافي" + ], + "file": "grade_10/islamic_10/semester_1/unit_04/lesson_04.md" + }, + { + "id": "lesson_05", + "title": "الدرس 5: الصحابي الجليل خالد بن الوليد رضي الله عنه", + "outcomes": [ + "الصحابي خالد بن الوليد" + ], + "file": "grade_10/islamic_10/semester_1/unit_04/lesson_05.md" + }, + { + "id": "lesson_06", + "title": "الدرس 6: الحياء زينة الإنسان", + "outcomes": [ + "الحياء زينة الإنسان" + ], + "file": "grade_10/islamic_10/semester_1/unit_04/lesson_06.md" } ] } @@ -2646,12 +2742,12 @@ "name": "الفصل الدراسي الثاني", "units": { "unit_01": { - "name": "الوحدة الأولى", + "name": "الوحدة الأولى: سورة آل عمران وصلح الحديبية والإجارة", "lessons": [ { "id": "lesson_01", "title": "الدرس 1: سورة آل عمران: الآيات الكريمة (189-195)", - "file": "grade_10\/islamic_10\/semester_2\/unit_01\/lesson_01.md", + "file": "grade_10/islamic_10/semester_2/unit_01/lesson_01.md", "outcomes": [ "الدرس 1: سورة آل عمران: الآيات الكريمة (189-195)" ] @@ -2659,7 +2755,7 @@ { "id": "lesson_02", "title": "الدرس 2: صلح الحديبية", - "file": "grade_10\/islamic_10\/semester_2\/unit_01\/lesson_02.md", + "file": "grade_10/islamic_10/semester_2/unit_01/lesson_02.md", "outcomes": [ "الدرس 2: صلح الحديبية" ] @@ -2667,7 +2763,7 @@ { "id": "lesson_03", "title": "الدرس 3: الخرافة وموقف الإسلام منها", - "file": "grade_10\/islamic_10\/semester_2\/unit_01\/lesson_03.md", + "file": "grade_10/islamic_10/semester_2/unit_01/lesson_03.md", "outcomes": [ "الدرس 3: الخرافة وموقف الإسلام منها" ] @@ -2675,7 +2771,7 @@ { "id": "lesson_04", "title": "الدرس 4: الحكم الشرعي التكليفي وأقسامه", - "file": "grade_10\/islamic_10\/semester_2\/unit_01\/lesson_04.md", + "file": "grade_10/islamic_10/semester_2/unit_01/lesson_04.md", "outcomes": [ "الدرس 4: الحكم الشرعي التكليفي وأقسامه" ] @@ -2683,7 +2779,7 @@ { "id": "lesson_05", "title": "الدرس 5: من أنواع الوقف الاختياري الجائز (الوقف الحسن)", - "file": "grade_10\/islamic_10\/semester_2\/unit_01\/lesson_05.md", + "file": "grade_10/islamic_10/semester_2/unit_01/lesson_05.md", "outcomes": [ "الدرس 5: من أنواع الوقف الاختياري الجائز (الوقف الحسن)" ] @@ -2691,7 +2787,7 @@ { "id": "lesson_06", "title": "الدرس 6: الإجارة وأحكامها في الفقه الإسلامي", - "file": "grade_10\/islamic_10\/semester_2\/unit_01\/lesson_06.md", + "file": "grade_10/islamic_10/semester_2/unit_01/lesson_06.md", "outcomes": [ "الدرس 6: الإجارة وأحكامها في الفقه الإسلامي" ] @@ -2699,7 +2795,7 @@ { "id": "lesson_07", "title": "الدرس 7: دور القوات المسلحة الأردنية في الدفاع عن فلسطين ومقدساتها", - "file": "grade_10\/islamic_10\/semester_2\/unit_01\/lesson_07.md", + "file": "grade_10/islamic_10/semester_2/unit_01/lesson_07.md", "outcomes": [ "الدرس 7: دور القوات المسلحة الأردنية في الدفاع عن فلسطين ومقدساتها" ] @@ -2707,12 +2803,12 @@ ] }, "unit_02": { - "name": "الوحدة الثانية", + "name": "الوحدة الثانية: صحيح مسلم وسورة الغاشية والإعارة", "lessons": [ { "id": "lesson_01", "title": "الدرس 1: سورة الغاشية", - "file": "grade_10\/islamic_10\/semester_2\/unit_02\/lesson_01.md", + "file": "grade_10/islamic_10/semester_2/unit_02/lesson_01.md", "outcomes": [ "الدرس 1: سورة الغاشية" ] @@ -2720,7 +2816,7 @@ { "id": "lesson_02", "title": "الدرس 2: صحيح الإمام مسلم رحمه الله", - "file": "grade_10\/islamic_10\/semester_2\/unit_02\/lesson_02.md", + "file": "grade_10/islamic_10/semester_2/unit_02/lesson_02.md", "outcomes": [ "الدرس 2: صحيح الإمام مسلم رحمه الله" ] @@ -2728,7 +2824,7 @@ { "id": "lesson_03", "title": "الدرس 3: من خصائص الشريعة الإسلامية: المرونة", - "file": "grade_10\/islamic_10\/semester_2\/unit_02\/lesson_03.md", + "file": "grade_10/islamic_10/semester_2/unit_02/lesson_03.md", "outcomes": [ "الدرس 3: من خصائص الشريعة الإسلامية: المرونة" ] @@ -2736,7 +2832,7 @@ { "id": "lesson_04", "title": "الدرس 4: الوقف الاختياري غير الجائز (الوقف القبيح)", - "file": "grade_10\/islamic_10\/semester_2\/unit_02\/lesson_04.md", + "file": "grade_10/islamic_10/semester_2/unit_02/lesson_04.md", "outcomes": [ "الدرس 4: الوقف الاختياري غير الجائز (الوقف القبيح)" ] @@ -2744,7 +2840,7 @@ { "id": "lesson_05", "title": "الدرس 5: الإعارة وأحكامها في الفقه الإسلامي", - "file": "grade_10\/islamic_10\/semester_2\/unit_02\/lesson_05.md", + "file": "grade_10/islamic_10/semester_2/unit_02/lesson_05.md", "outcomes": [ "الدرس 5: الإعارة وأحكامها في الفقه الإسلامي" ] @@ -2752,7 +2848,7 @@ { "id": "lesson_06", "title": "الدرس 6: الإسلام والفن", - "file": "grade_10\/islamic_10\/semester_2\/unit_02\/lesson_06.md", + "file": "grade_10/islamic_10/semester_2/unit_02/lesson_06.md", "outcomes": [ "الدرس 6: الإسلام والفن" ] @@ -2760,12 +2856,12 @@ ] }, "unit_03": { - "name": "الوحدة الثالثة", + "name": "الوحدة الثالثة: القرآن الكريم والجعالة والشورى", "lessons": [ { "id": "lesson_01", "title": "الدرس 1: حق المواطنة", - "file": "grade_10\/islamic_10\/semester_2\/unit_03\/lesson_01.md", + "file": "grade_10/islamic_10/semester_2/unit_03/lesson_01.md", "outcomes": [ "الدرس 1: حق المواطنة" ] @@ -2773,7 +2869,7 @@ { "id": "lesson_02", "title": "الدرس 2: المحافظة على الموارد البيئية", - "file": "grade_10\/islamic_10\/semester_2\/unit_03\/lesson_02.md", + "file": "grade_10/islamic_10/semester_2/unit_03/lesson_02.md", "outcomes": [ "الدرس 2: المحافظة على الموارد البيئية" ] @@ -2781,7 +2877,7 @@ { "id": "lesson_03", "title": "الدرس 3: التبرع بالأعضاء", - "file": "grade_10\/islamic_10\/semester_2\/unit_03\/lesson_03.md", + "file": "grade_10/islamic_10/semester_2/unit_03/lesson_03.md", "outcomes": [ "الدرس 3: التبرع بالأعضاء" ] @@ -2789,7 +2885,7 @@ { "id": "lesson_04", "title": "الدرس 4: الوقف وأحكامه في الفقه الإسلامي", - "file": "grade_10\/islamic_10\/semester_2\/unit_03\/lesson_04.md", + "file": "grade_10/islamic_10/semester_2/unit_03/lesson_04.md", "outcomes": [ "الدرس 4: الوقف وأحكامه في الفقه الإسلامي" ] @@ -2797,7 +2893,7 @@ { "id": "lesson_05", "title": "الدرس 5: الإيمان والعمل", - "file": "grade_10\/islamic_10\/semester_2\/unit_03\/lesson_05.md", + "file": "grade_10/islamic_10/semester_2/unit_03/lesson_05.md", "outcomes": [ "الدرس 5: الإيمان والعمل" ] @@ -2805,7 +2901,7 @@ { "id": "lesson_06", "title": "الدرس 6: تطبيقات على أحكام وقف التلاوة في القرآن الكريم (1)", - "file": "grade_10\/islamic_10\/semester_2\/unit_03\/lesson_06.md", + "file": "grade_10/islamic_10/semester_2/unit_03/lesson_06.md", "outcomes": [ "الدرس 6: تطبيقات على أحكام وقف التلاوة في القرآن الكريم (1)" ] @@ -2813,7 +2909,7 @@ { "id": "lesson_07", "title": "الدرس 7: من روائع حضارتنا: المنجزات العلمية", - "file": "grade_10\/islamic_10\/semester_2\/unit_03\/lesson_07.md", + "file": "grade_10/islamic_10/semester_2/unit_03/lesson_07.md", "outcomes": [ "الدرس 7: من روائع حضارتنا: المنجزات العلمية" ] @@ -2821,12 +2917,12 @@ ] }, "unit_04": { - "name": "الوحدة الرابعة", + "name": "الوحدة الرابعة: المعاملات المالية الحديثة والسيرة النبوية", "lessons": [ { "id": "lesson_01", "title": "الدرس 1: سورة النساء: الآيتان الكريمتان (58-59)", - "file": "grade_10\/islamic_10\/semester_2\/unit_04\/lesson_01.md", + "file": "grade_10/islamic_10/semester_2/unit_04/lesson_01.md", "outcomes": [ "الدرس 1: سورة النساء: الآيتان الكريمتان (58-59)" ] @@ -2834,7 +2930,7 @@ { "id": "lesson_02", "title": "الدرس 2: الحديث الشريف: سبعة يظلهم الله في ظله", - "file": "grade_10\/islamic_10\/semester_2\/unit_04\/lesson_02.md", + "file": "grade_10/islamic_10/semester_2/unit_04/lesson_02.md", "outcomes": [ "الدرس 2: الحديث الشريف: سبعة يظلهم الله في ظله" ] @@ -2842,7 +2938,7 @@ { "id": "lesson_03", "title": "الدرس 3: اللباس والزينة في الإسلام", - "file": "grade_10\/islamic_10\/semester_2\/unit_04\/lesson_03.md", + "file": "grade_10/islamic_10/semester_2/unit_04/lesson_03.md", "outcomes": [ "الدرس 3: اللباس والزينة في الإسلام" ] @@ -2850,7 +2946,7 @@ { "id": "lesson_04", "title": "الدرس 4: تطبيقات على أحكام وقف التلاوة في القرآن الكريم (2)", - "file": "grade_10\/islamic_10\/semester_2\/unit_04\/lesson_04.md", + "file": "grade_10/islamic_10/semester_2/unit_04/lesson_04.md", "outcomes": [ "الدرس 4: تطبيقات على أحكام وقف التلاوة في القرآن الكريم (2)" ] @@ -2858,7 +2954,7 @@ { "id": "lesson_05", "title": "الدرس 5: الوديعة وأحكامها في الفقه الإسلامي", - "file": "grade_10\/islamic_10\/semester_2\/unit_04\/lesson_05.md", + "file": "grade_10/islamic_10/semester_2/unit_04/lesson_05.md", "outcomes": [ "الدرس 5: الوديعة وأحكامها في الفقه الإسلامي" ] @@ -2866,7 +2962,7 @@ { "id": "lesson_06", "title": "الدرس 6: الصحابي الجليل أبو عبيدة عامر بن الجراح رضي الله عنه", - "file": "grade_10\/islamic_10\/semester_2\/unit_04\/lesson_06.md", + "file": "grade_10/islamic_10/semester_2/unit_04/lesson_06.md", "outcomes": [ "الدرس 6: الصحابي الجليل أبو عبيدة عامر بن الجراح رضي الله عنه" ] @@ -2878,13 +2974,13 @@ } }, "history_10": { - "name": "تاريخ الأردن (Jordan History 10)", + "name": "التاريخ (History 10)", "semesters": { "semester_1": { "name": "الفصل الدراسي الأول", "units": { "unit_01": { - "name": "الوحدة الأولى: نشأة الدولة الأردنية ومحطات الاستقلال", + "name": "الوحدة الأولى: الإمبراطورية الفارسية", "lessons": [ { "id": "lesson_01", @@ -2892,12 +2988,12 @@ "outcomes": [ "الدرس الأول: الإمبراطورية الفارسية: النشأة والتطور" ], - "file": "grade_10\/history_10\/semester_1\/unit_01\/lesson_01.md" + "file": "grade_10/history_10/semester_1/unit_01/lesson_01.md" }, { "id": "lesson_02", "title": "الدرس الثاني: الدولة الساسانية (226م - 651م)", - "file": "grade_10\/history_10\/semester_1\/unit_01\/lesson_02.md", + "file": "grade_10/history_10/semester_1/unit_01/lesson_02.md", "outcomes": [ "الدرس الثاني: الدولة الساسانية (226م - 651م)" ] @@ -2905,7 +3001,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: الحكم والمؤسسات في الدولة الساسانية", - "file": "grade_10\/history_10\/semester_1\/unit_01\/lesson_03.md", + "file": "grade_10/history_10/semester_1/unit_01/lesson_03.md", "outcomes": [ "الدرس الثالث: الحكم والمؤسسات في الدولة الساسانية" ] @@ -2913,7 +3009,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: الحياة العامة في الدولة الساسانية", - "file": "grade_10\/history_10\/semester_1\/unit_01\/lesson_04.md", + "file": "grade_10/history_10/semester_1/unit_01/lesson_04.md", "outcomes": [ "الدرس الرابع: الحياة العامة في الدولة الساسانية" ] @@ -2921,7 +3017,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: علاقات الدولة الساسانية الخارجية", - "file": "grade_10\/history_10\/semester_1\/unit_01\/lesson_05.md", + "file": "grade_10/history_10/semester_1/unit_01/lesson_05.md", "outcomes": [ "الدرس الخامس: علاقات الدولة الساسانية الخارجية" ] @@ -2929,7 +3025,7 @@ { "id": "lesson_06", "title": "الدرس السادس: نهاية الدولة الساسانية", - "file": "grade_10\/history_10\/semester_1\/unit_01\/lesson_06.md", + "file": "grade_10/history_10/semester_1/unit_01/lesson_06.md", "outcomes": [ "الدرس السادس: نهاية الدولة الساسانية" ] @@ -2937,7 +3033,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الأولى: الإمبراطورية الفارسية", - "file": "grade_10\/history_10\/semester_1\/unit_01\/unit_review.md", + "file": "grade_10/history_10/semester_1/unit_01/unit_review.md", "outcomes": [ "مراجعة الوحدة الأولى: الإمبراطورية الفارسية" ] @@ -2945,12 +3041,12 @@ ] }, "unit_02": { - "name": "الوحدة الثانية", + "name": "الوحدة الثانية: الدولة العثمانية", "lessons": [ { "id": "lesson_01", "title": "الدرس الأول: نشأة الدولة العثمانية", - "file": "grade_10\/history_10\/semester_1\/unit_02\/lesson_01.md", + "file": "grade_10/history_10/semester_1/unit_02/lesson_01.md", "outcomes": [ "الدرس الأول: نشأة الدولة العثمانية" ] @@ -2958,7 +3054,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: فتح القسطنطينية (إسطنبول)", - "file": "grade_10\/history_10\/semester_1\/unit_02\/lesson_02.md", + "file": "grade_10/history_10/semester_1/unit_02/lesson_02.md", "outcomes": [ "الدرس الثاني: فتح القسطنطينية (إسطنبول)" ] @@ -2966,7 +3062,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: الحياة العامة في الدولة العثمانية", - "file": "grade_10\/history_10\/semester_1\/unit_02\/lesson_03.md", + "file": "grade_10/history_10/semester_1/unit_02/lesson_03.md", "outcomes": [ "الدرس الثالث: الحياة العامة في الدولة العثمانية" ] @@ -2974,7 +3070,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: الوطن العربي في ظل الحكم العثماني", - "file": "grade_10\/history_10\/semester_1\/unit_02\/lesson_04.md", + "file": "grade_10/history_10/semester_1/unit_02/lesson_04.md", "outcomes": [ "الدرس الرابع: الوطن العربي في ظل الحكم العثماني" ] @@ -2982,7 +3078,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: الأردن في ظل الحكم العثماني", - "file": "grade_10\/history_10\/semester_1\/unit_02\/lesson_05.md", + "file": "grade_10/history_10/semester_1/unit_02/lesson_05.md", "outcomes": [ "الدرس الخامس: الأردن في ظل الحكم العثماني" ] @@ -2990,7 +3086,7 @@ { "id": "lesson_06", "title": "الدرس السادس: نهاية الدولة العثمانية", - "file": "grade_10\/history_10\/semester_1\/unit_02\/lesson_06.md", + "file": "grade_10/history_10/semester_1/unit_02/lesson_06.md", "outcomes": [ "الدرس السادس: نهاية الدولة العثمانية" ] @@ -2998,7 +3094,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الثانية: الدولة العثمانية", - "file": "grade_10\/history_10\/semester_1\/unit_02\/unit_review.md", + "file": "grade_10/history_10/semester_1/unit_02/unit_review.md", "outcomes": [ "مراجعة الوحدة الثانية: الدولة العثمانية" ] @@ -3011,12 +3107,12 @@ "name": "الفصل الدراسي الثاني", "units": { "unit_03": { - "name": "الوحدة الثالثة", + "name": "الوحدة الثالثة: ثورات غيرت العالم الحديث", "lessons": [ { "id": "lesson_01", "title": "الدرس الأول: الثورة الصناعية", - "file": "grade_10\/history_10\/semester_2\/unit_03\/lesson_01.md", + "file": "grade_10/history_10/semester_2/unit_03/lesson_01.md", "outcomes": [ "الدرس الأول: الثورة الصناعية" ] @@ -3024,7 +3120,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: إنجازات الثورة الصناعية", - "file": "grade_10\/history_10\/semester_2\/unit_03\/lesson_02.md", + "file": "grade_10/history_10/semester_2/unit_03/lesson_02.md", "outcomes": [ "الدرس الثاني: إنجازات الثورة الصناعية" ] @@ -3032,7 +3128,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: الثورة الأمريكية", - "file": "grade_10\/history_10\/semester_2\/unit_03\/lesson_03.md", + "file": "grade_10/history_10/semester_2/unit_03/lesson_03.md", "outcomes": [ "الدرس الثالث: الثورة الأمريكية" ] @@ -3040,7 +3136,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: مراحل الثورة الأمريكية", - "file": "grade_10\/history_10\/semester_2\/unit_03\/lesson_04.md", + "file": "grade_10/history_10/semester_2/unit_03/lesson_04.md", "outcomes": [ "الدرس الرابع: مراحل الثورة الأمريكية" ] @@ -3048,7 +3144,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: الثورة الفرنسية", - "file": "grade_10\/history_10\/semester_2\/unit_03\/lesson_05.md", + "file": "grade_10/history_10/semester_2/unit_03/lesson_05.md", "outcomes": [ "الدرس الخامس: الثورة الفرنسية" ] @@ -3056,7 +3152,7 @@ { "id": "lesson_06", "title": "الدرس السادس: نتائج الثورة الفرنسية", - "file": "grade_10\/history_10\/semester_2\/unit_03\/lesson_06.md", + "file": "grade_10/history_10/semester_2/unit_03/lesson_06.md", "outcomes": [ "الدرس السادس: نتائج الثورة الفرنسية" ] @@ -3064,7 +3160,7 @@ { "id": "lesson_07", "title": "الدرس السابع: الثورة الروسية", - "file": "grade_10\/history_10\/semester_2\/unit_03\/lesson_07.md", + "file": "grade_10/history_10/semester_2/unit_03/lesson_07.md", "outcomes": [ "الدرس السابع: الثورة الروسية" ] @@ -3072,7 +3168,7 @@ { "id": "lesson_08", "title": "الدرس الثامن: العالم في القرن العشرين", - "file": "grade_10\/history_10\/semester_2\/unit_03\/lesson_08.md", + "file": "grade_10/history_10/semester_2/unit_03/lesson_08.md", "outcomes": [ "الدرس الثامن: العالم في القرن العشرين" ] @@ -3080,7 +3176,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الثالثة: ثورات غيرت العالم الحديث", - "file": "grade_10\/history_10\/semester_2\/unit_03\/unit_review.md", + "file": "grade_10/history_10/semester_2/unit_03/unit_review.md", "outcomes": [ "مراجعة الوحدة الثالثة: ثورات غيرت العالم الحديث" ] @@ -3088,12 +3184,12 @@ ] }, "unit_04": { - "name": "الوحدة الرابعة", + "name": "الوحدة الرابعة: شخصيات من التاريخ", "lessons": [ { "id": "lesson_01", "title": "الدرس الأول: محمد علي باشا (1769م - 1849م)", - "file": "grade_10\/history_10\/semester_2\/unit_04\/lesson_01.md", + "file": "grade_10/history_10/semester_2/unit_04/lesson_01.md", "outcomes": [ "الدرس الأول: محمد علي باشا (1769م - 1849م)" ] @@ -3101,7 +3197,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: المهاتما غاندي (1869م - 1948م)", - "file": "grade_10\/history_10\/semester_2\/unit_04\/lesson_02.md", + "file": "grade_10/history_10/semester_2/unit_04/lesson_02.md", "outcomes": [ "الدرس الثاني: المهاتما غاندي (1869م - 1948م)" ] @@ -3109,7 +3205,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: زيغريد هونكه (1913م - 1999م)", - "file": "grade_10\/history_10\/semester_2\/unit_04\/lesson_03.md", + "file": "grade_10/history_10/semester_2/unit_04/lesson_03.md", "outcomes": [ "الدرس الثالث: زيغريد هونكه (1913م - 1999م)" ] @@ -3117,7 +3213,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الرابعة: شخصيات من التاريخ", - "file": "grade_10\/history_10\/semester_2\/unit_04\/unit_review.md", + "file": "grade_10/history_10/semester_2/unit_04/unit_review.md", "outcomes": [ "مراجعة الوحدة الرابعة: شخصيات من التاريخ" ] @@ -3140,7 +3236,7 @@ { "id": "lesson_01", "title": "الدرس الأول: الغلاف الجوي", - "file": "grade_10\/geography_10\/semester_1\/unit_01\/lesson_01.md", + "file": "grade_10/geography_10/semester_1/unit_01/lesson_01.md", "outcomes": [ "الدرس الأول: الغلاف الجوي" ] @@ -3148,7 +3244,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: الغلاف الحيوي", - "file": "grade_10\/geography_10\/semester_1\/unit_01\/lesson_02.md", + "file": "grade_10/geography_10/semester_1/unit_01/lesson_02.md", "outcomes": [ "الدرس الثاني: الغلاف الحيوي" ] @@ -3156,7 +3252,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: التنوع الحيوي", - "file": "grade_10\/geography_10\/semester_1\/unit_01\/lesson_03.md", + "file": "grade_10/geography_10/semester_1/unit_01/lesson_03.md", "outcomes": [ "الدرس الثالث: التنوع الحيوي" ] @@ -3164,7 +3260,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الأولى: الجغرافيا الطبيعية", - "file": "grade_10\/geography_10\/semester_1\/unit_01\/unit_review.md", + "file": "grade_10/geography_10/semester_1/unit_01/unit_review.md", "outcomes": [ "مراجعة الوحدة الأولى: الجغرافيا الطبيعية" ] @@ -3177,7 +3273,7 @@ { "id": "lesson_01", "title": "الدرس الأول: مقومات السياحة", - "file": "grade_10\/geography_10\/semester_1\/unit_02\/lesson_01.md", + "file": "grade_10/geography_10/semester_1/unit_02/lesson_01.md", "outcomes": [ "الدرس الأول: مقومات السياحة" ] @@ -3185,7 +3281,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: الآثار الاقتصادية والاجتماعية للسياحة", - "file": "grade_10\/geography_10\/semester_1\/unit_02\/lesson_02.md", + "file": "grade_10/geography_10/semester_1/unit_02/lesson_02.md", "outcomes": [ "الدرس الثاني: الآثار الاقتصادية والاجتماعية للسياحة" ] @@ -3193,7 +3289,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: النقل", - "file": "grade_10\/geography_10\/semester_1\/unit_02\/lesson_03.md", + "file": "grade_10/geography_10/semester_1/unit_02/lesson_03.md", "outcomes": [ "الدرس الثالث: النقل" ] @@ -3201,7 +3297,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الثانية: السياحة والنقل", - "file": "grade_10\/geography_10\/semester_1\/unit_02\/unit_review.md", + "file": "grade_10/geography_10/semester_1/unit_02/unit_review.md", "outcomes": [ "مراجعة الوحدة الثانية: السياحة والنقل" ] @@ -3214,7 +3310,7 @@ { "id": "lesson_01", "title": "الدرس الأول: الخرائط الموضوعية", - "file": "grade_10\/geography_10\/semester_1\/unit_03\/lesson_01.md", + "file": "grade_10/geography_10/semester_1/unit_03/lesson_01.md", "outcomes": [ "الدرس الأول: الخرائط الموضوعية" ] @@ -3222,7 +3318,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: نظم المعلومات الجغرافية", - "file": "grade_10\/geography_10\/semester_1\/unit_03\/lesson_02.md", + "file": "grade_10/geography_10/semester_1/unit_03/lesson_02.md", "outcomes": [ "الدرس الثاني: نظم المعلومات الجغرافية" ] @@ -3230,7 +3326,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: الأقمار الصناعية وتحليل الصور الفضائية", - "file": "grade_10\/geography_10\/semester_1\/unit_03\/lesson_03.md", + "file": "grade_10/geography_10/semester_1/unit_03/lesson_03.md", "outcomes": [ "الدرس الثالث: الأقمار الصناعية وتحليل الصور الفضائية" ] @@ -3238,7 +3334,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الثالثة: التقنيات الجغرافية", - "file": "grade_10\/geography_10\/semester_1\/unit_03\/unit_review.md", + "file": "grade_10/geography_10/semester_1/unit_03/unit_review.md", "outcomes": [ "مراجعة الوحدة الثالثة: التقنيات الجغرافية" ] @@ -3256,7 +3352,7 @@ { "id": "lesson_01", "title": "الدرس الأول: الموارد المائية", - "file": "grade_10\/geography_10\/semester_2\/unit_04\/lesson_01.md", + "file": "grade_10/geography_10/semester_2/unit_04/lesson_01.md", "outcomes": [ "الدرس الأول: الموارد المائية" ] @@ -3264,7 +3360,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: الموارد الزراعية", - "file": "grade_10\/geography_10\/semester_2\/unit_04\/lesson_02.md", + "file": "grade_10/geography_10/semester_2/unit_04/lesson_02.md", "outcomes": [ "الدرس الثاني: الموارد الزراعية" ] @@ -3272,7 +3368,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: الموارد المعدنية", - "file": "grade_10\/geography_10\/semester_2\/unit_04\/lesson_03.md", + "file": "grade_10/geography_10/semester_2/unit_04/lesson_03.md", "outcomes": [ "الدرس الثالث: الموارد المعدنية" ] @@ -3280,7 +3376,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الرابعة: الموارد الطبيعية", - "file": "grade_10\/geography_10\/semester_2\/unit_04\/unit_review.md", + "file": "grade_10/geography_10/semester_2/unit_04/unit_review.md", "outcomes": [ "مراجعة الوحدة الرابعة: الموارد الطبيعية" ] @@ -3293,7 +3389,7 @@ { "id": "lesson_01", "title": "الدرس الأول: التنمية المستدامة", - "file": "grade_10\/geography_10\/semester_2\/unit_05\/lesson_01.md", + "file": "grade_10/geography_10/semester_2/unit_05/lesson_01.md", "outcomes": [ "الدرس الأول: التنمية المستدامة" ] @@ -3301,7 +3397,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: مصادر الطاقة وأنواعها", - "file": "grade_10\/geography_10\/semester_2\/unit_05\/lesson_02.md", + "file": "grade_10/geography_10/semester_2/unit_05/lesson_02.md", "outcomes": [ "الدرس الثاني: مصادر الطاقة وأنواعها" ] @@ -3309,7 +3405,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: الريادة والابتكار", - "file": "grade_10\/geography_10\/semester_2\/unit_05\/lesson_03.md", + "file": "grade_10/geography_10/semester_2/unit_05/lesson_03.md", "outcomes": [ "الدرس الثالث: الريادة والابتكار" ] @@ -3317,7 +3413,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الخامسة: التنمية المستدامة", - "file": "grade_10\/geography_10\/semester_2\/unit_05\/unit_review.md", + "file": "grade_10/geography_10/semester_2/unit_05/unit_review.md", "outcomes": [ "مراجعة الوحدة الخامسة: التنمية المستدامة" ] @@ -3330,7 +3426,7 @@ { "id": "lesson_01", "title": "الدرس الأول: أمريكا الشمالية والوسطى: الملامح الطبيعية والبشرية", - "file": "grade_10\/geography_10\/semester_2\/unit_06\/lesson_01.md", + "file": "grade_10/geography_10/semester_2/unit_06/lesson_01.md", "outcomes": [ "الدرس الأول: أمريكا الشمالية والوسطى: الملامح الطبيعية والبشرية" ] @@ -3338,7 +3434,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: أمريكا الجنوبية: الملامح الطبيعية والبشرية", - "file": "grade_10\/geography_10\/semester_2\/unit_06\/lesson_02.md", + "file": "grade_10/geography_10/semester_2/unit_06/lesson_02.md", "outcomes": [ "الدرس الثاني: أمريكا الجنوبية: الملامح الطبيعية والبشرية" ] @@ -3346,7 +3442,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: أوقيانوسيا: الملامح الطبيعية والبشرية", - "file": "grade_10\/geography_10\/semester_2\/unit_06\/lesson_03.md", + "file": "grade_10/geography_10/semester_2/unit_06/lesson_03.md", "outcomes": [ "الدرس الثالث: أوقيانوسيا: الملامح الطبيعية والبشرية" ] @@ -3354,7 +3450,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة السادسة: جغرافيا العالم الجديد", - "file": "grade_10\/geography_10\/semester_2\/unit_06\/unit_review.md", + "file": "grade_10/geography_10/semester_2/unit_06/unit_review.md", "outcomes": [ "مراجعة الوحدة السادسة: جغرافيا العالم الجديد" ] @@ -3385,7 +3481,7 @@ { "id": "lesson_01", "title": "الدرس الأول: التربية الوطنية والمدنية: المفهوم والدلالات", - "file": "grade_10\/civics_10\/semester_1\/unit_01\/lesson_01.md", + "file": "grade_10/civics_10/semester_1/unit_01/lesson_01.md", "outcomes": [ "الدرس الأول: التربية الوطنية والمدنية: المفهوم والدلالات" ] @@ -3393,7 +3489,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: هويتي الأردنية", - "file": "grade_10\/civics_10\/semester_1\/unit_01\/lesson_02.md", + "file": "grade_10/civics_10/semester_1/unit_01/lesson_02.md", "outcomes": [ "الدرس الثاني: هويتي الأردنية" ] @@ -3401,7 +3497,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: المواطنة", - "file": "grade_10\/civics_10\/semester_1\/unit_01\/lesson_03.md", + "file": "grade_10/civics_10/semester_1/unit_01/lesson_03.md", "outcomes": [ "الدرس الثالث: المواطنة" ] @@ -3409,7 +3505,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: الديمقراطية", - "file": "grade_10\/civics_10\/semester_1\/unit_01\/lesson_04.md", + "file": "grade_10/civics_10/semester_1/unit_01/lesson_04.md", "outcomes": [ "الدرس الرابع: الديمقراطية" ] @@ -3417,7 +3513,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: خدمة العلم", - "file": "grade_10\/civics_10\/semester_1\/unit_01\/lesson_05.md", + "file": "grade_10/civics_10/semester_1/unit_01/lesson_05.md", "outcomes": [ "الدرس الخامس: خدمة العلم" ] @@ -3425,7 +3521,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الأولى: التربية الوطنية والمدنية", - "file": "grade_10\/civics_10\/semester_1\/unit_01\/unit_review.md", + "file": "grade_10/civics_10/semester_1/unit_01/unit_review.md", "outcomes": [ "مراجعة الوحدة الأولى: التربية الوطنية والمدنية" ] @@ -3438,7 +3534,7 @@ { "id": "lesson_01", "title": "الدرس الأول: الدستور الأردني وسيادة القانون", - "file": "grade_10\/civics_10\/semester_1\/unit_02\/lesson_01.md", + "file": "grade_10/civics_10/semester_1/unit_02/lesson_01.md", "outcomes": [ "الدرس الأول: الدستور الأردني وسيادة القانون" ] @@ -3446,7 +3542,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: المشاركة في الحياة العامة", - "file": "grade_10\/civics_10\/semester_1\/unit_02\/lesson_02.md", + "file": "grade_10/civics_10/semester_1/unit_02/lesson_02.md", "outcomes": [ "الدرس الثاني: المشاركة في الحياة العامة" ] @@ -3454,7 +3550,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: الانتخابات (أنا والصندوق)", - "file": "grade_10\/civics_10\/semester_1\/unit_02\/lesson_03.md", + "file": "grade_10/civics_10/semester_1/unit_02/lesson_03.md", "outcomes": [ "الدرس الثالث: الانتخابات (أنا والصندوق)" ] @@ -3462,7 +3558,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: الأحزاب السياسية", - "file": "grade_10\/civics_10\/semester_1\/unit_02\/lesson_04.md", + "file": "grade_10/civics_10/semester_1/unit_02/lesson_04.md", "outcomes": [ "الدرس الرابع: الأحزاب السياسية" ] @@ -3470,7 +3566,7 @@ { "id": "lesson_05", "title": "الدرس الخامس: أردن المستقبل", - "file": "grade_10\/civics_10\/semester_1\/unit_02\/lesson_05.md", + "file": "grade_10/civics_10/semester_1/unit_02/lesson_05.md", "outcomes": [ "الدرس الخامس: أردن المستقبل" ] @@ -3478,7 +3574,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الثانية: المشاركة في الحياة العامة", - "file": "grade_10\/civics_10\/semester_1\/unit_02\/unit_review.md", + "file": "grade_10/civics_10/semester_1/unit_02/unit_review.md", "outcomes": [ "مراجعة الوحدة الثانية: المشاركة في الحياة العامة" ] @@ -3496,7 +3592,7 @@ { "id": "lesson_01", "title": "الدرس الأول: الشرعة الدولية لحقوق الإنسان", - "file": "grade_10\/civics_10\/semester_2\/unit_03\/lesson_01.md", + "file": "grade_10/civics_10/semester_2/unit_03/lesson_01.md", "outcomes": [ "الدرس الأول: الشرعة الدولية لحقوق الإنسان" ] @@ -3504,7 +3600,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: حقوق الإنسان في الأردن", - "file": "grade_10\/civics_10\/semester_2\/unit_03\/lesson_02.md", + "file": "grade_10/civics_10/semester_2/unit_03/lesson_02.md", "outcomes": [ "الدرس الثاني: حقوق الإنسان في الأردن" ] @@ -3512,7 +3608,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الثالثة: حقوق الإنسان", - "file": "grade_10\/civics_10\/semester_2\/unit_03\/unit_review.md", + "file": "grade_10/civics_10/semester_2/unit_03/unit_review.md", "outcomes": [ "مراجعة الوحدة الثالثة: حقوق الإنسان" ] @@ -3525,7 +3621,7 @@ { "id": "lesson_01", "title": "الدرس الأول: العلاقات الأردنية العربية", - "file": "grade_10\/civics_10\/semester_2\/unit_04\/lesson_01.md", + "file": "grade_10/civics_10/semester_2/unit_04/lesson_01.md", "outcomes": [ "الدرس الأول: العلاقات الأردنية العربية" ] @@ -3533,7 +3629,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: العلاقات الأردنية الدولية", - "file": "grade_10\/civics_10\/semester_2\/unit_04\/lesson_02.md", + "file": "grade_10/civics_10/semester_2/unit_04/lesson_02.md", "outcomes": [ "الدرس الثاني: العلاقات الأردنية الدولية" ] @@ -3541,7 +3637,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الرابعة: العلاقات الأردنية الدولية", - "file": "grade_10\/civics_10\/semester_2\/unit_04\/unit_review.md", + "file": "grade_10/civics_10/semester_2/unit_04/unit_review.md", "outcomes": [ "مراجعة الوحدة الرابعة: العلاقات الأردنية الدولية" ] @@ -3554,7 +3650,7 @@ { "id": "lesson_01", "title": "الدرس الأول: التراث الوطني", - "file": "grade_10\/civics_10\/semester_2\/unit_05\/lesson_01.md", + "file": "grade_10/civics_10/semester_2/unit_05/lesson_01.md", "outcomes": [ "الدرس الأول: التراث الوطني" ] @@ -3562,7 +3658,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: المحافظة على التراث", - "file": "grade_10\/civics_10\/semester_2\/unit_05\/lesson_02.md", + "file": "grade_10/civics_10/semester_2/unit_05/lesson_02.md", "outcomes": [ "الدرس الثاني: المحافظة على التراث" ] @@ -3570,7 +3666,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة الخامسة: التراث", - "file": "grade_10\/civics_10\/semester_2\/unit_05\/unit_review.md", + "file": "grade_10/civics_10/semester_2/unit_05/unit_review.md", "outcomes": [ "مراجعة الوحدة الخامسة: التراث" ] @@ -3583,7 +3679,7 @@ { "id": "lesson_01", "title": "الدرس الأول: النزاهة الوطنية", - "file": "grade_10\/civics_10\/semester_2\/unit_06\/lesson_01.md", + "file": "grade_10/civics_10/semester_2/unit_06/lesson_01.md", "outcomes": [ "الدرس الأول: النزاهة الوطنية" ] @@ -3591,7 +3687,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: مكافحة الفساد والمحسوبية", - "file": "grade_10\/civics_10\/semester_2\/unit_06\/lesson_02.md", + "file": "grade_10/civics_10/semester_2/unit_06/lesson_02.md", "outcomes": [ "الدرس الثاني: مكافحة الفساد والمحسوبية" ] @@ -3599,7 +3695,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة السادسة: النزاهة ومكافحة الفساد", - "file": "grade_10\/civics_10\/semester_2\/unit_06\/unit_review.md", + "file": "grade_10/civics_10/semester_2/unit_06/unit_review.md", "outcomes": [ "مراجعة الوحدة السادسة: النزاهة ومكافحة الفساد" ] @@ -3612,7 +3708,7 @@ { "id": "lesson_01", "title": "الدرس الأول: المحتوى الإعلامي", - "file": "grade_10\/civics_10\/semester_2\/unit_07\/lesson_01.md", + "file": "grade_10/civics_10/semester_2/unit_07/lesson_01.md", "outcomes": [ "الدرس الأول: المحتوى الإعلامي" ] @@ -3620,7 +3716,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: السلوك", - "file": "grade_10\/civics_10\/semester_2\/unit_07\/lesson_02.md", + "file": "grade_10/civics_10/semester_2/unit_07/lesson_02.md", "outcomes": [ "الدرس الثاني: السلوك" ] @@ -3628,7 +3724,7 @@ { "id": "unit_review", "title": "مراجعة الوحدة السابعة: مهارات حياتية", - "file": "grade_10\/civics_10\/semester_2\/unit_07\/unit_review.md", + "file": "grade_10/civics_10/semester_2/unit_07/unit_review.md", "outcomes": [ "مراجعة الوحدة السابعة: مهارات حياتية" ] @@ -3659,7 +3755,7 @@ { "id": "lesson_01", "title": "الدرس (1): المشروع وإدارته", - "file": "grade_10\/financial_10\/semester_1\/unit_01\/lesson_01.md", + "file": "grade_10/financial_10/semester_1/unit_01/lesson_01.md", "outcomes": [ "الدرس (1): المشروع وإدارته" ] @@ -3667,7 +3763,7 @@ { "id": "lesson_02", "title": "الدرس (2): فريق المشروع", - "file": "grade_10\/financial_10\/semester_1\/unit_01\/lesson_02.md", + "file": "grade_10/financial_10/semester_1/unit_01/lesson_02.md", "outcomes": [ "الدرس (2): فريق المشروع" ] @@ -3675,7 +3771,7 @@ { "id": "lesson_03", "title": "الدرس (3): مراحل إدارة المشروع وفريق العمل", - "file": "grade_10\/financial_10\/semester_1\/unit_01\/lesson_03.md", + "file": "grade_10/financial_10/semester_1/unit_01/lesson_03.md", "outcomes": [ "الدرس (3): مراحل إدارة المشروع وفريق العمل" ] @@ -3683,7 +3779,7 @@ { "id": "lesson_04", "title": "الدرس (4): نماذج بدء المشروع والتخطيط الزمني", - "file": "grade_10\/financial_10\/semester_1\/unit_01\/lesson_04.md", + "file": "grade_10/financial_10/semester_1/unit_01/lesson_04.md", "outcomes": [ "الدرس (4): نماذج بدء المشروع والتخطيط الزمني" ] @@ -3691,7 +3787,7 @@ { "id": "lesson_05", "title": "الدرس (5): مراقبة المشروع وتقييمه", - "file": "grade_10\/financial_10\/semester_1\/unit_01\/lesson_05.md", + "file": "grade_10/financial_10/semester_1/unit_01/lesson_05.md", "outcomes": [ "الدرس (5): مراقبة المشروع وتقييمه" ] @@ -3699,7 +3795,7 @@ { "id": "unit_test_and_project", "title": "اختبار نهاية الوحدة ومشروع الوحدة: إدارة المشروعات", - "file": "grade_10\/financial_10\/semester_1\/unit_01\/unit_test_and_project.md", + "file": "grade_10/financial_10/semester_1/unit_01/unit_test_and_project.md", "outcomes": [ "اختبار نهاية الوحدة ومشروع الوحدة: إدارة المشروعات" ] @@ -3712,7 +3808,7 @@ { "id": "lesson_01", "title": "الدرس الأول: مفهوم دراسة الجدوى الاقتصادية وأهميتها", - "file": "grade_10\/financial_10\/semester_1\/unit_02\/lesson_01.md", + "file": "grade_10/financial_10/semester_1/unit_02/lesson_01.md", "outcomes": [ "الدرس الأول: مفهوم دراسة الجدوى الاقتصادية وأهميتها" ] @@ -3720,7 +3816,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: خطوات إعداد دراسة الجدوى والتحليل المالي", - "file": "grade_10\/financial_10\/semester_1\/unit_02\/lesson_02.md", + "file": "grade_10/financial_10/semester_1/unit_02/lesson_02.md", "outcomes": [ "الدرس الثاني: خطوات إعداد دراسة الجدوى والتحليل المالي" ] @@ -3728,7 +3824,7 @@ { "id": "unit_review", "title": "اختبار ومراجعة الوحدة الثانية: الجدوى الاقتصادية للمشروعات", - "file": "grade_10\/financial_10\/semester_1\/unit_02\/unit_review.md", + "file": "grade_10/financial_10/semester_1/unit_02/unit_review.md", "outcomes": [ "اختبار ومراجعة الوحدة الثانية: الجدوى الاقتصادية للمشروعات" ] @@ -3754,12 +3850,12 @@ "name": "الفصل الدراسي الأول", "units": { "unit_01": { - "name": "الوحدة الأولى", + "name": "الوحدة الأولى: تحليل البيانات (Data Analysis)", "lessons": [ { "id": "lesson_01", "title": "الدرس الأول: البيانات والمعلومات (Information and Data)", - "file": "grade_10\/digital_skills_10\/semester_1\/unit_01\/lesson_01.md", + "file": "grade_10/digital_skills_10/semester_1/unit_01/lesson_01.md", "outcomes": [ "الدرس الأول: البيانات والمعلومات (Information and Data)" ] @@ -3767,7 +3863,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: أنواع البيانات وطرائق تنظيمها (Data Types & Organization)", - "file": "grade_10\/digital_skills_10\/semester_1\/unit_01\/lesson_02.md", + "file": "grade_10/digital_skills_10/semester_1/unit_01/lesson_02.md", "outcomes": [ "الدرس الثاني: أنواع البيانات وطرائق تنظيمها (Data Types & Organization)" ] @@ -3775,7 +3871,7 @@ { "id": "lesson_03", "title": "الدرس الثالث: التمثيل المرئي للبيانات (Data Visualization)", - "file": "grade_10\/digital_skills_10\/semester_1\/unit_01\/lesson_03.md", + "file": "grade_10/digital_skills_10/semester_1/unit_01/lesson_03.md", "outcomes": [ "الدرس الثالث: التمثيل المرئي للبيانات (Data Visualization)" ] @@ -3783,7 +3879,7 @@ { "id": "lesson_04", "title": "الدرس الرابع: تحليل البيانات (Data Analysis with Excel)", - "file": "grade_10\/digital_skills_10\/semester_1\/unit_01\/lesson_04.md", + "file": "grade_10/digital_skills_10/semester_1/unit_01/lesson_04.md", "outcomes": [ "الدرس الرابع: تحليل البيانات (Data Analysis with Excel)" ] @@ -3791,7 +3887,7 @@ { "id": "unit_summary", "title": "ملخص وأسئلة الوحدة وتقويم ذاتي: تحليل البيانات", - "file": "grade_10\/digital_skills_10\/semester_1\/unit_01\/unit_summary.md", + "file": "grade_10/digital_skills_10/semester_1/unit_01/unit_summary.md", "outcomes": [ "ملخص وأسئلة الوحدة وتقويم ذاتي: تحليل البيانات" ] @@ -3804,12 +3900,12 @@ "name": "الفصل الدراسي الثاني", "units": { "unit_01": { - "name": "الوحدة الأولى", + "name": "الوحدة الأولى: إنترنت الأشياء (Internet of Things)", "lessons": [ { "id": "lesson_01", "title": "الدرس الأول: مفهوم إنترنت الأشياء ومكوناتها ووظائفها", - "file": "grade_10\/digital_skills_10\/semester_2\/unit_01\/lesson_01.md", + "file": "grade_10/digital_skills_10/semester_2/unit_01/lesson_01.md", "outcomes": [ "الدرس الأول: مفهوم إنترنت الأشياء ومكوناتها ووظائفها" ] @@ -3817,7 +3913,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: تكوين شبكة إنترنت الأشياء ومحاكاة نقل البيانات", - "file": "grade_10\/digital_skills_10\/semester_2\/unit_01\/lesson_02.md", + "file": "grade_10/digital_skills_10/semester_2/unit_01/lesson_02.md", "outcomes": [ "الدرس الثاني: تكوين شبكة إنترنت الأشياء ومحاكاة نقل البيانات" ] @@ -3825,7 +3921,7 @@ { "id": "unit_review", "title": "مراجعة ومشروع الوحدة الأولى: إنترنت الأشياء", - "file": "grade_10\/digital_skills_10\/semester_2\/unit_01\/unit_review.md", + "file": "grade_10/digital_skills_10/semester_2/unit_01/unit_review.md", "outcomes": [ "مراجعة ومشروع الوحدة الأولى: إنترنت الأشياء" ] @@ -3833,12 +3929,12 @@ ] }, "unit_02": { - "name": "الوحدة الثانية", + "name": "الوحدة الثانية: الذكاء الاصطناعي (Artificial Intelligence)", "lessons": [ { "id": "lesson_01", "title": "الدرس الأول: أنظمة قواعد المعرفة ومفهوم المنطق في الذكاء الاصطناعي", - "file": "grade_10\/digital_skills_10\/semester_2\/unit_02\/lesson_01.md", + "file": "grade_10/digital_skills_10/semester_2/unit_02/lesson_01.md", "outcomes": [ "الدرس الأول: أنظمة قواعد المعرفة ومفهوم المنطق في الذكاء الاصطناعي" ] @@ -3846,7 +3942,7 @@ { "id": "lesson_02", "title": "الدرس الثاني: تطبيقات الذكاء الاصطناعي وتقنية النانو والهولوغرام", - "file": "grade_10\/digital_skills_10\/semester_2\/unit_02\/lesson_02.md", + "file": "grade_10/digital_skills_10/semester_2/unit_02/lesson_02.md", "outcomes": [ "الدرس الثاني: تطبيقات الذكاء الاصطناعي وتقنية النانو والهولوغرام" ] @@ -3854,7 +3950,7 @@ { "id": "unit_review", "title": "ملخص وأسئلة الوحدة الثانية: الذكاء الاصطناعي", - "file": "grade_10\/digital_skills_10\/semester_2\/unit_02\/unit_review.md", + "file": "grade_10/digital_skills_10/semester_2/unit_02/unit_review.md", "outcomes": [ "ملخص وأسئلة الوحدة الثانية: الذكاء الاصطناعي" ]