Initial commit for intaleq_admin

This commit is contained in:
Hamza-Ayed
2026-01-20 23:39:59 +03:00
parent 0b17f93aaa
commit a367bc7e5c
53 changed files with 20383 additions and 14662 deletions

View File

@@ -32,6 +32,42 @@ class CaptainAdminController extends GetxController {
update();
}
Future deletCaptain() async {
isLoading = true;
update();
var res = await CRUD().get(
link: AppLink.deleteCaptain,
payload: {},
);
var d = jsonDecode(res);
if (d['status'] == 'success') {
captainData = d;
}
isLoading = false;
update();
}
Future find_driver_by_phone(String phone) async {
isLoading = true;
update();
var res = await CRUD().post(
link: AppLink.find_driver_by_phone,
payload: {'phone': phone},
);
var d = (res);
if (d != 'failure') {
captainData = d;
} else {
captainData = {};
Get.snackbar('Error', 'No captain found with this phone number',
backgroundColor: AppColor.redColor);
}
isLoading = false;
update();
}
Future addCaptainPrizeToWallet() async {
String? paymentId;
//todo link to add wallet to captain

View File

@@ -21,41 +21,47 @@ class DashboardController extends GetxController {
isLoading = true;
update();
// الطلب من السيرفر الرئيسي
// 🔹 Request main dashboard data
var res = await CRUD().get(link: AppLink.getdashbord, payload: {});
print('📡 Main dashboard response: $res');
if (res != 'failure') {
var d = jsonDecode(res);
// Log.print('d: ${d}');
dashbord = d['message']; // هذا عبارة عن List<Map>
print('✅ Decoded main dashboard: ${jsonEncode(d)}');
dashbord = d['message'];
} else {
print('❌ Failed to load main dashboard');
}
// الطلب من سيرفر المحافظ
// 🔹 Request wallet dashboard data
var resPayments = await CRUD().postWallet(
link: AppLink.getPaymentsDashboard,
payload: {},
);
print('💳 Wallet dashboard response: $resPayments');
if (resPayments != 'failure') {
var p = resPayments;
// Log.print('p: ${p}');
print('✅ Decoded wallet dashboard: ${jsonEncode(p)}');
// نتأكد أن الكل Map بداخل List
if (dashbord.isNotEmpty &&
p['message'] is List &&
p['message'].isNotEmpty) {
dashbord[0].addAll(p['message'][0]); // ندمج المعلومات داخل نفس الـ Map
dashbord[0].addAll(p['message'][0]);
}
} else {
print('❌ Failed to load wallet dashboard');
}
// كريدت الرسائل
// 🔹 Check SMS credit
var res2 = await CRUD().kazumiSMS(
link: 'https://sms.kazumi.me/api/sms/check-credit',
payload: {"username": "Sefer", "password": AK.smsPasswordEgypt},
);
creditSMS = res2['credit'];
Log.print(' res2[credit]: ${res2['credit']}');
Log.print('creditSMS: ${creditSMS}');
print('📱 SMS Credit Response: ${jsonEncode(res2)}');
print('💰 creditSMS: $creditSMS');
isLoading = false;
update();
@@ -70,7 +76,7 @@ class DashboardController extends GetxController {
// box.read(BoxName.tokensDrivers)['message'][i]['phone'].toString(),
smsText.text,
);
// Log.print('CRUD().phoneDriversTest.: ${phoneNumber['phone']}');
// print('CRUD().phoneDriversTest.: ${phoneNumber['phone']}');
Future.delayed(const Duration(microseconds: 20));
}
Get.back();

View File

@@ -43,6 +43,70 @@ class PassengerAdminController extends GetxController {
Get.back();
}
// داخل الـController نفسه
Future<bool> updatePassenger({
required String id, // أو مرّر phoneLookup بدل id لو حاب
String? firstName,
String? lastName,
String? phone,
}) async {
// لا نرسل طلب إذا ما في أي تغيير
if ((firstName == null || firstName.trim().isEmpty) &&
(lastName == null || lastName.trim().isEmpty) &&
(phone == null || phone.trim().isEmpty)) {
return false;
}
// فلتر بسيط للأرقام فقط
// String _normalizePhone(String s) => s.replaceAll(RegExp(r'\D+'), '');
final Map<String, dynamic> payload = {
'id':
id, // لو بدك تستخدم phone_lookup بدل id: احذف هذا وأرسل {'phone_lookup': phoneLookup}
};
if (firstName != null && firstName.trim().isNotEmpty) {
payload['first_name'] = firstName.trim();
}
if (lastName != null && lastName.trim().isNotEmpty) {
payload['last_name'] = lastName.trim();
}
if (phone != null && phone.trim().isNotEmpty) {
payload['phone'] = (phone);
}
// حالة تحميل
isLoading = true;
update();
try {
final res = await CRUD().post(
link: AppLink.admin_update_passenger, // عدّل الرابط حسب اسم مسارك
payload: payload,
);
final d = (res);
final ok = (d['status'] == 'success');
if (ok) {
// (اختياري) حدّث الكاش/الواجهة — مثلاً أعد الجلب
Get.snackbar('Update successful',
d['message']?.toString() ?? 'Passenger updated successfully',
backgroundColor: AppColor.greenColor);
// await getPassengerCount(); // أو حدّث passengersData محليًا إذا متاح
} else {
// (اختياري) أظهر رسالة خطأ
// Get.snackbar('Update failed', d['message']?.toString() ?? 'Unknown error');
}
return ok;
} catch (e) {
// Get.snackbar('Error', e.toString());
return false;
} finally {
isLoading = false;
update();
}
}
void addPassengerPrizeToWalletSecure() async {
try {

View File

@@ -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();
}
}
}

