Update: 2026-06-26 17:29:23

This commit is contained in:
Hamza-Ayed
2026-06-26 17:29:23 +03:00
parent a323da29aa
commit 9ded734e38
139 changed files with 1815 additions and 2676 deletions

View File

@@ -1,6 +1,5 @@
import '../env/env.dart';
import '../main.dart';
import 'box_name.dart';
class AppLink {
static String seferPaymentServer =

View File

@@ -4,8 +4,8 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:local_auth/local_auth.dart';
import '../../constant/colors.dart';
import '../../constant/links.dart';
import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class CaptainAdminController extends GetxController {
@@ -60,8 +60,7 @@ class CaptainAdminController extends GetxController {
captainData = d;
} else {
captainData = {};
Get.snackbar('Error', 'No captain found with this phone number',
backgroundColor: AppColor.redColor);
mySnackbarError('No captain found with this phone number');
}
isLoading = false;
@@ -102,17 +101,16 @@ class CaptainAdminController extends GetxController {
if (didAuthenticate) {
// User authenticated successfully, proceed with payment
await addCaptainPrizeToWallet();
Get.snackbar('Prize Added', '', backgroundColor: AppColor.greenColor);
mySnackbarSuccess('Prize Added');
} else {
// Authentication failed, handle accordingly
Get.snackbar('Authentication failed', '',
backgroundColor: AppColor.redColor);
mySnackbarError('Authentication failed');
// 'Authentication failed');
}
} else {
// Local authentication not available, proceed with payment without authentication
await addCaptainPrizeToWallet();
Get.snackbar('Prize Added', '', backgroundColor: AppColor.greenColor);
mySnackbarSuccess('Prize Added');
}
} catch (e) {
rethrow;

View File

@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class ComplaintController extends GetxController {
@@ -41,7 +42,7 @@ class ComplaintController extends GetxController {
complaintList.clear();
}
} catch (e) {
Get.snackbar("خطأ", "فشل جلب الشكاوى: $e");
mySnackbarError("فشل جلب الشكاوى: $e");
} finally {
isLoading.value = false;
}
@@ -61,7 +62,7 @@ class ComplaintController extends GetxController {
}
return false;
} catch (e) {
Get.snackbar("خطأ", "فشل تحديث الشكوى: $e");
mySnackbarError("فشل تحديث الشكوى: $e");
return false;
} finally {
isLoading.value = false;

View File

@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class DriverDocsController extends GetxController {
@@ -49,7 +50,7 @@ class DriverDocsController extends GetxController {
}
}
} catch (e) {
Get.snackbar("خطأ", "فشل جلب السائقين: $e");
mySnackbarError("فشل جلب السائقين: $e");
} finally {
isLoading.value = false;
isMoreLoading.value = false;
@@ -70,7 +71,7 @@ class DriverDocsController extends GetxController {
}
}
} catch (e) {
Get.snackbar("خطأ", "فشل جلب تفاصيل السائق: $e");
mySnackbarError("فشل جلب تفاصيل السائق: $e");
}
return null;
}
@@ -88,7 +89,7 @@ class DriverDocsController extends GetxController {
}
return false;
} catch (e) {
Get.snackbar("خطأ", "فشل اعتماد السائق: $e");
mySnackbarError("فشل اعتماد السائق: $e");
return false;
} finally {
isLoading.value = false;

View File

@@ -1,34 +1,51 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class KazanController extends GetxController {
var kazanData = {}.obs;
var isLoading = false.obs;
var selectedCountry = 'Syria'.obs;
final CRUD _crud = CRUD();
final List<Map<String, String>> countries = [
{'code': 'syria', 'name': 'سوريا', 'flag': '🇸🇾'},
{'code': 'jordan', 'name': 'الأردن', 'flag': '🇯🇴'},
{'code': 'egypt', 'name': 'مصر', 'flag': '🇪🇬'},
];
@override
void onInit() {
super.onInit();
getKazan();
}
void setCountry(String countryName) {
selectedCountry.value = countryName;
getKazan();
}
Future<void> getKazan() async {
isLoading.value = true;
try {
var response = await _crud.get(link: "${AppLink.getKazanPercent}?country=syria");
final countryParam = selectedCountry.value.toLowerCase();
var response = await _crud.get(link: "${AppLink.getKazanPercent}?country=$countryParam");
if (response != null && response != 'failure' && response != 'token_expired') {
var decoded = response is String ? jsonDecode(response) : response;
if (decoded['status'] == "success") {
var message = decoded['message'];
if (message is List && message.isNotEmpty) {
kazanData.value = message[0];
kazanData.value = Map<String, dynamic>.from(message[0]);
kazanData['country'] = selectedCountry.value;
} else {
kazanData.value = {'country': selectedCountry.value};
}
}
}
} catch (e) {
Get.snackbar("خطأ", "فشل جلب بيانات التسعير: $e");
mySnackbarError('فشل جلب بيانات التسعير: $e');
} finally {
isLoading.value = false;
}
@@ -37,8 +54,9 @@ class KazanController extends GetxController {
Future<bool> updateKazan(Map<String, dynamic> data) async {
isLoading.value = true;
try {
data['country'] = selectedCountry.value;
final String link = data.containsKey('id') ? AppLink.updateKazanPercent : AppLink.addKazanPercent;
Map<String, String> payload = {};
data.forEach((key, value) {
payload[key] = value.toString();
@@ -51,7 +69,7 @@ class KazanController extends GetxController {
}
return false;
} catch (e) {
Get.snackbar("خطأ", "فشل تحديث التسعير: $e");
mySnackbarError('فشل تحديث التسعير: $e');
return false;
} finally {
isLoading.value = false;

View File

@@ -1,5 +1,6 @@
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class MarketingController extends GetxController {
@@ -38,22 +39,14 @@ class MarketingController extends GetxController {
void toggleAutopilot(bool value) {
isAutopilotEnabled = value;
update();
Get.snackbar(
"Autopilot Updated".tr,
value ? "Full Autopilot mode enabled".tr : "Approval Mode enabled".tr,
snackPosition: SnackPosition.BOTTOM,
);
mySnackbarInfo(value ? "Full Autopilot mode enabled".tr : "Approval Mode enabled".tr);
}
// --- System Prompt Configuration Saver ---
void savePrompt(String newPrompt) {
systemPrompt = newPrompt;
update();
Get.snackbar(
"Configuration Saved".tr,
"Gemini system instructions updated successfully".tr,
snackPosition: SnackPosition.BOTTOM,
);
mySnackbarSuccess("Gemini system instructions updated successfully".tr);
}
Future<void> fetchAnomalies() async {
@@ -72,10 +65,10 @@ class MarketingController extends GetxController {
if (res is Map && res['status'] == 'success') {
priceAnomalies = res['message'] ?? [];
} else {
Get.snackbar("Error", "Failed to fetch price anomalies");
mySnackbarError("Failed to fetch price anomalies");
}
} catch (e) {
Get.snackbar("Error", "Network error while loading anomalies");
mySnackbarError("Network error while loading anomalies");
} finally {
isLoading = false;
update();
@@ -123,10 +116,10 @@ class MarketingController extends GetxController {
if (res is Map && res['status'] == 'success') {
campaignsLog = res['message'] ?? [];
} else {
Get.snackbar("Error", "Failed to fetch campaign logs");
mySnackbarError("Failed to fetch campaign logs");
}
} catch (e) {
Get.snackbar("Error", "Network error while loading campaign logs");
mySnackbarError("Network error while loading campaign logs");
} finally {
isLoading = false;
update();
@@ -175,17 +168,17 @@ class MarketingController extends GetxController {
);
if (res is Map) {
if (res['status'] == 'success') {
Get.snackbar("Success", "AI campaign triggered successfully! Promos sent.");
mySnackbarSuccess("AI campaign triggered successfully! Promos sent.");
fetchCampaignsLog();
fetchTelemetry();
} else {
Get.snackbar("Campaign Alert", res['message'] ?? "Campaign rate limited.");
mySnackbarWarning(res['message'] ?? "Campaign rate limited.");
}
} else {
Get.snackbar("Error", "Failed to trigger AI campaign");
mySnackbarError("Failed to trigger AI campaign");
}
} catch (e) {
Get.snackbar("Error", "Network error while triggering campaign");
mySnackbarError("Network error while triggering campaign");
} finally {
isLoading = false;
update();
@@ -224,10 +217,10 @@ class MarketingController extends GetxController {
simulatorRecommendationMessage = data['recommendation_message'];
}
} else {
Get.snackbar("Simulation Error", res['message'] ?? "Failed to run simulation");
mySnackbarError(res['message'] ?? "Failed to run simulation");
}
} catch (e) {
Get.snackbar("Error", "Network error during simulation");
mySnackbarError("Network error during simulation");
} finally {
isLoading = false;
update();
@@ -340,9 +333,4 @@ class MarketingController extends GetxController {
}
}
@override
void onInit() {
super.onInit();
// Initially fetch these too if needed, but fetch them specifically when country changes
}
}

View File

@@ -4,8 +4,8 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:local_auth/local_auth.dart';
import '../../constant/colors.dart';
import '../../constant/links.dart';
import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class PassengerAdminController extends GetxController {
@@ -90,9 +90,7 @@ class PassengerAdminController extends GetxController {
final ok = (d['status'] == 'success');
if (ok) {
// (اختياري) حدّث الكاش/الواجهة — مثلاً أعد الجلب
Get.snackbar('Update successful',
d['message']?.toString() ?? 'Passenger updated successfully',
backgroundColor: AppColor.greenColor);
mySnackbarSuccess(d['message']?.toString() ?? 'Passenger updated successfully');
// await getPassengerCount(); // أو حدّث passengersData محليًا إذا متاح
} else {
// (اختياري) أظهر رسالة خطأ
@@ -120,17 +118,16 @@ class PassengerAdminController extends GetxController {
if (didAuthenticate) {
// User authenticated successfully, proceed with payment
await addPassengerPrizeToWallet();
Get.snackbar('Prize Added', '', backgroundColor: AppColor.greenColor);
mySnackbarSuccess('Prize Added');
} else {
// Authentication failed, handle accordingly
Get.snackbar('Authentication failed', '',
backgroundColor: AppColor.redColor);
mySnackbarError('Authentication failed');
// 'Authentication failed');
}
} else {
// Local authentication not available, proceed with payment without authentication
await addPassengerPrizeToWallet();
Get.snackbar('Prize Added', '', backgroundColor: AppColor.greenColor);
mySnackbarSuccess('Prize Added');
}
} catch (e) {
rethrow;

View File

@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class PromoController extends GetxController {
@@ -27,7 +28,7 @@ class PromoController extends GetxController {
promoList.clear();
}
} catch (e) {
Get.snackbar("خطأ", "فشل جلب أكواد الخصم: $e");
mySnackbarError("فشل جلب أكواد الخصم: $e");
} finally {
isLoading.value = false;
}
@@ -43,7 +44,7 @@ class PromoController extends GetxController {
}
return false;
} catch (e) {
Get.snackbar("خطأ", "فشل إضافة كود الخصم: $e");
mySnackbarError("فشل إضافة كود الخصم: $e");
return false;
} finally {
isLoading.value = false;
@@ -59,7 +60,7 @@ class PromoController extends GetxController {
}
return false;
} catch (e) {
Get.snackbar("خطأ", "فشل حذف كود الخصم: $e");
mySnackbarError("فشل حذف كود الخصم: $e");
return false;
}
}
@@ -74,7 +75,7 @@ class PromoController extends GetxController {
}
return false;
} catch (e) {
Get.snackbar("خطأ", "فشل تحديث كود الخصم: $e");
mySnackbarError("فشل تحديث كود الخصم: $e");
return false;
} finally {
isLoading.value = false;

View File

@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class QualityController extends GetxController {
@@ -21,10 +21,10 @@ class QualityController extends GetxController {
driversBlacklist = res['message']['drivers'] ?? [];
passengersBlacklist = res['message']['passengers'] ?? [];
} else {
Get.snackbar("Error", "Failed to fetch blacklist");
mySnackbarError("Failed to fetch blacklist");
}
} catch (e) {
Get.snackbar("Error", "Network error");
mySnackbarError("Network error");
} finally {
isLoading = false;
update();
@@ -41,13 +41,13 @@ class QualityController extends GetxController {
},
);
if (res is Map && res['status'] == 'success') {
Get.snackbar("Success", "Driver unblocked successfully");
mySnackbarSuccess("Driver unblocked successfully");
fetchBlacklist(); // Refresh
} else {
Get.snackbar("Error", res['message'] ?? "Failed to unblock driver");
mySnackbarError(res['message'] ?? "Failed to unblock driver");
}
} catch (e) {
Get.snackbar("Error", "Network error");
mySnackbarError("Network error");
}
}
@@ -61,13 +61,13 @@ class QualityController extends GetxController {
},
);
if (res is Map && res['status'] == 'success') {
Get.snackbar("Success", "Passenger unblocked successfully");
mySnackbarSuccess("Passenger unblocked successfully");
fetchBlacklist(); // Refresh
} else {
Get.snackbar("Error", res['message'] ?? "Failed to unblock passenger");
mySnackbarError(res['message'] ?? "Failed to unblock passenger");
}
} catch (e) {
Get.snackbar("Error", "Network error");
mySnackbarError("Network error");
}
}
@@ -82,11 +82,11 @@ class QualityController extends GetxController {
if (res is Map && res['status'] == 'success') {
scorecardData = res['message'];
} else {
Get.snackbar("Error", "Failed to fetch scorecard");
mySnackbarError("Failed to fetch scorecard");
scorecardData = {};
}
} catch (e) {
Get.snackbar("Error", "Network error");
mySnackbarError("Network error");
scorecardData = {};
} finally {
isLoading = false;
@@ -94,9 +94,4 @@ class QualityController extends GetxController {
}
}
@override
void onInit() {
super.onInit();
// fetchBlacklist() can be called when opening the page
}
}

View File

@@ -1,16 +1,15 @@
import 'dart:convert';
import 'dart:ffi';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/info.dart';
import '../../constant/links.dart';
import '../../constant/style.dart';
import '../../main.dart';
import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class RegisterCaptainController extends GetxController {
@@ -102,7 +101,7 @@ class RegisterCaptainController extends GetxController {
driverNotCompleteRegistration = d;
update();
} else {
Get.snackbar(res, '');
mySnackbarError(res);
}
}
@@ -279,9 +278,7 @@ class RegisterCaptainController extends GetxController {
await addRegistrationCarEgypt();
if (isCarSaved && isDriverSaved) {
Get.snackbar('added', '',
backgroundColor:
AppColor.greenColor); // Get.offAll(() => HomeCaptain());
mySnackbarSuccess('added'); // Get.offAll(() => HomeCaptain());
// Get.offAll(() => HomeCaptain());
}
}
@@ -348,11 +345,9 @@ class RegisterCaptainController extends GetxController {
// Handle response
if (status1['status'] == 'success') {
isDriverSaved = true;
Get.snackbar('Success', 'Driver data saved successfully',
backgroundColor: AppColor.greenColor);
mySnackbarSuccess('Driver data saved successfully');
} else {
Get.snackbar('Error', 'Failed to save driver data',
backgroundColor: Colors.red);
mySnackbarError('Failed to save driver data');
}
}
@@ -363,7 +358,7 @@ class RegisterCaptainController extends GetxController {
"InspectionResult": responseCriminalRecordEgypt['InspectionResult'],
});
if (res != 'failure') {
Get.snackbar('uploaded sucssefuly'.tr, '');
mySnackbarSuccess('uploaded sucssefuly'.tr);
}
}
@@ -394,8 +389,7 @@ class RegisterCaptainController extends GetxController {
var status = jsonDecode(res);
if (status['status'] == 'success') {
isCarSaved = true;
Get.snackbar('Success', 'message',
backgroundColor: AppColor.greenColor);
mySnackbarSuccess('message');
}
} catch (e) {}
}
@@ -557,7 +551,6 @@ class RegisterCaptainController extends GetxController {
var responseData = jsonDecode(response.body);
// Process the responseData as needed
var result = responseData['candidates'][0]['content']['parts'][0]['text'];
RegExp regex = RegExp(r"```json([^`]*)```");
String? jsonString =
regex.firstMatch(responseData.toString())?.group(1)?.trim();
@@ -580,8 +573,7 @@ class RegisterCaptainController extends GetxController {
update();
} else {
Get.snackbar('Error', "JSON string not found",
backgroundColor: AppColor.redColor);
mySnackbarError("JSON string not found");
}
// Rest of your code...

View File

@@ -1,8 +1,8 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/constant/links.dart';
import 'package:siro_admin/controller/functions/crud.dart';
import 'package:siro_admin/views/widgets/snackbar.dart';
import '../../print.dart';
class SecurityV2Controller extends GetxController {
@@ -26,9 +26,7 @@ class SecurityV2Controller extends GetxController {
if (res == 'failure' || res == 'token_expired') {
Log.print('CRUD returned: $res');
Get.snackbar("خطأ بالاتصال", "السيرفر أرجع: $res",
backgroundColor: const Color(0x88FF0000),
colorText: const Color(0xFFFFFFFF));
mySnackbarError("السيرفر أرجع: $res");
auditLogs = [];
} else if (res != null) {
var d = res is String ? jsonDecode(res) : res;
@@ -44,16 +42,12 @@ class SecurityV2Controller extends GetxController {
}
} else {
Log.print('Status not success: ${d['status']}');
Get.snackbar("خطأ من السيرفر", "${d['message'] ?? d['status']}",
backgroundColor: const Color(0x88FF0000),
colorText: const Color(0xFFFFFFFF));
mySnackbarError("${d['message'] ?? d['status']}");
}
}
} catch (e) {
Log.print('Error fetching audit logs: $e');
Get.snackbar("خطأ برمجي", "$e",
backgroundColor: const Color(0x88FF0000),
colorText: const Color(0xFFFFFFFF));
mySnackbarError("$e");
}
isLoading = false;

View File

@@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../functions/crud.dart';
import '../functions/device_info.dart';
import '../../views/widgets/snackbar.dart';
class StaffController extends GetxController {
@@ -53,10 +52,10 @@ class StaffController extends GetxController {
_clearFields();
Get.back();
} else {
mySnackeBarError('فشل في إضافة الموظف. يرجى المحاولة لاحقاً');
mySnackbarError('فشل في إضافة الموظف. يرجى المحاولة لاحقاً');
}
} catch (e) {
mySnackeBarError('حدث خطأ: $e');
mySnackbarError('حدث خطأ: $e');
} finally {
isLoading = false;
update();

View File

@@ -303,10 +303,11 @@ class StaticController extends GetxController {
totalMonthlyPassengers = data[0]['totalMonthly'].toString();
}
final spots = _generateSpots(data, 'day', 'totalPassengers', start, end);
if (isCompare)
if (isCompare) {
chartDataPassengersCompare = spots;
else
} else {
chartDataPassengers = spots;
}
}
// ─── Rides ────────────────────────────────────────────────
@@ -322,10 +323,11 @@ class StaticController extends GetxController {
totalMonthlyRides = data[0]['totalMonthly'].toString();
}
final spots = _generateSpots(data, 'day', 'totalRides', start, end);
if (isCompare)
if (isCompare) {
chartDataRidesCompare = spots;
else
} else {
chartDataRides = spots;
}
}
// ─── Drivers ──────────────────────────────────────────────

View File

@@ -7,7 +7,6 @@ import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/links.dart';
import '../../main.dart';
import '../../print.dart';
import '../functions/crud.dart';
class WalletAdminController extends GetxController {
@@ -46,7 +45,7 @@ class WalletAdminController extends GetxController {
driversWalletPoints[i]['email'].toString(),
);
await Future.delayed(const Duration(seconds: 3));
} on FormatException catch (e) {
} on FormatException {
// Handle the error or rethrow the exception as needed
}
}

View File

@@ -1,11 +1,10 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'otp_helper.dart';
class OtpVerificationAdmin extends StatefulWidget {
final String phone;
const OtpVerificationAdmin({required this.phone});
const OtpVerificationAdmin({super.key, required this.phone});
@override
State<OtpVerificationAdmin> createState() => _OtpVerificationAdminState();

View File

@@ -37,12 +37,12 @@ class OtpHelper extends GetxController {
mySnackbarSuccess('تم إرسال رمز التحقق إلى رقمك عبر WhatsApp');
return true;
} else {
mySnackeBarError('حدث خطأ من الخادم. حاول مجددًا.');
mySnackbarError('حدث خطأ من الخادم. حاول مجددًا.');
return false;
}
} catch (e) {
Log.print('OTP SEND ERROR: $e');
mySnackeBarError('حدث خطأ أثناء الإرسال: $e');
mySnackbarError('حدث خطأ أثناء الإرسال: $e');
return false;
}
}
@@ -68,14 +68,14 @@ class OtpHelper extends GetxController {
mySnackbarSuccess('تم التحقق من الرقم بنجاح');
await checkAdminLogin();
} else {
mySnackeBarError(response['message'] ?? 'فشل في التحقق.');
mySnackbarError(response['message'] ?? 'فشل في التحقق.');
}
} else {
mySnackeBarError('فشل من الخادم. حاول مرة أخرى.');
mySnackbarError('فشل من الخادم. حاول مرة أخرى.');
}
} catch (e) {
Log.print('OTP VERIFY ERROR: $e');
mySnackeBarError('خطأ في التحقق: $e');
mySnackbarError('خطأ في التحقق: $e');
}
}
@@ -110,7 +110,7 @@ class OtpHelper extends GetxController {
}
} catch (e) {
Log.print('LOGIN ERROR: $e');
mySnackeBarError('حدث خطأ أثناء تسجيل الدخول: $e');
mySnackbarError('حدث خطأ أثناء تسجيل الدخول: $e');
return false;
}
}
@@ -141,7 +141,7 @@ class OtpHelper extends GetxController {
}
} catch (e) {
Log.print('OTP VERIFY LOGIN ERROR: $e');
mySnackeBarError('خطأ في التحقق من الرمز: $e');
mySnackbarError('خطأ في التحقق من الرمز: $e');
}
}
@@ -161,8 +161,9 @@ class OtpHelper extends GetxController {
await box.write('admin_role', role);
Log.print('Admin role saved: $role');
}
if (data['phone'] != null)
if (data['phone'] != null) {
await box.write(BoxName.adminPhone, data['phone']);
}
}
await box.write(BoxName.phoneVerified, true);
@@ -197,7 +198,7 @@ class OtpHelper extends GetxController {
Get.back();
verifyLoginOtp(phone, otpCode, password, fingerprint);
} else {
mySnackeBarError('الرجاء إدخال رمز صحيح');
mySnackbarError('الرجاء إدخال رمز صحيح');
}
},
);

