Update Saqel Platform: 2026-09-12 08:50:01
This commit is contained in:
@@ -30,6 +30,9 @@ 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 '../virtual_labs/labs_gallery_screen.dart';
|
||||
import '../virtual_labs/labs_registry.dart';
|
||||
import '../virtual_labs/subject_virtual_labs_view.dart';
|
||||
import '../../../data/models/exam_model.dart';
|
||||
import '../../../data/repositories/app_repositories.dart';
|
||||
import '../../../data/repositories/curriculum_repository.dart';
|
||||
@@ -98,6 +101,7 @@ class _SubjectHubScreenState extends State<SubjectHubScreen>
|
||||
widget.subject.title.contains('دراسات');
|
||||
|
||||
bool get _hasLab =>
|
||||
Grade10LabsRegistry.bySubjectNormalized(widget.subject.title).isNotEmpty ||
|
||||
_isMathSubject ||
|
||||
_isEnglishSubject ||
|
||||
_isChemistrySubject ||
|
||||
@@ -109,46 +113,57 @@ class _SubjectHubScreenState extends State<SubjectHubScreen>
|
||||
_isHistorySubject;
|
||||
|
||||
Tab get _dynamicLabTab {
|
||||
if (_isMathSubject) {
|
||||
final norm = Grade10LabsRegistry.normalizeSubject(widget.subject.title);
|
||||
if (_isMathSubject || norm == 'الرياضيات') {
|
||||
return const Tab(
|
||||
icon: Icon(CupertinoIcons.function, size: 18),
|
||||
text: 'معمل جيوجبرا للرياضيات 📐',
|
||||
text: 'مختبر الرياضيات والهندسة 📐',
|
||||
);
|
||||
} else if (_isEnglishSubject) {
|
||||
} else if (_isEnglishSubject || norm == 'اللغة الإنجليزية') {
|
||||
return const Tab(
|
||||
icon: Icon(CupertinoIcons.headphones, size: 18),
|
||||
text: 'مختبر الصوتيات والقواعد 🎧',
|
||||
);
|
||||
} else if (_isArabicSubject) {
|
||||
} else if (_isArabicSubject || norm == 'العربية لغتي') {
|
||||
return const Tab(
|
||||
icon: Icon(CupertinoIcons.arrow_branch, size: 18),
|
||||
text: 'مختبر الإعراب والعروض 📜',
|
||||
);
|
||||
} else if (_isIslamicSubject) {
|
||||
} else if (_isIslamicSubject || norm == 'التربية الإسلامية') {
|
||||
return const Tab(
|
||||
icon: Icon(CupertinoIcons.money_dollar_circle_fill, size: 18),
|
||||
text: 'حاسبة المواريث والتجويد ⚖️',
|
||||
);
|
||||
} else if (_isHistorySubject) {
|
||||
} else if (_isHistorySubject || norm == 'تاريخ الأردن') {
|
||||
return const Tab(
|
||||
icon: Icon(CupertinoIcons.time_solid, size: 18),
|
||||
text: 'الخط الزمني لتاريخ الأردن 🏛️',
|
||||
);
|
||||
} else if (_isChemistrySubject) {
|
||||
} else if (_isChemistrySubject || norm == 'الكيمياء') {
|
||||
return const Tab(
|
||||
icon: Icon(CupertinoIcons.flame_fill, size: 18),
|
||||
text: 'مختبر الكيمياء والطيوف ⚗️',
|
||||
);
|
||||
} else if (_isBiologySubject) {
|
||||
} else if (_isBiologySubject || norm == 'العلوم الحياتية') {
|
||||
return const Tab(
|
||||
icon: Icon(CupertinoIcons.viewfinder, size: 18),
|
||||
text: 'مختبر الأحياء والمجهر 🔬',
|
||||
);
|
||||
} else if (_isEarthSciencesSubject) {
|
||||
} else if (_isEarthSciencesSubject || norm == 'علوم الأرض والبيئة') {
|
||||
return const Tab(
|
||||
icon: Icon(CupertinoIcons.globe, size: 18),
|
||||
text: 'مختبر علوم الأرض والصخور 🌍',
|
||||
);
|
||||
} else if (norm == 'الثقافة المالية') {
|
||||
return const Tab(
|
||||
icon: Icon(CupertinoIcons.chart_bar_square_fill, size: 18),
|
||||
text: 'مختبر الثقافة المالية والجدوى 📊',
|
||||
);
|
||||
} else if (norm == 'المهارات الرقمية') {
|
||||
return const Tab(
|
||||
icon: Icon(CupertinoIcons.desktopcomputer, size: 18),
|
||||
text: 'مختبر الخوارزميات والبرمجة 💻',
|
||||
);
|
||||
} else {
|
||||
return const Tab(
|
||||
icon: Icon(CupertinoIcons.bolt_fill, size: 18),
|
||||
@@ -158,25 +173,44 @@ class _SubjectHubScreenState extends State<SubjectHubScreen>
|
||||
}
|
||||
|
||||
Widget get _dynamicLabWidget {
|
||||
Widget? specializedTool;
|
||||
String? specializedTitle;
|
||||
|
||||
if (_isMathSubject) {
|
||||
return const MathInteractiveLabView();
|
||||
specializedTool = const MathInteractiveLabView();
|
||||
specializedTitle = 'معمل جيوجبرا 📐';
|
||||
} else if (_isEnglishSubject) {
|
||||
return const EnglishInteractiveLabView();
|
||||
specializedTool = const EnglishInteractiveLabView();
|
||||
specializedTitle = 'مدرب الصوتيات 🎙️';
|
||||
} else if (_isArabicSubject) {
|
||||
return const ArabicInteractiveLabView();
|
||||
specializedTool = const ArabicInteractiveLabView();
|
||||
specializedTitle = 'شجرة الإعراب 📜';
|
||||
} else if (_isIslamicSubject) {
|
||||
return const IslamicInteractiveLabView();
|
||||
specializedTool = const IslamicInteractiveLabView();
|
||||
specializedTitle = 'حاسبة المواريث ⚖️';
|
||||
} else if (_isHistorySubject) {
|
||||
return const HistoryInteractiveTimelineView();
|
||||
specializedTool = const HistoryInteractiveTimelineView();
|
||||
specializedTitle = 'الخط الزمني الشامل 🏛️';
|
||||
} else if (_isChemistrySubject) {
|
||||
return const ChemistryInteractiveLabView();
|
||||
specializedTool = const ChemistryInteractiveLabView();
|
||||
specializedTitle = 'طيف ذرة بور ⚗️';
|
||||
} else if (_isBiologySubject) {
|
||||
return const BiologyInteractiveLabView();
|
||||
specializedTool = const BiologyInteractiveLabView();
|
||||
specializedTitle = 'المجهر الرقمي 🔬';
|
||||
} else if (_isEarthSciencesSubject) {
|
||||
return const EarthSciencesInteractiveLabView();
|
||||
} else {
|
||||
return const PhysicsInteractiveLabView();
|
||||
specializedTool = const EarthSciencesInteractiveLabView();
|
||||
specializedTitle = 'دورة الصخور والطبقات 🌍';
|
||||
} else if (_isPhysicsSubject) {
|
||||
specializedTool = const PhysicsInteractiveLabView();
|
||||
specializedTitle = 'محاكاة المتجهات ⚡';
|
||||
}
|
||||
|
||||
return SubjectVirtualLabsView(
|
||||
subjectTitle: widget.subject.title,
|
||||
primaryColor: widget.subject.primaryColor,
|
||||
specializedToolWidget: specializedTool,
|
||||
specializedToolTitle: specializedTitle,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -207,6 +241,23 @@ class _SubjectHubScreenState extends State<SubjectHubScreen>
|
||||
widget.subject.title,
|
||||
style: AppTypography.titleLarge.copyWith(color: Colors.white, fontSize: 18),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: 'المختبرات الافتراضية لكل درس',
|
||||
icon: const Icon(CupertinoIcons.lab_flask_solid,
|
||||
color: AppColors.saqelCyan, size: 22),
|
||||
onPressed: () {
|
||||
Navigator.of(context).push(
|
||||
CupertinoPageRoute(
|
||||
builder: (_) => const Scaffold(
|
||||
backgroundColor: AppColors.darkBackground,
|
||||
body: SafeArea(child: VirtualLabsGalleryScreen()),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
bottom: TabBar(
|
||||
controller: _tabController,
|
||||
indicatorColor: widget.subject.primaryColor,
|
||||
@@ -297,11 +348,17 @@ class _SubjectHubScreenState extends State<SubjectHubScreen>
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 15),
|
||||
),
|
||||
subtitle: Text(
|
||||
'${unit.lessons.length} دروس تفاعلية • فحص فهم سقراطي',
|
||||
'${unit.lessons.length} دروس تفاعلية • فحص فهم مستمر',
|
||||
style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 12),
|
||||
),
|
||||
children: unit.lessons.map((lesson) {
|
||||
final hasVid = lesson.hasVideo;
|
||||
final lab = Grade10LabsRegistry.findForLesson(
|
||||
subjectTitle: widget.subject.title,
|
||||
lessonTitle: lesson.title,
|
||||
lessonId: lesson.id,
|
||||
curriculumLessonId: lesson.curriculumLessonId,
|
||||
);
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 14, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
@@ -313,69 +370,128 @@ class _SubjectHubScreenState extends State<SubjectHubScreen>
|
||||
: Colors.white.withAlpha(15),
|
||||
),
|
||||
),
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 4),
|
||||
leading: Container(
|
||||
width: 38,
|
||||
height: 38,
|
||||
decoration: BoxDecoration(
|
||||
gradient: hasVid
|
||||
? LinearGradient(
|
||||
colors: [widget.subject.primaryColor, widget.subject.secondaryColor],
|
||||
)
|
||||
: null,
|
||||
color: hasVid ? null : Colors.white.withAlpha(10),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: hasVid ? null : Border.all(color: Colors.white12),
|
||||
),
|
||||
child: Icon(
|
||||
hasVid ? CupertinoIcons.play_fill : CupertinoIcons.film,
|
||||
color: hasVid ? Colors.black : AppColors.textSecondaryDark,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
lesson.title,
|
||||
style: TextStyle(
|
||||
color: hasVid ? Colors.white : Colors.white70,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
Text(
|
||||
'${lesson.durationSeconds ~/ 60} دقيقة',
|
||||
style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 4),
|
||||
leading: Container(
|
||||
width: 38,
|
||||
height: 38,
|
||||
decoration: BoxDecoration(
|
||||
color: hasVid
|
||||
? AppColors.saqelCyan.withAlpha(25)
|
||||
: Colors.white.withAlpha(12),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
gradient: hasVid
|
||||
? LinearGradient(
|
||||
colors: [widget.subject.primaryColor, widget.subject.secondaryColor],
|
||||
)
|
||||
: null,
|
||||
color: hasVid ? null : Colors.white.withAlpha(10),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: hasVid ? null : Border.all(color: Colors.white12),
|
||||
),
|
||||
child: Text(
|
||||
hasVid ? '${lesson.checkpointsCount} فحوصات' : 'قريباً',
|
||||
style: TextStyle(
|
||||
color: hasVid ? AppColors.saqelCyan : AppColors.textSecondaryDark,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w700,
|
||||
child: Icon(
|
||||
hasVid ? CupertinoIcons.play_fill : CupertinoIcons.film,
|
||||
color: hasVid ? Colors.black : AppColors.textSecondaryDark,
|
||||
size: 18,
|
||||
),
|
||||
),
|
||||
title: Text(
|
||||
lesson.title,
|
||||
style: TextStyle(
|
||||
color: hasVid ? Colors.white : Colors.white70,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 13,
|
||||
),
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
Text(
|
||||
'${lesson.durationSeconds ~/ 60} دقيقة',
|
||||
style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 11),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: hasVid
|
||||
? AppColors.saqelCyan.withAlpha(25)
|
||||
: Colors.white.withAlpha(12),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Text(
|
||||
hasVid ? '${lesson.checkpointsCount} فحوصات' : 'قريباً',
|
||||
style: TextStyle(
|
||||
color: hasVid ? AppColors.saqelCyan : AppColors.textSecondaryDark,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: Icon(
|
||||
hasVid ? CupertinoIcons.chevron_back : CupertinoIcons.clock,
|
||||
color: hasVid ? AppColors.textSecondaryDark : Colors.white24,
|
||||
size: 16,
|
||||
),
|
||||
onTap: () {
|
||||
_showLessonVideoSelector(context, lesson);
|
||||
},
|
||||
),
|
||||
if (lab != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 0, 12, 8),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
onTap: () => Grade10LabsRegistry.openLab(context, lab),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.saqelCyan.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: AppColors.saqelCyan.withValues(alpha: 0.25)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
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,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.saqelCyan.withValues(alpha: 0.2),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
'بدء التجربة',
|
||||
style: TextStyle(
|
||||
color: AppColors.saqelCyan,
|
||||
fontSize: 10.5,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 3),
|
||||
Icon(CupertinoIcons.arrow_left, color: AppColors.saqelCyan, size: 10),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: Icon(
|
||||
hasVid ? CupertinoIcons.chevron_back : CupertinoIcons.clock,
|
||||
color: hasVid ? AppColors.textSecondaryDark : Colors.white24,
|
||||
size: 16,
|
||||
),
|
||||
onTap: () {
|
||||
_showLessonVideoSelector(context, lesson);
|
||||
},
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
@@ -804,6 +920,13 @@ class _SubjectHubScreenState extends State<SubjectHubScreen>
|
||||
|
||||
/// Empty state when no video exists yet for this lesson (Zero Mock Videos)
|
||||
void _showNoVideoAvailableSheet(BuildContext context, CurriculumLessonItemModel lesson) {
|
||||
final lab = Grade10LabsRegistry.findForLesson(
|
||||
subjectTitle: widget.subject.title,
|
||||
lessonTitle: lesson.title,
|
||||
lessonId: lesson.id,
|
||||
curriculumLessonId: lesson.curriculumLessonId,
|
||||
);
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: AppColors.darkSurface,
|
||||
@@ -848,16 +971,37 @@ class _SubjectHubScreenState extends State<SubjectHubScreen>
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text(
|
||||
'لا يوجد شرح مصور منشور لهذا الدرس بعد. يمكنك قراءة شروحات الدرس المعتمدة أو الانتقال للمختبر التفاعلي.',
|
||||
'لا يوجد شرح مصور منشور لهذا الدرس بعد. يمكنك خوض التجربة الافتراضية للدرس مباشرة أو قراءة الشروحات المعتمدة.',
|
||||
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 13, height: 1.5),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
if (lab != null) ...[
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.saqelCyan,
|
||||
foregroundColor: Colors.black,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(ctx).pop();
|
||||
Grade10LabsRegistry.openLab(context, lab);
|
||||
},
|
||||
icon: const Icon(CupertinoIcons.lab_flask_solid, size: 18, color: Colors.black),
|
||||
label: Text('فتح المختبر الافتراضي: ${lab.lessonAr} 🧪',
|
||||
style: const TextStyle(fontWeight: FontWeight.w900, fontSize: 13.5)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.appleBlue,
|
||||
child: OutlinedButton.icon(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
side: const BorderSide(color: AppColors.darkCardBorder),
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
@@ -883,19 +1027,18 @@ class _SubjectHubScreenState extends State<SubjectHubScreen>
|
||||
const SizedBox(height: 10),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: OutlinedButton.icon(
|
||||
style: OutlinedButton.styleFrom(
|
||||
child: TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppColors.saqelCyan,
|
||||
side: BorderSide(color: AppColors.saqelCyan.withOpacity(0.5)),
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.of(ctx).pop();
|
||||
_tabController.animateTo(4);
|
||||
_tabController.animateTo(1);
|
||||
},
|
||||
icon: const Icon(CupertinoIcons.lab_flask_solid, size: 18),
|
||||
label: const Text('الانتقال إلى المختبر التفاعلي للمادة 🧪', style: TextStyle(fontWeight: FontWeight.w700)),
|
||||
icon: const Icon(CupertinoIcons.arrow_left_circle, size: 16),
|
||||
label: const Text('استعراض كافة تجارب المادة (تبويب المختبر) 🧪',
|
||||
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 12.5)),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user