E-learning Mandatory Courses - CR# 7150

Courses-CR#-7150
Sultan khan 2 weeks ago
parent 89ea554c7a
commit a426bc332e

@ -642,5 +642,19 @@
"yearsUsed": "سنوات الاستخدام", "yearsUsed": "سنوات الاستخدام",
"faRequest": "طلب التخلص من FA", "faRequest": "طلب التخلص من FA",
"showMore": "عرض المزيد", "showMore": "عرض المزيد",
"buyerDetails": "تفاصيل المشتري" "buyerDetails": "تفاصيل المشتري",
} "ticketDetails": "تفاصيل التذكرة",
"currentTicketBalance": "رصيد التذكرة الحالي",
"ticketHistory": "تاريخ التذكرة",
"proceedBooking": "المتابعة للحجز",
"courses": "الدورات التدريبية",
"course": "دورة",
"status": "الحالة",
"deadline": "الموعد النهائي",
"overdue": "متأخر",
"open": "فتح",
"noCoursesAvailable": "لا توجد دورات متاحة",
"pullToRefresh": "اسحب للتحديث",
"courseUrlNotAvailable": "رابط الدورة غير متوفر"
}

@ -640,5 +640,18 @@
"yearsUsed": "Years Used", "yearsUsed": "Years Used",
"faRequest":"FA Disposal Request", "faRequest":"FA Disposal Request",
"showMore": "Show More", "showMore": "Show More",
"buyerDetails": "Buyer Details" "buyerDetails": "Buyer Details",
"ticketDetails": "Ticket Details",
"currentTicketBalance": "Current Ticket Balance",
"ticketHistory": "Ticket History",
"proceedBooking": "Proceed For Booking",
"courses": "Courses",
"course": "Course",
"status": "Status",
"deadline": "Deadline",
"overdue": "Overdue",
"open": "Open",
"noCoursesAvailable": "No Courses Available",
"pullToRefresh": "Pull to refresh",
"courseUrlNotAvailable": "Course URL not available"
} }

@ -14,6 +14,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/get_attendance_tracking_list_model.dart';
import 'package:mohem_flutter_app/models/dashboard/itg_forms_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/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/generic_response_model.dart';
import 'package:mohem_flutter_app/models/itg/itg_main_response.dart'; import 'package:mohem_flutter_app/models/itg/itg_main_response.dart';
import 'package:mohem_flutter_app/models/itg/itg_response_model.dart'; import 'package:mohem_flutter_app/models/itg/itg_response_model.dart';
@ -411,4 +412,29 @@ class DashboardApiClient {
// postParams, // postParams,
// ); // );
// } // }
//Fetch All Courses Status
Future<CoursesResponseModel?> fetchAllCoursesStatus() async {
String url = "https://elearning.hmg.com/moodle_dev/api/Fetch_All_Courses_Status.php";
Map<String, String> 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<String, dynamic> postParams = {
"username": AppState().memberInformationList?.eMPLOYEENUMBER ?? "",
};
return await ApiClient().postJsonForObject(
(json) {
CoursesResponseModel responseData = CoursesResponseModel.fromJson(json);
return responseData;
},
url,
postParams,
headers: headers,
isFormData: false,
);
}
} }

