import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:dio/dio.dart'; import '../../../core/storage/secure_storage.dart'; import '../../../core/utils/app_snackbar.dart'; import '../../../core/utils/logger.dart'; import '../../../app/routes/app_pages.dart'; class DashboardController extends GetxController { final SecureStorage _storage = SecureStorage(); final Dio _dio = Dio(BaseOptions( baseUrl: 'https://musadaq.intaleqapp.com/api/v1', connectTimeout: const Duration(seconds: 15), receiveTimeout: const Duration(seconds: 15), responseType: ResponseType.json, )); var isLoading = true.obs; var stats = {}.obs; var recentActivities = [].obs; var userRole = ''.obs; @override void onInit() { super.onInit(); _loadDashboardData(); } Future _loadDashboardData() async { try { isLoading.value = true; final token = await _storage.getToken(); if (token == null || token.isEmpty) { Get.offAllNamed(AppRoutes.PHONE_INPUT); return; } _dio.options.headers['Authorization'] = 'Bearer $token'; // Fetch Stats final statsResponse = await _dio.get('/dashboard/stats'); if (statsResponse.data['success'] == true) { stats.value = statsResponse.data['data']; userRole.value = statsResponse.data['data']['role'] ?? ''; } // Fetch Recent Activity final activityResponse = await _dio.get('/dashboard/recent-activity'); if (activityResponse.data['success'] == true) { recentActivities.value = activityResponse.data['data']; } } on DioException catch (e) { AppLogger.error('Dashboard Data Fetch Error', e); if (e.response?.statusCode == 401 || e.response?.statusCode == 403) { await logout(); } else { AppSnackbar.showError( 'خطأ', 'فشل في جلب البيانات. الرجاء التحقق من اتصالك بالإنترنت.'); } } catch (e) { AppLogger.error('Unexpected error fetching dashboard', e); } finally { isLoading.value = false; } } Future logout() async { await _storage.clearAll(); Get.offAllNamed(AppRoutes.PHONE_INPUT); } void startVoiceAssistant() { final textController = TextEditingController(); Get.bottomSheet( Container( padding: const EdgeInsets.all(24), decoration: BoxDecoration( color: Get.isDarkMode ? const Color(0xFF1E1E2E) : Colors.white, borderRadius: const BorderRadius.only( topLeft: Radius.circular(30), topRight: Radius.circular(30)), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 40, height: 4, margin: const EdgeInsets.only(bottom: 20), decoration: BoxDecoration( color: Colors.grey.withOpacity(0.3), borderRadius: BorderRadius.circular(2)), ), Row( children: [ const Icon(Icons.auto_awesome, color: Color(0xFF5EEAD4)), const SizedBox(width: 12), Text( 'المساعد الذكي (Grok-Beta)', style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: Get.isDarkMode ? Colors.white : const Color(0xFF0F4C81)), ), ], ), const SizedBox(height: 20), TextField( autofocus: true, controller: textController, style: TextStyle( color: Get.isDarkMode ? Colors.white : Colors.black), decoration: InputDecoration( hintText: 'اكتب أمرك هنا (مثلاً: أريد رؤية الفواتير)...', hintStyle: const TextStyle(fontSize: 14, color: Colors.grey), filled: true, fillColor: Get.isDarkMode ? const Color(0xFF1A1A2E) : const Color(0xFFF1F5F9), border: OutlineInputBorder( borderRadius: BorderRadius.circular(16), borderSide: BorderSide.none), prefixIcon: const Icon(Icons.mic_none_rounded, color: Colors.grey), suffixIcon: IconButton( icon: const Icon(Icons.send_rounded, color: Color(0xFF0F4C81)), onPressed: () => _handleVoiceCommand(textController.text), ), ), onSubmitted: (val) => _handleVoiceCommand(val), ), const SizedBox(height: 12), Text( 'ملاحظة: جاري تفعيل ميزة التعرف الصوتي المباشر...', style: TextStyle(fontSize: 11, color: Colors.grey.withOpacity(0.7)), ), const SizedBox(height: 20), ], ), ), isScrollControlled: true, ); } Future _handleVoiceCommand(String text) async { if (text.trim().isEmpty) return; Get.back(); // Close bottom sheet AppSnackbar.showWarning('جاري التحليل...', 'يتم تحليل طلبك بواسطة Grok AI'); try { final token = await _storage.getToken(); final response = await _dio.post( '/voice/parse-intent-grok', data: {'text': text}, options: Options(headers: {'Authorization': 'Bearer $token'}), ); if (response.data['success'] == true) { final action = response.data['data']['action']; final params = response.data['data']['params']; final confirmation = response.data['data']['confirmation'] ?? 'تم!'; AppSnackbar.showSuccess('تم!', confirmation); _executeAction(action, params); } else { AppSnackbar.showError( 'خطأ', response.data['message'] ?? 'فشل تحليل الطلب'); } } on DioException catch (e) { AppLogger.error('Voice Assistant Error', e); String errorMsg = 'حدث خطأ أثناء التواصل مع خادم الذكاء الاصطناعي'; if (e.response != null && e.response?.data != null) { if (e.response?.data is Map && e.response?.data['message'] != null) { errorMsg = e.response?.data['message']; } } AppSnackbar.showError('خطأ', errorMsg); } catch (e) { AppLogger.error('Voice Assistant Error', e); AppSnackbar.showError('خطأ', 'حدث خطأ غير متوقع'); } } void _executeAction(String action, dynamic params) { switch (action) { case 'list_invoices': Get.toNamed(AppRoutes.INVOICES); break; case 'open_scanner': Get.toNamed(AppRoutes.SCANNER); break; case 'navigate': final screen = params['screen']?.toString().toLowerCase(); if (screen == 'settings') Get.toNamed(AppRoutes.SETTINGS); if (screen == 'dashboard') Get.back(); break; default: AppSnackbar.showWarning( 'تنبيه', 'الأمر مفهوم ولكن لم يتم ربط الأكشن برمجياً بعد.'); } } void refreshData() { _loadDashboardData(); } }