Initial commit for intaleq_admin
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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('حدث خطأ من الخادم. حاول مجددًا.');
|
||||
|
||||
37
lib/controller/drivers/driver_not_active_controller.dart
Normal file
37
lib/controller/drivers/driver_not_active_controller.dart
Normal 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
59
lib/controller/firebase/notification_service.dart
Normal file
59
lib/controller/firebase/notification_service.dart
Normal 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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._(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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',
|
||||
|
||||
139
lib/controller/rides/ride_lookup_controller.dart
Normal file
139
lib/controller/rides/ride_lookup_controller.dart
Normal 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";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user