Update Saqel Platform: 2026-08-31 01:52:23
This commit is contained in:
@@ -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)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user