View File

@@ -21,13 +21,15 @@ class OtpHelper extends GetxController {
/// إرسال OTP
static Future<bool> sendOtp(String phoneNumber) async {
try {
// await CRUD().getJWT();
final response = await CRUD().post(
link: _sendOtpUrl,
payload: {'receiver': phoneNumber},
);
// Log.print('_sendOtpUrl: ${_sendOtpUrl}');
// Log.print('response: ${response}');
if (response != 'failure') {
mySnackeBarError('تم إرسال رمز التحقق إلى رقمك عبر WhatsApp');
mySnackbarSuccess('تم إرسال رمز التحقق إلى رقمك عبر WhatsApp');
return true;
} else {
mySnackeBarError('حدث خطأ من الخادم. حاول مجددًا.');

View File

@@ -0,0 +1,37 @@
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
class DriverController extends GetxController {
List drivers = [];
Map driverDetails = {};
// جلب السائقين pending
getDriversPending() async {
var res = await CRUD().post(
link: AppLink.getDriversPending, // رابط drivers_pending_list.php
payload: {},
);
if (res != 'failure') {
drivers = (res)['message'];
update(['drivers']); // تحديث الـ UI
} else {
Get.snackbar('Error', 'Failed to load drivers');
}
}
// جلب تفاصيل سائق واحد
getDriverDetails(String driverId) async {
var res = await CRUD().post(
link: AppLink.getDriverDetails, // رابط driver_details.php
payload: {"id": driverId},
);
if (res != 'failure') {
driverDetails = (res)['message'];
update(['driverDetails']); // تحديث صفحة التفاصيل
} else {
Get.snackbar('Error', 'Failed to load driver details');
}
}
}

View File

@@ -0,0 +1,59 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../../print.dart';
class NotificationService {
// تأكد من أن هذا هو الرابط الصحيح لملف الإرسال
static const String _serverUrl =
'https://syria.intaleq.xyz/intaleq/fcm/send_fcm.php';
static Future<void> sendNotification({
required String target,
required String title,
required String body,
required String? category, // <-- [الإضافة الأولى]
String? tone,
List<String>? driverList,
bool isTopic = false,
}) async {
try {
final Map<String, dynamic> payload = {
'target': target,
'title': title,
'body': body,
'isTopic': isTopic,
};
if (category != null) {
payload['category'] = category; // <-- [الإضافة الثانية]
}
if (tone != null) {
payload['tone'] = tone;
}
if (driverList != null) {
// [مهم] تطبيق السائق يرسل passengerList
payload['passengerList'] = jsonEncode(driverList);
}
final response = await http.post(
Uri.parse(_serverUrl),
headers: {
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(payload),
);
if (response.statusCode == 200) {
print('✅ Notification sent successfully.');
} else {
print(
'❌ Failed to send notification. Status code: ${response.statusCode}');
}
} catch (e) {
print('❌ An error occurred while sending notification: $e');
}
}
}

View File

@@ -1,8 +1,6 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:jwt_decoder/jwt_decoder.dart';
@@ -16,9 +14,7 @@ import '../../constant/links.dart';
import '../../env/env.dart';
import '../../main.dart';
import '../../print.dart';
import '../../views/widgets/elevated_btn.dart';
import 'device_info.dart';
import 'encrypt_decrypt.dart';
import 'security_checks.dart';
class CRUD {
@@ -225,7 +221,7 @@ class CRUD {
String fingerPrint = await DeviceHelper.getDeviceFingerprint();
print('fingerPrint: ${fingerPrint}');
await SecurityChecks.isDeviceRootedFromNative(Get.context!);
//await SecurityChecks.isDeviceRootedFromNative(Get.context!);
dev = Platform.isAndroid ? 'android' : 'ios';
var payload = {
@@ -239,11 +235,11 @@ class CRUD {
Uri.parse(AppLink.loginWalletAdmin),
body: payload,
);
// Log.print('response.request: ${response1.request}');
// Log.print('response.body: ${response1.body}');
// print(payload);
// Log.print(
// 'jsonDecode(response1.body)["jwt"]: ${jsonDecode(response1.body)['jwt']}');
Log.print('response.request: ${response1.request}');
Log.print('response.body: ${response1.body}');
print(payload);
Log.print(
'jsonDecode(response1.body)["jwt"]: ${jsonDecode(response1.body)['jwt']}');
await box.write(BoxName.hmac, jsonDecode(response1.body)['hmac']);
return jsonDecode(response1.body)['jwt'].toString();
}
@@ -268,9 +264,9 @@ class CRUD {
'X-HMAC-Auth': hmac.toString(),
},
);
Log.print('response.request:${response.request}');
Log.print('response.body: ${response.body}');
Log.print('payload:$payload');
// Log.print('response.request:${response.request}');
// Log.print('response.body: ${response.body}');
// Log.print('payload:$payload');
if (response.statusCode == 200) {
try {
var jsonData = jsonDecode(response.body);

View File

@@ -5,12 +5,6 @@
import 'dart:async';
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:device_info_plus/device_info_plus.dart';
import '../../main.dart';
class DeviceHelper {
static Future<String> getDeviceFingerprint() async {
@@ -27,13 +21,17 @@ class DeviceHelper {
// Fetch iOS-specific device information
IosDeviceInfo iosInfo = await deviceInfoPlugin.iosInfo;
deviceData = iosInfo.toMap(); // Convert to a map for easier access
} else if (Platform.isMacOS) {
// Fetch macOS-specific device information
MacOsDeviceInfo macInfo = await deviceInfoPlugin.macOsInfo;
deviceData = macInfo.toMap();
} else {
throw UnsupportedError('Unsupported platform');
}
// Extract relevant device information
final String deviceId = Platform.isAndroid
? deviceData['androidId'] ?? deviceData['serialNumber'] ?? 'unknown'
? deviceData['fingerprint'] ?? 'unknown'
: deviceData['identifierForVendor'] ?? 'unknown';
final String deviceModel = deviceData['model'] ?? 'unknown';
@@ -45,6 +43,7 @@ class DeviceHelper {
// Generate and return the encrypted fingerprint
final String fingerprint = '${deviceId}_${deviceModel}_$osVersion';
// print(EncryptionHelper.instance.encryptData(fingerprint));
return (fingerprint);
} catch (e) {

View File

@@ -30,10 +30,10 @@ class EncryptionHelper {
}
debugPrint("Initializing EncryptionHelper...");
var keyOfApp = r(Env.keyOfApp).toString().split(Env.addd)[0];
Log.print('keyOfApp: ${keyOfApp}');
// Log.print('keyOfApp: ${keyOfApp}');
var initializationVector =
r(Env.initializationVector).toString().split(Env.addd)[0];
Log.print('initializationVector: ${initializationVector}');
// Log.print('initializationVector: ${initializationVector}');
// Set the global instance
_instance = EncryptionHelper._(

View File

@@ -13,8 +13,10 @@ import 'package:path_provider/path_provider.dart' as path_provider;
import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/info.dart';
import '../../main.dart';
import '../../print.dart';
import 'encrypt_decrypt.dart';
class ImageController extends GetxController {
File? myImage;
@@ -152,7 +154,7 @@ class ImageController extends GetxController {
} catch (e) {
print('Error in choosImage: $e');
Get.snackbar('Image Upload Failed'.tr, e.toString(),
backgroundColor: AppColor.primaryColor);
backgroundColor: AppColor.redColor);
} finally {
isloading = false;
update();
@@ -241,20 +243,25 @@ class ImageController extends GetxController {
'POST',
Uri.parse(link),
);
Log.print('request: ${request}');
var length = await file.length();
var stream = http.ByteStream(file.openRead());
final headers = {
'Authorization':
'Bearer ${r(box.read(BoxName.jwt)).split(AppInformation.addd)[0]}',
// 'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
};
Log.print('headers: ${headers}');
var multipartFile = http.MultipartFile(
'image',
stream,
length,
filename: basename(file.path),
);
request.headers.addAll({
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',
});
request.headers.addAll(headers);
// Set the file name to the driverID
request.files.add(
http.MultipartFile(
'image',
@@ -270,8 +277,10 @@ class ImageController extends GetxController {
var res = await http.Response.fromStream(myrequest);
if (res.statusCode == 200) {
Log.print('jsonDecode(res.body): ${jsonDecode(res.body)}');
Get.snackbar('title', 'message', backgroundColor: AppColor.greenColor);
if (jsonDecode(res.body)['status'] == 'Image uploaded successfully!') {
Get.snackbar('Success'.tr, 'Image uploaded successfully!'.tr,
backgroundColor: AppColor.greenColor);
}
return jsonDecode(res.body);
} else {
throw Exception(

View File

@@ -43,6 +43,38 @@ class WalletController extends GetxController {
}
}
Future addDriverWallet(String paymentMethod, driverID, point, phone) async {
// paymentToken = await generateToken(count);
// var paymentID = await getPaymentId(paymentMethod, point.toString());
await CRUD().postWallet(link: AppLink.addFromAdmin, payload: {
'driverID': driverID.toString(),
'paymentID': 'gift_connect_$driverID${DateTime.timestamp()}'.toString(),
'amount': point,
'token': 'gift_connect',
'paymentMethod': paymentMethod,
'phone': phone,
});
}
Future addDrivergift3000(String paymentMethod, driverID, point, phone) async {
// paymentToken = await generateToken(count);
// var paymentID = await getPaymentId(paymentMethod, point.toString());
var res = await CRUD().postWallet(link: AppLink.add300ToDriver, payload: {
'driverID': driverID.toString(),
'paymentID': paymentMethod,
'amount': point,
'token': 'gift_connect_30000',
'paymentMethod': paymentMethod,
'phone': phone,
});
if (res != 'failure') {
Get.snackbar('success', 'addDrivergift3000',
backgroundColor: AppColor.greenColor);
} else {
Get.snackbar('error', res, backgroundColor: AppColor.redColor);
}
}
Future addSeferWallet(String point, driverID) async {
var amount = (int.parse(point) * -1).toStringAsFixed(0);
var seferToken = await generateTokenDriver(amount, driverID);

View File

@@ -10,6 +10,7 @@ import 'package:sefer_admin1/views/widgets/my_textField.dart';
import '../constant/style.dart';
import '../print.dart';
import 'firebase/notification_service.dart';
class NotificationController extends GetxController {
final formKey = GlobalKey<FormState>();
@@ -18,14 +19,14 @@ class NotificationController extends GetxController {
List<String> tokensDriver = [];
List<String> tokensPassengers = [];
getTokensDrivers() async {
await FirebaseMessagesController().loadAllPagesAndSendNotifications();
}
// getTokensDrivers() async {
// await FirebaseMessagesController().loadAllPagesAndSendNotifications();
// }
getTokensPassengers() async {
await FirebaseMessagesController()
.loadAllPagesAndSendNotificationsPassengers();
}
// getTokensPassengers() async {
// await FirebaseMessagesController()
// .loadAllPagesAndSendNotificationsPassengers();
// }
Future<dynamic> sendNotificationDrivers() {
return Get.defaultDialog(
@@ -60,34 +61,40 @@ class NotificationController extends GetxController {
// tokensDriver = box.read(BoxName.tokensDrivers)['message'];
// Log.print('tokensDriver: ${tokensDriver}');
// if (formKey.currentState!.validate()) {
box.read(BoxName.tokensDrivers)['message'].length;
for (var i = 0;
i < box.read(BoxName.tokensDrivers)['message'].length;
i++) {
// for (var i = 0; i < 2; i++) {
// print(i);
var res = await CRUD()
.post(link: AppLink.addNotificationCaptain, payload: {
"driverID": box
.read(BoxName.tokensDrivers)['message'][i]['id']
.toString(),
"title": title.text,
"body": body.text,
"isPin": 'unPin',
});
Log.print(
'res: ${res}for ${box.read(BoxName.tokensDrivers)['message'][i]['id']}');
// Log.print('tokensDriver[i]: ${tokensDriver[i]}');
Future.delayed(const Duration(microseconds: 50));
FirebaseMessagesController().sendNotificationToAnyWithoutData(
title.text,
body.text,
box
.read(BoxName.tokensDrivers)['message'][i]['token']
.toString(),
'tone2.wav');
}
// box.read(BoxName.tokensDrivers)['message'].length;
// for (var i = 0;
// i < box.read(BoxName.tokensDrivers)['message'].length;
// i++) {
// // for (var i = 0; i < 2; i++) {
// // print(i);
// var res = await CRUD()
// .post(link: AppLink.addNotificationCaptain, payload: {
// "driverID": box
// .read(BoxName.tokensDrivers)['message'][i]['id']
// .toString(),
// "title": title.text,
// "body": body.text,
// "isPin": 'unPin',
// });
// Log.print(
// 'res: ${res}for ${box.read(BoxName.tokensDrivers)['message'][i]['id']}');
// // Log.print('tokensDriver[i]: ${tokensDriver[i]}');
// Future.delayed(const Duration(microseconds: 50));
NotificationService.sendNotification(
target: 'drivers', // الإرسال لجميع المشتركين في "service"
title: title.text,
body: body.text,
isTopic: true,
category: 'fromAdmin', // فئة توضح نوع الإشعار
);
// FirebaseMessagesController().sendNotificationToAnyWithoutData(
// title.text,
// body.text,
// box
// .read(BoxName.tokensDrivers)['message'][i]['token']
// .toString(),
// 'tone2.wav');
// }
Get.back();
// }
}),
@@ -129,38 +136,45 @@ class NotificationController extends GetxController {
title: 'send'.tr,
onPressed: () async {
// tokensPassengers = box.read(BoxName.tokensPassengers);
var tokensPassengersData =
box.read(BoxName.tokensPassengers)['data'];
// var tokensPassengersData =
// box.read(BoxName.tokensPassengers)['data'];
// Debug print to check structure of the 'data' field
print('Tokens Passengers Data: $tokensPassengersData');
// // Debug print to check structure of the 'data' field
// print('Tokens Passengers Data: $tokensPassengersData');
if (tokensPassengersData is List) {
for (var i = 0; i < tokensPassengersData.length; i++) {
if (formKey.currentState!.validate()) {
var res = await CRUD()
.post(link: AppLink.addNotificationPassenger, payload: {
"passenger_id":
tokensPassengersData[i]['passengerID'].toString(),
"title": title.text,
"body": body.text,
});
Log.print('res: ${res}');
FirebaseMessagesController()
.sendNotificationToAnyWithoutData(
title.text,
body.text,
tokensPassengersData[i]['token']
.toString(), // Access token correctly
'order.wav',
);
}
}
Get.back();
} else {
// Handle the case where 'data' is not a list
print('Data is not a list: $tokensPassengersData');
}
// if (tokensPassengersData is List) {
// for (var i = 0; i < tokensPassengersData.length; i++) {
// if (formKey.currentState!.validate()) {
// var res = await CRUD()
// .post(link: AppLink.addNotificationPassenger, payload: {
// "passenger_id":
// tokensPassengersData[i]['passengerID'].toString(),
// "title": title.text,
// "body": body.text,
// });
// Log.print('res: ${res}');
// FirebaseMessagesController()
// .sendNotificationToAnyWithoutData(
// title.text,
// body.text,
// tokensPassengersData[i]['token']
// .toString(), // Access token correctly
// 'order.wav',
// );
// }
// }
NotificationService.sendNotification(
target: 'passengers', // الإرسال لجميع المشتركين في "service"
title: title.text,
body: body.text,
isTopic: true,
category: 'fromAdmin', // فئة توضح نوع الإشعار
);
Get.back();
// } else {
// // Handle the case where 'data' is not a list
// print('Data is not a list: $tokensPassengersData');
// }
}),
cancel: MyElevatedButton(
title: 'cancel',

View File

@@ -0,0 +1,139 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
class RideLookupController extends GetxController {
final TextEditingController phoneCtrl = TextEditingController();
bool isLoading = false;
Map<String, dynamic>? passenger; // {id, first_name, last_name, phone}
Map<String, dynamic>? ride; // Ride details
// Status filter for the search tab
String currentStatusFilter = '';
// Whitelist of allowed statuses for the Update Dropdown
// UPDATED: Matches the exact types you requested
final List<String> statusOptions = const [
'Pending',
'Accepted',
'EnRoute',
'Arrived',
'Started',
'Completed',
'Canceled',
];
String? selectedStatus;
// Hydrate dropdown value from the current ride data
void hydrateSelectedFromRide() {
final cur = (ride?['status'] ?? '') as String;
selectedStatus = statusOptions.contains(cur) ? cur : null;
update();
}
Future<bool> updateRideStatus({String? note}) async {
if (ride == null) return false;
if (selectedStatus == null || selectedStatus!.isEmpty) return false;
isLoading = true;
update();
try {
final res = await CRUD().post(
link: AppLink.admin_update_ride_status,
payload: {
'id': "${ride!['id']}",
'status': selectedStatus!,
if (note != null && note.trim().isNotEmpty) 'reason': note.trim(),
},
);
final d = jsonDecode(res);
final ok = (d['status'] == 'success');
if (ok) {
// Update local ride details from response
final updated = (d['message'] ?? d)['ride'];
if (updated != null) {
ride = Map<String, dynamic>.from(updated);
}
update();
return true;
} else {
return false;
}
} catch (_) {
return false;
} finally {
isLoading = false;
update();
}
}
// Updated to accept status filter
Future<bool> searchLatest({String? status}) async {
final phone = phoneCtrl.text.trim();
// If status is passed, update the current filter
if (status != null) {
currentStatusFilter = status;
}
// If phone is empty, we stop unless your API supports fetching "latest of all users"
if (phone.isEmpty) {
return false;
}
isLoading = true;
update();
try {
final res = await CRUD().post(
link: AppLink.admin_get_rides_by_phone,
payload: {
'phone': phone,
// If filter is 'All', send empty string to PHP, otherwise send the exact status
'status': currentStatusFilter == 'All' ? '' : currentStatusFilter,
},
);
final d = res;
if (d['status'] == 'success') {
passenger = (d['message'] ?? d)['passenger'];
ride = (d['message'] ?? d)['ride'];
// Hydrate the dropdown for the update section based on the fetched ride
hydrateSelectedFromRide();
update();
return true;
} else {
passenger = null;
ride = null;
update();
return false;
}
} catch (_) {
passenger = null;
ride = null;
update();
return false;
} finally {
isLoading = false;
update();
}
}
String rideHeader() {
if (ride == null) return '';
final id = ride!['id'] ?? '';
final st = (ride!['status'] ?? '').toString();
return "Ride #$id$st";
}
}