@ -54,6 +54,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/announcement_details.dart';
import 'package:mohem_flutter_app/ui/screens/announcements/announcements.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/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/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/eit/add_eit.dart';
import 'package:mohem_flutter_app/ui/screens/event_activity/event_activity.dart'; import 'package:mohem_flutter_app/ui/screens/event_activity/event_activity.dart';
@ -166,6 +167,9 @@ class AppRoutes {
static const String offersAndDiscounts = "/offersAndDiscounts"; static const String offersAndDiscounts = "/offersAndDiscounts";
static const String offersAndDiscountsDetails = "/offersAndDiscountsDetails"; static const String offersAndDiscountsDetails = "/offersAndDiscountsDetails";
// Courses
static const String courses = "/courses";
//Pay slip //Pay slip
static const String monthlyPaySlip = "/monthlyPaySlip"; static const String monthlyPaySlip = "/monthlyPaySlip";
@ -291,6 +295,9 @@ class AppRoutes {
offersAndDiscounts: (BuildContext context) => OffersAndDiscountsHome(), offersAndDiscounts: (BuildContext context) => OffersAndDiscountsHome(),
offersAndDiscountsDetails: (BuildContext context) => OffersAndDiscountsDetails(), offersAndDiscountsDetails: (BuildContext context) => OffersAndDiscountsDetails(),
// Courses
courses: (BuildContext context) => const CoursesScreen(),
//pay slip //pay slip
monthlyPaySlip: (BuildContext context) => MonthlyPaySlipScreen(), monthlyPaySlip: (BuildContext context) => MonthlyPaySlipScreen(),
performanceEvaluation: (BuildContext context) => PerformanceAppraisal(), performanceEvaluation: (BuildContext context) => PerformanceAppraisal(),

@ -563,7 +563,7 @@ class CodegenLoader extends AssetLoader{
"addAtLeastOneAttachment": "الرجاء إضافة مرفق واحد على الأقل.", "addAtLeastOneAttachment": "الرجاء إضافة مرفق واحد على الأقل.",
"pleaseClickButtonToJoinMarathon": "الرجاء الضغط على الزر أدناه للانضمام إلى الماراثون", "pleaseClickButtonToJoinMarathon": "الرجاء الضغط على الزر أدناه للانضمام إلى الماراثون",
"youCannotJoinTheMarathon": "لا يمكنك الانضمام إلى الماراثون لأنك تجاوزت الحد الزمني", "youCannotJoinTheMarathon": "لا يمكنك الانضمام إلى الماراثون لأنك تجاوزت الحد الزمني",
"open": "يفتح", "open": "فتح",
"paymentRequest": "طلب الدفع", "paymentRequest": "طلب الدفع",
"paymentDetails": "تفاصيل الدفع", "paymentDetails": "تفاصيل الدفع",
"requestNo": "رقم الطلب", "requestNo": "رقم الطلب",
@ -655,7 +655,18 @@ class CodegenLoader extends AssetLoader{
"yearsUsed": "سنوات الاستخدام", "yearsUsed": "سنوات الاستخدام",
"faRequest": "طلب التخلص من FA", "faRequest": "طلب التخلص من FA",
"showMore": "عرض المزيد", "showMore": "عرض المزيد",
"buyerDetails": "تفاصيل المشتري" "buyerDetails": "تفاصيل المشتري",
"ticketDetails": "تفاصيل التذكرة",
"currentTicketBalance": "رصيد التذكرة الحالي",
"ticketHistory": "تاريخ التذكرة",
"proceedBooking": "المتابعة للحجز",
"courses": "الدورات التدريبية",
"course": "دورة",
"status": "الحالة",
"deadline": "الموعد النهائي",
"noCoursesAvailable": "لا توجد دورات متاحة",
"pullToRefresh": "اسحب للتحديث",
"courseUrlNotAvailable": "رابط الدورة غير متوفر"
}; };
static const Map<String,dynamic> _en_US = { static const Map<String,dynamic> _en_US = {
"mohemm": "Mohemm", "mohemm": "Mohemm",
@ -1220,7 +1231,7 @@ static const Map<String,dynamic> _en_US = {
"userSearch": "User Search", "userSearch": "User Search",
"userName": "User Name", "userName": "User Name",
"userId": "UserID", "userId": "UserID",
"open": "open", "open": "Open",
"addAtLeastOneAttachment": "Please add at least one attachment.", "addAtLeastOneAttachment": "Please add at least one attachment.",
"pleaseClickButtonToJoinMarathon": "Press the button below to join the Marathon.", "pleaseClickButtonToJoinMarathon": "Press the button below to join the Marathon.",
"youCannotJoinTheMarathon": "You cannot join the Marathon because you have exceeded the time limit.", "youCannotJoinTheMarathon": "You cannot join the Marathon because you have exceeded the time limit.",
@ -1298,7 +1309,18 @@ static const Map<String,dynamic> _en_US = {
"yearsUsed": "Years Used", "yearsUsed": "Years Used",
"faRequest": "FA Disposal Request", "faRequest": "FA Disposal Request",
"showMore": "Show More", "showMore": "Show More",
"buyerDetails": "Buyer Details" "buyerDetails": "Buyer Details",
"ticketDetails": "Ticket Details",
"currentTicketBalance": "Current Ticket Balance",
"ticketHistory": "Ticket History",
"proceedBooking": "Proceed For Booking",
"courses": "Courses",
"course": "Course",
"status": "Status",
"deadline": "Deadline",
"noCoursesAvailable": "No Courses Available",
"pullToRefresh": "Pull to refresh",
"courseUrlNotAvailable": "Course URL not available"
}; };
static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US}; static const Map<String, Map<String,dynamic>> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US};
} }