View File

@@ -2,7 +2,8 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/constant/links.dart';
import 'package:siro_admin/controller/functions/crud.dart';
import 'package:siro_admin/main.dart'; // للوصول لـ box
import 'package:siro_admin/main.dart';
import 'package:siro_admin/views/widgets/snackbar.dart';
class AdminRegisterController extends GetxController {
final nameCtrl = TextEditingController();
@@ -32,20 +33,14 @@ class AdminRegisterController extends GetxController {
if (response != 'failure') {
if (response['status'] == 'pending') {
Get.snackbar('نجاح', response['message'] ?? 'تم تقديم الطلب بنجاح',
backgroundColor: Colors.green.withOpacity(0.8),
colorText: Colors.white);
mySnackbarSuccess(response['message'] ?? 'تم تقديم الطلب بنجاح');
Future.delayed(const Duration(seconds: 2), () => Get.back());
} else {
Get.snackbar('خطأ', 'حدث خطأ غير متوقع',
backgroundColor: Colors.red.withOpacity(0.8),
colorText: Colors.white);
mySnackbarError('حدث خطأ غير متوقع');
}
}
} catch (e) {
Get.snackbar('خطأ', 'فشل في الاتصال بالخادم',
backgroundColor: Colors.red.withOpacity(0.8),
colorText: Colors.white);
mySnackbarError('فشل في الاتصال بالخادم');
} finally {
isLoading.value = false;
}

View File

@@ -6,8 +6,8 @@ import 'package:http/http.dart' as http;
import '../../../constant/links.dart';
import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../main.dart';
import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class PaymobPayout extends GetxController {
@@ -65,13 +65,10 @@ class PaymobPayout extends GetxController {
'passengerID': 'admin',
'driverID': box.read(BoxName.driverID).toString(),
});
Get.snackbar('Transaction successful'.tr,
'${'Transaction successful'.tr} ${dec['amount']}',
backgroundColor: AppColor.greenColor);
mySnackbarSuccess('${'Transaction successful'.tr} ${dec['amount']}');
// Get.find<CaptainWalletController>().getCaptainWalletFromRide();
} else if (dec['disbursement_status'] == 'failed') {
Get.snackbar('Transaction failed'.tr, 'Transaction failed'.tr,
backgroundColor: AppColor.redColor);
mySnackbarError('Transaction failed'.tr);
}
}
@@ -90,7 +87,7 @@ class PaymobPayout extends GetxController {
"bank_code": bankCode, //"CIB",
"bank_transaction_type": "cash_transfer"
};
var res = await http
await http
.post(
Uri.parse('https://payouts.paymobsolutions.com/api/secure/disburse/'),
headers: headers,

View File

@@ -1,6 +1,7 @@
import 'package:get/get.dart';
import '../../constant/links.dart';
import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class DriverController extends GetxController {
@@ -17,7 +18,7 @@ class DriverController extends GetxController {
drivers = (res)['message'];
update(['drivers']); // تحديث الـ UI
} else {
Get.snackbar('Error', 'Failed to load drivers');
mySnackbarError('Failed to load drivers');
}
}
@@ -31,7 +32,7 @@ class DriverController extends GetxController {
driverDetails = (res)['message'];
update(['driverDetails']); // تحديث صفحة التفاصيل
} else {
Get.snackbar('Error', 'Failed to load driver details');
mySnackbarError('Failed to load driver details');
}
}
}

View File

@@ -2,8 +2,8 @@ import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/colors.dart';
import '../../constant/links.dart';
import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class Driverthebest extends GetxController {
@@ -15,7 +15,7 @@ class Driverthebest extends GetxController {
driver = jsonDecode(res)['message'];
update();
} else {
Get.snackbar('error', '', backgroundColor: AppColor.redColor);
mySnackbarError('error');
}
}
@@ -35,7 +35,7 @@ class DriverTheBestGizaController extends GetxController {
driver = jsonDecode(res)['message'];
update();
} else {
Get.snackbar('error', '', backgroundColor: AppColor.redColor);
mySnackbarError('error');
}
}
@@ -56,7 +56,7 @@ class DriverTheBestAlexandriaController extends GetxController {
driver = jsonDecode(res)['message'];
update();
} else {
Get.snackbar('error', '', backgroundColor: AppColor.redColor);
mySnackbarError('error');
}
}

View File

@@ -3,9 +3,9 @@ import 'dart:math';
import 'package:flutter/widgets.dart';
import 'package:get/get.dart';
import 'package:siro_admin/constant/colors.dart';
import 'package:siro_admin/constant/links.dart';
import 'package:siro_admin/controller/functions/crud.dart';
import 'package:siro_admin/views/widgets/snackbar.dart';
class EmployeeController extends GetxController {
List employee = [];
@@ -19,7 +19,7 @@ class EmployeeController extends GetxController {
fetchEmployee() async {
var res = await CRUD().get(link: AppLink.getEmployee, payload: {});
if (res is String && (res == 'failure' || res == 'token_expired')) {
Get.snackbar('error', 'Failed to load employees', backgroundColor: AppColor.redColor);
mySnackbarError('Failed to load employees');
return;
}
@@ -28,7 +28,7 @@ class EmployeeController extends GetxController {
employee = jsonData['message'];
update();
} catch (e) {
Get.snackbar('error', 'Invalid server response', backgroundColor: AppColor.redColor);
mySnackbarError('Invalid server response');
}
}
@@ -59,8 +59,7 @@ class EmployeeController extends GetxController {
// You can handle the success case here, such as showing a success message
Get.back();
Get.snackbar('Success', 'Employee added successfully',
backgroundColor: AppColor.greenColor);
mySnackbarSuccess('Employee added successfully');
name.clear();
education.clear();
site.clear();
@@ -69,8 +68,7 @@ class EmployeeController extends GetxController {
fetchEmployee();
} else {
// Handle the error case by showing a snackbar with an error message
Get.snackbar('Error', 'Failed to add employee',
backgroundColor: AppColor.redColor);
mySnackbarError('Failed to add employee');
}
}

View File

@@ -1,27 +1,22 @@
import 'dart:convert';
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:secure_string_operations/secure_string_operations.dart';
import 'package:siro_admin/constant/info.dart';
import 'package:siro_admin/env/env.dart';
import '../../constant/api_key.dart';
import '../../constant/box_name.dart';
import '../../constant/char_map.dart';
import '../../constant/colors.dart';
import '../../constant/links.dart';
import '../../constant/style.dart';
import '../../main.dart';
import '../../print.dart';
import '../../views/widgets/elevated_btn.dart';
import '../functions/encrypt_decrypt.dart';
import '../../views/widgets/snackbar.dart';
import '../notification_controller.dart';
import 'local_notification.dart';
import 'notification_service.dart';
import 'token_access.dart';
class FirebaseMessagesController extends GetxController {
final fcmToken = FirebaseMessaging.instance;
@@ -53,7 +48,7 @@ class FirebaseMessagesController extends GetxController {
await messaging.requestPermission();
} else if (Platform.isIOS) {
// Request permission for iOS
NotificationSettings settings = await messaging.requestPermission(
await messaging.requestPermission(
alert: true,
announcement: true,
badge: true,
@@ -107,7 +102,7 @@ class FirebaseMessagesController extends GetxController {
);
var jsonResponse = jsonDecode(res.body);
Log.print('jsonResponse: ${jsonResponse}');
Log.print('jsonResponse: $jsonResponse');
if (jsonResponse['status'] == 'success') {
// var newData = jsonResponse['data'] as List;
// Log.print('newData: ${newData}');
@@ -185,7 +180,7 @@ class FirebaseMessagesController extends GetxController {
// }
isSendingNotifications = false;
Get.snackbar("Success", "All notifications sent!");
mySnackbarSuccess("All notifications sent!");
}
bool isSendingNotificationsPassenger = false;
@@ -204,7 +199,7 @@ class FirebaseMessagesController extends GetxController {
// }
isSendingNotificationsPassenger = false;
Get.snackbar("Success", "All notifications sent!");
mySnackbarSuccess("All notifications sent!");
}
Future getAllTokenPassengers() async {
@@ -241,9 +236,7 @@ class FirebaseMessagesController extends GetxController {
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
// If the app is in the background or terminated, show a system tray message
RemoteNotification? notification = message.notification;
AndroidNotification? android = notification?.android;
// if (notification != null && android != null) {
// if (message.notification != null) {
if (message.data.isNotEmpty && message.notification != null) {
fireBaseTitles(message);
}
@@ -267,26 +260,8 @@ class FirebaseMessagesController extends GetxController {
}
}
SnackbarController driverAppliedTripSnakBar() {
return Get.snackbar(
'Driver Applied the Ride for You'.tr,
'',
colorText: AppColor.greenColor,
duration: const Duration(seconds: 3),
snackPosition: SnackPosition.TOP,
titleText: Text(
'Applied'.tr,
style: const TextStyle(color: AppColor.redColor),
),
messageText: Text(
'Driver Applied the Ride for You'.tr,
style: AppStyle.title,
),
icon: const Icon(Icons.approval),
shouldIconPulse: true,
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(16),
);
void driverAppliedTripSnakBar() {
mySnackbarSuccess('Driver Applied the Ride for You'.tr);
}
Future<dynamic> passengerDialog(String message) {

View File

@@ -14,9 +14,9 @@ import '../../constant/links.dart';
import '../../env/env.dart';
import '../../main.dart';
import '../../print.dart';
import '../../views/widgets/snackbar.dart';
import 'device_info.dart';
import 'encrypt_decrypt.dart';
import 'security_checks.dart';
import 'ssl_pinning.dart';
class CRUD {
@@ -36,7 +36,7 @@ class CRUD {
'password': AK.passnpassenger,
'aud': '${AK.allowed}$dev',
};
Log.print('payload: ${payload}');
Log.print('payload: $payload');
var response1 = await _client.post(
Uri.parse(AppLink.loginJwtDriver),
body: payload,
@@ -46,7 +46,7 @@ class CRUD {
final decodedResponse1 = jsonDecode(response1.body);
final jwt = decodedResponse1['jwt'];
Log.print('jwt: ${jwt}');
Log.print('jwt: $jwt');
await box.write(BoxName.jwt, X.c(X.c(X.c(jwt, cn), cC), cs));
await storage.write(key: BoxName.jwt, value: X.c(X.c(X.c(jwt, cn), cC), cs));
// await AppInitializer().getKey();
@@ -334,11 +334,11 @@ class CRUD {
Future<dynamic> postWallet(
{required String link, Map<String, dynamic>? payload}) async {
var s = await getJwtWallet();
Log.print('jwt: ${s}');
Log.print('jwt: $s');
final hmac = box.read(BoxName.hmac);
Log.print('hmac: ${hmac}');
Log.print('hmac: $hmac');
var url = Uri.parse(link);
Log.print('url: ${url}');
Log.print('url: $url');
// إضافة الـ HMAC للـ payload لزيادة التوافقية
if (payload != null && hmac != null) {
@@ -389,9 +389,9 @@ class CRUD {
link: AppLink.send_whatsapp_message,
payload: {'receiver': to, 'message': message});
if (res != 'failure') {
Get.snackbar('Success', 'Message sent successfully');
mySnackbarSuccess('Message sent successfully');
} else {
Get.snackbar('Error', 'Failed to send message');
mySnackbarError('Failed to send message');
}
}
@@ -399,7 +399,6 @@ class CRUD {
required String channelName,
required String uid,
}) async {
var uid = box.read(BoxName.phone) ?? box.read(BoxName.phoneDriver);
var res = await _client.get(
Uri.parse(
'https://repulsive-pig-rugby-shirt.cyclic.app/token?channelName=$channelName'),
@@ -450,15 +449,6 @@ class CRUD {
}
Future allMethodForAI(String prompt, driverID, imagePath) async {
// await ImageController().choosImage(linkPHP, imagePath);
Future.delayed(const Duration(seconds: 2));
var extractedString = await arabicTextExtractByVisionAndAI(
imagePath: imagePath, driverID: driverID);
var json = jsonDecode(extractedString);
var textValues = getAllTextValuesWithLineNumbers(json);
// List<String> textValues = getAllTextValues(json);
// await AI().geminiAiExtraction(prompt, textValues);
}
Map<String, List<Map<String, String>>> getAllTextValuesWithLineNumbers(
@@ -616,7 +606,7 @@ class CRUD {
"receiver": phone
});
var res = await _client.post(
await _client.post(
Uri.parse(AppLink.sendSms),
body: body,
headers: headers,

View File

@@ -12,7 +12,7 @@ import '../../print.dart';
class DeviceHelper {
static Future<String> getDeviceFingerprint() async {
final DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
var deviceData;
Map<String, dynamic> deviceData;
try {
if (Platform.isAndroid) {
@@ -58,9 +58,6 @@ class SecurityHelper {
bool isNotTrust = false;
bool isJailBroken = false;
bool isRealDevice = true;
bool isOnExternalStorage = false;
bool checkForIssues = false;
bool isDevMode = false;
bool isTampered = false;
String bundleId = "";
@@ -68,15 +65,6 @@ class SecurityHelper {
isNotTrust = await JailbreakRootDetection.instance.isNotTrust;
isJailBroken = await JailbreakRootDetection.instance.isJailBroken;
isRealDevice = await JailbreakRootDetection.instance.isRealDevice;
isOnExternalStorage =
await JailbreakRootDetection.instance.isOnExternalStorage;
List<JailbreakIssue> issues =
await JailbreakRootDetection.instance.checkForIssues;
checkForIssues = issues.isNotEmpty;
isDevMode = await JailbreakRootDetection.instance.isDevMode;
PackageInfo packageInfo = await PackageInfo.fromPlatform();
bundleId = packageInfo.packageName;
if (bundleId.isNotEmpty) {

View File

@@ -26,8 +26,6 @@ class DigitObscuringFormatter extends TextInputFormatter {
TextSelection updateCursorPosition(
String maskedText, TextSelection currentSelection) {
final cursorPosition = currentSelection.baseOffset;
final cursorOffset =
currentSelection.extentOffset - currentSelection.baseOffset;
final totalDigits = maskedText.length;
const visibleDigits = 4;
final hiddenDigits = totalDigits - visibleDigits * 2;

View File

@@ -4,8 +4,6 @@ import 'package:secure_string_operations/secure_string_operations.dart';
import '../../constant/char_map.dart';
import '../../env/env.dart';
import '../../main.dart';
import '../../print.dart';
class EncryptionHelper {
static EncryptionHelper? _instance;

View File

@@ -10,6 +10,7 @@ import '../../constant/style.dart';
import '../../main.dart';
import '../../views/widgets/elevated_btn.dart';
import '../../views/widgets/my_textField.dart';
import '../../views/widgets/snackbar.dart';
import 'crud.dart';
class LogOutController extends GetxController {
@@ -20,12 +21,11 @@ class LogOutController extends GetxController {
Future deleteMyAccountDriver(String id) async {
await CRUD().post(link: AppLink.removeUser, payload: {'id': id}).then(
(value) => Get.snackbar('Deleted'.tr, 'Your Account is Deleted',
backgroundColor: AppColor.redColor));
(value) => mySnackbarWarning('Your Account is Deleted'.tr));
}
checkBeforeDelete() async {
var res = await CRUD().post(
await CRUD().post(
link: AppLink.deletecaptainAccounr,
payload: {'id': box.read(BoxName.driverID)}).then((value) => exit(0));
}
@@ -88,7 +88,7 @@ class LogOutController extends GetxController {
),
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(AppColor.redColor),
backgroundColor: WidgetStateProperty.all(AppColor.redColor),
),
onPressed: () {
// box.remove(BoxName.agreeTerms);
@@ -131,7 +131,7 @@ class LogOutController extends GetxController {
),
ElevatedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(AppColor.redColor),
backgroundColor: WidgetStateProperty.all(AppColor.redColor),
),
onPressed: () {
// box.remove(BoxName.agreeTerms);
@@ -172,9 +172,7 @@ class LogOutController extends GetxController {
'email': box.read(BoxName.email),
});
} else {
Get.snackbar('Email Wrong'.tr, 'Email you inserted is Wrong.'.tr,
snackPosition: SnackPosition.BOTTOM,
backgroundColor: AppColor.redColor);
mySnackbarError('Email you inserted is Wrong.'.tr);
}
}
}

View File

@@ -1,7 +1,5 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';

View File

@@ -5,7 +5,6 @@ import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart';
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.dart' as path_provider;
@@ -16,6 +15,7 @@ import '../../constant/colors.dart';
import '../../constant/info.dart';
import '../../main.dart';
import '../../print.dart';
import '../../views/widgets/snackbar.dart';
import 'encrypt_decrypt.dart';
class ImageController extends GetxController {
@@ -141,7 +141,7 @@ class ImageController extends GetxController {
File compressedImage = await compressImage(processedImage);
print('link =$link');
Log.print('link: ${link}');
Log.print('link: $link');
await uploadImage(
compressedImage,
@@ -153,8 +153,7 @@ class ImageController extends GetxController {
);
} catch (e) {
print('Error in choosImage: $e');
Get.snackbar('Image Upload Failed'.tr, e.toString(),
backgroundColor: AppColor.redColor);
mySnackbarError('Image Upload Failed'.tr);
} finally {
isloading = false;
update();
@@ -229,8 +228,7 @@ class ImageController extends GetxController {
link,
);
} catch (e) {
Get.snackbar('Image Upload Failed'.tr, e.toString(),
backgroundColor: AppColor.redColor);
mySnackbarError('Image Upload Failed'.tr);
} finally {
isloading = false;
update();
@@ -243,7 +241,7 @@ class ImageController extends GetxController {
'POST',
Uri.parse(link),
);
Log.print('request: ${request}');
Log.print('request: $request');
var length = await file.length();
var stream = http.ByteStream(file.openRead());
final headers = {
@@ -251,14 +249,8 @@ class ImageController extends GetxController {
'Bearer ${r(box.read(BoxName.jwt)).split(AppInformation.addd)[0]}',
// 'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
};
Log.print('headers: ${headers}');
Log.print('headers: $headers');
var multipartFile = http.MultipartFile(
'image',
stream,
length,
filename: basename(file.path),
);
request.headers.addAll(headers);
// Set the file name to the driverID
@@ -278,8 +270,7 @@ class ImageController extends GetxController {
if (res.statusCode == 200) {
Log.print('jsonDecode(res.body): ${jsonDecode(res.body)}');
if (jsonDecode(res.body)['status'] == 'Image uploaded successfully!') {
Get.snackbar('Success'.tr, 'Image uploaded successfully!'.tr,
backgroundColor: AppColor.greenColor);
mySnackbarSuccess('Image uploaded successfully!'.tr);
}
return jsonDecode(res.body);
} else {
@@ -330,8 +321,7 @@ class ImageController extends GetxController {
link,
);
} catch (e) {
Get.snackbar('Image Upload Failed'.tr, e.toString(),
backgroundColor: AppColor.redColor);
mySnackbarError('Image Upload Failed'.tr);
} finally {
isloading = false;
update();
@@ -346,12 +336,6 @@ class ImageController extends GetxController {
var length = await file.length();
var stream = http.ByteStream(file.openRead());
var multipartFile = http.MultipartFile(
'image',
stream,
length,
filename: basename(file.path),
);
request.headers.addAll({
'Authorization':
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',

View File

@@ -1,10 +1,9 @@
import 'dart:convert';
import 'package:get/get.dart';
import 'package:siro_admin/constant/colors.dart';
import '../../constant/links.dart';
import '../firebase/firbase_messge.dart';
import '../../views/widgets/snackbar.dart';
import 'crud.dart';
class WalletController extends GetxController {
@@ -35,11 +34,9 @@ class WalletController extends GetxController {
// token, // Access token correctly
// 'ding.wav',
// );
Get.snackbar('success', 'addPaymentToDriver',
backgroundColor: AppColor.greenColor);
mySnackbarSuccess('addPaymentToDriver');
} else {
Get.snackbar('error', 'addPaymentToDriver',
backgroundColor: AppColor.redColor);
mySnackbarError('addPaymentToDriver');
}
}
@@ -68,10 +65,9 @@ class WalletController extends GetxController {
'phone': phone,
});
if (res != 'failure') {
Get.snackbar('success', 'addDrivergift300',
backgroundColor: AppColor.greenColor);
mySnackbarSuccess('addDrivergift300');
} else {
Get.snackbar('error', res, backgroundColor: AppColor.redColor);
mySnackbarError(res);
}
}
@@ -86,11 +82,9 @@ class WalletController extends GetxController {
'driverId': driverID.toString(),
});
if (res != 'failure') {
Get.snackbar('success', 'addSeferWallet',
backgroundColor: AppColor.greenColor);
mySnackbarSuccess('addSeferWallet');
} else {
Get.snackbar('error', 'addSeferWallet',
backgroundColor: AppColor.redColor);
mySnackbarError('addSeferWallet');
}
}
}

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../views/widgets/snackbar.dart';
import 'firebase/notification_service.dart';
class NotificationController extends GetxController {
@@ -131,13 +132,7 @@ class NotificationController extends GetxController {
onPressed: () async {
if (titleController.text.trim().isEmpty ||
bodyController.text.trim().isEmpty) {
Get.snackbar(
"تنبيه",
"الرجاء تعبئة جميع الحقول",
backgroundColor: Colors.amber.withOpacity(0.8),
colorText: Colors.white,
snackPosition: SnackPosition.TOP,
);
mySnackbarWarning("الرجاء تعبئة جميع الحقول");
return;
}
@@ -171,13 +166,7 @@ class NotificationController extends GetxController {
/// تنفيذ عملية الإرسال الفعلية
Future<void> _processSending(String target) async {
// إظهار تنبيه بدء العملية
Get.snackbar(
"جاري الإرسال",
"يتم إرسال الإشعار لـ $target...",
backgroundColor: _primaryAccent.withOpacity(0.2),
colorText: Colors.white,
duration: const Duration(seconds: 2),
);
mySnackbarInfo("يتم إرسال الإشعار لـ $target...");
try {
// استدعاء خدمة الإرسال
@@ -189,19 +178,9 @@ class NotificationController extends GetxController {
category: 'fromAdmin',
);
Get.snackbar(
"نجاح",
"تم إرسال الإشعار بنجاح",
backgroundColor: Colors.green.withOpacity(0.5),
colorText: Colors.white,
);
mySnackbarSuccess("تم إرسال الإشعار بنجاح");
} catch (e) {
Get.snackbar(
"خطأ",
"فشل إرسال الإشعار: $e",
backgroundColor: Colors.red.withOpacity(0.5),
colorText: Colors.white,
);
mySnackbarError("فشل إرسال الإشعار: $e");
}
}

View File

@@ -3,7 +3,6 @@ import 'dart:async';
import 'package:get/get.dart';
import 'package:siro_admin/constant/links.dart';
import '../../print.dart';
// --- Models ---

View File

@@ -78,7 +78,6 @@ class MainApp extends StatelessWidget {
primary: AppColor.accent,
secondary: AppColor.accent,
surface: AppColor.surface,
background: AppColor.bg,
error: AppColor.danger,
),
dividerColor: AppColor.divider,

View File

@@ -1,5 +1,4 @@
import 'dart:math';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
@@ -10,21 +9,19 @@ import 'package:siro_admin/views/admin/quality/blacklist_page.dart';
import '../../constant/box_name.dart';
import '../../constant/colors.dart';
import '../../constant/style.dart';
import '../../controller/admin/dashboard_controller.dart';
import '../../controller/admin/static_controller.dart';
import '../../controller/functions/crud.dart';
import '../../controller/notification_controller.dart';
import '../../main.dart';
import '../../print.dart';
import '../widgets/snackbar.dart';
import '../invoice/invoice_list_page.dart';
import 'captain/captain.dart';
import 'captain/syrian_driver_not_active.dart';
import 'drivers/monitor_ride.dart';
import 'employee/employee_page.dart';
import 'enceypt/driver_fingerprint_migration.dart';
import 'enceypt/encrypt.dart';
import 'enceypt/fingerprint_migration.dart';
import 'error/error/error_page.dart';
import 'packages.dart';
import 'passenger/passenger.dart';
@@ -62,7 +59,6 @@ class _AdminHomePageState extends State<AdminHomePage>
static const Color _surface = AppColor.surface;
static const Color _surfaceElevated = AppColor.surfaceElevated;
static const Color _accent = AppColor.accent;
static const Color _accentSoft = AppColor.accentSoft;
static const Color _accentBorder = AppColor.accentBorder;
static const Color _danger = AppColor.danger;
static const Color _warning = AppColor.warning;
@@ -135,8 +131,9 @@ class _AdminHomePageState extends State<AdminHomePage>
delegate: SliverChildBuilderDelegate(
(context, index) {
final category = categories[index];
if (category.items.isEmpty)
if (category.items.isEmpty) {
return const SizedBox.shrink();
}
return AnimationConfiguration.staggeredList(
position: index,
@@ -792,8 +789,8 @@ class _AdminHomePageState extends State<AdminHomePage>
ActionCategory(
title: 'المالية والإدارة',
items: [
ActionItem('الإدارة المالية V2', Icons.account_balance_rounded, _accent,
() => Get.to(() => const FinancialV2Page())),
ActionItem('الإدارة المالية V2', Icons.account_balance_rounded,
_accent, () => Get.to(() => const FinancialV2Page())),
ActionItem('المحفظة', Icons.account_balance_wallet_rounded, _accent,
() => Get.to(() => Wallet())),
ActionItem('هدية 300', Icons.card_giftcard_rounded, _warning,
@@ -839,10 +836,10 @@ class _AdminHomePageState extends State<AdminHomePage>
() => Get.to(() => ServerMonitorPage())),
ActionItem('سجل الأخطاء', Icons.error_outline_rounded, _danger,
() => Get.to(() => ErrorListPage())),
ActionItem('encrypt fp', Icons.error_outline_rounded, _danger,
() => Get.to(() => FingerprintMigrationTool())),
ActionItem('encrypt fp drivers', Icons.error_outline_rounded,
_danger, () => Get.to(() => DriverFingerprintMigrationTool())),
// ActionItem('encrypt fp', Icons.error_outline_rounded, _danger,
// () => Get.to(() => FingerprintMigrationTool())),
// ActionItem('encrypt fp drivers', Icons.error_outline_rounded,
// _danger, () => Get.to(() => DriverFingerprintMigrationTool())),
ActionItem(
'أداة التشفير',
Icons.lock_rounded,
@@ -952,7 +949,8 @@ class _AdminHomePageState extends State<AdminHomePage>
color: const Color(0xFF4CAF50).withAlpha(30), // ~0.12 opacity
shape: BoxShape.circle,
border: Border.all(
color: const Color(0xFF4CAF50).withAlpha(64)), // ~0.25 opacity
color: const Color(0xFF4CAF50)
.withAlpha(64)), // ~0.25 opacity
),
child: const Icon(Icons.message_rounded,
color: Color(0xFF4CAF50), size: 28),
@@ -1029,20 +1027,11 @@ class _AdminHomePageState extends State<AdminHomePage>
Get.back();
var driverPhones = box
.read(BoxName.tokensDrivers)['message'] as List?;
if (driverPhones == null || driverPhones.isEmpty)
if (driverPhones == null || driverPhones.isEmpty) {
return;
}
Get.snackbar(
'بدأ الإرسال',
'سيتم الإرسال في الخلفية',
backgroundColor:
const Color(0xFF4CAF50).withOpacity(0.15),
colorText: _textPrimary,
borderRadius: 12,
margin: const EdgeInsets.all(16),
icon: const Icon(Icons.check_circle_rounded,
color: Color(0xFF4CAF50)),
);
mySnackbarInfo('سيتم الإرسال في الخلفية');
for (var driverData in driverPhones) {
if (driverData['phone'] != null) {

View File

@@ -15,12 +15,12 @@ class CaptainsPage extends StatelessWidget {
Get.put(CaptainAdminController());
final TextEditingController searchController = TextEditingController();
String myPhone = box.read(BoxName.adminPhone).toString();
bool isSuperAdmin = false;
final String myPhone = box.read(BoxName.adminPhone).toString();
bool get isSuperAdmin =>
myPhone == '963942542053' || myPhone == '963992952235';
@override
Widget build(BuildContext context) {
isSuperAdmin = myPhone == '963942542053' || myPhone == '963992952235';
return MyScafolld(
title: 'Search for Captain'.tr,

View File

@@ -4,11 +4,11 @@ import 'package:get/get.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../controller/admin/captain_admin_controller.dart';
import '../../../controller/firebase/firbase_messge.dart';
import '../../../main.dart'; // Import main to access myPhone
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_scafold.dart';
import '../../widgets/my_textField.dart';
import '../../widgets/snackbar.dart';
import '../quality/driver_scorecard_page.dart';
import 'form_captain.dart';
@@ -387,8 +387,7 @@ class CaptainDetailsPage extends StatelessWidget {
// data['passengerToken'] ?? '', // Safety check
// 'order.wav');
Get.back();
Get.snackbar("Success", "Notification Sent",
backgroundColor: Colors.green.withOpacity(0.2));
mySnackbarSuccess("Notification Sent");
}
},
),
@@ -413,8 +412,7 @@ class CaptainDetailsPage extends StatelessWidget {
// Call delete function here
// controller.deleteCaptain(user['id']);
Get.back();
Get.snackbar("Deleted", "Captain has been removed",
backgroundColor: Colors.red.withOpacity(0.2));
mySnackbarWarning("Captain has been removed");
},
child: Text('Delete'.tr, style: const TextStyle(color: Colors.white)),
),

View File

@@ -2,11 +2,11 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/controller/functions/crud.dart';
import '../../../constant/links.dart';
import '../../../constant/style.dart';
import '../../../controller/admin/captain_admin_controller.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_scafold.dart';
import '../../widgets/my_textField.dart';
import '../../widgets/snackbar.dart';
class FormCaptain extends StatefulWidget {
const FormCaptain({super.key});
@@ -69,7 +69,7 @@ class _FormCaptainState extends State<FormCaptain> {
print('Updating data: $updatedData');
Get.back(); // Go back after saving
Get.snackbar('Success', 'Captain data updated successfully!');
mySnackbarSuccess('Captain data updated successfully!');
}
// controller.updateCaptain(updatedData);
}

View File

@@ -9,7 +9,7 @@ import '../../widgets/my_scafold.dart';
import '../../widgets/mycircular.dart';
class RegisterCaptain extends StatelessWidget {
RegisterCaptain({super.key});
const RegisterCaptain({super.key});
@override
Widget build(BuildContext context) {

View File

@@ -7,7 +7,6 @@ import '../../../constant/info.dart';
import '../../../constant/char_map.dart';
import '../../../controller/drivers/driver_not_active_controller.dart';
import '../../../controller/functions/encrypt_decrypt.dart';
import '../../../main.dart';
import '../../../print.dart';
import 'driver_details_not_active_page.dart';

View File

@@ -9,10 +9,8 @@ class DashboardV2Widget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Initialize controller
final controller = Get.put(DashboardV2Controller());
return GetBuilder<DashboardV2Controller>(
init: DashboardV2Controller(),
builder: (ctrl) {
if (ctrl.isLoading) {
return const SliverToBoxAdapter(

View File

@@ -12,23 +12,23 @@ class DashboardStatCard extends StatelessWidget {
final Color? valueColor;
const DashboardStatCard({
Key? key,
super.key,
required this.title,
required this.value,
this.icon,
this.iconColor,
this.backgroundColor,
this.valueColor,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {
// Attempt to use AppStyle.boxDecoration1 properties if it's a BoxDecoration
BoxDecoration? baseDecoration = AppStyle.boxDecoration1;
Color? finalBackgroundColor =
backgroundColor ?? baseDecoration?.color ?? Theme.of(context).cardColor;
backgroundColor ?? baseDecoration.color ?? Theme.of(context).cardColor;
BorderRadius? finalBorderRadius =
baseDecoration?.borderRadius?.resolve(Directionality.of(context)) ??
baseDecoration.borderRadius?.resolve(Directionality.of(context)) ??
BorderRadius.circular(12.0);
return Container(

View File

@@ -5,6 +5,7 @@ import '../../../constant/style.dart';
import '../../../controller/admin/driver_docs_controller.dart';
import '../../widgets/my_scafold.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/snackbar.dart';
import '../../../constant/links.dart';
class DriverDocsReviewPage extends StatelessWidget {
@@ -100,7 +101,7 @@ class DriverDocsReviewPage extends StatelessWidget {
const SizedBox(height: 24),
Text('الوثائق المرفوعة', style: AppStyle.title),
const SizedBox(height: 12),
...docs.map((doc) => _buildDocCard(doc)).toList(),
...docs.map((doc) => _buildDocCard(doc)),
const SizedBox(height: 32),
MyElevatedButton(
title: 'اعتماد وتفعيل الحساب',
@@ -110,7 +111,7 @@ class DriverDocsReviewPage extends StatelessWidget {
bool success = await controller.approveDriver(id);
if (success) {
Get.back();
Get.snackbar('نجاح', 'تم تفعيل حساب السائق بنجاح');
mySnackbarSuccess('تم تفعيل حساب السائق بنجاح');
}
},
),

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/controller/functions/crud.dart';
import 'package:siro_admin/controller/functions/wallet.dart';
import 'package:siro_admin/views/widgets/snackbar.dart';
import '../../../constant/links.dart'; // تأكد من المسار
@@ -17,11 +18,6 @@ class DriverGiftCheckerController extends GetxController {
// قائمة السائقين (سنقوم بتحميلها للبحث عن الـ ID)
List<dynamic> driversCache = [];
@override
void onInit() {
super.onInit();
// fetchDriverCache(); // تحميل البيانات عند فتح الصفحة
}
// 1. تحميل قائمة السائقين لاستخراج الـ ID منها
Future<void> fetchDriverCache() async {
@@ -45,8 +41,7 @@ class DriverGiftCheckerController extends GetxController {
String phoneInput = phoneController.text.trim();
if (phoneInput.isEmpty) {
Get.snackbar("تنبيه", "يرجى إدخال رقم الهاتف",
backgroundColor: Colors.orange);
mySnackbarWarning("يرجى إدخال رقم الهاتف");
return;
}
await fetchDriverCache();

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart'; // Ensure get_storage is in pubspec.yaml
import 'package:siro_admin/controller/functions/wallet.dart';
import 'package:siro_admin/views/widgets/snackbar.dart';
// --- New Controller to handle the specific JSON URL ---
class DriverCacheController extends GetxController {
@@ -73,13 +74,7 @@ class DriverCacheController extends GetxController {
paidDrivers.clear();
box.remove('paid_drivers');
update();
Get.snackbar(
"Storage Cleared",
"Paid status history has been reset",
backgroundColor: Colors.redAccent,
colorText: Colors.white,
snackPosition: SnackPosition.BOTTOM,
);
mySnackbarInfo("Paid status history has been reset");
}
// Check if driver is already paid
@@ -93,9 +88,6 @@ class DriverTheBestRedesigned extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Put the new controller
final controller = Get.put(DriverCacheController());
return Scaffold(
backgroundColor: const Color(0xFFF8FAFC), // slate-50 background
body: SafeArea(
@@ -580,8 +572,7 @@ class DriverTheBestRedesigned extends StatelessWidget {
String driverId = driver['driver_id']?.toString() ?? '';
String phone = driver['phone']?.toString() ?? '';
if (driverId.isEmpty || driverId == 'null') {
Get.snackbar("Error", "Cannot pay driver with missing ID",
backgroundColor: Colors.red, colorText: Colors.white);
mySnackbarError("Cannot pay driver with missing ID");
return;
}
@@ -642,10 +633,7 @@ class DriverTheBestRedesigned extends StatelessWidget {
Get.back(); // Close Dialog
Get.snackbar("Success", "Payment of $amount EGP sent to driver",
backgroundColor: Colors.green,
colorText: Colors.white,
snackPosition: SnackPosition.BOTTOM);
mySnackbarSuccess("Payment of $amount EGP sent to driver");
},
textCancel: 'Cancel',
onCancel: () => Get.back(),

View File

@@ -6,6 +6,7 @@ import 'package:latlong2/latlong.dart';
import 'package:siro_admin/constant/links.dart';
// Keep your specific imports
import 'package:siro_admin/controller/functions/crud.dart';
import 'package:siro_admin/views/widgets/snackbar.dart';
/// --------------------------------------------------------------------------
/// 1. DATA MODELS
@@ -49,20 +50,23 @@ String normalizePhone(String input) {
final clean = input.replaceAll(RegExp(r'\D+'), '');
// Syria: 099XXXXXXX or 9639XXXXXXX
if (clean.length == 10 && clean.startsWith('09'))
if (clean.length == 10 && clean.startsWith('09')) {
return '963${clean.substring(1)}';
}
if (clean.length == 12 && clean.startsWith('963')) return clean;
if (clean.length == 9 && clean.startsWith('9')) return '963$clean';
// Jordan: 079XXXXXXX or 9627XXXXXXX
if (clean.length == 10 && clean.startsWith('07'))
if (clean.length == 10 && clean.startsWith('07')) {
return '962${clean.substring(1)}';
}
if (clean.length == 12 && clean.startsWith('962')) return clean;
if (clean.length == 9 && clean.startsWith('7')) return '962$clean';
// Egypt: 010XXXXXXXX or 2010XXXXXXXX
if (clean.length == 11 && clean.startsWith('01'))
if (clean.length == 11 && clean.startsWith('01')) {
return '20${clean.substring(1)}';
}
if (clean.length == 13 && clean.startsWith('20')) return clean;
return clean;
@@ -107,15 +111,7 @@ class RideMonitorController extends GetxController {
void startSearch() {
if (phoneInputController.text.trim().isEmpty) {
Get.snackbar(
"تنبيه",
"يرجى إدخال رقم الهاتف أولاً",
backgroundColor: Colors.redAccent.withOpacity(0.9),
colorText: Colors.white,
snackPosition: SnackPosition.TOP,
margin: const EdgeInsets.all(15),
borderRadius: 15,
);
mySnackbarWarning("يرجى إدخال رقم الهاتف أولاً");
return;
}
@@ -297,9 +293,10 @@ class RideMonitorScreen extends StatelessWidget {
appBar: PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),
child: Obx(() {
if (controller.isTracking.value)
if (controller.isTracking.value) {
return const SizedBox
.shrink(); // إخفاء الـ AppBar في وضع التتبع للخريطة الكاملة
}
return AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
@@ -460,7 +457,7 @@ class RideMonitorScreen extends StatelessWidget {
PolylineLayer(
polylines: [
Polyline(
points: controller.routePolyline.value,
points: controller.routePolyline,
strokeWidth: 6.0,
color: primaryColor.withOpacity(0.9),
borderStrokeWidth: 2.0,

View File

@@ -1,266 +0,0 @@
// ═══════════════════════════════════════════════════════════════
// driver_fingerprint_migration.dart
// ───────────────────────────────────────────────────────────────
// المنطق ببساطة:
// 1. خذ البصمة كما هي من DB
// 2. split('_') → احذف آخر جزء (OS version)
// 3. join('_') → encrypt → رفع
//
// مثال:
// "abc123_SamsungA51_13" → "abc123_SamsungA51" → encrypt
// "TECNO_LH7n-GL_14" → "TECNO_LH7n-GL" → encrypt
// "unknown_2412DPC0AG_15" → "unknown_2412DPC0AG" → encrypt
// ═══════════════════════════════════════════════════════════════
import 'package:flutter/material.dart';
import '../../../constant/links.dart';
import '../../../controller/functions/crud.dart';
import '../../../controller/functions/encrypt_decrypt.dart';
import '../../../print.dart';
class DriverFingerprintMigrationTool extends StatefulWidget {
const DriverFingerprintMigrationTool({super.key});
@override
State<DriverFingerprintMigrationTool> createState() =>
_DriverFingerprintMigrationToolState();
}
class _DriverFingerprintMigrationToolState
extends State<DriverFingerprintMigrationTool> {
bool _isRunning = false;
bool _isDone = false;
int _total = 0;
int _processed = 0;
int _updated = 0;
int _failed = 0;
String _currentLog = '';
static const int _batchSize = 50;
// ─────────────────────────────────────────────────────────────
// المنطق الأساسي — حذف آخر جزء بعد "_"
// ─────────────────────────────────────────────────────────────
String _removeLastSegment(String raw) {
final parts = raw.split('_');
if (parts.length <= 1) return raw; // جزء واحد — ما في شيء نحذفه
parts.removeLast();
return parts.join('_');
}
Future<void> _startMigration() async {
setState(() {
_isRunning = true;
_isDone = false;
_processed = 0;
_updated = 0;
_failed = 0;
_currentLog = 'جارٍ جلب بصمات السائقين...';
});
try {
final records = await _fetchAll();
if (records == null) {
_log('❌ فشل في جلب البيانات');
setState(() => _isRunning = false);
return;
}
_total = records.length;
_log('✅ تم جلب $_total بصمة — بدء المعالجة...');
for (int i = 0; i < records.length; i += _batchSize) {
final batch = records.skip(i).take(_batchSize).toList();
_log('⚙️ معالجة ${i + 1}${i + batch.length} من $_total');
await Future.wait(batch.map(_processSingle));
if (i + _batchSize < records.length) {
await Future.delayed(const Duration(milliseconds: 300));
}
}
_log('🎉 اكتمل!\nمحدَّث: $_updated | فاشل: $_failed');
setState(() {
_isDone = true;
_isRunning = false;
});
} catch (e) {
_log('❌ خطأ: $e');
setState(() => _isRunning = false);
}
}
Future<List<Map<String, dynamic>>?> _fetchAll() async {
try {
final response = await CRUD().post(
link: AppLink.getAllDriverFingerprints,
payload: {'admin_key': 'iuyweiruinakjbfkajkjlkmalkcxnlahd'},
);
if (response == 'failure' || response == null) return null;
final data = response['data'];
if (data is! List) return null;
return List<Map<String, dynamic>>.from(data);
} catch (e) {
Log.print('fetchAll error: $e');
return null;
}
}
Future<void> _processSingle(Map<String, dynamic> record) async {
final captainId = record['captain_id']?.toString() ?? '';
final rawFp = record['fingerPrint']?.toString() ?? '';
if (captainId.isEmpty || rawFp.isEmpty) {
setState(() {
_failed++;
_processed++;
});
return;
}
try {
// ── حذف آخر جزء (OS version) ─────────────────────────────
final String newRaw = _removeLastSegment(rawFp);
final String encrypted = EncryptionHelper.instance.encryptData(newRaw);
Log.print('🔄 [$captainId] "$rawFp" → "$newRaw" → encrypted');
// ── رفع للسيرفر ──────────────────────────────────────────
final res = await CRUD().post(
link: AppLink.updateDriverFingerprintAdmin,
payload: {
'captain_id': captainId,
'fingerprint': encrypted,
'admin_key': 'iuyweiruinakjbfkajkjlkmalkcxnlahd',
},
);
if (res != 'failure' && res?['status'] == 'success') {
setState(() {
_updated++;
_processed++;
});
} else {
setState(() {
_failed++;
_processed++;
});
}
} catch (e) {
Log.print('❌ [$captainId]: $e');
setState(() {
_failed++;
_processed++;
});
}
}
void _log(String msg) {
Log.print(msg);
setState(() => _currentLog = msg);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Driver FP Migration')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.orange.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.orange.shade200),
),
child: const Text(
'⚠️ تُستخدم مرة واحدة فقط\n\n'
'"abc123_Samsung_13" → "abc123_Samsung" → encrypt\n'
'"TECNO_LH7n_14" → "TECNO_LH7n" → encrypt',
style:
TextStyle(fontSize: 13, height: 1.7, fontFamily: 'monospace'),
),
),
const SizedBox(height: 24),
if (_total > 0) ...[
Text('التقدم: $_processed / $_total',
style: const TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
LinearProgressIndicator(
value: _total > 0 ? _processed / _total : 0,
backgroundColor: Colors.grey.shade200,
color: _isDone ? Colors.green : Colors.blue,
minHeight: 8,
),
const SizedBox(height: 16),
],
if (_processed > 0)
Row(children: [
_chip('محدَّث', _updated, Colors.green),
const SizedBox(width: 8),
_chip('فاشل', _failed, Colors.red),
]),
const SizedBox(height: 16),
if (_currentLog.isNotEmpty)
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
),
child: Text(_currentLog,
style:
const TextStyle(fontFamily: 'monospace', fontSize: 12)),
),
const Spacer(),
SizedBox(
width: double.infinity,
height: 52,
child: ElevatedButton(
onPressed: (_isRunning || _isDone) ? null : _startMigration,
style: ElevatedButton.styleFrom(
backgroundColor: _isDone ? Colors.green : Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
child: _isRunning
? const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white, strokeWidth: 2)),
SizedBox(width: 12),
Text('جارٍ الترحيل...',
style:
TextStyle(color: Colors.white, fontSize: 16)),
],
)
: Text(
_isDone ? '✅ اكتمل الترحيل' : 'بدء ترحيل بصمات السائقين',
style: const TextStyle(color: Colors.white, fontSize: 16),
),
),
),
]),
),
);
}
Widget _chip(String label, int value, Color color) => Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: color.withOpacity(0.3)),
),
child: Text('$label: $value',
style: TextStyle(color: color, fontWeight: FontWeight.bold)),
);
}

View File

@@ -9,14 +9,11 @@ import '../../../constant/box_name.dart';
// ─── Custom Colors ────────────────────────────────────────────────────────────
class _AppColors {
static const bg = Color(0xFF0A0D14);
static const surface = Color(0xFF111622);
static const card = Color(0xFF161D2E);
static const border = Color(0xFF1F2D4A);
static const accent = Color(0xFF00E5FF);
static const accentDim = Color(0xFF0097A7);
static const accentGlow = Color(0x2200E5FF);
static const accentDecrypt = Color(0xFF7C4DFF);
static const accentDecryptDim = Color(0xFF512DA8);
static const accentDecryptGlow = Color(0x227C4DFF);
static const textPrimary = Color(0xFFE8F0FE);
static const textSec = Color(0xFF7A8BAA);
@@ -27,7 +24,7 @@ class _AppColors {
class EncryptToolPage extends StatefulWidget {
final String adminToken;
const EncryptToolPage({Key? key, required this.adminToken}) : super(key: key);
const EncryptToolPage({super.key, required this.adminToken});
@override
State<EncryptToolPage> createState() => _EncryptToolPageState();

View File

@@ -1,366 +0,0 @@
// ═══════════════════════════════════════════════════════════════
// fingerprint_migration.dart
// ───────────────────────────────────────────────────────────────
// أداة ترحيل البصمات القديمة للنظام الجديد
// ───────────────────────────────────────────────────────────────
// المشكلة:
// البصمة القديمة = encrypt(androidId_model_osVersion)
// البصمة الجديدة = encrypt(androidId_model)
//
// الحل:
// 1. نجيب كل البصمات من السيرفر (batch 50 في المرة)
// 2. نفك تشفير كل بصمة بـ EncryptionHelper
// 3. نحذف آخر جزء (osVersion) مع الـ _ قبله
// 4. نعيد التشفير
// 5. نرفع البصمة المحدّثة للسيرفر
//
// يُستخدم مرة واحدة فقط ثم يُحذف من التطبيق
// ═══════════════════════════════════════════════════════════════
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:siro_admin/controller/functions/encrypt_decrypt.dart' as X;
import '../../../constant/char_map.dart';
import '../../../constant/links.dart';
import '../../../controller/functions/crud.dart';
import '../../../controller/functions/encrypt_decrypt.dart';
import '../../../print.dart';
class FingerprintMigrationTool extends StatefulWidget {
const FingerprintMigrationTool({super.key});
@override
State<FingerprintMigrationTool> createState() =>
_FingerprintMigrationToolState();
}
class _FingerprintMigrationToolState extends State<FingerprintMigrationTool> {
// ── حالة الترحيل ──────────────────────────────────────────
bool _isRunning = false;
bool _isDone = false;
int _total = 0;
int _processed = 0;
int _updated = 0; // بصمات تم تحديثها
int _skipped = 0; // بصمات كانت بالفعل بالنظام الجديد
int _failed = 0; // فشل في المعالجة
String _currentLog = '';
static const int _batchSize = 50;
// ─────────────────────────────────────────────────────────────
// الدالة الرئيسية للترحيل
// ─────────────────────────────────────────────────────────────
Future<void> _startMigration() async {
setState(() {
_isRunning = true;
_isDone = false;
_processed = 0;
_updated = 0;
_skipped = 0;
_failed = 0;
_currentLog = 'جارٍ جلب البصمات من السيرفر...';
});
try {
// ── 1. جلب كل البصمات من السيرفر ──────────────────────
final allFingerprints = await _fetchAllFingerprints();
if (allFingerprints == null) {
_log('❌ فشل في جلب البيانات من السيرفر');
setState(() => _isRunning = false);
return;
}
_total = allFingerprints.length;
_log('✅ تم جلب $_total بصمة — بدء المعالجة...');
// ── 2. معالجة على batches ──────────────────────────────
for (int i = 0; i < allFingerprints.length; i += _batchSize) {
final batch = allFingerprints.skip(i).take(_batchSize).toList();
_log('⚙️ معالجة ${i + 1}${i + batch.length} من $_total');
// معالجة الـ batch بالتوازي
await Future.wait(
batch.map((record) => _processSingleRecord(record)),
);
// استراحة قصيرة بين الـ batches لحماية السيرفر
if (i + _batchSize < allFingerprints.length) {
await Future.delayed(const Duration(milliseconds: 300));
}
}
_log('🎉 اكتمل الترحيل!\n'
'محدَّث: $_updated | متجاوز: $_skipped | فاشل: $_failed');
setState(() {
_isDone = true;
_isRunning = false;
});
} catch (e) {
_log('❌ خطأ عام: $e');
setState(() => _isRunning = false);
}
}
// ─────────────────────────────────────────────────────────────
// جلب كل البصمات من السيرفر
// ─────────────────────────────────────────────────────────────
Future<List<Map<String, dynamic>>?> _fetchAllFingerprints() async {
try {
final response = await CRUD().post(
link: AppLink.getAllFingerprints, // أضفه في AppLink
payload: {
'admin_key': 'iuyweiruinakjbfkajkjlkmalkcxnlahd'
}, // مفتاح أمان للـ endpoint
);
if (response == 'failure' || response == null) return null;
final data = response['data'];
if (data is! List) return null;
return List<Map<String, dynamic>>.from(data);
} catch (e) {
Log.print('fetchAllFingerprints error: $e');
return null;
}
}
// ─────────────────────────────────────────────────────────────
// معالجة بصمة واحدة
// ─────────────────────────────────────────────────────────────
Future<void> _processSingleRecord(Map<String, dynamic> record) async {
final String passengerID = record['passengerID']?.toString() ?? '';
final String encryptedFp = record['fingerPrint']?.toString() ?? '';
final String userType = record['userType']?.toString() ?? 'passenger';
if (passengerID.isEmpty || encryptedFp.isEmpty) {
setState(() {
_failed++;
_processed++;
});
return;
}
try {
// ── فك التشفير ────────────────────────────────────────
final String rawFp = EncryptionHelper.instance.decryptData(encryptedFp);
// ── تحليل البصمة ──────────────────────────────────────
// الشكل القديم: "androidId_model_osVersion" (3 أجزاء أو أكثر)
// الشكل الجديد: "androidId_model" (جزءان فقط)
final List<String> parts = rawFp.split('_');
if (parts.length <= 2) {
// البصمة بالفعل بالنظام الجديد — تجاوزها
setState(() {
_skipped++;
_processed++;
});
return;
}
// ── حذف آخر جزء (osVersion) ──────────────────────────
// مثال: "abc123_SamsungA51_13" → "abc123_SamsungA51"
// نأخذ أول جزأين فقط بغض النظر عن عدد الأجزاء
final String newRawFp = '${parts[0]}_${parts[1]}';
// ── إعادة التشفير ─────────────────────────────────────
final String newEncryptedFp =
EncryptionHelper.instance.encryptData(newRawFp);
// ── رفع البصمة الجديدة للسيرفر ───────────────────────
final response = await CRUD().post(
link: AppLink.updateFingerprintAdmin, // أضفه في AppLink
payload: {
'passengerID': passengerID,
'fingerprint': newEncryptedFp,
'userType': userType,
'admin_key': 'iuyweiruinakjbfkajkjlkmalkcxnlahd',
},
);
if (response != 'failure' && response?['status'] == 'success') {
setState(() {
_updated++;
_processed++;
});
Log.print('✅ Updated: $passengerID | $rawFp$newRawFp');
} else {
setState(() {
_failed++;
_processed++;
});
Log.print('❌ Failed update: $passengerID');
}
} catch (e) {
// فشل فك التشفير أو إعادة التشفير
setState(() {
_failed++;
_processed++;
});
Log.print('❌ Process error for $passengerID: $e');
}
}
void _log(String message) {
Log.print(message);
setState(() => _currentLog = message);
}
// ─────────────────────────────────────────────────────────────
// UI
// ─────────────────────────────────────────────────────────────
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Fingerprint Migration Tool')),
body: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// ── شرح الأداة ──────────────────────────────────
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.orange.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.orange.shade200),
),
child: const Text(
'⚠️ هذه الأداة تُستخدم مرة واحدة فقط\n'
'تقوم بتحديث بصمات الأجهزة القديمة\n'
'لتكون متوافقة مع النظام الجديد (بدون OS version)',
style: TextStyle(fontSize: 14, height: 1.6),
),
),
Container(
child: TextButton(
onPressed: () {
print(EncryptionHelper.instance.decryptData('hbgbitbXrXrBr'));
},
child: Text(
"Decrypt Test",
),
),
),
Container(
child: TextButton(
onPressed: () {
print(EncryptionHelper.instance.encryptData(
'1B501143-C579-461C-B556-4E8B390EEFE1_iPhone'));
},
child: Text(
"Encrypt Test",
),
),
),
Container(
child: TextButton(
onPressed: () {
print(r('hbgbitbXrXrBr'));
},
child: Text(
"decrypt X.r",
),
),
),
const SizedBox(height: 24),
// ── شريط التقدم ─────────────────────────────────
if (_total > 0) ...[
Text('التقدم: $_processed / $_total'),
const SizedBox(height: 8),
LinearProgressIndicator(
value: _total > 0 ? _processed / _total : 0,
backgroundColor: Colors.grey.shade200,
color: _isDone ? Colors.green : Colors.blue,
),
const SizedBox(height: 16),
],
// ── إحصائيات ────────────────────────────────────
if (_processed > 0)
Row(children: [
_statChip('محدَّث', _updated, Colors.green),
const SizedBox(width: 8),
_statChip('متجاوز', _skipped, Colors.blue),
const SizedBox(width: 8),
_statChip('فاشل', _failed, Colors.red),
]),
const SizedBox(height: 16),
// ── السجل الحالي ─────────────────────────────────
if (_currentLog.isNotEmpty)
Container(
width: double.infinity,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
),
child: Text(_currentLog,
style: const TextStyle(fontFamily: 'monospace')),
),
const Spacer(),
// ── زر التشغيل ──────────────────────────────────
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
onPressed: (_isRunning || _isDone) ? null : _startMigration,
style: ElevatedButton.styleFrom(
backgroundColor: _isDone ? Colors.green : Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
child: _isRunning
? const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white, strokeWidth: 2),
),
SizedBox(width: 12),
Text('جارٍ الترحيل...',
style: TextStyle(color: Colors.white)),
],
)
: Text(
_isDone ? '✅ اكتمل الترحيل' : 'بدء الترحيل',
style:
const TextStyle(color: Colors.white, fontSize: 16),
),
),
),
],
),
),
);
}
Widget _statChip(String label, int value, Color color) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
border: Border.all(color: color.withOpacity(0.3)),
),
child: Text('$label: $value',
style: TextStyle(color: color, fontWeight: FontWeight.bold)),
);
}
}

View File

@@ -1,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:siro_admin/constant/colors.dart';
import 'package:siro_admin/constant/links.dart';
import 'package:siro_admin/controller/functions/crud.dart';
@@ -40,7 +39,7 @@ class ErrorLog {
}
class ErrorListPage extends StatefulWidget {
const ErrorListPage({Key? key}) : super(key: key);
const ErrorListPage({super.key});
@override
State<ErrorListPage> createState() => _ErrorListPageState();

View File

@@ -204,8 +204,9 @@ class FinancialV2Page extends StatelessWidget {
}
Widget _buildSettlementsList(List<dynamic> settlements) {
if (settlements.isEmpty)
if (settlements.isEmpty) {
return const Center(child: Text('لا توجد تسويات معلقة'));
}
return ListView.builder(
shrinkWrap: true,

View File

@@ -214,7 +214,7 @@ class MarketingPage extends StatelessWidget {
const SizedBox(width: 24),
_buildStatItem(
'الفارق',
'${((double.tryParse(anomaly['competitor_price']?.toString() ?? '0') ?? 0) - (double.tryParse(anomaly['our_price']?.toString() ?? '0') ?? 0)).toStringAsFixed(2)}',
((double.tryParse(anomaly['competitor_price']?.toString() ?? '0') ?? 0) - (double.tryParse(anomaly['our_price']?.toString() ?? '0') ?? 0)).toStringAsFixed(2),
_info,
),
],
@@ -264,7 +264,7 @@ class MarketingPage extends StatelessWidget {
: 0.0;
List<FlSpot> competitorSpots = [];
if (hourlyData is List && hourlyData.isNotEmpty) {
if (hourlyData.isNotEmpty) {
for (int i = 0; i < hourlyData.length && i < 24; i++) {
final val = hourlyData[i];
final double avg = double.tryParse((val['avg_price_per_km'] ?? 0).toString()) ?? 0.0;
@@ -280,7 +280,7 @@ class MarketingPage extends StatelessWidget {
List<FlSpot> siroSpots = [];
if (siroSpeedPrice > 0) {
for (int i = 0; i < (hourlyData is List ? hourlyData.length : 1); i++) {
for (int i = 0; i < hourlyData.length; i++) {
siroSpots.add(FlSpot(i.toDouble(), siroSpeedPrice));
}
}
@@ -303,7 +303,7 @@ class MarketingPage extends StatelessWidget {
),
const SizedBox(height: 4),
Text(
hourlyData is List && hourlyData.isNotEmpty
hourlyData.isNotEmpty
? 'مقارنة أسعار Siro مقابل متوسط المنافسين لآخر ${hourlyData.length} ساعة'
: 'مقارنة أسعار Siro مقابل متوسط المنافسين لآخر 24 ساعة',
style: const TextStyle(fontSize: 10, color: _textSecondary),
@@ -346,7 +346,7 @@ class MarketingPage extends StatelessWidget {
),
borderData: FlBorderData(show: false),
minX: 0,
maxX: ((hourlyData is List ? hourlyData.length : 1) - 1).toDouble(),
maxX: (hourlyData.length - 1).toDouble(),
minY: 0,
maxY: chartMaxY,
lineBarsData: [
@@ -391,10 +391,8 @@ class MarketingPage extends StatelessWidget {
return GetBuilder<MarketingController>(
builder: (c) {
final heatmapList = c.heatmapData;
final siroSpeedPrice = c.currentSiroPriceHeatmap;
List<Map<String, dynamic>> regions = [];
if (heatmapList is List && heatmapList.isNotEmpty) {
if (heatmapList.isNotEmpty) {
for (final item in heatmapList) {
final double pci = double.tryParse(item['pci'].toString()) ?? 1.0;
final pct = ((1 - pci) * 100).abs().toStringAsFixed(1);
@@ -433,7 +431,6 @@ class MarketingPage extends StatelessWidget {
),
const SizedBox(height: 12),
...regions.map((region) {
final pciVal = (region['pci'] as num).toDouble();
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Column(
@@ -462,7 +459,7 @@ class MarketingPage extends StatelessWidget {
],
),
);
}).toList(),
}),
],
),
),
@@ -725,7 +722,7 @@ class MarketingPage extends StatelessWidget {
style: const TextStyle(fontSize: 10, color: _textSecondary),
),
value: c.isAutopilotEnabled,
activeColor: _accent,
activeThumbColor: _accent,
onChanged: c.toggleAutopilot,
),
),
@@ -978,65 +975,4 @@ class MarketingPage extends StatelessWidget {
);
}
Widget _buildWinbackCampaignsSection(BuildContext context, MarketingController c) {
return Card(
color: _surface,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16), side: const BorderSide(color: _divider)),
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.radar, color: _accent, size: 24),
SizedBox(width: 8),
Text('حملات استعادة العملاء (Win-Back)', style: TextStyle(fontWeight: FontWeight.bold, fontSize: 14, color: _textPrimary)),
],
),
const SizedBox(height: 8),
const Text(
'يتم البحث عن الركاب المنقطعين عن التطبيق والذين يتواجدون حالياً بالقرب من مناطق تشهد أسعار ذروة لدى المنافسين.',
style: TextStyle(color: _textSecondary, fontSize: 11),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: () => c.fetchWinbackTargets(),
icon: const Icon(Icons.person_search, size: 18),
label: const Text('بحث عن أهداف حالية'),
style: ElevatedButton.styleFrom(
backgroundColor: _accent.withOpacity(0.1),
foregroundColor: _accent,
elevation: 0,
),
),
),
if (c.winbackTotalCount > 0) ...[
const SizedBox(width: 12),
Expanded(
child: ElevatedButton.icon(
onPressed: () {
// TODO: Implement trigger specific winback campaign
Get.snackbar("Campaign Triggered", "SMS sent to ${c.winbackTotalCount} targets");
},
icon: const Icon(Icons.send),
label: Text('إرسال SMS لـ ${c.winbackTotalCount}'),
style: ElevatedButton.styleFrom(
backgroundColor: _success,
foregroundColor: Colors.white,
),
),
),
]
],
),
],
),
),
);
}
}

View File

@@ -3,7 +3,7 @@ import 'package:get/get.dart';
import 'dart:convert';
import 'package:siro_admin/constant/links.dart';
import 'package:siro_admin/controller/functions/crud.dart';
import 'package:siro_admin/views/widgets/my_textField.dart';
import 'package:siro_admin/views/widgets/snackbar.dart';
import '../../print.dart';
@@ -14,7 +14,6 @@ const Color _bg = Color(0xFF0D1117);
const Color _surface = Color(0xFF161B22);
const Color _surfaceElevated = Color(0xFF1C2333);
const Color _accent = Color(0xFF00D4AA);
const Color _danger = Color(0xFFFF5370);
const Color _warning = Color(0xFFFFCB6B);
const Color _info = Color(0xFF82AAFF);
const Color _textPrimary = Color(0xFFE6EDF3);
@@ -556,15 +555,7 @@ class PackageController extends GetxController {
updatePackages(String id, String version) async {
if (version.trim().isEmpty) {
Get.snackbar(
'تنبيه',
'يرجى إدخال رقم الإصدار',
backgroundColor: _warning.withOpacity(0.15),
colorText: _textPrimary,
borderRadius: 12,
margin: const EdgeInsets.all(16),
icon: const Icon(Icons.warning_rounded, color: _warning),
);
mySnackbarWarning('يرجى إدخال رقم الإصدار');
return;
}
@@ -578,26 +569,10 @@ class PackageController extends GetxController {
if (response != 'failure') {
Get.back();
Get.snackbar(
'تم التحديث',
'تم تحديث الإصدار بنجاح',
backgroundColor: _accent.withOpacity(0.15),
colorText: _textPrimary,
borderRadius: 12,
margin: const EdgeInsets.all(16),
icon: const Icon(Icons.check_circle_rounded, color: _accent),
);
mySnackbarSuccess('تم تحديث الإصدار بنجاح');
fetchPackages();
} else {
Get.snackbar(
'خطأ',
'فشل التحديث، يرجى المحاولة مجدداً',
backgroundColor: _danger.withOpacity(0.15),
colorText: _textPrimary,
borderRadius: 12,
margin: const EdgeInsets.all(16),
icon: const Icon(Icons.error_rounded, color: _danger),
);
mySnackbarError('فشل التحديث، يرجى المحاولة مجدداً');
}
}
}

View File

@@ -10,6 +10,7 @@ import '../../widgets/elevated_btn.dart';
import '../../widgets/my_scafold.dart';
import '../../widgets/my_textField.dart';
import '../../widgets/mycircular.dart';
import '../../widgets/snackbar.dart';
import 'passenger_details_page.dart';
class Passengrs extends StatelessWidget {
@@ -376,15 +377,7 @@ class Passengrs extends StatelessWidget {
Text('Cancel'.tr, style: const TextStyle(color: Colors.grey))),
);
} else {
Get.snackbar(
'Not Allowed'.tr,
'Prizes can only be added on Saturdays.'.tr,
backgroundColor: Colors.red.withOpacity(0.1),
colorText: Colors.red,
icon: const Icon(Icons.error_outline, color: Colors.red),
snackPosition: SnackPosition.TOP,
margin: const EdgeInsets.all(10),
);
mySnackbarWarning('Prizes can only be added on Saturdays.'.tr);
}
}
}

View File

@@ -3,15 +3,14 @@ import 'package:get/get.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../views/widgets/snackbar.dart';
import '../../../controller/admin/passenger_admin_controller.dart';
import '../../../controller/functions/crud.dart';
import '../../../controller/firebase/firbase_messge.dart';
import '../../../constant/links.dart';
import '../../../main.dart'; // To access 'box' for admin phone check
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_scafold.dart';
import '../../widgets/my_textField.dart';
import 'form_passenger.dart';
class PassengerDetailsPage extends StatelessWidget {
const PassengerDetailsPage({super.key});
@@ -399,8 +398,7 @@ class PassengerDetailsPage extends StatelessWidget {
// data['passengerToken'],
// 'order.wav');
Get.back();
Get.snackbar('Success', 'Notification sent successfully!',
backgroundColor: Colors.green.withOpacity(0.2));
mySnackbarSuccess('Notification sent successfully!');
}
},
),
@@ -438,13 +436,11 @@ class PassengerDetailsPage extends StatelessWidget {
// 3. Handle Result
if (res['status'] == 'success') {
Get.back(); // Go back to list page
Get.snackbar('Deleted', 'Passenger removed successfully',
backgroundColor: Colors.red.withOpacity(0.2));
mySnackbarWarning('Passenger removed successfully');
// Ideally, trigger a refresh on the controller here
// Get.find<PassengerAdminController>().getAll();
} else {
Get.snackbar('Error', res['message'] ?? 'Failed to delete',
backgroundColor: Colors.red.withOpacity(0.2));
mySnackbarError(res['message'] ?? 'Failed to delete');
}
},
child: Text('Delete'.tr, style: const TextStyle(color: Colors.white)),

View File

@@ -3,6 +3,7 @@ import 'package:get/get.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../controller/admin/kazan_controller.dart';
import '../../../views/widgets/snackbar.dart';
import '../../widgets/my_scafold.dart';
import '../../widgets/elevated_btn.dart';
@@ -17,39 +18,77 @@ class KazanEditorPage extends StatelessWidget {
title: 'تعديل أسعار كازان'.tr,
isleading: true,
body: [
Obx(() => controller.isLoading.value && controller.kazanData.isEmpty
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
Column(
children: [
_buildCountryDropdown(),
Expanded(
child: Obx(() => controller.isLoading.value && controller.kazanData.isEmpty
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildSectionHeader('⚙️ الإعدادات العامة'),
_buildGeneralSettings(),
const SizedBox(height: 24),
_buildSectionHeader('🚗 أسعار الكيلومتر لكل نوع سيارة'),
_buildKmPricesGrid(),
const SizedBox(height: 24),
_buildSectionHeader('⏱️ أسعار الدقيقة (حسب وقت اليوم)'),
_buildMinutePrices(),
const SizedBox(height: 32),
MyElevatedButton(
title: '💾 حفظ جميع التعديلات',
icon: Icons.save_rounded,
onPressed: () => _handleSave(),
),
const SizedBox(height: 100),
],
),
)),
),
],
),
],
);
}
Widget _buildCountryDropdown() {
return Container(
width: double.infinity,
padding: const EdgeInsets.fromLTRB(16, 12, 16, 4),
child: Obx(() => Container(
padding: const EdgeInsets.symmetric(horizontal: 16),
decoration: BoxDecoration(
color: AppColor.surface,
borderRadius: BorderRadius.circular(14),
border: Border.all(color: AppColor.divider),
),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: controller.selectedCountry.value,
isExpanded: true,
icon: const Icon(Icons.keyboard_arrow_down_rounded),
style: AppStyle.title.copyWith(fontSize: 16),
items: controller.countries.map((c) {
return DropdownMenuItem<String>(
value: c['name'],
child: Row(
children: [
// ⚙️ الإعدادات العامة
_buildSectionHeader('⚙️ الإعدادات العامة'),
_buildGeneralSettings(),
const SizedBox(height: 24),
// 🚗 أسعار الكيلومتر لكل نوع سيارة
_buildSectionHeader('🚗 أسعار الكيلومتر لكل نوع سيارة'),
_buildKmPricesGrid(),
const SizedBox(height: 24),
// ⏱️ أسعار الدقيقة
_buildSectionHeader('⏱️ أسعار الدقيقة (حسب وقت اليوم)'),
_buildMinutePrices(),
const SizedBox(height: 32),
// 💾 زر الحفظ
MyElevatedButton(
title: '💾 حفظ جميع التعديلات',
icon: Icons.save_rounded,
onPressed: () => _handleSave(),
),
const SizedBox(height: 100),
Text(c['flag']!, style: const TextStyle(fontSize: 22)),
const SizedBox(width: 12),
Text(c['name']!),
],
),
)),
],
);
}).toList(),
onChanged: (val) {
if (val != null) controller.setCountry(val);
},
),
),
)),
);
}
@@ -373,26 +412,18 @@ class KazanEditorPage extends StatelessWidget {
final data = Map<String, dynamic>.from(controller.kazanData);
data['adminId'] = 'admin1';
// التأكد من وجود country (إذا لم يكن موجوداً، استخدم 'syria')
// التأكد من وجود country
if (!data.containsKey('country') ||
data['country'] == null ||
data['country'].toString().isEmpty) {
data['country'] = 'Syria';
data['country'] = controller.selectedCountry.value;
}
bool success = await controller.updateKazan(data);
if (success) {
Get.snackbar("نجاح", "تم تحديث الأسعار بنجاح",
backgroundColor: AppColor.successSoft,
colorText: AppColor.textPrimary,
snackPosition: SnackPosition.BOTTOM,
margin: const EdgeInsets.all(16));
mySnackbarSuccess('تم تحديث أسعار ${controller.selectedCountry.value} بنجاح');
} else {
Get.snackbar("خطأ", "فشل تحديث الأسعار",
backgroundColor: Colors.red.shade100,
colorText: AppColor.textPrimary,
snackPosition: SnackPosition.BOTTOM,
margin: const EdgeInsets.all(16));
mySnackbarError('فشل تحديث الأسعار');
}
}
}

View File

@@ -435,7 +435,7 @@ class _RidesDashboardScreenState extends State<RidesDashboardScreen>
const Color(0xFFEF4444))),
Obx(() => _buildStatCard(
'الإيرادات',
'${controller.totalRevenue.value.toStringAsFixed(0)}',
controller.totalRevenue.value.toStringAsFixed(0),
Icons.payments_rounded,
const Color(0xFFF59E0B))),
],
@@ -817,8 +817,9 @@ class _RidesDashboardScreenState extends State<RidesDashboardScreen>
GestureDetector(
onTap: () async {
String formattedPhone = phone;
if (!formattedPhone.startsWith('+'))
if (!formattedPhone.startsWith('+')) {
formattedPhone = '+$formattedPhone';
}
final Uri launchUri = Uri(scheme: 'tel', path: formattedPhone);
if (await canLaunchUrl(launchUri)) await launchUrl(launchUri);
},
@@ -860,11 +861,13 @@ class _RidesDashboardScreenState extends State<RidesDashboardScreen>
// Helper Methods for Status
Color _getStatusColor(String status) {
if (status == 'Begin' || status == 'Arrived')
if (status == 'Begin' || status == 'Arrived') {
return const Color(0xFF10B981);
}
if (status == 'Finished') return const Color(0xFF14B8A6);
if (status.contains('Cancel') || status == 'TimeOut')
if (status.contains('Cancel') || status == 'TimeOut') {
return const Color(0xFFEF4444);
}
if (status == 'New') return const Color(0xFF3B82F6);
return Colors.grey;
}
@@ -872,8 +875,9 @@ class _RidesDashboardScreenState extends State<RidesDashboardScreen>
String _getStatusText(String status) {
if (status == 'Begin' || status == 'Arrived') return 'جارية';
if (status == 'Finished') return 'مكتملة';
if (status == 'CancelFromDriver' || status == 'CancelFromDriverAfterApply')
if (status == 'CancelFromDriver' || status == 'CancelFromDriverAfterApply') {
return 'ألغى السائق';
}
if (status == 'CancelFromPassenger') return 'ألغى الراكب';
if (status == 'TimeOut') return 'انتهى الوقت';
if (status == 'New') return 'جديدة';
@@ -1147,12 +1151,6 @@ class _RideMapMonitorScreenState extends State<RideMapMonitorScreen> {
required String phone,
required Color color,
}) {
String displayPhone = phone;
if (!widget.isAdmin && phone.length > 4) {
displayPhone =
phone.substring(phone.length - 4).padLeft(phone.length, '*');
}
return Row(
children: [
Container(
@@ -1192,8 +1190,9 @@ class _RideMapMonitorScreenState extends State<RideMapMonitorScreen> {
GestureDetector(
onTap: () async {
String formattedPhone = phone;
if (!formattedPhone.startsWith('+'))
if (!formattedPhone.startsWith('+')) {
formattedPhone = '+$formattedPhone';
}
final Uri launchUri = Uri(scheme: 'tel', path: formattedPhone);
if (await canLaunchUrl(launchUri)) await launchUrl(launchUri);
},

View File

@@ -9,7 +9,7 @@ import '../../widgets/mycircular.dart';
class Rides extends StatelessWidget {
Rides({super.key});
RideAdminController rideAdminController = Get.put(RideAdminController());
final RideAdminController rideAdminController = Get.put(RideAdminController());
@override
Widget build(BuildContext context) {
return MyScafolld(title: 'Rides'.tr, isleading: true, body: [

View File

@@ -2,15 +2,12 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/constant/colors.dart';
import 'package:siro_admin/controller/admin/security_v2_controller.dart';
import 'package:intl/intl.dart';
class AuditLogsPage extends StatelessWidget {
const AuditLogsPage({super.key});
@override
Widget build(BuildContext context) {
final controller = Get.put(SecurityV2Controller());
return Scaffold(
backgroundColor: AppColor.bg,
appBar: AppBar(

View File

@@ -3,7 +3,7 @@ import 'package:get/get.dart';
import '../../../controller/server/server_monitor_controller.dart';
class ServerMonitorPage extends StatelessWidget {
const ServerMonitorPage({Key? key}) : super(key: key);
const ServerMonitorPage({super.key});
@override
Widget build(BuildContext context) {

View File

@@ -187,7 +187,7 @@ class AddStaffPage extends StatelessWidget {
border: Border.all(color: Colors.white.withOpacity(0.05)),
),
child: DropdownButtonFormField<String>(
value: value,
initialValue: value,
dropdownColor: fillColor,
style: const TextStyle(color: Colors.white),
decoration: InputDecoration(

View File

@@ -33,7 +33,7 @@ class _PendingAdminsPageState extends State<PendingAdminsPage> {
});
}
} catch (e) {
mySnackeBarError('فشل في جلب البيانات: $e');
mySnackbarError('فشل في جلب البيانات: $e');
} finally {
setState(() => _isLoading = false);
}
@@ -53,7 +53,7 @@ class _PendingAdminsPageState extends State<PendingAdminsPage> {
_fetchPendingAdmins(); // تحديث القائمة
}
} catch (e) {
mySnackeBarError('حدث خطأ: $e');
mySnackbarError('حدث خطأ: $e');
}
}

View File

@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/constant/colors.dart';
import 'package:siro_admin/controller/admin/analytics_v2_controller.dart';
import 'package:intl/intl.dart';
class AdvancedAnalyticsPage extends StatelessWidget {
const AdvancedAnalyticsPage({super.key});
@@ -191,8 +190,9 @@ class AdvancedAnalyticsPage extends StatelessWidget {
final passengers = data['passenger_daily'] as List<dynamic>? ?? [];
final drivers = data['driver_daily'] as List<dynamic>? ?? [];
if (passengers.isEmpty && drivers.isEmpty)
if (passengers.isEmpty && drivers.isEmpty) {
return const Center(child: Text('لا توجد بيانات'));
}
List<BarChartGroupData> barGroups = [];
int maxLength =

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart' hide TextDirection;
import 'package:siro_admin/controller/functions/launch.dart';
import '../../../controller/admin/static_controller.dart';

View File

@@ -26,8 +26,6 @@ class StaticDash extends StatelessWidget {
@override
Widget build(BuildContext context) {
final controller = Get.put(StaticController());
return Scaffold(
backgroundColor: _bg,
body: GetBuilder<StaticController>(

View File

@@ -9,7 +9,7 @@ import '../../widgets/my_scafold.dart';
class Wallet extends StatelessWidget {
Wallet({super.key});
WalletAdminController walletAdminController =
final WalletAdminController walletAdminController =
Get.put(WalletAdminController());
@override
Widget build(BuildContext context) {

View File

@@ -1,11 +1,8 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_admin/env/env.dart';
import '../../controller/auth/login_controller.dart';
import '../../controller/auth/otp_helper.dart';
import '../../controller/functions/crud.dart';
import '../../print.dart';
import '../widgets/snackbar.dart';
import '../admin/admin_home_page.dart';
// ─── Colors (نفس نظام الألوان المستخدم في التطبيق) ──────────────────────────
@@ -15,10 +12,8 @@ class _C {
static const border = Color(0xFF1F2D4A);
static const accent = Color(0xFF00E5FF);
static const accentGlow = Color(0x2200E5FF);
static const accentDim = Color(0xFF0097A7);
static const textPrimary = Color(0xFFE8F0FE);
static const textSec = Color(0xFF7A8BAA);
static const error = Color(0xFFFF5252);
static const inputBg = Color(0xFF0C1120);
}
@@ -44,7 +39,7 @@ class _AdminLoginPageState extends State<AdminLoginPage>
final phone = _phoneController.text.trim();
if (password.isEmpty) {
Get.snackbar('خطأ', 'يرجى إدخال كلمة المرور');
mySnackbarError('يرجى إدخال كلمة المرور');
return;
}
@@ -506,7 +501,7 @@ class _SubmitButtonState extends State<_SubmitButton>
children: [
Icon(Icons.send_rounded, color: Colors.white, size: 18),
SizedBox(width: 10),
const Text(
Text(
'تسجيل الدخول',
style: TextStyle(
color: Colors.white,

View File

@@ -5,7 +5,6 @@ import 'package:siro_admin/controller/auth/register_controller.dart';
class _C {
static const bg = Color(0xFF0A0D14);
static const card = Color(0xFF161D2E);
static const border = Color(0xFF1F2D4A);
static const accent = Color(0xFF00E5FF);
static const textPrimary = Color(0xFFE8F0FE);
static const textSec = Color(0xFF7A8BAA);

View File

@@ -10,6 +10,7 @@ import '../../constant/box_name.dart';
import '../../constant/info.dart';
import '../../controller/functions/encrypt_decrypt.dart';
import '../../main.dart';
import '../../views/widgets/snackbar.dart';
class AddInvoicePage extends StatefulWidget {
const AddInvoicePage({super.key});
@@ -38,7 +39,6 @@ class _AddInvoicePageState extends State<AddInvoicePage> {
Future<void> uploadInvoice() async {
if (!_formKey.currentState!.validate()) return;
final driverID = '123'; // قيمة افتراضية أو يمكن جلبها من الكونترولر
final invoiceNumber = generateInvoiceNumber();
final amount = _amountController.text.trim();
final itemName = _itemNameController.text.trim();
@@ -56,7 +56,6 @@ class _AddInvoicePageState extends State<AddInvoicePage> {
final uri = Uri.parse(AppLink.addInvoice);
final request = http.MultipartRequest('POST', uri)
..fields['driverID'] = driverID
..fields['invoiceNumber'] = invoiceNumber
..fields['amount'] = amount
..fields['name'] = itemName
@@ -84,15 +83,7 @@ class _AddInvoicePageState extends State<AddInvoicePage> {
}
if (data['status'] == 'success') {
Get.snackbar(
'نجاح',
'تم حفظ الفاتورة بنجاح',
backgroundColor: Colors.green.withOpacity(0.1),
colorText: Colors.green[800],
snackPosition: SnackPosition.TOP,
margin: const EdgeInsets.all(10),
borderRadius: 20,
);
mySnackbarSuccess('تم حفظ الفاتورة بنجاح');
_itemNameController.clear();
_amountController.clear();
@@ -103,20 +94,10 @@ class _AddInvoicePageState extends State<AddInvoicePage> {
Get.back(result: true);
});
} else {
Get.snackbar(
'تنبيه',
data['message'] ?? 'حدث خطأ غير معروف',
backgroundColor: Colors.red.withOpacity(0.1),
colorText: Colors.red[800],
);
mySnackbarWarning(data['message'] ?? 'حدث خطأ غير معروف');
}
} catch (e) {
Get.snackbar(
'خطأ في الاتصال',
e.toString(),
backgroundColor: Colors.red.withOpacity(0.1),
colorText: Colors.red[800],
);
mySnackbarError(e.toString());
} finally {
if (mounted) setState(() => _isLoading = false);
}

View File

@@ -3,6 +3,7 @@ import 'package:get/get.dart';
import '../../constant/links.dart';
import '../../controller/functions/crud.dart';
import '../../print.dart';
import '../widgets/snackbar.dart';
import 'add_invoice_page.dart';
// نفترض أن هذا الموديل موجود في مشروعك، إذا لم يكن موجوداً يرجى إضافته أو تعديل الاستيراد
@@ -55,9 +56,7 @@ class _InvoiceListPageState extends State<InvoiceListPage> {
} else {
if (mounted) {
setState(() => isLoading = false);
Get.snackbar("تنبيه", "لا توجد فواتير لعرضها أو حدث خطأ",
backgroundColor: Colors.orange.withOpacity(0.2),
colorText: Colors.orange[900]);
mySnackbarWarning("لا توجد فواتير لعرضها أو حدث خطأ");
}
}
} catch (e) {
@@ -325,8 +324,7 @@ class _InvoiceListPageState extends State<InvoiceListPage> {
if (imageUrl != null && imageUrl.isNotEmpty) {
_showImageDialog(context, imageUrl);
} else {
Get.snackbar("تنبيه", "لا توجد صورة مرفقة",
backgroundColor: Colors.grey[200], colorText: Colors.black);
mySnackbarWarning("لا توجد صورة مرفقة");
}
},
child: Padding(
@@ -407,7 +405,7 @@ class _InvoiceListPageState extends State<InvoiceListPage> {
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"$amount",
amount,
style: TextStyle(
color: moneyColor,
fontWeight: FontWeight.w900,

View File

@@ -9,11 +9,11 @@ class MyCircleContainer extends StatelessWidget {
final Color borderColor;
MyCircleContainer({
Key? key,
super.key,
required this.child,
this.backgroundColor = AppColor.secondaryColor,
this.borderColor = AppColor.accentColor,
}) : super(key: key);
});
final controller = Get.put(CircleController());

View File

@@ -1,4 +1,3 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -13,12 +12,12 @@ class MyElevatedButton extends StatelessWidget {
final IconData? icon;
const MyElevatedButton({
Key? key,
super.key,
required this.title,
required this.onPressed,
this.kolor,
this.icon,
}) : super(key: key);
});
@override
Widget build(BuildContext context) {

View File

@@ -5,11 +5,11 @@ import '../../constant/style.dart';
class IconWidgetMenu extends StatelessWidget {
const IconWidgetMenu({
Key? key,
super.key,
required this.onpressed,
required this.icon,
required this.title,
}) : super(key: key);
});
final VoidCallback onpressed;
final IconData icon;

View File

@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import '../../constant/colors.dart';
import '../../constant/style.dart';

View File

@@ -1,5 +1,4 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';

View File

@@ -2,122 +2,261 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import '../../constant/colors.dart';
enum _SnackVariant { success, error, info, warning }
class SnackbarConfig {
static const duration = Duration(seconds: 3);
static const animationDuration = Duration(milliseconds: 300);
static const margin = EdgeInsets.symmetric(horizontal: 16, vertical: 10);
static const borderRadius = 12.0;
static const elevation = 6.0;
extension _VariantProps on _SnackVariant {
Color get baseColor => switch (this) {
_SnackVariant.success => const Color(0xFF1A9E5C),
_SnackVariant.error => const Color(0xFFD93025),
_SnackVariant.info => const Color(0xFF1A73E8),
_SnackVariant.warning => const Color(0xFFF29900),
};
static final BoxShadow shadow = BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 2),
);
Color get surfaceColor => switch (this) {
_SnackVariant.success => const Color(0xFFF0FBF5),
_SnackVariant.error => const Color(0xFFFEF2F1),
_SnackVariant.info => const Color(0xFFF0F6FF),
_SnackVariant.warning => const Color(0xFFFFF8E6),
};
IconData get icon => switch (this) {
_SnackVariant.success => Icons.check_circle_rounded,
_SnackVariant.error => Icons.error_rounded,
_SnackVariant.info => Icons.info_rounded,
_SnackVariant.warning => Icons.warning_amber_rounded,
};
String get label => switch (this) {
_SnackVariant.success => 'Success',
_SnackVariant.error => 'Error',
_SnackVariant.info => 'Info',
_SnackVariant.warning => 'Warning',
};
}
SnackbarController mySnackeBarError(String message) {
// Trigger error haptic feedback
HapticFeedback.mediumImpact();
class _SnackContent extends StatefulWidget {
final String message;
final _SnackVariant variant;
return Get.snackbar(
'Error'.tr,
message,
backgroundColor: AppColor.redColor.withOpacity(0.95),
colorText: AppColor.secondaryColor,
icon: const Icon(
Icons.error_outline_rounded,
color: AppColor.secondaryColor,
size: 28,
),
shouldIconPulse: true,
snackPosition: SnackPosition.TOP,
margin: SnackbarConfig.margin,
borderRadius: SnackbarConfig.borderRadius,
duration: SnackbarConfig.duration,
animationDuration: SnackbarConfig.animationDuration,
forwardAnimationCurve: Curves.easeOutCirc,
reverseAnimationCurve: Curves.easeInCirc,
boxShadows: [SnackbarConfig.shadow],
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
titleText: Text(
'Error'.tr,
style: const TextStyle(
fontWeight: FontWeight.w700,
color: Colors.white,
fontSize: 16,
letterSpacing: 0.2,
const _SnackContent({required this.message, required this.variant});
@override
State<_SnackContent> createState() => _SnackContentState();
}
class _SnackContentState extends State<_SnackContent>
with TickerProviderStateMixin {
late final AnimationController _ctrl;
late final AnimationController _scaleCtrl;
late final Animation<double> _scaleAnim;
late final Animation<double> _progressAnim;
static const Duration _displayDuration = Duration(seconds: 4);
@override
void initState() {
super.initState();
_ctrl = AnimationController(vsync: this, duration: _displayDuration);
_scaleCtrl = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 500),
);
_scaleAnim = CurvedAnimation(
parent: _scaleCtrl,
curve: Curves.elasticOut,
);
_progressAnim = Tween<double>(begin: 1.0, end: 0.0).animate(
CurvedAnimation(parent: _ctrl, curve: Curves.linear),
);
_scaleCtrl.forward();
_ctrl.forward();
}
@override
void dispose() {
_ctrl.dispose();
_scaleCtrl.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final v = widget.variant;
final accent = v.baseColor;
final surface = v.surfaceColor;
return Container(
margin: const EdgeInsets.symmetric(horizontal: 14, vertical: 8),
decoration: BoxDecoration(
color: surface,
borderRadius: BorderRadius.circular(18),
border: Border.all(color: accent.withAlpha(46), width: 1.2),
boxShadow: [
BoxShadow(
color: accent.withAlpha(31),
blurRadius: 20,
spreadRadius: -2,
offset: const Offset(0, 6),
),
BoxShadow(
color: Colors.black.withAlpha(15),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
),
messageText: Text(
message,
style: TextStyle(
color: Colors.white.withOpacity(0.95),
fontSize: 14,
height: 1.3,
child: ClipRRect(
borderRadius: BorderRadius.circular(18),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.fromLTRB(14, 14, 10, 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
ScaleTransition(
scale: _scaleAnim,
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: accent.withAlpha(31),
shape: BoxShape.circle,
),
child: Icon(v.icon, color: accent, size: 22),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
v.label.tr,
style: TextStyle(
color: accent,
fontSize: 13,
fontWeight: FontWeight.w700,
letterSpacing: 0.2,
),
),
const SizedBox(height: 3),
Text(
widget.message,
style: const TextStyle(
color: Color(0xFF424242),
fontSize: 13.5,
height: 1.4,
fontWeight: FontWeight.w400,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
),
GestureDetector(
onTap: () {
HapticFeedback.lightImpact();
_closeSnackbar(context);
},
child: Container(
width: 30,
height: 30,
margin: const EdgeInsets.only(left: 6),
decoration: BoxDecoration(
color: Colors.grey.withAlpha(25),
shape: BoxShape.circle,
),
child: Icon(
Icons.close_rounded,
size: 16,
color: Colors.grey[500],
),
),
),
],
),
),
AnimatedBuilder(
animation: _progressAnim,
builder: (_, __) => Stack(
children: [
Container(height: 3, color: accent.withAlpha(20)),
FractionallySizedBox(
widthFactor: _progressAnim.value,
child: Container(
height: 3,
decoration: BoxDecoration(
color: accent.withAlpha(115),
borderRadius: const BorderRadius.only(
topRight: Radius.circular(4),
bottomRight: Radius.circular(4),
),
),
),
),
],
),
),
],
),
),
),
onTap: (_) {
);
}
void _closeSnackbar(BuildContext context) {
ScaffoldMessenger.maybeOf(context)?.hideCurrentSnackBar();
}
}
int _retryCount = 0;
void _show(_SnackVariant variant, String message) {
if (Get.context == null) {
if (_retryCount < 3) {
_retryCount++;
WidgetsBinding.instance.addPostFrameCallback((_) => _show(variant, message));
}
return;
}
_retryCount = 0;
final context = Get.context;
if (context == null) return;
final messenger = ScaffoldMessenger.maybeOf(context);
if (messenger == null) return;
messenger.clearSnackBars();
switch (variant) {
case _SnackVariant.error:
case _SnackVariant.warning:
HapticFeedback.mediumImpact();
case _SnackVariant.success:
HapticFeedback.lightImpact();
Get.closeCurrentSnackbar();
},
isDismissible: true,
dismissDirection: DismissDirection.horizontal,
overlayBlur: 0.8,
overlayColor: Colors.black12,
case _SnackVariant.info:
HapticFeedback.selectionClick();
}
messenger.showSnackBar(
SnackBar(
content: _SnackContent(message: message, variant: variant),
backgroundColor: Colors.transparent,
elevation: 0,
margin: EdgeInsets.zero,
padding: EdgeInsets.zero,
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 4),
dismissDirection: DismissDirection.up,
),
);
}
SnackbarController mySnackbarSuccess(String message) {
// Trigger success haptic feedback
HapticFeedback.lightImpact();
return Get.snackbar(
'Success'.tr,
message,
backgroundColor: AppColor.greenColor.withOpacity(0.95),
colorText: AppColor.secondaryColor,
icon: const Icon(
Icons.check_circle_outline_rounded,
color: AppColor.secondaryColor,
size: 28,
),
shouldIconPulse: true,
snackPosition: SnackPosition.TOP,
margin: SnackbarConfig.margin,
borderRadius: SnackbarConfig.borderRadius,
duration: SnackbarConfig.duration,
animationDuration: SnackbarConfig.animationDuration,
forwardAnimationCurve: Curves.easeOutCirc,
reverseAnimationCurve: Curves.easeInCirc,
boxShadows: [SnackbarConfig.shadow],
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
titleText: Text(
'Success'.tr,
style: const TextStyle(
fontWeight: FontWeight.w700,
color: Colors.white,
fontSize: 16,
letterSpacing: 0.2,
),
),
messageText: Text(
message,
style: TextStyle(
color: Colors.white.withOpacity(0.95),
fontSize: 14,
height: 1.3,
),
),
onTap: (_) {
HapticFeedback.lightImpact();
Get.closeCurrentSnackbar();
},
isDismissible: true,
dismissDirection: DismissDirection.horizontal,
overlayBlur: 0.8,
overlayColor: Colors.black12,
);
}
void mySnackbarSuccess(String message) => _show(_SnackVariant.success, message);
void mySnackbarError(String message) => _show(_SnackVariant.error, message);
void mySnackbarInfo(String message) => _show(_SnackVariant.info, message);
void mySnackbarWarning(String message) => _show(_SnackVariant.warning, message);

View File

@@ -617,7 +617,7 @@ packages:
source: hosted
version: "4.1.2"
image:
dependency: transitive
dependency: "direct main"
description:
name: image
sha256: f9881ff4998044947ec38d098bc7c8316ae1186fa786eddffdb867b9bc94dfce
@@ -937,7 +937,7 @@ packages:
source: hosted
version: "1.9.1"
path_provider:
dependency: transitive
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"

View File

@@ -69,6 +69,8 @@ dependencies:
jailbreak_root_detection: ^1.1.5
package_info_plus: ^4.0.2
image: any
path_provider: any
dev_dependencies:
flutter_test:
sdk: flutter