Files
Siro/siro_driver/lib/controller/home/statistics/statistics_controller.dart
2026-06-15 01:37:41 +03:00

294 lines
9.7 KiB
Dart
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_driver/constant/box_name.dart';
import 'package:siro_driver/constant/links.dart';
import 'package:siro_driver/controller/functions/crud.dart';
import 'package:siro_driver/models/model/driver/rides_summary_model.dart';
import '../../../main.dart';
class StatisticsController extends GetxController {
bool isLoading = false;
// ═══ Weekly Data ═══
List<DayStat> weeklyStats = [];
double weeklyEarnings = 0;
int weeklyTrips = 0;
double weeklyHours = 0;
// ═══ Monthly Data ═══
List<MonthlyPriceDriverModel> monthlyEarnings = [];
List<MonthlyRideModel> monthlyRides = [];
List<MonthlyDataModel> monthlyDuration = [];
double monthlyTotalEarnings = 0;
int monthlyTotalTrips = 0;
double monthlyTotalHours = 0;
String bestDay = '--';
double bestDayEarnings = 0;
// ═══ Tab State ═══
int selectedTab = 0; // 0=weekly, 1=monthly
@override
void onInit() {
super.onInit();
reloadData();
}
void changeTab(int tab) {
selectedTab = tab;
update();
}
// ═══════ جلب البيانات الأسبوعية ═══════
Future<void> fetchWeeklyData() async {
isLoading = true;
update();
try {
var res = await CRUD().get(
link: AppLink.getWeeklyAggregate,
payload: {
'driver_id': box.read(BoxName.driverID).toString(),
},
);
if (res != null && res != 'failure') {
box.write('stats_weekly_cache', res);
var data = jsonDecode(res);
if (data['message'] is List) {
weeklyStats = (data['message'] as List)
.map((e) => DayStat.fromJson(e))
.toList();
weeklyEarnings = weeklyStats.fold(0, (s, d) => s + d.earnings);
weeklyTrips = weeklyStats.fold(0, (s, d) => s + d.trips);
weeklyHours = weeklyStats.fold(0, (s, d) => s + d.hours);
}
}
} catch (e) {
debugPrint('❌ [Stats] Weekly fetch error: $e');
// Fallback: generate from local data
_generateLocalWeeklyData();
}
isLoading = false;
update();
}
// ═══════ جلب البيانات الشهرية ═══════
Future<void> fetchMonthlyData() async {
try {
// 1. أرباح شهرية
var earningsRes = await CRUD().getWallet(
link: AppLink.getAllPaymentFromRide,
payload: {'driverID': box.read(BoxName.driverID).toString()},
);
if (earningsRes != null && earningsRes != 'failure') {
box.write('stats_monthly_earnings_cache', earningsRes);
var data = jsonDecode(earningsRes);
if (data['message'] is List) {
monthlyEarnings = (data['message'] as List)
.map((e) => MonthlyPriceDriverModel.fromJson(e))
.toList();
monthlyTotalEarnings =
monthlyEarnings.fold(0, (s, d) => s + d.pricePerDay);
// أفضل يوم
if (monthlyEarnings.isNotEmpty) {
var best = monthlyEarnings
.reduce((a, b) => a.pricePerDay > b.pricePerDay ? a : b);
bestDay = best.day.toString();
bestDayEarnings = best.pricePerDay;
}
}
}
// 2. رحلات شهرية
var ridesRes = await CRUD().get(
link: AppLink.getRidesDriverByDay,
payload: {'driver_id': box.read(BoxName.driverID).toString()},
);
if (ridesRes != null && ridesRes != 'failure') {
box.write('stats_monthly_rides_cache', ridesRes);
var data = jsonDecode(ridesRes);
if (data['message'] is List) {
monthlyRides = (data['message'] as List)
.map((e) => MonthlyRideModel.fromJson(e))
.toList();
monthlyTotalTrips = monthlyRides.fold(0, (s, d) => s + d.countRide);
}
}
// 3. ساعات شهرية
var durationRes = await CRUD().get(
link: AppLink.getTotalDriverDuration,
payload: {'driver_id': box.read(BoxName.driverID).toString()},
);
if (durationRes != null && durationRes != 'failure') {
box.write('stats_monthly_duration_cache', durationRes);
var data = jsonDecode(durationRes);
if (data['message'] is List) {
monthlyDuration = (data['message'] as List)
.map((e) => MonthlyDataModel.fromJson(e))
.toList();
monthlyTotalHours =
monthlyDuration.fold(0, (s, d) => s + d.totalDuration.toDouble());
}
}
} catch (e) {
debugPrint('❌ [Stats] Monthly fetch error: $e');
}
update();
}
void _generateLocalWeeklyData() {
// Fallback بيانات محلية عند عدم توفر الـ API
final now = DateTime.now();
weeklyStats = List.generate(7, (i) {
final day = now.subtract(Duration(days: 6 - i));
return DayStat(
date: day,
dayName: _getDayName(day.weekday),
earnings: 0,
trips: 0,
hours: 0,
);
});
}
String _getDayName(int weekday) {
const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
return days[weekday - 1];
}
bool _isFetching = false;
Future<void> reloadData({bool force = false}) async {
if (_isFetching) return;
_isFetching = true;
// التحقق من التخزين المؤقت (3 ساعات)
String? lastFetchStr = box.read('stats_last_fetch');
if (!force && lastFetchStr != null) {
try {
DateTime lastFetch = DateTime.parse(lastFetchStr);
if (DateTime.now().difference(lastFetch).inHours < 3) {
debugPrint(' [Statistics] Loading cached statistics (last fetch: $lastFetchStr)');
// تحميل الكاش الأسبوعي
String? weeklyCache = box.read('stats_weekly_cache');
if (weeklyCache != null) {
var data = jsonDecode(weeklyCache);
if (data['message'] is List) {
weeklyStats = (data['message'] as List)
.map((e) => DayStat.fromJson(e))
.toList();
weeklyEarnings = weeklyStats.fold(0, (s, d) => s + d.earnings);
weeklyTrips = weeklyStats.fold(0, (s, d) => s + d.trips);
weeklyHours = weeklyStats.fold(0, (s, d) => s + d.hours);
}
}
// تحميل الكاش الشهري (الأرباح)
String? earningsCache = box.read('stats_monthly_earnings_cache');
if (earningsCache != null) {
var data = jsonDecode(earningsCache);
if (data['message'] is List) {
monthlyEarnings = (data['message'] as List)
.map((e) => MonthlyPriceDriverModel.fromJson(e))
.toList();
monthlyTotalEarnings =
monthlyEarnings.fold(0, (s, d) => s + d.pricePerDay);
if (monthlyEarnings.isNotEmpty) {
var best = monthlyEarnings
.reduce((a, b) => a.pricePerDay > b.pricePerDay ? a : b);
bestDay = best.day.toString();
bestDayEarnings = best.pricePerDay;
}
}
}
// تحميل الكاش الشهري (الرحلات)
String? ridesCache = box.read('stats_monthly_rides_cache');
if (ridesCache != null) {
var data = jsonDecode(ridesCache);
if (data['message'] is List) {
monthlyRides = (data['message'] as List)
.map((e) => MonthlyRideModel.fromJson(e))
.toList();
monthlyTotalTrips = monthlyRides.fold(0, (s, d) => s + d.countRide);
}
}
// تحميل الكاش الشهري (المدد)
String? durationCache = box.read('stats_monthly_duration_cache');
if (durationCache != null) {
var data = jsonDecode(durationCache);
if (data['message'] is List) {
monthlyDuration = (data['message'] as List)
.map((e) => MonthlyDataModel.fromJson(e))
.toList();
monthlyTotalHours =
monthlyDuration.fold(0, (s, d) => s + d.totalDuration.toDouble());
}
}
_isFetching = false;
update();
return;
}
} catch (e) {
debugPrint('⚠️ [Statistics] Failed to load cached statistics: $e');
}
}
isLoading = true;
update();
try {
debugPrint('📊 [Statistics] Reloading data from server...');
await fetchWeeklyData();
await fetchMonthlyData();
box.write('stats_last_fetch', DateTime.now().toIso8601String());
debugPrint('📊 [Statistics] Data reload complete.');
} catch (e) {
debugPrint('❌ [Statistics] Error reloading data: $e');
} finally {
_isFetching = false;
isLoading = false;
update();
}
}
}
// ═══════ نموذج إحصائية اليوم ═══════
class DayStat {
final DateTime date;
final String dayName;
final double earnings;
final int trips;
final double hours;
DayStat({
required this.date,
required this.dayName,
required this.earnings,
required this.trips,
required this.hours,
});
factory DayStat.fromJson(Map<String, dynamic> json) {
final date =
DateTime.tryParse(json['day']?.toString() ?? '') ?? DateTime.now();
const dayNames = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
return DayStat(
date: date,
dayName: dayNames[date.weekday - 1],
earnings: double.tryParse(json['earnings']?.toString() ?? '0') ?? 0,
trips: int.tryParse(json['trips']?.toString() ?? '0') ?? 0,
hours: double.tryParse(json['hours']?.toString() ?? '0') ?? 0,
);
}
}