diff --git a/app/modules_app/dashboard/ai_usage.php b/app/modules_app/dashboard/ai_usage.php index 83962a7..9fed7b7 100644 --- a/app/modules_app/dashboard/ai_usage.php +++ b/app/modules_app/dashboard/ai_usage.php @@ -75,6 +75,6 @@ try { ], 'إحصائيات استخدام الذكاء الاصطناعي'); } catch (\Exception $e) { - error_log("AI Usage Stats Error: " . $e->getMessage()); - json_error('خطأ في جلب إحصائيات AI', 500); + error_log("AI Usage Stats Error: " . $e->getMessage() . " | " . $e->getTraceAsString()); + json_error('خطأ في جلب إحصائيات AI: ' . $e->getMessage(), 500); } diff --git a/app/modules_app/referral/my_code.php b/app/modules_app/referral/my_code.php index 3702468..f4d609f 100644 --- a/app/modules_app/referral/my_code.php +++ b/app/modules_app/referral/my_code.php @@ -11,16 +11,21 @@ $decoded = AuthMiddleware::check(); $db = Database::getInstance(); $userId = $decoded['user_id']; -$tenantId = $decoded['tenant_id']; +$tenantId = $decoded['tenant_id'] ?? null; try { // Check if user already has a referral code - $stmt = $db->prepare("SELECT * FROM referral_codes WHERE user_id = ?"); + $stmt = $db->prepare("SELECT * FROM referral_codes WHERE user_id = ? LIMIT 1"); $stmt->execute([$userId]); $existing = $stmt->fetch(); + $rewardRules = [ + 'per_registration' => '1 شهر مجاني على الباقة الحالية', + 'per_subscription' => '2 شهر مجاني + رفع الحد 50 فاتورة', + ]; + if ($existing) { - // Get referral stats + // Get referral stats (safe — returns zeros if no referrals yet) $statsStmt = $db->prepare(" SELECT COUNT(*) as total_referrals, @@ -33,22 +38,28 @@ try { $stats = $statsStmt->fetch(); // Recent referrals - $recentStmt = $db->prepare(" - SELECT r.*, u.name as referred_name - FROM referrals r - LEFT JOIN users u ON r.referred_id = u.id - WHERE r.referrer_id = ? - ORDER BY r.created_at DESC LIMIT 10 - "); - $recentStmt->execute([$userId]); - $recent = $recentStmt->fetchAll(); + $recent = []; + try { + $recentStmt = $db->prepare(" + SELECT r.*, u.name as referred_name + FROM referrals r + LEFT JOIN users u ON r.referred_id = u.id + WHERE r.referrer_id = ? + ORDER BY r.created_at DESC LIMIT 10 + "); + $recentStmt->execute([$userId]); + $recent = $recentStmt->fetchAll(); - // Decrypt names - foreach ($recent as &$ref) { - if (!empty($ref['referred_name'])) { - $dec = \App\Core\Encryption::decrypt($ref['referred_name']); - $ref['referred_name'] = ($dec !== false && $dec !== null) ? $dec : $ref['referred_name']; + // Decrypt names + foreach ($recent as &$ref) { + if (!empty($ref['referred_name'])) { + $dec = \App\Core\Encryption::decrypt($ref['referred_name']); + $ref['referred_name'] = ($dec !== false && $dec !== null) ? $dec : $ref['referred_name']; + } } + } catch (\Exception $e) { + // If referrals table query fails, just return empty + error_log("Referral recent query: " . $e->getMessage()); } json_success([ @@ -62,17 +73,14 @@ try { 'rewards_claimed' => (int)($stats['rewards_claimed'] ?? 0), ], 'recent' => $recent, - 'reward_rules' => [ - 'per_registration' => '1 شهر مجاني على الباقة الحالية', - 'per_subscription' => '2 شهر مجاني + رفع الحد 50 فاتورة', - ], + 'reward_rules' => $rewardRules, ], 'رمز الإحالة الخاص بك'); } else { // Generate new referral code $code = 'MSQ-' . strtoupper(substr(md5($userId . time()), 0, 6)); - $db->prepare("INSERT INTO referral_codes (id, user_id, tenant_id, code, created_at) VALUES (UUID(), ?, ?, ?, NOW())") - ->execute([$userId, $tenantId, $code]); + $insertStmt = $db->prepare("INSERT INTO referral_codes (id, user_id, tenant_id, code, created_at) VALUES (UUID(), ?, ?, ?, NOW())"); + $insertStmt->execute([$userId, $tenantId ?? '', $code]); json_success([ 'code' => $code, @@ -80,13 +88,10 @@ try { 'created_at' => date('Y-m-d H:i:s'), 'stats' => ['total' => 0, 'registered' => 0, 'subscribed' => 0, 'rewards_claimed' => 0], 'recent' => [], - 'reward_rules' => [ - 'per_registration' => '1 شهر مجاني على الباقة الحالية', - 'per_subscription' => '2 شهر مجاني + رفع الحد 50 فاتورة', - ], + 'reward_rules' => $rewardRules, ], 'تم إنشاء رمز الإحالة'); } } catch (\Exception $e) { - error_log("Referral error: " . $e->getMessage()); - json_error('حدث خطأ في نظام الإحالة', 500); + error_log("Referral error: " . $e->getMessage() . " | Trace: " . $e->getTraceAsString()); + json_error('حدث خطأ في نظام الإحالة: ' . $e->getMessage(), 500); } diff --git a/musadaq-app/ios/Runner/Info.plist b/musadaq-app/ios/Runner/Info.plist index b1e5f9e..02b2ee5 100644 --- a/musadaq-app/ios/Runner/Info.plist +++ b/musadaq-app/ios/Runner/Info.plist @@ -24,6 +24,11 @@ ???? CFBundleVersion $(FLUTTER_BUILD_NUMBER) + LSApplicationQueriesSchemes + + whatsapp + https + LSRequiresIPhoneOS NSCameraUsageDescription diff --git a/musadaq-app/lib/features/referral/controllers/referral_controller.dart b/musadaq-app/lib/features/referral/controllers/referral_controller.dart index 84c1668..5b6f96b 100644 --- a/musadaq-app/lib/features/referral/controllers/referral_controller.dart +++ b/musadaq-app/lib/features/referral/controllers/referral_controller.dart @@ -1,9 +1,12 @@ +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import '../../../core/network/dio_client.dart'; import '../../../core/utils/logger.dart'; class ReferralController extends GetxController { var isLoading = true.obs; + var hasError = false.obs; + var errorMessage = ''.obs; var referralData = Rxn>(); @override @@ -21,11 +24,18 @@ class ReferralController extends GetxController { Future fetchReferralCode() async { try { isLoading.value = true; + hasError.value = false; final res = await DioClient().client.get('referral/my-code'); if (res.data['success'] == true) { referralData.value = Map.from(res.data['data']); + } else { + hasError.value = true; + errorMessage.value = res.data['message'] ?? 'خطأ غير معروف'; + debugPrint('Referral API error: ${res.data}'); } } catch (e) { + hasError.value = true; + errorMessage.value = 'تعذّر الاتصال بالخادم'; AppLogger.error('Failed to fetch referral code', e); } finally { isLoading.value = false; diff --git a/musadaq-app/lib/features/referral/views/referral_view.dart b/musadaq-app/lib/features/referral/views/referral_view.dart index e32c0f6..b55fea4 100644 --- a/musadaq-app/lib/features/referral/views/referral_view.dart +++ b/musadaq-app/lib/features/referral/views/referral_view.dart @@ -160,7 +160,7 @@ class ReferralView extends StatelessWidget { } void _shareViaWhatsApp(String code, String link) async { - final message = Uri.encodeComponent( + final rawMessage = 'مرحباً 👋\n\n' 'أدعوك لتجربة تطبيق *مُصادَق* — أذكى نظام فوترة إلكتروني في الأردن 🇯🇴\n\n' '✅ استخراج الفواتير بالذكاء الاصطناعي\n' @@ -168,23 +168,28 @@ class ReferralView extends StatelessWidget { '✅ تقارير ضريبية جاهزة\n\n' '🎁 استخدم رمز الدعوة: *$code*\n' 'واحصل على شهر مجاني!\n\n' - '🔗 $link' - ); + '🔗 $link'; - final whatsappUrl = Uri.parse('https://wa.me/?text=$message'); + final encodedMessage = Uri.encodeComponent(rawMessage); - try { - if (await canLaunchUrl(whatsappUrl)) { - await launchUrl(whatsappUrl, mode: LaunchMode.externalApplication); - } else { - // Fallback — copy to clipboard - Clipboard.setData(ClipboardData(text: Uri.decodeComponent(message))); - AppSnackbar.showInfo('واتساب غير متوفر', 'تم نسخ الرسالة — ألصقها يدوياً'); + // Try multiple approaches for WhatsApp + final urls = [ + Uri.parse('whatsapp://send?text=$encodedMessage'), + Uri.parse('https://wa.me/?text=$encodedMessage'), + ]; + + for (final url in urls) { + try { + final launched = await launchUrl(url, mode: LaunchMode.externalApplication); + if (launched) return; // Success! + } catch (_) { + continue; // Try next URL } - } catch (e) { - Clipboard.setData(ClipboardData(text: Uri.decodeComponent(message))); - AppSnackbar.showInfo('واتساب غير متوفر', 'تم نسخ الرسالة — ألصقها يدوياً'); } + + // All failed — copy to clipboard + Clipboard.setData(ClipboardData(text: rawMessage)); + AppSnackbar.showSuccess('تم نسخ الرسالة', 'ألصقها في واتساب أو أي تطبيق مراسلة'); } Widget _buildStatsRow(Map stats, bool isDark) { diff --git a/musadaq-app/pubspec.lock b/musadaq-app/pubspec.lock index 9c2ffbe..83bfa23 100644 --- a/musadaq-app/pubspec.lock +++ b/musadaq-app/pubspec.lock @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" checked_yaml: dependency: transitive description: @@ -748,26 +748,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "11.0.2" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.10" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.0.2" lints: dependency: transitive description: @@ -836,18 +836,18 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.18" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" matrix2d: dependency: transitive description: @@ -860,10 +860,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" mime: dependency: transitive description: @@ -1401,10 +1401,10 @@ packages: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.9" timing: dependency: transitive description: @@ -1497,10 +1497,10 @@ packages: dependency: transitive description: name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.2.0" vm_service: dependency: transitive description: @@ -1582,5 +1582,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.8.0 <4.0.0" + dart: ">=3.9.0-0 <4.0.0" flutter: ">=3.32.0"