diff --git a/musadaq-app/lib/features/companies/controllers/add_company_controller.dart b/musadaq-app/lib/features/companies/controllers/add_company_controller.dart index a6bff1d..730c8d4 100644 --- a/musadaq-app/lib/features/companies/controllers/add_company_controller.dart +++ b/musadaq-app/lib/features/companies/controllers/add_company_controller.dart @@ -3,6 +3,9 @@ import 'package:get/get.dart'; import 'package:dio/dio.dart'; import '../../../core/network/dio_client.dart'; import '../../../core/utils/app_snackbar.dart'; +import '../../../core/utils/logger.dart'; +import '../../../core/storage/secure_storage.dart'; +import '../../dashboard/controllers/dashboard_controller.dart'; import 'companies_management_controller.dart'; class AddCompanyController extends GetxController { @@ -12,6 +15,56 @@ class AddCompanyController extends GetxController { var isSubmitting = false.obs; + var isSuperAdmin = false.obs; + var tenants = >[].obs; + var selectedTenantId = RxnString(); + var isLoadingTenants = false.obs; + + final Dio _dio = DioClient().client; + final SecureStorage _storage = SecureStorage(); + + @override + void onInit() { + super.onInit(); + _checkRoleAndFetchTenants(); + } + + Future _checkRoleAndFetchTenants() async { + String role = ''; + + // 1. Try to get from DashboardController first + if (Get.isRegistered()) { + role = Get.find().userRole.value; + } + + // 2. Fallback to SecureStorage + if (role.isEmpty) { + role = await _storage.read('user_role') ?? ''; + } + + if (role == 'super_admin') { + isSuperAdmin.value = true; + _fetchTenants(); + } + } + + Future _fetchTenants() async { + try { + isLoadingTenants.value = true; + final response = await _dio.get('tenants'); + if (response.data['success'] == true) { + tenants.value = List>.from(response.data['data']); + if (tenants.isNotEmpty) { + selectedTenantId.value = tenants.first['id']; + } + } + } catch (e) { + AppLogger.error('Failed to fetch tenants for company creation', e); + } finally { + isLoadingTenants.value = false; + } + } + @override void onClose() { nameController.dispose(); @@ -29,16 +82,28 @@ class AddCompanyController extends GetxController { return; } + if (isSuperAdmin.value && selectedTenantId.value == null) { + AppSnackbar.showWarning( + 'تنبيه', 'الرجاء اختيار المكتب المحاسبي التابع له هذه الشركة'); + return; + } + try { isSubmitting.value = true; - final dio = DioClient().client; - final response = await dio.post('companies/create', data: { + + final data = { 'name': name, 'tax_identification_number': tin, 'commercial_registration_number': crnController.text.trim(), - }); + }; - if (response.data['success'] == true) { + if (isSuperAdmin.value) { + data['tenant_id'] = selectedTenantId.value!; + } + + final response = await _dio.post('companies/create', data: data); + + if (response.statusCode == 200 || response.statusCode == 201) { AppSnackbar.showSuccess('نجاح', 'تمت إضافة الشركة بنجاح'); // Refresh list if controller exists @@ -47,13 +112,16 @@ class AddCompanyController extends GetxController { } Get.back(); + } else { + AppSnackbar.showError('خطأ', 'فشل إضافة الشركة'); } } on DioException catch (e) { if (e.response?.statusCode == 403) { AppSnackbar.showError( 'خطأ', 'لقد وصلت للحد الأقصى المسموح به للشركات في باقتك'); } else { - AppSnackbar.showError('خطأ', 'تعذر إضافة الشركة'); + AppSnackbar.showError( + 'خطأ', e.response?.data?['message'] ?? 'تعذر إضافة الشركة'); } } catch (e) { AppSnackbar.showError('خطأ', 'حدث خطأ غير متوقع'); diff --git a/musadaq-app/lib/features/companies/views/add_company_view.dart b/musadaq-app/lib/features/companies/views/add_company_view.dart index 1725424..a682b8a 100644 --- a/musadaq-app/lib/features/companies/views/add_company_view.dart +++ b/musadaq-app/lib/features/companies/views/add_company_view.dart @@ -49,6 +49,46 @@ class AddCompanyView extends StatelessWidget { keyboardType: TextInputType.number, isDark: isDark, ), + Obx(() { + if (controller.isSuperAdmin.value) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + const Text('المكتب المحاسبي التابعة له', style: TextStyle(fontWeight: FontWeight.bold)), + const SizedBox(height: 8), + if (controller.isLoadingTenants.value) + const CircularProgressIndicator() + else + Container( + padding: const EdgeInsets.symmetric(horizontal: 12), + decoration: BoxDecoration( + color: isDark ? Colors.white.withValues(alpha: 0.05) : Colors.white, + borderRadius: BorderRadius.circular(12), + border: Border.all(color: isDark ? Colors.white24 : Colors.grey.shade300), + ), + child: DropdownButtonHideUnderline( + child: DropdownButton( + value: controller.selectedTenantId.value, + isExpanded: true, + dropdownColor: isDark ? const Color(0xFF1E1E2E) : Colors.white, + items: controller.tenants.map((tenant) { + return DropdownMenuItem( + value: tenant['id'], + child: Text(tenant['name'] ?? 'مكتب غير معروف'), + ); + }).toList(), + onChanged: (val) { + if (val != null) controller.selectedTenantId.value = val; + }, + ), + ), + ), + ], + ); + } + return const SizedBox.shrink(); + }), const SizedBox(height: 40), SizedBox( width: double.infinity, diff --git a/musadaq-app/lib/features/companies/views/companies_management_view.dart b/musadaq-app/lib/features/companies/views/companies_management_view.dart index 685a0a9..75d94a7 100644 --- a/musadaq-app/lib/features/companies/views/companies_management_view.dart +++ b/musadaq-app/lib/features/companies/views/companies_management_view.dart @@ -112,8 +112,14 @@ class CompaniesManagementView extends StatelessWidget { children: [ _buildDetailChip(Icons.numbers, company['tax_identification_number'] ?? 'غير محدد', isDark), const SizedBox(width: 8), - if (company['is_jofotara_connected'] == true || company['is_jofotara_connected'] == 1) + if (company['is_jofotara_connected'] == true || company['is_jofotara_connected'] == 1) ...[ _buildDetailChip(Icons.link, 'مرتبطة بجوفوتارا', isDark, color: const Color(0xFF10B981)), + const SizedBox(width: 8), + ], + if (company['tenant_name'] != null) + Expanded( + child: _buildDetailChip(Icons.account_balance, company['tenant_name'], isDark, color: Colors.blueAccent), + ), ], ), const SizedBox(height: 12), diff --git a/musadaq-app/lib/features/dashboard/controllers/dashboard_controller.dart b/musadaq-app/lib/features/dashboard/controllers/dashboard_controller.dart index 57a126a..bac3db8 100644 --- a/musadaq-app/lib/features/dashboard/controllers/dashboard_controller.dart +++ b/musadaq-app/lib/features/dashboard/controllers/dashboard_controller.dart @@ -61,6 +61,10 @@ class DashboardController extends GetxController { if (statsResponse.data['success'] == true) { stats.value = statsResponse.data['data']; userRole.value = statsResponse.data['data']['role'] ?? ''; + // Save role to storage for other controllers to use + if (userRole.value.isNotEmpty) { + await _storage.write('user_role', userRole.value); + } } // Fetch Recent Activity diff --git a/musadaq-app/lib/features/users/controllers/add_user_controller.dart b/musadaq-app/lib/features/users/controllers/add_user_controller.dart index c7acb4e..f527110 100644 --- a/musadaq-app/lib/features/users/controllers/add_user_controller.dart +++ b/musadaq-app/lib/features/users/controllers/add_user_controller.dart @@ -5,6 +5,7 @@ import '../../../core/network/dio_client.dart'; import '../../../core/utils/app_snackbar.dart'; import '../../../core/utils/logger.dart'; import '../../../core/storage/secure_storage.dart'; +import '../../dashboard/controllers/dashboard_controller.dart'; import 'users_management_controller.dart'; class AddUserController extends GetxController { @@ -30,7 +31,18 @@ class AddUserController extends GetxController { } Future _checkRoleAndFetchTenants() async { - final role = await _storage.read('user_role'); + String role = ''; + + // 1. Try to get from DashboardController first (fastest) + if (Get.isRegistered()) { + role = Get.find().userRole.value; + } + + // 2. Fallback to SecureStorage + if (role.isEmpty) { + role = await _storage.read('user_role') ?? ''; + } + if (role == 'super_admin') { isSuperAdmin.value = true; _fetchTenants(); diff --git a/musadaq-app/lib/features/users/views/users_management_view.dart b/musadaq-app/lib/features/users/views/users_management_view.dart index 9d9e052..60a8f9d 100644 --- a/musadaq-app/lib/features/users/views/users_management_view.dart +++ b/musadaq-app/lib/features/users/views/users_management_view.dart @@ -13,7 +13,8 @@ class UsersManagementView extends StatelessWidget { return Scaffold( appBar: AppBar( - title: const Text('إدارة مستخدمي النظام', style: TextStyle(fontFamily: 'El Messiri')), + title: const Text('إدارة مستخدمي النظام', + style: TextStyle(fontFamily: 'El Messiri')), centerTitle: true, backgroundColor: const Color(0xFF0F4C81), foregroundColor: Colors.white, @@ -37,9 +38,11 @@ class UsersManagementView extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(Icons.people_outline, size: 80, color: Colors.grey.shade400), + Icon(Icons.people_outline, + size: 80, color: Colors.grey.shade400), const SizedBox(height: 16), - const Text('لا يوجد موظفين مسجلين', style: TextStyle(fontSize: 18, color: Colors.grey)), + const Text('لا يوجد موظفين مسجلين', + style: TextStyle(fontSize: 18, color: Colors.grey)), ], ), ); @@ -55,28 +58,53 @@ class UsersManagementView extends StatelessWidget { return Card( elevation: 2, margin: const EdgeInsets.only(bottom: 12), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12)), child: ListTile( leading: CircleAvatar( - backgroundColor: const Color(0xFF0F4C81).withValues(alpha: 0.1), + backgroundColor: + const Color(0xFF0F4C81).withValues(alpha: 0.1), child: const Icon(Icons.person, color: Color(0xFF0F4C81)), ), title: Text( user['name'] ?? 'مستخدم', style: const TextStyle(fontWeight: FontWeight.bold), ), - subtitle: Text(user['email'] ?? ''), trailing: Container( - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + padding: + const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: const Color(0xFF10B981).withValues(alpha: 0.1), borderRadius: BorderRadius.circular(6), ), child: Text( user['role'] ?? '', - style: const TextStyle(color: Color(0xFF10B981), fontSize: 12), + style: const TextStyle( + color: Color(0xFF10B981), fontSize: 12), ), ), + isThreeLine: user['tenant_name'] != null, + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(user['email'] ?? ''), + if (user['tenant_name'] != null) ...[ + const SizedBox(height: 4), + Row( + children: [ + const Icon(Icons.account_balance, + size: 12, color: Colors.grey), + const SizedBox(width: 4), + Text( + user['tenant_name'], + style: const TextStyle( + fontSize: 12, color: Colors.grey), + ), + ], + ), + ], + ], + ), ), ); },