@ -628,5 +628,17 @@ abstract class LocaleKeys {
static const faRequest = 'faRequest'; static const faRequest = 'faRequest';
static const showMore = 'showMore'; static const showMore = 'showMore';
static const buyerDetails = 'buyerDetails'; static const buyerDetails = 'buyerDetails';
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';
} }

@ -0,0 +1,91 @@
class CoursesResponseModel {
bool? success;
int? status;
String? username;
List<CourseStatus>? coursesStatus;
int? totalCourses;
CoursesResponseModel({
this.success,
this.status,
this.username,
this.coursesStatus,
this.totalCourses,
});
CoursesResponseModel.fromJson(Map<String, dynamic> json) {
success = json['success'];
status = json['status'];
username = json['username'];
if (json['courses_status'] != null) {
coursesStatus = <CourseStatus>[];
json['courses_status'].forEach((v) {
coursesStatus!.add(CourseStatus.fromJson(v));
});
}
totalCourses = json['total_courses'];
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
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;
CourseStatus({
this.cId,
this.cFullname,
this.cNameAr,
this.cShortname,
this.cStatus,
this.cIsMohemmAffective,
this.cStartdate,
this.cEnddate,
this.cUrl,
});
CourseStatus.fromJson(Map<String, dynamic> 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'];
}
Map<String, dynamic> toJson() {
Map<String, dynamic> data = <String, dynamic>{};
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;
return data;
}
}

@ -136,10 +136,21 @@ class ActionButtonsColl {
late String btnTextEn; late String btnTextEn;
late String btnTextAr; late String btnTextAr;
late String actionValue; late String actionValue;
late dynamic iconOrImage; dynamic iconOrImage;
String? actionType;
late int orderNo; 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<String, dynamic> json) { ActionButtonsColl.fromJson(Map<String, dynamic> json) {
actionButtonId = json['actionButtonId']; actionButtonId = json['actionButtonId'];
@ -147,7 +158,9 @@ class ActionButtonsColl {
btnTextAr = json['btnTextAr']; btnTextAr = json['btnTextAr'];
actionValue = json['actionValue']; actionValue = json['actionValue'];
iconOrImage = json['iconOrImage']; iconOrImage = json['iconOrImage'];
actionType = json['actionType'];
orderNo = json['orderNo']; orderNo = json['orderNo'];
url = json['url'];
} }
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
@ -157,7 +170,9 @@ class ActionButtonsColl {
data['btnTextAr'] = this.btnTextAr; data['btnTextAr'] = this.btnTextAr;
data['actionValue'] = this.actionValue; data['actionValue'] = this.actionValue;
data['iconOrImage'] = this.iconOrImage; data['iconOrImage'] = this.iconOrImage;
data['actionType'] = this.actionType;
data['orderNo'] = this.orderNo; data['orderNo'] = this.orderNo;
data['url'] = this.url;
return data; return data;
} }
} }

@ -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/menu_entries.dart';
import 'package:mohem_flutter_app/models/dashboard/menus.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/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/eit/get_eit_transaction_model.dart';
import 'package:mohem_flutter_app/models/generic_response_model.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart';
import 'package:mohem_flutter_app/models/itg/itg_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; bool isOffersLoading = true;
List<OffersListModel> getOffersList = []; List<OffersListModel> getOffersList = [];
//Courses
bool isCoursesLoading = true;
CoursesResponseModel? coursesResponse;
List<CourseStatus> coursesList = [];
//Attendance Tracking API's & Methods //Attendance Tracking API's & Methods
Future<bool> fetchAttendanceTracking(context) async { Future<bool> fetchAttendanceTracking(context) async {
try { try {
@ -121,6 +127,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
DrawerMenuItem("assets/images/drawer/performance_evaluation.svg", LocaleKeys.performanceEvaluation.tr(), AppRoutes.performanceEvaluation), DrawerMenuItem("assets/images/drawer/performance_evaluation.svg", LocaleKeys.performanceEvaluation.tr(), AppRoutes.performanceEvaluation),
DrawerMenuItem("assets/images/drawer/mowadhafi.svg", LocaleKeys.mowadhafhi.tr(), AppRoutes.mowadhafhi), DrawerMenuItem("assets/images/drawer/mowadhafi.svg", LocaleKeys.mowadhafhi.tr(), AppRoutes.mowadhafhi),
DrawerMenuItem("assets/images/drawer/pending_trasactions.svg", LocaleKeys.pendingTransactions.tr(), AppRoutes.pendingTransactions), 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/drawer_marathon.svg", LocaleKeys.brainMarathon.tr(), AppRoutes.marathonIntroScreen),
DrawerMenuItem("assets/images/drawer/change_password.svg", LocaleKeys.changePassword.tr(), AppRoutes.changePassword), DrawerMenuItem("assets/images/drawer/change_password.svg", LocaleKeys.changePassword.tr(), AppRoutes.changePassword),
]; ];
@ -409,6 +416,30 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
return res; return res;
} }
//Courses API's & Methods
Future<void> 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() { void notify() {
notifyListeners(); notifyListeners();
} }

@ -26,7 +26,7 @@ class ITGAdsScreen extends StatefulWidget {
class _ITGAdsScreenState extends State<ITGAdsScreen> { class _ITGAdsScreenState extends State<ITGAdsScreen> {
late Future<VideoPlayerController> _futureController; late Future<VideoPlayerController> _futureController;
late VideoPlayerController _controller; VideoPlayerController? _controller;
bool skip = false; bool skip = false;
bool isVideo = false; bool isVideo = false;
bool isImage = false; bool isImage = false;
@ -125,7 +125,7 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
@override @override
void dispose() { void dispose() {
if (_controller != null) _controller.dispose(); _controller?.dispose();
super.dispose(); super.dispose();
} }
@ -166,11 +166,11 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
child: isAudio child: isAudio
? Lottie.asset(MyLottieConsts.audioPlaybackLottie) ? Lottie.asset(MyLottieConsts.audioPlaybackLottie)
: AspectRatio( : AspectRatio(
aspectRatio: _controller.value.aspectRatio, aspectRatio: _controller!.value.aspectRatio,
child: Stack( child: Stack(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
children: [ children: [
VideoPlayer(_controller), VideoPlayer(_controller!),
if (advertisementData?.isOptional ?? false) if (advertisementData?.isOptional ?? false)
Positioned( Positioned(
top: 4, top: 4,
@ -230,7 +230,7 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
MyVideoProgressIndicator( MyVideoProgressIndicator(
_controller, _controller!,
allowScrubbing: false, allowScrubbing: false,
//padding: EdgeInsets.only(left: 60, right: 60), //padding: EdgeInsets.only(left: 60, right: 60),
), ),
@ -386,16 +386,29 @@ class _ITGAdsScreenState extends State<ITGAdsScreen> {
if (advertisementData!.actionButtonsColl![index].actionValue.toLowerCase() == "skip") { if (advertisementData!.actionButtonsColl![index].actionValue.toLowerCase() == "skip") {
Navigator.pop(context); Navigator.pop(context);
} else { } else {
Uri uri = Uri.parse(advertisementData!.actionButtonsColl![index].iconOrImage); if (advertisementData!.actionButtonsColl![index].actionType!.toLowerCase() == 'redirect' && advertisementData!.actionButtonsColl![index].actionValue!.toLowerCase() ==
// if (await canLaunchUrl(uri)) { 'external') {
await launchUrl(uri, mode: LaunchMode.externalApplication).catchError((err) { Uri uri = Uri.parse(advertisementData!.actionButtonsColl![index].url!);
// print(err); await launchUrl(uri, mode: LaunchMode.externalApplication).catchError((err) {
Utils.showToast('Could not launch'); // print(err);
}); Utils.showToast('Could not launch');
Navigator.pop(context); });
// } else { Navigator.pop(context);
// Utils.showToast('Could not launch'); } else if (advertisementData!.actionButtonsColl![index].actionType!.toLowerCase() == 'redirect' && advertisementData!.actionButtonsColl![index].actionValue!.toLowerCase() ==
// } 'internal') {
await Navigator.pushNamed(context, advertisementData!.actionButtonsColl![index].url!);
} 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');
// }
}
} }
}, },
), ),

@ -0,0 +1,328 @@
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: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<CoursesScreen> {
late DashboardProviderModel data;
final RefreshController _refreshController = RefreshController(initialRefresh: false);
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
data = Provider.of<DashboardProviderModel>(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<DashboardProviderModel>(
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<CourseStatus> 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 ?? ''),
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.cStartdate != null && course.cStartdate!.isNotEmpty)
Row(
children: [
const Icon(
Icons.calendar_month_rounded,
size: 16,
color: MyColors.darkIconColor,
),
6.width,
LocaleKeys.startDateT.tr().toText11(color: MyColors.grey98Color),
8.width,
Expanded(
child: _formatDate(course.cStartdate!).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) {
if (url.isEmpty) {
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();
}
}

@ -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/int_extensions.dart';
import 'package:mohem_flutter_app/extensions/string_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/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/models/dashboard/get_accural_ticket_balance_model.dart';
import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart';
import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart';
@ -55,7 +56,7 @@ class _TicketDetailedScreenState extends State<TicketDetailedScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: Colors.white, backgroundColor: Colors.white,
appBar: AppBarWidget(context, title: "Ticket Details"), appBar: AppBarWidget(context, title:LocaleKeys.ticketDetails.tr()),
body: Column( body: Column(
children: <Widget>[ children: <Widget>[
ListView( ListView(
@ -74,7 +75,7 @@ class _TicketDetailedScreenState extends State<TicketDetailedScreen> {
children: <Widget>[ children: <Widget>[
Row( Row(
children: <Widget>[ children: <Widget>[
"Current Ticket Balance".toText20().expanded, LocaleKeys.currentTicketBalance.tr().toText20().expanded,
Row( Row(
children: <Widget>[ children: <Widget>[
const Icon(Icons.calendar_month_rounded, color: MyColors.darkIconColor, size: 16), const Icon(Icons.calendar_month_rounded, color: MyColors.darkIconColor, size: 16),
@ -118,7 +119,7 @@ class _TicketDetailedScreenState extends State<TicketDetailedScreen> {
shrinkWrap: true, shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
children: <Widget>[ children: <Widget>[
"Tickets History".toText20().expanded, LocaleKeys.ticketHistory.tr().toText20().expanded,
12.height, 12.height,
ListView.separated( ListView.separated(
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
@ -175,7 +176,7 @@ class _TicketDetailedScreenState extends State<TicketDetailedScreen> {
], ],
], ],
).expanded, ).expanded,
DefaultButton("Proceed For Booking", () { DefaultButton(LocaleKeys.proceedBooking.tr(), () {
if (widget.url != null && widget.jwtToken != null) { if (widget.url != null && widget.jwtToken != null) {
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: widget.url, jwtToken: widget.jwtToken))); Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: widget.url, jwtToken: widget.jwtToken)));
} }

Loading…
Cancel
Save