|
|
|
|
@@ -1,238 +1,507 @@
|
|
|
|
|
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 '../../models/model/passengers_model.dart';
|
|
|
|
|
import '../../print.dart';
|
|
|
|
|
import '../functions/crud.dart';
|
|
|
|
|
|
|
|
|
|
class StaticController extends GetxController {
|
|
|
|
|
Map<String, dynamic> jsonData1 = {};
|
|
|
|
|
Map<String, dynamic> jsonData2 = {};
|
|
|
|
|
List staticList = [];
|
|
|
|
|
var chartDataPassengers;
|
|
|
|
|
var chartDataDrivers;
|
|
|
|
|
var chartDataDriversCalling;
|
|
|
|
|
var chartDataRides;
|
|
|
|
|
var chartDataEmployee;
|
|
|
|
|
var chartDataEmployeeMaryam;
|
|
|
|
|
var chartDataEmployeeRawda;
|
|
|
|
|
var chartDataEmployeeMena;
|
|
|
|
|
var chartDataEmployeeSefer4;
|
|
|
|
|
var chartDataDriversMatchingNotes;
|
|
|
|
|
// --- 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;
|
|
|
|
|
String totalMonthlyPassengers = '';
|
|
|
|
|
String totalMonthlyRides = '';
|
|
|
|
|
String totalMonthlyEmployee = '';
|
|
|
|
|
String totalMonthlyDrivers = '';
|
|
|
|
|
late List<MonthlyPassengerInstall> passengersData;
|
|
|
|
|
late List<MonthlyRidesInstall> ridesData;
|
|
|
|
|
late List<MonthlyEmployeeData> employeeData;
|
|
|
|
|
late List<MonthlyDriverInstall> driversData;
|
|
|
|
|
|
|
|
|
|
Future<void> fetch() async {
|
|
|
|
|
isLoading = true;
|
|
|
|
|
update(); // Notify the observers about the loading state change
|
|
|
|
|
// --- Daily Notes State ---
|
|
|
|
|
bool isLoadingNotes = false;
|
|
|
|
|
List<dynamic> dailyNotesList = [];
|
|
|
|
|
|
|
|
|
|
var res = await CRUD().get(
|
|
|
|
|
link: AppLink.getPassengersStatic,
|
|
|
|
|
payload: {},
|
|
|
|
|
);
|
|
|
|
|
jsonData1 = jsonDecode(res);
|
|
|
|
|
var jsonResponse = jsonDecode(res) as Map<String, dynamic>;
|
|
|
|
|
isLoading = false;
|
|
|
|
|
final List<dynamic> jsonData = jsonResponse['message'];
|
|
|
|
|
totalMonthlyPassengers = jsonData[0]['totalMonthly'].toString();
|
|
|
|
|
passengersData = jsonData.map<MonthlyPassengerInstall>((item) {
|
|
|
|
|
return MonthlyPassengerInstall.fromJson(item);
|
|
|
|
|
}).toList();
|
|
|
|
|
final List<FlSpot> spots = passengersData
|
|
|
|
|
.map((data) => FlSpot(
|
|
|
|
|
data.day.toDouble(),
|
|
|
|
|
data.totalPassengers.toDouble(),
|
|
|
|
|
))
|
|
|
|
|
.toList();
|
|
|
|
|
chartDataPassengers = spots;
|
|
|
|
|
// --- Chart Data (Current Range) ---
|
|
|
|
|
List<FlSpot> chartDataPassengers = [];
|
|
|
|
|
List<FlSpot> chartDataDrivers = [];
|
|
|
|
|
List<FlSpot> chartDataRides = [];
|
|
|
|
|
List<FlSpot> chartDataDriversMatchingNotes = [];
|
|
|
|
|
|
|
|
|
|
update(); // Notify the observers about the data and loading state change
|
|
|
|
|
// 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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> fetchRides() async {
|
|
|
|
|
isLoading = true;
|
|
|
|
|
update(); // Notify the observers about the loading state change
|
|
|
|
|
|
|
|
|
|
var res = await CRUD().get(
|
|
|
|
|
link: AppLink.getRidesStatic,
|
|
|
|
|
payload: {},
|
|
|
|
|
);
|
|
|
|
|
jsonData1 = jsonDecode(res);
|
|
|
|
|
var jsonResponse = jsonDecode(res) as Map<String, dynamic>;
|
|
|
|
|
isLoading = false;
|
|
|
|
|
final List<dynamic> jsonData = jsonResponse['message'];
|
|
|
|
|
totalMonthlyRides = jsonData[0]['totalMonthly'].toString();
|
|
|
|
|
ridesData = jsonData.map<MonthlyRidesInstall>((item) {
|
|
|
|
|
return MonthlyRidesInstall.fromJson(item);
|
|
|
|
|
}).toList();
|
|
|
|
|
final List<FlSpot> spots = ridesData
|
|
|
|
|
.map((data) => FlSpot(
|
|
|
|
|
data.day.toDouble(),
|
|
|
|
|
data.totalRides.toDouble(),
|
|
|
|
|
))
|
|
|
|
|
.toList();
|
|
|
|
|
chartDataRides = spots;
|
|
|
|
|
|
|
|
|
|
update(); // Notify the observers about the data and loading state change
|
|
|
|
|
// --- Helpers for View ---
|
|
|
|
|
double get daysInPeriod {
|
|
|
|
|
if (startDate == null || endDate == null) return 31;
|
|
|
|
|
return endDate!.difference(startDate!).inDays + 1.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future<void> fetchEmployee() async {
|
|
|
|
|
try {
|
|
|
|
|
isLoading = true;
|
|
|
|
|
update();
|
|
|
|
|
String get currentDateString {
|
|
|
|
|
if (startDate == null || endDate == null) return "";
|
|
|
|
|
return "${DateFormat('yyyy-MM-dd').format(startDate!)} : ${DateFormat('yyyy-MM-dd').format(endDate!)}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var res = await CRUD().get(link: AppLink.getEmployeeStatic, payload: {});
|
|
|
|
|
// --- Date Actions ---
|
|
|
|
|
void updateDateRange(DateTime start, DateTime end) {
|
|
|
|
|
startDate = start;
|
|
|
|
|
endDate = end;
|
|
|
|
|
if (isComparing) _calculateCompareDates();
|
|
|
|
|
getAll();
|
|
|
|
|
update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// First check if the response is valid JSON
|
|
|
|
|
if (res == 'failure') {
|
|
|
|
|
throw FormatException('Invalid response: $res');
|
|
|
|
|
}
|
|
|
|
|
void _calculateCompareDates() {
|
|
|
|
|
if (startDate == null || endDate == null) return;
|
|
|
|
|
Duration duration = endDate!.difference(startDate!);
|
|
|
|
|
compareEndDate = startDate!.subtract(const Duration(days: 1));
|
|
|
|
|
compareStartDate = compareEndDate!.subtract(duration);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var jsonResponse = jsonDecode(res) as Map<String, dynamic>;
|
|
|
|
|
Future<void> toggleComparison() async {
|
|
|
|
|
isComparing = !isComparing;
|
|
|
|
|
if (isComparing) {
|
|
|
|
|
_calculateCompareDates();
|
|
|
|
|
} else {
|
|
|
|
|
compareStartDate = null;
|
|
|
|
|
compareEndDate = null;
|
|
|
|
|
_clearComparisonData();
|
|
|
|
|
}
|
|
|
|
|
await getAll();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialize empty lists for all chart data
|
|
|
|
|
chartDataEmployeeMaryam = <FlSpot>[];
|
|
|
|
|
chartDataEmployeeRawda = <FlSpot>[];
|
|
|
|
|
chartDataEmployeeMena = <FlSpot>[];
|
|
|
|
|
chartDataEmployeeSefer4 = <FlSpot>[];
|
|
|
|
|
totalMonthlyRides = '0';
|
|
|
|
|
void _clearComparisonData() {
|
|
|
|
|
chartDataPassengersCompare.clear();
|
|
|
|
|
chartDataDriversCompare.clear();
|
|
|
|
|
chartDataRidesCompare.clear();
|
|
|
|
|
chartDataDriversMatchingNotesCompare.clear();
|
|
|
|
|
|
|
|
|
|
// Check for error response
|
|
|
|
|
if (jsonResponse['status'] == 'failure') {
|
|
|
|
|
isLoading = false;
|
|
|
|
|
update();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
chartDataEmployeerama1Compare.clear();
|
|
|
|
|
chartDataEmployeeshahdCompare.clear();
|
|
|
|
|
chartDataEmployeeRama2Compare.clear();
|
|
|
|
|
chartDataEmployeeSefer4Compare.clear();
|
|
|
|
|
|
|
|
|
|
final List<dynamic> jsonData = jsonResponse['message'];
|
|
|
|
|
if (jsonData.isEmpty) {
|
|
|
|
|
isLoading = false;
|
|
|
|
|
update();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
chartDataCallsrama1Compare.clear();
|
|
|
|
|
chartDataCallsShahdCompare.clear();
|
|
|
|
|
chartDataCallsRama2Compare.clear();
|
|
|
|
|
chartDataCallsSefer4Compare.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
totalMonthlyRides = jsonData[0]['totalMonthly']?.toString() ?? '0';
|
|
|
|
|
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(),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Group data by employee
|
|
|
|
|
Map<String, List<MonthlyEmployeeData>> employeeDataMap = {};
|
|
|
|
|
// --- Main Fetch Logic ---
|
|
|
|
|
Future getAll() async {
|
|
|
|
|
if (startDate == null || endDate == null) return;
|
|
|
|
|
|
|
|
|
|
for (var item in jsonData) {
|
|
|
|
|
var employeeData = MonthlyEmployeeData.fromJson(item);
|
|
|
|
|
if (!employeeDataMap.containsKey(employeeData.name)) {
|
|
|
|
|
employeeDataMap[employeeData.name] = [];
|
|
|
|
|
}
|
|
|
|
|
employeeDataMap[employeeData.name]!.add(employeeData);
|
|
|
|
|
}
|
|
|
|
|
isLoading = true;
|
|
|
|
|
update();
|
|
|
|
|
|
|
|
|
|
final today = DateTime.now().day;
|
|
|
|
|
await Future.wait([
|
|
|
|
|
fetchPassengers(isCompare: false),
|
|
|
|
|
fetchRides(isCompare: false),
|
|
|
|
|
fetchDrivers(isCompare: false),
|
|
|
|
|
fetchEmployee(isCompare: false),
|
|
|
|
|
fetchEditorCalls(isCompare: false),
|
|
|
|
|
fetchEmploymentStats(),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// Create data for each employee
|
|
|
|
|
final employeeNames = {
|
|
|
|
|
'maryam': chartDataEmployeeMaryam,
|
|
|
|
|
'yasmine': chartDataEmployeeRawda,
|
|
|
|
|
'mena': chartDataEmployeeMena,
|
|
|
|
|
'ashjan': chartDataEmployeeSefer4,
|
|
|
|
|
};
|
|
|
|
|
if (isComparing && compareStartDate != null && compareEndDate != null) {
|
|
|
|
|
await Future.wait([
|
|
|
|
|
fetchPassengers(isCompare: true),
|
|
|
|
|
fetchRides(isCompare: true),
|
|
|
|
|
fetchDrivers(isCompare: true),
|
|
|
|
|
fetchEmployee(isCompare: true),
|
|
|
|
|
fetchEditorCalls(isCompare: true),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
employeeNames.forEach((name, chartData) {
|
|
|
|
|
var spots = <FlSpot>[];
|
|
|
|
|
for (int day = 1; day <= today; day++) {
|
|
|
|
|
spots.add(FlSpot(
|
|
|
|
|
day.toDouble(),
|
|
|
|
|
employeeDataMap[name]
|
|
|
|
|
?.firstWhere(
|
|
|
|
|
(e) => e.day == day,
|
|
|
|
|
orElse: () => MonthlyEmployeeData(
|
|
|
|
|
day: day,
|
|
|
|
|
totalEmployees: 0,
|
|
|
|
|
name: name,
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
.totalEmployees
|
|
|
|
|
.toDouble() ??
|
|
|
|
|
0,
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
isLoading = false;
|
|
|
|
|
update();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Explicitly cast to List<FlSpot>
|
|
|
|
|
if (name == 'maryam')
|
|
|
|
|
chartDataEmployeeMaryam = List<FlSpot>.from(spots);
|
|
|
|
|
if (name == 'yasmine')
|
|
|
|
|
chartDataEmployeeRawda = List<FlSpot>.from(spots);
|
|
|
|
|
if (name == 'mena') chartDataEmployeeMena = List<FlSpot>.from(spots);
|
|
|
|
|
if (name == 'ashjan')
|
|
|
|
|
chartDataEmployeeSefer4 = List<FlSpot>.from(spots);
|
|
|
|
|
});
|
|
|
|
|
} catch (e) {
|
|
|
|
|
Log.print('Error in fetchEmployee: $e');
|
|
|
|
|
// Set empty FlSpot lists in case of error
|
|
|
|
|
chartDataEmployeeMaryam = <FlSpot>[];
|
|
|
|
|
chartDataEmployeeRawda = <FlSpot>[];
|
|
|
|
|
chartDataEmployeeMena = <FlSpot>[];
|
|
|
|
|
chartDataEmployeeSefer4 = <FlSpot>[];
|
|
|
|
|
totalMonthlyRides = '0';
|
|
|
|
|
} finally {
|
|
|
|
|
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> fetchDrivers() async {
|
|
|
|
|
isLoading = true;
|
|
|
|
|
update(); // Notify the observers about the loading state change
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
|
|
var res = await CRUD().get(
|
|
|
|
|
link: AppLink.getdriverstotalMonthly,
|
|
|
|
|
payload: {},
|
|
|
|
|
);
|
|
|
|
|
jsonData2 = jsonDecode(res);
|
|
|
|
|
var jsonResponse = jsonDecode(res) as Map<String, dynamic>;
|
|
|
|
|
isLoading = false;
|
|
|
|
|
final List<dynamic> jsonData = jsonResponse['message'];
|
|
|
|
|
staticList = jsonData;
|
|
|
|
|
totalMonthlyDrivers = jsonData[0]['totalDrivers'].toString();
|
|
|
|
|
driversData = jsonData.map<MonthlyDriverInstall>((item) {
|
|
|
|
|
return MonthlyDriverInstall.fromJson(item);
|
|
|
|
|
}).toList();
|
|
|
|
|
final List<FlSpot> spots = driversData
|
|
|
|
|
.map((data) => FlSpot(
|
|
|
|
|
data.day.toDouble(),
|
|
|
|
|
data.dailyTotalDrivers.toDouble(),
|
|
|
|
|
))
|
|
|
|
|
.toList();
|
|
|
|
|
chartDataDrivers = spots;
|
|
|
|
|
final List<FlSpot> spotsCalling = driversData
|
|
|
|
|
.map((data) => FlSpot(
|
|
|
|
|
data.day.toDouble(),
|
|
|
|
|
data.dailyTotalCallingDrivers.toDouble(),
|
|
|
|
|
))
|
|
|
|
|
.toList();
|
|
|
|
|
chartDataDriversCalling = spotsCalling;
|
|
|
|
|
final List<FlSpot> spotsTotalMatchingNotes = driversData
|
|
|
|
|
.map((data) => FlSpot(
|
|
|
|
|
data.day.toDouble(),
|
|
|
|
|
data.dailyMatchingNotes.toDouble(),
|
|
|
|
|
))
|
|
|
|
|
.toList();
|
|
|
|
|
chartDataDriversMatchingNotes = spotsTotalMatchingNotes;
|
|
|
|
|
if (isCompare) {
|
|
|
|
|
chartDataEmployeerama1Compare = [];
|
|
|
|
|
chartDataEmployeeshahdCompare = [];
|
|
|
|
|
chartDataEmployeeRama2Compare = [];
|
|
|
|
|
chartDataEmployeeSefer4Compare = [];
|
|
|
|
|
} else {
|
|
|
|
|
chartDataEmployeerama1 = [];
|
|
|
|
|
chartDataEmployeeshahd = [];
|
|
|
|
|
chartDataEmployeeRama2 = [];
|
|
|
|
|
chartDataEmployeeSefer4 = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
update(); // Notify the observers about the data and loading state change
|
|
|
|
|
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 getAll() async {
|
|
|
|
|
await fetch();
|
|
|
|
|
await fetchRides();
|
|
|
|
|
await fetchDrivers();
|
|
|
|
|
await fetchEmployee();
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|