diff --git a/assets/images/drawer/courses.svg b/assets/images/drawer/courses.svg
new file mode 100644
index 0000000..d15e742
--- /dev/null
+++ b/assets/images/drawer/courses.svg
@@ -0,0 +1,8 @@
+
+
diff --git a/assets/images/ticket_bal.svg b/assets/images/ticket_bal.svg
new file mode 100644
index 0000000..8fadde8
--- /dev/null
+++ b/assets/images/ticket_bal.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/vacation_rule.svg b/assets/images/vacation_rule.svg
new file mode 100644
index 0000000..0a536f7
--- /dev/null
+++ b/assets/images/vacation_rule.svg
@@ -0,0 +1,16 @@
+
diff --git a/assets/images/worklist/approve.svg b/assets/images/worklist/approve.svg
new file mode 100644
index 0000000..8d62ae9
--- /dev/null
+++ b/assets/images/worklist/approve.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/images/worklist/reject.svg b/assets/images/worklist/reject.svg
new file mode 100644
index 0000000..cb5da34
--- /dev/null
+++ b/assets/images/worklist/reject.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json
index 20b52e5..f09dfb4 100644
--- a/assets/langs/ar-SA.json
+++ b/assets/langs/ar-SA.json
@@ -644,6 +644,18 @@
"yearsUsed": "سنوات الاستخدام",
"faRequest": "طلب التخلص من FA",
"showMore": "عرض المزيد",
+ "ticketDetails": "تفاصيل التذكرة",
+ "currentTicketBalance": "رصيد التذكرة الحالي",
+ "ticketHistory": "تاريخ التذكرة",
+ "proceedBooking": "المتابعة للحجز",
+ "courses": "الدورات التدريبية",
+ "course": "دورة",
+ "status": "الحالة",
+ "deadline": "الموعد النهائي",
+ "overdue": "متأخر",
+ "noCoursesAvailable": "لا توجد دورات متاحة",
+ "pullToRefresh": "اسحب للتحديث",
+ "courseUrlNotAvailable": "رابط الدورة غير متوفر",
"buyerDetails": "تفاصيل المشتري",
"etqanOVR": "الإبلاغ عن حادثة",
"etqanRequest": "طلبات الحوادث",
@@ -658,4 +670,5 @@
"pleaseSelectProject": "الرجاء اختيار مشروع",
"viewAttachment" : "عرض المرفق",
"parkingQr": "وقوف السيارات QR"
-}
\ No newline at end of file
+}
+
diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json
index db106e6..216fe85 100644
--- a/assets/langs/en-US.json
+++ b/assets/langs/en-US.json
@@ -642,6 +642,18 @@
"yearsUsed": "Years Used",
"faRequest":"FA Disposal Request",
"showMore": "Show More",
+ "ticketDetails": "Ticket Details",
+ "currentTicketBalance": "Current Ticket Balance",
+ "ticketHistory": "Ticket History",
+ "proceedBooking": "Proceed For Booking",
+ "courses": "Courses",
+ "course": "Course",
+ "status": "Status",
+ "deadline": "Deadline",
+ "overdue": "Overdue",
+ "noCoursesAvailable": "No Courses Available",
+ "pullToRefresh": "Pull to refresh",
+ "courseUrlNotAvailable": "Course URL not available",
"buyerDetails": "Buyer Details",
"etqanOVR": "Report An Incident",
"etqanRequest": "Incident Requests",
diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart
index 0b8247f..27fe289 100644
--- a/lib/api/dashboard_api_client.dart
+++ b/lib/api/dashboard_api_client.dart
@@ -15,6 +15,7 @@ import 'package:mohem_flutter_app/models/dashboard/get_accural_ticket_balance_mo
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/list_menu.dart';
+import 'package:mohem_flutter_app/models/dashboard/courses_response_model.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/get_employee_parking_details_model.dart';
import 'package:mohem_flutter_app/models/itg/itg_main_response.dart';
@@ -430,4 +431,29 @@ class DashboardApiClient {
postParams,
);
}
+
+ //Fetch All Courses Status
+ Future fetchAllCoursesStatus() async {
+ String url = "https://elearning.hmg.com/moodle_dev/api/Fetch_All_Courses_Status.php";
+
+ Map headers = {
+ 'x-api-key': 'bLi@mbXJeXTHd/)h&LFh%25+%25(Nbnaq6hBg%7d%5dyQbthY%7bv6ew6-5UT\$NASwucn%7d,_PSJpuwNCCen2%7djj%7b00HR2T-%5b,k7W%7d-0yepK?%258',
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ };
+
+ Map postParams = {
+ "username": AppState().memberInformationList?.eMPLOYEENUMBER ?? "",
+ };
+
+ return await ApiClient().postJsonForObject(
+ (json) {
+ CoursesResponseModel responseData = CoursesResponseModel.fromJson(json);
+ return responseData;
+ },
+ url,
+ postParams,
+ headers: headers,
+ isFormData: false,
+ );
+ }
}
diff --git a/lib/config/routes.dart b/lib/config/routes.dart
index c1eec7f..9bae8df 100644
--- a/lib/config/routes.dart
+++ b/lib/config/routes.dart
@@ -58,6 +58,7 @@ import 'package:mohem_flutter_app/ui/profile/profile_screen.dart';
import 'package:mohem_flutter_app/ui/screens/announcements/announcement_details.dart';
import 'package:mohem_flutter_app/ui/screens/announcements/announcements.dart';
import 'package:mohem_flutter_app/ui/screens/child_education/child_education_assistance.dart';
+import 'package:mohem_flutter_app/ui/screens/courses/courses_screen.dart';
// import 'package:mohem_flutter_app/ui/my_attendance/work_from_home_screen.dart';
import 'package:mohem_flutter_app/ui/screens/eit/add_eit.dart';
import 'package:mohem_flutter_app/ui/screens/event_activity/event_activity.dart';
@@ -170,6 +171,9 @@ class AppRoutes {
static const String offersAndDiscounts = "/offersAndDiscounts";
static const String offersAndDiscountsDetails = "/offersAndDiscountsDetails";
+ // Courses
+ static const String courses = "/courses";
+
//Pay slip
static const String monthlyPaySlip = "/monthlyPaySlip";
@@ -302,6 +306,9 @@ class AppRoutes {
offersAndDiscounts: (BuildContext context) => OffersAndDiscountsHome(),
offersAndDiscountsDetails: (BuildContext context) => OffersAndDiscountsDetails(),
+ // Courses
+ courses: (BuildContext context) => const CoursesScreen(),
+
//pay slip
monthlyPaySlip: (BuildContext context) => MonthlyPaySlipScreen(),
performanceEvaluation: (BuildContext context) => PerformanceAppraisal(),
diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart
index d22edf2..9ef43c9 100644
--- a/lib/generated/codegen_loader.g.dart
+++ b/lib/generated/codegen_loader.g.dart
@@ -657,6 +657,18 @@ class CodegenLoader extends AssetLoader{
"yearsUsed": "سنوات الاستخدام",
"faRequest": "طلب التخلص من FA",
"showMore": "عرض المزيد",
+ "ticketDetails": "تفاصيل التذكرة",
+ "currentTicketBalance": "رصيد التذكرة الحالي",
+ "ticketHistory": "تاريخ التذكرة",
+ "proceedBooking": "المتابعة للحجز",
+ "courses": "الدورات التدريبية",
+ "course": "دورة",
+ "status": "الحالة",
+ "deadline": "الموعد النهائي",
+ "overdue": "متأخر",
+ "noCoursesAvailable": "لا توجد دورات متاحة",
+ "pullToRefresh": "اسحب للتحديث",
+ "courseUrlNotAvailable": "رابط الدورة غير متوفر",
"buyerDetails": "تفاصيل المشتري",
"etqanOVR": "الإبلاغ عن حادثة",
"etqanRequest": "طلبات الحوادث",
@@ -1315,6 +1327,18 @@ static const Map _en_US = {
"yearsUsed": "Years Used",
"faRequest": "FA Disposal Request",
"showMore": "Show More",
+ "ticketDetails": "Ticket Details",
+ "currentTicketBalance": "Current Ticket Balance",
+ "ticketHistory": "Ticket History",
+ "proceedBooking": "Proceed For Booking",
+ "courses": "Courses",
+ "course": "Course",
+ "status": "Status",
+ "deadline": "Deadline",
+ "overdue": "Overdue",
+ "noCoursesAvailable": "No Courses Available",
+ "pullToRefresh": "Pull to refresh",
+ "courseUrlNotAvailable": "Course URL not available",
"buyerDetails": "Buyer Details",
"etqanOVR": "Report An Incident",
"etqanRequest": "Incident Requests",
diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart
index dd76183..a9f4584 100644
--- a/lib/generated/locale_keys.g.dart
+++ b/lib/generated/locale_keys.g.dart
@@ -629,6 +629,18 @@ abstract class LocaleKeys {
static const yearsUsed = 'yearsUsed';
static const faRequest = 'faRequest';
static const showMore = 'showMore';
+ static const ticketDetails = 'ticketDetails';
+ static const currentTicketBalance = 'currentTicketBalance';
+ static const ticketHistory = 'ticketHistory';
+ static const proceedBooking = 'proceedBooking';
+ static const courses = 'courses';
+ static const course = 'course';
+ static const status = 'status';
+ static const deadline = 'deadline';
+ static const overdue = 'overdue';
+ static const noCoursesAvailable = 'noCoursesAvailable';
+ static const pullToRefresh = 'pullToRefresh';
+ static const courseUrlNotAvailable = 'courseUrlNotAvailable';
static const buyerDetails = 'buyerDetails';
static const etqanOVR = 'etqanOVR';
static const etqanRequest = 'etqanRequest';
diff --git a/lib/models/dashboard/courses_response_model.dart b/lib/models/dashboard/courses_response_model.dart
new file mode 100644
index 0000000..730d4a4
--- /dev/null
+++ b/lib/models/dashboard/courses_response_model.dart
@@ -0,0 +1,94 @@
+class CoursesResponseModel {
+ bool? success;
+ int? status;
+ String? username;
+ List? coursesStatus;
+ int? totalCourses;
+
+ CoursesResponseModel({
+ this.success,
+ this.status,
+ this.username,
+ this.coursesStatus,
+ this.totalCourses,
+ });
+
+ CoursesResponseModel.fromJson(Map json) {
+ success = json['success'];
+ status = json['status'];
+ username = json['username'];
+ if (json['courses_status'] != null) {
+ coursesStatus = [];
+ json['courses_status'].forEach((v) {
+ coursesStatus!.add(CourseStatus.fromJson(v));
+ });
+ }
+ totalCourses = json['total_courses'];
+ }
+
+ Map toJson() {
+ Map data = {};
+ data['success'] = success;
+ data['status'] = status;
+ data['username'] = username;
+ if (coursesStatus != null) {
+ data['courses_status'] = coursesStatus!.map((v) => v.toJson()).toList();
+ }
+ data['total_courses'] = totalCourses;
+ return data;
+ }
+}
+
+class CourseStatus {
+ String? cId;
+ String? cFullname;
+ String? cNameAr;
+ String? cShortname;
+ String? cStatus;
+ String? cIsMohemmAffective;
+ String? cStartdate;
+ String? cEnddate;
+ String? cUrl;
+ String? token;
+ CourseStatus({
+ this.cId,
+ this.cFullname,
+ this.cNameAr,
+ this.cShortname,
+ this.cStatus,
+ this.cIsMohemmAffective,
+ this.cStartdate,
+ this.cEnddate,
+ this.cUrl,
+ this.token
+ });
+
+ CourseStatus.fromJson(Map json) {
+ cId = json['c_id'];
+ cFullname = json['c_fullname'];
+ cNameAr = json['c_name_ar'];
+ cShortname = json['c_shortname'];
+ cStatus = json['c_status'];
+ cIsMohemmAffective = json['c_is_mohemm_affective'];
+ cStartdate = json['c_startdate'];
+ cEnddate = json['c_enddate'];
+ cUrl = json['c_url'];
+ token =json['c_token'];
+ }
+
+ Map toJson() {
+ Map data = {};
+ data['c_id'] = cId;
+ data['c_fullname'] = cFullname;
+ data['c_name_ar'] = cNameAr;
+ data['c_shortname'] = cShortname;
+ data['c_status'] = cStatus;
+ data['c_is_mohemm_affective'] = cIsMohemmAffective;
+ data['c_startdate'] = cStartdate;
+ data['c_enddate'] = cEnddate;
+ data['c_url'] = cUrl;
+ data['c_token'] = token;
+ return data;
+ }
+}
+
diff --git a/lib/models/itg/advertisement.dart b/lib/models/itg/advertisement.dart
index 002f73d..2379d37 100644
--- a/lib/models/itg/advertisement.dart
+++ b/lib/models/itg/advertisement.dart
@@ -136,10 +136,21 @@ class ActionButtonsColl {
late String btnTextEn;
late String btnTextAr;
late String actionValue;
- late dynamic iconOrImage;
+ dynamic iconOrImage;
+ String? actionType;
late int orderNo;
+ String? url;
- ActionButtonsColl({required this.actionButtonId, required this.btnTextEn, required this.btnTextAr, required this.actionValue, required this.iconOrImage, required this.orderNo});
+ ActionButtonsColl({
+ required this.actionButtonId,
+ required this.btnTextEn,
+ required this.btnTextAr,
+ required this.actionValue,
+ this.iconOrImage,
+ this.actionType,
+ required this.orderNo,
+ this.url,
+ });
ActionButtonsColl.fromJson(Map json) {
actionButtonId = json['actionButtonId'];
@@ -147,7 +158,9 @@ class ActionButtonsColl {
btnTextAr = json['btnTextAr'];
actionValue = json['actionValue'];
iconOrImage = json['iconOrImage'];
+ actionType = json['actionType'];
orderNo = json['orderNo'];
+ url = json['url'];
}
Map toJson() {
@@ -157,7 +170,9 @@ class ActionButtonsColl {
data['btnTextAr'] = this.btnTextAr;
data['actionValue'] = this.actionValue;
data['iconOrImage'] = this.iconOrImage;
+ data['actionType'] = this.actionType;
data['orderNo'] = this.orderNo;
+ data['url'] = this.url;
return data;
}
}
diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart
index b99872f..be8617d 100644
--- a/lib/provider/dashboard_provider_model.dart
+++ b/lib/provider/dashboard_provider_model.dart
@@ -19,6 +19,7 @@ import 'package:mohem_flutter_app/models/dashboard/list_menu.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/dashboard/mohemm_itg_pending_task_responseitem.dart';
+import 'package:mohem_flutter_app/models/dashboard/courses_response_model.dart';
import 'package:mohem_flutter_app/models/eit/get_eit_transaction_model.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/itg/itg_response_model.dart';
@@ -67,6 +68,11 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
bool isOffersLoading = true;
List getOffersList = [];
+ //Courses
+ bool isCoursesLoading = true;
+ CoursesResponseModel? coursesResponse;
+ List coursesList = [];
+
//Attendance Tracking API's & Methods
Future fetchAttendanceTracking(context) async {
try {
@@ -124,6 +130,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
DrawerMenuItem("assets/images/drawer/mowadhafi.svg", LocaleKeys.keen.tr(), AppRoutes.etqanOvr),
// DrawerMenuItem("assets/images/drawer/car_parking_icon.svg", LocaleKeys.parkingQr.tr(), AppRoutes.parkingQr),
DrawerMenuItem("assets/images/drawer/pending_trasactions.svg", LocaleKeys.pendingTransactions.tr(), AppRoutes.pendingTransactions),
+ DrawerMenuItem("assets/images/drawer/courses.svg", "courses".tr(), AppRoutes.courses),
// DrawerMenuItem("assets/images/drawer/drawer_marathon.svg", LocaleKeys.brainMarathon.tr(), AppRoutes.marathonIntroScreen),
DrawerMenuItem("assets/images/drawer/change_password.svg", LocaleKeys.changePassword.tr(), AppRoutes.changePassword),
];
@@ -403,6 +410,30 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
return res;
}
+ //Courses API's & Methods
+ Future fetchCourses(BuildContext context) async {
+ try {
+ isCoursesLoading = true;
+ notifyListeners();
+
+ coursesResponse = await DashboardApiClient().fetchAllCoursesStatus();
+
+ if (coursesResponse != null && coursesResponse!.success == true) {
+ coursesList = coursesResponse!.coursesStatus ?? [];
+ } else {
+ coursesList = [];
+ }
+
+ isCoursesLoading = false;
+ notifyListeners();
+ } catch (ex) {
+ isCoursesLoading = false;
+ coursesList = [];
+ notifyListeners();
+ Utils.handleException(ex, context, null);
+ }
+ }
+
void notify() {
notifyListeners();
}
diff --git a/lib/ui/landing/itg/its_add_screen_video_image.dart b/lib/ui/landing/itg/its_add_screen_video_image.dart
index 2076d7c..23bfc66 100644
--- a/lib/ui/landing/itg/its_add_screen_video_image.dart
+++ b/lib/ui/landing/itg/its_add_screen_video_image.dart
@@ -6,14 +6,19 @@ import 'package:mohem_flutter_app/app_state/app_state.dart';
import 'package:mohem_flutter_app/classes/colors.dart';
import 'package:mohem_flutter_app/classes/lottie_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';
import 'package:mohem_flutter_app/extensions/string_extensions.dart';
import 'package:mohem_flutter_app/extensions/widget_extensions.dart';
import 'package:mohem_flutter_app/main.dart';
+import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart';
import 'package:mohem_flutter_app/models/itg/advertisement.dart' as ads;
+import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
+import 'package:mohem_flutter_app/ui/my_attendance/services_menu_list_screen.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/widgets/my_video_progress_indicator.dart';
+import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:video_player/video_player.dart';
@@ -26,7 +31,7 @@ class ITGAdsScreen extends StatefulWidget {
class _ITGAdsScreenState extends State {
late Future _futureController;
- late VideoPlayerController _controller;
+ VideoPlayerController? _controller;
bool skip = false;
bool isVideo = false;
bool isImage = false;
@@ -125,7 +130,7 @@ class _ITGAdsScreenState extends State {
@override
void dispose() {
- if (_controller != null) _controller.dispose();
+ _controller?.dispose();
super.dispose();
}
@@ -166,11 +171,11 @@ class _ITGAdsScreenState extends State {
child: isAudio
? Lottie.asset(MyLottieConsts.audioPlaybackLottie)
: AspectRatio(
- aspectRatio: _controller.value.aspectRatio,
+ aspectRatio: _controller!.value.aspectRatio,
child: Stack(
alignment: Alignment.bottomCenter,
children: [
- VideoPlayer(_controller),
+ VideoPlayer(_controller!),
if (advertisementData?.isOptional ?? false)
Positioned(
top: 4,
@@ -230,7 +235,7 @@ class _ITGAdsScreenState extends State {
alignment: Alignment.center,
children: [
MyVideoProgressIndicator(
- _controller,
+ _controller!,
allowScrubbing: false,
//padding: EdgeInsets.only(left: 60, right: 60),
),
@@ -386,16 +391,39 @@ class _ITGAdsScreenState extends State {
if (advertisementData!.actionButtonsColl![index].actionValue.toLowerCase() == "skip") {
Navigator.pop(context);
} else {
- Uri uri = Uri.parse(advertisementData!.actionButtonsColl![index].iconOrImage);
- // if (await canLaunchUrl(uri)) {
- await launchUrl(uri, mode: LaunchMode.externalApplication).catchError((err) {
- // print(err);
- Utils.showToast('Could not launch');
- });
- Navigator.pop(context);
- // } else {
- // Utils.showToast('Could not launch');
- // }
+ if (advertisementData!.actionButtonsColl![index].actionType!.toLowerCase() == 'redirect' && advertisementData!.actionButtonsColl![index].actionValue!.toLowerCase() ==
+ 'external') {
+ Uri uri = Uri.parse(advertisementData!.actionButtonsColl![index].url!);
+ await launchUrl(uri, mode: LaunchMode.externalApplication).catchError((err) {
+ // print(err);
+ Utils.showToast('Could not launch');
+ });
+ Navigator.pop(context);
+ } else if (advertisementData!.actionButtonsColl![index].actionType!.toLowerCase() == 'redirect' && advertisementData!.actionButtonsColl![index].actionValue!.toLowerCase() ==
+ 'internal') {
+
+ await Navigator.pushNamed(context, advertisementData!.actionButtonsColl![index].url!);
+
+ } else if(advertisementData!.actionButtonsColl![index].actionType!.toLowerCase() == 'eit' && advertisementData!.actionButtonsColl![index].actionValue!.toLowerCase() ==
+ 'internal'){
+ var pro = Provider.of(context, listen: false);
+ List _menuList =
+ pro.getMenuEntriesList?.where((element) => element.parentMenuName == advertisementData!.actionButtonsColl![index].url && (element.menuEntryType == "FUNCTION" || element.menuEntryType == "MENU")).toList() ?? [];
+ Navigator.pushNamed(context, AppRoutes.servicesMenuListScreen, arguments: ServicesMenuListScreenParams(advertisementData!.advertisementTitle!, _menuList));
+
+ }
+ else{
+ Uri uri = Uri.parse(advertisementData!.actionButtonsColl![index].iconOrImage);
+ // if (await canLaunchUrl(uri)) {
+ await launchUrl(uri, mode: LaunchMode.externalApplication).catchError((err) {
+ // print(err);
+ Utils.showToast('Could not launch');
+ });
+ Navigator.pop(context);
+ // } else {
+ // Utils.showToast('Could not launch');
+ // }
+ }
}
},
),
diff --git a/lib/ui/screens/courses/courses_screen.dart b/lib/ui/screens/courses/courses_screen.dart
new file mode 100644
index 0000000..598b6cf
--- /dev/null
+++ b/lib/ui/screens/courses/courses_screen.dart
@@ -0,0 +1,333 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_inappwebview/flutter_inappwebview.dart';
+import 'package:flutter_svg/flutter_svg.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/inAppWebView.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/dashboard/courses_response_model.dart';
+import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
+import 'package:mohem_flutter_app/theme/colors.dart' as theme_colors;
+import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
+import 'package:mohem_flutter_app/widgets/sso_webview_widget.dart';
+import 'package:provider/provider.dart';
+import 'package:pull_to_refresh/pull_to_refresh.dart';
+
+class CoursesScreen extends StatefulWidget {
+ const CoursesScreen({Key? key}) : super(key: key);
+
+ @override
+ _CoursesScreenState createState() => _CoursesScreenState();
+}
+
+class _CoursesScreenState extends State {
+ late DashboardProviderModel data;
+ final RefreshController _refreshController = RefreshController(initialRefresh: false);
+
+ @override
+ void initState() {
+ super.initState();
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ data = Provider.of(context, listen: false);
+ _onRefresh();
+ });
+ }
+
+ void _onRefresh() async {
+ await data.fetchCourses(context);
+ _refreshController.refreshCompleted();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBarWidget(
+ context,
+ title: LocaleKeys.courses.tr(),
+ ),
+ body: Consumer(
+ builder: (context, provider, child) {
+ return SmartRefresher(
+ enablePullDown: true,
+ enablePullUp: false,
+ controller: _refreshController,
+ onRefresh: _onRefresh,
+ child: provider.isCoursesLoading
+ ? _buildShimmerLoading()
+ : provider.coursesList.isEmpty
+ ? _buildEmptyState()
+ : _buildCoursesList(provider.coursesList),
+ );
+ },
+ ),
+ );
+ }
+
+ Widget _buildShimmerLoading() {
+ return ListView.builder(
+ padding: const EdgeInsets.all(21),
+ itemCount: 5,
+ itemBuilder: (context, index) {
+ return Container(
+ margin: const EdgeInsets.only(bottom: 12),
+ height: 150,
+ 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(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ height: 20,
+ width: 200,
+ decoration: BoxDecoration(
+ color: Colors.grey[300],
+ borderRadius: BorderRadius.circular(4),
+ ),
+ ).toShimmer(),
+ 12.height,
+ Container(
+ height: 14,
+ width: 100,
+ decoration: BoxDecoration(
+ color: Colors.grey[300],
+ borderRadius: BorderRadius.circular(4),
+ ),
+ ).toShimmer(),
+ const Spacer(),
+ Container(
+ height: 40,
+ decoration: BoxDecoration(
+ color: Colors.grey[300],
+ borderRadius: BorderRadius.circular(6),
+ ),
+ ).toShimmer(),
+ ],
+ ).paddingAll(14),
+ );
+ },
+ );
+ }
+
+ Widget _buildEmptyState() {
+ return Utils.getNoDataWidget(context);
+ }
+
+ Widget _buildCoursesList(List courses) {
+ return ListView.separated(
+ physics: const BouncingScrollPhysics(),
+ shrinkWrap: true,
+ padding: const EdgeInsets.all(21),
+ itemBuilder: (BuildContext context, int index) {
+ return _buildCourseCard(courses[index]);
+ },
+ separatorBuilder: (BuildContext context, int index) => 12.height,
+ itemCount: courses.length,
+ );
+ }
+
+ Widget _buildCourseCard(CourseStatus course) {
+ bool isArabic = AppState().isArabic(context);
+ String courseName = isArabic && course.cNameAr != null && course.cNameAr!.isNotEmpty
+ ? course.cNameAr!
+ : course.cFullname ?? '';
+
+ String status = course.cStatus ?? 'Unknown';
+ Color statusColor = _getStatusColor(status);
+
+ return InkWell(
+ onTap: () => _openCourse(course.cUrl, course.token),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // Course Title
+ Row(
+ children: [
+ Container(
+ padding: const EdgeInsets.all(8),
+ decoration: BoxDecoration(
+ color: MyColors.lightGreyEFColor,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: const Icon(
+ Icons.school_rounded,
+ color: MyColors.gradiantStartColor,
+ size: 20,
+ ),
+ ),
+ 12.width,
+ Expanded(
+ child: courseName.toText14(isBold: true, color: MyColors.darkTextColor),
+ ),
+ ],
+ ),
+ 12.height,
+ // Status Badge
+ Row(
+ children: [
+ Icon(
+ _getStatusIcon(status),
+ size: 16,
+ color: statusColor,
+ ),
+ 6.width,
+ LocaleKeys.status.tr().toText11(color: MyColors.grey98Color),
+ 8.width,
+ Container(
+ padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
+ decoration: BoxDecoration(
+ color: statusColor.withOpacity(0.1),
+ borderRadius: BorderRadius.circular(12),
+ border: Border.all(
+ color: statusColor.withOpacity(0.3),
+ width: 1,
+ ),
+ ),
+ child: status.toText10(color: statusColor, isBold: true),
+ ),
+ ],
+ ),
+ 10.height,
+ // Start Date
+ if (course.cEnddate != null && course.cEnddate!.isNotEmpty)
+ Row(
+ children: [
+ const Icon(
+ Icons.calendar_month_rounded,
+ size: 16,
+ color: MyColors.darkIconColor,
+ ),
+ 6.width,
+ LocaleKeys.endDateT.tr().toText11(color: MyColors.grey98Color),
+ 8.width,
+ Expanded(
+ child: _formatDate(course.cEnddate!).toText11(
+ color: MyColors.darkTextColor,
+ isBold: true,
+ ),
+ ),
+ ],
+ ),
+ if (course.cStartdate != null && course.cStartdate!.isNotEmpty) 14.height,
+ // Open Button
+ Row(
+ mainAxisAlignment: MainAxisAlignment.end,
+ children: [
+ LocaleKeys.open.tr().toText12(color: MyColors.gradiantStartColor, isUnderLine: true),
+ 6.width,
+ RotatedBox(
+ quarterTurns: AppState().isArabic(context) ? 2 : 4,
+ child: SvgPicture.asset(
+ "assets/images/arrow_next.svg",
+ color: MyColors.gradiantStartColor,
+ width: 16,
+ height: 16,
+ ),
+ ),
+ ],
+ ),
+ ],
+ ).objectContainerView(),
+ );
+ }
+
+
+ IconData _getStatusIcon(String status) {
+ if (status.toLowerCase().contains('passed')) {
+ return Icons.check_circle_rounded;
+ } else if (status.toLowerCase().contains('not passed') || status.toLowerCase().contains('failed')) {
+ return Icons.cancel_rounded;
+ } else if (status.toLowerCase().contains('in progress')) {
+ return Icons.pending_rounded;
+ } else {
+ return Icons.info_rounded;
+ }
+ }
+
+ Color _getStatusColor(String status) {
+ if (status.toLowerCase().contains('passed') && !status.toLowerCase().contains('not passed')) {
+ return Colors.green;
+ } else if (status.toLowerCase().contains('not passed') || status.toLowerCase().contains('failed')) {
+ return Colors.red;
+ } else if (status.toLowerCase().contains('in progress')) {
+ return Colors.yellow[700]!;
+ } else {
+ return MyColors.grey98Color;
+ }
+ }
+
+ DateTime? _parseDate(String? date) {
+ if (date == null || date.isEmpty) return null;
+ try {
+ return DateTime.parse(date.replaceAll(' ', 'T'));
+ } catch (e) {
+ try {
+ DateFormat format = DateFormat('yyyy-MM-dd HH:mm:ss');
+ return format.parse(date);
+ } catch (e) {
+ return null;
+ }
+ }
+ }
+
+ String _formatDate(String date) {
+ DateTime? dateTime = _parseDate(date);
+ if (dateTime == null) return date;
+
+ bool isArabic = AppState().isArabic(context);
+ DateFormat format = DateFormat('dd MMM yyyy, hh:mm a', isArabic ? 'ar' : 'en');
+ return format.format(dateTime);
+ }
+
+ void _openCourse(String? url, String? jwtToken) {
+
+
+ Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: url, jwtToken: jwtToken)));
+ //Utils.showToast(LocaleKeys.courseUrlNotAvailable.tr());
+
+
+ // return;
+ // }
+ //
+ // MyInAppBrowser browser = MyInAppBrowser(
+ // onExitCallback: () {},
+ // onLoadStartCallback: (String url) {},
+ // );
+ //
+ // var settings = InAppBrowserClassSettings(
+ // webViewSettings: InAppWebViewSettings(
+ // useShouldOverrideUrlLoading: false,
+ // transparentBackground: false,
+ // ),
+ // browserSettings: InAppBrowserSettings(
+ // hideUrlBar: true,
+ // hideTitleBar: false,
+ // toolbarTopBackgroundColor: theme_colors.primaryColor,
+ // ),
+ // );
+ //
+ // browser.openUrlRequest(
+ // urlRequest: URLRequest(url: WebUri(url)),
+ // settings: settings,
+ // );
+ }
+
+ @override
+ void dispose() {
+ _refreshController.dispose();
+ super.dispose();
+ }
+}
diff --git a/lib/ui/screens/ticket/ticket_detailed_screen.dart b/lib/ui/screens/ticket/ticket_detailed_screen.dart
index d406153..c4d232f 100644
--- a/lib/ui/screens/ticket/ticket_detailed_screen.dart
+++ b/lib/ui/screens/ticket/ticket_detailed_screen.dart
@@ -9,6 +9,7 @@ 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/dashboard/get_accural_ticket_balance_model.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
@@ -55,7 +56,7 @@ class _TicketDetailedScreenState extends State {
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
- appBar: AppBarWidget(context, title: "Ticket Details"),
+ appBar: AppBarWidget(context, title:LocaleKeys.ticketDetails.tr()),
body: Column(
children: [
ListView(
@@ -74,7 +75,7 @@ class _TicketDetailedScreenState extends State {
children: [
Row(
children: [
- "Current Ticket Balance".toText20().expanded,
+ LocaleKeys.currentTicketBalance.tr().toText20().expanded,
Row(
children: [
const Icon(Icons.calendar_month_rounded, color: MyColors.darkIconColor, size: 16),
@@ -118,7 +119,7 @@ class _TicketDetailedScreenState extends State {
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [
- "Tickets History".toText20().expanded,
+ LocaleKeys.ticketHistory.tr().toText20().expanded,
12.height,
ListView.separated(
physics: const NeverScrollableScrollPhysics(),
@@ -175,7 +176,7 @@ class _TicketDetailedScreenState extends State {
],
],
).expanded,
- DefaultButton("Proceed For Booking", () {
+ DefaultButton(LocaleKeys.proceedBooking.tr(), () {
if (widget.url != null && widget.jwtToken != null) {
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: widget.url, jwtToken: widget.jwtToken)));
}
diff --git a/lib/widgets/sso_webview_widget.dart b/lib/widgets/sso_webview_widget.dart
index 5877a63..ee4ed7a 100644
--- a/lib/widgets/sso_webview_widget.dart
+++ b/lib/widgets/sso_webview_widget.dart
@@ -15,10 +15,13 @@ class SsoLoginWebView extends StatefulWidget {
class _SsoLoginWebViewState extends State {
late final WebViewController _controller;
-
+ String? url;
@override
void initState() {
// TODO: implement initState
+
+ url = widget.url ?? "https://Paxes-release.techmaster.in/SSO/HMG";
+
super.initState();
_controller =
WebViewController()
@@ -46,7 +49,7 @@ class _SsoLoginWebViewState extends State {
-
Redirecting...
@@ -56,6 +59,6 @@ class _SsoLoginWebViewState extends State {
@override
Widget build(BuildContext context) {
- return Scaffold(appBar: AppBar(title: Text('Logging in...')), body: WebViewWidget(controller: _controller));
+ return Scaffold(appBar: null, body:SafeArea(child: WebViewWidget(controller: _controller)));
}
}