import 'dart:convert'; import 'package:fl_chart/fl_chart.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import '../../constant/links.dart'; import '../../print.dart'; import '../functions/crud.dart'; class StaticController extends GetxController { // --- Date & State Management --- DateTime? startDate = DateTime(DateTime.now().year, DateTime.now().month, 1); DateTime? endDate = DateTime(DateTime.now().year, DateTime.now().month + 1, 0); DateTime? compareStartDate; DateTime? compareEndDate; bool isComparing = false; bool isLoading = false; // --- Daily Notes State --- bool isLoadingNotes = false; List dailyNotesList = []; // --- Chart Data (Current Range) --- List chartDataPassengers = []; List chartDataDrivers = []; List chartDataRides = []; List chartDataDriversMatchingNotes = []; // Employee Data (Notes/General Stats) List chartDataEmployeerama1 = []; List chartDataEmployeeshahd = []; List chartDataEmployeeRama2 = []; List chartDataEmployeeSefer4 = []; // Employee Data (Calls/Activations Stats) List chartDataCallsrama1 = []; List chartDataCallsShahd = []; List chartDataCallsRama2 = []; List chartDataCallsSefer4 = []; // --- Chart Data (Comparison Range) --- List chartDataPassengersCompare = []; List chartDataDriversCompare = []; List chartDataRidesCompare = []; List chartDataDriversMatchingNotesCompare = []; // Employee Comparison (Notes) List chartDataEmployeerama1Compare = []; List chartDataEmployeeshahdCompare = []; List chartDataEmployeeRama2Compare = []; List chartDataEmployeeSefer4Compare = []; // Employee Comparison (Calls/Activations) List chartDataCallsrama1Compare = []; List chartDataCallsShahdCompare = []; List chartDataCallsRama2Compare = []; List chartDataCallsSefer4Compare = []; // --- Totals --- String totalMonthlyPassengers = '0'; String totalMonthlyRides = '0'; String totalMonthlyDrivers = '0'; // --- Raw Lists --- List staticList = []; // --- Employment Type Stats List (Simple Count) --- List> employmentStatsList = []; @override void onInit() { super.onInit(); getAll(); } // --- Helpers for View --- double get daysInPeriod { if (startDate == null || endDate == null) return 31; return endDate!.difference(startDate!).inDays + 1.0; } String get currentDateString { if (startDate == null || endDate == null) return ""; return "${DateFormat('yyyy-MM-dd').format(startDate!)} : ${DateFormat('yyyy-MM-dd').format(endDate!)}"; } // --- Date Actions --- void updateDateRange(DateTime start, DateTime end) { startDate = start; endDate = end; if (isComparing) _calculateCompareDates(); getAll(); update(); } void _calculateCompareDates() { if (startDate == null || endDate == null) return; Duration duration = endDate!.difference(startDate!); compareEndDate = startDate!.subtract(const Duration(days: 1)); compareStartDate = compareEndDate!.subtract(duration); } Future toggleComparison() async { isComparing = !isComparing; if (isComparing) { _calculateCompareDates(); } else { compareStartDate = null; compareEndDate = null; _clearComparisonData(); } await getAll(); } void _clearComparisonData() { chartDataPassengersCompare.clear(); chartDataDriversCompare.clear(); chartDataRidesCompare.clear(); chartDataDriversMatchingNotesCompare.clear(); chartDataEmployeerama1Compare.clear(); chartDataEmployeeshahdCompare.clear(); chartDataEmployeeRama2Compare.clear(); chartDataEmployeeSefer4Compare.clear(); chartDataCallsrama1Compare.clear(); chartDataCallsShahdCompare.clear(); chartDataCallsRama2Compare.clear(); chartDataCallsSefer4Compare.clear(); } Map _getPayload(DateTime start, DateTime end) { return { "start_date": DateFormat('yyyy-MM-dd').format(start), "end_date": DateFormat('yyyy-MM-dd').format(end), "month": start.month.toString(), "year": start.year.toString(), }; } // --- Main Fetch Logic --- Future getAll() async { if (startDate == null || endDate == null) return; isLoading = true; update(); await Future.wait([ fetchPassengers(isCompare: false), fetchRides(isCompare: false), fetchDrivers(isCompare: false), fetchEmployee(isCompare: false), fetchEditorCalls(isCompare: false), fetchEmploymentStats(), ]); if (isComparing && compareStartDate != null && compareEndDate != null) { await Future.wait([ fetchPassengers(isCompare: true), fetchRides(isCompare: true), fetchDrivers(isCompare: true), fetchEmployee(isCompare: true), fetchEditorCalls(isCompare: true), ]); } isLoading = false; update(); } // ... (Existing Functions _generateSpots, fetchPassengers, etc.) ... List _generateSpots(List data, String dateKey, String valueKey, DateTime startOfRange) { List spots = []; Map dataMap = {}; for (var item in data) { String dateStr = item[dateKey].toString(); double val = double.tryParse(item[valueKey].toString()) ?? 0.0; dataMap[dateStr] = val; } DateTime rangeEnd = (startOfRange == startDate) ? endDate! : compareEndDate!; int totalDays = rangeEnd.difference(startOfRange).inDays + 1; for (int i = 0; i < totalDays; i++) { DateTime currentDate = startOfRange.add(Duration(days: i)); String dateKeyStr = DateFormat('yyyy-MM-dd').format(currentDate); double value = dataMap[dateKeyStr] ?? 0.0; spots.add(FlSpot((i + 1).toDouble(), value)); } return spots; } Future fetchPassengers({bool isCompare = false}) async { DateTime start = isCompare ? compareStartDate! : startDate!; DateTime end = isCompare ? compareEndDate! : endDate!; var res = await CRUD().get( link: AppLink.getPassengersStatic, payload: _getPayload(start, end)); var jsonResponse = jsonDecode(res); if (jsonResponse['status'] == 'failure') return; final List jsonData = jsonResponse['message']; if (!isCompare && jsonData.isNotEmpty && jsonData[0]['totalMonthly'] != null) { totalMonthlyPassengers = jsonData[0]['totalMonthly'].toString(); } List spots = _generateSpots(jsonData, 'day', 'totalPassengers', start); if (isCompare) chartDataPassengersCompare = spots; else chartDataPassengers = spots; } Future fetchRides({bool isCompare = false}) async { DateTime start = isCompare ? compareStartDate! : startDate!; DateTime end = isCompare ? compareEndDate! : endDate!; var res = await CRUD() .get(link: AppLink.getRidesStatic, payload: _getPayload(start, end)); var jsonResponse = jsonDecode(res); if (jsonResponse['status'] == 'failure') return; final List jsonData = jsonResponse['message']; if (!isCompare && jsonData.isNotEmpty && jsonData[0]['totalMonthly'] != null) { totalMonthlyRides = jsonData[0]['totalMonthly'].toString(); } List spots = _generateSpots(jsonData, 'day', 'totalRides', start); if (isCompare) chartDataRidesCompare = spots; else chartDataRides = spots; } Future fetchDrivers({bool isCompare = false}) async { DateTime start = isCompare ? compareStartDate! : startDate!; DateTime end = isCompare ? compareEndDate! : endDate!; var res = await CRUD().get( link: AppLink.getdriverstotalMonthly, payload: _getPayload(start, end)); var jsonResponse = jsonDecode(res); if (jsonResponse['status'] == 'failure') return; final List jsonData = jsonResponse['message']; if (!isCompare && jsonData.isNotEmpty && jsonData[0]['totalMonthlyDrivers'] != null) { totalMonthlyDrivers = jsonData[0]['totalMonthlyDrivers'].toString(); } if (!isCompare) { staticList = jsonData; } List spotsDrivers = _generateSpots(jsonData, 'day', 'dailyTotalDrivers', start); List spotsNotes = _generateSpots(jsonData, 'day', 'dailyMatchingNotes', start); if (isCompare) { chartDataDriversCompare = spotsDrivers; chartDataDriversMatchingNotesCompare = spotsNotes; } else { chartDataDrivers = spotsDrivers; chartDataDriversMatchingNotes = spotsNotes; } } Future fetchEmployee({bool isCompare = false}) async { try { DateTime start = isCompare ? compareStartDate! : startDate!; DateTime end = isCompare ? compareEndDate! : endDate!; var res = await CRUD().get( link: AppLink.getEmployeeStatic, payload: _getPayload(start, end)); if (isCompare) { chartDataEmployeerama1Compare = []; chartDataEmployeeshahdCompare = []; chartDataEmployeeRama2Compare = []; chartDataEmployeeSefer4Compare = []; } else { chartDataEmployeerama1 = []; chartDataEmployeeshahd = []; chartDataEmployeeRama2 = []; chartDataEmployeeSefer4 = []; } if (res == 'failure') return; var jsonResponse = jsonDecode(res) as Map; if (jsonResponse['status'] == 'failure') return; final List jsonData = jsonResponse['message']; if (jsonData.isEmpty) return; Map> dateNameMap = {}; for (var item in jsonData) { String dateKeyStr = item['date'] ?? item['day']; String name = item['NAME'].toString().toLowerCase().trim(); double count = double.tryParse(item['count'].toString()) ?? 0.0; if (!dateNameMap.containsKey(dateKeyStr)) dateNameMap[dateKeyStr] = {}; if (dateNameMap[dateKeyStr]!.containsKey(name)) { dateNameMap[dateKeyStr]![name] = dateNameMap[dateKeyStr]![name]! + count; } else { dateNameMap[dateKeyStr]![name] = count; } } final targetLists = isCompare ? { 'rama1': chartDataEmployeerama1Compare, 'shahd': chartDataEmployeeshahdCompare, 'rama2': chartDataEmployeeRama2Compare, 'mayar': chartDataEmployeeSefer4Compare } : { 'rama1': chartDataEmployeerama1, 'shahd': chartDataEmployeeshahd, 'rama2': chartDataEmployeeRama2, 'mayar': chartDataEmployeeSefer4 }; int totalDays = end.difference(start).inDays + 1; targetLists.forEach((key, listToFill) { for (int i = 0; i < totalDays; i++) { DateTime currentDate = start.add(Duration(days: i)); String currentDateStr = DateFormat('yyyy-MM-dd').format(currentDate); double value = 0; Map? dayData = dateNameMap[currentDateStr]; if (dayData != null) { // if (key == 'mayar') { // value = (dayData['mayar'] ?? 0) + // (dayData['rama1'] ?? 0) + // (dayData['sefer4'] ?? 0); // } else { value = dayData[key] ?? 0; // } } listToFill.add(FlSpot((i + 1).toDouble(), value)); } }); } catch (e) { Log.print('Error in fetchEmployee: $e'); } } Future fetchEditorCalls({bool isCompare = false}) async { try { DateTime start = isCompare ? compareStartDate! : startDate!; DateTime end = isCompare ? compareEndDate! : endDate!; var res = await CRUD().get( link: AppLink.getEditorStatsCalls, payload: _getPayload(start, end)); if (isCompare) { chartDataCallsrama1Compare = []; chartDataCallsShahdCompare = []; chartDataCallsRama2Compare = []; chartDataCallsSefer4Compare = []; } else { chartDataCallsrama1 = []; chartDataCallsShahd = []; chartDataCallsRama2 = []; chartDataCallsSefer4 = []; } if (res == 'failure') return; var jsonResponse = jsonDecode(res) as Map; if (jsonResponse['status'] == 'failure') return; final List jsonData = jsonResponse['message']; if (jsonData.isEmpty) return; Map> dateNameMap = {}; for (var item in jsonData) { String dateKeyStr = item['date'] ?? item['day']; String name = item['NAME'].toString().toLowerCase().trim(); double count = double.tryParse(item['count'].toString()) ?? 0.0; if (!dateNameMap.containsKey(dateKeyStr)) { dateNameMap[dateKeyStr] = {}; } if (dateNameMap[dateKeyStr]!.containsKey(name)) { dateNameMap[dateKeyStr]![name] = dateNameMap[dateKeyStr]![name]! + count; } else { dateNameMap[dateKeyStr]![name] = count; } } final targetLists = isCompare ? { 'rama1': chartDataCallsrama1Compare, 'shahd': chartDataCallsShahdCompare, 'rama2': chartDataCallsRama2Compare, 'mayar': chartDataCallsSefer4Compare, } : { 'rama1': chartDataCallsrama1, 'shahd': chartDataCallsShahd, 'rama2': chartDataCallsRama2, 'mayar': chartDataCallsSefer4, }; int totalDays = end.difference(start).inDays + 1; targetLists.forEach((key, listToFill) { for (int i = 0; i < totalDays; i++) { DateTime currentDate = start.add(Duration(days: i)); String currentDateStr = DateFormat('yyyy-MM-dd').format(currentDate); double value = 0; Map? dayData = dateNameMap[currentDateStr]; if (dayData != null) { // if (key == 'mayar_group') { // value = (dayData['mayar'] ?? 0) + // (dayData['rama1'] ?? 0) + // (dayData['sefer4'] ?? 0); // } else { value = dayData[key] ?? 0; // } } listToFill.add(FlSpot((i + 1).toDouble(), value)); } }); } catch (e) { Log.print('Error in fetchEditorCalls: $e'); } } // --- 🔴 FIXED: Fetch Employment Stats with Unique Check --- Future fetchEmploymentStats() async { try { // لا نستخدم .clear() هنا، سنقوم باستبدال القائمة بالكامل في النهاية var res = await CRUD().get( link: AppLink.getEmployeeDriverAfterCallingRegister, payload: _getPayload(startDate!, endDate!)); if (res == 'failure') return; var jsonResponse = jsonDecode(res); if (jsonResponse['status'] == 'success') { if (jsonResponse['message'] != null && jsonResponse['message']['data'] != null) { List data = jsonResponse['message']['data']; List allowedNames = ['shahd', 'mayar', 'rama1', 'rama2']; // استخدام Map لضمان عدم تكرار الأسماء (تجميع القيم) Map uniqueMap = {}; for (var item in data) { String name = item['employmentType'].toString().toLowerCase().trim(); int count = int.tryParse(item['count'].toString()) ?? 0; if (allowedNames.contains(name)) { if (uniqueMap.containsKey(name)) { uniqueMap[name] = uniqueMap[name]! + count; } else { uniqueMap[name] = count; } } } // تحويل الـ Map إلى القائمة النهائية List> tempList = []; uniqueMap.forEach((key, value) { tempList.add({'name': key, 'count': value}); }); // استبدال القائمة القديمة بالقائمة الجديدة النظيفة employmentStatsList = tempList; } } } catch (e) { Log.print("Error fetchEmploymentStats: $e"); } } // --- Fetch Daily Notes Log --- Future fetchDailyNotes(DateTime date) async { try { isLoadingNotes = true; dailyNotesList.clear(); update(); var res = await CRUD().post( link: AppLink.getNotesForEmployee, payload: {"date": DateFormat('yyyy-MM-dd').format(date)}); if (res != 'failure') { var jsonResponse = (res); if (jsonResponse['status'] == 'success') { dailyNotesList = jsonResponse['message']; } } } catch (e) { Log.print("Error fetchDailyNotes: $e"); } finally { isLoadingNotes = false; update(); } } }