Update Saqel Platform: 2026-08-31 01:52:23

This commit is contained in:
Hamza-Ayed
2026-08-31 01:52:24 +03:00
parent 4e29915383
commit ccf5f13a09
419 changed files with 19777 additions and 41 deletions
@@ -0,0 +1,559 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../core/localization/app_strings.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_theme.dart';
import '../../../core/utils/saqel_toast.dart';
import '../../../data/models/user_model.dart';
import '../../../data/models/lesson_model.dart';
import '../../../logic/cubits/auth_cubit.dart';
import '../../../logic/cubits/dashboard_cubits.dart';
import '../../widgets/luxury_widgets.dart';
class UnifiedHomeScreen extends StatefulWidget {
final UserModel user;
final String activeRole;
const UnifiedHomeScreen({
super.key,
required this.user,
required this.activeRole,
});
@override
State<UnifiedHomeScreen> createState() => _UnifiedHomeScreenState();
}
class _UnifiedHomeScreenState extends State<UnifiedHomeScreen> {
late String _currentRole;
@override
void initState() {
super.initState();
_currentRole = widget.activeRole;
_refreshData();
}
void _refreshData() {
if (_currentRole == 'student') {
context.read<StudentCubit>().fetchLessons(
gradeLevel: widget.user.gradeLevel,
stream: widget.user.stream,
initialReadiness: widget.user.readinessScore ?? 0.0,
);
} else {
context.read<GuardianCubit>().fetchDashboard();
}
}
void _switchRole(String newRole) {
if (_currentRole == newRole) return;
setState(() => _currentRole = newRole);
context.read<AuthCubit>().switchRole(newRole);
_refreshData();
}
@override
Widget build(BuildContext context) {
final isStudent = _currentRole == 'student';
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: RadialGradient(
center: isStudent ? const Alignment(0.8, -0.6) : const Alignment(-0.8, -0.6),
radius: 1.3,
colors: isStudent
? const [Color(0xFF0A2540), AppColors.darkBackground]
: const [Color(0xFF332008), AppColors.darkBackground],
),
),
child: SafeArea(
child: Column(
children: [
_buildTopBar(isStudent),
Expanded(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: isStudent ? _buildStudentTab() : _buildGuardianTab(),
),
),
],
),
),
),
);
}
/// Top App Bar with Persona Switcher & Logout
Widget _buildTopBar(bool isStudent) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
decoration: const BoxDecoration(
color: Color(0x990D111A),
border: Border(
bottom: BorderSide(color: AppColors.darkCardBorder),
),
),
child: Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: isStudent
? const [AppColors.appleBlue, AppColors.saqelCyan]
: const [AppColors.guardianAmber, Color(0xFFFBBF24)],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12),
),
child: const Center(
child: Text(
'ص',
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w900, color: Colors.black),
),
),
),
const SizedBox(width: 12),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.user.name,
style: AppTypography.titleMedium.copyWith(color: Colors.white),
),
Text(
isStudent ? AppStrings.studentPortal : AppStrings.guardianPortal,
style: AppTypography.labelSmall.copyWith(color: AppColors.textMutedDark),
),
],
),
const Spacer(),
// Persona Toggle
Container(
padding: const EdgeInsets.all(3),
decoration: BoxDecoration(
color: AppColors.darkSurface,
borderRadius: BorderRadius.circular(18),
border: Border.all(color: AppColors.darkCardBorder),
),
child: Row(
children: [
_buildRolePill('الطالب', isStudent, () => _switchRole('student')),
_buildRolePill('ولي الأمر', !isStudent, () => _switchRole('guardian')),
],
),
),
const SizedBox(width: 8),
// Logout Icon
IconButton(
icon: const Icon(CupertinoIcons.power, color: AppColors.errorRed, size: 20),
onPressed: () => context.read<AuthCubit>().logout(),
tooltip: AppStrings.logout,
),
],
),
);
}
Widget _buildRolePill(String title, bool isSelected, VoidCallback onTap) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: isSelected ? const Color(0x33FFFFFF) : Colors.transparent,
borderRadius: BorderRadius.circular(14),
),
child: Text(
title,
style: TextStyle(
fontSize: 12,
fontWeight: isSelected ? FontWeight.w700 : FontWeight.w500,
color: isSelected ? Colors.white : AppColors.textMutedDark,
),
),
),
);
}
/// 🎓 Real Student Tab (Data from API)
Widget _buildStudentTab() {
return BlocBuilder<StudentCubit, StudentState>(
builder: (context, state) {
if (state is StudentLoading) {
return const Center(
child: CircularProgressIndicator(color: AppColors.saqelCyan),
);
} else if (state is StudentError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(CupertinoIcons.exclamationmark_triangle, color: AppColors.errorRed, size: 40),
const SizedBox(height: 12),
Text(state.message, style: const TextStyle(color: Colors.white)),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _refreshData,
child: Text(AppStrings.retry),
),
],
),
);
} else if (state is StudentEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(CupertinoIcons.book, color: AppColors.textMutedDark, size: 48),
const SizedBox(height: 12),
Text(AppStrings.noLessonsFound, style: const TextStyle(color: AppColors.textSecondaryDark)),
],
),
);
} else if (state is StudentLoaded) {
return RefreshIndicator(
onRefresh: () async => _refreshData(),
child: ListView(
padding: const EdgeInsets.all(20),
children: [
// Real Readiness Score Card
LuxuryCard(
borderColor: AppColors.saqelCyan.withAlpha(50),
child: Row(
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
decoration: BoxDecoration(
color: AppColors.saqelCyan.withAlpha(30),
borderRadius: BorderRadius.circular(6),
),
child: Text(
widget.user.gradeLevel ?? 'توجيهي 2008',
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
color: AppColors.saqelCyan,
),
),
),
const SizedBox(height: 8),
Text(
AppStrings.readinessScore,
style: AppTypography.headlineMedium.copyWith(color: Colors.white),
),
const SizedBox(height: 4),
Text(
'تقييم حقيقي مبني على الكويزات والفحوصات السقراطية',
style: AppTypography.bodyMedium.copyWith(color: AppColors.textSecondaryDark, fontSize: 12),
),
],
),
),
Container(
width: 64,
height: 64,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: AppColors.saqelCyan, width: 3),
),
child: Center(
child: Text(
'${state.readinessScore.toStringAsFixed(0)}%',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w900, color: Colors.white),
),
),
),
],
),
),
const SizedBox(height: 20),
// Real Lessons List from API
Text(
AppStrings.tawjihiCurriculum,
style: AppTypography.titleMedium.copyWith(color: Colors.white),
),
const SizedBox(height: 12),
...state.lessons.map((lesson) => _buildRealLessonItem(lesson)),
],
),
);
}
return const SizedBox.shrink();
},
);
}
Widget _buildRealLessonItem(LessonModel lesson) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: LuxuryCard(
padding: const EdgeInsets.all(16),
onTap: () {
SaqelToast.showInfo(
context,
'تشغيل الدرس: ${lesson.title}',
title: lesson.courseTitle,
);
},
child: Row(
children: [
Container(
width: 44,
height: 44,
decoration: BoxDecoration(
color: AppColors.appleBlue.withAlpha(30),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(CupertinoIcons.play_fill, color: AppColors.appleBlue, size: 20),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
lesson.courseTitle,
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w700,
color: AppColors.saqelCyan,
),
),
if (lesson.isAiVersion) ...[
const SizedBox(width: 6),
Container(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
decoration: BoxDecoration(
color: AppColors.adminViolet.withAlpha(40),
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'ذكاء اصطناعي ✨',
style: TextStyle(fontSize: 9, fontWeight: FontWeight.w700, color: Color(0xFFA78BFA)),
),
),
],
],
),
const SizedBox(height: 4),
Text(
lesson.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w700, color: Colors.white),
),
const SizedBox(height: 2),
Text(
lesson.formattedDuration,
style: const TextStyle(fontSize: 11, color: AppColors.textMutedDark),
),
],
),
),
],
),
),
);
}
/// 👨‍👧‍👦 Real Guardian Tab (Data from API)
Widget _buildGuardianTab() {
return BlocBuilder<GuardianCubit, GuardianState>(
builder: (context, state) {
if (state is GuardianLoading) {
return const Center(
child: CircularProgressIndicator(color: AppColors.guardianAmber),
);
} else if (state is GuardianError) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(CupertinoIcons.exclamationmark_triangle, color: AppColors.errorRed, size: 40),
const SizedBox(height: 12),
Text(state.message, style: const TextStyle(color: Colors.white)),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _refreshData,
child: Text(AppStrings.retry),
),
],
),
);
} else if (state is GuardianEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(CupertinoIcons.person_2, color: AppColors.textMutedDark, size: 48),
const SizedBox(height: 12),
Text(AppStrings.noChildrenLinked, style: const TextStyle(color: AppColors.textSecondaryDark)),
],
),
);
} else if (state is GuardianLoaded) {
final selectedChild = state.children[state.selectedChildIndex];
return RefreshIndicator(
onRefresh: () async => _refreshData(),
child: ListView(
padding: const EdgeInsets.all(20),
children: [
Text(
AppStrings.linkedChildren,
style: AppTypography.titleMedium.copyWith(color: Colors.white),
),
const SizedBox(height: 10),
// Multi-Child Switcher
Row(
children: List.generate(state.children.length, (index) {
final isSelected = index == state.selectedChildIndex;
final child = state.children[index];
return Expanded(
child: GestureDetector(
onTap: () => context.read<GuardianCubit>().selectChild(index),
child: Container(
margin: EdgeInsets.only(left: index == 0 ? 8 : 0),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isSelected ? AppColors.guardianAmber.withAlpha(35) : AppColors.darkCard,
borderRadius: BorderRadius.circular(14),
border: Border.all(
color: isSelected ? AppColors.guardianAmber : AppColors.darkCardBorder,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
child.name,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w700,
color: isSelected ? const Color(0xFFFBBF24) : Colors.white,
),
),
const SizedBox(height: 2),
Text(
child.gradeLevel ?? 'طالب مسجل',
style: const TextStyle(fontSize: 10.5, color: AppColors.textMutedDark),
),
],
),
),
),
);
}),
),
const SizedBox(height: 20),
// Selected Child Performance Bento
LuxuryCard(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
'تقرير الطالب: ${selectedChild.name}',
style: AppTypography.titleMedium.copyWith(color: Colors.white),
),
const Spacer(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: AppColors.teacherEmerald.withAlpha(30),
borderRadius: BorderRadius.circular(6),
),
child: Text(
'جاهزية ${selectedChild.readinessScore.toStringAsFixed(1)}%',
style: const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w800,
color: AppColors.teacherEmerald,
),
),
),
],
),
const SizedBox(height: 6),
Text(
'المدرسة: ${selectedChild.schoolName ?? 'مدرسة الثقافة العسكرية'}',
style: const TextStyle(fontSize: 12, color: AppColors.textMutedDark),
),
const SizedBox(height: 16),
const Divider(color: AppColors.darkCardBorder),
const SizedBox(height: 10),
_buildGuardianStat('الامتحانات المنجزة بنجاح', '${selectedChild.examsPassed} من ${selectedChild.examsTotal}'),
_buildGuardianStat('الرقم الوطني', selectedChild.nationalId ?? 'مسجل بالهاتف'),
],
),
),
const SizedBox(height: 16),
// Real WhatsApp Reports Integration Banner
LuxuryCard(
backgroundColor: const Color(0xFF075E54).withAlpha(35),
borderColor: AppColors.whatsappGreen.withAlpha(80),
child: const Row(
children: [
Icon(CupertinoIcons.chat_bubble_fill, color: AppColors.whatsappGreen, size: 24),
SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'تقارير بوابة نبيه للواتساب',
style: TextStyle(fontSize: 13.5, fontWeight: FontWeight.w700, color: Colors.white),
),
SizedBox(height: 2),
Text(
'تصلك إشعارات الأداء والغياب آلياً عبر رقم الواتساب المسجل',
style: TextStyle(fontSize: 11, color: AppColors.textSecondaryDark),
),
],
),
),
],
),
),
],
),
);
}
return const SizedBox.shrink();
},
);
}
Widget _buildGuardianStat(String label, String value) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Text(label, style: const TextStyle(fontSize: 12, color: AppColors.textSecondaryDark)),
const Spacer(),
Text(value, style: const TextStyle(fontSize: 12.5, fontWeight: FontWeight.w700, color: Colors.white)),
],
),
);
}
}