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
@@ -63,7 +63,8 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
void _onSendOtp() {
final phone = _phoneController.text.trim();
if (phone.isEmpty || phone.length < 9) {
SaqelToast.showError(context, 'يرجى إدخال رقم هاتف أردني صحيح يبدأ بـ 07');
SaqelToast.showError(
context, 'يرجى إدخال رقم هاتف أردني صحيح يبدأ بـ 07');
return;
}
context.read<TeacherAuthCubit>().sendOtp(phone);
@@ -76,18 +77,22 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
SaqelToast.showError(context, 'يرجى إدخال رمز التحقق المكون من 6 أرقام');
return;
}
if (_fullNameController.text.trim().isEmpty || _selectedSubject == null || _schoolController.text.trim().isEmpty || _selectedGrades.isEmpty) {
SaqelToast.showError(context, 'أكمل الاسم والمادة والمدرسة وصفًا واحدًا على الأقل قبل التحقق');
if (_fullNameController.text.trim().isEmpty ||
_selectedSubject == null ||
_schoolController.text.trim().isEmpty ||
_selectedGrades.isEmpty) {
SaqelToast.showError(context,
'أكمل الاسم والمادة والمدرسة وصفًا واحدًا على الأقل قبل التحقق');
return;
}
context.read<TeacherAuthCubit>().verifyOtp(
phone,
otp,
fullName: _fullNameController.text.trim(),
specialization: _selectedSubject,
schoolName: _schoolController.text.trim(),
gradesTaught: _selectedGrades.toList(),
);
phone,
otp,
fullName: _fullNameController.text.trim(),
specialization: _selectedSubject,
schoolName: _schoolController.text.trim(),
gradesTaught: _selectedGrades.toList(),
);
}
@override
@@ -105,7 +110,8 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
child: BlocConsumer<TeacherAuthCubit, TeacherAuthState>(
listener: (context, state) {
if (state is TeacherAuthError) {
SaqelToast.showError(context, state.message, title: 'تنبيه المصادقة');
SaqelToast.showError(context, state.message,
title: 'تنبيه المصادقة');
} else if (state is TeacherAuthOtpSent) {
setState(() => _isEnteringOtp = true);
SaqelToast.showWhatsApp(
@@ -117,14 +123,16 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
},
builder: (context, state) {
final isLoading = state is TeacherAuthLoading;
final isOtpStep = _isEnteringOtp || state is TeacherAuthOtpSent;
final isOtpStep =
_isEnteringOtp || state is TeacherAuthOtpSent;
return Container(
padding: const EdgeInsets.all(28),
decoration: BoxDecoration(
color: TeacherTheme.surfaceDark,
borderRadius: BorderRadius.circular(24),
border: Border.all(color: TeacherTheme.surfaceBorder, width: 1.2),
border: Border.all(
color: TeacherTheme.surfaceBorder, width: 1.2),
boxShadow: const [
BoxShadow(
color: Color(0x3310B981),
@@ -143,7 +151,10 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
height: 68,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [TeacherTheme.emeraldPrimary, TeacherTheme.emeraldDark],
colors: [
TeacherTheme.emeraldPrimary,
TeacherTheme.emeraldDark
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
@@ -194,11 +205,14 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
// Backend Live Gateway Badge
Center(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 4),
decoration: BoxDecoration(
color: const Color(0xFF0C241D),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: TeacherTheme.emeraldPrimary.withOpacity(0.4)),
border: Border.all(
color: TeacherTheme.emeraldPrimary
.withOpacity(0.4)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
@@ -213,7 +227,9 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
),
const SizedBox(width: 6),
Text(
AppConfig.baseUrl.replaceAll('https://', '').replaceAll('http://', ''),
AppConfig.baseUrl
.replaceAll('https://', '')
.replaceAll('http://', ''),
style: const TextStyle(
color: TeacherTheme.emeraldLight,
fontSize: 11,
@@ -230,7 +246,10 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
// Step 1: Teacher Info & Phone
const Text(
'اسم المعلم المعتمد:',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12.5),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12.5),
),
const SizedBox(height: 6),
_buildTextField(
@@ -242,30 +261,41 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
const Text(
'المادة الأكاديمية التخصصية:',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12.5),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12.5),
),
const SizedBox(height: 6),
Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
padding:
const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color: TeacherTheme.surfaceCard,
borderRadius: BorderRadius.circular(14),
border: Border.all(color: TeacherTheme.surfaceBorder),
border: Border.all(
color: TeacherTheme.surfaceBorder),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: _selectedSubject,
isExpanded: true,
dropdownColor: TeacherTheme.surfaceDark,
icon: const Icon(CupertinoIcons.chevron_down, color: TeacherTheme.emeraldPrimary, size: 18),
icon: const Icon(CupertinoIcons.chevron_down,
color: TeacherTheme.emeraldPrimary,
size: 18),
items: _availableSubjects.map((s) {
return DropdownMenuItem(
value: s,
child: Text(s, style: const TextStyle(color: Colors.white, fontSize: 12.5)),
child: Text(s,
style: const TextStyle(
color: Colors.white,
fontSize: 12.5)),
);
}).toList(),
onChanged: (val) {
if (val != null) setState(() => _selectedSubject = val);
if (val != null)
setState(() => _selectedSubject = val);
},
),
),
@@ -274,7 +304,10 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
const Text(
'المدرسة / المديرية التابع لها:',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12.5),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12.5),
),
const SizedBox(height: 6),
_buildTextField(
@@ -286,21 +319,34 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
const Text(
'الصفوف والشعب التي تدرسها:',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12.5),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12.5),
),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: _allGrades.map((grade) {
final isSelected = _selectedGrades.contains(grade);
final isSelected =
_selectedGrades.contains(grade);
return FilterChip(
selected: isSelected,
label: Text(grade, style: TextStyle(color: isSelected ? Colors.black : Colors.white, fontSize: 11.5, fontWeight: FontWeight.bold)),
label: Text(grade,
style: TextStyle(
color: isSelected
? Colors.black
: Colors.white,
fontSize: 11.5,
fontWeight: FontWeight.bold)),
selectedColor: TeacherTheme.emeraldPrimary,
backgroundColor: TeacherTheme.surfaceCard,
checkmarkColor: Colors.black,
side: BorderSide(color: isSelected ? TeacherTheme.emeraldPrimary : TeacherTheme.surfaceBorder),
side: BorderSide(
color: isSelected
? TeacherTheme.emeraldPrimary
: TeacherTheme.surfaceBorder),
onSelected: (selected) {
setState(() {
if (selected) {
@@ -319,7 +365,10 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
const Text(
'رقم الهاتف للدخول والتحقق (WhatsApp OTP):',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 12.5),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 12.5),
),
const SizedBox(height: 6),
_buildTextField(
@@ -335,15 +384,23 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
style: ElevatedButton.styleFrom(
backgroundColor: TeacherTheme.emeraldPrimary,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
padding:
const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14)),
elevation: 0,
),
child: isLoading
? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(color: Colors.black, strokeWidth: 2))
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.black, strokeWidth: 2))
: const Text(
'إرسال رمز التحقق عبر الواتساب 📲',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w900),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w900),
),
),
] else ...[
@@ -353,25 +410,34 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
decoration: BoxDecoration(
color: const Color(0xFF0C241D),
borderRadius: BorderRadius.circular(16),
border: Border.all(color: const Color(0xFF25D366).withOpacity(0.5)),
border: Border.all(
color: const Color(0xFF25D366)
.withOpacity(0.5)),
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Icon(CupertinoIcons.chat_bubble_2_fill, color: Color(0xFF25D366), size: 20),
Icon(CupertinoIcons.chat_bubble_2_fill,
color: Color(0xFF25D366), size: 20),
SizedBox(width: 8),
Text(
'تم إرسال الرمز عبر الواتساب بنجاح 📲',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 13),
),
],
),
const SizedBox(height: 6),
Text(
'أدخل رمز التحقق المرسل إلى: ${_phoneController.text}',
style: const TextStyle(color: TeacherTheme.emeraldLight, fontSize: 13, fontWeight: FontWeight.w700),
style: const TextStyle(
color: TeacherTheme.emeraldLight,
fontSize: 13,
fontWeight: FontWeight.w700),
textAlign: TextAlign.center,
),
],
@@ -381,7 +447,10 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
const Text(
'رمز التحقق المعتمد (OTP):',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 13),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 13),
),
const SizedBox(height: 8),
_buildOtpField(controller: _otpController),
@@ -392,15 +461,23 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
style: ElevatedButton.styleFrom(
backgroundColor: TeacherTheme.emeraldPrimary,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
padding:
const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14)),
elevation: 0,
),
child: isLoading
? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(color: Colors.black, strokeWidth: 2))
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.black, strokeWidth: 2))
: const Text(
'تأكيد الدخول لملف المعلم ✨',
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w900),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w900),
),
),
const SizedBox(height: 14),
@@ -410,16 +487,31 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
children: [
TextButton.icon(
onPressed: isLoading ? null : _onSendOtp,
icon: const Icon(CupertinoIcons.refresh_circled, size: 16, color: Color(0xFF25D366)),
label: const Text('إعادة إرسال الرمز 📲', style: TextStyle(color: Color(0xFF25D366), fontSize: 12, fontWeight: FontWeight.bold)),
icon: const Icon(
CupertinoIcons.refresh_circled,
size: 16,
color: Color(0xFF25D366)),
label: const Text('إعادة إرسال الرمز 📲',
style: TextStyle(
color: Color(0xFF25D366),
fontSize: 12,
fontWeight: FontWeight.bold)),
),
TextButton.icon(
onPressed: () {
setState(() => _isEnteringOtp = false);
context.read<TeacherAuthCubit>().resetToPhone();
context
.read<TeacherAuthCubit>()
.resetToPhone();
},
icon: const Icon(CupertinoIcons.pencil_ellipsis_rectangle, size: 16, color: Color(0xFF94A3B8)),
label: const Text('تعديل رقم الهاتف ✏️', style: TextStyle(color: Color(0xFF94A3B8), fontSize: 12)),
icon: const Icon(
CupertinoIcons.pencil_ellipsis_rectangle,
size: 16,
color: Color(0xFF94A3B8)),
label: const Text('تعديل رقم الهاتف ✏️',
style: TextStyle(
color: Color(0xFF94A3B8),
fontSize: 12)),
),
],
),
@@ -452,13 +544,15 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
child: TextField(
controller: controller,
keyboardType: keyboardType,
style: const TextStyle(color: Colors.white, fontSize: 13.5, fontWeight: FontWeight.w600),
style: const TextStyle(
color: Colors.white, fontSize: 13.5, fontWeight: FontWeight.w600),
decoration: InputDecoration(
prefixIcon: Icon(icon, color: TeacherTheme.emeraldPrimary, size: 18),
hintText: hint,
hintStyle: const TextStyle(color: Color(0xFF64748B), fontSize: 12.5),
border: InputBorder.none,
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
),
),
);
@@ -471,7 +565,8 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
decoration: BoxDecoration(
color: TeacherTheme.surfaceCard,
borderRadius: BorderRadius.circular(16),
border: Border.all(color: TeacherTheme.emeraldPrimary.withOpacity(0.6), width: 1.5),
border: Border.all(
color: TeacherTheme.emeraldPrimary.withOpacity(0.6), width: 1.5),
boxShadow: const [
BoxShadow(
color: Color(0x2210B981),
@@ -493,7 +588,8 @@ class _TeacherAuthScreenState extends State<TeacherAuthScreen> {
),
decoration: const InputDecoration(
counterText: '',
prefixIcon: Icon(CupertinoIcons.lock_shield_fill, color: TeacherTheme.emeraldPrimary, size: 22),
prefixIcon: Icon(CupertinoIcons.lock_shield_fill,
color: TeacherTheme.emeraldPrimary, size: 22),
hintText: '• • • • • •',
hintStyle: TextStyle(
color: Color(0xFF475569),
@@ -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))),
],
);
}
@@ -32,7 +32,8 @@ class TeacherMainShell extends StatefulWidget {
class _TeacherMainShellState extends State<TeacherMainShell> {
int _currentTabIndex = 0;
void _openEditProfileDialog(BuildContext context, TeacherProfileModel profile) {
void _openEditProfileDialog(
BuildContext context, TeacherProfileModel profile) {
final nameCtrl = TextEditingController(text: profile.name);
final subjectCtrl = TextEditingController(text: profile.specialization);
final schoolCtrl = TextEditingController(text: profile.schoolName);
@@ -64,7 +65,9 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
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: SingleChildScrollView(
child: Column(
@@ -73,37 +76,56 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
children: [
Row(
children: [
const Icon(CupertinoIcons.pencil_circle_fill, color: TeacherTheme.emeraldPrimary, size: 28),
const Icon(CupertinoIcons.pencil_circle_fill,
color: TeacherTheme.emeraldPrimary, size: 28),
const SizedBox(width: 10),
const Text(
'تحديث بيانات المعلم والصفوف والمباحث 📋',
style: TextStyle(color: Colors.white, fontSize: 15, fontWeight: FontWeight.w800),
style: TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.w800),
),
const Spacer(),
IconButton(
onPressed: () => Navigator.of(ctx).pop(),
icon: const Icon(CupertinoIcons.xmark_circle_fill, color: Colors.white54, size: 20),
icon: const Icon(CupertinoIcons.xmark_circle_fill,
color: Colors.white54, size: 20),
),
],
),
const SizedBox(height: 16),
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),
_buildInputBox(controller: nameCtrl, hint: 'الاسم الكامل'),
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),
_buildInputBox(controller: subjectCtrl, hint: 'المادة (مثال: الفيزياء)'),
_buildInputBox(
controller: subjectCtrl, hint: 'المادة (مثال: الفيزياء)'),
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),
_buildInputBox(controller: schoolCtrl, hint: 'المدرسة التابع لها'),
_buildInputBox(
controller: schoolCtrl, hint: 'المدرسة التابع لها'),
const SizedBox(height: 14),
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: 8),
Wrap(
spacing: 8,
@@ -112,11 +134,18 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
final isSelected = selectedGrades.contains(grade);
return FilterChip(
selected: isSelected,
label: Text(grade, style: TextStyle(color: isSelected ? Colors.black : Colors.white, fontSize: 11.5, fontWeight: FontWeight.bold)),
label: Text(grade,
style: TextStyle(
color: isSelected ? Colors.black : Colors.white,
fontSize: 11.5,
fontWeight: FontWeight.bold)),
selectedColor: TeacherTheme.emeraldPrimary,
backgroundColor: TeacherTheme.surfaceCard,
checkmarkColor: Colors.black,
side: BorderSide(color: isSelected ? TeacherTheme.emeraldPrimary : TeacherTheme.surfaceBorder),
side: BorderSide(
color: isSelected
? TeacherTheme.emeraldPrimary
: TeacherTheme.surfaceBorder),
onSelected: (selected) {
setModalState(() {
if (selected) {
@@ -132,7 +161,6 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
}).toList(),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
context.read<TeacherAuthCubit>().updateProfile(
@@ -143,15 +171,19 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
bio: bioCtrl.text.trim(),
);
Navigator.of(ctx).pop();
SaqelToast.showSuccess(context, 'تم تحديث بيانات المعلم والصفوف بنجاح ✨');
SaqelToast.showSuccess(
context, 'تم تحديث بيانات المعلم والصفوف بنجاح ✨');
},
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)),
),
child: const Text('حفظ التعديلات واعتماد الملف 💾', style: TextStyle(fontSize: 13.5, fontWeight: FontWeight.w900)),
child: const Text('حفظ التعديلات واعتماد الملف 💾',
style: TextStyle(
fontSize: 13.5, fontWeight: FontWeight.w900)),
),
],
),
@@ -162,7 +194,8 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
);
}
Widget _buildInputBox({required TextEditingController controller, required String hint}) {
Widget _buildInputBox(
{required TextEditingController controller, required String hint}) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 2),
decoration: BoxDecoration(
@@ -173,7 +206,10 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
child: TextField(
controller: controller,
style: const TextStyle(color: Colors.white, fontSize: 13),
decoration: InputDecoration(border: InputBorder.none, hintText: hint, hintStyle: const TextStyle(color: Color(0xFF64748B), fontSize: 12)),
decoration: InputDecoration(
border: InputBorder.none,
hintText: hint,
hintStyle: const TextStyle(color: Color(0xFF64748B), fontSize: 12)),
),
);
}
@@ -185,7 +221,8 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
textDirection: TextDirection.rtl,
child: CupertinoAlertDialog(
title: const Text('تسجيل الخروج من استوديو المعلم 🚪'),
content: const Text('هل أنت متأكد من رغبتك في إنهاء جلسة العمل الحالية؟'),
content:
const Text('هل أنت متأكد من رغبتك في إنهاء جلسة العمل الحالية؟'),
actions: [
CupertinoDialogAction(
onPressed: () => Navigator.of(ctx).pop(),
@@ -224,7 +261,9 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
profile = authState.profile;
}
final initialLetter = profile.name.isNotEmpty ? profile.name.trim().characters.first : 'م';
final initialLetter = profile.name.isNotEmpty
? profile.name.trim().characters.first
: 'م';
return Directionality(
textDirection: TextDirection.rtl,
@@ -242,7 +281,10 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
height: 38,
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [TeacherTheme.emeraldPrimary, TeacherTheme.emeraldDark],
colors: [
TeacherTheme.emeraldPrimary,
TeacherTheme.emeraldDark
],
),
borderRadius: BorderRadius.circular(10),
),
@@ -276,12 +318,14 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
),
),
const SizedBox(width: 4),
const Icon(CupertinoIcons.checkmark_seal_fill, color: TeacherTheme.emeraldPrimary, size: 15),
const Icon(CupertinoIcons.checkmark_seal_fill,
color: TeacherTheme.emeraldPrimary, size: 15),
],
),
Text(
'${profile.specialization} · ${profile.gradesTaught.join("، ")}',
style: const TextStyle(fontSize: 10.5, color: Color(0xFF94A3B8)),
style: const TextStyle(
fontSize: 10.5, color: Color(0xFF94A3B8)),
overflow: TextOverflow.ellipsis,
),
],
@@ -294,13 +338,15 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
// Edit Profile Button
IconButton(
tooltip: 'تعديل بيانات المعلم والصفوف',
icon: const Icon(CupertinoIcons.slider_horizontal_3, color: TeacherTheme.emeraldPrimary, size: 20),
icon: const Icon(CupertinoIcons.slider_horizontal_3,
color: TeacherTheme.emeraldPrimary, size: 20),
onPressed: () => _openEditProfileDialog(context, profile),
),
// Logout Button
IconButton(
tooltip: 'تسجيل الخروج',
icon: const Icon(CupertinoIcons.square_arrow_right, color: Color(0xFFFF453A), size: 20),
icon: const Icon(CupertinoIcons.square_arrow_right,
color: Color(0xFFFF453A), size: 20),
onPressed: () => _confirmLogout(context),
),
],
@@ -317,7 +363,8 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
bottomNavigationBar: Container(
decoration: const BoxDecoration(
color: TeacherTheme.surfaceDark,
border: Border(top: BorderSide(color: TeacherTheme.surfaceBorder)),
border:
Border(top: BorderSide(color: TeacherTheme.surfaceBorder)),
),
child: BottomNavigationBar(
currentIndex: _currentTabIndex,
@@ -325,7 +372,8 @@ class _TeacherMainShellState extends State<TeacherMainShell> {
backgroundColor: Colors.transparent,
selectedItemColor: TeacherTheme.emeraldPrimary,
unselectedItemColor: const Color(0xFF64748B),
selectedLabelStyle: const TextStyle(fontWeight: FontWeight.w800, fontSize: 11),
selectedLabelStyle:
const TextStyle(fontWeight: FontWeight.w800, fontSize: 11),
unselectedLabelStyle: const TextStyle(fontSize: 10.5),
type: BottomNavigationBarType.fixed,
elevation: 0,