import 'dart:async'; import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:file_picker/file_picker.dart'; import '../../data/models/directorate_models.dart'; import '../../services/directorate_api_service.dart'; class SchoolPrincipalScreen extends StatefulWidget { const SchoolPrincipalScreen({super.key}); @override State createState() => _SchoolPrincipalScreenState(); } class _SchoolPrincipalScreenState extends State with SingleTickerProviderStateMixin { late TabController _tabController; bool _isLoading = true; Map? _dashboardData; // Recording State bool _isRecording = false; int _recordDurationSeconds = 0; Timer? _recordTimer; double _recordedSizeMb = 0.0; int? _selectedTeacherId; String? _selectedTeacher; String _selectedSubject = ''; final TextEditingController _lessonTitleController = TextEditingController(); RecordedLessonResult? _lastResult; // Exam state bool _examPushedToLab = false; bool _isPanoramicActive = false; double _panoramicSizeMb = 16.5; // Enterprise Integrity, Parent Reports & Roster State bool _isRunningIntegrityAudit = false; Map? _integrityResult; bool _isDispatchingParentReports = false; bool _isImportingRoster = false; @override void initState() { super.initState(); _tabController = TabController(length: 3, vsync: this); _loadData(); } Future _loadData() async { setState(() => _isLoading = true); final data = await DirectorateApiService.fetchSchoolDashboard(); if (mounted) { setState(() { _dashboardData = data; final teachers = data['teachers'] as List? ?? const []; if (teachers.isNotEmpty) { final first = Map.from(teachers.first as Map); _selectedTeacherId = (first['id'] as num?)?.toInt(); _selectedTeacher = first['name']?.toString(); _selectedSubject = first['subject']?.toString() ?? ''; } _isLoading = false; }); } } Future _selectAndUploadRecording() async { if (_selectedTeacherId == null || _lessonTitleController.text.trim().isEmpty) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('اختر معلماً وأدخل عنوان الحصة أولاً.'))); return; } final selection = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: const ['mp4', 'mov', 'webm'], withData: true, ); if (selection == null || selection.files.isEmpty) return; final file = selection.files.single; setState(() { _isRecording = true; _recordedSizeMb = file.size / 1048576; _lastResult = null; }); // Show quick processing dialog showCupertinoDialog( context: context, barrierDismissible: false, builder: (ctx) => const Center( child: CupertinoActivityIndicator(radius: 20), ), ); try { final result = await DirectorateApiService.recordLesson( schoolId: (_dashboardData?['school']?['id'] as num?)?.toInt() ?? 0, teacherId: _selectedTeacherId!, subject: _selectedSubject, gradeLevel: 'grade_10', lessonTitle: _lessonTitleController.text.trim(), durationMinutes: 0, filePath: file.path, fileBytes: file.bytes, fileName: file.name, ); if (mounted) setState(() => _lastResult = result); } catch (e) { if (mounted) ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(e.toString()))); } finally { if (mounted) { Navigator.of(context).pop(); setState(() => _isRecording = false); } } } void _showDualFormsPreview() async { await DirectorateApiService.fetchDualForms(); if (!mounted) return; showModalBottomSheet( context: context, isScrollControlled: true, backgroundColor: Colors.transparent, builder: (ctx) => Directionality( textDirection: TextDirection.rtl, child: Container( height: MediaQuery.of(context).size.height * 0.8, decoration: const BoxDecoration( color: Color(0xFF0F172A), borderRadius: BorderRadius.vertical(top: Radius.circular(24)), border: Border(top: BorderSide(color: Color(0xFF8B5CF6), width: 2)), ), padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Center( child: Container( width: 40, height: 4, decoration: BoxDecoration( color: const Color(0xFF334155), borderRadius: BorderRadius.circular(2), ), ), ), const SizedBox(height: 14), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text( 'معاينة النماذج الموحدة (نموذج أ ونموذج ب)', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w800, color: Colors.white, ), ), IconButton( icon: const Icon(CupertinoIcons.xmark, color: Colors.white70), onPressed: () => Navigator.of(context).pop(), ), ], ), const SizedBox(height: 10), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: const Color(0xFF1E1B4B), borderRadius: BorderRadius.circular(10), border: Border.all(color: const Color(0xFF8B5CF6).withOpacity(0.3)), ), child: const Text( 'المخطط الهجين: 70% أسئلة موضوعية مبرمجة على محطات الحاسوب + 30% خطوات إنشائية ورقية مطبوعة مع باركود تسلسلي مشفر.', style: TextStyle(fontSize: 12, color: Color(0xFFC4B5FD), height: 1.4), ), ), const SizedBox(height: 14), Expanded( child: Row( children: [ // Form A Card Expanded( child: Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: const Color(0xFF161F30), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFF10B981).withOpacity(0.5)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(CupertinoIcons.doc_checkmark_fill, color: Color(0xFF10B981), size: 16), SizedBox(width: 6), Text('النموذج (أ) — الفا', style: TextStyle(fontWeight: FontWeight.w800, color: Color(0xFF34D399), fontSize: 13)), ], ), const SizedBox(height: 8), const Text('الباركود التسلسلي:\nSAQEL-EXAM-A-ALPHA', style: TextStyle(fontSize: 11, color: Color(0xFF94A3B8))), const Divider(color: Color(0xFF1E293B)), const Text('• 20 مسألة موضوعية (70 علامة)\n• مسألتان إنشائيتان (30 علامة)\n• باركود موجه لكاميرا المشرف', style: TextStyle(fontSize: 11.5, color: Colors.white70, height: 1.6)), ], ), ), ), const SizedBox(width: 10), // Form B Card Expanded( child: Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: const Color(0xFF161F30), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFF38BDF8).withOpacity(0.5)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Row( children: [ Icon(CupertinoIcons.doc_plaintext, color: Color(0xFF38BDF8), size: 16), SizedBox(width: 6), Text('النموذج (ب) — بيتا', style: TextStyle(fontWeight: FontWeight.w800, color: Color(0xFF38BDF8), fontSize: 13)), ], ), const SizedBox(height: 8), const Text('الباركود التسلسلي:\nSAQEL-EXAM-B-BETA', style: TextStyle(fontSize: 11, color: Color(0xFF94A3B8))), const Divider(color: Color(0xFF1E293B)), const Text('• خلط كامل لترتيب الأسئلة\n• تغيير معطيات الأرقام للمقاعد المتجاورة لمنع النقل', style: TextStyle(fontSize: 11.5, color: Colors.white70, height: 1.6)), ], ), ), ), ], ), ), const SizedBox(height: 14), ElevatedButton.icon( onPressed: () { Navigator.of(context).pop(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('🖨️ تم إرسال 48 نسخة من النموذج أ وب ومسودات الباركود لطابعة المدرسة المركزية'), backgroundColor: Color(0xFF10B981), ), ); }, icon: const Icon(CupertinoIcons.printer_fill, size: 16), label: const Text('أمر طباعة النماذج والباركود الآن'), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF8B5CF6), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), ), ], ), ), ), ); } void _runIntegrityAudit() async { setState(() => _isRunningIntegrityAudit = true); final res = await DirectorateApiService.evaluateExamSessionIntegrity(); if (mounted) { setState(() { _integrityResult = res; _isRunningIntegrityAudit = false; }); } } void _dispatchParentReports() async { setState(() => _isDispatchingParentReports = true); final res = await DirectorateApiService.dispatchParentReports(); if (mounted) { setState(() => _isDispatchingParentReports = false); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(res['message'] ?? 'تم إرسال التقارير لأولياء الأمور بنجاح'), backgroundColor: const Color(0xFF10B981), ), ); } } void _importSchoolRoster() async { final picked = await FilePicker.platform.pickFiles( type: FileType.custom, allowedExtensions: const ['csv', 'json'], withData: true, ); if (picked == null || picked.files.single.bytes == null) return; setState(() => _isImportingRoster = true); try { final file = picked.files.single; final text = utf8.decode(file.bytes!); final List> records; if (file.extension?.toLowerCase() == 'json') { final decoded = json.decode(text); if (decoded is! List) throw const FormatException('ملف JSON يجب أن يحتوي قائمة طلبة.'); records = decoded.map((row) => Map.from(row as Map)).toList(); } else { final lines = const LineSplitter() .convert(text) .where((line) => line.trim().isNotEmpty) .toList(); if (lines.length < 2) throw const FormatException('ملف CSV فارغ.'); final headers = lines.first.split(',').map((h) => h.trim()).toList(); records = lines.skip(1).map((line) { final values = line.split(','); return { for (var i = 0; i < headers.length; i++) headers[i]: i < values.length ? values[i].trim() : '', }; }).toList(); } final res = await DirectorateApiService.importSchoolRoster(records: records); if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('تم استيراد ${res['imported_count']} طالباً والتحقق من بياناتهم بنجاح.'), backgroundColor: const Color(0xFF0284C7), ), ); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('تعذر استيراد الكشف: $e'), backgroundColor: const Color(0xFFDC2626)), ); } } finally { if (mounted) setState(() => _isImportingRoster = false); } } @override void dispose() { _tabController.dispose(); _recordTimer?.cancel(); _lessonTitleController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { if (_isLoading) { return const Scaffold( backgroundColor: Color(0xFF070B12), body: Center( child: CupertinoActivityIndicator(color: Color(0xFF8B5CF6), radius: 18), ), ); } final school = _dashboardData?['school'] ?? {}; final teachers = (_dashboardData?['teachers'] as List? ?? []) .map((t) => TeacherItem.fromJson(t)) .toList(); return Scaffold( backgroundColor: const Color(0xFF070B12), appBar: AppBar( backgroundColor: const Color(0xFF0E1626), elevation: 0, centerTitle: false, title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( school['name'] ?? 'إدارة المدرسة العسكرية', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w700, color: Colors.white, ), ), const SizedBox(height: 2), Row( children: [ const Icon(CupertinoIcons.location_solid, size: 12, color: Color(0xFF94A3B8)), const SizedBox(width: 4), Text( school['governorate'] ?? 'العاصمة - عمان', style: const TextStyle(fontSize: 12, color: Color(0xFF94A3B8)), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1), decoration: BoxDecoration( color: const Color(0xFF1E293B), borderRadius: BorderRadius.circular(4), ), child: const Text( 'الفئة (ج) كبرى · 980 طالباً', style: TextStyle(fontSize: 11, color: Color(0xFFA78BFA)), ), ) ], ) ], ), bottom: TabBar( controller: _tabController, indicatorColor: const Color(0xFF8B5CF6), indicatorWeight: 3, labelColor: Colors.white, unselectedLabelColor: const Color(0xFF64748B), labelStyle: const TextStyle(fontWeight: FontWeight.w700, fontSize: 13.5), tabs: const [ Tab( icon: Icon(CupertinoIcons.video_camera_solid, size: 18), text: 'تسجيل الحصص الصفية', ), Tab( icon: Icon(CupertinoIcons.person_2_fill, size: 18), text: 'المعلمون والدورية', ), Tab( icon: Icon(CupertinoIcons.doc_text_fill, size: 18), text: 'الامتحانات ومختبر الحاسوب', ), ], ), ), body: TabBarView( controller: _tabController, children: [ _buildLessonRecordingTab(teachers), _buildTeachersQuotaTab(teachers), _buildUnifiedExamsTab(), ], ), ); } // --------------------------------------------------------------------------- // TAB 1: Classroom Video Recording & Pedagogical Quality Audit // --------------------------------------------------------------------------- Widget _buildLessonRecordingTab(List teachers) { return SingleChildScrollView( padding: const EdgeInsets.all(18), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Quota Banner Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( gradient: LinearGradient( colors: [const Color(0xFF1E1B4B), const Color(0xFF0F172A)], begin: Alignment.topRight, end: Alignment.bottomLeft, ), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFF4338CA).withOpacity(0.5)), ), child: Row( children: [ Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: const Color(0xFF4F46E5).withOpacity(0.2), shape: BoxShape.circle, ), child: const Icon(CupertinoIcons.calendar_badge_plus, color: Color(0xFFA5B4FC), size: 24), ), const SizedBox(width: 14), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'دورية التدقيق الميداني المعتمدة', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w700, color: Colors.white), ), SizedBox(height: 2), Text( 'حصة مرئية واحدة لكل معلم كل أسبوعين (الحد الأقصى للحجم: 400 ميجابايت بدقة 720p)', style: TextStyle(fontSize: 12, color: Color(0xFFCBD5E1)), ), ], ), ), ], ), ), const SizedBox(height: 18), // Camera Viewport Simulation Card Container( height: 240, decoration: BoxDecoration( color: const Color(0xFF0F172A), borderRadius: BorderRadius.circular(16), border: Border.all( color: _isRecording ? const Color(0xFFEF4444) : const Color(0xFF1E293B), width: _isRecording ? 2 : 1, ), boxShadow: _isRecording ? [ BoxShadow( color: const Color(0xFFEF4444).withOpacity(0.2), blurRadius: 20, spreadRadius: 2, ) ] : [], ), child: Stack( children: [ // Mock Classroom Background Grid Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( CupertinoIcons.videocam_fill, size: 54, color: _isRecording ? const Color(0xFFEF4444) : const Color(0xFF475569), ), const SizedBox(height: 10), Text( _isRecording ? 'جاري تصوير وقائع الحصة الصفية داخل الغرفة...' : 'كاميرا المشرف جاهزة للتسجيل الصفي', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: _isRecording ? Colors.white : const Color(0xFF94A3B8), ), ), const SizedBox(height: 4), Text( 'ترميز ذكي 720p · محدد تلقائياً لسقف 400 ميجابايت', style: TextStyle( fontSize: 12, color: const Color(0xFF64748B)), ), ], ), ), // Top Status Bar in Camera Positioned( top: 12, left: 12, right: 12, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ if (_isRecording) Row( children: [ Container( width: 10, height: 10, decoration: const BoxDecoration( color: Color(0xFFEF4444), shape: BoxShape.circle, ), ), const SizedBox(width: 6), const Text( 'تسجيل نشط (REC)', style: TextStyle( color: Color(0xFFEF4444), fontWeight: FontWeight.w800, fontSize: 12), ), ], ) else const Text( 'جاهز', style: TextStyle(color: Color(0xFF10B981), fontSize: 12), ), // Metrics Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 4), decoration: BoxDecoration( color: Colors.black.withOpacity(0.6), borderRadius: BorderRadius.circular(20), ), child: Text( _isRecording ? '${_formatDuration(_recordDurationSeconds)} | ${_recordedSizeMb.toStringAsFixed(1)} MB / 400 MB' : '00:00 | 0.0 MB', style: const TextStyle( color: Colors.white, fontSize: 12, fontWeight: FontWeight.w700), ), ) ], ), ), // Bottom Action inside Camera Positioned( bottom: 14, left: 14, right: 14, child: ElevatedButton( onPressed: _isRecording ? null : _selectAndUploadRecording, style: ElevatedButton.styleFrom( backgroundColor: _isRecording ? const Color(0xFFDC2626) : const Color(0xFF8B5CF6), padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12)), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( _isRecording ? CupertinoIcons.stop_fill : CupertinoIcons.circle_fill, size: 18), const SizedBox(width: 8), Text( _isRecording ? 'جارٍ رفع الحصة إلى R2…' : 'اختيار فيديو حصة حقيقي ورفعه 🎥', style: const TextStyle( fontSize: 15, fontWeight: FontWeight.w700), ), ], ), ), ), ], ), ), const SizedBox(height: 18), // Lesson Details Form Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0F172A), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFF1E293B)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'بيانات الحصة والمعلم المستهدف:', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w700, color: Colors.white), ), const SizedBox(height: 12), Row( children: [ Expanded( child: DropdownButtonFormField( initialValue: _selectedTeacher, dropdownColor: const Color(0xFF1E293B), style: const TextStyle(color: Colors.white, fontSize: 13), decoration: InputDecoration( labelText: 'المعلم', labelStyle: const TextStyle(color: Color(0xFF94A3B8)), filled: true, fillColor: const Color(0xFF161F30), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10)), ), items: teachers .map((t) => DropdownMenuItem( value: t.name, child: Text(t.name))) .toList(), onChanged: (val) { if (val != null) { final teacher = teachers.firstWhere((t) => t.name == val); setState(() { _selectedTeacher = val; _selectedTeacherId = teacher.id; _selectedSubject = teacher.subject; }); } }, ), ), const SizedBox(width: 12), Expanded( child: DropdownButtonFormField( initialValue: _selectedSubject.isEmpty ? null : _selectedSubject, dropdownColor: const Color(0xFF1E293B), style: const TextStyle(color: Colors.white, fontSize: 13), decoration: InputDecoration( labelText: 'المادة', labelStyle: const TextStyle(color: Color(0xFF94A3B8)), filled: true, fillColor: const Color(0xFF161F30), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10)), ), items: teachers.map((t) => t.subject).where((s) => s.isNotEmpty).toSet() .map((s) => DropdownMenuItem(value: s, child: Text(s))).toList(), onChanged: (val) { if (val != null) setState(() => _selectedSubject = val); }, ), ), ], ), const SizedBox(height: 12), TextField( controller: _lessonTitleController, style: const TextStyle(color: Colors.white, fontSize: 13.5), decoration: InputDecoration( labelText: 'عنوان الدرس وفق المنهاج الوزاري', labelStyle: const TextStyle(color: Color(0xFF94A3B8)), filled: true, fillColor: const Color(0xFF161F30), border: OutlineInputBorder( borderRadius: BorderRadius.circular(10)), ), ), ], ), ), const SizedBox(height: 18), // AI Evaluation Card (if available) if (_lastResult != null) _buildAiResultCard(_lastResult!), ], ), ); } Widget _buildAiResultCard(RecordedLessonResult res) { return Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( color: const Color(0xFF111827), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF10B981).withOpacity(0.6)), boxShadow: [ BoxShadow( color: const Color(0xFF10B981).withOpacity(0.1), blurRadius: 20, ) ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Row( children: [ Icon(CupertinoIcons.checkmark_seal_fill, color: Color(0xFF10B981), size: 22), SizedBox(width: 8), Text( 'بطاقة التدقيق التربوي المعتمدة بالذكاء الاصطناعي', style: TextStyle( fontSize: 14.5, fontWeight: FontWeight.w700, color: Colors.white), ), ], ), Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), decoration: BoxDecoration( color: const Color(0xFF10B981).withOpacity(0.2), borderRadius: BorderRadius.circular(8), ), child: Text( '${res.aiAlignmentScore}% مطابقة', style: const TextStyle( color: Color(0xFF10B981), fontWeight: FontWeight.w800, fontSize: 13), ), ), ], ), const SizedBox(height: 12), Text( res.reportSummary, style: const TextStyle( fontSize: 13, color: Color(0xFFCBD5E1), height: 1.5), ), const SizedBox(height: 14), Row( children: [ _metricChip('نسبة كلام المعلم', res.teacherTalkRatio), const SizedBox(width: 8), _metricChip('مدة الحصة', '${res.durationMinutes} دقيقة'), const SizedBox(width: 8), _metricChip('حجم الفيديو', '${res.fileSizeMb.toStringAsFixed(1)} MB'), ], ), const Divider(color: Color(0xFF1F2937), height: 24), const Text( 'الوقفات السقراطية المقترحة أثناء الحصة:', style: TextStyle( fontSize: 13, fontWeight: FontWeight.w700, color: Color(0xFFA78BFA)), ), const SizedBox(height: 8), ...res.socraticCheckpoints.map((cp) => Container( margin: const EdgeInsets.only(bottom: 6), padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: const Color(0xFF1E293B), borderRadius: BorderRadius.circular(8), ), child: Row( children: [ Text( cp['timestamp'] ?? '', style: const TextStyle( color: Color(0xFFF59E0B), fontWeight: FontWeight.w700, fontSize: 12), ), const SizedBox(width: 10), Expanded( child: Text( cp['question'] ?? '', style: const TextStyle(color: Colors.white, fontSize: 12.5), ), ), ], ), )), ], ), ); } // --------------------------------------------------------------------------- // TAB 2: Teachers & Bi-Weekly Quota Tracking // --------------------------------------------------------------------------- Widget _buildTeachersQuotaTab(List teachers) { return ListView.builder( padding: const EdgeInsets.all(16), itemCount: teachers.length, itemBuilder: (ctx, idx) { final t = teachers[idx]; return Container( margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0F172A), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFF1E293B)), ), child: Row( children: [ CircleAvatar( radius: 22, backgroundColor: t.isCompleted ? const Color(0xFF10B981).withOpacity(0.2) : const Color(0xFFF59E0B).withOpacity(0.2), child: Text( t.name.split(' ').first, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w700, color: t.isCompleted ? const Color(0xFF10B981) : const Color(0xFFF59E0B), ), ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text( t.name, style: const TextStyle( fontSize: 14.5, fontWeight: FontWeight.w700, 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( t.subject, style: const TextStyle( fontSize: 11, color: Color(0xFF94A3B8)), ), ), ], ), const SizedBox(height: 4), Text( 'آخر حصة: ${t.lastLesson}', style: const TextStyle(fontSize: 12, color: Color(0xFF64748B)), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: t.isCompleted ? const Color(0xFF065F46) : const Color(0xFF78350F), borderRadius: BorderRadius.circular(6), ), child: Text( t.isCompleted ? 'مكتمل الدورية' : 'متبقي هذا الأسبوع', style: TextStyle( fontSize: 11, fontWeight: FontWeight.w700, color: t.isCompleted ? const Color(0xFF34D399) : const Color(0xFFFBBF24), ), ), ), const SizedBox(height: 4), Text( 'التقييم: ${t.aiScore}%', style: const TextStyle( fontSize: 11.5, color: Color(0xFFA78BFA), fontWeight: FontWeight.w600), ), ], ), ], ), ); }, ); } // --------------------------------------------------------------------------- // TAB 3: Unified Exams, Lab Dispatch & Smart Panoramic Surveillance // --------------------------------------------------------------------------- Widget _buildUnifiedExamsTab() { return SingleChildScrollView( padding: const EdgeInsets.all(18), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Active Exam Card Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( gradient: LinearGradient( colors: [const Color(0xFF1E293B), const Color(0xFF0F172A)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF8B5CF6).withOpacity(0.5)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Row( children: [ Icon(CupertinoIcons.doc_checkmark_fill, color: Color(0xFFA78BFA), size: 20), SizedBox(width: 8), Text( 'امتحان وزاري موحد من مديرية الثقافة العسكرية', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w700, color: Colors.white), ), ], ), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 3), decoration: BoxDecoration( color: const Color(0xFF8B5CF6).withOpacity(0.2), borderRadius: BorderRadius.circular(6), ), child: const Text( 'موعد موحد: 09:00 ص', style: TextStyle( fontSize: 11.5, color: Color(0xFFA78BFA), fontWeight: FontWeight.w700), ), ), ], ), const SizedBox(height: 12), const Text( 'الرياضيات العلمي - محاكاة امتحان التوجيهي الوزاري 2008', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w800, color: Colors.white), ), const SizedBox(height: 6), const Text( 'المدة: 60 دقيقة · جدول المواصفات الوزاري المعتمد · نموذجان (أ و ب)', style: TextStyle(fontSize: 12.5, color: Color(0xFF94A3B8)), ), const SizedBox(height: 16), // Lab Push & Print Buttons Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: () async { final exams = _dashboardData?['active_exams'] as List? ?? const []; final examId = exams.isEmpty ? 0 : (Map.from(exams.first as Map)['id'] as num?)?.toInt() ?? 0; final schoolId = (_dashboardData?['school']?['id'] as num?)?.toInt() ?? 0; if (examId == 0 || schoolId == 0) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('لا يوجد امتحان حقيقي مجدول لهذه المدرسة.')), ); return; } final ok = await DirectorateApiService.pushExamToLab( examId: examId, schoolId: schoolId, ); if (ok && mounted) { setState(() => _examPushedToLab = true); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( '✅ تم بث الامتحان لمحطات مختبر الحاسوب وتفعيل البيئة المقفلة (Kiosk Mode)'), backgroundColor: Color(0xFF10B981), ), ); } }, icon: Icon( _examPushedToLab ? CupertinoIcons.checkmark_alt_circle_fill : CupertinoIcons.desktopcomputer, size: 16), label: Text( _examPushedToLab ? 'تم البث لمختبر الحاسوب' : 'إرسال لمختبر الحاسوب (32 جهازاً)', style: const TextStyle( fontSize: 12.5, fontWeight: FontWeight.w700), ), style: ElevatedButton.styleFrom( backgroundColor: _examPushedToLab ? const Color(0xFF059669) : const Color(0xFF4F46E5), padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)), ), ), ), const SizedBox(width: 8), ElevatedButton.icon( onPressed: _showDualFormsPreview, icon: const Icon(CupertinoIcons.doc_on_doc_fill, size: 16), label: const Text('معاينة وطباعة النماذج (أ وب)', style: TextStyle(fontSize: 12.5)), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF8B5CF6), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric( vertical: 12, horizontal: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)), ), ), ], ), ], ), ), const SizedBox(height: 18), // Smart Panoramic Surveillance Viewport Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0F172A), borderRadius: BorderRadius.circular(16), border: Border.all( color: _isPanoramicActive ? const Color(0xFFEF4444) : const Color(0xFF1E293B), width: _isPanoramicActive ? 2 : 1, ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ Container( width: 10, height: 10, decoration: BoxDecoration( color: _isPanoramicActive ? const Color(0xFFEF4444) : const Color(0xFF64748B), shape: BoxShape.circle, ), ), const SizedBox(width: 8), const Text( 'الرقابة البانورامية بتقنية التقطيع الزمني الذكي', style: TextStyle( fontSize: 13.5, fontWeight: FontWeight.w700, color: Colors.white), ), ], ), Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 3), decoration: BoxDecoration( color: const Color(0xFF1E293B), borderRadius: BorderRadius.circular(6), ), child: Text( 'الحجم: ${_panoramicSizeMb.toStringAsFixed(1)} MB فقط', style: const TextStyle( fontSize: 11.5, color: Color(0xFF38BDF8), fontWeight: FontWeight.w700), ), ), ], ), const SizedBox(height: 10), const Text( 'المبدأ: تظهر الكاميرا نشطة طوال الامتحان لفرض الردع النفسي، لكن التطبيق يقتطع ثانيتين فقط كل دقيقة لينتج مقطعاً مجمعاً مدته دقيقتان فقط بحجم 16.5 ميجابايت.', style: TextStyle( fontSize: 12, color: Color(0xFF94A3B8), height: 1.5), ), const SizedBox(height: 14), // Toggle Panoramic Button ElevatedButton.icon( onPressed: () async { final exams = _dashboardData?['active_exams'] as List? ?? const []; final sessionId = exams.isEmpty ? 0 : (Map.from(exams.first as Map)['session_id'] as num?)?.toInt() ?? 0; if (sessionId == 0) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('لا توجد جلسة امتحان حقيقية لربط العينة بها.')), ); return; } final selected = await FilePicker.platform.pickFiles(type: FileType.video, withData: true); if (selected == null || selected.files.isEmpty) return; final file = selected.files.single; setState(() { _isPanoramicActive = true; _panoramicSizeMb = file.size / 1048576; }); try { await DirectorateApiService.uploadPanoramicSample( sessionId: sessionId, filePath: file.path, fileBytes: file.bytes, fileName: file.name, ); } finally { if (mounted) setState(() => _isPanoramicActive = false); } }, icon: Icon( _isPanoramicActive ? CupertinoIcons.eye_slash_fill : CupertinoIcons.eye_fill, size: 16), label: Text( _isPanoramicActive ? 'إيقاف نمط الرقابة البانورامية' : 'تفعيل نمط الرقابة البانورامية في القاعة 👁️', style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w700), ), style: ElevatedButton.styleFrom( backgroundColor: _isPanoramicActive ? const Color(0xFFB91C1C) : const Color(0xFF0284C7), padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10)), ), ), ], ), ), const SizedBox(height: 18), // Anomaly Indicator Summary & Statistical Detection 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), ), ElevatedButton.icon( onPressed: _isRunningIntegrityAudit ? null : _runIntegrityAudit, icon: _isRunningIntegrityAudit ? const SizedBox(width: 14, height: 14, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white)) : const Icon(CupertinoIcons.waveform_path_ecg, size: 15), label: Text( _isRunningIntegrityAudit ? 'جاري الفحص...' : 'فحص النزاهة اللحظي ⚡', style: const TextStyle(fontSize: 11.5, fontWeight: FontWeight.w800), ), style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF0284C7), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), ), ], ), const SizedBox(height: 12), _buildAnomalyTile( title: 'مؤشر السرعة المستحيلة (أقل من 15 ثانية لمسألة تفاضل معقدة)', status: _integrityResult != null ? 'تم رصد حالة مشبوهة ⚠️' : 'سليم', isGood: _integrityResult == null, ), _buildAnomalyTile( title: 'مؤشر تكتل الأخطاء المتطابقة (خيارات خاطئة نادرة بين مقاعد متجاورة)', status: _integrityResult != null ? 'تطابق في مقعد 05 و 06 ⚠️' : 'سليم', isGood: _integrityResult == null, ), _buildAnomalyTile( title: 'مؤشر القفزة التاريخية المفاجئة (ارتفاع 45% دفعة واحدة)', status: _integrityResult != null ? 'حالة واحدة تتطلب مطابقة الخطوات الورقية ⚠️' : 'سليم', isGood: _integrityResult == null, ), // Live Anomalies Feed if (_integrityResult != null && (_integrityResult!['anomalies'] as List? ?? []).isNotEmpty) ...[ const Divider(color: Color(0xFF1E293B), height: 20), const Text('🚨 تنبيهات الشذوذ المكتشفة ومقترحات المشرف:', style: TextStyle(fontSize: 12.5, fontWeight: FontWeight.w800, color: Color(0xFFEF4444))), const SizedBox(height: 8), ...((_integrityResult!['anomalies'] as List).take(2).map((anom) => Container( margin: const EdgeInsets.only(bottom: 8), padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: const Color(0xFF7F1D1D).withOpacity(0.25), borderRadius: BorderRadius.circular(8), border: Border.all(color: const Color(0xFFEF4444).withOpacity(0.4)), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('${anom['title']} · ${anom['student_name']} (${anom['seat_number']})', style: const TextStyle(fontSize: 11.5, fontWeight: FontWeight.w800, color: Color(0xFFFCA5A5))), const SizedBox(height: 4), Text(anom['details'] ?? '', style: const TextStyle(fontSize: 11, color: Colors.white70)), const SizedBox(height: 4), Text('الإجراء: ${anom['recommended_action']}', style: const TextStyle(fontSize: 10.5, color: Color(0xFFFBBF24), fontWeight: FontWeight.w700)), ], ), ))), ], ], ), ), const SizedBox(height: 18), // Monthly Parent Reports Dispatch Card (Nabeh WhatsApp Gateway) Container( padding: const EdgeInsets.all(16), 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: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: const Color(0xFF10B981).withOpacity(0.2), shape: BoxShape.circle, ), child: const Icon(CupertinoIcons.chat_bubble_2_fill, color: Color(0xFF34D399), size: 22), ), const SizedBox(width: 14), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'بث تقارير أولياء الأمور عبر الواتساب (بوابة نبيه) 📲', style: TextStyle(fontSize: 13.5, fontWeight: FontWeight.w800, color: Colors.white), ), SizedBox(height: 3), Text( 'إرسال ومضة رقمية شهرية لـ 450 ولي أمر تتضمن الحضور ودفتر الأخطاء ودرجات الامتحان الموحد.', style: TextStyle(fontSize: 11, color: Color(0xFF94A3B8), height: 1.3), ), ], ), ), const SizedBox(width: 8), ElevatedButton( onPressed: _isDispatchingParentReports ? null : _dispatchParentReports, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF10B981), foregroundColor: Colors.black, padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), child: _isDispatchingParentReports ? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.black)) : const Text('بث الدفعة الآن', style: TextStyle(fontSize: 12, fontWeight: FontWeight.w800)), ), ], ), ), const SizedBox(height: 14), // Encrypted School Roster Import Card (AES-256-GCM) Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: const Color(0xFF0F172A), borderRadius: BorderRadius.circular(16), border: Border.all(color: const Color(0xFF1E293B)), ), child: Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: const Color(0xFF38BDF8).withOpacity(0.2), shape: BoxShape.circle, ), child: const Icon(CupertinoIcons.lock_shield_fill, color: Color(0xFF38BDF8), size: 22), ), const SizedBox(width: 14), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'استيراد كشف المدرسة وتشفير الأرقام الوطنية 🔒', style: TextStyle(fontSize: 13.5, fontWeight: FontWeight.w800, color: Colors.white), ), SizedBox(height: 3), Text( 'تشفير فوري سيادي بـ AES-256-GCM مع التحقق من الـ 10 خانات الرقمية ومؤشر HMAC الأعمى.', style: TextStyle(fontSize: 11, color: Color(0xFF94A3B8), height: 1.3), ), ], ), ), const SizedBox(width: 8), ElevatedButton( onPressed: _isImportingRoster ? null : _importSchoolRoster, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFF0284C7), foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), ), child: _isImportingRoster ? const SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white)) : const Text('استيراد الكشف', style: TextStyle(fontSize: 12, fontWeight: FontWeight.w800)), ), ], ), ), ], ), ); } Widget _buildAnomalyTile( {required String title, required String status, required bool isGood}) { return Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( children: [ Icon( isGood ? CupertinoIcons.check_mark_circled_solid : CupertinoIcons.exclamationmark_triangle_fill, color: isGood ? const Color(0xFF10B981) : const Color(0xFFEF4444), size: 16, ), const SizedBox(width: 8), Expanded( child: Text(title, style: const TextStyle(fontSize: 12.5, color: Colors.white)), ), Text( status, style: TextStyle( fontSize: 11.5, fontWeight: FontWeight.w600, color: isGood ? const Color(0xFF34D399) : const Color(0xFFF87171), ), ), ], ), ); } Widget _metricChip(String label, String value) { return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: const Color(0xFF1E293B), borderRadius: BorderRadius.circular(6), ), child: Row( children: [ Text('$label: ', style: const TextStyle(fontSize: 11, color: Color(0xFF94A3B8))), Text(value, style: const TextStyle( fontSize: 11, fontWeight: FontWeight.w700, color: Colors.white)), ], ), ); } String _formatDuration(int seconds) { final m = seconds ~/ 60; final s = seconds % 60; return '${m.toString().padLeft(2, '0')}:${s.toString().padLeft(2, '0')}'; } }