12 Commits 617e268148 ... 9e1f99d2b8

Autore SHA1 Messaggio Data
  Yulian 9e1f99d2b8 before push 9 mesi fa
  Yulian 1337ea3280 set pindah2 filter pake indexed stack 11 mesi fa
  Yulian d2f634452b rapikan param history 11 mesi fa
  Yulian ee83788aac beresin context di history 11 mesi fa
  Yulian 00fdde5d93 beresin home & detail 11 mesi fa
  Yulian a8974652fd beresin account 11 mesi fa
  Yulian e715330edc beresin context di login & qr 11 mesi fa
  Yulian c88361e6f5 fix context get & post 11 mesi fa
  Yulian cb549169fb upgrade flutter to 3.35, rapikan pubspec yaml 11 mesi fa
  Yulian 8f3b422739 fix: some hint & pemanggilan context 11 mesi fa
  Yulian a5fab5f975 bersih2 MaterialStateProperty 1 anno fa
  Yulian 9e648bff34 warning beres 1 anno fa
61 ha cambiato i file con 2178 aggiunte e 1693 eliminazioni
  1. 1 1
      android/app/build.gradle.kts
  2. 2 1
      android/settings.gradle.kts
  3. 5 4
      lib/app_router.dart
  4. 30 34
      lib/main.dart
  5. 58 57
      lib/src/api/api_auth_provider.dart
  6. 14 9
      lib/src/api/jwt_token.dart
  7. 3 6
      lib/src/bloc/login/login_bloc.dart
  8. 23 16
      lib/src/layouts/auth/change_code.dart
  9. 3 4
      lib/src/layouts/auth/end_session.dart
  10. 68 48
      lib/src/layouts/auth/login.dart
  11. 23 26
      lib/src/layouts/auth/qr.dart
  12. 40 37
      lib/src/layouts/components/auto_login.dart
  13. 6 4
      lib/src/layouts/components/camera.dart
  14. 13 16
      lib/src/layouts/components/error_page.dart
  15. 19 21
      lib/src/layouts/components/photo_chat.dart
  16. 4 7
      lib/src/layouts/components/responsive.dart
  17. 59 71
      lib/src/layouts/components/template.dart
  18. 46 40
      lib/src/layouts/functions/account.dart
  19. 13 11
      lib/src/layouts/functions/detail.dart
  20. 140 105
      lib/src/layouts/functions/history.dart
  21. 99 93
      lib/src/layouts/functions/home.dart
  22. 5 8
      lib/src/layouts/functions/message.dart
  23. 7 5
      lib/src/layouts/functions/request.dart
  24. 0 3
      lib/src/layouts/landing/landing_page.dart
  25. 44 23
      lib/src/layouts/mobile/app_mobile.dart
  26. 3 3
      lib/src/layouts/mobile/banner_detail.dart
  27. 12 13
      lib/src/layouts/mobile/history_detail.dart
  28. 7 7
      lib/src/layouts/mobile/history_detail_pending.dart
  29. 25 23
      lib/src/layouts/mobile/history_forum.dart
  30. 5 5
      lib/src/layouts/mobile/history_rating.dart
  31. 42 40
      lib/src/layouts/mobile/menu_account.dart
  32. 206 171
      lib/src/layouts/mobile/menu_history.dart
  33. 98 78
      lib/src/layouts/mobile/menu_home.dart
  34. 1 1
      lib/src/layouts/mobile/message_broadcast.dart
  35. 9 8
      lib/src/layouts/mobile/message_chat.dart
  36. 2 2
      lib/src/layouts/mobile/message_list.dart
  37. 3 3
      lib/src/layouts/mobile/password.dart
  38. 5 5
      lib/src/layouts/mobile/request_create.dart
  39. 7 7
      lib/src/layouts/mobile/request_select.dart
  40. 5 5
      lib/src/layouts/mobile/request_success.dart
  41. 3 3
      lib/src/layouts/web/banner_detail.dart
  42. 3 3
      lib/src/layouts/web/history_detail.dart
  43. 2 2
      lib/src/layouts/web/history_detail_pending.dart
  44. 4 4
      lib/src/layouts/web/history_forum.dart
  45. 205 206
      lib/src/layouts/web/menu_account.dart
  46. 133 129
      lib/src/layouts/web/menu_history.dart
  47. 12 12
      lib/src/layouts/web/menu_home.dart
  48. 4 4
      lib/src/layouts/web/message_broadcast.dart
  49. 148 149
      lib/src/layouts/web/message_list.dart
  50. 4 4
      lib/src/layouts/web/password.dart
  51. 3 3
      lib/src/layouts/web/request_create.dart
  52. 9 9
      lib/src/layouts/web/request_select.dart
  53. 5 5
      lib/src/layouts/web/request_success.dart
  54. 1 1
      lib/src/model/token/token.g.dart
  55. 18 7
      lib/src/utils/U.dart
  56. 0 1
      lib/src/utils/cache_manager.dart
  57. 7 16
      lib/src/utils/dio_logging_interceptors.dart
  58. 45 29
      lib/src/utils/provider.dart
  59. 297 0
      lib/src/utils/ui_service.dart
  60. 45 37
      pubspec.lock
  61. 75 48
      pubspec.yaml

+ 1 - 1
android/app/build.gradle.kts

@@ -109,7 +109,7 @@ android {
         applicationId = "com.datacomsolusindo.telnow2.telnow_mobile2"
         // You can update the following values to match your application needs.
         // For more information, see: https://flutter.dev/to/review-gradle-config.
-        minSdk = 23//flutter.minSdkVersion
+        minSdk = flutter.minSdkVersion//flutter.minSdkVersion
         targetSdk = flutter.targetSdkVersion
         versionCode = flutter.versionCode
         versionName = flutter.versionName

+ 2 - 1
android/settings.gradle.kts

@@ -51,7 +51,8 @@ plugins {
     // START: FlutterFire Configuration
     id("com.google.gms.google-services") version("4.3.15") apply false
     // END: FlutterFire Configuration
-    id("org.jetbrains.kotlin.android") version "1.8.22" apply false
+    id("org.jetbrains.kotlin.android") version "2.1.0" apply false
+//    id("org.jetbrains.kotlin.android") version "1.8.22" apply false
 }
 
 include(":app")

+ 5 - 4
lib/app_router.dart

@@ -8,6 +8,7 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 
 @AutoRouterConfig(replaceInRouteName: 'Screen|Page,Route')
 class AppRouter extends RootStackRouter {
+  AppRouter({super.navigatorKey});
   final SharedPreferencesManager _sharedPreferencesManager = locator<SharedPreferencesManager>();
 
   @override
@@ -55,7 +56,7 @@ class AppRouter extends RootStackRouter {
     AutoRouteGuard.simple((resolver, router) async{
       RouteMatch<dynamic> vRedirector = resolver.route;
       String? rawPid = '';
-      var pid;
+      String? pid = '';
       bool stb = false;
 
       if(vRedirector.params.isNotEmpty){
@@ -65,7 +66,7 @@ class AppRouter extends RootStackRouter {
       try {
         pid = Uri.decodeComponent(rawPid);
       } catch (e) {
-        print("err : $e");
+        debugPrint("err : $e");
         stb = true;
       }
 
@@ -82,7 +83,7 @@ class AppRouter extends RootStackRouter {
 
       if (U.getAccessCode() == null) {
         await _sharedPreferencesManager.putString(SharedPreferencesManager.keyBaseUrl, U.rewriteUrl(vRedirector.fragment));
-        await _sharedPreferencesManager.putString(SharedPreferencesManager.keyAccessCode, pid);
+        await _sharedPreferencesManager.putString(SharedPreferencesManager.keyAccessCode, pid!);
         await _sharedPreferencesManager.putString(SharedPreferencesManager.keySerialCode, 'P-$pid}');
         try {
           var ver = await U.reloadLicense();
@@ -105,7 +106,7 @@ class AppRouter extends RootStackRouter {
             var ver = await U.reloadLicense();
             await _sharedPreferencesManager.putInt(SharedPreferencesManager.version, ver['version']);
           } catch (e) {
-            print('Error get2: ${e.toString()}');
+            debugPrint('Error get2: ${e.toString()}');
             await _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyBaseUrl);
             await _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyAccessCode);
             await _sharedPreferencesManager.clearKey(SharedPreferencesManager.keySerialCode);

+ 30 - 34
lib/main.dart

@@ -29,6 +29,7 @@ import 'package:http/http.dart' as http;
 import 'package:telnow_mobile_new/src/api/jwt_token.dart';
 
 import 'app_router.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 // final SharedPreferencesManager _sharedPreferencesManager = locator<SharedPreferencesManager>();
 var isDebug = true;
@@ -67,9 +68,7 @@ void main() async{
 
     await setupLocator();
     SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
-    HttpOverrides.global = new MyHttpOverrides();
-    // WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
-    // FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
+    HttpOverrides.global = MyHttpOverrides();
     runApp(
         EasyLocalization(
             path: 'assets/lang',
@@ -91,12 +90,11 @@ void main() async{
         )
     );
   } catch (error, stacktrace) {
-    print('$error & $stacktrace');
+    debugPrint('$error & $stacktrace');
   }
 }
 
 class MyCustomScrollBehavior extends MaterialScrollBehavior {
-  // Override behavior methods and getters like dragDevices
   @override
   Set<PointerDeviceKind> get dragDevices {
     return {
@@ -106,11 +104,10 @@ class MyCustomScrollBehavior extends MaterialScrollBehavior {
   }
 }
 
+final _appRouter = AppRouter(navigatorKey: UIService.navigatorKey);
 class MyApp extends StatelessWidget {
-  MyApp({super.key});
-  final _appRouter = AppRouter();
+  const MyApp({super.key});
 
-  // This widget is the root of your application.
   @override
   Widget build(BuildContext context) {
     return MultiProvider(
@@ -123,6 +120,8 @@ class MyApp extends StatelessWidget {
         ChangeNotifierProvider(create: (_) => MessageModule()),
       ],
       child: MaterialApp.router(
+        // routerDelegate: _appRouter.delegate(),
+        // routeInformationParser: _appRouter.defaultRouteParser(),
         scrollBehavior: MyCustomScrollBehavior(),
         debugShowCheckedModeBanner: false,
         localizationsDelegates: context.localizationDelegates,
@@ -149,7 +148,7 @@ class NavigationService {
 
 @RoutePage()
 class HomeGuardPage extends StatefulWidget {
-  const HomeGuardPage({Key? key}) : super(key: key);
+  const HomeGuardPage({super.key});
 
   @override
   State<HomeGuardPage> createState() => _HomeGuardPageState();
@@ -161,11 +160,9 @@ class _HomeGuardPageState extends State<HomeGuardPage> {
   @override
   Widget build(BuildContext context) {
     eventBus.on().listen((event) {
-      // print("eventBus => ${event.toString()}");
       if(event.toString() == 'logout') {
-        // token.logout();
-        context.navigateToPath('/end-session');
-        // return;
+        UIService.navigateNamed('/end-session');
+        // context.navigateToPath('/end-session');
       }
     });
     // TODO: implement build
@@ -189,21 +186,21 @@ class NotificationClass {
   final ApiAuthProvider _apiAuthProvider = ApiAuthProvider();
 
   initFirebaseMessaging(BuildContext context) {
+    final locale = context.locale.toString();
+
     FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
       var mid = message.data['mid'];
       var token = await U.getFcmToken();
-      // print('tokennya');
-      // print(token);
       if (token != null) {
         _apiAuthProvider.postDataNoAuth('/api/notifications/received/$token/$mid').then((value) =>
-            print('Send confirm succes!')
+            debugPrint('Send confirm succes!')
         );
       }
 
       if(kIsWeb){
         QuickNotify.notify(
           title: message.data['subject'],
-          content: context.locale.toString() == 'id' ? message.data['description'] : message.data['descriptionEn'],
+          content: locale == 'id' ? message.data['description'] : message.data['descriptionEn'],
         );
       }
       else{
@@ -234,34 +231,38 @@ class NotificationClass {
         );
         var platformChannelSpecifics = NotificationDetails(android: androidPlatformChannelSpecifics);
         U.flutterLocalNotificationsPlugin.show(0, message.data['subject'],
-            context.locale.toString() == 'id' ? message.data['description'] : message.data['descriptionEn'], platformChannelSpecifics,
+            locale == 'id' ? message.data['description'] : message.data['descriptionEn'], platformChannelSpecifics,
             payload: jsonEncode(message.data));
       }
     });
 
     FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
+
     FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage? message) {
       if (message != null && !U.hidePayload) {
-        // print(message.data);
-        goNotification(message.data, context);
+        // goNotification(message.data, context);
+        UIService.goNotification(message.data);
       }
     });
+
     FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
       U.setHidePayload(false);
-      goNotification(message.data, context);
+      UIService.goNotification(message.data);
+      // goNotification(message.data, context);
     });
   }
 
-  getActiveNotif() async {
-    List<ActiveNotification>? activeNotif = await U.flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.getActiveNotifications();
-    return activeNotif;
+  getActiveNotification() async {
+    List<ActiveNotification>? activeNotification = await U.flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.getActiveNotifications();
+    return activeNotification;
   }
 
-  startNotification(BuildContext context, {code}) async {
+  startNotification({code}) async {
+    final locale = NavigationService.navigatorKey.currentContext?.locale.toString();
     var token = await U.getFcmToken();
     if (token != null) {
-      Map data = {'token': token, 'language': code ?? context.locale.toString().toUpperCase()};
-      var res = _apiAuthProvider.postData('/api/fcmTokens/register', null, data, context);
+      Map data = {'token': token, 'language': code ?? locale?.toUpperCase()};
+      var res = _apiAuthProvider.postData('/api/fcmTokens/register', null, data);
       return res;
     }
   }
@@ -269,7 +270,7 @@ class NotificationClass {
   stopNotification(BuildContext context) async {
     var token = await U.getFcmToken();
     if (token != null) {
-      var res = _apiAuthProvider.postData('/api/fcmTokens/remove/$token', null, null, context);
+      var res = _apiAuthProvider.postData('/api/fcmTokens/remove/$token', null, null);
       return res;
     }
   }
@@ -287,11 +288,6 @@ class NotificationClass {
       context.router.removeLast();
       context.navigateToPath("/app/$pid/menu/history");
     }
-    // if (list['type'] == 'FORUM') {
-    //   Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: Forum(list['info'], list['currentStatus'] == 'DIMULAI' || list['currentStatus'] == 'DISELESAIKAN' ? true : false)));
-    // } else {
-    //   Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: DetailMisi(list['info'])));
-    // }
   }
 }
 
@@ -307,7 +303,7 @@ Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
   FirebaseMessaging.instance.getToken().then((token) async {
     if (token != null) {
       http.post(Uri.https(U.decodeBase64Url(prefs.getString(SharedPreferencesManager.keyBaseUrl)!).split('//')[1], '$decAccCode/api/notifications/received/$token/$mid')).then((value) {
-        print("kirim confirm");
+        debugPrint("kirim confirm");
         prefs.setString(SharedPreferencesManager.lastMid, mid);
       });
     }

+ 58 - 57
lib/src/api/api_auth_provider.dart

@@ -3,13 +3,13 @@ import 'dart:convert';
 import 'dart:io';
 import 'package:dio/dio.dart';
 import 'package:flutter/cupertino.dart';
-import 'package:flutter/foundation.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
 import 'package:telnow_mobile_new/src/model/login/login_body.dart';
 import 'package:telnow_mobile_new/src/model/refreshtoken/refresh_token_body.dart';
 import 'package:telnow_mobile_new/src/model/token/token.dart';
 import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:telnow_mobile_new/src/utils/dio_logging_interceptors.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:easy_localization/easy_localization.dart';
@@ -21,7 +21,7 @@ const String host = '';
 // const String host = '192.168.100.18:8080';
 
 class ApiAuthProvider {
-  final Dio _dio = new Dio();
+  final Dio _dio = Dio();
   final SharedPreferencesManager _sharedPreferencesManager = locator<SharedPreferencesManager>();
 
   final String displayVersion = '4.0.17'; //versi aplikasi untuk di tampilkan
@@ -41,7 +41,7 @@ class ApiAuthProvider {
   final String clientSecret = '3JskYu5zxlXRDv6g';
   // final String clientIdWeb = 'web-apHca0ncOX';
   // final String clientSecretWeb = '1qeIwW8Wu9AF4DRF';
-  var isDebug;
+  bool isDebug = false;
 
   ApiAuthProvider() {
 //     print('ApiAuthProvider called');
@@ -76,15 +76,11 @@ class ApiAuthProvider {
       var strData = "";
       var i = 0;
       loginData.forEach((key, value) {
-        strData = strData +
-            (i == 0 ? '' : '&') +
-            key +
-            '=' +
-            Uri.encodeComponent(value);
+        strData = '$strData${i == 0 ? '' : '&'}$key=${Uri.encodeComponent(value)}';
         i++;
       });
       // print(strData);
-      final response = await _dio.post('/oauth/token?' + strData,
+      final response = await _dio.post('/oauth/token?$strData',
           options: Options(
             headers: {
               'Accept': 'application/json',
@@ -130,16 +126,12 @@ class ApiAuthProvider {
       var strData = "";
       var i = 0;
       refreshData.forEach((key, value) {
-        strData = strData +
-            (i == 0 ? '' : '&') +
-            key +
-            '=' +
-            Uri.encodeComponent(value);
+        strData = '$strData${i == 0 ? '' : '&'}$key=${Uri.encodeComponent(value)}';
         i++;
       });
       // print(strData);
       final response = await _dio.post(
-        '/oauth/token?' + strData,
+        '/oauth/token?$strData',
         options: Options(
           headers: {
             'Accept': 'application/json',
@@ -172,7 +164,7 @@ class ApiAuthProvider {
       );
       return json.decode(response.data!);
     } on DioException catch (error) {
-      print(error.response);
+      debugPrint(error.response.toString());
       if (error.response == null) {
         try {
           final result = await InternetAddress.lookup('google.com');
@@ -189,7 +181,7 @@ class ApiAuthProvider {
           handlingError(context, 2); //error connection
         } else {
           await Future.delayed(Duration(milliseconds: 200));
-          return getData(path, params, context, secondCheck: true);
+          return getData(path, params, secondCheck: true);
         }
         //error server
       } else if (error.response?.statusCode == 401) {
@@ -200,18 +192,18 @@ class ApiAuthProvider {
           handlingError(context, 2); //error connection
         } else {
           await Future.delayed(Duration(milliseconds: 200));
-          return getData(path, params, context, secondCheck: true);
+          return getData(path, params, secondCheck: true);
         }
       }
       return Future.error(error);
     } catch (error) {
-      print(error.toString());
+      debugPrint(error.toString());
       handlingError(context, 1);
       return Future.error(error);
     }
   }
 
-  Future getData(String path, var params, BuildContext context, {bool secondCheck = false}) async {
+  Future getData(String path, var params, {bool secondCheck = false}) async {
     try {
       Response<String> response = await _dio.getUri(
         Uri(path: path, queryParameters: params),
@@ -221,31 +213,28 @@ class ApiAuthProvider {
           },
         ),
       );
-//      print(response.data);
       return json.decode(response.data!);
     } on DioException catch (error) {
-      // print(error.response!.statusCode);
-      bool isOpen = ModalRoute.of(context)?.isCurrent != true;
+      bool isOpen = UIService.isCurrentRouteInactive;
       if (error.response == null) {
         try {
           final result = await InternetAddress.lookup('google.com');
           if (!isOpen && result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
-            handlingError(context, 1); //error server
+            UIService.handlingError(ErrorType.noInternet); //error server
           }
         } on SocketException catch (_) {
-          // if(!isOpen) handlingError(context, 0); //no internet
         }
+
       } else if (!isOpen && error.response!.statusCode! >= 500) {
-        handlingError(context, 1); //error server
+        UIService.handlingError(ErrorType.serverError); //error server
       } else if (!isOpen && error.response?.statusCode == 401) {
-        handlingError(context, 3); //error auth
+        UIService.handlingError(ErrorType.invalidAccount); //error auth
       } else {
         if(!isOpen){
           if (secondCheck) {
-            // print('secondCheck');
-            handlingError(context, 2); //error connection
+            UIService.handlingError(ErrorType.connectionError); //error connection
           } else {
-            return getData(path, params, context, secondCheck: true);
+            return getData(path, params, secondCheck: true);
           }
         }
       }
@@ -253,7 +242,7 @@ class ApiAuthProvider {
     }
   }
 
-  Future postData(String path, var params, var data, context) async {
+  Future postData(String path, var params, var data) async {
     try {
       Response response = await _dio.postUri(
         Uri(path: path, queryParameters: params),
@@ -272,37 +261,50 @@ class ApiAuthProvider {
         try {
           final result = await InternetAddress.lookup('google.com');
           if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
-            showError(context, 'errorConnection'.tr());
+            UIService.showError('errorConnection'.tr());
+            // showError(context, 'errorConnection'.tr());
           }
         } on SocketException catch (_) {
-          showError(context, 'noInternet'.tr());
+          UIService.showError('noInternet'.tr());
+          // showError(context, 'noInternet'.tr());
         }
       } else if (error.response!.statusCode! >= 500) {
-        showError(context, 'errorConnection'.tr());
+        UIService.showError('errorConnection'.tr());
+        // showError(context, 'errorConnection'.tr());
       } else if (error.response?.statusCode == 401) {
-        handlingError(context, 3); //error auth
+        UIService.handlingError(ErrorType.invalidAccount);
+        // handlingError(context, 3); //error auth
       } else if (error.response?.statusCode == 422) {
         if(error.response?.data['message'] == 'Worktime did not found'){
-          showError(context, 'notFoundWorktime'.tr());
+          UIService.showError('notFoundWorktime'.tr());
+          // showError(context, 'notFoundWorktime'.tr());
         } else if(error.response?.data['message'] == 'Cant send broadcast message, you have not permission.'){
-          showError(context, 'broadcastPermission'.tr());
+          UIService.showError('broadcastPermission'.tr());
+          // showError(context, 'broadcastPermission'.tr());
         } else if(error.response?.data['message'] == 'Cant send broadcast message, you have not permission.'){
-          showError(context, 'broadcastPermission'.tr());
+          UIService.showError('broadcastPermission'.tr());
+          // showError(context, 'broadcastPermission'.tr());
         } else if(error.response?.data['message'] == 'request did not match informant rights'){
-          showError(context, 'idNotMatch'.tr().replaceAll("#ID", data['user_id']));
+          UIService.showError('idNotMatch'.tr().replaceAll("#ID", data['user_id']));
+          // showError(context, 'idNotMatch'.tr().replaceAll("#ID", data['user_id']));
         } else if(error.response?.data['message'] == 'Informant user id not found.' ||
             error.response?.data['message'] == 'invalid user informant requested'
         ){
-          showError(context, 'idNotFound'.tr());
+          UIService.showError('idNotFound'.tr());
+          // showError(context, 'idNotFound'.tr());
         } else if(error.response?.data['message'] == 'You are not registered as receptionist or room attendant.'){
-          showError(context, 'informantNotRegistered'.tr());
+          UIService.showError('informantNotRegistered'.tr());
+          // showError(context, 'informantNotRegistered'.tr());
         } else if(error.response?.data['message'] == 'invalid parent ticket'){
-          showError(context, 'invalidParentTicket'.tr());
+          UIService.showError('invalidParentTicket'.tr());
+          // showError(context, 'invalidParentTicket'.tr());
         } else {
-          showError(context, error.response?.data['message']);
+          UIService.showError(error.response?.data['message']);
+          // showError(context, error.response?.data['message']);
         }
       } else {
-        showError(context, 'errorServer'.tr());
+        UIService.showError('errorServer'.tr());
+        // showError(context, 'errorServer'.tr());
       }
       return null;
     }
@@ -314,12 +316,12 @@ class ApiAuthProvider {
       Response response = await _dio.postUri(Uri(path: path));
       return response.data;
     } on DioException catch (error) {
-     print(error.response);
+     debugPrint(error.response.toString());
       return null;
     }
   }
 
-  Future patchData(String path, var data, context, {var params}) async {
+  Future patchData(String path, var data, {var params}) async {
     try {
       Response response = await _dio.patchUri(
         Uri(path: path, queryParameters: params),
@@ -337,26 +339,25 @@ class ApiAuthProvider {
         try {
           final result = await InternetAddress.lookup('google.com');
           if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
-            showError(context, 'errorConnection'.tr());
+            UIService.showError('errorConnection'.tr());
           }
         } on SocketException catch (_) {
-          showError(context, 'noInternet'.tr());
+          UIService.showError('noInternet'.tr());
         }
       } else if (error.response!.statusCode! >= 500) {
-        showError(context, 'errorConnection'.tr());
+        UIService.showError('errorConnection'.tr());
       } else if (error.response?.statusCode == 401) {
-        handlingError(context, 3); //error auth
+        UIService.handlingError(ErrorType.invalidAccount); //error auth
       } else if (error.response?.statusCode == 422) {
         if (error.response?.data['message'] == 'Old Password Not Match.') {
-          showError(context, 'wrongOldPass'.tr());
-        } else if (error.response?.data['message'] ==
-            'New password already in used.') {
-          showError(context, 'alreadyUsePass'.tr());
+          UIService.showError('wrongOldPass'.tr());
+        } else if (error.response?.data['message'] == 'New password already in used.') {
+          UIService.showError('alreadyUsePass'.tr());
         } else {
-          showError(context, error.response?.data['message']);
+          UIService.showError(error.response?.data['message']);
         }
       } else {
-        showError(context, 'errorServer'.tr());
+        UIService.showError('errorServer'.tr());
       }
       return null;
     }
@@ -429,6 +430,6 @@ class ApiAuthProvider {
   }
 
   String getServiceAsset(String key){
-    return _dio.options.baseUrl+'/assets/lotties/$key';
+    return '${_dio.options.baseUrl}/assets/lotties/$key';
   }
 }

+ 14 - 9
lib/src/api/jwt_token.dart

@@ -3,13 +3,14 @@ import 'dart:io';
 
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
+import 'package:telnow_mobile_new/main.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
-import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'api_auth_provider.dart';
 
 class JwtToken{
@@ -65,18 +66,22 @@ class JwtToken{
     return utf8.decode(base64Url.decode(output));
   }
 
-  Future<dynamic> getUserData(BuildContext context) async{
+  Future<dynamic> getUserData() async{
     bool isTokenExist = _sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyAccessToken)!;
     String token = _sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessToken)!;
     String username = _sharedPreferencesManager.getString(SharedPreferencesManager.keyUsername)!;
 
+    void setLocale(code){
+      NavigationService.navigatorKey.currentContext?.setLocale(Locale(code));
+    }
+
     if(isTokenExist && token != ''){
       var jwt = parseJwtPayLoad(token);
-      var url = '/api/informants/'+jwt['userId'].toString();
+      var url = '/api/informants/${jwt['userId']}';
       Map<String, dynamic>? user;
 
       if(U.getInternetStatus()){
-        user = await _apiAuthProvider.getData(url, null, context);
+        user = await _apiAuthProvider.getData(url, null);
       }
       else{
         var val = await CacheMan.readData(url);
@@ -86,13 +91,13 @@ class JwtToken{
       }
 
       if((user != null && user['userId'].toLowerCase() != username.toLowerCase())){
-        U.getChangedPassword() ? handlingError(context, 3) : (){}; //invalid user
+        if (U.getChangedPassword()) UIService.handlingError(ErrorType.invalidAccount);
         return null;
       } else if(user != null && user['dateExpired'] != null){
         final dateToCheck = DateTime.parse(user['dateExpired']);
         final now = DateTime.now();
         if(now.isAfter(dateToCheck)){
-          handlingError(context, 4); //expired
+          UIService.handlingError(ErrorType.expiredAccount); //expired
           return null;
         }
       }
@@ -100,9 +105,9 @@ class JwtToken{
       if(user != null){
         if(U.newServerVersion(1754624839)){
           var code = U.getLang(user['language']).toLowerCase();
-          context.setLocale(Locale(code));
+          setLocale(code);
         } else {
-          context.setLocale(Locale(user['language'].toLowerCase()));
+          setLocale(user['language'].toLowerCase());
         }
       }
 
@@ -111,7 +116,7 @@ class JwtToken{
       }
       return user;
     } else {
-      U.getChangedPassword() ? handlingError(context, 3) : (){}; //invalid user
+      U.getChangedPassword() ? UIService.handlingError(ErrorType.invalidAccount) : (){}; //invalid user
       return Future.error("not found");
     }
   }

+ 3 - 6
lib/src/bloc/login/login_bloc.dart

@@ -30,19 +30,16 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
   final ApiAuthRepository apiAuthRepository = ApiAuthRepository();
   final SharedPreferencesManager sharedPreferencesManager = locator<SharedPreferencesManager>();
 
-  LoginBloc(LoginState initialState) : super(initialState);
+  LoginBloc(super.initialState);
 
   // @override
-  // LoginState get initialState => LoginInitial();
-
-  @override
   Stream<LoginState> mapEventToState(LoginEvent event) async* {
     LoginBody loginBody = event.loginBody;
-    if (loginBody.username == null || loginBody.username.isEmpty) {
+    if (loginBody.username.isEmpty) {
       yield LoginFailure('username');
       return;
     }
-    else if (loginBody.password == null || loginBody.password.isEmpty) {
+    else if (loginBody.password.isEmpty) {
       yield LoginFailure('password');
       return;
     }

+ 23 - 16
lib/src/layouts/auth/change_code.dart

@@ -8,13 +8,14 @@ import 'package:telnow_mobile_new/src/api/jwt_token.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:toggle_switch/toggle_switch.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:http/http.dart' as http;
 
 @RoutePage()
 class ChangeCodePage extends StatefulWidget {
-  const ChangeCodePage({Key? key}) : super(key: key);
+  const ChangeCodePage({super.key});
 
   @override
   State<ChangeCodePage> createState() => _ChangeCodePageState();
@@ -31,7 +32,11 @@ class _ChangeCodePageState extends State<ChangeCodePage> {
   String imageUrl = '';
 
   getNewLicense(rawPid) async{
-    try{imageUrl = U.decodeBase64Url(U.getBaseUrl()!) + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';}catch(e){}
+    try{
+      imageUrl = '${U.decodeBase64Url(U.getBaseUrl()!)}${U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))}/assets/background/tn';
+    }catch(e){
+      debugPrint(e.toString());
+    }
     var pid = Uri.decodeComponent(rawPid);
     var response = await http.post(Uri.https(U.decodeBase64Url(U.getBaseUrl()!).split('//')[1], '${U.decodeBase64Url(pid)}/api/license'));
     var ver = json.decode(response.body);
@@ -120,8 +125,8 @@ class _ChangeCodePageState extends State<ChangeCodePage> {
                             height: 30, width: 100,
                             child: TextButton(
                               style: ButtonStyle(
-                                  backgroundColor: MaterialStateProperty.all<Color>(Colors.black12),
-                                  shape: MaterialStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)))
+                                  backgroundColor: WidgetStateProperty.all<Color>(Colors.black12),
+                                  shape: WidgetStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)))
                               ),
                               child: Text('textCancel'.tr(), style: TextStyle(color: Colors.black, fontSize: 12)),
                               onPressed: (){
@@ -141,8 +146,8 @@ class _ChangeCodePageState extends State<ChangeCodePage> {
                             height: 30, width: 100,
                             child: TextButton(
                               style: ButtonStyle(
-                                  backgroundColor: MaterialStateProperty.all<Color>(Color(0xff0D497F)),
-                                  shape: MaterialStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)))
+                                  backgroundColor: WidgetStateProperty.all<Color>(Color(0xff0D497F)),
+                                  shape: WidgetStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)))
                               ),
                               child: Text('textContinue'.tr(), style: TextStyle(color: Colors.white, fontSize: 12)),
                               onPressed: ()async{
@@ -160,31 +165,33 @@ class _ChangeCodePageState extends State<ChangeCodePage> {
                                     var tkn = await U.getFcmToken();
                                     if (tkn == null) {
                                       token.logout();
-                                      context.router.removeLast();
-                                      context.navigateToPath("/app/$rawPid/login");
+                                      // context.router.removeLast();
+                                      UIService.navigateNamed("/app/$rawPid/login");
                                     } else {
                                       var param = {'token': tkn, 'whiteListToken': false};
-                                      var res = await _apiAuthProvider.postData('/api/fcmTokens/remove/', null, param, context);
+                                      var res = await _apiAuthProvider.postData('/api/fcmTokens/remove/', null, param);
                                       if (res != null && res['success']) {
                                         token.logout();
-                                        context.router.removeLast();
-                                        context.navigateToPath("/app/$rawPid/login");
+                                        // context.router.removeLast();
+                                        UIService.navigateNamed("/app/$rawPid/login");
                                       }
                                     }
                                   }
                                   else{
                                     token.logout();
-                                    context.router.removeLast();
-                                    context.navigateToPath("/app/$rawPid/login");
+                                    UIService.navigateNamed("/app/$rawPid/login");
+                                    // context.router.removeLast();
+                                    // context.navigateToPath("/app/$rawPid/login");
                                   }
                                 } catch(e) {
-                                  print(e.toString());
+                                  debugPrint(e.toString());
                                   await _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyAccessCode);
                                   await _sharedPreferencesManager.clearKey(SharedPreferencesManager.keySerialCode);
                                   await _sharedPreferencesManager.clearKey(SharedPreferencesManager.version);
                                   token.logout();
-                                  context.router.removeLast();
-                                  context.navigateToPath("/qr");
+                                  UIService.navigateNamed("/qr");
+                                  // context.router.removeLast();
+                                  // context.navigateToPath("/qr");
                                 }
                               },
                             ),

+ 3 - 4
lib/src/layouts/auth/end_session.dart

@@ -1,4 +1,3 @@
-import 'package:auto_route/annotations.dart';
 import 'package:auto_route/auto_route.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/foundation.dart';
@@ -20,15 +19,15 @@ class EndSessionPage extends StatefulWidget {
 class _EndSessionPageState extends State<EndSessionPage> {
   @override
   Widget build(BuildContext context) {
-    bool useAsset = false;
+    // bool useAsset = false;
     String imageUrl = '';
     final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
 
     try {
-      imageUrl = apiAuthProvider.baseUrl + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';
+      imageUrl = '${apiAuthProvider.baseUrl}${U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))}/assets/background/tn';
       http.head(Uri.parse(imageUrl));
     }catch(e){
-      useAsset = true;
+      // useAsset = true;
     }
 
     return Scaffold(

+ 68 - 48
lib/src/layouts/auth/login.dart

@@ -16,6 +16,7 @@ import 'package:telnow_mobile_new/src/model/login/login_body.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/C.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
 import 'package:toggle_switch/toggle_switch.dart';
 import 'package:url_launcher/url_launcher.dart';
@@ -23,7 +24,7 @@ import 'package:telnow_mobile_new/src/model/token/token.dart';
 
 @RoutePage()
 class LoginPage extends StatefulWidget {
-  const LoginPage({Key? key}) : super(key: key);
+  const LoginPage({super.key});
 
   @override
   State<LoginPage> createState() => _LoginPageState();
@@ -47,7 +48,11 @@ class _LoginPageState extends State<LoginPage> {
 
   @override
   void initState() {
-    try{imageUrl = apiAuthProvider.baseUrl + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';}catch(e){}
+    try{
+      imageUrl = '${apiAuthProvider.baseUrl}${U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))}/assets/background/tn';
+    }catch(e){
+      debugPrint(e.toString());
+    }
     U.getServerVersion();
     getCompanyName();
     // TODO: implement initState
@@ -109,7 +114,7 @@ class _LoginPageState extends State<LoginPage> {
                       width: 160, height: 160,
                       decoration: BoxDecoration(
                         borderRadius: BorderRadius.all(Radius.circular(80)),
-                        image: DecorationImage(image: CachedNetworkImageProvider(companyLogo+'?bridge-cache=true', cacheManager: CacheManager(CacheMan.config(companyLogo))), fit: BoxFit.cover)
+                        image: DecorationImage(image: CachedNetworkImageProvider('$companyLogo?bridge-cache=true', cacheManager: CacheManager(CacheMan.config(companyLogo))), fit: BoxFit.cover)
                       ),
                     ):SizedBox(height: 115),
                     Expanded(
@@ -147,7 +152,7 @@ class _LoginPageState extends State<LoginPage> {
                           ),
                           companyName.isNotEmpty?Text(companyName, style: TextStyle(fontSize: 16, color: Colors.white), textAlign: TextAlign.center):Container(),
                           SizedBox(height: companyName.isNotEmpty?5:0),
-                          serialNumber.isNotEmpty?Text('serialNumber'.tr() + ' ' + serialNumber, style: TextStyle(color: Colors.white, fontSize: 14)):Container(),
+                          serialNumber.isNotEmpty?Text('${'serialNumber'.tr()} $serialNumber', style: TextStyle(color: Colors.white, fontSize: 14)):Container(),
                         ],
                       ),
                     ),
@@ -157,8 +162,8 @@ class _LoginPageState extends State<LoginPage> {
                       child: GestureDetector(
                         child: Text('policy'.tr(), style: TextStyle(color: Colors.white, fontSize: 14, decoration: TextDecoration.underline)),
                         onTap: () async {
-                          Uri _url = Uri.parse('https://telmessenger.com/privacy-police/telnow.html');
-                          await canLaunchUrl(_url) ? await launchUrl(_url) : print('Could not launch $_url');
+                          Uri url = Uri.parse('https://telmessenger.com/privacy-police/telnow.html');
+                          await canLaunchUrl(url) ? await launchUrl(url) : debugPrint('Could not launch $url');
                         },
                       ),
                     ),
@@ -243,6 +248,7 @@ class _LoginPageState extends State<LoginPage> {
   }
 
   loginAction() async{
+    final languageTag = context.locale.toLanguageTag();
     if(!loading){
       setState(()=>loading = true);
 
@@ -254,60 +260,64 @@ class _LoginPageState extends State<LoginPage> {
       }
 
       if (errMsg.isNotEmpty) {
-        showError(context, errMsg);
+        UIService.showError(errMsg);
+        // showError(context, errMsg);
         setState(()=>loading = false);
         return;
       }
 
       Token token = await apiAuthRepository.postLoginUser(LoginBody(username.trim(), password.trim(), "password"));
       if (token.error != null) {
-        showError(context, token.error);
+        UIService.showError(token.error.toString());
         setState(()=>loading = false);
         return;
       }
 
-      var pid = U.getPidFromUrl(context.router.currentUrl);
+      var pid = U.getPidFromUrl(UIService.getCurrentUrl());
       await sharedPreferencesManager.putString(SharedPreferencesManager.keyAccessToken, token.accessToken!);
       await sharedPreferencesManager.putString(SharedPreferencesManager.keyRefreshToken, token.refreshToken!);
 
-      // print(JwtDecoder.decode(token.accessToken!));
       var accTkn = JwtDecoder.decode(token.accessToken!);
-      switchLang(context, context.locale.toLanguageTag(), accTkn);
+      switchLang(languageTag, accTkn);
 
-      if(await setUsername()){
+      if(await setUsername() && mounted){
         var data = {
           'username': username.trim(),
           'password': password.trim()
         };
-        Navigator.push(context, MaterialPageRoute(builder: (context) => ForceChgPassPage(data: data))).then((value) async {
+        Navigator.push(
+          context,
+          MaterialPageRoute(builder: (_) => ForceChgPassPage(data: data)),
+        ).then((_) async {
           if (!U.getChangedPassword()) {
             await U.clearPreferences();
             U.setHidePayload(true);
-            context.router.removeLast();
-            context.navigateToPath("/app/$pid/menu");
-            return;
+            UIService.navigateNamed("/app/$pid/menu");
           }
         });
-      }
-      else{
+
+      } else {
         await sharedPreferencesManager.putBool(SharedPreferencesManager.keyIsLogin, true);
         await sharedPreferencesManager.putString(SharedPreferencesManager.keyScoope, 'INSIDE');
         await sharedPreferencesManager.putInt(SharedPreferencesManager.keyCountRefreshToken, 0);
         await sharedPreferencesManager.putString(SharedPreferencesManager.keyPendingData, jsonEncode([]));
         U.setHidePayload(true);
-        context.router.removeLast();
-        context.navigateToPath("/app/$pid/menu");
+        UIService.navigateNamed("/app/$pid/menu");
+        // context.router.removeLast();
+        // context.navigateToPath("/app/$pid/menu");
       }
     }
   }
 
-  switchLang(BuildContext context, code, tkn) async {
-    // print("switchLang called");
+  switchLang(code, tkn) async {
+    // final ctx = UIService.context;
+    // if(ctx == null) return;
+
     var lg = code.toUpperCase();
     if(lang.indexOf(code) > 1){
       lg = lang.indexOf(code) - 1;
     }
-    var p;
+    Map<String, dynamic> p;
     if(U.newServerVersion(1754624839)){
       p = {
         'userId': tkn['user_name'].toString().replaceFirst('inf-', ""),
@@ -321,12 +331,14 @@ class _LoginPageState extends State<LoginPage> {
       };
     }
     try{
-      var res = await apiAuthProvider.patchData('/api/informants/' + tkn['userId'].toString(), p, context);
+      var res = await apiAuthProvider.patchData('/api/informants/${tkn['userId']}', p);
       if (res != null) {
         // print("res $res");
         // context.setLocale(Locale(code));
       }
-    }catch(e){}
+    }catch(e){
+      debugPrint(e.toString());
+    }
 
   }
 
@@ -339,7 +351,7 @@ class _LoginPageState extends State<LoginPage> {
 
       return jwt['forceChgPass'];
     } catch(e) {
-      print(e.toString());
+      debugPrint(e.toString());
     }
   }
 }
@@ -437,8 +449,8 @@ changeLangLogin(BuildContext context, List lang){
 //------------------------------------------------------------------------------
 
 class ForceChgPassPage extends StatefulWidget {
-  Map<String, String> data;
-  ForceChgPassPage({required this.data, Key? key}) : super(key: key);
+  final Map<String, String> data;
+  const ForceChgPassPage({required this.data, super.key});
 
   @override
   State<ForceChgPassPage> createState() => _ForceChgPassPageState();
@@ -450,7 +462,7 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
   final ApiAuthRepository apiAuthRepository = ApiAuthRepository();
   final SharedPreferencesManager sharedPreferencesManager = locator<SharedPreferencesManager>();
 
-  var tokenData;
+  Map<String, dynamic> tokenData = {};
   String companyName = '';
   String serialNumber = '';
   String guestName = '';
@@ -458,11 +470,15 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
   String imageUrl = '';
 
   String newPassword = '';
-  String cfrmPassword = '';
+  String confirmPassword = '';
 
   @override
   void initState() {
-    try{imageUrl = apiAuthProvider.baseUrl + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';}catch(e){}
+    try{
+      imageUrl = '${apiAuthProvider.baseUrl}${U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))}/assets/background/tn';
+    }catch(e){
+      debugPrint(e.toString());
+    }
     getTokenData();
     getCompanyName();
     // TODO: implement initState
@@ -472,7 +488,7 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
   getTokenData() async {
     String accessToken = sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessToken)!;
     var jwt = token.parseJwtPayLoad(accessToken);
-    var user = await apiAuthProvider.getData('/api/informants/'+jwt['userId'].toString(), null, context);
+    var user = await apiAuthProvider.getData('/api/informants/${jwt['userId']}', null);
     if(user!=null){
       setState((){
         tokenData = jwt;
@@ -483,11 +499,11 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
   }
 
   getCompanyName() async {
-    var lics = await U.reloadLicense();
-    if (lics != null && mounted) {
+    var license = await U.reloadLicense();
+    if (license != null && mounted) {
       setState(() {
-        companyName = lics['companyName']??'';
-        serialNumber = lics['serialNumber']??'';
+        companyName = license['companyName']??'';
+        serialNumber = license['serialNumber']??'';
       });
     }
   }
@@ -579,7 +595,7 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
                         children: [
                           loginFieldTemplate(placeholder: 'newPassword'.tr(), value: newPassword, action: (val)=>newPassword = val, isPassword: true, submit: (val)=>loginAction()),
                           SizedBox(height: 12),
-                          loginFieldTemplate(placeholder: 'confirmPassword'.tr(), value: cfrmPassword, action: (val)=>cfrmPassword = val, isPassword: true, submit: (val)=>loginAction()),
+                          loginFieldTemplate(placeholder: 'confirmPassword'.tr(), value: confirmPassword, action: (val)=>confirmPassword = val, isPassword: true, submit: (val)=>loginAction()),
                           GestureDetector(
                             child: Container(
                               width: double.infinity,
@@ -607,7 +623,7 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
                     Container(
                       margin: const EdgeInsets.only(top: 145),
                       alignment: Alignment.bottomCenter,
-                      child: Text('version'.tr() + ' ' + ApiAuthProvider().displayVersion, style: TextStyle(color: Colors.white, fontSize: 14))
+                      child: Text('${'version'.tr()} ${ApiAuthProvider().displayVersion}', style: TextStyle(color: Colors.white, fontSize: 14))
                     ),
                   ],
                 ),
@@ -621,21 +637,22 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
 
   loginAction() async{
     if(!loading){
-      if(newPassword.isNotEmpty && cfrmPassword.isNotEmpty){
-        if(newPassword.trim() == cfrmPassword.trim()){
+      if(newPassword.isNotEmpty && confirmPassword.isNotEmpty){
+        if(newPassword.trim() == confirmPassword.trim()){
           setState(()=>loading = true);
           var data = {'oldPassword': widget.data['password'], 'password': newPassword.trim()};
 
           try {
-            var res = await apiAuthProvider.patchData('/api/informants/${tokenData['userId']}/changePassword', data, context);
+            var res = await apiAuthProvider.patchData('/api/informants/${tokenData['userId']}/changePassword', data);
             if (res != null) {
-              String password = tokenData['related'] && tokenData['tenantCode'] != null && tokenData['tenantCode'] != '' ? tokenData['tenantCode'] + ' ' + data['password'] : data['password'];
+              String password = tokenData['related'] && tokenData['tenantCode'] != null && tokenData['tenantCode'] != '' ?  tokenData['tenantCode'] + ' ${data['password']}' : data['password'];
 
               LoginBody loginBody = LoginBody(widget.data['username']!, password, 'password');
               Token token = await apiAuthRepository.postLoginUser(loginBody);
               if (token.error != null) {
                 setState(()=>loading = false);
-                showError(context, token.error);
+                UIService.showError(token.error.toString());
+                // showError(context, token.error);
               } else {
                 await U.setChangedPassword(true);
                 var parsedToken = U.token.parseJwtPayLoad(token.accessToken!);
@@ -648,13 +665,14 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
                 await sharedPreferencesManager.putString(SharedPreferencesManager.keyPendingData, jsonEncode([]));
                 U.setHidePayload(true);
                 // var pid = U.getAccessCode();
-                var pid = U.getPidFromUrl(context.router.currentUrl);
-                context.router.removeLast();
-                context.navigateToPath("/app/$pid/menu");
+                var pid = U.getPidFromUrl(UIService.getCurrentUrl());
+                UIService.navigateNamed("/app/$pid/menu");
+                // context.router.removeLast();
+                // context.navigateToPath("/app/$pid/menu");
               }
             }
           } catch(e) {
-            print(e.toString());
+            debugPrint(e.toString());
             setState(()=>loading = false);
           }
         }
@@ -663,7 +681,9 @@ class _ForceChgPassPageState extends State<ForceChgPassPage> {
         }
       }
       else{
-        showError(context, 'cannotEmpty'.tr());
+        debugPrint("Mbuh");
+        UIService.showError('cannotEmpty'.tr());
+        // showError(context, 'cannotEmpty'.tr());
       }
     }
   }

+ 23 - 26
lib/src/layouts/auth/qr.dart

@@ -14,13 +14,14 @@ import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/layouts/auth/login.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:toggle_switch/toggle_switch.dart';
 import 'package:url_launcher/url_launcher.dart';
 import 'package:http/http.dart' as http;
 
 @RoutePage()
 class NewQrPage extends StatefulWidget {
-  const NewQrPage({Key? key}) : super(key: key);
+  const NewQrPage({super.key});
 
   @override
   State<NewQrPage> createState() => _NewQrPageState();
@@ -31,7 +32,7 @@ class _NewQrPageState extends State<NewQrPage> {
   final ApiAuthRepository apiAuthRepository = ApiAuthRepository();
   final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
   String? accCode = "";
-  bool? _isAccCodeExist = false;
+  // bool? _isAccCodeExist = false;
 
   String companyName = '';
   String serialNumber = '';
@@ -43,7 +44,7 @@ class _NewQrPageState extends State<NewQrPage> {
   @override
   void initState() {
     try {
-      imageUrl = apiAuthProvider.baseUrl + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';
+      imageUrl = '${apiAuthProvider.baseUrl}${U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))}/assets/background/tn';
       http.head(Uri.parse(imageUrl));
     }catch(e){
       useAsset = true;
@@ -168,8 +169,8 @@ class _NewQrPageState extends State<NewQrPage> {
                     GestureDetector(
                       child: Text('policy'.tr(), style: TextStyle(color: Colors.white, fontSize: 14, decoration: TextDecoration.underline)),
                       onTap: () async {
-                        Uri _url = Uri.parse('https://telmessenger.com/privacy-police/telnow.html');
-                        await canLaunchUrl(_url) ? await launchUrl(_url) : print('Could not launch $_url');
+                        Uri url = Uri.parse('https://telmessenger.com/privacy-police/telnow.html');
+                        await canLaunchUrl(url) ? await launchUrl(url) : debugPrint('Could not launch $url');
                       },
                     ),
                     SizedBox(height: 5),
@@ -229,12 +230,11 @@ class _NewQrPageState extends State<NewQrPage> {
   Future scanDialog() async {
     try {
       final result = await InternetAddress.lookup('google.com');
-      if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) {
-        // String barcode = await FlutterBarcodeScanner.scanBarcode('#ff6666', 'Cancel', true, ScanMode.QR);
+      if (result.isNotEmpty && result[0].rawAddress.isNotEmpty && mounted) {
         showModalBottomSheet<void>(
           context: context,
           backgroundColor: Colors.white,
-          builder: (BuildContext contextt) {
+          builder: (BuildContext ctx) {
             return SafeArea(
               child: Column(
                 mainAxisSize: MainAxisSize.min,
@@ -250,7 +250,7 @@ class _NewQrPageState extends State<NewQrPage> {
                     leading: Icon(Icons.image),
                     title: Text('fromGallery'.tr(), style: TextStyle(color: textColor)),
                     onTap: () async{
-                      Navigator.of(contextt).pop();
+                      Navigator.of(ctx).pop();
                       XFile? file = await ImagePicker().pickImage(source: ImageSource.gallery);
                       if(file != null){
                         final BarcodeCapture? barcodeCapture = await MobileScannerController().analyzeImage(file.path);
@@ -259,7 +259,7 @@ class _NewQrPageState extends State<NewQrPage> {
                           scan(str);
                         }
                         else{
-                          showError(context, 'messageInvalidCode'.tr());
+                          UIService.showError('messageInvalidCode'.tr());
                         }
                       }
                     },
@@ -268,7 +268,7 @@ class _NewQrPageState extends State<NewQrPage> {
                     leading: Icon(Icons.camera),
                     title: Text('fromCamera'.tr(), style: TextStyle(color: textColor)),
                     onTap: () async{
-                      Navigator.of(contextt).pop();
+                      Navigator.of(ctx).pop();
                       Navigator.push(context, MaterialPageRoute(builder: (context) => const ScanBarcodePage())).then((barcode){
                         if(barcode != null){
                           scan(barcode);
@@ -283,26 +283,23 @@ class _NewQrPageState extends State<NewQrPage> {
         );
       }
     } on SocketException catch (_) {
-      handlingError(context, 0); //no internet
+      UIService.handlingError(ErrorType.noInternet); //no internet
     } on PlatformException catch (e) {
       if (e.code != '') {
-        showError(context, 'messageCamPermission'.tr());
+        UIService.showError('messageCamPermission'.tr());
       } else {
-        showError(context, 'messageInvalidCode'.tr());
+        UIService.showError('messageInvalidCode'.tr());
       }
     } catch (e) {
-      showError(context, 'messageInvalidCode'.tr());
+      UIService.showError('messageInvalidCode'.tr());
     }
   }
 
   Future scan(String barcode) async {
-// print("called scan()");
     try {
       var pidString = barcode.split("app/").last;
       var pid = Uri.decodeComponent(pidString.split('/').first);
       var splitPID = pid.split('#');
-// print(splitPID);
-// print(U.decodeBase64Url(splitPID[1]));
 
       await _sharedPreferencesManager.clearKey(SharedPreferencesManager.version);
       await _sharedPreferencesManager.putString(SharedPreferencesManager.keyBaseUrl, U.rewriteUrl(splitPID[1]));
@@ -311,15 +308,17 @@ class _NewQrPageState extends State<NewQrPage> {
       apiAuthRepository.init();
 
       if (!pidString.contains('loginme')) {
-        context.router.removeLast();
-        context.navigateToPath("/app/${Uri.encodeComponent(splitPID[0])}/login");
+        UIService.navigateNamed("/app/${Uri.encodeComponent(splitPID[0])}/login");
+        // context.router.removeLast();
+        // context.navigateToPath("/app/${Uri.encodeComponent(splitPID[0])}/login");
       } else {
-        context.router.removeLast();
-        context.navigateToPath("/app/${Uri.encodeComponent(splitPID[0])}/loginme"+pidString.split("/loginme").last);
+        UIService.navigateNamed("/app/${Uri.encodeComponent(splitPID[0])}/loginme${pidString.split("/loginme").last}");
+        // context.router.removeLast();
+        // context.navigateToPath("/app/${Uri.encodeComponent(splitPID[0])}/loginme${pidString.split("/loginme").last}");
       }
     } catch (e) {
-      print(e);
-      if(mounted) showError(context, 'messageInvalidCode'.tr());
+      debugPrint(e.toString());
+      UIService.showError('messageInvalidCode'.tr());
     }
   }
 }
@@ -338,8 +337,6 @@ class _ScanBarcodePageState extends State<ScanBarcodePage> {
     return Scaffold(
       body: MobileScanner(
         onDetect: (barcodes) {
-          // print('sini');
-          // print(barcodes.barcodes.singleOrNull?.displayValue);
           Navigator.of(context).pop(barcodes.barcodes.firstOrNull?.displayValue);
         },
       ),

+ 40 - 37
lib/src/layouts/components/auto_login.dart

@@ -13,9 +13,12 @@ import 'package:telnow_mobile_new/src/model/token/token.dart';
 import 'package:flutter/material.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 @RoutePage()
 class AutoLoginPage extends StatefulWidget {
+  const AutoLoginPage({super.key});
+
   @override
   State<AutoLoginPage> createState() => _AutoLoginPageState();
 }
@@ -35,8 +38,8 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
   String progressMsg = 'initializing'.tr();
 
   String newPassword = '';
-  String cfrmPassword = '';
-  Map <String, String> Ddata = {};
+  String confirmPassword = '';
+  Map <String, String> dData = {};
 
   @override
   void initState() {
@@ -59,7 +62,7 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
           case 'prm':prmJson = U.decodeBase64Url(Uri.decodeComponent(v));
         }
       });
-      Ddata = {
+      dData = {
         "username": jsonDecode(prmJson)["username"],
         "password": jsonDecode(prmJson)["password"]
       };
@@ -67,26 +70,24 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
       getTokenData();
     } catch(e) {
       U.showDebugToast(e.toString());
-      print("invalid param: " + e.toString());
+      debugPrint("invalid param: $e");
       redirectToLoginPage = true;
       Future.delayed(Duration.zero, (){
-        context.router.removeLast();
-        context.navigateToPath("/app/${U.getAccessCode()}");
+        UIService.navigateNamed("/app/${U.getAccessCode()}");
+        // context.router.removeLast();
+        // context.navigateToPath("/app/${U.getAccessCode()}");
       });
     }
   }
 
   getTokenData({int n = 0}) async {
     JwtToken jwtToken = JwtToken();
-    // if (n > 2){
-    //
-    // }
     showLoading(context);
     try {
-      Token token = await apiAuthRepository.postLoginUser(LoginBody(Ddata["username"]!.trim(), Ddata["password"]!.trim(), "password"));
+      Token token = await apiAuthRepository.postLoginUser(LoginBody(dData["username"]!.trim(), dData["password"]!.trim(), "password"));
       if (token.error != null) {
-        closeLoading(context);
-        showError(context, token.error);
+        UIService.closeLoading();
+        UIService.showError(token.error.toString());
         setState(() {
           loading = false;
           invalidUserLogin = true;
@@ -101,9 +102,9 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
       String accessToken = sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessToken)!;
       var jwt = jwtToken.parseJwtPayLoad(accessToken);
       // print(jwt);
-      var user = await apiAuthProvider.getData('/api/informants/'+jwt['userId'].toString(), null, context);
+      var user = await apiAuthProvider.getData('/api/informants/${jwt['userId']}', null);
       if(user != null){
-        closeLoading(context);
+        UIService.closeLoading();
         setState((){
           tokenData = jwt;
           context.setLocale(Locale(tokenData!['language'].toLowerCase()));
@@ -111,18 +112,18 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
         });
       }
     } catch(e){
-      closeLoading(context);
-      print(e.toString());
+      UIService.closeLoading();
+      debugPrint(e.toString());
       U.showDebugToast(e.toString());
       getTokenData(n: n + 1);
     }
   }
 
   getCompanyName() async {
-    var lics = await U.getLicense();
-    if (lics != null && lics['companyName'] != null && mounted) {
+    var license = await U.getLicense();
+    if (license != null && license['companyName'] != null && mounted) {
       setState(() {
-        companyName = lics['companyName'];
+        companyName = license['companyName'];
       });
     }
   }
@@ -131,11 +132,12 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
     try {
       // U.clearAccessCode();
       U.clearPreferences().then((value) {
-        context.router.removeLast();
-        context.navigateToPath('/qr?new=true');
+        UIService.navigateNamed('/qr?new=true');
+        // context.router.removeLast();
+        // context.navigateToPath('/qr?new=true');
       });
     } catch(e) {
-      print(e.toString());
+      debugPrint(e.toString());
       U.showDebugToast(e.toString());
     }
   }
@@ -204,7 +206,7 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
                     ),
                     !invalidUserLogin ? Column(
                       children: [
-                        Text('welcome'.tr()+' $guestName', style: TextStyle(color: Colors.white, fontSize: 16), textAlign: TextAlign.center),
+                        Text('${'welcome'.tr()} $guestName', style: TextStyle(color: Colors.white, fontSize: 16), textAlign: TextAlign.center),
                         SizedBox(height: 16),
                         Text('createNewPassText'.tr(), style: TextStyle(color: Colors.white, fontSize: 16), textAlign: TextAlign.center),
                       ],
@@ -214,7 +216,7 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
                         SizedBox(height: 80),
                         loginFieldTemplate(placeholder: 'newPassword'.tr(), value: newPassword, action: (val)=>newPassword = val, isPassword: true, submit: (val)=>loginAction()),
                         SizedBox(height: 12),
-                        loginFieldTemplate(placeholder: 'confirmPassword'.tr(), value: cfrmPassword, action: (val)=>cfrmPassword = val, isPassword: true, submit: (val)=>loginAction()),
+                        loginFieldTemplate(placeholder: 'confirmPassword'.tr(), value: confirmPassword, action: (val)=>confirmPassword = val, isPassword: true, submit: (val)=>loginAction()),
                         GestureDetector(
                           child: Container(
                             width: double.infinity,
@@ -265,55 +267,56 @@ class _AutoLoginPageState extends State<AutoLoginPage> {
 
   loginAction() async{
     if(!loading && tokenData != null){
-      if(newPassword.isNotEmpty && cfrmPassword.isNotEmpty){
-        if(newPassword.trim() == cfrmPassword.trim()){
+      if(newPassword.isNotEmpty && confirmPassword.isNotEmpty){
+        if(newPassword.trim() == confirmPassword.trim()){
           setState(() {
             loading = true;
             onProgress = true;
           });
-          var data = {'oldPassword': Ddata['password'], 'password': newPassword.trim()};
+          var data = {'oldPassword': dData['password'], 'password': newPassword.trim()};
 
           try {
-            var res = await apiAuthProvider.patchData('/api/informants/${tokenData!['userId']}/changePassword', data, context);
+            var res = await apiAuthProvider.patchData('/api/informants/${tokenData!['userId']}/changePassword', data);
             if (res != null) {
               // setState(() => progressMsg = "Initializing...");
-              String password = tokenData!['related'] && tokenData!['tenantCode'] != null && tokenData!['tenantCode'] != '' ? tokenData!['tenantCode'] + ' ' + data['password'] : data['password'];
+              String password = tokenData!['related'] && tokenData!['tenantCode'] != null && tokenData!['tenantCode'] != '' ? tokenData!['tenantCode'] + ' ${data['password']}' : data['password'];
 
-              LoginBody loginBody = LoginBody(Ddata['username']!, password, 'password');
+              LoginBody loginBody = LoginBody(dData['username']!, password, 'password');
               Token token = await apiAuthRepository.postLoginUser(loginBody);
               if (token.error != null) {
                 setState(() => loading = false);
-                showError(context, token.error);
+                UIService.showError(token.error.toString());
               } else {
                 setState(() => progressMsg = "redirecting".tr());
                 await U.setChangedPassword(true);
                 var parsedToken = U.token.parseJwtPayLoad(token.accessToken!);
                 await sharedPreferencesManager.putString(SharedPreferencesManager.keyAccessToken, token.accessToken!);
                 await sharedPreferencesManager.putString(SharedPreferencesManager.keyRefreshToken, token.refreshToken!);
-                await sharedPreferencesManager.putString(SharedPreferencesManager.keyUsername, tokenData!['related'] ? parsedToken['user_name'].split("-").last : Ddata['username']);
+                await sharedPreferencesManager.putString(SharedPreferencesManager.keyUsername, tokenData!['related'] ? parsedToken['user_name'].split("-").last : dData['username']);
                 await sharedPreferencesManager.putBool(SharedPreferencesManager.keyIsLogin, true);
                 await sharedPreferencesManager.putString(SharedPreferencesManager.keyScoope, 'INSIDE');
                 await sharedPreferencesManager.putInt(SharedPreferencesManager.keyCountRefreshToken, 0);
                 U.setHidePayload(true);
                 var pid = U.getAccessCode();
-                context.router.removeLast();
-                context.navigateToPath("/app/$pid/menu");
+                UIService.navigateNamed("/app/$pid/menu");
+                // context.router.removeLast();
+                // context.navigateToPath("/app/$pid/menu");
               }
             }
             else{
               setState(() => loading = false);
             }
           } catch(e) {
-            print(e.toString());
+            debugPrint(e.toString());
             setState(() => loading = false);
           }
         }
         else{
-          showError(context, 'checkNewPass'.tr());
+          UIService.showError('checkNewPass'.tr());
         }
       }
       else{
-        showError(context, 'cannotEmpty'.tr());
+        UIService.showError('cannotEmpty'.tr());
       }
     }
   }

+ 6 - 4
lib/src/layouts/components/camera.dart

@@ -1,9 +1,10 @@
 import 'package:camera/camera.dart';
 import 'package:flutter/material.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 class OpenCamera extends StatefulWidget {
   final List<CameraDescription>? cameras;
-  const OpenCamera({required this.cameras, Key? key}) : super(key: key);
+  const OpenCamera({required this.cameras, super.key});
 
   @override
   State<OpenCamera> createState() => _OpenCameraState();
@@ -35,9 +36,10 @@ class _OpenCameraState extends State<OpenCamera> {
     try {
       await _cameraController.setFlashMode(FlashMode.off);
       XFile pickedFile = await _cameraController.takePicture();
-      Navigator.of(context).pop(pickedFile);
+      UIService.navigatorKey.currentState?.pop(pickedFile);
+      // Navigator.of(context).pop(pickedFile);
     } on CameraException catch (e) {
-      debugPrint('Error occured while taking picture: $e');
+      debugPrint('Error occurred while taking picture: $e');
       return null;
     }
   }
@@ -59,7 +61,7 @@ class _OpenCameraState extends State<OpenCamera> {
   Widget build(BuildContext context) {
     return Scaffold(
         body: SafeArea(
-          child: Container(
+          child: SizedBox(
             width: double.infinity, height: double.infinity,
             child: Stack(children: [
               (_cameraController.value.isInitialized) ? CameraPreview(_cameraController) : Container(color: Colors.black, child: const Center(child: CircularProgressIndicator())),

+ 13 - 16
lib/src/layouts/components/error_page.dart

@@ -6,33 +6,31 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 
 @RoutePage()
 class ErrorPage extends StatefulWidget {
-  final code;
-  final errMsg;
+  final String code;
+  final String errMsg;
 
-  ErrorPage(this.code, this.errMsg);
+  const ErrorPage(this.code, this.errMsg, {super.key});
 
   @override
-  _ErrorPageState createState() => _ErrorPageState();
+  ErrorPageState createState() => ErrorPageState();
 }
 
-class _ErrorPageState extends State<ErrorPage> {
+class ErrorPageState extends State<ErrorPage> {
   final ApiAuthProvider _apiAuthProvider = ApiAuthProvider();
   String stringError = '';
   String imageUrl = '';
 
   @override
   void initState() {
-    try{imageUrl = _apiAuthProvider.baseUrl + U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))+'/assets/background/tn';}catch(e){}
+    try{
+      imageUrl = '${_apiAuthProvider.baseUrl}${U.decodeBase64Url(Uri.decodeComponent(U.getAccessCode()!))}/assets/background/tn';
+    } catch(e) {
+      debugPrint(e.toString());
+    }
     // TODO: implement initState
     switch (widget.errMsg) {
       case "_strErrVer":
-        stringError = "invalidVersion".tr() +
-          ". " +
-          "sugestVersion".tr() + " " +
-          U.getVersion().toString() +
-          ". " +
-          "currentVersion".tr() + " " +
-          ApiAuthProvider().currentVersion.toString() + ".";
+        stringError = "${"invalidVersion".tr()}. ${"sugestVersion".tr()} ${U.getVersion()}. ${"currentVersion".tr()} ${ApiAuthProvider().currentVersion}.";
         break;
         default:
           stringError = widget.errMsg;
@@ -45,14 +43,13 @@ class _ErrorPageState extends State<ErrorPage> {
     return Scaffold(
       body: Stack(
         children: [
-          Container(
+          SizedBox(
             width: double.infinity, height: double.infinity,
             child: Image.network(imageUrl, fit: BoxFit.cover, width: double.infinity, height: double.infinity, errorBuilder: (context, error, stackTrace) {
               return Image.asset('assets/image/background/background.jpg', fit: BoxFit.cover, width: double.infinity, height: double.infinity);
             }),
           ),
           Container(
-            // decoration: BoxDecoration(color: Color(0xff0D497F).withValues(alpha: 0.85)),
             decoration: BoxDecoration(
                 gradient: LinearGradient(
                     begin: Alignment.topCenter, end: Alignment.bottomCenter,
@@ -69,7 +66,7 @@ class _ErrorPageState extends State<ErrorPage> {
                 Padding(
                   padding: EdgeInsets.only(bottom: 30),
                   child: Image(
-                    image: new AssetImage("assets/image/logo/logo.png"),
+                    image: AssetImage("assets/image/logo/logo.png"),
                     width: 100,
                   ),
                 ),

+ 19 - 21
lib/src/layouts/components/photo_chat.dart

@@ -1,6 +1,4 @@
 import 'dart:convert';
-import 'dart:typed_data';
-
 import 'package:camera/camera.dart';
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/foundation.dart';
@@ -12,29 +10,29 @@ import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
 import 'package:telnow_mobile_new/src/layouts/components/camera.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:image/image.dart' as img;
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 class PhotoChat extends StatefulWidget {
-  final data;
+  final Map<String, dynamic> data;
   final ImageSource? media;
   final bool? isBroadcast;
   final Uint8List? image;
 
-  PhotoChat(this.data, this.media, this.isBroadcast, this.image);
+  const PhotoChat(this.data, this.media, this.isBroadcast, this.image, {super.key});
 
   @override
-  _PhotoChatState createState() => _PhotoChatState();
+  PhotoChatState createState() => PhotoChatState();
 }
 
-class _PhotoChatState extends State<PhotoChat> {
+class PhotoChatState extends State<PhotoChat> {
   final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
-  TextEditingController controllerPesan = new TextEditingController();
+  TextEditingController controllerPesan = TextEditingController();
   Uint8List? image;
 
   Future getImage() async {
-//    print('photochat in');
     XFile? pickedFile;
     if(widget.media == ImageSource.camera){
-      await availableCameras().then((camera) => Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: OpenCamera(cameras: camera))).then((value) async{
+      await availableCameras().then((camera) => Navigator.push(UIService.navigatorKey.currentContext!, PageTransition(type: PageTransitionType.rightToLeft, child: OpenCamera(cameras: camera))).then((value) async{
         if(value!=null){
           pickedFile = value;
         }
@@ -45,15 +43,16 @@ class _PhotoChatState extends State<PhotoChat> {
     }
 
     if (pickedFile != null) {
-      var imagess = img.decodeImage(await pickedFile!.readAsBytes());
-      var imgPercent = (1000 / (imagess!.width / 100)).toDouble();
-      if (imagess.width > 1000) {
-        imagess = img.copyResize(imagess, width: ((imagess.width / 100) * imgPercent).toInt(), height: ((imagess.height / 100) * imgPercent).toInt());
+      var images = img.decodeImage(await pickedFile!.readAsBytes());
+      var imgPercent = (1000 / (images!.width / 100)).toDouble();
+      if (images.width > 1000) {
+        images = img.copyResize(images, width: ((images.width / 100) * imgPercent).toInt(), height: ((images.height / 100) * imgPercent).toInt());
       }
-      var compressed = img.encodeJpg(imagess, quality: 60);
+      var compressed = img.encodeJpg(images, quality: 60);
       setState(() => image = compressed);
     } else {
-      Navigator.of(context).pop(null);
+      UIService.navigatorKey.currentState?.pop(null);
+      // Navigator.of(context).pop(null);
     }
   }
 
@@ -83,7 +82,7 @@ class _PhotoChatState extends State<PhotoChat> {
         ),
       ),
       body: Center(
-        child: Container(
+        child: SizedBox(
           width: U.bodyWidth(context),
           child: Stack(
             children: [
@@ -97,7 +96,6 @@ class _PhotoChatState extends State<PhotoChat> {
               Container(
                 alignment: Alignment.bottomCenter,
                 child: Container(
-//              width: double.infinity,
                   padding: const EdgeInsets.all(10),
                   color: Colors.black54,
                   child: Row(
@@ -129,12 +127,12 @@ class _PhotoChatState extends State<PhotoChat> {
                             height: 50,
                             child: ElevatedButton(
                               style: ButtonStyle(
-                                backgroundColor: MaterialStateProperty.all<Color>(primaryColor),
-                                shape: MaterialStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(
+                                backgroundColor: WidgetStateProperty.all<Color>(primaryColor),
+                                shape: WidgetStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(
                                   borderRadius: BorderRadius.circular(50),
                                 )),
-                                elevation: MaterialStateProperty.all(2),
-                                padding: MaterialStateProperty.all(const EdgeInsets.only(left: 1)),
+                                elevation: WidgetStateProperty.all(2),
+                                padding: WidgetStateProperty.all(const EdgeInsets.only(left: 1)),
                               ),
                               child: Image.asset('assets/image/icon/Send.png', width: 25, height: 25),
                               onPressed: () async {

+ 4 - 7
lib/src/layouts/components/responsive.dart

@@ -1,9 +1,6 @@
 import 'package:auto_route/auto_route.dart';
 import 'package:flutter/material.dart';
 import 'package:telnow_mobile_new/src/layouts/mobile/app_mobile.dart';
-import 'package:telnow_mobile_new/src/layouts/mobile/menu_account.dart';
-import 'package:telnow_mobile_new/src/layouts/mobile/menu_history.dart';
-import 'package:telnow_mobile_new/src/layouts/mobile/menu_home.dart';
 import 'package:telnow_mobile_new/src/layouts/web/app_web.dart';
 import 'package:telnow_mobile_new/src/layouts/web/menu_account.dart';
 import 'package:telnow_mobile_new/src/layouts/web/menu_history.dart';
@@ -16,7 +13,7 @@ class AppResponsive extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return U.webView(context)?WebAppPage(AutoRouter()):MobAppPage(AutoRouter());
+    return U.webView(context) ? WebAppPage(AutoRouter()) : MobAppPage(AutoRouter());
   }
 }
 
@@ -26,7 +23,7 @@ class HomeResponsive extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return U.webView(context)?WebHomePage():MobMenuTemplate(child: MobHomePage());
+    return U.webView(context) ? WebHomePage() : MobMenuTemplate();
   }
 }
 
@@ -36,7 +33,7 @@ class HistoryResponsive extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return U.webView(context)?WebHistoryPage():MobMenuTemplate(child: MobHistoryPage());
+    return U.webView(context) ? WebHistoryPage() : MobMenuTemplate();
   }
 }
 
@@ -46,7 +43,7 @@ class AccountResponsive extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
-    return U.webView(context)?WebAccountPage():MobMenuTemplate(child: MobAccountPage());
+    return U.webView(context) ? WebAccountPage() : MobMenuTemplate();
   }
 }
 

+ 59 - 71
lib/src/layouts/components/template.dart

@@ -18,19 +18,14 @@ import 'package:lottie/lottie.dart';
 import 'package:page_transition/page_transition.dart';
 import 'package:photo_view/photo_view.dart';
 import 'package:photo_view/photo_view_gallery.dart';
-import 'package:provider/provider.dart';
-import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
 import 'package:telnow_mobile_new/src/api/jwt_token.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
 import 'package:telnow_mobile_new/src/layouts/auth/qr.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
-import 'package:telnow_mobile_new/src/utils/dio_logging_interceptors.dart';
 import 'package:uuid/uuid.dart';
 
-import '../../utils/provider.dart';
-
 PreferredSizeWidget appBarTemplate({required BuildContext context, required String title, String icon = 'arrow-left', List<Widget>? action, exc = false}){
   return AppBar(
     elevation: 0,
@@ -46,16 +41,16 @@ PreferredSizeWidget appBarTemplate({required BuildContext context, required Stri
   );
 }
 
-Widget buttonTemplate({double width = double.infinity, double height = 51, required String text, required action(), backgroundColor = primaryColor, textColor = Colors.white, borderColor = primaryColor}){
+Widget buttonTemplate({double width = double.infinity, double height = 51, required String text, required Function() action, backgroundColor = primaryColor, textColor = Colors.white, borderColor = primaryColor}){
   return SizedBox(
     width: width,
     height: height,
     child: ElevatedButton(
       style: ButtonStyle(
-          elevation: MaterialStateProperty.all<double>(0),
-          backgroundColor: MaterialStateProperty.all<Color>(backgroundColor),
-          shape: MaterialStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))),
-          side: MaterialStateProperty.all<BorderSide>(BorderSide(color: borderColor))
+          elevation: WidgetStateProperty.all<double>(0),
+          backgroundColor: WidgetStateProperty.all<Color>(backgroundColor),
+          shape: WidgetStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))),
+          side: WidgetStateProperty.all<BorderSide>(BorderSide(color: borderColor))
       ),
       onPressed: ()=>action(),
       child: Text(text, style: TextStyle(fontSize: 16, color: textColor, fontWeight: FontWeight.w500)),
@@ -93,16 +88,16 @@ Widget textHorizontal(String title, String subtitle, {double size = 14, double o
               },
             ),
           ],
-        ):Text("$subtitle", style: TextStyle(color: textColor, fontSize: size), maxLines: 3, overflow: TextOverflow.ellipsis, textAlign: TextAlign.end,),
+        ):Text(subtitle, style: TextStyle(color: textColor, fontSize: size), maxLines: 3, overflow: TextOverflow.ellipsis, textAlign: TextAlign.end,),
       ),
     ],
   );
 }
 
-Widget divider({opacity = 0.15, EdgeInsetsGeometry padding = const EdgeInsets.only(top: 0)}){
+Widget divider({double opacity = 0.15, EdgeInsetsGeometry padding = const EdgeInsets.only(top: 0), Color color = textColor, thickness = 1.0}){
   return Padding(
     padding: padding,
-    child: Divider(height: 1, thickness: 1, color: textColor.withValues(alpha: opacity)),
+    child: Divider(height: 1, thickness: thickness, color: color.withValues(alpha: opacity)),
   );
 }
 
@@ -122,17 +117,17 @@ Widget separator(){
 Widget categoryContainer({required BuildContext context, required String iconUrl}){
   double size = U.bodyWidth(context)/6;
 
-  String ext = iconUrl.split(".").last;
+  // String ext = iconUrl.split(".").last;
 
   return Container(
     padding: EdgeInsets.all(5),
-    child: Image(image: CachedNetworkImageProvider(iconUrl+'?bridge-cache=true', cacheManager: CacheManager(CacheMan.config(iconUrl+'?bridge-cache=true'))), width: size, height: size),
     decoration: BoxDecoration(
         color: Colors.white,
         borderRadius: BorderRadius.all(Radius.circular(20)),
         border: Border.all(width: 0.2, color: Colors.black12),
         boxShadow: [BoxShadow(color: Colors.grey.withValues(alpha: 0.3), blurRadius: 2, offset: Offset(0, 2))]
     ),
+    child: Image(image: CachedNetworkImageProvider('$iconUrl?bridge-cache=true', cacheManager: CacheManager(CacheMan.config('$iconUrl?bridge-cache=true'))), width: size, height: size),
   );
 }
 
@@ -149,18 +144,6 @@ Widget shimmerTopMenu(BuildContext context){
           borderRadius: BorderRadius.all(Radius.circular(20)),
         ),
       );
-
-      // return Shimmer.fromColors(
-      //   baseColor: Colors.grey[400]!,
-      //   highlightColor: Colors.white,
-      //   child: Container(
-      //     width: size, height: size,
-      //     decoration: BoxDecoration(
-      //       color: Colors.white,
-      //       borderRadius: BorderRadius.all(Radius.circular(20)),
-      //     ),
-      //   ),
-      // );
     }),
   );
 }
@@ -171,6 +154,11 @@ Widget requestTiles({required String image, required String title, required Stri
   return Container(
     margin: EdgeInsets.symmetric(vertical: vertical),
     padding: EdgeInsetsDirectional.only(end: border?16:0),
+    decoration: BoxDecoration(
+        color: Colors.white,
+        border: border?Border.all(color: textColor.withValues(alpha: 0.15)):null,
+        borderRadius: border?BorderRadius.all(Radius.circular(12)):null
+    ),
     child: Row(
       children: [
         imageTiles(imageUrl: image),
@@ -187,11 +175,6 @@ Widget requestTiles({required String image, required String title, required Stri
         )
       ],
     ),
-    decoration: BoxDecoration(
-        color: Colors.white,
-        border: border?Border.all(color: textColor.withValues(alpha: 0.15)):null,
-        borderRadius: border?BorderRadius.all(Radius.circular(12)):null
-    ),
   );
 }
 
@@ -208,7 +191,7 @@ Widget imageTiles({required String imageUrl, double width = 100, double height =
         color: textColor.withValues(alpha: 0.1),
         borderRadius: BorderRadius.all(Radius.circular(radius)),
         image: DecorationImage(
-          image: CachedNetworkImageProvider(imageUrl+'?bridge-cache=true', cacheManager: CacheManager(CacheMan.config(imageUrl+'?bridge-cache=true'))),
+          image: CachedNetworkImageProvider('$imageUrl?bridge-cache=true', cacheManager: CacheManager(CacheMan.config('$imageUrl?bridge-cache=true'))),
           fit: BoxFit.cover,
         ),
       ),
@@ -216,15 +199,24 @@ Widget imageTiles({required String imageUrl, double width = 100, double height =
   ): Container(
     height: height,
     width: width,
-    child: Center(child: Text("noImage".tr(), style: TextStyle(fontStyle: FontStyle.italic, fontSize: width<100?10:14, color: Colors.black38), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center,)),
     decoration: BoxDecoration(
         color: Colors.black12,
         borderRadius: BorderRadius.all(Radius.circular(12.0))
     ),
+    child: Center(child: Text("noImage".tr(), style: TextStyle(fontStyle: FontStyle.italic, fontSize: width<100?10:14, color: Colors.black38), maxLines: 2, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center,)),
   );
 }
 
-Widget loginFieldTemplate({required String value, placeholder = '', required action(value), required submit(value), isPassword = false}){
+typedef FieldCallback = void Function(String value);
+Widget loginFieldTemplate(
+    {
+      required String value,
+      placeholder = '',
+      required FieldCallback action,
+      required FieldCallback submit,
+      isPassword = false
+    }
+){
   TextEditingController controller = TextEditingController()..text = value;
   bool hidePass = true;
   return SizedBox(
@@ -262,26 +254,20 @@ Widget loadingTemplate(VoidCallback setState){
     setState();
   });
   return Center(
-    child: Container(
+    child: SizedBox(
       height: 36,
-      child: LoadingIndicator(
-        indicatorType: Indicator.ballPulseRise,
-        colors: U.defaultRainbowColors(),
-        strokeWidth: 2,
-        backgroundColor: Colors.black.withValues(alpha: 0),
-        pathBackgroundColor: Colors.black,
-      ),
+      child: loadingTemplateNoVoid()
     ),
   );
 }
 
 Widget loadingTemplateNoVoid(){
   return Center(
-    child: Container(
+    child: SizedBox(
       height: 36,
       child: LoadingIndicator(
-        indicatorType: Indicator.ballPulseRise,
-        colors: U.defaultRainbowColors(),
+        indicatorType: Indicator.ballClipRotatePulse,
+        colors: U.loadingIndicatorColors(),
         strokeWidth: 2,
         backgroundColor: Colors.black.withValues(alpha: 0),
         pathBackgroundColor: Colors.black,
@@ -330,22 +316,22 @@ Widget infoContainer(String text){
   return Container(
     width: double.infinity,
     padding: EdgeInsets.all(12),
-    child: Text(text, style: TextStyle(color: textColor), textAlign: TextAlign.center),
     decoration: BoxDecoration(
         color: secondaryColor.withValues(alpha: 0.1),
         border: Border.all(color: secondaryColor),
         borderRadius: BorderRadius.all(Radius.circular(12))
     ),
+    child: Text(text, style: TextStyle(color: textColor), textAlign: TextAlign.center),
   );
 }
 
 class PhotoPreview extends StatelessWidget {
-  final title;
+  final String title;
   final imageUrl;
   final bool isUrl;
   final bool isNetwork;
 
-  PhotoPreview(this.title, this.imageUrl, this.isUrl, {this.isNetwork = false});
+  const PhotoPreview(this.title, this.imageUrl, this.isUrl, {super.key, this.isNetwork = false});
 
   @override
   Widget build(BuildContext context) {
@@ -362,7 +348,7 @@ class PhotoPreview extends StatelessWidget {
           onTap: () => Navigator.of(context).pop(),
         ),
       ),
-      body: Container(
+      body: SizedBox(
           width: MediaQuery.of(context).size.width,
           height: MediaQuery.of(context).size.height,
           child: PhotoView(
@@ -390,12 +376,12 @@ class PhotoPreviewGallery extends StatelessWidget {
         title: Text(title, style: TextStyle(fontSize: 18)),
         backgroundColor: Colors.black.withValues(alpha: 0.4),
         titleSpacing: 0,
-        leading: new IconButton(
-          icon: new Icon(Icons.arrow_back_rounded),
+        leading: IconButton(
+          icon: Icon(Icons.arrow_back_rounded),
           onPressed: () => Navigator.of(context).pop(),
         ),
       ),
-      body: Container(
+      body: SizedBox(
           width: MediaQuery.of(context).size.width,
           height: MediaQuery.of(context).size.height,
           child: PhotoViewGallery.builder(
@@ -404,7 +390,7 @@ class PhotoPreviewGallery extends StatelessWidget {
             builder: (context, index) {
               var uuid = Uuid().v1().replaceAll('-', '');
               return PhotoViewGalleryPageOptions(
-                imageProvider: (isUrl?NetworkImage(imageList[index][column]+'?uuid='+uuid):imageList[index] is File?FileImage(imageList[index]):MemoryImage(imageList[index])) as ImageProvider<Object>?,
+                imageProvider: (isUrl ? NetworkImage('${imageList[index][column]}?uuid=$uuid') : imageList[index] is File ? FileImage(imageList[index]) : MemoryImage(imageList[index])) as ImageProvider<Object>?,
                 maxScale: 5.0, minScale: 0.3,
               );
             },
@@ -417,7 +403,7 @@ class PhotoPreviewGallery extends StatelessWidget {
 //------------------------------------------------------------------------------
 
 class MyClipper extends CustomClipper<Path> {
-  final isNip;
+  final bool isNip;
 
   MyClipper(this.isNip);
 
@@ -448,7 +434,7 @@ class MyClipper extends CustomClipper<Path> {
 }
 
 class YourClipper extends CustomClipper<Path> {
-  final isNip;
+  final bool isNip;
 
   YourClipper(this.isNip);
 
@@ -529,7 +515,7 @@ void showError(BuildContext context, message) {
     flushbarPosition: FlushbarPosition.BOTTOM,
     margin: EdgeInsets.all(8),
     borderRadius: BorderRadius.all(Radius.circular(8)),
-  )..show(context);
+  ).show(context);
 }
 
 void showSuccess(context, message) {
@@ -544,7 +530,7 @@ void showSuccess(context, message) {
     flushbarPosition: FlushbarPosition.BOTTOM,
     margin: EdgeInsets.all(8),
     borderRadius: BorderRadius.all(Radius.circular(8)),
-  )..show(context);
+  ).show(context);
 }
 
 String convertDate(date, String locale) {
@@ -559,9 +545,9 @@ String convertDate(date, String locale) {
 
   final aDate = DateTime(dateToCheck.year, dateToCheck.month, dateToCheck.day);
   if (aDate == today) {
-    return 'today'.tr()+' '+DateFormat('HH:mm', locale).format(DateTime.parse(date));
+    return '${'today'.tr()} ${DateFormat('HH:mm', locale).format(DateTime.parse(date))}';
   } else if (aDate == yesterday) {
-    return 'yesterday'.tr()+' '+DateFormat('HH:mm', locale).format(DateTime.parse(date));
+    return '${'yesterday'.tr()} ${DateFormat('HH:mm', locale).format(DateTime.parse(date))}';
   } else {
     return DateFormat('dd MMM HH:mm', locale).format(DateTime.parse(date));
   }
@@ -572,7 +558,7 @@ String convertDate(date, String locale) {
 class RefreshPage extends StatelessWidget {
   final VoidCallback onRefresh;
 
-  RefreshPage(this.onRefresh);
+  const RefreshPage(this.onRefresh, {super.key});
 
   @override
   Widget build(BuildContext context) {
@@ -599,6 +585,8 @@ final JwtToken token = JwtToken();
 final SharedPreferencesManager _sharedPreferencesManager = locator<SharedPreferencesManager>();
 
 class NoDataPage extends StatelessWidget {
+  const NoDataPage({super.key});
+
   @override
   Widget build(BuildContext context) {
     return Center(
@@ -677,7 +665,7 @@ handlingError(context, type) {
                   width: 200,
                   padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
                   child: Image(
-                      image: AssetImage('assets/image/error/' + data[type]['image']!))),
+                      image: AssetImage('assets/image/error/${data[type]['image']!}'))),
               Padding(
                 padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
                 child: Text(data[type]['title']!,
@@ -699,13 +687,13 @@ handlingError(context, type) {
                     child: TextButton(
                       style: ButtonStyle(
                           backgroundColor:
-                          MaterialStateProperty.all<Color>(primaryColor),
-                          shape: MaterialStateProperty.all<
+                          WidgetStateProperty.all<Color>(primaryColor),
+                          shape: WidgetStateProperty.all<
                               RoundedRectangleBorder>(
                               RoundedRectangleBorder(
                                 borderRadius: BorderRadius.circular(5),
                               ))),
-                      child: new Text('logout'.tr().toUpperCase(),
+                      child: Text('logout'.tr().toUpperCase(),
                           style: TextStyle(color: Colors.white),
                           textAlign: TextAlign.center),
                       onPressed: () {
@@ -744,7 +732,7 @@ handlingError(context, type) {
                       },
                     )),
               )
-                  : Container(height: 1, width: 1)
+                  : SizedBox(height: 1, width: 1)
             ],
           ),
         );
@@ -755,11 +743,11 @@ handlingError(context, type) {
 class NoImageFound extends StatelessWidget {
   bool show = true;
 
-  NoImageFound(this.show);
+  NoImageFound(this.show, {super.key});
 
   @override
   Widget build(BuildContext context) {
-    return Container(
+    return SizedBox(
       width: MediaQuery.of(context).size.width - 150,
       height: MediaQuery.of(context).size.height / 4,
       child: Stack(
@@ -776,12 +764,12 @@ class NoImageFound extends StatelessWidget {
                 child: show
                     ? Container(
                   padding: const EdgeInsets.all(10),
-                  child: Text('notFoundImg'.tr(),
-                      style: TextStyle(fontSize: 12)),
                   decoration: BoxDecoration(
                       color: Colors.white24,
                       borderRadius:
                       BorderRadius.all(Radius.circular(50))),
+                  child: Text('notFoundImg'.tr(),
+                      style: TextStyle(fontSize: 12)),
                 )
                     : Center(
                   child: CircularProgressIndicator(),
@@ -828,7 +816,7 @@ showLoading(BuildContext context, {String? text, String? lottie}){
     barrierColor: lottie!=null?Colors.white:null,
     builder: (BuildContext context) {
       return WillPopScope(
-        onWillPop: ()=>Future.value(false),
+        onWillPop: () => Future.value(false),
         child: Center(
           child: Column(
             mainAxisSize: MainAxisSize.min,

+ 46 - 40
lib/src/layouts/functions/account.dart

@@ -1,5 +1,4 @@
 import 'package:auto_route/auto_route.dart';
-import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/cupertino.dart';
 import 'package:provider/provider.dart';
 import 'package:telnow_mobile_new/main.dart';
@@ -10,25 +9,28 @@ import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferenc
 import 'package:telnow_mobile_new/src/utils/C.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 class AccountFunction{
   final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
   final NotificationClass notification = NotificationClass();
   final JwtToken token = JwtToken();
-  SharedPreferencesManager _sharedPreferencesManager = SharedPreferencesManager();
+  final SharedPreferencesManager _sharedPreferencesManager = SharedPreferencesManager();
+
   getUser(BuildContext context) async {
-    var res = await token.getUserData(context);
+    final userModule = Provider.of<UserModule>(context, listen: false);
+    var res = await token.getUserData();
     if (res != null) {
       var ver = await apiAuthProvider.getDataNoAuth('/api/license');
-      Provider.of<UserModule>(context, listen: false).setSerialNum(ver['serialNumber']);
-      Provider.of<UserModule>(context, listen: false).setList(res);
+      userModule.setSerialNum(ver['serialNumber']);
+      userModule.setList(res);
     } else {
-      Provider.of<UserModule>(context, listen: false).setResetData(true);
+      userModule.setResetData(true);
     }
   }
 
-  switchLang(BuildContext context, code, user) async {
-    showLoading(context);
+  switchLang(code, user) async {
+    UIService.showLoading();
     var locale = 'id';
     if(code is int){
       var vl = U.retValidLang();
@@ -38,30 +40,30 @@ class AccountFunction{
       locale = code;
     }
     try{
-      var p;
+      Map<String, dynamic> p;
       if(await U.isCompatibleWith(VersionKey.multiBahasa)){
         p = {'userId': user['userId'], 'language': 'ID', '_language': code.toUpperCase()};
       } else {
         p = {'userId': user['userId'], 'language': code.toUpperCase()};
       }
-      var res = await apiAuthProvider.patchData('/api/informants/' + user['id'].toString(), p, context);
+      var res = await apiAuthProvider.patchData('/api/informants/${user['id']}', p);
       if (res != null) {
-        closeLoading(context);
-        context.setLocale(Locale(locale));
-        notification.startNotification(context, code: code);
-        // Navigator.of(context).pop();
+        UIService.closeLoading();
+        UIService.setLocale(Locale(locale));
+        // context.setLocale(Locale(locale));
+        notification.startNotification(code: code);
       } else {
-        closeLoading(context);
+        UIService.closeLoading();
       }
     }catch(e){
-      closeLoading(context);
+      UIService.closeLoading();
     }
   }
 
-  Future<bool> setDndStatus(BuildContext context, bool dnd)async{
-    var res = await apiAuthProvider.patchData('/api/informants/${Provider.of<UserModule>(context, listen: false).user()['id']}/setDnd', null, context, params: {'dnd': '${!dnd}'});
+  Future<bool> setDndStatus(String id, bool dnd)async{
+    var res = await apiAuthProvider.patchData('/api/informants/$id/setDnd', null, params: {'dnd': '${!dnd}'});
     if (res != null) {
-      Provider.of<UserModule>(context, listen: false).setDndStatus(dnd);
+      // Provider.of<UserModule>(context, listen: false).setDndStatus(dnd);
       return true;
     }
     return false;
@@ -71,33 +73,37 @@ class AccountFunction{
     showLoading(context, text: '');
     String pid = U.getPidFromUrl(context.router.currentUrl);
 
+    void loggingOut(){
+      Navigator.of(context).pop();
+      UIService.closeLoading();
+      token.logout();
+      context.navigateToPath("/app/$pid/login");
+    }
+
     try {
-      var tkn = await _sharedPreferencesManager.getString(SharedPreferencesManager.keyFcmToken);
-      // var tkn = await U.getFcmToken();
-      if (tkn == null) {
-        Navigator.of(context).pop();
-        closeLoading(context);
-        token.logout();
-        // context.router.removeLast();
-        context.navigateToPath("/app/$pid/login");
-      } else {
+      var tkn = _sharedPreferencesManager.getString(SharedPreferencesManager.keyFcmToken);
+
+      if (tkn == null)
+      {
+        loggingOut();
+      }
+      else
+      {
         var param = {'token': tkn, 'whiteListToken': false};
-        var res = await apiAuthProvider.postData('/api/fcmTokens/remove/', null, param, context);
-        if (res != null && res['success']) {
-          Navigator.of(context).pop();
-          closeLoading(context);
-          token.logout();
-          // context.router.removeLast();
-          context.navigateToPath("/app/$pid/login");
+        var res = await apiAuthProvider.postData('/api/fcmTokens/remove/', null, param);
+
+        if (res != null && res['success'])
+        {
+          loggingOut();
         }
-        else{
-          Future.delayed(const Duration(seconds: 5), () => closeLoading(context));
+        else
+        {
+          Future.delayed(const Duration(seconds: 5), () => UIService.closeLoading());
         }
       }
     } catch(e) {
-      print(e.toString());
-      // context.router.removeLast();
-      context.navigateToPath("/app/$pid/login");
+      debugPrint(e.toString());
+      UIService.navigateNamed("/app/$pid/login");
     }
   }
 }

+ 13 - 11
lib/src/layouts/functions/detail.dart

@@ -7,6 +7,7 @@ import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
 import 'package:telnow_mobile_new/src/api/jwt_token.dart';
 import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:translator/translator.dart';
 
 class DetailFunction{
@@ -14,8 +15,8 @@ class DetailFunction{
   final JwtToken token = JwtToken();
   final translator = GoogleTranslator();
 
-  Future getMission(BuildContext context, list) async{
-    var userData = await token.getUserData(context);
+  Future getMission(String locale, list) async{
+    var userData = await token.getUserData();
     var data = list;
     if (data['datetimeScheduled'] != null && data['datetimeScheduled'] != '' && data['noteFormat'] == 'DATE') {
       var date = data['datetimeScheduled'];
@@ -27,7 +28,7 @@ class DetailFunction{
     data['noteCompleteTranslate'] = '';
     data['noteCancelTranslate'] = '';
 
-    var locale = context.locale.toString() == 'zh' ? 'zh-cn' : context.locale.toString();
+    if(locale == 'zh') locale = 'zh-cn';
 
     if (data['noteStart'] == 'transferNoteBySystem'){
       data['noteStart'] = 'transferNoteBySystem'.tr();
@@ -76,23 +77,24 @@ class DetailFunction{
 
   cancelRequest(BuildContext context, list, controllerNote){
     var note = controllerNote.text.trim();
-    if (note.length > 128) {
+    if (note.length > 128)
+    {
       showError(context, 'noteLongAlert'.tr());
     }
-    else{
+    else
+    {
       dialogConfirm(context: context, title: 'buttonCancel'.tr(), text: 'confirmCancel'.tr(), actionYes: ()async{
         showLoading(context);
         var data = {
           'note': controllerNote.text.trim(),
         };
 
-        var res = await apiAuthProvider.postData('/api/requestHistories/search/request/cancel/'+list['ticketNo'], null, data, context);
-        if(res != null){
-          closeLoading(context);
+        var res = await apiAuthProvider.postData('/api/requestHistories/search/request/cancel/${list['ticketNo']}', null, data);
+        if(res != null && context.mounted){
+          UIService.closeLoading();
           navigateBack(context, exc: true);
-        }
-        else{
-          closeLoading(context);
+        } else {
+          UIService.closeLoading();
         }
       });
     }

+ 140 - 105
lib/src/layouts/functions/history.dart

@@ -1,178 +1,208 @@
 import 'dart:convert';
 
 import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
 import 'package:telnow_mobile_new/src/api/jwt_token.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
-import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/layouts/mobile/request_create.dart';
 import 'package:telnow_mobile_new/src/layouts/web/request_create.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
+enum HistoryTab {
+  ongoing,
+  done;
+
+  List get filters {
+    switch(this){
+      case HistoryTab.ongoing:
+        return [
+          {'title': 'stateQueue'.tr(), 'value': 'queue', 'filter': '{"or":[{"f":["currentState","EQ","DIANTRIKAN"]},{"f":["currentState","EQ","DIPROSES"]}]}'},
+          {'title': 'stateDone'.tr(), 'value': 'done', 'filter': '{"f":["currentState","EQ","DIMULAI"]}'},
+          {'title': 'hold'.tr(), 'value': 'hold', 'filter': '{"f":["currentState","EQ","HOLD"]}'}
+        ];
+      case HistoryTab.done:
+        return [
+          {'title': 'stateFinish'.tr(), 'value': 'finish', 'filter': '{"or":[{"f":["currentState","EQ","DISELESAIKAN"]},{"f":["currentState","EQ","TUNTAS"]}]}'},
+          {'title': 'stateCancel'.tr(), 'value': 'cancel', 'filter': '{"f":["currentState","EQ","DIBATALKAN"]}'}
+        ];
+    }
+  }
+}
 class HistoryFunction{
   final JwtToken token = JwtToken();
   final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
   final SharedPreferencesManager sharedPreferencesManager = locator<SharedPreferencesManager>();
 
+  final BuildContext context = UIService.context!;
+  late final HistoryModule historyModule = Provider.of<HistoryModule>(context, listen: false);
+  late final UserModule userModule = Provider.of<UserModule>(context, listen: false);
+
+  List filterList = [
+    {'title': 'stateQueue'.tr(), 'value': 1, 'filter': '{"or":[{"f":["currentState","EQ","DIANTRIKAN"]},{"f":["currentState","EQ","DIPROSES"]}]}'},
+    {'title': 'stateDone'.tr(), 'value': 2, 'filter': '{"f":["currentState","EQ","DIMULAI"]}'},
+    {'title': 'hold'.tr(), 'value': 3, 'filter': '{"f":["currentState","EQ","HOLD"]}'},
+    {'title': 'stateFinish'.tr(), 'value': 4, 'filter': '{"or":[{"f":["currentState","EQ","DISELESAIKAN"]},{"f":["currentState","EQ","TUNTAS"]}]}'},
+    {'title': 'stateCancel'.tr(), 'value': 5, 'filter': '{"f":["currentState","EQ","DIBATALKAN"]}'}
+  ];
+
   List doneList = [
-    {'title': 'stateFinish'.tr(), 'value': 3, 'filter': '{"or":[{"f":["currentState","EQ","DISELESAIKAN"]},{"f":["currentState","EQ","TUNTAS"]}]}'},
-    {'title': 'stateCancel'.tr(), 'value': 4, 'filter': '{"f":["currentState","EQ","DIBATALKAN"]}'},
+    {'title': 'stateFinish'.tr(), 'value': 4, 'filter': '{"or":[{"f":["currentState","EQ","DISELESAIKAN"]},{"f":["currentState","EQ","TUNTAS"]}]}'},
+    {'title': 'stateCancel'.tr(), 'value': 5, 'filter': '{"f":["currentState","EQ","DIBATALKAN"]}'},
   ];
+
   List ongoingList = [
     {'title': 'stateQueue'.tr(), 'value': 1, 'filter': '{"or":[{"f":["currentState","EQ","DIANTRIKAN"]},{"f":["currentState","EQ","DIPROSES"]}]}'},
     {'title': 'stateDone'.tr(), 'value': 2, 'filter': '{"f":["currentState","EQ","DIMULAI"]}'},
     {'title': 'hold'.tr(), 'value': 3, 'filter': '{"f":["currentState","EQ","HOLD"]}'},
   ];
 
-  getActiveForum(BuildContext context, {bool loadAfterAction = false}) async {
+  getActiveForum({bool loadAfterAction = false}) async {
     try {
       String urlForum = '/api/notifications/search/forumNotification';
 
       var val = await CacheMan.readData(urlForum);
       if (val != null) {
-        Provider.of<HistoryModule>(context, listen: false).setActiveForum(val['data']);
+        historyModule.setActiveForum(val['data']);
       }
 
-      var data = await apiAuthProvider.getData(urlForum, null, context);
+      var data = await apiAuthProvider.getData(urlForum, null);
       if (data != null) {
-        Provider.of<HistoryModule>(context, listen: false).setActiveForum(data);
+        historyModule.setActiveForum(data);
         CacheMan.writeData(urlForum, data);
-        getUser(context);
-      }
-      else{
-        getUser(context);
       }
+      // kayaknya ga guna getUser disini. 031125
+      // getUser();
     } catch (e) {
-      print(e.toString());
+      debugPrint(e.toString());
     }
   }
 
-  getUser(BuildContext context) async {
+  getUser() async {
     try {
-      var res = await token.getUserData(context);
+      var res = await token.getUserData();
       if (res != null) {
-        Provider.of<UserModule>(context, listen: false).setList(res);
-        getMission(context);
+        userModule.setList(res);
+        // dipindah di event onRefresh, paralel sama get notifForum. 031125
+        // getMission();
       } else {
-        Provider.of<UserModule>(context, listen: false).setResetData(true);
+        userModule.setResetData(true);
       }
     } catch (e) {
-      Provider.of<UserModule>(context, listen: false).setResetData(true);
+      userModule.setResetData(true);
     }
   }
 
-  getMission(BuildContext context) async {
+  getMission() async {
+    HistoryTab tab = historyModule.activeTab();
+    List filters = tab.filters;
+    String selectedFilter = historyModule.selectedFilter();
+    String state = '${tab.name}::$selectedFilter';
+
     try {
-      if (!Provider.of<HistoryModule>(context, listen: false).isLoadHistory() && !Provider.of<HistoryModule>(context, listen: false).stopLoadHistory()) {
-        Provider.of<HistoryModule>(context, listen: false).setLoadHistory(true);
+      // debugPrint('historyModule.isLoadHistory(state: state): ${historyModule.isLoadHistory(state: state)}');
+      if (!historyModule.isLoadHistory(state: state) && !historyModule.stopLoadHistory()) {
+        historyModule.setLoadHistory(true, state);
         var sort = ['datetimeRequest,desc'];
-        var filter = '';
+        String filter;
 
-        if (Provider.of<HistoryModule>(context, listen: false).selectedFilter() == 0) {
-          if (Provider.of<HistoryModule>(context, listen: false).pressAttention() != 0) {
-            for (int i = 0; i < doneList.length; i++) {
-              if (Provider.of<HistoryModule>(context, listen: false).checkPressAttention(doneList[i]['value'])) {
-                filter = doneList[i]['filter'];
-              }
-            }
-          } else {
+        // If no selected filter button, use default filter;
+        if(selectedFilter == 'none'){
+          if(historyModule.activeTab().name == "done"){
             filter = '{"or":[{"f":["currentState","EQ","DISELESAIKAN"]},{"f":["currentState","EQ","TUNTAS"]},{"f":["currentState","EQ","DIBATALKAN"]}]}';
-          }
-        } else {
-          if((Provider.of<HistoryModule>(context, listen: false).pressAttention() == 0 || Provider.of<HistoryModule>(context, listen: false).pressAttention() == 1) && U.newServerVersion(1709864293) && !U.getInternetStatus()){
-            List pendingList = sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyPendingData)!?jsonDecode(sharedPreferencesManager.getString(SharedPreferencesManager.keyPendingData)!):[];
-            Provider.of<HistoryModule>(context, listen: false).setPendingData(pendingList);
-          }
-
-          if (Provider.of<HistoryModule>(context, listen: false).pressAttention() != 0) {
-            for (int i = 0; i < ongoingList.length; i++) {
-              if (Provider.of<HistoryModule>(context, listen: false).checkPressAttention(ongoingList[i]['value'])) {
-                filter = ongoingList[i]['filter'];
-              }
-            }
           } else {
             filter = '{"or":[{"f":["currentState","EQ","DIANTRIKAN"]},{"f":["currentState","EQ","DIPROSES"]},{"f":["currentState","EQ","DIMULAI"]}]}';
           }
+        } else {
+          filter = filters.firstWhere((f) => f['value'] == selectedFilter)['filter'];
+        }
+
+        // If no internet connection and selected status is On going and filter button is Queued, display pending data.
+        if(historyModule.activeTab().name == "ongoing" && (selectedFilter == 'none' || selectedFilter == 'queue') && U.newServerVersion(1709864293) && !U.getInternetStatus()){
+          List pendingList = sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyPendingData)!?jsonDecode(sharedPreferencesManager.getString(SharedPreferencesManager.keyPendingData)!):[];
+          historyModule.setPendingData(pendingList);
         }
 
         String url = '/api/requestHistories/search/myReqHistory';
 
         String key = url + filter;
         var val = await CacheMan.readData(key);
-        if (val != null && Provider.of<HistoryModule>(context, listen: false).page() == 0) {
-          Provider.of<HistoryModule>(context, listen: false).setMisi(val['data']);
+        if (val != null && historyModule.page() == 0) {
+          historyModule.setDataRequests(state, val['data']);
         }
 
-        var misi = await apiAuthProvider.getData(url, {'size': '30', 'page': Provider.of<HistoryModule>(context, listen: false).page().toString(), 'sort': sort, 'filter': filter}, context);
-        if (misi != null) {
+        var mission = await apiAuthProvider.getData(url, {'size': '30', 'page': historyModule.page().toString(), 'sort': sort, 'filter': filter});
+        if (mission != null){
+          // debugPrint("Cek tekan kene!!");
+          historyModule.setLoadHistory(false, state);
           List tempData = [];
-          if (misi.containsKey('_embedded')) {
-            for (int i = 0; i < misi['_embedded']['requestHistories'].length; i++) {
-              var _isFrm = false;
-              var _id = null;
-              var _msg = null;
+          if (mission.containsKey('_embedded')) {
+            // debugPrint('Balikan data: ${mission['_embedded']['requestHistories'].length}');
+            for (int i = 0; i < mission['_embedded']['requestHistories'].length; i++) {
+              var isFrm = false;
+              String id = "";
+              String msg = "";
 
-              Provider.of<HistoryModule>(context, listen: false).activeForum().forEach((el) {
-                if (el['ticketId'] == misi['_embedded']['requestHistories'][i]['ticketNo']) {
-                  _id = el['id'];
-                  _msg = el['desc'];
+              historyModule.activeForum().forEach((el) {
+                if (el['ticketId'] == mission['_embedded']['requestHistories'][i]['ticketNo']) {
+                  id = el['id'];
+                  msg = el['desc'];
                 }
               });
 
-              if (_id != null) _isFrm = true;
-              misi['_embedded']['requestHistories'][i]['_hasForum'] = _isFrm;
-              misi['_embedded']['requestHistories'][i]['_forumId'] = _id;
-              misi['_embedded']['requestHistories'][i]['_forumMsg'] = _msg;
+              if (id != "") isFrm = true;
+              mission['_embedded']['requestHistories'][i]['_hasForum'] = isFrm;
+              mission['_embedded']['requestHistories'][i]['_forumId'] = id;
+              mission['_embedded']['requestHistories'][i]['_forumMsg'] = msg;
 
-              tempData.add(misi['_embedded']['requestHistories'][i]);
+              tempData.add(mission['_embedded']['requestHistories'][i]);
             }
           }
 
-          Provider.of<HistoryModule>(context, listen: false).setLoadHistory(false);
-          if (tempData.length == 0) {
-            Provider.of<HistoryModule>(context, listen: false).setStopLoadHistory(true);
-            Provider.of<HistoryModule>(context, listen: false).clearMisi();
+          // debugPrint('Filter Index: ${indexFilterSelected} & Filter: $filter');
+          // historyModule.setLoadHistory(false);
+          if (tempData.isEmpty) {
+            historyModule.setStopLoadHistory(true);
+            historyModule.clearDataRequests();
             CacheMan.writeData(key, tempData);
           } else {
-            if (Provider.of<HistoryModule>(context, listen: false).page() == 0) {
+            if (historyModule.page() == 0) {
               CacheMan.writeData(key, tempData);
-              Provider.of<HistoryModule>(context, listen: false).clearMisi();
+              historyModule.clearDataRequests();
             }
-            Provider.of<HistoryModule>(context, listen: false).addPage();
+            historyModule.addPage();
           }
-          // print("tempData");
-          // print(tempData);
-          Provider.of<HistoryModule>(context, listen: false).setMisi(tempData);
+          historyModule.setDataRequests(state, tempData);
 
-          if (Provider.of<HistoryModule>(context, listen: false).getMisiLength() >= misi['page']['totalElements']) {
-            Provider.of<HistoryModule>(context, listen: false).setStopLoadHistory(true);
+          if (historyModule.getMisiLength() >= mission['page']['totalElements']) {
+            historyModule.setStopLoadHistory(true);
           }
-        }
-        else{
-          Provider.of<HistoryModule>(context, listen: false).setLoadHistory(false);
-          Provider.of<HistoryModule>(context, listen: false).setStopLoadHistory(true);
+        } else {
+          historyModule.setLoadHistory(false, state);
+          historyModule.setStopLoadHistory(true);
         }
       }
     } catch (e) {
-      print(e.toString());
+      debugPrint(e.toString());
     }
   }
 
-  onRefresh(BuildContext context) async {
-    Provider.of<HistoryModule>(context, listen: false).clearMisi();
-    Provider.of<HistoryModule>(context, listen: false).setPendingData([]);
-    Provider.of<HistoryModule>(context, listen: false).setActiveForum([]);
-    Provider.of<HistoryModule>(context, listen: false).setSelectedIndexes([]);
-    Provider.of<HistoryModule>(context, listen: false).setMultiSelectMode(false);
-    Provider.of<HistoryModule>(context, listen: false).setStopLoadHistory(false);
-    Provider.of<HistoryModule>(context, listen: false).setPage(0);
-    getActiveForum(context);
+  onRefresh() async {
+    historyModule.setCurrentPage();
+    historyModule.clearDataRequests();
+    historyModule.setPendingData([]);
+    historyModule.setActiveForum([]);
+    historyModule.setSelectedIndexes([]);
+    historyModule.setMultiSelectMode(false);
+    historyModule.setStopLoadHistory(false);
+    historyModule.setPage(0);
+    getMission();
+    getActiveForum();
   }
 
   bool askForRate(list) {
@@ -190,12 +220,12 @@ class HistoryFunction{
   }
 
   selectedController(BuildContext context, i) {
-    if (Provider.of<HistoryModule>(context, listen: false).selectedIndexes().contains(i)) {
-      Provider.of<HistoryModule>(context, listen: false).removeSelectedIndexes(i);
+    if (historyModule.selectedIndexes().contains(i)) {
+      historyModule.removeSelectedIndexes(i);
     } else {
-      Provider.of<HistoryModule>(context, listen: false).addSelectedIndexes(i);
+      historyModule.addSelectedIndexes(i);
     }
-    Provider.of<HistoryModule>(context, listen: false).setMultiSelectMode(Provider.of<HistoryModule>(context, listen: false).selectedIndexes().length == 0 ? false : true);
+    historyModule.setMultiSelectMode(historyModule.selectedIndexes().isEmpty ? false : true);
   }
 
   deleteData(BuildContext context) {
@@ -217,12 +247,12 @@ class HistoryFunction{
                 onPressed: () async {
                   Navigator.of(contextt).pop();
                   List<String> params = [];
-                  Provider.of<HistoryModule>(context, listen: false).selectedIndexes().forEach((e) {
-                    params.add(Provider.of<HistoryModule>(context, listen: false).dataMisi()[e]["ticketNo"]);
+                  historyModule.selectedIndexes().forEach((e) {
+                    params.add(historyModule.dataRequests()[e]["ticketNo"]);
                   });
-                  var res = await apiAuthProvider.postData("/api/requestHistories/deleteMyHistory", {"ticketNumbers": params}, null, context);
+                  var res = await apiAuthProvider.postData("/api/requestHistories/deleteMyHistory", {"ticketNumbers": params}, null);
                   if (res != null) {
-                    onRefresh(context);
+                    onRefresh();
                   }
                 },
                 child: Text("buttonYes".tr())),
@@ -232,27 +262,32 @@ class HistoryFunction{
     );
   }
 
-  requestAgainAction(BuildContext context, int i) async{
+  requestAgainAction(int i) async{
+    final isWebView = U.webView(context);
     try {
       String tenant = '';
       String filter = '';
 
-      if ((Provider.of<HistoryModule>(context, listen: false).dataMisi()[i]['requestGroupCode']).split(" ").length != 1) {
-        tenant = ',{"f":["tenantCode","EQ","${(Provider.of<HistoryModule>(context, listen: false).dataMisi()[i]['requestGroupCode']).split(" ").first}"]}';
+      if ((historyModule.dataRequests()[i]['requestGroupCode']).split(" ").length != 1) {
+        tenant = ',{"f":["tenantCode","EQ","${(historyModule.dataRequests()[i]['requestGroupCode']).split(" ").first}"]}';
       }
-      filter = '{"and":[{"f":["code","EQ","${Provider.of<HistoryModule>(context, listen: false).dataMisi()[i]['requestCode']}"]}$tenant]}';
+      filter = '{"and":[{"f":["code","EQ","${historyModule.dataRequests()[i]['requestCode']}"]}$tenant]}';
 
-      var res = await apiAuthProvider.getData('/api/requests/search/customFind', {'filter': filter}, context);
+      var res = await apiAuthProvider.getData('/api/requests/search/customFind', {'filter': filter});
       if (res != null) {
         if (res.containsKey('_embedded')) {
-          navigateTo(context, U.webView(context)?WebReqCreatePage(user: Provider.of<UserModule>(context, listen: false).user(), request: res['_embedded']['requests'][0]):MobReqCreatePage(user: Provider.of<UserModule>(context, listen: false).user(), request: res['_embedded']['requests'][0]));
+          UIService.navigateTo(
+            isWebView ?
+              WebReqCreatePage(user: userModule.user(), request: res['_embedded']['requests'][0]) :
+              MobReqCreatePage(user: userModule.user(), request: res['_embedded']['requests'][0])
+          );
         } else {
-          showError(context, 'reqCodeNotFound'.tr());
+          UIService.showError('reqCodeNotFound'.tr());
         }
       }
     } catch (e) {
-      showError(context, e.toString());
-      print(e);
+      UIService.showError(e.toString());
+      debugPrint(e.toString());
     }
   }
 }

+ 99 - 93
lib/src/layouts/functions/home.dart

@@ -9,37 +9,43 @@ import 'package:telnow_mobile_new/src/utils/C.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 class HomeFunction{
   final SharedPreferencesManager sharedPreferencesManager = locator<SharedPreferencesManager>();
   final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
   final JwtToken token = JwtToken();
+  final BuildContext context = UIService.context!;
+  late final ServiceModule serviceModule = Provider.of<ServiceModule>(context, listen: false);
+  late final UserModule userModule = Provider.of<UserModule>(context, listen: false);
 
   getProfileData(BuildContext context)async{
-    if (!Provider.of<ServiceModule>(context, listen: false).initialized()) {
-      Provider.of<ServiceModule>(context, listen: false).setInitialized(true);
+    if (!serviceModule.initialized()) {
+      serviceModule.setInitialized(true);
 
       try{
         var license = await U.getLicense(showErr: false);
-        Provider.of<UserModule>(context, listen: false).setHouseKeeping(license['houseKeepingFunction']);
-      }catch(e){}
+        userModule.setHouseKeeping(license['houseKeepingFunction']);
+      }catch(e){
+        debugPrint(e.toString());
+      }
 
       try{
-        var res = await token.getUserData(context);
+        var res = await token.getUserData();
         if(res != null){
-          Provider.of<UserModule>(context, listen: false).setList(res);
-          Provider.of<UserModule>(context, listen: false).setProfile(res['_profile']['data']);
-          Provider.of<UserModule>(context, listen: false).setDndStatus(res['dnd']);
+          userModule.setList(res);
+          userModule.setProfile(res['_profile']['data']);
+          userModule.setDndStatus(res['dnd']);
 
           getOptions(context);
         } else {
-          Provider.of<ServiceModule>(context, listen: false).setInitialized(false);
-          Provider.of<UserModule>(context, listen: false).setResetData(true);
+          serviceModule.setInitialized(false);
+          userModule.setResetData(true);
         }
       }catch(e){
         showError(context, e.toString());
-        Provider.of<ServiceModule>(context, listen: false).setInitialized(false);
-        Provider.of<UserModule>(context, listen: false).setResetData(true);
+        serviceModule.setInitialized(false);
+        userModule.setResetData(true);
       }
     }
   }
@@ -50,174 +56,174 @@ class HomeFunction{
 
       var val = await CacheMan.readData(url);
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setScoope(val['data']);
+        serviceModule.setScoope(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, null, context);
+      var res = await apiAuthProvider.getData(url, null);
       if (res != null) {
         CacheMan.writeData(url, res);
-        Provider.of<ServiceModule>(context, listen: false).setScoope(res);
+        serviceModule.setScoope(res);
       }
 
-      if(Provider.of<ServiceModule>(context, listen: false).scoope().isNotEmpty){
+      if(serviceModule.scoope().isNotEmpty){
         //dev-note: cek 1-1, mekanisme load nya. ada yg bikin berat tidak?
-        for (int i = 0; i < Provider.of<ServiceModule>(context, listen: false).scoope().length; i++) {
-          if (sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyScoope)! && sharedPreferencesManager.getString(SharedPreferencesManager.keyScoope) == Provider.of<ServiceModule>(context, listen: false).scoope()[i]['key']) {
-            Provider.of<ServiceModule>(context, listen: false).setScoopeValue(Provider.of<ServiceModule>(context, listen: false).scoope()[i]['key']);
-            Provider.of<ServiceModule>(context, listen: false).setScoopeName(Provider.of<ServiceModule>(context, listen: false).scoope()[i]['value']);
+        for (int i = 0; i < serviceModule.scoope().length; i++) {
+          if (sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyScoope)! && sharedPreferencesManager.getString(SharedPreferencesManager.keyScoope) == serviceModule.scoope()[i]['key']) {
+            serviceModule.setScoopeValue(serviceModule.scoope()[i]['key']);
+            serviceModule.setScoopeName(serviceModule.scoope()[i]['value']);
           }
         }
 
-        var profile = Provider.of<UserModule>(context, listen: false).profile();
+        var profile = userModule.profile();
         if(profile['topMenu']['show'] != null && profile['topMenu']['show'] == true) {
           if(U.newServerVersion(1709864293)){
-            getTopMenuNew(context);
+            getTopMenuNew();
           }
           else{
-            getTopMenu(context);
-            getReqGroup(context);
+            getTopMenu();
+            getReqGroup();
           }
         }
         if(profile['specialOffer'] != null && profile['specialOffer']['show'] == true) getSpecialOffer(context);
-        if(profile['frequentlyRequested'] != null && profile['frequentlyRequested']['show'] == true) getFrequentlyRequested(context);
+        if(profile['frequentlyRequested'] != null && profile['frequentlyRequested']['show'] == true) getFrequentlyRequested();
         if(profile['banner'] != null && profile['banner']['show'] == true) getBanner(context);
         if(profile['quickAction'] != null && profile['quickAction']['show'] == true) getQuickAction(context);
         getUnreadMessages(context);
       }
-      Provider.of<ServiceModule>(context, listen: false).setInitialized(false);
+      serviceModule.setInitialized(false);
     } catch (e) {
-      Provider.of<ServiceModule>(context, listen: false).setInitialized(false);
+      serviceModule.setInitialized(false);
       print(e.toString());
     }
   }
 
   getSpecialOffer(BuildContext context) async {
     try {
-      String url = '/api/informants/search/request/specialOffer/'+Provider.of<ServiceModule>(context, listen: false).scoopeValue();
+      String url = '/api/informants/search/request/specialOffer/'+serviceModule.scoopeValue();
 
       var val = await CacheMan.readData(url);
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setSpecialOffer(val['data']);
+        serviceModule.setSpecialOffer(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, null, context);
+      var res = await apiAuthProvider.getData(url, null);
       if (res != null) {
         if (res.length > 0) {
           CacheMan.writeData(url, res);
-          Provider.of<ServiceModule>(context, listen: false).setSpecialOffer(res);
+          serviceModule.setSpecialOffer(res);
         } else {
-          Provider.of<ServiceModule>(context, listen: false).setSpecialOffer([]);
+          serviceModule.setSpecialOffer([]);
         }
       }
     } catch (e) {
       print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setSpecialOffer([]);
+      serviceModule.setSpecialOffer([]);
     }
   }
 
-  getTopMenuNew(BuildContext context) async {
+  getTopMenuNew() async {
     try {
-      var url = '/api/requestGroups/search/dashboard/topMenu/'+Provider.of<ServiceModule>(context, listen: false).scoopeValue();
+      var url = '/api/requestGroups/search/dashboard/topMenu/'+serviceModule.scoopeValue();
 
       var val = await CacheMan.readData('$url?responseServantGroup=${U.servantDisplay()}');
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setTopMenu(val['data']['topMenu']);
-        Provider.of<ServiceModule>(context, listen: false).setReqGroup(val['data']['excludeTopMenu']);
+        serviceModule.setTopMenu(val['data']['topMenu']);
+        serviceModule.setReqGroup(val['data']['excludeTopMenu']);
       }
 
-      var res = await apiAuthProvider.getData(url, {'responseServantGroup': U.servantDisplay().toString()}, context);
+      var res = await apiAuthProvider.getData(url, {'responseServantGroup': U.servantDisplay().toString()});
       if (res != null) {
         CacheMan.writeData('$url?responseServantGroup=${U.servantDisplay()}', res);
-        Provider.of<ServiceModule>(context, listen: false).setTopMenu(res['topMenu']);
-        Provider.of<ServiceModule>(context, listen: false).setReqGroup(res['excludeTopMenu']);
+        serviceModule.setTopMenu(res['topMenu']);
+        serviceModule.setReqGroup(res['excludeTopMenu']);
       }
     } catch (e) {
       print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setTopMenu([]);
-      Provider.of<ServiceModule>(context, listen: false).setReqGroup([]);
+      serviceModule.setTopMenu([]);
+      serviceModule.setReqGroup([]);
     }
   }
 
-  getTopMenu(BuildContext context) async {
+  getTopMenu() async {
     try {
-      var url = '/api/requestGroups/search/topMenu/'+Provider.of<ServiceModule>(context, listen: false).scoopeValue();
+      var url = '/api/requestGroups/search/topMenu/'+serviceModule.scoopeValue();
 
       var val = await CacheMan.readData('$url?responseServantGroup=${U.servantDisplay()}');
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setTopMenu(val['data']);
+        serviceModule.setTopMenu(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, {'responseServantGroup': U.servantDisplay().toString()}, context);
+      var res = await apiAuthProvider.getData(url, {'responseServantGroup': U.servantDisplay().toString()});
       if (res != null) {
         List tempData = [];
         tempData.addAll(res);
 
         if (tempData.length > 0) {
           CacheMan.writeData('$url?responseServantGroup=${U.servantDisplay()}', res);
-          Provider.of<ServiceModule>(context, listen: false).setTopMenu(tempData);
+          serviceModule.setTopMenu(tempData);
         } else {
           CacheMan.writeData('$url?responseServantGroup=${U.servantDisplay()}', []);
-          Provider.of<ServiceModule>(context, listen: false).setTopMenu([]);
+          serviceModule.setTopMenu([]);
         }
       }
       else{
-        Provider.of<ServiceModule>(context, listen: false).setTopMenu([]);
+        serviceModule.setTopMenu([]);
       }
     } catch (e) {
       print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setTopMenu([]);
+      serviceModule.setTopMenu([]);
     }
   }
 
-  getReqGroup(BuildContext context) async {
+  getReqGroup() async {
     try {
-      var url = '/api/requestGroups/search/excludeTopMenu/'+Provider.of<ServiceModule>(context, listen: false).scoopeValue();
+      var url = '/api/requestGroups/search/excludeTopMenu/'+serviceModule.scoopeValue();
 
       var val = await CacheMan.readData('$url?responseServantGroup=${U.servantDisplay()}');
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setReqGroup(val['data']);
+        serviceModule.setReqGroup(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, {'responseServantGroup': U.servantDisplay().toString()}, context);
+      var res = await apiAuthProvider.getData(url, {'responseServantGroup': U.servantDisplay().toString()});
       if (res != null) {
         List tempData = [];
         tempData.addAll(res);
 
         if (tempData.length > 0) {
           CacheMan.writeData('$url?responseServantGroup=${U.servantDisplay()}', res);
-          Provider.of<ServiceModule>(context, listen: false).setReqGroup(tempData);
+          serviceModule.setReqGroup(tempData);
         } else {
           CacheMan.writeData('$url?responseServantGroup=${U.servantDisplay()}', []);
-          Provider.of<ServiceModule>(context, listen: false).setReqGroup([]);
+          serviceModule.setReqGroup([]);
         }
       }
     } catch (e) {
       print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setReqGroup([]);
+      serviceModule.setReqGroup([]);
     }
   }
 
-  getFrequentlyRequested(BuildContext context) async {
+  getFrequentlyRequested() async {
     try {
-      var url = '/api/requestHistories/search/frequently/request/' + Provider.of<ServiceModule>(context, listen: false).scoopeValue();
+      var url = '/api/requestHistories/search/frequently/request/' + serviceModule.scoopeValue();
 
       var val = await CacheMan.readData(url);
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setData(val['data']);
+        serviceModule.setData(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, null, context);
+      var res = await apiAuthProvider.getData(url, null);
       if (res != null) {
         if (res.length > 0) {
           CacheMan.writeData(url, res);
-          Provider.of<ServiceModule>(context, listen: false).setData(res);
+          serviceModule.setData(res);
         } else {
-          Provider.of<ServiceModule>(context, listen: false).setData([]);
+          serviceModule.setData([]);
         }
       }
     } catch (e) {
-      print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setData([]);
+      debugPrint(e.toString());
+      serviceModule.setData([]);
     }
   }
 
@@ -227,78 +233,78 @@ class HomeFunction{
 
       var val = await CacheMan.readData(url);
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setBanner(val['data']);
+        serviceModule.setBanner(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, {'size': '20'}, context);
+      var res = await apiAuthProvider.getData(url, {'size': '20'});
       if (res != null) {
         if (res.length > 0) {
           CacheMan.writeData(url, res);
-          Provider.of<ServiceModule>(context, listen: false).setBanner(res);
+          serviceModule.setBanner(res);
         } else {
-          Provider.of<ServiceModule>(context, listen: false).setBanner([]);
+          serviceModule.setBanner([]);
         }
       }
     } catch (e) {
       print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setBanner([]);
+      serviceModule.setBanner([]);
     }
   }
 
   getQuickAction(BuildContext context) async {
     try {
-      String url = '/api/informants/search/request/quickAction/'+Provider.of<ServiceModule>(context, listen: false).scoopeValue();
+      String url = '/api/informants/search/request/quickAction/'+serviceModule.scoopeValue();
 
       var val = await CacheMan.readData(url);
       if (val != null) {
-        Provider.of<ServiceModule>(context, listen: false).setQuickAct(val['data']);
+        serviceModule.setQuickAct(val['data']);
       }
 
-      var res = await apiAuthProvider.getData(url, null, context);
+      var res = await apiAuthProvider.getData(url, null);
       if (res != null) {
         if (res.length > 0) {
           CacheMan.writeData(url, res);
-          Provider.of<ServiceModule>(context, listen: false).setQuickAct(res);
+          serviceModule.setQuickAct(res);
         } else {
-          Provider.of<ServiceModule>(context, listen: false).setQuickAct([]);
+          serviceModule.setQuickAct([]);
         }
       }
     } catch (e) {
       print(e.toString());
-      Provider.of<ServiceModule>(context, listen: false).setQuickAct([]);
+      serviceModule.setQuickAct([]);
     }
   }
 
   getContactCenter(BuildContext context) async {
     try{
       var url = "/api/contactCenter/whatsapp" ;
-      var res = await apiAuthProvider.getData(url, null, context);
+      var res = await apiAuthProvider.getData(url, null);
       if (res != null) {
-        Provider.of<ServiceModule>(context, listen: false).setContactCenter(res);
+        serviceModule.setContactCenter(res);
       }
     }catch(e){
-      Provider.of<ServiceModule>(context, listen: false).setContactCenter('');
+      serviceModule.setContactCenter('');
     }
   }
 
   getUnreadMessages(BuildContext context) async {
     try {
-      var res = await apiAuthProvider.getData('/api/messages/search/myMessages', null, context);
+      var res = await apiAuthProvider.getData('/api/messages/search/myMessages', null);
       if (res != null) {
         List tempData = [];
-        Provider.of<ServiceModule>(context, listen: false).setUnreadMessage(false);
+        serviceModule.setUnreadMessage(false);
         if(res.containsKey('_embedded')){
           for (int i = 0; i < res['_embedded']['myMessages'].length; i++) {
-            if(res['_embedded']['myMessages'][i]['lastReadStatus'] == 'UNREAD' && res['_embedded']['myMessages'][i]['userId'] != Provider.of<UserModule>(context, listen: false).user()['userId']){
-              Provider.of<ServiceModule>(context, listen: false).setUnreadMessage(true);
+            if(res['_embedded']['myMessages'][i]['lastReadStatus'] == 'UNREAD' && res['_embedded']['myMessages'][i]['userId'] != userModule.user()['userId']){
+              serviceModule.setUnreadMessage(true);
             }
             tempData.add(res['_embedded']['myMessages'][i]);
           }
         }
 
-        Provider.of<ServiceModule>(context, listen: false).setMessage([]);
+        serviceModule.setMessage([]);
         if (tempData.length > 0) {
-          Provider.of<ServiceModule>(context, listen: false).setMessage(tempData);
+          serviceModule.setMessage(tempData);
         }
       }
     } catch (e) {
@@ -308,11 +314,11 @@ class HomeFunction{
     if(await U.isCompatibleWith(VersionKey.multiBahasa) == false) return;
     try{
       String url = '/api/messages/search/myForum';
-      var res = await apiAuthProvider.getData(url, null, context);
+      var res = await apiAuthProvider.getData(url, null);
       if (res != null) {
         for (int i = 0; i < res.length; i++) {
-          if (res[i]['readStatus'] == 'UNREAD' && res[i]['userId'] != Provider.of<UserModule>(context, listen: false).user()['userId']){
-            Provider.of<ServiceModule>(context, listen: false).setUnreadMessage(true);
+          if (res[i]['readStatus'] == 'UNREAD' && res[i]['userId'] != userModule.user()['userId']){
+            serviceModule.setUnreadMessage(true);
           }
         }
       }
@@ -320,18 +326,18 @@ class HomeFunction{
   }
 
   onRefresh(BuildContext context){
-    var profile = Provider.of<UserModule>(context, listen: false).profile();
+    var profile = userModule.profile();
     if(profile['topMenu']['show'] != null && profile['topMenu']['show'] == true) {
       if(U.newServerVersion(1709864293)){
-        getTopMenuNew(context);
+        getTopMenuNew();
       }
       else{
-        getTopMenu(context);
-        getReqGroup(context);
+        getTopMenu();
+        getReqGroup();
       }
     }
     if(profile['specialOffer'] != null && profile['specialOffer']['show'] == true) getSpecialOffer(context);
-    if(profile['frequentlyRequested'] != null && profile['frequentlyRequested']['show'] == true) getFrequentlyRequested(context);
+    if(profile['frequentlyRequested'] != null && profile['frequentlyRequested']['show'] == true) getFrequentlyRequested();
     if(profile['banner'] != null && profile['banner']['show'] == true) getBanner(context);
     if(profile['quickAction'] != null && profile['quickAction']['show'] == true) getQuickAction(context);
     getUnreadMessages(context);

+ 5 - 8
lib/src/layouts/functions/message.dart

@@ -1,8 +1,6 @@
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/cupertino.dart';
-import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
 import 'package:provider/provider.dart';
 import 'package:telnow_mobile_new/src/api/api_auth_provider.dart';
 import 'package:telnow_mobile_new/src/api/jwt_token.dart';
@@ -14,14 +12,13 @@ import 'package:telnow_mobile_new/src/utils/C.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
-import 'package:url_launcher/url_launcher.dart';
 
 class MessageFunction{
   final ApiAuthProvider apiAuthProvider = ApiAuthProvider();
   final JwtToken token = JwtToken();
 
   getUser(BuildContext context)async{
-    var res = await token.getUserData(context);
+    var res = await token.getUserData();
     if(res != null){
       Provider.of<MessageModule>(context, listen: false).setUser(res);
       getDataMessages(context);
@@ -36,7 +33,7 @@ class MessageFunction{
       Provider.of<MessageModule>(context, listen: false).setData(val['data']);
     }
 
-    var res = await apiAuthProvider.getData(url, null, context);
+    var res = await apiAuthProvider.getData(url, null);
     if (res != null) {
       List tempData = [];
       if (res['_embedded'] != null) {
@@ -61,7 +58,7 @@ class MessageFunction{
       messageModule.setForum(val['data']);
     }
 
-    var res = await apiAuthProvider.getData(url, null, context);
+    var res = await apiAuthProvider.getData(url, null);
     if (res != null) {
       List tempData = [];
       for (int i = 0; i < res.length; i++) {
@@ -77,7 +74,7 @@ class MessageFunction{
   }
 
   setAsRead(context, ticketNo) async {
-    var res = await apiAuthProvider.postData('/api/notifications/readMyForum/$ticketNo', null, null, context);
+    var res = await apiAuthProvider.postData('/api/notifications/readMyForum/$ticketNo', null, null);
     if (res != null) {
     }
   }
@@ -92,7 +89,7 @@ class MessageFunction{
     }
 
     try{
-      var res = await apiAuthProvider.getData(url, {'isPaged': 'true', 'size': '1000', 'sort': 'description'}, context);
+      var res = await apiAuthProvider.getData(url, {'isPaged': 'true', 'size': '1000', 'sort': 'description'});
       if (res != null) {
         List tempData = [];
         if (res['_embedded'] != null) {

+ 7 - 5
lib/src/layouts/functions/request.dart

@@ -19,6 +19,7 @@ import 'package:telnow_mobile_new/src/utils/cache_manager.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
 
 import 'package:image/image.dart' as img;
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:uuid/uuid.dart';
 
 class RequestFunction{
@@ -76,7 +77,7 @@ class RequestFunction{
         }
         List tempData = [];
 
-        var res = await apiAuthProvider.getData(url, {'filter': filter, 'nopage': 'true'}, context);
+        var res = await apiAuthProvider.getData(url, {'filter': filter, 'nopage': 'true'});
         if (res != null && res.toString() != "[]") {
           if (res.containsKey('_embedded')) {
             for (int i = 0; i < res['_embedded']['requests'].length; i++) {
@@ -102,7 +103,7 @@ class RequestFunction{
   }
 
   Future getSuggestionLocation(BuildContext context) async{
-    var res = await apiAuthProvider.getData('/api/requestHistories/search/location/suggestion', null, context);
+    var res = await apiAuthProvider.getData('/api/requestHistories/search/location/suggestion', null);
     if(res != null){
       Provider.of<CreateSerModule>(context, listen: false).setSuggestion(res);
     }
@@ -145,7 +146,8 @@ class RequestFunction{
   }
 
   sendRequest(BuildContext context, widget, controllerNote, controllerReferenceNumber, controllerLocation, controllerDateString, currentSliderValue) async{
-    showLoading(context, lottie: kIsWeb && !isCanvasKit ? null : 'Paperplane.json', text: 'sendingRequest'.tr());
+    // showLoading(context, lottie: kIsWeb && !isCanvasKit ? null : 'Paperplane.json', text: 'sendingRequest'.tr());
+    UIService.showLoading(text: 'sendingRequest'.tr(), lottie: kIsWeb && !isCanvasKit ? null : 'Paperplane.json' );
     var data;
     data = {
       'note': controllerNote.text.trim() == '' ? '' : controllerNote.text.trim(),
@@ -167,7 +169,7 @@ class RequestFunction{
       });
       data['images'] = imageEncode;
 
-      var res = await apiAuthProvider.postData('/api/requestHistories/search/request/' + widget.request['type'] + '/' + widget.request['id'].toString() + '/' + widget.request['noteFormat'] + '/submit', null, data, context);
+      var res = await apiAuthProvider.postData('/api/requestHistories/search/request/' + widget.request['type'] + '/' + widget.request['id'].toString() + '/' + widget.request['noteFormat'] + '/submit', null, data);
       if (res != null) {
         Future.delayed(Duration(seconds: 3), (){
           closeLoading(context);
@@ -222,7 +224,7 @@ class RequestFunction{
       data['req'][0]['images'] = imageEncode;
 
       try{
-        var res = await apiAuthProvider.postData('/api/receptionists/send/request', null, data, context);
+        var res = await apiAuthProvider.postData('/api/receptionists/send/request', null, data);
         print("res ##> $res");
         if (res != null) {
           Future.delayed(Duration(seconds: 3), (){

+ 0 - 3
lib/src/layouts/landing/landing_page.dart

@@ -1,6 +1,3 @@
-import 'dart:convert';
-import 'dart:typed_data';
-
 import 'package:auto_route/annotations.dart';
 import 'package:auto_route/auto_route.dart';
 import 'package:easy_localization/easy_localization.dart';

+ 44 - 23
lib/src/layouts/mobile/app_mobile.dart

@@ -6,13 +6,18 @@ import 'package:easy_localization/easy_localization.dart';
 import 'package:firebase_messaging/firebase_messaging.dart';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
+import 'package:lazy_load_indexed_stack/lazy_load_indexed_stack.dart';
 import 'package:quick_notify_2/quick_notify.dart';
 import 'package:simple_connection_checker/simple_connection_checker.dart';
 import 'package:telnow_mobile_new/main.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
 import 'package:telnow_mobile_new/src/layouts/components/template.dart';
+import 'package:telnow_mobile_new/src/layouts/mobile/menu_account.dart';
+import 'package:telnow_mobile_new/src/layouts/mobile/menu_history.dart';
+import 'package:telnow_mobile_new/src/layouts/mobile/menu_home.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 class MobAppPage extends StatefulWidget {
   final Widget child;
@@ -59,32 +64,37 @@ class _MobAppPageState extends State<MobAppPage>with TickerProviderStateMixin {
 
   checkConnection() async{
     await U.getServerVersion();
-    // getOtherList();
-    SimpleConnectionChecker _simpleConnectionChecker = SimpleConnectionChecker();
-    subscription = _simpleConnectionChecker.onConnectionChange.listen((connected) {
+    final ctx = UIService.context;
+    var langCode = ctx!.locale.toString();
+
+    void initFirebase(){
+      notification.initFirebaseMessaging(context);
+    }
+
+    void checkPendingRequest(){
+      U.checkPendingRequest(context);
+      if(ModalRoute.of(context)?.isCurrent != true) navigateBack(context);
+    }
+
+    SimpleConnectionChecker simpleConnectionChecker = SimpleConnectionChecker();
+    subscription = simpleConnectionChecker.onConnectionChange.listen((connected) {
       U.setInternetStatus(connected);
-      setState(()=>begin = true);
+      setState(() => begin = true);
       if(connected){
-        setState(()=>isConnected = true);
-        notification.initFirebaseMessaging(context);
+        setState(() => isConnected = true);
+        initFirebase();
         var valLang = U.retValidLang();
-        var langCode = context.locale.toString();
-        var code;
+        int? code;
         if(valLang.indexOf(langCode) > 1){
           code = valLang.indexOf(langCode) - 1;
         }
-        notification.startNotification(context, code: code);
-
-        if(U.newServerVersion(1709864293)){
-          U.checkPendingRequest(context);
-        }
-        if(ModalRoute.of(context)?.isCurrent != true) navigateBack(context);
-      }
-      else{
+        notification.startNotification(code: code);
+        checkPendingRequest();
+      } else {
         Future.delayed(Duration(seconds: 4), (){
           if(!U.getInternetStatus()){
-            handlingError(context, 0);
-            setState(()=>isConnected = false);
+            UIService.handlingError(ErrorType.noInternet);
+            setState(() => isConnected = false);
           }
         });
       }
@@ -119,8 +129,8 @@ class _MobAppPageState extends State<MobAppPage>with TickerProviderStateMixin {
 //---------------------------------------------------------------------------------------------------------------
 
 class MobMenuTemplate extends StatefulWidget {
-  final Widget child;
-  const MobMenuTemplate({required this.child, Key? key}) : super(key: key);
+  // final Widget child;
+  const MobMenuTemplate({super.key});
 
   @override
   State<MobMenuTemplate> createState() => _MobMenuTemplateState();
@@ -151,6 +161,17 @@ class _MobMenuTemplateState extends State<MobMenuTemplate> {
     });
   }
 
+  indexedStack(index){
+    return LazyLoadIndexedStack(
+      index: index,
+      children: [
+        MobHomePage(),
+        MobHistoryPage(),
+        MobAccountPage()
+      ],
+    );
+  }
+
   @override
   void initState() {
     getUser();
@@ -170,10 +191,10 @@ class _MobMenuTemplateState extends State<MobMenuTemplate> {
 
     return Scaffold(
       backgroundColor: backgroundColor,
-      body: !kIsWeb?DoubleBackToExit(
+      body: !kIsWeb ? DoubleBackToExit(
         snackBarMessage: 'pressAgain'.tr(),
-        child: widget.child,
-      ):widget.child,
+        child: indexedStack(index)
+      ) : indexedStack(index),
       bottomNavigationBar: BottomNavigationBar(
         backgroundColor: Colors.white,
         selectedItemColor: textColor,

+ 3 - 3
lib/src/layouts/mobile/banner_detail.dart

@@ -9,9 +9,9 @@ import 'package:url_launcher/url_launcher.dart';
 import 'package:youtube_player_iframe/youtube_player_iframe.dart';
 
 class MobBannerDetailPage extends StatefulWidget {
-  Map<String, dynamic> user;
-  Map<String, dynamic> data;
-  MobBannerDetailPage({required this.user, required this.data, super.key});
+  final Map<String, dynamic> user;
+  final Map<String, dynamic> data;
+  const MobBannerDetailPage({required this.user, required this.data, super.key});
 
   @override
   State<MobBannerDetailPage> createState() => _MobBannerDetailPageState();

+ 12 - 13
lib/src/layouts/mobile/history_detail.dart

@@ -8,12 +8,13 @@ import 'package:telnow_mobile_new/src/layouts/mobile/history_forum.dart';
 import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 import 'package:timelines_plus/timelines_plus.dart';
 import 'package:url_launcher/url_launcher.dart';
 
 class MobHistoryDetailPage extends StatefulWidget {
-  int index;
-  MobHistoryDetailPage({required this.index, super.key});
+  final int index;
+  const MobHistoryDetailPage({required this.index, super.key});
 
   @override
   State<MobHistoryDetailPage> createState() => _MobHistoryDetailPageState();
@@ -43,7 +44,7 @@ class _MobHistoryDetailPageState extends State<MobHistoryDetailPage> {
   }
 
   getData() async{
-    var res = await detFunc.getMission(context, Provider.of<HistoryModule>(context, listen: false).dataMisi()[widget.index]);
+    var res = await detFunc.getMission(UIService.getLocale(), Provider.of<HistoryModule>(context, listen: false).dataRequests()[widget.index]);
     setState((){
       user = res['user'];
       list = res['list'];
@@ -327,7 +328,7 @@ class _MobHistoryDetailPageState extends State<MobHistoryDetailPage> {
                 ),
               ):_timeLimit ? showButton(context) : loadingTemplate(() {if(mounted) setState(()=>_timeLimit=true);},),
             ),
-            divider(),
+            divider(color: primaryColor, opacity: 1.0, thickness: 2.0),
             list.isEmpty || list['autoResponse'] || list['currentState'] == 'DISELESAIKAN' || list['currentState'] == 'TUNTAS' || list['currentState'] == 'DIBATALKAN' ? Container() : Container(
               padding: EdgeInsets.symmetric(vertical: 10, horizontal: 16), color: Colors.white,
               child: list['currentState'] == 'DIMULAI' && list['_canNotCancel'] ? Text('canNotCancel2'.tr(), style: TextStyle(color: textColor, fontSize: 14), textAlign: TextAlign.center) : Container(
@@ -482,15 +483,13 @@ class _MobHistoryDetailPageState extends State<MobHistoryDetailPage> {
               mainAxisAlignment: MainAxisAlignment.spaceBetween,
               children: [
                 Text('state'.tr(), style: TextStyle(fontSize: 14, color: textColor)),
-                Container(
-                  child: Row(
-                    mainAxisAlignment: MainAxisAlignment.end,
-                    children: [
-                      Image(image: AssetImage('assets/image/general/Watch.png'), width: 20, height: 20),
-                      SizedBox(width: 5),
-                      Text('hold'.tr(), style: TextStyle(fontSize: 14, color: primaryColor)),
-                    ],
-                  ),
+                Row(
+                  mainAxisAlignment: MainAxisAlignment.end,
+                  children: [
+                    Image(image: AssetImage('assets/image/general/Watch.png'), width: 20, height: 20),
+                    SizedBox(width: 5),
+                    Text('hold'.tr(), style: TextStyle(fontSize: 14, color: primaryColor)),
+                  ],
                 )
               ],
             ),

+ 7 - 7
lib/src/layouts/mobile/history_detail_pending.dart

@@ -9,8 +9,8 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:timelines_plus/timelines_plus.dart';
 
 class MobHistoryDetailPendingPage extends StatefulWidget {
-  Map<String, dynamic> data;
-  MobHistoryDetailPendingPage({required this.data, super.key});
+  final Map<String, dynamic> data;
+  const MobHistoryDetailPendingPage({required this.data, super.key});
 
   @override
   State<MobHistoryDetailPendingPage> createState() => _MobHistoryDetailPendingPageState();
@@ -59,7 +59,7 @@ class _MobHistoryDetailPendingPageState extends State<MobHistoryDetailPendingPag
                             SizedBox(height: 16),
                             divider(opacity: 0.05),
                             SizedBox(height: 16),
-                            attachment_new(widget.data['imageList']),
+                            attachmentNew(widget.data['imageList']),
                             SizedBox(height: 16),
                             textVertical('note'.tr(), widget.data['note'] != '' ? widget.data['note'] : '-'),
                             SizedBox(height: 24),
@@ -136,15 +136,15 @@ class _MobHistoryDetailPendingPageState extends State<MobHistoryDetailPendingPag
     );
   }
 
-  Widget attachment_new(List images){
+  Widget attachmentNew(List images){
     var imageWidth = ((U.bodyWidth(context)-32)/5)-5;
 
     List imageList = [];
-    images.forEach((element) {
+    for (var element in images) {
       imageList.add(base64Decode(element));
-    });
+    }
 
-    return imageList.length > 0 ? Column(
+    return imageList.isNotEmpty ? Column(
       crossAxisAlignment: CrossAxisAlignment.start,
       children: [
         Text('image'.tr(), style: TextStyle(color: textColor)),

+ 25 - 23
lib/src/layouts/mobile/history_forum.dart

@@ -39,7 +39,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
 
   String? idChat;
   String? imagePath;
-  TextEditingController controllerPesan = new TextEditingController();
+  TextEditingController controllerMessage = TextEditingController();
   ScrollController scrollController = ScrollController();
   List messageData = [];
   List deletedMessage = [];
@@ -75,9 +75,9 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
 
 
   getData() async {
-    idChat = U.decodeBase64Url(_sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessCode)!) + '-' + widget.data['ticketNo'];
+    idChat = '${U.decodeBase64Url(_sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessCode)!)}-${widget.data['ticketNo']}';
     if (!kIsWeb) imagePath = await token.getPath() + '/TelNow/Images/';
-    var res = await token.getUserData(context);
+    var res = await token.getUserData();
     if (res != null) {
       username = res['userId'];
       getMessage();
@@ -86,7 +86,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
   }
 
   setAsRead(ticketNo) async {
-    var res = await apiAuthProvider.postData('/api/notifications/readMyForum/$ticketNo', null, null, context);
+    var res = await apiAuthProvider.postData('/api/notifications/readMyForum/$ticketNo', null, null);
     if (res != null) {
     }
   }
@@ -95,7 +95,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
     if (!isLoad && !stopLoad) {
       setState(() => isLoad = true);
       setAsRead(widget.data['ticketNo']);
-      var mymess = await apiAuthProvider.getData('/api/messages/search/myMessages/' + idChat!, {'isPaged': 'true', 'page': page.toString(), 'size': '20'}, context);
+      var mymess = await apiAuthProvider.getData('/api/messages/search/myMessages/${idChat!}', {'isPaged': 'true', 'page': page.toString(), 'size': '20'});
       if (mymess.containsKey('_embedded')) {
         List data = mymess['_embedded']['myMessages'];
         for (int i = 0; i < data.length; i++) {
@@ -145,7 +145,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
           var translate = await translator.translate(element['msg'] ?? '', to: loc);
           element['translate'] = translate.text;
         }catch(e){
-          print("Error translate: ${e.toString()}");
+          debugPrint("Error translate: ${e.toString()}");
         }
       }
     });
@@ -160,7 +160,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
           if (result.type == DocumentChangeType.added && isAfterLoad) {
             if (
             (
-                (username == data!['from']['user'] && data!['senderType'] != 'SERVANT') &&
+                (username == data!['from']['user'] && data['senderType'] != 'SERVANT') &&
                     messageData.where((element) => element['uniqueId'] == data['uniqueId']).isNotEmpty
             ) ||
                 (username != data['from']['user'] && data['readStatus'] == 'DELETED')
@@ -175,7 +175,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                   var translate = await translator.translate(data['msg']??'', to: locale);
                   data['translate'] = translate.text;
                 }catch(e){
-                  print("Error translate: ${e.toString()}");
+                  debugPrint("Error translate: ${e.toString()}");
                 }
               }
               bool alreadyExists = messageData.any((element) => element['uniqueId'] == data['uniqueId']);
@@ -185,7 +185,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
             }
             downloadImage(data);
           } else if (result.type == DocumentChangeType.modified && isAfterLoad) {
-            if (messageData.where((element) => element['uniqueId'] == data!['uniqueId']).length > 0) {
+            if (messageData.where((element) => element['uniqueId'] == data!['uniqueId']).isNotEmpty) {
               int index = messageData.indexWhere((element) => element['uniqueId'] == data!['uniqueId']);
               messageData[index]['read'] = data!['read'];
               messageData[index]['readStatus'] = data['readStatus'];
@@ -194,7 +194,9 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
           if(mounted) setState(() {});
         });
       });
-    }catch(e){}
+    }catch(e){
+      debugPrint(e.toString());
+    }
 
     if(mounted) {
       setState(() {
@@ -230,7 +232,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
   sendMessage(data) async {
     setState(() {
       isSend = false;
-      controllerPesan.clear();
+      controllerMessage.clear();
       messageData.add({
         'msg': data['text'],
         'datetime': DateTime.now().toString(),
@@ -245,7 +247,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
       scrollBottom = true;
     });
 
-    var res = await apiAuthProvider.postData('/api/messages', null, data, context);
+    var res = await apiAuthProvider.postData('/api/messages', null, data);
     if (res != null) {
       int index = messageData.indexWhere((element) => element['uniqueId'] == data['uniqueId']);
       setState(() {
@@ -286,7 +288,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
   @override
   Widget build(BuildContext context) {
     WidgetsBinding.instance.addPostFrameCallback((_) {
-      if (messageData.length > 0) {
+      if (messageData.isNotEmpty) {
         if (scrollController.position.maxScrollExtent > 0 && !isReverse) {
           setState(() => isReverse = true);
         }
@@ -321,7 +323,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
             child: Container(
               alignment: Alignment.topCenter,
               width: U.bodyWidth(context),
-              child: messageData.length == 0 && !isAfterLoad ? loadingTemplateNoVoid() : SingleChildScrollView(
+              child: messageData.isEmpty && !isAfterLoad ? loadingTemplateNoVoid() : SingleChildScrollView(
                 controller: scrollController,
                 reverse: isReverse,
                 padding: EdgeInsets.fromLTRB(10, 10, 10, 7),
@@ -413,7 +415,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                                         }
                                       },
                                       onLongPress: (link) {
-                                        Clipboard.setData(new ClipboardData(text: link)).then((value){
+                                        Clipboard.setData(ClipboardData(text: link)).then((value){
                                           showSuccess('link_copied'.tr(), context);
                                         });
                                       },
@@ -444,11 +446,11 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                           var expander = Expanded(
                               flex: 8,
                               child: Column(
+                                crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
                                 children: [
                                   bubleChat(widget, timeBubble, isNip, isMe),
                                   SizedBox(height: 5),
                                 ],
-                                crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
                               )
                           );
 
@@ -492,7 +494,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                     child: SizedBox(
                       width: double.infinity,
                       child: TextField(
-                        controller: controllerPesan,
+                        controller: controllerMessage,
                         maxLength: 256,
                         style: const TextStyle(fontSize: 14, color: Colors.black),
                         keyboardType: TextInputType.multiline,
@@ -500,7 +502,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                         maxLines: 5,
                         decoration: InputDecoration(
                             counterText: '',
-                            hintText: 'writeMessage'.tr()+'..',
+                            hintText: '${'writeMessage'.tr()}..',
                             hintStyle: TextStyle(color: textColor.withValues(alpha: 0.5), fontSize: 14),
                             filled: true,
                             fillColor: backgroundColor,
@@ -515,7 +517,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                                   "userId": username,
                                   "recipientId": "#forum",
                                   "senderType": "INFORMANT",
-                                  "text": controllerPesan.text.trim(),
+                                  "text": controllerMessage.text.trim(),
                                   "chatId": idChat,
                                   "images": null
                                 };
@@ -611,21 +613,21 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
                   GestureDetector(
                     child: Container(
                       padding: EdgeInsets.all(12),
-                      child: Image.asset('assets/image/icon/Send.png', width: 25, height: 25),
                       decoration: BoxDecoration(
                           color: primaryColor,
                           borderRadius: BorderRadius.all(Radius.circular(50))
                       ),
+                      child: Image.asset('assets/image/icon/Send.png', width: 25, height: 25),
                     ),
                     onTap: (){
-                      if(controllerPesan.text.isNotEmpty){
+                      if(controllerMessage.text.isNotEmpty){
                         var uuid = Uuid().v1().replaceAll('-', '');
                         var data = {
                           "uniqueId": uuid,
                           "userId": username,
                           "recipientId": "#forum",
                           "senderType": "INFORMANT",
-                          "text": controllerPesan.text.trim(),
+                          "text": controllerMessage.text.trim(),
                           "chatId": idChat,
                           "images": null
                         };
@@ -661,7 +663,7 @@ class _MobHistoryForumPageState extends State<MobHistoryForumPage> {
         });
       }
     } catch (e) {
-      print(e.toString());
+      debugPrint(e.toString());
     }
   }
 

+ 5 - 5
lib/src/layouts/mobile/history_rating.dart

@@ -17,7 +17,7 @@ class _MobHistoryRatingPageState extends State<MobHistoryRatingPage> {
   String description = '';
   String ratingAspect = '';
   List aspectList = [];
-  TextEditingController controllerOptOther = new TextEditingController();
+  TextEditingController controllerOptOther = TextEditingController();
 
   var rating = [
     {'key': 1, 'image': "assets/image/icon/very_dissatisfied.png", 'label': 'disatisfied'.tr()},
@@ -29,7 +29,7 @@ class _MobHistoryRatingPageState extends State<MobHistoryRatingPage> {
 
   getAspectList(){
     String locale = context.locale.toString();
-    List optionList = widget.data['_ratingAspect'+locale[0].toUpperCase()+locale[1]] != null && widget.data['_ratingAspect'+locale[0].toUpperCase()+locale[1]].trim() != '' ? widget.data['_ratingAspect'+locale[0].toUpperCase()+locale[1]].split(';') : [];
+    List optionList = widget.data['_ratingAspect${locale[0].toUpperCase()}${locale[1]}'] != null && widget.data['_ratingAspect${locale[0].toUpperCase()}${locale[1]}'].trim() != '' ? widget.data['_ratingAspect${locale[0].toUpperCase()}${locale[1]}'].split(';') : [];
     if(optionList.isNotEmpty){
       optionList.add('OTHER_OPTION');
     }
@@ -109,7 +109,7 @@ class _MobHistoryRatingPageState extends State<MobHistoryRatingPage> {
                       child: Column(
                         crossAxisAlignment: CrossAxisAlignment.start,
                         children: [
-                          Text('whatMakesYou'.tr()+' '+description+'?', style: TextStyle(fontSize: 16)),
+                          Text('${'whatMakesYou'.tr()} $description?', style: TextStyle(fontSize: 16)),
                           SizedBox(height: 12),
                           Wrap(
                             runSpacing: 10, spacing: 10,
@@ -117,12 +117,12 @@ class _MobHistoryRatingPageState extends State<MobHistoryRatingPage> {
                               return GestureDetector(
                                 child: Container(
                                   padding: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
-                                  child: Text(aspectList[i]=='OTHER_OPTION'?'letMeWrite'.tr():aspectList[i], style: TextStyle(color: textColor)),
                                   decoration: BoxDecoration(
                                     color: aspectList[i] == ratingAspect ? primaryColor.withValues(alpha: 0.3) : Colors.white,
                                     border: Border.all(color: aspectList[i] == ratingAspect ? primaryColor : textColor),
                                     borderRadius: BorderRadius.all(Radius.circular(50))
                                   ),
+                                  child: Text(aspectList[i]=='OTHER_OPTION'?'letMeWrite'.tr():aspectList[i], style: TextStyle(color: textColor)),
                                 ),
                                 onTap: (){
                                   setState(() {
@@ -156,7 +156,7 @@ class _MobHistoryRatingPageState extends State<MobHistoryRatingPage> {
                             data['aspect'] = controllerOptOther.text;
                           }
 
-                          var res = await ApiAuthProvider() .postData('/api/requestHistories/rateSatisfy', null, data, context);
+                          var res = await ApiAuthProvider() .postData('/api/requestHistories/rateSatisfy', null, data);
                           closeLoading(context);
                           if (res != null) {
                             Navigator.of(context).pop(tempRating);

+ 42 - 40
lib/src/layouts/mobile/menu_account.dart

@@ -12,6 +12,7 @@ import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
 import 'package:permission_handler/permission_handler.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 import '../../utils/C.dart';
 
@@ -24,7 +25,7 @@ class MobAccountPage extends StatefulWidget {
 
 class _MobAccountPageState extends State<MobAccountPage> {
   final AccountFunction accFunc = AccountFunction();
-  
+
   bool isDeniedNotifPermission = false;
   bool serDis = false;
   bool dnd = false;
@@ -35,7 +36,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
 
   @override
   void initState() {
-    Provider.of<UserModule>(context, listen: false).reset();
+    // Provider.of<UserModule>(context, listen: false).reset();
 
     accFunc.getUser(context);
     setToggle();
@@ -71,6 +72,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
 
   @override
   Widget build(BuildContext context) {
+    final UserModule userModule = Provider.of<UserModule>(context);
     codeOflang = {
       "id": 'bahasa'.tr(),
       "en": 'english'.tr(),
@@ -83,7 +85,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
       "hi": 'hindi'.tr(),
       "nl": 'dutch'.tr()
     };
-    return Provider.of<UserModule>(context).user().isNotEmpty ? Scaffold(
+    return userModule.user().isNotEmpty ? Scaffold(
       backgroundColor: backgroundColor,
       appBar: AppBar(
         elevation: 0,
@@ -108,12 +110,12 @@ class _MobAccountPageState extends State<MobAccountPage> {
                       child: Row(
                         children: [
                           CircleAvatar(
-                            child: Text(Provider.of<UserModule>(context).user()['name'][0].toString().toUpperCase(), style: TextStyle(color: Colors.white)),
                             backgroundColor: primaryColor,
                             radius: 25,
+                            child: Text(userModule.user()['name'][0].toString().toUpperCase(), style: TextStyle(color: Colors.white)),
                           ),
                           SizedBox(width: 16),
-                          Expanded(child: Text(Provider.of<UserModule>(context).user()['name'], style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: textColor), maxLines: 2, overflow: TextOverflow.ellipsis))
+                          Expanded(child: Text(userModule.user()['name'], style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: textColor), maxLines: 2, overflow: TextOverflow.ellipsis))
                         ],
                       ),
                     ),
@@ -125,16 +127,16 @@ class _MobAccountPageState extends State<MobAccountPage> {
                         children: [
                           Text('info_label'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
                           SizedBox(height: 20),
-                          textHorizontal('userId'.tr(), Provider.of<UserModule>(context).user()['relatedTo'] ?? Provider.of<UserModule>(context).user()['userId'], size: 16, opacity: 0.75),
+                          textHorizontal('userId'.tr(), userModule.user()['relatedTo'] ?? userModule.user()['userId'], size: 16, opacity: 0.75),
                           SizedBox(height: 12),
-                          textHorizontal('location'.tr(), Provider.of<UserModule>(context).user()['location'] ?? '-', size: 16, opacity: 0.75),
+                          textHorizontal('location'.tr(), userModule.user()['location'] ?? '-', size: 16, opacity: 0.75),
                           SizedBox(height: 12),
-                          textHorizontal('servantGroup'.tr(), Provider.of<UserModule>(context).user()['requestTypeListDescriptionMobile'] != null ? Provider.of<UserModule>(context).user()['requestTypeListDescriptionMobile'].toLowerCase() == 'semua' ? 'all'.tr() : Provider.of<UserModule>(context).user()['requestTypeListDescriptionMobile'] : '-', size: 16, opacity: 0.75),
+                          textHorizontal('servantGroup'.tr(), userModule.user()['requestTypeListDescriptionMobile'] != null ? userModule.user()['requestTypeListDescriptionMobile'].toLowerCase() == 'semua' ? 'all'.tr() : userModule.user()['requestTypeListDescriptionMobile'] : '-', size: 16, opacity: 0.75),
                         ],
                       ),
                     ),
-                    Provider.of<UserModule>(context).houseKeeping()?separator():Container(),
-                    Provider.of<UserModule>(context).houseKeeping()?Container(
+                    userModule.houseKeeping()?separator():Container(),
+                    userModule.houseKeeping()?Container(
                       padding: EdgeInsets.all(16),
                       child: Column(
                         crossAxisAlignment: CrossAxisAlignment.start,
@@ -147,35 +149,35 @@ class _MobAccountPageState extends State<MobAccountPage> {
                               Text(dnd?'active_dnd'.tr():'inactive_dnd'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
                               SizedBox(width: 5),
                               GestureDetector(
+                                onTap: userModule.user()['checkedIn']?(){
+                                  dialogConfirm(context: context, title: 'set_dnd_status'.tr(), text: dnd?'msg_change_inactive'.tr():'msg_change_active'.tr(), actionYes: ()async{
+                                    bool res = await accFunc.setDndStatus(userModule.user()['id'], dnd);
+                                    if(res){
+                                      setState(() => dnd = !dnd);
+                                    }
+                                  });
+                                }:null,
                                 child: Container(
-                                  decoration: BoxDecoration(border: Border.all(color: Provider.of<UserModule>(context).user()['checkedIn']?primaryColor:Colors.black38, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
+                                  decoration: BoxDecoration(border: Border.all(color: userModule.user()['checkedIn']?primaryColor:Colors.black38, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
                                   child: Row(
                                     children: [
                                       !dnd?Container(
-                                        width: 20, height: 20, decoration: BoxDecoration(color: Provider.of<UserModule>(context).user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                      ):Container(width: 20, height: 20),
+                                        width: 20, height: 20, decoration: BoxDecoration(color: userModule.user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                      ):SizedBox(width: 20, height: 20),
                                       dnd?Container(
-                                        width: 20, height: 20, decoration: BoxDecoration(color: Provider.of<UserModule>(context).user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                      ):Container(width: 20, height: 20),
+                                        width: 20, height: 20, decoration: BoxDecoration(color: userModule.user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                      ):SizedBox(width: 20, height: 20),
                                     ],
                                   ),
                                 ),
-                                onTap: Provider.of<UserModule>(context).user()['checkedIn']?(){
-                                  dialogConfirm(context: context, title: 'set_dnd_status'.tr(), text: dnd?'msg_change_inactive'.tr():'msg_change_active'.tr(), actionYes: ()async{
-                                    bool res = await accFunc.setDndStatus(context, dnd);
-                                    if(res){
-                                      setState(() => dnd = !dnd);
-                                    }
-                                  });
-                                }:null,
                               ),
                             ],
                           )
                         ],
                       ),
                     ):Container(),
-                    !Provider.of<UserModule>(context).user()['_profile']['isRoom']?separator():Container(),
-                    !Provider.of<UserModule>(context).user()['_profile']['isRoom']?Container(
+                    !userModule.user()['_profile']['isRoom']?separator():Container(),
+                    !userModule.user()['_profile']['isRoom']?Container(
                       padding: EdgeInsets.all(16),
                       child: Column(
                         crossAxisAlignment: CrossAxisAlignment.start,
@@ -193,10 +195,10 @@ class _MobAccountPageState extends State<MobAccountPage> {
                                     children: [
                                       !serDis?Container(
                                         width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                      ):Container(width: 20, height: 20),
+                                      ):SizedBox(width: 20, height: 20),
                                       serDis?Container(
                                         width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                      ):Container(width: 20, height: 20),
+                                      ):SizedBox(width: 20, height: 20),
                                     ],
                                   ),
                                 ),
@@ -262,10 +264,10 @@ class _MobAccountPageState extends State<MobAccountPage> {
                                       children: [
                                         !autoTranslate?Container(
                                           width: 20, height: 20, decoration: BoxDecoration(color: Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                        ):Container(width: 20, height: 20),
+                                        ):SizedBox(width: 20, height: 20),
                                         autoTranslate?Container(
                                           width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                        ):Container(width: 20, height: 20),
+                                        ):SizedBox(width: 20, height: 20),
                                       ],
                                     ),
                                   ),
@@ -301,9 +303,9 @@ class _MobAccountPageState extends State<MobAccountPage> {
                                 ],
                               ),
                             ),
-                            onTap: ()=>Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: MobPasswordPage(user: Provider.of<UserModule>(context, listen: false).user()))).then((value) {
-                              value??false;
-                              if (value) showSuccess(context, 'messagePassChanged'.tr());
+                            onTap: ()=> Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: MobPasswordPage(user: Provider.of<UserModule>(context, listen: false).user()))).then((value) {
+                              value ?? false;
+                              if (value) UIService.showSuccess('messagePassChanged'.tr());
                             }),
                           ),
                           divider(),
@@ -364,7 +366,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
                       onTap: (){
                         var size = MediaQuery.of(context).size.width - (MediaQuery.of(context).size.width/3);
                         var pid = U.getPidFromUrl(context.router.currentUrl);
-                        var qr_data = 'https://telnow.telmessenger.com/#/app/${pid}%23${U.getBaseUrl()}';
+                        var qrData = 'https://telnow.telmessenger.com/#/app/$pid%23${U.getBaseUrl()}';
                         showDialog(
                           context: context,
                           builder: (BuildContext context) {
@@ -378,7 +380,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
                                     width: size,
                                     height: size,
                                     child: QrImageView(
-                                      data: qr_data,
+                                      data: qrData,
                                       version: QrVersions.auto,
                                       gapless: true,
                                     ),
@@ -427,7 +429,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
           )
         ],
       ),
-    ) : Provider.of<UserModule>(context).resetData() ? RefreshPage(() {
+    ) : userModule.resetData() ? RefreshPage(() {
       Provider.of<UserModule>(context, listen: false).setResetData(false);
       accFunc.getUser(context);
     }) : _timeLimit ? showButton(context) : loadingTemplate(() {if(mounted) setState(()=>_timeLimit=true);},);
@@ -466,7 +468,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
             title: Text('bahasa'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()=='id'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != 'id' ? () async {
-              accFunc.switchLang(context, 'id', user);
+              accFunc.switchLang('id', user);
               Navigator.of(context).pop();
             } : null,
           ),
@@ -474,7 +476,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
             title: Text('english'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()=='en'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != 'en' ? () async {
-              accFunc.switchLang(context, 'en', user);
+              accFunc.switchLang('en', user);
               Navigator.of(context).pop();
             } : null,
           ),
@@ -483,7 +485,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
             title: Text(codeOflang[lang[2]], style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()==lang[2]?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != lang[2] ? () async {
-              accFunc.switchLang(context, 1, user);
+              accFunc.switchLang(1, user);
               Navigator.of(context).pop();
             } : null,
           ) : Container(),
@@ -491,7 +493,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
             title: Text(codeOflang[lang[3]], style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()==lang[3]?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != lang[3] ? () async {
-              accFunc.switchLang(context, 2, user);
+              accFunc.switchLang(2, user);
               Navigator.of(context).pop();
             } : null,
           ) : Container(),
@@ -499,7 +501,7 @@ class _MobAccountPageState extends State<MobAccountPage> {
             title: Text(codeOflang[lang[4]], style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()==lang[4]?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != lang[4] ? () async {
-              accFunc.switchLang(context, 3, user);
+              accFunc.switchLang(3, user);
               Navigator.of(context).pop();
             } : null,
           ) : Container(),

File diff suppressed because it is too large
+ 206 - 171
lib/src/layouts/mobile/menu_history.dart


File diff suppressed because it is too large
+ 98 - 78
lib/src/layouts/mobile/menu_home.dart


+ 1 - 1
lib/src/layouts/mobile/message_broadcast.dart

@@ -60,7 +60,7 @@ class _MobMessageBroadcastPageState extends State<MobMessageBroadcastPage> {
     setState(() {
       _isSending = true;
     });
-    var res = await _apiAuthProvider.postData('/api/messages/broadcast', params, data, context);
+    var res = await _apiAuthProvider.postData('/api/messages/broadcast', params, data);
     // print("res : ${res.toString()}");
     if (res != null && mounted) {
       Navigator.pop(context, 'pop');

+ 9 - 8
lib/src/layouts/mobile/message_chat.dart

@@ -59,7 +59,7 @@ class _MobMessageChatPageState extends State<MobMessageChatPage> {
 
   setReadStatus() async {
     if (!widget.askBroadcast) {
-      await apiAuthProvider.patchData('/api/messages/' + widget.messageId, {'readStatus': 'READ'}, context);
+      await apiAuthProvider.patchData('/api/messages/' + widget.messageId, {'readStatus': 'READ'});
     }
   }
 
@@ -83,7 +83,7 @@ class _MobMessageChatPageState extends State<MobMessageChatPage> {
     }
     if (!isLoad && !stopLoad) {
       setState(() => isLoad = true);
-      var mymess = await apiAuthProvider.getData('/api/messages/search/$url', {'isPaged': 'true', 'page': page.toString(), 'size': '20', 'filter': filter, 'tenant': widget.tenant}, context);
+      var mymess = await apiAuthProvider.getData('/api/messages/search/$url', {'isPaged': 'true', 'page': page.toString(), 'size': '20', 'filter': filter, 'tenant': widget.tenant});
       if (mymess.containsKey('_embedded') && mounted) {
         List data = mymess['_embedded']['myMessages'];
         for (int i = 0; i < data.length; i++) {
@@ -184,6 +184,7 @@ class _MobMessageChatPageState extends State<MobMessageChatPage> {
   }
 
   deleteCollection() {
+    debugPrint("popped");
     FirebaseFirestore.instance.collection("tmMessages").doc('messages').collection(idChat!).get().then((value) {
       for (DocumentSnapshot ds in value.docs) {
         ds.reference.delete();
@@ -233,7 +234,7 @@ class _MobMessageChatPageState extends State<MobMessageChatPage> {
   Widget build(BuildContext context) {
     var bodyWidth = U.bodyWidth(context);
     WidgetsBinding.instance.addPostFrameCallback((_) {
-      if (messageData.length > 0) {
+      if (messageData.isNotEmpty) {
         if (scrollController.position.maxScrollExtent > 0 && !isReverse) {
           setState(() => isReverse = true);
         }
@@ -244,11 +245,11 @@ class _MobMessageChatPageState extends State<MobMessageChatPage> {
       }
     });
 
-    return WillPopScope(
-      onWillPop: () async {
+    return PopScope(
+      onPopInvoked: (didPop) async {
         deleteCollection();
         Navigator.of(context).pop();
-        return true;
+        // return true;
       },
       child: Scaffold(
         backgroundColor: Colors.white,
@@ -258,10 +259,10 @@ class _MobMessageChatPageState extends State<MobMessageChatPage> {
           children: [
             divider(),
             U.getInternetStatus()?Expanded(
-              child: Container(width: bodyWidth,
+              child: SizedBox(width: bodyWidth,
                 child:LayoutBuilder(
                     builder: (context,constraint) {
-                      return messageData.length == 0 && !isAfterLoad ? loadingTemplateNoVoid() : Column(
+                      return messageData.isEmpty && !isAfterLoad ? loadingTemplateNoVoid() : Column(
                         children: [
                           Expanded(
                             child: SingleChildScrollView(

+ 2 - 2
lib/src/layouts/mobile/message_list.dart

@@ -12,8 +12,8 @@ import 'package:telnow_mobile_new/src/utils/provider.dart';
 import 'history_forum.dart';
 
 class MobMessageListPage extends StatefulWidget {
-  Map<String, dynamic>? user;
-  MobMessageListPage(this.user, {super.key});
+  final Map<String, dynamic>? user;
+  const MobMessageListPage(this.user, {super.key});
 
   @override
   State<MobMessageListPage> createState() => _MobMessageListPageState();

+ 3 - 3
lib/src/layouts/mobile/password.dart

@@ -11,8 +11,8 @@ import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferenc
 import 'package:telnow_mobile_new/src/utils/U.dart';
 
 class MobPasswordPage extends StatefulWidget {
-  Map<String, dynamic> user;
-  MobPasswordPage({required this.user, super.key});
+  final Map<String, dynamic> user;
+  const MobPasswordPage({required this.user, super.key});
 
   @override
   State<MobPasswordPage> createState() => _MobPasswordPageState();
@@ -88,7 +88,7 @@ class _MobPasswordPageState extends State<MobPasswordPage> {
                       'password':controllerNew.trim()
                     };
             
-                    var res = await apiAuthProvider.patchData('/api/informants/'+widget.user['id'].toString()+'/changePassword', data, context);
+                    var res = await apiAuthProvider.patchData('/api/informants/'+widget.user['id'].toString()+'/changePassword', data);
                     if(res != null){
                       String username = widget.user['_isRelated']?widget.user['relatedTo']:widget.user['userId'];
                       String? password = widget.user['_isRelated'] && widget.user['tenantCode']!=null && widget.user['tenantCode']!=''?widget.user['tenantCode']+' '+data['password']:data['password'];

+ 5 - 5
lib/src/layouts/mobile/request_create.dart

@@ -19,17 +19,17 @@ import 'package:telnow_mobile_new/src/utils/provider.dart';
 import 'package:toggle_switch/toggle_switch.dart';
 
 class MobReqCreatePage extends StatefulWidget {
-  Map<String, dynamic> user;
-  Map<String, dynamic> request;
+  final Map<String, dynamic> user;
+  final Map<String, dynamic> request;
   final bool fromSearch;
-  MobReqCreatePage({required this.user, required this.request, this.fromSearch = false, super.key});
+  const MobReqCreatePage({required this.user, required this.request, this.fromSearch = false, super.key});
 
   @override
   State<MobReqCreatePage> createState() => _MobReqCreatePageState();
 }
 
 getColorScheme(val){
-  var color;
+  Color color;
   switch (val){
     case 0: color = Color(0xFF4FB66C); break;
     case 50: color = Color(0xFFFFA800); break;
@@ -508,7 +508,7 @@ class _MobReqCreatePageState extends State<MobReqCreatePage> {
               ),
             ),
           ),
-          divider(),
+          divider(color: primaryColor, thickness: 2.0, opacity: 1.0),
           Container(
             alignment: Alignment.bottomCenter,
             width: U.bodyWidth(context), color: Colors.white,

+ 7 - 7
lib/src/layouts/mobile/request_select.dart

@@ -10,13 +10,13 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
 
 class MobReqSelectPage extends StatefulWidget {
-  String title;
-  String? groupCode;
-  String scope;
-  String? tenantCode;
-  String? placeholder;
-  Map<String, dynamic> user;
-  MobReqSelectPage({required this.title, this.groupCode, required this.scope, this.tenantCode, required this.user, this.placeholder, super.key});
+  final String title;
+  final String? groupCode;
+  final String scope;
+  final String? tenantCode;
+  final String? placeholder;
+  final Map<String, dynamic> user;
+  const MobReqSelectPage({required this.title, this.groupCode, required this.scope, this.tenantCode, required this.user, this.placeholder, super.key});
 
   @override
   State<MobReqSelectPage> createState() => _MobReqSelectPageState();

+ 5 - 5
lib/src/layouts/mobile/request_success.dart

@@ -12,10 +12,10 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:url_launcher/url_launcher.dart';
 
 class MobReqSuccessPage extends StatefulWidget {
-  Map<String, dynamic> user;
+  final Map<String, dynamic> user;
   final String ticketNo;
   final bool fromSearch;
-  MobReqSuccessPage({required this.user, required this.ticketNo, this.fromSearch = false, super.key});
+  const MobReqSuccessPage({required this.user, required this.ticketNo, this.fromSearch = false, super.key});
 
   @override
   State<MobReqSuccessPage> createState() => _MobReqSuccessPageState();
@@ -34,7 +34,7 @@ class _MobReqSuccessPageState extends State<MobReqSuccessPage> {
   }
 
   getMission() async{
-    var data = await apiAuthProvider.getData('/api/requestHistories/'+widget.ticketNo, null, context);
+    var data = await apiAuthProvider.getData('/api/requestHistories/'+widget.ticketNo, null);
     if(data != null) {
       if (data['datetimeScheduled'] != null && data['datetimeScheduled'] != '' && data['noteFormat'] == 'DATE') {
         var date = data['datetimeScheduled'];
@@ -259,9 +259,9 @@ class _MobReqSuccessPageState extends State<MobReqSuccessPage> {
 // --------------------------------------------------------------------------------------------------------------------------------------------------------
 
 class MobReqSuccessPendingPage extends StatefulWidget {
-  Map<String, dynamic> data;
+  final Map<String, dynamic> data;
   final bool fromSearch;
-  MobReqSuccessPendingPage({required this.data, this.fromSearch = false, super.key});
+  const MobReqSuccessPendingPage({required this.data, this.fromSearch = false, super.key});
 
   @override
   State<MobReqSuccessPendingPage> createState() => _MobReqSuccessPendingPageState();

+ 3 - 3
lib/src/layouts/web/banner_detail.dart

@@ -8,9 +8,9 @@ import 'package:url_launcher/url_launcher.dart';
 import 'package:youtube_player_iframe/youtube_player_iframe.dart';
 
 class WebBannerDetailPage extends StatefulWidget {
-  Map<String, dynamic> user;
-  Map<String, dynamic> data;
-  WebBannerDetailPage({required this.user, required this.data, super.key});
+  final Map<String, dynamic> user;
+  final Map<String, dynamic> data;
+  const WebBannerDetailPage({required this.user, required this.data, super.key});
 
   @override
   State<WebBannerDetailPage> createState() => _WebBannerDetailPageState();

+ 3 - 3
lib/src/layouts/web/history_detail.dart

@@ -11,8 +11,8 @@ import 'package:timelines_plus/timelines_plus.dart';
 import 'package:url_launcher/url_launcher.dart';
 
 class WebHistoryDetailPage extends StatefulWidget {
-  int index;
-  WebHistoryDetailPage({required this.index, super.key});
+  final int index;
+  const WebHistoryDetailPage({required this.index, super.key});
 
   @override
   State<WebHistoryDetailPage> createState() => _WebHistoryDetailPageState();
@@ -42,7 +42,7 @@ class _WebHistoryDetailPageState extends State<WebHistoryDetailPage> {
   }
 
   getData() async{
-    var res = await detFunc.getMission(context, Provider.of<HistoryModule>(context, listen: false).dataMisi()[widget.index]);
+    var res = await detFunc.getMission(context.locale.toString(), Provider.of<HistoryModule>(context, listen: false).dataRequests()[widget.index]);
     setState((){
       user = res['user'];
       list = res['list'];

+ 2 - 2
lib/src/layouts/web/history_detail_pending.dart

@@ -9,8 +9,8 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:timelines_plus/timelines_plus.dart';
 
 class WebHistoryDetailPendingPage extends StatefulWidget {
-  Map<String, dynamic> data;
-  WebHistoryDetailPendingPage({required this.data, super.key});
+  final Map<String, dynamic> data;
+  const WebHistoryDetailPendingPage({required this.data, super.key});
 
   @override
   State<WebHistoryDetailPendingPage> createState() => _WebHistoryDetailPendingPageState();

+ 4 - 4
lib/src/layouts/web/history_forum.dart

@@ -78,7 +78,7 @@ class _WebHistoryForumPageState extends State<WebHistoryForumPage> {
 
   getData() async {
     idChat = U.decodeBase64Url(_sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessCode)!) + '-' + widget.data['ticketNo'];
-    var res = await token.getUserData(context);
+    var res = await token.getUserData();
     if (res != null) {
       username = res['userId'];
       getMessage();
@@ -87,7 +87,7 @@ class _WebHistoryForumPageState extends State<WebHistoryForumPage> {
   }
 
   setAsRead(ticketNo) async {
-    var res = await apiAuthProvider.postData('/api/notifications/readMyForum/$ticketNo', null, null, context);
+    var res = await apiAuthProvider.postData('/api/notifications/readMyForum/$ticketNo', null, null);
     if (res != null) {
     }
   }
@@ -96,7 +96,7 @@ class _WebHistoryForumPageState extends State<WebHistoryForumPage> {
     if (!isLoad && !stopLoad) {
       setState(() => isLoad = true);
       setAsRead(widget.data['ticketNo']);
-      var mymess = await apiAuthProvider.getData('/api/messages/search/myMessages/' + idChat!, {'isPaged': 'true', 'page': page.toString(), 'size': '20'}, context);
+      var mymess = await apiAuthProvider.getData('/api/messages/search/myMessages/' + idChat!, {'isPaged': 'true', 'page': page.toString(), 'size': '20'});
       if (mymess.containsKey('_embedded')) {
         List data = mymess['_embedded']['myMessages'];
         for (int i = 0; i < data.length; i++) {
@@ -215,7 +215,7 @@ class _WebHistoryForumPageState extends State<WebHistoryForumPage> {
       scrollBottom = true;
     });
 
-    var res = await apiAuthProvider.postData('/api/messages', null, data, context);
+    var res = await apiAuthProvider.postData('/api/messages', null, data);
     if (res != null) {
       int index = messageData.indexWhere((element) => element['uniqueId'] == data['uniqueId']);
       setState(() {

+ 205 - 206
lib/src/layouts/web/menu_account.dart

@@ -12,6 +12,7 @@ import 'package:telnow_mobile_new/src/layouts/web/password.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
 import 'package:permission_handler/permission_handler.dart';
+import 'package:telnow_mobile_new/src/utils/ui_service.dart';
 
 import '../../utils/C.dart';
 
@@ -62,7 +63,6 @@ class _WebAccountPageState extends State<WebAccountPage> {
       lang = license['languages'] != null ? license['languages'].split(',') : [];
     }
     setState(() {
-      dnd = Provider.of<UserModule>(context, listen: false).dndStatus();
       serDis = U.servantDisplay();
       autoTranslate = U.autoTranslate();
     });
@@ -70,6 +70,9 @@ class _WebAccountPageState extends State<WebAccountPage> {
 
   @override
   Widget build(BuildContext context) {
+    final UserModule userModule = Provider.of<UserModule>(context);
+    dnd = userModule.dndStatus();
+
     codeOflang = {
       "id": 'bahasa'.tr(),
       "en": 'english'.tr(),
@@ -82,7 +85,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
       "hi": 'hindi'.tr(),
       "nl": 'dutch'.tr()
     };
-    return Provider.of<UserModule>(context).user().isNotEmpty ? Scaffold(
+    return userModule.user().isNotEmpty ? Scaffold(
       backgroundColor: backgroundColor,
       appBar: PreferredSize(preferredSize: Size.fromHeight(0), child: AppBar(elevation: 0, backgroundColor: primaryColor)),
       body: Column(
@@ -124,12 +127,12 @@ class _WebAccountPageState extends State<WebAccountPage> {
                                 child: Row(
                                   children: [
                                     CircleAvatar(
-                                      child: Text(Provider.of<UserModule>(context).user()['name'][0].toString().toUpperCase(), style: TextStyle(color: Colors.white)),
                                       backgroundColor: primaryColor,
                                       radius: 25,
+                                      child: Text(userModule.user()['name'][0].toString().toUpperCase(), style: TextStyle(color: Colors.white)),
                                     ),
                                     SizedBox(width: 16),
-                                    Expanded(child: Text(Provider.of<UserModule>(context).user()['name'], style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: textColor), maxLines: 2, overflow: TextOverflow.ellipsis))
+                                    Expanded(child: Text(userModule.user()['name'], style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: textColor), maxLines: 2, overflow: TextOverflow.ellipsis))
                                   ],
                                 ),
                               ),
@@ -141,11 +144,11 @@ class _WebAccountPageState extends State<WebAccountPage> {
                                   children: [
                                     Text('info_label'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
                                     SizedBox(height: 20),
-                                    textHorizontal('userId'.tr(), Provider.of<UserModule>(context).user()['relatedTo'] != null ? Provider.of<UserModule>(context).user()['relatedTo'] : Provider.of<UserModule>(context).user()['userId'], size: 16, opacity: 0.75),
+                                    textHorizontal('userId'.tr(), userModule.user()['relatedTo'] ?? userModule.user()['userId'], size: 16, opacity: 0.75),
                                     SizedBox(height: 12),
-                                    textHorizontal('location'.tr(), Provider.of<UserModule>(context).user()['location'] != null ? Provider.of<UserModule>(context).user()['location'] : '-', size: 16, opacity: 0.75),
+                                    textHorizontal('location'.tr(), userModule.user()['location'] ?? '-', size: 16, opacity: 0.75),
                                     SizedBox(height: 12),
-                                    textHorizontal('servantGroup'.tr(), Provider.of<UserModule>(context).user()['requestTypeListDescriptionMobile'] != null ? Provider.of<UserModule>(context).user()['requestTypeListDescriptionMobile'].toLowerCase() == 'semua' ? 'all'.tr() : Provider.of<UserModule>(context).user()['requestTypeListDescriptionMobile'] : '-', size: 16, opacity: 0.75),
+                                    textHorizontal('servantGroup'.tr(), userModule.user()['requestTypeListDescriptionMobile'] != null ? userModule.user()['requestTypeListDescriptionMobile'].toLowerCase() == 'semua' ? 'all'.tr() : userModule.user()['requestTypeListDescriptionMobile'] : '-', size: 16, opacity: 0.75),
                                   ],
                                 ),
                               ),
@@ -172,215 +175,213 @@ class _WebAccountPageState extends State<WebAccountPage> {
                       child: Column(
                         crossAxisAlignment: CrossAxisAlignment.start,
                         children: [
-                          Provider.of<UserModule>(context).houseKeeping()?Container(
-                            child: Column(
-                              crossAxisAlignment: CrossAxisAlignment.start,
-                              children: [
-                                Text('doNotDisturb'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
-                                SizedBox(height: 20),
-                                Row(
-                                  children: [
-                                    Expanded(child: Text('set_dnd_status'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16))),
-                                    Text(dnd?'active_dnd'.tr():'inactive_dnd'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
-                                    SizedBox(width: 5),
-                                    GestureDetector(
-                                      child: Container(
-                                        decoration: BoxDecoration(border: Border.all(color: Provider.of<UserModule>(context).user()['checkedIn']?primaryColor:Colors.black38, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
-                                        child: Row(
-                                          children: [
-                                            !dnd?Container(
-                                              width: 20, height: 20, decoration: BoxDecoration(color: Provider.of<UserModule>(context).user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                            ):Container(width: 20, height: 20),
-                                            dnd?Container(
-                                              width: 20, height: 20, decoration: BoxDecoration(color: Provider.of<UserModule>(context).user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                            ):Container(width: 20, height: 20),
-                                          ],
-                                        ),
-                                      ),
-                                      onTap: Provider.of<UserModule>(context).user()['checkedIn']?(){
-                                        dialogConfirm(context: context, title: 'set_dnd_status'.tr(), text: dnd?'msg_change_inactive'.tr():'msg_change_active'.tr(), actionYes: ()async{
-                                          bool res = await accFunc.setDndStatus(context, dnd);
-                                          if(res){
-                                            setState(() => dnd = !dnd);
-                                          }
+                          if (userModule.houseKeeping()) Column(
+                            crossAxisAlignment: CrossAxisAlignment.start,
+                            children: [
+                              Text('doNotDisturb'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
+                              SizedBox(height: 20),
+                              Row(
+                                children: [
+                                  Expanded(child: Text('set_dnd_status'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16))),
+                                  Text(dnd?'active_dnd'.tr():'inactive_dnd'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
+                                  SizedBox(width: 5),
+                                  GestureDetector(
+                                    onTap: (){
+                                      if(userModule.user()['checkedIn']){
+                                        dialogConfirm(
+                                          context: context,
+                                          title: 'set_dnd_status'.tr(),
+                                          text: dnd ? 'msg_change_inactive'.tr() : 'msg_change_active'.tr(),
+                                          actionYes: ()async{
+                                            bool res = await accFunc.setDndStatus(userModule.user()['id'], dnd);
+                                            if(res){
+                                              userModule.setDndStatus(!dnd);
+                                            }
                                         });
-                                      }:null,
+                                      }
+                                    },
+                                    child: Container(
+                                      decoration: BoxDecoration(border: Border.all(color: userModule.user()['checkedIn']?primaryColor:Colors.black38, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
+                                      child: Row(
+                                        children: [
+                                          !dnd?Container(
+                                            width: 20, height: 20, decoration: BoxDecoration(color: userModule.user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                          ):SizedBox(width: 20, height: 20),
+                                          dnd?Container(
+                                            width: 20, height: 20, decoration: BoxDecoration(color: userModule.user()['checkedIn']?primaryColor:Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                          ):SizedBox(width: 20, height: 20),
+                                        ],
+                                      ),
                                     ),
-                                  ],
-                                )
-                              ],
-                            ),
-                          ):Container(),
-                          Provider.of<UserModule>(context).houseKeeping()?Padding(padding: EdgeInsets.symmetric(vertical: 16), child: separator()):Container(),
-
-                          !Provider.of<UserModule>(context).user()['_profile']['isRoom']?Container(
-                            child: Column(
-                              crossAxisAlignment: CrossAxisAlignment.start,
-                              children: [
-                                Text('display_menu'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
-                                SizedBox(height: 20),
-                                Row(
+                                  ),
+                                ],
+                              )
+                            ],
+                          ),
+                          if(userModule.houseKeeping()) Padding(padding: EdgeInsets.symmetric(vertical: 16), child: separator()),
+                          if(!userModule.user()['_profile']['isRoom']) Column(
+                            crossAxisAlignment: CrossAxisAlignment.start,
+                            children: [
+                              Text('display_menu'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
+                              SizedBox(height: 20),
+                              Row(
+                                children: [
+                                  Expanded(child: Text(serDis?'ser_group'.tr():'req_group'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16))),
+                                  SizedBox(width: 5),
+                                  GestureDetector(
+                                    child: Container(
+                                      decoration: BoxDecoration(border: Border.all(color: primaryColor, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
+                                      child: Row(
+                                        children: [
+                                          !serDis?Container(
+                                            width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                          ):SizedBox(width: 20, height: 20),
+                                          serDis?Container(
+                                            width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                          ):SizedBox(width: 20, height: 20),
+                                        ],
+                                      ),
+                                    ),
+                                    onTap: (){
+                                      setState(() {
+                                        if(serDis){
+                                          U.setServantDisplay(false);
+                                          serDis = false;
+                                        }
+                                        else{
+                                          U.setServantDisplay(true);
+                                          serDis = true;
+                                        }
+                                      });
+                                    },
+                                  ),
+                                ],
+                              )
+                            ],
+                          ),
+                          if(!userModule.user()['_profile']['isRoom']) Padding(padding: EdgeInsets.symmetric(vertical: 16), child: separator()),
+                          Column(
+                            crossAxisAlignment: CrossAxisAlignment.start,
+                            children: [
+                              Text('setting'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
+                              SizedBox(height: 6),
+                              GestureDetector(
+                                child: Container(
+                                  color: Colors.white,
+                                  padding: EdgeInsets.symmetric(vertical: 16),
+                                  child: Row(
+                                    children: [
+                                      U.iconsax('bold/global', color: textColor.withValues(alpha: 0.75)),
+                                      SizedBox(width: 16),
+                                      Expanded(child: Text('language'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16), overflow: TextOverflow.ellipsis)),
+                                      Text(codeOflang[context.locale.toString()]!, style: TextStyle(color: textColor)),
+                                      SizedBox(width: 12),
+                                      U.iconsax('arrow-right-3', size: 16, color: textColor.withValues(alpha: 0.75)),
+                                    ],
+                                  ),
+                                ),
+                                onTap: () => changeLang(context, Provider.of<UserModule>(context, listen: false).user()),
+                              ),
+                              divider(),
+                              Container(
+                                color: Colors.white,
+                                padding: EdgeInsets.symmetric(vertical: 16),
+                                child: Row(
                                   children: [
-                                    Expanded(child: Text(serDis?'ser_group'.tr():'req_group'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16))),
+                                    Icon(Icons.g_translate_rounded, color: textColor.withValues(alpha: 0.75)),
+                                    SizedBox(width: 16),
+                                    Expanded(child: Text('auto_translate'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16), overflow: TextOverflow.ellipsis)),
+                                    Text(autoTranslate?'ON':'OFF', style: TextStyle(color: textColor)),
                                     SizedBox(width: 5),
                                     GestureDetector(
                                       child: Container(
-                                        decoration: BoxDecoration(border: Border.all(color: primaryColor, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
+                                        decoration: BoxDecoration(border: Border.all(color: autoTranslate?primaryColor:Colors.black38, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
                                         child: Row(
                                           children: [
-                                            !serDis?Container(
-                                              width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                            ):Container(width: 20, height: 20),
-                                            serDis?Container(
+                                            !autoTranslate?Container(
+                                              width: 20, height: 20, decoration: BoxDecoration(color: Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
+                                            ):SizedBox(width: 20, height: 20),
+                                            autoTranslate?Container(
                                               width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                            ):Container(width: 20, height: 20),
+                                            ):SizedBox(width: 20, height: 20),
                                           ],
                                         ),
                                       ),
                                       onTap: (){
                                         setState(() {
-                                          if(serDis){
-                                            U.setServantDisplay(false);
-                                            serDis = false;
+                                          if(autoTranslate){
+                                            U.setAutoTranslate(false);
+                                            autoTranslate = false;
                                           }
                                           else{
-                                            U.setServantDisplay(true);
-                                            serDis = true;
+                                            U.setAutoTranslate(true);
+                                            autoTranslate = true;
                                           }
                                         });
                                       },
                                     ),
                                   ],
-                                )
-                              ],
-                            ),
-                          ):Container(),
-                          !Provider.of<UserModule>(context).user()['_profile']['isRoom']?Padding(padding: EdgeInsets.symmetric(vertical: 16), child: separator()):Container(),
-
-                          Container(
-                            child: Column(
-                              crossAxisAlignment: CrossAxisAlignment.start,
-                              children: [
-                                Text('setting'.tr(), style: TextStyle(color: textColor, fontSize: 16, fontWeight: FontWeight.w500)),
-                                SizedBox(height: 6),
-                                GestureDetector(
-                                  child: Container(
-                                    color: Colors.white,
-                                    padding: EdgeInsets.symmetric(vertical: 16),
-                                    child: Row(
-                                      children: [
-                                        U.iconsax('bold/global', color: textColor.withValues(alpha: 0.75)),
-                                        SizedBox(width: 16),
-                                        Expanded(child: Text('language'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16), overflow: TextOverflow.ellipsis)),
-                                        Text(codeOflang[context.locale.toString()]!, style: TextStyle(color: textColor)),
-                                        SizedBox(width: 12),
-                                        U.iconsax('arrow-right-3', size: 16, color: textColor.withValues(alpha: 0.75)),
-                                      ],
-                                    ),
-                                  ),
-                                  onTap: () => changeLang(context, Provider.of<UserModule>(context, listen: false).user()),
                                 ),
-                                divider(),
-                                Container(
+                              ),
+                              divider(),
+                              GestureDetector(
+                                child: Container(
                                   color: Colors.white,
                                   padding: EdgeInsets.symmetric(vertical: 16),
                                   child: Row(
                                     children: [
-                                      Icon(Icons.g_translate_rounded, color: textColor.withValues(alpha: 0.75)),
+                                      U.iconsax('bold/key', color: textColor.withValues(alpha: 0.75)),
                                       SizedBox(width: 16),
-                                      Expanded(child: Text('auto_translate'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16), overflow: TextOverflow.ellipsis)),
-                                      Text(autoTranslate?'ON':'OFF', style: TextStyle(color: textColor)),
-                                      SizedBox(width: 5),
-                                      GestureDetector(
-                                        child: Container(
-                                          decoration: BoxDecoration(border: Border.all(color: autoTranslate?primaryColor:Colors.black38, width: 1.5), borderRadius: BorderRadius.all(Radius.circular(50))),
-                                          child: Row(
-                                            children: [
-                                              !autoTranslate?Container(
-                                                width: 20, height: 20, decoration: BoxDecoration(color: Colors.black38, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                              ):Container(width: 20, height: 20),
-                                              autoTranslate?Container(
-                                                width: 20, height: 20, decoration: BoxDecoration(color: primaryColor, borderRadius: BorderRadius.all(Radius.circular(20))),
-                                              ):Container(width: 20, height: 20),
-                                            ],
-                                          ),
-                                        ),
-                                        onTap: (){
-                                          setState(() {
-                                            if(autoTranslate){
-                                              U.setAutoTranslate(false);
-                                              autoTranslate = false;
-                                            }
-                                            else{
-                                              U.setAutoTranslate(true);
-                                              autoTranslate = true;
-                                            }
-                                          });
-                                        },
-                                      ),
+                                      Text('password'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16)),
+                                      Expanded(child: Text('settingPasswordText'.tr(), style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis, textAlign: TextAlign.end)),
+                                      SizedBox(width: 12),
+                                      U.iconsax('arrow-right-3', size: 16, color: textColor.withValues(alpha: 0.75)),
                                     ],
                                   ),
                                 ),
-                                divider(),
-                                GestureDetector(
-                                  child: Container(
-                                    color: Colors.white,
-                                    padding: EdgeInsets.symmetric(vertical: 16),
-                                    child: Row(
-                                      children: [
-                                        U.iconsax('bold/key', color: textColor.withValues(alpha: 0.75)),
-                                        SizedBox(width: 16),
-                                        Text('password'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16)),
-                                        Expanded(child: Text('settingPasswordText'.tr(), style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis, textAlign: TextAlign.end)),
-                                        SizedBox(width: 12),
-                                        U.iconsax('arrow-right-3', size: 16, color: textColor.withValues(alpha: 0.75)),
-                                      ],
-                                    ),
-                                  ),
-                                  onTap: ()=>Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: WebPasswordPage(user: Provider.of<UserModule>(context, listen: false).user()))).then((value) {
-                                    value??false;
-                                    if (value) showSuccess(context, 'messagePassChanged'.tr());
-                                  }),
-                                ),
-                                divider(),
-                                GestureDetector(
-                                  child: Container(
-                                    color: Colors.white,
-                                    padding: EdgeInsets.symmetric(vertical: 16),
-                                    child: Row(
-                                      children: [
-                                        U.iconsax('bold/logout', color: textColor.withValues(alpha: 0.75)),
-                                        SizedBox(width: 16),
-                                        Expanded(child: Text('logout'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16), overflow: TextOverflow.ellipsis)),
-                                        U.iconsax('arrow-right-3', size: 16, color: textColor.withValues(alpha: 0.75)),
-                                      ],
-                                    ),
+                                onTap: ()=>Navigator.push(context, PageTransition(type: PageTransitionType.rightToLeft, child: WebPasswordPage(user: Provider.of<UserModule>(context, listen: false).user()))).then((value) {
+                                  value??false;
+                                  if (value) UIService.showSuccess('messagePassChanged'.tr());
+                                }),
+                              ),
+                              divider(),
+                              GestureDetector(
+                                child: Container(
+                                  color: Colors.white,
+                                  padding: EdgeInsets.symmetric(vertical: 16),
+                                  child: Row(
+                                    children: [
+                                      U.iconsax('bold/logout', color: textColor.withValues(alpha: 0.75)),
+                                      SizedBox(width: 16),
+                                      Expanded(child: Text('logout'.tr(), style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 16), overflow: TextOverflow.ellipsis)),
+                                      U.iconsax('arrow-right-3', size: 16, color: textColor.withValues(alpha: 0.75)),
+                                    ],
                                   ),
-                                  onTap: (){
-                                    showDialog(
-                                      context: context,
-                                      builder: (BuildContext context) {
-                                        return AlertDialog(
-                                          title: Text("logout".tr()),
-                                          content: Text("textLogout".tr()),
-                                          actions: <Widget>[
-                                            TextButton(
-                                              child: Text("buttonNo".tr()),
-                                              onPressed: () {
-                                                Navigator.of(context).pop();
-                                              },
-                                            ),
-                                            TextButton(
-                                                onPressed: () => accFunc.logoutAction(context),
-                                                child: Text("buttonYes".tr())),
-                                          ],
-                                        );
-                                      },
-                                    );
-                                  },
                                 ),
-                              ],
-                            ),
+                                onTap: (){
+                                  showDialog(
+                                    context: context,
+                                    builder: (BuildContext context) {
+                                      return AlertDialog(
+                                        title: Text("logout".tr()),
+                                        content: Text("textLogout".tr()),
+                                        actions: <Widget>[
+                                          TextButton(
+                                            child: Text("buttonNo".tr()),
+                                            onPressed: () {
+                                              Navigator.of(context).pop();
+                                            },
+                                          ),
+                                          TextButton(
+                                              onPressed: () => accFunc.logoutAction(context),
+                                              child: Text("buttonYes".tr())),
+                                        ],
+                                      );
+                                    },
+                                  );
+                                },
+                              ),
+                            ],
                           ),
                           separator(),
                           GestureDetector(
@@ -399,7 +400,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
                             onTap: (){
                               var size = MediaQuery.of(context).size.height/2;
                               var pid = U.getPidFromUrl(context.router.currentUrl);
-                              var qr_data = 'https://telnow.telmessenger.com/#/app/${pid}%23${U.getBaseUrl()}';
+                              var qrData = 'https://telnow.telmessenger.com/#/app/$pid%23${U.getBaseUrl()}';
                               showDialog(
                                 context: context,
                                 builder: (BuildContext context) {
@@ -413,7 +414,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
                                           width: size,
                                           height: size,
                                           child: QrImageView(
-                                            data: qr_data,
+                                            data: qrData,
                                             version: QrVersions.auto,
                                             gapless: true,
                                           ),
@@ -428,21 +429,19 @@ class _WebAccountPageState extends State<WebAccountPage> {
                             },
                           ),
                           isDeniedNotifPermission ? Padding(padding: EdgeInsets.symmetric(vertical: 16), child: separator()) : Container(),
-                          isDeniedNotifPermission ? Container(
-                            child: GestureDetector(
-                              child: Column(
-                                crossAxisAlignment: CrossAxisAlignment.start,
-                                children: [
-                                  Text("not_allow_permission".tr()),
-                                  SizedBox(height: 8,),
-                                  Text("tap_here".tr(), style: TextStyle(color: Color(0xFF198AF2)),),
-                                ],
-                              ),
-                              onTap: () => AppSettings.openAppSettings(type: AppSettingsType.notification).then((value) async {
-                                // print("after open setting");
-                                // checkPermission(2);
-                              }),
+                          isDeniedNotifPermission ? GestureDetector(
+                            child: Column(
+                              crossAxisAlignment: CrossAxisAlignment.start,
+                              children: [
+                                Text("not_allow_permission".tr()),
+                                SizedBox(height: 8,),
+                                Text("tap_here".tr(), style: TextStyle(color: Color(0xFF198AF2)),),
+                              ],
                             ),
+                            onTap: () => AppSettings.openAppSettings(type: AppSettingsType.notification).then((value) async {
+                              // print("after open setting");
+                              // checkPermission(2);
+                            }),
                           ) : Container(),
                         ],
                       ),
@@ -454,7 +453,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
           )
         ],
       ),
-    ) : Provider.of<UserModule>(context).resetData() ? RefreshPage(() {
+    ) : userModule.resetData() ? RefreshPage(() {
       Provider.of<UserModule>(context, listen: false).setResetData(false);
       accFunc.getUser(context);
     }) : _timeLimit ? showButton(context) : loadingTemplate(() {if(mounted) setState(()=>_timeLimit=true);},);
@@ -495,7 +494,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
             title: Text('bahasa'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()=='id'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != 'id' ? () async {
-              accFunc.switchLang(context, 'id', user);
+              accFunc.switchLang('id', user);
               Navigator.of(cd).pop();
             } : null,
           ),
@@ -503,7 +502,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
             title: Text('english'.tr(), style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()=='en'?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != 'en' ? () async {
-              accFunc.switchLang(context, 'en', user);
+              accFunc.switchLang('en', user);
               Navigator.of(cd).pop();
             } : null,
           ),
@@ -511,7 +510,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
             title: Text(codeOflang[lang[2]], style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()==lang[2]?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != lang[2] ? () async {
-              accFunc.switchLang(context, 1, user);
+              accFunc.switchLang(1, user);
               Navigator.of(cd).pop();
             } : null,
           ) : Container(),
@@ -519,7 +518,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
             title: Text(codeOflang[lang[3]], style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()==lang[3]?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != lang[3] ? () async {
-              accFunc.switchLang(context, 2, user);
+              accFunc.switchLang(2, user);
               Navigator.of(cd).pop();
             } : null,
           ) : Container(),
@@ -527,7 +526,7 @@ class _WebAccountPageState extends State<WebAccountPage> {
             title: Text(codeOflang[lang[4]], style: TextStyle(color: textColor, fontSize: 14)),
             trailing: Icon(context.locale.toString()==lang[4]?Icons.radio_button_checked:Icons.radio_button_off, color: primaryColor, size: 18),
             onTap: context.locale.toString() != lang[4] ? () async {
-              accFunc.switchLang(context, 3, user);
+              accFunc.switchLang(3, user);
               Navigator.of(cd).pop();
             } : null,
           ) : Container(),

+ 133 - 129
lib/src/layouts/web/menu_history.dart

@@ -21,6 +21,8 @@ class WebHistoryPage extends StatefulWidget {
 class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStateMixin {
   final HistoryFunction hisFunc = HistoryFunction();
   late AnimationController _animationController;
+  late final HistoryModule historyModule = Provider.of<HistoryModule>(context, listen: false);
+  late final UserModule userModule = Provider.of<UserModule>(context, listen: false);
 
   var rating = [
     {'key': 1, 'image': "assets/image/icon/very_dissatisfied.png", 'label': 'disatisfied'.tr()},
@@ -34,12 +36,12 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
   @override
   void initState() {
     // print(U.newServerVersion(1716279633));
-    Provider.of<HistoryModule>(context, listen: false).reset();
-    _animationController = new AnimationController(vsync: this, duration: Duration(seconds: 1));
+    historyModule.reset();
+    _animationController = AnimationController(vsync: this, duration: Duration(seconds: 1));
     _animationController.repeat(reverse: true);
 
     WidgetsBinding.instance.addPostFrameCallback((_) {
-      hisFunc.getActiveForum(context);
+      hisFunc.getActiveForum();
     });
     // TODO: implement initState
     super.initState();
@@ -53,9 +55,11 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
 
   @override
   Widget build(BuildContext context) {
-    return Provider.of<UserModule>(context).resetData() ? RefreshPage(() {
-      Provider.of<UserModule>(context, listen: false).setResetData(false);
-      hisFunc.getActiveForum(context);
+    final HistoryModule historyModuleListen = Provider.of<HistoryModule>(context);
+    final UserModule userModuleListen = Provider.of<UserModule>(context);
+    return userModuleListen.resetData() ? RefreshPage(() {
+      userModule.setResetData(false);
+      hisFunc.getActiveForum();
     }) : Scaffold(
       backgroundColor: backgroundColor,
       appBar: PreferredSize(preferredSize: Size.fromHeight(0), child: AppBar(elevation: 0, backgroundColor: primaryColor)),
@@ -67,9 +71,10 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
               mainAxisAlignment: MainAxisAlignment.spaceBetween,
               children: [
                 Text('history'.tr(), style: TextStyle(color: textColor, fontSize: 17, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
-                Provider.of<HistoryModule>(context).multiSelectMode() ? GestureDetector(
+                historyModuleListen.multiSelectMode() ? GestureDetector(
                   child: Container(
                     padding: EdgeInsets.fromLTRB(16, 9, 16, 9),
+                    decoration: BoxDecoration(color: primaryColor.withValues(alpha: 0.1), border: Border.all(color: primaryColor), borderRadius: BorderRadius.all(Radius.circular(50))),
                     child: Row(
                       children: [
                         Text('delete'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)),
@@ -77,7 +82,6 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                         U.iconsax('trash', color: primaryColor, size: 20)
                       ],
                     ),
-                    decoration: BoxDecoration(color: primaryColor.withValues(alpha: 0.1), border: Border.all(color: primaryColor), borderRadius: BorderRadius.all(Radius.circular(50))),
                   ),
                   onTap: () => hisFunc.deleteData(context),
                 ) : GestureDetector(
@@ -102,52 +106,52 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                     GestureDetector(
                       child: Container(
                         padding: EdgeInsets.symmetric(vertical: 5, horizontal: 16),
-                        child: Text('ongoing'.tr(), style: TextStyle(color: Provider.of<HistoryModule>(context).selectedFilter() == 1 ? textColor : textColor.withValues(alpha: 0.65), fontSize: 16)),
-                        decoration: BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: Provider.of<HistoryModule>(context).selectedFilter() == 1 ? primaryColor : primaryColor.withValues(alpha: 0.3), width: Provider.of<HistoryModule>(context).selectedFilter() == 1 ? 2 : 1))),
+                        decoration: BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: historyModuleListen.activeTab() == 1 ? primaryColor : primaryColor.withValues(alpha: 0.3), width: historyModuleListen.activeTab() == 1 ? 2 : 1))),
+                        child: Text('ongoing'.tr(), style: TextStyle(color: historyModuleListen.activeTab() == 1 ? textColor : textColor.withValues(alpha: 0.65), fontSize: 16)),
                       ),
                       onTap: () {
-                        if (!Provider.of<HistoryModule>(context, listen: false).isLoadHistory()) {
-                          Provider.of<HistoryModule>(context, listen: false).setPressAttention(0);
-                          Provider.of<HistoryModule>(context, listen: false).setSelectedFilter(1);
-                          hisFunc.onRefresh(context);
+                        if (!historyModule.isLoadHistory()) {
+                          historyModule.setSelectedFilter(0);
+                          historyModule.setActiveTab(HistoryTab.ongoing);
+                          hisFunc.onRefresh();
                         }
                       },
                     ),
                     GestureDetector(
                       child: Container(
                         padding: EdgeInsets.symmetric(vertical: 5, horizontal: 16),
-                        child: Text('done'.tr(), style: TextStyle(color: Provider.of<HistoryModule>(context).selectedFilter() == 0 ? textColor : textColor.withValues(alpha: 0.65), fontSize: 16)),
-                        decoration: BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: Provider.of<HistoryModule>(context).selectedFilter() == 0 ? primaryColor : primaryColor.withValues(alpha: 0.3), width: Provider.of<HistoryModule>(context).selectedFilter() == 0 ? 2 : 1))),
+                        decoration: BoxDecoration(color: Colors.white, border: Border(bottom: BorderSide(color: historyModuleListen.activeTab() == 0 ? primaryColor : primaryColor.withValues(alpha: 0.3), width: historyModuleListen.activeTab() == 0 ? 2 : 1))),
+                        child: Text('done'.tr(), style: TextStyle(color: historyModuleListen.activeTab() == 0 ? textColor : textColor.withValues(alpha: 0.65), fontSize: 16)),
                       ),
                       onTap: () {
-                        if (!Provider.of<HistoryModule>(context, listen: false).isLoadHistory()) {
-                          Provider.of<HistoryModule>(context, listen: false).setPressAttention(0);
-                          Provider.of<HistoryModule>(context, listen: false).setSelectedFilter(0);
-                          hisFunc.onRefresh(context);
+                        if (!historyModule.isLoadHistory()) {
+                          historyModule.setSelectedFilter(0);
+                          historyModule.setActiveTab(HistoryTab.done);
+                          hisFunc.onRefresh();
                         }
                       },
                     ),
                   ],
                 ),
                 SizedBox(height: 20),
-                Provider.of<HistoryModule>(context).selectedFilter() == 0 ? doneFilter() : ongoingFilter(),
+                historyModuleListen.activeTab() == 0 ? doneFilter() : ongoingFilter(),
               ],
             ),
           ),
           Expanded(
-            child: Provider.of<UserModule>(context).user().isNotEmpty ? Container(
+            child: userModuleListen.user().isNotEmpty ? Container(
               width: double.infinity,
               padding: EdgeInsets.symmetric(horizontal: 100),
               child: LazyLoadScrollView(
-                onEndOfPage: () => hisFunc.getMission(context),
+                onEndOfPage: () => hisFunc.getMission(),
                 scrollOffset: 500,
                 child: EasyRefresh(
                   header: MaterialHeader(clamping: true, color: primaryColor),
-                  onRefresh: ()=>hisFunc.onRefresh(context),
-                  child: Provider.of<HistoryModule>(context).selectedFilter() == 0 ? doneContainer() : ongoingContainer(),
+                  onRefresh: ()=>hisFunc.onRefresh(),
+                  child: historyModuleListen.activeTab() == 0 ? doneContainer() : ongoingContainer(),
                 ),
               ),
-            ) : Provider.of<UserModule>(context).resetData() ? Container() : _timeLimit ? showButton(context) : loadingTemplate(() {if(mounted) setState(()=>_timeLimit=true);},),
+            ) : userModuleListen.resetData() ? Container() : _timeLimit ? showButton(context) : loadingTemplate(() {if(mounted) setState(()=>_timeLimit=true);},),
           )
         ],
       ),
@@ -155,6 +159,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
   }
 
   Widget ongoingFilter() {
+    final HistoryModule historyModuleListen = Provider.of<HistoryModule>(context);
     return SingleChildScrollView(
       scrollDirection: Axis.horizontal,
       child: Row(
@@ -163,20 +168,20 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
             child: Container(
               margin: EdgeInsets.only(right: 16),
               padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
-              child: Text(hisFunc.ongoingList[i]['title'], style: TextStyle(color: Provider.of<HistoryModule>(context).checkPressAttention(hisFunc.ongoingList[i]['value']) ? Colors.white : textColor, fontSize: 16)),
               decoration: BoxDecoration(
-                  color: Provider.of<HistoryModule>(context).checkPressAttention(hisFunc.ongoingList[i]['value']) ? primaryColor : Color(0xffF2F8F7),
-                  border: Border.all(color: Provider.of<HistoryModule>(context).checkPressAttention(hisFunc.ongoingList[i]['value']) ? primaryColor : Color(0xffBEC1C1)),
+                  color: historyModuleListen.checkSelectedFilter(hisFunc.ongoingList[i]['value']) ? primaryColor : Color(0xffF2F8F7),
+                  border: Border.all(color: historyModuleListen.checkSelectedFilter(hisFunc.ongoingList[i]['value']) ? primaryColor : Color(0xffBEC1C1)),
                   borderRadius: BorderRadius.all(Radius.circular(50))),
+              child: Text(hisFunc.ongoingList[i]['title'], style: TextStyle(color: historyModuleListen.checkSelectedFilter(hisFunc.ongoingList[i]['value']) ? Colors.white : textColor, fontSize: 16)),
             ),
             onTap: () {
-              if (!Provider.of<HistoryModule>(context, listen: false).isLoadHistory()) {
-                if (Provider.of<HistoryModule>(context, listen: false).checkPressAttention(hisFunc.ongoingList[i]['value'])) {
-                  Provider.of<HistoryModule>(context, listen: false).setPressAttention(0);
-                  hisFunc.onRefresh(context);
+              if (!historyModule.isLoadHistory()) {
+                if (historyModule.checkSelectedFilter(hisFunc.ongoingList[i]['value'])) {
+                  historyModule.setSelectedFilter(0);
+                  hisFunc.onRefresh();
                 } else {
-                  Provider.of<HistoryModule>(context, listen: false).setPressAttention(hisFunc.ongoingList[i]['value']);
-                  hisFunc.onRefresh(context);
+                  historyModule.setSelectedFilter(hisFunc.ongoingList[i]['value']);
+                  hisFunc.onRefresh();
                 }
               }
             },
@@ -187,11 +192,12 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
   }
 
   Widget ongoingContainer() {
+    final HistoryModule historyModuleListen = Provider.of<HistoryModule>(context);
     return SingleChildScrollView(
       child: Column(
         children: [
           U.newServerVersion(1709864293) && !U.getInternetStatus() ? Column(
-            children: List.generate(Provider.of<HistoryModule>(context).dataPending().length, (i) {
+            children: List.generate(historyModuleListen.dataPending().length, (i) {
               return GestureDetector(
                 child: Container(
                   width: double.infinity,
@@ -203,7 +209,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                       Row(
                         mainAxisAlignment: MainAxisAlignment.spaceBetween,
                         children: [
-                          Text(convertDate(Provider.of<HistoryModule>(context).dataPending()[i]['datetimeRequest'], context.locale.toString()), style: TextStyle(color: primaryColor, fontSize: 14)),
+                          Text(convertDate(historyModuleListen.dataPending()[i]['datetimeRequest'], context.locale.toString()), style: TextStyle(color: primaryColor, fontSize: 14)),
                           renderStatus('sending'.tr(), Color(0xff02C539).withValues(alpha: 0.2))
                         ],
                       ),
@@ -212,22 +218,22 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                       SizedBox(height: 10),
                       Row(
                         children: [
-                          imageTiles(imageUrl: Provider.of<HistoryModule>(context).dataPending()[i]['image'] ?? "null"),
+                          imageTiles(imageUrl: historyModuleListen.dataPending()[i]['image'] ?? "null"),
                           SizedBox(width: 25),
                           Expanded(
                             child: Column(
                               crossAxisAlignment: CrossAxisAlignment.start,
                               children: [
-                                Text(Provider.of<HistoryModule>(context).dataPending()[i]['title'], style: TextStyle(color: textColor, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
+                                Text(historyModuleListen.dataPending()[i]['title'], style: TextStyle(color: textColor, fontWeight: FontWeight.w500), overflow: TextOverflow.ellipsis),
                                 SizedBox(height: 10),
-                                Text(Provider.of<HistoryModule>(context).dataPending()[i][U.langColumn(context, 'sub')], style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
+                                Text(historyModuleListen.dataPending()[i][U.langColumn(context, 'sub')], style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
                                 SizedBox(height: 5),
-                                Text(Provider.of<HistoryModule>(context).dataPending()[i][U.langColumn(context, 'desc')], style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
+                                Text(historyModuleListen.dataPending()[i][U.langColumn(context, 'desc')], style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
                                 dashed(top: 10, bottom: 10),
                                 Row(
                                   mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                   children: [
-                                    Text('location'.tr()+': '+Provider.of<HistoryModule>(context).dataPending()[i]['location'], style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
+                                    Text('${'location'.tr()}: ${historyModuleListen.dataPending()[i]['location']}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                                     Row(
                                       children: [
                                         Container(
@@ -250,13 +256,13 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                 ),
                 onTap: () {
                   if(!U.getInternetStatus()){
-                    // navigateTo(context, MobHistoryDetailPendingPage(data: Provider.of<HistoryModule>(context, listen: false).dataPending()[i])).then((val) {
+                    // navigateTo(context, MobHistoryDetailPendingPage(data: historyModule.dataPending()[i])).then((val) {
                     //   val = val??false;
                     //   if(val) hisFunc.onRefresh(context);
                     // });
                   }
                   else{
-                    hisFunc.onRefresh(context);
+                    hisFunc.onRefresh();
                   }
                 },
               );
@@ -264,7 +270,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
           ) : Container(),
 
           Column(
-            children: List.generate(Provider.of<HistoryModule>(context).dataMisi().length, (i) {
+            children: List.generate(historyModuleListen.dataRequests().length, (i) {
               return GestureDetector(
                 child: Container(
                   width: double.infinity,
@@ -275,27 +281,25 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                     children: [
                       Row(
                         children: [
-                          Text(convertDate(Provider.of<HistoryModule>(context).dataMisi()[i]['datetimeRequest'], context.locale.toString()), style: TextStyle(color: primaryColor, fontSize: 14)),
+                          Text(convertDate(historyModuleListen.dataRequests()[i]['datetimeRequest'], context.locale.toString()), style: TextStyle(color: primaryColor, fontSize: 14)),
                           SizedBox(width: 15),
                           Expanded(
-                            child: Text('ticketNumber'.tr()+': '+Provider.of<HistoryModule>(context).dataMisi()[i]['ticketNo'], style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
+                            child: Text('${'ticketNumber'.tr()}: ${historyModuleListen.dataRequests()[i]['ticketNo']}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                           ),
-                          Provider.of<HistoryModule>(context).dataMisi()[i]['currentState'] == 'DIMULAI' ? Container(
-                            child: Row(
-                              children: [
-                                Container(
-                                  width: 25, height: 25,
-                                  decoration: BoxDecoration(
-                                      border: Border.all(color: primaryColor), borderRadius: BorderRadius.all(Radius.circular(25)),
-                                      image: DecorationImage(image: AssetImage('assets/image/general/Avatar.jpg'), fit: BoxFit.cover)
-                                  ),
+                          historyModuleListen.dataRequests()[i]['currentState'] == 'DIMULAI' ? Row(
+                            children: [
+                              Container(
+                                width: 25, height: 25,
+                                decoration: BoxDecoration(
+                                    border: Border.all(color: primaryColor), borderRadius: BorderRadius.all(Radius.circular(25)),
+                                    image: DecorationImage(image: AssetImage('assets/image/general/Avatar.jpg'), fit: BoxFit.cover)
                                 ),
-                                SizedBox(width: 8),
-                                Text(Provider.of<HistoryModule>(context).dataMisi()[i]['servantNameStart'] + 'isWorking'.tr(), style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis)
-                              ],
-                            ),
+                              ),
+                              SizedBox(width: 8),
+                              Text(historyModuleListen.dataRequests()[i]['servantNameStart'] + 'isWorking'.tr(), style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis)
+                            ],
                           ) : Container(),
-                          Provider.of<HistoryModule>(context).dataMisi()[i]['_activeHoldRequest'] != null?renderStatus('hold'.tr(), Color(0xffD3D3D3)):Provider.of<HistoryModule>(context).dataMisi()[i]['currentState'] == 'DIMULAI'?renderStatus('onProgress'.tr(), Color(0xffCCA600).withValues(alpha: 0.2)):renderStatus('queued'.tr(), Color(0xff02C539).withValues(alpha: 0.2))
+                          historyModuleListen.dataRequests()[i]['_activeHoldRequest'] != null?renderStatus('hold'.tr(), Color(0xffD3D3D3)):historyModuleListen.dataRequests()[i]['currentState'] == 'DIMULAI'?renderStatus('onProgress'.tr(), Color(0xffCCA600).withValues(alpha: 0.2)):renderStatus('queued'.tr(), Color(0xff02C539).withValues(alpha: 0.2))
                         ],
                       ),
                       SizedBox(height: 10),
@@ -303,42 +307,40 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                       SizedBox(height: 10),
                       Row(
                         children: [
-                          imageTiles(imageUrl: Provider.of<HistoryModule>(context).dataMisi()[i]['_requestImage'] ?? "null", width: 150, height: 120),
+                          imageTiles(imageUrl: historyModuleListen.dataRequests()[i]['_requestImage'] ?? "null", width: 150, height: 120),
                           SizedBox(width: 25),
                           Expanded(
                             child: Column(
                               crossAxisAlignment: CrossAxisAlignment.start,
                               children: [
-                                Text(Provider.of<HistoryModule>(context).dataMisi()[i][U.langColumn(context, 'requestGroupDescription')], style: TextStyle(color: textColor, fontSize: 16), overflow: TextOverflow.ellipsis),
+                                Text(historyModuleListen.dataRequests()[i][U.langColumn(context, 'requestGroupDescription')], style: TextStyle(color: textColor, fontSize: 16), overflow: TextOverflow.ellipsis),
                                 SizedBox(height: 10),
-                                Text(Provider.of<HistoryModule>(context).dataMisi()[i][U.langColumn(context, 'requestSubject')], style: TextStyle(color: textColor, fontWeight: FontWeight.w600), overflow: TextOverflow.ellipsis),
+                                Text(historyModuleListen.dataRequests()[i][U.langColumn(context, 'requestSubject')], style: TextStyle(color: textColor, fontWeight: FontWeight.w600), overflow: TextOverflow.ellipsis),
                                 SizedBox(height: 5),
-                                Text(Provider.of<HistoryModule>(context).dataMisi()[i][U.langColumn(context, '_subjectDescription')]??'', style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
+                                Text(historyModuleListen.dataRequests()[i][U.langColumn(context, '_subjectDescription')]??'', style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
                                 dashed(top: 10, bottom: 10),
-                                renderRequested(Provider.of<HistoryModule>(context).dataMisi()[i]),
+                                renderRequested(historyModuleListen.dataRequests()[i]),
                                 Row(
                                   mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                   crossAxisAlignment: CrossAxisAlignment.center ,
                                   children: [
-                                    Text('location'.tr()+': '+Provider.of<HistoryModule>(context).dataMisi()[i]['ipphoneExtLocation'], style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
-                                    Provider.of<HistoryModule>(context).dataMisi()[i]['_activeHoldRequest'] != null ? Container(
-                                      child: Row(
-                                        children: [
-                                          Container(
-                                            width: 25, height: 25,
-                                            decoration: BoxDecoration(image: DecorationImage(image: AssetImage('assets/image/general/Watch.png'), fit: BoxFit.cover)),
-                                          ),
-                                          SizedBox(width: 8),
-                                          Text(Provider.of<HistoryModule>(context).dataMisi()[i]['_activeHoldRequest']['description'], style: TextStyle(color: textColor, fontSize: 14))
-                                        ],
-                                      ),
-                                    ) : Provider.of<HistoryModule>(context).dataMisi()[i]['_hasForum'] && Provider.of<HistoryModule>(context).dataMisi()[i]['_forumMsg'] != null ? ConstrainedBox(
+                                    Text('${'location'.tr()}: ${historyModuleListen.dataRequests()[i]['ipphoneExtLocation']}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
+                                    historyModuleListen.dataRequests()[i]['_activeHoldRequest'] != null ? Row(
+                                      children: [
+                                        Container(
+                                          width: 25, height: 25,
+                                          decoration: BoxDecoration(image: DecorationImage(image: AssetImage('assets/image/general/Watch.png'), fit: BoxFit.cover)),
+                                        ),
+                                        SizedBox(width: 8),
+                                        Text(historyModuleListen.dataRequests()[i]['_activeHoldRequest']['description'], style: TextStyle(color: textColor, fontSize: 14))
+                                      ],
+                                    ) : historyModuleListen.dataRequests()[i]['_hasForum'] && historyModuleListen.dataRequests()[i]['_forumMsg'] != null ? ConstrainedBox(
                                       constraints: BoxConstraints(minWidth: 10),
                                       child: Row(
                                         children: [
                                           FadeTransition(opacity: _animationController, child: U.iconsax('messages-3', color: primaryColor)),
                                           SizedBox(width: 8),
-                                          Text('“${Provider.of<HistoryModule>(context).dataMisi()[i]['_forumMsg']}”', style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 14, fontStyle: FontStyle.italic))
+                                          Text('“${historyModuleListen.dataRequests()[i]['_forumMsg']}”', style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 14, fontStyle: FontStyle.italic))
                                         ],
                                       ),
                                     ) : Container(),
@@ -354,14 +356,14 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                 ),
                 onTap: () => navigateTo(context, WebHistoryDetailPage(index: i)).then((val) {
                   val = val??false;
-                  if(val) Provider.of<HistoryModule>(context, listen: false).cancelMisi(i);
+                  if(val) historyModule.cancelMisi(i);
                 }),
               );
             }),
           ),
-          !Provider.of<HistoryModule>(context).stopLoadHistory() || (Provider.of<HistoryModule>(context).isLoadHistory() && Provider.of<HistoryModule>(context).page() > 0)
-              ? Container(margin: EdgeInsets.only(top: Provider.of<HistoryModule>(context).page() > 0 ? 0 : 50), child: loadingTemplateNoVoid())
-              : Provider.of<HistoryModule>(context).dataMisi().isEmpty && Provider.of<HistoryModule>(context).dataPending().isEmpty
+          !historyModuleListen.stopLoadHistory() || (historyModuleListen.isLoadHistory() && historyModuleListen.page() > 0)
+              ? Container(margin: EdgeInsets.only(top: historyModuleListen.page() > 0 ? 0 : 50), child: loadingTemplateNoVoid())
+              : historyModuleListen.dataRequests().isEmpty && historyModuleListen.dataPending().isEmpty
               ? NoDataPage()
               : Container(),
         ],
@@ -370,6 +372,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
   }
 
   Widget doneFilter() {
+    final HistoryModule historyModuleListen = Provider.of<HistoryModule>(context);
     return Row(
       children: List.generate(hisFunc.doneList.length, (i) {
         return GestureDetector(
@@ -377,19 +380,19 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
             margin: EdgeInsets.only(right: 16),
             padding: EdgeInsets.symmetric(vertical: 8, horizontal: 16),
             decoration: BoxDecoration(
-                color: Provider.of<HistoryModule>(context).checkPressAttention(hisFunc.doneList[i]['value']) ? primaryColor : Color(0xffF2F8F7),
-                border: Border.all(color: Provider.of<HistoryModule>(context).checkPressAttention(hisFunc.doneList[i]['value']) ? primaryColor : Color(0xffBEC1C1)),
+                color: historyModuleListen.checkSelectedFilter(hisFunc.doneList[i]['value']) ? primaryColor : Color(0xffF2F8F7),
+                border: Border.all(color: historyModuleListen.checkSelectedFilter(hisFunc.doneList[i]['value']) ? primaryColor : Color(0xffBEC1C1)),
                 borderRadius: BorderRadius.all(Radius.circular(50))),
-            child: Text(hisFunc.doneList[i]['title'], style: TextStyle(color: Provider.of<HistoryModule>(context).checkPressAttention(hisFunc.doneList[i]['value']) ? Colors.white : textColor, fontSize: 16)),
+            child: Text(hisFunc.doneList[i]['title'], style: TextStyle(color: historyModuleListen.checkSelectedFilter(hisFunc.doneList[i]['value']) ? Colors.white : textColor, fontSize: 16)),
           ),
           onTap: () {
-            if (!Provider.of<HistoryModule>(context, listen: false).isLoadHistory()) {
-              if (Provider.of<HistoryModule>(context, listen: false).checkPressAttention(hisFunc.doneList[i]['value'])) {
-                Provider.of<HistoryModule>(context, listen: false).setPressAttention(0);
-                hisFunc.onRefresh(context);
+            if (!historyModule.isLoadHistory()) {
+              if (historyModule.checkSelectedFilter(hisFunc.doneList[i]['value'])) {
+                historyModule.setSelectedFilter(0);
+                hisFunc.onRefresh();
               } else {
-                Provider.of<HistoryModule>(context, listen: false).setPressAttention(hisFunc.doneList[i]['value']);
-                hisFunc.onRefresh(context);
+                historyModule.setSelectedFilter(hisFunc.doneList[i]['value']);
+                hisFunc.onRefresh();
               }
             }
           },
@@ -399,11 +402,12 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
   }
 
   Widget doneContainer() {
+    final HistoryModule historyModuleListen = Provider.of<HistoryModule>(context);
     return SingleChildScrollView(
       child: Column(
         children: [
           Column(
-              children: List.generate(Provider.of<HistoryModule>(context).dataMisi().length, (i) {
+              children: List.generate(historyModuleListen.dataRequests().length, (i) {
                 return GestureDetector(
                   child: Container(
                     width: double.infinity,
@@ -414,12 +418,12 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                       children: [
                         Row(
                           children: [
-                            Text(convertDate(Provider.of<HistoryModule>(context).dataMisi()[i]['datetimeRequest'], context.locale.toString()), style: TextStyle(color: primaryColor, fontSize: 14)),
+                            Text(convertDate(historyModuleListen.dataRequests()[i]['datetimeRequest'], context.locale.toString()), style: TextStyle(color: primaryColor, fontSize: 14)),
                             SizedBox(width: 15),
                             Expanded(
-                              child: Text('ticketNumber'.tr()+': '+Provider.of<HistoryModule>(context).dataMisi()[i]['ticketNo'], style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
+                              child: Text('${'ticketNumber'.tr()}: ${historyModuleListen.dataRequests()[i]['ticketNo']}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                             ),
-                            Provider.of<HistoryModule>(context).dataMisi()[i]['currentState'] == 'DISELESAIKAN' || Provider.of<HistoryModule>(context).dataMisi()[i]['currentState'] == 'TUNTAS' ? renderStatus('stateFinish'.tr(), primaryColor.withValues(alpha: 0.4)) : renderStatus('stateCancel'.tr(), Color(0xffD81010).withValues(alpha: 0.4))
+                            historyModuleListen.dataRequests()[i]['currentState'] == 'DISELESAIKAN' || historyModuleListen.dataRequests()[i]['currentState'] == 'TUNTAS' ? renderStatus('stateFinish'.tr(), primaryColor.withValues(alpha: 0.4)) : renderStatus('stateCancel'.tr(), Color(0xffD81010).withValues(alpha: 0.4))
                           ],
                         ),
                         SizedBox(height: 10),
@@ -427,20 +431,20 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                         SizedBox(height: 10),
                         Row(
                           children: [
-                            imageTiles(imageUrl: Provider.of<HistoryModule>(context).dataMisi()[i]['_requestImage'] ?? "null", width: 150, height: 120),
+                            imageTiles(imageUrl: historyModuleListen.dataRequests()[i]['_requestImage'] ?? "null", width: 150, height: 120),
                             SizedBox(width: 25),
                             Expanded(
                               child: Column(
                                 crossAxisAlignment: CrossAxisAlignment.start,
                                 children: [
-                                  Text(Provider.of<HistoryModule>(context).dataMisi()[i][U.langColumn(context, 'requestGroupDescription')], style: TextStyle(color: textColor, fontSize: 16), overflow: TextOverflow.ellipsis),
+                                  Text(historyModuleListen.dataRequests()[i][U.langColumn(context, 'requestGroupDescription')], style: TextStyle(color: textColor, fontSize: 16), overflow: TextOverflow.ellipsis),
                                   SizedBox(height: 10),
-                                  Text(Provider.of<HistoryModule>(context).dataMisi()[i][U.langColumn(context, 'requestSubject')], style: TextStyle(color: textColor, fontWeight: FontWeight.w600), overflow: TextOverflow.ellipsis),
+                                  Text(historyModuleListen.dataRequests()[i][U.langColumn(context, 'requestSubject')], style: TextStyle(color: textColor, fontWeight: FontWeight.w600), overflow: TextOverflow.ellipsis),
                                   SizedBox(height: 5),
-                                  Text(Provider.of<HistoryModule>(context).dataMisi()[i][U.langColumn(context, '_subjectDescription')]??'', style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
+                                  Text(historyModuleListen.dataRequests()[i][U.langColumn(context, '_subjectDescription')]??'', style: TextStyle(color: textColor), overflow: TextOverflow.ellipsis),
                                   dashed(top: 10, bottom: 10),
-                                  renderRequested(Provider.of<HistoryModule>(context).dataMisi()[i]),
-                                  Text('location'.tr()+': '+Provider.of<HistoryModule>(context).dataMisi()[i]['ipphoneExtLocation'], style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
+                                  renderRequested(historyModuleListen.dataRequests()[i]),
+                                  Text('${'location'.tr()}: ${historyModuleListen.dataRequests()[i]['ipphoneExtLocation']}', style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                                 ],
                               ),
                             )
@@ -452,31 +456,31 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                         Row(
                           mainAxisAlignment: MainAxisAlignment.spaceBetween,
                           children: [
-                            !Provider.of<HistoryModule>(context).dataMisi()[i]['autoResponse'] && Provider.of<HistoryModule>(context).dataMisi()[i]['servantIdentityStart'] != '#system' && (Provider.of<HistoryModule>(context).dataMisi()[i]['currentState'] == 'TUNTAS' || Provider.of<HistoryModule>(context).dataMisi()[i]['currentState'] == 'DISELESAIKAN') ? Container(
-                              child: hisFunc.askForRate(Provider.of<HistoryModule>(context, listen: true).dataMisi()[i]) ? GestureDetector(
+                            !historyModuleListen.dataRequests()[i]['autoResponse'] && historyModuleListen.dataRequests()[i]['servantIdentityStart'] != '#system' && (historyModuleListen.dataRequests()[i]['currentState'] == 'TUNTAS' || historyModuleListen.dataRequests()[i]['currentState'] == 'DISELESAIKAN') ? Container(
+                              child: hisFunc.askForRate(Provider.of<HistoryModule>(context, listen: true).dataRequests()[i]) ? GestureDetector(
                                 child: Container(
                                   padding: EdgeInsets.symmetric(vertical: 4, horizontal: 16),
-                                  child: Text('rateReq'.tr(), style: TextStyle(color: secondaryColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                                   decoration: BoxDecoration(color: Colors.white, border: Border.all(color: secondaryColor), borderRadius: BorderRadius.all(Radius.circular(50))),
+                                  child: Text('rateReq'.tr(), style: TextStyle(color: secondaryColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                                 ),
                                 onTap: () {
-                                  U.newServerVersion(1716279633) ? rateMissionNew(context, Provider.of<HistoryModule>(context, listen: false).dataMisi()[i], i) : rateMission(context, Provider.of<HistoryModule>(context, listen: false).dataMisi()[i], i);
+                                  U.newServerVersion(1716279633) ? rateMissionNew(context, historyModule.dataRequests()[i], i) : rateMission(context, historyModule.dataRequests()[i], i);
                                 },
-                              ) : !hisFunc.isAutoResponse(Provider.of<HistoryModule>(context).dataMisi()[i]) ? Row(
+                              ) : !hisFunc.isAutoResponse(historyModuleListen.dataRequests()[i]) ? Row(
                                 children: [
-                                  Image(image: AssetImage(rating[Provider.of<HistoryModule>(context).dataMisi()[i]['satisfactionRate'] - 1]['image'].toString()), width: 25),
+                                  Image(image: AssetImage(rating[historyModuleListen.dataRequests()[i]['satisfactionRate'] - 1]['image'].toString()), width: 25),
                                   SizedBox(width: 5),
-                                  Text(rating[Provider.of<HistoryModule>(context).dataMisi()[i]['satisfactionRate'] - 1]['label'].toString(), style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
+                                  Text(rating[historyModuleListen.dataRequests()[i]['satisfactionRate'] - 1]['label'].toString(), style: TextStyle(color: textColor, fontSize: 14), overflow: TextOverflow.ellipsis),
                                 ],
                               ) : Container(),
-                            ) : message(Provider.of<HistoryModule>(context).dataMisi()[i]['_hasForum'], Provider.of<HistoryModule>(context).dataMisi()[i]['_forumMsg'] ?? "null"),
+                            ) : message(historyModuleListen.dataRequests()[i]['_hasForum'], historyModuleListen.dataRequests()[i]['_forumMsg'] ?? "null"),
                             GestureDetector(
                               child: Container(
                                 padding: EdgeInsets.symmetric(vertical: 4, horizontal: 16),
-                                child: Text('reqAgain'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)),
                                 decoration: BoxDecoration(color: Colors.white, border: Border.all(color: primaryColor), borderRadius: BorderRadius.all(Radius.circular(50))),
+                                child: Text('reqAgain'.tr(), style: TextStyle(color: primaryColor, fontSize: 14)),
                               ),
-                              onTap: () => hisFunc.requestAgainAction(context, i),
+                              onTap: () => hisFunc.requestAgainAction(i),
                             )
                           ],
                         ),
@@ -484,7 +488,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                     ),
                   ),
                   onTap: () {
-                    if (Provider.of<HistoryModule>(context, listen: false).multiSelectMode()) {
+                    if (historyModule.multiSelectMode()) {
                       hisFunc.selectedController(context, i);
                     } else {
                       navigateTo(context, WebHistoryDetailPage(index: i));
@@ -494,9 +498,9 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                 );
               })
           ),
-          !Provider.of<HistoryModule>(context).stopLoadHistory() || (Provider.of<HistoryModule>(context).isLoadHistory() && Provider.of<HistoryModule>(context).page() > 0)
-              ? Container(margin: EdgeInsets.only(top: Provider.of<HistoryModule>(context).page() > 0 ? 0 : 50), child: loadingTemplateNoVoid())
-              : Provider.of<HistoryModule>(context).dataMisi().length == 0 ? NoDataPage() : Container(),
+          !historyModuleListen.stopLoadHistory() || (historyModuleListen.isLoadHistory() && historyModuleListen.page() > 0)
+              ? Container(margin: EdgeInsets.only(top: historyModuleListen.page() > 0 ? 0 : 50), child: loadingTemplateNoVoid())
+              : historyModuleListen.dataRequests().isEmpty ? NoDataPage() : Container(),
         ],
       ),
     );
@@ -504,7 +508,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
 
   Widget renderRequested(list){
     if(list['receptionistId'] != null){
-      var user = Provider.of<UserModule>(context, listen: false).user();
+      var user = userModule.user();
       if(user['roomAttendant'] && user['userId'] != list['informantUserId'] && user['userId'] == list['receptionistId']){
         return Column(
           crossAxisAlignment: CrossAxisAlignment.start,
@@ -529,14 +533,14 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
     return Container();
   }
 
-  Widget message(bool _hasForum, String _forumMsg, {double top = 0}) {
-    return _hasForum ? Container(
+  Widget message(bool hasForum, String forumMsg, {double top = 0}) {
+    return hasForum ? Container(
       margin: EdgeInsets.only(top: top),
       child: Row(
         children: [
           FadeTransition(opacity: _animationController, child: U.iconsax('messages-3', color: primaryColor)),
           SizedBox(width: 8),
-          Text('“$_forumMsg”', style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 14, fontStyle: FontStyle.italic), overflow: TextOverflow.ellipsis),
+          Text('“$forumMsg”', style: TextStyle(color: textColor.withValues(alpha: 0.75), fontSize: 14, fontStyle: FontStyle.italic), overflow: TextOverflow.ellipsis),
         ],
       ),
     ) : Container();
@@ -546,10 +550,10 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
     return Container(
       padding: EdgeInsets.symmetric(vertical: 3, horizontal: 10),
       margin: EdgeInsets.only(left: 10),
-      child: Text(text, style: TextStyle(fontSize: 12, color: textColor)),
       decoration: BoxDecoration(
         color: color, borderRadius: BorderRadius.all(Radius.circular(3))
       ),
+      child: Text(text, style: TextStyle(fontSize: 12, color: textColor)),
     );
   }
 
@@ -631,9 +635,9 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                                     if (tempRating > 0) {
                                       var data = {"ticketNo": list['ticketNo'], "rateSatisfy": tempRating};
 
-                                      var res = await ApiAuthProvider() .postData('/api/requestHistories/rateSatisfy', null, data, contexts);
+                                      var res = await ApiAuthProvider() .postData('/api/requestHistories/rateSatisfy', null, data);
                                       if (res != null) {
-                                        Provider.of<HistoryModule>(context, listen: false).setSatisfaction(index, tempRating);
+                                        historyModule.setSatisfaction(index, tempRating);
                                         navigateBack(contexts);
                                       }
                                     }
@@ -664,7 +668,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
           String description = '';
           String ratingAspect = '';
           TextEditingController controllerOptOther = new TextEditingController();
-          List aspectList = list['_ratingAspect'+locale[0].toUpperCase()+locale[1]] != null && list['_ratingAspect'+locale[0].toUpperCase()+locale[1]].trim() != '' ? list['_ratingAspect'+locale[0].toUpperCase()+locale[1]].split(';') : [];
+          List aspectList = list['_ratingAspect${locale[0].toUpperCase()}${locale[1]}'] != null && list['_ratingAspect'+locale[0].toUpperCase()+locale[1]].trim() != '' ? list['_ratingAspect'+locale[0].toUpperCase()+locale[1]].split(';') : [];
           if(aspectList.isNotEmpty){
             aspectList.add('OTHER_OPTION');
           }
@@ -693,7 +697,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                             Row(
                               mainAxisAlignment: MainAxisAlignment.spaceBetween,
                               children: [
-                                Text('ticketNumber'.tr()+': '+list['ticketNo'], style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w300)),
+                                Text('${'ticketNumber'.tr()}: '+list['ticketNo'], style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w300)),
                                 Text(convertDate(list['datetimeRequest'], context.locale.toString()), style: TextStyle(color: Colors.white, fontSize: 12, fontWeight: FontWeight.w300)),
                               ],
                             ),
@@ -735,7 +739,7 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                             tempRating != 0 ? Column(
                               crossAxisAlignment: CrossAxisAlignment.start,
                               children: [
-                                Text('whatMakesYou'.tr()+' '+description+'?', style: TextStyle(fontSize: 16)),
+                                Text('${'whatMakesYou'.tr()} $description?', style: TextStyle(fontSize: 16)),
                                 SizedBox(height: 12),
                                 Wrap(
                                   runSpacing: 10, spacing: 10,
@@ -799,9 +803,9 @@ class _WebHistoryPageState extends State<WebHistoryPage> with TickerProviderStat
                                         data['aspect'] = controllerOptOther.text;
                                       }
 
-                                      var res = await ApiAuthProvider() .postData('/api/requestHistories/rateSatisfy', null, data, contexts);
+                                      var res = await ApiAuthProvider() .postData('/api/requestHistories/rateSatisfy', null, data);
                                       if (res != null) {
-                                        Provider.of<HistoryModule>(context, listen: false).setSatisfaction(index, tempRating);
+                                        historyModule.setSatisfaction(index, tempRating);
                                         navigateBack(contexts);
                                       }
                                     }

File diff suppressed because it is too large
+ 12 - 12
lib/src/layouts/web/menu_home.dart


+ 4 - 4
lib/src/layouts/web/message_broadcast.dart

@@ -9,9 +9,9 @@ import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/utils/U.dart';
 
 class WebMessageBroadcastPage extends StatefulWidget {
-  Map<String, dynamic> user;
-  List tenants;
-  WebMessageBroadcastPage(this.user, this.tenants, {super.key});
+  final Map<String, dynamic> user;
+  final List tenants;
+  const WebMessageBroadcastPage(this.user, this.tenants, {super.key});
 
   @override
   State<WebMessageBroadcastPage> createState() => _WebMessageBroadcastPageState();
@@ -67,7 +67,7 @@ class _WebMessageBroadcastPageState extends State<WebMessageBroadcastPage> {
     setState(() {
       isSending = true;
     });
-    var res = await ApiAuthProvider().postData('/api/messages/broadcast', params, data, context);
+    var res = await ApiAuthProvider().postData('/api/messages/broadcast', params, data);
     if (res != null) {
       Navigator.pop(context, 'pop');
     }

+ 148 - 149
lib/src/layouts/web/message_list.dart

@@ -22,11 +22,10 @@ import 'dart:ui' as ui;
 import 'package:uuid/uuid.dart';
 
 import '../components/photo_chat.dart';
-import '../mobile/message_chat.dart';
 
 class WebMessageListPage extends StatefulWidget {
-  Map<String, dynamic>? user;
-  WebMessageListPage(this.user, {super.key});
+  final Map<String, dynamic>? user;
+  const WebMessageListPage(this.user, {super.key});
 
   @override
   State<WebMessageListPage> createState() => _WebMessageListPageState();
@@ -109,7 +108,7 @@ class _WebMessageListPageState extends State<WebMessageListPage> {
 
       scrollController.addListener(() => scrollListener());
       if (!isMe) {
-        await apiAuthProvider.patchData('/api/messages/' + data['id'].toString(), {'readStatus': 'READ'}, context);
+        await apiAuthProvider.patchData('/api/messages/' + data['id'].toString(), {'readStatus': 'READ'});
       }
 
       getMessage();
@@ -145,7 +144,7 @@ class _WebMessageListPageState extends State<WebMessageListPage> {
     }
     if (!isLoad && !stopLoad) {
       isLoad = true;
-      var mymess = await apiAuthProvider.getData('/api/messages/search/$url', {'isPaged': 'true', 'page': page.toString(), 'size': '20', 'filter': filter, 'tenant': tenant}, context);
+      var mymess = await apiAuthProvider.getData('/api/messages/search/$url', {'isPaged': 'true', 'page': page.toString(), 'size': '20', 'filter': filter, 'tenant': tenant});
       if (mymess.containsKey('_embedded') && mounted) {
         List data = mymess['_embedded']['myMessages'];
         for (int i = 0; i < data.length; i++) {
@@ -189,7 +188,7 @@ class _WebMessageListPageState extends State<WebMessageListPage> {
     try{
       var mymess = await apiAuthProvider.getData(
           '/api/messages/search/myMessages/${forumChatId}',
-          {'isPaged': 'true', 'page': page.toString(), 'size': '20'}, context);
+          {'isPaged': 'true', 'page': page.toString(), 'size': '20'});
 
       if (mymess.containsKey('_embedded')) {
         List data = mymess['_embedded']['myMessages'];
@@ -436,147 +435,147 @@ class _WebMessageListPageState extends State<WebMessageListPage> {
       );
     }
 
-    Widget chat(){
-      final messageModule = Provider.of<MessageModule>(context);
-      final dataList = messageModule.data();
-      final currentUser = messageModule.user();
-      final forumList = messageModule.forum();
-
-      return dataList.isEmpty ? Expanded(
-        child: Center(
-          child: Text('noMessageText'.tr()),
-        ),
-      ) : SingleChildScrollView(
-        padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
-        child: Column(
-          mainAxisSize: MainAxisSize.max,
-          children: List.generate(dataList.length, (i) {
-            final item = dataList[i];
-            final isMe = item['userId'] == currentUser['userId'];
-            final avatar = item['senderAvatar'];
-            final chatIdMatch = idChat != null && idChat == item['chatId'];
-            final recipientColor = U.getColor(isMe ? item['recipient'] : item['recipientId']);
-            final recipientName = item['recipientName'];
-            final senderName = item['senderName'];
-            final lastText = item['lastText'] ?? '';
-            final isImage = item['isImage'] ?? false;
-            final fileType = item['fileType'] ?? '';
-            final lastDate = item['lastDateTimeSend'];
-            final lastReadStatus = item['lastReadStatus'];
-            final forumReadStatus = forumList[i]['readStatus'];
-
-            return GestureDetector(
-              onTap: () => selectMessage(item, isMe),
-              child: Container(
-                padding: EdgeInsets.all(10),
-                decoration: BoxDecoration(
-                  color: chatIdMatch ? Color(0xff26DA17).withAlpha(50) : Colors.white,
-                  borderRadius: BorderRadius.circular(12),
-                ),
-                child: Row(
-                  children: [
-                    avatar != null && avatar != ''
-                        ? CircleAvatar(
-                      backgroundImage: NetworkImage(avatar),
-                      radius: 24,
-                    )
-                        : Container(
-                      height: 48,
-                      width: 48,
-                      decoration: BoxDecoration(
-                        shape: BoxShape.circle,
-                        color: Color(recipientColor),
-                      ),
-                      child: Center(
-                        child: Text(
-                          isMe
-                              ? recipientName == "all_informants"
-                              ? "allInformants".tr()[0]
-                              : recipientName[0]
-                              : senderName[0],
-                          style: TextStyle(
-                            color: Colors.white,
-                            fontWeight: FontWeight.bold,
-                            fontSize: 18,
-                          ),
-                        ),
-                      ),
-                    ),
-                    SizedBox(width: 20),
-                    Expanded(
-                      child: Column(
-                        crossAxisAlignment: CrossAxisAlignment.start,
-                        children: [
-                          Text(
-                            isMe
-                                ? recipientName == "all_informants"
-                                ? "allInformants".tr()
-                                : recipientName
-                                : senderName,
-                            style: TextStyle(fontWeight: FontWeight.w600),
-                          ),
-                          SizedBox(height: 5),
-                          Row(
-                            children: [
-                              if (isMe)
-                                Padding(
-                                  padding: EdgeInsets.only(right: 5),
-                                  child: Icon(Icons.done_all, size: 18, color: Colors.blueGrey),
-                                ),
-                              if (isImage)
-                                Row(
-                                  children: [
-                                    Icon(
-                                      fileType == 'pdf' ? Icons.picture_as_pdf : Icons.image,
-                                      color: Colors.black45,
-                                      size: 16,
-                                    ),
-                                    SizedBox(width: 6),
-                                    if (lastText == '')
-                                      Text(fileType == 'pdf' ? 'pdfFile'.tr() : 'photo'.tr()),
-                                  ],
-                                ),
-                              Expanded(
-                                child: Text(
-                                  lastText,
-                                  style: TextStyle(fontSize: 13),
-                                  maxLines: 1,
-                                  overflow: TextOverflow.ellipsis,
-                                ),
-                              ),
-                            ],
-                          ),
-                        ],
-                      ),
-                    ),
-                    SizedBox(width: 20),
-                    Column(
-                      crossAxisAlignment: CrossAxisAlignment.end,
-                      children: [
-                        Text(
-                          convertDate(lastDate, context.locale.toString()),
-                          style: TextStyle(
-                            fontSize: 11,
-                            fontWeight: forumReadStatus == 'UNREAD' ? FontWeight.w600 : FontWeight.w300,
-                            color: lastReadStatus == 'UNREAD' && !isMe ? primaryColor : Colors.black45,
-                          ),
-                        ),
-                        SizedBox(height: 12),
-                        Icon(
-                          Icons.circle,
-                          size: 12,
-                          color: primaryColor.withAlpha(lastReadStatus == 'UNREAD' && !isMe ? 1 : 0),
-                        ),
-                      ],
-                    ),
-                  ],
-                ),
-              ),
-            );
-          }),
-        ),
-      );
-    }
+    // Widget chat(){
+    //   final messageModule = Provider.of<MessageModule>(context);
+    //   final dataList = messageModule.data();
+    //   final currentUser = messageModule.user();
+    //   final forumList = messageModule.forum();
+    //
+    //   return dataList.isEmpty ? Expanded(
+    //     child: Center(
+    //       child: Text('noMessageText'.tr()),
+    //     ),
+    //   ) : SingleChildScrollView(
+    //     padding: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
+    //     child: Column(
+    //       mainAxisSize: MainAxisSize.max,
+    //       children: List.generate(dataList.length, (i) {
+    //         final item = dataList[i];
+    //         final isMe = item['userId'] == currentUser['userId'];
+    //         final avatar = item['senderAvatar'];
+    //         final chatIdMatch = idChat != null && idChat == item['chatId'];
+    //         final recipientColor = U.getColor(isMe ? item['recipient'] : item['recipientId']);
+    //         final recipientName = item['recipientName'];
+    //         final senderName = item['senderName'];
+    //         final lastText = item['lastText'] ?? '';
+    //         final isImage = item['isImage'] ?? false;
+    //         final fileType = item['fileType'] ?? '';
+    //         final lastDate = item['lastDateTimeSend'];
+    //         final lastReadStatus = item['lastReadStatus'];
+    //         final forumReadStatus = forumList[i]['readStatus'];
+    //
+    //         return GestureDetector(
+    //           onTap: () => selectMessage(item, isMe),
+    //           child: Container(
+    //             padding: EdgeInsets.all(10),
+    //             decoration: BoxDecoration(
+    //               color: chatIdMatch ? Color(0xff26DA17).withAlpha(50) : Colors.white,
+    //               borderRadius: BorderRadius.circular(12),
+    //             ),
+    //             child: Row(
+    //               children: [
+    //                 avatar != null && avatar != ''
+    //                     ? CircleAvatar(
+    //                   backgroundImage: NetworkImage(avatar),
+    //                   radius: 24,
+    //                 )
+    //                     : Container(
+    //                   height: 48,
+    //                   width: 48,
+    //                   decoration: BoxDecoration(
+    //                     shape: BoxShape.circle,
+    //                     color: Color(recipientColor),
+    //                   ),
+    //                   child: Center(
+    //                     child: Text(
+    //                       isMe
+    //                           ? recipientName == "all_informants"
+    //                           ? "allInformants".tr()[0]
+    //                           : recipientName[0]
+    //                           : senderName[0],
+    //                       style: TextStyle(
+    //                         color: Colors.white,
+    //                         fontWeight: FontWeight.bold,
+    //                         fontSize: 18,
+    //                       ),
+    //                     ),
+    //                   ),
+    //                 ),
+    //                 SizedBox(width: 20),
+    //                 Expanded(
+    //                   child: Column(
+    //                     crossAxisAlignment: CrossAxisAlignment.start,
+    //                     children: [
+    //                       Text(
+    //                         isMe
+    //                             ? recipientName == "all_informants"
+    //                             ? "allInformants".tr()
+    //                             : recipientName
+    //                             : senderName,
+    //                         style: TextStyle(fontWeight: FontWeight.w600),
+    //                       ),
+    //                       SizedBox(height: 5),
+    //                       Row(
+    //                         children: [
+    //                           if (isMe)
+    //                             Padding(
+    //                               padding: EdgeInsets.only(right: 5),
+    //                               child: Icon(Icons.done_all, size: 18, color: Colors.blueGrey),
+    //                             ),
+    //                           if (isImage)
+    //                             Row(
+    //                               children: [
+    //                                 Icon(
+    //                                   fileType == 'pdf' ? Icons.picture_as_pdf : Icons.image,
+    //                                   color: Colors.black45,
+    //                                   size: 16,
+    //                                 ),
+    //                                 SizedBox(width: 6),
+    //                                 if (lastText == '')
+    //                                   Text(fileType == 'pdf' ? 'pdfFile'.tr() : 'photo'.tr()),
+    //                               ],
+    //                             ),
+    //                           Expanded(
+    //                             child: Text(
+    //                               lastText,
+    //                               style: TextStyle(fontSize: 13),
+    //                               maxLines: 1,
+    //                               overflow: TextOverflow.ellipsis,
+    //                             ),
+    //                           ),
+    //                         ],
+    //                       ),
+    //                     ],
+    //                   ),
+    //                 ),
+    //                 SizedBox(width: 20),
+    //                 Column(
+    //                   crossAxisAlignment: CrossAxisAlignment.end,
+    //                   children: [
+    //                     Text(
+    //                       convertDate(lastDate, context.locale.toString()),
+    //                       style: TextStyle(
+    //                         fontSize: 11,
+    //                         fontWeight: forumReadStatus == 'UNREAD' ? FontWeight.w600 : FontWeight.w300,
+    //                         color: lastReadStatus == 'UNREAD' && !isMe ? primaryColor : Colors.black45,
+    //                       ),
+    //                     ),
+    //                     SizedBox(height: 12),
+    //                     Icon(
+    //                       Icons.circle,
+    //                       size: 12,
+    //                       color: primaryColor.withAlpha(lastReadStatus == 'UNREAD' && !isMe ? 1 : 0),
+    //                     ),
+    //                   ],
+    //                 ),
+    //               ],
+    //             ),
+    //           ),
+    //         );
+    //       }),
+    //     ),
+    //   );
+    // }
 
     Widget webChat(BuildContext context) {
       final messageModule = Provider.of<MessageModule>(context);
@@ -607,7 +606,7 @@ class _WebMessageListPageState extends State<WebMessageListPage> {
             final chatId = item['chatId'];
             final recipientId = item['recipientId'];
             final recipient = item['recipient'];
-            final id = item['id'];
+            // final id = item['id'];
 
             return GestureDetector(
               onTap: () {
@@ -997,7 +996,7 @@ class _WebMessageListPageState extends State<WebMessageListPage> {
       });
       scrollBottom = true;
 
-      var res = await apiAuthProvider.postData('/api/messages', null, data, context);
+      var res = await apiAuthProvider.postData('/api/messages', null, data);
       if (res != null) {
         int index = forumData.indexWhere((element) => element['uniqueId'] == data['uniqueId']);
         forumData[index]['read'] = false;

+ 4 - 4
lib/src/layouts/web/password.dart

@@ -11,8 +11,8 @@ import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferenc
 import 'package:telnow_mobile_new/src/utils/U.dart';
 
 class WebPasswordPage extends StatefulWidget {
-  Map<String, dynamic> user;
-  WebPasswordPage({required this.user, super.key});
+  final Map<String, dynamic> user;
+  const WebPasswordPage({required this.user, super.key});
 
   @override
   State<WebPasswordPage> createState() => _WebPasswordPageState();
@@ -57,6 +57,7 @@ class _WebPasswordPageState extends State<WebPasswordPage> {
                     alignment: Alignment.topLeft,
                     width: MediaQuery.of(context).size.width/3,
                     padding: EdgeInsets.all(20),
+                    decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
                     child: Column(
                       crossAxisAlignment: CrossAxisAlignment.start,
                       children: [
@@ -94,7 +95,7 @@ class _WebPasswordPageState extends State<WebPasswordPage> {
                                 'password':controllerNew.trim()
                               };
 
-                              var res = await apiAuthProvider.patchData('/api/informants/'+widget.user['id'].toString()+'/changePassword', data, context);
+                              var res = await apiAuthProvider.patchData('/api/informants/'+widget.user['id'].toString()+'/changePassword', data);
                               if(res != null){
                                 String username = widget.user['_isRelated']?widget.user['relatedTo']:widget.user['userId'];
                                 String? password = widget.user['_isRelated'] && widget.user['tenantCode']!=null && widget.user['tenantCode']!=''?widget.user['tenantCode']+' '+data['password']:data['password'];
@@ -122,7 +123,6 @@ class _WebPasswordPageState extends State<WebPasswordPage> {
                         })
                       ],
                     ),
-                    decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
                   ),
                   Expanded(child: Container())
                 ],

+ 3 - 3
lib/src/layouts/web/request_create.dart

@@ -14,10 +14,10 @@ import 'package:telnow_mobile_new/src/utils/provider.dart';
 import 'package:toggle_switch/toggle_switch.dart';
 
 class WebReqCreatePage extends StatefulWidget {
-  Map<String, dynamic> user;
-  Map<String, dynamic> request;
+  final Map<String, dynamic> user;
+  final Map<String, dynamic> request;
   final bool fromSearch;
-  WebReqCreatePage({required this.user, required this.request, this.fromSearch = false, super.key});
+  const WebReqCreatePage({required this.user, required this.request, this.fromSearch = false, super.key});
 
   @override
   State<WebReqCreatePage> createState() => _WebReqCreatePageState();

+ 9 - 9
lib/src/layouts/web/request_select.dart

@@ -10,13 +10,13 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:telnow_mobile_new/src/utils/provider.dart';
 
 class WebReqSelectPage extends StatefulWidget {
-  String title;
-  String? groupCode;
-  String scope;
-  String? tenantCode;
-  String? placeholder;
-  Map<String, dynamic> user;
-  WebReqSelectPage({required this.title, this.groupCode, required this.scope, this.tenantCode, required this.user, this.placeholder, super.key});
+  final String title;
+  final String? groupCode;
+  final String scope;
+  final String? tenantCode;
+  final String? placeholder;
+  final Map<String, dynamic> user;
+  const WebReqSelectPage({required this.title, this.groupCode, required this.scope, this.tenantCode, required this.user, this.placeholder, super.key});
 
   @override
   State<WebReqSelectPage> createState() => _WebReqSelectPageState();
@@ -87,7 +87,7 @@ class _WebReqSelectPageState extends State<WebReqSelectPage> {
                 SizedBox(
                   height: 40, width: 100,
                   child: ElevatedButton(
-                    style: ButtonStyle(elevation: MaterialStateProperty.all<double>(0), backgroundColor: MaterialStateProperty.all<Color>(primaryColor), shape: MaterialStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(50))), side: MaterialStateProperty.all<BorderSide>(BorderSide(color: primaryColor))),
+                    style: ButtonStyle(elevation: WidgetStateProperty.all<double>(0), backgroundColor: WidgetStateProperty.all<Color>(primaryColor), shape: WidgetStateProperty.all<RoundedRectangleBorder>(RoundedRectangleBorder(borderRadius: BorderRadius.circular(50))), side: WidgetStateProperty.all<BorderSide>(BorderSide(color: primaryColor))),
                     onPressed: ()=>reqFunc.getData(context, searchController.text.trim()==''?null:searchController.text.trim(), widget),
                     child: Text('searchButton'.tr(), style: TextStyle(fontSize: 14, color: Colors.white, fontWeight: FontWeight.w600)),
                   ),
@@ -138,6 +138,7 @@ class _WebReqSelectPageState extends State<WebReqSelectPage> {
                             constraints: BoxConstraints(minWidth: minWidth, minHeight: minHeight),
                             child: Container(
                               width: size, height: size*1.4,
+                              decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
                               child: Column(
                                 crossAxisAlignment: CrossAxisAlignment.start,
                                 children: [
@@ -156,7 +157,6 @@ class _WebReqSelectPageState extends State<WebReqSelectPage> {
                                   )
                                 ],
                               ),
-                              decoration: BoxDecoration(color: Colors.white, border: Border.all(color: textColor.withValues(alpha: 0.15)), borderRadius: BorderRadius.all(Radius.circular(12))),
                             ),
                           ),
                           onTap: ()=>navigateTo(context, WebReqCreatePage(user: widget.user, request: Provider.of<RequestModule>(context, listen: false).data()[i], fromSearch: true)),

+ 5 - 5
lib/src/layouts/web/request_success.dart

@@ -11,10 +11,10 @@ import 'package:telnow_mobile_new/src/utils/U.dart';
 import 'package:url_launcher/url_launcher.dart';
 
 class WebReqSuccessPage extends StatefulWidget {
-  Map<String, dynamic> user;
+  final Map<String, dynamic> user;
   final String ticketNo;
   final bool fromSearch;
-  WebReqSuccessPage({required this.user, required this.ticketNo, this.fromSearch = false, super.key});
+  const WebReqSuccessPage({required this.user, required this.ticketNo, this.fromSearch = false, super.key});
 
   @override
   State<WebReqSuccessPage> createState() => _WebReqSuccessPageState();
@@ -33,7 +33,7 @@ class _WebReqSuccessPageState extends State<WebReqSuccessPage> {
   }
 
   getMission() async{
-    var data = await apiAuthProvider.getData('/api/requestHistories/'+widget.ticketNo, null, context);
+    var data = await apiAuthProvider.getData('/api/requestHistories/'+widget.ticketNo, null);
     if(data != null) {
       if (data['datetimeScheduled'] != null && data['datetimeScheduled'] != '' && data['noteFormat'] == 'DATE') {
         var date = data['datetimeScheduled'];
@@ -276,9 +276,9 @@ class _WebReqSuccessPageState extends State<WebReqSuccessPage> {
 // -----------------------------------------------------------------------------------------------------------------------------
 
 class WebReqSuccessPendingPage extends StatefulWidget {
-  Map<String, dynamic> data;
+  final Map<String, dynamic> data;
   final bool fromSearch;
-  WebReqSuccessPendingPage({required this.data, this.fromSearch = false, super.key});
+  const WebReqSuccessPendingPage({required this.data, this.fromSearch = false, super.key});
 
   @override
   State<WebReqSuccessPendingPage> createState() => _WebReqSuccessPendingPageState();

+ 1 - 1
lib/src/model/token/token.g.dart

@@ -6,7 +6,7 @@ part of 'token.dart';
 // JsonSerializableGenerator
 // **************************************************************************
 
-Token _$TokenFromJson(Map<String?, dynamic?> json) {
+Token _$TokenFromJson(Map<String?, dynamic> json) {
   return Token(
     json['access_token'] as String,
     json['token_type'] as String,

+ 18 - 7
lib/src/utils/U.dart

@@ -18,7 +18,6 @@ import 'package:telnow_mobile_new/src/injector/injector.dart';
 import 'package:telnow_mobile_new/src/model/refreshtoken/refresh_token_body.dart';
 import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
-import 'package:synchronized/synchronized.dart';
 
 import 'cache_manager.dart';
 import 'C.dart';
@@ -46,7 +45,7 @@ class U {
   static final Codec<String, String> stringToBase64Url = utf8.fuse(base64Url);
   static final JwtToken token = JwtToken();
 
-  static final _lockAuth = Lock();
+  // static final _lockAuth = Lock();
   static int _authLastMs = 0;
   static Token? _lastReqToken;
 
@@ -87,6 +86,7 @@ class U {
         Future.error(error);
       }
     }
+    return null;
   }
 
   static Future waitForRefreshAuth() async {
@@ -110,7 +110,7 @@ class U {
       var nw = DateTime.now().millisecondsSinceEpoch;
       if (_authLastMs == 0 || nw - _authLastMs >= 5000) {
         // print("refresh token request");
-        ApiAuthRepository apiAuthRepository = ApiAuthRepository();
+        // ApiAuthRepository apiAuthRepository = ApiAuthRepository();
         var token = await _tryRefreshAuth(refreshTokenBody);
         _authLastMs = DateTime.now().millisecondsSinceEpoch;
         _lastReqToken = token;
@@ -218,7 +218,7 @@ class U {
   static Future<dynamic> getUserData(context) async {
     if (_userData == null) {
       try {
-        _userData = await token.getUserData(context);
+        _userData = await token.getUserData();
       } catch (error) {
         print(error.toString());
         U.showDebugToast(error.toString());
@@ -392,7 +392,7 @@ class U {
   static Map _otherLabelList = {};
   static getOtherLabelList(BuildContext context) async{
     if(newServerVersion(1725245109)){
-      var res = await ApiAuthProvider().getData('/api/systemSettings/search/additional', {'key': 'request-for-other'}, context);
+      var res = await ApiAuthProvider().getData('/api/systemSettings/search/additional', {'key': 'request-for-other'});
       if(res != null){
         _otherLabelList = res;
       }
@@ -521,6 +521,17 @@ class U {
       ];
   }
 
+  static List<Color> loadingIndicatorColors() {
+      return [
+        Color(0xFF17BC91),
+        Color(0xFF72D3B7),
+        Color(0xFF3FE365),
+        Color(0xFFBAE33F),
+        Color(0xFFE3D53F),
+        Color(0xFFE33F3F)
+      ];
+  }
+
   static Widget iconsax(String name, {Color color = Colors.black, double size = 24}){
     return SvgPicture.asset(
       'assets/iconsax/${name}.svg',
@@ -587,7 +598,7 @@ class U {
         if(element['others']){
           data['req'][0]['uniqueId'] = element['uniqueId'];
           data['req'][0]['images'] = element['imageList'];
-          var res = await ApiAuthProvider().postData('/api/receptionists/send/request', null, data, context);
+          var res = await ApiAuthProvider().postData('/api/receptionists/send/request', null, data);
           if (res != null) {
             uniqueId.add(res['uniqueId']);
             // print('success send pending request others');
@@ -596,7 +607,7 @@ class U {
         else{
           data['uniqueId'] = element['uniqueId'];
           data['images'] = element['imageList'];
-          var res = await ApiAuthProvider().postData('/api/requestHistories/search/request/' + element['type'] + '/' + element['id'].toString() + '/' + element['noteFormat'] + '/submit', null, data, context);
+          var res = await ApiAuthProvider().postData('/api/requestHistories/search/request/${element['type']}/${element['id'].toString()}/${element['noteFormat']}/submit', null, data);
           if (res != null) {
             uniqueId.add(res['uniqueId']);
             // print('success send pending request');

+ 0 - 1
lib/src/utils/cache_manager.dart

@@ -1,7 +1,6 @@
 import 'package:cache_manager/cache_manager.dart';
 import 'package:flutter_cache_manager/flutter_cache_manager.dart';
 import 'package:shared_preferences/shared_preferences.dart';
-import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 
 class CacheMan {
   static void writeData(String key, dynamic data) {

+ 7 - 16
lib/src/utils/dio_logging_interceptors.dart

@@ -1,8 +1,8 @@
 import 'package:dio/dio.dart';
+import 'package:flutter/cupertino.dart';
 import 'package:telnow_mobile_new/src/injector/injector.dart';
 import 'package:telnow_mobile_new/src/model/refreshtoken/refresh_token_body.dart';
 import 'package:telnow_mobile_new/src/model/token/token.dart';
-import 'package:telnow_mobile_new/src/layouts/components/template.dart';
 import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
 import 'package:telnow_mobile_new/src/utils/u.dart';
 import 'package:event_bus/event_bus.dart';
@@ -33,31 +33,22 @@ class DioLoggingInterceptors extends InterceptorsWrapper {
   }
 
   @override
-  void onError(DioError dioError, ErrorInterceptorHandler handler) async {
-    // print(dioError.response?.data.toString());
-    // print(dioError.response?.realUri.toString());
+  void onError(DioException err, ErrorInterceptorHandler handler) async {
     try {
-      if (dioError.response?.statusCode == 401) {
-        // print("auth error");
+      if (err.response?.statusCode == 401) {
         bool? checkRefreshCount = _sharedPreferencesManager.isKeyExists(SharedPreferencesManager.keyCountRefreshToken);
         int? refreshCount = _sharedPreferencesManager.getInt(SharedPreferencesManager.keyCountRefreshToken);
         if(checkRefreshCount! && refreshCount! > 2){
-          // handlingError(NavigationService.navigatorKey.currentContext, 3);
           eventBus.fire("logout");
           return;
         }
 
-        // if (dioError.response!.data.toString().contains("Invalid refresh token")) {
-        //   handlingError(NavigationService.navigatorKey.currentContext, 3);
-        // }
-        if (dioError.response!.data.toString().contains("Access token expired")) {
-          // print("token expired");
-
+        if (err.response!.data.toString().contains("Access token expired")) {
           String? refreshToken = _sharedPreferencesManager.getString(SharedPreferencesManager.keyRefreshToken)!;
           RefreshTokenBody? refreshTokenBody = RefreshTokenBody('refresh_token', refreshToken);
           Token? token = await U.refreshAuth(refreshTokenBody);
           if (token != null) {
-            RequestOptions? options = dioError.response?.requestOptions;
+            RequestOptions? options = err.response?.requestOptions;
             options?.headers.addAll({'requirestoken': true});
             try {
               var r = await _dio.fetch(options!);
@@ -65,7 +56,7 @@ class DioLoggingInterceptors extends InterceptorsWrapper {
               await _sharedPreferencesManager.putInt(SharedPreferencesManager.keyCountRefreshToken, 0);
               return;
             } catch (error2) {
-              print(error2);
+              debugPrint(error2.toString());
             }
           }
         }
@@ -76,6 +67,6 @@ class DioLoggingInterceptors extends InterceptorsWrapper {
       eventBus.fire("logout");
       // handlingError(NavigationService.navigatorKey.currentContext, 3);
     }
-    super.onError(dioError, handler);
+    super.onError(err, handler);
   }
 }

+ 45 - 29
lib/src/utils/provider.dart

@@ -3,7 +3,7 @@ import 'dart:typed_data';
 
 import 'package:easy_localization/easy_localization.dart';
 import 'package:flutter/material.dart';
-import 'package:telnow_mobile_new/src/utils/U.dart';
+import 'package:telnow_mobile_new/src/layouts/functions/history.dart';
 
 class UserModule with ChangeNotifier {
   static String _serialNumber = '';
@@ -302,29 +302,31 @@ class ServiceModule with ChangeNotifier {
 
 class HistoryModule with ChangeNotifier {
   static int _page = 0;
-  static bool _isLoadHistory = false;
+  static Map<String, bool> _isLoadHistory = {};
   static bool _stopLoadHistory = false;
   static bool _multiSelectMode = false;
   static bool _setLoading = false;
   static List _selectedIndexes = [];
   static List _activeForum = [];
 
-  static List _data = [];
+  static Map<String, List> _data = {};
   static List _pendingData = [];
-  static int _pressAttention = 0;
-  static int _selectedFilter = 1;
+  static String _selectedFilter = 'none';
+  static String _currentPage = 'ongoing::none';
+  static HistoryTab _activeTab = HistoryTab.ongoing;
 
-  List dataMisi() => _data;
+  List dataRequests({String state = ''}) => _data[state != '' ? state : _currentPage]!;
   List dataPending() => _pendingData;
-  int pressAttention() => _pressAttention;
-  int selectedFilter() => _selectedFilter;
+  String selectedFilter() => _selectedFilter;
+  HistoryTab activeTab() => _activeTab;
   int page() => _page;
-  bool isLoadHistory() => _isLoadHistory;
+  bool isLoadHistory({String state = ''}) => _isLoadHistory[state != '' ? state : _currentPage]!;
   bool stopLoadHistory() => _stopLoadHistory;
   bool multiSelectMode() => _multiSelectMode;
   bool setLoading() => _setLoading;
   List selectedIndexes() => _selectedIndexes;
   List activeForum() => _activeForum;
+  String currentPage() => _currentPage;
 
   void addSelectedIndexes(i) {
     _selectedIndexes.add(i);
@@ -341,16 +343,21 @@ class HistoryModule with ChangeNotifier {
     notifyListeners();
   }
 
-  void setPressAttention(value) {
-    _pressAttention = value;
+  void setSelectedFilter(value) {
+    _selectedFilter = value;
     notifyListeners();
   }
 
-  void setSelectedFilter(value) {
-    _selectedFilter = value;
+  void setActiveTab(HistoryTab value) {
+    _activeTab = value;
     notifyListeners();
   }
 
+  void setCurrentPage() {
+    // debugPrint('called to set current page with value: ${_activeTab.name}::$_selectedFilter');
+    _currentPage = '${_activeTab.name}::$_selectedFilter';
+  }
+
   void setPage(value) {
     _page = value;
     notifyListeners();
@@ -361,8 +368,8 @@ class HistoryModule with ChangeNotifier {
     notifyListeners();
   }
 
-  void setLoadHistory(value) {
-    _isLoadHistory = value;
+  void setLoadHistory(bool value, String state) {
+    _isLoadHistory[state] = value;
     notifyListeners();
   }
 
@@ -381,13 +388,22 @@ class HistoryModule with ChangeNotifier {
     notifyListeners();
   }
 
-  void setMisi(list) {
-    _data.addAll(list);
+  void setDataRequests(String keyState, List list) {
+    // debugPrint('set data request => $keyState: ${list.length} data');
+    _data[keyState]!.addAll(list);
     notifyListeners();
   }
 
-  void clearMisi() {
-    _data.clear();
+  void initDataPages(Map<String, List> data){
+    _data = data;
+  }
+
+  void initIsLoadPages(Map<String, bool> data){
+    _isLoadHistory = data;
+  }
+
+  void clearDataRequests() {
+    _data[_currentPage]!.clear();
     notifyListeners();
   }
 
@@ -401,39 +417,39 @@ class HistoryModule with ChangeNotifier {
     notifyListeners();
   }
 
-  bool checkPressAttention(value){
-    return _pressAttention == value;
+  bool checkSelectedFilter(value){
+    return _selectedFilter == value;
   }
 
   int getMisiLength(){
-    return _data.length;
+    return _data[_currentPage]!.length;
   }
 
   void setSatisfaction(index, rate) {
-    _data[index]['satisfactionRate'] = rate;
+    _data[_currentPage]![index]['satisfactionRate'] = rate;
     notifyListeners();
   }
 
   void cancelMisi(index) {
-    _data.removeAt(index);
+    _data[_currentPage]!.removeAt(index);
     notifyListeners();
   }
 
   void setForumFalse(index) {
-    _data[index]['_hasForum'] = false;
+    _data[_currentPage]![index]['_hasForum'] = false;
     notifyListeners();
   }
 
   void reset(){
     _page = 0;
-    _isLoadHistory = false;
+    _isLoadHistory = {};
     _stopLoadHistory = false;
     _multiSelectMode = false;
     _setLoading = false;
-    _data = [];
+    _data = {};
     _pendingData = [];
-    _pressAttention = 0;
-    _selectedFilter = 1;
+    _selectedFilter = 'none';
+    _activeTab = HistoryTab.ongoing;
     _selectedIndexes = [];
     _activeForum = [];
   }

+ 297 - 0
lib/src/utils/ui_service.dart

@@ -0,0 +1,297 @@
+import 'dart:convert';
+import 'package:another_flushbar/flushbar.dart';
+import 'package:auto_route/auto_route.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:lottie/lottie.dart';
+import 'package:page_transition/page_transition.dart';
+import 'package:telnow_mobile_new/src/injector/injector.dart';
+import 'package:telnow_mobile_new/src/layouts/auth/qr.dart';
+import 'package:telnow_mobile_new/src/layouts/mobile/history_forum.dart';
+import 'package:telnow_mobile_new/src/layouts/mobile/message_list.dart';
+import 'U.dart';
+import 'package:telnow_mobile_new/src/storage/sharedpreferences/shared_preferences_manager.dart';
+import 'package:telnow_mobile_new/src/api/jwt_token.dart';
+
+enum ErrorType {
+  noInternet, serverError, connectionError, invalidAccount, expiredAccount
+}
+final SharedPreferencesManager _sharedPreferencesManager = locator<SharedPreferencesManager>();
+final JwtToken token = JwtToken();
+
+class UIService {
+  static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
+
+  static BuildContext? get context => navigatorKey.currentContext;
+
+  static void goNotification(Map<String, dynamic> list) {
+    final ctx = context;
+    if (ctx == null) return;
+
+    if (list['type'] == 'MESSAGE') {
+      Navigator.push(
+        ctx,
+        PageTransition(
+          type: PageTransitionType.rightToLeft,
+          child: MobMessageListPage(null),
+        ),
+      );
+    } else if (list['type'] == 'FORUM') {
+      Navigator.push(
+        ctx,
+        PageTransition(
+          type: PageTransitionType.rightToLeft,
+          child: MobHistoryForumPage(
+            data: jsonDecode(list['requestHistory']),
+          ),
+        ),
+      );
+    } else {
+      final pid = U.getPidFromUrl(ctx.router.currentUrl);
+      ctx.router.removeLast();
+      ctx.navigateToPath("/app/$pid/menu/history");
+    }
+  }
+
+  static void navigateTo(Widget child) {
+    final ctx = context;
+    if (ctx != null) {
+      Navigator.push(ctx, PageTransition(type: PageTransitionType.rightToLeft, child: child));
+    }
+  }
+
+  static void navigateNamed(String routePath) {
+    final ctx = context;
+    if (ctx != null) {
+      AutoRouter.of(ctx).removeLast();
+      AutoRouter.of(ctx).navigatePath(routePath);
+    }
+  }
+
+  static void navigateBack({exc = false}){
+    final ctx = context;
+    if (ctx != null) {
+      Navigator.of(ctx).pop(exc);
+    }
+  }
+
+  static void showError(String message) {
+    final ctx = context;
+    if (ctx != null) {
+      Flushbar(
+        message: message,
+        icon: Icon(
+          Icons.info_outline,
+          size: 28.0,
+          color: Colors.red,
+        ),
+        duration: Duration(seconds: 5),
+        flushbarPosition: FlushbarPosition.BOTTOM,
+        margin: EdgeInsets.all(8),
+        borderRadius: BorderRadius.all(Radius.circular(8)),
+      ).show(ctx);
+    }
+  }
+
+  static void showSuccess(message) {
+    final ctx = context;
+    if (ctx != null) {
+      Flushbar(
+        message: message,
+        icon: Icon(
+          Icons.info_outline,
+          size: 28.0,
+          color: Colors.green,
+        ),
+        duration: Duration(seconds: 5),
+        flushbarPosition: FlushbarPosition.BOTTOM,
+        margin: EdgeInsets.all(8),
+        borderRadius: BorderRadius.all(Radius.circular(8)),
+      ).show(ctx);
+    }
+  }
+
+  static bool get isCurrentRouteInactive {
+    final ctx = context;
+    final route = ModalRoute.of(ctx ?? navigatorKey.currentState?.context ?? ctx!);
+    return route?.isCurrent != true;
+  }
+
+  static void pop() {
+    navigatorKey.currentState?.maybePop();
+  }
+
+  static void setLocale(code) {
+    final ctx = context;
+    if(ctx == null) return;
+    ctx.setLocale(code);
+  }
+
+  static String getLocale() {
+    return context!.locale.toString();
+  }
+
+  static void showLoading({String? text, String? lottie}){
+    final ctx = context;
+    if(ctx == null) return;
+    showDialog(
+      context: ctx,
+      barrierDismissible: false,
+      barrierColor: lottie != null ? Colors.white : null,
+        builder: (BuildContext context) {
+          return WillPopScope(
+            onWillPop: () => Future.value(false),
+            child: Material(
+              type: MaterialType.transparency,
+              child: Center(
+                child: Column(
+                  mainAxisSize: MainAxisSize.min,
+                  children: [
+                    lottie != null
+                        ? Lottie.asset('assets/image/lottie/$lottie', width: 250, height: 250, fit: BoxFit.fill)
+                        : CircularProgressIndicator(color: primaryColor),
+                    SizedBox(height: 5),
+                    Text(
+                      text ?? 'inProcess'.tr(),
+                      style: TextStyle(color: lottie != null ? Colors.black : Colors.white),
+                    ),
+                  ],
+                ),
+              ),
+            ),
+          );
+        }
+    );
+  }
+
+  static void closeLoading(){
+    Navigator.of(navigatorKey.currentContext!, rootNavigator: true).pop();
+  }
+
+  static void handlingError(ErrorType type) {
+    var data = [
+      {
+        'image': 'NoInternet.png',
+        'title': 'noInternetTitle'.tr(),
+        'description': 'noInternetDesc'.tr()
+      },
+      {
+        'image': 'ErrorServer.png',
+        'title': 'errorServerTitle'.tr(),
+        'description': 'errorServerDesc'.tr()
+      },
+      {
+        'image': 'ErrorConnection.png',
+        'title': 'errorConnectTitle'.tr(),
+        'description': 'errorConnectDesc'.tr()
+      },
+      {
+        'image': 'ErrorAuth.png',
+        'title': 'invalidAccountTitle'.tr(),
+        'description': 'invalidAccountDesc'.tr()
+      },
+      {
+        'image': 'ErrorAuth.png',
+        'title': 'expAccountTitle'.tr(),
+        'description': 'expAccountDesc'.tr()
+      }
+    ];
+    int idx = type.index;
+
+    final ctx = context;
+    if (ctx == null) return;
+
+    void clearSharedPreferencesKey(){
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyAccessCode);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keySerialCode);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyAccessToken);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyRefreshToken);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyUsername);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyIsLogin);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyScoope);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.debugString);
+      _sharedPreferencesManager.clearKey(SharedPreferencesManager.keyHistoryMark);
+    }
+
+    showModalBottomSheet<void>(
+      context: ctx,
+      backgroundColor: Colors.white,
+      isDismissible: false,
+      enableDrag: false,
+      builder: (BuildContext context) {
+        return SingleChildScrollView(
+          child: Column(
+            mainAxisSize: MainAxisSize.min,
+            children: <Widget>[
+              Container(
+                padding: const EdgeInsets.fromLTRB(15, 10, 15, 0),
+                alignment: Alignment.centerRight,
+                child: GestureDetector(
+                  child: Icon(Icons.clear),
+                  onTap: () => Navigator.of(context).pop(),
+                ),
+              ),
+              Container(
+                width: 200,
+                padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
+                child: Image(
+                  image: AssetImage('assets/image/error/${data[idx]['image']!}'))),
+              Padding(
+                padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
+                child: Text(data[idx]['title']!,
+                  style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold),
+                  textAlign: TextAlign.center),
+              ),
+              Padding(
+                padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
+                child: Text(data[idx]['description']!,
+                  style: TextStyle(fontSize: 14),
+                  textAlign: TextAlign.center),
+              ),
+              if (idx > 2)
+              Padding(
+                padding: const EdgeInsets.fromLTRB(15, 0, 15, 10),
+                child: SizedBox(
+                  width: double.infinity,
+                  height: 45,
+                  child: TextButton(
+                    style: ButtonStyle(
+                      backgroundColor:
+                      WidgetStateProperty.all<Color>(primaryColor),
+                      shape: WidgetStateProperty.all<
+                        RoundedRectangleBorder>(
+                        RoundedRectangleBorder(
+                          borderRadius: BorderRadius.circular(5),
+                        ))),
+                    child: Text('logout'.tr().toUpperCase(),
+                      style: TextStyle(color: Colors.white),
+                      textAlign: TextAlign.center),
+                    onPressed: () {
+                      if (idx == 4) {
+                        clearSharedPreferencesKey();
+                        Navigator.pushAndRemoveUntil(
+                          context,
+                          MaterialPageRoute(
+                            builder: (context) => NewQrPage()),
+                          ModalRoute.withName("/home"));
+                      } else {
+                        var pid = U.getPidFromUrl(context.router.currentUrl);
+                        token.logout();
+                        context.router.removeLast();
+                        context.navigateToPath("/app/$pid/login");
+                      }
+                    },
+                  )),
+              )
+            ],
+          ),
+        );
+      });
+  }
+
+  static String getCurrentUrl(){
+    final ctx = context;
+    if(ctx == null) return '';
+    return ctx.router.currentUrl;
+  }
+}

+ 45 - 37
pubspec.lock

@@ -277,10 +277,10 @@ packages:
     dependency: transitive
     description:
       name: characters
-      sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
+      sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
       url: "https://pub.dev"
     source: hosted
-    version: "1.3.0"
+    version: "1.4.0"
   checked_yaml:
     dependency: transitive
     description:
@@ -293,10 +293,10 @@ packages:
     dependency: transitive
     description:
       name: clock
-      sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
+      sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
       url: "https://pub.dev"
     source: hosted
-    version: "1.1.1"
+    version: "1.1.2"
   cloud_firestore:
     dependency: "direct main"
     description:
@@ -333,10 +333,10 @@ packages:
     dependency: transitive
     description:
       name: collection
-      sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf
+      sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
       url: "https://pub.dev"
     source: hosted
-    version: "1.19.0"
+    version: "1.19.1"
   convert:
     dependency: transitive
     description:
@@ -493,10 +493,10 @@ packages:
     dependency: transitive
     description:
       name: fake_async
-      sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
+      sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
       url: "https://pub.dev"
     source: hosted
-    version: "1.3.1"
+    version: "1.3.3"
   ffi:
     dependency: transitive
     description:
@@ -814,13 +814,13 @@ packages:
     source: hosted
     version: "0.15.5"
   http:
-    dependency: transitive
+    dependency: "direct main"
     description:
       name: http
-      sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
+      sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
       url: "https://pub.dev"
     source: hosted
-    version: "1.3.0"
+    version: "1.5.0"
   http_multi_server:
     dependency: transitive
     description:
@@ -913,10 +913,10 @@ packages:
     dependency: transitive
     description:
       name: intl
-      sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
+      sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
       url: "https://pub.dev"
     source: hosted
-    version: "0.19.0"
+    version: "0.20.2"
   io:
     dependency: transitive
     description:
@@ -934,7 +934,7 @@ packages:
     source: hosted
     version: "0.7.1"
   json_annotation:
-    dependency: "direct main"
+    dependency: transitive
     description:
       name: json_annotation
       sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
@@ -949,6 +949,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.0.1"
+  lazy_load_indexed_stack:
+    dependency: "direct main"
+    description:
+      name: lazy_load_indexed_stack
+      sha256: "6026d0f1753ae0427587d6095f823143c1d3e4459c8640ebf0a808044ad8de1c"
+      url: "https://pub.dev"
+    source: hosted
+    version: "1.2.1"
   lazy_load_scrollview:
     dependency: "direct main"
     description:
@@ -961,26 +969,26 @@ packages:
     dependency: transitive
     description:
       name: leak_tracker
-      sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06"
+      sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
       url: "https://pub.dev"
     source: hosted
-    version: "10.0.7"
+    version: "11.0.2"
   leak_tracker_flutter_testing:
     dependency: transitive
     description:
       name: leak_tracker_flutter_testing
-      sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379"
+      sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
       url: "https://pub.dev"
     source: hosted
-    version: "3.0.8"
+    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"
   linkify:
     dependency: transitive
     description:
@@ -1025,10 +1033,10 @@ packages:
     dependency: transitive
     description:
       name: matcher
-      sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
+      sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
       url: "https://pub.dev"
     source: hosted
-    version: "0.12.16+1"
+    version: "0.12.17"
   material_color_utilities:
     dependency: transitive
     description:
@@ -1041,10 +1049,10 @@ packages:
     dependency: transitive
     description:
       name: meta
-      sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
+      sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
       url: "https://pub.dev"
     source: hosted
-    version: "1.15.0"
+    version: "1.16.0"
   mime:
     dependency: transitive
     description:
@@ -1185,10 +1193,10 @@ packages:
     dependency: transitive
     description:
       name: path
-      sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
+      sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
       url: "https://pub.dev"
     source: hosted
-    version: "1.9.0"
+    version: "1.9.1"
   path_drawing:
     dependency: transitive
     description:
@@ -1409,10 +1417,10 @@ packages:
     dependency: "direct main"
     description:
       name: searchfield
-      sha256: "9d091c2731868926e2aeb9ac551d1b9116a4533a424373119509d754ae0d0f45"
+      sha256: c06d4e7b916472c60926a2b79656e0c55480d60a06aef542d1e8708567da90ad
       url: "https://pub.dev"
     source: hosted
-    version: "1.2.4"
+    version: "1.3.5"
   selectable_autolink_text:
     dependency: "direct main"
     description:
@@ -1582,18 +1590,18 @@ packages:
     dependency: transitive
     description:
       name: stack_trace
-      sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
+      sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
       url: "https://pub.dev"
     source: hosted
-    version: "1.12.0"
+    version: "1.12.1"
   stream_channel:
     dependency: transitive
     description:
       name: stream_channel
-      sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
+      sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
       url: "https://pub.dev"
     source: hosted
-    version: "2.1.2"
+    version: "2.1.4"
   stream_transform:
     dependency: transitive
     description:
@@ -1630,10 +1638,10 @@ packages:
     dependency: transitive
     description:
       name: test_api
-      sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
+      sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
       url: "https://pub.dev"
     source: hosted
-    version: "0.7.3"
+    version: "0.7.6"
   timelines_plus:
     dependency: "direct main"
     description:
@@ -1798,10 +1806,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"
   version:
     dependency: transitive
     description:
@@ -1939,5 +1947,5 @@ packages:
     source: hosted
     version: "3.1.1"
 sdks:
-  dart: ">=3.6.1 <4.0.0"
+  dart: ">=3.8.0-0 <4.0.0"
   flutter: ">=3.27.0"

+ 75 - 48
pubspec.yaml

@@ -1,5 +1,5 @@
 name: telnow_mobile_new
-description: "A new Flutter project."
+description: "TelNow application by Datacom Solusindo."
 # The following line prevents the package from being accidentally published to
 # pub.dev using `flutter pub publish`. This is preferred for private packages.
 publish_to: 'none' # Remove this line if you wish to publish to pub.dev
@@ -34,69 +34,96 @@ dependencies:
 
   # The following adds the Cupertino Icons font to your application.
   # Use with the CupertinoIcons class for iOS style icons.
+
+  # 🌱 Core Flutter & UI
   cupertino_icons: ^1.0.8
-  change_app_package_name: ^1.4.0
-  firebase_core: ^3.11.0
+  flutter_bloc: ^9.0.0
+  flutter_custom_clippers: ^2.1.0
+  flutter_datetime_picker_plus: ^2.2.0
+  flutter_linkify: ^6.0.0
+  flutter_local_notifications: ^19.3.0
+  flutter_native_splash: ^2.4.4
+  flutter_randomcolor: ^1.0.16
+  flutter_svg: ^2.2.0
+  fluttertoast: ^8.2.10
+  loading_indicator: ^3.1.1
+  lottie: ^3.3.1
+  page_transition: ^2.2.1
+  shimmer: ^3.0.0
+  toggle_switch: ^2.3.0
+
+  # 🔥 Firebase & Auth
   firebase_auth: ^5.4.1
+  firebase_core: ^3.11.0
   firebase_messaging: ^15.2.1
   cloud_firestore: ^5.6.2
+
+  # 🌐 Networking & API
   dio: ^5.7.0
-  shared_preferences: ^2.4.0
-  flutter_local_notifications: ^19.3.0
+  http: ^1.5.0
+
+  # 🧠 State Management & Utilities
   get_it: ^8.0.3
-  double_back_to_exit: ^1.2.1
-  page_transition: ^2.2.1
-  badges: ^3.1.2
-  shimmer: ^3.0.0
-  easy_localization: ^3.0.7
-  toggle_switch: ^2.3.0
-  json_annotation: ^4.9.0
-  flutter_randomcolor: ^1.0.16
-  photo_view: ^0.15.0
-  image_picker: ^1.1.2
-  path_provider: ^2.1.5
-  uuid: ^4.5.1
+  provider: ^6.1.2
+  event_bus: ^2.0.1
+  synchronized: ^3.3.0+3
+
+  # 📁 File & Media Handling
   camera: ^0.11.2
   camera_android: ^0.10.10+4
-  another_flushbar: ^1.12.30
-  fluttertoast: ^8.2.10
-  synchronized: ^3.3.0+3
+  file_picker: ^9.0.0
   image: ^4.5.2
-  dotted_line: ^3.2.3
-  provider: ^6.1.2
-  easy_refresh: ^3.4.0
-  upgrader: ^11.3.1
-  qr_flutter: ^4.1.0
-  app_settings: ^5.1.1
-  translator: ^1.0.3+1
-  lottie: ^3.3.1
+  image_picker: ^1.1.2
+  open_file: ^3.5.10
+  path_provider: ^2.1.5
+  photo_view: ^0.15.0
+
+  # 🧭 Navigation & Routing
   auto_route: ^10.0.1
-  permission_handler: ^11.3.1
-  quick_notify_2: ^0.3.0-dev.0
-  timelines_plus: ^1.0.6
-  flutter_bloc: ^9.0.0
-  flutter_custom_clippers: ^2.1.0
-  flutter_datetime_picker_plus: ^2.2.0
-  file_picker: ^9.0.0
-  loading_indicator: ^3.1.1
-  url_launcher: ^6.3.1
+
+  # 🌍 Localization & Translation
+  easy_localization: ^3.0.7
+  translator: ^1.0.3+1
+
+  # 📊 Caching & Persistence
   cache_manager: ^1.0.0+4
-  flutter_cache_manager: ^3.4.1
   cached_network_image: ^3.4.1
-  lazy_load_scrollview: ^1.3.0
-  drag_and_drop_lists: ^0.4.2
+  flutter_cache_manager: ^3.4.1
+  shared_preferences: ^2.4.0
+
+  # 🎨 UI Enhancements
+  badges: ^3.1.2
   carousel_slider: ^5.0.0
-  simple_connection_checker: ^0.3.4
-  flutter_linkify: ^6.0.0
-  flutter_svg: ^2.2.0
+  dotted_line: ^3.2.3
+  drag_and_drop_lists: ^0.4.2
+  lazy_load_scrollview: ^1.3.0
+  searchfield: ^1.2.5
   selectable_autolink_text: ^2.6.0
-  flutter_native_splash: ^2.4.4
-  searchfield: ^1.2.4
-  youtube_player_iframe: ^5.2.1
+  timelines_plus: ^1.0.6
+
+  # 📱 Device & Permissions
+  app_settings: ^5.1.1
   mobile_scanner: ^6.0.6
-  open_file: ^3.5.10
+  permission_handler: ^11.3.1
+  url_launcher: ^6.3.1
+
+  # 🔒 Auth & Identity
   jwt_decoder: ^2.0.1
-  event_bus: ^2.0.1
+  uuid: ^4.5.1
+
+  # 📦 QR & Barcode
+  qr_flutter: ^4.1.0
+
+  # 🧪 Experimental & Misc
+  another_flushbar: ^1.12.30
+  change_app_package_name: ^1.4.0
+  double_back_to_exit: ^1.2.1
+  easy_refresh: ^3.4.0
+  quick_notify_2: ^0.3.0-dev.0
+  simple_connection_checker: ^0.3.4
+  upgrader: ^11.3.1
+  youtube_player_iframe: ^5.2.1
+  lazy_load_indexed_stack: ^1.2.1
 
 dev_dependencies:
   flutter_test: