diff --git a/lib/controllers/providers/api/user_provider.dart b/lib/controllers/providers/api/user_provider.dart index a989cbde..ec6f3242 100644 --- a/lib/controllers/providers/api/user_provider.dart +++ b/lib/controllers/providers/api/user_provider.dart @@ -446,17 +446,31 @@ class UserProvider extends ChangeNotifier { } } - Future getUserRota() async { + Future getUserTodayRota(String startDate, String endDate) async { Response response; try { - response = await ApiManager.instance.get(URLs.getUserRota); + response = await ApiManager.instance.get(URLs.getUserRota + "?fromDate=$startDate&toDate=$endDate"); if (response.statusCode >= 200 && response.statusCode < 300) { List dataList = json.decode(response.body); return UserRotaModel.fromJson(dataList.first); } return null; } catch (error) { - return null ; + return null; + } + } + + Future> getUserMonthlyRota(String startDate, String endDate) async { + Response response; + try { + response = await ApiManager.instance.get(URLs.getUserRota + "?fromDate=$startDate&toDate=$endDate"); + if (response.statusCode >= 200 && response.statusCode < 300) { + List dataList = json.decode(response.body); + return dataList.map((e) => UserRotaModel.fromJson(e)).toList(); + } + return []; + } catch (error) { + return []; } } diff --git a/lib/extensions/context_extension.dart b/lib/extensions/context_extension.dart index 96e79211..b7bb652a 100644 --- a/lib/extensions/context_extension.dart +++ b/lib/extensions/context_extension.dart @@ -14,6 +14,8 @@ extension BuildContextExtension on BuildContext { bool get isDark => Provider.of(this).theme == "dark"; + bool get isDarkNotListen => Provider.of(this, listen: false).theme == "dark"; + bool get isAr => Provider.of(this).language == "ar"; SettingProvider get settingProvider => Provider.of(this, listen: false); diff --git a/lib/extensions/widget_extensions.dart b/lib/extensions/widget_extensions.dart index 25aee136..946d2b29 100644 --- a/lib/extensions/widget_extensions.dart +++ b/lib/extensions/widget_extensions.dart @@ -186,5 +186,5 @@ extension WidgetExtensions on Widget { } extension DividerExtension on Divider { - Divider defaultStyle(BuildContext context) => Divider(thickness: 1, color: context.isDark ? AppColor.neutral20 : AppColor.neutral30); + Divider defaultStyle(BuildContext context) => Divider(thickness: 1, color: context.isDarkNotListen ? AppColor.neutral20 : AppColor.neutral30); } diff --git a/lib/views/pages/user/profile_page.dart b/lib/views/pages/user/profile_page.dart index b58a9002..f1a0650a 100644 --- a/lib/views/pages/user/profile_page.dart +++ b/lib/views/pages/user/profile_page.dart @@ -3,7 +3,9 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:image_cropper/image_cropper.dart'; import 'package:image_picker/image_picker.dart'; +import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; +import 'package:table_calendar/table_calendar.dart'; import 'package:test_sa/controllers/providers/api/user_provider.dart'; import 'package:test_sa/controllers/providers/settings/setting_provider.dart'; import 'package:test_sa/extensions/context_extension.dart'; @@ -17,9 +19,7 @@ import 'package:test_sa/modules/cm_module/views/components/action_button/footer_ import 'package:test_sa/new_views/common_widgets/app_dashed_button.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; import 'package:test_sa/new_views/common_widgets/default_app_bar.dart'; - import 'package:test_sa/views/pages/user/update_user_contact_info_bottomsheet.dart'; - import '../../../new_views/app_style/app_color.dart'; class ProfilePage extends StatefulWidget { @@ -33,20 +33,28 @@ class ProfilePage extends StatefulWidget { class _ProfilePageState extends State { late UserProvider _userProvider; - late SettingProvider _settingProvider; - late double _width; - late double _height; + late DateTime _initialDate, _firstDate, _lastDate; + User _user = User(); bool _firstTime = true; final GlobalKey _formKey = GlobalKey(); final GlobalKey _scaffoldKey = GlobalKey(); + bool isTodayRota = true; + + @override + void initState() { + super.initState(); + _initialDate = DateTime.now(); + _firstDate = DateTime.utc(2010, 10, 16); + _lastDate = DateTime.utc(2030, 3, 14); + } @override Widget build(BuildContext context) { _userProvider = Provider.of(context); - _settingProvider = Provider.of(context); - _width = MediaQuery.of(context).size.width; - _height = MediaQuery.of(context).size.height; + // _settingProvider = Provider.of(context); + // _width = MediaQuery.of(context).size.width; + // _height = MediaQuery.of(context).size.height; if (_firstTime) { _user = User.fromJsonCons(_userProvider.user!.toJson()); @@ -167,7 +175,10 @@ class _ProfilePageState extends State { shrinkWrap: true, padding: EdgeInsets.zero, itemBuilder: (context, index) { - return (_user.departmentName![index] ?? "N/A").bodyText(context).paddingOnly(top: 4, bottom: 4); + return Text( + _user.departmentName![index] ?? "N/A", + style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + ).paddingOnly(top: 2, bottom: 2); }, ), if ((_user.clientName ?? "").isNotEmpty) ...[ @@ -220,105 +231,40 @@ class _ProfilePageState extends State { ], ).toShadowContainer(context), if (context.settingProvider.isUserFlowMedical) - FutureBuilder( - future: _userProvider.getUserRota(), - builder: (cxt, snapshot) { - bool isLoading = snapshot.connectionState == ConnectionState.waiting; - if ((!isLoading && (snapshot.hasError || !snapshot.hasData))) { - return const SizedBox(); - } - - return Container( - margin: const EdgeInsets.only(top: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Rota Details", - style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + Container( + margin: const EdgeInsets.only(top: 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Rota Details", + style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral30 : AppColor.black10), + ), + 8.width, + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: AppColor.primary10, ), - 8.height, - if (isLoading) ...[ - Text( - 'Shift Start Time Time', - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), - ).toShimmer(context: context, isShow: isLoading), - 4.height, - Text( - 'Shift Actual Actual Start Time', - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), - ).toShimmer(context: context, isShow: isLoading) - ] else - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - // Text( - // 'Site: ${_user.clientName ?? "-"}', - // style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), - // ), - Text( - 'Shift Name: ${snapshot.data!.shiftName ?? "-"}', - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), - ), - Text( - 'Break: ${snapshot.data!.breakTime ?? "-"}', - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), - ), - if (snapshot.data!.leaveTypeCode?.isNotEmpty ?? false) - SizedBox( - width: double.infinity, - child: Column(children: [ - 8.height, - Text( - snapshot.data!.leaveTypeCode ?? "", - style: AppTextStyles.heading6.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120, fontWeight: FontWeight.w600), - ), - Text( - snapshot.data!.leaveDescription ?? "", - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), - ), - ]), - ) - else ...[ - Text( - 'Shift Start Time: ${snapshot.data!.shiftName?.split('-').first.trim() ?? "-"}', - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), - ), - Text( - 'Shift End Time: ${snapshot.data!.shiftName?.split('-').last.trim() ?? "-"}', - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), - ), - const Divider().defaultStyle(context), - Text( - 'Swipe In: ${snapshot.data!.shTActualStartTime?.toTime ?? "-"}', - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), - ), - Text( - 'Swipe Out: ${snapshot.data!.shTActualEndTime?.toTime ?? "-"}', - style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), - ), - ], - ], - ), - ], - ).toShadowContainer(context), - ); - - // if (!isDeviceSupported) { - // return Text("Your device did not support Fingerprint/Face ID.", style: AppTextStyles.tinyFont.copyWith(fontSize: 11, color: Colors.red)); - // } - - // return Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - // children: [ - // "Fingerprint/Face ID".heading5(context), - // customSwitch(localAuth).onPress(() { - // checkForLocalAuth(localAuth); - // }), - // ], - // ); - }), + child: Text( + isTodayRota ? "Monthly " : "Today", + style: AppTextStyles.bodyText2.copyWith(color: Colors.white), + ), + ).onPress(() => setState(() { + _initialDate = DateTime.now(); + isTodayRota = !isTodayRota; + })), + ], + ), + (isTodayRota ? todayRotaWidget() : monthRotaWidget()) + ], + ).toShadowContainer(context), + ), ], ).expanded, FooterActionButton.footerContainer( @@ -349,4 +295,201 @@ class _ProfilePageState extends State { ), ); } + + Widget todayRotaWidget() { + final DateFormat fmt = DateFormat('MM-dd-yyyy'); + final String startStr = fmt.format(_initialDate); + + return FutureBuilder( + future: _userProvider.getUserTodayRota(startStr, startStr), + builder: (context, snapshot) { + bool isLoading = snapshot.connectionState == ConnectionState.waiting; + if ((!isLoading && (snapshot.hasError || !snapshot.hasData))) { + return const SizedBox(); + } + + return rotaDetailsWidget(isLoading, snapshot.data); + }, + ); + } + + Widget monthRotaWidget() { + final DateTime startOfMonth = DateTime(_initialDate.year, _initialDate.month, 1); + final DateTime endOfMonth = DateTime(_initialDate.year, _initialDate.month + 1, 0); + + final DateFormat fmt = DateFormat('MM-dd-yyyy'); + final String startStr = fmt.format(startOfMonth); + final String endStr = fmt.format(endOfMonth); + + return FutureBuilder>( + future: _userProvider.getUserMonthlyRota(startStr, endStr), + builder: (context, snapshot) { + bool isLoading = snapshot.connectionState == ConnectionState.waiting; + if ((!isLoading && (snapshot.hasError || !snapshot.hasData))) { + return const SizedBox(); + } + + return TableCalendar( + firstDay: _firstDate, + lastDay: _lastDate, + focusedDay: _initialDate, + calendarFormat: CalendarFormat.month, + weekendDays: const [], + rowHeight: 45, + pageJumpingEnabled: true, + pageAnimationEnabled: true, + availableGestures: AvailableGestures.none, + onPageChanged: (dateTime) { + _initialDate = dateTime; + setState(() {}); + }, + onDaySelected: (selectedDate, focusDate) { + final List matches = snapshot.data?.where((e) { + final dt = DateTime.tryParse(e.scheduleDate ?? ''); + return dt != null && isSameDay(dt, selectedDate); + }).toList() ?? + []; + + final UserRotaModel? rota = matches.isNotEmpty ? matches.first : null; + + if (rota == null) { + "No details found".showToast; + return; + } + + context.showBottomSheet(rotaDetailsWidget(isLoading, rota), title: "Rota Details".addTranslation); + }, + calendarBuilders: CalendarBuilders( + markerBuilder: (context, date, eventsOnDay) { + if (isLoading) return null; + final List matches = snapshot.data?.where((e) { + final dt = DateTime.tryParse(e.scheduleDate ?? ''); + return dt != null && isSameDay(dt, date); + }).toList() ?? + []; + + final UserRotaModel? rota = matches.isNotEmpty ? matches.first : null; + + if (rota == null) return null; + return Container( + width: 6, + height: 6, + margin: const EdgeInsets.symmetric(horizontal: 1, vertical: 4), + decoration: BoxDecoration( + color: rota.leaveTypeCode == "ANNUAL" + ? AppColor.red30 + : rota.leaveTypeCode == "HOLIDAY" + ? AppColor.green15 + : AppColor.primary10, + shape: BoxShape.circle), + ); + }, + dowBuilder: (context, dateTime) { + final day = DateFormat("EE", context.isAr ? "ar" : "en").format(dateTime).toUpperCase(); + return Align(alignment: Alignment.center, child: day.bodyText(context).custom(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50)); + }, + defaultBuilder: (_cxt, dateTime, _) { + final day = DateFormat("d").format(dateTime); + if (isLoading) { + return const SizedBox(height: 36, width: 36).toShimmer(isShow: isLoading, context: _cxt); + } else { + null; + } + }, + ), + // eventLoader: (day) { + // print("eventLoader:$day"); + // return []; + // }, + daysOfWeekHeight: 35.toScreenHeight, + headerStyle: HeaderStyle( + leftChevronVisible: true, + rightChevronVisible: true, + formatButtonVisible: false, + titleCentered: true, + titleTextStyle: AppTextStyles.bodyText, + headerPadding: const EdgeInsets.all(0), + ), + calendarStyle: CalendarStyle( + isTodayHighlighted: false, + todayDecoration: const BoxDecoration(color: AppColor.primary10, shape: BoxShape.circle), + defaultTextStyle: AppTextStyles.bodyText, + defaultDecoration: BoxDecoration(shape: BoxShape.circle, color: context.isDark ? AppColor.neutral50 : AppColor.neutral30), + ), + ).paddingOnly(top: 16); + }, + ); + } + + Widget rotaDetailsWidget(bool isLoading, UserRotaModel? userRota) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 16.height, + if (isLoading) ...[ + Text( + 'Shift Start Time Time', + style: AppTextStyles.bodyText.copyWith(color: context.isDarkNotListen ? AppColor.neutral10 : AppColor.neutral120), + ).toShimmer(context: context, isShow: isLoading), + 4.height, + Text( + 'Shift Actual Actual Start Time', + style: AppTextStyles.bodyText.copyWith(color: context.isDarkNotListen ? AppColor.neutral10 : AppColor.neutral120), + ).toShimmer(context: context, isShow: isLoading) + ] else + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + // Text( + // 'Site: ${_user.clientName ?? "-"}', + // style: AppTextStyles.bodyText.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral120), + // ), + Text( + 'Shift Name: ${userRota!.shiftName ?? "-"}', + style: AppTextStyles.bodyText.copyWith(color: context.isDarkNotListen ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Break: ${userRota.breakTime ?? "-"}', + style: AppTextStyles.bodyText.copyWith(color: context.isDarkNotListen ? AppColor.neutral10 : AppColor.neutral120), + ), + if (userRota.leaveTypeCode?.isNotEmpty ?? false) + SizedBox( + width: double.infinity, + child: Column(children: [ + 8.height, + Text( + userRota.leaveTypeCode ?? "", + style: AppTextStyles.heading6.copyWith(color: context.isDarkNotListen ? AppColor.neutral10 : AppColor.neutral120, fontWeight: FontWeight.w600), + ), + Text( + userRota.leaveDescription ?? "", + style: AppTextStyles.bodyText.copyWith(color: context.isDarkNotListen ? AppColor.neutral10 : AppColor.neutral120), + ), + ]), + ) + else ...[ + Text( + 'Shift Start Time: ${userRota.shiftName?.split('-').first.trim() ?? "-"}', + style: AppTextStyles.bodyText.copyWith(color: context.isDarkNotListen ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Shift End Time: ${userRota.shiftName?.split('-').last.trim() ?? "-"}', + style: AppTextStyles.bodyText.copyWith(color: context.isDarkNotListen ? AppColor.neutral10 : AppColor.neutral120), + ), + const Divider().defaultStyle(context), + Text( + 'Swipe In: ${userRota.shTActualStartTime?.toTime ?? "-"}', + style: AppTextStyles.bodyText.copyWith(color: context.isDarkNotListen ? AppColor.neutral10 : AppColor.neutral120), + ), + Text( + 'Swipe Out: ${userRota.shTActualEndTime?.toTime ?? "-"}', + style: AppTextStyles.bodyText.copyWith(color: context.isDarkNotListen ? AppColor.neutral10 : AppColor.neutral120), + ), + ], + ], + ), + ], + ); + } }