import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:siro_admin/constant/theme.dart'; import 'package:siro_admin/controller/admin/analytics_v2_controller.dart'; class AdvancedAnalyticsPage extends StatelessWidget { const AdvancedAnalyticsPage({super.key}); @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; return Scaffold( backgroundColor: cs.surface, body: Column( children: [ _buildAppBar(context, cs), Expanded( child: GetBuilder( init: Get.isRegistered() ? Get.find() : AnalyticsV2Controller(), builder: (ctrl) { if (ctrl.isLoading) { return _buildLoadingState(cs); } return SingleChildScrollView( physics: const BouncingScrollPhysics(), padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildSummarySection(ctrl.revenueData['summary'], cs), const SizedBox(height: 24), _buildSectionTitle('إيرادات آخر 30 يوم', cs), _buildRevenueChart(ctrl.revenueData['daily'] ?? [], cs), const SizedBox(height: 32), _buildSectionTitle('نمو المستخدمين (آخر 30 يوم)', cs), _buildGrowthChart(ctrl.growthData, cs), const SizedBox(height: 32), _buildSectionTitle('أفضل 10 سائقين (حسب الرحلات)', cs), _buildTopDriversList(ctrl.topDrivers, cs), const SizedBox(height: 40), ], ), ); }, ), ), ], ), ); } Widget _buildAppBar(BuildContext context, ColorScheme cs) { return Container( padding: const EdgeInsets.fromLTRB(16, 50, 16, 16), decoration: BoxDecoration( color: cs.surface, border: Border(bottom: BorderSide(color: cs.outline)), ), child: Row( children: [ GestureDetector( onTap: () => Get.back(), child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(10), border: Border.all(color: cs.outline), ), child: Icon(Icons.arrow_back_ios_new_rounded, color: cs.onSurfaceVariant, size: 16), ), ), const SizedBox(width: 12), Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: cs.primary.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(10), border: Border.all( color: cs.primary.withValues(alpha: 0.25)), ), child: Icon(Icons.analytics_rounded, color: cs.primary, size: 18), ), const SizedBox(width: 10), Text( 'التحليلات المتقدمة', style: TextStyle( color: cs.onSurface, fontSize: 18, fontWeight: FontWeight.w700, ), ), const Spacer(), GestureDetector( onTap: () => Get.find().fetchAllAnalytics(), child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(10), border: Border.all(color: cs.outline), ), child: Icon(Icons.refresh_rounded, color: cs.onSurfaceVariant, size: 18), ), ), ], ), ); } Widget _buildLoadingState(ColorScheme cs) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: 40, height: 40, child: CircularProgressIndicator( color: cs.primary, strokeWidth: 2, backgroundColor: cs.primary.withValues(alpha: 0.1), ), ), const SizedBox(height: 16), Text('جاري تحميل التحليلات...', style: TextStyle(color: cs.onSurfaceVariant, fontSize: 13)), ], ), ); } Widget _buildSectionTitle(String title, ColorScheme cs) { return Padding( padding: const EdgeInsets.only(bottom: 16), child: Row( children: [ Container( width: 3, height: 16, decoration: BoxDecoration( color: cs.primary, borderRadius: BorderRadius.circular(2), ), ), const SizedBox(width: 10), Text( title, style: TextStyle( color: cs.onSurface, fontSize: 16, fontWeight: FontWeight.w700, ), ), ], ), ); } Widget _buildSummarySection( Map? summary, ColorScheme cs) { if (summary == null) return const SizedBox(); return Row( children: [ _buildSummaryCard( 'إجمالي الإيرادات', '${summary['total_revenue_all'] ?? 0}', cs.tertiary, Icons.attach_money_rounded, cs, ), const SizedBox(width: 12), _buildSummaryCard( 'صافي الربح', '${summary['total_profit_all'] ?? 0}', cs.success, Icons.trending_up_rounded, cs, ), ], ); } Widget _buildSummaryCard( String title, String value, Color color, IconData icon, ColorScheme cs) { return Expanded( child: Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), border: Border.all(color: color.withValues(alpha: 0.2)), boxShadow: [ BoxShadow( color: color.withValues(alpha: 0.06), blurRadius: 20, offset: const Offset(0, 8), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: color.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(10), border: Border.all(color: color.withValues(alpha: 0.25)), ), child: Icon(icon, color: color, size: 18), ), const SizedBox(height: 12), Text( title, style: TextStyle( color: cs.onSurfaceVariant, fontSize: 12), ), const SizedBox(height: 6), Text( value, style: TextStyle( color: color, fontSize: 22, fontWeight: FontWeight.w800, height: 1, ), ), ], ), ), ); } Widget _buildRevenueChart(List daily, ColorScheme cs) { if (daily.isEmpty) { return Container( height: 200, decoration: BoxDecoration( color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), border: Border.all(color: cs.outline), ), child: Center( child: Text('لا توجد بيانات', style: TextStyle(color: cs.onSurfaceVariant)), ), ); } List revenueSpots = []; List profitSpots = []; for (int i = 0; i < daily.length; i++) { revenueSpots.add(FlSpot(i.toDouble(), double.tryParse(daily[i]['total_revenue'].toString()) ?? 0)); profitSpots.add(FlSpot(i.toDouble(), double.tryParse(daily[i]['company_profit'].toString()) ?? 0)); } return Container( height: 300, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), border: Border.all(color: cs.outline), boxShadow: [ BoxShadow( color: cs.shadow.withValues(alpha: 0.06), blurRadius: 20, offset: const Offset(0, 8), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ _LegendDot(color: cs.tertiary, label: 'الإيرادات'), const SizedBox(width: 16), _LegendDot(color: cs.success, label: 'الربح'), ], ), const SizedBox(height: 16), Expanded( child: Directionality( textDirection: TextDirection.ltr, child: LineChart( LineChartData( gridData: FlGridData( show: true, drawVerticalLine: false, getDrawingHorizontalLine: (v) => FlLine( color: cs.outline.withValues(alpha: 0.5), strokeWidth: 1), ), titlesData: FlTitlesData( show: true, rightTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false)), topTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false)), leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 40, getTitlesWidget: (v, _) => Text( v >= 1000 ? '${(v / 1000).toStringAsFixed(0)}k' : v.toInt().toString(), style: TextStyle( color: cs.onSurfaceVariant, fontSize: 9), ), ), ), bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 24, getTitlesWidget: (val, meta) { if (val.toInt() % 7 == 0 && val.toInt() < daily.length) { return Text( daily[val.toInt()]['date'] .toString() .substring(8), style: TextStyle( color: cs.onSurfaceVariant, fontSize: 9), ); } return const SizedBox(); }, ), ), ), borderData: FlBorderData(show: false), lineBarsData: [ LineChartBarData( spots: revenueSpots, isCurved: true, color: cs.tertiary, barWidth: 3, isStrokeCapRound: true, dotData: const FlDotData(show: false), belowBarData: BarAreaData( show: true, color: cs.tertiary.withValues(alpha: 0.08), ), ), LineChartBarData( spots: profitSpots, isCurved: true, color: cs.success, barWidth: 3, isStrokeCapRound: true, dotData: const FlDotData(show: false), belowBarData: BarAreaData( show: true, color: cs.success.withValues(alpha: 0.08), ), ), ], ), ), ), ), ], ), ); } Widget _buildGrowthChart(Map data, ColorScheme cs) { final passengers = data['passenger_daily'] as List? ?? []; final drivers = data['driver_daily'] as List? ?? []; if (passengers.isEmpty && drivers.isEmpty) { return Container( height: 200, decoration: BoxDecoration( color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), border: Border.all(color: cs.outline), ), child: Center( child: Text('لا توجد بيانات', style: TextStyle(color: cs.onSurfaceVariant)), ), ); } List barGroups = []; int maxLength = passengers.length > drivers.length ? passengers.length : drivers.length; for (int i = 0; i < maxLength; i++) { double pCount = i < passengers.length ? double.tryParse(passengers[i]['new_passengers'].toString()) ?? 0 : 0; double dCount = i < drivers.length ? double.tryParse(drivers[i]['new_drivers'].toString()) ?? 0 : 0; barGroups.add( BarChartGroupData( x: i, barRods: [ BarChartRodData( toY: pCount, color: cs.tertiary.withValues(alpha: 0.7), width: 8, borderRadius: BorderRadius.circular(4), ), BarChartRodData( toY: dCount, color: cs.warning.withValues(alpha: 0.7), width: 8, borderRadius: BorderRadius.circular(4), ), ], ), ); } return Container( height: 280, padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), border: Border.all(color: cs.outline), boxShadow: [ BoxShadow( color: cs.shadow.withValues(alpha: 0.06), blurRadius: 20, offset: const Offset(0, 8), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ _LegendDot(color: cs.tertiary, label: 'ركاب جدد'), const SizedBox(width: 16), _LegendDot( color: cs.warning, label: 'سائقين جدد'), ], ), const SizedBox(height: 16), Expanded( child: Directionality( textDirection: TextDirection.ltr, child: BarChart( BarChartData( barGroups: barGroups, borderData: FlBorderData(show: false), titlesData: FlTitlesData( show: true, rightTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false)), topTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false)), leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 40, getTitlesWidget: (v, _) => Text( v.toInt().toString(), style: TextStyle( color: cs.onSurfaceVariant, fontSize: 9), ), ), ), bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 24, getTitlesWidget: (val, meta) { if (val.toInt() % 7 == 0 && val.toInt() < passengers.length) { return Text( passengers[val.toInt()]['date'] .toString() .substring(8), style: TextStyle( color: cs.onSurfaceVariant, fontSize: 9), ); } return const SizedBox(); }, ), ), ), ), ), ), ), ], ), ); } Widget _buildTopDriversList(List drivers, ColorScheme cs) { if (drivers.isEmpty) { return Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(20), border: Border.all(color: cs.outline), ), child: Center( child: Text('لا توجد بيانات', style: TextStyle(color: cs.onSurfaceVariant)), ), ); } return ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: drivers.length, itemBuilder: (ctx, i) { final d = drivers[i]; final isTop3 = i < 3; final rankColors = [ cs.warning, cs.onSurfaceVariant, const Color(0xFFCD7F32), ]; return Container( margin: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(16), border: Border.all( color: isTop3 ? rankColors[i].withValues(alpha: 0.2) : cs.outline, ), ), child: Row( children: [ Container( width: 40, height: 40, decoration: BoxDecoration( color: isTop3 ? rankColors[i].withValues(alpha: 0.12) : cs.outline, shape: BoxShape.circle, border: Border.all( color: isTop3 ? rankColors[i].withValues(alpha: 0.3) : Colors.transparent, ), ), child: Center( child: Text( '${i + 1}', style: TextStyle( color: isTop3 ? rankColors[i] : cs.onSurfaceVariant, fontWeight: FontWeight.w800, fontSize: 14, ), ), ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '${d['first_name']} ${d['last_name']}', style: TextStyle( color: cs.onSurface, fontWeight: FontWeight.w600, fontSize: 14, ), ), const SizedBox(height: 3), Text( d['phone'] ?? '', style: TextStyle( color: cs.onSurfaceVariant, fontSize: 11, fontFamily: 'monospace', ), ), ], ), ), Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 3), decoration: BoxDecoration( color: cs.tertiary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: Text( '${d['completed_rides']} رحلة', style: TextStyle( color: cs.tertiary, fontWeight: FontWeight.w700, fontSize: 12, ), ), ), const SizedBox(height: 4), Text( '${d['total_revenue']} ل.س', style: TextStyle( color: cs.success, fontSize: 11, fontWeight: FontWeight.w600, ), ), ], ), ], ), ); }, ); } } class _LegendDot extends StatelessWidget { final Color color; final String label; const _LegendDot({required this.color, required this.label}); @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 8, height: 8, decoration: BoxDecoration( color: color, shape: BoxShape.circle, ), ), const SizedBox(width: 6), Text( label, style: TextStyle( color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 11, fontWeight: FontWeight.w500, ), ), ], ); } }