From ce984324cabd77893d58c4ddc725e810187945a6 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Mon, 1 Jun 2026 23:36:57 +0300 Subject: [PATCH] Fixes & Updates - 2026-06-01: Integrate Back-End v3 updates, fix call/connection issues across apps --- lib/constant/style.dart | 5 +- .../admin/complaint_controller.dart | 14 +++ .../admin/dashboard_controller.dart | 3 - .../admin/dashboard_v2_controller.dart | 4 +- .../admin/financial_v2_controller.dart | 12 +- lib/controller/auth/otp_helper.dart | 2 +- lib/controller/functions/crud.dart | 107 +++++++++++------- lib/print.dart | 9 +- .../admin/complaints/complaint_list_page.dart | 72 +++++++++--- .../admin/promo/promo_management_page.dart | 8 +- .../admin/static/advanced_analytics_page.dart | 2 +- macos/Runner.xcodeproj/project.pbxproj | 4 +- 12 files changed, 160 insertions(+), 82 deletions(-) diff --git a/lib/constant/style.dart b/lib/constant/style.dart index 02bdefd..694045b 100644 --- a/lib/constant/style.dart +++ b/lib/constant/style.dart @@ -1,13 +1,11 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -import '../main.dart'; -import 'box_name.dart'; import 'colors.dart'; class AppStyle { // --- Typography --- - + static TextStyle display = GoogleFonts.inter( fontWeight: FontWeight.w800, fontSize: 32, @@ -83,4 +81,3 @@ class AppStyle { static BoxDecoration boxDecoration = cardDecoration; static BoxDecoration boxDecoration1 = elevatedCard; } - diff --git a/lib/controller/admin/complaint_controller.dart b/lib/controller/admin/complaint_controller.dart index 52634fe..26a3062 100644 --- a/lib/controller/admin/complaint_controller.dart +++ b/lib/controller/admin/complaint_controller.dart @@ -6,7 +6,21 @@ import '../functions/crud.dart'; class ComplaintController extends GetxController { var complaintList = [].obs; var isLoading = false.obs; + var showOnlyDelayed = false.obs; final CRUD _crud = CRUD(); + + List get delayedComplaints { + final weekAgo = DateTime.now().subtract(const Duration(days: 7)); + return complaintList.where((c) { + if (c['statusComplaint'] == 'Resolved') return false; + try { + final date = DateTime.parse(c['date_filed']); + return date.isBefore(weekAgo); + } catch (e) { + return false; + } + }).toList(); + } @override void onInit() { diff --git a/lib/controller/admin/dashboard_controller.dart b/lib/controller/admin/dashboard_controller.dart index 3f9e642..d14906b 100644 --- a/lib/controller/admin/dashboard_controller.dart +++ b/lib/controller/admin/dashboard_controller.dart @@ -6,10 +6,8 @@ import 'package:sefer_admin1/constant/links.dart'; import 'package:sefer_admin1/controller/functions/crud.dart'; import 'package:sefer_admin1/controller/auth/otp_helper.dart'; -import '../../constant/api_key.dart'; import '../../constant/box_name.dart'; import '../../main.dart'; -import '../../print.dart'; class DashboardController extends GetxController { bool isLoading = false; @@ -38,7 +36,6 @@ class DashboardController extends GetxController { return; } - if (res != 'failure' && res != null) { try { var d = res is String ? jsonDecode(res) : res; diff --git a/lib/controller/admin/dashboard_v2_controller.dart b/lib/controller/admin/dashboard_v2_controller.dart index ca800ae..11283fb 100644 --- a/lib/controller/admin/dashboard_v2_controller.dart +++ b/lib/controller/admin/dashboard_v2_controller.dart @@ -16,8 +16,8 @@ class DashboardV2Controller extends GetxController { super.onInit(); fetchRealtimeData(); fetchSmartAlerts(); - // Auto refresh every 30 seconds - _timer = Timer.periodic(const Duration(seconds: 30), (timer) { + // Auto refresh every 2 minutes + _timer = Timer.periodic(const Duration(minutes: 2), (timer) { fetchRealtimeData(); fetchSmartAlerts(); }); diff --git a/lib/controller/admin/financial_v2_controller.dart b/lib/controller/admin/financial_v2_controller.dart index 04c3be3..35e07b1 100644 --- a/lib/controller/admin/financial_v2_controller.dart +++ b/lib/controller/admin/financial_v2_controller.dart @@ -6,7 +6,7 @@ import '../../print.dart'; class FinancialV2Controller extends GetxController { bool isLoading = true; - + Map stats = {}; List settlements = []; @@ -19,19 +19,20 @@ class FinancialV2Controller extends GetxController { Future fetchAllFinancials() async { isLoading = true; update(); - + await Future.wait([ fetchStats(), fetchSettlements(), ]); - + isLoading = false; update(); } Future fetchStats() async { try { - var res = await CRUD().get(link: AppLink.financialStatsV2, payload: {}); + var res = + await CRUD().getWallet(link: AppLink.financialStatsV2, payload: {}); if (res != 'failure' && res != null) { var d = res is String ? jsonDecode(res) : res; if (d['status'] == 'success') { @@ -45,7 +46,8 @@ class FinancialV2Controller extends GetxController { Future fetchSettlements() async { try { - var res = await CRUD().get(link: AppLink.settlementsV2, payload: {}); + var res = + await CRUD().getWallet(link: AppLink.settlementsV2, payload: {}); if (res != 'failure' && res != null) { var d = res is String ? jsonDecode(res) : res; if (d['status'] == 'success') { diff --git a/lib/controller/auth/otp_helper.dart b/lib/controller/auth/otp_helper.dart index 7e7ae85..ba3cb95 100644 --- a/lib/controller/auth/otp_helper.dart +++ b/lib/controller/auth/otp_helper.dart @@ -191,7 +191,7 @@ class OtpHelper extends GetxController { textConfirm: 'تحقق', confirmTextColor: Colors.white, onConfirm: () { - if (otpCode.length >= 5) { + if (otpCode.length >= 3) { Get.back(); verifyLoginOtp(phone, otpCode, password, fingerprint); } else { diff --git a/lib/controller/functions/crud.dart b/lib/controller/functions/crud.dart index 245591d..43c9f89 100644 --- a/lib/controller/functions/crud.dart +++ b/lib/controller/functions/crud.dart @@ -116,48 +116,6 @@ class CRUD { } } - Future getWallet({ - required String link, - Map? payload, - }) async { - var s = await getJwtWallet(); - final hmac = box.read(BoxName.hmac); - var url = Uri.parse(link); - - // إضافة الـ HMAC للـ payload لزيادة التوافقية - if (payload != null && hmac != null) { - payload['hmac'] = hmac.toString(); - } - - var response = await http.post( - url, - body: payload, - headers: { - "Content-Type": "application/x-www-form-urlencoded", - 'Authorization': 'Bearer $s', - 'X-HMAC-Auth': hmac.toString(), - 'X-Device-FP': box.read(BoxName.fingerPrint) ?? '', - }, - ); - - if (response.statusCode == 200) { - try { - var jsonData = jsonDecode(response.body); - if (jsonData['status'] == 'success') { - return jsonData; - } - return jsonData['status'] ?? 'failure'; - } catch (e) { - return 'failure'; - } - } else if (response.statusCode == 401) { - await getJwtWallet(); - return 'token_expired'; - } else { - return 'failure'; - } - } - Future post( {required String link, Map? payload}) async { var url = Uri.parse(link); @@ -248,7 +206,8 @@ class CRUD { mainToken = mainTokenEnc.toString().split(AppInformation.addd)[0]; } Log.print('Wallet SSO mainToken length: ${mainToken.length}'); - Log.print('Wallet SSO token starts with: ${mainToken.substring(0, mainToken.length > 10 ? 10 : mainToken.length)}'); + Log.print( + 'Wallet SSO token starts with: ${mainToken.substring(0, mainToken.length > 10 ? 10 : mainToken.length)}'); // استخدام الـ SSO للسيرفر الرئيسي إذا كان الأدمن مسجل دخوله var response1 = await http.post( @@ -307,6 +266,68 @@ class CRUD { return null; } + Future getWallet({ + required String link, + Map? payload, + bool isRetry = false, + }) async { + var s = await getJwtWallet(); + final hmac = box.read(BoxName.hmac); + var url = Uri.parse(link); + + Log.print('--- getWallet Execution ---'); + Log.print('URL: $url'); + Log.print('JWT: $s'); + Log.print('HMAC: $hmac'); + Log.print('Is Retry: $isRetry'); + Log.print('Payload: $payload'); + + if (payload != null && hmac != null) { + payload['hmac'] = hmac.toString(); + } + + try { + var response = await http.post( + url, + body: payload, + headers: { + "Content-Type": "application/x-www-form-urlencoded", + 'Authorization': 'Bearer $s', + 'X-HMAC-Auth': hmac.toString(), + 'X-Device-FP': box.read(BoxName.fingerPrint) ?? '', + }, + ); + + Log.print('Status Code: ${response.statusCode}'); + Log.print('Response Body: ${response.body}'); + + if (response.statusCode == 200) { + try { + var jsonData = jsonDecode(response.body); + if (jsonData['status'] == 'success') { + return jsonData; + } + Log.print('Logic Error: Status is not success. Data: $jsonData'); + return jsonData['status'] ?? 'failure'; + } catch (e) { + Log.print('JSON Decode Error in getWallet: $e'); + return 'failure'; + } + } else if (response.statusCode == 401 && !isRetry) { + Log.print('Token expired (401). Clearing cache and retrying...'); + await box.remove('wallet_jwt'); + await box.remove('wallet_jwt_expiry'); + return await getWallet(link: link, payload: payload, isRetry: true); + } else { + Log.print('HTTP Error in getWallet. Status: ${response.statusCode}'); + return 'failure'; + } + } catch (e) { + Log.print('HTTP Request Exception in getWallet: $e'); + return 'failure'; + } + } + Future postWallet( {required String link, Map? payload}) async { var s = await getJwtWallet(); diff --git a/lib/print.dart b/lib/print.dart index ccc2fb5..c13fa9d 100644 --- a/lib/print.dart +++ b/lib/print.dart @@ -1,10 +1,15 @@ import 'dart:developer' as developer; +import 'package:flutter/foundation.dart'; class Log { Log._(); - static void print(String value, {StackTrace? stackTrace}) { - developer.log(value, name: 'LOG', stackTrace: stackTrace); + static void print(dynamic value, {StackTrace? stackTrace}) { + // استخدام debugPrint بدلاً من print العادي لتجنب تعليق الجهاز في الرسائل الطويلة + debugPrint("LOG: ${value.toString()}"); + + // يمكن أيضاً إرسالها للـ developer log لضمان ظهورها في الـ Debug Console الخاص بـ VS Code + developer.log(value.toString(), name: 'LOG', stackTrace: stackTrace); } static Object? inspect(Object? object) { diff --git a/lib/views/admin/complaints/complaint_list_page.dart b/lib/views/admin/complaints/complaint_list_page.dart index 4e5e5dd..5f6a19e 100644 --- a/lib/views/admin/complaints/complaint_list_page.dart +++ b/lib/views/admin/complaints/complaint_list_page.dart @@ -18,19 +18,59 @@ class ComplaintListPage extends StatelessWidget { title: 'إدارة الشكاوى'.tr, isleading: true, body: [ - Obx(() => controller.isLoading.value && controller.complaintList.isEmpty - ? const Center(child: CircularProgressIndicator()) - : RefreshIndicator( - onRefresh: () => controller.getComplaints(), - child: ListView.builder( - padding: const EdgeInsets.fromLTRB(16, 16, 16, 80), - itemCount: controller.complaintList.length, - itemBuilder: (context, index) { - final complaint = controller.complaintList[index]; - return _buildComplaintCard(context, complaint); - }, - ), - )), + Obx(() { + if (controller.delayedComplaints.isNotEmpty) { + return Container( + margin: const EdgeInsets.all(16), + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: AppColor.danger.withOpacity(0.1), + borderRadius: BorderRadius.circular(16), + border: Border.all(color: AppColor.danger.withOpacity(0.3)), + ), + child: Column( + children: [ + Row( + children: [ + const Icon(Icons.warning_amber_rounded, color: AppColor.danger), + const SizedBox(width: 12), + Expanded( + child: Text( + 'هناك ${controller.delayedComplaints.length} شكاوى لم يتم حلها منذ أكثر من أسبوع!', + style: AppStyle.body.copyWith(color: AppColor.danger, fontWeight: FontWeight.bold), + ), + ), + ], + ), + const SizedBox(height: 12), + Obx(() => MyElevatedButton( + title: controller.showOnlyDelayed.value ? 'عرض جميع الشكاوى' : 'عرض الشكاوى المتأخرة فقط', + onPressed: () => controller.showOnlyDelayed.toggle(), + kolor: AppColor.danger, + )), + ], + ), + ); + } + return const SizedBox.shrink(); + }), + Obx(() { + final list = controller.showOnlyDelayed.value ? controller.delayedComplaints : controller.complaintList; + if (controller.isLoading.value && list.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + return RefreshIndicator( + onRefresh: () => controller.getComplaints(), + child: ListView.builder( + padding: const EdgeInsets.fromLTRB(16, 16, 16, 80), + itemCount: list.length, + itemBuilder: (context, index) { + final complaint = list[index]; + return _buildComplaintCard(context, complaint); + }, + ), + ); + }), ], ); } @@ -45,7 +85,9 @@ class ComplaintListPage extends StatelessWidget { tilePadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), leading: _buildStatusIndicator(c['statusComplaint'], statusColor), title: Text( - c['complaint_type']?.toString() ?? 'شكوى عامة', + c['description']?.toString() ?? 'بدون وصف', + maxLines: 1, + overflow: TextOverflow.ellipsis, style: AppStyle.title, ), subtitle: Column( @@ -53,7 +95,7 @@ class ComplaintListPage extends StatelessWidget { children: [ const SizedBox(height: 4), Text( - 'الرحلة: ${c['ride_id']}', + 'النوع: ${c['complaint_type'] ?? 'عام'} | الرحلة: ${c['ride_id']}', style: AppStyle.caption, ), const SizedBox(height: 4), diff --git a/lib/views/admin/promo/promo_management_page.dart b/lib/views/admin/promo/promo_management_page.dart index 67dd578..2e583bc 100644 --- a/lib/views/admin/promo/promo_management_page.dart +++ b/lib/views/admin/promo/promo_management_page.dart @@ -79,7 +79,7 @@ class PromoManagementPage extends StatelessWidget { children: [ Icon(Icons.money_rounded, size: 14, color: AppColor.success), const SizedBox(width: 4), - Text('${promo['amount']} ${'دينار'.tr}', style: AppStyle.number.copyWith(color: AppColor.success)), + Text('% ${promo['amount']}', style: AppStyle.number.copyWith(color: AppColor.success)), const SizedBox(width: 12), Icon(Icons.person_rounded, size: 14, color: AppColor.info), const SizedBox(width: 4), @@ -166,10 +166,10 @@ class PromoManagementPage extends StatelessWidget { ), MyTextForm( controller: amountController, - label: 'المبلغ', - hint: 'أدخل قيمة الخصم', + label: 'نسبة الخصم', + hint: 'أدخل نسبة الخصم (مثال: 25)', type: TextInputType.number, - prefixIcon: Icons.attach_money_rounded, + prefixIcon: Icons.percent_rounded, ), MyTextForm( controller: descController, diff --git a/lib/views/admin/static/advanced_analytics_page.dart b/lib/views/admin/static/advanced_analytics_page.dart index 4b2dc95..3897b55 100644 --- a/lib/views/admin/static/advanced_analytics_page.dart +++ b/lib/views/admin/static/advanced_analytics_page.dart @@ -307,7 +307,7 @@ class AdvancedAnalyticsPage extends StatelessWidget { Text('${d['completed_rides']} رحلة', style: const TextStyle( color: AppColor.info, fontWeight: FontWeight.bold)), - Text('${d['total_revenue']} ج.م', + Text('${d['total_revenue']} ل.س', style: const TextStyle( color: AppColor.success, fontSize: 12, diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index be5737c..6007140 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -241,7 +241,7 @@ 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, 3FC083AA4C1F1997BBCE63BC /* [CP] Embed Pods Frameworks */, - 6131D245E36334FE0924BDA0 /* [CP] Copy Pods Resources */, + 89800430F081423EB842813E /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -401,7 +401,7 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 6131D245E36334FE0924BDA0 /* [CP] Copy Pods Resources */ = { + 89800430F081423EB842813E /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = (