Update: 2026-05-08 23:18:10
This commit is contained in:
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:freerasp/freerasp.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// Device Security Service
|
||||
/// Enterprise-grade jailbreak/root/tamper detection for Musadaq using freeRASP.
|
||||
@@ -71,6 +72,14 @@ class DeviceSecurityService extends GetxService {
|
||||
/// Handle threat detection
|
||||
void _onThreatDetected(String threat) {
|
||||
debugPrint('[Security] ⚠️ THREAT: $threat');
|
||||
|
||||
// RELAXED SECURITY FOR DEBUGGING
|
||||
// Only block the app in release mode or if explicitly requested.
|
||||
if (kDebugMode) {
|
||||
debugPrint('[Security] Skipping block due to kDebugMode');
|
||||
return;
|
||||
}
|
||||
|
||||
threatDetails.add(threat);
|
||||
isCompromised.value = true;
|
||||
|
||||
|
||||
@@ -32,14 +32,14 @@ class HomeWidgetService extends GetxService {
|
||||
/// Refresh widget data from API
|
||||
Future<void> refreshWidgetData() async {
|
||||
try {
|
||||
final res = await DioClient().client.get('/v1/dashboard/stats');
|
||||
final res = await DioClient().client.get('dashboard/stats');
|
||||
if (res.data['success'] == true) {
|
||||
final data = res.data['data'];
|
||||
totalInvoices.value = data['invoices']?['total'] ?? 0;
|
||||
pendingInvoices.value = data['invoices']?['pending'] ?? 0;
|
||||
}
|
||||
|
||||
final subRes = await DioClient().client.get('/v1/subscriptions/current');
|
||||
final subRes = await DioClient().client.get('subscriptions/current');
|
||||
if (subRes.data['success'] == true) {
|
||||
final sub = subRes.data['data'];
|
||||
quotaUsed.value = sub['invoices']?['used'] ?? 0;
|
||||
|
||||
@@ -72,7 +72,7 @@ class AiUsageView extends StatelessWidget {
|
||||
Widget _buildHeroCard(Map<String, dynamic> overall, bool isDark) {
|
||||
final totalRequests = overall['total_requests'] ?? 0;
|
||||
final totalTokens = overall['total_tokens'] ?? 0;
|
||||
final totalCostJod = (overall['total_cost_jod'] ?? 0).toDouble();
|
||||
final totalCostJod = double.tryParse(overall['total_cost_jod']?.toString() ?? '0') ?? 0.0;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
@@ -122,7 +122,7 @@ class AiUsageView extends StatelessWidget {
|
||||
Widget _buildPeriodCard(String title, Map<String, dynamic> data, IconData icon, Color color, bool isDark) {
|
||||
final requests = data['requests'] ?? 0;
|
||||
final tokens = data['tokens'] ?? 0;
|
||||
final costJod = (data['cost_jod'] ?? 0).toDouble();
|
||||
final costJod = double.tryParse(data['cost_jod']?.toString() ?? '0') ?? 0.0;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
@@ -153,8 +153,8 @@ class AiUsageView extends StatelessWidget {
|
||||
}
|
||||
|
||||
Widget _buildCostPerInvoiceCard(Map<String, dynamic> overall, bool isDark) {
|
||||
final avgCost = (overall['avg_cost_per_invoice_jod'] ?? 0).toDouble();
|
||||
final avgTokens = (overall['avg_tokens_per_invoice'] ?? 0).toDouble();
|
||||
final avgCost = double.tryParse(overall['avg_cost_per_invoice_jod']?.toString() ?? '0') ?? 0.0;
|
||||
final avgTokens = double.tryParse(overall['avg_tokens_per_invoice']?.toString() ?? '0') ?? 0.0;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
@@ -238,7 +238,7 @@ class AiUsageView extends StatelessWidget {
|
||||
final date = d['date'] ?? '';
|
||||
final requests = d['requests'] ?? 0;
|
||||
final tokens = d['tokens'] ?? 0;
|
||||
final costJod = (d['cost_jod'] ?? 0).toDouble();
|
||||
final costJod = double.tryParse(d['cost_jod']?.toString() ?? '0') ?? 0.0;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
|
||||
@@ -98,4 +98,22 @@ class SubscriptionController extends GetxController {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<bool> cancelPaymentRequest(String paymentId) async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
final res = await DioClient().client.post('payments/cancel', data: {'payment_id': paymentId});
|
||||
if (res.data['success'] == true) {
|
||||
AppSnackbar.showSuccess('تم الإلغاء', 'تم إلغاء طلب الدفع بنجاح');
|
||||
await loadAll();
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
AppLogger.error('Failed to cancel payment', e);
|
||||
AppSnackbar.showError('خطأ', 'فشل إلغاء طلب الدفع');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,21 +169,57 @@ class SubscriptionView extends StatelessWidget {
|
||||
Text('رقم المرجع: ${payment['reference_number']}', style: const TextStyle(fontFamily: 'monospace', fontSize: 14, fontWeight: FontWeight.bold)),
|
||||
Text('المبلغ: ${payment['amount_jod']} JOD', style: const TextStyle(fontSize: 13)),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
icon: const Icon(Icons.upload_file, size: 18),
|
||||
label: const Text('رفع وصل الدفع'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFF59E0B),
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton.icon(
|
||||
icon: const Icon(Icons.upload_file, size: 18),
|
||||
label: const Text('رفع الوصل'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFFF59E0B),
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
onPressed: () {
|
||||
Get.toNamed('/payment-receipt', arguments: payment);
|
||||
},
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
// Navigate to receipt upload screen
|
||||
Get.toNamed('/payment-receipt', arguments: payment);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: OutlinedButton.icon(
|
||||
icon: const Icon(Icons.close, size: 18),
|
||||
label: const Text('إلغاء الطلب'),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: Colors.red,
|
||||
side: const BorderSide(color: Colors.red),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
|
||||
),
|
||||
onPressed: () {
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: const Text('إلغاء الطلب'),
|
||||
content: const Text('هل أنت متأكد من إلغاء طلب الدفع هذا؟'),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Get.back(), child: const Text('تراجع')),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
final ctrl = Get.find<SubscriptionController>();
|
||||
ctrl.cancelPaymentRequest(payment['id'].toString());
|
||||
},
|
||||
child: const Text('نعم، إلغاء', style: TextStyle(color: Colors.red)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user