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: