From a5876ec1d3acfe2a6e520254c1bd4a905fcc9239 Mon Sep 17 00:00:00 2001 From: WaseemAbbasi22 Date: Mon, 6 Jan 2025 09:54:09 +0300 Subject: [PATCH] swipe module added --- assets/images/ic_done.png | Bin 0 -> 18338 bytes assets/images/ic_nfc.png | Bin 0 -> 19686 bytes assets/images/nfc_icon.svg | 12 + assets/images/success_swipe.svg | 11 + assets/images/swipe.svg | 8 + assets/images/swipe_success.svg | 14 + assets/images/wifi_icon.svg | 5 + lib/controllers/api_routes/api_manager.dart | 8 +- lib/controllers/api_routes/urls.dart | 3 +- .../providers/api/user_provider.dart | 157 +- lib/dashboard_latest/dashboard_provider.dart | 1 + lib/dashboard_latest/dashboard_view.dart | 100 +- .../widgets/request_category_fragment.dart | 6 +- lib/l10n/app_ar.arb | 17 +- lib/l10n/app_en.arb | 18 +- lib/main.dart | 5 + .../new_models/general_response_model.dart | 10 +- lib/models/new_models/swipe_model.dart | 42 +- .../new_models/swipe_transaction_history.dart | 47 + .../new_models/swipe_transaction_model.dart | 47 + lib/models/new_models/update_password.dart | 27 +- lib/models/new_models/verify_otp_model.dart | 41 + lib/models/user.dart | 16 + lib/new_views/common_widgets/app_drawer.dart | 4 + .../common_widgets/app_text_form_field.dart | 3 + .../forget_passwod_verify_otp.dart | 166 ++ .../reset_password_view.dart | 121 ++ lib/new_views/pages/land_page/land_page.dart | 10 +- .../land_page/mark_attendance_widget.dart | 1732 ++++++++--------- lib/new_views/pages/login_page.dart | 30 +- .../circular_animated_widget.dart | 99 + .../swipe_module/dialoge/confirm_dialog.dart | 61 + .../dialoge/nfc_reader_sheet.dart | 203 ++ .../swipe_module/dialoge/success_dialog.dart | 76 + .../swipe_module/enums/swipe_type.dart | 18 + .../swipe_module/mark_attendance_widget.dart | 821 ++++++++ .../swipe_module/models/swipe_model.dart | 68 + .../models/swipe_transaction_history.dart | 47 + .../models/swipe_transaction_model.dart | 47 + .../non_hmg_employee_swipe_view.dart | 84 + .../swipe_module/swipe_history_view.dart | 189 ++ .../swipe_module/swipe_success_view.dart | 60 + .../swipe_module/utils/location_utils.dart | 67 + .../utils/swipe_general_utils.dart | 530 +++++ localization_error.txt | 3 +- 45 files changed, 4045 insertions(+), 989 deletions(-) create mode 100644 assets/images/ic_done.png create mode 100644 assets/images/ic_nfc.png create mode 100644 assets/images/nfc_icon.svg create mode 100644 assets/images/success_swipe.svg create mode 100644 assets/images/swipe.svg create mode 100644 assets/images/swipe_success.svg create mode 100644 assets/images/wifi_icon.svg create mode 100644 lib/models/new_models/swipe_transaction_history.dart create mode 100644 lib/models/new_models/swipe_transaction_model.dart create mode 100644 lib/models/new_models/verify_otp_model.dart create mode 100644 lib/new_views/forget_password_module/forget_passwod_verify_otp.dart create mode 100644 lib/new_views/forget_password_module/reset_password_view.dart create mode 100644 lib/new_views/swipe_module/circular_animated_widget.dart create mode 100644 lib/new_views/swipe_module/dialoge/confirm_dialog.dart create mode 100644 lib/new_views/swipe_module/dialoge/nfc_reader_sheet.dart create mode 100644 lib/new_views/swipe_module/dialoge/success_dialog.dart create mode 100644 lib/new_views/swipe_module/enums/swipe_type.dart create mode 100644 lib/new_views/swipe_module/mark_attendance_widget.dart create mode 100644 lib/new_views/swipe_module/models/swipe_model.dart create mode 100644 lib/new_views/swipe_module/models/swipe_transaction_history.dart create mode 100644 lib/new_views/swipe_module/models/swipe_transaction_model.dart create mode 100644 lib/new_views/swipe_module/non_hmg_employee_swipe_view.dart create mode 100644 lib/new_views/swipe_module/swipe_history_view.dart create mode 100644 lib/new_views/swipe_module/swipe_success_view.dart create mode 100644 lib/new_views/swipe_module/utils/location_utils.dart create mode 100644 lib/new_views/swipe_module/utils/swipe_general_utils.dart diff --git a/assets/images/ic_done.png b/assets/images/ic_done.png new file mode 100644 index 0000000000000000000000000000000000000000..5b802855ce8942032b0948c1fdaa56e885b4c78e GIT binary patch literal 18338 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 literal 0 HcmV?d00001 diff --git a/assets/images/ic_nfc.png b/assets/images/ic_nfc.png new file mode 100644 index 0000000000000000000000000000000000000000..274e1b8c0d81daca3127e8a2b6d0522b43d6bb8d GIT binary patch literal 19686 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 literal 0 HcmV?d00001 diff --git a/assets/images/nfc_icon.svg b/assets/images/nfc_icon.svg new file mode 100644 index 00000000..11e61f7c --- /dev/null +++ b/assets/images/nfc_icon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/assets/images/success_swipe.svg b/assets/images/success_swipe.svg new file mode 100644 index 00000000..499c58c7 --- /dev/null +++ b/assets/images/success_swipe.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/images/swipe.svg b/assets/images/swipe.svg new file mode 100644 index 00000000..d389f93a --- /dev/null +++ b/assets/images/swipe.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/images/swipe_success.svg b/assets/images/swipe_success.svg new file mode 100644 index 00000000..68021f2e --- /dev/null +++ b/assets/images/swipe_success.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/assets/images/wifi_icon.svg b/assets/images/wifi_icon.svg new file mode 100644 index 00000000..645396d9 --- /dev/null +++ b/assets/images/wifi_icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/lib/controllers/api_routes/api_manager.dart b/lib/controllers/api_routes/api_manager.dart index 3dd80f7c..cefbbe9f 100644 --- a/lib/controllers/api_routes/api_manager.dart +++ b/lib/controllers/api_routes/api_manager.dart @@ -50,7 +50,7 @@ class ApiManager { return response; } - Future post(String url, {Map? headers, required Map body}) async { + Future post(String url, {Map? headers, required Map body, bool ?showToast = true}) async { headers ??= {}; headers.addAll(_headers); @@ -74,7 +74,7 @@ class ApiManager { } else { if (jsonDecode(response.body) is Map) { final message = jsonDecode(response.body)["message"]; - if (message != null && message.toString().isNotEmpty) { + if (message != null && message.toString().isNotEmpty&&showToast!) { Fluttertoast.showToast(msg: message ?? "", toastLength: Toast.LENGTH_LONG); } } @@ -117,7 +117,7 @@ class ApiManager { return response; } - Future postWithOutBody(String url, {Map? headers}) async { + Future postWithOutBody(String url, {Map? headers,bool showLoading = true}) async { headers ??= {}; headers.addAll(_headers); @@ -139,7 +139,7 @@ class ApiManager { } else { if (jsonDecode(response.body) is Map) { final message = jsonDecode(response.body)["message"]; - if (message != null && message.toString().isNotEmpty) { + if (message != null && message.toString().isNotEmpty&&showLoading) { Fluttertoast.showToast(msg: message ?? "", toastLength: Toast.LENGTH_LONG); } } diff --git a/lib/controllers/api_routes/urls.dart b/lib/controllers/api_routes/urls.dart index 2062185b..45d4f56b 100644 --- a/lib/controllers/api_routes/urls.dart +++ b/lib/controllers/api_routes/urls.dart @@ -76,9 +76,10 @@ class URLs { static get sendOtpUrl=> '$_baseUrl/SmsNotification/SendOTP/'; static get verifyOtpUrl=> '$_baseUrl/SmsNotification/VerifyOTP/'; // 08051 - //Swipe module Apis need to ask backend why base url changed for this api.. + //Swipe module Api.. static get swipeUrl=> '$_baseUrl/Swipe/Swipe'; static get getSwipeLastTransactionUrl=> '$_baseUrl/Swipe/GetLastTransaction'; + static get getSwipeTransactionHistoryUrl=> '$_baseUrl/Swipe/GetTransactions'; //service request..... static get getServiceRequests => "$_baseUrl/CallRequest/GetCallRequests"; // get diff --git a/lib/controllers/providers/api/user_provider.dart b/lib/controllers/providers/api/user_provider.dart index eacef64d..53ba507b 100644 --- a/lib/controllers/providers/api/user_provider.dart +++ b/lib/controllers/providers/api/user_provider.dart @@ -6,7 +6,12 @@ import 'package:http/http.dart'; import 'package:test_sa/controllers/api_routes/api_manager.dart'; import 'package:test_sa/controllers/api_routes/urls.dart'; import 'package:test_sa/models/new_models/general_response_model.dart'; +import 'package:test_sa/models/new_models/update_password.dart'; +import 'package:test_sa/models/new_models/verify_otp_model.dart'; import 'package:test_sa/models/user.dart'; +import 'package:test_sa/new_views/swipe_module/models/swipe_model.dart'; +import 'package:test_sa/new_views/swipe_module/models/swipe_transaction_history.dart'; +import 'package:test_sa/new_views/swipe_module/models/swipe_transaction_model.dart'; import '../../../new_views/common_widgets/app_lazy_loading.dart'; @@ -26,6 +31,31 @@ class UserProvider extends ChangeNotifier { File? profileImage; + VerifyOtpModel _verifyOtpModel= VerifyOtpModel(); + SwipeTransaction _swipeTransactionModel = SwipeTransaction(); + List _swipeHistory = []; + + SwipeTransaction get swipeTransactionModel => _swipeTransactionModel; + + List get swipeHistory => _swipeHistory; + + set swipeHistory(List value) { + _swipeHistory = value; + notifyListeners(); + } + + set swipeTransactionModel(SwipeTransaction value) { + _swipeTransactionModel = value; + notifyListeners(); + } + + VerifyOtpModel get verifyOtpModel => _verifyOtpModel; + + set verifyOtpModel(VerifyOtpModel value) { + _verifyOtpModel = value; + notifyListeners(); + } + void setUser(User user) { _user = user; ApiManager.instance.user = user; @@ -118,93 +148,91 @@ class UserProvider extends ChangeNotifier { // return response.statusCode; // } Future sendForgetPasswordOtp({required BuildContext context, required String employeeId}) async { - GeneralResponseModel responseModel= GeneralResponseModel(responseCode: -1) ; - if (_loading == true) return responseModel; + GeneralResponseModel generalResponseModel = GeneralResponseModel(responseCode: -1); + if (_loading == true) return generalResponseModel; _loading = true; notifyListeners(); Response response; try { - showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); - response = await ApiManager.instance.postWithOutBody( - URLs.sendOtpUrl + '?$employeeId', - ); - responseModel = GeneralResponseModel.fromJson(json.decode(response.body)); + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + response = await ApiManager.instance.postWithOutBody(URLs.sendForgetPasswordOtp + '?employeeId=$employeeId', showLoading: false); + generalResponseModel = GeneralResponseModel.fromJson(json.decode(response.body)); _loading = false; if (response.statusCode >= 200 && response.statusCode < 300) { notifyListeners(); Navigator.pop(context); - return responseModel; + return generalResponseModel; } notifyListeners(); Navigator.pop(context); - return responseModel; + return generalResponseModel; } catch (error) { // debugPrint(error); Navigator.pop(context); _loading = false; notifyListeners(); - return responseModel; + return generalResponseModel; } } - Future forgetPasswordValidateOtp({required BuildContext context, required String employeeId, required String otp}) async { - GeneralResponseModel responseModel= GeneralResponseModel(responseCode: -1) ; - if (_loading == true) return responseModel; + GeneralResponseModel generalResponseModel = GeneralResponseModel(responseCode: -1); + // if (_loading == true) return generalResponseModel; _loading = true; notifyListeners(); Response response; try { - showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); response = await ApiManager.instance.postWithOutBody( - URLs.sendOtpUrl + '?employeeId=$employeeId&otp=$otp', + URLs.sendForgetPasswordValidateOtp + '?employeeId=$employeeId&otp=$otp', ); - responseModel = GeneralResponseModel.fromJson(json.decode(response.body)); + generalResponseModel = GeneralResponseModel.fromJson(json.decode(response.body)); _loading = false; if (response.statusCode >= 200 && response.statusCode < 300) { + if (generalResponseModel.data != null) { + verifyOtpModel = VerifyOtpModel.fromJson(generalResponseModel.data); + } notifyListeners(); Navigator.pop(context); - return responseModel; + return generalResponseModel; } notifyListeners(); Navigator.pop(context); - return responseModel; + return generalResponseModel; } catch (error) { // debugPrint(error); Navigator.pop(context); _loading = false; notifyListeners(); - return responseModel; + return generalResponseModel; } } - Future updateNewPassword({required BuildContext context, required String userId}) async { - GeneralResponseModel responseModel= GeneralResponseModel(responseCode: -1) ; - if (_loading == true) return responseModel; + Future updateNewPassword({required BuildContext context, required UpdatePasswordModel updatePasswordModel}) async { + GeneralResponseModel generalResponseModel = GeneralResponseModel(responseCode: -1); + if (_loading == true) return generalResponseModel; _loading = true; notifyListeners(); Response response; try { - showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); - response = await ApiManager.instance.postWithOutBody( - URLs.sendOtpUrl + userId, - ); - responseModel = GeneralResponseModel.fromJson(json.decode(response.body)); + showDialog(context: context, barrierDismissible: false, builder: (context) => const AppLazyLoading()); + response = await ApiManager.instance.post(URLs.updateNewPassword, body: updatePasswordModel.toJson()); + generalResponseModel = GeneralResponseModel.fromJson(json.decode(response.body)); _loading = false; if (response.statusCode >= 200 && response.statusCode < 300) { notifyListeners(); Navigator.pop(context); - return responseModel; + return generalResponseModel; } notifyListeners(); Navigator.pop(context); - return responseModel; + return generalResponseModel; } catch (error) { // debugPrint(error); Navigator.pop(context); _loading = false; notifyListeners(); - return responseModel; + return generalResponseModel; } } Future uploadProfileImage(String userId, File image) async { @@ -233,6 +261,75 @@ class UserProvider extends ChangeNotifier { return -1; } } + Future makeSwipe({required Swipe model}) async { + isLoading = true; + SwipeModel swipeResponse = SwipeModel(data: false, message: '', responseCode: 0, isSuccess: false); + notifyListeners(); + Response response; + // try { + response = await ApiManager.instance.post(URLs.swipeUrl, body: model.toJson(), showToast: false); + swipeResponse = SwipeModel.fromJson(json.decode(response.body)); + + if (response.statusCode >= 200 && response.statusCode < 300) { + isUserConfirmSwipe = true; + await getSwipeLastTransaction(userId: user!.userID!); + notifyListeners(); + } + + notifyListeners(); + return swipeResponse; + // } catch (error) { + // notifyListeners(); + // return swipeResponse; + // } + } + + Future getSwipeLastTransaction({required String userId}) async { + isLoading = true; + notifyListeners(); + Response response; + var body = { + "userId": userId, + }; + try { + response = await ApiManager.instance.post(URLs.getSwipeLastTransactionUrl, body: body); + + if (response.statusCode >= 200 && response.statusCode < 300) { + swipeTransactionModel = SwipeTransaction.fromJson(json.decode(response.body)['data']); + } + notifyListeners(); + return response.statusCode; + } catch (error) { + notifyListeners(); + return -1; + } + } + + Future getSwipeTransactionHistory({required String userId, required DateTime dateFrom,required DateTime dateTo}) async { + isLoading = true; + notifyListeners(); + Response response; + var body = { + "userId": userId, + "dateFrom": dateFrom.toIso8601String(), + "dateTo": dateTo.toIso8601String(), + }; + try { + response = await ApiManager.instance.post(URLs.getSwipeTransactionHistoryUrl, body: body); + + if (response.statusCode >= 200 && response.statusCode < 300) { + List dataList = GeneralResponseModel.fromJson(json.decode(response.body)).data; + swipeHistory = List.generate(dataList.length, (index) => SwipeHistory.fromJson(dataList[index])); + } + isLoading = false; + notifyListeners(); + return response.statusCode; + } catch (error) { + isLoading = false; + notifyListeners(); + return -1; + } + } // Future updateProfile({ // required String host, diff --git a/lib/dashboard_latest/dashboard_provider.dart b/lib/dashboard_latest/dashboard_provider.dart index 0e24590d..ff6df398 100644 --- a/lib/dashboard_latest/dashboard_provider.dart +++ b/lib/dashboard_latest/dashboard_provider.dart @@ -270,6 +270,7 @@ class DashBoardProvider extends ChangeNotifier { if (isHighPriority) body["isHighPriority"] = true; if (isOverdue) body["isOverdue"] = true; response = await ApiManager.instance.post(url, body: body); + print('data i got is ${response.body}'); stateCode = response.statusCode; if (response.statusCode >= 200 && response.statusCode < 300) { diff --git a/lib/dashboard_latest/dashboard_view.dart b/lib/dashboard_latest/dashboard_view.dart index 881b0cda..3caa6abf 100644 --- a/lib/dashboard_latest/dashboard_view.dart +++ b/lib/dashboard_latest/dashboard_view.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:nfc_manager/nfc_manager.dart'; import 'package:provider/provider.dart'; import 'package:test_sa/controllers/notification/firebase_notification_manger.dart'; import 'package:test_sa/controllers/notification/notification_manger.dart'; @@ -12,10 +13,14 @@ import 'package:test_sa/dashboard_latest/dashboard_provider.dart'; import 'package:test_sa/dashboard_latest/widgets/app_bar_widget.dart'; import 'package:test_sa/dashboard_latest/widgets/progress_fragment.dart'; import 'package:test_sa/dashboard_latest/widgets/requests_fragment.dart'; +import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/models/enums/user_types.dart'; import 'package:test_sa/models/user.dart'; -import 'package:test_sa/service_request_latest/service_request_detail_provider.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/swipe_module/circular_animated_widget.dart'; +import 'package:test_sa/new_views/swipe_module/utils/swipe_general_utils.dart'; import 'widgets/request_category_fragment.dart'; @@ -79,28 +84,12 @@ class _DashboardViewState extends State { }); } - // Future getAllRequests() async { - // allRequestsProvider.isAllLoading = true; - // allRequestsProvider.isFilterRequestLoading = true; - // allRequestsProvider.currentListIndex = 0; - // allRequestsProvider.filterRequest = null; - // var tabs = RequestUtils.getTabs(userType: userProvider.user!.type!, context: context); - // allRequestsProvider.status = tabs[0]['status']; - // allRequestsProvider.getRequests(); - // allRequestsProvider.pageNum = 1; - // dashBoardProvider.getRequestDetail(usersType: userProvider.user!.type!, status: tabs[0]['status']); - // allRequestsProvider.getFilterRequests(showLoader: true, status: tabs[0]['status']).whenComplete(() { - // allRequestsProvider.requestDetailList = allRequestsProvider.filterRequest; - // }); - // allRequestsProvider.isAllLoading = false; - // notificationsProvider.getSystemNotifications(user: userProvider.user!, resetProvider: true); - // } + void handleScroll() async { _scrollController = ScrollController(); _scrollController.addListener(() async { if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent && !_dashBoardProvider.isDetailLoading) { - print('handle scroll called..'); _dashBoardProvider.pageNum = _dashBoardProvider.pageNum + 1; _dashBoardProvider.getRequestDetail( usersType: user.type!, showLoader: false, status: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].tag, tabId: _dashBoardProvider.tabs[_dashBoardProvider.currentListIndex].id); @@ -117,30 +106,73 @@ class _DashboardViewState extends State { @override Widget build(BuildContext context) { bool isNurse = (Provider.of(context, listen: false).user!.type) == UsersTypes.normal_user; + final user = Provider.of(context, listen: false).user; return Scaffold( - // backgroundColor: AppColor.background(context), appBar: PreferredSize( preferredSize: const Size.fromHeight(kToolbarHeight), child: AppBarWidget( onDrawerPress: widget.onDrawerPress, )), - body: RefreshIndicator( - onRefresh: () async { - getRequests(); - return Future.delayed(const Duration(milliseconds: 250)); - }, - child: Scrollbar( - controller: _scrollController, - child: SingleChildScrollView( - // physics: AlwaysScrollableScrollPhysics(), - controller: _scrollController, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [16.height, ProgressFragment(), 16.height, if (!isNurse) const RequestsFragment(), const RequestCategoryFragment()], + body: Stack( + children: [ + RefreshIndicator( + onRefresh: () async { + getRequests(); + return Future.delayed(const Duration(milliseconds: 250)); + }, + child: Scrollbar( + controller: _scrollController, + child: SingleChildScrollView( + // physics: AlwaysScrollableScrollPhysics(), + controller: _scrollController, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [16.height, ProgressFragment(), 16.height, if (!isNurse) const RequestsFragment(), const RequestCategoryFragment()], + ), + ), ), ), - ), + //TODO need to fix the alignment when no data is found + if (user!=null&&user.employeeIsHMG==false) + Positioned( + right: user.type == UsersTypes.engineer ? 20.toScreenWidth : null, + left: user.type != UsersTypes.engineer ? 20.toScreenWidth : null, + bottom: 20.toScreenHeight, + child: GestureDetector( + onTap: () async { + bool isNfcSupported = await NfcManager.instance.isAvailable(); + SwipeGeneralUtils.instance.showSwipeTypeBottomSheetSheet(isNfcSupported: isNfcSupported); + }, + child: CircularAnimatedContainer(child: Container( + width: 100.toScreenWidth, + height: 100.toScreenHeight, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: AppColor.white10, + border: Border.all(color: AppColor.primary10.withOpacity(0.5), width: 2), + ), + child: Consumer( + builder: (context, userProvider,child) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + 'swipe'.toSvgAsset(width: 32, height: 32), + 8.height, + Text( + ("${context.translation.checkIn}\n${userProvider.swipeTransactionModel.swipeTime != null ? SwipeGeneralUtils.instance.formatTimeOnly(userProvider.swipeTransactionModel.swipeTime!) : '--:--'}"), + style: AppTextStyles.bodyText2.copyWith(color: AppColor.white936, fontWeight: FontWeight.w500, fontFamily: "Poppins"), + ), + + ], + ); + } + ), + ),), + ), + ), + ], ), ); } diff --git a/lib/dashboard_latest/widgets/request_category_fragment.dart b/lib/dashboard_latest/widgets/request_category_fragment.dart index 9c6d4bf5..cf77a2e4 100644 --- a/lib/dashboard_latest/widgets/request_category_fragment.dart +++ b/lib/dashboard_latest/widgets/request_category_fragment.dart @@ -21,6 +21,8 @@ class RequestCategoryFragment extends StatelessWidget { Widget build(BuildContext context) { return Consumer(builder: (context, dashboardProvider, child) { bool isNurse = (Provider.of(context, listen: false).user?.type) == UsersTypes.normal_user; + debugPrint('condition i got is ${dashboardProvider.requestDetailList }'); + return Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ //TODO uncommit this to get the calendar card.. !isNurse && dashboardProvider.tabs.isNotEmpty && dashboardProvider.tabs[dashboardProvider.currentListIndex].tag == 0 @@ -53,7 +55,7 @@ class RequestCategoryFragment extends StatelessWidget { context: context, userType: Provider.of(context, listen: false).user?.type!, ), - dashboardProvider.isDetailLoading || dashboardProvider.requestDetailList == null + dashboardProvider.isDetailLoading ? Column( mainAxisSize: MainAxisSize.max, children: List.generate(3, (index) { @@ -62,7 +64,7 @@ class RequestCategoryFragment extends StatelessWidget { child: const SizedBox().toRequestShimmer(context, dashboardProvider.isDetailLoading), ); })).paddingOnly(start: 16, end: 16, top: 16) - : (dashboardProvider.requestDetailList?.data?.isEmpty ?? true) + : (dashboardProvider.requestDetailList==null||dashboardProvider.requestDetailList?.data?.isEmpty==true) ? const NoDataFound().paddingOnly(top: 50).center : RequestCategoryList(dashboardProvider.requestDetailList?.data ?? [], dashboardProvider.isDetailLoading, dashboardProvider.requestDetailList?.totalRows ?? 0), ]); diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb index 1c26a336..8ce8856c 100644 --- a/lib/l10n/app_ar.arb +++ b/lib/l10n/app_ar.arb @@ -511,5 +511,20 @@ "youCanSetTheReminderInAlarmToRemindYouBeforeVisit": "يمكنك تعيين التذكير في المنبه لتذكيرك قبل الزيارة. الرجاء اختيار الوقت أدناه", "setReminder": "تعيين التذكير", "provideQrCodeToEngineer": "قدم رمز الاستجابة السريعة أدناه للمهندس للتحقق من وصوله", - "callResponse": "استجابة الاتصال" + "callResponse": "استجابة الاتصال", + "newPassword": "كلمة المرور الجديدة", + "otpVerification": "التحقق من OTP", + "pleaseEnterTheOtpSentTo": "يرجى إدخال OTP المرسل إلى", + "resetPassword": "إعادة تعيين كلمة المرور", + "pleaseEnterTheNewPassword": "يرجى إدخال كلمة المرور الجديدة", + "verify": "تحقق", + "employeeIdIsRequired": "رقم الموظف مطلوب", + "youHaveSuccessfullyMarkedYourAttendance": "لقد قمت بتسجيل حضورك بنجاح", + "markAttendance": "تسجيل الحضور", + "selectMethodToMarkAttendance": "اختر الطريقة لتسجيل الحضور", + "swipeTypeName": "اسم نوع السحب", + "userName": "اسم المستخدم", + "siteName": "اسم الموقع", + "pointName": "اسم النقطة", + "checkIn": "تسجيل الدخول" } \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index fe1a160a..74f488b9 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -514,5 +514,21 @@ "doYouWantToSetReminder": "Do you want to set reminder?", "youCanSetTheReminderInAlarmToRemindYouBeforeVisit": "You can set the reminder in alarm to remind you before visit. Please select the time below", "setReminder": "Set Reminder", - "searchAsset" : "Search Asset" + "searchAsset" : "Search Asset", + "newPassword": "New Password", + "resetPassword": "Reset Password", + "otpVerification": "OTP Verification", + "pleaseEnterTheOtpSentTo": "Please enter the OTP sent to", + "pleaseEnterTheNewPassword": "Please enter the new password", + "markAttendance": "Mark Attendance", + "selectMethodToMarkAttendance": "Select the method to mark the attendance", + "verify": "Verify", + "employeeIdIsRequired": "Employee Id is required", + "successful": "Successful", + "youHaveSuccessfullyMarkedYourAttendance": "You have successfully marked your attendance", + "swipeTypeName": "Swipe Type Name", + "userName": "User Name", + "siteName": "Site Name", + "pointName": "Point Name", + "checkIn": "Check in" } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index c3079aec..cff76122 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -30,6 +30,8 @@ import 'package:test_sa/new_views/pages/login_page.dart'; import 'package:test_sa/new_views/pages/report_bug_page.dart'; import 'package:test_sa/new_views/pages/settings_page.dart'; import 'package:test_sa/new_views/pages/splash_page.dart'; +import 'package:test_sa/new_views/swipe_module/swipe_history_view.dart'; +import 'package:test_sa/new_views/swipe_module/swipe_success_view.dart'; import 'package:test_sa/providers/asset_transfer/asset_transfer_status_provider.dart'; import 'package:test_sa/providers/department_provider.dart'; import 'package:test_sa/providers/gas_request_providers/cylinder_size_provider.dart'; @@ -251,6 +253,7 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => CommentsProvider()), ChangeNotifierProvider(create: (_) => GasRefillCommentsProvider()), + ///todo deleted //ChangeNotifierProvider(create: (_) => RequestStatusProvider()), ChangeNotifierProvider(create: (_) => VendorProvider()), @@ -309,6 +312,8 @@ class MyApp extends StatelessWidget { ProfilePage.id: (_) => const ProfilePage(), ReportBugPage.id: (_) => const ReportBugPage(), HelpCenterPage.id: (_) => const HelpCenterPage(), + SwipeSuccessView.routeName: (_) => const SwipeSuccessView(), + SwipeHistoryView.routeName: (_) => const SwipeHistoryView(), }, ), ), diff --git a/lib/models/new_models/general_response_model.dart b/lib/models/new_models/general_response_model.dart index 64c7d031..97f202a8 100644 --- a/lib/models/new_models/general_response_model.dart +++ b/lib/models/new_models/general_response_model.dart @@ -1,10 +1,10 @@ class GeneralResponseModel { - bool? data; + dynamic data; String? message; - String? title; - String? innerMessage; - int? responseCode; - bool? isSuccess; + String ?title; + String ?innerMessage; + int ?responseCode; + bool ?isSuccess; GeneralResponseModel({this.data, this.message, this.title, this.innerMessage, this.responseCode, this.isSuccess}); diff --git a/lib/models/new_models/swipe_model.dart b/lib/models/new_models/swipe_model.dart index 54868703..030ac00a 100644 --- a/lib/models/new_models/swipe_model.dart +++ b/lib/models/new_models/swipe_model.dart @@ -1,27 +1,29 @@ +import 'package:flutter/material.dart'; + class SwipeModel { - final bool data; - final String message; - final String? title; - final String? innerMessage; - final int responseCode; - final bool isSuccess; + final bool? data; + final String? message; + final String ?title; + final String ?innerMessage; + final int ?responseCode; + final bool ?isSuccess; SwipeModel({ - required this.data, - required this.message, + this.data, + this.message, this.title, this.innerMessage, - required this.responseCode, - required this.isSuccess, + this.responseCode, + this.isSuccess, }); factory SwipeModel.fromJson(Map json) { return SwipeModel( data: json['data'] as bool, message: json['message'] as String, - title: json['title'] as String?, - innerMessage: json['innerMessage'] as String?, + title: json['title'] as String, + innerMessage: json['innerMessage'] as String, responseCode: json['responseCode'] as int, isSuccess: json['isSuccess'] as bool, ); @@ -42,16 +44,16 @@ class SwipeModel { } class Swipe { - final int swipeTypeValue; - final String value; - final double ?latitude; - final double ?longitude; + final int ?swipeTypeValue; + final String? value; + final double? latitude; + final double? longitude; Swipe({ - required this.swipeTypeValue, - required this.value, - required this.latitude, - required this.longitude, + this.swipeTypeValue, + this.value, + this.latitude, + this.longitude, }); Map toJson() { diff --git a/lib/models/new_models/swipe_transaction_history.dart b/lib/models/new_models/swipe_transaction_history.dart new file mode 100644 index 00000000..7bb0137a --- /dev/null +++ b/lib/models/new_models/swipe_transaction_history.dart @@ -0,0 +1,47 @@ +class SwipeHistory { + final int ?id; + final String? swipeTypeName; + final String? userName; + final String ?siteName; + final String ?pointName; + final String ?swipeTime; + final bool ?isSuccess; + final String? errorMessage; + + SwipeHistory({ + this.id, + this.swipeTypeName, + this.userName, + this.siteName, + this.pointName, + this.swipeTime, + this.isSuccess, + this.errorMessage, + }); + + factory SwipeHistory.fromJson(Map json) { + return SwipeHistory( + id: json['id'], + swipeTypeName: json['swipeTypeName'], + userName: json['userName'], + siteName: json['siteName'], + pointName: json['pointName'], + swipeTime: json['swipeTime']!=null? DateTime.parse(json['swipeTime']).toIso8601String():'', + isSuccess: json['isSuccess'], + errorMessage: json['errorMessage'], + ); + } + + Map toJson() { + return { + 'id': id, + 'swipeTypeName': swipeTypeName, + 'userName': userName, + 'siteName': siteName, + 'pointName': pointName, + 'swipeTime': swipeTime, + 'isSuccess': isSuccess, + 'errorMessage': errorMessage, + }; + } +} diff --git a/lib/models/new_models/swipe_transaction_model.dart b/lib/models/new_models/swipe_transaction_model.dart new file mode 100644 index 00000000..ff1d82fd --- /dev/null +++ b/lib/models/new_models/swipe_transaction_model.dart @@ -0,0 +1,47 @@ +class SwipeTransaction { + final int ?id; + final String? swipeTypeName; + final String ?userName; + final String? siteName; + final String? pointName; + final DateTime? swipeTime; + final bool? isSuccess; + final String? errorMessage; + + SwipeTransaction({ + this.id, + this.swipeTypeName, + this.userName, + this.siteName, + this.pointName, + this.swipeTime, + this.isSuccess, + this.errorMessage, + }); + + factory SwipeTransaction.fromJson(Map json) { + return SwipeTransaction( + id: json['id'] as int, + swipeTypeName: json['swipeTypeName'] , + userName: json['userName'] , + siteName: json['siteName'] , + pointName: json['pointName'] , + swipeTime: DateTime.parse(json['swipeTime']), + isSuccess: json['isSuccess'] , + errorMessage: json['errorMessage'] , + ); + } + + // Map toJson() { + // return { + // 'id': id, + // 'swipeTypeName': swipeTypeName, + // 'userName': userName, + // 'siteName': siteName, + // 'pointName': pointName, + // 'swipeTime': swipeTime.toIso8601String(), + // 'isSuccess': isSuccess, + // 'errorMessage': errorMessage, + // }; + // } +} diff --git a/lib/models/new_models/update_password.dart b/lib/models/new_models/update_password.dart index 34d7ec52..e66d7361 100644 --- a/lib/models/new_models/update_password.dart +++ b/lib/models/new_models/update_password.dart @@ -1,30 +1,23 @@ -class UpdatePassword { - final String password; - final String confirmPassword; - final String email; - final String token; +import 'package:flutter/material.dart'; - UpdatePassword({ +class UpdatePasswordModel { + final String? password; + final String ?confirmPassword; + final String ?userId; + final String ?token; + + UpdatePasswordModel({ required this.password, required this.confirmPassword, - required this.email, + required this.userId, required this.token, }); - factory UpdatePassword.fromJson(Map json) { - return UpdatePassword( - password: json['password'] as String, - confirmPassword: json['confirmPassword'] as String, - email: json['email'] as String, - token: json['token'] as String, - ); - } - Map toJson() { return { 'password': password, 'confirmPassword': confirmPassword, - 'email': email, + 'userId': userId, 'token': token, }; } diff --git a/lib/models/new_models/verify_otp_model.dart b/lib/models/new_models/verify_otp_model.dart new file mode 100644 index 00000000..a1dfbb49 --- /dev/null +++ b/lib/models/new_models/verify_otp_model.dart @@ -0,0 +1,41 @@ + + +import 'package:flutter/material.dart'; + +class VerifyOtpModel { + final String ?userId; + final String ?userName; + final String ?email; + final String ?token; + final DateTime? validTo; + + VerifyOtpModel({ + this.userId, + this.userName, + this.email, + this.token, + this.validTo, + }); + + // Factory constructor to create an instance from a JSON map + factory VerifyOtpModel.fromJson(Map json) { + return VerifyOtpModel( + userId: json['userId'], + userName: json['userName'], + email: json['email'], + token: json['token'], + validTo: DateTime.parse(json['validTo']), + ); + } + + // Convert the instance to a JSON map + Map toJson() { + return { + 'userId': userId, + 'userName': userName, + 'email': email, + 'token': token, + 'validTo': validTo?.toIso8601String(), + }; + } +} diff --git a/lib/models/user.dart b/lib/models/user.dart index 40e95b58..ac3ace61 100644 --- a/lib/models/user.dart +++ b/lib/models/user.dart @@ -36,6 +36,10 @@ class User { bool? lockoutEnabled; int? accessFailedCount; List? assetGroups; + bool? employeeIsHMG; + bool? enableWifi; + bool? enableNFC; + bool?enableQR; User({ this.clientId, @@ -68,6 +72,10 @@ class User { this.lockoutEnd, this.lockoutEnabled, this.accessFailedCount, + this.employeeIsHMG, + this.enableNFC, + this.enableQR, + this.enableWifi, }); bool get isLiveToken => tokenlife != null && (DateTime.tryParse(tokenlife!)?.isAfter(DateTime.now()) ?? false); @@ -150,6 +158,10 @@ class User { map['lockoutEnd'] = lockoutEnd; map['lockoutEnabled'] = lockoutEnabled; map['accessFailedCount'] = accessFailedCount; + map['employeeIsHMG'] = employeeIsHMG; + map['enableWifi'] = enableWifi; + map['enableNFC'] = enableNFC; + map['enableQR'] = enableQR; return map; } @@ -207,6 +219,10 @@ class User { lockoutEnd = json['lockoutEnd']; lockoutEnabled = json['lockoutEnabled']; accessFailedCount = json['accessFailedCount']; + employeeIsHMG = json['employeeIsHMG']; + enableWifi = json['enableWifi']; + enableNFC = json['enableNFC']; + enableQR = json['enableQR']; } } diff --git a/lib/new_views/common_widgets/app_drawer.dart b/lib/new_views/common_widgets/app_drawer.dart index 034f427c..0558822d 100644 --- a/lib/new_views/common_widgets/app_drawer.dart +++ b/lib/new_views/common_widgets/app_drawer.dart @@ -6,6 +6,7 @@ import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/pages/login_page.dart'; +import 'package:test_sa/new_views/swipe_module/swipe_history_view.dart'; import 'package:test_sa/views/app_style/sizing.dart'; import 'package:test_sa/views/pages/user/notifications/notifications_page.dart'; @@ -71,6 +72,9 @@ class AppDrawer extends StatelessWidget { // drawerItem("rate_us", context.translation.rateUs, context), // 18.height, drawerItem("setting", context.translation.settings, context).onPress(() => Navigator.of(context).pushNamed(SettingsPage.id)), + 18.height, + if(userProvider.user!=null&&userProvider.user?.employeeIsHMG==false) + drawerItem("swipe", "Swipe History", context) .onPress(() => Navigator.of(context).pushNamed(SwipeHistoryView.routeName)), // 18.height, // drawerItem("report", context.translation.reportBg, context) /*.onPress(() => Navigator.of(context).pushNamed(ReportBugPage.id))*/, // 18.height, diff --git a/lib/new_views/common_widgets/app_text_form_field.dart b/lib/new_views/common_widgets/app_text_form_field.dart index f811e452..dd8bc5f7 100644 --- a/lib/new_views/common_widgets/app_text_form_field.dart +++ b/lib/new_views/common_widgets/app_text_form_field.dart @@ -37,6 +37,7 @@ class AppTextFormField extends StatefulWidget { final Color? backgroundColor; final bool alignLabelWithHint; final bool showShadow; + final bool showBorder; final EdgeInsets? contentPadding; final bool showWithoutDecoration; final VoidCallback? onTap; @@ -57,6 +58,7 @@ class AppTextFormField extends StatefulWidget { this.initialValue, // Provide default value this.enable = true, this.showSpeechToText = false, + this.showBorder = false, this.style, this.contentPadding, this.textAlign, @@ -243,6 +245,7 @@ class _AppTextFormFieldState extends State { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), + border: widget.showBorder?Border.all(color: AppColor.white10,width: 1):null, boxShadow: widget.showShadow ? [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10)] : null, ), child: textField); diff --git a/lib/new_views/forget_password_module/forget_passwod_verify_otp.dart b/lib/new_views/forget_password_module/forget_passwod_verify_otp.dart new file mode 100644 index 00000000..e6c81c27 --- /dev/null +++ b/lib/new_views/forget_password_module/forget_passwod_verify_otp.dart @@ -0,0 +1,166 @@ + + +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:pinput/pinput.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/user_provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/new_models/general_response_model.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/forget_password_module/reset_password_view.dart'; + +class ForgetPasswordVerifyOtpView extends StatefulWidget { + Map data={}; + + ForgetPasswordVerifyOtpView({Key ?key,required this.data}) : super(key: key); + + @override + State createState() => _ForgetPasswordVerifyOtpViewState(); +} + +class _ForgetPasswordVerifyOtpViewState extends State { + String otp = ''; + Timer? _timer; + int _remainingSeconds = 180; // 3 minutes in seconds + + @override + void initState() { + super.initState(); + _startTimer(); + } + + @override + void dispose() { + _timer?.cancel(); + super.dispose(); + } + + void _startTimer() { + setState(() { + _remainingSeconds = 180; + }); + + _timer = Timer.periodic(const Duration(seconds: 1), (timer) { + if (_remainingSeconds > 0) { + setState(() { + _remainingSeconds--; + }); + } else { + _timer?.cancel(); + } + }); + } + + @override + Widget build(BuildContext context) { + final defaultPinTheme = PinTheme( + width: 68.toScreenWidth, + height: 86.toScreenHeight, + textStyle: const TextStyle( + fontSize: 22, + color: Colors.black, + ), + decoration: BoxDecoration( + color: AppColor.white10, + borderRadius: BorderRadius.circular(15), + border: Border.all(color: AppColor.white10, width: 1), + ), + ); + + final minutes = (_remainingSeconds ~/ 60).toString().padLeft(2, '0'); + final seconds = (_remainingSeconds % 60).toString().padLeft(2, '0'); + + return Scaffold( + body: Column( + children: [ + SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + context.translation.otpVerification.heading2(context).custom(color: AppColor.white936, fontWeight: FontWeight.w500), + 8.height, + '${context.translation.pleaseEnterTheOtpSentTo} ${widget.data['phoneNumber']}'.bodyText2(context).custom(color: AppColor.neutral120, fontWeight: FontWeight.w500), + 40.height, + Center( + child: Pinput( + length: 4, + defaultPinTheme: defaultPinTheme, + focusedPinTheme: defaultPinTheme.copyWith( + decoration: defaultPinTheme.decoration?.copyWith( + border: Border.all(color: AppColor.neutral40, width: 1), + ), + ), + onCompleted: (pin) { + // setState(() { + otp = pin; + //}); + verifyOtp(); + }, + ), + ), + 18.height, + Row( + // mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + InkWell( + onTap: _remainingSeconds == 0 + ? () async { + UserProvider _userProvider = Provider.of(context, listen: false); + String employeeId = widget.data['employeeId']; + GeneralResponseModel response = await _userProvider.sendForgetPasswordOtp( + context: context, + employeeId: employeeId, + ); + print('Response of send OTP: ${response.toJson()}'); + + // Restart the timer + _startTimer(); + } + : null, + child: Text( + 'Resend', + style: TextStyle( + color: _remainingSeconds == 0 ? AppColor.blueStatus(context) : AppColor.neutral40, + fontWeight: FontWeight.w500, + fontSize: 16.toScreenWidth, + decorationColor: AppColor.primary30, + decoration: _remainingSeconds == 0 ? TextDecoration.underline : null, + ), + ), + ), + 7.width, + '$minutes:$seconds'.bodyText(context).custom(color: AppColor.neutral10), + ], + ), + ], + ), + ).center.expanded, + AppFilledButton( + label: context.translation.verify, + maxWidth: true, + onPressed: verifyOtp, + ), + ], + ).paddingOnly(start: 20, end: 20, bottom: 16), + ); + } + void verifyOtp() async{ + if (otp.isNotEmpty) { + UserProvider _userProvider = Provider.of(context, listen: false); + GeneralResponseModel generalResponseModel = await _userProvider.forgetPasswordValidateOtp( + context: context, + employeeId:widget.data['employeeId'], + otp: otp, + ); + if (generalResponseModel.isSuccess==true) { + Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => ResetPasswordView())); + } + } + } +} diff --git a/lib/new_views/forget_password_module/reset_password_view.dart b/lib/new_views/forget_password_module/reset_password_view.dart new file mode 100644 index 00000000..13197c9e --- /dev/null +++ b/lib/new_views/forget_password_module/reset_password_view.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/new_models/general_response_model.dart'; +import 'package:test_sa/models/new_models/update_password.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; +import 'package:test_sa/new_views/common_widgets/app_text_form_field.dart'; +import 'package:test_sa/new_views/pages/login_page.dart'; + +import '../../controllers/providers/api/user_provider.dart'; +import '../../controllers/validator/validator.dart'; + +class ResetPasswordView extends StatefulWidget { + static const String routeName = "/resetPassword"; + + ResetPasswordView({Key? key}) : super(key: key); + + @override + State createState() => _ResetPasswordViewState(); +} + +class _ResetPasswordViewState extends State { + bool _passwordVisible = false; + bool _confirmPasswordVisible = false; + + String newPassword = ""; + String confirmPassword = ""; + + final GlobalKey _formKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return Form( + key: _formKey, + child: Scaffold( + body: Column( + children: [ + SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + context.translation.resetPassword.heading2(context).custom(color: AppColor.white936, fontWeight: FontWeight.w500), + 8.height, + context.translation.pleaseEnterTheNewPassword.bodyText2(context).custom(color: AppColor.neutral120, fontWeight: FontWeight.w500), + 32.height, + AppTextFormField( + initialValue: "", + showBorder: true, + validator: (value) => Validator.hasValue(value) ? null : context.translation.requiredField, + labelText: context.translation.password, + obscureText: !_passwordVisible, + suffixIcon: Icon( + // Based on passwordVisible state choose the icon + _passwordVisible ? Icons.visibility : Icons.visibility_off, + color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, + ).paddingOnly(end: 12).onPress(() { + setState(() { + _passwordVisible = !_passwordVisible; + }); + }), + onSaved: (value) { + newPassword = value; + }, + ), + 16.height, + AppTextFormField( + initialValue: "", + labelText: context.translation.confirmPassword, + obscureText: !_confirmPasswordVisible, + showBorder: true, + validator: (value) => Validator.isValidPassword(value!) + ? null + : value.isEmpty + ? context.translation.requiredField + : context.translation.passwordLengthMessage, + onSaved: (value) { + confirmPassword = value; + }, + suffixIcon: Icon( + _confirmPasswordVisible ? Icons.visibility : Icons.visibility_off, + color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, + ).paddingOnly(end: 12).onPress(() { + setState(() { + _confirmPasswordVisible = !_confirmPasswordVisible; + }); + }), + ), + ], + ), + ).center.expanded, + AppFilledButton(label: context.translation.resetPassword, maxWidth: true, onPressed: () => _resetPassword(context)), + ], + ).paddingOnly(start: 16, end: 16, bottom: 24, top: 24), + ), + ); + } + + Future _resetPassword(BuildContext context) async { + if (!_formKey.currentState!.validate()) return; + _formKey.currentState?.save(); + if (newPassword != confirmPassword) { + Fluttertoast.showToast(msg: 'Password not matched try again'); + } else { + UserProvider userProvider = Provider.of(context,listen: false); + UpdatePasswordModel updatePasswordModel = + UpdatePasswordModel(password: newPassword, confirmPassword: confirmPassword, userId: userProvider.verifyOtpModel.userId, token: userProvider.verifyOtpModel.token); + GeneralResponseModel generalResponseModel = await userProvider.updateNewPassword(context: context, updatePasswordModel: updatePasswordModel); + if (generalResponseModel.isSuccess==true) { + newPassword = ''; + confirmPassword = ''; + Navigator.of(context).pushNamedAndRemoveUntil(LoginPage.routeName, (routes) => true); + } + } + } +} diff --git a/lib/new_views/pages/land_page/land_page.dart b/lib/new_views/pages/land_page/land_page.dart index 4b021818..282963ed 100644 --- a/lib/new_views/pages/land_page/land_page.dart +++ b/lib/new_views/pages/land_page/land_page.dart @@ -103,7 +103,11 @@ class _LandPageState extends State { Widget build(BuildContext context) { if (_userProvider == null) { _userProvider = Provider.of(context, listen: false); - if (_userProvider!.isUserConfirmSwipe) { + if (_userProvider!.user != null && _userProvider!.user!.employeeIsHMG==false) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _userProvider!.getSwipeLastTransaction(userId: _userProvider!.user!.userID!); + }); + } _pages = [ DashboardView(onDrawerPress: (() { _scaffoldKey.currentState!.isDrawerOpen ? _scaffoldKey.currentState!.closeDrawer() : _scaffoldKey.currentState!.openDrawer(); @@ -114,7 +118,7 @@ class _LandPageState extends State { // if (_userProvider!.user!.type != UsersTypes.engineer) const CalendarPage(), const MyAssetsPage(fromBottomBar: true), ]; - } + checkLocalAuth(); } @@ -144,7 +148,7 @@ class _LandPageState extends State { )) : null, drawer: const AppDrawer(), - body: _pages.isEmpty ? MarkAttendanceWidget() : _pages[currentPageIndex], + body: _pages[currentPageIndex], bottomNavigationBar: _pages.isEmpty ? null : AppBottomNavigationBar( diff --git a/lib/new_views/pages/land_page/mark_attendance_widget.dart b/lib/new_views/pages/land_page/mark_attendance_widget.dart index ab5769b1..1ab12c67 100644 --- a/lib/new_views/pages/land_page/mark_attendance_widget.dart +++ b/lib/new_views/pages/land_page/mark_attendance_widget.dart @@ -1,866 +1,866 @@ -import 'dart:async'; -import 'dart:developer'; -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:geolocator/geolocator.dart'; -import 'package:huawei_location/huawei_location.dart'; -import 'package:nfc_manager/nfc_manager.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:provider/provider.dart'; -import 'package:test_sa/dashboard_latest/dashboard_provider.dart'; -// import 'package:platform_device_id/platform_device_id.dart'; -import 'package:test_sa/dashboard_latest/widgets/app_bar_widget.dart'; -import 'package:test_sa/extensions/enum_extensions.dart'; -import 'package:test_sa/extensions/text_extensions.dart'; -import 'package:test_sa/extensions/widget_extensions.dart'; -import 'package:test_sa/helper/utils.dart'; -import 'package:test_sa/models/enums/swipe_type.dart'; -import 'package:test_sa/models/lookup.dart'; -import 'package:test_sa/models/new_models/swipe_model.dart'; -import 'package:test_sa/nfc/nfc_reader_sheet.dart'; -import 'package:test_sa/utilities/Location.dart' as location; -import 'package:test_sa/views/widgets/dialogs/confirm_dialog.dart'; -import 'package:test_sa/views/widgets/dialogs/success_dialog.dart'; -import 'package:test_sa/views/widgets/qr_scanner_dialog.dart'; -import 'package:wifi_iot/wifi_iot.dart'; - -import '../../app_style/app_color.dart'; - -class MarkAttendanceWidget extends StatefulWidget { - // DashboardProviderModel model; - double topPadding; - bool isFromDashboard; - - MarkAttendanceWidget( {Key? key, this.topPadding = 0, this.isFromDashboard = false}) : super(key: key); - // todo MarkAttendanceWidget(this.model, {Key? key, this.topPadding = 0, this.isFromDashboard = false}) : super(key: key); - - @override - _MarkAttendanceWidgetState createState() { - return _MarkAttendanceWidgetState(); - } -} - -class _MarkAttendanceWidgetState extends State { - bool isNfcEnabled = false, isNfcLocationEnabled = false, isQrEnabled = false, isQrLocationEnabled = false, isWifiEnabled = false, isWifiLocationEnabled = false; - - int _locationUpdateCbId = 0; - - @override - void initState() { - super.initState(); - checkAttendanceAvailability(); - } - - void checkAttendanceAvailability() async { - bool isAvailable = await NfcManager.instance.isAvailable(); - // setState(() { - // AppState().privilegeListModel!.forEach((PrivilegeListModel element) { - // if (element.serviceName == "enableNFC") { - // if (isAvailable) if (element.previlege ?? false) isNfcEnabled = true; - // } else if (element.serviceName == "enableQR") { - // if (element.previlege ?? false) isQrEnabled = true; - // } else if (element.serviceName == "enableWIFI") { - // if (element.previlege ?? false) isWifiEnabled = true; - // } else if (element.serviceName!.trim() == "enableLocationNFC") { - // if (element.previlege ?? false) isNfcLocationEnabled = true; - // } else if (element.serviceName == "enableLocationQR") { - // if (element.previlege ?? false) isQrLocationEnabled = true; - // } else if (element.serviceName == "enableLocationWIFI") { - // if (element.previlege ?? false) isWifiLocationEnabled = true; - // } - // }); - // }); - } - - void checkHuaweiLocationPermission(String attendanceType) async { - // Permission_Handler permissionHandler = PermissionHandler(); - location.Location.isEnabled((bool isEnabled) async { - if (isEnabled) { - location.Location.havePermission((bool permission) async { - if (permission) { - getHuaweiCurrentLocation(attendanceType); - } else { - bool has = await requestPermissions(); - if (has) { - getHuaweiCurrentLocation(attendanceType); - } else { - showDialog( - context: context, - builder: (BuildContext cxt) => ConfirmDialog( - message: "You need to give location permission to mark attendance", - onTap: () { - Navigator.pop(context); - }, - ), - ); - } - } - }); - } else { - showDialog( - context: context, - builder: (BuildContext cxt) => ConfirmDialog( - message: "You need to enable location services to mark attendance", - onTap: () async { - Navigator.pop(context); - await Geolocator.openLocationSettings(); - }, - ), - ); - } - }); - - // if (await permissionHandler.hasLocationPermission()) { - // getHuaweiCurrentLocation(attendanceType); - // } else { - // bool has = await requestPermissions(); - // if (has) { - // getHuaweiCurrentLocation(attendanceType); - // } else { - // showDialog( - // context: context, - // builder: (BuildContext cxt) => ConfirmDialog( - // message: "You need to give location permission to mark attendance", - // onTap: () { - // Navigator.pop(context); - // }, - // ), - // ); - // } - // } - } - - Future requestPermissions() async { - var result = await [ - Permission.location, - ].request(); - return (result[Permission.location] == PermissionStatus.granted || result[Permission.locationAlways] == PermissionStatus.granted); - } - - @override - void dispose() { - super.dispose(); - // Stop Session - NfcManager.instance.stopSession(); - } - - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.only(left: 21, right: 21, bottom: 21, top: widget.topPadding), - decoration: const BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: Colors.white), - width: double.infinity, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - // LocaleKeys.markAttendance.tr().toSectionHeading(), - // LocaleKeys.selectMethodOfAttendance.tr().toText11(color: const Color(0xff535353)), - GridView( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - padding: const EdgeInsets.only(bottom: 0, top: 21), - gridDelegate: - SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: (MediaQuery.of(context).size.width < 550) ? 3 : 5, childAspectRatio: 1 / 1, crossAxisSpacing: 8, mainAxisSpacing: 8), - children: [ - attendanceMethod(SwipeTypeEnum.NFC.name, Icons.nfc, true, () { - log('i am here...nfc'); - handleSwipe(swipeType: SwipeTypeEnum.NFC, isEnable: true); - return; - // if (AppState().getIsHuawei) { - if (false) { - checkHuaweiLocationPermission("NFC"); - } else { - location.Location.isEnabled((bool isEnabled) { - if (isEnabled) { - location.Location.havePermission((bool permission) { - if (permission) { - Utils.showLoading(context); - location.Location.getCurrentLocation( - (Position position, bool isMocked) { - if (isMocked) { - Utils.hideLoading(context); - markFakeAttendance("NFC", position.latitude.toString() ?? "", position.longitude.toString() ?? ""); - } else { - Utils.hideLoading(context); - //todo performNfcAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); - } - }, - () { - Utils.hideLoading(context); - Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); - }, - context, - ); - } else { - showDialog( - context: context, - builder: (BuildContext cxt) => ConfirmDialog( - message: "You need to give location permission to mark attendance", - onTap: () async { - Navigator.pop(context); - await Geolocator.openAppSettings(); - }, - ), - ); - } - }); - } else { - showDialog( - context: context, - builder: (BuildContext cxt) => ConfirmDialog( - message: "You need to enable location services to mark attendance", - onTap: () async { - Navigator.pop(context); - await Geolocator.openLocationSettings(); - }, - ), - ); - } - }); - } - }), - //if (isWifiEnabled) //todo - attendanceMethod("Wifi", Icons.wifi, isWifiEnabled, () { - // if (AppState().getIsHuawei) { - if (false) { - checkHuaweiLocationPermission("WIFI"); - } else { - location.Location.isEnabled((bool isEnabled) { - if (isEnabled) { - location.Location.havePermission((bool permission) { - if (permission) { - Utils.showLoading(context); - location.Location.getCurrentLocation( - (Position position, bool isMocked) { - if (isMocked) { - Utils.hideLoading(context); - markFakeAttendance("WIFI", position.latitude.toString() ?? "", position.longitude.toString() ?? ""); - } else { - Utils.hideLoading(context); - //todo performWifiAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); - } - }, - () { - Utils.hideLoading(context); - Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); - }, - context, - ); - } else { - showDialog( - context: context, - builder: (BuildContext cxt) => ConfirmDialog( - message: "You need to give location permission to mark attendance", - onTap: () async { - Navigator.pop(context); - await Geolocator.openAppSettings(); - }, - ), - ); - } - }); - } else { - showDialog( - context: context, - builder: (BuildContext cxt) => ConfirmDialog( - message: "You need to enable location services to mark attendance", - onTap: () async { - Navigator.pop(context); - await Geolocator.openLocationSettings(); - }, - ), - ); - } - }); - } - }), - // if (isQrEnabled) //todo - attendanceMethod(SwipeTypeEnum.QR.name, Icons.qr_code_2, true, () async { - handleSwipe(swipeType: SwipeTypeEnum.QR, isEnable: true); - return; - // if (AppState().getIsHuawei) { - if (false) { - checkHuaweiLocationPermission("QR"); - } else { - location.Location.isEnabled((bool isEnabled) { - if (isEnabled) { - location.Location.havePermission((bool permission) { - if (permission) { - Utils.showLoading(context); - location.Location.getCurrentLocation( - (Position position, bool isMocked) { - if (isMocked) { - Utils.hideLoading(context); - markFakeAttendance("QR", position.latitude.toString() ?? "", position.longitude.toString() ?? ""); - } else { - Utils.hideLoading(context); - //todo performQrCodeAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); - } - }, - () { - Utils.hideLoading(context); - Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); - }, - context, - ); - } else { - showDialog( - context: context, - builder: (BuildContext cxt) => ConfirmDialog( - message: "You need to give location permission to mark attendance", - onTap: () async { - Navigator.pop(context); - await Geolocator.openAppSettings(); - }, - ), - ); - } - }); - } else { - showDialog( - context: context, - builder: (BuildContext cxt) => ConfirmDialog( - message: "You need to enable location services to mark attendance", - onTap: () async { - Navigator.pop(context); - await Geolocator.openLocationSettings(); - }, - ), - ); - } - }); - } - }), - ], - ) - ], - ), - ); - } - - void handleSwipe({required SwipeTypeEnum swipeType,required bool isEnable,}){ - log('handle swipe value is ${swipeType.name}'); - // if (AppState().getIsHuawei) { - if (false) { - checkHuaweiLocationPermission("NFC"); - } else { - location.Location.isEnabled((bool isEnabled) { - if (isEnabled) { - location.Location.havePermission((bool permission) { - if (permission) { - Utils.showLoading(context); - location.Location.getCurrentLocation( - (Position position, bool isMocked) { - if (isMocked) { - Utils.hideLoading(context); - markFakeAttendance(swipeType.name, position.latitude.toString() ?? "", position.longitude.toString() ?? ""); - } else { - Utils.hideLoading(context); - //todo performNfcAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); - handleSwipeOperation(swipeType: swipeType,lat: position.latitude,lang: position.longitude); - } - }, - () { - Utils.hideLoading(context); - Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); - }, - context, - ); - } else { - showDialog( - context: context, - builder: (BuildContext cxt) => ConfirmDialog( - message: "You need to give location permission to mark attendance", - onTap: () async { - Navigator.pop(context); - await Geolocator.openAppSettings(); - }, - ), - ); - } - }); - } else { - showDialog( - context: context, - builder: (BuildContext cxt) => ConfirmDialog( - message: "You need to enable location services to mark attendance", - onTap: () async { - Navigator.pop(context); - await Geolocator.openLocationSettings(); - }, - ), - ); - } - }); - } - } - - void handleSwipeOperation({required SwipeTypeEnum swipeType,double ?lat, double ?lang}){ - switch(swipeType){ - case SwipeTypeEnum.NFC: - handleNfcAttendance(latitude: lat,longitude: lang); - return; - case SwipeTypeEnum.QR: - performQrCodeAttendance(latitude: lat,longitude: lang); - return; - case SwipeTypeEnum.Wifi: - return; - - } - } - - void getHuaweiCurrentLocation(String attendanceType) async { - try { - Utils.showLoading(context); - FusedLocationProviderClient locationService = FusedLocationProviderClient()..initFusedLocationService(); - LocationRequest locationRequest = LocationRequest(); - locationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY; - locationRequest.interval = 500; - List locationRequestList = [locationRequest]; - LocationSettingsRequest locationSettingsRequest = LocationSettingsRequest(requests: locationRequestList); - - late StreamSubscription _streamSubscription; - int requestCode = (await (locationService.requestLocationUpdates(locationRequest)))!; - - _streamSubscription = locationService.onLocationData!.listen( - (Location location) async { - Utils.hideLoading(context); - await locationService.removeLocationUpdates(requestCode); - if (attendanceType == "QR") { - // todo performQrCodeAttendance(widget.model, lat: location.latitude.toString() ?? "", lng: location.longitude.toString() ?? ""); - } - if (attendanceType == "WIFI") { - // todo performWifiAttendance(widget.model, lat: location.latitude.toString() ?? "", lng: location.longitude.toString() ?? ""); - } - if (attendanceType == "NFC") { - //todo performNfcAttendance(widget.model, lat: location.latitude.toString() ?? "", lng: location.longitude.toString() ?? ""); - } - requestCode = 0; - }, - ); - - // locationService.checkLocationSettings(locationSettingsRequest).then((settings) async { - // await locationService.getLastLocation().then((value) { - // if (value.latitude == null || value.longitude == null) { - // showDialog( - // context: context, - // builder: (BuildContext cxt) => ConfirmDialog( - // message: "Unable to get your location, Please check your location settings & try again.", - // onTap: () { - // Navigator.pop(context); - // }, - // ), - // ); - // } else { - // if (attendanceType == "QR") { - // performQrCodeAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); - // } - // if (attendanceType == "WIFI") { - // performWifiAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); - // } - // if (attendanceType == "NFC") { - // performNfcAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); - // } - // } - // }).catchError((error) { - // log("HUAWEI LOCATION getLastLocation ERROR!!!!!"); - // log(error); - // }); - // }).catchError((error) { - // log("HUAWEI LOCATION checkLocationSettings ERROR!!!!!"); - // log(error); - // if (error.code == "LOCATION_SETTINGS_NOT_AVAILABLE") { - // // Location service not enabled. - // } - // }); - } catch (error) { - log("HUAWEI LOCATION ERROR!!!!!"); - log('$error'); - Utils.hideLoading(context); - // Utils.handleException(error, context, null); - } - } - - Future handleNfcAttendance({double? latitude = 0, double? longitude = 0}) async { - final dashBoardProvider = Provider.of(context, listen: false); - - if (Platform.isIOS) { - Utils.readNFc(onRead: (String nfcId) async { - await _processNfcAttendance(dashBoardProvider, nfcId, latitude, longitude); - }); - } else { - showNfcReader(context, onNcfScan: (String? nfcId) async { - await _processNfcAttendance(dashBoardProvider, nfcId ?? '', latitude, longitude); - }); - } - } - - - Future _processNfcAttendance( - DashBoardProvider dashBoardProvider, - String nfcId, - double? latitude, - double? longitude, - ) async { - Utils.showLoading(context); - try { - final swipeModel = Swipe( - swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), - value: nfcId, - latitude: latitude, - longitude: longitude, - ); - - final swipeResponse = await dashBoardProvider.makeSwipe(model: swipeModel); - log('swipe response i got is ${swipeResponse.toJson()}'); - - if (swipeResponse.responseCode != 1) { - Utils.hideLoading(context); - _showErrorDialog(swipeResponse.message ?? "Unexpected error occurred"); - } else { - final isSuccess = swipeResponse.data; - log('nfc swipe response is ${isSuccess}'); - if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3)); - Utils.hideLoading(context); - _showSuccessDialog(); - } - } catch (error) { - Utils.hideLoading(context); - // Uncomment below line for error handling if needed - // Utils.handleException(error, context, null); - } - } - - - void _showErrorDialog(String message) { - showDialog( - context: context, - builder: (context) => ConfirmDialog( - message: message, - onTap: () => Navigator.pop(context), - ), - ); - } - - void _showSuccessDialog() { - showMDialog( - context, - backgroundColor: Colors.transparent, - isDismissable: true, - child: SuccessDialog(widget.isFromDashboard), - ); - } - - - //older code.... - Future performNfcAttendance({double? lat = 0, double ?lng = 0}) async { - DashBoardProvider dashBoardProvider = Provider.of(context,listen:false); - if (Platform.isIOS) { - Utils.readNFc(onRead: (String nfcId) async { - Utils.showLoading(context); - try { - SwipeModel? swipeResponse = await dashBoardProvider.makeSwipe(model: Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: '', latitude: lat, longitude: lng)); - if (swipeResponse.responseCode != 1) { - Utils.hideLoading(context); - showDialog( - context: context, - builder: (BuildContext cxt) => ConfirmDialog( - message: swipeResponse.message ?? "Unexpected error occurred", - onTap: () { - Navigator.pop(context); - }, - ), - ); - } else { - bool status = swipeResponse.data; - if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3)); - Utils.hideLoading(context); - showMDialog( - context, - backgroundColor: Colors.transparent, - isDismissable: true, - child: SuccessDialog(widget.isFromDashboard), - ); - } - } catch (ex) { - Utils.hideLoading(context); - // Utils.handleException(ex, context, null); - } - }); - } else { - showNfcReader(context, onNcfScan: (String? nfcId) async { - Utils.showLoading(context); - try { - SwipeModel? swipeResponse = await dashBoardProvider.makeSwipe(model: Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: nfcId??'', latitude: lat, longitude: lng)); - log('api response i got is ${swipeResponse.toJson()}'); - if (swipeResponse.responseCode != 1) { - Utils.hideLoading(context); - showDialog( - context: context, - builder: (BuildContext cxt) => ConfirmDialog( - message: swipeResponse.message ?? "Unexpected error occurred", - onTap: () { - Navigator.pop(context); - }, - ), - ); - } else { - bool status = swipeResponse.data; //use this status to get transactions. - if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3)); - Utils.hideLoading(context); - showMDialog( - context, - backgroundColor: Colors.transparent, - isDismissable: true, - child: SuccessDialog(widget.isFromDashboard), - ); - } - } catch (ex) { - Utils.hideLoading(context); - // Utils.handleException(ex, context, null); - } - // Utils.showLoading(context); - // try { - // GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 2, nfcValue: nfcId ?? "", isGpsRequired: isNfcLocationEnabled, lat: lat, long: lng); - // if (g?.messageStatus != 1) { - // Utils.hideLoading(context); - // showDialog( - // context: context, - // builder: (BuildContext cxt) => ConfirmDialog( - // message: g?.errorEndUserMessage ?? "Unexpected error occurred", - // onTap: () { - // Navigator.pop(context); - // }, - // ), - // ); - // } else { - // bool status = await model.fetchAttendanceTracking(context); - // Utils.hideLoading(context); - // showMDialog( - // context, - // backgroundColor: Colors.transparent, - // isDismissable: false, - // child: SuccessDialog(widget.isFromDashboard), - // ); - // } - // } catch (ex) { - // log(ex); - // Utils.hideLoading(context); - // // Utils.handleException(ex, context, (String msg) { - // // Utils.confirmDialog(context, msg); - // // }); - // } - }); - } - } - - - - void showMDialog(context, {Widget? child, Color? backgroundColor, bool isDismissable = true, bool isBusniessCard = false}) async { - return showDialog( - context: context, - barrierDismissible: isDismissable, - builder: (context) { - return Dialog( - shape: isBusniessCard - ? const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(15.0), - ), - ) - : null, - backgroundColor: backgroundColor, - child: child, - ); - }, - ); - } - // - // Future checkSession() async { - // try { - // Utils.showLoading(context); - // await DashboardApiClient().getOpenMissingSwipes(); - // Utils.hideLoading(context); - // return true; - // } catch (ex) { - // Utils.hideLoading(context); - // Utils.handleException(ex, context, null); - // return false; - // } - // } - - - //TODO need to confirm .... - // Future performWifiAttendance({double? latitude, double? lng}) async { - // // if (Platform.isAndroid) { - // // if (!(await checkSession())) { - // // return; - // // } - // // } - // Utils.showLoading(context); - // bool isConnected = await WiFiForIoTPlugin.connect(AppState().getMohemmWifiSSID ?? "", - // password: AppState().getMohemmWifiPassword ?? "", joinOnce: Platform.isIOS ? false : true, security: NetworkSecurity.WPA, withInternet: false); - // - // if (Platform.isIOS) { - // if (await WiFiForIoTPlugin.getSSID() == AppState().getMohemmWifiSSID) { - // isConnected = true; - // } else { - // isConnected = false; - // } - // } - // - // if (isConnected && AppState().isAuthenticated) { - // await WiFiForIoTPlugin.forceWifiUsage(true); - // await Future.delayed(const Duration(seconds: 6)); - // try { - // GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 3, nfcValue: "", isGpsRequired: isWifiLocationEnabled, lat: lat, long: lng); - // bool status = await model.fetchAttendanceTracking(context); - // Utils.hideLoading(context); - // await closeWifiRequest(); - // if (g?.messageStatus == 2) { - // showDialog( - // barrierDismissible: true, - // context: context, - // builder: (cxt) => ConfirmDialog( - // message: g?.errorEndUserMessage ?? "", - // onTap: () { - // Navigator.pop(context); - // }, - // onCloseTap: () {}, - // ), - // ); - // } else { - // showMDialog( - // context, - // backgroundColor: Colors.transparent, - // isDismissable: false, - // child: SuccessDialog(widget.isFromDashboard), - // ); - // } - // } catch (ex) { - // await closeWifiRequest(); - // Utils.hideLoading(context); - // Utils.handleException(ex, context, null); - // } - // } else { - // if (AppState().isAuthenticated) { - // Utils.hideLoading(context); - // Utils.confirmDialog(context, "LocaleKeys.comeNearHMGWifi.tr()"); - // } else { - // await closeWifiRequest(); - // } - // } - // } - - Future closeWifiRequest() async { - if (Platform.isAndroid) { - await WiFiForIoTPlugin.forceWifiUsage(false); - } - return await WiFiForIoTPlugin.disconnect(); - } - - Future performQrCodeAttendance( {double ? latitude , double ?longitude}) async { - DashBoardProvider dashBoardProvider = Provider.of(context,listen: false); - var qrCodeValue = await Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) => QrScannerDialog(), - ), - ); - if (qrCodeValue != null) { - Utils.showLoading(context); - try { - final swipeModel = Swipe( - swipeTypeValue: SwipeTypeEnum.QR.getIntFromSwipeTypeEnum(), - value: qrCodeValue, - latitude: latitude, - longitude: longitude, - ); - log('model i got to scan qr is ${swipeModel.toJson()}'); - final swipeResponse = await dashBoardProvider.makeSwipe(model: swipeModel); - log('response of swipe is ${swipeResponse.toJson()}'); - - bool status = await swipeResponse.data; - Utils.hideLoading(context); - if (swipeResponse.responseCode == 2) { - showDialog( - barrierDismissible: true, - context: context, - builder: (cxt) => ConfirmDialog( - message: swipeResponse.message ?? "", - onTap: () { - Navigator.pop(context); - }, - onCloseTap: () {}, - ), - ); - } else { - showMDialog( - context, - backgroundColor: Colors.transparent, - isDismissable: true, - child: SuccessDialog(widget.isFromDashboard), - ); - } - } catch (ex) { - log('$ex'); - Utils.hideLoading(context); - //this need to confirm where it comes.. - // Utils.handleException(ex, context, null); - } - } - } - - void markFakeAttendance(dynamic sourceName, String lat, String long) async { - Utils.showLoading(context); - try { - // await DashboardApiClient().markFakeLocation(sourceName: sourceName, lat: lat, long: long); - Utils.hideLoading(context); - Utils.confirmDialog(context, "LocaleKeys.fakeLocation.tr()"); - } catch (ex) { - log('$ex'); - Utils.hideLoading(context); - //Utils.handleException(ex, context, null); - } - } - - Widget attendanceMethod(String title, IconData iconData, bool isEnabled, VoidCallback onPress) => Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), - color: isEnabled ? null : Colors.grey.withOpacity(.5), - gradient: isEnabled - ? const LinearGradient( - transform: GradientRotation(.64), - begin: Alignment.topRight, - end: Alignment.bottomLeft, - colors: [ - //ToDo set Colors according to design provided by designer... - Colors.blue, - Colors.green, - // AppColor.gradiantEndColor, - // MyColors.gradiantStartColor, - ], - ) - : null, - ), - clipBehavior: Clip.antiAlias, - padding: const EdgeInsets.only(left: 10, right: 10, top: 14, bottom: 14), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // SvgPicture.asset(image, color: Colors.white, alignment: Alignment.topLeft).expanded, - Icon(iconData, color: isEnabled ? AppColor.black35 : Colors.grey), - title.heading6(context), - // title.toText17(isBold: true, color: Colors.white), - ], - ), - ).onPress( - () { - log('isEnabled is ${!isEnabled}'); - if (!isEnabled) return; - onPress(); - }, - ); -} +// import 'dart:async'; +// import 'dart:developer'; +// import 'dart:io'; +// +// import 'package:flutter/material.dart'; +// import 'package:flutter_svg/flutter_svg.dart'; +// import 'package:geolocator/geolocator.dart'; +// import 'package:huawei_location/huawei_location.dart'; +// import 'package:nfc_manager/nfc_manager.dart'; +// import 'package:permission_handler/permission_handler.dart'; +// import 'package:provider/provider.dart'; +// import 'package:test_sa/dashboard_latest/dashboard_provider.dart'; +// // import 'package:platform_device_id/platform_device_id.dart'; +// import 'package:test_sa/dashboard_latest/widgets/app_bar_widget.dart'; +// import 'package:test_sa/extensions/enum_extensions.dart'; +// import 'package:test_sa/extensions/text_extensions.dart'; +// import 'package:test_sa/extensions/widget_extensions.dart'; +// import 'package:test_sa/helper/utils.dart'; +// import 'package:test_sa/models/enums/swipe_type.dart'; +// import 'package:test_sa/models/lookup.dart'; +// import 'package:test_sa/models/new_models/swipe_model.dart'; +// import 'package:test_sa/nfc/nfc_reader_sheet.dart'; +// import 'package:test_sa/utilities/Location.dart' as location; +// import 'package:test_sa/views/widgets/dialogs/confirm_dialog.dart'; +// import 'package:test_sa/views/widgets/dialogs/success_dialog.dart'; +// import 'package:test_sa/views/widgets/qr_scanner_dialog.dart'; +// import 'package:wifi_iot/wifi_iot.dart'; +// +// import '../../app_style/app_color.dart'; +// +// class MarkAttendanceWidget extends StatefulWidget { +// // DashboardProviderModel model; +// double topPadding; +// bool isFromDashboard; +// +// MarkAttendanceWidget( {Key? key, this.topPadding = 0, this.isFromDashboard = false}) : super(key: key); +// // todo MarkAttendanceWidget(this.model, {Key? key, this.topPadding = 0, this.isFromDashboard = false}) : super(key: key); +// +// @override +// _MarkAttendanceWidgetState createState() { +// return _MarkAttendanceWidgetState(); +// } +// } +// +// class _MarkAttendanceWidgetState extends State { +// bool isNfcEnabled = false, isNfcLocationEnabled = false, isQrEnabled = false, isQrLocationEnabled = false, isWifiEnabled = false, isWifiLocationEnabled = false; +// +// int _locationUpdateCbId = 0; +// +// @override +// void initState() { +// super.initState(); +// checkAttendanceAvailability(); +// } +// +// void checkAttendanceAvailability() async { +// bool isAvailable = await NfcManager.instance.isAvailable(); +// // setState(() { +// // AppState().privilegeListModel!.forEach((PrivilegeListModel element) { +// // if (element.serviceName == "enableNFC") { +// // if (isAvailable) if (element.previlege ?? false) isNfcEnabled = true; +// // } else if (element.serviceName == "enableQR") { +// // if (element.previlege ?? false) isQrEnabled = true; +// // } else if (element.serviceName == "enableWIFI") { +// // if (element.previlege ?? false) isWifiEnabled = true; +// // } else if (element.serviceName!.trim() == "enableLocationNFC") { +// // if (element.previlege ?? false) isNfcLocationEnabled = true; +// // } else if (element.serviceName == "enableLocationQR") { +// // if (element.previlege ?? false) isQrLocationEnabled = true; +// // } else if (element.serviceName == "enableLocationWIFI") { +// // if (element.previlege ?? false) isWifiLocationEnabled = true; +// // } +// // }); +// // }); +// } +// +// void checkHuaweiLocationPermission(String attendanceType) async { +// // Permission_Handler permissionHandler = PermissionHandler(); +// location.Location.isEnabled((bool isEnabled) async { +// if (isEnabled) { +// location.Location.havePermission((bool permission) async { +// if (permission) { +// getHuaweiCurrentLocation(attendanceType); +// } else { +// bool has = await requestPermissions(); +// if (has) { +// getHuaweiCurrentLocation(attendanceType); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to give location permission to mark attendance", +// onTap: () { +// Navigator.pop(context); +// }, +// ), +// ); +// } +// } +// }); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to enable location services to mark attendance", +// onTap: () async { +// Navigator.pop(context); +// await Geolocator.openLocationSettings(); +// }, +// ), +// ); +// } +// }); +// +// // if (await permissionHandler.hasLocationPermission()) { +// // getHuaweiCurrentLocation(attendanceType); +// // } else { +// // bool has = await requestPermissions(); +// // if (has) { +// // getHuaweiCurrentLocation(attendanceType); +// // } else { +// // showDialog( +// // context: context, +// // builder: (BuildContext cxt) => ConfirmDialog( +// // message: "You need to give location permission to mark attendance", +// // onTap: () { +// // Navigator.pop(context); +// // }, +// // ), +// // ); +// // } +// // } +// } +// +// Future requestPermissions() async { +// var result = await [ +// Permission.location, +// ].request(); +// return (result[Permission.location] == PermissionStatus.granted || result[Permission.locationAlways] == PermissionStatus.granted); +// } +// +// @override +// void dispose() { +// super.dispose(); +// // Stop Session +// NfcManager.instance.stopSession(); +// } +// +// @override +// Widget build(BuildContext context) { +// return Container( +// padding: EdgeInsets.only(left: 21, right: 21, bottom: 21, top: widget.topPadding), +// decoration: const BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: Colors.white), +// width: double.infinity, +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// mainAxisSize: MainAxisSize.min, +// children: [ +// // LocaleKeys.markAttendance.tr().toSectionHeading(), +// // LocaleKeys.selectMethodOfAttendance.tr().toText11(color: const Color(0xff535353)), +// GridView( +// physics: const NeverScrollableScrollPhysics(), +// shrinkWrap: true, +// padding: const EdgeInsets.only(bottom: 0, top: 21), +// gridDelegate: +// SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: (MediaQuery.of(context).size.width < 550) ? 3 : 5, childAspectRatio: 1 / 1, crossAxisSpacing: 8, mainAxisSpacing: 8), +// children: [ +// attendanceMethod(SwipeTypeEnum.NFC.name, Icons.nfc, true, () { +// log('i am here...nfc'); +// handleSwipe(swipeType: SwipeTypeEnum.NFC, isEnable: true); +// return; +// // if (AppState().getIsHuawei) { +// if (false) { +// checkHuaweiLocationPermission("NFC"); +// } else { +// location.Location.isEnabled((bool isEnabled) { +// if (isEnabled) { +// location.Location.havePermission((bool permission) { +// if (permission) { +// Utils.showLoading(context); +// location.Location.getCurrentLocation( +// (Position position, bool isMocked) { +// if (isMocked) { +// Utils.hideLoading(context); +// markFakeAttendance("NFC", position.latitude.toString() ?? "", position.longitude.toString() ?? ""); +// } else { +// Utils.hideLoading(context); +// //todo performNfcAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); +// } +// }, +// () { +// Utils.hideLoading(context); +// Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); +// }, +// context, +// ); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to give location permission to mark attendance", +// onTap: () async { +// Navigator.pop(context); +// await Geolocator.openAppSettings(); +// }, +// ), +// ); +// } +// }); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to enable location services to mark attendance", +// onTap: () async { +// Navigator.pop(context); +// await Geolocator.openLocationSettings(); +// }, +// ), +// ); +// } +// }); +// } +// }), +// //if (isWifiEnabled) //todo +// attendanceMethod("Wifi", Icons.wifi, isWifiEnabled, () { +// // if (AppState().getIsHuawei) { +// if (false) { +// checkHuaweiLocationPermission("WIFI"); +// } else { +// location.Location.isEnabled((bool isEnabled) { +// if (isEnabled) { +// location.Location.havePermission((bool permission) { +// if (permission) { +// Utils.showLoading(context); +// location.Location.getCurrentLocation( +// (Position position, bool isMocked) { +// if (isMocked) { +// Utils.hideLoading(context); +// markFakeAttendance("WIFI", position.latitude.toString() ?? "", position.longitude.toString() ?? ""); +// } else { +// Utils.hideLoading(context); +// //todo performWifiAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); +// } +// }, +// () { +// Utils.hideLoading(context); +// Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); +// }, +// context, +// ); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to give location permission to mark attendance", +// onTap: () async { +// Navigator.pop(context); +// await Geolocator.openAppSettings(); +// }, +// ), +// ); +// } +// }); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to enable location services to mark attendance", +// onTap: () async { +// Navigator.pop(context); +// await Geolocator.openLocationSettings(); +// }, +// ), +// ); +// } +// }); +// } +// }), +// // if (isQrEnabled) //todo +// attendanceMethod(SwipeTypeEnum.QR.name, Icons.qr_code_2, true, () async { +// handleSwipe(swipeType: SwipeTypeEnum.QR, isEnable: true); +// return; +// // if (AppState().getIsHuawei) { +// if (false) { +// checkHuaweiLocationPermission("QR"); +// } else { +// location.Location.isEnabled((bool isEnabled) { +// if (isEnabled) { +// location.Location.havePermission((bool permission) { +// if (permission) { +// Utils.showLoading(context); +// location.Location.getCurrentLocation( +// (Position position, bool isMocked) { +// if (isMocked) { +// Utils.hideLoading(context); +// markFakeAttendance("QR", position.latitude.toString() ?? "", position.longitude.toString() ?? ""); +// } else { +// Utils.hideLoading(context); +// //todo performQrCodeAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); +// } +// }, +// () { +// Utils.hideLoading(context); +// Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); +// }, +// context, +// ); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to give location permission to mark attendance", +// onTap: () async { +// Navigator.pop(context); +// await Geolocator.openAppSettings(); +// }, +// ), +// ); +// } +// }); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to enable location services to mark attendance", +// onTap: () async { +// Navigator.pop(context); +// await Geolocator.openLocationSettings(); +// }, +// ), +// ); +// } +// }); +// } +// }), +// ], +// ) +// ], +// ), +// ); +// } +// +// void handleSwipe({required SwipeTypeEnum swipeType,required bool isEnable,}){ +// log('handle swipe value is ${swipeType.name}'); +// // if (AppState().getIsHuawei) { +// if (false) { +// checkHuaweiLocationPermission("NFC"); +// } else { +// location.Location.isEnabled((bool isEnabled) { +// if (isEnabled) { +// location.Location.havePermission((bool permission) { +// if (permission) { +// Utils.showLoading(context); +// location.Location.getCurrentLocation( +// (Position position, bool isMocked) { +// if (isMocked) { +// Utils.hideLoading(context); +// markFakeAttendance(swipeType.name, position.latitude.toString() ?? "", position.longitude.toString() ?? ""); +// } else { +// Utils.hideLoading(context); +// //todo performNfcAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); +// handleSwipeOperation(swipeType: swipeType,lat: position.latitude,lang: position.longitude); +// } +// }, +// () { +// Utils.hideLoading(context); +// Utils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); +// }, +// context, +// ); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to give location permission to mark attendance", +// onTap: () async { +// Navigator.pop(context); +// await Geolocator.openAppSettings(); +// }, +// ), +// ); +// } +// }); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to enable location services to mark attendance", +// onTap: () async { +// Navigator.pop(context); +// await Geolocator.openLocationSettings(); +// }, +// ), +// ); +// } +// }); +// } +// } +// +// void handleSwipeOperation({required SwipeTypeEnum swipeType,double ?lat, double ?lang}){ +// switch(swipeType){ +// case SwipeTypeEnum.NFC: +// handleNfcAttendance(latitude: lat,longitude: lang); +// return; +// case SwipeTypeEnum.QR: +// performQrCodeAttendance(latitude: lat,longitude: lang); +// return; +// case SwipeTypeEnum.Wifi: +// return; +// +// } +// } +// +// void getHuaweiCurrentLocation(String attendanceType) async { +// try { +// Utils.showLoading(context); +// FusedLocationProviderClient locationService = FusedLocationProviderClient()..initFusedLocationService(); +// LocationRequest locationRequest = LocationRequest(); +// locationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY; +// locationRequest.interval = 500; +// List locationRequestList = [locationRequest]; +// LocationSettingsRequest locationSettingsRequest = LocationSettingsRequest(requests: locationRequestList); +// +// late StreamSubscription _streamSubscription; +// int requestCode = (await (locationService.requestLocationUpdates(locationRequest)))!; +// +// _streamSubscription = locationService.onLocationData!.listen( +// (Location location) async { +// Utils.hideLoading(context); +// await locationService.removeLocationUpdates(requestCode); +// if (attendanceType == "QR") { +// // todo performQrCodeAttendance(widget.model, lat: location.latitude.toString() ?? "", lng: location.longitude.toString() ?? ""); +// } +// if (attendanceType == "WIFI") { +// // todo performWifiAttendance(widget.model, lat: location.latitude.toString() ?? "", lng: location.longitude.toString() ?? ""); +// } +// if (attendanceType == "NFC") { +// //todo performNfcAttendance(widget.model, lat: location.latitude.toString() ?? "", lng: location.longitude.toString() ?? ""); +// } +// requestCode = 0; +// }, +// ); +// +// // locationService.checkLocationSettings(locationSettingsRequest).then((settings) async { +// // await locationService.getLastLocation().then((value) { +// // if (value.latitude == null || value.longitude == null) { +// // showDialog( +// // context: context, +// // builder: (BuildContext cxt) => ConfirmDialog( +// // message: "Unable to get your location, Please check your location settings & try again.", +// // onTap: () { +// // Navigator.pop(context); +// // }, +// // ), +// // ); +// // } else { +// // if (attendanceType == "QR") { +// // performQrCodeAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); +// // } +// // if (attendanceType == "WIFI") { +// // performWifiAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); +// // } +// // if (attendanceType == "NFC") { +// // performNfcAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); +// // } +// // } +// // }).catchError((error) { +// // log("HUAWEI LOCATION getLastLocation ERROR!!!!!"); +// // log(error); +// // }); +// // }).catchError((error) { +// // log("HUAWEI LOCATION checkLocationSettings ERROR!!!!!"); +// // log(error); +// // if (error.code == "LOCATION_SETTINGS_NOT_AVAILABLE") { +// // // Location service not enabled. +// // } +// // }); +// } catch (error) { +// log("HUAWEI LOCATION ERROR!!!!!"); +// log('$error'); +// Utils.hideLoading(context); +// // Utils.handleException(error, context, null); +// } +// } +// +// Future handleNfcAttendance({double? latitude = 0, double? longitude = 0}) async { +// final dashBoardProvider = Provider.of(context, listen: false); +// +// if (Platform.isIOS) { +// Utils.readNFc(onRead: (String nfcId) async { +// await _processNfcAttendance(dashBoardProvider, nfcId, latitude, longitude); +// }); +// } else { +// showNfcReader(context, onNcfScan: (String? nfcId) async { +// await _processNfcAttendance(dashBoardProvider, nfcId ?? '', latitude, longitude); +// }); +// } +// } +// +// +// Future _processNfcAttendance( +// DashBoardProvider dashBoardProvider, +// String nfcId, +// double? latitude, +// double? longitude, +// ) async { +// Utils.showLoading(context); +// try { +// final swipeModel = Swipe( +// swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), +// value: nfcId, +// latitude: latitude, +// longitude: longitude, +// ); +// +// final swipeResponse = await dashBoardProvider.makeSwipe(model: swipeModel); +// log('swipe response i got is ${swipeResponse.toJson()}'); +// +// if (swipeResponse.responseCode != 1) { +// Utils.hideLoading(context); +// _showErrorDialog(swipeResponse.message ?? "Unexpected error occurred"); +// } else { +// final isSuccess = swipeResponse.data; +// log('nfc swipe response is ${isSuccess}'); +// if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3)); +// Utils.hideLoading(context); +// _showSuccessDialog(); +// } +// } catch (error) { +// Utils.hideLoading(context); +// // Uncomment below line for error handling if needed +// // Utils.handleException(error, context, null); +// } +// } +// +// +// void _showErrorDialog(String message) { +// showDialog( +// context: context, +// builder: (context) => ConfirmDialog( +// message: message, +// onTap: () => Navigator.pop(context), +// ), +// ); +// } +// +// void _showSuccessDialog() { +// showMDialog( +// context, +// backgroundColor: Colors.transparent, +// isDismissable: true, +// child: SuccessDialog(widget.isFromDashboard), +// ); +// } +// +// +// //older code.... +// Future performNfcAttendance({double? lat = 0, double ?lng = 0}) async { +// DashBoardProvider dashBoardProvider = Provider.of(context,listen:false); +// if (Platform.isIOS) { +// Utils.readNFc(onRead: (String nfcId) async { +// Utils.showLoading(context); +// try { +// SwipeModel? swipeResponse = await dashBoardProvider.makeSwipe(model: Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: '', latitude: lat, longitude: lng)); +// if (swipeResponse.responseCode != 1) { +// Utils.hideLoading(context); +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: swipeResponse.message ?? "Unexpected error occurred", +// onTap: () { +// Navigator.pop(context); +// }, +// ), +// ); +// } else { +// bool status = swipeResponse.data; +// if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3)); +// Utils.hideLoading(context); +// showMDialog( +// context, +// backgroundColor: Colors.transparent, +// isDismissable: true, +// child: SuccessDialog(widget.isFromDashboard), +// ); +// } +// } catch (ex) { +// Utils.hideLoading(context); +// // Utils.handleException(ex, context, null); +// } +// }); +// } else { +// showNfcReader(context, onNcfScan: (String? nfcId) async { +// Utils.showLoading(context); +// try { +// SwipeModel? swipeResponse = await dashBoardProvider.makeSwipe(model: Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: nfcId??'', latitude: lat, longitude: lng)); +// log('api response i got is ${swipeResponse.toJson()}'); +// if (swipeResponse.responseCode != 1) { +// Utils.hideLoading(context); +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: swipeResponse.message ?? "Unexpected error occurred", +// onTap: () { +// Navigator.pop(context); +// }, +// ), +// ); +// } else { +// bool status = swipeResponse.data; //use this status to get transactions. +// if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3)); +// Utils.hideLoading(context); +// showMDialog( +// context, +// backgroundColor: Colors.transparent, +// isDismissable: true, +// child: SuccessDialog(widget.isFromDashboard), +// ); +// } +// } catch (ex) { +// Utils.hideLoading(context); +// // Utils.handleException(ex, context, null); +// } +// // Utils.showLoading(context); +// // try { +// // GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 2, nfcValue: nfcId ?? "", isGpsRequired: isNfcLocationEnabled, lat: lat, long: lng); +// // if (g?.messageStatus != 1) { +// // Utils.hideLoading(context); +// // showDialog( +// // context: context, +// // builder: (BuildContext cxt) => ConfirmDialog( +// // message: g?.errorEndUserMessage ?? "Unexpected error occurred", +// // onTap: () { +// // Navigator.pop(context); +// // }, +// // ), +// // ); +// // } else { +// // bool status = await model.fetchAttendanceTracking(context); +// // Utils.hideLoading(context); +// // showMDialog( +// // context, +// // backgroundColor: Colors.transparent, +// // isDismissable: false, +// // child: SuccessDialog(widget.isFromDashboard), +// // ); +// // } +// // } catch (ex) { +// // log(ex); +// // Utils.hideLoading(context); +// // // Utils.handleException(ex, context, (String msg) { +// // // Utils.confirmDialog(context, msg); +// // // }); +// // } +// }); +// } +// } +// +// +// +// void showMDialog(context, {Widget? child, Color? backgroundColor, bool isDismissable = true, bool isBusniessCard = false}) async { +// return showDialog( +// context: context, +// barrierDismissible: isDismissable, +// builder: (context) { +// return Dialog( +// shape: isBusniessCard +// ? const RoundedRectangleBorder( +// borderRadius: BorderRadius.all( +// Radius.circular(15.0), +// ), +// ) +// : null, +// backgroundColor: backgroundColor, +// child: child, +// ); +// }, +// ); +// } +// // +// // Future checkSession() async { +// // try { +// // Utils.showLoading(context); +// // await DashboardApiClient().getOpenMissingSwipes(); +// // Utils.hideLoading(context); +// // return true; +// // } catch (ex) { +// // Utils.hideLoading(context); +// // Utils.handleException(ex, context, null); +// // return false; +// // } +// // } +// +// +// //TODO need to confirm .... +// // Future performWifiAttendance({double? latitude, double? lng}) async { +// // // if (Platform.isAndroid) { +// // // if (!(await checkSession())) { +// // // return; +// // // } +// // // } +// // Utils.showLoading(context); +// // bool isConnected = await WiFiForIoTPlugin.connect(AppState().getMohemmWifiSSID ?? "", +// // password: AppState().getMohemmWifiPassword ?? "", joinOnce: Platform.isIOS ? false : true, security: NetworkSecurity.WPA, withInternet: false); +// // +// // if (Platform.isIOS) { +// // if (await WiFiForIoTPlugin.getSSID() == AppState().getMohemmWifiSSID) { +// // isConnected = true; +// // } else { +// // isConnected = false; +// // } +// // } +// // +// // if (isConnected && AppState().isAuthenticated) { +// // await WiFiForIoTPlugin.forceWifiUsage(true); +// // await Future.delayed(const Duration(seconds: 6)); +// // try { +// // GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 3, nfcValue: "", isGpsRequired: isWifiLocationEnabled, lat: lat, long: lng); +// // bool status = await model.fetchAttendanceTracking(context); +// // Utils.hideLoading(context); +// // await closeWifiRequest(); +// // if (g?.messageStatus == 2) { +// // showDialog( +// // barrierDismissible: true, +// // context: context, +// // builder: (cxt) => ConfirmDialog( +// // message: g?.errorEndUserMessage ?? "", +// // onTap: () { +// // Navigator.pop(context); +// // }, +// // onCloseTap: () {}, +// // ), +// // ); +// // } else { +// // showMDialog( +// // context, +// // backgroundColor: Colors.transparent, +// // isDismissable: false, +// // child: SuccessDialog(widget.isFromDashboard), +// // ); +// // } +// // } catch (ex) { +// // await closeWifiRequest(); +// // Utils.hideLoading(context); +// // Utils.handleException(ex, context, null); +// // } +// // } else { +// // if (AppState().isAuthenticated) { +// // Utils.hideLoading(context); +// // Utils.confirmDialog(context, "LocaleKeys.comeNearHMGWifi.tr()"); +// // } else { +// // await closeWifiRequest(); +// // } +// // } +// // } +// +// Future closeWifiRequest() async { +// if (Platform.isAndroid) { +// await WiFiForIoTPlugin.forceWifiUsage(false); +// } +// return await WiFiForIoTPlugin.disconnect(); +// } +// +// Future performQrCodeAttendance( {double ? latitude , double ?longitude}) async { +// DashBoardProvider dashBoardProvider = Provider.of(context,listen: false); +// var qrCodeValue = await Navigator.of(context).push( +// MaterialPageRoute( +// builder: (BuildContext context) => QrScannerDialog(), +// ), +// ); +// if (qrCodeValue != null) { +// Utils.showLoading(context); +// try { +// final swipeModel = Swipe( +// swipeTypeValue: SwipeTypeEnum.QR.getIntFromSwipeTypeEnum(), +// value: qrCodeValue, +// latitude: latitude, +// longitude: longitude, +// ); +// log('model i got to scan qr is ${swipeModel.toJson()}'); +// final swipeResponse = await dashBoardProvider.makeSwipe(model: swipeModel); +// log('response of swipe is ${swipeResponse.toJson()}'); +// +// bool status = await swipeResponse.data; +// Utils.hideLoading(context); +// if (swipeResponse.responseCode == 2) { +// showDialog( +// barrierDismissible: true, +// context: context, +// builder: (cxt) => ConfirmDialog( +// message: swipeResponse.message ?? "", +// onTap: () { +// Navigator.pop(context); +// }, +// onCloseTap: () {}, +// ), +// ); +// } else { +// showMDialog( +// context, +// backgroundColor: Colors.transparent, +// isDismissable: true, +// child: SuccessDialog(widget.isFromDashboard), +// ); +// } +// } catch (ex) { +// log('$ex'); +// Utils.hideLoading(context); +// //this need to confirm where it comes.. +// // Utils.handleException(ex, context, null); +// } +// } +// } +// +// void markFakeAttendance(dynamic sourceName, String lat, String long) async { +// Utils.showLoading(context); +// try { +// // await DashboardApiClient().markFakeLocation(sourceName: sourceName, lat: lat, long: long); +// Utils.hideLoading(context); +// Utils.confirmDialog(context, "LocaleKeys.fakeLocation.tr()"); +// } catch (ex) { +// log('$ex'); +// Utils.hideLoading(context); +// //Utils.handleException(ex, context, null); +// } +// } +// +// Widget attendanceMethod(String title, IconData iconData, bool isEnabled, VoidCallback onPress) => Container( +// decoration: BoxDecoration( +// borderRadius: BorderRadius.circular(15), +// color: isEnabled ? null : Colors.grey.withOpacity(.5), +// gradient: isEnabled +// ? const LinearGradient( +// transform: GradientRotation(.64), +// begin: Alignment.topRight, +// end: Alignment.bottomLeft, +// colors: [ +// //ToDo set Colors according to design provided by designer... +// Colors.blue, +// Colors.green, +// // AppColor.gradiantEndColor, +// // MyColors.gradiantStartColor, +// ], +// ) +// : null, +// ), +// clipBehavior: Clip.antiAlias, +// padding: const EdgeInsets.only(left: 10, right: 10, top: 14, bottom: 14), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// // SvgPicture.asset(image, color: Colors.white, alignment: Alignment.topLeft).expanded, +// Icon(iconData, color: isEnabled ? AppColor.black35 : Colors.grey), +// title.heading6(context), +// // title.toText17(isBold: true, color: Colors.white), +// ], +// ), +// ).onPress( +// () { +// log('isEnabled is ${!isEnabled}'); +// if (!isEnabled) return; +// onPress(); +// }, +// ); +// } diff --git a/lib/new_views/pages/login_page.dart b/lib/new_views/pages/login_page.dart index 7b0de848..86cf1d63 100644 --- a/lib/new_views/pages/login_page.dart +++ b/lib/new_views/pages/login_page.dart @@ -149,7 +149,9 @@ import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/models/new_models/general_response_model.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/forget_password_module/forget_passwod_verify_otp.dart'; import 'package:test_sa/new_views/pages/land_page/land_page.dart'; import '../../controllers/providers/api/user_provider.dart'; @@ -173,7 +175,8 @@ class _LoginPageState extends State { late UserProvider _userProvider; SettingProvider? _settingProvider; final GlobalKey _formKey = GlobalKey(); - + TextEditingController userNameController = TextEditingController(); + bool _passwordVisible = false; bool rememberMe = false; @override @@ -190,6 +193,7 @@ class _LoginPageState extends State { rememberMe = _settingProvider!.rememberMe; if (rememberMe) { _user.userName = _settingProvider!.username; + userNameController.text=_user.userName??''; _user.password = _settingProvider!.password; } } @@ -239,6 +243,7 @@ class _LoginPageState extends State { children: [ AppTextFormField( initialValue: _user.userName ?? "", + controller: userNameController, backgroundColor: AppColor.white20, validator: (value) => Validator.hasValue(value!) ? null : context.translation.requiredField, labelText: context.translation.username, @@ -260,7 +265,14 @@ class _LoginPageState extends State { style: TextStyle(fontWeight: FontWeight.w500, fontSize: 12, color: Color(0xff3B3D4A)), labelStyle: TextStyle(fontWeight: FontWeight.w500, fontSize: 11, color: Color(0xff767676)), contentPadding: EdgeInsets.symmetric(horizontal: 16.toScreenWidth, vertical: 12.toScreenHeight), - obscureText: true, + obscureText: !_passwordVisible, + suffixIcon: Icon( + // Based on passwordVisible state choose the icon + _passwordVisible ? Icons.visibility : Icons.visibility_off, + color: context.isDark ? AppColor.neutral10 : AppColor.neutral20, + ).paddingOnly(end: 12).onPress(() { setState(() { + _passwordVisible = !_passwordVisible; + }); }), validator: (value) => Validator.isValidPassword(value!) ? null : value.isEmpty @@ -274,8 +286,18 @@ class _LoginPageState extends State { Align( alignment: AlignmentDirectional.centerEnd, child: InkWell( - onTap: () { - print('i am here...'); + onTap: () async { + if(userNameController.text.isNotEmpty){ + GeneralResponseModel response = await _userProvider.sendForgetPasswordOtp(context: context, employeeId: userNameController.text); + if( response.isSuccess==true){ + Navigator.push(context, MaterialPageRoute(builder: (context)=>ForgetPasswordVerifyOtpView(data: {'employeeId':userNameController.text,'phoneNumber':response.data},))); + // Navigator.push(context, ForgetPasswordVerifyOtpView.routeName); + }else{ + Fluttertoast.showToast(msg: response.message ?? context.translation.failedToCompleteRequest); + } + }else{ + Fluttertoast.showToast(msg: context.translation.employeeIdIsRequired); + } }, child: context.translation.forgotPassword.bodyText(context).custom(color: AppColor.primary10, fontWeight: FontWeight.w500), ), diff --git a/lib/new_views/swipe_module/circular_animated_widget.dart b/lib/new_views/swipe_module/circular_animated_widget.dart new file mode 100644 index 00000000..14ee20e1 --- /dev/null +++ b/lib/new_views/swipe_module/circular_animated_widget.dart @@ -0,0 +1,99 @@ +import 'package:flutter/animation.dart'; +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; + + +class CircularAnimatedContainer extends StatefulWidget { + Widget child; + + CircularAnimatedContainer({Key? key, required this.child}) : super(key: key); + @override + _CircularAnimatedContainerState createState() => _CircularAnimatedContainerState(); +} + +class _CircularAnimatedContainerState extends State + with SingleTickerProviderStateMixin { + AnimationController ?_controller; + Animation? _animation; + + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(seconds: 2), + vsync: this, + )..repeat(); + + _animation = CurvedAnimation( + parent: _controller!, + curve: Curves.easeInOut, // Applies the ease-in-out effect + ); + // Repeats animation + } + + @override + void dispose() { + _controller!.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Center( + child: Stack( + alignment: Alignment.center, + children: [ + widget.child, + AnimatedBuilder( + animation: _animation!, + builder: (context, child) { + return CustomPaint( + painter: CircularProgressPainter( + progress: _animation!.value), + size: Size(100.toScreenHeight, 100.toScreenWidth), + ); + }, + ), + ], + ), + ); + } +} + +class CircularProgressPainter extends CustomPainter { + final double progress; + + CircularProgressPainter({ required this.progress}); + @override + void paint(Canvas canvas, Size size) { + + + final Paint paint = Paint() + ..color = AppColor.primary10 + ..style = PaintingStyle.stroke + ..strokeWidth = 3 + ..strokeCap = StrokeCap.round; + + final center = Offset(size.width / 2, size.height / 2); + final radius = size.width / 2.05; + final double startAngle = 2.5 * 3.141592653589793 * progress; + final double sweepAngle = 2 * 3.141592653589793 * progress; + // const double startAngle = -90 * (3.141592653589793 / 180); + // final double sweepAngle = 2.05 * 3.141592653589793 * progress; + + canvas.drawArc( + Rect.fromCircle(center: center, radius: radius), + startAngle, + sweepAngle, + false, + paint, + ); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +} \ No newline at end of file diff --git a/lib/new_views/swipe_module/dialoge/confirm_dialog.dart b/lib/new_views/swipe_module/dialoge/confirm_dialog.dart new file mode 100644 index 00000000..3e07310f --- /dev/null +++ b/lib/new_views/swipe_module/dialoge/confirm_dialog.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; + +class ConfirmDialog extends StatelessWidget { + final String ?title; + final String ?message; + final String ?okTitle; + final VoidCallback ?onTap; + final VoidCallback ?onCloseTap; + + const ConfirmDialog({Key ?key, this.title, this.message, this.okTitle, this.onTap, this.onCloseTap}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: Colors.white, + shape: const RoundedRectangleBorder(), + insetPadding: const EdgeInsets.only(left: 21, right: 21), + child: Padding( + padding: const EdgeInsets.only(left: 20, right: 20, top: 18, bottom: 28), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + title ?? "Confirm", + style: const TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: Colors.black87, height: 35 / 24, letterSpacing: -0.96), + ).paddingOnly(top: 16), + ), + IconButton( + padding: EdgeInsets.zero, + icon: const Icon(Icons.close), + color: Colors.black87, + constraints: const BoxConstraints(), + onPressed: () => onCloseTap ?? Navigator.pop(context), + // onPressed: () => Navigator.pop(context), + ) + ], + ), + message!=null?message!.heading5(context).custom(color: AppColor.neutral70):const SizedBox(), + 28.height, + AppFilledButton( + label: okTitle ?? "OK", + onPressed: onTap ?? () => Navigator.pop(context), + textColor: Colors.white, + //color: Ap.green, + ), + ], + ), + ), + ); + } +} diff --git a/lib/new_views/swipe_module/dialoge/nfc_reader_sheet.dart b/lib/new_views/swipe_module/dialoge/nfc_reader_sheet.dart new file mode 100644 index 00000000..76e108a3 --- /dev/null +++ b/lib/new_views/swipe_module/dialoge/nfc_reader_sheet.dart @@ -0,0 +1,203 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:nfc_manager/nfc_manager.dart'; +import 'package:nfc_manager/platform_tags.dart'; + +void showNfcReader(BuildContext context, { Function(String nfcId) ?onNcfScan}) { + showModalBottomSheet( + context: context, + enableDrag: false, + isDismissible: false, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)), + ), + backgroundColor: Colors.white, + builder: (context) { + return NfcLayout( + onNcfScan: onNcfScan, + ); + }, + ); +} + +class NfcLayout extends StatefulWidget { + Function(String nfcId) ?onNcfScan; + + NfcLayout({this.onNcfScan}); + + @override + _NfcLayoutState createState() => _NfcLayoutState(); +} + +class _NfcLayoutState extends State { + bool _reading = false; + Widget? mainWidget; + String? nfcId; + + @override + void initState() { + super.initState(); + + NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async { + var f; + if (Platform.isAndroid) { + f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); + } else { + f = MifareUltralight(tag: tag, identifier: tag.data["mifare"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); + } + String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); + nfcId = identifier; + + setState(() { + _reading = true; + mainWidget = doneNfc(); + }); + + Future.delayed(const Duration(seconds: 1), () { + NfcManager.instance.stopSession(); + Navigator.pop(context); + // if (Platform.isAndroid) { + // Navigator.pop(context); + // } else { + // Navigator.pop(context); + // Navigator.pop(context); + // } + widget.onNcfScan!(nfcId!); + }); + }).catchError((err) { + print(err); + }); + } + + @override + Widget build(BuildContext context) { + (mainWidget == null && !_reading) ? mainWidget = scanNfc() : mainWidget = doneNfc(); + return AnimatedSwitcher(duration: Duration(milliseconds: 500), child: mainWidget); + } + + Widget scanNfc() { + return Container( + key: ValueKey(1), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 30, + ), + const Text( + "Ready To Scan", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 24, + ), + ), + SizedBox( + height: 30, + ), + + Image.asset( + "assets/images/ic_nfc.png", + height: MediaQuery.of(context).size.width / 3, + width: double.infinity, + ), + const SizedBox( + height: 30, + ), + const Text( + "Approach an NFC Tag", + style: TextStyle( + fontSize: 18, + ), + ), + const SizedBox( + height: 30, + ), + ButtonTheme( + minWidth: MediaQuery.of(context).size.width / 1.2, + height: 45.0, + buttonColor: Colors.grey[300], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + child: TextButton( + onPressed: () { + NfcManager.instance.stopSession(); + Navigator.pop(context); + }, + // elevation: 0, + child: const Text("CANCEL"), + ), + ), + SizedBox( + height: 30, + ), + ], + ), + ); + } + + Widget doneNfc() { + return Container( + key: ValueKey(2), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + height: 30, + ), + Text( + "Successfully Scanned", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 24, + ), + ), + SizedBox( + height: 30, + ), + Image.asset( + // "assets/icons/nfc/ic_done.png", + "assets/images/ic_done.png", + height: MediaQuery.of(context).size.width / 3, + width: double.infinity, + ), + SizedBox( + height: 30, + ), + Text( + "Approach an NFC Tag", + style: TextStyle( + fontSize: 18, + ), + ), + SizedBox( + height: 30, + ), + ButtonTheme( + minWidth: MediaQuery.of(context).size.width / 1.2, + height: 45.0, + buttonColor: Colors.grey[300], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), + child: TextButton( + // onPressed: () { + // _stream?.cancel(); + // widget.onNcfScan(nfcId); + // Navigator.pop(context); + // }, + onPressed: null, + // elevation: 0, + child: Text("DONE"), + ), + ), + SizedBox( + height: 30, + ), + ], + ), + ); + } +} diff --git a/lib/new_views/swipe_module/dialoge/success_dialog.dart b/lib/new_views/swipe_module/dialoge/success_dialog.dart new file mode 100644 index 00000000..e03472c5 --- /dev/null +++ b/lib/new_views/swipe_module/dialoge/success_dialog.dart @@ -0,0 +1,76 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:just_audio/just_audio.dart'; +import 'package:lottie/lottie.dart'; + +class SuccessDialog extends StatefulWidget { + bool isFromDashboard; + + SuccessDialog(this.isFromDashboard); + + @override + State createState() => _SuccessDialogState(); +} + +class _SuccessDialogState extends State with TickerProviderStateMixin { + AnimationController? _controller; + + @override + void initState() { + _controller = AnimationController(vsync: this); + + super.initState(); + } + + Future playSuccessSound() async { + AudioPlayer player = AudioPlayer(); + String audioAsset = ""; + if (Platform.isAndroid) { + audioAsset = "assets/audio/success_tone_android.mp3"; + } else { + audioAsset = "assets/audio/success_tone_ios.caf"; + } + await player.setAsset(audioAsset); + await player.load(); + player.play(); + } + + @override + Widget build(BuildContext context) { + double size = MediaQuery.of(context).size.width / 1.8; + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: size, + height: size, + child: Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(25.0), + ), + child: Lottie.asset( + //TODO replace with original lottie file... + 'assets/lottie/done.json', + repeat: false, + reverse: false, + controller: _controller, + frameRate: FrameRate(60.0), + onLoaded: (LottieComposition v) async { + await playSuccessSound(); + _controller + !..duration = v.duration + ..forward().whenComplete(() async { + Navigator.pop(context); + if (widget.isFromDashboard) Navigator.pop(context); + }); + }, + ), + ), + ), + ], + ); + } +} diff --git a/lib/new_views/swipe_module/enums/swipe_type.dart b/lib/new_views/swipe_module/enums/swipe_type.dart new file mode 100644 index 00000000..8c4ff449 --- /dev/null +++ b/lib/new_views/swipe_module/enums/swipe_type.dart @@ -0,0 +1,18 @@ +enum SwipeTypeEnum { + NFC, // 1 + QR, // 2 + Wifi, // 3 +} + +extension EnumExtensionsSwipeType on SwipeTypeEnum { + int getIntFromSwipeTypeEnum() { + switch (this) { + case SwipeTypeEnum.NFC: + return 1; + case SwipeTypeEnum.QR: + return 2; + case SwipeTypeEnum.Wifi: + return 3; + } + } +} \ No newline at end of file diff --git a/lib/new_views/swipe_module/mark_attendance_widget.dart b/lib/new_views/swipe_module/mark_attendance_widget.dart new file mode 100644 index 00000000..6a6740e8 --- /dev/null +++ b/lib/new_views/swipe_module/mark_attendance_widget.dart @@ -0,0 +1,821 @@ +// import 'dart:async'; +// import 'dart:developer'; +// import 'dart:io'; +// +// import 'package:flutter/material.dart'; +// import 'package:geolocator/geolocator.dart'; +// import 'package:huawei_location/huawei_location.dart'; +// import 'package:nfc_manager/nfc_manager.dart'; +// import 'package:permission_handler/permission_handler.dart'; +// import 'package:provider/provider.dart'; +// import 'package:test_sa/controllers/providers/api/user_provider.dart'; +// import 'package:test_sa/extensions/int_extensions.dart'; +// import 'package:test_sa/extensions/text_extensions.dart'; +// import 'package:test_sa/extensions/widget_extensions.dart'; +// import 'package:test_sa/new_views/app_style/app_color.dart'; +// import 'package:test_sa/new_views/pages/land_page/nfc/nfc_reader_sheet.dart'; +// import 'package:test_sa/new_views/swipe_module/dialoge/confirm_dialog.dart'; +// import 'package:test_sa/new_views/swipe_module/dialoge/qr_scanner_dialog.dart'; +// import 'package:test_sa/new_views/swipe_module/dialoge/success_dialog.dart'; +// import 'package:test_sa/new_views/swipe_module/enums/swipe_type.dart'; +// import 'package:test_sa/new_views/swipe_module/models/swipe_model.dart'; +// import 'package:test_sa/new_views/swipe_module/swipe_success_view.dart'; +// import 'package:test_sa/new_views/swipe_module/utils/location_utils.dart'; +// import 'package:test_sa/new_views/swipe_module/utils/swipe_general_utils.dart'; +// import 'package:wifi_iot/wifi_iot.dart'; +// todo @sikander delete this file after compelte +// class MarkAttendanceWidget extends StatefulWidget { +// double topPadding; +// bool isFromDashboard; +// +// MarkAttendanceWidget({Key key, this.topPadding = 0, this.isFromDashboard = false}) : super(key: key); +// +// // todo MarkAttendanceWidget(this.model, {Key? key, this.topPadding = 0, this.isFromDashboard = false}) : super(key: key); +// +// @override +// _MarkAttendanceWidgetState createState() { +// return _MarkAttendanceWidgetState(); +// } +// } +// +// class _MarkAttendanceWidgetState extends State { +// bool isNfcEnabled = false, isQrEnabled = false, isWifiEnabled = false; +// UserProvider _userProvider; +// +// int _locationUpdateCbId = 0; +// +// @override +// void initState() { +// super.initState(); +// checkAttendanceAvailability(); +// } +// +// void checkAttendanceAvailability() async { +// _userProvider = Provider.of(context, listen: false); +// bool isAvailable = await NfcManager.instance.isAvailable(); +// log('backend enabled values are nfc ${_userProvider.user.enableNFC} qr ${_userProvider.user.enableQR} wifi ${_userProvider.user.enableWifi}'); +// setState(() { +// if (isAvailable && _userProvider.user.enableNFC) isNfcEnabled = true; +// if (_userProvider.user.enableQR) isQrEnabled = true; +// if (_userProvider.user.enableWifi) isWifiEnabled = true; +// }); +// } +// +// void checkHuaweiLocationPermission(SwipeTypeEnum attendanceType, BuildContext context) async { +// // Permission_Handler permissionHandler = PermissionHandler(); +// LocationUtilities.isEnabled((bool isEnabled) async { +// if (isEnabled) { +// LocationUtilities.havePermission((bool permission) async { +// if (permission) { +// getHuaweiCurrentLocation(attendanceType, context); +// } else { +// bool has = await requestPermissions(); +// if (has) { +// getHuaweiCurrentLocation(attendanceType, context); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to give location permission to mark attendance", +// onTap: () { +// Navigator.pop(context); +// }, +// ), +// ); +// } +// } +// }); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to enable location services to mark attendance", +// onTap: () async { +// Navigator.pop(context); +// await Geolocator.openLocationSettings(); +// }, +// ), +// ); +// } +// }); +// +// // if (await permissionHandler.hasLocationPermission()) { +// // getHuaweiCurrentLocation(attendanceType); +// // } else { +// // bool has = await requestPermissions(); +// // if (has) { +// // getHuaweiCurrentLocation(attendanceType); +// // } else { +// // showDialog( +// // context: context, +// // builder: (BuildContext cxt) => ConfirmDialog( +// // message: "You need to give location permission to mark attendance", +// // onTap: () { +// // Navigator.pop(context); +// // }, +// // ), +// // ); +// // } +// // } +// } +// +// Future requestPermissions() async { +// var result = await [ +// Permission.location, +// ].request(); +// return (result[Permission.location] == PermissionStatus.granted || result[Permission.locationAlways] == PermissionStatus.granted); +// } +// +// @override +// void dispose() { +// super.dispose(); +// // Stop Session +// NfcManager.instance.stopSession(); +// } +// +// @override +// Widget build(BuildContext context) { +// return Container( +// padding: EdgeInsets.only(left: 21, right: 21, bottom: 21, top: widget.topPadding), +// decoration: const BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: Colors.white), +// width: double.infinity, +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// mainAxisSize: MainAxisSize.min, +// children: [ +// 20.height, +// 'click me '.heading5(context), +// // LocaleKeys.markAttendance.tr().toSectionHeading(), +// // LocaleKeys.selectMethodOfAttendance.tr().toText11(color: const Color(0xff535353)), +// GridView( +// physics: const NeverScrollableScrollPhysics(), +// shrinkWrap: true, +// padding: const EdgeInsets.only(bottom: 0, top: 21), +// gridDelegate: +// SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: (MediaQuery.of(context).size.width < 550) ? 3 : 5, childAspectRatio: 1 / 1, crossAxisSpacing: 8, mainAxisSpacing: 8), +// children: availableAttendanceMethodList(context: context), +// ) +// ], +// ), +// ); +// } +// +// void handleSwipe({ +// SwipeTypeEnum swipeType, +// @required bool isEnable, +// @required BuildContext context, +// }) { +// // if (AppState().getIsHuawei) { +// if (false) { +// checkHuaweiLocationPermission(swipeType, context); +// } else { +// LocationUtilities.isEnabled((bool isEnabled) { +// if (isEnabled) { +// LocationUtilities.havePermission((bool permission) { +// if (permission) { +// SwipeGeneralUtils.showLoading(context); +// LocationUtilities.getCurrentLocation( +// (Position position, bool isMocked) { +// if (isMocked) { +// SwipeGeneralUtils.hideLoading(context); +// SwipeGeneralUtils.markFakeAttendance(swipeType.name, position.latitude.toString() ?? "", position.longitude.toString() ?? "", context); +// } else { +// SwipeGeneralUtils.hideLoading(context); +// //todo performNfcAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); +// handleSwipeOperation(swipeType: swipeType, lat: position.latitude, lang: position.longitude); +// } +// }, +// () { +// SwipeGeneralUtils.hideLoading(context); +// SwipeGeneralUtils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); +// }, +// context, +// ); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to give location permission to mark attendance", +// onTap: () async { +// Navigator.pop(context); +// await Geolocator.openAppSettings(); +// }, +// ), +// ); +// } +// }); +// } else { +// showDialog( +// context: context, +// builder: (BuildContext cxt) => ConfirmDialog( +// message: "You need to enable location services to mark attendance", +// onTap: () async { +// Navigator.pop(context); +// await Geolocator.openLocationSettings(); +// }, +// ), +// ); +// } +// }); +// } +// } +// +// void handleSwipeOperation({@required SwipeTypeEnum swipeType, double lat, double lang, BuildContext context}) { +// switch (swipeType) { +// case SwipeTypeEnum.NFC: +// handleNfcAttendance(latitude: lat, longitude: lang, context: context); +// return; +// case SwipeTypeEnum.QR: +// performQrCodeAttendance(latitude: lat, longitude: lang, context: context); +// return; +// case SwipeTypeEnum.Wifi: +// //TODO need to implement. +// return; +// } +// } +// +// void getHuaweiCurrentLocation(SwipeTypeEnum attendanceType, BuildContext context) async { +// try { +// SwipeGeneralUtils.showLoading(context); +// FusedLocationProviderClient locationService = FusedLocationProviderClient()..initFusedLocationService(); +// LocationRequest locationRequest = LocationRequest(); +// locationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY; +// locationRequest.interval = 500; +// List locationRequestList = [locationRequest]; +// LocationSettingsRequest locationSettingsRequest = LocationSettingsRequest(requests: locationRequestList); +// +// StreamSubscription _streamSubscription; +// int requestCode = (await (locationService.requestLocationUpdates(locationRequest))); +// +// _streamSubscription = locationService.onLocationData.listen( +// (Location location) async { +// SwipeGeneralUtils.hideLoading(context); +// await locationService.removeLocationUpdates(requestCode); +// handleSwipeOperation(swipeType: attendanceType); +// requestCode = 0; +// }, +// ); +// +// // locationService.checkLocationSettings(locationSettingsRequest).then((settings) async { +// // await locationService.getLastLocation().then((value) { +// // if (value.latitude == null || value.longitude == null) { +// // showDialog( +// // context: context, +// // builder: (BuildContext cxt) => ConfirmDialog( +// // message: "Unable to get your location, Please check your location settings & try again.", +// // onTap: () { +// // Navigator.pop(context); +// // }, +// // ), +// // ); +// // } else { +// // if (attendanceType == "QR") { +// // performQrCodeAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); +// // } +// // if (attendanceType == "WIFI") { +// // performWifiAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); +// // } +// // if (attendanceType == "NFC") { +// // performNfcAttendance(widget.model, lat: value.latitude.toString() ?? "", lng: value.longitude.toString() ?? ""); +// // } +// // } +// // }).catchError((error) { +// // log("HUAWEI LOCATION getLastLocation ERROR!!!!!"); +// // log(error); +// // }); +// // }).catchError((error) { +// // log("HUAWEI LOCATION checkLocationSettings ERROR!!!!!"); +// // log(error); +// // if (error.code == "LOCATION_SETTINGS_NOT_AVAILABLE") { +// // // Location service not enabled. +// // } +// // }); +// } catch (error) { +// log("HUAWEI LOCATION ERROR!!!!!"); +// log('$error'); +// SwipeGeneralUtils.hideLoading(context); +// // SwipeGeneralUtils.handleException(error, context, null); +// } +// } +// +// Future handleNfcAttendance({double latitude = 0, double longitude = 0, BuildContext context}) async { +// final userProvider = Provider.of(context, listen: false); +// +// if (Platform.isIOS) { +// SwipeGeneralUtils.readNFc(onRead: (String nfcId) async { +// await _processNfcAttendance(userProvider, nfcId, latitude, longitude, context); +// }); +// } else { +// showNfcReader(context, onNcfScan: (String nfcId) async { +// await _processNfcAttendance(userProvider, nfcId ?? '', latitude, longitude, context); +// }); +// } +// } +// +// Future _processNfcAttendance( +// UserProvider userProvider, +// String nfcId, +// double latitude, +// double longitude, +// BuildContext context, +// ) async { +// SwipeGeneralUtils.showLoading(context); +// try { +// // final swipeModel = Swipe( +// // swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), +// // value: nfcId, +// // latitude: latitude, +// // longitude: longitude, +// // ); +// //Test model... +// final swipeModel = Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: '123', latitude: 24.70865415364271, longitude: 46.66600861881879); +// final swipeResponse = await userProvider.makeSwipe(model: swipeModel); +// if (swipeResponse.isSuccess) { +// if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3)); +// SwipeGeneralUtils.hideLoading(context); +// Navigator.pushNamed(context, SwipeSuccessView.routeName); +// } else { +// SwipeGeneralUtils.hideLoading(context); +// SwipeGeneralUtils.showErrorDialog(message: swipeResponse.message ?? "Unexpected error occurred", context: context); +// } +// } catch (errSwipeGeneralUtilsor) { +// SwipeGeneralUtils.hideLoading(context); +// // Uncomment below line for error handling if needed +// // SwipeGeneralUtils.handleException(error, context, null); +// } +// } +// +// Future closeWifiRequest() async { +// if (Platform.isAndroid) { +// await WiFiForIoTPlugin.forceWifiUsage(false); +// } +// return await WiFiForIoTPlugin.disconnect(); +// } +// +// Future performQrCodeAttendance({double latitude, double longitude, BuildContext context}) async { +// UserProvider userProvider = Provider.of(context, listen: false); +// var qrCodeValue = await Navigator.of(context).push( +// MaterialPageRoute( +// builder: (BuildContext context) => QrScannerDialog(), +// ), +// ); +// if (qrCodeValue != null) { +// SwipeGeneralUtils.showLoading(context); +// try { +// //test model.. +// final swipeModel = Swipe( +// swipeTypeValue: SwipeTypeEnum.QR.getIntFromSwipeTypeEnum(), +// value: '2323', +// latitude: 24.70865415364271, +// longitude: 46.66600861881879, +// ); +// final swipeResponse = await userProvider.makeSwipe(model: swipeModel); +// if (swipeResponse.isSuccess) { +// SwipeGeneralUtils.hideLoading(context); +// SwipeGeneralUtils.showMDialog(context, backgroundColor: Colors.transparent, isDismissable: true, child: SuccessDialog(widget.isFromDashboard)); +// } else { +// SwipeGeneralUtils.hideLoading(context); +// showDialog( +// barrierDismissible: true, +// context: context, +// builder: (cxt) => ConfirmDialog( +// message: swipeResponse.message ?? "", +// onTap: () { +// Navigator.pop(context); +// }, +// onCloseTap: () {} +// ), +// ); +// } +// } catch (ex) { +// SwipeGeneralUtils.hideLoading(context); +// } +// } +// } +// +// List availableAttendanceMethodList({@required BuildContext context}) { +// List availableMethods = []; +// if (isNfcEnabled) { +// availableMethods.add(attendanceMethod(SwipeTypeEnum.NFC.name, 'nfc_icon', isNfcEnabled, () { +// handleSwipe(swipeType: SwipeTypeEnum.NFC, isEnable: isNfcEnabled, context: context); +// })); +// } +// if (isQrEnabled) { +// availableMethods.add(attendanceMethod(SwipeTypeEnum.QR.name, 'wifi_icon', isQrEnabled, () { +// handleSwipe(swipeType: SwipeTypeEnum.QR, isEnable: isQrEnabled, context: context); +// })); +// } +// if (isWifiEnabled) { +// availableMethods.add(attendanceMethod(SwipeTypeEnum.Wifi.name, 'wifi_icon', isWifiEnabled, () { +// handleSwipe(swipeType: SwipeTypeEnum.Wifi, isEnable: isWifiEnabled, context: context); +// })); +// } +// return availableMethods; +// } +// +// Widget attendanceMethod(String title, String icon, bool isEnabled, VoidCallback onPress) { +// return Container( +// padding: EdgeInsets.all(12), +// decoration: BoxDecoration( +// color: Colors.white, +// borderRadius: BorderRadius.circular(18), +// border: Border.all(color: AppColor.white40, width: 2), +// ), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// children: [ +// icon.toSvgAsset(), +// title.heading5(context).custom(color: AppColor.neutral50), +// // Text( +// // title, +// // style: TextStyle(fontSize: 12, fontWeight: FontWeight.w500), +// // ), +// ], +// ), +// ).onPress( +// () { +// log('isEnabled is ${!isEnabled}'); +// if (!isEnabled) return; +// onPress(); +// }, +// ); +// // return Container( +// // decoration: BoxDecoration( +// // borderRadius: BorderRadius.circular(15), +// // color: isEnabled ? null : Colors.grey.withOpacity(.5), +// // gradient: isEnabled +// // ? const LinearGradient( +// // transform: GradientRotation(.64), +// // begin: Alignment.topRight, +// // end: Alignment.bottomLeft, +// // colors: [ +// // //ToDo set Colors according to design provided by designer... +// // Colors.blue, +// // Colors.green, +// // // AppColor.gradiantEndColor, +// // // MyColors.gradiantStartColor, +// // ], +// // ) +// // : null, +// // ), +// // clipBehavior: Clip.antiAlias, +// // padding: const EdgeInsets.only(left: 10, right: 10, top: 14, bottom: 14), +// // child: Column( +// // crossAxisAlignment: CrossAxisAlignment.start, +// // children: [ +// // // SvgPicture.asset(image, color: Colors.white, alignment: Alignment.topLeft).expanded, +// // Icon(iconData, color: isEnabled ? AppColor.black35 : Colors.grey), +// // title.heading6(context), +// // // title.toText17(isBold: true, color: Colors.white), +// // ], +// // ), +// // ).onPress( +// // () { +// // log('isEnabled is ${!isEnabled}'); +// // if (!isEnabled) return; +// // onPress(); +// // }, +// // ); +// } +// } +// +// // Widget customListItem({required String icon, required String heading, required String subHeading, required VoidCallback onTap}) { +// // return GestureDetector( +// // onTap: onTap, // Handles the tap +// // child: Card( +// // shape: RoundedRectangleBorder( +// // borderRadius: BorderRadius.circular(14), // Circular border radius +// // ), +// // color: Colors.white, +// // child: Row( +// // crossAxisAlignment: CrossAxisAlignment.start, // Align items at the top +// // children: [ +// // // Icon Section +// // icon +// // .toSvgAsset( +// // width: 32, +// // color: AppColor.neutral120, +// // height: 29, +// // ) +// // .paddingOnly(top: 8), +// // 14.width, +// // Expanded( +// // child: Column( +// // crossAxisAlignment: CrossAxisAlignment.start, +// // children: [ +// // Text( +// // heading, +// // style: AppTextStyles.heading6.copyWith(color: AppColor.neutral50), +// // ), +// // 7.height, +// // Text( +// // subHeading, +// // style: AppTextStyles.bodyText2.copyWith(color: AppColor.neutral120), +// // ), +// // ], +// // ).paddingOnly(end: 50), +// // ), +// // ], +// // ).paddingAll(12), +// // ), +// // ); +// // } +// +// //qr older code... +// +// //nfc older code widget +// +// // // if (AppState().getIsHuawei) { +// // if (false) { +// // checkHuaweiLocationPermission(SwipeTypeEnum.NFC); +// // } else { +// // LocationUtilities.isEnabled((bool isEnabled) { +// // if (isEnabled) { +// // LocationUtilities.havePermission((bool permission) { +// // if (permission) { +// // SwipeGeneralUtils.showLoading(context); +// // LocationUtilities.getCurrentLocation( +// // (Position position, bool isMocked) { +// // if (isMocked) { +// // SwipeGeneralUtils.hideLoading(context); +// // SwipeGeneralUtils.markFakeAttendance("NFC", position.latitude.toString() ?? "", position.longitude.toString() ?? "",context); +// // } else { +// // SwipeGeneralUtils.hideLoading(context); +// // //todo performNfcAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); +// // } +// // }, +// // () { +// // SwipeGeneralUtils.hideLoading(context); +// // SwipeGeneralUtils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); +// // }, +// // context, +// // ); +// // } else { +// // showDialog( +// // context: context, +// // builder: (BuildContext cxt) => ConfirmDialog( +// // message: "You need to give location permission to mark attendance", +// // onTap: () async { +// // Navigator.pop(context); +// // await Geolocator.openAppSettings(); +// // }, +// // ), +// // ); +// // } +// // }); +// // } else { +// // showDialog( +// // context: context, +// // builder: (BuildContext cxt) => ConfirmDialog( +// // message: "You need to enable location services to mark attendance", +// // onTap: () async { +// // Navigator.pop(context); +// // await Geolocator.openLocationSettings(); +// // }, +// // ), +// // ); +// // } +// // }); +// // } +// +// //older code.... +// // Future performNfcAttendance({double lat = 0, double lng = 0}) async { +// // UserProvider userProvider = Provider.of(context,listen:false); +// // if (Platform.isIOS) { +// // SwipeGeneralUtils.readNFc(onRead: (String nfcId) async { +// // SwipeGeneralUtils.showLoading(context); +// // try { +// // SwipeModel swipeResponse = await userProvider.makeSwipe(model: Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: '', latitude: lat, longitude: lng)); +// // if (swipeResponse.responseCode != 1) { +// // SwipeGeneralUtils.hideLoading(context); +// // showDialog( +// // context: context, +// // builder: (BuildContext cxt) => ConfirmDialog( +// // message: swipeResponse.message ?? "Unexpected error occurred", +// // onTap: () { +// // Navigator.pop(context); +// // }, +// // ), +// // ); +// // } else { +// // bool status = swipeResponse.data; +// // if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3)); +// // SwipeGeneralUtils.hideLoading(context); +// // SwipeGeneralUtils.showMDialog( +// // context, +// // backgroundColor: Colors.transparent, +// // isDismissable: true, +// // child: SuccessDialog(widget.isFromDashboard), +// // ); +// // } +// // } catch (ex) { +// // SwipeGeneralUtils.hideLoading(context); +// // // SwipeGeneralUtils.handleException(ex, context, null); +// // } +// // }); +// // } else { +// // showNfcReader(context, onNcfScan: (String nfcId) async { +// // SwipeGeneralUtils.showLoading(context); +// // try { +// // SwipeModel swipeResponse = await userProvider.makeSwipe(model: Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: nfcId??'', latitude: lat, longitude: lng)); +// // log('api response i got is ${swipeResponse.toJson()}'); +// // if (swipeResponse.responseCode != 1) { +// // SwipeGeneralUtils.hideLoading(context); +// // showDialog( +// // context: context, +// // builder: (BuildContext cxt) => ConfirmDialog( +// // message: swipeResponse.message ?? "Unexpected error occurred", +// // onTap: () { +// // Navigator.pop(context); +// // }, +// // ), +// // ); +// // } else { +// // bool status = swipeResponse.data; //use this status to get transactions. +// // if (Platform.isIOS) await Future.delayed(const Duration(seconds: 3)); +// // SwipeGeneralUtils.hideLoading(context); +// // SwipeGeneralUtils.showMDialog( +// // context, +// // backgroundColor: Colors.transparent, +// // isDismissable: true, +// // child: SuccessDialog(widget.isFromDashboard), +// // ); +// // } +// // } catch (ex) { +// // SwipeGeneralUtils.hideLoading(context); +// // // SwipeGeneralUtils.handleException(ex, context, null); +// // } +// // // SwipeGeneralUtils.showLoading(context); +// // // try { +// // // GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 2, nfcValue: nfcId ?? "", isGpsRequired: isNfcLocationEnabled, lat: lat, long: lng); +// // // if (g?.messageStatus != 1) { +// // // SwipeGeneralUtils.hideLoading(context); +// // // showDialog( +// // // context: context, +// // // builder: (BuildContext cxt) => ConfirmDialog( +// // // message: g?.errorEndUserMessage ?? "Unexpected error occurred", +// // // onTap: () { +// // // Navigator.pop(context); +// // // }, +// // // ), +// // // ); +// // // } else { +// // // bool status = await model.fetchAttendanceTracking(context); +// // // SwipeGeneralUtils.hideLoading(context); +// // // showMDialog( +// // // context, +// // // backgroundColor: Colors.transparent, +// // // isDismissable: false, +// // // child: SuccessDialog(widget.isFromDashboard), +// // // ); +// // // } +// // // } catch (ex) { +// // // log(ex); +// // // SwipeGeneralUtils.hideLoading(context); +// // // // SwipeGeneralUtils.handleException(ex, context, (String msg) { +// // // // SwipeGeneralUtils.confirmDialog(context, msg); +// // // // }); +// // // } +// // }); +// // } +// // } +// +// // +// // Future checkSession() async { +// // try { +// // SwipeGeneralUtils.showLoading(context); +// // await DashboardApiClient().getOpenMissingSwipes(); +// // SwipeGeneralUtils.hideLoading(context); +// // return true; +// // } catch (ex) { +// // SwipeGeneralUtils.hideLoading(context); +// // SwipeGeneralUtils.handleException(ex, context, null); +// // return false; +// // } +// // } +// +// //TODO need to confirm .... +// Future performWifiAttendance({double? latitude, double? lng}) async { +// // if (Platform.isAndroid) { +// // if (!(await checkSession())) { +// // return; +// // } +// // } +// SwipeGeneralUtils.showLoading(context); +// bool isConnected = await WiFiForIoTPlugin.connect(AppState().getMohemmWifiSSID ?? "", +// password: AppState().getMohemmWifiPassword ?? "", joinOnce: Platform.isIOS ? false : true, security: NetworkSecurity.WPA, withInternet: false); +// +// if (Platform.isIOS) { +// if (await WiFiForIoTPlugin.getSSID() == AppState().getMohemmWifiSSID) { +// isConnected = true; +// } else { +// isConnected = false; +// } +// } +// +// if (isConnected && AppState().isAuthenticated) { +// await WiFiForIoTPlugin.forceWifiUsage(true); +// await Future.delayed(const Duration(seconds: 6)); +// try { +// GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 3, nfcValue: "", isGpsRequired: isWifiLocationEnabled, lat: lat, long: lng); +// bool status = await model.fetchAttendanceTracking(context); +// SwipeGeneralUtils.hideLoading(context); +// await closeWifiRequest(); +// if (g?.messageStatus == 2) { +// showDialog( +// barrierDismissible: true, +// context: context, +// builder: (cxt) => ConfirmDialog( +// message: g?.errorEndUserMessage ?? "", +// onTap: () { +// Navigator.pop(context); +// }, +// onCloseTap: () {}, +// ), +// ); +// } else { +// showMDialog( +// context, +// backgroundColor: Colors.transparent, +// isDismissable: false, +// child: SuccessDialog(widget.isFromDashboard), +// ); +// } +// } catch (ex) { +// await closeWifiRequest(); +// SwipeGeneralUtils.hideLoading(context); +// SwipeGeneralUtils.handleException(ex, context, null); +// } +// } else { +// if (AppState().isAuthenticated) { +// SwipeGeneralUtils.hideLoading(context); +// SwipeGeneralUtils.confirmDialog(context, "LocaleKeys.comeNearHMGWifi.tr()"); +// } else { +// await closeWifiRequest(); +// } +// } +// } +// +// // older grid widget.. +// // attendanceMethod(SwipeTypeEnum.NFC.name, Icons.nfc, isNfcEnabled, () { +// // handleSwipe(swipeType: SwipeTypeEnum.NFC, isEnable: isNfcEnabled); +// // }), +// // attendanceMethod(SwipeTypeEnum.QR.name, Icons.qr_code_2, isQrEnabled, () async { +// // handleSwipe(swipeType: SwipeTypeEnum.QR, isEnable: true); +// // }), +// // //if (isWifiEnabled) //todo +// // attendanceMethod(SwipeTypeEnum.Wifi.name, Icons.wifi, isWifiEnabled, () { +// // // if (AppState().getIsHuawei) { +// // if (false) { +// // checkHuaweiLocationPermission(SwipeTypeEnum.Wifi); +// // } else { +// // LocationUtilities.isEnabled((bool isEnabled) { +// // if (isEnabled) { +// // LocationUtilities.havePermission((bool permission) { +// // if (permission) { +// // SwipeGeneralUtils.showLoading(context); +// // LocationUtilities.getCurrentLocation( +// // (Position position, bool isMocked) { +// // if (isMocked) { +// // SwipeGeneralUtils.hideLoading(context); +// // SwipeGeneralUtils.markFakeAttendance("WIFI", position.latitude.toString() ?? "", position.longitude.toString() ?? "", context); +// // } else { +// // SwipeGeneralUtils.hideLoading(context); +// // //todo performWifiAttendance(widget.model, lat: position.latitude.toString() ?? "", lng: position.longitude.toString() ?? ""); +// // } +// // }, +// // () { +// // SwipeGeneralUtils.hideLoading(context); +// // SwipeGeneralUtils.confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); +// // }, +// // context, +// // ); +// // } else { +// // showDialog( +// // context: context, +// // builder: (BuildContext cxt) => ConfirmDialog( +// // message: "You need to give location permission to mark attendance", +// // onTap: () async { +// // Navigator.pop(context); +// // await Geolocator.openAppSettings(); +// // }, +// // ), +// // ); +// // } +// // }); +// // } else { +// // showDialog( +// // context: context, +// // builder: (BuildContext cxt) => ConfirmDialog( +// // message: "You need to enable location services to mark attendance", +// // onTap: () async { +// // Navigator.pop(context); +// // await Geolocator.openLocationSettings(); +// // }, +// // ), +// // ); +// // } +// // }); +// // } +// // }), diff --git a/lib/new_views/swipe_module/models/swipe_model.dart b/lib/new_views/swipe_module/models/swipe_model.dart new file mode 100644 index 00000000..7251a838 --- /dev/null +++ b/lib/new_views/swipe_module/models/swipe_model.dart @@ -0,0 +1,68 @@ + +import 'package:flutter/material.dart'; + +class SwipeModel { + final bool? data; + final String? message; + final String ?title; + final String ?innerMessage; + final int ?responseCode; + final bool ?isSuccess; + + SwipeModel({ + this.data, + this.message, + this.title, + this.innerMessage, + this.responseCode, + this.isSuccess, + }); + + factory SwipeModel.fromJson(Map json) { + return SwipeModel( + data: json['data'], + message: json['message'] , + title: json['title'] , + innerMessage: json['innerMessage'] , + responseCode: json['responseCode'], + isSuccess: json['isSuccess'] , + ); + } + + Map toJson() { + return { + 'data': data, + 'message': message, + 'title': title, + 'innerMessage': innerMessage, + 'responseCode': responseCode, + 'isSuccess': isSuccess, + }; + } + + +} + +class Swipe { + final int ?swipeTypeValue; + final String? value; + final double? latitude; + final double? longitude; + + Swipe({ + this.swipeTypeValue, + this.value, + this.latitude, + this.longitude, + }); + + Map toJson() { + return { + 'swipeTypeValue': swipeTypeValue, + 'value': value, + 'latitude': latitude, + 'longitude': longitude, + }; + } +} + diff --git a/lib/new_views/swipe_module/models/swipe_transaction_history.dart b/lib/new_views/swipe_module/models/swipe_transaction_history.dart new file mode 100644 index 00000000..7bb0137a --- /dev/null +++ b/lib/new_views/swipe_module/models/swipe_transaction_history.dart @@ -0,0 +1,47 @@ +class SwipeHistory { + final int ?id; + final String? swipeTypeName; + final String? userName; + final String ?siteName; + final String ?pointName; + final String ?swipeTime; + final bool ?isSuccess; + final String? errorMessage; + + SwipeHistory({ + this.id, + this.swipeTypeName, + this.userName, + this.siteName, + this.pointName, + this.swipeTime, + this.isSuccess, + this.errorMessage, + }); + + factory SwipeHistory.fromJson(Map json) { + return SwipeHistory( + id: json['id'], + swipeTypeName: json['swipeTypeName'], + userName: json['userName'], + siteName: json['siteName'], + pointName: json['pointName'], + swipeTime: json['swipeTime']!=null? DateTime.parse(json['swipeTime']).toIso8601String():'', + isSuccess: json['isSuccess'], + errorMessage: json['errorMessage'], + ); + } + + Map toJson() { + return { + 'id': id, + 'swipeTypeName': swipeTypeName, + 'userName': userName, + 'siteName': siteName, + 'pointName': pointName, + 'swipeTime': swipeTime, + 'isSuccess': isSuccess, + 'errorMessage': errorMessage, + }; + } +} diff --git a/lib/new_views/swipe_module/models/swipe_transaction_model.dart b/lib/new_views/swipe_module/models/swipe_transaction_model.dart new file mode 100644 index 00000000..ff1d82fd --- /dev/null +++ b/lib/new_views/swipe_module/models/swipe_transaction_model.dart @@ -0,0 +1,47 @@ +class SwipeTransaction { + final int ?id; + final String? swipeTypeName; + final String ?userName; + final String? siteName; + final String? pointName; + final DateTime? swipeTime; + final bool? isSuccess; + final String? errorMessage; + + SwipeTransaction({ + this.id, + this.swipeTypeName, + this.userName, + this.siteName, + this.pointName, + this.swipeTime, + this.isSuccess, + this.errorMessage, + }); + + factory SwipeTransaction.fromJson(Map json) { + return SwipeTransaction( + id: json['id'] as int, + swipeTypeName: json['swipeTypeName'] , + userName: json['userName'] , + siteName: json['siteName'] , + pointName: json['pointName'] , + swipeTime: DateTime.parse(json['swipeTime']), + isSuccess: json['isSuccess'] , + errorMessage: json['errorMessage'] , + ); + } + + // Map toJson() { + // return { + // 'id': id, + // 'swipeTypeName': swipeTypeName, + // 'userName': userName, + // 'siteName': siteName, + // 'pointName': pointName, + // 'swipeTime': swipeTime.toIso8601String(), + // 'isSuccess': isSuccess, + // 'errorMessage': errorMessage, + // }; + // } +} diff --git a/lib/new_views/swipe_module/non_hmg_employee_swipe_view.dart b/lib/new_views/swipe_module/non_hmg_employee_swipe_view.dart new file mode 100644 index 00000000..8f392199 --- /dev/null +++ b/lib/new_views/swipe_module/non_hmg_employee_swipe_view.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:nfc_manager/nfc_manager.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; + +class NonHmgEmployeeSwipeView extends StatefulWidget { + NonHmgEmployeeSwipeView({Key? key}) : super(key: key); + + @override + _NonHmgEmployeeSwipeViewState createState() { + return _NonHmgEmployeeSwipeViewState(); + } +} + +class _NonHmgEmployeeSwipeViewState extends State { + bool isNfcEnabled = false; + + @override + void initState() { + super.initState(); + checkForNfcAndLocationPermission(); + } + + void checkForNfcAndLocationPermission() async { + isNfcEnabled = await NfcManager.instance.isAvailable(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + margin: const EdgeInsets.only(top: 21), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(30), color: Colors.white, border: Border.all(color: AppColor.white936.withOpacity(.05), width: 1)), + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Mark Attendance", + style: AppTextStyles.heading5.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.neutral50), + ), + GridView( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: const EdgeInsets.only(bottom: 0, top: 16), + gridDelegate: + SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: (MediaQuery.of(context).size.width < 550) ? 3 : 5, childAspectRatio: 1 / 1, crossAxisSpacing: 16, mainAxisSpacing: 16), + children: [ + gridItem("Nfc", Icons.nfc, isNfcEnabled).onPress(isNfcEnabled ? () {} : null), + gridItem("Qr Scan", Icons.qr_code, true).onPress(() {}), + ], + ) + ], + ), + ); + } + + Widget gridItem(String label, IconData iconData, bool enable) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(25), color: AppColor.white30, border: Border.all(color: AppColor.white936.withOpacity(.03), width: 1)), + alignment: Alignment.center, + child: Column( + //mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Icon(iconData, color: enable ? AppColor.black35 : Colors.grey), + Text( + label, + style: AppTextStyles.heading6.copyWith(color: enable ? AppColor.black35 : Colors.grey), + ), + ], + ), + ); + } +} diff --git a/lib/new_views/swipe_module/swipe_history_view.dart b/lib/new_views/swipe_module/swipe_history_view.dart new file mode 100644 index 00000000..84169833 --- /dev/null +++ b/lib/new_views/swipe_module/swipe_history_view.dart @@ -0,0 +1,189 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:test_sa/controllers/providers/api/user_provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/string_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; +import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; +import 'package:test_sa/views/widgets/date_and_time/date_picker.dart'; +import 'package:test_sa/views/widgets/loaders/lazy_loading.dart'; +import 'package:test_sa/views/widgets/loaders/no_data_found.dart'; + + +import 'models/swipe_transaction_history.dart'; + +class SwipeHistoryView extends StatefulWidget { + static const routeName = '/swipe_list_view'; + + const SwipeHistoryView({Key? key}) : super(key: key); + + @override + State createState() => _SwipeHistoryViewState(); +} + + +class _SwipeHistoryViewState extends State { + DateTime dateFrom = DateTime.now(); + DateTime dateTo = DateTime.now(); + UserProvider? _userProvider; + @override + void initState() { + getSwipeHistory(); + super.initState(); + } + + void getSwipeHistory () { + _userProvider = Provider.of(context,listen:false); + WidgetsBinding.instance.addPostFrameCallback((_) async { + await _userProvider!.getSwipeTransactionHistory(userId: _userProvider!.user!.userID!,dateFrom: dateFrom,dateTo: dateTo); + }); + } + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const DefaultAppBar(title: 'Swipe History',), + body: Column( + crossAxisAlignment:CrossAxisAlignment.start, + children: [ + + 8.height, + Row( + children: [ + ADatePicker( + label: context.translation.from, + date: dateFrom, + from: DateTime(DateTime.now().year - 5, DateTime.now().month, DateTime.now().day), + formatDateWithTime: true, + onDatePicker: (selectedDate) { + if (selectedDate != null) { + showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ).then((selectedTime) { + // Handle the selected date and time here. + if (selectedTime != null) { + DateTime selectedDateTime = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + selectedTime.hour, + selectedTime.minute, + ); + if (selectedDateTime != null) { + setState(() { + dateFrom = selectedDateTime; + }); + } + } + }); + } + }, + ).expanded, + 8.width, + ADatePicker( + label: context.translation.to, + date: dateTo, + from: DateTime(DateTime.now().year - 5, DateTime.now().month, DateTime.now().day), + formatDateWithTime: true, + onDatePicker: (selectedDate) { + if (selectedDate != null) { + showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ).then((selectedTime) { + // Handle the selected date and time here. + if (selectedTime != null) { + DateTime selectedDateTime = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + selectedTime.hour, + selectedTime.minute, + ); + if (selectedDateTime != null) { + setState(() { + dateTo = selectedDateTime; + + }); + + } + } + }); + } + }, + ).expanded, + ], + ), + 12.height, + + AppFilledButton(label: context.translation.search, maxWidth: false, onPressed: getSwipeHistory), + 8.height, + const Divider(thickness: 2,color:AppColor.white60 ,), + + Consumer( + builder: (context, snapshot,child) { + return SwipeHistoryList(snapshot.swipeHistory ?? [], snapshot.isLoading).expanded; + } + ), + + + ], + ).paddingAll(20), + ); + } +} + + + + +class SwipeHistoryList extends StatelessWidget { + List list; + bool isLoading; + + SwipeHistoryList(this.list, this.isLoading, {Key ?key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return (list.isEmpty && !isLoading) + ? NoDataFound(message: context.translation.noDataFound).center + : ListView.separated( + padding: EdgeInsets.only(top: 12.toScreenHeight), + itemBuilder: (cxt, index) { + if (isLoading) return const SizedBox().toRequestShimmer(cxt, isLoading); + return SwipeHistoryCard(list[index]); + }, + separatorBuilder: (cxt, index) => 12.height, + itemCount: isLoading ? 6 : list.length); + } +} +class SwipeHistoryCard extends StatelessWidget { + final SwipeHistory swipeHistory; + final bool showShadow; + const SwipeHistoryCard(this.swipeHistory, {Key? key, this.showShadow = true}) : super(key: key); + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(swipeHistory.swipeTime!.toServiceRequestDetailsFormat, textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50)), + ], + ), + 8.height, + '${context.translation.swipeTypeName}: ${swipeHistory.swipeTypeName?.cleanupWhitespace.capitalizeFirstOfEach}'.bodyText(context), + '${context.translation.userName}: ${swipeHistory.userName}'.bodyText(context), + '${context.translation.siteName}: ${swipeHistory.siteName}'.bodyText(context), + '${context.translation.pointName}: ${swipeHistory.pointName}'.bodyText(context), + 8.height, + ], + ).toShadowContainer(context, showShadow: showShadow); + } +} + diff --git a/lib/new_views/swipe_module/swipe_success_view.dart b/lib/new_views/swipe_module/swipe_success_view.dart new file mode 100644 index 00000000..8c55ebb3 --- /dev/null +++ b/lib/new_views/swipe_module/swipe_success_view.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; + +class SwipeSuccessView extends StatelessWidget { + static const routeName = '/swipe_success_view'; + const SwipeSuccessView({Key ?key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Column( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment:CrossAxisAlignment.center, + children: [ + 'swipe_success'.toSvgAsset(), + 17.height, + context.translation.successful.heading4(context).custom(color: AppColor.white936), + 8.height, + context.translation.youHaveSuccessfullyMarkedYourAttendance.bodyText2(context).custom(color: AppColor.neutral120), + ], + ).expanded, + AppFilledButton( + label: 'Close', + maxWidth: true, + onPressed:(){ + Navigator.pop(context); + }, + ), + ], + ).paddingOnly(start: 20, end: 20, bottom: 16), + ); + } +} + + +// ClipOval( +// child: Container( +// color: Colors.grey.withOpacity(0.2), +// padding: const EdgeInsets.all(10), +// child: ClipOval( +// child: Container( +// color: AppColor.white.withOpacity(0.2), +// padding: const EdgeInsets.all(10), +// child: ClipOval( +// child: Container( +// color: AppColor.white, +// child: 'success_swipe'.toSvgAsset().paddingAll(50), +// ), +// ).paddingAll(20), +// ), +// ), +// ), +// ) diff --git a/lib/new_views/swipe_module/utils/location_utils.dart b/lib/new_views/swipe_module/utils/location_utils.dart new file mode 100644 index 00000000..8e02e3da --- /dev/null +++ b/lib/new_views/swipe_module/utils/location_utils.dart @@ -0,0 +1,67 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:permission_handler/permission_handler.dart'; + +class LocationUtilities { + static void havePermission(Function(bool) callback) { + Geolocator.checkPermission().then((value) async { + if (value == LocationPermission.denied || value == LocationPermission.deniedForever) { + value = await Geolocator.requestPermission(); + callback(![LocationPermission.denied, LocationPermission.deniedForever].contains(value)); + } else { + callback(true); + } + }); + } + + static void isEnabled(Function(bool) callback) { + Geolocator.isLocationServiceEnabled().then((value) => callback(value)); + } + + static bool _listeningSettingChange = true; + + static void listenGPS({bool change = true, Function(bool) ?onChange}) async { + _listeningSettingChange = change; + if (change == false) return; + + Future.doWhile(() async { + await Future.delayed(const Duration(milliseconds: 1000)); + var enable = await Geolocator.isLocationServiceEnabled(); + onChange!(enable); + return _listeningSettingChange; + }); + } + + static void locationFun(Function(bool) completion, BuildContext context) { + Permission.location.isGranted.then((isGranted) { + if (!isGranted) { + Permission.location.request().then((granted) { + completion(granted == PermissionStatus.granted); + }); + } + completion(isGranted); + }); + } + + static void getCurrentLocation(Function(Position position, bool isMocked) callback, Function errorCallBack, BuildContext context) { + void done(Position position) { + //AppStorage.sp.saveLocation(position); + bool isMocked = position.isMocked; + callback(position, isMocked); + } + + locationFun((granted) { + if (granted) { + Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.medium, timeLimit: const Duration(seconds: 5)).then((value) { + done(value); + }).catchError((err) { + errorCallBack(); + }); + } else { + // AppPermissions + } + }, context); + } +} diff --git a/lib/new_views/swipe_module/utils/swipe_general_utils.dart b/lib/new_views/swipe_module/utils/swipe_general_utils.dart new file mode 100644 index 00000000..49964366 --- /dev/null +++ b/lib/new_views/swipe_module/utils/swipe_general_utils.dart @@ -0,0 +1,530 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:developer'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:google_api_availability/google_api_availability.dart'; +import 'package:huawei_location/huawei_location.dart'; +import 'package:intl/intl.dart'; +import 'package:nfc_manager/nfc_manager.dart'; +import 'package:nfc_manager/platform_tags.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:test_sa/controllers/providers/api/user_provider.dart'; +import 'package:test_sa/extensions/context_extension.dart'; +import 'package:test_sa/extensions/int_extensions.dart'; +import 'package:test_sa/extensions/text_extensions.dart'; +import 'package:test_sa/extensions/widget_extensions.dart'; +import 'package:test_sa/main.dart'; +import 'package:test_sa/new_views/app_style/app_color.dart'; +import 'package:test_sa/new_views/common_widgets/app_lazy_loading.dart'; +import 'package:test_sa/new_views/swipe_module/dialoge/confirm_dialog.dart'; +import 'package:test_sa/new_views/swipe_module/dialoge/nfc_reader_sheet.dart'; +import 'package:test_sa/new_views/swipe_module/enums/swipe_type.dart'; +import 'package:test_sa/new_views/swipe_module/models/swipe_model.dart'; +import 'package:test_sa/new_views/swipe_module/swipe_success_view.dart'; +import 'package:test_sa/new_views/swipe_module/utils/location_utils.dart'; +import 'package:test_sa/views/widgets/qr/scan_qr.dart'; +import 'package:wifi_iot/wifi_iot.dart'; + +class SwipeGeneralUtils { + SwipeGeneralUtils._(); + + static SwipeGeneralUtils instance = SwipeGeneralUtils._(); + static bool _isLoadingVisible = false; + + static bool get isLoading => _isLoadingVisible; + + void markFakeAttendance(dynamic sourceName, String lat, String long, @required BuildContext context) async { + showLoading(context); + try { + hideLoading(navigatorKey.currentState!.overlay!.context); + confirmDialog(navigatorKey.currentState!.overlay!.context, "Fake Location)"); + } catch (ex) { + log('$ex'); + hideLoading(context); + //handleException(ex, context, null); + } + } + + void showLoading(BuildContext context) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _isLoadingVisible = true; + showDialog( + context: context, + barrierColor: Colors.black.withOpacity(0.5), + useRootNavigator: false, + builder: (BuildContext context) => const AppLazyLoading(), + ).then((value) { + _isLoadingVisible = false; + }); + }); + } + + void hideLoading(BuildContext context) { + if (_isLoadingVisible) { + _isLoadingVisible = false; + Navigator.of(context).pop(); + } + _isLoadingVisible = false; + } + + static Future getStringFromPrefs(String key) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.getString(key) ?? ""; + } + + void confirmDialog(cxt, String message, {VoidCallback? onTap}) { + showDialog( + context: cxt, + builder: (BuildContext cxt) => ConfirmDialog(message: message, onTap: onTap), + ); + } + + void showErrorDialog({String ?message, required BuildContext context}) { + showDialog( + context: context, + builder: (context) => ConfirmDialog(message: message, title: 'Error', onTap: () => Navigator.pop(context)), + ); + } + + void showMDialog(context, {Widget ?child, Color? backgroundColor, bool isDismissable = true, bool isBusniessCard = false}) async { + return showDialog( + context: context, + barrierDismissible: isDismissable, + builder: (context) { + return Dialog( + shape: isBusniessCard + ? const RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(15.0), + ), + ) + : null, + backgroundColor: backgroundColor, + child: child, + ); + }, + ); + } + + Widget attendanceTypeCard(String title, String icon, bool isEnabled, VoidCallback onPress, BuildContext context) { + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: isEnabled ? Colors.white : AppColor.white70, + borderRadius: BorderRadius.circular(18), + border: Border.all(color: AppColor.white70, width: 2), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + icon.toSvgAsset(color: isEnabled ? null : Colors.grey.withOpacity(0.5)), + title.heading5(context).custom(color: isEnabled ? AppColor.neutral50 : Colors.grey.withOpacity(0.5)), + ], + ), + ).onPress( + () { + if (!isEnabled) return; + onPress(); + }, + ); + } + + //huawei permission part.... + void getHuaweiCurrentLocation({SwipeTypeEnum ?attendanceType, required BuildContext context}) async { + try { + showLoading(context); + FusedLocationProviderClient locationService = FusedLocationProviderClient()..initFusedLocationService(); + LocationRequest locationRequest = LocationRequest(); + locationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY; + locationRequest.interval = 500; + List locationRequestList = [locationRequest]; + LocationSettingsRequest locationSettingsRequest = LocationSettingsRequest(requests: locationRequestList); + + StreamSubscription _streamSubscription; + int requestCode = (await (locationService.requestLocationUpdates(locationRequest)))!; + _streamSubscription = locationService.onLocationData!.listen( + (Location location) async { + hideLoading(context); + await locationService.removeLocationUpdates(requestCode); + handleSwipeOperation(swipeType: attendanceType!, context: context, lat: location.latitude??0, long: location.longitude??0); + requestCode = 0; + //TODO cancel this stream when listening done.. + // _streamSubscription.cancel(); + }, + ); + } catch (error) { + log("HUAWEI LOCATION ERROR!!!!!"); + log('$error'); + hideLoading(context); + // handleException(error, context, null); + } + } + + Future requestPermissions() async { + var result = await [ + Permission.location, + ].request(); + return (result[Permission.location] == PermissionStatus.granted || result[Permission.locationAlways] == PermissionStatus.granted); + } + + void checkHuaweiLocationPermission({required SwipeTypeEnum attendanceType,required BuildContext context}) async { + // Permission_Handler permissionHandler = PermissionHandler(); + LocationUtilities.isEnabled((bool isEnabled) async { + if (isEnabled) { + LocationUtilities.havePermission((bool permission) async { + if (permission) { + getHuaweiCurrentLocation(attendanceType: attendanceType, context: context); + } else { + bool has = await requestPermissions(); + if (has) { + getHuaweiCurrentLocation(attendanceType: attendanceType, context: context); + } else { + showDialog( + context: context, + builder: (BuildContext cxt) => ConfirmDialog( + message: "You need to give location permission to mark attendance", + onTap: () { + Navigator.pop(context); + }, + ), + ); + } + } + }); + } else { + showDialog( + context: context, + builder: (BuildContext cxt) => ConfirmDialog( + message: "You need to enable location services to mark attendance", + onTap: () async { + Navigator.pop(context); + await Geolocator.openLocationSettings(); + }, + ), + ); + } + }); + + // if (await permissionHandler.hasLocationPermission()) { + // getHuaweiCurrentLocation(attendanceType); + // } else { + // bool has = await requestPermissions(); + // if (has) { + // getHuaweiCurrentLocation(attendanceType); + // } else { + // showDialog( + // context: context, + // builder: (BuildContext cxt) => ConfirmDialog( + // message: "You need to give location permission to mark attendance", + // onTap: () { + // Navigator.pop(context); + // }, + // ), + // ); + // } + // } + } + + void handleSwipeOperation({required SwipeTypeEnum swipeType,required double lat,required double long,required BuildContext context}) { + switch (swipeType) { + case SwipeTypeEnum.NFC: + handleNfcAttendance(latitude: lat, longitude: long, context: context); + return; + case SwipeTypeEnum.QR: + performQrCodeAttendance(latitude: lat, longitude: long, context: context); + return; + case SwipeTypeEnum.Wifi: + performWifiAttendance(latitude: lat, long: long, context: context); + return; + } + } + + String formatTimeOnly(DateTime dateTime) { + return DateFormat.Hms().format(dateTime); + } + + Future performQrCodeAttendance({double ?latitude, double? longitude,required BuildContext context}) async { + UserProvider userProvider = Provider.of(context, listen: false); + + String qrCodeValue = await Navigator.of(context).push( + MaterialPageRoute(builder: (_) => ScanQr()), + ) as String; + + if (qrCodeValue != null) { + showLoading(context); + try { + final swipeModel = Swipe( + swipeTypeValue: SwipeTypeEnum.QR.getIntFromSwipeTypeEnum(), + value: qrCodeValue, + latitude: latitude, + longitude: longitude, + ); + + await userProvider.makeSwipe(model: swipeModel).then((swipeResponse) { + if (swipeResponse.isSuccess==true) { + hideLoading(context); + Navigator.pushNamed(context, SwipeSuccessView.routeName); + } else { + hideLoading(context); + showDialog( + barrierDismissible: true, + context: context, + builder: (cxt) => ConfirmDialog( + message: swipeResponse.message ?? "", + onTap: () { + Navigator.pop(context); + }, + onCloseTap: () {}, + ), + ); + } + }); + } catch (ex) { + log('$ex'); + hideLoading(context); + //this need to confirm where it comes.. + // handleException(ex, context, null); + } + } + } + + Future handleNfcAttendance({double ?latitude = 0, double? longitude = 0,required BuildContext context}) async { + // UserProvider _userProvider = Provider.of(context,listen:false); + + if (Platform.isIOS) { + readNFc(onRead: (String nfcId) async { + await _processNfcAttendance(nfcId, latitude, longitude, context); + }); + } else { + showNfcReader(context, onNcfScan: (String nfcId) async { + await _processNfcAttendance(nfcId ?? '', latitude, longitude, context); + }); + } + } + + Future _processNfcAttendance( + String nfcId, + double ?latitude, + double ?longitude, + BuildContext context, + ) async { + showLoading(context); + try { + final swipeModel = Swipe(swipeTypeValue: SwipeTypeEnum.NFC.getIntFromSwipeTypeEnum(), value: nfcId, latitude: latitude, longitude: longitude); + UserProvider userProvider = Provider.of(context, listen: false); + + final swipeResponse = await userProvider.makeSwipe(model: swipeModel); + + if (swipeResponse.isSuccess==true) { + hideLoading(context); + Navigator.pushNamed(context, SwipeSuccessView.routeName); + } else { + hideLoading(context); + showErrorDialog(message: swipeResponse.message ?? "Unexpected error occurred", context: context); + } + } catch (errSwipeGeneralUtilsor) { + hideLoading(context); + } + } + + void handleSwipe({required SwipeTypeEnum swipeType, required bool isEnable, required BuildContext context}) async { + if (Platform.isAndroid && !(await isGoogleServicesAvailable())) { + checkHuaweiLocationPermission(attendanceType: swipeType, context: context); + } else { + LocationUtilities.isEnabled((bool isEnabled) { + if (isEnabled) { + LocationUtilities.havePermission((bool permission) { + if (permission) { + showLoading(context); + LocationUtilities.getCurrentLocation( + (Position position, bool isMocked) { + if (isMocked) { + hideLoading(context); + markFakeAttendance(swipeType.name, position.latitude.toString() ?? "", position.longitude.toString() ?? "", context); + } else { + hideLoading(context); + handleSwipeOperation(swipeType: swipeType, lat: position.latitude, long: position.longitude, context: context); + } + }, + () { + hideLoading(context); + confirmDialog(context, "Unable to determine your location, Please make sure that your location services are turned on & working."); + }, + context, + ); + } else { + showInfoDialog( + message: "You need to give location permission to mark attendance", + onTap: () async { + await Geolocator.openAppSettings(); + }); + } + }); + } else { + showInfoDialog( + message: "You need to enable location services to mark attendance", + onTap: () async { + await Geolocator.openLocationSettings(); + }); + } + }); + } + } + + void showInfoDialog({required String message, VoidCallback? onTap}) { + showDialog( + context: navigatorKey.currentState!.overlay!.context, + builder: (BuildContext cxt) => ConfirmDialog( + message: message, + onTap: () async { + Navigator.pop(navigatorKey.currentState!.overlay!.context); + onTap!(); + }, + ), + ); + } + + List availableAttendanceMethodList({required BuildContext context, required UserProvider userProvider, required bool isNfcSupported}) { + List availableMethods = []; + if (userProvider.user!.enableNFC!) { + availableMethods.add(attendanceTypeCard(SwipeTypeEnum.NFC.name, 'nfc_icon', isNfcSupported, () { + Navigator.pop(context); + handleSwipe(swipeType: SwipeTypeEnum.NFC, isEnable: userProvider.user!.enableNFC!, context: navigatorKey.currentState!.overlay!.context); + }, context)); + } + if (userProvider.user!.enableQR!) { + availableMethods.add(attendanceTypeCard(SwipeTypeEnum.QR.name, 'qr', userProvider.user!.enableQR!, () { + Navigator.pop(context); + handleSwipe(swipeType: SwipeTypeEnum.QR, isEnable: userProvider.user!.enableQR!, context: navigatorKey.currentState!.overlay!.context); + }, context)); + } + if (kDebugMode) { + userProvider.user!.enableWifi!= true; + } + + if (userProvider.user!.enableWifi!) { + availableMethods.add(attendanceTypeCard(SwipeTypeEnum.Wifi.name, 'wifi_icon', userProvider.user!.enableWifi!, () { + Navigator.pop(context); + handleSwipe(swipeType: SwipeTypeEnum.Wifi, isEnable: userProvider.user!.enableWifi!, context: navigatorKey.currentState!.overlay!.context); + }, context)); + } + return availableMethods; + } + + void showSwipeTypeBottomSheetSheet({required bool isNfcSupported}) { + BuildContext context = navigatorKey.currentState!.overlay!.context; + UserProvider _userProvider = Provider.of(context, listen: false); + + showModalBottomSheet( + context: context, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(20), + ), + ), + clipBehavior: Clip.antiAliasWithSaveLayer, + builder: (BuildContext context) => Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + context.translation.markAttendance.heading4(context).custom(color: AppColor.white936), + 8.height, + context.translation.selectMethodToMarkAttendance.bodyText2(context).custom(color: AppColor.neutral120), + 12.height, + GridView( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 1, crossAxisSpacing: 12, mainAxisSpacing: 12), + children: availableAttendanceMethodList(context: context, userProvider: _userProvider, isNfcSupported: isNfcSupported)), + ], + ).paddingAll(16), + ); + } + + void readNFc({Function(String) ?onRead}) { + NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async { + MifareUltralight f; + if (Platform.isAndroid) { + f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); + } else { + f = MifareUltralight(tag: tag, identifier: tag.data["mifare"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); + } + String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); + NfcManager.instance.stopSession(); + onRead!(identifier); + }).catchError((err) { + print(err); + }); + } + + //HUAWEI DECISION MAKING + Future isGoogleServicesAvailable() async { + GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability(); + String status = availability.toString().split('.').last; + if (status == "success") { + return true; + } + return false; + } + + Future performWifiAttendance({required double latitude, required double long, required BuildContext context}) async { + String ssId = String.fromCharCodes(base64Decode("SE1HLU1PSEVNTQ==")); + String password = String.fromCharCodes(base64Decode("TTBoZW1tQDEyMTI=")); + showLoading(context); + bool isConnected = await WiFiForIoTPlugin.connect(ssId, password: password, joinOnce: Platform.isIOS ? false : true, security: NetworkSecurity.WPA, withInternet: false); + + if (Platform.isIOS) { + if (await WiFiForIoTPlugin.getSSID() == ssId) { + isConnected = true; + } else { + isConnected = false; + } + } + UserProvider userProvider = Provider.of(context, listen: false); + + if (isConnected) { + await WiFiForIoTPlugin.forceWifiUsage(true); + await Future.delayed(const Duration(seconds: 6)); + try { + final swipeModel = Swipe(swipeTypeValue: SwipeTypeEnum.Wifi.getIntFromSwipeTypeEnum(), value: '', latitude: latitude, longitude: long); + + final swipeResponse = await userProvider.makeSwipe(model: swipeModel); + await closeWifiRequest(); + if (swipeResponse.isSuccess==true) { + hideLoading(context); + Navigator.pushNamed(context, SwipeSuccessView.routeName); + } else { + hideLoading(context); + await Future.delayed(const Duration(milliseconds: 250)); + showErrorDialog(message: swipeResponse.message ?? "Unexpected error occurred", context: context); + } + } catch (errSwipeGeneralUtilsor) { + hideLoading(context); + await closeWifiRequest(); + } + } else { + // if (userProvider.) { + hideLoading(context); + confirmDialog(context, "Come Near HMG Wifi"); + // } else { + // await closeWifiRequest(); + // } + await closeWifiRequest(); + } + } + + Future closeWifiRequest() async { + if (Platform.isAndroid) { + await WiFiForIoTPlugin.forceWifiUsage(false); + } + return await WiFiForIoTPlugin.disconnect(); + } +} diff --git a/localization_error.txt b/localization_error.txt index b7a7b76f..44f680c9 100644 --- a/localization_error.txt +++ b/localization_error.txt @@ -4,6 +4,7 @@ "signInToYour", "overdue", "newR", - "noDataFound" + "noDataFound", + "successful" ] }