fix(platform): grade-gate student app, isolate per-video resume & checkpoints, remove fake teachers, enhance arabic lab, clean flutter analyze

This commit is contained in:
Hamza-Ayed
2026-09-04 23:41:32 +03:00
parent ae00aa943e
commit e7bc20bc98
22 changed files with 291 additions and 316 deletions
@@ -1,6 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../../core/theme/app_colors.dart';
/// ==============================================================================
/// SAQEL ENTERPRISE - ARABIC INTERACTIVE LAB (شجرة الإعراب ومختبر العروض)
@@ -140,12 +139,109 @@ class _ArabicInteractiveLabViewState extends State<ArabicInteractiveLabView>
},
],
},
{
'fullText': 'المُعَلِّمُ المُخْلِصُ مُحْتَرَمٌ قَدْرُهُ بَيْنَ النَّاسِ',
'type': 'جملة اسمية تشتمل على اسم فاعل واسم مفعول وعملهما',
'rootNode': 'الجملة الاسمية والمشتقات العاملة (الصف العاشر)',
'words': [
{
'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': 'ظرف مكان',
},
{
'word': 'النَّاسِ',
'role': 'مضاف إليه مجرور',
'case': 'مجرور وعلامة جره الكسرة الظاهرة على آخره',
'explanation': 'اسم مجرور بالإضافة جاء بعد ظرف المكان.',
'tag': 'مضاف إليه',
},
],
},
{
'fullText': 'مَنْ يَزْرَعْ خَيْراً يَحْصُدْ ثِمَاراً طَيِّبَةً',
'type': 'أسلوب شرط جازم (منهاج العاشر الأساسي)',
'rootNode': 'أسلوب الشرط (اسم شرط + فعل الشرط + جواب الشرط)',
'words': [
{
'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': 'مفعول به',
},
{
'word': 'طَيِّبَةً',
'role': 'نعت لثماراً',
'case': 'منصوب وعلامة نصبه تنوين الفتح',
'explanation': 'صفة طابقت موصوفها في التنكير والجمع والتأنيث والنصب.',
'tag': 'نعت منصوب',
},
],
},
];
// Poetry Meter State
int _selectedMeterIndex = 0;
bool _isPlayingBeat = false;
int _currentBeatStep = 0;
final List<Map<String, dynamic>> _poetryMeters = [
{
@@ -186,7 +282,9 @@ class _ArabicInteractiveLabViewState extends State<ArabicInteractiveLabView>
@override
void initState() {
super.initState();
_tabController = TabController(length: 2, vsync: this);
_tabController = TabController(length: 2, vsync: this)..addListener(() {
if (mounted) setState(() {});
});
}
@override
@@ -339,8 +437,8 @@ class _ArabicInteractiveLabViewState extends State<ArabicInteractiveLabView>
_selectedWordIndex = null;
}),
child: Container(
margin: EdgeInsets.only(left: idx == 0 ? 8 : 0),
padding: const EdgeInsets.symmetric(vertical: 10),
margin: const EdgeInsets.only(left: 8),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: isCur
? const Color(0xFF1E293B)
@@ -1,6 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../../core/theme/app_colors.dart';
/// ==============================================================================
/// SAQEL ENTERPRISE - JORDAN HISTORY INTERACTIVE TIMELINE (الخط الزمني لتاريخ الأردن)
@@ -1,6 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../../core/theme/app_colors.dart';
/// ==============================================================================
/// SAQEL ENTERPRISE - ISLAMIC INHERITANCE & TAJWEED LAB (حاسبة المواريث والتجويد)
@@ -787,211 +787,21 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
);
}
/// Multi-Teacher and AI Video Selector Bottom Sheet
/// Direct Launch of Socratic Video Player or Transparent No-Video Sheet
void _showLessonVideoSelector(BuildContext context, CurriculumLessonItemModel lesson) {
if (!lesson.hasVideo) {
_showNoVideoAvailableSheet(context, lesson);
return;
}
showModalBottomSheet(
context: context,
backgroundColor: AppColors.darkSurface,
isScrollControlled: true,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
),
builder: (ctx) => SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Colors.white24,
borderRadius: BorderRadius.circular(2),
),
),
),
const SizedBox(height: 16),
Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: AppColors.saqelCyan.withAlpha(25),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(CupertinoIcons.play_circle_fill, color: AppColors.saqelCyan, size: 24),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'اختر أسلوب الشرح المناسب لك 🎥',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 16),
),
const SizedBox(height: 3),
Text(
lesson.title,
style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 12),
),
],
),
),
],
),
const SizedBox(height: 18),
// Option 1: Official Saqel AI Master Lesson
_buildVideoOptionCard(
title: 'شرح الذكاء الاصطناعي لمنصة صَقِل 🤖',
subtitle: 'الشرح الرسمي المعتمد وتفكيك المفاهيم خطوة بخطوة',
tag: 'المنهاج المعتمد + سقراطي',
tagColor: AppColors.saqelCyan,
icon: CupertinoIcons.sparkles,
iconColor: AppColors.saqelCyan,
onTap: () {
Navigator.of(ctx).pop();
Navigator.of(context).push(
CupertinoPageRoute(
builder: (_) => SocraticVideoPlayerScreen(
lesson: lesson,
subject: widget.subject,
instructorName: 'منصة صَقِل الذكية 🤖',
videoSourceLabel: 'شرح الذكاء الاصطناعي الرسمي',
),
),
);
},
),
const SizedBox(height: 12),
// Option 2: Certified Teacher 1
_buildVideoOptionCard(
title: 'شرح الأستاذ رعد الخمايسة 👨‍🏫',
subtitle: 'خبير التوجيهي — حل نماذج وأسئلة وزارية إضافية',
tag: 'معلم معتمد 🌟 4.9',
tagColor: AppColors.guardianAmber,
icon: CupertinoIcons.person_crop_circle_badge_checkmark,
iconColor: AppColors.guardianAmber,
onTap: () {
Navigator.of(ctx).pop();
Navigator.of(context).push(
CupertinoPageRoute(
builder: (_) => SocraticVideoPlayerScreen(
lesson: lesson,
subject: widget.subject,
instructorName: 'أ. رعد الخمايسة (معلم معتمد)',
videoSourceLabel: 'شرح الأستاذ رعد الخمايسة',
),
),
);
},
),
const SizedBox(height: 12),
// Option 3: Certified Teacher 2
_buildVideoOptionCard(
title: 'شرح الأستاذ إياد النجار 👨‍🏫',
subtitle: 'كبير المعلمين — تبسيط المفاهيم المعقدة والتحليل البياني',
tag: 'معلم معتمد 🌟 4.8',
tagColor: AppColors.emeraldGreen,
icon: CupertinoIcons.person_crop_circle_fill,
iconColor: AppColors.emeraldGreen,
onTap: () {
Navigator.of(ctx).pop();
Navigator.of(context).push(
CupertinoPageRoute(
builder: (_) => SocraticVideoPlayerScreen(
lesson: lesson,
subject: widget.subject,
instructorName: 'أ. إياد النجار (كبير المعلمين)',
videoSourceLabel: 'شرح الأستاذ إياد النجار',
),
),
);
},
),
const SizedBox(height: 16),
],
),
),
),
);
}
Widget _buildVideoOptionCard({
required String title,
required String subtitle,
required String tag,
required Color tagColor,
required IconData icon,
required Color iconColor,
required VoidCallback onTap,
}) {
return InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(14),
child: Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: AppColors.darkCard,
borderRadius: BorderRadius.circular(14),
border: Border.all(color: AppColors.darkCardBorder),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: iconColor.withAlpha(25),
borderRadius: BorderRadius.circular(12),
),
child: Icon(icon, color: iconColor, size: 22),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 13.5),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 2),
decoration: BoxDecoration(
color: tagColor.withAlpha(25),
borderRadius: BorderRadius.circular(6),
),
child: Text(
tag,
style: TextStyle(color: tagColor, fontSize: 10, fontWeight: FontWeight.w700),
),
),
],
),
const SizedBox(height: 4),
Text(
subtitle,
style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11.5),
),
],
),
),
const SizedBox(width: 8),
const Icon(CupertinoIcons.chevron_back, color: AppColors.textSecondaryDark, size: 14),
],
// Direct authentic launch: zero mockups, zero fake teacher names
Navigator.of(context).push(
CupertinoPageRoute(
builder: (_) => SocraticVideoPlayerScreen(
lesson: lesson,
subject: widget.subject,
instructorName: 'منصة صَقِل التعليمية المعتمدة',
videoSourceLabel: 'الشرح الرقمي الرسمي المعتمد',
),
),
);
@@ -188,45 +188,42 @@ class _SubjectsGridScreenState extends State<SubjectsGridScreen> {
),
const SizedBox(height: 16),
// K-12 Grade Level Selector Bar (الصف الأول -> التوجيهي)
SizedBox(
height: 42,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: (state.availableGrades.isNotEmpty ? state.availableGrades : GradeLevelModel.allK12Grades).length,
itemBuilder: (context, gIdx) {
final grade = (state.availableGrades.isNotEmpty ? state.availableGrades : GradeLevelModel.allK12Grades)[gIdx];
final isSelected = state.selectedGrade == grade.key || state.selectedGrade.replaceAll('grade_', '') == grade.key.replaceAll('grade_', '');
return Padding(
padding: const EdgeInsets.only(left: 8),
child: InkWell(
onTap: () => context.read<CurriculumCubit>().selectGrade(grade.key),
borderRadius: BorderRadius.circular(14),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: isSelected ? AppColors.saqelCyan.withAlpha(30) : AppColors.darkSurface,
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: isSelected ? AppColors.saqelCyan : AppColors.darkCardBorder,
width: isSelected ? 1.5 : 1.0,
),
),
child: Center(
child: Text(
grade.name,
style: TextStyle(
color: isSelected ? AppColors.saqelCyan : AppColors.textSecondaryDark,
fontWeight: isSelected ? FontWeight.w800 : FontWeight.w500,
fontSize: 12.5,
),
),
),
),
// Official Academic Grade Level Banner (Strict Grade-Gate — الصف العاشر فقط)
Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: AppColors.saqelCyan.withAlpha(20),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: AppColors.saqelCyan.withAlpha(60)),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: AppColors.saqelCyan.withAlpha(35),
borderRadius: BorderRadius.circular(10),
),
);
},
child: const Icon(CupertinoIcons.lock_shield_fill, color: AppColors.saqelCyan, size: 18),
),
const SizedBox(width: 12),
const Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'الصف العاشر الأساسي — المسار الأكاديمي المعتمد',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 13.5),
),
SizedBox(height: 2),
Text(
'خطة دراسية موحدة ومطابقة للمنهاج الوزاري الأردني المطور',
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 11),
),
],
),
),
],
),
),
const SizedBox(height: 20),
@@ -1,6 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import '../../../core/theme/app_colors.dart';
import '../../../data/models/error_notebook_model.dart';
import '../../../data/repositories/error_notebook_repository.dart';
@@ -51,6 +51,7 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
late AnimationController _watermarkController;
VideoPlayerController? _videoController;
bool _isVideoInitialized = false;
String? _videoInitError;
int _lastSyncedPosition = -1;
Timer? _controlsHideTimer;
bool _showVideoControls = true;
@@ -162,6 +163,7 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
if (mounted) {
setState(() {
_isVideoInitialized = true;
_videoInitError = null;
});
_revealVideoControls();
if (state.currentPositionSeconds > 3) {
@@ -178,6 +180,13 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
_videoController!.play();
}
}
}).catchError((err) {
if (mounted) {
setState(() {
_isVideoInitialized = false;
_videoInitError = 'الفيديو قيد المزامنة والتجهيز على خادم البث المباشر';
});
}
});
} else if (_isVideoInitialized) {
// Sync play/pause state
@@ -311,7 +320,31 @@ class _SocraticVideoPlayerScreenState extends State<SocraticVideoPlayerScreen> w
),
),
// Center Lesson Title & Visuals
if (_showVideoControls) Center(
if (_videoInitError != null)
Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(CupertinoIcons.videocam_fill, color: AppColors.guardianAmber, size: 44),
const SizedBox(height: 12),
Text(
_videoInitError!,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w700),
),
const SizedBox(height: 8),
const Text(
'يمكنك دراسة نتاجات وملخص الدرس عبر تبويب الكتب والمذكرات',
textAlign: TextAlign.center,
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 12),
),
],
),
),
)
else if (_showVideoControls) Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [