diff --git a/lib/controller/auth/login_controller.dart b/lib/controller/auth/login_controller.dart index 0ae276f..833f19a 100644 --- a/lib/controller/auth/login_controller.dart +++ b/lib/controller/auth/login_controller.dart @@ -79,8 +79,7 @@ class LoginController extends MyController { enableRemoteLogging(); logSafe("✅ Remote logging enabled after login."); - // ✅ Commented out FCM token registration after login - /* + final fcmToken = await LocalStorage.getFcmToken(); if (fcmToken?.isNotEmpty ?? false) { final success = await AuthService.registerDeviceToken(fcmToken!); @@ -90,7 +89,7 @@ class LoginController extends MyController { : "âš ī¸ Failed to register FCM token after login.", level: LogLevel.warning); } - */ + logSafe("Login successful for user: ${loginData['username']}"); Get.toNamed('/home'); diff --git a/lib/controller/auth/mpin_controller.dart b/lib/controller/auth/mpin_controller.dart index d9fa2d0..baeeeac 100644 --- a/lib/controller/auth/mpin_controller.dart +++ b/lib/controller/auth/mpin_controller.dart @@ -6,7 +6,7 @@ import 'package:marco/helpers/widgets/my_form_validator.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/view/dashboard/dashboard_screen.dart'; import 'package:marco/helpers/services/app_logger.dart'; -// import 'package:marco/helpers/services/firebase/firebase_messaging_service.dart'; // 🔴 Commented out +import 'package:marco/helpers/services/firebase/firebase_messaging_service.dart'; class MPINController extends GetxController { final MyFormValidator basicValidator = MyFormValidator(); @@ -256,14 +256,12 @@ class MPINController extends GetxController { try { isLoading.value = true; - // ✅ Fetch FCM Token here (DISABLED) - // final fcmToken = await FirebaseNotificationService().getFcmToken(); + final fcmToken = await FirebaseNotificationService().getFcmToken(); final response = await AuthService.verifyMpin( mpin: enteredMPIN, mpinToken: mpinToken, - // fcmToken: fcmToken ?? '', // 🔴 Commented out - fcmToken: '', // ✅ Passing empty string instead + fcmToken: fcmToken ?? '', ); isLoading.value = false; diff --git a/lib/helpers/services/app_initializer.dart b/lib/helpers/services/app_initializer.dart index ee9963e..1fa0da4 100644 --- a/lib/helpers/services/app_initializer.dart +++ b/lib/helpers/services/app_initializer.dart @@ -2,14 +2,14 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:url_strategy/url_strategy.dart'; -// import 'package:firebase_core/firebase_core.dart'; // ❌ Commented out Firebase +import 'package:firebase_core/firebase_core.dart'; import 'package:marco/controller/permission_controller.dart'; import 'package:marco/controller/project_controller.dart'; import 'package:marco/helpers/services/storage/local_storage.dart'; import 'package:marco/helpers/services/app_logger.dart'; import 'package:marco/helpers/services/auth_service.dart'; -// import 'package:marco/helpers/services/firebase/firebase_messaging_service.dart'; // ❌ Commented out FCM +import 'package:marco/helpers/services/firebase/firebase_messaging_service.dart'; import 'package:marco/helpers/services/device_info_service.dart'; import 'package:marco/helpers/theme/theme_customizer.dart'; import 'package:marco/helpers/theme/app_theme.dart'; @@ -20,7 +20,7 @@ Future initializeApp() async { await Future.wait([ _setupUI(), - // _setupFirebase(), // ❌ Commented out Firebase init + _setupFirebase(), _setupLocalStorage(), ]); @@ -28,7 +28,7 @@ Future initializeApp() async { await _handleAuthTokens(); await _setupTheme(); await _setupControllers(); - // await _setupFirebaseMessaging(); // ❌ Commented out FCM init + await _setupFirebaseMessaging(); _finalizeAppStyle(); @@ -56,17 +56,19 @@ Future _setupUI() async { logSafe("💡 UI setup completed."); } -// ❌ Commented out Firebase setup -/* + Future _setupFirebase() async { await Firebase.initializeApp(); logSafe("💡 Firebase initialized."); } -*/ Future _setupLocalStorage() async { - await LocalStorage.init(); - logSafe("💡 Local storage initialized."); + if (!LocalStorage.isInitialized) { + await LocalStorage.init(); + logSafe("💡 Local storage initialized."); + } else { + logSafe("â„šī¸ Local storage already initialized, skipping."); + } } Future _setupDeviceInfo() async { @@ -118,12 +120,12 @@ Future _setupControllers() async { } // ❌ Commented out Firebase Messaging setup -/* + Future _setupFirebaseMessaging() async { await FirebaseNotificationService().initialize(); logSafe("💡 Firebase Messaging initialized."); } -*/ + void _finalizeAppStyle() { AppStyle.init(); diff --git a/lib/helpers/services/auth_service.dart b/lib/helpers/services/auth_service.dart index 1f1d3e8..d12382e 100644 --- a/lib/helpers/services/auth_service.dart +++ b/lib/helpers/services/auth_service.dart @@ -54,7 +54,20 @@ class AuthService { } final body = {"fcmToken": fcmToken}; + final headers = { + ..._headers, + 'Authorization': 'Bearer $token', + }; + final endpoint = "$_baseUrl/auth/set/device-token"; + + // 🔹 Log request details + logSafe("📡 Device Token API Request"); + logSafe("âžĄī¸ Endpoint: $endpoint"); + logSafe("âžĄī¸ Headers: ${jsonEncode(headers)}"); + logSafe("âžĄī¸ Payload: ${jsonEncode(body)}"); + final data = await _post("/auth/set/device-token", body, authToken: token); + if (data != null && data['success'] == true) { logSafe("✅ Device token registered successfully."); return true; diff --git a/lib/helpers/services/firebase/firebase_messaging_service.dart b/lib/helpers/services/firebase/firebase_messaging_service.dart index 697a4ea..5cb98c5 100644 --- a/lib/helpers/services/firebase/firebase_messaging_service.dart +++ b/lib/helpers/services/firebase/firebase_messaging_service.dart @@ -1,9 +1,5 @@ -import 'dart:convert'; import 'package:firebase_messaging/firebase_messaging.dart'; -import 'package:googleapis_auth/auth_io.dart'; import 'package:logger/logger.dart'; -import 'package:http/http.dart' as http; -import 'package:flutter/services.dart' show rootBundle; import 'package:marco/helpers/services/local_notification_service.dart'; import 'package:marco/helpers/services/storage/local_storage.dart'; @@ -15,10 +11,6 @@ class FirebaseNotificationService { final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; final Logger _logger = Logger(); - static const _fcmScopes = [ - 'https://www.googleapis.com/auth/firebase.messaging', - ]; - /// Initialize FCM (Firebase.initializeApp() should be called once globally) Future initialize() async { _logger.i('✅ FirebaseMessaging initializing...'); @@ -119,80 +111,8 @@ class FirebaseNotificationService { } } - /// Send a test notification using FCM v1 API - Future sendTestNotification(String deviceToken) async { - try { - final client = await _getAuthenticatedHttpClient(); - if (client == null) return; - - final projectId = await _getProjectId(); - if (projectId == null) return; - - _logger.i('🏗 Firebase Project ID: $projectId'); - - final url = Uri.parse( - 'https://fcm.googleapis.com/v1/projects/$projectId/messages:send'); - final payload = _buildNotificationPayload(deviceToken); - - final response = await client.post( - url, - headers: {'Content-Type': 'application/json'}, - body: jsonEncode(payload), - ); - - if (response.statusCode == 200) { - _logger.i('✅ Test notification sent successfully'); - } else { - _logger.e('❌ Send failed: ${response.statusCode} ${response.body}'); - } - - client.close(); - } catch (e, s) { - _logger.e('❌ Error sending notification', error: e, stackTrace: s); - } - } - - /// Authenticated HTTP client using service account - Future _getAuthenticatedHttpClient() async { - try { - final credentials = ServiceAccountCredentials.fromJson( - json.decode(await rootBundle.loadString('assets/service-account.json')), - ); - return clientViaServiceAccount(credentials, _fcmScopes); - } catch (e, s) { - _logger.e('❌ Failed to authenticate', error: e, stackTrace: s); - return null; - } - } - - /// Get Project ID from service account - Future _getProjectId() async { - try { - final jsonMap = json - .decode(await rootBundle.loadString('assets/service-account.json')); - return jsonMap['project_id']; - } catch (e) { - _logger.e('❌ Failed to load project_id: $e'); - return null; - } - } - - /// Build FCM v1 payload - Map _buildNotificationPayload(String token) => { - "message": { - "token": token, - "notification": { - "title": "Test Notification", - "body": "This is a test message from Flutter (v1 API)" - }, - "data": { - "click_action": "FLUTTER_NOTIFICATION_CLICK", - "type": "expense_updated", // Example - "expense_id": "1234" - }, - } - }; - + + /// Handle tap on notification void _handleNotificationTap(RemoteMessage message) { _logger.i('📌 Notification tapped: ${message.data}'); diff --git a/lib/helpers/services/notification_action_handler.dart b/lib/helpers/services/notification_action_handler.dart index f044136..a3f3fd0 100644 --- a/lib/helpers/services/notification_action_handler.dart +++ b/lib/helpers/services/notification_action_handler.dart @@ -6,6 +6,10 @@ import 'package:marco/controller/task_planning/daily_task_controller.dart'; import 'package:marco/controller/task_Planning/daily_task_Planning_controller.dart'; import 'package:marco/controller/expense/expense_screen_controller.dart'; import 'package:marco/controller/expense/expense_detail_controller.dart'; +import 'package:marco/controller/directory/directory_controller.dart'; +import 'package:marco/controller/directory/notes_controller.dart'; +import 'package:marco/controller/document/user_document_controller.dart'; +import 'package:marco/controller/document/document_details_controller.dart'; /// Handles incoming FCM notification actions and updates UI/controllers. class NotificationActionHandler { @@ -37,7 +41,7 @@ class NotificationActionHandler { static void _handleByType(String type, Map data) { switch (type) { case 'expense_updated': - // No specific handler yet + _handleExpenseUpdated(data); break; case 'attendance_updated': _handleAttendanceUpdated(data); @@ -51,12 +55,14 @@ class NotificationActionHandler { static void _handleByKeyword( String keyword, String? action, Map data) { switch (keyword) { + /// 🔹 Attendance case 'Attendance': if (_isAttendanceAction(action)) { _handleAttendanceUpdated(data); } break; + /// 🔹 Tasks case 'Report_Task': _handleTaskUpdated(data, isComment: false); break; @@ -64,11 +70,7 @@ class NotificationActionHandler { case 'Task_Comment': _handleTaskUpdated(data, isComment: true); break; - case 'Expenses_Modified': - _handleExpenseUpdated(data); - break; - // ✅ New cases case 'Task_Modified': case 'WorkArea_Modified': case 'Floor_Modified': @@ -76,11 +78,41 @@ class NotificationActionHandler { _handleTaskPlanningUpdated(data); break; + /// 🔹 Expenses + case 'Expenses_Modified': + _handleExpenseUpdated(data); + break; + + /// 🔹 Documents + case 'Employee_Document_Modified': + case 'Project_Document_Modified': + _handleDocumentModified(data); + break; + + /// 🔹 Directory / Contacts + case 'Contact_Modified': + _handleContactModified(data); + break; + + case 'Contact_Note_Modified': + _handleContactNoteModified(data); + break; + + case 'Bucket_Modified': + _handleBucketModified(data); + break; + + case 'Bucket_Assigned': + _handleBucketAssigned(data); + break; + default: _logger.w('âš ī¸ Unhandled notification keyword: $keyword'); } } + /// ---------------------- HANDLERS ---------------------- + static void _handleTaskPlanningUpdated(Map data) { final projectId = data['ProjectId']; if (projectId == null) { @@ -99,7 +131,6 @@ class NotificationActionHandler { ); } - /// Validates the set of allowed Attendance actions static bool _isAttendanceAction(String? action) { const validActions = { 'CHECK_IN', @@ -132,7 +163,6 @@ class NotificationActionHandler { // Update Expense Detail (if open and matches this expenseId) _safeControllerUpdate( onFound: (controller) async { - // only refresh if the open screen is for this expense if (controller.expense.value?.id == expenseId) { await controller.fetchExpenseDetails(); _logger @@ -166,7 +196,97 @@ class NotificationActionHandler { ); } - /// Generic reusable method for safe GetX controller access + log handling + /// ---------------------- DOCUMENT HANDLER ---------------------- + static void _handleDocumentModified(Map data) { + final entityTypeId = data['EntityTypeId']; + final entityId = data['EntityId']; + + if (entityTypeId == null || entityId == null) { + _logger.w( + "âš ī¸ Document update received without EntityTypeId/EntityId: $data"); + return; + } + + // Refresh document list + _safeControllerUpdate( + onFound: (controller) async { + await controller.fetchDocuments( + entityTypeId: entityTypeId, + entityId: entityId, + reset: true, + ); + }, + notFoundMessage: 'âš ī¸ DocumentController not found, cannot refresh list.', + successMessage: '✅ DocumentController refreshed from notification.', + ); + + // Refresh document details (if open and matches) + // Refresh document details (if open and matches) + final documentId = data['DocumentId']; + if (documentId != null) { + _safeControllerUpdate( + onFound: (controller) async { + if (controller.documentDetails.value?.data?.id == documentId) { + await controller.fetchDocumentDetails(documentId); + _logger.i( + "✅ DocumentDetailsController refreshed for Document $documentId"); + } + }, + notFoundMessage: 'â„šī¸ DocumentDetailsController not active, skipping.', + successMessage: '✅ DocumentDetailsController checked for refresh.', + ); + } + } + + /// ---------------------- DIRECTORY HANDLERS ---------------------- + + static void _handleContactModified(Map data) { + _safeControllerUpdate( + onFound: (controller) => controller.fetchContacts(), + notFoundMessage: 'âš ī¸ DirectoryController not found, cannot refresh.', + successMessage: '✅ Directory contacts refreshed from notification.', + ); + } + + static void _handleContactNoteModified(Map data) { + final contactId = data['contactId']; + + _safeControllerUpdate( + onFound: (controller) { + if (contactId != null) { + controller.fetchCommentsForContact(contactId); + } + }, + notFoundMessage: + 'âš ī¸ DirectoryController not found, cannot refresh notes.', + successMessage: '✅ Directory comments refreshed from notification.', + ); + + _safeControllerUpdate( + onFound: (controller) => controller.fetchNotes(), + notFoundMessage: 'âš ī¸ NotesController not found, cannot refresh.', + successMessage: '✅ Notes refreshed from notification.', + ); + } + + static void _handleBucketModified(Map data) { + _safeControllerUpdate( + onFound: (controller) => controller.fetchBuckets(), + notFoundMessage: 'âš ī¸ DirectoryController not found, cannot refresh.', + successMessage: '✅ Buckets refreshed from notification.', + ); + } + + static void _handleBucketAssigned(Map data) { + _safeControllerUpdate( + onFound: (controller) => controller.fetchBuckets(), + notFoundMessage: 'âš ī¸ DirectoryController not found, cannot refresh.', + successMessage: '✅ Bucket assignments refreshed from notification.', + ); + } + + /// ---------------------- UTILITY ---------------------- + static void _safeControllerUpdate({ required void Function(T controller) onFound, required String notFoundMessage, diff --git a/lib/helpers/services/storage/local_storage.dart b/lib/helpers/services/storage/local_storage.dart index 90d49f0..c91c4c4 100644 --- a/lib/helpers/services/storage/local_storage.dart +++ b/lib/helpers/services/storage/local_storage.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; + import 'package:marco/controller/project_controller.dart'; import 'package:marco/helpers/services/auth_service.dart'; import 'package:marco/helpers/services/localizations/language.dart'; @@ -19,10 +20,13 @@ class LocalStorage { static const String _employeeInfoKey = "employee_info"; static const String _mpinTokenKey = "mpinToken"; static const String _isMpinKey = "isMpin"; - static const String _fcmTokenKey = 'fcm_token'; + static const String _fcmTokenKey = "fcm_token"; static const String _menuStorageKey = "dynamic_menus"; static SharedPreferences? _preferencesInstance; + static bool _initialized = false; + + static bool get isInitialized => _initialized; static SharedPreferences get preferences { if (_preferencesInstance == null) { @@ -31,49 +35,54 @@ class LocalStorage { return _preferencesInstance!; } - /// Initialization + /// Initialization (idempotent) static Future init() async { + if (_initialized) return; _preferencesInstance = await SharedPreferences.getInstance(); - await initData(); + await _initData(); + _initialized = true; } - static Future initData() async { + static Future _initData() async { AuthService.isLoggedIn = preferences.getBool(_loggedInUserKey) ?? false; ThemeCustomizer.fromJSON(preferences.getString(_themeCustomizerKey)); } -/// ================== Sidebar Menu ================== -static Future setMenus(List menus) async { - try { - final jsonList = menus.map((e) => e.toJson()).toList(); - return preferences.setString(_menuStorageKey, jsonEncode(jsonList)); - } catch (e) { - print("Error saving menus: $e"); - return false; - } -} -static List getMenus() { - final storedJson = preferences.getString(_menuStorageKey); - if (storedJson == null) return []; - try { - return (jsonDecode(storedJson) as List) - .map((e) => MenuItem.fromJson(e as Map)) - .toList(); - } catch (e) { - print("Error loading menus: $e"); - return []; + // ================== Sidebar Menu ================== + static Future setMenus(List menus) async { + try { + final jsonList = menus.map((e) => e.toJson()).toList(); + return preferences.setString(_menuStorageKey, jsonEncode(jsonList)); + } catch (e) { + print("Error saving menus: $e"); + return false; + } } -} -static Future removeMenus() => preferences.remove(_menuStorageKey); - /// ================== User Permissions ================== - static Future setUserPermissions( - List permissions) async { + static List getMenus() { + if (!_initialized) return []; + final storedJson = preferences.getString(_menuStorageKey); + if (storedJson == null) return []; + try { + return (jsonDecode(storedJson) as List) + .map((e) => MenuItem.fromJson(e as Map)) + .toList(); + } catch (e) { + print("Error loading menus: $e"); + return []; + } + } + + static Future removeMenus() => preferences.remove(_menuStorageKey); + + // ================== User Permissions ================== + static Future setUserPermissions(List permissions) async { final jsonList = permissions.map((e) => e.toJson()).toList(); return preferences.setString(_userPermissionsKey, jsonEncode(jsonList)); } static List getUserPermissions() { + if (!_initialized) return []; final storedJson = preferences.getString(_userPermissionsKey); if (storedJson == null) return []; return (jsonDecode(storedJson) as List) @@ -84,11 +93,12 @@ static Future removeMenus() => preferences.remove(_menuStorageKey); static Future removeUserPermissions() => preferences.remove(_userPermissionsKey); - /// ================== Employee Info ================== + // ================== Employee Info ================== static Future setEmployeeInfo(EmployeeInfo employeeInfo) => preferences.setString(_employeeInfoKey, jsonEncode(employeeInfo.toJson())); static EmployeeInfo? getEmployeeInfo() { + if (!_initialized) return null; final storedJson = preferences.getString(_employeeInfoKey); return storedJson == null ? null @@ -98,7 +108,7 @@ static Future removeMenus() => preferences.remove(_menuStorageKey); static Future removeEmployeeInfo() => preferences.remove(_employeeInfoKey); - /// ================== Login / Logout ================== + // ================== Login / Logout ================== static Future setLoggedInUser(bool loggedIn) => preferences.setBool(_loggedInUserKey, loggedIn); @@ -110,7 +120,6 @@ static Future removeMenus() => preferences.remove(_menuStorageKey); final refreshToken = getRefreshToken(); final fcmToken = getFcmToken(); - // Call API only if both tokens exist if (refreshToken != null && fcmToken != null) { await AuthService.logoutApi(refreshToken, fcmToken); } @@ -118,7 +127,6 @@ static Future removeMenus() => preferences.remove(_menuStorageKey); print("Logout API error: $e"); } - /// ===== Local Cleanup ===== await removeLoggedInUser(); await removeToken(_jwtTokenKey); await removeToken(_refreshTokenKey); @@ -126,7 +134,7 @@ static Future removeMenus() => preferences.remove(_menuStorageKey); await removeEmployeeInfo(); await removeMpinToken(); await removeIsMpin(); - await removeMenus(); // clear menus on logout + await removeMenus(); await preferences.remove("mpin_verified"); await preferences.remove(_languageKey); await preferences.remove(_themeCustomizerKey); @@ -139,20 +147,22 @@ static Future removeMenus() => preferences.remove(_menuStorageKey); Get.offAllNamed('/auth/login-option'); } - /// ================== Theme & Language ================== + // ================== Theme & Language ================== static Future setCustomizer(ThemeCustomizer themeCustomizer) => preferences.setString(_themeCustomizerKey, themeCustomizer.toJSON()); static Future setLanguage(Language language) => preferences.setString(_languageKey, language.locale.languageCode); - static String? getLanguage() => preferences.getString(_languageKey); + static String? getLanguage() => + _initialized ? preferences.getString(_languageKey) : null; - /// ================== Tokens ================== + // ================== Tokens ================== static Future setToken(String key, String token) => preferences.setString(key, token); - static String? getToken(String key) => preferences.getString(key); + static String? getToken(String key) => + _initialized ? preferences.getString(key) : null; static Future removeToken(String key) => preferences.remove(key); @@ -166,34 +176,39 @@ static Future removeMenus() => preferences.remove(_menuStorageKey); static String? getRefreshToken() => getToken(_refreshTokenKey); - /// ================== FCM Token ================== + // ================== FCM Token ================== static Future setFcmToken(String token) => preferences.setString(_fcmTokenKey, token); - static String? getFcmToken() => preferences.getString(_fcmTokenKey); + static String? getFcmToken() => + _initialized ? preferences.getString(_fcmTokenKey) : null; - /// ================== MPIN ================== + // ================== MPIN ================== static Future setMpinToken(String token) => preferences.setString(_mpinTokenKey, token); - static String? getMpinToken() => preferences.getString(_mpinTokenKey); + static String? getMpinToken() => + _initialized ? preferences.getString(_mpinTokenKey) : null; static Future removeMpinToken() => preferences.remove(_mpinTokenKey); static Future setIsMpin(bool value) => preferences.setBool(_isMpinKey, value); - static bool getIsMpin() => preferences.getBool(_isMpinKey) ?? false; + static bool getIsMpin() => + _initialized ? preferences.getBool(_isMpinKey) ?? false : false; static Future removeIsMpin() => preferences.remove(_isMpinKey); - /// ================== Generic Set/Get ================== + // ================== Generic Set/Get ================== static Future setBool(String key, bool value) => preferences.setBool(key, value); - static bool? getBool(String key) => preferences.getBool(key); + static bool? getBool(String key) => + _initialized ? preferences.getBool(key) : null; - static String? getString(String key) => preferences.getString(key); + static String? getString(String key) => + _initialized ? preferences.getString(key) : null; static Future saveString(String key, String value) => preferences.setString(key, value); diff --git a/lib/main.dart b/lib/main.dart index fb56445..77307e9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,13 +7,22 @@ import 'package:marco/view/my_app.dart'; import 'package:marco/helpers/theme/app_notifier.dart'; import 'package:marco/helpers/services/app_logger.dart'; import 'package:marco/view/layouts/offline_screen.dart'; +import 'package:marco/helpers/services/storage/local_storage.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); + // Initialize logging system await initLogging(); logSafe("App starting..."); + + // ✅ Ensure local storage is ready before enabling remote logging + await LocalStorage.init(); + logSafe("💡 Local storage initialized (early init for logging)."); + + // Now safe to enable remote logging enableRemoteLogging(); + try { await initializeApp(); logSafe("App initialized successfully."); diff --git a/lib/view/dashboard/dashboard_screen.dart b/lib/view/dashboard/dashboard_screen.dart index a2269e3..466d60b 100644 --- a/lib/view/dashboard/dashboard_screen.dart +++ b/lib/view/dashboard/dashboard_screen.dart @@ -16,8 +16,6 @@ import 'package:marco/controller/dynamicMenu/dynamic_menu_controller.dart'; import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; import 'package:marco/helpers/widgets/dashbaord/dashboard_overview_widgets.dart'; -// import 'package:marco/helpers/services/firebase/firebase_messaging_service.dart'; // ❌ Commented out - class DashboardScreen extends StatefulWidget { const DashboardScreen({super.key}); @@ -63,20 +61,6 @@ class _DashboardScreenState extends State with UIMixin { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // ❌ Commented out FCM Test Button - /* - ElevatedButton( - onPressed: () async { - final fcmService = FirebaseNotificationService(); - final token = await fcmService.getFcmToken(); - if (token != null) { - await fcmService.sendTestNotification(token); - } - }, - child: const Text("Send Test Notification"), - ), - MySpacing.height(10), - */ _buildDashboardStats(context), MySpacing.height(24), SizedBox( @@ -235,7 +219,6 @@ class _DashboardScreenState extends State with UIMixin { ); } - /// Stat Card /// Dashboard Statistics Section with Compact Cards Widget _buildDashboardStats(BuildContext context) { return Obx(() {