Update Saqel Platform: 2026-09-08 18:19:28

This commit is contained in:
Hamza-Ayed
2026-09-08 18:19:28 +03:00
parent 75451ebc3f
commit e5cbd89323
22 changed files with 1537 additions and 595 deletions
@@ -21,13 +21,16 @@ class TeacherAssignmentsQnATab extends StatefulWidget {
const TeacherAssignmentsQnATab({super.key});
@override
State<TeacherAssignmentsQnATab> createState() => _TeacherAssignmentsQnATabState();
State<TeacherAssignmentsQnATab> createState() =>
_TeacherAssignmentsQnATabState();
}
class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
final TextEditingController _broadcastTitleCtrl = TextEditingController(text: 'توضيح بيداغوجي: مسار الشحنة في الوعاء المغناطيسي');
final TextEditingController _broadcastTitleCtrl = TextEditingController(
text: 'توضيح بيداغوجي: مسار الشحنة في الوعاء المغناطيسي');
final TextEditingController _broadcastMessageCtrl = TextEditingController(
text: 'أعزائي طلبة الصف العاشر: انتبهوا إلى أن قوة لورنتز تكون دائماً عمودية على كل من متجه السرعة ومتجه المجال، لذلك لا تبذل شغلاً ولا تغيّر الطاقة الحركية، بل تُوجّه الجسيم في مسار حلزوني محصور داخل الوعاء.',
text:
'أعزائي طلبة الصف العاشر: انتبهوا إلى أن قوة لورنتز تكون دائماً عمودية على كل من متجه السرعة ومتجه المجال، لذلك لا تبذل شغلاً ولا تغيّر الطاقة الحركية، بل تُوجّه الجسيم في مسار حلزوني محصور داخل الوعاء.',
);
String _selectedBroadcastGrade = 'الصف العاشر الأساسي';
@@ -68,7 +71,8 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
className: 'الصف العاشر الأساسي (شعبة أ وب)',
dueDate: 'الخميس القادم الساعة 08:00 م',
);
SaqelToast.showSuccess(context, 'تم إسناد ورقة العمل وإشعار الطلبة بنجاح 🚀');
SaqelToast.showSuccess(
context, 'تم إسناد ورقة العمل وإشعار الطلبة بنجاح 🚀');
},
child: const Text('إسناد للطلبة الآن'),
),
@@ -95,7 +99,8 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
decoration: const BoxDecoration(
color: TeacherTheme.surfaceDark,
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
border: Border(top: BorderSide(color: TeacherTheme.emeraldPrimary, width: 2)),
border: Border(
top: BorderSide(color: TeacherTheme.emeraldPrimary, width: 2)),
),
child: Column(
mainAxisSize: MainAxisSize.min,
@@ -110,7 +115,8 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
color: TeacherTheme.emeraldPrimary.withOpacity(0.15),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(CupertinoIcons.waveform_path_badge_plus, color: TeacherTheme.emeraldPrimary, size: 24),
child: const Icon(CupertinoIcons.waveform_path_badge_plus,
color: TeacherTheme.emeraldPrimary, size: 24),
),
const SizedBox(width: 12),
const Expanded(
@@ -119,12 +125,16 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
children: [
Text(
'بث مقطع صوتي وإشعار فوري للشعبة 🎙️',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 15),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 15),
),
SizedBox(height: 2),
Text(
'يصل التسجيل الصوتي عبر خادم الويب سوكت (0ms) لكافة الطلبة المشتركين',
style: TextStyle(color: Color(0xFF94A3B8), fontSize: 11),
style:
TextStyle(color: Color(0xFF94A3B8), fontSize: 11),
),
],
),
@@ -133,18 +143,28 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
),
const SizedBox(height: 18),
const Text('عنوان التوجيه الصوتي:', style: TextStyle(color: Colors.white70, fontSize: 12, fontWeight: FontWeight.bold)),
const Text('عنوان التوجيه الصوتي:',
style: TextStyle(
color: Colors.white70,
fontSize: 12,
fontWeight: FontWeight.bold)),
const SizedBox(height: 6),
_buildInputContainer(
child: TextField(
controller: _broadcastTitleCtrl,
style: const TextStyle(color: Colors.white, fontSize: 13),
decoration: const InputDecoration(border: InputBorder.none, hintText: 'عنوان التوجيه الصوتي'),
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'عنوان التوجيه الصوتي'),
),
),
const SizedBox(height: 12),
const Text('الشعبة المستهدفة:', style: TextStyle(color: Colors.white70, fontSize: 12, fontWeight: FontWeight.bold)),
const Text('الشعبة المستهدفة:',
style: TextStyle(
color: Colors.white70,
fontSize: 12,
fontWeight: FontWeight.bold)),
const SizedBox(height: 6),
_buildInputContainer(
child: DropdownButtonHideUnderline(
@@ -152,51 +172,79 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
value: _selectedBroadcastGrade,
isExpanded: true,
dropdownColor: TeacherTheme.surfaceDark,
icon: const Icon(CupertinoIcons.chevron_down, color: TeacherTheme.emeraldPrimary, size: 16),
icon: const Icon(CupertinoIcons.chevron_down,
color: TeacherTheme.emeraldPrimary, size: 16),
items: const [
DropdownMenuItem(value: 'الصف العاشر الأساسي', child: Text('الصف العاشر الأساسي (فيزياء)', style: TextStyle(color: Colors.white, fontSize: 12.5))),
DropdownMenuItem(value: 'الأول ثانوي العلمي', child: Text('الأول ثانوي العلمي (ميكانيكا)', style: TextStyle(color: Colors.white, fontSize: 12.5))),
DropdownMenuItem(value: 'الثاني ثانوي (التوجيهي)', child: Text('الثاني ثانوي التوجيهي (مكثف)', style: TextStyle(color: Colors.white, fontSize: 12.5))),
DropdownMenuItem(
value: 'الصف العاشر الأساسي',
child: Text('الصف العاشر الأساسي (فيزياء)',
style: TextStyle(
color: Colors.white, fontSize: 12.5))),
DropdownMenuItem(
value: 'الأول ثانوي العلمي',
child: Text('الأول ثانوي العلمي (ميكانيكا)',
style: TextStyle(
color: Colors.white, fontSize: 12.5))),
DropdownMenuItem(
value: 'الثاني ثانوي (التوجيهي)',
child: Text('الثاني ثانوي التوجيهي (مكثف)',
style: TextStyle(
color: Colors.white, fontSize: 12.5))),
],
onChanged: (val) {
if (val != null) setState(() => _selectedBroadcastGrade = val);
if (val != null)
setState(() => _selectedBroadcastGrade = val);
},
),
),
),
const SizedBox(height: 12),
const Text('نص الشرح أو ملخص المقطع الصوتي:', style: TextStyle(color: Colors.white70, fontSize: 12, fontWeight: FontWeight.bold)),
const Text('نص الشرح أو ملخص المقطع الصوتي:',
style: TextStyle(
color: Colors.white70,
fontSize: 12,
fontWeight: FontWeight.bold)),
const SizedBox(height: 6),
_buildInputContainer(
child: TextField(
controller: _broadcastMessageCtrl,
maxLines: 4,
style: const TextStyle(color: Colors.white, fontSize: 12.5, height: 1.4),
decoration: const InputDecoration(border: InputBorder.none, hintText: 'اكتب التوجيه المفاهيمي للشعبة...'),
style: const TextStyle(
color: Colors.white, fontSize: 12.5, height: 1.4),
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'اكتب التوجيه المفاهيمي للشعبة...'),
),
),
const SizedBox(height: 16),
// Voice Recording Strip Simulation
Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
padding:
const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: const Color(0xFF0C241D),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: TeacherTheme.emeraldPrimary.withOpacity(0.5)),
border: Border.all(
color: TeacherTheme.emeraldPrimary.withOpacity(0.5)),
),
child: const Row(
children: [
Icon(CupertinoIcons.mic_fill, color: TeacherTheme.emeraldPrimary, size: 18),
Icon(CupertinoIcons.mic_fill,
color: TeacherTheme.emeraldPrimary, size: 18),
SizedBox(width: 10),
Expanded(
child: Text(
'مقطع صوتي مسجل بنقاء عالي: 01:14 دقيقة (ترميز AAC - 128kbps)',
style: TextStyle(color: TeacherTheme.emeraldLight, fontSize: 11.5, fontWeight: FontWeight.w600),
style: TextStyle(
color: TeacherTheme.emeraldLight,
fontSize: 11.5,
fontWeight: FontWeight.w600),
),
),
Icon(CupertinoIcons.play_circle_fill, color: TeacherTheme.emeraldPrimary, size: 22),
Icon(CupertinoIcons.play_circle_fill,
color: TeacherTheme.emeraldPrimary, size: 22),
],
),
),
@@ -205,7 +253,9 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
ElevatedButton.icon(
onPressed: () async {
Navigator.of(ctx).pop();
final success = await context.read<TeacherQnACubit>().broadcastAudioAnnouncement(
final success = await context
.read<TeacherQnACubit>()
.broadcastAudioAnnouncement(
title: _broadcastTitleCtrl.text.trim(),
explanationText: _broadcastMessageCtrl.text.trim(),
gradeLevel: _selectedBroadcastGrade,
@@ -219,12 +269,15 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
}
},
icon: const Icon(CupertinoIcons.paperplane_fill, size: 16),
label: const Text('بث المقطع الصوتي للشعبة الآن 🚀', style: TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
label: const Text('بث المقطع الصوتي للشعبة الآن 🚀',
style:
TextStyle(fontSize: 13, fontWeight: FontWeight.bold)),
style: ElevatedButton.styleFrom(
backgroundColor: TeacherTheme.emeraldPrimary,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
),
],
@@ -244,17 +297,22 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
decoration: const BoxDecoration(
color: TeacherTheme.surfaceCard,
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
border: Border(top: BorderSide(color: TeacherTheme.emeraldPrimary, width: 2)),
border: Border(
top: BorderSide(color: TeacherTheme.emeraldPrimary, width: 2)),
),
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(CupertinoIcons.mic_circle_fill, size: 56, color: TeacherTheme.emeraldPrimary),
const Icon(CupertinoIcons.mic_circle_fill,
size: 56, color: TeacherTheme.emeraldPrimary),
const SizedBox(height: 12),
Text(
'تسجيل رد صوتي سقراطي للطالبة ($studentName) 🎙️',
style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w800, color: Colors.white),
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w800,
color: Colors.white),
),
const SizedBox(height: 6),
const Text(
@@ -267,7 +325,8 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
onPressed: () {
context.read<TeacherQnACubit>().sendVoiceReply(
doubtId,
voiceText: 'رد الأستاذ صوتياً على استفسار الطالبة $studentName حول الوعاء المغناطيسي وحصر البلازما.',
voiceText:
'رد الأستاذ صوتياً على استفسار الطالبة $studentName حول الوعاء المغناطيسي وحصر البلازما.',
);
Navigator.of(ctx).pop();
SaqelToast.showSuccess(
@@ -282,7 +341,8 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
backgroundColor: TeacherTheme.emeraldPrimary,
foregroundColor: Colors.black,
minimumSize: const Size(double.infinity, 44),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
),
],
@@ -297,7 +357,9 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
return BlocBuilder<TeacherQnACubit, TeacherQnAState>(
builder: (context, state) {
if (state.isLoading) {
return const Center(child: CupertinoActivityIndicator(color: TeacherTheme.emeraldPrimary));
return const Center(
child: CupertinoActivityIndicator(
color: TeacherTheme.emeraldPrimary));
}
return SingleChildScrollView(
@@ -315,7 +377,8 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(18),
border: Border.all(color: TeacherTheme.emeraldPrimary, width: 1.2),
border: Border.all(
color: TeacherTheme.emeraldPrimary, width: 1.2),
boxShadow: const [
BoxShadow(
color: Color(0x3310B981),
@@ -334,7 +397,8 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
shape: BoxShape.circle,
border: Border.all(color: TeacherTheme.emeraldPrimary),
),
child: const Icon(CupertinoIcons.waveform_path_ecg, color: TeacherTheme.emeraldPrimary, size: 24),
child: const Icon(CupertinoIcons.waveform_path_ecg,
color: TeacherTheme.emeraldPrimary, size: 24),
),
const SizedBox(width: 14),
const Expanded(
@@ -345,16 +409,21 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
children: [
Text(
'بث صوتي وتوجيهي عبر الويب سوكت',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 13.5),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 13.5),
),
SizedBox(width: 6),
Icon(CupertinoIcons.antenna_radiowaves_left_right, color: TeacherTheme.emeraldPrimary, size: 14),
Icon(CupertinoIcons.antenna_radiowaves_left_right,
color: TeacherTheme.emeraldPrimary, size: 14),
],
),
SizedBox(height: 4),
Text(
'بث مقطع صوتي حي وشرح مباشر لجميع طلبة الشعبة المشتركين (0ms)',
style: TextStyle(color: Color(0xFF94A3B8), fontSize: 11),
style: TextStyle(
color: Color(0xFF94A3B8), fontSize: 11),
),
],
),
@@ -364,10 +433,14 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
style: ElevatedButton.styleFrom(
backgroundColor: TeacherTheme.emeraldPrimary,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 10),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
child: const Text('بدء البث 🎙️', style: TextStyle(fontSize: 12, fontWeight: FontWeight.w800)),
child: const Text('بدء البث 🎙️',
style: TextStyle(
fontSize: 12, fontWeight: FontWeight.w800)),
),
],
),
@@ -380,18 +453,27 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
children: [
const Row(
children: [
Icon(CupertinoIcons.doc_text_fill, color: TeacherTheme.emeraldPrimary, size: 16),
Icon(CupertinoIcons.doc_text_fill,
color: TeacherTheme.emeraldPrimary, size: 16),
SizedBox(width: 6),
Text(
'أوراق العمل والواجبات الوزارية 📝',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w800, color: Colors.white),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: Colors.white),
),
],
),
TextButton.icon(
onPressed: _dispatchNewHomework,
icon: const Icon(CupertinoIcons.plus_circle_fill, size: 15, color: TeacherTheme.emeraldPrimary),
label: const Text('إسناد واجب جديد', style: TextStyle(color: TeacherTheme.emeraldPrimary, fontSize: 12, fontWeight: FontWeight.bold)),
icon: const Icon(CupertinoIcons.plus_circle_fill,
size: 15, color: TeacherTheme.emeraldPrimary),
label: const Text('إسناد واجب جديد',
style: TextStyle(
color: TeacherTheme.emeraldPrimary,
fontSize: 12,
fontWeight: FontWeight.bold)),
),
],
),
@@ -404,11 +486,15 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
// Student Inquiries / Real Chat Header
const Row(
children: [
Icon(CupertinoIcons.chat_bubble_2_fill, color: TeacherTheme.cyberCyan, size: 18),
Icon(CupertinoIcons.chat_bubble_2_fill,
color: TeacherTheme.cyberCyan, size: 18),
SizedBox(width: 8),
Text(
'استفسارات الطلبة والمحادثات المباشرة (غرفة الشكوك) 💬',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w800, color: Colors.white),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: Colors.white),
),
],
),
@@ -444,7 +530,10 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
decoration: BoxDecoration(
color: TeacherTheme.surfaceDark,
borderRadius: BorderRadius.circular(14),
border: Border.all(color: isCompleted ? TeacherTheme.surfaceBorder : TeacherTheme.emeraldPrimary.withOpacity(0.3)),
border: Border.all(
color: isCompleted
? TeacherTheme.surfaceBorder
: TeacherTheme.emeraldPrimary.withOpacity(0.3)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -455,13 +544,18 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
Expanded(
child: Text(
hw.title,
style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w800, color: Colors.white),
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w800,
color: Colors.white),
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: isCompleted ? const Color(0xFF1E293B) : TeacherTheme.emeraldPrimary.withOpacity(0.15),
color: isCompleted
? const Color(0xFF1E293B)
: TeacherTheme.emeraldPrimary.withOpacity(0.15),
borderRadius: BorderRadius.circular(6),
),
child: Text(
@@ -469,7 +563,9 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
style: TextStyle(
fontSize: 10.5,
fontWeight: FontWeight.w800,
color: isCompleted ? const Color(0xFF94A3B8) : TeacherTheme.emeraldPrimary,
color: isCompleted
? const Color(0xFF94A3B8)
: TeacherTheme.emeraldPrimary,
),
),
),
@@ -478,9 +574,15 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
const SizedBox(height: 6),
Row(
children: [
Text(hw.className, style: const TextStyle(fontSize: 11, color: Color(0xFF94A3B8))),
Text(hw.className,
style:
const TextStyle(fontSize: 11, color: Color(0xFF94A3B8))),
const Spacer(),
Text('المعدل: ${hw.averageScore}', style: const TextStyle(fontSize: 11, fontWeight: FontWeight.bold, color: TeacherTheme.royalGold)),
Text('المعدل: ${hw.averageScore}',
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.bold,
color: TeacherTheme.royalGold)),
],
),
],
@@ -496,7 +598,9 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
color: TeacherTheme.surfaceDark,
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: doubt.replied ? TeacherTheme.surfaceBorder : TeacherTheme.cyberCyan.withOpacity(0.5),
color: doubt.replied
? TeacherTheme.surfaceBorder
: TeacherTheme.cyberCyan.withOpacity(0.5),
),
),
child: Column(
@@ -517,7 +621,10 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
child: Center(
child: Text(
doubt.studentName.characters.first,
style: const TextStyle(color: TeacherTheme.cyberCyan, fontWeight: FontWeight.bold, fontSize: 13),
style: const TextStyle(
color: TeacherTheme.cyberCyan,
fontWeight: FontWeight.bold,
fontSize: 13),
),
),
),
@@ -525,13 +632,21 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(doubt.studentName, style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w800, fontSize: 12.5)),
Text(doubt.className, style: const TextStyle(color: Color(0xFF94A3B8), fontSize: 10.5)),
Text(doubt.studentName,
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.w800,
fontSize: 12.5)),
Text(doubt.className,
style: const TextStyle(
color: Color(0xFF94A3B8), fontSize: 10.5)),
],
),
],
),
Text(doubt.time, style: const TextStyle(color: Color(0xFF64748B), fontSize: 10.5)),
Text(doubt.time,
style: const TextStyle(
color: Color(0xFF64748B), fontSize: 10.5)),
],
),
const SizedBox(height: 10),
@@ -543,7 +658,8 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
),
child: Text(
doubt.question,
style: const TextStyle(color: Colors.white70, fontSize: 12, height: 1.4),
style: const TextStyle(
color: Colors.white70, fontSize: 12, height: 1.4),
),
),
const SizedBox(height: 10),
@@ -552,7 +668,8 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
children: [
if (doubt.replied)
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: TeacherTheme.emeraldPrimary.withOpacity(0.15),
borderRadius: BorderRadius.circular(6),
@@ -560,22 +677,32 @@ class _TeacherAssignmentsQnATabState extends State<TeacherAssignmentsQnATab> {
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(CupertinoIcons.checkmark_alt_circle_fill, color: TeacherTheme.emeraldPrimary, size: 14),
Icon(CupertinoIcons.checkmark_alt_circle_fill,
color: TeacherTheme.emeraldPrimary, size: 14),
SizedBox(width: 4),
Text('تم الرد بصوت مسجل 🎧', style: TextStyle(color: TeacherTheme.emeraldPrimary, fontSize: 11, fontWeight: FontWeight.bold)),
Text('تم الرد بصوت مسجل 🎧',
style: TextStyle(
color: TeacherTheme.emeraldPrimary,
fontSize: 11,
fontWeight: FontWeight.bold)),
],
),
)
else
ElevatedButton.icon(
onPressed: () => _recordVoiceReply(doubt.id, doubt.studentName),
onPressed: () =>
_recordVoiceReply(doubt.id, doubt.studentName),
icon: const Icon(CupertinoIcons.mic_fill, size: 14),
label: const Text('تسجيل رد صوتي سقراطي 🎙️', style: TextStyle(fontSize: 11.5, fontWeight: FontWeight.w800)),
label: const Text('تسجيل رد صوتي سقراطي 🎙️',
style: TextStyle(
fontSize: 11.5, fontWeight: FontWeight.w800)),
style: ElevatedButton.styleFrom(
backgroundColor: TeacherTheme.emeraldPrimary,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
),
),
],
@@ -31,9 +31,10 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
context.read<TeacherMonetizationCubit>().loadMonetization();
}
void _showCliqPayoutDialog(BuildContext ctx, double availableBalance, String defaultAlias) {
void _showCliqPayoutDialog(
BuildContext ctx, double availableBalance, String defaultAlias) {
final TextEditingController cliqController = TextEditingController(
text: defaultAlias.isNotEmpty ? defaultAlias : '0798583052@CLIQ',
text: defaultAlias,
);
final TextEditingController amountController = TextEditingController(
text: availableBalance > 0 ? availableBalance.toInt().toString() : '50',
@@ -62,17 +63,22 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
children: [
const Row(
children: [
Icon(CupertinoIcons.bolt_fill, color: TeacherTheme.emeraldPrimary, size: 22),
Icon(CupertinoIcons.bolt_fill,
color: TeacherTheme.emeraldPrimary, size: 22),
SizedBox(width: 8),
Text(
'طلب سحب فوري عبر كليك (CliQ)',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w800, color: Colors.white),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w800,
color: Colors.white),
),
],
),
IconButton(
onPressed: () => Navigator.pop(bCtx),
icon: const Icon(CupertinoIcons.xmark_circle_fill, color: Color(0xFF64748B)),
icon: const Icon(CupertinoIcons.xmark_circle_fill,
color: Color(0xFF64748B)),
),
],
),
@@ -87,11 +93,15 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
style: const TextStyle(color: Colors.white, fontSize: 13),
decoration: InputDecoration(
labelText: 'معرّف كليك (CliQ Alias) أو رقم الهاتف',
labelStyle: const TextStyle(color: Color(0xFF94A3B8), fontSize: 12),
labelStyle:
const TextStyle(color: Color(0xFF94A3B8), fontSize: 12),
filled: true,
fillColor: const Color(0xFF1E293B),
prefixIcon: const Icon(CupertinoIcons.person_crop_circle, color: TeacherTheme.emeraldPrimary, size: 20),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none),
prefixIcon: const Icon(CupertinoIcons.person_crop_circle,
color: TeacherTheme.emeraldPrimary, size: 20),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none),
),
),
const SizedBox(height: 12),
@@ -101,11 +111,15 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
style: const TextStyle(color: Colors.white, fontSize: 13),
decoration: InputDecoration(
labelText: 'المبلغ المطلوب سحبه (دينار أردني)',
labelStyle: const TextStyle(color: Color(0xFF94A3B8), fontSize: 12),
labelStyle:
const TextStyle(color: Color(0xFF94A3B8), fontSize: 12),
filled: true,
fillColor: const Color(0xFF1E293B),
prefixIcon: const Icon(CupertinoIcons.money_dollar, color: TeacherTheme.emeraldPrimary, size: 20),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none),
prefixIcon: const Icon(CupertinoIcons.money_dollar,
color: TeacherTheme.emeraldPrimary, size: 20),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none),
),
),
const SizedBox(height: 18),
@@ -121,18 +135,21 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
Navigator.pop(bCtx);
ScaffoldMessenger.of(ctx).showSnackBar(
SnackBar(
content: Text('تم إدراج طلب السحب بمبلغ ${amountController.text} د.أ في طابور كليك بنجاح ⚡'),
content: Text(
'تم إدراج طلب السحب بمبلغ ${amountController.text} د.أ في طابور كليك بنجاح ⚡'),
backgroundColor: TeacherTheme.emeraldPrimary,
),
);
},
icon: const Icon(CupertinoIcons.paperplane_fill, size: 16),
label: const Text('تأكيد وإرسال لطابور السحب الفوري (CliQ Queue)'),
label:
const Text('تأكيد وإرسال لطابور السحب الفوري (CliQ Queue)'),
style: ElevatedButton.styleFrom(
backgroundColor: TeacherTheme.emeraldPrimary,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
),
],
@@ -142,7 +159,8 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
}
void _showIbanPayoutDialog(BuildContext ctx, double availableBalance) {
final TextEditingController ibanController = TextEditingController(text: 'JO94MEPA0000000000000000000000');
final TextEditingController ibanController =
TextEditingController(text: 'JO94MEPA0000000000000000000000');
final TextEditingController amountController = TextEditingController(
text: availableBalance > 0 ? availableBalance.toInt().toString() : '100',
);
@@ -170,17 +188,22 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
children: [
const Row(
children: [
Icon(CupertinoIcons.building_2_fill, color: TeacherTheme.cyberCyan, size: 22),
Icon(CupertinoIcons.building_2_fill,
color: TeacherTheme.cyberCyan, size: 22),
SizedBox(width: 8),
Text(
'طلب تحويل بنكي رسمي (IBAN Transfer)',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w800, color: Colors.white),
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w800,
color: Colors.white),
),
],
),
IconButton(
onPressed: () => Navigator.pop(bCtx),
icon: const Icon(CupertinoIcons.xmark_circle_fill, color: Color(0xFF64748B)),
icon: const Icon(CupertinoIcons.xmark_circle_fill,
color: Color(0xFF64748B)),
),
],
),
@@ -195,11 +218,15 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
style: const TextStyle(color: Colors.white, fontSize: 13),
decoration: InputDecoration(
labelText: 'رقم الحساب المصرفي الدولي (IBAN)',
labelStyle: const TextStyle(color: Color(0xFF94A3B8), fontSize: 12),
labelStyle:
const TextStyle(color: Color(0xFF94A3B8), fontSize: 12),
filled: true,
fillColor: const Color(0xFF1E293B),
prefixIcon: const Icon(CupertinoIcons.creditcard, color: TeacherTheme.cyberCyan, size: 20),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none),
prefixIcon: const Icon(CupertinoIcons.creditcard,
color: TeacherTheme.cyberCyan, size: 20),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none),
),
),
const SizedBox(height: 12),
@@ -209,11 +236,15 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
style: const TextStyle(color: Colors.white, fontSize: 13),
decoration: InputDecoration(
labelText: 'المبلغ المطلوب تحويله (دينار أردني)',
labelStyle: const TextStyle(color: Color(0xFF94A3B8), fontSize: 12),
labelStyle:
const TextStyle(color: Color(0xFF94A3B8), fontSize: 12),
filled: true,
fillColor: const Color(0xFF1E293B),
prefixIcon: const Icon(CupertinoIcons.money_dollar, color: TeacherTheme.cyberCyan, size: 20),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide.none),
prefixIcon: const Icon(CupertinoIcons.money_dollar,
color: TeacherTheme.cyberCyan, size: 20),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none),
),
),
const SizedBox(height: 18),
@@ -229,7 +260,8 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
Navigator.pop(bCtx);
ScaffoldMessenger.of(ctx).showSnackBar(
SnackBar(
content: Text('تم تسجيل أمر التحويل المصرفي بمبلغ ${amountController.text} د.أ بنجاح 🏦'),
content: Text(
'تم تسجيل أمر التحويل المصرفي بمبلغ ${amountController.text} د.أ بنجاح 🏦'),
backgroundColor: TeacherTheme.cyberCyan,
),
);
@@ -240,7 +272,8 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
backgroundColor: TeacherTheme.cyberCyan,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
),
],
@@ -274,11 +307,15 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
children: [
Text(
'تصنيف قاعدة الطلبة (المؤسسي مقابل الخارجي) 👥',
style: TextStyle(fontSize: 13.5, fontWeight: FontWeight.w800, color: Colors.white),
style: TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w800,
color: Colors.white),
),
Text(
'تحديث الكشوفات: اليوم',
style: TextStyle(fontSize: 11, color: Color(0xFF64748B)),
style:
TextStyle(fontSize: 11, color: Color(0xFF64748B)),
),
],
),
@@ -288,7 +325,9 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
decoration: BoxDecoration(
color: const Color(0xFF064E3B).withOpacity(0.3),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: TeacherTheme.emeraldPrimary.withOpacity(0.3)),
border: Border.all(
color:
TeacherTheme.emeraldPrimary.withOpacity(0.3)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -298,18 +337,26 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
children: [
Text(
'طلبة الثقافة العسكرية والمدارس الشريكة',
style: TextStyle(fontSize: 12.5, fontWeight: FontWeight.w700, color: Colors.white),
style: TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w700,
color: Colors.white),
),
SizedBox(height: 2),
Text(
'مشمولون برسم (0.00 د.أ) ضمن العقد المؤسسي',
style: TextStyle(fontSize: 11, color: TeacherTheme.emeraldLight),
style: TextStyle(
fontSize: 11,
color: TeacherTheme.emeraldLight),
),
],
),
Text(
'${state.institutionalStudents.toInt()} طالباً 🎖️',
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w900, color: TeacherTheme.emeraldLight),
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w900,
color: TeacherTheme.emeraldLight),
),
],
),
@@ -320,7 +367,8 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
decoration: BoxDecoration(
color: const Color(0xFF0369A1).withOpacity(0.2),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFF0284C7).withOpacity(0.3)),
border: Border.all(
color: const Color(0xFF0284C7).withOpacity(0.3)),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -330,18 +378,26 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
children: [
Text(
'الطلبة المستقلون (سوق صَقِل المفتوح)',
style: TextStyle(fontSize: 12.5, fontWeight: FontWeight.w700, color: Colors.white),
style: TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w700,
color: Colors.white),
),
SizedBox(height: 2),
Text(
'مشتركون عبر نظام الدفع الفوري كليك (CliQ)',
style: TextStyle(fontSize: 11, color: TeacherTheme.cyberCyan),
style: TextStyle(
fontSize: 11,
color: TeacherTheme.cyberCyan),
),
],
),
Text(
'${state.studentSubscribers.toInt()} طالباً 💳',
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w900, color: TeacherTheme.cyberCyan),
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w900,
color: TeacherTheme.cyberCyan),
),
],
),
@@ -361,7 +417,8 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: TeacherTheme.emeraldPrimary.withOpacity(0.4)),
border: Border.all(
color: TeacherTheme.emeraldPrimary.withOpacity(0.4)),
boxShadow: [
BoxShadow(
color: TeacherTheme.emeraldPrimary.withOpacity(0.15),
@@ -385,14 +442,18 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
padding: const EdgeInsets.symmetric(
horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.3),
borderRadius: BorderRadius.circular(6),
),
child: const Text(
'CliQ & IBAN',
style: TextStyle(fontSize: 11, color: TeacherTheme.emeraldLight, fontWeight: FontWeight.w700),
style: TextStyle(
fontSize: 11,
color: TeacherTheme.emeraldLight,
fontWeight: FontWeight.w700),
),
)
],
@@ -411,7 +472,8 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
const SizedBox(height: 4),
Text(
'إجمالي المبالغ المسحوبة سابقاً: ${state.totalWithdrawn.toStringAsFixed(0)} د.أ',
style: const TextStyle(fontSize: 11.5, color: Color(0xFF6EE7B7)),
style: const TextStyle(
fontSize: 11.5, color: Color(0xFF6EE7B7)),
),
],
const SizedBox(height: 14),
@@ -420,32 +482,43 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
Expanded(
child: ElevatedButton.icon(
onPressed: () {
_showCliqPayoutDialog(context, state.availableBalance, state.cliqAlias);
_showCliqPayoutDialog(context,
state.availableBalance, state.cliqAlias);
},
icon: const Icon(CupertinoIcons.bolt_fill, size: 16),
icon:
const Icon(CupertinoIcons.bolt_fill, size: 16),
label: const Text(
'سحب فوري عبر كليك (CliQ)',
style: TextStyle(fontSize: 12, fontWeight: FontWeight.w800),
style: TextStyle(
fontSize: 12, fontWeight: FontWeight.w800),
),
style: ElevatedButton.styleFrom(
backgroundColor: TeacherTheme.emeraldPrimary,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(vertical: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
),
),
const SizedBox(width: 8),
OutlinedButton.icon(
onPressed: () {
_showIbanPayoutDialog(context, state.availableBalance);
_showIbanPayoutDialog(
context, state.availableBalance);
},
icon: const Icon(CupertinoIcons.building_2_fill, size: 16, color: Colors.white),
label: const Text('تحويل بنكي', style: TextStyle(fontSize: 12, color: Colors.white)),
icon: const Icon(CupertinoIcons.building_2_fill,
size: 16, color: Colors.white),
label: const Text('تحويل بنكي',
style:
TextStyle(fontSize: 12, color: Colors.white)),
style: OutlinedButton.styleFrom(
side: BorderSide(color: Colors.white.withOpacity(0.3)),
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 12),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
side: BorderSide(
color: Colors.white.withOpacity(0.3)),
padding: const EdgeInsets.symmetric(
vertical: 12, horizontal: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
),
),
],
@@ -495,33 +568,22 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
TeacherTheme.cyberCyan,
),
const Divider(color: TeacherTheme.surfaceBorder, height: 24),
const Divider(
color: TeacherTheme.surfaceBorder, height: 24),
// Subscribers Slider
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'محاكي المشتركين خارج الثقافة:',
style: TextStyle(fontSize: 12, color: Color(0xFF94A3B8)),
),
Text(
'${state.studentSubscribers.toInt()} طالب مشترك',
style: const TextStyle(fontSize: 13.5, fontWeight: FontWeight.w800, color: TeacherTheme.emeraldLight),
),
const Text('المشتركون الفعليون:',
style: TextStyle(
fontSize: 12, color: Color(0xFF94A3B8))),
Text('${state.studentSubscribers.toInt()} طالب مشترك',
style: const TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w800,
color: TeacherTheme.emeraldLight)),
],
),
Slider(
value: state.studentSubscribers,
min: 50,
max: 2000,
divisions: 39,
activeColor: TeacherTheme.emeraldPrimary,
inactiveColor: TeacherTheme.surfaceBorder,
onChanged: (val) {
context.read<TeacherMonetizationCubit>().updateSubscribers(val);
},
),
],
),
),
@@ -530,19 +592,26 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
// Active Courses Selling
const Text(
'الدورات المنشورة في سوق صَقِل الخارجي:',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w800, color: Colors.white),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: Colors.white),
),
const SizedBox(height: 10),
if (state.courses.isNotEmpty)
...state.courses.map((course) => Padding(
padding: const EdgeInsets.only(bottom: 8),
child: _courseEarningCard(
title: course.title,
students: course.militaryStudents + course.externalSubscribers,
revenue: '${(course.priceJod * (course.externalSubscribers > 0 ? course.externalSubscribers : 1) * 0.55).toStringAsFixed(0)} د.أ',
),
)).toList()
...state.courses
.map((course) => Padding(
padding: const EdgeInsets.only(bottom: 8),
child: _courseEarningCard(
title: course.title,
students: course.militaryStudents +
course.externalSubscribers,
revenue:
'${(course.priceJod * (course.externalSubscribers > 0 ? course.externalSubscribers : 1) * 0.55).toStringAsFixed(0)} د.أ',
),
))
.toList()
else ...[
_courseEarningCard(
title: 'الفيزياء والعلوم التطبيقية — الصف العاشر الأساسي',
@@ -556,11 +625,15 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
const SizedBox(height: 18),
const Row(
children: [
Icon(CupertinoIcons.arrow_right_arrow_left, size: 16, color: TeacherTheme.emeraldPrimary),
Icon(CupertinoIcons.arrow_right_arrow_left,
size: 16, color: TeacherTheme.emeraldPrimary),
SizedBox(width: 8),
Text(
'سجل عمليات وسحوبات كليك (CliQ & IBAN Queue):',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w800, color: Colors.white),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w800,
color: Colors.white),
),
],
),
@@ -583,7 +656,9 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
color: TeacherTheme.surfaceCard,
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isDone ? TeacherTheme.emeraldPrimary.withOpacity(0.3) : TeacherTheme.surfaceBorder,
color: isDone
? TeacherTheme.emeraldPrimary.withOpacity(0.3)
: TeacherTheme.surfaceBorder,
),
),
child: Row(
@@ -592,9 +667,13 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
Row(
children: [
Icon(
isDone ? CupertinoIcons.checkmark_circle_fill : CupertinoIcons.clock_fill,
isDone
? CupertinoIcons.checkmark_circle_fill
: CupertinoIcons.clock_fill,
size: 20,
color: isDone ? TeacherTheme.emeraldPrimary : TeacherTheme.royalGold,
color: isDone
? TeacherTheme.emeraldPrimary
: TeacherTheme.royalGold,
),
const SizedBox(width: 10),
Column(
@@ -602,12 +681,16 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
children: [
Text(
payout.cliqAlias,
style: const TextStyle(fontSize: 12.5, fontWeight: FontWeight.w700, color: Colors.white),
style: const TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w700,
color: Colors.white),
),
const SizedBox(height: 2),
Text(
payout.requestedAt,
style: const TextStyle(fontSize: 11, color: Color(0xFF64748B)),
style:
const TextStyle(fontSize: 11, color: Color(0xFF64748B)),
),
],
),
@@ -621,14 +704,18 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
style: TextStyle(
fontSize: 13.5,
fontWeight: FontWeight.w900,
color: isDone ? TeacherTheme.emeraldPrimary : TeacherTheme.royalGold,
color: isDone
? TeacherTheme.emeraldPrimary
: TeacherTheme.royalGold,
),
),
Text(
isDone ? 'تم التحويل بنجاح' : 'في طابور المقاصة ⚡',
style: TextStyle(
fontSize: 10,
color: isDone ? TeacherTheme.emeraldLight : TeacherTheme.royalGold,
color: isDone
? TeacherTheme.emeraldLight
: TeacherTheme.royalGold,
),
),
],
@@ -644,12 +731,19 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
children: [
Row(
children: [
Container(width: 8, height: 8, decoration: BoxDecoration(color: color, shape: BoxShape.circle)),
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(label,
style: const TextStyle(fontSize: 12.5, color: Colors.white)),
],
),
Text(amount, style: TextStyle(fontSize: 13.5, fontWeight: FontWeight.w900, color: color)),
Text(amount,
style: TextStyle(
fontSize: 13.5, fontWeight: FontWeight.w900, color: color)),
],
);
}
@@ -673,17 +767,28 @@ class _TeacherMonetizationTabState extends State<TeacherMonetizationTab> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w800, color: Colors.white)),
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))),
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: TeacherTheme.emeraldPrimary)),
const Text('صافي أرباحك', style: TextStyle(fontSize: 10, color: Color(0xFF64748B))),
Text(revenue,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w900,
color: TeacherTheme.emeraldPrimary)),
const Text('صافي أرباحك',
style: TextStyle(fontSize: 10, color: Color(0xFF64748B))),
],
),
],
@@ -20,22 +20,24 @@ class TeacherReputationScorecardTab extends StatefulWidget {
const TeacherReputationScorecardTab({super.key});
@override
State<TeacherReputationScorecardTab> createState() => _TeacherReputationScorecardTabState();
State<TeacherReputationScorecardTab> createState() =>
_TeacherReputationScorecardTabState();
}
class _TeacherReputationScorecardTabState extends State<TeacherReputationScorecardTab> {
class _TeacherReputationScorecardTabState
extends State<TeacherReputationScorecardTab> {
final TeacherRepository _repository = TeacherRepository();
bool _isLoading = true;
int _studentsEnrolled = 83;
double _compositeMerit = 96.5;
double _starRating = 4.92;
double _successRate = 98.2;
double _curriculumAlignment = 0.96;
double _socraticInteraction = 0.89;
double _audioClarity = 0.94;
double _cognitiveFocus = 0.92;
String _reputationTier = 'معلم نخبوي معتمد 💎';
String _aiGuidance = 'توجيه الذكاء الاصطناعي الأسبوعي: نسبة الالتزام الوزاري ممتازة (96%). يُوصى بإضافة وقفة سقراطية استنتاجية في الدقيقة 14 من الدرس القادم لتعزيز تفاعل الطلبة.';
int _studentsEnrolled = 0;
double _compositeMerit = 0;
double _starRating = 0;
double _successRate = 0;
double _curriculumAlignment = 0;
double _socraticInteraction = 0;
double _audioClarity = 0;
double _cognitiveFocus = 0;
String _reputationTier = 'بانتظار بيانات الأداء';
String _aiGuidance =
'ستظهر التوصية بعد توفر حصص محللة وتفاعل فعلي من الطلبة.';
@override
void initState() {
@@ -48,28 +50,38 @@ class _TeacherReputationScorecardTabState extends State<TeacherReputationScoreca
final data = await _repository.getMyReputation();
if (mounted) {
setState(() {
_studentsEnrolled = (data['total_students_enrolled'] as num?)?.toInt() ?? 83;
_compositeMerit = (data['composite_merit_score'] as num?)?.toDouble() ?? 96.5;
_starRating = (data['star_equivalent'] as num?)?.toDouble() ?? 4.92;
_successRate = (data['success_rate_percentage'] as num?)?.toDouble() ?? 98.2;
_curriculumAlignment = ((data['curriculum_alignment_pct'] as num?)?.toDouble() ?? 96.0) / 100.0;
_socraticInteraction = ((data['socratic_interaction_pct'] as num?)?.toDouble() ?? 89.0) / 100.0;
_audioClarity = ((data['audio_clarity_pct'] as num?)?.toDouble() ?? 94.0) / 100.0;
_cognitiveFocus = ((data['cognitive_focus_pct'] as num?)?.toDouble() ?? 92.0) / 100.0;
_reputationTier = data['reputation_tier']?.toString() ?? 'معلم نخبوي معتمد 💎';
_studentsEnrolled =
(data['total_students_enrolled'] as num?)?.toInt() ?? 0;
_compositeMerit =
(data['composite_merit_score'] as num?)?.toDouble() ?? 0;
_starRating = (data['star_equivalent'] as num?)?.toDouble() ?? 0;
_successRate =
(data['success_rate_percentage'] as num?)?.toDouble() ?? 0;
_curriculumAlignment =
((data['curriculum_alignment_pct'] as num?)?.toDouble() ?? 0) /
100.0;
_socraticInteraction =
((data['socratic_interaction_pct'] as num?)?.toDouble() ?? 0) /
100.0;
_audioClarity =
((data['audio_clarity_pct'] as num?)?.toDouble() ?? 0) / 100.0;
_cognitiveFocus =
((data['cognitive_focus_pct'] as num?)?.toDouble() ?? 0) / 100.0;
_reputationTier =
data['reputation_tier']?.toString() ?? 'بانتظار بيانات الأداء';
_aiGuidance = data['ai_recommendation']?.toString() ?? _aiGuidance;
_isLoading = false;
});
}
} catch (_) {
if (mounted) setState(() => _isLoading = false);
// Keep the honest zero-state when no reputation data is available.
}
}
@override
Widget build(BuildContext context) {
final authState = context.watch<TeacherAuthCubit>().state;
final teacherName = (authState is TeacherAuthenticated) ? authState.profile.name : 'المهندس حمزة الغويريين';
final teacherName =
(authState is TeacherAuthenticated) ? authState.profile.name : 'المعلم';
return RefreshIndicator(
onRefresh: _loadReputation,
@@ -91,14 +103,16 @@ class _TeacherReputationScorecardTabState extends State<TeacherReputationScoreca
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: const Color(0xFF8B5CF6).withOpacity(0.5)),
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),
child: Icon(CupertinoIcons.rosette,
size: 36, color: Colors.white),
),
const SizedBox(height: 12),
Text(
@@ -111,7 +125,8 @@ class _TeacherReputationScorecardTabState extends State<TeacherReputationScoreca
),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: TeacherTheme.emeraldPrimary.withOpacity(0.2),
borderRadius: BorderRadius.circular(20),
@@ -129,9 +144,15 @@ class _TeacherReputationScorecardTabState extends State<TeacherReputationScoreca
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_ScoreStat(label: 'التقييم العام', value: '${_starRating.toStringAsFixed(2)} / 5.0'),
_ScoreStat(label: 'الطلاب المخدومون', value: '$_studentsEnrolled طالب'),
_ScoreStat(label: 'نسبة النجاح', value: '${_successRate.toStringAsFixed(1)}%'),
_ScoreStat(
label: 'التقييم العام',
value: '${_starRating.toStringAsFixed(2)} / 5.0'),
_ScoreStat(
label: 'الطلاب المخدومون',
value: '$_studentsEnrolled طالب'),
_ScoreStat(
label: 'نسبة النجاح',
value: '${_successRate.toStringAsFixed(1)}%'),
],
),
],
@@ -159,13 +180,17 @@ class _TeacherReputationScorecardTabState extends State<TeacherReputationScoreca
),
),
const SizedBox(height: 14),
_metricProgress('الالتزام بالمنهاج والنتاجات الوزارية', _curriculumAlignment, TeacherTheme.emeraldPrimary),
_metricProgress('الالتزام بالمنهاج والنتاجات الوزارية',
_curriculumAlignment, TeacherTheme.emeraldPrimary),
const SizedBox(height: 12),
_metricProgress('التفاعل السقراطي وإثارة التفكير', _socraticInteraction, TeacherTheme.cyberCyan),
_metricProgress('التفاعل السقراطي وإثارة التفكير',
_socraticInteraction, TeacherTheme.cyberCyan),
const SizedBox(height: 12),
_metricProgress('الوضوح الصوتي وسلامة اللغة', _audioClarity, TeacherTheme.royalGold),
_metricProgress('الوضوح الصوتي وسلامة اللغة', _audioClarity,
TeacherTheme.royalGold),
const SizedBox(height: 12),
_metricProgress('إدارة الوقت والتركيز الإدراكي (20-25 دقيقة)', _cognitiveFocus, const Color(0xFF8B5CF6)),
_metricProgress('إدارة الوقت والتركيز الإدراكي (20-25 دقيقة)',
_cognitiveFocus, const Color(0xFF8B5CF6)),
],
),
),
@@ -182,12 +207,14 @@ class _TeacherReputationScorecardTabState extends State<TeacherReputationScoreca
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(CupertinoIcons.lightbulb_fill, color: TeacherTheme.royalGold, size: 20),
const Icon(CupertinoIcons.lightbulb_fill,
color: TeacherTheme.royalGold, size: 20),
const SizedBox(width: 10),
Expanded(
child: Text(
_aiGuidance,
style: const TextStyle(fontSize: 12, color: Color(0xFFCBD5E1), height: 1.4),
style: const TextStyle(
fontSize: 12, color: Color(0xFFCBD5E1), height: 1.4),
),
),
],
@@ -206,8 +233,11 @@ class _TeacherReputationScorecardTabState extends State<TeacherReputationScoreca
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)),
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),
@@ -235,9 +265,14 @@ class _ScoreStat extends StatelessWidget {
Widget build(BuildContext context) {
return Column(
children: [
Text(value, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w900, color: Colors.white)),
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))),
Text(label,
style: const TextStyle(fontSize: 11, color: Color(0xFF94A3B8))),
],
);
}
@@ -24,6 +24,103 @@ class TeacherStudioUploadTab extends StatefulWidget {
State<TeacherStudioUploadTab> createState() => _TeacherStudioUploadTabState();
}
class _CurriculumSelectors extends StatelessWidget {
final TeacherStudioState state;
final VoidCallback onLessonChanged;
const _CurriculumSelectors(
{required this.state, required this.onLessonChanged});
Map<String, dynamic> _map(dynamic value) =>
value is Map ? Map<String, dynamic>.from(value) : {};
String _name(dynamic value, String fallback) =>
value is Map ? value['name']?.toString() ?? fallback : fallback;
@override
Widget build(BuildContext context) {
final subjects = _map(state.curriculumTree[state.gradeKey]?['subjects']);
final semesters = _map(subjects[state.subjectKey]?['semesters']);
final units = _map(semesters[state.semesterKey]?['units']);
final lessons = units[state.unitKey]?['lessons'] is List
? units[state.unitKey]['lessons'] as List
: const [];
if (state.isCurriculumLoading)
return const Center(
child:
CupertinoActivityIndicator(color: TeacherTheme.emeraldPrimary));
if (state.curriculumTree.isEmpty)
return const Text('لم تُرجع واجهة المنهاج أي صفوف متاحة.',
style: TextStyle(color: Color(0xFF94A3B8)));
return Wrap(
spacing: 10,
runSpacing: 10,
children: [
_drop(
'الصف',
state.gradeKey,
state.curriculumTree.entries
.map((e) => MapEntry(e.key, _name(e.value, e.key))),
(v) => context.read<TeacherStudioCubit>().selectGrade(v)),
_drop(
'المبحث',
state.subjectKey,
subjects.entries.map((e) => MapEntry(e.key, _name(e.value, e.key))),
(v) => context.read<TeacherStudioCubit>().selectSubject(v)),
_drop(
'الفصل',
state.semesterKey,
semesters.entries
.map((e) => MapEntry(e.key, _name(e.value, e.key))),
(v) => context.read<TeacherStudioCubit>().selectSemester(v)),
_drop(
'الوحدة',
state.unitKey,
units.entries.map((e) => MapEntry(e.key, _name(e.value, e.key))),
(v) => context.read<TeacherStudioCubit>().selectUnit(v)),
_drop(
'الدرس',
state.lessonKey,
lessons.whereType<Map>().map((e) => MapEntry(
e['id']?.toString() ?? '', e['title']?.toString() ?? '')), (v) {
context.read<TeacherStudioCubit>().selectLesson(v);
onLessonChanged();
}),
],
);
}
Widget _drop(
String label,
String value,
Iterable<MapEntry<String, String>> entries,
ValueChanged<String> changed) {
final items = entries.where((e) => e.key.isNotEmpty).toList();
return SizedBox(
width: 260,
child: DropdownButtonFormField<String>(
value: items.any((e) => e.key == value) ? value : null,
isExpanded: true,
dropdownColor: const Color(0xFF161F30),
style: const TextStyle(color: Colors.white, fontSize: 12),
decoration: InputDecoration(
labelText: label,
labelStyle: const TextStyle(color: Color(0xFF94A3B8)),
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(10))),
items: items
.map((e) => DropdownMenuItem(
value: e.key,
child: Text(e.value, overflow: TextOverflow.ellipsis)))
.toList(),
onChanged: items.isEmpty
? null
: (v) {
if (v != null) changed(v);
},
),
);
}
}
class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
late final TextEditingController _titleController;
@@ -33,6 +130,13 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
_titleController = TextEditingController(
text: context.read<TeacherStudioCubit>().state.lessonTitle,
);
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (!mounted) return;
await context.read<TeacherStudioCubit>().loadCurriculum();
if (mounted)
_titleController.text =
context.read<TeacherStudioCubit>().state.lessonTitle;
});
}
@override
@@ -42,21 +146,23 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
}
Future<void> _pickVideo(BuildContext ctx) async {
final result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: const ['mp4', 'mov', 'webm'],
withData: kIsWeb,
);
if (!mounted || result == null || result.files.isEmpty) return;
final file = result.files.single;
ctx.read<TeacherStudioCubit>().selectVideoFile(
try {
final result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: const ['mp4', 'mov', 'webm'],
withData: kIsWeb);
if (!mounted || result == null || result.files.isEmpty) return;
final file = result.files.single;
ctx.read<TeacherStudioCubit>().selectVideoFile(
file.name,
file.size / (1024 * 1024),
ctx.read<TeacherStudioCubit>().state.durationMinutes,
path: file.path,
bytes: file.bytes,
);
bytes: file.bytes);
} catch (e) {
if (mounted)
ctx.read<TeacherStudioCubit>().reportError('تعذر فتح الملف: $e');
}
}
@override
@@ -80,14 +186,16 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: TeacherTheme.emeraldPrimary.withOpacity(0.4)),
border: Border.all(
color: TeacherTheme.emeraldPrimary.withOpacity(0.4)),
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(CupertinoIcons.sparkles, color: TeacherTheme.emeraldLight, size: 20),
Icon(CupertinoIcons.sparkles,
color: TeacherTheme.emeraldLight, size: 20),
SizedBox(width: 8),
Text(
'بوابة جودة حصص الأستوديو الرقمية (صَقِل 2.0)',
@@ -135,19 +243,35 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
const SizedBox(height: 8),
TextField(
controller: _titleController,
style: const TextStyle(color: Colors.white, fontSize: 13.5),
onChanged: (val) => context.read<TeacherStudioCubit>().updateLessonTitle(val),
style:
const TextStyle(color: Colors.white, fontSize: 13.5),
onChanged: (val) => context
.read<TeacherStudioCubit>()
.updateLessonTitle(val),
decoration: InputDecoration(
filled: true,
fillColor: const Color(0xFF161F30),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide: const BorderSide(color: Color(0xFF334155)),
borderSide:
const BorderSide(color: Color(0xFF334155)),
),
),
),
const SizedBox(height: 16),
_CurriculumSelectors(
state: state,
onLessonChanged: () {
final title = context
.read<TeacherStudioCubit>()
.state
.lessonTitle;
_titleController.text = title;
},
),
const SizedBox(height: 16),
// Video Attachment Dropzone Card
const Text(
'ملف فيديو الحصة للشرح الرقمي (1080p):',
@@ -164,7 +288,9 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
color: const Color(0xFF161F30),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: state.selectedFileName != null ? TeacherTheme.emeraldPrimary : const Color(0xFF334155),
color: state.selectedFileName != null
? TeacherTheme.emeraldPrimary
: const Color(0xFF334155),
width: state.selectedFileName != null ? 1.5 : 1.0,
),
),
@@ -174,12 +300,17 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
width: 44,
height: 44,
decoration: BoxDecoration(
color: (state.selectedFileName != null ? TeacherTheme.emeraldPrimary : const Color(0xFF334155)).withOpacity(0.2),
color: (state.selectedFileName != null
? TeacherTheme.emeraldPrimary
: const Color(0xFF334155))
.withOpacity(0.2),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
CupertinoIcons.videocam_fill,
color: state.selectedFileName != null ? TeacherTheme.emeraldLight : const Color(0xFF94A3B8),
color: state.selectedFileName != null
? TeacherTheme.emeraldLight
: const Color(0xFF94A3B8),
size: 24,
),
),
@@ -189,29 +320,44 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
state.selectedFileName ?? 'لم يتم اختيار ملف فيديو بعد',
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13),
state.selectedFileName ??
'لم يتم اختيار ملف فيديو بعد',
style: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 13),
),
const SizedBox(height: 2),
Text(
state.selectedFileName != null
? 'الحجم: ${state.selectedFileSizeMb ?? 48.2} MB • المدة المستخرجة: ${state.durationMinutes.toStringAsFixed(1)} دقيقة'
? 'الحجم: ${state.selectedFileSizeMb?.toStringAsFixed(2)} MB • أدخل المدة الفعلية أدناه'
: 'الصيغ المدعومة: MP4, MOV, WebM (جودة 1080p)',
style: const TextStyle(color: Color(0xFF94A3B8), fontSize: 11),
style: const TextStyle(
color: Color(0xFF94A3B8), fontSize: 11),
),
],
),
),
ElevatedButton.icon(
onPressed: () => _pickVideo(context),
icon: const Icon(CupertinoIcons.folder_badge_plus, size: 15),
label: Text(state.selectedFileName != null ? 'تغيير الملف' : 'اختيار ملف 📁'),
icon: const Icon(CupertinoIcons.folder_badge_plus,
size: 15),
label: Text(state.selectedFileName != null
? 'تغيير الملف'
: 'اختيار ملف 📁'),
style: ElevatedButton.styleFrom(
backgroundColor: state.selectedFileName != null ? const Color(0xFF1E293B) : TeacherTheme.emeraldPrimary,
foregroundColor: state.selectedFileName != null ? Colors.white : Colors.black,
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
textStyle: const TextStyle(fontSize: 11.5, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
backgroundColor: state.selectedFileName != null
? const Color(0xFF1E293B)
: TeacherTheme.emeraldPrimary,
foregroundColor: state.selectedFileName != null
? Colors.white
: Colors.black,
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 8),
textStyle: const TextStyle(
fontSize: 11.5, fontWeight: FontWeight.bold),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8)),
),
),
],
@@ -232,7 +378,8 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
),
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
padding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: isDurationGood
? TeacherTheme.emeraldPrimary.withOpacity(0.2)
@@ -244,7 +391,9 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w900,
color: isDurationGood ? TeacherTheme.emeraldLight : TeacherTheme.crimsonRed,
color: isDurationGood
? TeacherTheme.emeraldLight
: TeacherTheme.crimsonRed,
),
),
),
@@ -255,7 +404,9 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
min: 5.0,
max: 45.0,
divisions: 40,
activeColor: isDurationGood ? TeacherTheme.emeraldPrimary : TeacherTheme.crimsonRed,
activeColor: isDurationGood
? TeacherTheme.emeraldPrimary
: TeacherTheme.crimsonRed,
inactiveColor: TeacherTheme.surfaceBorder,
onChanged: (val) {
context.read<TeacherStudioCubit>().updateDuration(val);
@@ -283,7 +434,9 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
? CupertinoIcons.check_mark_circled_solid
: CupertinoIcons.exclamationmark_triangle_fill,
size: 18,
color: isDurationGood ? TeacherTheme.emeraldLight : TeacherTheme.crimsonRed,
color: isDurationGood
? TeacherTheme.emeraldLight
: TeacherTheme.crimsonRed,
),
const SizedBox(width: 8),
Expanded(
@@ -293,7 +446,9 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
: 'تحذير إدراكي: الشرح يتجاوز 25 دقيقة! ينخفض استيعاب الطلبة تدريجياً، يرجى اختصار المقطع أو تقسيمه.',
style: TextStyle(
fontSize: 11.5,
color: isDurationGood ? const Color(0xFF6EE7B7) : const Color(0xFFFCA5A5),
color: isDurationGood
? const Color(0xFF6EE7B7)
: const Color(0xFFFCA5A5),
),
),
),
@@ -307,20 +462,25 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
onPressed: state.isAuditing
? null
: () {
context.read<TeacherStudioCubit>().runQualityGate();
context
.read<TeacherStudioCubit>()
.runQualityGate();
},
icon: state.isAuditing
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2, color: Colors.black),
child: CircularProgressIndicator(
strokeWidth: 2, color: Colors.black),
)
: const Icon(CupertinoIcons.checkmark_shield_fill, size: 18),
: const Icon(CupertinoIcons.checkmark_shield_fill,
size: 18),
label: Text(
state.isAuditing
? 'جاري التدقيق التربوي عبر محرك صَقِل...'
: 'فحص الحصة عبر بوابة الجودة الذكية (عتبة 85%) ⚡',
style: const TextStyle(fontSize: 13.5, fontWeight: FontWeight.w800),
style: const TextStyle(
fontSize: 13.5, fontWeight: FontWeight.w800),
),
style: ElevatedButton.styleFrom(
backgroundColor: TeacherTheme.emeraldPrimary,
@@ -331,13 +491,22 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
),
),
),
if (state.errorMessage != null) ...[
const SizedBox(height: 10),
Text(state.errorMessage!,
style: const TextStyle(
color: TeacherTheme.crimsonRed,
fontSize: 12,
fontWeight: FontWeight.w700)),
],
],
),
),
const SizedBox(height: 16),
// Quality Audit Result Card
if (state.auditResult != null) _buildAuditResultCard(context, state, state.auditResult!),
if (state.auditResult != null)
_buildAuditResultCard(context, state, state.auditResult!),
],
),
);
@@ -345,7 +514,8 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
);
}
Widget _buildAuditResultCard(BuildContext context, TeacherStudioState state, TeacherLessonAuditModel res) {
Widget _buildAuditResultCard(BuildContext context, TeacherStudioState state,
TeacherLessonAuditModel res) {
final int score = res.qualityScore;
final bool isApproved = score >= 85;
@@ -355,7 +525,9 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
color: TeacherTheme.surfaceCard,
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: isApproved ? TeacherTheme.emeraldPrimary : TeacherTheme.crimsonRed,
color: isApproved
? TeacherTheme.emeraldPrimary
: TeacherTheme.crimsonRed,
width: 1.5,
),
),
@@ -368,23 +540,32 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
Row(
children: [
Icon(
isApproved ? CupertinoIcons.checkmark_seal_fill : CupertinoIcons.xmark_seal_fill,
color: isApproved ? TeacherTheme.emeraldPrimary : TeacherTheme.crimsonRed,
isApproved
? CupertinoIcons.checkmark_seal_fill
: CupertinoIcons.xmark_seal_fill,
color: isApproved
? TeacherTheme.emeraldPrimary
: TeacherTheme.crimsonRed,
size: 22,
),
const SizedBox(width: 8),
Text(
isApproved ? 'جاهزة لرفع الملف والتحليل' : 'بيانات الحصة بحاجة للمراجعة',
isApproved
? 'جاهزة لرفع الملف والتحليل'
: 'بيانات الحصة بحاجة للمراجعة',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w900,
color: isApproved ? TeacherTheme.emeraldPrimary : TeacherTheme.crimsonRed,
color: isApproved
? TeacherTheme.emeraldPrimary
: TeacherTheme.crimsonRed,
),
),
],
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
padding:
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: isApproved
? TeacherTheme.emeraldPrimary.withOpacity(0.2)
@@ -396,7 +577,9 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w900,
color: isApproved ? TeacherTheme.emeraldLight : TeacherTheme.crimsonRed,
color: isApproved
? TeacherTheme.emeraldLight
: TeacherTheme.crimsonRed,
),
),
),
@@ -419,7 +602,8 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
const SizedBox(height: 6),
_rowMetric('نقاء الصوت ومخارج الحروف:', res.audioClarity),
const SizedBox(height: 6),
_rowMetric('الفواصل السقراطية التفاعلية:', '${res.socraticStopsCount} محطات تفكيرية إلزامية'),
_rowMetric('الفواصل السقراطية التفاعلية:',
'${res.socraticStopsCount} محطات تفكيرية إلزامية'),
const SizedBox(height: 16),
if (isApproved) ...[
@@ -430,11 +614,19 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(width: 18, height: 18, child: CircularProgressIndicator(color: TeacherTheme.emeraldPrimary, strokeWidth: 2)),
SizedBox(
width: 18,
height: 18,
child: CircularProgressIndicator(
color: TeacherTheme.emeraldPrimary,
strokeWidth: 2)),
SizedBox(width: 12),
Text(
'جاري رفع الفيديو وبدء معالجة HLS والتحليل السقراطي... ⏳',
style: TextStyle(color: TeacherTheme.emeraldLight, fontSize: 12.5, fontWeight: FontWeight.bold),
style: TextStyle(
color: TeacherTheme.emeraldLight,
fontSize: 12.5,
fontWeight: FontWeight.bold),
),
],
),
@@ -449,20 +641,26 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
),
child: Row(
children: [
const Icon(CupertinoIcons.checkmark_seal_fill, color: TeacherTheme.emeraldLight, size: 22),
const Icon(CupertinoIcons.checkmark_seal_fill,
color: TeacherTheme.emeraldLight, size: 22),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
state.uploadSuccessMessage ?? 'تم رفع ونشر الحصة بنجاح في المنهاج وسوق صَقِل! 🚀',
style: const TextStyle(color: Colors.white, fontSize: 13, fontWeight: FontWeight.w900),
state.uploadSuccessMessage ??
'تم رفع ونشر الحصة بنجاح في المنهاج وسوق صَقِل! 🚀',
style: const TextStyle(
color: Colors.white,
fontSize: 13,
fontWeight: FontWeight.w900),
),
const SizedBox(height: 2),
const Text(
'الحصة متاحة الآن لطلبة الصف العاشر وسوق التسييل التجاري 🎖️',
style: TextStyle(color: TeacherTheme.emeraldLight, fontSize: 11),
style: TextStyle(
color: TeacherTheme.emeraldLight, fontSize: 11),
),
],
),
@@ -476,12 +674,15 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
context.read<TeacherStudioCubit>().uploadAndPublishLesson();
},
icon: const Icon(CupertinoIcons.cloud_upload_fill, size: 18),
label: const Text('رفع ونشر الحصة في المنهاج وسوق التسييل 🚀', style: TextStyle(fontSize: 13.5, fontWeight: FontWeight.w900)),
label: const Text('رفع ونشر الحصة في المنهاج وسوق التسييل 🚀',
style:
TextStyle(fontSize: 13.5, fontWeight: FontWeight.w900)),
style: ElevatedButton.styleFrom(
backgroundColor: TeacherTheme.emeraldDark,
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 46),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
elevation: 0,
),
),
@@ -495,8 +696,13 @@ class _TeacherStudioUploadTabState extends State<TeacherStudioUploadTab> {
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))),
Text(label,
style: const TextStyle(fontSize: 12, color: Color(0xFF94A3B8))),
Text('$value',
style: const TextStyle(
fontSize: 12.5,
fontWeight: FontWeight.w700,
color: Color(0xFFCBD5E1))),
],
);
}