From fb8d6c7b20d3c074d6f269c2636bd67a28e9d556 Mon Sep 17 00:00:00 2001 From: devmirza121 Date: Mon, 31 Jan 2022 17:46:43 +0300 Subject: [PATCH 1/9] Dashboard API's 1.1 --- assets/images/play.svg | 3 + assets/images/thumb.svg | 8 + lib/api/api_client.dart | 4 +- lib/api/dashboard_api_client.dart | 5 +- lib/config/routes.dart | 4 +- lib/extensions/widget_extensions.dart | 19 ++ lib/main.dart | 18 +- .../get_attendance_tracking_list_model.dart | 6 +- lib/models/generic_response_model.dart | 12 +- lib/provider/dashboard_provider_model.dart | 39 ++++ .../{dashboard.dart => dashboard_screen.dart} | 182 ++++++++++-------- .../shimmer/dashboard_shimmer_widget.dart | 82 ++++++++ pubspec.lock | 14 ++ pubspec.yaml | 2 + 14 files changed, 308 insertions(+), 90 deletions(-) create mode 100644 assets/images/play.svg create mode 100644 assets/images/thumb.svg create mode 100644 lib/provider/dashboard_provider_model.dart rename lib/ui/landing/{dashboard.dart => dashboard_screen.dart} (64%) create mode 100644 lib/widgets/shimmer/dashboard_shimmer_widget.dart diff --git a/assets/images/play.svg b/assets/images/play.svg new file mode 100644 index 0000000..1608b2e --- /dev/null +++ b/assets/images/play.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/thumb.svg b/assets/images/thumb.svg new file mode 100644 index 0000000..855ba71 --- /dev/null +++ b/assets/images/thumb.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/lib/api/api_client.dart b/lib/api/api_client.dart index a08ed01..609aca9 100644 --- a/lib/api/api_client.dart +++ b/lib/api/api_client.dart @@ -7,6 +7,8 @@ import 'package:http/http.dart'; import 'package:http/io_client.dart'; import 'package:mohem_flutter_app/exceptions/api_exception.dart'; +import '../main.dart'; + typedef FactoryConstructor = U Function(dynamic); class APIError { @@ -76,7 +78,7 @@ class ApiClient { var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: _headers, retryTimes: retryTimes); try { if (!kReleaseMode) { - print("res: " + response.body); + logger.i("res: " + response.body); } var jsonData = jsonDecode(response.body); if (jsonData["ErrorMessage"] == null) { diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index 5b6f961..ad513e9 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -4,6 +4,7 @@ import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/models/basic_member_information_model.dart'; import 'package:mohem_flutter_app/models/check_mobile_app_version_model.dart'; +import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart'; import 'package:mohem_flutter_app/models/member_login_list_model.dart'; @@ -17,13 +18,13 @@ class DashbaordApiClient { factory DashbaordApiClient() => _instance; - Future getAttendanceTracking() async { + Future getAttendanceTracking() async { String url = "${ApiConsts.erpRest}GET_Attendance_Tracking"; Map postParams = {}; postParams.addAll(AppState().postParamsJson); return await ApiClient().postJsonForObject((json) { GenericResponseModel responseData = GenericResponseModel.fromJson(json); - return responseData; + return responseData.getAttendanceTrackingList; }, url, postParams); } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 39a4ebc..8e5a64e 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:mohem_flutter_app/ui/landing/dashboard.dart'; +import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/ui/landing/today_attendance_screen.dart'; import 'package:mohem_flutter_app/ui/login/forgot_password_screen.dart'; import 'package:mohem_flutter_app/ui/login/login_screen.dart'; @@ -28,7 +28,7 @@ class AppRoutes { static final Map routes = { login: (context) => LoginScreen(), verifyLogin: (context) => VerifyLoginScreen(), - dashboard: (context) => Dashboard(), + dashboard: (context) => DashboardScreen(), newPassword: (context) => NewPasswordScreen(), forgotPassword: (context) => ForgotPasswordScreen(), todayAttendance: (context) => TodayAttendanceScreen(), diff --git a/lib/extensions/widget_extensions.dart b/lib/extensions/widget_extensions.dart index 787894d..dc083ce 100644 --- a/lib/extensions/widget_extensions.dart +++ b/lib/extensions/widget_extensions.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:shimmer/shimmer.dart'; extension WidgetExtensions on Widget { Widget onPress(VoidCallback onTap) => InkWell(onTap: onTap, child: this); @@ -8,4 +9,22 @@ extension WidgetExtensions on Widget { Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) => Padding(padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), child: this); + + Widget toShimmer() => Shimmer.fromColors( + baseColor: Color(0xffe8eff0), + highlightColor: Colors.white, + child: Container( + child: this, + color: Colors.white, + )); + + Widget animatedSwither() => AnimatedSwitcher( + duration: const Duration(milliseconds: 500), + // transitionBuilder: (Widget child, Animation animation) { + // return ScaleTransition(scale: animation, child: child); + // }, + switchInCurve: Curves.linearToEaseOut, + switchOutCurve: Curves.linearToEaseOut, + child: this, + ); } diff --git a/lib/main.dart b/lib/main.dart index 99cff95..0b05d10 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,11 +6,22 @@ import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/config/app_provider.dart'; import 'package:mohem_flutter_app/generated/codegen_loader.g.dart'; import 'package:mohem_flutter_app/models/post_params_model.dart'; +import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/theme/app_theme.dart'; +import 'package:provider/provider.dart'; import 'package:sizer/sizer.dart'; +import 'package:logger/logger.dart'; import 'config/routes.dart'; + +var logger = Logger( + filter: null, // Use the default LogFilter (-> only log in debug mode) + printer: PrettyPrinter(), // Use the PrettyPrinter to format and print log + output: null, // U +); + + Future main() async { WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); @@ -22,7 +33,12 @@ Future main() async { ], path: 'assets/langs', assetLoader: CodegenLoader(), - child: MyApp(), + child: MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => DashboardProviderModel()), + ], + child: MyApp(), + ), ), ); } diff --git a/lib/models/dashboard/get_attendance_tracking_list_model.dart b/lib/models/dashboard/get_attendance_tracking_list_model.dart index 66b9ed8..fb415b0 100644 --- a/lib/models/dashboard/get_attendance_tracking_list_model.dart +++ b/lib/models/dashboard/get_attendance_tracking_list_model.dart @@ -1,5 +1,5 @@ -class GetAttendanceTrackingList { - GetAttendanceTrackingList({ +class GetAttendanceTracking { + GetAttendanceTracking({ this.pBreakHours, this.pLateInHours, this.pRemainingHours, @@ -25,7 +25,7 @@ class GetAttendanceTrackingList { dynamic pSwipeIn; dynamic pSwipeOut; - factory GetAttendanceTrackingList.fromMap(Map json) => GetAttendanceTrackingList( + factory GetAttendanceTracking.fromMap(Map json) => GetAttendanceTracking( pBreakHours: json["P_BREAK_HOURS"] == null ? null : json["P_BREAK_HOURS"], pLateInHours: json["P_LATE_IN_HOURS"] == null ? null : json["P_LATE_IN_HOURS"], pRemainingHours: json["P_REMAINING_HOURS"] == null ? null : json["P_REMAINING_HOURS"], diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index fad6e51..86552a0 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -67,7 +67,7 @@ class GenericResponseModel { List? getAddressNotificationBodyList; List? getApprovesList; List? getAttachementList; - GetAttendanceTrackingList? getAttendanceTrackingList; + GetAttendanceTracking? getAttendanceTrackingList; List? getBasicDetColsStructureList; List? getBasicDetDffStructureList; List? getBasicDetNtfBodyList; @@ -567,15 +567,15 @@ class GenericResponseModel { getAbsenceCollectionNotificationBodyList = json['GetAbsenceCollectionNotificationBodyList']; getAbsenceDffStructureList = json['GetAbsenceDffStructureList']; getAbsenceTransactionList = json['GetAbsenceTransactionList']; - getAccrualBalancesList: + getAccrualBalancesList= json["GetAccrualBalancesList"] == null ? null : List.from(json["GetAccrualBalancesList"].map((x) => GetAccrualBalancesList.fromJson(x))); getActionHistoryList = json['GetActionHistoryList']; getAddressDffStructureList = json['GetAddressDffStructureList']; getAddressNotificationBodyList = json['GetAddressNotificationBodyList']; getApprovesList = json['GetApprovesList']; getAttachementList = json['GetAttachementList']; - getAttendanceTrackingList: - json["GetAttendanceTrackingList"] == null ? null : GetAttendanceTrackingList.fromMap(json["GetAttendanceTrackingList"]); + getAttendanceTrackingList= + json["GetAttendanceTrackingList"] == null ? null : GetAttendanceTracking.fromMap(json["GetAttendanceTrackingList"]); getBasicDetColsStructureList = json['GetBasicDetColsStructureList']; getBasicDetDffStructureList = json['GetBasicDetDffStructureList']; getBasicDetNtfBodyList = json['GetBasicDetNtfBodyList']; @@ -617,8 +617,8 @@ class GenericResponseModel { getNotificationButtonsList = json['GetNotificationButtonsList']; getNotificationReassignModeList = json['GetNotificationReassignModeList']; getObjectValuesList = json['GetObjectValuesList']; - getOpenMissingSwipesList: json["GetOpenMissingSwipesList"] == null ? null : GetOpenMissingSwipesList.fromJson(json["GetOpenMissingSwipesList"]); - getOpenNotificationsList: + getOpenMissingSwipesList= json["GetOpenMissingSwipesList"] == null ? null : GetOpenMissingSwipesList.fromJson(json["GetOpenMissingSwipesList"]); + getOpenNotificationsList= json["GetOpenNotificationsList"] == null ? null : List.from(json["GetOpenNotificationsList"].map((x) => GetOpenNotificationsList.fromMap(x))); getOpenNotificationsNumList = json['GetOpenNotificationsNumList']; getOpenPeriodDatesList = json['GetOpenPeriodDatesList']; diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart new file mode 100644 index 0000000..6ccbfc1 --- /dev/null +++ b/lib/provider/dashboard_provider_model.dart @@ -0,0 +1,39 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/main.dart'; +import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; + +/// Mix-in [DiagnosticableTreeMixin] to have access to [debugFillProperties] for the devtool +// ignore: prefer_mixin +class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { + bool isAttendanceTrackingLoading = true; + int isTimeRemainingInSeconds = 0; + GetAttendanceTracking? attendanceTracking; + + fetchAttendanceTracking() async { + try { + attendanceTracking = await DashbaordApiClient().getAttendanceTracking(); + isAttendanceTrackingLoading = false; + isTimeRemainingInSeconds = calculateSeconds("00:00:30"); + notifyListeners(); + } catch (ex) { + Utils.handleException(ex, null); + } + } + + int calculateSeconds(String time) { + int hour = int.parse(time.split(":")[0]); + int mints = int.parse(time.split(":")[1]); + int seconds = int.parse(time.split(":")[2]); + return ((hour * 60 * 60) + (mints * 60) + seconds); + } + + update() { + isAttendanceTrackingLoading = !isAttendanceTrackingLoading; + attendanceTracking?.pSwipeIn = "a"; + isTimeRemainingInSeconds = calculateSeconds("00:00:30"); + notifyListeners(); + } +} diff --git a/lib/ui/landing/dashboard.dart b/lib/ui/landing/dashboard_screen.dart similarity index 64% rename from lib/ui/landing/dashboard.dart rename to lib/ui/landing/dashboard_screen.dart index e8c2157..4a4ad94 100644 --- a/lib/ui/landing/dashboard.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; @@ -9,26 +10,33 @@ import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; +import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/theme/colors.dart'; import 'package:mohem_flutter_app/widgets/circular_avatar.dart'; +import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; +import 'package:provider/provider.dart'; import 'package:shimmer/shimmer.dart'; -class Dashboard extends StatefulWidget { - Dashboard({Key? key}) : super(key: key); +class DashboardScreen extends StatefulWidget { + DashboardScreen({Key? key}) : super(key: key); @override - _DashboardState createState() { - return _DashboardState(); + _DashboardScreenState createState() { + return _DashboardScreenState(); } } -class _DashboardState extends State { +class _DashboardScreenState extends State { @override void initState() { super.initState(); - DashbaordApiClient().getAttendanceTracking(); + final data = Provider.of(context, listen: false); + data.fetchAttendanceTracking(); } + int endTime = 0; + @override void dispose() { super.dispose(); @@ -44,8 +52,9 @@ class _DashboardState extends State { List iconT = ["assets/images/monthly_attendance.svg", "assets/images/work_from_home.svg", "assets/images/ticket_request.svg", "assets/images/work_from_home.svg"]; List namesD = ["Nostalgia Perfume Perfume", "Al Nafoura", "AlJadi", "Nostalgia Perfume"]; - // DashbaordApiClient().getOpenNotifications(); - DashbaordApiClient().getOpenMissingSwipes(); + final data = Provider.of(context); + + endTime=DateTime.now().millisecondsSinceEpoch + Duration(seconds: data.isTimeRemainingInSeconds).inMilliseconds; return Scaffold( body: Column( children: [ @@ -93,7 +102,9 @@ class _DashboardState extends State { ) ], ), - ) + ).onPress(() { + data.update(); + }) ], ).paddingOnly(left: 21, right: 21, top: 48, bottom: 7), Expanded( @@ -111,75 +122,96 @@ class _DashboardState extends State { Expanded( child: AspectRatio( aspectRatio: 159 / 159, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), - gradient: const LinearGradient(transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomRight, colors: [ - MyColors.gradiantEndColor, - MyColors.gradiantStartColor, - ]), - ), - child: Stack( - alignment: Alignment.center, - children: [ - // SvgPicture.asset("assets/images/"), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + child: (data.isAttendanceTrackingLoading + ? GetAttendanceTrackingShimmer() + : Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + gradient: const LinearGradient(transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomRight, colors: [ + MyColors.gradiantEndColor, + MyColors.gradiantStartColor, + ]), + ), + child: Stack( + alignment: Alignment.center, children: [ - LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true), - 9.height, - "07:55:12".toText14(color: Colors.white, isBold: true), - LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), - 9.height, - const ClipRRect( - borderRadius: BorderRadius.all( - Radius.circular(20), - ), - child: LinearProgressIndicator( - value: 0.7, - minHeight: 8, - valueColor: const AlwaysStoppedAnimation(Colors.white), - backgroundColor: const Color(0xff196D73), - ), - ), - ], - ).paddingOnly(top: 12, right: 15, left: 12), - ), - Row( - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, + if (data.attendanceTracking?.pSwipeIn == null) SvgPicture.asset("assets/images/thumb.svg"), + Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [LocaleKeys.checkIn.tr().toText12(color: Colors.white), "09:00".toText14(color: Colors.white, isBold: true), 4.height], - ).paddingOnly(left: 12), - ), - Container( - width: 45, - height: 45, - padding: const EdgeInsets.only(left: 14, right: 14), - decoration: const BoxDecoration( - color: Color(0xff259EA4), - borderRadius: BorderRadius.only( - bottomRight: Radius.circular(15), - ), + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + CountdownTimer( + endTime: endTime, + onEnd: null, + endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), + textStyle: TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold), + ), + LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true), + if (data.attendanceTracking?.pSwipeIn == null) "01-02-2022".toText12(color: Colors.white), + if (data.attendanceTracking?.pSwipeIn != null) + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 9.height, + "07:55:12".toText14(color: Colors.white, isBold: true), + LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), + 9.height, + const ClipRRect( + borderRadius: BorderRadius.all( + Radius.circular(20), + ), + child: LinearProgressIndicator( + value: 0.7, + minHeight: 8, + valueColor: const AlwaysStoppedAnimation(Colors.white), + backgroundColor: const Color(0xff196D73), + ), + ), + ], + ), + ], + ).paddingOnly(top: 12, right: 15, left: 12), + ), + Row( + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.checkIn.tr().toText12(color: Colors.white), + (data.attendanceTracking?.pSwipeIn == null ? "--:--" : "09:00").toText14(color: Colors.white, isBold: true), + 4.height + ], + ).paddingOnly(left: 12), + ), + Container( + width: 45, + height: 45, + padding: const EdgeInsets.only(left: 14, right: 14), + decoration: const BoxDecoration( + color: Color(0xff259EA4), + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(15), + ), + ), + child: SvgPicture.asset(data.attendanceTracking?.pSwipeIn == null ? "assets/images/play.svg" : "assets/images/stop.svg"), + ), + ], + ), + ], ), - child: SvgPicture.asset("assets/images/stop.svg"), - ), - ], - ), - ], - ), - ], - ), - ).onPress(() { - Navigator.pushNamed(context, AppRoutes.todayAttendance); - }), + ], + ), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.todayAttendance); + })) + .animatedSwither(), ), ), 9.width, diff --git a/lib/widgets/shimmer/dashboard_shimmer_widget.dart b/lib/widgets/shimmer/dashboard_shimmer_widget.dart new file mode 100644 index 0000000..1c63d71 --- /dev/null +++ b/lib/widgets/shimmer/dashboard_shimmer_widget.dart @@ -0,0 +1,82 @@ +import 'package:easy_localization/src/public_ext.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:shimmer/shimmer.dart'; + +class GetAttendanceTrackingShimmer extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + height: double.infinity, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Stack( + alignment: Alignment.center, + children: [ + // SvgPicture.asset("assets/images/"), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true).toShimmer(), + 16.height, + "07:55:12".toText10(color: Colors.white, isBold: true).toShimmer(), + 3.height, + LocaleKeys.timeLeftToday.tr().toText10(color: Colors.white).toShimmer(), + 9.height, + const ClipRRect( + borderRadius: BorderRadius.all( + Radius.circular(20), + ), + child: LinearProgressIndicator( + value: 0.7, + minHeight: 8, + valueColor: const AlwaysStoppedAnimation(Colors.white), + backgroundColor: const Color(0xff196D73), + ), + ).toShimmer(), + ], + ).paddingOnly(top: 12, right: 15, left: 12), + ), + Row( + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [LocaleKeys.checkIn.tr().toText12(color: Colors.white).toShimmer(),], + ).paddingOnly(left: 12), + ), + Container( + width: 45, + height: 45, + // color: Colors.blue, + padding: const EdgeInsets.only(left: 14, right: 14), + ).toShimmer(), + ], + ), + ], + ), + ], + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 1ae9d08..58ce64a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -104,6 +104,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_countdown_timer: + dependency: "direct main" + description: + name: flutter_countdown_timer + url: "https://pub.dartlang.org" + source: hosted + version: "4.1.0" flutter_lints: dependency: "direct dev" description: @@ -196,6 +203,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.9" + logger: + dependency: "direct main" + description: + name: logger + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 868012d..e7d1182 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,8 @@ dependencies: local_auth: ^1.1.9 fluttertoast: ^8.0.8 shimmer: ^2.0.0 + logger: ^1.1.0 + flutter_countdown_timer: ^4.1.0 dev_dependencies: From b6670289b12de5707e8f4a642835047c7318493c Mon Sep 17 00:00:00 2001 From: devmirza121 Date: Mon, 7 Feb 2022 11:09:15 +0300 Subject: [PATCH 2/9] Dashboard API's 1.2 --- lib/api/dashboard_api_client.dart | 22 ++ lib/api/login_api_client.dart | 1 + lib/extensions/string_extensions.dart | 8 + lib/extensions/widget_extensions.dart | 15 +- lib/main.dart | 6 +- lib/models/dashboard/list_menu.dart | 39 ++ lib/models/dashboard/menu_entries.dart | 60 +++ lib/models/dashboard/menus.dart | 10 + lib/models/generic_response_model.dart | 23 +- lib/provider/dashboard_provider_model.dart | 142 +++++++- lib/ui/landing/dashboard_screen.dart | 341 +++++++----------- lib/ui/landing/widget/menus_widget.dart | 138 +++++++ lib/ui/landing/widget/services_widget.dart | 147 ++++++++ lib/widgets/Updater.dart | 39 ++ .../shimmer/dashboard_shimmer_widget.dart | 109 +++++- 15 files changed, 864 insertions(+), 236 deletions(-) create mode 100644 lib/models/dashboard/list_menu.dart create mode 100644 lib/models/dashboard/menu_entries.dart create mode 100644 lib/models/dashboard/menus.dart create mode 100644 lib/ui/landing/widget/menus_widget.dart create mode 100644 lib/ui/landing/widget/services_widget.dart create mode 100644 lib/widgets/Updater.dart diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index ad513e9..b98a777 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -67,4 +67,26 @@ class DashbaordApiClient { return responseData; }, url, postParams); } + + //Menus List + Future getListMenu() async { + String url = "${ApiConsts.erpRest}GET_MENU"; + Map postParams = {}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData; + }, url, postParams); + } + + //GET_MENU_ENTRIES + Future getGetMenuEntries() async { + String url = "${ApiConsts.erpRest}GET_MENU_ENTRIES"; + Map postParams = {"P_SELECTED_RESP_ID": -999,"P_MENU_TYPE":"E"}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData; + }, url, postParams); + } } diff --git a/lib/api/login_api_client.dart b/lib/api/login_api_client.dart index fb6bb80..86b7ace 100644 --- a/lib/api/login_api_client.dart +++ b/lib/api/login_api_client.dart @@ -55,6 +55,7 @@ class LoginApiClient { AppState().postParamsObject?.pSessionId = responseData.pSESSIONID; AppState().postParamsObject?.pUserName = AppState().getUserName; AppState().postParamsObject?.pSelectedEmployeeNumber = AppState().getUserName; + return responseData; }, url, postParams); } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 9db4829..5d4f8cd 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -2,6 +2,14 @@ import 'package:flutter/cupertino.dart'; import 'package:intl/intl.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +extension CapExtension on String { + String get toCamelCase => "${this[0].toUpperCase()}${this.substring(1)}"; + String get inCaps => '${this[0].toUpperCase()}${this.substring(1)}'; + String get allInCaps => this.toUpperCase(); + String get capitalizeFirstofEach => this.trim().length > 0 ? this.trim().toLowerCase().split(" ").map((str) => str.inCaps).join(" ") : ""; +} + + extension EmailValidator on String { Widget get toWidget => Text(this); diff --git a/lib/extensions/widget_extensions.dart b/lib/extensions/widget_extensions.dart index dc083ce..130c867 100644 --- a/lib/extensions/widget_extensions.dart +++ b/lib/extensions/widget_extensions.dart @@ -10,13 +10,14 @@ extension WidgetExtensions on Widget { Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) => Padding(padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), child: this); - Widget toShimmer() => Shimmer.fromColors( - baseColor: Color(0xffe8eff0), - highlightColor: Colors.white, - child: Container( - child: this, - color: Colors.white, - )); + Widget toShimmer({bool isShow=true}) =>isShow? Shimmer.fromColors( + baseColor: Color(0xffe8eff0), + highlightColor: Colors.white, + child: Container( + child: this, + color: Colors.white, + ), + ):Container(child: this,); Widget animatedSwither() => AnimatedSwitcher( duration: const Duration(milliseconds: 500), diff --git a/lib/main.dart b/lib/main.dart index 0b05d10..1146ff0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,9 +16,9 @@ import 'config/routes.dart'; var logger = Logger( - filter: null, // Use the default LogFilter (-> only log in debug mode) - printer: PrettyPrinter(), // Use the PrettyPrinter to format and print log - output: null, // U + // filter: null, // Use the default LogFilter (-> only log in debug mode) + printer: PrettyPrinter(lineLength: 0), // Use the PrettyPrinter to format and print log + // output: null, // U ); diff --git a/lib/models/dashboard/list_menu.dart b/lib/models/dashboard/list_menu.dart new file mode 100644 index 0000000..ba327c2 --- /dev/null +++ b/lib/models/dashboard/list_menu.dart @@ -0,0 +1,39 @@ +class ListMenu { + ListMenu({ + this.menuId, + this.menuName, + this.menuType, + this.requestGroupId, + this.requestGroupName, + this.respId, + this.subMenuName, + }); + + int? menuId; + String? menuName; + String? menuType; + int? requestGroupId; + String? requestGroupName; + dynamic? respId; + String? subMenuName; + + factory ListMenu.fromJson(Map json) => ListMenu( + menuId: json["MENU_ID"] == null ? null : json["MENU_ID"], + menuName: json["MENU_NAME"] == null ? null : json["MENU_NAME"], + menuType: json["MENU_TYPE"] == null ? null : json["MENU_TYPE"], + requestGroupId: json["REQUEST_GROUP_ID"] == null ? null : json["REQUEST_GROUP_ID"], + requestGroupName: json["REQUEST_GROUP_NAME"] == null ? null : json["REQUEST_GROUP_NAME"], + respId: json["RESP_ID"], + subMenuName: json["SUB_MENU_NAME"] == null ? null : json["SUB_MENU_NAME"], + ); + + Map toJson() => { + "MENU_ID": menuId == null ? null : menuId, + "MENU_NAME": menuName == null ? null : menuName, + "MENU_TYPE": menuType == null ? null : menuType, + "REQUEST_GROUP_ID": requestGroupId == null ? null : requestGroupId, + "REQUEST_GROUP_NAME": requestGroupName == null ? null : requestGroupName, + "RESP_ID": respId, + "SUB_MENU_NAME": subMenuName == null ? null : subMenuName, + }; +} diff --git a/lib/models/dashboard/menu_entries.dart b/lib/models/dashboard/menu_entries.dart new file mode 100644 index 0000000..7678ad8 --- /dev/null +++ b/lib/models/dashboard/menu_entries.dart @@ -0,0 +1,60 @@ +class GetMenuEntriesList { + GetMenuEntriesList({ + this.addButton, + this.deleteButton, + this.entrySequence, + this.functionName, + this.icon, + this.lvl, + this.menuEntryType, + this.menuName, + this.parentMenuName, + this.prompt, + this.requestType, + this.updateButton, + }); + + String? addButton; + String? deleteButton; + int? entrySequence; + String? functionName; + String? icon; + int? lvl; + String? menuEntryType; + String? menuName; + String? parentMenuName; + String? prompt; + String? requestType; + String? updateButton; + + factory GetMenuEntriesList.fromJson(Map json) => GetMenuEntriesList( + addButton: json["ADD_BUTTON"] == null ? null : json["ADD_BUTTON"], + deleteButton: json["DELETE_BUTTON"] == null ? null : json["DELETE_BUTTON"], + entrySequence: json["ENTRY_SEQUENCE"] == null ? null : json["ENTRY_SEQUENCE"], + functionName: json["FUNCTION_NAME"] == null ? null : json["FUNCTION_NAME"], + icon: json["ICON"] == null ? null : json["ICON"], + lvl: json["LVL"] == null ? null : json["LVL"], + menuEntryType: json["MENU_ENTRY_TYPE"] == null ? null : json["MENU_ENTRY_TYPE"], + menuName: json["MENU_NAME"] == null ? null : json["MENU_NAME"], + parentMenuName: json["PARENT_MENU_NAME"] == null ? null : json["PARENT_MENU_NAME"], + prompt: json["PROMPT"] == null ? null : json["PROMPT"], + requestType: json["REQUEST_TYPE"] == null ? null : json["REQUEST_TYPE"], + updateButton: json["UPDATE_BUTTON"] == null ? null :json["UPDATE_BUTTON"], + ); + + Map toJson() => { + "ADD_BUTTON": addButton == null ? null :addButton, + "DELETE_BUTTON": deleteButton == null ? null : deleteButton, + "ENTRY_SEQUENCE": entrySequence == null ? null : entrySequence, + "FUNCTION_NAME": functionName == null ? null : functionName, + "ICON": icon == null ? null : icon, + "LVL": lvl == null ? null : lvl, + "MENU_ENTRY_TYPE": menuEntryType == null ? null : menuEntryType, + "MENU_NAME": menuName == null ? null : menuName, + "PARENT_MENU_NAME": parentMenuName == null ? null : parentMenuName, + "PROMPT": prompt == null ? null : prompt, + "REQUEST_TYPE": requestType == null ? null : requestType, + "UPDATE_BUTTON": updateButton == null ? null : updateButton, + }; +} + diff --git a/lib/models/dashboard/menus.dart b/lib/models/dashboard/menus.dart new file mode 100644 index 0000000..7b88a33 --- /dev/null +++ b/lib/models/dashboard/menus.dart @@ -0,0 +1,10 @@ +import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; + +class Menus { + GetMenuEntriesList menuEntry; + List menuEntiesList; + + Menus(this.menuEntry, this.menuEntiesList); + + +} diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index 86552a0..8ac68bb 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -5,6 +5,8 @@ import 'dashboard/get_accrual_balances_list_model.dart'; import 'dashboard/get_attendance_tracking_list_model.dart'; import 'dashboard/get_open_missing_swipes_list_model.dart'; import 'dashboard/get_open_notifications_list.dart'; +import 'dashboard/list_menu.dart'; +import 'dashboard/menu_entries.dart'; import 'member_information_list_model.dart'; import 'privilege_list_model.dart'; @@ -103,7 +105,7 @@ class GenericResponseModel { List? getItemTypeNotificationsList; List? getItemTypesList; List? getLookupValuesList; - List? getMenuEntriesList; + List? getMenuEntriesList; List? getMoItemHistoryList; List? getMoNotificationBodyList; List? getNotificationButtonsList; @@ -169,7 +171,7 @@ class GenericResponseModel { String? listItemImagesDetails; String? listItemMaster; String? listMedicineDetails; - String? listMenu; + List? listMenu; String? listNewEmployees; String? listRadScreen; String? logInTokenID; @@ -198,7 +200,7 @@ class GenericResponseModel { String? pINFORMATION; int? pMBLID; String? pNUMOFSUBORDINATES; - String? pOPENNTFNUMBER; + int? pOPENNTFNUMBER; String? pQUESTION; int? pSESSIONID; String? pSchema; @@ -567,15 +569,13 @@ class GenericResponseModel { getAbsenceCollectionNotificationBodyList = json['GetAbsenceCollectionNotificationBodyList']; getAbsenceDffStructureList = json['GetAbsenceDffStructureList']; getAbsenceTransactionList = json['GetAbsenceTransactionList']; - getAccrualBalancesList= - json["GetAccrualBalancesList"] == null ? null : List.from(json["GetAccrualBalancesList"].map((x) => GetAccrualBalancesList.fromJson(x))); + getAccrualBalancesList = json["GetAccrualBalancesList"] == null ? null : List.from(json["GetAccrualBalancesList"].map((x) => GetAccrualBalancesList.fromJson(x))); getActionHistoryList = json['GetActionHistoryList']; getAddressDffStructureList = json['GetAddressDffStructureList']; getAddressNotificationBodyList = json['GetAddressNotificationBodyList']; getApprovesList = json['GetApprovesList']; getAttachementList = json['GetAttachementList']; - getAttendanceTrackingList= - json["GetAttendanceTrackingList"] == null ? null : GetAttendanceTracking.fromMap(json["GetAttendanceTrackingList"]); + getAttendanceTrackingList = json["GetAttendanceTrackingList"] == null ? null : GetAttendanceTracking.fromMap(json["GetAttendanceTrackingList"]); getBasicDetColsStructureList = json['GetBasicDetColsStructureList']; getBasicDetDffStructureList = json['GetBasicDetDffStructureList']; getBasicDetNtfBodyList = json['GetBasicDetNtfBodyList']; @@ -611,15 +611,14 @@ class GenericResponseModel { getItemTypeNotificationsList = json['GetItemTypeNotificationsList']; getItemTypesList = json['GetItemTypesList']; getLookupValuesList = json['GetLookupValuesList']; - getMenuEntriesList = json['GetMenuEntriesList']; + getMenuEntriesList= json["GetMenuEntriesList"] == null ? null : List.from(json["GetMenuEntriesList"].map((x) => GetMenuEntriesList.fromJson(x))); getMoItemHistoryList = json['GetMoItemHistoryList']; getMoNotificationBodyList = json['GetMoNotificationBodyList']; getNotificationButtonsList = json['GetNotificationButtonsList']; getNotificationReassignModeList = json['GetNotificationReassignModeList']; getObjectValuesList = json['GetObjectValuesList']; - getOpenMissingSwipesList= json["GetOpenMissingSwipesList"] == null ? null : GetOpenMissingSwipesList.fromJson(json["GetOpenMissingSwipesList"]); - getOpenNotificationsList= - json["GetOpenNotificationsList"] == null ? null : List.from(json["GetOpenNotificationsList"].map((x) => GetOpenNotificationsList.fromMap(x))); + getOpenMissingSwipesList = json["GetOpenMissingSwipesList"] == null ? null : GetOpenMissingSwipesList.fromJson(json["GetOpenMissingSwipesList"]); + getOpenNotificationsList = json["GetOpenNotificationsList"] == null ? null : List.from(json["GetOpenNotificationsList"].map((x) => GetOpenNotificationsList.fromMap(x))); getOpenNotificationsNumList = json['GetOpenNotificationsNumList']; getOpenPeriodDatesList = json['GetOpenPeriodDatesList']; getOrganizationsSalariesList = json['GetOrganizationsSalariesList']; @@ -678,7 +677,7 @@ class GenericResponseModel { listItemImagesDetails = json['List_ItemImagesDetails']; listItemMaster = json['List_ItemMaster']; listMedicineDetails = json['List_MedicineDetails']; - listMenu = json['List_Menu']; + listMenu = json["List_Menu"] == null ? null : List.from(json["List_Menu"].map((x) => ListMenu.fromJson(x))); listNewEmployees = json['List_NewEmployees']; listRadScreen = json['List_RadScreen']; logInTokenID = json['LogInTokenID']; diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index 6ccbfc1..b52448e 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -1,23 +1,54 @@ +import 'dart:convert'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; +import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; +import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; +import 'package:mohem_flutter_app/models/dashboard/menus.dart'; +import 'package:mohem_flutter_app/models/generic_response_model.dart'; +import 'package:mohem_flutter_app/widgets/Updater.dart'; /// Mix-in [DiagnosticableTreeMixin] to have access to [debugFillProperties] for the devtool // ignore: prefer_mixin class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { + //Attendance Tracking bool isAttendanceTrackingLoading = true; - int isTimeRemainingInSeconds = 0; + int endTime = 0, isTimeRemainingInSeconds = 0; GetAttendanceTracking? attendanceTracking; + //Work List + bool isWorkListLoading = true; + int workListCounter = 0; + + //Misssing Swipe + bool isMissingSwipeLoading = true; + int missingSwipeCounter = 0; + + //Leave and Ticket Balance + bool isLeaveTicketBalanceLoading = true; + double leaveBalance = 0; + double ticketBalance = 0; + + //Menu Entries + bool isServicesMenusLoading = true; + List? homeMenus; + List? getMenuEntriesList; + + //Attendance Tracking API's & Methods fetchAttendanceTracking() async { try { attendanceTracking = await DashbaordApiClient().getAttendanceTracking(); isAttendanceTrackingLoading = false; - isTimeRemainingInSeconds = calculateSeconds("00:00:30"); - notifyListeners(); + isTimeRemainingInSeconds = calculateSeconds("00:00:00"); + endTime = DateTime.now().millisecondsSinceEpoch + Duration(seconds: isTimeRemainingInSeconds).inMilliseconds; + print("isTimeRemainingInSeconds " + isTimeRemainingInSeconds.toString()); + print("endTime " + endTime.toString()); + + // notifyListeners(); } catch (ex) { Utils.handleException(ex, null); } @@ -32,8 +63,111 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { update() { isAttendanceTrackingLoading = !isAttendanceTrackingLoading; + isWorkListLoading = !isWorkListLoading; attendanceTracking?.pSwipeIn = "a"; - isTimeRemainingInSeconds = calculateSeconds("00:00:30"); + isTimeRemainingInSeconds = calculateSeconds("00:10:30"); + endTime = DateTime.now().millisecondsSinceEpoch + Duration(seconds: isTimeRemainingInSeconds).inMilliseconds; + notifyListeners(); } + + //Work List API's & Methods + fetchWorkListCounter() async { + try { + GenericResponseModel? genericResponseModel = await DashbaordApiClient().getOpenNotifications(); + isWorkListLoading = false; + workListCounter = genericResponseModel?.pOPENNTFNUMBER ?? 0; + ItgFormsModel? itgFormsModel = await DashbaordApiClient().getItgFormsPendingTask(); + workListCounter = workListCounter + (itgFormsModel?.totalCount ?? 0); + notifyListeners(); + } catch (ex) { + isWorkListLoading = false; + logger.wtf(ex); + notifyListeners(); + Utils.handleException(ex, null); + } + } + + //Missing Siwpe API's & Methods + fetchMissingSwipe() async { + try { + GenericResponseModel? genericResponseModel = await DashbaordApiClient().getOpenMissingSwipes(); + isMissingSwipeLoading = false; + missingSwipeCounter = genericResponseModel!.getOpenMissingSwipesList!.pOpenMissingSwipes ?? 0; + notifyListeners(); + } catch (ex) { + isMissingSwipeLoading = false; + logger.wtf(ex); + notifyListeners(); + Utils.handleException(ex, null); + } + } + + //Leave and Ticket Balance API's & Methods + fetchLeaveTicketBalance() async { + try { + GenericResponseModel? genericResponseModel = await DashbaordApiClient().getAccrualBalances(); + isLeaveTicketBalanceLoading = false; + leaveBalance = genericResponseModel?.getAccrualBalancesList![0].accrualNetEntitlement ?? 0.0; + ticketBalance = (genericResponseModel?.getAccrualBalancesList![1].accrualNetEntitlement ?? 0.0) + (genericResponseModel?.getAccrualBalancesList![2].accrualNetEntitlement ?? 0.0); + notifyListeners(); + } catch (ex) { + isLeaveTicketBalanceLoading = false; + logger.wtf(ex); + notifyListeners(); + Utils.handleException(ex, null); + } + } + + //List Menu API's & Methods + fetchListMenu() async { + try { + GenericResponseModel? genericResponseModel = await DashbaordApiClient().getListMenu(); + Map map = {}; + print(jsonEncode(genericResponseModel!.listMenu)); + for (int i = 0; i < genericResponseModel!.listMenu!.length; i++) { + print(genericResponseModel!.listMenu![i]!.menuName ?? ""); + map[genericResponseModel!.listMenu![i]!.menuName ?? ""] = i.toString(); + } + logger.i(map); + notifyListeners(); + } catch (ex) { + logger.wtf(ex); + notifyListeners(); + Utils.handleException(ex, null); + } + } + + //Menu Entries API's & Methods + fetchMenuEntries() async { + try { + GenericResponseModel? genericResponseModel = await DashbaordApiClient().getGetMenuEntries(); + getMenuEntriesList = genericResponseModel!.getMenuEntriesList; + homeMenus = parseMenues(getMenuEntriesList ?? []); + isServicesMenusLoading = false; + notifyListeners(); + } catch (ex) { + logger.wtf(ex); + notifyListeners(); + Utils.handleException(ex, null); + } + } + + List parseMenues(List getMenuEntriesList) { + List menus = []; + for (int i = 0; i < getMenuEntriesList.length; i++) { + if (getMenuEntriesList[i].parentMenuName!.isEmpty) { + menus.add(Menus(getMenuEntriesList[i], getMenuEntriesList.where((element) => getMenuEntriesList[i].menuName == element.parentMenuName).toList())); + } + } + + //Verify Menus by printing in log + // for(int i=0;i { + late var data; + @override void initState() { super.initState(); - final data = Provider.of(context, listen: false); + data = Provider.of(context, listen: false); data.fetchAttendanceTracking(); + data.fetchWorkListCounter(); + data.fetchMissingSwipe(); + data.fetchLeaveTicketBalance(); + // data.fetchMenuEntries(); } - int endTime = 0; - @override void dispose() { super.dispose(); @@ -44,17 +56,8 @@ class _DashboardScreenState extends State { @override Widget build(BuildContext context) { - List names = [LocaleKeys.workList.tr(), LocaleKeys.missingSwipes.tr(), LocaleKeys.leaveBalance.tr(), LocaleKeys.ticketBalance.tr()]; - List namesInt = [118, 02, 18.5, 03]; - List namesColor = [0xff125765, 0xff239D8F, 0xff2BB8A8, 0xff1D92AA]; - - List namesT = [LocaleKeys.monthlyAttendance.tr(), LocaleKeys.workFromHome.tr(), LocaleKeys.ticketRequest.tr(), LocaleKeys.monthlyAttendance.tr()]; - List iconT = ["assets/images/monthly_attendance.svg", "assets/images/work_from_home.svg", "assets/images/ticket_request.svg", "assets/images/work_from_home.svg"]; - List namesD = ["Nostalgia Perfume Perfume", "Al Nafoura", "AlJadi", "Nostalgia Perfume"]; - final data = Provider.of(context); - - endTime=DateTime.now().millisecondsSinceEpoch + Duration(seconds: data.isTimeRemainingInSeconds).inMilliseconds; + return Scaffold( body: Column( children: [ @@ -108,214 +111,134 @@ class _DashboardScreenState extends State { ], ).paddingOnly(left: 21, right: 21, top: 48, bottom: 7), Expanded( - child: ListView( - padding: EdgeInsets.zero, + child: Column( + // padding: EdgeInsets.zero, + // physics: NeverScrollableScrollPhysics(), children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.goodMorning.tr().toText14(color: MyColors.grey77Color), - "Mahmoud Shrouf".toText24(isBold: true), - 16.height, - Row( + Expanded( + child: SingleChildScrollView( + child: Column( children: [ - Expanded( - child: AspectRatio( - aspectRatio: 159 / 159, - child: (data.isAttendanceTrackingLoading - ? GetAttendanceTrackingShimmer() - : Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), - gradient: const LinearGradient(transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomRight, colors: [ - MyColors.gradiantEndColor, - MyColors.gradiantStartColor, - ]), - ), - child: Stack( - alignment: Alignment.center, - children: [ - if (data.attendanceTracking?.pSwipeIn == null) SvgPicture.asset("assets/images/thumb.svg"), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - CountdownTimer( - endTime: endTime, - onEnd: null, - endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), - textStyle: TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.goodMorning.tr().toText14(color: MyColors.grey77Color), + "Mahmoud Shrouf".toText24(isBold: true), + 16.height, + Row( + children: [ + Expanded( + child: AspectRatio( + aspectRatio: 159 / 159, + child: Consumer( + builder: (context, model, child) { + return (model.isAttendanceTrackingLoading + ? GetAttendanceTrackingShimmer() + : Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + gradient: const LinearGradient(transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomRight, colors: [ + MyColors.gradiantEndColor, + MyColors.gradiantStartColor, + ]), ), - LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true), - if (data.attendanceTracking?.pSwipeIn == null) "01-02-2022".toText12(color: Colors.white), - if (data.attendanceTracking?.pSwipeIn != null) - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 9.height, - "07:55:12".toText14(color: Colors.white, isBold: true), - LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), - 9.height, - const ClipRRect( - borderRadius: BorderRadius.all( - Radius.circular(20), + child: Stack( + alignment: Alignment.center, + children: [ + if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true), + if (model.isTimeRemainingInSeconds == 0) "01-02-2022".toText12(color: Colors.white), + if (model.isTimeRemainingInSeconds != 0) + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 9.height, + CountdownTimer( + endTime: model.endTime, + onEnd: null, + endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), + textStyle: TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold), + ), + LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), + 9.height, + const ClipRRect( + borderRadius: BorderRadius.all( + Radius.circular(20), + ), + child: LinearProgressIndicator( + value: 0.7, + minHeight: 8, + valueColor: const AlwaysStoppedAnimation(Colors.white), + backgroundColor: const Color(0xff196D73), + ), + ), + ], + ), + ], + ).paddingOnly(top: 12, right: 15, left: 12), ), - child: LinearProgressIndicator( - value: 0.7, - minHeight: 8, - valueColor: const AlwaysStoppedAnimation(Colors.white), - backgroundColor: const Color(0xff196D73), + Row( + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.checkIn.tr().toText12(color: Colors.white), + (model.isTimeRemainingInSeconds == 0 ? "--:--" : "09:00").toText14(color: Colors.white, isBold: true), + 4.height + ], + ).paddingOnly(left: 12), + ), + Container( + width: 45, + height: 45, + padding: const EdgeInsets.only(left: 14, right: 14), + decoration: const BoxDecoration( + color: Color(0xff259EA4), + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(15), + ), + ), + child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/play.svg" : "assets/images/stop.svg"), + ), + ], ), - ), - ], - ), - ], - ).paddingOnly(top: 12, right: 15, left: 12), - ), - Row( - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.checkIn.tr().toText12(color: Colors.white), - (data.attendanceTracking?.pSwipeIn == null ? "--:--" : "09:00").toText14(color: Colors.white, isBold: true), - 4.height + ], + ), ], - ).paddingOnly(left: 12), - ), - Container( - width: 45, - height: 45, - padding: const EdgeInsets.only(left: 14, right: 14), - decoration: const BoxDecoration( - color: Color(0xff259EA4), - borderRadius: BorderRadius.only( - bottomRight: Radius.circular(15), - ), ), - child: SvgPicture.asset(data.attendanceTracking?.pSwipeIn == null ? "assets/images/play.svg" : "assets/images/stop.svg"), - ), - ], - ), - ], - ), - ], - ), - ).onPress(() { - Navigator.pushNamed(context, AppRoutes.todayAttendance); - })) - .animatedSwither(), - ), - ), - 9.width, - Expanded( - child: GridView.builder( - shrinkWrap: true, - primary: false, - physics: const NeverScrollableScrollPhysics(), - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 2 / 2, crossAxisSpacing: 9, mainAxisSpacing: 9), - padding: EdgeInsets.zero, - itemCount: 4, - itemBuilder: (BuildContext context, int index) { - return Container( - decoration: BoxDecoration( - color: Color(namesColor[index]), - borderRadius: BorderRadius.circular(10), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.todayAttendance); + })) + .animatedSwither(); + }, + )), ), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - names[index].toText12(color: Colors.white), - Row( - children: [ - Expanded( - child: namesInt[index].toStringAsFixed(1).toText16(color: Colors.white, isBold: true), - ), - SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white) - ], - ) - ], - ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), - ).onPress(() { - Navigator.pushNamed(context, AppRoutes.workList); - }); - }, - ), - ), - ], - ), - 20.height, - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - "Other".tr().toText12(), - LocaleKeys.services.tr().toText24(isBold: true), - ], - ), - ), - LocaleKeys.viewAllServices.tr().toText12(isUnderLine: true), - ], - ), - ], - ).paddingOnly(left: 21, right: 21, top: 7), - SizedBox( - height: 105 + 26, - child: ListView.separated( - shrinkWrap: true, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), - scrollDirection: Axis.horizontal, - itemBuilder: (cxt, index) { - return AspectRatio( - aspectRatio: 105 / 105, - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(15), - boxShadow: [ - BoxShadow( - color: const Color(0xff000000).withOpacity(.05), - blurRadius: 26, - offset: const Offset(0, -3), + 9.width, + Expanded( + child: MenusWidget(), ), ], ), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SvgPicture.asset(iconT[index]), - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: namesT[index].toText11(isBold: true), - ), - SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4) - ], - ) - ], - ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12), - ), - ); - }, - separatorBuilder: (cxt, index) => 9.width, - itemCount: 4), + 20.height, + ], + ).paddingOnly(left: 21, right: 21, top: 7), + ServicesWidget(), + 8.height, + ], + ), + ), ), - 8.height, Container( width: double.infinity, padding: EdgeInsets.only(top: 31), diff --git a/lib/ui/landing/widget/menus_widget.dart b/lib/ui/landing/widget/menus_widget.dart new file mode 100644 index 0000000..4d13775 --- /dev/null +++ b/lib/ui/landing/widget/menus_widget.dart @@ -0,0 +1,138 @@ +import 'package:easy_localization/src/public_ext.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:mohem_flutter_app/config/routes.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; +import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; +import 'package:provider/provider.dart'; + +class MenusWidget extends StatelessWidget { + @override + Widget build(BuildContext context) { + List namesColor = [0xff125765, 0xff239D8F, 0xff2BB8A8, 0xff1D92AA]; + + return Consumer(builder: (context, data, child) { + return GridView( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 2 / 2, crossAxisSpacing: 9, mainAxisSpacing: 9), + padding: EdgeInsets.zero, + shrinkWrap: true, + primary: false, + physics: const NeverScrollableScrollPhysics(), + children: [ + data.isWorkListLoading + ? MenuShimmer().onPress(() { + data.fetchWorkListCounter(); + }) + : Container( + decoration: BoxDecoration( + color: Color(namesColor[0]), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.workList.tr().toText12(color: Colors.white), + Row( + children: [ + Expanded( + child: data.workListCounter.toString().toText16(color: Colors.white, isBold: true), + ), + SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white) + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), + ).onPress(() { + // Navigator.pushNamed(context, AppRoutes.workList); + data.fetchWorkListCounter(); + }), + data.isMissingSwipeLoading + ? MenuShimmer().onPress(() { + data.fetchWorkListCounter(); + }) + : Container( + decoration: BoxDecoration( + color: Color(namesColor[1]), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.missingSwipes.tr().toText12(color: Colors.white), + Row( + children: [ + Expanded( + child: data.missingSwipeCounter.toString().toText16(color: Colors.white, isBold: true), + ), + SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white) + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.workList); + }), + data.isLeaveTicketBalanceLoading + ? MenuShimmer().onPress(() { + data.fetchWorkListCounter(); + }) + : Container( + decoration: BoxDecoration( + color: Color(namesColor[2]), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.leaveBalance.tr().toText12(color: Colors.white), + Row( + children: [ + Expanded( + child: data.leaveBalance.toString().toText16(color: Colors.white, isBold: true), + ), + SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white) + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.workList); + }), + data.isLeaveTicketBalanceLoading + ? MenuShimmer().onPress(() { + data.fetchWorkListCounter(); + }) + : Container( + decoration: BoxDecoration( + color: Color(namesColor[3]), + borderRadius: BorderRadius.circular(10), + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.ticketBalance.tr().toText12(color: Colors.white), + Row( + children: [ + Expanded( + child: data.ticketBalance.toString().toText16(color: Colors.white, isBold: true), + ), + SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white) + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.workList); + }) + ], + ); + }); + } +} diff --git a/lib/ui/landing/widget/services_widget.dart b/lib/ui/landing/widget/services_widget.dart new file mode 100644 index 0000000..f397a4c --- /dev/null +++ b/lib/ui/landing/widget/services_widget.dart @@ -0,0 +1,147 @@ +import 'package:easy_localization/src/public_ext.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; +import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; +import 'package:provider/provider.dart'; + +class ServicesWidget extends StatelessWidget { + @override + Widget build(BuildContext context) { + List namesT = [LocaleKeys.monthlyAttendance.tr(), LocaleKeys.workFromHome.tr(), LocaleKeys.ticketRequest.tr(), LocaleKeys.monthlyAttendance.tr()]; + List iconT = [ + "assets/images/monthly_attendance.svg", + "assets/images/work_from_home.svg", + "assets/images/ticket_request.svg", + "assets/images/work_from_home.svg", + "assets/images/work_from_home.svg", + "assets/images/work_from_home.svg", + "assets/images/work_from_home.svg", + "assets/images/work_from_home.svg" + ]; + + return Consumer( + builder: (context, data, child) { + return data.isServicesMenusLoading + ? whileLoading() + : ListView.separated( + itemBuilder: (context, parentIndex) { + return Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + firstWord(data.homeMenus![parentIndex].menuEntry.prompt!).toText12(), + lastWord(data.homeMenus![parentIndex].menuEntry.prompt!).toText24(isBold: true), + ], + ), + ), + LocaleKeys.viewAllServices.tr().toText12(isUnderLine: true), + ], + ).paddingOnly(left: 21, right: 21), + SizedBox( + height: 105 + 26, + child: ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), + scrollDirection: Axis.horizontal, + itemBuilder: (cxt, index) { + return AspectRatio( + aspectRatio: 105 / 105, + child: data.isServicesMenusLoading + ? ServicesMenuShimmer() + : Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.asset(iconT[index]), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: data.homeMenus![parentIndex].menuEntiesList[index].prompt!.toText11(isBold: true), + ), + SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4) + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12), + ), + ); + }, + separatorBuilder: (cxt, index) => 9.width, + itemCount: data.homeMenus![parentIndex].menuEntiesList.length), + ), + ], + ); + }, + separatorBuilder: (context, index) { + return 12.height; + }, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: data.homeMenus!.length); + }, + ); + } + + String firstWord(String value) { + return value.split(" ").length > 1 ? value.split(" ")[0] : ""; + } + + String lastWord(String value) { + var parts = value.split(" "); + if (parts.length == 1) { + return value; + } else { + int i = value.indexOf(" "); + return value.substring(i + 1).toCamelCase; + } + } + + Widget whileLoading() { + return Column( + children: [ + ServicesHeaderShimmer().paddingOnly(left: 21, right: 21), + SizedBox( + height: 105 + 26, + child: ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), + scrollDirection: Axis.horizontal, + itemBuilder: (cxt, index) { + return AspectRatio( + aspectRatio: 105 / 105, + child: ServicesMenuShimmer(), + ); + }, + separatorBuilder: (cxt, index) => 9.width, + itemCount: 4), + ), + ], + ); + } +} diff --git a/lib/widgets/Updater.dart b/lib/widgets/Updater.dart new file mode 100644 index 0000000..154173d --- /dev/null +++ b/lib/widgets/Updater.dart @@ -0,0 +1,39 @@ +/* ZiK */ + +import 'dart:async'; +import 'package:flutter/cupertino.dart'; + +typedef ChildProvider = Widget Function(BuildContext context, E? data); + +class Updater extends StatelessWidget{ + final ChildProvider childProvider; + StreamController? sink; + T? initialData; + List _history = []; + + Stream? _stream; + Updater({T? initialData, required this.childProvider}){ + this.sink = StreamController(); + this.initialData = initialData; + _stream = this.sink?.stream; + } + + @override + Widget build(BuildContext context) { + return StreamBuilder( + initialData: this.initialData, + stream: _stream, + builder: (ctx, snapshot){ + return childProvider(context, snapshot.data); + }); + } + + pushData(T? data) { + _history.add(data); + sink?.sink.add(data); + } + + List getDataHistory() => _history; + T? getLatestData() => _history.last; + +} \ No newline at end of file diff --git a/lib/widgets/shimmer/dashboard_shimmer_widget.dart b/lib/widgets/shimmer/dashboard_shimmer_widget.dart index 1c63d71..5b5bd60 100644 --- a/lib/widgets/shimmer/dashboard_shimmer_widget.dart +++ b/lib/widgets/shimmer/dashboard_shimmer_widget.dart @@ -1,5 +1,6 @@ import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; @@ -62,7 +63,9 @@ class GetAttendanceTrackingShimmer extends StatelessWidget { child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, - children: [LocaleKeys.checkIn.tr().toText12(color: Colors.white).toShimmer(),], + children: [ + LocaleKeys.checkIn.tr().toText12(color: Colors.white).toShimmer(), + ], ).paddingOnly(left: 12), ), Container( @@ -80,3 +83,107 @@ class GetAttendanceTrackingShimmer extends StatelessWidget { ); } } + +class MenuShimmer extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.workList.tr().toText12(color: Colors.white).toShimmer(), + Row( + children: [ + Expanded( + flex: 3, + child: 123.toString().toText10(color: Colors.white, isBold: true).toShimmer(), + ), + 12.width, + SvgPicture.asset("assets/images/arrow_next.svg", color: Colors.white).toShimmer() + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), + ); + } +} + +class ServicesHeaderShimmer extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + "Other".tr().toText10().toShimmer(), + 6.height, + LocaleKeys.services.tr().toText12(isBold: true).toShimmer(), + ], + ), + ), + LocaleKeys.viewAllServices.tr().toText12(isUnderLine: true).toShimmer(), + ], + ); + } +} + +class ServicesMenuShimmer extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.asset("assets/images/monthly_attendance.svg").toShimmer(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + "Attendan".toText11(isBold: false).toShimmer(), + 5.height, + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: "Attendance".toText11(isBold: false).toShimmer(), + ), + 6.width, + SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4).toShimmer() + ], + ), + ], + ) + ], + ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12), + ); + } +} From 6b9ea200adb5a51e79ef31dd49c4750fc3cd25ca Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 13 Feb 2022 11:44:49 +0300 Subject: [PATCH 3/9] login flow added. --- android/app/build.gradle | 5 +- android/app/google-services.json | 40 + android/app/src/main/AndroidManifest.xml | 3 +- .../com/mohem_flutter_app/MainActivity.kt | 17 +- android/build.gradle | 1 + assets/langs/ar-SA.json | 11 + assets/langs/en-US.json | 11 + lib/api/login_api_client.dart | 37 +- lib/app_state/app_state.dart | 20 + lib/classes/consts.dart | 9 +- lib/classes/utils.dart | 21 + lib/config/routes.dart | 3 + lib/generated/codegen_loader.g.dart | 22 + lib/main.dart | 3 +- lib/models/generic_response_model.dart | 14 +- .../get_mobile_login_info_list_model.dart | 64 ++ lib/models/member_information_list_model.dart | 17 + lib/models/privilege_list_model.dart | 17 + lib/ui/login/forgot_password_screen.dart | 37 +- lib/ui/login/login_screen.dart | 75 +- lib/ui/login/new_password_screen.dart | 184 +++-- lib/ui/login/verify_last_login_screen.dart | 750 ++++++++++++++++++ lib/ui/login/verify_login_screen.dart | 241 +++--- lib/widgets/dialogs/confirm_dialog.dart | 66 ++ lib/widgets/input_widget.dart | 22 +- pubspec.yaml | 2 + 26 files changed, 1504 insertions(+), 188 deletions(-) create mode 100644 android/app/google-services.json create mode 100644 lib/models/get_mobile_login_info_list_model.dart create mode 100644 lib/ui/login/verify_last_login_screen.dart create mode 100644 lib/widgets/dialogs/confirm_dialog.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index 4e4b395..596d981 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -23,6 +23,7 @@ if (flutterVersionName == null) { apply plugin: 'com.android.application' apply plugin: 'kotlin-android' +apply plugin: 'com.google.gms.google-services' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { @@ -43,8 +44,8 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.mohem_flutter_app" - minSdkVersion 16 + applicationId "hmg.cloudSolutions.mohem" + minSdkVersion 21 targetSdkVersion 30 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/android/app/google-services.json b/android/app/google-services.json new file mode 100644 index 0000000..62fbaea --- /dev/null +++ b/android/app/google-services.json @@ -0,0 +1,40 @@ +{ + "project_info": { + "project_number": "679409052782", + "firebase_url": "https://mohemm-dce93.firebaseio.com", + "project_id": "mohemm-dce93", + "storage_bucket": "mohemm-dce93.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:679409052782:android:dba155ac0859d7fea78a7f", + "android_client_info": { + "package_name": "hmg.cloudSolutions.mohem" + } + }, + "oauth_client": [ + { + "client_id": "679409052782-mtd6d8rjltucnm9uatn6g7et08sm6lbv.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyDgWjuSBIKGghWxYg_KGBRIZTi-O_UA8mU" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "679409052782-mtd6d8rjltucnm9uatn6g7et08sm6lbv.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 472e794..8d53eaf 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,8 +1,9 @@ + _instance; + Future getMobileLoginInfoNEW(String deviceToken, String deviceType) async { + String url = "${ApiConsts.erpRest}Mohemm_GetMobileLoginInfo_NEW"; + Map postParams = {}; + postParams["DeviceToken"] = deviceToken; + postParams["DeviceType"] = deviceType; + return await ApiClient().postJsonForObject((json) { + GenericResponseModel? responseData = GenericResponseModel.fromJson(json); + return (responseData.mohemmGetMobileLoginInfoList?.length ?? 0) > 0 ? responseData.mohemmGetMobileLoginInfoList!.first : null; + }, url, postParams); + } + + Future insertMobileLoginInfoNEW( + String email, int sessionId, String employeeName, int loginType, String mobileNumber, String userName, String deviceToken, String deviceType) async { + String url = "${ApiConsts.erpRest}Mohemm_InsertMobileLoginInfo"; + Map postParams = { + "MobileNumber": mobileNumber, + "P_USER_NAME": userName, + "UserName": userName, + "CompanyID": 1, // todo 'sikander' @discuss umer for companyID + "DeviceToken": deviceToken, + "LoginType": loginType, + "EmployeeName": employeeName, + "P_SESSION_ID": sessionId, + "P_EMAIL_ADDRESS": email + }; + postParams["DeviceToken"] = deviceToken; + postParams["DeviceType"] = deviceType; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel? responseData = GenericResponseModel.fromJson(json); + return responseData; + }, url, postParams); + } + Future checkMobileAppVersion() async { String url = "${ApiConsts.utilitiesRest}CheckMobileAppVersion"; Map postParams = {}; @@ -76,7 +111,7 @@ class LoginApiClient { postParams.addAll(AppState().postParamsJson); return await ApiClient().postJsonForObject((json) { GenericResponseModel responseData = GenericResponseModel.fromJson(json); - AppState().setForgetPasswordTokenID = responseData.tokenID; + AppState().setForgetPasswordTokenID = responseData.forgetPasswordTokenID; return responseData; }, url, postParams); } diff --git a/lib/app_state/app_state.dart b/lib/app_state/app_state.dart index 2af87d8..1c7f5b0 100644 --- a/lib/app_state/app_state.dart +++ b/lib/app_state/app_state.dart @@ -1,5 +1,8 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:mohem_flutter_app/models/member_information_list_model.dart'; import 'package:mohem_flutter_app/models/member_login_list_model.dart'; import 'package:mohem_flutter_app/models/post_params_model.dart'; +import 'package:mohem_flutter_app/models/privilege_list_model.dart'; class AppState { static final AppState _instance = AppState._internal(); @@ -24,13 +27,30 @@ class AppState { this._postParams = _postParams; } + bool isArabic(context) => EasyLocalization.of(context)?.locale.languageCode == "ar"; + String? username; + // todo ''sikander' added password for now, later will remove & improve + String? password; set setUserName(_username) => username = _username; + set setUserPassword(_password) => password = _password; MemberLoginListModel? _memberLoginList; MemberLoginListModel? get memberLoginList => _memberLoginList; set setMemberLoginListModel(MemberLoginListModel? _memberLoginList) => this._memberLoginList = _memberLoginList; + + MemberInformationListModel? _memberInformationList; + + MemberInformationListModel? get memberInformationList => _memberInformationList; + + set setMemberInformationListModel(MemberInformationListModel? _memberInformationList) => this._memberInformationList = _memberInformationList; + + List? _privilegeListModel; + + List? get privilegeListModel => _privilegeListModel; + + set setPrivilegeListModel(List? _privilegeListModel) => this._privilegeListModel = _privilegeListModel; } diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index b0453c0..8177750 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -8,12 +8,13 @@ class ApiConsts { static String user = baseUrlServices + "api/User/"; } -class GlobalConsts { +class SharedPrefsConsts { static String isRememberMe = "remember_me"; - static String email = "email"; + static String username = "username"; static String password = "password"; - static String bookmark = "bookmark"; - static String fontZoomSize = "font_zoom_size"; + static String privilegeList = "privilegeList"; + static String firebaseToken = "firebaseToken"; + static String memberInformation = "memberInformation"; static String welcomeVideoUrl = "welcomeVideoUrl"; static String doNotShowWelcomeVideo = "doNotShowWelcomeVideo"; } diff --git a/lib/classes/utils.dart b/lib/classes/utils.dart index d80fdf5..ae8041f 100644 --- a/lib/classes/utils.dart +++ b/lib/classes/utils.dart @@ -3,7 +3,9 @@ import 'package:fluttertoast/fluttertoast.dart'; // import 'package:fluttertoast/fluttertoast.dart'; import 'package:mohem_flutter_app/exceptions/api_exception.dart'; +import 'package:mohem_flutter_app/widgets/dialogs/confirm_dialog.dart'; import 'package:mohem_flutter_app/widgets/loading_dialog.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class Utils { static bool _isLoadingVisible = false; @@ -52,6 +54,16 @@ class Utils { _isLoadingVisible = false; } + static Future getStringFromPrefs(String key) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return prefs.getString(key) ?? ""; + } + + static Future saveStringFromPrefs(String key, String value) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + return await prefs.setString(key, value); + } + static void handleException(dynamic exception, Function(String)? onErrorMessage) { String errorMessage; if (exception is APIException) { @@ -69,4 +81,13 @@ class Utils { showToast(errorMessage); } } + + static void confirmDialog(cxt, String message) { + showDialog( + context: cxt, + builder: (cxt) => ConfirmDialog( + message: message, + ), + ); + } } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 39a4ebc..f0362d9 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -4,6 +4,7 @@ import 'package:mohem_flutter_app/ui/landing/today_attendance_screen.dart'; import 'package:mohem_flutter_app/ui/login/forgot_password_screen.dart'; import 'package:mohem_flutter_app/ui/login/login_screen.dart'; import 'package:mohem_flutter_app/ui/login/new_password_screen.dart'; +import 'package:mohem_flutter_app/ui/login/verify_last_login_screen.dart'; import 'package:mohem_flutter_app/ui/login/verify_login_screen.dart'; import 'package:mohem_flutter_app/ui/work_list/missing_swipe/missing_swipe_screen.dart'; import 'package:mohem_flutter_app/ui/work_list/work_list_screen.dart'; @@ -14,6 +15,7 @@ class AppRoutes { static const String loginVerifyAccount = "/loginVerifyAccount"; static const String login = "/login"; static const String verifyLogin = "/verifyLogin"; + static const String verifyLastLogin = "/verifyLastLogin"; static const String forgotPassword = "/forgotPassword"; static const String newPassword = "/newPassword"; static const String loginVerification = "/loginVerification"; @@ -28,6 +30,7 @@ class AppRoutes { static final Map routes = { login: (context) => LoginScreen(), verifyLogin: (context) => VerifyLoginScreen(), + verifyLastLogin: (context) => VerifyLastLoginScreen(), dashboard: (context) => Dashboard(), newPassword: (context) => NewPasswordScreen(), forgotPassword: (context) => ForgotPasswordScreen(), diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index b43f3bd..3d67def 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -64,7 +64,18 @@ class CodegenLoader extends AssetLoader{ "employeeId": "هوية الموظف", "loginCodeWillSentToMobileNumber": "الرجاء إدخال معرف الموظف الخاص بك ، وسيتم إرسال رمز تسجيل الدخول إلى رقم هاتفك المحمول", "changePassword": "تغيير كلمة المرور", + "ok": "موافق", + "confirm": "تؤكد", + "passwordChangedSuccessfully": "تم تغيير الرقم السري بنجاح", "itemsForSale": "سلع للبيع", + "doNotUseRecentPassword": "لا تستخدم كلمة مرور حديثة", + "atLeastOneLowercase": "حرف صغير واحد على الأقل", + "atLeastOneUppercase": "حرف كبير واحد على الأقل", + "atLeastOneNumeric": "رقم واحد على الأقل", + "minimum8Characters": "8 أحرف على الأقل", + "doNotAddRepeatingLetters": "لا تقم بإضافة أحرف متكررة", + "itShouldContainSpecialCharacter": "يجب أن يحتوي على طابع خاص", + "confirmPasswordMustMatch": "يجب أن يتطابق تأكيد كلمة المرور", "msg": "Hello {} in the {} world ", "msg_named": "{} are written in the {lang} language", "clickMe": "Click me", @@ -153,7 +164,18 @@ static const Map en_US = { "employeeId": "Employee ID", "loginCodeWillSentToMobileNumber": "Please Enter your Employee ID, A login code will be sent to your mobile number", "changePassword": "Change Password", + "ok": "OK", + "confirm": "Confirm", + "passwordChangedSuccessfully": "Password changed successfully", "itemsForSale": "Items for Sale", + "doNotUseRecentPassword": "Do not use recent password", + "atLeastOneLowercase": "At least one lowercase", + "atLeastOneUppercase": "At least one uppercase", + "atLeastOneNumeric": "At least one numeric", + "minimum8Characters": "Minimum 8 characters", + "doNotAddRepeatingLetters": "Do not add repeating letters", + "itShouldContainSpecialCharacter": "It should contain special character", + "confirmPasswordMustMatch": "Confirm password must match", "msg": "Hello {} in the {} world ", "msg_named": "{} are written in the {lang} language", "clickMe": "Click me", diff --git a/lib/main.dart b/lib/main.dart index 99cff95..7276ac0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,12 +8,13 @@ import 'package:mohem_flutter_app/generated/codegen_loader.g.dart'; import 'package:mohem_flutter_app/models/post_params_model.dart'; import 'package:mohem_flutter_app/theme/app_theme.dart'; import 'package:sizer/sizer.dart'; - +import 'package:firebase_core/firebase_core.dart'; import 'config/routes.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); + await Firebase.initializeApp(); runApp( EasyLocalization( supportedLocales: const [ diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index b993db8..8e60858 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -1,6 +1,7 @@ import 'package:mohem_flutter_app/models/member_login_list_model.dart'; import 'basic_member_information_model.dart'; +import 'get_mobile_login_info_list_model.dart'; import 'member_information_list_model.dart'; import 'privilege_list_model.dart'; @@ -174,7 +175,7 @@ class GenericResponseModel { String? mohemmGetBusinessCardEnabledList; List? mohemmGetFavoriteReplacementsList; String? mohemmGetMobileDeviceInfobyEmpInfoList; - String? mohemmGetMobileLoginInfoList; + List? mohemmGetMobileLoginInfoList; String? mohemmGetPatientIDList; String? mohemmITGResponseItem; bool? mohemmIsChangeIsActiveBusinessCardEnable; @@ -685,7 +686,12 @@ class GenericResponseModel { mohemmGetBusinessCardEnabledList = json['Mohemm_GetBusinessCardEnabledList']; mohemmGetFavoriteReplacementsList = json['Mohemm_GetFavoriteReplacementsList']; mohemmGetMobileDeviceInfobyEmpInfoList = json['Mohemm_GetMobileDeviceInfobyEmpInfoList']; - mohemmGetMobileLoginInfoList = json['Mohemm_GetMobileLoginInfoList']; + if (json['Mohemm_GetMobileLoginInfoList'] != null) { + mohemmGetMobileLoginInfoList = []; + json['Mohemm_GetMobileLoginInfoList'].forEach((v) { + mohemmGetMobileLoginInfoList!.add(new GetMobileLoginInfoListModel.fromJson(v)); + }); + } mohemmGetPatientIDList = json['Mohemm_GetPatientID_List']; mohemmITGResponseItem = json['Mohemm_ITG_ResponseItem']; mohemmIsChangeIsActiveBusinessCardEnable = json['Mohemm_IsChangeIsActiveBusinessCardEnable']; @@ -951,7 +957,9 @@ class GenericResponseModel { data['Mohemm_GetBusinessCardEnabledList'] = this.mohemmGetBusinessCardEnabledList; data['Mohemm_GetFavoriteReplacementsList'] = this.mohemmGetFavoriteReplacementsList; data['Mohemm_GetMobileDeviceInfobyEmpInfoList'] = this.mohemmGetMobileDeviceInfobyEmpInfoList; - data['Mohemm_GetMobileLoginInfoList'] = this.mohemmGetMobileLoginInfoList; + if (this.mohemmGetMobileLoginInfoList != null) { + data['Mohemm_GetMobileLoginInfoList'] = this.mohemmGetMobileLoginInfoList!.map((v) => v.toJson()).toList(); + } data['Mohemm_GetPatientID_List'] = this.mohemmGetPatientIDList; data['Mohemm_ITG_ResponseItem'] = this.mohemmITGResponseItem; data['Mohemm_IsChangeIsActiveBusinessCardEnable'] = this.mohemmIsChangeIsActiveBusinessCardEnable; diff --git a/lib/models/get_mobile_login_info_list_model.dart b/lib/models/get_mobile_login_info_list_model.dart new file mode 100644 index 0000000..ff419f2 --- /dev/null +++ b/lib/models/get_mobile_login_info_list_model.dart @@ -0,0 +1,64 @@ +class GetMobileLoginInfoListModel { + int? iD; + int? employeeID; + int? channelID; + int? companyID; + String? deviceType; + String? deviceToken; + int? language; + int? gender; + int? loginType; + String? createdOn; + String? editedOn; + String? employeeName; + bool? businessCardPrivilege; + + GetMobileLoginInfoListModel( + {this.iD, + this.employeeID, + this.channelID, + this.companyID, + this.deviceType, + this.deviceToken, + this.language, + this.gender, + this.loginType, + this.createdOn, + this.editedOn, + this.employeeName, + this.businessCardPrivilege}); + + GetMobileLoginInfoListModel.fromJson(Map json) { + iD = json['ID']; + employeeID = json['EmployeeID']; + channelID = json['ChannelID']; + companyID = json['CompanyID']; + deviceType = json['DeviceType']; + deviceToken = json['DeviceToken']; + language = json['Language']; + gender = json['Gender']; + loginType = json['LoginType']; + createdOn = json['CreatedOn']; + editedOn = json['EditedOn']; + employeeName = json['EmployeeName']; + businessCardPrivilege = json['BusinessCardPrivilege']; + } + + Map toJson() { + final Map data = Map(); + data['ID'] = iD; + data['EmployeeID'] = employeeID; + data['ChannelID'] = channelID; + data['CompanyID'] = companyID; + data['DeviceType'] = deviceType; + data['DeviceToken'] = deviceToken; + data['Language'] = language; + data['Gender'] = gender; + data['LoginType'] = loginType; + data['CreatedOn'] = createdOn; + data['EditedOn'] = editedOn; + data['EmployeeName'] = employeeName; + data['BusinessCardPrivilege'] = businessCardPrivilege; + return data; + } +} diff --git a/lib/models/member_information_list_model.dart b/lib/models/member_information_list_model.dart index a3bc05f..da95ed0 100644 --- a/lib/models/member_information_list_model.dart +++ b/lib/models/member_information_list_model.dart @@ -1,3 +1,8 @@ +import 'dart:convert'; + +import 'package:mohem_flutter_app/classes/consts.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + class MemberInformationListModel { String? aCTUALTERMINATIONDATE; String? aSSIGNMENTENDDATE; @@ -333,4 +338,16 @@ class MemberInformationListModel { data['USER_STATUS'] = this.uSERSTATUS; return data; } + + static Future> getFromPrefs() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List encodedList = prefs.getStringList(SharedPrefsConsts.memberInformation) ?? []; + return encodedList.map((e) => MemberInformationListModel.fromJson(jsonDecode(e))).toList(); + } + + static void saveToPrefs(List list) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List encodedList = list.map((e) => jsonEncode(e.toJson())).toList(); + await prefs.setStringList(SharedPrefsConsts.memberInformation, encodedList); + } } \ No newline at end of file diff --git a/lib/models/privilege_list_model.dart b/lib/models/privilege_list_model.dart index 3ef3954..ad83500 100644 --- a/lib/models/privilege_list_model.dart +++ b/lib/models/privilege_list_model.dart @@ -1,3 +1,8 @@ +import 'dart:convert'; + +import 'package:mohem_flutter_app/classes/consts.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + class PrivilegeListModel { int? iD; String? serviceName; @@ -18,4 +23,16 @@ class PrivilegeListModel { data['Previlege'] = this.previlege; return data; } + + static Future> getFromPrefs() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List encodedList = prefs.getStringList(SharedPrefsConsts.privilegeList) ?? []; + return encodedList.map((e) => PrivilegeListModel.fromJson(jsonDecode(e))).toList(); + } + + static void saveToPrefs(List list) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List encodedList = list.map((e) => jsonEncode(e.toJson())).toList(); + await prefs.setStringList(SharedPrefsConsts.privilegeList, encodedList); + } } diff --git a/lib/ui/login/forgot_password_screen.dart b/lib/ui/login/forgot_password_screen.dart index 89bd66e..17b105f 100644 --- a/lib/ui/login/forgot_password_screen.dart +++ b/lib/ui/login/forgot_password_screen.dart @@ -37,24 +37,30 @@ class _ForgotPasswordScreenState extends State { super.dispose(); } - void performLogin() async { - // Utils.showLoading(context); + void performForgotPassword() async { + if (employeeId.text.isEmpty) { + return; + } + Utils.showLoading(context); try { _basicMemberInformation = await LoginApiClient().getBasicUserInformation("CS", employeeId.text); genericResponseModel = await LoginApiClient().sendPublicActivationCode(_basicMemberInformation?.pMOBILENUMBER, employeeId.text); + Utils.hideLoading(context); OtpDialog( context, 1, int.tryParse(_basicMemberInformation?.pMOBILENUMBER ?? ""), (value) async { + Utils.showLoading(context); GenericResponseModel? genericResponseModel = await LoginApiClient().checkPublicActivationCode(value, employeeId.text); if (genericResponseModel?.errorMessage != null) { Utils.showToast(genericResponseModel?.errorMessage ?? ""); return; } - - Navigator.pushNamed(context, AppRoutes.newPassword); - // this.checkActivationCode(value: value); + Utils.hideLoading(context); + await Navigator.pushNamed(context, AppRoutes.newPassword, arguments: employeeId.text); + Navigator.pop(context); + Navigator.pop(context); }, () => { Navigator.pop(context), @@ -62,8 +68,8 @@ class _ForgotPasswordScreenState extends State { ).displayDialog(context); } catch (ex) { print(ex); + Utils.hideLoading(context); Utils.handleException(ex, null); - // Utils.hideLoading(context); } } @@ -95,7 +101,14 @@ class _ForgotPasswordScreenState extends State { LocaleKeys.forgotPassword.tr().toText24(isBold: true), LocaleKeys.loginCodeWillSentToMobileNumber.tr().toText16(), 16.height, - InputWidget(LocaleKeys.employeeId.tr(), "123456", employeeId), + InputWidget( + LocaleKeys.employeeId.tr(), + "123456", + employeeId, + onChange: (value) { + setState(() {}); + }, + ), ], ), ) @@ -103,9 +116,13 @@ class _ForgotPasswordScreenState extends State { ), ), ), - DefaultButton(LocaleKeys.changePassword.tr(), () async { - //Navigator.pushNamed(context, AppRoutes.verifyLogin); - }) + DefaultButton( + LocaleKeys.changePassword.tr(), + employeeId.text.isEmpty + ? null + : () async { + performForgotPassword(); + }) .insideContainer ], ), diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 5f0f266..d7c91ee 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -1,11 +1,15 @@ +import 'dart:io'; + import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/src/public_ext.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:mohem_flutter_app/api/login_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; @@ -13,7 +17,10 @@ import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/models/check_mobile_app_version_model.dart'; +import 'package:mohem_flutter_app/models/get_mobile_login_info_list_model.dart'; +import 'package:mohem_flutter_app/models/member_information_list_model.dart'; import 'package:mohem_flutter_app/models/member_login_list_model.dart'; +import 'package:mohem_flutter_app/models/privilege_list_model.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/input_widget.dart'; @@ -33,9 +40,15 @@ class _LoginScreenState extends State { CheckMobileAppVersionModel? _checkMobileAppVersion; MemberLoginListModel? _memberLoginList; + final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; + + bool _autoLogin = false; + @override void initState() { super.initState(); + checkFirebaseToken(); + //checkPrefs(); } @override @@ -43,6 +56,48 @@ class _LoginScreenState extends State { super.dispose(); } + Future checkPrefs() async { + String username = await Utils.getStringFromPrefs(SharedPrefsConsts.username); + if (username.isNotEmpty) { + String password = await Utils.getStringFromPrefs(SharedPrefsConsts.password); + // String firebaseToken = await Utils.getStringFromPrefs(SharedPrefsConsts.firebaseToken); + // print("firebaseToken:$firebaseToken"); + this.username.text = username; + this.password.text = password; + _autoLogin = true; + } + } + + String? firebaseToken; + + Future checkFirebaseToken() async { + try { + Utils.showLoading(context); + firebaseToken = await _firebaseMessaging.getToken(); + GetMobileLoginInfoListModel? loginInfo = await LoginApiClient().getMobileLoginInfoNEW(firebaseToken ?? "", Platform.isAndroid ? "android" : "ios"); + if (loginInfo == null) { + Utils.hideLoading(context); + print("Device token not found"); + return; + } else { + await checkPrefs(); + Utils.hideLoading(context); + performLogin(); + } + } catch (ex) { + print(ex); + Utils.hideLoading(context); + Utils.handleException(ex, null); + } + } + + // Future getFirebaseToken() async { + // String? firebaseToken = await _firebaseMessaging.getToken(); + // if (firebaseToken != null) { + // await Utils.saveStringFromPrefs(SharedPrefsConsts.firebaseToken, firebaseToken); + // } + // } + void performLogin() async { Utils.showLoading(context); try { @@ -50,13 +105,23 @@ class _LoginScreenState extends State { _memberLoginList = await LoginApiClient().memberLogin(username.text, password.text); AppState().setMemberLoginListModel = _memberLoginList; AppState().username = username.text; - print(_memberLoginList?.toJson()); + AppState().password = password.text; + if (_autoLogin) { + AppState().setMemberInformationListModel = (await MemberInformationListModel.getFromPrefs()).first; + AppState().setPrivilegeListModel = await PrivilegeListModel.getFromPrefs(); + } Utils.hideLoading(context); - Navigator.pushNamed(context, AppRoutes.verifyLogin); + if (_autoLogin) { + Navigator.pushNamed(context, AppRoutes.verifyLastLogin); + } else { + Navigator.pushNamed(context, AppRoutes.verifyLogin, arguments: "$firebaseToken"); + } } catch (ex) { print(ex); - Utils.handleException(ex, null); Utils.hideLoading(context); + Utils.handleException(ex, (msg) { + Utils.confirmDialog(context, msg); + }); } } @@ -77,7 +142,7 @@ class _LoginScreenState extends State { Expanded(child: SizedBox()), Row( children: [ - LocaleKeys.english.tr().toText14(color: MyColors.textMixColor).onPress(() { + LocaleKeys.english.tr().toText14(color: AppState().isArabic(context) ? null : MyColors.textMixColor).onPress(() { context.setLocale(const Locale("en", "US")); }), Container( @@ -86,7 +151,7 @@ class _LoginScreenState extends State { height: 16, margin: const EdgeInsets.only(left: 10, right: 10), ), - LocaleKeys.arabic.tr().toText14().onPress(() { + LocaleKeys.arabic.tr().toText14(color: !AppState().isArabic(context) ? null : MyColors.textMixColor).onPress(() { context.setLocale(const Locale("ar", "SA")); }), ], diff --git a/lib/ui/login/new_password_screen.dart b/lib/ui/login/new_password_screen.dart index 3fc37f1..e0a8f94 100644 --- a/lib/ui/login/new_password_screen.dart +++ b/lib/ui/login/new_password_screen.dart @@ -1,7 +1,9 @@ import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/login_api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; -import 'package:mohem_flutter_app/config/routes.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; @@ -22,6 +24,8 @@ class _NewPasswordScreenState extends State { TextEditingController password = TextEditingController(); TextEditingController confirmPassword = TextEditingController(); + String? userName; + @override void initState() { super.initState(); @@ -32,8 +36,26 @@ class _NewPasswordScreenState extends State { super.dispose(); } + void setNewPassword() async { + Utils.showLoading(context); + try { + var genericResponseModel = await LoginApiClient().changePasswordForget(AppState().getForgetPasswordTokenID ?? "", password.text, confirmPassword.text, userName); + Utils.hideLoading(context); + Utils.showToast(LocaleKeys.passwordChangedSuccessfully.tr()); + Navigator.pop(context); + } catch (ex) { + print(ex); + Utils.hideLoading(context); + Utils.handleException(ex, (msg) { + Utils.confirmDialog(context, msg); + }); + } + } + @override Widget build(BuildContext context) { + userName ??= ModalRoute.of(context)!.settings.arguments as String; + return Scaffold( appBar: AppBar( backgroundColor: Colors.transparent, @@ -46,53 +68,125 @@ class _NewPasswordScreenState extends State { children: [ //const SizedBox(height: 23), Expanded( - child: Padding( - padding: const EdgeInsets.all(21.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Row( - // children: [ - // Expanded(child: SizedBox()), - // Row( - // children: [ - // LocaleKeys.english.tr().toText14(color: MyColors.textMixColor).onPress(() {}), - // Container( - // width: 1, - // color: MyColors.darkWhiteColor, - // height: 16, - // margin: const EdgeInsets.only(left: 10, right: 10), - // ), - // LocaleKeys.arabic.tr().toText14().onPress(() {}), - // ], - // ), - // ], - // ), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - LocaleKeys.setTheNewPassword.tr().toText24(isBold: true), - LocaleKeys.typeYourNewPasswordBelow.tr().toText16(), - 16.height, - InputWidget(LocaleKeys.password.tr(), "**********", password), - 12.height, - InputWidget(LocaleKeys.confirmPassword.tr(), "**********", confirmPassword, isObscureText: true) - ], - ), - ) - ], - ), - ), + child: ListView( + // mainAxisSize: MainAxisSize.min, + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.center, + children: [ + LocaleKeys.setTheNewPassword.tr().toText24(isBold: true), + LocaleKeys.typeYourNewPasswordBelow.tr().toText16(), + 16.height, + InputWidget( + LocaleKeys.password.tr(), + "**********", + password, + onChange: (value) { + setState(() {}); + }, + ), + 12.height, + InputWidget( + LocaleKeys.confirmPassword.tr(), + "**********", + confirmPassword, + isObscureText: true, + onChange: (value) { + setState(() {}); + }, + ), + 12.height, + passwordConstraintsUI(LocaleKeys.doNotUseRecentPassword.tr(), true), + 8.height, + passwordConstraintsUI(LocaleKeys.atLeastOneLowercase.tr(), checkRegEx(r'[a-z]')), + 8.height, + passwordConstraintsUI(LocaleKeys.atLeastOneUppercase.tr(), checkRegEx(r'[A-Z]')), + 8.height, + passwordConstraintsUI(LocaleKeys.atLeastOneNumeric.tr(), checkRegEx(r'[0-9]')), + 8.height, + passwordConstraintsUI(LocaleKeys.minimum8Characters.tr(), password.text.length >= 8), + 8.height, + passwordConstraintsUI(LocaleKeys.doNotAddRepeatingLetters.tr(), checkRepeatedChars(password.text)), + 8.height, + passwordConstraintsUI(LocaleKeys.itShouldContainSpecialCharacter.tr(), checkRegEx(r'[!@#$%^&*(),.?":{}|<>]')), + 8.height, + passwordConstraintsUI(LocaleKeys.confirmPasswordMustMatch.tr(), password.text.isNotEmpty && password.text == confirmPassword.text), + 12.height, + ], + ).paddingAll(21), ), - DefaultButton(LocaleKeys.update.tr(), () async { - - // Navigator.pushNamed(context, AppRoutes.verifyLogin); - }).insideContainer + DefaultButton( + LocaleKeys.update.tr(), + (!isPasswordCompliant(password.text, 8)) + ? null + : () async { + setNewPassword(); + }) + .insideContainer ], ), ); } + + bool checkRegEx(String pattern) { + return RegExp(pattern).hasMatch(password.text); + } + + String recentPassword = ""; + + bool checkRepeatedCharacters(String value) { + if (value.isEmpty) { + return false; + } + for (int i = 0; i < value.length; i++) { + //if(i) + } + + return true; + } + + bool isPasswordCompliant(String? password, int minLength) { + if (password == null || password.isEmpty) { + return false; + } + + bool hasUppercase = password.contains(RegExp(r'[A-Z]')); + bool hasDigits = password.contains(RegExp(r'[0-9]')); + bool hasLowercase = password.contains(RegExp(r'[a-z]')); + bool hasSpecialCharacters = password.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]')); + bool hasMinLength = password.length >= minLength; + bool isMatched = password == confirmPassword.text; + + return hasDigits && hasUppercase && hasLowercase && hasSpecialCharacters && hasMinLength && isMatched && checkRepeatedChars(password); + } + + bool checkRepeatedChars(String password) { + bool isNonRepeatedLetters = true; + if (password.length > 2) { + for (int i = 0; i < password.length; i++) { + String char = password[i]; + try { + if (char == password[i + 1]) { + isNonRepeatedLetters = false; + break; + } + } catch (ex) {} + } + } + return isNonRepeatedLetters; + } + + Widget passwordConstraintsUI(String description, bool check) { + return Row( + children: [ + 4.width, + SizedBox( + width: 12, + height: 12, + child: Checkbox(fillColor: MaterialStateProperty.all(MyColors.gradiantEndColor), shape: const CircleBorder(), value: check, onChanged: null), + ), + 8.width, + description.toText14() + ], + ); + } } diff --git a/lib/ui/login/verify_last_login_screen.dart b/lib/ui/login/verify_last_login_screen.dart new file mode 100644 index 0000000..0c8554f --- /dev/null +++ b/lib/ui/login/verify_last_login_screen.dart @@ -0,0 +1,750 @@ +import 'package:easy_localization/src/public_ext.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:local_auth/auth_strings.dart'; +import 'package:local_auth/local_auth.dart'; +import 'package:mohem_flutter_app/api/login_api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/config/routes.dart'; +import 'package:mohem_flutter_app/dialogs/otp_dialog.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/basic_member_information_model.dart'; +import 'package:mohem_flutter_app/models/generic_response_model.dart'; +import 'package:mohem_flutter_app/widgets/button/default_button.dart'; + +class VerifyLastLoginScreen extends StatefulWidget { + VerifyLastLoginScreen({Key? key}) : super(key: key); + + @override + _VerifyLastLoginScreenState createState() { + return _VerifyLastLoginScreenState(); + } +} + +class _VerifyLastLoginScreenState extends State { + final LocalAuthentication auth = LocalAuthentication(); + List _availableBioMetricType = []; + + @override + void initState() { + _getAvailableBiometrics(); + // setDefault(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + String empName = AppState().isArabic(context) ? AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr! : AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn!; + return Scaffold( + appBar: AppBar( + backgroundColor: Colors.transparent, + leading: IconButton( + icon: const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor), + onPressed: () => Navigator.pop(context), + ), + actions: [Center(child: "Employee Digital ID".toText12(color: MyColors.textMixColor, isUnderLine: true).onPress(() {})), 21.width], + ), + body: Column( + children: [ + Expanded( + child: ListView( + padding: const EdgeInsets.all(21), + physics: const BouncingScrollPhysics(), + children: [ + //12.height, + if (true) + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.welcomeBack.tr().toText12(), + empName.toText24(isBold: true), + 10.height, + LocaleKeys.wouldYouLikeToLoginWithCurrentUsername.tr().toText16(), + Container( + height: 72, + margin: const EdgeInsets.only(top: 23, bottom: 23), + alignment: Alignment.center, + padding: const EdgeInsets.only(left: 17, right: 12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.white, + border: Border.all( + color: const Color(0xffefefef), + width: 1, + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + LocaleKeys.lastLoginDetails.tr().toText16(), + // Text( + // user.editedOn != null + // ? DateUtil.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(user.editedOn)) + // : user.createdOn != null + // ? DateUtil.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(user.createdOn)) + // : '--', + // style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.48), + // ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + LocaleKeys.verificationType.tr().toText10(color: MyColors.grey57Color), + Text( + "SMS", + // " " + getType(user.logInType, context), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xff2B353E), + ), + ), + Expanded(child: SizedBox()), + // Text( + // user.editedOn != null + // ? DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(user.editedOn), false) + // : user.createdOn != null + // ? DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(user.createdOn), false) + // : '--', + // style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xff575757), letterSpacing: -0.48), + // ), + ], + ) + ], + ), + ), + LocaleKeys.pleaseVerify.tr().toText16(), + GridView( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 13, mainAxisSpacing: 9), + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.only(top: 9), + shrinkWrap: true, + children: [ + getButton(3), + getButton(2), + getButton(1), + getButton(4), + ], + ) + ], + ) + // else + // Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ + // Image.asset( + // 'assets/images/habib-logo.png', + // height: 90, + // width: 90, + // ), + // SizedBox(height: 23), + // this.onlySMSBox == false + // ? Text( + // TranslationBase.of(context).verifyLoginWith, + // style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.64, height: 25 / 16), + // ) + // : Text( + // TranslationBase.of(context).verifyFingerprint2, + // style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.64, height: 25 / 16), + // ), + // SizedBox(height: 23), + // Text( + // TranslationBase.of(context).pleaseVerify, + // style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff2E303A), letterSpacing: -0.64), + // ), + // GridView( + // gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 13, mainAxisSpacing: 9), + // physics: NeverScrollableScrollPhysics(), + // padding: EdgeInsets.only(top: 9), + // shrinkWrap: true, + // children: [ + // if (onlySMSBox == false) getButton(3), + // if (onlySMSBox == false) getButton(2), + // getButton(1), + // getButton(4), + // ], + // ), + // ]), + ], + ), + ), + 12.height, + DefaultButton( + LocaleKeys.useAnotherAccount.tr(), + () => { + //Navigator.of(context).pushNamed(LOGIN_TYPE) + }, + ).insideContainer, + ], + ), + ); + } + + Future _getAvailableBiometrics() async { + try { + _availableBioMetricType = await auth.getAvailableBiometrics(); + } on PlatformException catch (e) { + // AppToast.showErrorToast(message: e.message); + print(e); + } + if (mounted) setState(() {}); + } + + // authenticateUser(int type, {int isActive}) { + // GifLoaderDialogUtils.showMyDialog(context); + // if (type == 2 || type == 3) { + // fingrePrintBefore = type; + // } + // this.selectedOption = fingrePrintBefore != null ? fingrePrintBefore : type; + // + // switch (type) { + // case 1: + // this.loginWithSMS(type); + // break; + // case 2: + // this.loginWithFingurePrintFace(type, isActive); + // break; + // case 3: + // this.loginWithFingurePrintFace(type, isActive); + // break; + // case 4: + // this.loginWithSMS(type); + // break; + // default: + // break; + // } + // sharedPref.setInt(LAST_LOGIN, this.selectedOption); //this.cs.sharedService.setStorage(this.selectedOption, AuthenticationService.LAST_LOGIN); + // } +// +// loginWithSMS(type) { +// //if (!el.disabled) { +// if (this.user != null && this.registerd_data == null) { +// this.checkUserAuthentication(type); +// } else { +// if (this.loginTokenID != null) { +// // Future.delayed(Duration(seconds: 1), () { +// this.sendActivationCode(type); +// // }); +// } else { +// this.checkUserAuthentication(type); +// } +// } +// } +// +// checkUserAuthentication(type) { +// showLoader(true); +// var req = getCommonRequest(type: type); +// req.logInTokenID = ""; +// +// var request = CheckPatientAuthenticationReq.fromJson(req.toJson()); +// +// sharedPref.setObject(REGISTER_DATA_FOR_REGISTER, request); +// authService +// .checkPatientAuthentication(request) +// .then((value) => { +// GifLoaderDialogUtils.hideDialog(context), +// if (value['isSMSSent']) +// { +// sharedPref.setString(LOGIN_TOKEN_ID, value['LogInTokenID']), +// this.loginTokenID = value['LogInTokenID'], +// sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, request), +// // Future.delayed(Duration(seconds: 1), () { +// this.sendActivationCode(type) +// // }) +// } +// else +// { +// if (value['IsAuthenticated']) {this.checkActivationCode()} +// } +// }) +// .catchError((err) { +// print(err); +// GifLoaderDialogUtils.hideDialog(context); +// }); +// } +// +// sendActivationCode(type) async { +// var request = this.getCommonRequest(type: type); +// request.sMSSignature = await SMSOTP.getSignature(); +// GifLoaderDialogUtils.showMyDialog(context); +// if (healthId != null) { +// // final DateFormat dateFormat = DateFormat('MM/dd/yyyy'); +// // final DateFormat dateFormat2 = DateFormat('dd/MM/yyyy'); +// request.dob = dob; //isHijri == 1 ? dob : dateFormat2.format(dateFormat.parse(dob)); +// request.healthId = healthId; +// request.isHijri = isHijri; +// await this.authService.sendActivationCodeRegister(request).then((result) { +// GifLoaderDialogUtils.hideDialog(context); +// if (result != null && result['isSMSSent'] == true) { +// this.startSMSService(type); +// } +// }).catchError((r) { +// GifLoaderDialogUtils.hideDialog(context); +// }); +// } else { +// request.dob = ""; +// request.healthId = ""; +// request.isHijri = 0; +// await this.authService.sendActivationCode(request).then((result) { +// GifLoaderDialogUtils.hideDialog(context); +// if (result != null && result['isSMSSent'] == true) { +// this.startSMSService(type); +// } +// }).catchError((r) { +// GifLoaderDialogUtils.hideDialog(context); +// }); +// } +// } +// +// var tempType; +// +// startSMSService(type) { +// tempType = type; +// new SMSOTP( +// context, +// type, +// this.mobileNumber, +// (value) { +// this.checkActivationCode(value: value); +// }, +// () => { +// Navigator.pop(context), +// }, +// ).displayDialog(context); +// } +// +// loginWithFingurePrintFace(type, int isActive) async { +// if (isActive == 1 || isActive == 0) { +// const iosStrings = +// const IOSAuthMessages(cancelButton: 'cancel', goToSettingsButton: 'settings', goToSettingsDescription: 'Please set up your Touch ID.', lockOut: 'Please reenable your Touch ID'); +// +// try { +// authenticated = await auth.authenticateWithBiometrics(localizedReason: 'Scan your fingerprint to authenticate', useErrorDialogs: true, stickyAuth: true, iOSAuthStrings: iosStrings); +// } on PlatformException catch (e) { +// GifLoaderDialogUtils.hideDialog(context); +// AppToast.showErrorToast(message: 'Please enable your Touch or Face ID'); +// } +// +// if (authenticated == true) { +// // if (user != null && (user.logInType == 2 || user.logInType == 3)) { +// // this.checkActivationCode(); +// // } else { +// +// var request = this.getCommonRequest(type: type); +// this.getMobileInfo(request); +// //} +// } +// } +// } +// +// getMobileInfo(request) { +// // GifLoaderDialogUtils.showMyDialog(context); +// this.authService.getLoginInfo(request).then((result) { +// GifLoaderDialogUtils.hideDialog(context); +// if (result['SMSLoginRequired'] == false) { +// this.loginTokenID = result['LogInTokenID']; +// this.patientOutSA = result['PatientOutSA']; +// // sms for register the biometric +// if (result['isSMSSent']) { +// setState(() { +// isMoreOption = true; +// this.onlySMSBox = true; +// // this.fingrePrintBefore = true; +// }); +// //this.button(); +// } else { +// setDefault(); +// checkActivationCode(); +// } +// } else { +// if (result['IsAuthenticated'] == true) { +// setState(() { +// isMoreOption = true; +// this.onlySMSBox = true; +// // this.fingrePrintBefore = true; +// }); +// } +// } +// }).catchError((err) { +// GifLoaderDialogUtils.hideDialog(context); +// print(err); +// }); +// } +// +// setDefault() async { +// if (await sharedPref.getObject(IMEI_USER_DATA) != null) user = SelectDeviceIMEIRES.fromJson(await sharedPref.getObject(IMEI_USER_DATA)); +// +// if (await sharedPref.getObject(REGISTER_DATA_FOR_LOGIIN) != null) { +// isMoreOption = true; +// this.registerd_data = CheckPatientAuthenticationReq.fromJson(await sharedPref.getObject(REGISTER_DATA_FOR_LOGIIN)); +// } +// +// this.mobileNumber = this.registerd_data != null ? this.registerd_data.patientMobileNumber : int.parse(this.user.mobile); +// this.zipCode = this.registerd_data != null +// ? this.registerd_data.zipCode +// : this.user.outSA == true +// ? "971" +// : "966"; +// this.patientOutSA = this.registerd_data != null +// ? this.registerd_data.zipCode == "966" +// ? 0 +// : 1 +// : this.user.outSA; +// if (this.registerd_data != null) { +// this.loginTokenID = await sharedPref.getString(LOGIN_TOKEN_ID); +// this.loginType = this.registerd_data.searchType; +// } +// var nhic = await sharedPref.getObject(NHIC_DATA); +// if (nhic != null) { +// final DateFormat dateFormat = DateFormat('MM/dd/yyyy'); +// final DateFormat dateFormat2 = DateFormat('dd/MM/yyyy'); +// dob = nhic['IsHijri'] ? nhic['DateOfBirth'] : dateFormat2.format(dateFormat.parse(nhic['DateOfBirth'])); +// +// isHijri = nhic['IsHijri'] ? 1 : 0; +// healthId = nhic['HealthId']; +// } +// this.deviceToken = await sharedPref.getString(PUSH_TOKEN); +// this.lastLogin = await sharedPref.getInt(LAST_LOGIN) != null +// ? await sharedPref.getInt(LAST_LOGIN) +// : user != null +// ? user.logInType +// : null; +// +// //this.cs.sharedService.getStorage(AuthenticationService.LAST_LOGIN); +// } +// +// getCommonRequest({type}) { +// var request = SendActivationRequest(); +// request.patientMobileNumber = this.mobileNumber; +// request.mobileNo = '0' + this.mobileNumber.toString(); +// request.deviceToken = this.deviceToken; +// request.projectOutSA = this.patientOutSA == true ? true : false; +// request.loginType = this.selectedOption; +// request.oTPSendType = type == 1 ? type : 2; //this.selectedOption == 1 ? 1 : 2; +// request.zipCode = this.zipCode; +// +// request.logInTokenID = this.loginTokenID ?? ""; +// +// if (this.registerd_data != null) { +// request.searchType = this.registerd_data.searchType != null ? this.registerd_data.searchType : 1; +// request.patientID = this.registerd_data.patientID != null ? this.registerd_data.patientID : 0; +// request.patientIdentificationID = request.nationalID = this.registerd_data.patientIdentificationID != null ? this.registerd_data.patientIdentificationID : '0'; +// +// request.isRegister = this.registerd_data.isRegister; +// } else { +// request.searchType = request.searchType != null ? request.searchType : 2; +// request.patientID = this.user.patientID != null ? this.user.patientID : 0; +// request.nationalID = request.nationalID != null ? request.nationalID : '0'; +// request.patientIdentificationID = request.patientIdentificationID != null ? request.patientIdentificationID : '0'; +// request.isRegister = false; +// } +// request.deviceTypeID = request.searchType; +// return request; +// } +// +// // checkActivationCode({value}) async { +// // // Navigator.pop(context); +// // GifLoaderDialogUtils.showMyDialog(context); +// // var request = this.getCommonRequest().toJson(); +// // dynamic res; +// // if (healthId != null) { +// // request['DOB'] = dob; +// // request['HealthId'] = healthId; +// // request['IsHijri'] = isHijri; +// // +// // authService +// // .checkActivationCodeRegister(request, value) +// // .then((result) => { +// // res = result, +// // if (result is Map) +// // { +// // result = CheckActivationCode.fromJson(result), +// // if (this.registerd_data != null && this.registerd_data.isRegister == true) +// // { +// // widget.changePageViewIndex(1), +// // Navigator.popUntil(context, (route) => Utils.route(route, equalsTo: RegisterNew)), +// // } +// // } +// // else +// // { +// // // Navigator.of(context).pop(), +// // GifLoaderDialogUtils.hideDialog(context), +// // Future.delayed(Duration(seconds: 1), () { +// // AppToast.showErrorToast(message: result); +// // }), +// // } +// // }) +// // .catchError((err) { +// // print(err); +// // GifLoaderDialogUtils.hideDialog(context); +// // Future.delayed(Duration(seconds: 1), () { +// // AppToast.showErrorToast(message: err); +// // startSMSService(tempType); +// // }); +// // }); +// // } else { +// // authService +// // .checkActivationCode(request, value) +// // .then((result) => { +// // res = result, +// // if (result is Map) +// // { +// // result = CheckActivationCode.fromJson(result), +// // if (this.registerd_data != null && this.registerd_data.isRegister == true) +// // { +// // widget.changePageViewIndex(1), +// // Navigator.popUntil(context, (route) => Utils.route(route, equalsTo: RegisterNew)), +// // } +// // else +// // { +// // sharedPref.remove(FAMILY_FILE), +// // result.list.isFamily = false, +// // userData = result.list, +// // sharedPref.setString(BLOOD_TYPE, result.patientBloodType), +// // authenticatedUserObject.user = result.list, +// // projectViewModel.setPrivilege(privilegeList: res), +// // sharedPref.setObject(MAIN_USER, result.list), +// // sharedPref.setObject(USER_PROFILE, result.list), +// // loginTokenID = result.logInTokenID, +// // sharedPref.setObject(LOGIN_TOKEN_ID, result.logInTokenID), +// // sharedPref.setString(TOKEN, result.authenticationTokenID), +// // checkIfUserAgreedBefore(result), +// // } +// // } +// // else +// // { +// // // // Navigator.of(context).pop(), +// // // GifLoaderDialogUtils.hideDialog(context), +// // // Future.delayed(Duration(seconds: 1), () { +// // // AppToast.showErrorToast(message: result); +// // // startSMSService(tempType); +// // // }), +// // } +// // }) +// // .catchError((err) { +// // // print(err); +// // // GifLoaderDialogUtils.hideDialog(context); +// // // Future.delayed(Duration(seconds: 1), () { +// // // AppToast.showErrorToast(message: err); +// // // startSMSService(tempType); +// // // }); +// // }); +// // } +// // } +// +// // checkIfUserAgreedBefore(CheckActivationCode result) { +// // if (result.isNeedUserAgreement == true) { +// // //move to agreement page. +// // } else { +// // goToHome(); +// // } +// // } +// +// insertIMEI() { +// authService.insertDeviceImei(selectedOption).then((value) => {}).catchError((err) { +// print(err); +// }); +// } +// +// // getToDoCount() { +// // toDoProvider.setState(0, true, "0"); +// // ClinicListService service = new ClinicListService(); +// // service.getActiveAppointmentNo(context).then((res) { +// // if (res['MessageStatus'] == 1) { +// // toDoProvider.setState(res['AppointmentActiveNumber'], true, "0"); +// // } else {} +// // }).catchError((err) { +// // print(err); +// // }); +// // } +// +// // goToHome() async { +// // authenticatedUserObject.isLogin = true; +// // appointmentRateViewModel.isLogin = true; +// // projectViewModel.isLogin = true; +// // projectViewModel.user = authenticatedUserObject.user; +// // await authenticatedUserObject.getUser(getUser: true); +// // +// // // getToDoCount(); +// // +// // appointmentRateViewModel +// // .getIsLastAppointmentRatedList() +// // .then((value) => { +// // getToDoCount(), +// // GifLoaderDialogUtils.hideDialog(AppGlobal.context), +// // if (appointmentRateViewModel.isHaveAppointmentNotRate) +// // { +// // Navigator.pushAndRemoveUntil( +// // context, +// // FadePage( +// // page: RateAppointmentDoctor(), +// // ), +// // (r) => false) +// // } +// // else +// // { +// // Navigator.pushAndRemoveUntil( +// // context, +// // FadePage( +// // page: LandingPage(), +// // ), +// // (r) => false) +// // }, +// // insertIMEI() +// // }) +// // .catchError((err) { +// // print(err); +// // }); +// // } +// +// loading(flag) { +// // setState(() { +// // isLoading = flag; +// // }); +// } +// + + Future loginWithFaceIDAndBiometrics() async { + IOSAuthMessages iosStrings = + const IOSAuthMessages(cancelButton: 'cancel', goToSettingsButton: 'settings', goToSettingsDescription: 'Please set up your Touch ID.', lockOut: 'Please reenable your Touch ID'); + bool authenticated = false; + try { + authenticated = await auth.authenticate(localizedReason: 'Scan your fingerprint to authenticate', useErrorDialogs: true, stickyAuth: true, iOSAuthStrings: iosStrings); + } on PlatformException catch (e) { + print(e); + Utils.hideLoading(context); + Utils.showToast("Please enable your Touch or Face ID"); + } + return authenticated; + } + + Widget _loginOptionButton(String _title, String _icon, int _flag, int? _loginIndex) { + bool isDisable = ((_flag == 3 && !checkBiometricIsAvailable(BiometricType.face)) || (_flag == 2 && !checkBiometricIsAvailable(BiometricType.fingerprint))); + print("$_title:$isDisable"); + return InkWell( + onTap: isDisable + ? null + : () async { + if (_flag == 0) { + setState(() { + // isMoreOption = true; + }); + } else { + Utils.showLoading(context); + if (_flag == 2 || _flag == 3) { + bool authenticateWithFaceAndTouchID = await loginWithFaceIDAndBiometrics(); + if (authenticateWithFaceAndTouchID) { + Navigator.pushNamedAndRemoveUntil(context, AppRoutes.dashboard, (Route route) => false); + return; + } else { + Utils.hideLoading(context); + return; + } + } + await LoginApiClient().checkMobileAppVersion(); + await LoginApiClient().memberLogin(AppState().username!, AppState().password!); + BasicMemberInformationModel? memberInformationModel = await LoginApiClient() + .mohemmSendActivationCodeByOTPNotificationType(checkBiometricIsAvailable(BiometricType.fingerprint) ? 1 : 0, AppState().memberLoginList?.pMOBILENUMBER, _flag, AppState().username); + Utils.hideLoading(context); + OtpDialog( + context, + _flag, + int.tryParse(AppState().memberLoginList?.pMOBILENUMBER ?? ""), + (value) async { + Utils.showLoading(context); + try { + GenericResponseModel? genericResponseModel = await LoginApiClient().checkActivationCode(false, AppState().memberLoginList?.pMOBILENUMBER, value, AppState().username); + if (genericResponseModel?.errorMessage != null) { + Utils.showToast(genericResponseModel?.errorMessage ?? ""); + // Navigator.pop(context); + } + Utils.hideLoading(context); + Navigator.pop(context); + Navigator.pushNamedAndRemoveUntil(context, AppRoutes.dashboard, (Route route) => false); + } catch (ex) { + print(ex); + Utils.hideLoading(context); + Utils.handleException(ex, null); + } + }, + () => { + Navigator.pop(context), + }, + ).displayDialog(context); + + // authenticateUser(_flag, isActive: _loginIndex); + } + }, + child: Container( + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 15, top: 28), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + color: isDisable ? Colors.grey.withOpacity(0.3) : Colors.white, + border: Border.all(color: MyColors.lightGreyEFColor, width: 1), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SvgPicture.asset( + _icon, + height: 38, + width: 38, + color: isDisable ? MyColors.darkTextColor.withOpacity(0.7) : null, + ), + _title.toText16() + ], + ), + ), + ); + } + + Widget getButton(int flag) { + switch (flag) { + case 4: + return _loginOptionButton(LocaleKeys.verifyThroughWhatsapp.tr(), 'assets/images/login/verify_whatsapp.svg', flag, null); + case 1: + return _loginOptionButton(LocaleKeys.verifyThroughSMS.tr(), 'assets/images/login/verify_sms.svg', flag, null); + case 2: + return _loginOptionButton(LocaleKeys.verifyThroughFingerprint.tr(), 'assets/images/login/verify_thumb.svg', flag, BiometricType.fingerprint.index); + case 3: + return _loginOptionButton(LocaleKeys.verifyThroughFace.tr(), 'assets/images/login/verify_face.svg', flag, BiometricType.face.index); + default: + return const SizedBox(); + } + } + + bool checkBiometricIsAvailable(BiometricType biometricType) { + bool isAvailable = false; + for (int i = 0; i < _availableBioMetricType.length; i++) { + if (biometricType == _availableBioMetricType[i]) { + isAvailable = true; + break; + } + } + return isAvailable; + } +// +// formatDate(date) { +// return date; +// return DateFormat('MMM dd, yyy, kk:mm').format(date); +// } +// +// showLoader(bool isTrue) { +// setState(() { +// // isLoading = isTrue; +// }); +// } + +} diff --git a/lib/ui/login/verify_login_screen.dart b/lib/ui/login/verify_login_screen.dart index b28ad4d..bf64d8e 100644 --- a/lib/ui/login/verify_login_screen.dart +++ b/lib/ui/login/verify_login_screen.dart @@ -1,20 +1,25 @@ +import 'dart:io'; + import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:local_auth/auth_strings.dart'; import 'package:local_auth/local_auth.dart'; import 'package:mohem_flutter_app/api/login_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/consts.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/dialogs/otp_dialog.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; -import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/models/basic_member_information_model.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart'; +import 'package:mohem_flutter_app/models/member_information_list_model.dart'; +import 'package:mohem_flutter_app/models/privilege_list_model.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; class VerifyLoginScreen extends StatefulWidget { @@ -30,6 +35,8 @@ class _VerifyLoginScreenState extends State { final LocalAuthentication auth = LocalAuthentication(); List _availableBioMetricType = []; + String? firebaseToken; + @override void initState() { _getAvailableBiometrics(); @@ -39,6 +46,7 @@ class _VerifyLoginScreenState extends State { @override Widget build(BuildContext context) { + firebaseToken ??= ModalRoute.of(context)!.settings.arguments as String; return Scaffold( appBar: AppBar( backgroundColor: Colors.transparent, @@ -46,7 +54,7 @@ class _VerifyLoginScreenState extends State { icon: const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor), onPressed: () => Navigator.pop(context), ), - actions: [Center(child: "Employee Digital ID".toText12(color: MyColors.textMixColor, isUnderLine: true).onPress(() {})), 21.width], + // actions: [Center(child: "Employee Digital ID".toText12(color: MyColors.textMixColor, isUnderLine: true).onPress(() {})), 21.width], ), body: Column( children: [ @@ -61,69 +69,69 @@ class _VerifyLoginScreenState extends State { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.welcomeBack.tr().toText12(), - "Mohammad Hussain".toText24(isBold: true), - 10.height, - LocaleKeys.wouldYouLikeToLoginWithCurrentUsername.tr().toText16(), - Container( - height: 72, - margin: const EdgeInsets.only(top: 23, bottom: 23), - alignment: Alignment.center, - padding: EdgeInsets.only(left: 17, right: 12), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: Colors.white, - border: Border.all( - color: Color(0xffefefef), - width: 1, - ), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - LocaleKeys.lastLoginDetails.tr().toText16(), - // Text( - // user.editedOn != null - // ? DateUtil.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(user.editedOn)) - // : user.createdOn != null - // ? DateUtil.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(user.createdOn)) - // : '--', - // style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.48), - // ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - LocaleKeys.verificationType.tr().toText10(color: MyColors.grey57Color), - Text( - "SMS", - // " " + getType(user.logInType, context), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xff2B353E), - ), - ), - Expanded(child: SizedBox()), - // Text( - // user.editedOn != null - // ? DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(user.editedOn), false) - // : user.createdOn != null - // ? DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(user.createdOn), false) - // : '--', - // style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xff575757), letterSpacing: -0.48), - // ), - ], - ) - ], - ), - ), + // LocaleKeys.welcomeBack.tr().toText12(), + // "Mohammad Hussain".toText24(isBold: true), + // 10.height, + // LocaleKeys.wouldYouLikeToLoginWithCurrentUsername.tr().toText16(), + // Container( + // height: 72, + // margin: const EdgeInsets.only(top: 23, bottom: 23), + // alignment: Alignment.center, + // padding: EdgeInsets.only(left: 17, right: 12), + // decoration: BoxDecoration( + // borderRadius: BorderRadius.circular(10), + // color: Colors.white, + // border: Border.all( + // color: Color(0xffefefef), + // width: 1, + // ), + // ), + // child: Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // LocaleKeys.lastLoginDetails.tr().toText16(), + // // Text( + // // user.editedOn != null + // // ? DateUtil.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(user.editedOn)) + // // : user.createdOn != null + // // ? DateUtil.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(user.createdOn)) + // // : '--', + // // style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.48), + // // ), + // ], + // ), + // Row( + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // crossAxisAlignment: CrossAxisAlignment.center, + // children: [ + // LocaleKeys.verificationType.tr().toText10(color: MyColors.grey57Color), + // Text( + // "SMS", + // // " " + getType(user.logInType, context), + // style: TextStyle( + // fontSize: 12, + // fontWeight: FontWeight.w600, + // color: Color(0xff2B353E), + // ), + // ), + // Expanded(child: SizedBox()), + // // Text( + // // user.editedOn != null + // // ? DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(user.editedOn), false) + // // : user.createdOn != null + // // ? DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(user.createdOn), false) + // // : '--', + // // style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xff575757), letterSpacing: -0.48), + // // ), + // ], + // ) + // ], + // ), + // ), LocaleKeys.pleaseVerify.tr().toText16(), GridView( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 13, mainAxisSpacing: 9), @@ -612,9 +620,23 @@ class _VerifyLoginScreenState extends State { // // }); // } // + + Future loginWithFaceIDAndBiometrics() async { + IOSAuthMessages iosStrings = + const IOSAuthMessages(cancelButton: 'cancel', goToSettingsButton: 'settings', goToSettingsDescription: 'Please set up your Touch ID.', lockOut: 'Please reenable your Touch ID'); + bool authenticated = false; + try { + authenticated = await auth.authenticate(localizedReason: 'Scan your fingerprint to authenticate', useErrorDialogs: true, stickyAuth: true, iOSAuthStrings: iosStrings); + } on PlatformException catch (e) { + print(e); + Utils.hideLoading(context); + Utils.showToast("Please enable your Touch or Face ID"); + } + return authenticated; + } + Widget _loginOptionButton(String _title, String _icon, int _flag, int? _loginIndex) { bool isDisable = ((_flag == 3 && !checkBiometricIsAvailable(BiometricType.face)) || (_flag == 2 && !checkBiometricIsAvailable(BiometricType.fingerprint))); - print("$_title:$isDisable"); return InkWell( onTap: isDisable ? null @@ -624,37 +646,64 @@ class _VerifyLoginScreenState extends State { // isMoreOption = true; }); } else { - Utils.showLoading(context); - BasicMemberInformationModel? memberInformationModel = await LoginApiClient() - .mohemmSendActivationCodeByOTPNotificationType(checkBiometricIsAvailable(BiometricType.fingerprint) ? 1 : 0, AppState().memberLoginList?.pMOBILENUMBER, _flag, AppState().username); - Utils.hideLoading(context); - OtpDialog( - context, - _flag, - int.tryParse(AppState().memberLoginList?.pMOBILENUMBER ?? ""), - (value) async { - Utils.showLoading(context); - try { - GenericResponseModel? genericResponseModel = await LoginApiClient().checkActivationCode(false, AppState().memberLoginList?.pMOBILENUMBER, value, AppState().username); - if (genericResponseModel?.errorMessage != null) { - Utils.showToast(genericResponseModel?.errorMessage ?? ""); - // Navigator.pop(context); - } - Utils.hideLoading(context); - Navigator.pop(context); - Navigator.pushNamedAndRemoveUntil(context, AppRoutes.dashboard, (Route route) => false); - } catch (ex) { - print(ex); - Utils.hideLoading(context); - Utils.handleException(ex, null); + try { + Utils.showLoading(context); + if (_flag == 2 || _flag == 3) { + bool authenticateWithFaceAndTouchID = await loginWithFaceIDAndBiometrics(); + if (!authenticateWithFaceAndTouchID) { + return; } - }, - () => { - Navigator.pop(context), - }, - ).displayDialog(context); - - // authenticateUser(_flag, isActive: _loginIndex); + } + await LoginApiClient().checkMobileAppVersion(); + await LoginApiClient().memberLogin(AppState().username!, AppState().password!); + BasicMemberInformationModel? memberInformationModel = await LoginApiClient().mohemmSendActivationCodeByOTPNotificationType( + checkBiometricIsAvailable(BiometricType.fingerprint) ? 1 : 0, AppState().memberLoginList?.pMOBILENUMBER, _flag, AppState().username); + Utils.hideLoading(context); + OtpDialog( + context, + _flag, + int.tryParse(AppState().memberLoginList?.pMOBILENUMBER ?? ""), + (value) async { + Utils.showLoading(context); + try { + GenericResponseModel? genericResponseModel = await LoginApiClient().checkActivationCode(false, AppState().memberLoginList?.pMOBILENUMBER, value, AppState().username); + GenericResponseModel? genericResponseModel1 = await LoginApiClient().insertMobileLoginInfoNEW( + AppState().memberLoginList?.pEMAILADDRESS ?? "", + genericResponseModel?.pSESSIONID ?? 0, + genericResponseModel?.memberInformationList![0].eMPLOYEENAME ?? "", + _flag, + AppState().memberLoginList?.pMOBILENUMBER ?? "", + AppState().username!, + firebaseToken!, + Platform.isAndroid ? "android" : "ios"); + if (genericResponseModel?.errorMessage != null) { + Utils.showToast(genericResponseModel?.errorMessage ?? ""); + } else { + AppState().setPrivilegeListModel = genericResponseModel!.privilegeList ?? []; + AppState().setMemberInformationListModel = genericResponseModel.memberInformationList?.first; + MemberInformationListModel.saveToPrefs(genericResponseModel.memberInformationList ?? []); + PrivilegeListModel.saveToPrefs(genericResponseModel.privilegeList ?? []); + Utils.saveStringFromPrefs(SharedPrefsConsts.username, AppState().username!); + Utils.saveStringFromPrefs(SharedPrefsConsts.password, AppState().password!); + } + Utils.hideLoading(context); + Navigator.pop(context); + Navigator.pushNamedAndRemoveUntil(context, AppRoutes.dashboard, (Route route) => false); + } catch (ex) { + print(ex); + Utils.hideLoading(context); + Utils.handleException(ex, null); + } + }, + () => { + Navigator.pop(context), + }, + ).displayDialog(context); + } catch (ex) { + print(ex); + Utils.hideLoading(context); + Utils.handleException(ex, null); + } } }, child: Container( diff --git a/lib/widgets/dialogs/confirm_dialog.dart b/lib/widgets/dialogs/confirm_dialog.dart new file mode 100644 index 0000000..7264a9b --- /dev/null +++ b/lib/widgets/dialogs/confirm_dialog.dart @@ -0,0 +1,66 @@ +import 'package:easy_localization/src/public_ext.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/widgets/button/default_button.dart'; + +class ConfirmDialog extends StatelessWidget { + final String? title; + final String? message; + final String? okTitle; + final VoidCallback? onTap; + + const ConfirmDialog({Key? key, this.title, @required this.message, this.okTitle, this.onTap}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: Colors.white, + shape: RoundedRectangleBorder(), + insetPadding: EdgeInsets.only(left: 21, right: 21), + child: Padding( + padding: 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: Padding( + padding: const EdgeInsets.only(top: 16.0), + child: Text( + title ?? LocaleKeys.confirm.tr(), + style: TextStyle(fontSize: 24, fontWeight: FontWeight.w600, color: Color(0xff2B353E), height: 35 / 24, letterSpacing: -0.96), + ), + ), + ), + IconButton( + padding: EdgeInsets.zero, + icon: Icon(Icons.close), + color: Color(0xff2B353E), + constraints: BoxConstraints(), + onPressed: () { + Navigator.pop(context); + }, + ) + ], + ), + Text( + message ?? "", + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff808080), letterSpacing: -0.48), + ), + SizedBox(height: 28), + DefaultButton( + okTitle ?? LocaleKeys.ok.tr(), + onTap == null ? () => Navigator.pop(context) : onTap, + textColor: Colors.white, + //color: Ap.green, + ), + ], + ), + ), + ); + } +} diff --git a/lib/widgets/input_widget.dart b/lib/widgets/input_widget.dart index 8b248e4..7761816 100644 --- a/lib/widgets/input_widget.dart +++ b/lib/widgets/input_widget.dart @@ -12,15 +12,10 @@ class InputWidget extends StatelessWidget { final bool isInputTypeNum; final bool isObscureText; final bool isBackgroundEnable; + final Function(String)? onChange; InputWidget(this.labelText, this.hintText, this.controller, - {this.isObscureText = false, - this.suffixTap, - this.isEnable = true, - this.hasSelection = false, - this.lines = 1, - this.isInputTypeNum = false, - this.isBackgroundEnable = false}); + {this.isObscureText = false, this.suffixTap, this.isEnable = true, this.hasSelection = false, this.lines = 1, this.onChange, this.isInputTypeNum = false, this.isBackgroundEnable = false}); @override Widget build(BuildContext context) { @@ -56,14 +51,12 @@ class InputWidget extends StatelessWidget { TextField( enabled: isEnable, scrollPadding: EdgeInsets.zero, - keyboardType: isInputTypeNum - ? TextInputType.number - : TextInputType.text, + keyboardType: isInputTypeNum ? TextInputType.number : TextInputType.text, controller: controller, maxLines: lines, obscuringCharacter: "*", obscureText: isObscureText, - onChanged: (value) => {}, + onChanged: onChange, style: const TextStyle( fontSize: 14, height: 21 / 14, @@ -82,12 +75,7 @@ class InputWidget extends StatelessWidget { letterSpacing: -0.56, ), suffixIconConstraints: const BoxConstraints(minWidth: 50), - suffixIcon: suffixTap == null - ? null - : IconButton( - icon: const Icon(Icons.mic, - color: MyColors.darkTextColor), - onPressed: suffixTap), + suffixIcon: suffixTap == null ? null : IconButton(icon: const Icon(Icons.mic, color: MyColors.darkTextColor), onPressed: suffixTap), contentPadding: EdgeInsets.zero, border: InputBorder.none, focusedBorder: InputBorder.none, diff --git a/pubspec.yaml b/pubspec.yaml index dfbb0eb..611dbf1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,6 +44,8 @@ dependencies: sizer: ^2.0.15 local_auth: ^1.1.9 fluttertoast: ^8.0.8 + shared_preferences: ^2.0.12 + firebase_messaging: ^11.2.6 dev_dependencies: From 9c40878c4a9e49a4ffbd2852f74d0bad3be7f43a Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 13 Feb 2022 11:47:06 +0300 Subject: [PATCH 4/9] locale commited --- lib/generated/locale_keys.g.dart | 80 ++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 lib/generated/locale_keys.g.dart diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart new file mode 100644 index 0000000..b5ecc57 --- /dev/null +++ b/lib/generated/locale_keys.g.dart @@ -0,0 +1,80 @@ +// DO NOT EDIT. This is code generated via package:easy_localization/generate.dart + +abstract class LocaleKeys { + static const mohemm = 'mohemm'; + static const english = 'english'; + static const arabic = 'arabic'; + static const login = 'login'; + static const pleaseEnterLoginDetails = 'pleaseEnterLoginDetails'; + static const username = 'username'; + static const password = 'password'; + static const welcomeBack = 'welcomeBack'; + static const wouldYouLikeToLoginWithCurrentUsername = 'wouldYouLikeToLoginWithCurrentUsername'; + static const lastLoginDetails = 'lastLoginDetails'; + static const verificationType = 'verificationType'; + static const pleaseVerify = 'pleaseVerify'; + static const verifyThroughFace = 'verifyThroughFace'; + static const verifyThroughFingerprint = 'verifyThroughFingerprint'; + static const verifyThroughSMS = 'verifyThroughSMS'; + static const verifyThroughWhatsapp = 'verifyThroughWhatsapp'; + static const useAnotherAccount = 'useAnotherAccount'; + static const pleaseEnterTheVerificationCodeSentTo = 'pleaseEnterTheVerificationCodeSentTo'; + static const theVerificationCodeWillExpireIn = 'theVerificationCodeWillExpireIn'; + static const goodMorning = 'goodMorning'; + static const markAttendance = 'markAttendance'; + static const timeLeftToday = 'timeLeftToday'; + static const checkIn = 'checkIn'; + static const workList = 'workList'; + static const leaveBalance = 'leaveBalance'; + static const missingSwipes = 'missingSwipes'; + static const ticketBalance = 'ticketBalance'; + static const services = 'services'; + static const viewAllServices = 'viewAllServices'; + static const monthlyAttendance = 'monthlyAttendance'; + static const workFromHome = 'workFromHome'; + static const ticketRequest = 'ticketRequest'; + static const viewAllOffers = 'viewAllOffers'; + static const offers = 'offers'; + static const discounts = 'discounts'; + static const newString = 'newString'; + static const setTheNewPassword = 'setTheNewPassword'; + static const typeYourNewPasswordBelow = 'typeYourNewPasswordBelow'; + static const confirmPassword = 'confirmPassword'; + static const update = 'update'; + static const title = 'title'; + static const home = 'home'; + static const mySalary = 'mySalary'; + static const createRequest = 'createRequest'; + static const forgotPassword = 'forgotPassword'; + static const employeeId = 'employeeId'; + static const loginCodeWillSentToMobileNumber = 'loginCodeWillSentToMobileNumber'; + static const changePassword = 'changePassword'; + static const ok = 'ok'; + static const confirm = 'confirm'; + static const passwordChangedSuccessfully = 'passwordChangedSuccessfully'; + static const itemsForSale = 'itemsForSale'; + static const doNotUseRecentPassword = 'doNotUseRecentPassword'; + static const atLeastOneLowercase = 'atLeastOneLowercase'; + static const atLeastOneUppercase = 'atLeastOneUppercase'; + static const atLeastOneNumeric = 'atLeastOneNumeric'; + static const minimum8Characters = 'minimum8Characters'; + static const doNotAddRepeatingLetters = 'doNotAddRepeatingLetters'; + static const itShouldContainSpecialCharacter = 'itShouldContainSpecialCharacter'; + static const confirmPasswordMustMatch = 'confirmPasswordMustMatch'; + static const msg = 'msg'; + static const msg_named = 'msg_named'; + static const clickMe = 'clickMe'; + static const human = 'human'; + static const resources = 'resources'; + static const profile_reset_password_label = 'profile.reset_password.label'; + static const profile_reset_password_username = 'profile.reset_password.username'; + static const profile_reset_password_password = 'profile.reset_password.password'; + static const profile_reset_password = 'profile.reset_password'; + static const profile = 'profile'; + static const clicked = 'clicked'; + static const amount = 'amount'; + static const gender_with_arg = 'gender.with_arg'; + static const gender = 'gender'; + static const reset_locale = 'reset_locale'; + +} From 7027299a489ac8666022991b7fd0c90669f50493 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Sun, 13 Feb 2022 11:57:49 +0300 Subject: [PATCH 5/9] merge improvements --- lib/ui/login/verify_last_login_screen.dart | 6 +++--- lib/ui/login/verify_login_screen.dart | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/ui/login/verify_last_login_screen.dart b/lib/ui/login/verify_last_login_screen.dart index 0c8554f..282c7a5 100644 --- a/lib/ui/login/verify_last_login_screen.dart +++ b/lib/ui/login/verify_last_login_screen.dart @@ -653,9 +653,9 @@ class _VerifyLastLoginScreenState extends State { } } await LoginApiClient().checkMobileAppVersion(); - await LoginApiClient().memberLogin(AppState().username!, AppState().password!); + await LoginApiClient().memberLogin(AppState().getUserName!, AppState().password!); BasicMemberInformationModel? memberInformationModel = await LoginApiClient() - .mohemmSendActivationCodeByOTPNotificationType(checkBiometricIsAvailable(BiometricType.fingerprint) ? 1 : 0, AppState().memberLoginList?.pMOBILENUMBER, _flag, AppState().username); + .mohemmSendActivationCodeByOTPNotificationType(checkBiometricIsAvailable(BiometricType.fingerprint) ? 1 : 0, AppState().memberLoginList?.pMOBILENUMBER, _flag, AppState().getUserName); Utils.hideLoading(context); OtpDialog( context, @@ -664,7 +664,7 @@ class _VerifyLastLoginScreenState extends State { (value) async { Utils.showLoading(context); try { - GenericResponseModel? genericResponseModel = await LoginApiClient().checkActivationCode(false, AppState().memberLoginList?.pMOBILENUMBER, value, AppState().username); + GenericResponseModel? genericResponseModel = await LoginApiClient().checkActivationCode(false, AppState().memberLoginList?.pMOBILENUMBER, value, AppState().getUserName); if (genericResponseModel?.errorMessage != null) { Utils.showToast(genericResponseModel?.errorMessage ?? ""); // Navigator.pop(context); diff --git a/lib/ui/login/verify_login_screen.dart b/lib/ui/login/verify_login_screen.dart index bf64d8e..8e9255f 100644 --- a/lib/ui/login/verify_login_screen.dart +++ b/lib/ui/login/verify_login_screen.dart @@ -655,9 +655,9 @@ class _VerifyLoginScreenState extends State { } } await LoginApiClient().checkMobileAppVersion(); - await LoginApiClient().memberLogin(AppState().username!, AppState().password!); + await LoginApiClient().memberLogin(AppState().getUserName!, AppState().password!); BasicMemberInformationModel? memberInformationModel = await LoginApiClient().mohemmSendActivationCodeByOTPNotificationType( - checkBiometricIsAvailable(BiometricType.fingerprint) ? 1 : 0, AppState().memberLoginList?.pMOBILENUMBER, _flag, AppState().username); + checkBiometricIsAvailable(BiometricType.fingerprint) ? 1 : 0, AppState().memberLoginList?.pMOBILENUMBER, _flag, AppState().getUserName); Utils.hideLoading(context); OtpDialog( context, @@ -666,14 +666,14 @@ class _VerifyLoginScreenState extends State { (value) async { Utils.showLoading(context); try { - GenericResponseModel? genericResponseModel = await LoginApiClient().checkActivationCode(false, AppState().memberLoginList?.pMOBILENUMBER, value, AppState().username); + GenericResponseModel? genericResponseModel = await LoginApiClient().checkActivationCode(false, AppState().memberLoginList?.pMOBILENUMBER, value, AppState().getUserName); GenericResponseModel? genericResponseModel1 = await LoginApiClient().insertMobileLoginInfoNEW( AppState().memberLoginList?.pEMAILADDRESS ?? "", genericResponseModel?.pSESSIONID ?? 0, genericResponseModel?.memberInformationList![0].eMPLOYEENAME ?? "", _flag, AppState().memberLoginList?.pMOBILENUMBER ?? "", - AppState().username!, + AppState().getUserName!, firebaseToken!, Platform.isAndroid ? "android" : "ios"); if (genericResponseModel?.errorMessage != null) { @@ -683,7 +683,7 @@ class _VerifyLoginScreenState extends State { AppState().setMemberInformationListModel = genericResponseModel.memberInformationList?.first; MemberInformationListModel.saveToPrefs(genericResponseModel.memberInformationList ?? []); PrivilegeListModel.saveToPrefs(genericResponseModel.privilegeList ?? []); - Utils.saveStringFromPrefs(SharedPrefsConsts.username, AppState().username!); + Utils.saveStringFromPrefs(SharedPrefsConsts.username, AppState().getUserName!); Utils.saveStringFromPrefs(SharedPrefsConsts.password, AppState().password!); } Utils.hideLoading(context); From c4b7046a90dadafcf0f3d141a17e21eeb1318754 Mon Sep 17 00:00:00 2001 From: devmirza121 Date: Tue, 1 Mar 2022 10:39:16 +0300 Subject: [PATCH 6/9] Dashboard API's 1.3 --- assets/icons/create_req.svg | 7 +++ assets/icons/home.svg | 7 +++ assets/icons/item_for_sale.svg | 3 + assets/icons/work_list.svg | 15 +++++ lib/ui/landing/dashboard_screen.dart | 65 +++++++++++++++++++++- lib/ui/landing/widget/services_widget.dart | 25 +++++---- lib/ui/login/login_screen.dart | 2 +- 7 files changed, 110 insertions(+), 14 deletions(-) create mode 100644 assets/icons/create_req.svg create mode 100644 assets/icons/home.svg create mode 100644 assets/icons/item_for_sale.svg create mode 100644 assets/icons/work_list.svg diff --git a/assets/icons/create_req.svg b/assets/icons/create_req.svg new file mode 100644 index 0000000..a87e809 --- /dev/null +++ b/assets/icons/create_req.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/home.svg b/assets/icons/home.svg new file mode 100644 index 0000000..fb67997 --- /dev/null +++ b/assets/icons/home.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/icons/item_for_sale.svg b/assets/icons/item_for_sale.svg new file mode 100644 index 0000000..0a87567 --- /dev/null +++ b/assets/icons/item_for_sale.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/work_list.svg b/assets/icons/work_list.svg new file mode 100644 index 0000000..a802c53 --- /dev/null +++ b/assets/icons/work_list.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index 92ea49d..747ef1c 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -46,7 +46,7 @@ class _DashboardScreenState extends State { data.fetchWorkListCounter(); data.fetchMissingSwipe(); data.fetchLeaveTicketBalance(); - // data.fetchMenuEntries(); + data.fetchMenuEntries(); } @override @@ -329,6 +329,69 @@ class _DashboardScreenState extends State { ) ], ), + bottomNavigationBar: BottomNavigationBar( + items: [ + BottomNavigationBarItem( + icon: Padding( + padding: const EdgeInsets.all(4.0), + child: SvgPicture.asset( + "assets/icons/home.svg", + width: 20, + height: 20, + ), + ), + label: 'Home', + ), + BottomNavigationBarItem( + icon: Padding( + padding: const EdgeInsets.all(4.0), + child: SvgPicture.asset( + "assets/icons/create_req.svg", + width: 20, + height: 20, + ), + ), + label: 'Create Request', + ), + BottomNavigationBarItem( + icon: Padding( + padding: const EdgeInsets.all(4.0), + child: SvgPicture.asset( + "assets/icons/work_list.svg", + width: 20, + height: 20, + ), + ), + label: 'Work List', + ), + BottomNavigationBarItem( + icon: Padding( + padding: const EdgeInsets.all(4.0), + child: SvgPicture.asset( + "assets/icons/item_for_sale.svg", + width: 20, + height: 20, + ), + ), + label: 'Items for Sale', + ), + ], + currentIndex: 0, + selectedLabelStyle: TextStyle( + fontSize: 8, + color: Color(0xff989898), + fontWeight: FontWeight.w600, + ), + unselectedLabelStyle: TextStyle( + fontSize: 8, + color: Color(0xff989898), + fontWeight: FontWeight.w600, + ), + type: BottomNavigationBarType.fixed, + selectedItemColor: Colors.black, + backgroundColor: Color(0xffF8F8F8), + onTap: (v) {}, + ), ); } } diff --git a/lib/ui/landing/widget/services_widget.dart b/lib/ui/landing/widget/services_widget.dart index f397a4c..89a0aa6 100644 --- a/lib/ui/landing/widget/services_widget.dart +++ b/lib/ui/landing/widget/services_widget.dart @@ -128,18 +128,19 @@ class ServicesWidget extends StatelessWidget { SizedBox( height: 105 + 26, child: ListView.separated( - shrinkWrap: true, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), - scrollDirection: Axis.horizontal, - itemBuilder: (cxt, index) { - return AspectRatio( - aspectRatio: 105 / 105, - child: ServicesMenuShimmer(), - ); - }, - separatorBuilder: (cxt, index) => 9.width, - itemCount: 4), + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), + scrollDirection: Axis.horizontal, + itemBuilder: (cxt, index) { + return AspectRatio( + aspectRatio: 105 / 105, + child: ServicesMenuShimmer(), + ); + }, + separatorBuilder: (cxt, index) => 9.width, + itemCount: 4, + ), ), ], ); diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 2353f99..fe7f118 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -63,7 +63,7 @@ class _LoginScreenState extends State { @override Widget build(BuildContext context) { username.text="15153"; - password.text="e123e123e123"; + password.text="s12s12s12"; return Scaffold( body: Column( children: [ From 4b5ab2cd7f6fb988b978b78a66daf5c795371e94 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Wed, 2 Mar 2022 10:04:46 +0300 Subject: [PATCH 7/9] wokrlist api's added & improvements --- assets/langs/ar-SA.json | 11 +- assets/langs/en-US.json | 7 + lib/api/api_client.dart | 100 +++- lib/api/dashboard_api_client.dart | 10 +- lib/api/login_api_client.dart | 2 +- lib/api/worklist/worklist_api_client.dart | 41 ++ lib/classes/colors.dart | 1 + lib/classes/date_uitl.dart | 444 +++++++++++++++++ lib/extensions/string_extensions.dart | 42 +- lib/extensions/widget_extensions.dart | 22 +- lib/generated/codegen_loader.g.dart | 386 +++++++-------- lib/generated/locale_keys.g.dart | 10 +- lib/main.dart | 38 +- lib/models/dashboard/itg_forms_model.dart | 240 +++++---- lib/models/dashboard/list_menu.dart | 32 +- lib/models/generic_response_model.dart | 20 +- lib/models/worklist_item_type_model.dart | 38 ++ lib/models/worklist_response_model.dart | 137 ++++++ lib/provider/dashboard_provider_model.dart | 36 +- lib/ui/app_bar.dart | 26 - lib/ui/landing/dashboard_screen.dart | 189 ++++--- lib/ui/landing/today_attendance_screen.dart | 4 +- lib/ui/landing/widget/menus_widget.dart | 16 +- lib/ui/login/login_screen.dart | 10 +- lib/ui/login/verify_last_login_screen.dart | 463 +----------------- .../missing_swipe/missing_swipe_screen.dart | 248 +++++++--- lib/ui/work_list/work_list_screen.dart | 342 +++++++++---- lib/widgets/app_bar_widget.dart | 44 ++ 28 files changed, 1828 insertions(+), 1131 deletions(-) create mode 100644 lib/api/worklist/worklist_api_client.dart create mode 100644 lib/classes/date_uitl.dart create mode 100644 lib/models/worklist_item_type_model.dart create mode 100644 lib/models/worklist_response_model.dart delete mode 100644 lib/ui/app_bar.dart create mode 100644 lib/widgets/app_bar_widget.dart diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index f88c890..e8892a8 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -60,11 +60,18 @@ "doNotAddRepeatingLetters": "لا تقم بإضافة أحرف متكررة", "itShouldContainSpecialCharacter": "يجب أن يحتوي على طابع خاص", "confirmPasswordMustMatch": "يجب أن يتطابق تأكيد كلمة المرور", + "sms": "رسالة قصيرة", + "fingerPrint": "بصمة", + "face": "التعرف على الوجه", + "whatsapp": "واتس اب", + "reject": "يرفض", + "approve": "يوافق", "msg": "Hello {} in the {} world ", "msg_named": "{} are written in the {lang} language", "clickMe": "Click me", - "human": "Human", - "resources": "Resources", + "human": "بشري", + "resources": "موارد", + "details": "تفاصيل", "profile": { "reset_password": { "label": "Reset Password", diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index a669a4f..f5b9b44 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -60,11 +60,18 @@ "doNotAddRepeatingLetters": "Do not add repeating letters", "itShouldContainSpecialCharacter": "It should contain special character", "confirmPasswordMustMatch": "Confirm password must match", + "sms": "SMS", + "fingerPrint": "Fingerprint", + "face": "Face", + "whatsapp": "Whatsapp", + "reject": "Reject", + "approve": "Approve", "msg": "Hello {} in the {} world ", "msg_named": "{} are written in the {lang} language", "clickMe": "Click me", "human": "Human", "resources": "Resources", + "details": "Details", "profile": { "reset_password": { "label": "Reset Password", diff --git a/lib/api/api_client.dart b/lib/api/api_client.dart index 609aca9..17eac0d 100644 --- a/lib/api/api_client.dart +++ b/lib/api/api_client.dart @@ -76,25 +76,25 @@ class ApiClient { print("body:$jsonObject"); } var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: _headers, retryTimes: retryTimes); - try { - if (!kReleaseMode) { - logger.i("res: " + response.body); - } - var jsonData = jsonDecode(response.body); - if (jsonData["ErrorMessage"] == null) { - return factoryConstructor(jsonData); - } else { - APIError? apiError; - apiError = APIError(jsonData['ErrorCode'], jsonData['ErrorMessage']); - throw APIException(APIException.BAD_REQUEST, error: apiError); - } - } catch (ex) { - if (ex is APIException) { - rethrow; - } else { - throw APIException(APIException.BAD_RESPONSE_FORMAT, arguments: ex); - } + // try { + if (!kReleaseMode) { + logger.i("res: " + response.body); } + var jsonData = jsonDecode(response.body); + if (jsonData["ErrorMessage"] == null) { + return factoryConstructor(jsonData); + } else { + APIError? apiError; + apiError = APIError(jsonData['ErrorCode'], jsonData['ErrorMessage']); + throw APIException(APIException.BAD_REQUEST, error: apiError); + } + // } catch (ex) { + // if (ex is APIException) { + // rethrow; + // } else { + // throw APIException(APIException.BAD_RESPONSE_FORMAT, arguments: ex); + // } + // } } Future postJsonForResponse(String url, T jsonObject, {String? token, Map? queryParameters, Map? headers, int retryTimes = 0}) async { @@ -162,6 +162,68 @@ class ApiClient { } } + Future getJsonForResponse(String url, {String? token, Map? queryParameters, Map? headers, int retryTimes = 0}) async { + if (headers == null) { + headers = {'Content-Type': 'application/json'}; + } else { + headers['Content-Type'] = 'application/json'; + } + return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes); + } + + Future _getForResponse(String url, {String? token, Map? queryParameters, Map? headers, int retryTimes = 0}) async { + try { + var _headers = {}; + if (token != null) { + _headers['Authorization'] = 'Bearer $token'; + } + + if (headers != null && headers.isNotEmpty) { + _headers.addAll(headers); + } + + if (queryParameters != null) { + var queryString = new Uri(queryParameters: queryParameters).query; + url = url + '?' + queryString; + } + var response = await _get(Uri.parse(url), headers: _headers).timeout(Duration(seconds: 60)); + + if (response.statusCode >= 200 && response.statusCode < 300) { + return response; + } else { + throw _throwAPIException(response); + } + } on SocketException catch (e) { + if (retryTimes > 0) { + print('will retry after 3 seconds...'); + await Future.delayed(Duration(seconds: 3)); + return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); + } else { + throw APIException(APIException.OTHER, arguments: e); + } + } on HttpException catch (e) { + if (retryTimes > 0) { + print('will retry after 3 seconds...'); + await Future.delayed(Duration(seconds: 3)); + return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); + } else { + throw APIException(APIException.OTHER, arguments: e); + } + } on TimeoutException catch (e) { + throw APIException(APIException.TIMEOUT, arguments: e); + } on ClientException catch (e) { + if (retryTimes > 0) { + print('will retry after 3 seconds...'); + await Future.delayed(Duration(seconds: 3)); + return await _getForResponse(url, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes - 1); + } else { + throw APIException(APIException.OTHER, arguments: e); + } + } + } + + Future _get(url, {Map? headers}) => _withClient((client) => client.get(url, headers: headers)); + bool _certificateCheck(X509Certificate cert, String host, int port) => true; Future _withClient(Future Function(Client) fn) async { @@ -175,4 +237,4 @@ class ApiClient { } Future _post(url, {Map? headers, body, Encoding? encoding}) => _withClient((client) => client.post(url, headers: headers, body: body, encoding: encoding)); -} + } diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index b98a777..76e2453 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -11,12 +11,12 @@ import 'package:mohem_flutter_app/models/member_login_list_model.dart'; import 'api_client.dart'; -class DashbaordApiClient { - static final DashbaordApiClient _instance = DashbaordApiClient._internal(); +class DashboardApiClient { + static final DashboardApiClient _instance = DashboardApiClient._internal(); - DashbaordApiClient._internal(); + DashboardApiClient._internal(); - factory DashbaordApiClient() => _instance; + factory DashboardApiClient() => _instance; Future getAttendanceTracking() async { String url = "${ApiConsts.erpRest}GET_Attendance_Tracking"; @@ -82,7 +82,7 @@ class DashbaordApiClient { //GET_MENU_ENTRIES Future getGetMenuEntries() async { String url = "${ApiConsts.erpRest}GET_MENU_ENTRIES"; - Map postParams = {"P_SELECTED_RESP_ID": -999,"P_MENU_TYPE":"E"}; + Map postParams = {"P_SELECTED_RESP_ID": -999, "P_MENU_TYPE": "E"}; postParams.addAll(AppState().postParamsJson); return await ApiClient().postJsonForObject((json) { GenericResponseModel responseData = GenericResponseModel.fromJson(json); diff --git a/lib/api/login_api_client.dart b/lib/api/login_api_client.dart index f69e317..070fa27 100644 --- a/lib/api/login_api_client.dart +++ b/lib/api/login_api_client.dart @@ -47,6 +47,7 @@ class LoginApiClient { postParams.addAll(AppState().postParamsJson); return await ApiClient().postJsonForObject((json) { GenericResponseModel? responseData = GenericResponseModel.fromJson(json); + AppState().setLogged = true; return responseData; }, url, postParams); } @@ -82,7 +83,6 @@ class LoginApiClient { postParams.addAll(AppState().postParamsJson); return await ApiClient().postJsonForObject((json) { GenericResponseModel responseData = GenericResponseModel.fromJson(json); - AppState().setLogged = true; AppState().postParamsObject?.setTokenID = responseData.tokenID; AppState().postParamsObject?.mobileNumber = responseData.basicMemberInformation!.pMOBILENUMBER; AppState().postParamsObject?.userName = AppState().getUserName; diff --git a/lib/api/worklist/worklist_api_client.dart b/lib/api/worklist/worklist_api_client.dart new file mode 100644 index 0000000..a473ba7 --- /dev/null +++ b/lib/api/worklist/worklist_api_client.dart @@ -0,0 +1,41 @@ +import 'dart:async'; + +import 'package:mohem_flutter_app/api/api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/consts.dart'; +import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; +import 'package:mohem_flutter_app/models/generic_response_model.dart'; +import 'package:mohem_flutter_app/models/worklist_response_model.dart'; + +class WorkListApiClient { + static final WorkListApiClient _instance = WorkListApiClient._internal(); + + WorkListApiClient._internal(); + + factory WorkListApiClient() => _instance; + + Future?> getWorkList(int pPageNum, String pItemType) async { + String url = "${ApiConsts.erpRest}GET_WORKLIST"; + Map postParams = { + "P_NOTIFICATION_TYPE": "1", + "P_PAGE_NUM": pPageNum, + "P_PAGE_LIMIT": 50, + "P_ITEM_TYPE": pItemType, + }; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel? responseData = GenericResponseModel.fromJson(json); + return responseData.getWorkList; + }, url, postParams); + } + + Future GetITGTaskCountRequestType() async { + String url = "${ApiConsts.cocRest}ITGGetTaskCountRequestType"; + Map postParams = {}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + ItgFormsModel responseData = ItgFormsModel.fromJson(json); + return responseData; + }, url, postParams); + } +} diff --git a/lib/classes/colors.dart b/lib/classes/colors.dart index 2e5eaef..0d80b05 100644 --- a/lib/classes/colors.dart +++ b/lib/classes/colors.dart @@ -16,6 +16,7 @@ class MyColors { static const Color grey98Color = Color(0xff989898); static const Color lightGreyEFColor = Color(0xffEFEFEF); static const Color lightGreyEDColor = Color(0xffEDEDED); + static const Color lightGreyE6Color = Color(0xffE6E6E6); static const Color lightGreyEAColor = Color(0xffEAEAEA); static const Color darkWhiteColor = Color(0xffE0E0E0); static const Color redColor = Color(0xffD02127); diff --git a/lib/classes/date_uitl.dart b/lib/classes/date_uitl.dart new file mode 100644 index 0000000..d0f4b1f --- /dev/null +++ b/lib/classes/date_uitl.dart @@ -0,0 +1,444 @@ +import 'package:intl/intl.dart'; + +class DateUtil { + /// convert String To Date function + /// [date] String we want to convert + static DateTime convertStringToDate(String date) { + // /Date(1585774800000+0300)/ + if (date != null) { + const start = "/Date("; + const end = "+0300)"; + final startIndex = date.indexOf(start); + final endIndex = date.indexOf(end, startIndex + start.length); + return DateTime.fromMillisecondsSinceEpoch( + int.parse( + date.substring(startIndex + start.length, endIndex), + ), + ); + } else + return DateTime.now(); + } + + static DateTime convertSimpleStringDateToDate(String date) { + return DateFormat("MM/dd/yyyy hh:mm:ss").parse(date); + } + + static DateTime convertStringToDateNoTimeZone(String date) { + // /Date(1585774800000+0300)/ + if (date != null) { + const start = "/Date("; + const end = ")"; + final startIndex = date.indexOf(start); + final endIndex = date.indexOf(end, startIndex + start.length); + return DateTime.fromMillisecondsSinceEpoch( + int.parse( + date.substring(startIndex + start.length, endIndex), + ), + ); + } else + return DateTime.now(); + } + + static DateTime convertStringToDateTime(String date) { + if (date != null) { + try { + var dateT = date.split('/'); + var year = dateT[2].substring(0, 4); + var dateP = DateTime(int.parse(year), int.parse(dateT[1]), int.parse(dateT[0])); + return dateP; + } catch (e) { + print(e); + } + + return DateTime.now(); + } else + return DateTime.now(); + } + + static String convertDateToString(DateTime date) { + const start = "/Date("; + const end = "+0300)"; + int milliseconds = date.millisecondsSinceEpoch; + + return start + "$milliseconds" + end; + } + + static String convertDateToStringLocation(DateTime date) { + const start = "/Date("; + const end = ")/"; + int milliseconds = date.millisecondsSinceEpoch; + + return start + "$milliseconds" + end; + } + + static String formatDateToDate(DateTime date, bool isArabic) { + return DateFormat('dd MMM yyy', isArabic ? "ar_SA" : "en_US").format(date); + } + + static String formatDateToTime(DateTime date) { + return DateFormat('hh:mm a').format(date); + } + + static String yearMonthDay(DateTime dateTime) { + String dateFormat = '${dateTime.year}-${dateTime.month}-${dateTime.day}'; + return dateFormat; + } + + static String time(DateTime dateTime) { + String dateFormat = '${dateTime.hour}:${dateTime.minute}:00'; + return dateFormat; + } + + static String convertDateMSToJsonDate(utc) { + var dt = new DateTime.fromMicrosecondsSinceEpoch(utc); + + return "/Date(" + (dt.millisecondsSinceEpoch * 1000).toString() + '+0300' + ")/"; + } + + /// check Date + /// [dateString] String we want to convert + static String checkDate(DateTime checkedTime) { + DateTime currentTime = DateTime.now(); + if ((currentTime.year == checkedTime.year) && (currentTime.month == checkedTime.month) && (currentTime.day == checkedTime.day)) { + return "Today"; + } else if ((currentTime.year == checkedTime.year) && (currentTime.month == checkedTime.month)) { + if ((currentTime.day - checkedTime.day) == 1) { + return "YESTERDAY"; + } else if ((currentTime.day - checkedTime.day) == -1) { + return "Tomorrow"; + } + + if ((currentTime.day - checkedTime.day) <= -2) { + return "Next Week"; + } else { + return "Old Date"; + } + } + return "Old Date"; + } + + static String getDateFormatted(String date) { + DateTime dateObj = DateUtil.convertStringToDate(date); + return DateUtil.getWeekDay(dateObj.weekday) + ", " + dateObj.day.toString() + " " + DateUtil.getMonth(dateObj.month) + " " + dateObj.year.toString(); + } + + static String getISODateFormat(DateTime dateTime) { + // 2020-04-30T00:00:00.000 + return dateTime.toIso8601String(); + } + + /// get month by + /// [month] convert month number in to month name + static getMonth(int month) { + switch (month) { + case 1: + return "January"; + case 2: + return "February"; + case 3: + return "March"; + case 4: + return "April"; + case 5: + return "May"; + case 6: + return "June"; + case 7: + return "July"; + case 8: + return "August"; + case 9: + return "September"; + case 10: + return "October"; + case 11: + return "November"; + case 12: + return "December"; + } + } + + /// get month by + /// [month] convert month number in to month name in Arabic + static getMonthArabic(int month) { + switch (month) { + case 1: + return "يناير"; + case 2: + return " فبراير"; + case 3: + return "مارس"; + case 4: + return "أبريل"; + case 5: + return "مايو"; + case 6: + return "يونيو"; + case 7: + return "يوليو"; + case 8: + return "أغسطس"; + case 9: + return "سبتمبر"; + case 10: + return " اكتوبر"; + case 11: + return " نوفمبر"; + case 12: + return "ديسمبر"; + } + } + + static getMonthByName(String month) { + switch (month.toLowerCase()) { + case 'january': + return 1; + case 'february': + return 2; + case 'march': + return 3; + case 'april': + return 4; + case 'may': + return 5; + case 'june': + return 6; + case 'july': + return 7; + case 'august': + return 8; + case 'september': + return 9; + case 'october': + return 10; + case 'november': + return 11; + case 'december': + return 12; + } + } + + static DateTime getMonthDateTime(String month, yearName) { + DateTime? date; + try { + date = DateTime(int.parse(yearName), getMonthByName(month)); + } catch (e) { + print(e); + } + return date ?? DateTime.now(); + } + + /// get month by + /// [weekDay] convert week day in int to week day name + static getWeekDay(int weekDay) { + switch (weekDay) { + case 1: + return "Monday"; + case 2: + return "Tuesday"; + case 3: + return "Wednesday"; + case 4: + return "Thursday"; + case 5: + return "Friday"; + case 6: + return "Saturday "; + case 7: + return "Sunday"; + } + } + + /// get month by + /// [weekDay] convert week day in int to week day name arabic + static getWeekDayArabic(int weekDay) { + switch (weekDay) { + case 1: + return "الاثنين"; + case 2: + return "الثلاثاء"; + case 3: + return "الاربعاء"; + case 4: + return "الخميس"; + case 5: + return "الجمعه"; + case 6: + return "السبت "; + case 7: + return "الاحد"; + } + } + + static getWeekDayEnglish(int weekDay) { + switch (weekDay) { + case 1: + return "Monday"; + case 2: + return "Tuesday"; + case 3: + return "Wednesday"; + case 4: + return "Thursday"; + case 5: + return "Friday"; + case 6: + return "Saturday "; + case 7: + return "Sunday"; + } + } + + /// get data formatted like Apr 26,2020 + /// [dateTime] convert DateTime to data formatted + static String getMonthDayYearDateFormatted(DateTime dateTime) { + if (dateTime != null) + return getMonth(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString(); + else + return ""; + } + + /// get data formatted like Apr 26,2020 + /// [dateTime] convert DateTime to data formatted Arabic + static String getMonthDayYearDateFormattedAr(DateTime dateTime) { + if (dateTime != null) + return getMonthArabic(dateTime.month) + " " + dateTime.day.toString() + ", " + dateTime.year.toString(); + else + return ""; + } + + /// get data formatted like Thursday, Apr 26,2020 + /// [dateTime] convert DateTime to date formatted + static String getWeekDayMonthDayYearDateFormatted(DateTime dateTime, String lang) { + // print(dateTime); + // print(dateTime.weekday); + // print(dateTime.weekday.getDayOfWeekEnumValue.value); + if (dateTime != null) + return lang == 'en' + ? getWeekDayEnglish(dateTime.weekday) + ", " + getMonth(dateTime.month) + " " + dateTime.day.toString() + " " + dateTime.year.toString() + : getWeekDayArabic(dateTime.weekday) + ", " + dateTime.day.toString() + " " + getMonthArabic(dateTime.month) + " " + dateTime.year.toString(); + else + return ""; + } + + static String getMonthDayYearLangDateFormatted(DateTime dateTime, String lang) { + if (dateTime != null) + return lang == 'en' + ? getMonth(dateTime.month) + " " + dateTime.day.toString() + " " + dateTime.year.toString() + : dateTime.day.toString() + " " + getMonthArabic(dateTime.month) + " " + dateTime.year.toString(); + else + return ""; + } + + /// get data formatted like 26/4/2020 + static String getDayMonthYearLangDateFormatted(DateTime dateTime, String lang) { + if (dateTime != null) + return lang == 'en' + ? dateTime.day.toString() + " " + getMonth(dateTime.month) + " " + dateTime.year.toString() + : dateTime.day.toString() + " " + getMonthArabic(dateTime.month) + " " + dateTime.year.toString(); + else + return ""; + } + + static String getMonthYearLangDateFormatted(DateTime dateTime, String lang) { + if (dateTime != null) + return lang == 'en' ? getMonth(dateTime.month) + " " + dateTime.year.toString() : getMonthArabic(dateTime.month) + " " + dateTime.year.toString(); + else + return ""; + } + + /// get data formatted like 26/4/2020 + /// [dateTime] convert DateTime to data formatted + static String getDayMonthYearDateFormatted(DateTime dateTime) { + if (dateTime != null) + return dateTime.day.toString() + "/" + dateTime.month.toString() + "/" + dateTime.year.toString(); + else + return ""; + } + + /// get data formatted like 26/4/2020 + /// [dateTime] convert DateTime to data formatted + static String getDayMonthDateFormatted(DateTime dateTime) { + if (dateTime != null) + return DateFormat('dd/MM').format(dateTime); + else + return ""; + } + + /// get data formatted like 26/4/2020 + /// [dateTime] convert DateTime to data formatted according to language + static String getDayMonthYearDateFormattedLang(DateTime dateTime, bool isArabic) { + if (dateTime != null) + return DateFormat('dd/MM/yyyy', isArabic ? "ar_SA" : "en_US").format(dateTime); + else + return ""; + } + + /// get data formatted like 10:30 according to lang + static String formatDateToTimeLang(DateTime date, bool isArabic) { + return DateFormat('HH:mm', isArabic ? "ar_SA" : "en_US").format(date); + } + + /// get data formatted like 26/4/2020 10:30 + /// [dateTime] convert DateTime to data formatted + static String getDayMonthYearHourMinuteDateFormatted(DateTime dateTime) { + if (dateTime != null) + return dateTime.day.toString() + "/" + dateTime.month.toString() + "/" + dateTime.year.toString() + " " + DateFormat('HH:mm').format(dateTime); + else + return ""; + } + + /// get data formatted like 2020-8-13 09:43:00 + /// [dateTime] convert DateTime to data formatted + static String getYearMonthDayHourMinSecDateFormatted(DateTime dateTime) { + if (dateTime != null) + return dateTime.year.toString() + + "-" + + dateTime.month.toString() + + "-" + + dateTime.day.toString() + + " " + + dateTime.hour.toString() + + ":" + + dateTime.minute.toString() + + ":" + + dateTime.second.toString(); + else + return ""; + } + + static String getFormattedDate(DateTime dateTime, String formattedString) { + return DateFormat(formattedString).format(dateTime); + } + + static convertISODateToJsonDate(String isoDate) { + return "/Date(" + DateFormat('mm-dd-yyy').parse(isoDate).millisecondsSinceEpoch.toString() + ")/"; + } + + // static String getDay(DayOfWeek dayOfWeek) { + // switch (dayOfWeek) { + // case DayOfWeek.Monday: + // return "Monday"; + // break; + // case DayOfWeek.Tuesday: + // return "Tuesday"; + // break; + // case DayOfWeek.Wednesday: + // return "Wednesday"; + // break; + // case DayOfWeek.Thursday: + // return "Thursday"; + // break; + // case DayOfWeek.Friday: + // return "Friday"; + // break; + // case DayOfWeek.Saturday: + // return "Saturday"; + // break; + // case DayOfWeek.Sunday: + // return "Sunday"; + // break; + // } + // return ""; + // } +} diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 5d4f8cd..2db6135 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -1,15 +1,19 @@ +import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/cupertino.dart'; import 'package:intl/intl.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; extension CapExtension on String { String get toCamelCase => "${this[0].toUpperCase()}${this.substring(1)}"; + String get inCaps => '${this[0].toUpperCase()}${this.substring(1)}'; + String get allInCaps => this.toUpperCase(); + String get capitalizeFirstofEach => this.trim().length > 0 ? this.trim().toLowerCase().split(" ").map((str) => str.inCaps).join(" ") : ""; } - extension EmailValidator on String { Widget get toWidget => Text(this); @@ -65,9 +69,9 @@ extension EmailValidator on String { style: TextStyle(height: 1, color: color ?? MyColors.darkTextColor, fontSize: 22, letterSpacing: -1.44, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), ); - Widget toText24({Color? color, bool isBold = false}) => Text( + Widget toText24({Color? color, bool isBold = false, bool considerHeight = true}) => Text( this, - style: TextStyle(height: 23 / 24, color: color ?? MyColors.darkTextColor, fontSize: 24, letterSpacing: -1.44, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), + style: TextStyle(height: considerHeight ? 23 / 24 : null, color: color ?? MyColors.darkTextColor, fontSize: 24, letterSpacing: -1.44, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), ); Widget toText32({Color? color, bool isBold = false}) => Text( @@ -75,6 +79,38 @@ extension EmailValidator on String { style: TextStyle(height: 32 / 32, color: color ?? MyColors.darkTextColor, fontSize: 32, letterSpacing: -1.92, fontWeight: isBold ? FontWeight.bold : FontWeight.w600), ); + Widget toSectionHeading({String upperHeading = "", String lowerHeading = ""}) { + String upper = ""; + String lower = ""; + String heading = this; + if (heading.isNotEmpty) { + List data = heading.split(" "); + + if (data.length > 1) { + upper = data[0]; + data.removeAt(0); + lower = data.join(" "); + } else { + lower = data[0]; + } + } + if (upperHeading.isNotEmpty) { + upper = upperHeading; + } + if (lowerHeading.isNotEmpty) { + lower = lowerHeading; + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + if (upper.isNotEmpty) upper.toText12(), + lower.toText24(isBold: true), + ], + ); + } + bool isValidEmail() { return RegExp(r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$').hasMatch(this); } diff --git a/lib/extensions/widget_extensions.dart b/lib/extensions/widget_extensions.dart index 130c867..5169f19 100644 --- a/lib/extensions/widget_extensions.dart +++ b/lib/extensions/widget_extensions.dart @@ -5,19 +5,27 @@ import 'package:shimmer/shimmer.dart'; extension WidgetExtensions on Widget { Widget onPress(VoidCallback onTap) => InkWell(onTap: onTap, child: this); + Widget get expanded => Expanded(child: this); + + Widget get center => Center(child: this); + Widget paddingAll(double _value) => Padding(padding: EdgeInsets.all(_value), child: this); Widget paddingOnly({double left = 0.0, double right = 0.0, double top = 0.0, double bottom = 0.0}) => Padding(padding: EdgeInsets.only(left: left, right: right, top: top, bottom: bottom), child: this); - Widget toShimmer({bool isShow=true}) =>isShow? Shimmer.fromColors( - baseColor: Color(0xffe8eff0), - highlightColor: Colors.white, - child: Container( + Widget toShimmer({bool isShow = true}) => isShow + ? Shimmer.fromColors( + baseColor: Color(0xffe8eff0), + highlightColor: Colors.white, + child: Container( + child: this, + color: Colors.white, + ), + ) + : Container( child: this, - color: Colors.white, - ), - ):Container(child: this,); + ); Widget animatedSwither() => AnimatedSwitcher( duration: const Duration(milliseconds: 500), diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 3d67def..f4a2a6c 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -6,213 +6,193 @@ import 'dart:ui'; import 'package:easy_localization/easy_localization.dart' show AssetLoader; -class CodegenLoader extends AssetLoader{ +class CodegenLoader extends AssetLoader { const CodegenLoader(); @override - Future> load(String fullPath, Locale locale ) { + Future> load(String fullPath, Locale locale) { return Future.value(mapLocales[locale.toString()]); } - static const Map ar_SA = { - "mohemm": "Mohemm", - "english": "English", - "arabic": "Arabic", - "login": "تسجيل الدخول", - "pleaseEnterLoginDetails": "الرجاء إدخال التفاصيل أدناه لتسجيل الدخول", - "username": "اسم المستخدم", - "password": "كلمة المرور", - "welcomeBack": "مرحبا بعودتك", - "wouldYouLikeToLoginWithCurrentUsername": "هل ترغب في تسجيل الدخول باسم المستخدم الحالي؟", - "lastLoginDetails": "تفاصيل تسجيل الدخول الأخير:", - "verificationType": "نوع التحقق:", - "pleaseVerify": "ارجوك تحقق", - "verifyThroughFace": "تحقق من خلال الوجه", - "verifyThroughFingerprint": "تحقق من خلال بصمة الإصبع", - "verifyThroughSMS": "تحقق من خلال الرسائل القصيرة", - "verifyThroughWhatsapp": "تحقق من خلال Whatsapp", - "useAnotherAccount": "استخدم حسابا آخر", - "pleaseEnterTheVerificationCodeSentTo": "الرجاء إدخال رمز التحقق المرسل إلى ", - "theVerificationCodeWillExpireIn": "ستنتهي صلاحية رمز التحقق في ", - "goodMorning": "صباح الخير", - "markAttendance": "علامة الحضور", - "timeLeftToday": "الوقت المتبقي اليوم", - "checkIn": "تحقق في", - "workList": "قائمة العمل", - "leaveBalance": "رصيد الاجازات", - "missingSwipes": "الضربات الشديدة في عداد المفقودين", - "ticketBalance": "رصيد التذكرة", - "other": "آخر", - "services": "خدمات", - "viewAllServices": "عرض جميع الخدمات", - "monthlyAttendance": "الحضور الشهري", - "workFromHome": "العمل من المنزل", - "ticketRequest": "طلب تذكرة", - "viewAllOffers": "مشاهدة جميع العروض", - "offers": "عروض & ", - "discounts": "الخصومات", - "newString": "جديد", - "setTheNewPassword": "قم بتعيين كلمة المرور الجديدة", - "typeYourNewPasswordBelow": "اكتب كلمة المرور الجديدة أدناه", - "confirmPassword": "تأكيد كلمة المرور", - "update": "تحديث", - "title": "عنوان", - "home": "مسكن", - "mySalary": "راتبي", - "createRequest": "إنشاء طلب", - "forgotPassword": "هل نسيت كلمة السر", - "employeeId": "هوية الموظف", - "loginCodeWillSentToMobileNumber": "الرجاء إدخال معرف الموظف الخاص بك ، وسيتم إرسال رمز تسجيل الدخول إلى رقم هاتفك المحمول", - "changePassword": "تغيير كلمة المرور", - "ok": "موافق", - "confirm": "تؤكد", - "passwordChangedSuccessfully": "تم تغيير الرقم السري بنجاح", - "itemsForSale": "سلع للبيع", - "doNotUseRecentPassword": "لا تستخدم كلمة مرور حديثة", - "atLeastOneLowercase": "حرف صغير واحد على الأقل", - "atLeastOneUppercase": "حرف كبير واحد على الأقل", - "atLeastOneNumeric": "رقم واحد على الأقل", - "minimum8Characters": "8 أحرف على الأقل", - "doNotAddRepeatingLetters": "لا تقم بإضافة أحرف متكررة", - "itShouldContainSpecialCharacter": "يجب أن يحتوي على طابع خاص", - "confirmPasswordMustMatch": "يجب أن يتطابق تأكيد كلمة المرور", - "msg": "Hello {} in the {} world ", - "msg_named": "{} are written in the {lang} language", - "clickMe": "Click me", - "human": "Human", - "resources": "Resources", - "profile": { - "reset_password": { - "label": "Reset Password", - "username": "Username", - "password": "password" - } - }, - "clicked": { - "zero": "You clicked {} times!", - "one": "You clicked {} time!", - "two": "You clicked {} times!", - "few": "You clicked {} times!", - "many": "You clicked {} times!", - "other": "You clicked {} times!" - }, - "amount": { - "zero": "Your amount : {} ", - "one": "Your amount : {} ", - "two": "Your amount : {} ", - "few": "Your amount : {} ", - "many": "Your amount : {} ", - "other": "Your amount : {} " - }, - "gender": { - "male": "Hi man ;) ", - "female": "Hello girl :)", - "with_arg": { - "male": "Hi man ;) {}", - "female": "Hello girl :) {}" - } - }, - "reset_locale": "Reset Language" -}; -static const Map en_US = { - "mohemm": "Mohemm", - "english": "English", - "arabic": "Arabic", - "login": "Login", - "pleaseEnterLoginDetails": "Please enter the detail below to login", - "username": "Username", - "password": "Password", - "welcomeBack": "Welcome back", - "wouldYouLikeToLoginWithCurrentUsername": "Would you like to login with current Username?", - "lastLoginDetails": "Last Login Details:", - "verificationType": "Verification Type:", - "pleaseVerify": "Please Verify", - "verifyThroughFace": "Verify Through Face", - "verifyThroughFingerprint": "Verify Through Fingerprint", - "verifyThroughSMS": "Verify Through SMS", - "verifyThroughWhatsapp": "Verify Through Whatsapp", - "useAnotherAccount": "Use Another Account", - "pleaseEnterTheVerificationCodeSentTo": "Please enter the verification code sent to ", - "theVerificationCodeWillExpireIn": "The verification code will expire in ", - "goodMorning": "Good Morning", - "markAttendance": "Mark Attendance", - "timeLeftToday": "Time Left Today", - "checkIn": "Check In", - "workList": "Work List", - "leaveBalance": "Leave Balance", - "missingSwipes": "Missing Swipes", - "ticketBalance": "Ticket Balance", - "other": "Other", - "services": "Services", - "viewAllServices": "View All Services", - "monthlyAttendance": "Monthly Attendance", - "workFromHome": "Work From Home", - "ticketRequest": "Ticket Request", - "viewAllOffers": "View All Offers", - "offers": "Offers & ", - "discounts": "Discounts", - "newString": "New", - "setTheNewPassword": "Set the new password", - "typeYourNewPasswordBelow": "Type your new password below", - "confirmPassword": "Confirm Password", - "update": "Update", - "title": "Title", - "home": "Home", - "mySalary": "My Salary", - "createRequest": "Create Request", - "forgotPassword": "Forgot Password", - "employeeId": "Employee ID", - "loginCodeWillSentToMobileNumber": "Please Enter your Employee ID, A login code will be sent to your mobile number", - "changePassword": "Change Password", - "ok": "OK", - "confirm": "Confirm", - "passwordChangedSuccessfully": "Password changed successfully", - "itemsForSale": "Items for Sale", - "doNotUseRecentPassword": "Do not use recent password", - "atLeastOneLowercase": "At least one lowercase", - "atLeastOneUppercase": "At least one uppercase", - "atLeastOneNumeric": "At least one numeric", - "minimum8Characters": "Minimum 8 characters", - "doNotAddRepeatingLetters": "Do not add repeating letters", - "itShouldContainSpecialCharacter": "It should contain special character", - "confirmPasswordMustMatch": "Confirm password must match", - "msg": "Hello {} in the {} world ", - "msg_named": "{} are written in the {lang} language", - "clickMe": "Click me", - "human": "Human", - "resources": "Resources", - "profile": { - "reset_password": { - "label": "Reset Password", - "username": "Username", - "password": "password" - } - }, - "clicked": { - "zero": "You clicked {} times!", - "one": "You clicked {} time!", - "two": "You clicked {} times!", - "few": "You clicked {} times!", - "many": "You clicked {} times!", - "other": "You clicked {} times!" - }, - "amount": { - "zero": "Your amount : {} ", - "one": "Your amount : {} ", - "two": "Your amount : {} ", - "few": "Your amount : {} ", - "many": "Your amount : {} ", - "other": "Your amount : {} " - }, - "gender": { - "male": "Hi man ;) ", - "female": "Hello girl :)", - "with_arg": { - "male": "Hi man ;) {}", - "female": "Hello girl :) {}" - } - }, - "reset_locale": "Reset Language" -}; -static const Map> mapLocales = {"ar_SA": ar_SA, "en_US": en_US}; + static const Map ar_SA = { + "mohemm": "Mohemm", + "english": "English", + "arabic": "Arabic", + "login": "تسجيل الدخول", + "pleaseEnterLoginDetails": "الرجاء إدخال التفاصيل أدناه لتسجيل الدخول", + "username": "اسم المستخدم", + "password": "كلمة المرور", + "welcomeBack": "مرحبا بعودتك", + "wouldYouLikeToLoginWithCurrentUsername": "هل ترغب في تسجيل الدخول باسم المستخدم الحالي؟", + "lastLoginDetails": "تفاصيل تسجيل الدخول الأخير:", + "verificationType": "نوع التحقق:", + "pleaseVerify": "ارجوك تحقق", + "verifyThroughFace": "تحقق من خلال الوجه", + "verifyThroughFingerprint": "تحقق من خلال بصمة الإصبع", + "verifyThroughSMS": "تحقق من خلال الرسائل القصيرة", + "verifyThroughWhatsapp": "تحقق من خلال Whatsapp", + "useAnotherAccount": "استخدم حسابا آخر", + "pleaseEnterTheVerificationCodeSentTo": "الرجاء إدخال رمز التحقق المرسل إلى ", + "theVerificationCodeWillExpireIn": "ستنتهي صلاحية رمز التحقق في ", + "goodMorning": "صباح الخير", + "markAttendance": "علامة الحضور", + "timeLeftToday": "الوقت المتبقي اليوم", + "checkIn": "تحقق في", + "workList": "قائمة العمل", + "leaveBalance": "رصيد الاجازات", + "missingSwipes": "الضربات الشديدة في عداد المفقودين", + "ticketBalance": "رصيد التذكرة", + "other": "آخر", + "services": "خدمات", + "viewAllServices": "عرض جميع الخدمات", + "monthlyAttendance": "الحضور الشهري", + "workFromHome": "العمل من المنزل", + "ticketRequest": "طلب تذكرة", + "viewAllOffers": "مشاهدة جميع العروض", + "offers": "عروض & ", + "discounts": "الخصومات", + "newString": "جديد", + "setTheNewPassword": "قم بتعيين كلمة المرور الجديدة", + "typeYourNewPasswordBelow": "اكتب كلمة المرور الجديدة أدناه", + "confirmPassword": "تأكيد كلمة المرور", + "update": "تحديث", + "title": "عنوان", + "home": "مسكن", + "mySalary": "راتبي", + "createRequest": "إنشاء طلب", + "forgotPassword": "هل نسيت كلمة السر", + "employeeId": "هوية الموظف", + "loginCodeWillSentToMobileNumber": "الرجاء إدخال معرف الموظف الخاص بك ، وسيتم إرسال رمز تسجيل الدخول إلى رقم هاتفك المحمول", + "changePassword": "تغيير كلمة المرور", + "ok": "موافق", + "confirm": "تؤكد", + "passwordChangedSuccessfully": "تم تغيير الرقم السري بنجاح", + "itemsForSale": "سلع للبيع", + "doNotUseRecentPassword": "لا تستخدم كلمة مرور حديثة", + "atLeastOneLowercase": "حرف صغير واحد على الأقل", + "atLeastOneUppercase": "حرف كبير واحد على الأقل", + "atLeastOneNumeric": "رقم واحد على الأقل", + "minimum8Characters": "8 أحرف على الأقل", + "doNotAddRepeatingLetters": "لا تقم بإضافة أحرف متكررة", + "itShouldContainSpecialCharacter": "يجب أن يحتوي على طابع خاص", + "confirmPasswordMustMatch": "يجب أن يتطابق تأكيد كلمة المرور", + "sms": "رسالة قصيرة", + "fingerPrint": "بصمة", + "face": "التعرف على الوجه", + "whatsapp": "واتس اب", + "msg": "Hello {} in the {} world ", + "msg_named": "{} are written in the {lang} language", + "clickMe": "Click me", + "human": "Human", + "resources": "Resources", + "profile": { + "reset_password": {"label": "Reset Password", "username": "Username", "password": "password"} + }, + "clicked": { + "zero": "You clicked {} times!", + "one": "You clicked {} time!", + "two": "You clicked {} times!", + "few": "You clicked {} times!", + "many": "You clicked {} times!", + "other": "You clicked {} times!" + }, + "amount": {"zero": "Your amount : {} ", "one": "Your amount : {} ", "two": "Your amount : {} ", "few": "Your amount : {} ", "many": "Your amount : {} ", "other": "Your amount : {} "}, + "gender": { + "male": "Hi man ;) ", + "female": "Hello girl :)", + "with_arg": {"male": "Hi man ;) {}", "female": "Hello girl :) {}"} + }, + "reset_locale": "Reset Language" + }; + static const Map en_US = { + "mohemm": "Mohemm", + "english": "English", + "arabic": "Arabic", + "login": "Login", + "pleaseEnterLoginDetails": "Please enter the detail below to login", + "username": "Username", + "password": "Password", + "welcomeBack": "Welcome back", + "wouldYouLikeToLoginWithCurrentUsername": "Would you like to login with current Username?", + "lastLoginDetails": "Last Login Details:", + "verificationType": "Verification Type:", + "pleaseVerify": "Please Verify", + "verifyThroughFace": "Verify Through Face", + "verifyThroughFingerprint": "Verify Through Fingerprint", + "verifyThroughSMS": "Verify Through SMS", + "verifyThroughWhatsapp": "Verify Through Whatsapp", + "useAnotherAccount": "Use Another Account", + "pleaseEnterTheVerificationCodeSentTo": "Please enter the verification code sent to ", + "theVerificationCodeWillExpireIn": "The verification code will expire in ", + "goodMorning": "Good Morning", + "markAttendance": "Mark Attendance", + "timeLeftToday": "Time Left Today", + "checkIn": "Check In", + "workList": "Work List", + "leaveBalance": "Leave Balance", + "missingSwipes": "Missing Swipes", + "ticketBalance": "Ticket Balance", + "other": "Other", + "services": "Services", + "viewAllServices": "View All Services", + "monthlyAttendance": "Monthly Attendance", + "workFromHome": "Work From Home", + "ticketRequest": "Ticket Request", + "viewAllOffers": "View All Offers", + "offers": "Offers & ", + "discounts": "Discounts", + "newString": "New", + "setTheNewPassword": "Set the new password", + "typeYourNewPasswordBelow": "Type your new password below", + "confirmPassword": "Confirm Password", + "update": "Update", + "title": "Title", + "home": "Home", + "mySalary": "My Salary", + "createRequest": "Create Request", + "forgotPassword": "Forgot Password", + "employeeId": "Employee ID", + "loginCodeWillSentToMobileNumber": "Please Enter your Employee ID, A login code will be sent to your mobile number", + "changePassword": "Change Password", + "ok": "OK", + "confirm": "Confirm", + "passwordChangedSuccessfully": "Password changed successfully", + "itemsForSale": "Items for Sale", + "doNotUseRecentPassword": "Do not use recent password", + "atLeastOneLowercase": "At least one lowercase", + "atLeastOneUppercase": "At least one uppercase", + "atLeastOneNumeric": "At least one numeric", + "minimum8Characters": "Minimum 8 characters", + "doNotAddRepeatingLetters": "Do not add repeating letters", + "itShouldContainSpecialCharacter": "It should contain special character", + "confirmPasswordMustMatch": "Confirm password must match", + "sms": "SMS", + "fingerPrint": "Fingerprint", + "face": "Face", + "whatsapp": "Whatsapp", + "msg": "Hello {} in the {} world ", + "msg_named": "{} are written in the {lang} language", + "clickMe": "Click me", + "human": "Human", + "resources": "Resources", + "profile": { + "reset_password": {"label": "Reset Password", "username": "Username", "password": "password"} + }, + "clicked": { + "zero": "You clicked {} times!", + "one": "You clicked {} time!", + "two": "You clicked {} times!", + "few": "You clicked {} times!", + "many": "You clicked {} times!", + "other": "You clicked {} times!" + }, + "amount": {"zero": "Your amount : {} ", "one": "Your amount : {} ", "two": "Your amount : {} ", "few": "Your amount : {} ", "many": "Your amount : {} ", "other": "Your amount : {} "}, + "gender": { + "male": "Hi man ;) ", + "female": "Hello girl :)", + "with_arg": {"male": "Hi man ;) {}", "female": "Hello girl :) {}"} + }, + "reset_locale": "Reset Language" + }; + static const Map> mapLocales = {"ar_SA": ar_SA, "en_US": en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index b5ecc57..55e543b 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -1,6 +1,6 @@ // DO NOT EDIT. This is code generated via package:easy_localization/generate.dart -abstract class LocaleKeys { +abstract class LocaleKeys { static const mohemm = 'mohemm'; static const english = 'english'; static const arabic = 'arabic'; @@ -61,11 +61,18 @@ abstract class LocaleKeys { static const doNotAddRepeatingLetters = 'doNotAddRepeatingLetters'; static const itShouldContainSpecialCharacter = 'itShouldContainSpecialCharacter'; static const confirmPasswordMustMatch = 'confirmPasswordMustMatch'; + static const sms = 'sms'; + static const fingerPrint = 'fingerPrint'; + static const face = 'face'; + static const whatsapp = 'whatsapp'; static const msg = 'msg'; static const msg_named = 'msg_named'; static const clickMe = 'clickMe'; static const human = 'human'; static const resources = 'resources'; + static const details = 'details'; + static const reject = 'reject'; + static const approve = 'approve'; static const profile_reset_password_label = 'profile.reset_password.label'; static const profile_reset_password_username = 'profile.reset_password.username'; static const profile_reset_password_password = 'profile.reset_password.password'; @@ -76,5 +83,4 @@ abstract class LocaleKeys { static const gender_with_arg = 'gender.with_arg'; static const gender = 'gender'; static const reset_locale = 'reset_locale'; - } diff --git a/lib/main.dart b/lib/main.dart index 49b0e35..20759d9 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,18 +14,19 @@ import 'package:firebase_core/firebase_core.dart'; import 'config/routes.dart'; import 'package:logger/logger.dart'; - var logger = Logger( // filter: null, // Use the default LogFilter (-> only log in debug mode) printer: PrettyPrinter(lineLength: 0), // Use the PrettyPrinter to format and print log // output: null, // U ); - Future main() async { WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); await Firebase.initializeApp(); + AppState().setPostParamsModel( + PostParamsModel(channel: 31, versionID: 3.2, mobileType: Platform.isAndroid ? "android" : "ios"), + ); runApp( EasyLocalization( supportedLocales: const [ @@ -57,23 +58,22 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return AppProvider( - child: Sizer( - builder: (context, orientation, deviceType) { - AppState().setPostParamsModel( - PostParamsModel(languageID: EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2, channel: 31, versionID: 3.2, mobileType: Platform.isAndroid ? "android" : "ios"), - ); - return MaterialApp( - theme: AppTheme.getTheme(EasyLocalization.of(context)?.locale.languageCode == "ar"), - debugShowCheckedModeBanner: false, - localizationsDelegates: context.localizationDelegates, - supportedLocales: context.supportedLocales, - locale: context.locale, - initialRoute: AppRoutes.initialRoute, - routes: AppRoutes.routes, - ); - }, - ), + return Sizer( + builder: (context, orientation, deviceType) { + print(AppState().postParamsObject?.toJson()); + var obj = AppState().postParamsObject; + obj?.languageID = EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2; + AppState().setPostParamsModel(obj!); + return MaterialApp( + theme: AppTheme.getTheme(EasyLocalization.of(context)?.locale.languageCode == "ar"), + debugShowCheckedModeBanner: false, + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: context.locale, + initialRoute: AppRoutes.initialRoute, + routes: AppRoutes.routes, + ); + }, ); } } diff --git a/lib/models/dashboard/itg_forms_model.dart b/lib/models/dashboard/itg_forms_model.dart index c5a0145..5ed719b 100644 --- a/lib/models/dashboard/itg_forms_model.dart +++ b/lib/models/dashboard/itg_forms_model.dart @@ -93,97 +93,163 @@ class ItgFormsModel { dynamic mohemmItgProjectsList; dynamic mohemmItgTicketTypesList; DateTime? referenceNumber; - dynamic requestType; + List? requestType; int? totalCount; int? statuseCode; factory ItgFormsModel.fromJson(Map json) => ItgFormsModel( - date: json["Date"], - languageId: json["LanguageID"] == null ? null : json["LanguageID"], - serviceName: json["ServiceName"] == null ? null : json["ServiceName"], - time: json["Time"], - androidLink: json["AndroidLink"], - authenticationTokenId: json["AuthenticationTokenID"], - data: json["Data"], - dataw: json["Dataw"] == null ? null : json["Dataw"], - dietType: json["DietType"] == null ? null : json["DietType"], - dietTypeId: json["DietTypeID"] == null ? null : json["DietTypeID"], - errorCode: json["ErrorCode"], - errorEndUserMessage: json["ErrorEndUserMessage"], - errorEndUserMessageN: json["ErrorEndUserMessageN"], - errorMessage: json["ErrorMessage"], - errorType: json["ErrorType"] == null ? null : json["ErrorType"], - foodCategory: json["FoodCategory"] == null ? null : json["FoodCategory"], - iosLink: json["IOSLink"], - isAuthenticated: json["IsAuthenticated"] == null ? null : json["IsAuthenticated"], - mealOrderStatus: json["MealOrderStatus"] == null ? null : json["MealOrderStatus"], - mealType: json["MealType"] == null ? null : json["MealType"], - messageStatus: json["MessageStatus"] == null ? null : json["MessageStatus"], - numberOfResultRecords: json["NumberOfResultRecords"] == null ? null : json["NumberOfResultRecords"], - patientBlodType: json["PatientBlodType"], - successMsg: json["SuccessMsg"] == null ? null : json["SuccessMsg"], - successMsgN: json["SuccessMsgN"], - vidaUpdatedResponse: json["VidaUpdatedResponse"], - itgRequest: json["ITGRequest"], - itgFormAttachmentsList: json["Itg_FormAttachmentsList"], - message: json["Message"] == null ? null : json["Message"], - mohemmItgDepartmentSectionsList: json["Mohemm_ITG_DepartmentSectionsList"], - mohemmItgProjectDepartmentsList: json["Mohemm_ITG_ProjectDepartmentsList"], - mohemmItgResponseItem: json["Mohemm_ITG_ResponseItem"], - mohemmItgSectionTopicsList: json["Mohemm_ITG_SectionTopicsList"], - mohemmItgTicketDetailsList: json["Mohemm_ITG_TicketDetailsList"], - mohemmItgTicketTransactionsList: json["Mohemm_ITG_TicketTransactionsList"], - mohemmItgTicketsByEmployeeList: json["Mohemm_ITG_TicketsByEmployeeList"], - mohemmItgProjectsList: json["Mohemm_Itg_ProjectsList"], - mohemmItgTicketTypesList: json["Mohemm_Itg_TicketTypesList"], - referenceNumber: json["ReferenceNumber"] == null ? null : DateTime.parse(json["ReferenceNumber"]), - requestType: json["RequestType"], - totalCount: json["TotalCount"] == null ? null : json["TotalCount"], - statuseCode: json["statuseCode"] == null ? null : json["statuseCode"], - ); + date: json["Date"], + languageId: json["LanguageID"] == null ? null : json["LanguageID"], + serviceName: json["ServiceName"] == null ? null : json["ServiceName"], + time: json["Time"], + androidLink: json["AndroidLink"], + authenticationTokenId: json["AuthenticationTokenID"], + data: json["Data"], + dataw: json["Dataw"] == null ? null : json["Dataw"], + dietType: json["DietType"] == null ? null : json["DietType"], + dietTypeId: json["DietTypeID"] == null ? null : json["DietTypeID"], + errorCode: json["ErrorCode"], + errorEndUserMessage: json["ErrorEndUserMessage"], + errorEndUserMessageN: json["ErrorEndUserMessageN"], + errorMessage: json["ErrorMessage"], + errorType: json["ErrorType"] == null ? null : json["ErrorType"], + foodCategory: json["FoodCategory"] == null ? null : json["FoodCategory"], + iosLink: json["IOSLink"], + isAuthenticated: json["IsAuthenticated"] == null ? null : json["IsAuthenticated"], + mealOrderStatus: json["MealOrderStatus"] == null ? null : json["MealOrderStatus"], + mealType: json["MealType"] == null ? null : json["MealType"], + messageStatus: json["MessageStatus"] == null ? null : json["MessageStatus"], + numberOfResultRecords: json["NumberOfResultRecords"] == null ? null : json["NumberOfResultRecords"], + patientBlodType: json["PatientBlodType"], + successMsg: json["SuccessMsg"] == null ? null : json["SuccessMsg"], + successMsgN: json["SuccessMsgN"], + vidaUpdatedResponse: json["VidaUpdatedResponse"], + itgRequest: json["ITGRequest"], + itgFormAttachmentsList: json["Itg_FormAttachmentsList"], + message: json["Message"] == null ? null : json["Message"], + mohemmItgDepartmentSectionsList: json["Mohemm_ITG_DepartmentSectionsList"], + mohemmItgProjectDepartmentsList: json["Mohemm_ITG_ProjectDepartmentsList"], + mohemmItgResponseItem: json["Mohemm_ITG_ResponseItem"], + mohemmItgSectionTopicsList: json["Mohemm_ITG_SectionTopicsList"], + mohemmItgTicketDetailsList: json["Mohemm_ITG_TicketDetailsList"], + mohemmItgTicketTransactionsList: json["Mohemm_ITG_TicketTransactionsList"], + mohemmItgTicketsByEmployeeList: json["Mohemm_ITG_TicketsByEmployeeList"], + mohemmItgProjectsList: json["Mohemm_Itg_ProjectsList"], + mohemmItgTicketTypesList: json["Mohemm_Itg_TicketTypesList"], + referenceNumber: json["ReferenceNumber"] == null ? null : DateTime.parse(json["ReferenceNumber"]), + requestType: json["RequestType"] == null ? [] : json['RequestType']!.map((v) => RequestType.fromJson(v)).toList(), + totalCount: json["TotalCount"] == null ? null : json["TotalCount"], + statuseCode: json["statuseCode"] == null ? null : json["statuseCode"], + ); Map toMap() => { - "Date": date, - "LanguageID": languageId == null ? null : languageId, - "ServiceName": serviceName == null ? null : serviceName, - "Time": time, - "AndroidLink": androidLink, - "AuthenticationTokenID": authenticationTokenId, - "Data": data, - "Dataw": dataw == null ? null : dataw, - "DietType": dietType == null ? null : dietType, - "DietTypeID": dietTypeId == null ? null : dietTypeId, - "ErrorCode": errorCode, - "ErrorEndUserMessage": errorEndUserMessage, - "ErrorEndUserMessageN": errorEndUserMessageN, - "ErrorMessage": errorMessage, - "ErrorType": errorType == null ? null : errorType, - "FoodCategory": foodCategory == null ? null : foodCategory, - "IOSLink": iosLink, - "IsAuthenticated": isAuthenticated == null ? null : isAuthenticated, - "MealOrderStatus": mealOrderStatus == null ? null : mealOrderStatus, - "MealType": mealType == null ? null : mealType, - "MessageStatus": messageStatus == null ? null : messageStatus, - "NumberOfResultRecords": numberOfResultRecords == null ? null : numberOfResultRecords, - "PatientBlodType": patientBlodType, - "SuccessMsg": successMsg == null ? null : successMsg, - "SuccessMsgN": successMsgN, - "VidaUpdatedResponse": vidaUpdatedResponse, - "ITGRequest": itgRequest, - "Itg_FormAttachmentsList": itgFormAttachmentsList, - "Message": message == null ? null : message, - "Mohemm_ITG_DepartmentSectionsList": mohemmItgDepartmentSectionsList, - "Mohemm_ITG_ProjectDepartmentsList": mohemmItgProjectDepartmentsList, - "Mohemm_ITG_ResponseItem": mohemmItgResponseItem, - "Mohemm_ITG_SectionTopicsList": mohemmItgSectionTopicsList, - "Mohemm_ITG_TicketDetailsList": mohemmItgTicketDetailsList, - "Mohemm_ITG_TicketTransactionsList": mohemmItgTicketTransactionsList, - "Mohemm_ITG_TicketsByEmployeeList": mohemmItgTicketsByEmployeeList, - "Mohemm_Itg_ProjectsList": mohemmItgProjectsList, - "Mohemm_Itg_TicketTypesList": mohemmItgTicketTypesList, - "ReferenceNumber": referenceNumber == null ? null : referenceNumber!.toIso8601String(), - "RequestType": requestType, - "TotalCount": totalCount == null ? null : totalCount, - "statuseCode": statuseCode == null ? null : statuseCode, - }; + "Date": date, + "LanguageID": languageId == null ? null : languageId, + "ServiceName": serviceName == null ? null : serviceName, + "Time": time, + "AndroidLink": androidLink, + "AuthenticationTokenID": authenticationTokenId, + "Data": data, + "Dataw": dataw == null ? null : dataw, + "DietType": dietType == null ? null : dietType, + "DietTypeID": dietTypeId == null ? null : dietTypeId, + "ErrorCode": errorCode, + "ErrorEndUserMessage": errorEndUserMessage, + "ErrorEndUserMessageN": errorEndUserMessageN, + "ErrorMessage": errorMessage, + "ErrorType": errorType == null ? null : errorType, + "FoodCategory": foodCategory == null ? null : foodCategory, + "IOSLink": iosLink, + "IsAuthenticated": isAuthenticated == null ? null : isAuthenticated, + "MealOrderStatus": mealOrderStatus == null ? null : mealOrderStatus, + "MealType": mealType == null ? null : mealType, + "MessageStatus": messageStatus == null ? null : messageStatus, + "NumberOfResultRecords": numberOfResultRecords == null ? null : numberOfResultRecords, + "PatientBlodType": patientBlodType, + "SuccessMsg": successMsg == null ? null : successMsg, + "SuccessMsgN": successMsgN, + "VidaUpdatedResponse": vidaUpdatedResponse, + "ITGRequest": itgRequest, + "Itg_FormAttachmentsList": itgFormAttachmentsList, + "Message": message == null ? null : message, + "Mohemm_ITG_DepartmentSectionsList": mohemmItgDepartmentSectionsList, + "Mohemm_ITG_ProjectDepartmentsList": mohemmItgProjectDepartmentsList, + "Mohemm_ITG_ResponseItem": mohemmItgResponseItem, + "Mohemm_ITG_SectionTopicsList": mohemmItgSectionTopicsList, + "Mohemm_ITG_TicketDetailsList": mohemmItgTicketDetailsList, + "Mohemm_ITG_TicketTransactionsList": mohemmItgTicketTransactionsList, + "Mohemm_ITG_TicketsByEmployeeList": mohemmItgTicketsByEmployeeList, + "Mohemm_Itg_ProjectsList": mohemmItgProjectsList, + "Mohemm_Itg_TicketTypesList": mohemmItgTicketTypesList, + "ReferenceNumber": referenceNumber == null ? null : referenceNumber!.toIso8601String(), + "RequestType": requestType == null ? null : requestType!.map((v) => v.toJson()).toList(), + "TotalCount": totalCount == null ? null : totalCount, + "statuseCode": statuseCode == null ? null : statuseCode, + }; +} + +class RequestType { + int? itemCount; + List? requestDetails; + String? requestTypeCode; + String? requestTypeName; + + RequestType({this.itemCount, this.requestDetails, this.requestTypeCode, this.requestTypeName}); + + RequestType.fromJson(Map json) { + itemCount = json['ItemCount']; + if (json['RequestDetails'] != null) { + requestDetails = []; + json['RequestDetails'].forEach((v) { + requestDetails!.add(new RequestDetails.fromJson(v)); + }); + } + requestTypeCode = json['RequestTypeCode']; + requestTypeName = json['RequestTypeName']; + } + + Map toJson() { + final Map data = new Map(); + data['ItemCount'] = this.itemCount; + if (this.requestDetails != null) { + data['RequestDetails'] = this.requestDetails!.map((v) => v.toJson()).toList(); + } + data['RequestTypeCode'] = this.requestTypeCode; + data['RequestTypeName'] = this.requestTypeName; + return data; + } +} + +class RequestDetails { + int? iD; + int? itemID; + String? listID; + String? listName; + String? modifiedDate; + String? title; + String? uRL; + + RequestDetails({this.iD, this.itemID, this.listID, this.listName, this.modifiedDate, this.title, this.uRL}); + + RequestDetails.fromJson(Map json) { + iD = json['ID']; + itemID = json['ItemID']; + listID = json['ListID']; + listName = json['ListName']; + modifiedDate = json['ModifiedDate']; + title = json['Title']; + uRL = json['URL']; + } + + Map toJson() { + final Map data = new Map(); + data['ID'] = this.iD; + data['ItemID'] = this.itemID; + data['ListID'] = this.listID; + data['ListName'] = this.listName; + data['ModifiedDate'] = this.modifiedDate; + data['Title'] = this.title; + data['URL'] = this.uRL; + return data; + } } diff --git a/lib/models/dashboard/list_menu.dart b/lib/models/dashboard/list_menu.dart index ba327c2..a55cd79 100644 --- a/lib/models/dashboard/list_menu.dart +++ b/lib/models/dashboard/list_menu.dart @@ -18,22 +18,22 @@ class ListMenu { String? subMenuName; factory ListMenu.fromJson(Map json) => ListMenu( - menuId: json["MENU_ID"] == null ? null : json["MENU_ID"], - menuName: json["MENU_NAME"] == null ? null : json["MENU_NAME"], - menuType: json["MENU_TYPE"] == null ? null : json["MENU_TYPE"], - requestGroupId: json["REQUEST_GROUP_ID"] == null ? null : json["REQUEST_GROUP_ID"], - requestGroupName: json["REQUEST_GROUP_NAME"] == null ? null : json["REQUEST_GROUP_NAME"], - respId: json["RESP_ID"], - subMenuName: json["SUB_MENU_NAME"] == null ? null : json["SUB_MENU_NAME"], - ); + menuId: json["MENU_ID"] == null ? null : json["MENU_ID"], + menuName: json["MENU_NAME"] == null ? null : json["MENU_NAME"], + menuType: json["MENU_TYPE"] == null ? null : json["MENU_TYPE"], + requestGroupId: json["REQUEST_GROUP_ID"] == null ? null : json["REQUEST_GROUP_ID"], + requestGroupName: json["REQUEST_GROUP_NAME"] == null ? null : json["REQUEST_GROUP_NAME"], + respId: json["RESP_ID"], + subMenuName: json["SUB_MENU_NAME"] == null ? null : json["SUB_MENU_NAME"], + ); Map toJson() => { - "MENU_ID": menuId == null ? null : menuId, - "MENU_NAME": menuName == null ? null : menuName, - "MENU_TYPE": menuType == null ? null : menuType, - "REQUEST_GROUP_ID": requestGroupId == null ? null : requestGroupId, - "REQUEST_GROUP_NAME": requestGroupName == null ? null : requestGroupName, - "RESP_ID": respId, - "SUB_MENU_NAME": subMenuName == null ? null : subMenuName, - }; + "MENU_ID": menuId == null ? null : menuId, + "MENU_NAME": menuName == null ? null : menuName, + "MENU_TYPE": menuType == null ? null : menuType, + "REQUEST_GROUP_ID": requestGroupId == null ? null : requestGroupId, + "REQUEST_GROUP_NAME": requestGroupName == null ? null : requestGroupName, + "RESP_ID": respId, + "SUB_MENU_NAME": subMenuName == null ? null : subMenuName, + }; } diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index a9781d1..0b5c497 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -1,4 +1,5 @@ import 'package:mohem_flutter_app/models/member_login_list_model.dart'; +import 'package:mohem_flutter_app/models/worklist_response_model.dart'; import 'basic_member_information_model.dart'; import 'get_mobile_login_info_list_model.dart'; @@ -150,7 +151,7 @@ class GenericResponseModel { List? getVaccinationOnHandList; List? getVaccinationsList; List? getValueSetValuesList; - List? getWorkList; + List? getWorkList; String? hRCertificateTemplate; String? imgURLsList; String? insertApInv; @@ -612,7 +613,7 @@ class GenericResponseModel { getItemTypeNotificationsList = json['GetItemTypeNotificationsList']; getItemTypesList = json['GetItemTypesList']; getLookupValuesList = json['GetLookupValuesList']; - getMenuEntriesList= json["GetMenuEntriesList"] == null ? null : List.from(json["GetMenuEntriesList"].map((x) => GetMenuEntriesList.fromJson(x))); + getMenuEntriesList = json["GetMenuEntriesList"] == null ? null : List.from(json["GetMenuEntriesList"].map((x) => GetMenuEntriesList.fromJson(x))); getMoItemHistoryList = json['GetMoItemHistoryList']; getMoNotificationBodyList = json['GetMoNotificationBodyList']; getNotificationButtonsList = json['GetNotificationButtonsList']; @@ -656,7 +657,14 @@ class GenericResponseModel { getVaccinationOnHandList = json['GetVaccinationOnHandList']; getVaccinationsList = json['GetVaccinationsList']; getValueSetValuesList = json['GetValueSetValuesList']; - getWorkList = json['GetWorkList']; + + if (json['GetWorkList'] != null) { + getWorkList = []; + json['GetWorkList'].forEach((v) { + getWorkList!.add(WorkListResponseModel.fromJson(v)); + }); + } + hRCertificateTemplate = json['HRCertificateTemplate']; imgURLsList = json['ImgURLsList']; insertApInv = json['InsertApInv']; @@ -930,7 +938,11 @@ class GenericResponseModel { data['GetVaccinationOnHandList'] = this.getVaccinationOnHandList; data['GetVaccinationsList'] = this.getVaccinationsList; data['GetValueSetValuesList'] = this.getValueSetValuesList; - data['GetWorkList'] = this.getWorkList; + + if (getWorkList != null) { + data['GetWorkList'] = getWorkList!.map((v) => v.toJson()).toList(); + } + data['HRCertificateTemplate'] = this.hRCertificateTemplate; data['ImgURLsList'] = this.imgURLsList; data['InsertApInv'] = this.insertApInv; diff --git a/lib/models/worklist_item_type_model.dart b/lib/models/worklist_item_type_model.dart new file mode 100644 index 0000000..49346c0 --- /dev/null +++ b/lib/models/worklist_item_type_model.dart @@ -0,0 +1,38 @@ +import 'dart:ui'; + +class WorkListItemTypeModelData { + late int value; + late String name; + late String fullName; + late bool active; + late List color; + late String icon; + late String key; + late bool disable; + + WorkListItemTypeModelData({required this.value, required this.name, required this.fullName, required this.active, required this.color, required this.icon, required this.key, required this.disable}); + + WorkListItemTypeModelData.fromJson(Map json) { + value = json['value']; + name = json['name']; + fullName = json['fullName']; + active = json['active']; + color = json['color']; + icon = json['icon']; + key = json['key']; + disable = json['disable']; + } + + Map toJson() { + final Map data = {}; + data['value'] = value; + data['name'] = name; + data['fullName'] = fullName; + data['active'] = active; + data['color'] = color; + data['icon'] = icon; + data['key'] = key; + data['disable'] = disable; + return data; + } +} diff --git a/lib/models/worklist_response_model.dart b/lib/models/worklist_response_model.dart new file mode 100644 index 0000000..4899d5a --- /dev/null +++ b/lib/models/worklist_response_model.dart @@ -0,0 +1,137 @@ + +class WorkListResponseModel { + String? bEGINDATE; + String? dUEDATE; + String? eNDDATE; + String? fROMROLE; + int? fROMROWNUM; + String? fROMUSER; + String? fUNCTIONNAME; + String? iTEMKEY; + String? iTEMTYPE; + String? iTEMTYPEDISPLAYNAME; + String? lANGUAGE; + String? mAILSTATUS; + String? mOREINFOROLE; + int? nOTIFICATIONID; + String? nOTIFICATIONNAME; + int? nOOFROWS; + String? oRIGINALRECIPIENT; + String? pONUMBER; + int? pRIORITY; + String? pRIORITYF; + String? pRNUMBER; + String? rECIPIENTROLE; + String? rEQUESTNUMBER; + String? rEQUESTTYPE; + String? rESPONDER; + int? rOWNUM; + String? sELECTEDEMPLOYEENUMBER; + String? sTATUS; + String? sUBJECT; + int? tOROWNUM; + String? tOUSER; + + WorkListResponseModel( + {this.bEGINDATE, + this.dUEDATE, + this.eNDDATE, + this.fROMROLE, + this.fROMROWNUM, + this.fROMUSER, + this.fUNCTIONNAME, + this.iTEMKEY, + this.iTEMTYPE, + this.iTEMTYPEDISPLAYNAME, + this.lANGUAGE, + this.mAILSTATUS, + this.mOREINFOROLE, + this.nOTIFICATIONID, + this.nOTIFICATIONNAME, + this.nOOFROWS, + this.oRIGINALRECIPIENT, + this.pONUMBER, + this.pRIORITY, + this.pRIORITYF, + this.pRNUMBER, + this.rECIPIENTROLE, + this.rEQUESTNUMBER, + this.rEQUESTTYPE, + this.rESPONDER, + this.rOWNUM, + this.sELECTEDEMPLOYEENUMBER, + this.sTATUS, + this.sUBJECT, + this.tOROWNUM, + this.tOUSER}); + + WorkListResponseModel.fromJson(Map json) { + bEGINDATE = json['BEGIN_DATE']; + dUEDATE = json['DUE_DATE']; + eNDDATE = json['END_DATE']; + fROMROLE = json['FROM_ROLE']; + fROMROWNUM = json['FROM_ROW_NUM']; + fROMUSER = json['FROM_USER']; + fUNCTIONNAME = json['FUNCTION_NAME']; + iTEMKEY = json['ITEM_KEY']; + iTEMTYPE = json['ITEM_TYPE']; + iTEMTYPEDISPLAYNAME = json['ITEM_TYPE_DISPLAY_NAME']; + lANGUAGE = json['LANGUAGE']; + mAILSTATUS = json['MAIL_STATUS']; + mOREINFOROLE = json['MORE_INFO_ROLE']; + nOTIFICATIONID = json['NOTIFICATION_ID']; + nOTIFICATIONNAME = json['NOTIFICATION_NAME']; + nOOFROWS = json['NO_OF_ROWS']; + oRIGINALRECIPIENT = json['ORIGINAL_RECIPIENT']; + pONUMBER = json['PO_NUMBER']; + pRIORITY = json['PRIORITY']; + pRIORITYF = json['PRIORITY_F']; + pRNUMBER = json['PR_NUMBER']; + rECIPIENTROLE = json['RECIPIENT_ROLE']; + rEQUESTNUMBER = json['REQUEST_NUMBER']; + rEQUESTTYPE = json['REQUEST_TYPE']; + rESPONDER = json['RESPONDER']; + rOWNUM = json['ROW_NUM']; + sELECTEDEMPLOYEENUMBER = json['SELECTED_EMPLOYEE_NUMBER']; + sTATUS = json['STATUS']; + sUBJECT = json['SUBJECT']; + tOROWNUM = json['TO_ROW_NUM']; + tOUSER = json['TO_USER']; + } + + Map toJson() { + final Map data = new Map(); + data['BEGIN_DATE'] = this.bEGINDATE; + data['DUE_DATE'] = this.dUEDATE; + data['END_DATE'] = this.eNDDATE; + data['FROM_ROLE'] = this.fROMROLE; + data['FROM_ROW_NUM'] = this.fROMROWNUM; + data['FROM_USER'] = this.fROMUSER; + data['FUNCTION_NAME'] = this.fUNCTIONNAME; + data['ITEM_KEY'] = this.iTEMKEY; + data['ITEM_TYPE'] = this.iTEMTYPE; + data['ITEM_TYPE_DISPLAY_NAME'] = this.iTEMTYPEDISPLAYNAME; + data['LANGUAGE'] = this.lANGUAGE; + data['MAIL_STATUS'] = this.mAILSTATUS; + data['MORE_INFO_ROLE'] = this.mOREINFOROLE; + data['NOTIFICATION_ID'] = this.nOTIFICATIONID; + data['NOTIFICATION_NAME'] = this.nOTIFICATIONNAME; + data['NO_OF_ROWS'] = this.nOOFROWS; + data['ORIGINAL_RECIPIENT'] = this.oRIGINALRECIPIENT; + data['PO_NUMBER'] = this.pONUMBER; + data['PRIORITY'] = this.pRIORITY; + data['PRIORITY_F'] = this.pRIORITYF; + data['PR_NUMBER'] = this.pRNUMBER; + data['RECIPIENT_ROLE'] = this.rECIPIENTROLE; + data['REQUEST_NUMBER'] = this.rEQUESTNUMBER; + data['REQUEST_TYPE'] = this.rEQUESTTYPE; + data['RESPONDER'] = this.rESPONDER; + data['ROW_NUM'] = this.rOWNUM; + data['SELECTED_EMPLOYEE_NUMBER'] = this.sELECTEDEMPLOYEENUMBER; + data['STATUS'] = this.sTATUS; + data['SUBJECT'] = this.sUBJECT; + data['TO_ROW_NUM'] = this.tOROWNUM; + data['TO_USER'] = this.tOUSER; + return data; + } +} \ No newline at end of file diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index b52448e..f15e244 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -6,6 +6,7 @@ import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/main.dart'; import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; +import 'package:mohem_flutter_app/models/dashboard/get_open_notifications_list.dart'; import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; import 'package:mohem_flutter_app/models/dashboard/menus.dart'; @@ -39,9 +40,9 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { List? getMenuEntriesList; //Attendance Tracking API's & Methods - fetchAttendanceTracking() async { + void fetchAttendanceTracking() async { try { - attendanceTracking = await DashbaordApiClient().getAttendanceTracking(); + attendanceTracking = await DashboardApiClient().getAttendanceTracking(); isAttendanceTrackingLoading = false; isTimeRemainingInSeconds = calculateSeconds("00:00:00"); endTime = DateTime.now().millisecondsSinceEpoch + Duration(seconds: isTimeRemainingInSeconds).inMilliseconds; @@ -67,31 +68,36 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { attendanceTracking?.pSwipeIn = "a"; isTimeRemainingInSeconds = calculateSeconds("00:10:30"); endTime = DateTime.now().millisecondsSinceEpoch + Duration(seconds: isTimeRemainingInSeconds).inMilliseconds; - notifyListeners(); } + ItgFormsModel? itgFormsModel; + List? getOpenNotificationsList; //Work List API's & Methods - fetchWorkListCounter() async { + Future fetchWorkListCounter(context, {bool showLoading = false}) async { try { - GenericResponseModel? genericResponseModel = await DashbaordApiClient().getOpenNotifications(); + if (showLoading) Utils.showLoading(context); + GenericResponseModel? genericResponseModel = await DashboardApiClient().getOpenNotifications(); isWorkListLoading = false; + getOpenNotificationsList = genericResponseModel?.getOpenNotificationsList; workListCounter = genericResponseModel?.pOPENNTFNUMBER ?? 0; - ItgFormsModel? itgFormsModel = await DashbaordApiClient().getItgFormsPendingTask(); + itgFormsModel = await DashboardApiClient().getItgFormsPendingTask(); workListCounter = workListCounter + (itgFormsModel?.totalCount ?? 0); + if (showLoading) Utils.hideLoading(context); notifyListeners(); } catch (ex) { isWorkListLoading = false; logger.wtf(ex); + if (showLoading) Utils.hideLoading(context); notifyListeners(); Utils.handleException(ex, null); } } //Missing Siwpe API's & Methods - fetchMissingSwipe() async { + Future fetchMissingSwipe() async { try { - GenericResponseModel? genericResponseModel = await DashbaordApiClient().getOpenMissingSwipes(); + GenericResponseModel? genericResponseModel = await DashboardApiClient().getOpenMissingSwipes(); isMissingSwipeLoading = false; missingSwipeCounter = genericResponseModel!.getOpenMissingSwipesList!.pOpenMissingSwipes ?? 0; notifyListeners(); @@ -104,9 +110,9 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } //Leave and Ticket Balance API's & Methods - fetchLeaveTicketBalance() async { + Future fetchLeaveTicketBalance() async { try { - GenericResponseModel? genericResponseModel = await DashbaordApiClient().getAccrualBalances(); + GenericResponseModel? genericResponseModel = await DashboardApiClient().getAccrualBalances(); isLeaveTicketBalanceLoading = false; leaveBalance = genericResponseModel?.getAccrualBalancesList![0].accrualNetEntitlement ?? 0.0; ticketBalance = (genericResponseModel?.getAccrualBalancesList![1].accrualNetEntitlement ?? 0.0) + (genericResponseModel?.getAccrualBalancesList![2].accrualNetEntitlement ?? 0.0); @@ -122,12 +128,12 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { //List Menu API's & Methods fetchListMenu() async { try { - GenericResponseModel? genericResponseModel = await DashbaordApiClient().getListMenu(); + GenericResponseModel? genericResponseModel = await DashboardApiClient().getListMenu(); Map map = {}; print(jsonEncode(genericResponseModel!.listMenu)); - for (int i = 0; i < genericResponseModel!.listMenu!.length; i++) { - print(genericResponseModel!.listMenu![i]!.menuName ?? ""); - map[genericResponseModel!.listMenu![i]!.menuName ?? ""] = i.toString(); + for (int i = 0; i < genericResponseModel.listMenu!.length; i++) { + print(genericResponseModel.listMenu![i].menuName ?? ""); + map[genericResponseModel.listMenu![i].menuName ?? ""] = i.toString(); } logger.i(map); notifyListeners(); @@ -141,7 +147,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { //Menu Entries API's & Methods fetchMenuEntries() async { try { - GenericResponseModel? genericResponseModel = await DashbaordApiClient().getGetMenuEntries(); + GenericResponseModel? genericResponseModel = await DashboardApiClient().getGetMenuEntries(); getMenuEntriesList = genericResponseModel!.getMenuEntriesList; homeMenus = parseMenues(getMenuEntriesList ?? []); isServicesMenusLoading = false; diff --git a/lib/ui/app_bar.dart b/lib/ui/app_bar.dart deleted file mode 100644 index c39e249..0000000 --- a/lib/ui/app_bar.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:easy_localization/src/public_ext.dart'; -import 'package:flutter/material.dart'; -import 'package:mohem_flutter_app/classes/colors.dart'; -import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; -import 'package:mohem_flutter_app/extensions/string_extensions.dart'; - -AppBar appBar(BuildContext context, {required String title}) { - return AppBar( - title: title.toText24(color: MyColors.darkTextColor), - centerTitle: false, - automaticallyImplyLeading: false, - backgroundColor: Colors.white, - - actions: [ - IconButton( - onPressed: () { - Navigator.pop(context); - }, - icon: Icon( - Icons.close, - color: MyColors.darkIconColor, - ), - ), - ], - ); -} diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index 92ea49d..f7260cf 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -1,30 +1,20 @@ -import 'dart:convert'; - import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; -import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_model.dart'; -import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; -import 'package:mohem_flutter_app/models/dashboard/menus.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; -import 'package:mohem_flutter_app/theme/colors.dart'; import 'package:mohem_flutter_app/ui/landing/widget/menus_widget.dart'; import 'package:mohem_flutter_app/ui/landing/widget/services_widget.dart'; import 'package:mohem_flutter_app/widgets/circular_avatar.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:provider/provider.dart'; -import 'package:shimmer/shimmer.dart'; - -import '../../main.dart'; class DashboardScreen extends StatefulWidget { DashboardScreen({Key? key}) : super(key: key); @@ -36,14 +26,14 @@ class DashboardScreen extends StatefulWidget { } class _DashboardScreenState extends State { - late var data; + late DashboardProviderModel data; @override void initState() { super.initState(); data = Provider.of(context, listen: false); data.fetchAttendanceTracking(); - data.fetchWorkListCounter(); + data.fetchWorkListCounter(context); data.fetchMissingSwipe(); data.fetchLeaveTicketBalance(); // data.fetchMenuEntries(); @@ -129,100 +119,101 @@ class _DashboardScreenState extends State { children: [ Expanded( child: AspectRatio( - aspectRatio: 159 / 159, - child: Consumer( - builder: (context, model, child) { - return (model.isAttendanceTrackingLoading - ? GetAttendanceTrackingShimmer() - : Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), - gradient: const LinearGradient(transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomRight, colors: [ - MyColors.gradiantEndColor, - MyColors.gradiantStartColor, - ]), - ), - child: Stack( - alignment: Alignment.center, - children: [ - if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true), - if (model.isTimeRemainingInSeconds == 0) "01-02-2022".toText12(color: Colors.white), - if (model.isTimeRemainingInSeconds != 0) - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 9.height, - CountdownTimer( - endTime: model.endTime, - onEnd: null, - endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), - textStyle: TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold), - ), - LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), - 9.height, - const ClipRRect( - borderRadius: BorderRadius.all( - Radius.circular(20), - ), - child: LinearProgressIndicator( - value: 0.7, - minHeight: 8, - valueColor: const AlwaysStoppedAnimation(Colors.white), - backgroundColor: const Color(0xff196D73), - ), - ), - ], - ), - ], - ).paddingOnly(top: 12, right: 15, left: 12), - ), - Row( + aspectRatio: 159 / 159, + child: Consumer( + builder: (context, model, child) { + return (model.isAttendanceTrackingLoading + ? GetAttendanceTrackingShimmer() + : Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + gradient: const LinearGradient(transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ + MyColors.gradiantEndColor, + MyColors.gradiantStartColor, + ]), + ), + child: Stack( + alignment: Alignment.center, + children: [ + if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: Column( + LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true), + if (model.isTimeRemainingInSeconds == 0) "01-02-2022".toText12(color: Colors.white), + if (model.isTimeRemainingInSeconds != 0) + Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - LocaleKeys.checkIn.tr().toText12(color: Colors.white), - (model.isTimeRemainingInSeconds == 0 ? "--:--" : "09:00").toText14(color: Colors.white, isBold: true), - 4.height + 9.height, + CountdownTimer( + endTime: model.endTime, + onEnd: null, + endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), + textStyle: TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold), + ), + LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), + 9.height, + const ClipRRect( + borderRadius: BorderRadius.all( + Radius.circular(20), + ), + child: LinearProgressIndicator( + value: 0.7, + minHeight: 8, + valueColor: const AlwaysStoppedAnimation(Colors.white), + backgroundColor: const Color(0xff196D73), + ), + ), ], - ).paddingOnly(left: 12), - ), - Container( - width: 45, - height: 45, - padding: const EdgeInsets.only(left: 14, right: 14), - decoration: const BoxDecoration( - color: Color(0xff259EA4), - borderRadius: BorderRadius.only( - bottomRight: Radius.circular(15), - ), ), - child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/play.svg" : "assets/images/stop.svg"), - ), ], - ), - ], - ), - ], - ), - ).onPress(() { - Navigator.pushNamed(context, AppRoutes.todayAttendance); - })) - .animatedSwither(); - }, - )), + ).paddingOnly(top: 12, right: 15, left: 12), + ), + Row( + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.checkIn.tr().toText12(color: Colors.white), + (model.isTimeRemainingInSeconds == 0 ? "--:--" : "09:00").toText14(color: Colors.white, isBold: true), + 4.height + ], + ).paddingOnly(left: 12), + ), + Container( + width: 45, + height: 45, + padding: const EdgeInsets.only(left: 14, right: 14), + decoration: const BoxDecoration( + color: Color(0xff259EA4), + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(15), + ), + ), + child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/play.svg" : "assets/images/stop.svg"), + ), + ], + ), + ], + ), + ], + ), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.todayAttendance); + })) + .animatedSwither(); + }, + ), + ), ), 9.width, Expanded( diff --git a/lib/ui/landing/today_attendance_screen.dart b/lib/ui/landing/today_attendance_screen.dart index ac42dd1..b2ab7aa 100644 --- a/lib/ui/landing/today_attendance_screen.dart +++ b/lib/ui/landing/today_attendance_screen.dart @@ -83,7 +83,7 @@ class _TodayAttendanceScreenState extends State { padding: EdgeInsets.only(left: 31, right: 31, top: 31, bottom: 16), decoration: BoxDecoration( borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), - gradient: const LinearGradient(transform: GradientRotation(.64), begin: Alignment.topRight, end: Alignment.bottomRight, colors: [ + gradient: const LinearGradient(transform: GradientRotation(.64), begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ MyColors.gradiantEndColor, MyColors.gradiantStartColor, ]), @@ -156,7 +156,7 @@ class _TodayAttendanceScreenState extends State { padding: const EdgeInsets.only(left: 10, right: 10, top: 14, bottom: 14), decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), - gradient: const LinearGradient(transform: GradientRotation(.64), begin: Alignment.topRight, end: Alignment.bottomRight, colors: [ + gradient: const LinearGradient(transform: GradientRotation(.64), begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ MyColors.gradiantEndColor, MyColors.gradiantStartColor, ]), diff --git a/lib/ui/landing/widget/menus_widget.dart b/lib/ui/landing/widget/menus_widget.dart index 4d13775..5d73f0a 100644 --- a/lib/ui/landing/widget/menus_widget.dart +++ b/lib/ui/landing/widget/menus_widget.dart @@ -14,7 +14,7 @@ class MenusWidget extends StatelessWidget { Widget build(BuildContext context) { List namesColor = [0xff125765, 0xff239D8F, 0xff2BB8A8, 0xff1D92AA]; - return Consumer(builder: (context, data, child) { + return Consumer(builder: (cxt, data, child) { return GridView( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 2 / 2, crossAxisSpacing: 9, mainAxisSpacing: 9), padding: EdgeInsets.zero, @@ -24,7 +24,7 @@ class MenusWidget extends StatelessWidget { children: [ data.isWorkListLoading ? MenuShimmer().onPress(() { - data.fetchWorkListCounter(); + data.fetchWorkListCounter(context, showLoading: true); }) : Container( decoration: BoxDecoration( @@ -46,13 +46,13 @@ class MenusWidget extends StatelessWidget { ) ], ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), - ).onPress(() { - // Navigator.pushNamed(context, AppRoutes.workList); - data.fetchWorkListCounter(); + ).onPress(() async { + //await data.fetchWorkListCounter(context, showLoading: true); + Navigator.pushNamed(context, AppRoutes.workList); }), data.isMissingSwipeLoading ? MenuShimmer().onPress(() { - data.fetchWorkListCounter(); + data.fetchWorkListCounter(context); }) : Container( decoration: BoxDecoration( @@ -79,7 +79,7 @@ class MenusWidget extends StatelessWidget { }), data.isLeaveTicketBalanceLoading ? MenuShimmer().onPress(() { - data.fetchWorkListCounter(); + data.fetchWorkListCounter(context); }) : Container( decoration: BoxDecoration( @@ -106,7 +106,7 @@ class MenusWidget extends StatelessWidget { }), data.isLeaveTicketBalanceLoading ? MenuShimmer().onPress(() { - data.fetchWorkListCounter(); + data.fetchWorkListCounter(context); }) : Container( decoration: BoxDecoration( diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 533e9ee..d98fda8 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -69,12 +69,12 @@ class _LoginScreenState extends State { } String? firebaseToken; - + GetMobileLoginInfoListModel? loginInfo; Future checkFirebaseToken() async { try { Utils.showLoading(context); firebaseToken = await _firebaseMessaging.getToken(); - GetMobileLoginInfoListModel? loginInfo = await LoginApiClient().getMobileLoginInfoNEW(firebaseToken ?? "", Platform.isAndroid ? "android" : "ios"); + loginInfo = await LoginApiClient().getMobileLoginInfoNEW(firebaseToken ?? "", Platform.isAndroid ? "android" : "ios"); if (loginInfo == null) { Utils.hideLoading(context); print("Device token not found"); @@ -112,7 +112,7 @@ class _LoginScreenState extends State { } Utils.hideLoading(context); if (_autoLogin) { - Navigator.pushNamed(context, AppRoutes.verifyLastLogin); + Navigator.pushNamed(context, AppRoutes.verifyLastLogin, arguments: loginInfo); } else { Navigator.pushNamed(context, AppRoutes.verifyLogin, arguments: "$firebaseToken"); } @@ -127,8 +127,8 @@ class _LoginScreenState extends State { @override Widget build(BuildContext context) { - username.text="15153"; - password.text="Riyadh@1234"; + username.text = "15153"; + password.text = "a123a123a123"; return Scaffold( body: Column( children: [ diff --git a/lib/ui/login/verify_last_login_screen.dart b/lib/ui/login/verify_last_login_screen.dart index 282c7a5..52ec751 100644 --- a/lib/ui/login/verify_last_login_screen.dart +++ b/lib/ui/login/verify_last_login_screen.dart @@ -7,6 +7,7 @@ import 'package:local_auth/local_auth.dart'; import 'package:mohem_flutter_app/api/login_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/date_uitl.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/dialogs/otp_dialog.dart'; @@ -16,6 +17,7 @@ import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/models/basic_member_information_model.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart'; +import 'package:mohem_flutter_app/models/get_mobile_login_info_list_model.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; class VerifyLastLoginScreen extends StatefulWidget { @@ -30,6 +32,7 @@ class VerifyLastLoginScreen extends StatefulWidget { class _VerifyLastLoginScreenState extends State { final LocalAuthentication auth = LocalAuthentication(); List _availableBioMetricType = []; + GetMobileLoginInfoListModel? mobileLoginInfoListModel; @override void initState() { @@ -40,6 +43,7 @@ class _VerifyLastLoginScreenState extends State { @override Widget build(BuildContext context) { + mobileLoginInfoListModel ??= ModalRoute.of(context)!.settings.arguments as GetMobileLoginInfoListModel; String empName = AppState().isArabic(context) ? AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEAr! : AppState().memberInformationList!.eMPLOYEEDISPLAYNAMEEn!; return Scaffold( appBar: AppBar( @@ -64,7 +68,7 @@ class _VerifyLastLoginScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.welcomeBack.tr().toText12(), - empName.toText24(isBold: true), + mobileLoginInfoListModel!.employeeName!.toText24(isBold: true), 10.height, LocaleKeys.wouldYouLikeToLoginWithCurrentUsername.tr().toText16(), Container( @@ -88,14 +92,7 @@ class _VerifyLastLoginScreenState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ LocaleKeys.lastLoginDetails.tr().toText16(), - // Text( - // user.editedOn != null - // ? DateUtil.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(user.editedOn)) - // : user.createdOn != null - // ? DateUtil.getDayMonthYearDateFormatted(DateUtil.convertStringToDate(user.createdOn)) - // : '--', - // style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.48), - // ), + DateUtil.formatDateToDate(DateUtil.convertStringToDate(mobileLoginInfoListModel!.editedOn!), false).toText12(), ], ), Row( @@ -103,24 +100,9 @@ class _VerifyLastLoginScreenState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ LocaleKeys.verificationType.tr().toText10(color: MyColors.grey57Color), - Text( - "SMS", - // " " + getType(user.logInType, context), - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xff2B353E), - ), - ), + getVerificationType(mobileLoginInfoListModel!.loginType!).toText12(), Expanded(child: SizedBox()), - // Text( - // user.editedOn != null - // ? DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(user.editedOn), false) - // : user.createdOn != null - // ? DateUtil.formatDateToTimeLang(DateUtil.convertStringToDate(user.createdOn), false) - // : '--', - // style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xff575757), letterSpacing: -0.48), - // ), + DateUtil.formatDateToTime(DateUtil.convertStringToDate(mobileLoginInfoListModel!.editedOn!)).toText12(), ], ) ], @@ -201,419 +183,18 @@ class _VerifyLastLoginScreenState extends State { if (mounted) setState(() {}); } - // authenticateUser(int type, {int isActive}) { - // GifLoaderDialogUtils.showMyDialog(context); - // if (type == 2 || type == 3) { - // fingrePrintBefore = type; - // } - // this.selectedOption = fingrePrintBefore != null ? fingrePrintBefore : type; - // - // switch (type) { - // case 1: - // this.loginWithSMS(type); - // break; - // case 2: - // this.loginWithFingurePrintFace(type, isActive); - // break; - // case 3: - // this.loginWithFingurePrintFace(type, isActive); - // break; - // case 4: - // this.loginWithSMS(type); - // break; - // default: - // break; - // } - // sharedPref.setInt(LAST_LOGIN, this.selectedOption); //this.cs.sharedService.setStorage(this.selectedOption, AuthenticationService.LAST_LOGIN); - // } -// -// loginWithSMS(type) { -// //if (!el.disabled) { -// if (this.user != null && this.registerd_data == null) { -// this.checkUserAuthentication(type); -// } else { -// if (this.loginTokenID != null) { -// // Future.delayed(Duration(seconds: 1), () { -// this.sendActivationCode(type); -// // }); -// } else { -// this.checkUserAuthentication(type); -// } -// } -// } -// -// checkUserAuthentication(type) { -// showLoader(true); -// var req = getCommonRequest(type: type); -// req.logInTokenID = ""; -// -// var request = CheckPatientAuthenticationReq.fromJson(req.toJson()); -// -// sharedPref.setObject(REGISTER_DATA_FOR_REGISTER, request); -// authService -// .checkPatientAuthentication(request) -// .then((value) => { -// GifLoaderDialogUtils.hideDialog(context), -// if (value['isSMSSent']) -// { -// sharedPref.setString(LOGIN_TOKEN_ID, value['LogInTokenID']), -// this.loginTokenID = value['LogInTokenID'], -// sharedPref.setObject(REGISTER_DATA_FOR_LOGIIN, request), -// // Future.delayed(Duration(seconds: 1), () { -// this.sendActivationCode(type) -// // }) -// } -// else -// { -// if (value['IsAuthenticated']) {this.checkActivationCode()} -// } -// }) -// .catchError((err) { -// print(err); -// GifLoaderDialogUtils.hideDialog(context); -// }); -// } -// -// sendActivationCode(type) async { -// var request = this.getCommonRequest(type: type); -// request.sMSSignature = await SMSOTP.getSignature(); -// GifLoaderDialogUtils.showMyDialog(context); -// if (healthId != null) { -// // final DateFormat dateFormat = DateFormat('MM/dd/yyyy'); -// // final DateFormat dateFormat2 = DateFormat('dd/MM/yyyy'); -// request.dob = dob; //isHijri == 1 ? dob : dateFormat2.format(dateFormat.parse(dob)); -// request.healthId = healthId; -// request.isHijri = isHijri; -// await this.authService.sendActivationCodeRegister(request).then((result) { -// GifLoaderDialogUtils.hideDialog(context); -// if (result != null && result['isSMSSent'] == true) { -// this.startSMSService(type); -// } -// }).catchError((r) { -// GifLoaderDialogUtils.hideDialog(context); -// }); -// } else { -// request.dob = ""; -// request.healthId = ""; -// request.isHijri = 0; -// await this.authService.sendActivationCode(request).then((result) { -// GifLoaderDialogUtils.hideDialog(context); -// if (result != null && result['isSMSSent'] == true) { -// this.startSMSService(type); -// } -// }).catchError((r) { -// GifLoaderDialogUtils.hideDialog(context); -// }); -// } -// } -// -// var tempType; -// -// startSMSService(type) { -// tempType = type; -// new SMSOTP( -// context, -// type, -// this.mobileNumber, -// (value) { -// this.checkActivationCode(value: value); -// }, -// () => { -// Navigator.pop(context), -// }, -// ).displayDialog(context); -// } -// -// loginWithFingurePrintFace(type, int isActive) async { -// if (isActive == 1 || isActive == 0) { -// const iosStrings = -// const IOSAuthMessages(cancelButton: 'cancel', goToSettingsButton: 'settings', goToSettingsDescription: 'Please set up your Touch ID.', lockOut: 'Please reenable your Touch ID'); -// -// try { -// authenticated = await auth.authenticateWithBiometrics(localizedReason: 'Scan your fingerprint to authenticate', useErrorDialogs: true, stickyAuth: true, iOSAuthStrings: iosStrings); -// } on PlatformException catch (e) { -// GifLoaderDialogUtils.hideDialog(context); -// AppToast.showErrorToast(message: 'Please enable your Touch or Face ID'); -// } -// -// if (authenticated == true) { -// // if (user != null && (user.logInType == 2 || user.logInType == 3)) { -// // this.checkActivationCode(); -// // } else { -// -// var request = this.getCommonRequest(type: type); -// this.getMobileInfo(request); -// //} -// } -// } -// } -// -// getMobileInfo(request) { -// // GifLoaderDialogUtils.showMyDialog(context); -// this.authService.getLoginInfo(request).then((result) { -// GifLoaderDialogUtils.hideDialog(context); -// if (result['SMSLoginRequired'] == false) { -// this.loginTokenID = result['LogInTokenID']; -// this.patientOutSA = result['PatientOutSA']; -// // sms for register the biometric -// if (result['isSMSSent']) { -// setState(() { -// isMoreOption = true; -// this.onlySMSBox = true; -// // this.fingrePrintBefore = true; -// }); -// //this.button(); -// } else { -// setDefault(); -// checkActivationCode(); -// } -// } else { -// if (result['IsAuthenticated'] == true) { -// setState(() { -// isMoreOption = true; -// this.onlySMSBox = true; -// // this.fingrePrintBefore = true; -// }); -// } -// } -// }).catchError((err) { -// GifLoaderDialogUtils.hideDialog(context); -// print(err); -// }); -// } -// -// setDefault() async { -// if (await sharedPref.getObject(IMEI_USER_DATA) != null) user = SelectDeviceIMEIRES.fromJson(await sharedPref.getObject(IMEI_USER_DATA)); -// -// if (await sharedPref.getObject(REGISTER_DATA_FOR_LOGIIN) != null) { -// isMoreOption = true; -// this.registerd_data = CheckPatientAuthenticationReq.fromJson(await sharedPref.getObject(REGISTER_DATA_FOR_LOGIIN)); -// } -// -// this.mobileNumber = this.registerd_data != null ? this.registerd_data.patientMobileNumber : int.parse(this.user.mobile); -// this.zipCode = this.registerd_data != null -// ? this.registerd_data.zipCode -// : this.user.outSA == true -// ? "971" -// : "966"; -// this.patientOutSA = this.registerd_data != null -// ? this.registerd_data.zipCode == "966" -// ? 0 -// : 1 -// : this.user.outSA; -// if (this.registerd_data != null) { -// this.loginTokenID = await sharedPref.getString(LOGIN_TOKEN_ID); -// this.loginType = this.registerd_data.searchType; -// } -// var nhic = await sharedPref.getObject(NHIC_DATA); -// if (nhic != null) { -// final DateFormat dateFormat = DateFormat('MM/dd/yyyy'); -// final DateFormat dateFormat2 = DateFormat('dd/MM/yyyy'); -// dob = nhic['IsHijri'] ? nhic['DateOfBirth'] : dateFormat2.format(dateFormat.parse(nhic['DateOfBirth'])); -// -// isHijri = nhic['IsHijri'] ? 1 : 0; -// healthId = nhic['HealthId']; -// } -// this.deviceToken = await sharedPref.getString(PUSH_TOKEN); -// this.lastLogin = await sharedPref.getInt(LAST_LOGIN) != null -// ? await sharedPref.getInt(LAST_LOGIN) -// : user != null -// ? user.logInType -// : null; -// -// //this.cs.sharedService.getStorage(AuthenticationService.LAST_LOGIN); -// } -// -// getCommonRequest({type}) { -// var request = SendActivationRequest(); -// request.patientMobileNumber = this.mobileNumber; -// request.mobileNo = '0' + this.mobileNumber.toString(); -// request.deviceToken = this.deviceToken; -// request.projectOutSA = this.patientOutSA == true ? true : false; -// request.loginType = this.selectedOption; -// request.oTPSendType = type == 1 ? type : 2; //this.selectedOption == 1 ? 1 : 2; -// request.zipCode = this.zipCode; -// -// request.logInTokenID = this.loginTokenID ?? ""; -// -// if (this.registerd_data != null) { -// request.searchType = this.registerd_data.searchType != null ? this.registerd_data.searchType : 1; -// request.patientID = this.registerd_data.patientID != null ? this.registerd_data.patientID : 0; -// request.patientIdentificationID = request.nationalID = this.registerd_data.patientIdentificationID != null ? this.registerd_data.patientIdentificationID : '0'; -// -// request.isRegister = this.registerd_data.isRegister; -// } else { -// request.searchType = request.searchType != null ? request.searchType : 2; -// request.patientID = this.user.patientID != null ? this.user.patientID : 0; -// request.nationalID = request.nationalID != null ? request.nationalID : '0'; -// request.patientIdentificationID = request.patientIdentificationID != null ? request.patientIdentificationID : '0'; -// request.isRegister = false; -// } -// request.deviceTypeID = request.searchType; -// return request; -// } -// -// // checkActivationCode({value}) async { -// // // Navigator.pop(context); -// // GifLoaderDialogUtils.showMyDialog(context); -// // var request = this.getCommonRequest().toJson(); -// // dynamic res; -// // if (healthId != null) { -// // request['DOB'] = dob; -// // request['HealthId'] = healthId; -// // request['IsHijri'] = isHijri; -// // -// // authService -// // .checkActivationCodeRegister(request, value) -// // .then((result) => { -// // res = result, -// // if (result is Map) -// // { -// // result = CheckActivationCode.fromJson(result), -// // if (this.registerd_data != null && this.registerd_data.isRegister == true) -// // { -// // widget.changePageViewIndex(1), -// // Navigator.popUntil(context, (route) => Utils.route(route, equalsTo: RegisterNew)), -// // } -// // } -// // else -// // { -// // // Navigator.of(context).pop(), -// // GifLoaderDialogUtils.hideDialog(context), -// // Future.delayed(Duration(seconds: 1), () { -// // AppToast.showErrorToast(message: result); -// // }), -// // } -// // }) -// // .catchError((err) { -// // print(err); -// // GifLoaderDialogUtils.hideDialog(context); -// // Future.delayed(Duration(seconds: 1), () { -// // AppToast.showErrorToast(message: err); -// // startSMSService(tempType); -// // }); -// // }); -// // } else { -// // authService -// // .checkActivationCode(request, value) -// // .then((result) => { -// // res = result, -// // if (result is Map) -// // { -// // result = CheckActivationCode.fromJson(result), -// // if (this.registerd_data != null && this.registerd_data.isRegister == true) -// // { -// // widget.changePageViewIndex(1), -// // Navigator.popUntil(context, (route) => Utils.route(route, equalsTo: RegisterNew)), -// // } -// // else -// // { -// // sharedPref.remove(FAMILY_FILE), -// // result.list.isFamily = false, -// // userData = result.list, -// // sharedPref.setString(BLOOD_TYPE, result.patientBloodType), -// // authenticatedUserObject.user = result.list, -// // projectViewModel.setPrivilege(privilegeList: res), -// // sharedPref.setObject(MAIN_USER, result.list), -// // sharedPref.setObject(USER_PROFILE, result.list), -// // loginTokenID = result.logInTokenID, -// // sharedPref.setObject(LOGIN_TOKEN_ID, result.logInTokenID), -// // sharedPref.setString(TOKEN, result.authenticationTokenID), -// // checkIfUserAgreedBefore(result), -// // } -// // } -// // else -// // { -// // // // Navigator.of(context).pop(), -// // // GifLoaderDialogUtils.hideDialog(context), -// // // Future.delayed(Duration(seconds: 1), () { -// // // AppToast.showErrorToast(message: result); -// // // startSMSService(tempType); -// // // }), -// // } -// // }) -// // .catchError((err) { -// // // print(err); -// // // GifLoaderDialogUtils.hideDialog(context); -// // // Future.delayed(Duration(seconds: 1), () { -// // // AppToast.showErrorToast(message: err); -// // // startSMSService(tempType); -// // // }); -// // }); -// // } -// // } -// -// // checkIfUserAgreedBefore(CheckActivationCode result) { -// // if (result.isNeedUserAgreement == true) { -// // //move to agreement page. -// // } else { -// // goToHome(); -// // } -// // } -// -// insertIMEI() { -// authService.insertDeviceImei(selectedOption).then((value) => {}).catchError((err) { -// print(err); -// }); -// } -// -// // getToDoCount() { -// // toDoProvider.setState(0, true, "0"); -// // ClinicListService service = new ClinicListService(); -// // service.getActiveAppointmentNo(context).then((res) { -// // if (res['MessageStatus'] == 1) { -// // toDoProvider.setState(res['AppointmentActiveNumber'], true, "0"); -// // } else {} -// // }).catchError((err) { -// // print(err); -// // }); -// // } -// -// // goToHome() async { -// // authenticatedUserObject.isLogin = true; -// // appointmentRateViewModel.isLogin = true; -// // projectViewModel.isLogin = true; -// // projectViewModel.user = authenticatedUserObject.user; -// // await authenticatedUserObject.getUser(getUser: true); -// // -// // // getToDoCount(); -// // -// // appointmentRateViewModel -// // .getIsLastAppointmentRatedList() -// // .then((value) => { -// // getToDoCount(), -// // GifLoaderDialogUtils.hideDialog(AppGlobal.context), -// // if (appointmentRateViewModel.isHaveAppointmentNotRate) -// // { -// // Navigator.pushAndRemoveUntil( -// // context, -// // FadePage( -// // page: RateAppointmentDoctor(), -// // ), -// // (r) => false) -// // } -// // else -// // { -// // Navigator.pushAndRemoveUntil( -// // context, -// // FadePage( -// // page: LandingPage(), -// // ), -// // (r) => false) -// // }, -// // insertIMEI() -// // }) -// // .catchError((err) { -// // print(err); -// // }); -// // } -// -// loading(flag) { -// // setState(() { -// // isLoading = flag; -// // }); -// } -// + String getVerificationType(int type) { + if (type == 1) { + LocaleKeys.sms.tr(); + } else if (type == 2) { + return LocaleKeys.fingerPrint.tr(); + } else if (type == 3) { + return LocaleKeys.face.tr(); + } else if (type == 4) { + return LocaleKeys.whatsapp.tr(); + } + return ""; + } Future loginWithFaceIDAndBiometrics() async { IOSAuthMessages iosStrings = @@ -654,8 +235,8 @@ class _VerifyLastLoginScreenState extends State { } await LoginApiClient().checkMobileAppVersion(); await LoginApiClient().memberLogin(AppState().getUserName!, AppState().password!); - BasicMemberInformationModel? memberInformationModel = await LoginApiClient() - .mohemmSendActivationCodeByOTPNotificationType(checkBiometricIsAvailable(BiometricType.fingerprint) ? 1 : 0, AppState().memberLoginList?.pMOBILENUMBER, _flag, AppState().getUserName); + BasicMemberInformationModel? memberInformationModel = await LoginApiClient().mohemmSendActivationCodeByOTPNotificationType( + checkBiometricIsAvailable(BiometricType.fingerprint) ? 1 : 0, AppState().memberLoginList?.pMOBILENUMBER, _flag, AppState().getUserName); Utils.hideLoading(context); OtpDialog( context, diff --git a/lib/ui/work_list/missing_swipe/missing_swipe_screen.dart b/lib/ui/work_list/missing_swipe/missing_swipe_screen.dart index 4af620e..0a27afc 100644 --- a/lib/ui/work_list/missing_swipe/missing_swipe_screen.dart +++ b/lib/ui/work_list/missing_swipe/missing_swipe_screen.dart @@ -1,56 +1,85 @@ +import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; -import 'package:mohem_flutter_app/ui/app_bar.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/ui/work_list/missing_swipe/fragments/actions_fragment.dart'; import 'package:mohem_flutter_app/ui/work_list/missing_swipe/fragments/attachments_fragment.dart'; import 'package:mohem_flutter_app/ui/work_list/missing_swipe/fragments/info_fragments.dart'; import 'package:mohem_flutter_app/ui/work_list/missing_swipe/fragments/request_fragment.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; -import 'package:mohem_flutter_app/extensions/int_extensions.dart'; -class MissingSwipeScreen extends StatelessWidget { +class MissingSwipeScreen extends StatefulWidget { + MissingSwipeScreen({Key? key}) : super(key: key); + + @override + _MissingSwipeScreenState createState() { + return _MissingSwipeScreenState(); + } +} + +class _MissingSwipeScreenState extends State { + int tabIndex = 0; + PageController controller = PageController(); + bool showFabOptions = false; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + @override Widget build(BuildContext context) { - return DefaultTabController( - length: 4, - child: Scaffold( - appBar: appBar(context, title: "Missing Swipe Request"), - body: Container( - width: double.infinity, - height: double.infinity, - child: Column( + return Scaffold( + appBar: AppBarWidget(context, title: LocaleKeys.details.tr()), + backgroundColor: Colors.white, + body: Stack( + children: [ + Column( children: [ Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.only(bottomLeft: Radius.circular(20), bottomRight: Radius.circular(20)), - gradient: LinearGradient(transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomRight, colors: [ - MyColors.gradiantEndColor, - MyColors.gradiantStartColor, - ]), + padding: const EdgeInsets.only(left: 21, right: 21, top: 16, bottom: 16), + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + ), + gradient: LinearGradient( + transform: GradientRotation(.83), + begin: Alignment.topRight, + end: Alignment.bottomLeft, + colors: [ + MyColors.gradiantEndColor, + MyColors.gradiantStartColor, + ], + ), ), - clipBehavior: Clip.antiAlias, - child: TabBar( - indicatorColor: Colors.white, - labelColor: Colors.white, - tabs: [ - Tab( - text: "Request", - ), - Tab( - text: "Actions", - ), - Tab( - text: "Attachments", - ), - Tab( - text: "Info.", - ), + child: Row( + children: [ + myTab("Request", 0), + myTab("Actions", 1), + myTab("Attachments", 2), + myTab("Info.", 3), ], ), ), Expanded( - child: TabBarView( + child: PageView( + controller: controller, + onPageChanged: (pageIndex) { + setState(() { + tabIndex = pageIndex; + }); + }, children: [ RequestFragment(), ActionsFragment(), @@ -60,39 +89,140 @@ class MissingSwipeScreen extends StatelessWidget { ), ), Container( - width: double.infinity, - height: 60, - padding: EdgeInsets.only(left: 21, right: 21), + padding: const EdgeInsets.only(top: 14, bottom: 14, left: 21, right: 21), + decoration: const BoxDecoration( + color: Colors.white, + border: Border( + top: BorderSide( + color: MyColors.lightGreyEFColor, + width: 1.0, + ), + ), + ), child: Row( children: [ - Expanded( - child: DefaultButton( - "Reject", - () {}, - colors: [ - Color(0xffEB8C90), - Color(0xffDE6C70), - ], - ), - ), - 12.width, - Expanded( - child: DefaultButton( - "Approve", - () {}, - colors: [ - Color(0xff32D892), - Color(0xff1AB170), - ], + DefaultButton( + LocaleKeys.reject.tr(), + () {}, + colors: const [ + Color(0xffEB8C90), + Color(0xffDE6C70), + ], + ).expanded, + 8.width, + DefaultButton( + LocaleKeys.approve.tr(), + () {}, + colors: const [ + Color(0xff32D892), + Color(0xff1AB170), + ], + ).expanded, + 8.width, + Container( + height: 43, + width: 43, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: MyColors.lightGreyE6Color, ), - ), + child: Icon(showFabOptions ? Icons.more_vert_rounded : Icons.more_horiz_rounded, color: MyColors.darkIconColor), + ).onPress(() { + setState(() { + showFabOptions = true; + }); + }) ], ), ) ], ), - ), + IgnorePointer( + ignoring: !showFabOptions, + child: AnimatedOpacity( + opacity: showFabOptions ? 1 : 0, + duration: const Duration(milliseconds: 250), + child: Container( + padding: const EdgeInsets.only(left: 21, right: 21, bottom: 75), + width: double.infinity, + height: double.infinity, + color: Colors.white.withOpacity(.67), + alignment: Alignment.bottomRight, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + myFab("Skip", "assets/images/skip.svg"), + 12.height, + myFab("Request Info", "assets/images/request_info.svg"), + 12.height, + myFab("Delegate", "assets/images/delegate.svg"), + ], + ), + ), + ).onPress(() { + setState(() { + showFabOptions = false; + }); + }), + ), + ], ), ); } + + Widget myTab(String title, int index) { + bool isSelected = (index == tabIndex); + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + title.toText12(color: isSelected ? Colors.white : Colors.white.withOpacity(.74), isCenter: true), + 4.height, + Container( + height: 8, + width: 8, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: isSelected ? Colors.white : Colors.transparent, + ), + ).onPress(() { + setState(() { + showFabOptions = true; + }); + }) + ], + ).onPress(() { + controller.jumpToPage(index); + }).expanded; + } + + Widget myFab(String title, String icon) { + return Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + title.toText16(), + 14.width, + Container( + height: 43, + width: 43, + padding: const EdgeInsets.all(12), + decoration: const BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + transform: GradientRotation(.46), + begin: Alignment.topRight, + end: Alignment.bottomLeft, + colors: [ + MyColors.gradiantEndColor, + MyColors.gradiantStartColor, + ], + ), + ), + child: SvgPicture.asset(icon), + ) + ], + ); + } } diff --git a/lib/ui/work_list/work_list_screen.dart b/lib/ui/work_list/work_list_screen.dart index 06a545c..1e46783 100644 --- a/lib/ui/work_list/work_list_screen.dart +++ b/lib/ui/work_list/work_list_screen.dart @@ -1,29 +1,116 @@ import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:mohem_flutter_app/api/worklist/worklist_api_client.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/date_uitl.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; -import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; -import 'package:mohem_flutter_app/ui/app_bar.dart'; -import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; +import 'package:mohem_flutter_app/models/worklist_item_type_model.dart'; +import 'package:mohem_flutter_app/models/worklist_response_model.dart'; +import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; +import 'package:provider/provider.dart'; class WorkListScreen extends StatefulWidget { + WorkListScreen({Key? key}) : super(key: key); + @override - State createState() => _WorkListScreenState(); + _WorkListScreenState createState() { + return _WorkListScreenState(); + } } class _WorkListScreenState extends State { + List workListItemTypes = [ + WorkListItemTypeModelData( + value: 0, name: 'HR', fullName: 'Human Resource', active: false, color: [Color(0xff32D892), Color(0xff1AB170)], icon: "assets/images/miss_swipe.svg", key: 'HRSSA', disable: false), + WorkListItemTypeModelData( + value: 0, name: 'MO', fullName: 'Move Order', active: false, color: [Color(0xff58DCFA), Color(0xff3CB9D5)], icon: "assets/images/miss_swipe.svg", key: 'INVMOA', disable: false), + WorkListItemTypeModelData( + value: 0, name: 'PR', fullName: 'Purchase Requisition', active: false, color: [Color(0xff48EACF), Color(0xff3DCAB3)], icon: "assets/images/miss_swipe.svg", key: 'REQAPPRV', disable: false), + WorkListItemTypeModelData( + value: 0, name: 'PO', fullName: 'Purchase Order', active: false, color: [Color(0xff5099E3), Color(0xff3670AA)], icon: "assets/images/miss_swipe.svg", key: 'POAPPRV', disable: false), + WorkListItemTypeModelData( + value: 0, name: 'ITG', fullName: 'ITG Forms', active: false, color: [Color(0xffEB8C90), Color(0xffDE6C70)], icon: "assets/images/miss_swipe.svg", key: 'ITG', disable: false), + WorkListItemTypeModelData( + value: 0, name: 'IC', fullName: 'Item Creation', active: false, color: [Color(0xff32D892), Color(0xff1AB170)], icon: "assets/images/miss_swipe.svg", key: 'INVITEM', disable: false), + WorkListItemTypeModelData( + value: 0, name: 'STAMP', fullName: 'Stamp', active: false, color: [Color(0xff32D892), Color(0xff1AB170)], icon: "assets/images/miss_swipe.svg", key: 'STAMP', disable: false), + ]; + + int workListItemIndex = 0; + + List? workList; + int pageNumber = 1; + + late DashboardProviderModel providerData; + + @override + void initState() { + super.initState(); + + providerData = Provider.of(context, listen: false); + workListItemTypes.forEach((workListElement) { + if (workListElement.key == "ITG") { + workListElement.value = providerData.itgFormsModel?.totalCount ?? 0; + } else { + var tempList = providerData.getOpenNotificationsList?.where((notificationElement) => notificationElement.itemType == workListElement.key).toList(); + if (tempList!.isNotEmpty) { + workListElement.value = tempList.first.openNtfNumber ?? 0; + } + } + }); + getWorkList(); + } + + ItgFormsModel? itgFormsModel; + int? itgRequestTypeIndex; + + void getWorkList() async { + try { + Utils.showLoading(context); + if (workListItemTypes[workListItemIndex].key == "ITG") { + itgFormsModel = await WorkListApiClient().GetITGTaskCountRequestType(); + List requestAllList = []; + for (int i = 0; i < (itgFormsModel?.requestType!.length ?? 0); i++) { + requestAllList = requestAllList + (itgFormsModel?.requestType![i].requestDetails ?? []); + } + itgFormsModel?.requestType!.insert(0, RequestType(requestDetails: requestAllList, requestTypeCode: "all", requestTypeName: "All")); + if ((itgFormsModel?.requestType?.length ?? 0) > 0) { + itgRequestTypeIndex = 0; + } + } else { + itgRequestTypeIndex = null; + workList = await WorkListApiClient().getWorkList(pageNumber, workListItemTypes[workListItemIndex].key); + } + Utils.hideLoading(context); + setState(() {}); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, null); + } + } + + @override + void dispose() { + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, - appBar: appBar( + appBar: AppBarWidget( context, title: LocaleKeys.workList.tr(), ), - body: Container( + body: SizedBox( width: double.infinity, height: double.infinity, child: Column( @@ -31,67 +118,151 @@ class _WorkListScreenState extends State { children: [ Container( width: double.infinity, - height: 2, - color: MyColors.darkWhiteColor, + height: 1, + color: MyColors.lightGreyEFColor, ), - Container( - width: double.infinity, + SizedBox( height: 40, - margin: EdgeInsets.only( - top: 21, - ), child: ListView.separated( itemBuilder: (context, index) { return Container( - padding: EdgeInsets.only( - left: 30, - right: 30, - ), + padding: const EdgeInsets.only(left: 21, right: 21, top: 8, bottom: 8), alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(6), - color: tabList[index].isSelected - ? MyColors.darkIconColor - : MyColors.lightGreyEAColor, - ), - child: tabList[index].title.toText12( - color: tabList[index].isSelected - ? MyColors.white - : MyColors.black, - ), - ); - }, - separatorBuilder: (context, index) { - return 8.width; + decoration: BoxDecoration(borderRadius: BorderRadius.circular(6), color: workListItemIndex == index ? MyColors.darkIconColor : MyColors.lightGreyEAColor), + child: ("${workListItemTypes[index].name} ${workListItemTypes[index].value > 0 ? "(${workListItemTypes[index].value})" : ""}") + .toText12(color: workListItemIndex == index ? MyColors.white : MyColors.black), + ).onPress(() { + if (workListItemIndex != index) { + workListItemIndex = index; + if (workListItemTypes[index].value == 0) { + workList = []; + } else { + workList = null; + } + setState(() {}); + if (workListItemTypes[index].value > 0) { + getWorkList(); + } + } + }); }, + separatorBuilder: (context, index) => 8.width, shrinkWrap: true, - itemCount: tabList.length, + itemCount: workListItemTypes.length, scrollDirection: Axis.horizontal, - padding: const EdgeInsets.only( - left: 21, - right: 21, + padding: const EdgeInsets.only(left: 21, right: 21), + ), + ).paddingOnly(top: 21, bottom: 21), + workListItemTypes[workListItemIndex].fullName.toSectionHeading().paddingOnly(left: 21, right: 21), + if (itgRequestTypeIndex != null) + SizedBox( + height: 40, + child: ListView.separated( + itemBuilder: (context, index) { + RequestType type = itgFormsModel!.requestType![index]; + return Container( + padding: const EdgeInsets.only(left: 21, right: 21, top: 8, bottom: 8), + alignment: Alignment.center, + decoration: BoxDecoration(borderRadius: BorderRadius.circular(30), color: itgRequestTypeIndex == index ? MyColors.darkIconColor : MyColors.lightGreyEAColor), + child: ("${type.requestTypeName}").toText12(color: itgRequestTypeIndex == index ? MyColors.white : MyColors.black), + ).onPress(() { + if (itgRequestTypeIndex != index) { + itgRequestTypeIndex = index; + setState(() {}); + } + }); + }, + separatorBuilder: (context, index) => 8.width, + shrinkWrap: true, + itemCount: itgFormsModel?.requestType?.length ?? 0, + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.only(left: 21, right: 21), ), + ).paddingOnly(top: 16, bottom: 16), + itgRequestTypeIndex != null + ? Expanded( + child: ListView.separated( + physics: BouncingScrollPhysics(), + itemBuilder: (context, index) { + return itgRowItem(workListItemTypes[workListItemIndex], itgFormsModel!.requestType![itgRequestTypeIndex!].requestDetails![index]); + }, + separatorBuilder: (context, index) => 12.height, + itemCount: itgFormsModel!.requestType![itgRequestTypeIndex!].requestDetails?.length ?? 0, + padding: EdgeInsets.only(top: 16, left: 21, right: 21), + ), + ) + : Expanded( + child: workList != null + ? ((workList!).isEmpty + ? "No History Available".toText16().center + : ListView.separated( + physics: BouncingScrollPhysics(), + itemBuilder: (context, index) { + return rowItem(workListItemTypes[workListItemIndex], workList![index]); + }, + separatorBuilder: (context, index) => 12.height, + itemCount: workList?.length ?? 0, + padding: EdgeInsets.only(top: 21, left: 21, right: 21), + )) + : const SizedBox(), + ), + ], + ), + ), + ); + } + + Widget itgRowItem(WorkListItemTypeModelData data, RequestDetails requestDetails) { + return InkWell( + onTap: () { + Navigator.pushNamed(context, AppRoutes.missingSwipe); + }, + child: Container( + width: double.infinity, + padding: const EdgeInsets.only(left: 12, right: 12, top: 10, bottom: 10), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + gradient: LinearGradient(transform: GradientRotation(.218), begin: Alignment.topRight, end: Alignment.bottomRight, colors: data.color), ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [SvgPicture.asset("assets/images/miss_swipe.svg", width: 20, height: 20, color: Colors.white), 2.height, data.name.toText10(color: Colors.white)], + ).paddingAll(6), ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - LocaleKeys.human.toText12(), - LocaleKeys.resources.tr().toText24(isBold: true), - ], - ).paddingOnly(top: 24, left: 21, right: 21), - 24.height, + 8.width, Expanded( - child: ListView.separated( - itemBuilder: (context, index) { - return rowItem(typesList[index]); - }, - separatorBuilder: (context, index) { - return 12.height; - }, - itemCount: typesList.length, - padding: EdgeInsets.only(left: 21, right: 21), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + requestDetails.title!.toText12(color: MyColors.grey57Color), + 10.height, + Row( + children: [ + DateUtil.formatDateToDate(DateUtil.convertStringToDate(requestDetails.modifiedDate!), false).toText10(color: MyColors.lightTextColor).expanded, + SvgPicture.asset( + "assets/images/arrow_next.svg", + color: MyColors.darkIconColor, + ) + ], + ), + ], ), ), ], @@ -100,14 +271,14 @@ class _WorkListScreenState extends State { ); } - Widget rowItem(Types types) { + Widget rowItem(WorkListItemTypeModelData data, WorkListResponseModel workData) { return InkWell( - onTap: (){ + onTap: () { Navigator.pushNamed(context, AppRoutes.missingSwipe); }, child: Container( width: double.infinity, - padding: EdgeInsets.all(12), + padding: const EdgeInsets.only(left: 12, right: 12, top: 10, bottom: 10), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), @@ -126,39 +297,24 @@ class _WorkListScreenState extends State { Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), - gradient: LinearGradient( - transform: GradientRotation(.46), - begin: Alignment.topRight, - end: Alignment.bottomRight, - colors: types.colors), + gradient: LinearGradient(transform: GradientRotation(.218), begin: Alignment.topRight, end: Alignment.bottomRight, colors: data.color), ), child: Column( crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SvgPicture.asset( - "assets/images/miss_swipe.svg", - color: Colors.white, - ), - 2.height, - types.title.toText10(color: Colors.white) - ], + children: [SvgPicture.asset("assets/images/miss_swipe.svg", width: 20, height: 20, color: Colors.white), 2.height, data.name.toText10(color: Colors.white)], ).paddingAll(6), ), - 12.width, + 8.width, Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - "Missing Swipe Request".toText16(), - "Missing Swipe Request for Hussain, Mohammad has been approved" - .toText10(), - 12.height, + workData.sUBJECT!.toText12(color: MyColors.grey57Color), + 10.height, Row( children: [ - Expanded( - child: "07 Jan 2021" - .toText10(color: MyColors.lightTextColor)), + DateUtil.formatDateToDate(DateUtil.convertSimpleStringDateToDate(workData.bEGINDATE!), false).toText10(color: MyColors.lightTextColor).expanded, SvgPicture.asset( "assets/images/arrow_next.svg", color: MyColors.darkIconColor, @@ -174,33 +330,3 @@ class _WorkListScreenState extends State { ); } } - -class Tabs { - String title; - bool isSelected; - - Tabs(this.title, this.isSelected); -} - -List tabList = [ - Tabs("All", true), - Tabs("HR", false), - Tabs("MO", false), - Tabs("PR", false), - Tabs("PO", false), -]; - -class Types { - String title; - List colors; - - Types(this.title, this.colors); -} - -List typesList = [ - Types("HR", [Color(0xff32D892), Color(0xff1AB170)]), - Types("ITG", [Color(0xffEB8C90), Color(0xffDE6C70)]), - Types("PO", [Color(0xff5099E3), Color(0xff3670AA)]), - Types("PR", [Color(0xff48EACF), Color(0xff3DCAB3)]), - Types("MO", [Color(0xff58DCFA), Color(0xff3CB9D5)]), -]; diff --git a/lib/widgets/app_bar_widget.dart b/lib/widgets/app_bar_widget.dart new file mode 100644 index 0000000..f079f7f --- /dev/null +++ b/lib/widgets/app_bar_widget.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; + +AppBar AppBarWidget(BuildContext context, {required String title, bool showHomeButton = false}) { + return AppBar( + leadingWidth: 0, + // leading: GestureDetector( + // behavior: HitTestBehavior.opaque, + // onTap: Feedback.wrapForTap(() => Navigator.maybePop(context), context), + // child: const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor), + // ), + //titleSpacing: -1.44, + title: Row( + children: [ + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: Feedback.wrapForTap(() => Navigator.maybePop(context), context), + child: const Icon(Icons.arrow_back_ios, color: MyColors.darkIconColor), + ), + 4.width, + title.toText24(color: MyColors.darkTextColor, isBold: true, considerHeight: false).expanded, + ], + ), + centerTitle: false, + elevation: 0, + backgroundColor: Colors.white, + actions: [ + if (showHomeButton) + IconButton( + onPressed: () { + // Navigator.pushAndRemoveUntil( + // context, + // MaterialPageRoute(builder: (context) => LandingPage()), + // (Route route) => false, + // ); + }, + icon: const Icon(Icons.home, color: MyColors.darkIconColor), + ), + ], + ); +} From 48a261d674bc728bcd71c27aef32764bee6843cd Mon Sep 17 00:00:00 2001 From: devmirza121 Date: Wed, 2 Mar 2022 10:52:31 +0300 Subject: [PATCH 8/9] Dashboard API's 1.4 --- lib/provider/dashboard_provider_model.dart | 6 +- lib/ui/login/login_screen.dart | 2 +- pubspec.lock | 514 --------------------- pubspec.yaml | 6 +- 4 files changed, 7 insertions(+), 521 deletions(-) delete mode 100644 pubspec.lock diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index b52448e..1a83041 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -125,9 +125,9 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { GenericResponseModel? genericResponseModel = await DashbaordApiClient().getListMenu(); Map map = {}; print(jsonEncode(genericResponseModel!.listMenu)); - for (int i = 0; i < genericResponseModel!.listMenu!.length; i++) { - print(genericResponseModel!.listMenu![i]!.menuName ?? ""); - map[genericResponseModel!.listMenu![i]!.menuName ?? ""] = i.toString(); + for (int i = 0; i < genericResponseModel.listMenu!.length; i++) { + print(genericResponseModel.listMenu![i].menuName ?? ""); + map[genericResponseModel.listMenu![i].menuName ?? ""] = i.toString(); } logger.i(map); notifyListeners(); diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 533e9ee..1ad1d62 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -128,7 +128,7 @@ class _LoginScreenState extends State { @override Widget build(BuildContext context) { username.text="15153"; - password.text="Riyadh@1234"; + password.text="Ab123456@"; return Scaffold( body: Column( children: [ diff --git a/pubspec.lock b/pubspec.lock deleted file mode 100644 index 58ce64a..0000000 --- a/pubspec.lock +++ /dev/null @@ -1,514 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - args: - dependency: transitive - description: - name: args - url: "https://pub.dartlang.org" - source: hosted - version: "2.3.0" - async: - dependency: transitive - description: - name: async - url: "https://pub.dartlang.org" - source: hosted - version: "2.8.2" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - characters: - dependency: transitive - description: - name: characters - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - charcode: - dependency: transitive - description: - name: charcode - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.1" - clock: - dependency: transitive - description: - name: clock - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - collection: - dependency: transitive - description: - name: collection - url: "https://pub.dartlang.org" - source: hosted - version: "1.15.0" - crypto: - dependency: transitive - description: - name: crypto - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1" - cupertino_icons: - dependency: "direct main" - description: - name: cupertino_icons - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.4" - easy_localization: - dependency: "direct main" - description: - name: easy_localization - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" - easy_logger: - dependency: transitive - description: - name: easy_logger - url: "https://pub.dartlang.org" - source: hosted - version: "0.0.2" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - ffi: - dependency: transitive - description: - name: ffi - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.2" - file: - dependency: transitive - description: - name: file - url: "https://pub.dartlang.org" - source: hosted - version: "6.1.2" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_countdown_timer: - dependency: "direct main" - description: - name: flutter_countdown_timer - url: "https://pub.dartlang.org" - source: hosted - version: "4.1.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.4" - flutter_localizations: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.5" - flutter_svg: - dependency: "direct main" - description: - name: flutter_svg - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - fluttertoast: - dependency: "direct main" - description: - name: fluttertoast - url: "https://pub.dartlang.org" - source: hosted - version: "8.0.8" - http: - dependency: "direct main" - description: - name: http - url: "https://pub.dartlang.org" - source: hosted - version: "0.13.4" - http_parser: - dependency: transitive - description: - name: http_parser - url: "https://pub.dartlang.org" - source: hosted - version: "4.0.0" - injector: - dependency: "direct main" - description: - name: injector - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" - intl: - dependency: transitive - description: - name: intl - url: "https://pub.dartlang.org" - source: hosted - version: "0.17.0" - js: - dependency: transitive - description: - name: js - url: "https://pub.dartlang.org" - source: hosted - version: "0.6.3" - lints: - dependency: transitive - description: - name: lints - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" - local_auth: - dependency: "direct main" - description: - name: local_auth - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.9" - logger: - dependency: "direct main" - description: - name: logger - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - matcher: - dependency: transitive - description: - name: matcher - url: "https://pub.dartlang.org" - source: hosted - version: "0.12.11" - meta: - dependency: transitive - description: - name: meta - url: "https://pub.dartlang.org" - source: hosted - version: "1.7.0" - nested: - dependency: transitive - description: - name: nested - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" - path: - dependency: transitive - description: - name: path - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.0" - path_drawing: - dependency: transitive - description: - name: path_drawing - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" - path_parsing: - dependency: transitive - description: - name: path_parsing - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" - path_provider: - dependency: "direct main" - description: - name: path_provider - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.8" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.9" - path_provider_ios: - dependency: transitive - description: - name: path_provider_ios - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.7" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.4" - path_provider_macos: - dependency: transitive - description: - name: path_provider_macos - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.4" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.4" - permission_handler: - dependency: "direct main" - description: - name: permission_handler - url: "https://pub.dartlang.org" - source: hosted - version: "8.3.0" - permission_handler_platform_interface: - dependency: transitive - description: - name: permission_handler_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "3.7.0" - petitparser: - dependency: transitive - description: - name: petitparser - url: "https://pub.dartlang.org" - source: hosted - version: "4.4.0" - platform: - dependency: transitive - description: - name: platform - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.0" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.2" - process: - dependency: transitive - description: - name: process - url: "https://pub.dartlang.org" - source: hosted - version: "4.2.4" - provider: - dependency: "direct main" - description: - name: provider - url: "https://pub.dartlang.org" - source: hosted - version: "6.0.1" - shared_preferences: - dependency: transitive - description: - name: shared_preferences - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.11" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.9" - shared_preferences_ios: - dependency: transitive - description: - name: shared_preferences_ios - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.8" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.3" - shared_preferences_macos: - dependency: transitive - description: - name: shared_preferences_macos - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.2" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.2" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.3" - shimmer: - dependency: "direct main" - description: - name: shimmer - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" - sizer: - dependency: "direct main" - description: - name: sizer - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.15" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: - dependency: transitive - description: - name: source_span - url: "https://pub.dartlang.org" - source: hosted - version: "1.8.1" - stack_trace: - dependency: transitive - description: - name: stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "1.10.0" - stream_channel: - dependency: transitive - description: - name: stream_channel - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - string_scanner: - dependency: transitive - description: - name: string_scanner - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" - test_api: - dependency: transitive - description: - name: test_api - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.3" - typed_data: - dependency: transitive - description: - name: typed_data - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.0" - universal_io: - dependency: transitive - description: - name: universal_io - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.4" - vector_math: - dependency: transitive - description: - name: vector_math - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.1" - win32: - dependency: transitive - description: - name: win32 - url: "https://pub.dartlang.org" - source: hosted - version: "2.3.1" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.0" - xml: - dependency: transitive - description: - name: xml - url: "https://pub.dartlang.org" - source: hosted - version: "5.3.1" -sdks: - dart: ">=2.14.0 <3.0.0" - flutter: ">=2.5.0" diff --git a/pubspec.yaml b/pubspec.yaml index ca8fe9c..37a3352 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.16.0 <3.0.0" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -39,13 +39,13 @@ dependencies: provider: ^6.0.1 easy_localization: ^3.0.0 http: ^0.13.4 - permission_handler: ^8.3.0 + permission_handler: ^9.2.0 flutter_svg: ^1.0.0 sizer: ^2.0.15 local_auth: ^1.1.9 fluttertoast: ^8.0.8 shared_preferences: ^2.0.12 - firebase_messaging: ^11.2.6 + firebase_messaging: ^11.2.8 shimmer: ^2.0.0 logger: ^1.1.0 flutter_countdown_timer: ^4.1.0 From 68729ddbb9294650fefc85c81c878aeb0ce89bed Mon Sep 17 00:00:00 2001 From: devmirza121 Date: Mon, 14 Mar 2022 15:41:23 +0300 Subject: [PATCH 9/9] NFC Attendance implement --- android/app/src/main/AndroidManifest.xml | 3 + assets/icons/nfc/ic_done.png | Bin 0 -> 18338 bytes assets/icons/nfc/ic_nfc.png | Bin 0 -> 19686 bytes lib/api/dashboard_api_client.dart | 24 ++ lib/app_state/app_state.dart | 2 +- lib/classes/app_permissions.dart | 30 ++ lib/classes/consts.dart | 4 +- lib/classes/utils.dart | 4 + lib/main.dart | 143 +++++++- lib/models/member_information_list_model.dart | 2 +- lib/provider/dashboard_provider_model.dart | 37 +- lib/ui/landing/dashboard_screen.dart | 10 +- lib/ui/landing/today_attendance_screen.dart | 334 ++++++++++++------ lib/ui/login/login_screen.dart | 8 +- lib/widgets/location/Location.dart | 249 +++++++++++++ lib/widgets/nfc/nfc_reader_sheet.dart | 187 ++++++++++ lib/widgets/swipe/nfc_reader_sheet.dart | 194 ++++++++++ pubspec.yaml | 12 + 18 files changed, 1105 insertions(+), 138 deletions(-) create mode 100644 assets/icons/nfc/ic_done.png create mode 100644 assets/icons/nfc/ic_nfc.png create mode 100644 lib/classes/app_permissions.dart create mode 100644 lib/widgets/location/Location.dart create mode 100644 lib/widgets/nfc/nfc_reader_sheet.dart create mode 100644 lib/widgets/swipe/nfc_reader_sheet.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8d53eaf..7be27d5 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,6 +2,9 @@ package="com.mohem_flutter_app"> + + + diff --git a/assets/icons/nfc/ic_done.png b/assets/icons/nfc/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/icons/nfc/ic_nfc.png b/assets/icons/nfc/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/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index 76e2453..f4c583d 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -8,6 +8,7 @@ import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_ import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart'; import 'package:mohem_flutter_app/models/member_login_list_model.dart'; +import 'package:uuid/uuid.dart'; import 'api_client.dart'; @@ -89,4 +90,27 @@ class DashboardApiClient { return responseData; }, url, postParams); } + + //Mark Attendance + Future markAttendance({String lat = "0", String? long = "0", required int pointType, String nfcValue = "", bool isGpsRequired = false}) async { + String url = "${ApiConsts.swpRest}AuthenticateAndSwipeUserSupportNFC"; + var uuid = Uuid(); + // Generate a v4 (random) id + + Map postParams = { + "UID": uuid.v4(), //Mobile Id + "Latitude": lat, + "Longitude": long, + "QRValue": "", + "PointType": pointType, // NFC=2, Wifi = 3, QR= 1, + "NFCValue": nfcValue, + "WifiValue": "", + "IsGpsRequired": isGpsRequired + }; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData; + }, url, postParams); + } } diff --git a/lib/app_state/app_state.dart b/lib/app_state/app_state.dart index 50aff0a..08d2f70 100644 --- a/lib/app_state/app_state.dart +++ b/lib/app_state/app_state.dart @@ -36,6 +36,7 @@ class AppState { bool isArabic(context) => EasyLocalization.of(context)?.locale.languageCode == "ar"; String? _username; + // todo ''sikander' added password for now, later will remove & improve String? password; @@ -43,7 +44,6 @@ class AppState { String? get getUserName => _username; - set setUserPassword(_password) => password = _password; MemberLoginListModel? _memberLoginList; diff --git a/lib/classes/app_permissions.dart b/lib/classes/app_permissions.dart new file mode 100644 index 0000000..a70342c --- /dev/null +++ b/lib/classes/app_permissions.dart @@ -0,0 +1,30 @@ +import 'package:permission_handler/permission_handler.dart'; + +class AppPermissions{ + static location(Function(bool) completion) { + Permission.location.isGranted.then((isGranted){ + if(!isGranted){ + Permission.location.request().then((granted){ + completion(granted == PermissionStatus.granted); + }); + } + completion(isGranted); + }); + + } + + static checkAll(Function(bool) completion){ + [ + Permission.location + ].request().then((value){ + + bool allGranted = false; + value.values.forEach((element) { + allGranted = allGranted && element == PermissionStatus.granted; + }); + + completion(allGranted); + + }); + } +} \ No newline at end of file diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index a11edec..a95e5b4 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -1,10 +1,12 @@ class ApiConsts { //static String baseUrl = "http://10.200.204.20:2801/"; // Local server static String baseUrl = "https://uat.hmgwebservices.com"; // UAT server - static String baseUrlServices = baseUrl + "/services/"; // server + // static String baseUrl = "https://hmgwebservices.com"; // Live server + static String baseUrlServices = baseUrl + "/Services/"; // server // static String baseUrlServices = "https://api.cssynapses.com/tangheem/"; // Live server static String utilitiesRest = baseUrlServices + "Utilities.svc/REST/"; static String erpRest = baseUrlServices + "ERP.svc/REST/"; + static String swpRest = baseUrlServices + "SWP.svc/REST/"; static String user = baseUrlServices + "api/User/"; static String cocRest = baseUrlServices + "COCWS.svc/REST/"; } diff --git a/lib/classes/utils.dart b/lib/classes/utils.dart index ae8041f..fc942c4 100644 --- a/lib/classes/utils.dart +++ b/lib/classes/utils.dart @@ -46,6 +46,10 @@ class Utils { }); } + static Future delay(int millis) async { + return await Future.delayed(Duration(milliseconds: millis)); + } + static void hideLoading(BuildContext context) { if (_isLoadingVisible) { _isLoadingVisible = false; diff --git a/lib/main.dart b/lib/main.dart index 20759d9..29a8e17 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'dart:typed_data'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -8,6 +9,9 @@ import 'package:mohem_flutter_app/generated/codegen_loader.g.dart'; import 'package:mohem_flutter_app/models/post_params_model.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/theme/app_theme.dart'; +import 'package:mohem_flutter_app/widgets/nfc/nfc_reader_sheet.dart'; +import 'package:nfc_manager/nfc_manager.dart'; +import 'package:nfc_manager/platform_tags.dart'; import 'package:provider/provider.dart'; import 'package:sizer/sizer.dart'; import 'package:firebase_core/firebase_core.dart'; @@ -25,7 +29,7 @@ Future main() async { await EasyLocalization.ensureInitialized(); await Firebase.initializeApp(); AppState().setPostParamsModel( - PostParamsModel(channel: 31, versionID: 3.2, mobileType: Platform.isAndroid ? "android" : "ios"), + PostParamsModel(channel: 31, versionID: 3.4, mobileType: Platform.isAndroid ? "android" : "ios"), ); runApp( EasyLocalization( @@ -77,3 +81,140 @@ class MyApp extends StatelessWidget { ); } } + +// class MyApp extends StatefulWidget { +// @override +// State createState() => MyAppState(); +// } +// +// class MyAppState extends State { +// ValueNotifier result = ValueNotifier(null); +// +// @override +// Widget build(BuildContext context) { +// return MaterialApp( +// home: Scaffold( +// appBar: AppBar(title: Text('NfcManager Plugin Example')), +// body: SafeArea( +// child: FutureBuilder( +// future: NfcManager.instance.isAvailable(), +// builder: (context, ss) => ss.data != true +// ? Center(child: Text('NfcManager.isAvailable(): ${ss.data}')) +// : Flex( +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// direction: Axis.vertical, +// children: [ +// Flexible( +// flex: 2, +// child: Container( +// margin: EdgeInsets.all(4), +// constraints: BoxConstraints.expand(), +// decoration: BoxDecoration(border: Border.all()), +// child: SingleChildScrollView( +// child: ValueListenableBuilder( +// valueListenable: result, +// builder: (context, value, _) => Text('${value ?? ''}'), +// ), +// ), +// ), +// ), +// Flexible( +// flex: 3, +// child: GridView.count( +// padding: EdgeInsets.all(4), +// crossAxisCount: 2, +// childAspectRatio: 4, +// crossAxisSpacing: 4, +// mainAxisSpacing: 4, +// children: [ +// ElevatedButton(child: Text('Tag Read'), onPressed: _tagRead), +// ElevatedButton(child: Text('Ndef Write'), onPressed: _ndefWrite), +// ElevatedButton(child: Text('Ndef Write Lock'), onPressed: _ndefWriteLock), +// ], +// ), +// ), +// ], +// ), +// ), +// ), +// ), +// ); +// } +// +// void _tagRead() { +// showNfcReader( +// context, +// onNcfScan: (String? nfcId) {}, +// ); +// // NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async { +// // result.value = tag.data; +// // print(tag.data); +// // var ndef = Ndef.from(tag); +// // +// // var f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); +// // final String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); +// // print(identifier); // => 0428fcf2255e81 +// // print(ndef!.additionalData); +// // +// // // onDiscovered callback +// // // final mifare = MiFare.from(tag); +// // // if (mifare == null) { +// // // print('Tag is not compatible with MiFare.'); +// // // return; +// // // } +// // // print(mifare.identifier); +// // // final String identifier = mifare.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); +// // // print(identifier); // => 0428fcf2255e81 +// // NfcManager.instance.stopSession(); +// // }); +// } +// +// void _ndefWrite() { +// NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async { +// var ndef = Ndef.from(tag); +// if (ndef == null || !ndef.isWritable) { +// result.value = 'Tag is not ndef writable'; +// NfcManager.instance.stopSession(errorMessage: result.value); +// return; +// } +// +// NdefMessage message = NdefMessage([ +// NdefRecord.createText('Hello World!'), +// NdefRecord.createUri(Uri.parse('https://flutter.dev')), +// NdefRecord.createMime('text/plain', Uint8List.fromList('Hello'.codeUnits)), +// NdefRecord.createExternal('com.example', 'mytype', Uint8List.fromList('mydata'.codeUnits)), +// ]); +// +// try { +// await ndef.write(message); +// result.value = 'Success to "Ndef Write"'; +// NfcManager.instance.stopSession(); +// } catch (e) { +// result.value = e; +// NfcManager.instance.stopSession(errorMessage: result.value.toString()); +// return; +// } +// }); +// } +// +// void _ndefWriteLock() { +// NfcManager.instance.startSession(onDiscovered: (NfcTag tag) async { +// var ndef = Ndef.from(tag); +// if (ndef == null) { +// result.value = 'Tag is not ndef'; +// NfcManager.instance.stopSession(errorMessage: result.value.toString()); +// return; +// } +// +// try { +// await ndef.writeLock(); +// result.value = 'Success to "Ndef Write Lock"'; +// NfcManager.instance.stopSession(); +// } catch (e) { +// result.value = e; +// NfcManager.instance.stopSession(errorMessage: result.value.toString()); +// return; +// } +// }); +// } +// } diff --git a/lib/models/member_information_list_model.dart b/lib/models/member_information_list_model.dart index da95ed0..dc64e53 100644 --- a/lib/models/member_information_list_model.dart +++ b/lib/models/member_information_list_model.dart @@ -32,7 +32,7 @@ class MemberInformationListModel { String? fREQUENCY; String? fREQUENCYMEANING; int? fROMROWNUM; - String? gRADEID; + int? gRADEID; String? gRADENAME; String? hIREDATE; int? jOBID; diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index f15e244..52fb6ad 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -19,6 +19,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { //Attendance Tracking bool isAttendanceTrackingLoading = true; int endTime = 0, isTimeRemainingInSeconds = 0; + double progress = 0.0; GetAttendanceTracking? attendanceTracking; //Work List @@ -40,19 +41,27 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { List? getMenuEntriesList; //Attendance Tracking API's & Methods - void fetchAttendanceTracking() async { + Future fetchAttendanceTracking() async { try { attendanceTracking = await DashboardApiClient().getAttendanceTracking(); + print("attendanceTracking:" + (attendanceTracking!.pRemainingHours).toString()); isAttendanceTrackingLoading = false; - isTimeRemainingInSeconds = calculateSeconds("00:00:00"); - endTime = DateTime.now().millisecondsSinceEpoch + Duration(seconds: isTimeRemainingInSeconds).inMilliseconds; - print("isTimeRemainingInSeconds " + isTimeRemainingInSeconds.toString()); - print("endTime " + endTime.toString()); + // isTimeRemainingInSeconds = calculateSeconds( "00:00:00"); + if (attendanceTracking?.pSwipeIn != null) { + isTimeRemainingInSeconds = calculateSeconds(attendanceTracking!.pRemainingHours ?? "00:00:00"); + int totalShiftTimeInSeconds = calculateSeconds(attendanceTracking!.pScheduledHours ?? "00:00:00"); + print("totalShiftTimeInSeconds: " + totalShiftTimeInSeconds.toString()); + print("isTimeRemainingInSeconds: " + isTimeRemainingInSeconds.toString()); + progress = (isTimeRemainingInSeconds / totalShiftTimeInSeconds); + endTime = DateTime.now().millisecondsSinceEpoch + Duration(seconds: isTimeRemainingInSeconds).inMilliseconds; + print("endTime " + endTime.toString()); + } - // notifyListeners(); + notifyListeners(); } catch (ex) { Utils.handleException(ex, null); } + return true; } int calculateSeconds(String time) { @@ -63,16 +72,18 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } update() { - isAttendanceTrackingLoading = !isAttendanceTrackingLoading; - isWorkListLoading = !isWorkListLoading; - attendanceTracking?.pSwipeIn = "a"; - isTimeRemainingInSeconds = calculateSeconds("00:10:30"); - endTime = DateTime.now().millisecondsSinceEpoch + Duration(seconds: isTimeRemainingInSeconds).inMilliseconds; - notifyListeners(); + fetchAttendanceTracking(); + // isAttendanceTrackingLoading = !isAttendanceTrackingLoading; + // isWorkListLoading = !isWorkListLoading; + // attendanceTracking?.pSwipeIn = "a"; + // isTimeRemainingInSeconds = calculateSeconds("00:10:30"); + // endTime = DateTime.now().millisecondsSinceEpoch + Duration(seconds: isTimeRemainingInSeconds).inMilliseconds; + // notifyListeners(); } ItgFormsModel? itgFormsModel; List? getOpenNotificationsList; + //Work List API's & Methods Future fetchWorkListCounter(context, {bool showLoading = false}) async { try { @@ -167,7 +178,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } } - //Verify Menus by printing in log + // Verify Menus by printing in log // for(int i=0;i { ), LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), 9.height, - const ClipRRect( + ClipRRect( borderRadius: BorderRadius.all( Radius.circular(20), ), child: LinearProgressIndicator( - value: 0.7, + value: model.progress, minHeight: 8, valueColor: const AlwaysStoppedAnimation(Colors.white), backgroundColor: const Color(0xff196D73), @@ -184,8 +184,10 @@ class _DashboardScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ LocaleKeys.checkIn.tr().toText12(color: Colors.white), - (model.isTimeRemainingInSeconds == 0 ? "--:--" : "09:00").toText14(color: Colors.white, isBold: true), - 4.height + (model.attendanceTracking!.pSwipeIn == null ? "--:--" : model.attendanceTracking!.pSwipeIn) + .toString() + .toText14(color: Colors.white, isBold: true), + 4.height, ], ).paddingOnly(left: 12), ), diff --git a/lib/ui/landing/today_attendance_screen.dart b/lib/ui/landing/today_attendance_screen.dart index b2ab7aa..2008484 100644 --- a/lib/ui/landing/today_attendance_screen.dart +++ b/lib/ui/landing/today_attendance_screen.dart @@ -1,12 +1,25 @@ import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/date_uitl.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/generic_response_model.dart'; import 'package:mohem_flutter_app/widgets/circular_step_progress_bar.dart'; +import 'package:mohem_flutter_app/widgets/nfc/nfc_reader_sheet.dart'; +import 'package:nfc_manager/nfc_manager.dart'; +import 'package:provider/provider.dart'; + +import '../../provider/dashboard_provider_model.dart'; +import '../../widgets/location/Location.dart'; class TodayAttendanceScreen extends StatefulWidget { TodayAttendanceScreen({Key? key}) : super(key: key); @@ -18,14 +31,45 @@ class TodayAttendanceScreen extends StatefulWidget { } class _TodayAttendanceScreenState extends State { + ValueNotifier result = ValueNotifier(null); + late DashboardProviderModel data; + bool isNfcEnabled = false, isNfcLocationEnabled = false, isQrEnabled = false, isQrLocationEnabled = false, isWifiEnabled = false, isWifiLocationEnabled = false; + @override void initState() { super.initState(); + checkAttendanceAvailablity(); + data = Provider.of(context, listen: false); + } + + checkAttendanceAvailablity() async { + bool isAvailable = await NfcManager.instance.isAvailable(); + setState(() { + AppState().privilegeListModel!.forEach((element) { + // Check availability + if (isAvailable) if (element.serviceName == "enableNFC") { + // 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 == "enableLocatoinNFC") { + 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; + } + }); + }); } @override void dispose() { super.dispose(); + // Stop Session + NfcManager.instance.stopSession(); } @override @@ -37,123 +81,170 @@ class _TodayAttendanceScreenState extends State { icon: const Icon(Icons.arrow_back_ios, color: Colors.white), onPressed: () => Navigator.pop(context), ), - ), - backgroundColor: Colors.white, - body: ListView( - children: [ - Container( - color: MyColors.backgroundBlackColor, - padding: EdgeInsets.only(top: 4,left: 21, right: 21, bottom: 21), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "June 13, 2021".toText24(isBold: true, color: Colors.white), - LocaleKeys.timeLeftToday.tr().toText16(color: Color(0xffACACAC)), - 21.height, - Center( - child: CircularStepProgressBar( - totalSteps: 16 * 4, - currentStep: 16, - width: 216, - height: 216, - selectedColor: MyColors.gradiantEndColor, - unselectedColor: MyColors.grey70Color, - child: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - "08:58:15".toText32(color: Colors.white, isBold: true), - 19.height, - "Shift Time".tr().toText12(color: MyColors.greyACColor), - "08:00 - 17:00".toText22(color: Colors.white, isBold: true), - ], - ), - ), - ), - ), - ], - ), - ), - Container( - color: MyColors.backgroundBlackColor, - child: Stack( - children: [ - Container( - height: 187, - padding: EdgeInsets.only(left: 31, right: 31, top: 31, bottom: 16), - decoration: BoxDecoration( - borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), - gradient: const LinearGradient(transform: GradientRotation(.64), begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ - MyColors.gradiantEndColor, - MyColors.gradiantStartColor, - ]), - ), - child: Column( - children: [ - Row( - children: [commonStatusView("Check In", "09:27"), commonStatusView("Check Out", "- - : - -")], - ), - 21.height, - Row( - children: [commonStatusView("Late In", "00:27"), commonStatusView("Regular", "08:00")], - ), - ], - ), - ), - Container( - width: double.infinity, - decoration: BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: Colors.white), - margin: EdgeInsets.only(top: 187 - 31), - padding: EdgeInsets.only(left: 21, right: 21, top: 24, bottom: 24), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - "Mark".tr().toText12(), - "Attendance".tr().toText24(), - "Select the method to mark the attendance".tr().toText12(color: Color(0xff535353)), - 24.height, - GridView( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - padding: EdgeInsets.zero, - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 1 / 1, crossAxisSpacing: 8, mainAxisSpacing: 8), - children: [ - attendanceMethod("NFC", "assets/images/nfc.svg", () {}), - attendanceMethod("Wifi", "assets/images/wufu.svg", () {}), - ], - ) - ], - ), - ), - // Positioned( - // top: 187 - 21, - // child: Container( - // padding: EdgeInsets.only(left: 31, right: 31, top: 31, bottom: 16), - // decoration: BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: Colors.white), - // child: Column( - // children: [ - // Row( - // children: [commonStatusView("Check In", "09:27"), commonStatusView("Check Out", "- - : - -")], - // ), - // 21.height, - // Row( - // children: [commonStatusView("Late In", "00:27"), commonStatusView("Regular", "08:00")], - // ), - // ], - // ), - // ), - // ), - ], + actions: [ + IconButton( + onPressed: () { + data.fetchAttendanceTracking(); + }, + icon: Icon( + Icons.ac_unit, + color: Colors.white, ), ) ], ), + backgroundColor: MyColors.backgroundBlackColor, + body: Consumer( + builder: (context, model, child) { + return (model.isAttendanceTrackingLoading + ? Center(child: CircularProgressIndicator()) + : ListView( + children: [ + Container( + color: MyColors.backgroundBlackColor, + padding: EdgeInsets.only(top: 4, left: 21, right: 21, bottom: 21), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DateUtil.getWeekDayMonthDayYearDateFormatted(DateTime.now(), "en").toText24(isBold: true, color: Colors.white), + LocaleKeys.timeLeftToday.tr().toText16(color: Color(0xffACACAC)), + 21.height, + Center( + child: CircularStepProgressBar( + totalSteps: 16 * 4, + currentStep: (model.progress * 100).toInt(), + width: 216, + height: 216, + selectedColor: MyColors.gradiantEndColor, + unselectedColor: MyColors.grey70Color, + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CountdownTimer( + endTime: model.endTime, + onEnd: null, + endWidget: "00:00:00".toText32(color: Colors.white, isBold: true), + textStyle: TextStyle(color: Colors.white, fontSize: 32, letterSpacing: -1.92, fontWeight: FontWeight.bold, height: 1), + ), + 19.height, + "Shift Time".tr().toText12(color: MyColors.greyACColor), + (model.attendanceTracking!.pShtName ?? "00:00:00").toString().toText22(color: Colors.white, isBold: true), + ], + ), + ), + ), + ), + ], + ), + ), + Container( + color: MyColors.backgroundBlackColor, + child: Stack( + children: [ + Container( + height: 187, + padding: EdgeInsets.only(left: 31, right: 31, top: 31, bottom: 16), + decoration: BoxDecoration( + borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), + gradient: const LinearGradient(transform: GradientRotation(.64), begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ + MyColors.gradiantEndColor, + MyColors.gradiantStartColor, + ]), + ), + child: Column( + children: [ + Row( + children: [ + commonStatusView("Check In", (model.attendanceTracking!.pSwipeIn) ?? "- - : - -"), + commonStatusView("Check Out", (model.attendanceTracking!.pSwipeOut) ?? "- - : - -") + ], + ), + 21.height, + Row( + children: [ + commonStatusView("Late In", (model.attendanceTracking!.pLateInHours) ?? "- - : - -"), + commonStatusView("Regular", (model.attendanceTracking!.pScheduledHours) ?? "- - : - -") + ], + ), + ], + ), + ), + Container( + width: double.infinity, + decoration: BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: Colors.white), + margin: EdgeInsets.only(top: 187 - 31), + padding: EdgeInsets.only(left: 21, right: 21, top: 24, bottom: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + "Mark".tr().toText12(), + "Attendance".tr().toText24(), + "Select the method to mark the attendance".tr().toText12(color: Color(0xff535353)), + 24.height, + GridView( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: EdgeInsets.zero, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 1 / 1, crossAxisSpacing: 8, mainAxisSpacing: 8), + children: [ + attendanceMethod("NFC", "assets/images/nfc.svg", isNfcEnabled, () { + showNfcReader(context, onNcfScan: (String? nfcId) async { + print(nfcId); + Utils.showLoading(context); + try { + GenericResponseModel? g = await DashboardApiClient().markAttendance(pointType: 2, nfcValue: nfcId ?? ""); + bool status = await model.fetchAttendanceTracking(); + Utils.hideLoading(context); + } catch (ex) { + print(ex); + Utils.hideLoading(context); + Utils.handleException(ex, (msg) { + Utils.confirmDialog(context, msg); + }); + } + }); + // Location.getCurrentLocation((LatLng? latlng) { + // print(latlng!.longitude.toString()); + // }); + }), + attendanceMethod("Wifi", "assets/images/wufu.svg", isWifiEnabled, () {}), + ], + ) + ], + ), + ), + // Positioned( + // top: 187 - 21, + // child: Container( + // padding: EdgeInsets.only(left: 31, right: 31, top: 31, bottom: 16), + // decoration: BoxDecoration(borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), color: Colors.white), + // child: Column( + // children: [ + // Row( + // children: [commonStatusView("Check In", "09:27"), commonStatusView("Check Out", "- - : - -")], + // ), + // 21.height, + // Row( + // children: [commonStatusView("Late In", "00:27"), commonStatusView("Regular", "08:00")], + // ), + // ], + // ), + // ), + // ), + ], + ), + ) + ], + )) + .animatedSwither(); + }, + ), ); } - Widget attendanceMethod(String title, String image, VoidCallback onPress) => Container( - padding: const EdgeInsets.only(left: 10, right: 10, top: 14, bottom: 14), + Widget attendanceMethod(String title, String image, bool isEnabled, VoidCallback onPress) => Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), gradient: const LinearGradient(transform: GradientRotation(.64), begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ @@ -161,9 +252,26 @@ class _TodayAttendanceScreenState extends State { MyColors.gradiantStartColor, ]), ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [Expanded(child: SvgPicture.asset(image)), title.toText17(isBold: true, color: Colors.white)], + clipBehavior: Clip.antiAlias, + child: Stack( + children: [ + Container( + padding: const EdgeInsets.only(left: 10, right: 10, top: 14, bottom: 14), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: SvgPicture.asset(image)), + title.toText17(isBold: true, color: Colors.white), + ], + ), + ), + if (!isEnabled) + Container( + width: double.infinity, + height: double.infinity, + color: Colors.grey.withOpacity(0.7), + ) + ], ), ).onPress(onPress); diff --git a/lib/ui/login/login_screen.dart b/lib/ui/login/login_screen.dart index 06ecb22..d5a6061 100644 --- a/lib/ui/login/login_screen.dart +++ b/lib/ui/login/login_screen.dart @@ -69,12 +69,12 @@ class _LoginScreenState extends State { } String? firebaseToken; - GetMobileLoginInfoListModel? loginInfo; + Future checkFirebaseToken() async { try { Utils.showLoading(context); firebaseToken = await _firebaseMessaging.getToken(); - loginInfo = await LoginApiClient().getMobileLoginInfoNEW(firebaseToken ?? "", Platform.isAndroid ? "android" : "ios"); + GetMobileLoginInfoListModel? loginInfo = await LoginApiClient().getMobileLoginInfoNEW(firebaseToken ?? "", Platform.isAndroid ? "android" : "ios"); if (loginInfo == null) { Utils.hideLoading(context); print("Device token not found"); @@ -112,7 +112,7 @@ class _LoginScreenState extends State { } Utils.hideLoading(context); if (_autoLogin) { - Navigator.pushNamed(context, AppRoutes.verifyLastLogin, arguments: loginInfo); + Navigator.pushNamed(context, AppRoutes.verifyLastLogin); } else { Navigator.pushNamed(context, AppRoutes.verifyLogin, arguments: "$firebaseToken"); } @@ -128,7 +128,7 @@ class _LoginScreenState extends State { @override Widget build(BuildContext context) { username.text="15153"; - password.text="Ab123456@"; + password.text="Xy12345@"; return Scaffold( body: Column( children: [ diff --git a/lib/widgets/location/Location.dart b/lib/widgets/location/Location.dart new file mode 100644 index 0000000..bd39e2e --- /dev/null +++ b/lib/widgets/location/Location.dart @@ -0,0 +1,249 @@ +import 'dart:async'; +import 'dart:math'; +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:geolocator/geolocator.dart'; +import 'package:google_directions_api/google_directions_api.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +// import 'package:geodesy/geodesy.dart' as geodesy; + +import '../../classes/app_permissions.dart'; +import '../../theme/colors.dart'; + + +//Created By Mr.Zohaib +class Location { + static _Map map = _Map(); + + static havePermission(Function(bool) callback) { + Geolocator.checkPermission().then((value) async { + if (value == LocationPermission.denied) { + value = await Geolocator.requestPermission(); + callback(![LocationPermission.denied, LocationPermission.deniedForever].contains(value)); + } else { + callback(true); + } + }); + } + + static isEnabled(Function(bool) callback) { + Geolocator.isLocationServiceEnabled().then((value) => callback(value)); + } + + static bool _listeningSettingChange = true; + + static listenGPS({bool change = true, Function(bool)? onChange}) async { + _listeningSettingChange = change; + if (change == false) return; + + Future.doWhile(() async { + await Utils.delay(1000); + var enable = await Geolocator.isLocationServiceEnabled(); + onChange!(enable); + return _listeningSettingChange; + }); + } + + static getCurrentLocation(Function(LatLng?) callback) { + done(Position position) { + //AppStorage.sp.saveLocation(position); + + LatLng? myCurrentLocation = LatLng(position.latitude, position.longitude); + callback(myCurrentLocation); + } + + AppPermissions.location((granted) { + + if (granted) + Geolocator.getLastKnownPosition(forceAndroidLocationManager: true).then((value) { + if (value == null) { + Geolocator.getCurrentPosition().then((value) { + done(value); + }); + } else { + done(value); + } + }); + }); + } + + // static LatLng locationAwayFrom( + // {required LatLng loc1, num distanceMeters = 200.0, num bearing = 270.0}) { + // geodesy.LatLng l1 = geodesy.LatLng(loc1.latitude, loc1.longitude); + // geodesy.LatLng destinationPoint = geodesy.Geodesy() + // .destinationPointByDistanceAndBearing(l1, distanceMeters, bearing); + // return LatLng(destinationPoint.latitude, destinationPoint.longitude); + // } + + static Future distanceTo(LatLng destination) async { + var myLoc = await Geolocator.getLastKnownPosition(); + var distance = 0.0; + if (myLoc != null) { + distance = Geolocator.distanceBetween(destination.latitude, destination.longitude, myLoc.latitude, myLoc.longitude); + } + return distance; + } +} + +class _Map { + Marker createMarker( + String id, { + required LatLng coordinates, + BitmapDescriptor? icon, + VoidCallback? onTap, + }) { + final MarkerId markerId = MarkerId(id); + return Marker( + icon: icon ?? BitmapDescriptor.defaultMarker, + markerId: markerId, + position: coordinates, + flat: false, + // infoWindow: InfoWindow(title: id, snippet: '*'), + onTap: onTap, + ); + } + + CameraPosition initialCamera({required Completer mapController, LatLng? position, double zoom = 12}) { + position = position ?? LatLng(24.7249303, 46.5416656); + CameraPosition riyadhEye = CameraPosition( + target: position, + zoom: zoom, + ); + mapController.future.then((controller) { + controller.animateCamera(CameraUpdate.newCameraPosition(riyadhEye)); + }); + return riyadhEye; + } + + CameraPosition moveTo(LatLng location, {double zoom = 12, double direction = 0.0, required Completer mapController, bool? animation}) { + var camera = CameraPosition(target: location, zoom: zoom, bearing: direction); + mapController.future.then((controller) { + animation ?? false ? controller.animateCamera(CameraUpdate.newCameraPosition(camera)) : controller.moveCamera(CameraUpdate.newCameraPosition(camera)); + }); + return camera; + } + + moveCamera(CameraPosition camera, @required Completer mapController, bool animation) { + mapController.future.then((controller) { + animation ? controller.animateCamera(CameraUpdate.newCameraPosition(camera)) : controller.moveCamera(CameraUpdate.newCameraPosition(camera)); + }); + } + + scrollBy({double x = 0, double y = 0, required Completer mapController, bool animation = true}) { + var camera = CameraUpdate.scrollBy(x, y); + mapController.future.then((controller) { + animation ? controller.animateCamera(camera) : controller.moveCamera(camera); + }); + } + + goToCurrentLocation({Completer? mapController, double? direction = 0.0, bool? animation}) { + Location.getCurrentLocation((location) { + moveTo(location!, zoom: 17, mapController: mapController!, animation: animation, direction: direction!); + }); + } + + var routes = Map(); + + setRoutePolylines(LatLng? source, LatLng? destination, Set polylines, Completer mapController, Function(DirectionsRoute?) completion) { + if (source == null || destination == null) { + completion(null); + return; + } + + var origin = '${source.latitude},${source.longitude}'; + var destin = '${destination.latitude},${destination.longitude}'; + var routeId = '$origin->$destination'; + + createPolyline(DirectionsRoute results) { + List polylineCoordinates = results.overviewPath!.map((e) => LatLng(e.latitude, e.longitude)).toList(); + PolylineId id = PolylineId("route"); + Polyline polyline = Polyline( + polylineId: id, + color: accentColor, + width: 5, + jointType: JointType.round, + startCap: Cap.roundCap, + endCap: Cap.roundCap, + points: polylineCoordinates, + ); + + polylines.removeWhere((element) => true); + polylines.add(polyline); + + LatLngBounds bound = getBounds(coordinates: polylineCoordinates); + focusCameraToLatLngBounds(bound: bound, mapController: mapController, padding: 100); + completion(routes[routeId]); + } + + var availableRoute = routes[routeId]; + if (availableRoute == null) { + var request = DirectionsRequest(origin: origin, destination: destin); + DirectionsService().route(request, (response, status) { + if (status == DirectionsStatus.ok && response.routes!.isNotEmpty) { + routes[routeId] = response.routes!.first; + createPolyline(response.routes!.first); + } + }); + } else { + createPolyline(availableRoute); + } + } + + LatLngBounds getBounds({required List coordinates}) { + var lngs = coordinates.map((c) => c.longitude).toList(); + var lats = coordinates.map((c) => c.latitude).toList(); + + double bottomMost = lngs.reduce(min); + double topMost = lngs.reduce(max); + double leftMost = lats.reduce(min); + double rightMost = lats.reduce(max); + + LatLngBounds bounds = LatLngBounds( + northeast: LatLng(rightMost, topMost), + southwest: LatLng(leftMost, bottomMost), + ); + return bounds; + + double? x0, x1, y0, y1; + for (LatLng latLng in coordinates) { + if (x0 == null) { + x0 = x1 = latLng.latitude; + y0 = y1 = latLng.longitude; + } else { + if (latLng.latitude > x1!) x1 = latLng.latitude; + if (latLng.latitude < x0) x0 = latLng.latitude; + if (latLng.longitude > y1!) y1 = latLng.longitude; + if (latLng.longitude < y0!) y0 = latLng.longitude; + } + } + return LatLngBounds(northeast: LatLng(x1!, y1!), southwest: LatLng(x0!, y0!)); + } + + focusCameraToLatLngBounds({LatLngBounds? bound, Completer? mapController, double? padding}) async { + if (bound == null) return; + + CameraUpdate camera = CameraUpdate.newLatLngBounds(bound, padding!); + final GoogleMapController controller = await mapController!.future; + controller.animateCamera(camera); + } + + focusCameraTo2Points({LatLng? point1, LatLng? point2, Completer? mapController, double? padding}) async { + var source = point1; + var destination = point2; + if (source != null && destination != null) { + // 'package:google_maps_flutter_platform_interface/src/types/location.dart': Failed assertion: line 72 pos 16: 'southwest.latitude <= northeast.latitude': is not true. + LatLngBounds bound; + if (source.latitude <= destination.latitude) { + bound = LatLngBounds(southwest: source, northeast: destination); + } else { + bound = LatLngBounds(southwest: destination, northeast: source); + } + + if (bound == null) return; + + focusCameraToLatLngBounds(bound: bound, mapController: mapController, padding: padding); + } + } +} diff --git a/lib/widgets/nfc/nfc_reader_sheet.dart b/lib/widgets/nfc/nfc_reader_sheet.dart new file mode 100644 index 0000000..84397ba --- /dev/null +++ b/lib/widgets/nfc/nfc_reader_sheet.dart @@ -0,0 +1,187 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:nfc_manager/nfc_manager.dart'; +import 'package:nfc_manager/platform_tags.dart'; + +void showNfcReader(BuildContext context, {required Function(String? nfcId) onNcfScan}) { + showModalBottomSheet( + context: context, + enableDrag: false, + isDismissible: false, + shape: 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({required 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 { + print(tag.data); + var f = MifareUltralight(tag: tag, identifier: tag.data["nfca"]["identifier"], type: 2, maxTransceiveLength: 252, timeout: 22); + final String identifier = f.identifier.map((e) => e.toRadixString(16).padLeft(2, '0')).join(''); + // print(identifier); // => 0428fcf2255e81 + nfcId = identifier; + + setState(() { + _reading = true; + mainWidget = doneNfc(); + }); + + Future.delayed(const Duration(seconds: 1), () { + NfcManager.instance.stopSession(); + Navigator.pop(context); + widget.onNcfScan(nfcId); + }); + }); + } + + @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, + ), + Text( + "Ready To Scan", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 24, + ), + ), + SizedBox( + height: 30, + ), + Image.asset( + "assets/icons/nfc/ic_nfc.png", + height: MediaQuery.of(context).size.width / 3, + ), + 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: RaisedButton( + onPressed: () { + NfcManager.instance.stopSession(); + Navigator.pop(context); + }, + elevation: 0, + child: 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", + height: MediaQuery.of(context).size.width / 3, + ), + 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: RaisedButton( + // onPressed: () { + // _stream?.cancel(); + // widget.onNcfScan(nfcId); + // Navigator.pop(context); + // }, + onPressed: null, + elevation: 0, + child: Text("DONE"), + ), + ), + SizedBox( + height: 30, + ), + ], + ), + ); + } +} diff --git a/lib/widgets/swipe/nfc_reader_sheet.dart b/lib/widgets/swipe/nfc_reader_sheet.dart new file mode 100644 index 0000000..3dc357d --- /dev/null +++ b/lib/widgets/swipe/nfc_reader_sheet.dart @@ -0,0 +1,194 @@ +// import 'dart:async'; +// +// import 'package:flutter/material.dart'; +// +// +// void showNfcReader(BuildContext context, {Function onNcfScan}) { +// showModalBottomSheet( +// context: context, +// enableDrag: false, +// isDismissible: false, +// shape: 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 onNcfScan; +// +// NfcLayout({this.onNcfScan}); +// +// @override +// _NfcLayoutState createState() => _NfcLayoutState(); +// } +// +// class _NfcLayoutState extends State { +// StreamSubscription _stream; +// bool _reading = false; +// Widget mainWidget; +// String nfcId; +// +// @override +// void initState() { +// super.initState(); +// +// setState(() { +// // _reading = true; +// // Start reading using NFC.readNDEF() +// _stream = NFC.readNDEF(once: false, throwOnUserCancel: false, readerMode: NFCDispatchReaderMode()).listen((NDEFMessage message) { +// setState(() { +// _reading = true; +// mainWidget = doneNfc(); +// }); +// Future.delayed(const Duration(milliseconds: 500), () { +// _stream?.cancel(); +// widget.onNcfScan(nfcId); +// Navigator.pop(context); +// }); +// print("read NDEF id: ${message.id}"); +// print("NFC Record " + message.payload); +// print("NFC Record Lenght " + message.records.length.toString()); +// print("NFC Record " + message.records.first.id); +// print("NFC Record " + message.records.first.payload); +// print("NFC Record " + message.records.first.data); +// print("NFC Record " + message.records.first.type); +// // widget.onNcfScan(message.id); +// nfcId = message.id; +// }, onError: (e) { +// // Check error handling guide below +// }); +// }); +// } +// +// @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, +// ), +// Text( +// "Ready To Scan", +// style: TextStyle( +// fontWeight: FontWeight.bold, +// fontSize: 24, +// ), +// ), +// SizedBox( +// height: 30, +// ), +// Image.asset( +// "assets/images/nfc/ic_nfc.png", +// height: MediaQuery.of(context).size.width / 3, +// ), +// 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: RaisedButton( +// onPressed: () { +// _stream?.cancel(); +// Navigator.pop(context); +// }, +// elevation: 0, +// child: 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/images/nfc/ic_done.png", +// height: MediaQuery.of(context).size.width / 3, +// ), +// 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: RaisedButton( +// // onPressed: () { +// // _stream?.cancel(); +// // widget.onNcfScan(nfcId); +// // Navigator.pop(context); +// // }, +// onPressed: null, +// elevation: 0, +// child: Text("DONE"), +// ), +// ), +// SizedBox( +// height: 30, +// ), +// ], +// ), +// ); +// } +// } diff --git a/pubspec.yaml b/pubspec.yaml index 37a3352..d38cc5a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,16 @@ dependencies: shimmer: ^2.0.0 logger: ^1.1.0 flutter_countdown_timer: ^4.1.0 + nfc_manager: ^3.1.1 + uuid: ^3.0.6 + + # maps + google_maps_flutter: ^2.0.2 + google_maps_utils: ^1.4.0+1 + google_directions_api: ^0.9.0 + geolocator: any + # flutter_compass: ^0.6.1 + google_maps_flutter_web: ^0.3.2 dev_dependencies: @@ -84,6 +94,8 @@ flutter: - assets/images/ - assets/images/login/ - assets/images/logos/ + - assets/icons/nfc/ic_nfc.png + - assets/icons/nfc/ic_done.png # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware.