Update Saqel Platform: 2026-09-10 12:26:22

This commit is contained in:
Hamza-Ayed
2026-09-10 12:32:01 +03:00
parent 09dfd665b5
commit 960d4c8604
28 changed files with 690 additions and 2864 deletions
@@ -28,7 +28,9 @@ class CurriculumDocumentViewerScreen extends StatefulWidget {
final String documentType; // 'worksheet', 'summary', 'textbook', 'lesson'
final String subjectTitle;
final String? subjectId;
final String? filePath;
final String? assetId;
final String? assetType;
final String? mimeType;
final String? customContent;
final String? simulationSlug;
@@ -38,7 +40,9 @@ class CurriculumDocumentViewerScreen extends StatefulWidget {
required this.documentType,
required this.subjectTitle,
this.subjectId,
this.filePath,
this.assetId,
this.assetType,
this.mimeType,
this.customContent,
this.simulationSlug,
});
@@ -68,11 +72,10 @@ class _CurriculumDocumentViewerScreenState extends State<CurriculumDocumentViewe
widget.subjectTitle.contains('رياضيات') ||
(widget.subjectId ?? '').toLowerCase().contains('math') ||
widget.title.contains('معادلات') ||
widget.title.contains('جيوجبرا') ||
(widget.filePath ?? '').toLowerCase().contains('geogebra');
widget.title.contains('جيوجبرا');
String get _effectiveSubjectId => widget.subjectId ?? (_isEnglish ? 'english_10' : (_isPhysics ? 'physics_10' : 'math_10'));
String get _effectiveFilePath => widget.filePath ?? widget.title;
String get _effectiveFilePath => widget.assetId ?? '';
@override
void initState() {
@@ -110,16 +113,29 @@ class _CurriculumDocumentViewerScreenState extends State<CurriculumDocumentViewe
filePath: _effectiveFilePath,
);
// 2. Attempt fetching from Live Server
if (widget.assetId == null || widget.assetId!.isEmpty) {
if (mounted) {
setState(() {
_loadError = 'المورد المنشور لا يحمل معرّف أصل صالحاً.';
_isLoading = false;
});
}
return;
}
if (widget.mimeType != null && !widget.mimeType!.toLowerCase().startsWith('text/')) {
if (mounted) {
setState(() {
_loadError = 'هذا المورد منشور، لكن عرضه داخل التطبيق غير مدعوم بعد.';
_isLoading = false;
});
}
return;
}
// 2. Fetch only the approved asset UUID from the published bundle.
try {
final res = await _api.get(
'/api/curriculum/document',
queryParams: {
'subject': _effectiveSubjectId,
'file': _effectiveFilePath,
'type': widget.documentType,
},
).timeout(const Duration(seconds: 3));
final res = await _api.get('/api/curriculum/assets/${widget.assetId}').timeout(const Duration(seconds: 3));
if (res is Map && res['content'] != null && res['content'].toString().trim().isNotEmpty) {
_processContent(res['content'].toString());
@@ -347,35 +347,14 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
/// Tab 2: Worksheets & Summaries
Widget _buildWorksheetsTab(BuildContext context) {
final s = widget.subject.id.toLowerCase();
final isMath = s.contains('math') || widget.subject.title.contains('رياضيات');
final isPhys = s.contains('physic') || widget.subject.title.contains('فيزياء');
final isEng = s.contains('english') || widget.subject.title.contains('إنجليز');
final worksheets = widget.subject.worksheets;
final defaultWorksheets = isMath
? [
const ResourceItemModel(title: 'ورقة عمل 1: الأسس والأنظمة والمعادلات الخاصة', filePath: 'grade_10/math_10/semester_1/resources/worksheet_1.md', type: 'worksheet'),
const ResourceItemModel(title: 'ملخص شامل: قوانين المعادلات والتحليل إلى العوامل', filePath: 'math_summary.md', type: 'summary'),
const ResourceItemModel(title: 'مراجعة تدريبية: حل أنظمة المعادلات بيانياً وجبرياً', filePath: 'math_exam_prep.md', type: 'worksheet'),
]
: (isPhys
? [
const ResourceItemModel(title: 'ورقة عمل وتطبيقات: تحليل المتجهات وقوانين نيوتن', filePath: 'physics_ws1.md', type: 'worksheet'),
const ResourceItemModel(title: 'ملخص شامل: الكميات القياسية والمتجهة والضرب النقطي', filePath: 'physics_summary.md', type: 'summary'),
const ResourceItemModel(title: 'دليل التجارب المخبرية: طاولة القوى والتسارع', filePath: 'physics_lab_guide.md', type: 'worksheet'),
]
: (isEng
? [
const ResourceItemModel(title: 'Action Pack 10 — Practice Worksheet: Unit 01 (Looking Good)', filePath: 'english_ws1.md', type: 'worksheet'),
const ResourceItemModel(title: 'Grammar & Vocabulary Revision: Articles & First Impressions', filePath: 'english_summary.md', type: 'summary'),
const ResourceItemModel(title: 'Unit 02 Reading & Grammar Worksheet (The Digital Mind)', filePath: 'english_ws2.md', type: 'worksheet'),
]
: [
const ResourceItemModel(title: 'ورقة عمل 1: المفاهيم الأساسية والتطبيقات', filePath: 'ws1.md', type: 'worksheet'),
const ResourceItemModel(title: 'ملخص شامل: القوانين والمعادلات الوزارية المقررة', filePath: 'summary.md', type: 'summary'),
]));
final worksheets = widget.subject.worksheets.isNotEmpty ? widget.subject.worksheets : defaultWorksheets;
if (worksheets.isEmpty) {
return _buildUnavailableResourcesState(
icon: CupertinoIcons.doc_text,
message: 'لا توجد أوراق عمل منشورة لهذه المادة بعد.',
);
}
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 20),
@@ -416,7 +395,15 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
IconButton(
icon: const Icon(CupertinoIcons.arrow_down_circle_fill, color: AppColors.saqelCyan, size: 28),
onPressed: () {
_showResourceSheet(context, ws.title, 'ورقة عمل ومذكرة مراجعة', 'PDF جاهز للطباعة بدقة عالية', filePath: ws.filePath);
_showResourceSheet(
context,
ws.title,
'ورقة عمل منشورة',
'مورد تمت مراجعته ونشره لهذه المادة.',
assetId: ws.assetId,
assetType: ws.assetType,
mimeType: ws.mimeType,
);
},
),
],
@@ -553,32 +540,14 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
/// Tab 4: Official Ministry Textbooks
Widget _buildTextbooksTab(BuildContext context) {
final s = widget.subject.id.toLowerCase();
final isMath = s.contains('math') || widget.subject.title.contains('رياضيات');
final isPhys = s.contains('physic') || widget.subject.title.contains('فيزياء');
final isEng = s.contains('english') || widget.subject.title.contains('إنجليز');
final textbooks = widget.subject.textbooks;
final defaultTextbooks = isMath
? [
const ResourceItemModel(title: 'كتاب الطالب المقرر — الرياضيات (أنظمة المعادلات والدائرة)', filePath: 'grade_10/math_10/semester_1/math_student_book.md', type: 'textbook'),
const ResourceItemModel(title: 'كتاب التمارين والأنشطة الإضافية — الرياضيات 10', filePath: 'grade_10/math_10/semester_1/math_workbook.md', type: 'textbook'),
]
: (isPhys
? [
const ResourceItemModel(title: 'كتاب الفيزياء المقرر — الطالب (المتجهات والحركة)', filePath: 'grade_10/physics_10/semester_1/physics_student_book.md', type: 'textbook'),
const ResourceItemModel(title: 'كتاب التجارب والأنشطة العلمية والعملية (طاولة القوى)', filePath: 'grade_10/physics_10/semester_1/physics_activities_book.md', type: 'textbook'),
]
: (isEng
? [
const ResourceItemModel(title: 'Action Pack 10 — Student\'s Book (Looking Good & The Digital Mind)', filePath: 'grade_10/english_10/semester_1/unit_01.md', type: 'textbook'),
const ResourceItemModel(title: 'Action Pack 10 — Activity Book & Literature Spot', filePath: 'grade_10/english_10/semester_1/activity_book.md', type: 'textbook'),
]
: [
const ResourceItemModel(title: 'كتاب الطالب المقرّر — منهاج وزارة التربية والتعليم', filePath: 'book.md', type: 'textbook'),
const ResourceItemModel(title: 'كتاب التجارب والأنشطة العلمية والعملية', filePath: 'workbook.md', type: 'textbook'),
]));
final textbooks = widget.subject.textbooks.isNotEmpty ? widget.subject.textbooks : defaultTextbooks;
if (textbooks.isEmpty) {
return _buildUnavailableResourcesState(
icon: CupertinoIcons.book,
message: 'لا يوجد كتاب أو ملف مصدر منشور لهذه المادة بعد.',
);
}
return ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 20),
@@ -619,7 +588,15 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
IconButton(
icon: const Icon(CupertinoIcons.eye_fill, color: AppColors.saqelCyan, size: 24),
onPressed: () {
_showResourceSheet(context, tb.title, 'الكتاب المدرسي المعتمد', 'نسخة وزارة التربية والتعليم المنقحة والمحدثة', filePath: tb.filePath);
_showResourceSheet(
context,
tb.title,
'كتاب أو ملف مصدر منشور',
'مورد تمت مراجعته ونشره لهذه المادة.',
assetId: tb.assetId,
assetType: tb.assetType,
mimeType: tb.mimeType,
);
},
),
],
@@ -630,7 +607,35 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
);
}
void _showResourceSheet(BuildContext context, String title, String subtitle, String description, {String? filePath}) {
Widget _buildUnavailableResourcesState({required IconData icon, required String message}) {
return Center(
child: Padding(
padding: const EdgeInsets.all(32),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: AppColors.textSecondaryDark, size: 34),
const SizedBox(height: 12),
Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(color: AppColors.textSecondaryDark, fontSize: 14, height: 1.5),
),
],
),
),
);
}
void _showResourceSheet(
BuildContext context,
String title,
String subtitle,
String description, {
required String assetId,
required String assetType,
required String mimeType,
}) {
showModalBottomSheet(
context: context,
backgroundColor: AppColors.darkSurface,
@@ -716,7 +721,9 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
documentType: title.contains('كتاب') ? 'textbook' : 'worksheet',
subjectTitle: widget.subject.title,
subjectId: widget.subject.id,
filePath: filePath,
assetId: assetId,
assetType: assetType,
mimeType: mimeType,
),
),
);
@@ -780,40 +787,9 @@ class _SubjectHubScreenState extends State<SubjectHubScreen> with SingleTickerPr
),
const SizedBox(height: 16),
const Text(
'شروحات هذا الدرس قيد التصوير والمراجعة من قبل نخبة المعلمين المعتمدين وفريق منصة صَقِل.\nيمكنك حالياً دراسة نتاجات وملخص الدرس عبر تبويب الكتب والمذكرات، أو خوض الاختبار التكيفي.',
'لا يوجد شرح منشور لهذا الدرس بعد. ستظهر فقط الموارد التي تُراجع وتُنشر لهذا الدرس أو للمادة.',
style: TextStyle(color: AppColors.textSecondaryDark, fontSize: 13, height: 1.5),
),
const SizedBox(height: 24),
Row(
children: [
Expanded(
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.appleBlue,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
),
onPressed: () {
Navigator.of(ctx).pop();
Navigator.of(context).push(
CupertinoPageRoute(
builder: (_) => CurriculumDocumentViewerScreen(
title: lesson.title,
documentType: 'lesson',
subjectTitle: widget.subject.title,
subjectId: widget.subject.id,
filePath: lesson.markdownFilePath,
),
),
);
},
icon: const Icon(CupertinoIcons.book, size: 18),
label: const Text('قراءة محتوى وملخص الدرس 📖', style: TextStyle(fontWeight: FontWeight.w800)),
),
),
],
),
],
),
),