499 lines
18 KiB
Dart
499 lines
18 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:sefer_driver/constant/finance_design_system.dart';
|
|
import 'package:sefer_driver/controller/gamification/gamification_controller.dart';
|
|
import 'package:sefer_driver/controller/home/statistics/statistics_controller.dart';
|
|
|
|
import 'widgets/stat_summary_card.dart';
|
|
import 'widgets/daily_goal_widget.dart';
|
|
import 'widgets/level_progress_widget.dart';
|
|
import 'widgets/today_chart_widget.dart';
|
|
import 'widgets/weekly_chart_widget.dart';
|
|
import 'widgets/monthly_chart_widget.dart';
|
|
|
|
class StatisticsDashboard extends StatelessWidget {
|
|
StatisticsDashboard({super.key});
|
|
|
|
final GamificationController gamController =
|
|
Get.put(GamificationController());
|
|
final StatisticsController statsController =
|
|
Get.put(StatisticsController());
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: FinanceDesignSystem.backgroundColor,
|
|
body: GetBuilder<GamificationController>(
|
|
builder: (gc) {
|
|
if (gc.isLoading) {
|
|
return const Center(
|
|
child: CircularProgressIndicator(
|
|
color: FinanceDesignSystem.primaryDark),
|
|
);
|
|
}
|
|
|
|
return CustomScrollView(
|
|
physics: const BouncingScrollPhysics(),
|
|
slivers: [
|
|
// ═══════ App Bar ═══════
|
|
SliverAppBar(
|
|
expandedHeight: 200,
|
|
pinned: true,
|
|
stretch: true,
|
|
backgroundColor: FinanceDesignSystem.primaryDark,
|
|
leading: IconButton(
|
|
icon: const Icon(Icons.arrow_back_ios_new_rounded,
|
|
color: Colors.white, size: 20),
|
|
onPressed: () => Get.back(),
|
|
),
|
|
actions: [
|
|
IconButton(
|
|
icon: const Icon(Icons.refresh_rounded,
|
|
color: Colors.white),
|
|
onPressed: () => gc.fetchGamificationData(),
|
|
),
|
|
],
|
|
flexibleSpace: FlexibleSpaceBar(
|
|
centerTitle: true,
|
|
title: Text(
|
|
'Statistics'.tr,
|
|
style: const TextStyle(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 18,
|
|
),
|
|
),
|
|
background: Stack(
|
|
fit: StackFit.expand,
|
|
children: [
|
|
Container(
|
|
decoration: const BoxDecoration(
|
|
gradient: FinanceDesignSystem.balanceGradient,
|
|
),
|
|
),
|
|
// أيقونة زخرفية
|
|
Positioned(
|
|
right: -40,
|
|
top: -20,
|
|
child: Icon(
|
|
Icons.bar_chart_rounded,
|
|
size: 200,
|
|
color: Colors.white.withOpacity(0.04),
|
|
),
|
|
),
|
|
Positioned(
|
|
left: -30,
|
|
bottom: 20,
|
|
child: Icon(
|
|
Icons.emoji_events_rounded,
|
|
size: 120,
|
|
color: Colors.white.withOpacity(0.03),
|
|
),
|
|
),
|
|
// بطاقة المستوى في الهيدر
|
|
Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const SizedBox(height: 40),
|
|
Text(
|
|
gc.currentLevel.emoji,
|
|
style: const TextStyle(fontSize: 40),
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
Get.locale?.languageCode == 'ar'
|
|
? gc.currentLevel.nameAr
|
|
: gc.currentLevel.nameEn,
|
|
style: TextStyle(
|
|
color: Colors.white.withOpacity(0.9),
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
'${gc.unlockedCount}/${gc.totalAchievements} ${'Achievements'.tr}',
|
|
style: TextStyle(
|
|
color: Colors.white.withOpacity(0.5),
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
|
|
// ═══════ المحتوى ═══════
|
|
SliverPadding(
|
|
padding: const EdgeInsets.fromLTRB(16, 24, 16, 40),
|
|
sliver: SliverList(
|
|
delegate: SliverChildListDelegate([
|
|
// 1. بطاقات الإحصائيات الأربعة
|
|
_buildSummaryCards(gc),
|
|
const SizedBox(
|
|
height: FinanceDesignSystem.verticalSectionPadding),
|
|
|
|
// 2. الهدف اليومي
|
|
DailyGoalWidget(controller: gc),
|
|
const SizedBox(
|
|
height: FinanceDesignSystem.verticalSectionPadding),
|
|
|
|
// 3. تقدم المستوى
|
|
LevelProgressWidget(controller: gc),
|
|
const SizedBox(
|
|
height: FinanceDesignSystem.verticalSectionPadding),
|
|
|
|
// 4. إحصائيات اليوم
|
|
TodayChartWidget(),
|
|
const SizedBox(
|
|
height: FinanceDesignSystem.verticalSectionPadding),
|
|
|
|
// 5. الرسم البياني الأسبوعي
|
|
const WeeklyChartWidget(),
|
|
const SizedBox(
|
|
height: FinanceDesignSystem.verticalSectionPadding),
|
|
|
|
// 6. التقرير الشهري
|
|
const MonthlyChartWidget(),
|
|
const SizedBox(
|
|
height: FinanceDesignSystem.verticalSectionPadding),
|
|
|
|
// 7. تقييم سلوك القيادة
|
|
_buildBehaviorSection(gc),
|
|
const SizedBox(
|
|
height: FinanceDesignSystem.verticalSectionPadding),
|
|
|
|
// 8. الإنجازات
|
|
_buildAchievementsSection(gc),
|
|
]),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
// ═══════ بطاقات الإحصائيات ═══════
|
|
Widget _buildSummaryCards(GamificationController gc) {
|
|
return GridView.count(
|
|
crossAxisCount: 2,
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
crossAxisSpacing: 12,
|
|
mainAxisSpacing: 12,
|
|
childAspectRatio: 1.5,
|
|
children: [
|
|
StatSummaryCard(
|
|
icon: Icons.local_taxi_rounded,
|
|
label: 'Total Trips'.tr,
|
|
value: gc.totalTrips.toString(),
|
|
color: FinanceDesignSystem.accentBlue,
|
|
),
|
|
StatSummaryCard(
|
|
icon: Icons.star_rounded,
|
|
label: 'Rating'.tr,
|
|
value: gc.averageRating.toStringAsFixed(1),
|
|
color: const Color(0xFFFFD700),
|
|
),
|
|
StatSummaryCard(
|
|
icon: Icons.whatshot_rounded,
|
|
label: 'Day Streak'.tr,
|
|
value: '${gc.consecutiveDays}',
|
|
color: const Color(0xFFFF5722),
|
|
),
|
|
StatSummaryCard(
|
|
icon: Icons.people_rounded,
|
|
label: 'Referrals'.tr,
|
|
value: gc.totalReferrals.toString(),
|
|
color: const Color(0xFF9C27B0),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
// ═══════ قسم تقييم السلوك ═══════
|
|
Widget _buildBehaviorSection(GamificationController gc) {
|
|
bool isExcellent = gc.behaviorScore >= 90;
|
|
bool isGood = gc.behaviorScore >= 75 && gc.behaviorScore < 90;
|
|
|
|
Color statusColor = isExcellent
|
|
? Colors.green
|
|
: isGood ? Colors.orange : Colors.red;
|
|
|
|
String statusText = isExcellent
|
|
? 'Excellent'.tr
|
|
: isGood ? 'Good'.tr : 'Needs Improvement'.tr;
|
|
|
|
return Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
color: FinanceDesignSystem.cardColor,
|
|
borderRadius: BorderRadius.circular(24),
|
|
boxShadow: const [BoxShadow(color: Colors.black12, blurRadius: 10, offset: Offset(0, 4))],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
const Icon(Icons.shield_rounded, color: FinanceDesignSystem.accentBlue),
|
|
const SizedBox(width: 8),
|
|
Text('Driving Behavior'.tr, style: FinanceDesignSystem.headingStyle),
|
|
],
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: statusColor.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Text(
|
|
statusText,
|
|
style: TextStyle(
|
|
color: statusColor,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 20),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
_behaviorStatItem(
|
|
title: 'Score'.tr,
|
|
value: '${gc.behaviorScore}%',
|
|
icon: Icons.speed_rounded,
|
|
color: FinanceDesignSystem.accentBlue,
|
|
),
|
|
_behaviorStatItem(
|
|
title: 'Max Speed'.tr,
|
|
value: '${gc.maxSpeed.toStringAsFixed(0)} km/h',
|
|
icon: Icons.directions_car_rounded,
|
|
color: Colors.orange,
|
|
),
|
|
_behaviorStatItem(
|
|
title: 'Hard Brakes'.tr,
|
|
value: '${gc.hardBrakes}',
|
|
icon: Icons.warning_rounded,
|
|
color: Colors.red,
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
ClipRRect(
|
|
borderRadius: BorderRadius.circular(8),
|
|
child: LinearProgressIndicator(
|
|
value: gc.behaviorScore / 100,
|
|
backgroundColor: FinanceDesignSystem.backgroundColor,
|
|
valueColor: AlwaysStoppedAnimation<Color>(statusColor),
|
|
minHeight: 8,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _behaviorStatItem({required String title, required String value, required IconData icon, required Color color}) {
|
|
return Column(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(10),
|
|
decoration: BoxDecoration(
|
|
color: color.withOpacity(0.1),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(icon, color: color, size: 24),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(value, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16, color: FinanceDesignSystem.primaryDark)),
|
|
Text(title, style: const TextStyle(fontSize: 12, color: FinanceDesignSystem.textSecondary)),
|
|
],
|
|
);
|
|
}
|
|
|
|
// ═══════ قسم الإنجازات ═══════
|
|
Widget _buildAchievementsSection(GamificationController gc) {
|
|
final unlockedAch =
|
|
gc.achievements.where((a) => a.isUnlocked).toList();
|
|
final lockedAch =
|
|
gc.achievements.where((a) => !a.isUnlocked).toList();
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text('Achievements'.tr, style: FinanceDesignSystem.headingStyle),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: FinanceDesignSystem.accentBlue.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(20),
|
|
),
|
|
child: Text(
|
|
'${gc.unlockedCount}/${gc.totalAchievements}',
|
|
style: const TextStyle(
|
|
color: FinanceDesignSystem.accentBlue,
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 12,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
|
|
// الإنجازات المفتوحة
|
|
if (unlockedAch.isNotEmpty) ...[
|
|
...unlockedAch.map((a) => _buildAchievementCard(a, true)),
|
|
const SizedBox(height: 16),
|
|
],
|
|
|
|
// الإنجازات المقفلة
|
|
...lockedAch.map((a) => _buildAchievementCard(a, false)),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildAchievementCard(Achievement ach, bool unlocked) {
|
|
final isAr = Get.locale?.languageCode == 'ar';
|
|
return Container(
|
|
margin: const EdgeInsets.only(bottom: 12),
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: unlocked ? Colors.white : Colors.grey.shade50,
|
|
borderRadius: BorderRadius.circular(FinanceDesignSystem.cardRadius),
|
|
border: unlocked
|
|
? Border.all(color: ach.color.withOpacity(0.3), width: 1.5)
|
|
: null,
|
|
boxShadow: unlocked
|
|
? [
|
|
BoxShadow(
|
|
color: ach.color.withOpacity(0.1),
|
|
blurRadius: 10,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
]
|
|
: null,
|
|
),
|
|
child: Row(
|
|
children: [
|
|
// أيقونة الإنجاز
|
|
Container(
|
|
width: 50,
|
|
height: 50,
|
|
decoration: BoxDecoration(
|
|
color: unlocked
|
|
? ach.color.withOpacity(0.15)
|
|
: Colors.grey.shade200,
|
|
borderRadius: BorderRadius.circular(14),
|
|
),
|
|
child: Icon(
|
|
ach.icon,
|
|
color: unlocked ? ach.color : Colors.grey.shade400,
|
|
size: 26,
|
|
),
|
|
),
|
|
const SizedBox(width: 14),
|
|
|
|
// المعلومات
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
isAr ? ach.titleAr : ach.titleEn,
|
|
style: TextStyle(
|
|
fontWeight: FontWeight.bold,
|
|
fontSize: 14,
|
|
color: unlocked
|
|
? FinanceDesignSystem.primaryDark
|
|
: Colors.grey.shade500,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
isAr ? ach.descriptionAr : ach.descriptionEn,
|
|
style: TextStyle(
|
|
fontSize: 11,
|
|
color: Colors.grey.shade500,
|
|
),
|
|
),
|
|
if (!unlocked) ...[
|
|
const SizedBox(height: 8),
|
|
// شريط التقدم
|
|
Stack(
|
|
children: [
|
|
Container(
|
|
height: 6,
|
|
decoration: BoxDecoration(
|
|
color: Colors.grey.shade200,
|
|
borderRadius: BorderRadius.circular(3),
|
|
),
|
|
),
|
|
FractionallySizedBox(
|
|
widthFactor: ach.progress,
|
|
child: Container(
|
|
height: 6,
|
|
decoration: BoxDecoration(
|
|
color: ach.color.withOpacity(0.6),
|
|
borderRadius: BorderRadius.circular(3),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 4),
|
|
Text(
|
|
'${ach.currentProgress}/${ach.target}',
|
|
style: TextStyle(
|
|
fontSize: 10,
|
|
color: Colors.grey.shade400,
|
|
fontWeight: FontWeight.w600,
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
|
|
// أيقونة الحالة
|
|
if (unlocked)
|
|
Container(
|
|
padding: const EdgeInsets.all(6),
|
|
decoration: BoxDecoration(
|
|
color: ach.color.withOpacity(0.15),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
Icons.check_rounded,
|
|
color: ach.color,
|
|
size: 18,
|
|
),
|
|
)
|
|
else
|
|
Icon(
|
|
Icons.lock_outline_rounded,
|
|
color: Colors.grey.shade300,
|
|
size: 20,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|