508 lines
17 KiB
Dart
508 lines
17 KiB
Dart
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<dynamic> dailyNotesList = [];
|
|
|
|
// --- Chart Data (Current Range) ---
|
|
List<FlSpot> chartDataPassengers = [];
|
|
List<FlSpot> chartDataDrivers = [];
|
|
List<FlSpot> chartDataRides = [];
|
|
List<FlSpot> chartDataDriversMatchingNotes = [];
|
|
|
|
// Employee Data (Notes/General Stats)
|
|
List<FlSpot> chartDataEmployeerama1 = [];
|
|
List<FlSpot> chartDataEmployeeshahd = [];
|
|
List<FlSpot> chartDataEmployeeRama2 = [];
|
|
List<FlSpot> chartDataEmployeeSefer4 = [];
|
|
|
|
// Employee Data (Calls/Activations Stats)
|
|
List<FlSpot> chartDataCallsrama1 = [];
|
|
List<FlSpot> chartDataCallsShahd = [];
|
|
List<FlSpot> chartDataCallsRama2 = [];
|
|
List<FlSpot> chartDataCallsSefer4 = [];
|
|
|
|
// --- Chart Data (Comparison Range) ---
|
|
List<FlSpot> chartDataPassengersCompare = [];
|
|
List<FlSpot> chartDataDriversCompare = [];
|
|
List<FlSpot> chartDataRidesCompare = [];
|
|
List<FlSpot> chartDataDriversMatchingNotesCompare = [];
|
|
|
|
// Employee Comparison (Notes)
|
|
List<FlSpot> chartDataEmployeerama1Compare = [];
|
|
List<FlSpot> chartDataEmployeeshahdCompare = [];
|
|
List<FlSpot> chartDataEmployeeRama2Compare = [];
|
|
List<FlSpot> chartDataEmployeeSefer4Compare = [];
|
|
|
|
// Employee Comparison (Calls/Activations)
|
|
List<FlSpot> chartDataCallsrama1Compare = [];
|
|
List<FlSpot> chartDataCallsShahdCompare = [];
|
|
List<FlSpot> chartDataCallsRama2Compare = [];
|
|
List<FlSpot> chartDataCallsSefer4Compare = [];
|
|
|
|
// --- Totals ---
|
|
String totalMonthlyPassengers = '0';
|
|
String totalMonthlyRides = '0';
|
|
String totalMonthlyDrivers = '0';
|
|
|
|
// --- Raw Lists ---
|
|
List staticList = [];
|
|
|
|
// --- Employment Type Stats List (Simple Count) ---
|
|
List<Map<String, dynamic>> 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<void> 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<String, dynamic> _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<FlSpot> _generateSpots(List<dynamic> data, String dateKey,
|
|
String valueKey, DateTime startOfRange) {
|
|
List<FlSpot> spots = [];
|
|
Map<String, double> 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<void> 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<dynamic> jsonData = jsonResponse['message'];
|
|
if (!isCompare &&
|
|
jsonData.isNotEmpty &&
|
|
jsonData[0]['totalMonthly'] != null) {
|
|
totalMonthlyPassengers = jsonData[0]['totalMonthly'].toString();
|
|
}
|
|
List<FlSpot> spots =
|
|
_generateSpots(jsonData, 'day', 'totalPassengers', start);
|
|
if (isCompare)
|
|
chartDataPassengersCompare = spots;
|
|
else
|
|
chartDataPassengers = spots;
|
|
}
|
|
|
|
Future<void> 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<dynamic> jsonData = jsonResponse['message'];
|
|
if (!isCompare &&
|
|
jsonData.isNotEmpty &&
|
|
jsonData[0]['totalMonthly'] != null) {
|
|
totalMonthlyRides = jsonData[0]['totalMonthly'].toString();
|
|
}
|
|
List<FlSpot> spots = _generateSpots(jsonData, 'day', 'totalRides', start);
|
|
if (isCompare)
|
|
chartDataRidesCompare = spots;
|
|
else
|
|
chartDataRides = spots;
|
|
}
|
|
|
|
Future<void> 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<dynamic> jsonData = jsonResponse['message'];
|
|
if (!isCompare &&
|
|
jsonData.isNotEmpty &&
|
|
jsonData[0]['totalMonthlyDrivers'] != null) {
|
|
totalMonthlyDrivers = jsonData[0]['totalMonthlyDrivers'].toString();
|
|
}
|
|
if (!isCompare) {
|
|
staticList = jsonData;
|
|
}
|
|
|
|
List<FlSpot> spotsDrivers =
|
|
_generateSpots(jsonData, 'day', 'dailyTotalDrivers', start);
|
|
List<FlSpot> spotsNotes =
|
|
_generateSpots(jsonData, 'day', 'dailyMatchingNotes', start);
|
|
if (isCompare) {
|
|
chartDataDriversCompare = spotsDrivers;
|
|
chartDataDriversMatchingNotesCompare = spotsNotes;
|
|
} else {
|
|
chartDataDrivers = spotsDrivers;
|
|
chartDataDriversMatchingNotes = spotsNotes;
|
|
}
|
|
}
|
|
|
|
Future<void> 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<String, dynamic>;
|
|
if (jsonResponse['status'] == 'failure') return;
|
|
final List<dynamic> jsonData = jsonResponse['message'];
|
|
if (jsonData.isEmpty) return;
|
|
|
|
Map<String, Map<String, double>> 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<String, double>? 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<void> 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<String, dynamic>;
|
|
if (jsonResponse['status'] == 'failure') return;
|
|
|
|
final List<dynamic> jsonData = jsonResponse['message'];
|
|
if (jsonData.isEmpty) return;
|
|
|
|
Map<String, Map<String, double>> 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<String, double>? 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<void> 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<dynamic> data = jsonResponse['message']['data'];
|
|
|
|
List<String> allowedNames = ['shahd', 'mayar', 'rama1', 'rama2'];
|
|
|
|
// استخدام Map لضمان عدم تكرار الأسماء (تجميع القيم)
|
|
Map<String, int> 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<Map<String, dynamic>> 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<void> 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();
|
|
}
|
|
}
|
|
}
|