Files
saqel/apps/teacher_app/lib/main.dart
T

1375 lines
54 KiB
Dart

import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const SaqelTeacherApp());
}
/// ==============================================================================
/// SAQEL ENTERPRISE (EDTECH 2.0) - TEACHER STUDIO & MONETIZATION APP
/// ==============================================================================
class SaqelTeacherApp extends StatelessWidget {
const SaqelTeacherApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'صَقِل — استوديو المعلم المعتمد',
debugShowCheckedModeBanner: false,
theme: ThemeData(
brightness: Brightness.dark,
scaffoldBackgroundColor: const Color(0xFF070B12),
fontFamily: '-apple-system',
fontFamilyFallback: const [
'SF Pro Display',
'SF Pro Text',
'SF Arabic',
'Cairo',
'system-ui',
],
colorScheme: const ColorScheme.dark(
primary: Color(0xFF10B981),
surface: Color(0xFF0F172A),
background: Color(0xFF070B12),
),
),
home: const TeacherMainShell(),
);
}
}
class TeacherMainShell extends StatefulWidget {
const TeacherMainShell({super.key});
@override
State<TeacherMainShell> createState() => _TeacherMainShellState();
}
class _TeacherMainShellState extends State<TeacherMainShell> {
int _currentTabIndex = 0;
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.rtl,
child: Scaffold(
backgroundColor: const Color(0xFF070B12),
appBar: AppBar(
backgroundColor: const Color(0xFF0E1626),
elevation: 0,
title: Row(
children: [
Container(
width: 36,
height: 36,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF10B981), Color(0xFF059669)],
),
borderRadius: BorderRadius.circular(10),
),
child: const Center(
child: Text(
'م',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w900,
color: Colors.black,
),
),
),
),
const SizedBox(width: 10),
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'استوديو المعلم المعتمد',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w800,
color: Colors.white,
),
),
SizedBox(width: 6),
Icon(CupertinoIcons.checkmark_seal_fill,
color: Color(0xFF10B981), size: 16),
],
),
Text(
'أ. أحمد المجالي · فيزياء التوجيهي (الثقافة العسكرية)',
style: TextStyle(fontSize: 11, color: Color(0xFF94A3B8)),
),
],
),
],
),
actions: [
Container(
margin: const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: const Color(0xFF10B981).withOpacity(0.15),
borderRadius: BorderRadius.circular(8),
border: Border.all(color: const Color(0xFF10B981).withOpacity(0.4)),
),
child: const Row(
children: [
Icon(CupertinoIcons.star_fill,
color: Color(0xFFFBBF24), size: 14),
SizedBox(width: 4),
Text(
'معتمد 94%',
style: TextStyle(
fontSize: 11.5,
fontWeight: FontWeight.w800,
color: Color(0xFF34D399),
),
),
],
),
),
],
),
body: IndexedStack(
index: _currentTabIndex,
children: const [
TeacherStudioUploadView(),
TeacherMonetizationView(),
TeacherReputationScorecardView(),
TeacherAssignmentsAndQnAView(),
],
),
bottomNavigationBar: Container(
decoration: const BoxDecoration(
color: Color(0xFF0E1626),
border: Border(top: BorderSide(color: Color(0xFF1E293B))),
),
child: BottomNavigationBar(
currentIndex: _currentTabIndex,
onTap: (idx) => setState(() => _currentTabIndex = idx),
backgroundColor: Colors.transparent,
selectedItemColor: const Color(0xFF10B981),
unselectedItemColor: const Color(0xFF64748B),
selectedLabelStyle:
const TextStyle(fontWeight: FontWeight.w800, fontSize: 11),
unselectedLabelStyle: const TextStyle(fontSize: 10.5),
type: BottomNavigationBarType.fixed,
elevation: 0,
items: const [
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.videocam_circle_fill),
label: 'استوديو الحصص',
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.money_dollar_circle_fill),
label: 'محفظة التسييل',
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.chart_bar_circle_fill),
label: 'رادار الأداء',
),
BottomNavigationBarItem(
icon: Icon(CupertinoIcons.chat_bubble_2_fill),
label: 'الواجبات والأسئلة',
),
],
),
),
),
);
}
}
/// ==============================================================================
/// TAB 1: STUDIO LESSON UPLOAD & QUALITY GATE (20-25 MIN COGNITIVE LIMIT)
/// ==============================================================================
class TeacherStudioUploadView extends StatefulWidget {
const TeacherStudioUploadView({super.key});
@override
State<TeacherStudioUploadView> createState() =>
_TeacherStudioUploadViewState();
}
class _TeacherStudioUploadViewState extends State<TeacherStudioUploadView> {
final TextEditingController _titleController = TextEditingController(
text: 'شرح قاعدة لنتز والحث الكهرومغناطيسي — فيزياء 2008');
double _durationMinutes = 22.5;
bool _isAuditing = false;
Map<String, dynamic>? _auditResult;
void _runQualityGate() async {
setState(() => _isAuditing = true);
try {
final response = await http.post(
Uri.parse('http://127.0.0.1:8000/api/teacher/audit-studio-video'),
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: json.encode({
'lesson_title': _titleController.text,
'duration_minutes': _durationMinutes,
'subject': 'الفيزياء',
}),
).timeout(const Duration(seconds: 4));
if (response.statusCode == 200) {
final decoded = json.decode(response.body);
if (decoded['status'] == 'success') {
setState(() {
_auditResult = decoded['data'];
_isAuditing = false;
});
return;
}
}
} catch (_) {
// Fallback local simulation
}
await Future.delayed(const Duration(milliseconds: 600));
if (mounted) {
setState(() {
_isAuditing = false;
final isValid = _durationMinutes >= 15.0 && _durationMinutes <= 25.0;
_auditResult = {
'lesson_title': _titleController.text,
'subject': 'الفيزياء',
'duration_minutes': _durationMinutes,
'duration_gate_passed': isValid,
'duration_warning': _durationMinutes > 25.0
? 'تنبيه إدراكي: مدة الحصة تتجاوز 25 دقيقة. أثبتت أبحاث معهد ماساتشوستس أن التركيز الذهني يهبط بعد الدقيقة 18. يُوصى بتقسيم الحصة إلى جزأين.'
: null,
'quality_score': 92,
'approval_status': 'approved_for_broadcast',
'curriculum_alignment': '96% تطابق مع مخرجات المنهاج الوزاري',
'audio_clarity': '95% نقاء صوتي ممتاز',
'socratic_stops_count': 3,
'decision':
'الحصة معتمدة ومؤهلة للبث المشفر والعرض للبيع خارج الثقافة العسكرية 🚀',
};
});
}
}
@override
Widget build(BuildContext context) {
final isDurationGood =
_durationMinutes >= 15.0 && _durationMinutes <= 25.0;
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Banner
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: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(CupertinoIcons.sparkles,
color: Color(0xFF34D399), size: 20),
SizedBox(width: 8),
Text(
'بوابة جودة حصص الأستوديو الرقمية (صَقِل 2.0)',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: Colors.white,
),
),
],
),
SizedBox(height: 6),
Text(
'تخضع كل حصة رقمية لمعايير التركيز الإدراكي (20-25 دقيقة كحد أقصى) وفحص الذكاء الاصطناعي بنسبة قبول لا تقل عن 85% قبل نشرها وتسييلها.',
style: TextStyle(
fontSize: 12,
color: Color(0xFF94A3B8),
height: 1.4,
),
),
],
),
),
const SizedBox(height: 16),
// Upload Details Card
Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFF1E293B)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'عنوان الحصة الصفية الرقمية:',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
const SizedBox(height: 8),
TextField(
controller: _titleController,
style: const TextStyle(color: Colors.white, fontSize: 13.5),
decoration: InputDecoration(
filled: true,
fillColor: const Color(0xFF161F30),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFF334155)),
),
),
),
const SizedBox(height: 16),
// Duration Slider
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'مدة الشرح الرقمي:',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: isDurationGood
? const Color(0xFF10B981).withOpacity(0.2)
: const Color(0xFFEF4444).withOpacity(0.2),
borderRadius: BorderRadius.circular(6),
),
child: Text(
'${_durationMinutes.toStringAsFixed(1)} دقيقة',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w900,
color: isDurationGood
? const Color(0xFF34D399)
: const Color(0xFFEF4444),
),
),
),
],
),
Slider(
value: _durationMinutes,
min: 5.0,
max: 45.0,
divisions: 40,
activeColor: isDurationGood
? const Color(0xFF10B981)
: const Color(0xFFEF4444),
inactiveColor: const Color(0xFF1E293B),
onChanged: (val) => setState(() {
_durationMinutes = val;
_auditResult = null;
}),
),
// Cognitive Indicator Message
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDurationGood
? const Color(0xFF064E3B).withOpacity(0.2)
: const Color(0xFF7F1D1D).withOpacity(0.2),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isDurationGood
? const Color(0xFF10B981).withOpacity(0.4)
: const Color(0xFFEF4444).withOpacity(0.4),
),
),
child: Row(
children: [
Icon(
isDurationGood
? CupertinoIcons.check_mark_circled_solid
: CupertinoIcons.exclamationmark_triangle_fill,
size: 18,
color: isDurationGood
? const Color(0xFF34D399)
: const Color(0xFFEF4444),
),
const SizedBox(width: 8),
Expanded(
child: Text(
isDurationGood
? 'مثالي: الشرح ضمن المدى الإدراكي القياسي (20 إلى 25 دقيقة) لمعادلة تركيز الحصة الصفية.'
: 'تحذير إدراكي: الشرح يتجاوز 25 دقيقة! ينخفض الاستيعاب بعد الدقيقة 18، يرجى اختصار المقطع أو تقسيمه.',
style: TextStyle(
fontSize: 11.5,
color: isDurationGood
? const Color(0xFF6EE7B7)
: const Color(0xFFFCA5A5),
),
),
),
],
),
),
const SizedBox(height: 18),
// Run Audit Button
ElevatedButton.icon(
onPressed: _isAuditing ? null : _runQualityGate,
icon: _isAuditing
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(
strokeWidth: 2, color: Colors.black),
)
: const Icon(CupertinoIcons.checkmark_shield_fill, size: 18),
label: Text(
_isAuditing
? 'جاري التدقيق التربوي عبر محرك صَقِل...'
: 'فحص الحصة عبر بوابة الجودة الذكية (عتبة 85%) ⚡',
style: const TextStyle(
fontSize: 13.5, fontWeight: FontWeight.w800),
),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF10B981),
foregroundColor: Colors.black,
minimumSize: const Size(double.infinity, 44),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
],
),
),
const SizedBox(height: 16),
// Quality Audit Result Card
if (_auditResult != null) _buildAuditResultCard(_auditResult!),
],
),
);
}
Widget _buildAuditResultCard(Map<String, dynamic> res) {
final score = res['quality_score'] as int? ?? 0;
final isApproved = score >= 85;
return Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: isApproved
? const Color(0xFF10B981)
: const Color(0xFFEF4444),
width: 1.5,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(
isApproved
? CupertinoIcons.check_mark_seal_fill
: CupertinoIcons.xmark_seal_fill,
color: isApproved
? const Color(0xFF10B981)
: const Color(0xFFEF4444),
size: 22,
),
const SizedBox(width: 8),
Text(
isApproved ? 'حصة معتمدة للبث والتسييل 🏅' : 'تجميد الحصة للمراجعة ⚠️',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w900,
color: isApproved
? const Color(0xFF10B981)
: const Color(0xFFEF4444),
),
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: isApproved
? const Color(0xFF10B981).withOpacity(0.2)
: const Color(0xFFEF4444).withOpacity(0.2),
borderRadius: BorderRadius.circular(8),
),
child: Text(
'درجة التقييم: $score%',
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w900,
color: isApproved
? const Color(0xFF34D399)
: const Color(0xFFEF4444),
),
),
),
],
),
const SizedBox(height: 12),
Text(
res['decision'] ?? '',
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: Colors.white,
height: 1.4,
),
),
const Divider(color: Color(0xFF1E293B), height: 20),
// Breakdown
_rowMetric('التطابق مع نتاجات المنهاج:', res['curriculum_alignment']),
const SizedBox(height: 6),
_rowMetric('نقاء الصوت ومخارج الحروف:', res['audio_clarity']),
const SizedBox(height: 6),
_rowMetric('الفواصل السقراطية التفاعلية:',
'${res['socratic_stops_count']} محطات تفكيرية إلزامية'),
const SizedBox(height: 14),
if (isApproved)
ElevatedButton.icon(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'تم ترحيل الحصة لتقطيع البث المشفر ونشرها في سوق صَقِل! 🚀'),
backgroundColor: Color(0xFF10B981),
),
);
},
icon: const Icon(CupertinoIcons.cloud_upload_fill, size: 16),
label: const Text('نشر الحصة في سوق التسييل التجاري 💰'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF059669),
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 42),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
),
],
),
);
}
Widget _rowMetric(String label, dynamic value) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label,
style: const TextStyle(fontSize: 12, color: Color(0xFF94A3B8))),
Text('$value',
style: const TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w700,
color: Color(0xFFCBD5E1))),
],
);
}
}
/// ==============================================================================
/// TAB 2: MONETIZATION & REVENUE SHARE DASHBOARD (55% / 15% / 30%)
/// ==============================================================================
class TeacherMonetizationView extends StatefulWidget {
const TeacherMonetizationView({super.key});
@override
State<TeacherMonetizationView> createState() =>
_TeacherMonetizationViewState();
}
class _TeacherMonetizationViewState extends State<TeacherMonetizationView> {
double _studentSubscribers = 380;
final double _pricePerCourse = 20.0;
@override
Widget build(BuildContext context) {
final grossRevenue = _studentSubscribers * _pricePerCourse;
final teacherShare = grossRevenue * 0.55;
final directorateShare = grossRevenue * 0.15;
final platformShare = grossRevenue * 0.30;
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Wallet Card
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF064E3B), Color(0xFF062E25)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: const Color(0xFF10B981).withOpacity(0.4)),
boxShadow: [
BoxShadow(
color: const Color(0xFF10B981).withOpacity(0.15),
blurRadius: 20,
offset: const Offset(0, 8),
)
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'الرصيد المتاح للسحب (حصة المعلم 55%):',
style: TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w700,
color: Color(0xFF6EE7B7),
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3),
borderRadius: BorderRadius.circular(6),
),
child: const Text(
'IBAN معتمد',
style: TextStyle(
fontSize: 11,
color: Color(0xFF34D399),
fontWeight: FontWeight.w700),
),
)
],
),
const SizedBox(height: 8),
Text(
'${teacherShare.toStringAsFixed(0)} دينار أردني',
style: const TextStyle(
fontSize: 30,
fontWeight: FontWeight.w900,
color: Colors.white,
letterSpacing: 0.5,
),
),
const SizedBox(height: 12),
ElevatedButton.icon(
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text(
'تم تقديم طلب التحويل لحسابك البنكي المعتمد بنجاح.'),
backgroundColor: Color(0xFF10B981),
),
);
},
icon: const Icon(CupertinoIcons.money_dollar, size: 18),
label: const Text('طلب تحويل الرصيد للحساب البنكي'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF10B981),
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
),
],
),
),
const SizedBox(height: 18),
// Interactive Revenue Share Formula
Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(18),
border: Border.all(color: const Color(0xFF1E293B)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'نموذج تقاسم العوائد الثلاثي المعتمد (الفصل السابع):',
style: TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w800,
color: Colors.white,
),
),
const SizedBox(height: 14),
// Split Bars
_splitRow('حصة المعلم المباشرة (55%):',
'${teacherShare.toStringAsFixed(0)} د.أ', const Color(0xFF10B981)),
const SizedBox(height: 8),
_splitRow('حصة مديرية الثقافة العسكرية (15%):',
'${directorateShare.toStringAsFixed(0)} د.أ', const Color(0xFFF59E0B)),
const SizedBox(height: 8),
_splitRow('حصة منصة صَقِل للبنية والسيرفرات (30%):',
'${platformShare.toStringAsFixed(0)} د.أ', const Color(0xFF38BDF8)),
const Divider(color: Color(0xFF1E293B), height: 24),
// Subscribers Slider
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text('محاكي المشتركين خارج الثقافة:',
style: TextStyle(fontSize: 12, color: Color(0xFF94A3B8))),
Text('${_studentSubscribers.toInt()} طالب مشترك',
style: const TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w800,
color: Color(0xFF34D399))),
],
),
Slider(
value: _studentSubscribers,
min: 50,
max: 2000,
divisions: 39,
activeColor: const Color(0xFF10B981),
inactiveColor: const Color(0xFF1E293B),
onChanged: (val) => setState(() => _studentSubscribers = val),
),
],
),
),
const SizedBox(height: 18),
// Active Courses Selling
const Text(
'الدورات المنشورة في سوق صَقِل الخارجي:',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: Colors.white,
),
),
const SizedBox(height: 10),
_courseEarningCard(
title: 'الفيزياء للتوجيهي العلمي (الفصل الأول)',
students: 240,
revenue: '2,640 د.أ',
),
const SizedBox(height: 8),
_courseEarningCard(
title: 'المكثف الشامل لقوانين نيوتن وحفظ الطاقة',
students: 140,
revenue: '1,540 د.أ',
),
],
),
);
}
Widget _splitRow(String label, String amount, Color color) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Container(width: 8, height: 8, decoration: BoxDecoration(color: color, shape: BoxShape.circle)),
const SizedBox(width: 8),
Text(label, style: const TextStyle(fontSize: 12.5, color: Colors.white)),
],
),
Text(amount, style: TextStyle(fontSize: 13.5, fontWeight: FontWeight.w900, color: color)),
],
);
}
Widget _courseEarningCard({
required String title,
required int students,
required String revenue,
}) {
return Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFF1E293B)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w800, color: Colors.white)),
const SizedBox(height: 3),
Text('$students طالب مشترك · رسوم 20 د.أ', style: const TextStyle(fontSize: 11, color: Color(0xFF94A3B8))),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(revenue, style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w900, color: Color(0xFF10B981))),
const Text('صافي أرباحك', style: TextStyle(fontSize: 10, color: Color(0xFF64748B))),
],
),
],
),
);
}
}
/// ==============================================================================
/// TAB 3: TEACHER REPUTATION & CERTIFICATION SCORECARD
/// ==============================================================================
class TeacherReputationScorecardView extends StatelessWidget {
const TeacherReputationScorecardView({super.key});
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Top Merit Card
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [Color(0xFF1E1B4B), Color(0xFF0F172A)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: const Color(0xFF8B5CF6).withOpacity(0.5)),
),
child: Column(
children: [
const CircleAvatar(
radius: 32,
backgroundColor: Color(0xFF8B5CF6),
child: Icon(CupertinoIcons.rosette, size: 36, color: Colors.white),
),
const SizedBox(height: 12),
const Text(
'أ. أحمد المجالي',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w900,
color: Colors.white,
),
),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: const Color(0xFF10B981).withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
),
child: const Text(
'🏅 معلم معتمد رسمياً من منصة صَقِل (فوق 90%)',
style: TextStyle(
fontSize: 11.5,
fontWeight: FontWeight.w800,
color: Color(0xFF34D399),
),
),
),
const SizedBox(height: 16),
const Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_ScoreStat(label: 'التقييم العام', value: '4.92 / 5.0'),
_ScoreStat(label: 'الطلاب المخدومون', value: '1,240 طالب'),
_ScoreStat(label: 'نسبة النجاح', value: '98.2%'),
],
),
],
),
),
const SizedBox(height: 18),
// Pedagogical Metrics Breakdown
Container(
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(18),
border: Border.all(color: const Color(0xFF1E293B)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'رادار الجودة التربوية المعتمد:',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: Colors.white,
),
),
const SizedBox(height: 14),
_metricProgress('الالتزام بالمنهاج والنتاجات الوزارية', 0.96, const Color(0xFF10B981)),
const SizedBox(height: 12),
_metricProgress('التفاعل السقراطي وإثارة التفكير', 0.89, const Color(0xFF38BDF8)),
const SizedBox(height: 12),
_metricProgress('الوضوح الصوتي وسلامة اللغة', 0.94, const Color(0xFFF59E0B)),
const SizedBox(height: 12),
_metricProgress('إدارة الوقت والتركيز الإدراكي (20-25 دقيقة)', 0.92, const Color(0xFF8B5CF6)),
],
),
),
const SizedBox(height: 18),
// AI Growth Insights Box
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFF1E293B),
borderRadius: BorderRadius.circular(14),
border: Border.all(color: const Color(0xFF334155)),
),
child: const Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(CupertinoIcons.lightbulb_fill, color: Color(0xFFFBBF24), size: 20),
SizedBox(width: 10),
Expanded(
child: Text(
'توجيه الذكاء الاصطناعي الأسبوعي: نسبة الالتزام الوزاري ممتازة (96%). يُوصى بإضافة وقفة سقراطية استنتاجية في الدقيقة 14 من الدرس القادم لتعزيز تفاعل الطلبة.',
style: TextStyle(fontSize: 12, color: Color(0xFFCBD5E1), height: 1.4),
),
),
],
),
),
],
),
);
}
Widget _metricProgress(String title, double value, Color color) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title, style: const TextStyle(fontSize: 12, color: Colors.white)),
Text('${(value * 100).toInt()}%',
style: TextStyle(fontSize: 12.5, fontWeight: FontWeight.w800, color: color)),
],
),
const SizedBox(height: 6),
ClipRRect(
borderRadius: BorderRadius.circular(4),
child: LinearProgressIndicator(
value: value,
minHeight: 6,
backgroundColor: const Color(0xFF1E293B),
valueColor: AlwaysStoppedAnimation<Color>(color),
),
),
],
);
}
}
class _ScoreStat extends StatelessWidget {
final String label;
final String value;
const _ScoreStat({required this.label, required this.value});
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(value, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w900, color: Colors.white)),
const SizedBox(height: 2),
Text(label, style: const TextStyle(fontSize: 11, color: Color(0xFF94A3B8))),
],
);
}
}
/// ==============================================================================
/// TAB 4: TEACHER ASSIGNMENTS & STUDENT DOUBT CLEARING (Q&A & VOICE REPLIES)
/// ==============================================================================
class TeacherAssignmentsAndQnAView extends StatefulWidget {
const TeacherAssignmentsAndQnAView({super.key});
@override
State<TeacherAssignmentsAndQnAView> createState() =>
_TeacherAssignmentsAndQnAViewState();
}
class _TeacherAssignmentsAndQnAViewState
extends State<TeacherAssignmentsAndQnAView> {
final List<Map<String, dynamic>> _homeworks = [
{
'title': 'ورقة عمل: قوانين نيوتن وتطبيقات المصاعد',
'class': 'الأول ثانوي العلمي (شعبة أ)',
'submitted': '38 من 42 طالباً',
'due_date': 'غداً الساعة 08:00 مساءً',
'average_score': '86%',
'status': 'active',
},
{
'title': 'واجب بيتي: مسائل تحليل المتجهات والضرب القياسي',
'class': 'الأول ثانوي العلمي (شعبة ب)',
'submitted': '41 من 41 طالباً',
'due_date': 'مكتمل التسليم والتصحيح الآلي',
'average_score': '92%',
'status': 'completed',
},
];
final List<Map<String, dynamic>> _studentDoubts = [
{
'student_name': 'محمد طارق الحنيطي',
'class': 'الأول ثانوي العلمي',
'question': 'أستاذ، لماذا ينعدم الوزن الظاهري في المصعد الساقط سقوطاً حراً؟ هل تفقد الأجسام كتلتها؟',
'time': 'منذ 25 دقيقة',
'replied': false,
'voice_reply': false,
},
{
'student_name': 'سيف الدين الرواشدة',
'class': 'الأول ثانوي العلمي',
'question': 'هل قوة الاحتكاك السكوني دائماً تساوي القوة المؤثرة حتى نصل للقيمة العظمى؟',
'time': 'منذ ساعتين',
'replied': true,
'voice_reply': true,
},
];
void _dispatchNewHomework() {
showCupertinoDialog(
context: context,
builder: (ctx) => Directionality(
textDirection: TextDirection.rtl,
child: CupertinoAlertDialog(
title: const Text('إسناد ورقة عمل تفاعلية جديدة 📝'),
content: const Text(
'سيقوم محرك صَقِل بتوليد ورقة عمل مكونة من 5 مسائل تدريبية متدرجة من بنك الأسئلة الوزاري وإرسالها فوراً لشعبتك مع التصحيح التلقائي.',
),
actions: [
CupertinoDialogAction(
onPressed: () => Navigator.of(ctx).pop(),
child: const Text('إلغاء'),
),
CupertinoDialogAction(
isDefaultAction: true,
onPressed: () {
Navigator.of(ctx).pop();
setState(() {
_homeworks.insert(0, {
'title': 'ورقة عمل: الشغل والطاقة الميكانيكية',
'class': 'الأول ثانوي العلمي (شعبة أ وب)',
'submitted': '0 من 83 طالباً',
'due_date': 'الخميس القادم',
'average_score': 'بانتظار الإجابات',
'status': 'active',
});
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('تم إسناد ورقة العمل وإشعار الطلبة بنجاح 🚀'),
backgroundColor: Color(0xFF10B981),
),
);
},
child: const Text('إسناد للطلبة الآن'),
),
],
),
),
);
}
void _recordVoiceReply(int doubtIndex) {
showModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
builder: (ctx) => Directionality(
textDirection: TextDirection.rtl,
child: Container(
decoration: const BoxDecoration(
color: Color(0xFF0F172A),
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
border: Border(top: BorderSide(color: Color(0xFF10B981), width: 2)),
),
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(CupertinoIcons.mic_circle_fill, size: 56, color: Color(0xFF10B981)),
const SizedBox(height: 12),
const Text(
'تسجيل رد صوتي سقراطي للطالب 🎙️',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w800, color: Colors.white),
),
const SizedBox(height: 6),
const Text(
'سيصل التسجيل الصوتي بجودة عالية داخل تطبيق الطالب مع التوجيه المفاهيمي.',
style: TextStyle(fontSize: 12, color: Color(0xFF94A3B8)),
textAlign: TextAlign.center,
),
const SizedBox(height: 20),
ElevatedButton.icon(
onPressed: () {
Navigator.of(ctx).pop();
setState(() {
_studentDoubts[doubtIndex]['replied'] = true;
_studentDoubts[doubtIndex]['voice_reply'] = true;
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('تم إرسال الرد الصوتي للطالب بنجاح 🎧'),
backgroundColor: Color(0xFF10B981),
),
);
},
icon: const Icon(CupertinoIcons.paperplane_fill, size: 16),
label: const Text('إنهاء وإرسال التسجيل الصوتي (00:42)'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF10B981),
foregroundColor: Colors.black,
minimumSize: const Size(double.infinity, 44),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
),
),
],
),
),
),
);
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Top Action Card
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(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'أوراق العمل والواجبات الوزارية 📝',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w800, color: Colors.white),
),
SizedBox(height: 3),
Text(
'تصحيح مؤتمت بنسبة 100% مرتبط ببنك الأسئلة',
style: TextStyle(fontSize: 11.5, color: Color(0xFF94A3B8)),
),
],
),
ElevatedButton.icon(
onPressed: _dispatchNewHomework,
icon: const Icon(CupertinoIcons.plus_circle_fill, size: 16),
label: const Text('إسناد واجب جديد', style: TextStyle(fontSize: 12, fontWeight: FontWeight.w800)),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF10B981),
foregroundColor: Colors.black,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
),
],
),
),
const SizedBox(height: 16),
// Homeworks List
..._homeworks.map((hw) => 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: const Color(0xFF1E293B)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(hw['title'], style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w800, color: Colors.white)),
const SizedBox(height: 4),
Text('${hw['class']} · ${hw['due_date']}', style: const TextStyle(fontSize: 11, color: Color(0xFF94A3B8))),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: const Color(0xFF10B981).withOpacity(0.15),
borderRadius: BorderRadius.circular(6),
),
child: Text(hw['submitted'], style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w700, color: Color(0xFF34D399))),
),
const SizedBox(height: 4),
Text('معدل الشعبة: ${hw['average_score']}', style: const TextStyle(fontSize: 10.5, color: Color(0xFF64748B))),
],
),
],
),
)),
const SizedBox(height: 18),
// Student Doubts Section
const Row(
children: [
Icon(CupertinoIcons.bubble_left_bubble_right_fill, color: Color(0xFF38BDF8), size: 18),
SizedBox(width: 8),
Text(
'استفسارات وأسئلة الطلبة الميدانية (غرفة الشكوك) 💬',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w800, color: Colors.white),
),
],
),
const SizedBox(height: 10),
..._studentDoubts.asMap().entries.map((entry) {
final idx = entry.key;
final d = entry.value;
final replied = d['replied'] as bool;
return Container(
margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: const Color(0xFF0F172A),
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: replied ? const Color(0xFF10B981).withOpacity(0.3) : const Color(0xFF38BDF8).withOpacity(0.4),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('${d['student_name']} · ${d['class']}', style: const TextStyle(fontSize: 12.5, fontWeight: FontWeight.w800, color: Color(0xFF38BDF8))),
Text(d['time'], style: const TextStyle(fontSize: 11, color: Color(0xFF64748B))),
],
),
const SizedBox(height: 8),
Text(
d['question'],
style: const TextStyle(fontSize: 13, color: Colors.white, height: 1.4),
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (replied)
const Row(
children: [
Icon(CupertinoIcons.checkmark_seal_fill, color: Color(0xFF34D399), size: 15),
SizedBox(width: 6),
Text('تم إرسال الرد الصوتي للطالب 🎧', style: TextStyle(fontSize: 11.5, color: Color(0xFF6EE7B7), fontWeight: FontWeight.w700)),
],
)
else
const Row(
children: [
Icon(CupertinoIcons.clock_fill, color: Color(0xFFFBBF24), size: 14),
SizedBox(width: 6),
Text('بانتظار إجابة المعلم', style: TextStyle(fontSize: 11.5, color: Color(0xFFFBBF24))),
],
),
if (!replied)
ElevatedButton.icon(
onPressed: () => _recordVoiceReply(idx),
icon: const Icon(CupertinoIcons.mic_fill, size: 14),
label: const Text('تسجيل رد صوتي 🎙️', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w700)),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0284C7),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
),
],
),
],
),
);
}),
],
),
);
}
}