From 3d91a1444e5681ea9fe220a9a09e5512aea26468 Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Wed, 26 Nov 2025 12:06:37 +0300 Subject: [PATCH 01/30] wallet ticket fix --- lib/ui/landing/widget/menus_widget.dart | 31 +++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/lib/ui/landing/widget/menus_widget.dart b/lib/ui/landing/widget/menus_widget.dart index bc5dceb..be04937 100644 --- a/lib/ui/landing/widget/menus_widget.dart +++ b/lib/ui/landing/widget/menus_widget.dart @@ -1,13 +1,17 @@ import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:mohem_flutter_app/api/eit_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/sso_auth_model.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart'; +import 'package:mohem_flutter_app/ui/screens/ticket/ticket_detailed_screen.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:provider/provider.dart'; @@ -117,8 +121,31 @@ class MenusWidget extends StatelessWidget { ), ], ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), - ).onPress(() { - Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(LocaleKeys.ticketBalance.tr(), "HMG_TKT_NEW_EIT_SS")); + ).onPress(() async { + // + // // Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => TicketDetailedScreen(url: url, jwtToken: ssoToken.data!.accessToken))); + // + // Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(LocaleKeys.ticketBalance.tr(), "HMG_TKT_NEW_EIT_SS")); + var pro = Provider.of(context, listen: false); + + // if (menuEntry.menuName == "HMG_TICKET_REQUESTS") { + Utils.showLoading(context); + //Ticket Work + if (pro.ticketBookingResponse != null && pro.ticketBookingResponse!.success) { + SSOAuthModel? ssoToken = await pro.fetchSSOAuthRedirection(clientID: pro.ticketBookingResponse!.clientId); + if (ssoToken != null) { + dynamic url = await pro.fetchURLRedirection(token: ssoToken.data!.accessToken!); + await pro.fetchTicketAccuralBalance(context, DateTime.now()); + pro.ticketHistoryTransactionList = await EITApiClient().getEITTransactions("HMG_TICKET_ITENARY_HR_EIT_SS", isCompleteList: true); + + Utils.hideLoading(context); + // Here Need Work + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => TicketDetailedScreen(url: url, jwtToken: ssoToken.data!.accessToken))); + } + //} + } else { + Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(LocaleKeys.ticketBalance.tr(), "HMG_TKT_NEW_EIT_SS")); + } }), ], ); From 90bf529865a76b7e4003c53704843de650106223 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 6 Jan 2026 09:24:29 +0300 Subject: [PATCH 02/30] CR-19329 - ERP MOWADHFAI System changes --- lib/ui/screens/mowadhafhi/mowadhafhi_hr_request.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/ui/screens/mowadhafhi/mowadhafhi_hr_request.dart b/lib/ui/screens/mowadhafhi/mowadhafhi_hr_request.dart index 44b3833..e7d2fde 100644 --- a/lib/ui/screens/mowadhafhi/mowadhafhi_hr_request.dart +++ b/lib/ui/screens/mowadhafhi/mowadhafhi_hr_request.dart @@ -150,7 +150,9 @@ class _MowadhafhiHRRequestState extends State { isReadOnly: false, onChange: (String value) { debugPrint(value); - description = value; + setState(() { + description = value; + }); }, ), ], @@ -171,7 +173,7 @@ class _MowadhafhiHRRequestState extends State { } bool checkValidation() { - if (selectedServiceType == null || selectedDepartment == null || selectedSection == null || selectedTopic == null) { + if (selectedServiceType == null || selectedDepartment == null || selectedSection == null || selectedTopic == null || description == "") { return false; } else { return true; From 99414bf787122b2610936699cc7ac110723fc505 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Wed, 7 Jan 2026 14:57:25 +0300 Subject: [PATCH 03/30] Itiqan Ovr Completed --- assets/langs/ar-SA.json | 6 +- assets/langs/en-US.json | 6 +- lib/api/etqan_ovr_api_client.dart | 129 ++++++++ lib/classes/consts.dart | 4 +- lib/config/routes.dart | 14 +- lib/generated/codegen_loader.g.dart | 12 +- lib/generated/locale_keys.g.dart | 4 + lib/main.dart | 2 + .../etqan_get_employee_incident_report.dart | 45 +++ .../etqan_ovr/etqan_get_requests_model.dart | 53 ++++ .../etqan_ovr/etqan_getprojects_model.dart | 53 ++++ lib/provider/dashboard_provider_model.dart | 1 + lib/provider/etqan_ovr_provider.dart | 181 ++++++++++++ lib/ui/etqan_ovr/etqan_create_request.dart | 279 ++++++++++++++++++ lib/ui/etqan_ovr/etqan_ovr_home.dart | 105 +++++++ lib/ui/etqan_ovr/etqan_request_detailed.dart | 110 +++++++ pubspec.yaml | 3 + 17 files changed, 1000 insertions(+), 7 deletions(-) create mode 100644 lib/api/etqan_ovr_api_client.dart create mode 100644 lib/models/etqan_ovr/etqan_get_employee_incident_report.dart create mode 100644 lib/models/etqan_ovr/etqan_get_requests_model.dart create mode 100644 lib/models/etqan_ovr/etqan_getprojects_model.dart create mode 100644 lib/provider/etqan_ovr_provider.dart create mode 100644 lib/ui/etqan_ovr/etqan_create_request.dart create mode 100644 lib/ui/etqan_ovr/etqan_ovr_home.dart create mode 100644 lib/ui/etqan_ovr/etqan_request_detailed.dart diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 916e8fb..ea64f78 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -642,5 +642,9 @@ "yearsUsed": "سنوات الاستخدام", "faRequest": "طلب التخلص من FA", "showMore": "عرض المزيد", - "buyerDetails": "تفاصيل المشتري" + "buyerDetails": "تفاصيل المشتري", + "etqanOVR": "إتقان (OVR)", + "etqanRequest": "طلب إلكتروني", + "selectProject": "حدد المشروع", + "project": "مشروع" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 5fcb35c..630e0ad 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -640,5 +640,9 @@ "yearsUsed": "Years Used", "faRequest":"FA Disposal Request", "showMore": "Show More", - "buyerDetails": "Buyer Details" + "buyerDetails": "Buyer Details", + "etqanOVR": "Etqan (OVR)", + "etqanRequest": "Etqan Request", + "selectProject": "Select Project", + "project": "Project" } \ No newline at end of file diff --git a/lib/api/etqan_ovr_api_client.dart b/lib/api/etqan_ovr_api_client.dart new file mode 100644 index 0000000..f9d97bd --- /dev/null +++ b/lib/api/etqan_ovr_api_client.dart @@ -0,0 +1,129 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:mohem_flutter_app/api/api_client.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/consts.dart'; +import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_employee_incident_report.dart'; +import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_requests_model.dart'; +import 'package:mohem_flutter_app/models/etqan_ovr/etqan_getprojects_model.dart'; +import 'package:mohem_flutter_app/models/generic_response_model.dart'; +import 'package:mohem_flutter_app/models/mowadhafhi/get_department_sections.dart'; +import 'package:mohem_flutter_app/models/mowadhafhi/get_project_departments.dart'; +import 'package:mohem_flutter_app/models/mowadhafhi/get_projects.dart'; +import 'package:mohem_flutter_app/models/mowadhafhi/get_section_topics.dart'; +import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_details.dart'; +import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_transactions.dart'; +import 'package:mohem_flutter_app/models/mowadhafhi/get_ticket_types.dart'; +import 'package:mohem_flutter_app/models/mowadhafhi/get_tickets_list.dart'; +import 'package:mohem_flutter_app/models/mowadhafhi/get_transaction_attachment_model.dart'; + +class EtqanApiClient { + static final EtqanApiClient _instance = EtqanApiClient._internal(); + + EtqanApiClient._internal(); + + factory EtqanApiClient() => _instance; + + Future> getEtqanProjects() async { + String url = "${ApiConsts.cocRest}ETQAN_GetProjects"; + Map postParams = {"P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject( + (json) { + List? responseData = []; + if (json["ETQAN_GetProjects_Response"] != null && json["ETQAN_GetProjects_Response"].isNotEmpty) { + json["ETQAN_GetProjects_Response"].forEach((element) { + responseData.add(EtqanGetProjectsResponse.fromJson(element)); + }); + } + return responseData; + }, + url, + postParams, + ); + } + + Future> getEmployeeEtqanRequests() async { + String url = "${ApiConsts.cocRest}ETQAN_GetEmployeeOVRRequests"; + Map postParams = { + "ETQAN_EmployeeID": AppState().memberInformationList?.eMPLOYEENUMBER, + "P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER, + }; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject( + (json) { + List responseData = []; + if (json["ETQAN_GetEmployeeOVRRequests_Response"] != null && json["ETQAN_GetEmployeeOVRRequests_Response"].isNotEmpty) { + json["ETQAN_GetEmployeeOVRRequests_Response"].forEach((element) { + responseData.add(EtqanGetEmployeeOvrRequestsResponse.fromJson(element)); + }); + } + + if (responseData.isNotEmpty) { + for (EtqanGetEmployeeOvrRequestsResponse action in responseData) { + action.createdDate = action.createdDate?.split(" ")[0]; + } + responseData.sort((EtqanGetEmployeeOvrRequestsResponse a, EtqanGetEmployeeOvrRequestsResponse b) => (b.createdDate ?? '').compareTo(a.createdDate ?? '')); + } + + return responseData; + }, + url, + postParams, + ); + } + + Future> getEtqanIncidentRequests(String ticketNo) async { + String url = "${ApiConsts.cocRest}ETQAN_GetIncidentRequest"; + Map postParams = {"ETQAN_TicketNumber": ticketNo, "P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject( + (json) { + List responseData = []; + if (json["ETQAN_GetIncidentRequest_Response"] != null && json["ETQAN_GetIncidentRequest_Response"].isNotEmpty) { + json["ETQAN_GetIncidentRequest_Response"].forEach((element) { + responseData.add(EtqanGetIncidentRequestResponse.fromJson(element)); + }); + } + return responseData; + }, + url, + postParams, + ); + } + + Future createRequest(String description, int? projectID, String? attachmentBase64) async { + String url = "${ApiConsts.cocRest}ETQAN_CreateIncident"; + Map postParams = { + "P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER, + "P_USER_NAME": AppState().memberInformationList?.eMPLOYEENUMBER, + "ETQAN_Emp_EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER, + "ETQAN_Description": description, + "ETQAN_ProjectId": projectID, + "ETQAN_FileInfoBase64": attachmentBase64, + "ETQAN_IsAnonymous": false, + }; + + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject( + (json) { + if (json["ETQAN_CreateIncident_Response"] != null) { + var response = json["ETQAN_CreateIncident_Response"]; + if (response["id"] != null && response["ticketNumber"] != null) { + return 1; + } + return null; + } + return null; + + // "ETQAN_CreateIncident_Response": { + // "id": "00adabe4-4a32-4b35-1f00-08de4d152e7c", + // "ticketNumber": "OVR/OLY/2026/224" + // }, + }, + url, + postParams, + ); + } +} diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index dbec87d..29d22d7 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -9,9 +9,9 @@ class ApiConsts { // static String baseUrl = "https://webservices.hmg.com"; // PreProd // static String baseUrl = "https://hmgwebservices.com"; // Live server - static String baseUrl = "https://mohemm.hmg.com"; // New Live server + // static String baseUrl = "https://mohemm.hmg.com"; // New Live server // - // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver + static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver // static String baseUrl = "http://10.20.200.111:1010/"; // static String baseUrl = "https://webservices.hmg.com"; // PreProd diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 4653880..bf78671 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -10,6 +10,9 @@ import 'package:mohem_flutter_app/ui/chat/favorite_users_screen.dart'; import 'package:mohem_flutter_app/ui/chat/group_chat_detaied_screen.dart'; import 'package:mohem_flutter_app/ui/chat/group_members.dart'; import 'package:mohem_flutter_app/ui/chat/manage_group.dart'; +import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_create_request.dart'; +import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_ovr_home.dart'; +import 'package:mohem_flutter_app/ui/etqan_ovr/etqan_request_detailed.dart'; import 'package:mohem_flutter_app/ui/landing/dashboard_screen.dart'; import 'package:mohem_flutter_app/ui/landing/itg/change_itg_ad_password_screen.dart'; import 'package:mohem_flutter_app/ui/landing/itg/its_add_screen_video_image.dart'; @@ -211,6 +214,10 @@ class AppRoutes { static const String activityScreen = "/activityScreen"; + static const String etqanOvr = "/etqanOvr"; + static const String etqanCreateRequest = "/etqanCreateRequest"; + static const String etqanGetIncidientRequest = "/etqanGetIncidientRequest"; + static final Map routes = { login: (BuildContext context) => LoginScreen(), verifyLogin: (BuildContext context) => VerifyLoginScreen(), @@ -328,6 +335,11 @@ class AppRoutes { unsafeDeviceScreen: (BuildContext context) => const UnsafeDeviceScreen(), appUpdateScreen: (BuildContext context) => const AppUpdateScreen(), childEducation: (BuildContext context) => ChildEducationAssistance(), - activityScreen:(BuildContext context) => const EventActivityScreen() + activityScreen:(BuildContext context) => const EventActivityScreen(), + + //ETQAN OVR + etqanOvr: (BuildContext context) => EtqanOvrHome(), + etqanCreateRequest: (BuildContext context) => EtqanOvrCreateRequest(), + etqanGetIncidientRequest: (BuildContext context) => EtqanOvrRequestDetailed(), }; } diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 6feb438..dc0c1b9 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -655,7 +655,11 @@ class CodegenLoader extends AssetLoader{ "yearsUsed": "سنوات الاستخدام", "faRequest": "طلب التخلص من FA", "showMore": "عرض المزيد", - "buyerDetails": "تفاصيل المشتري" + "buyerDetails": "تفاصيل المشتري", + "etqanOVR": "Etqan (OVR)", + "etqanRequest": "طلب إلكتروني", + "selectProject": "حدد المشروع", + "project": "مشروع" }; static const Map _en_US = { "mohemm": "Mohemm", @@ -1298,7 +1302,11 @@ static const Map _en_US = { "yearsUsed": "Years Used", "faRequest": "FA Disposal Request", "showMore": "Show More", - "buyerDetails": "Buyer Details" + "buyerDetails": "Buyer Details", + "etqanOVR": "Etqan (OVR)", + "etqanRequest": "Etqan Request", + "selectProject": "Select Project", + "project": "Project" }; static const Map> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 1a1cf95..c4efa13 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -628,5 +628,9 @@ abstract class LocaleKeys { static const faRequest = 'faRequest'; static const showMore = 'showMore'; static const buyerDetails = 'buyerDetails'; + static const etqanOVR = 'etqanOVR'; + static const etqanRequest = 'etqanRequest'; + static const selectProject = 'selectProject'; + static const project = 'project'; } diff --git a/lib/main.dart b/lib/main.dart index 263ed8e..022204d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,6 +12,7 @@ import 'package:mohem_flutter_app/models/post_params_model.dart'; import 'package:mohem_flutter_app/provider/chat_provider_model.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/provider/eit_provider_model.dart'; +import 'package:mohem_flutter_app/provider/etqan_ovr_provider.dart'; import 'package:mohem_flutter_app/theme/app_theme.dart'; import 'package:mohem_flutter_app/ui/marathon/marathon_provider.dart'; import 'package:month_year_picker/month_year_picker.dart'; @@ -53,6 +54,7 @@ Future main() async { ChangeNotifierProvider(create: (_) => EITProviderModel()), ChangeNotifierProvider(create: (_) => ChatProviderModel()), ChangeNotifierProvider(create: (_) => MarathonProvider()), + ChangeNotifierProvider(create: (_) => EtqanOvrProviderModel()), // ChangeNotifierProvider( // create: (_) => ChatCallProvider(), // ), diff --git a/lib/models/etqan_ovr/etqan_get_employee_incident_report.dart b/lib/models/etqan_ovr/etqan_get_employee_incident_report.dart new file mode 100644 index 0000000..3743cf3 --- /dev/null +++ b/lib/models/etqan_ovr/etqan_get_employee_incident_report.dart @@ -0,0 +1,45 @@ +import 'dart:convert'; + +class EtqanGetEmployeeIncidentRequestModel { + List? etqanGetIncidentRequestResponse; + + EtqanGetEmployeeIncidentRequestModel({ + this.etqanGetIncidentRequestResponse, + }); + + factory EtqanGetEmployeeIncidentRequestModel.fromRawJson(String str) => EtqanGetEmployeeIncidentRequestModel.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory EtqanGetEmployeeIncidentRequestModel.fromJson(Map json) => EtqanGetEmployeeIncidentRequestModel( + etqanGetIncidentRequestResponse: json["ETQAN_GetIncidentRequest_Response"] == null ? [] : List.from(json["ETQAN_GetIncidentRequest_Response"]!.map((x) => EtqanGetIncidentRequestResponse.fromJson(x))), + ); + + Map toJson() => { + "ETQAN_GetIncidentRequest_Response": etqanGetIncidentRequestResponse == null ? [] : List.from(etqanGetIncidentRequestResponse!.map((x) => x.toJson())), + }; +} + +class EtqanGetIncidentRequestResponse { + String? key; + String? value; + + EtqanGetIncidentRequestResponse({ + this.key, + this.value, + }); + + factory EtqanGetIncidentRequestResponse.fromRawJson(String str) => EtqanGetIncidentRequestResponse.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory EtqanGetIncidentRequestResponse.fromJson(Map json) => EtqanGetIncidentRequestResponse( + key: json["key"], + value: json["value"], + ); + + Map toJson() => { + "key": key, + "value": value, + }; +} diff --git a/lib/models/etqan_ovr/etqan_get_requests_model.dart b/lib/models/etqan_ovr/etqan_get_requests_model.dart new file mode 100644 index 0000000..c59e171 --- /dev/null +++ b/lib/models/etqan_ovr/etqan_get_requests_model.dart @@ -0,0 +1,53 @@ +import 'dart:convert'; + +class EtqanGetEmployeeRequestsModel { + List? etqanGetEmployeeOvrRequestsResponse; + + EtqanGetEmployeeRequestsModel({ + this.etqanGetEmployeeOvrRequestsResponse, + }); + + factory EtqanGetEmployeeRequestsModel.fromRawJson(String str) => EtqanGetEmployeeRequestsModel.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory EtqanGetEmployeeRequestsModel.fromJson(Map json) => EtqanGetEmployeeRequestsModel( + etqanGetEmployeeOvrRequestsResponse: json["ETQAN_GetEmployeeOVRRequests_Response"] == null ? [] : List.from(json["ETQAN_GetEmployeeOVRRequests_Response"]!.map((x) => EtqanGetEmployeeOvrRequestsResponse.fromJson(x))), + ); + + Map toJson() => { + "ETQAN_GetEmployeeOVRRequests_Response": etqanGetEmployeeOvrRequestsResponse == null ? [] : List.from(etqanGetEmployeeOvrRequestsResponse!.map((x) => x.toJson())), + }; +} + +class EtqanGetEmployeeOvrRequestsResponse { + String? createdDate; + String? description; + String? id; + String? ticketNumber; + + EtqanGetEmployeeOvrRequestsResponse({ + this.createdDate, + this.description, + this.id, + this.ticketNumber, + }); + + factory EtqanGetEmployeeOvrRequestsResponse.fromRawJson(String str) => EtqanGetEmployeeOvrRequestsResponse.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory EtqanGetEmployeeOvrRequestsResponse.fromJson(Map json) => EtqanGetEmployeeOvrRequestsResponse( + createdDate: json["createdDate"], + description: json["description"], + id: json["id"], + ticketNumber: json["ticketNumber"], + ); + + Map toJson() => { + "createdDate": createdDate, + "description": description, + "id": id, + "ticketNumber": ticketNumber, + }; +} diff --git a/lib/models/etqan_ovr/etqan_getprojects_model.dart b/lib/models/etqan_ovr/etqan_getprojects_model.dart new file mode 100644 index 0000000..5584faa --- /dev/null +++ b/lib/models/etqan_ovr/etqan_getprojects_model.dart @@ -0,0 +1,53 @@ +import 'dart:convert'; + +class EtqanGetProjectsModel { + List? etqanGetProjectsResponse; + + EtqanGetProjectsModel({ + this.etqanGetProjectsResponse, + }); + + factory EtqanGetProjectsModel.fromRawJson(String str) => EtqanGetProjectsModel.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory EtqanGetProjectsModel.fromJson(Map json) => EtqanGetProjectsModel( + etqanGetProjectsResponse: json["ETQAN_GetProjects_Response"] == null ? [] : List.from(json["ETQAN_GetProjects_Response"]!.map((x) => EtqanGetProjectsResponse.fromJson(x))), + ); + + Map toJson() => { + "ETQAN_GetProjects_Response": etqanGetProjectsResponse == null ? [] : List.from(etqanGetProjectsResponse!.map((x) => x.toJson())), + }; +} + +class EtqanGetProjectsResponse { + String? projectAr; + String? projectCode; + String? projectEn; + int? projectId; + + EtqanGetProjectsResponse({ + this.projectAr, + this.projectCode, + this.projectEn, + this.projectId, + }); + + factory EtqanGetProjectsResponse.fromRawJson(String str) => EtqanGetProjectsResponse.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory EtqanGetProjectsResponse.fromJson(Map json) => EtqanGetProjectsResponse( + projectAr: json["projectAr"], + projectCode: json["projectCode"], + projectEn: json["projectEn"], + projectId: json["projectId"], + ); + + Map toJson() => { + "projectAr": projectAr, + "projectCode": projectCode, + "projectEn": projectEn, + "projectId": projectId, + }; +} diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index 05c9ebb..55e16c3 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -120,6 +120,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { DrawerMenuItem("assets/images/drawer/my_profile.svg", LocaleKeys.myProfile.tr(), AppRoutes.profile), 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.etqanOVR.tr(), AppRoutes.etqanOvr), DrawerMenuItem("assets/images/drawer/pending_trasactions.svg", LocaleKeys.pendingTransactions.tr(), AppRoutes.pendingTransactions), // DrawerMenuItem("assets/images/drawer/drawer_marathon.svg", LocaleKeys.brainMarathon.tr(), AppRoutes.marathonIntroScreen), DrawerMenuItem("assets/images/drawer/change_password.svg", LocaleKeys.changePassword.tr(), AppRoutes.changePassword), diff --git a/lib/provider/etqan_ovr_provider.dart b/lib/provider/etqan_ovr_provider.dart new file mode 100644 index 0000000..a063463 --- /dev/null +++ b/lib/provider/etqan_ovr_provider.dart @@ -0,0 +1,181 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/etqan_ovr_api_client.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_employee_incident_report.dart'; +import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_requests_model.dart'; +import 'package:mohem_flutter_app/models/etqan_ovr/etqan_getprojects_model.dart'; +import 'package:speech_to_text/speech_recognition_result.dart'; +import 'package:speech_to_text/speech_to_text.dart'; + +class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { + // Home screen state + List? getEtqanProjectsList; + List? getEtqanEmployeeRequestsList; + List? getEtqanEmployeeIncidnetRequest; + bool isLoading = false; + String? _ticketId; + + // Speech to text state + final SpeechToText _speechToText = SpeechToText(); + bool _speechEnabled = false; + bool _isListening = false; + + // Getters for speech state + bool get speechEnabled => _speechEnabled; + + bool get isListening => _isListening; + + // Getter for ticketId + String get getTicketId => _ticketId ?? ''; + + // Setter for ticketId + void setTicketId(String ticketId) { + _ticketId = ticketId; + notifyListeners(); + } + + // Create request screen state + EtqanGetProjectsResponse? selectedProject; + String? selectedEmployeeNumber; + File? attachment; + String? attachmentBase64; + String description = ''; + + /// Initialize speech recognition - call this once when screen loads + Future initSpeech() async { + _speechEnabled = await _speechToText.initialize(); + notifyListeners(); + } + + /// Start listening for speech input + Future startListening(String localeId, TextEditingController controller) async { + await _speechToText.listen( + onResult: (SpeechRecognitionResult result) { + description = result.recognizedWords; + controller.text = result.recognizedWords; + controller.selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length)); + notifyListeners(); + }, + localeId: localeId, + ); + _isListening = true; + notifyListeners(); + } + + /// Stop listening for speech input + Future stopListening() async { + await _speechToText.stop(); + _isListening = false; + notifyListeners(); + } + + /// Toggle listening state + Future toggleListening(String localeId, TextEditingController controller) async { + if (_isListening) { + await stopListening(); + } else { + await startListening(localeId, controller); + } + } + + // Fetch Etqan Projects + Future fetchEtqanProjects(BuildContext context) async { + isLoading = true; + notifyListeners(); + Utils.showLoading(context); + getEtqanProjectsList?.clear(); + getEtqanProjectsList = await EtqanApiClient().getEtqanProjects(); + Utils.hideLoading(context); + isLoading = false; + notifyListeners(); + } + + // Fetch Etqan Employee Requests + Future fetchEtqanEmployeeRequests(BuildContext context) async { + isLoading = true; + notifyListeners(); + Utils.showLoading(context); + getEtqanEmployeeRequestsList?.clear(); + getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); + Utils.hideLoading(context); + isLoading = false; + notifyListeners(); + } + + // Fetch Etqan Employee Incident Report + Future fetchEtqanEmployeeIncidentRequests(BuildContext context, String ticketNo) async { + isLoading = true; + notifyListeners(); + Utils.showLoading(context); + getEtqanEmployeeIncidnetRequest?.clear(); + getEtqanEmployeeIncidnetRequest = await EtqanApiClient().getEtqanIncidentRequests(ticketNo); + Utils.hideLoading(context); + isLoading = false; + notifyListeners(); + } + + // Set selected project + void setSelectedProject(EtqanGetProjectsResponse? project) { + selectedProject = project; + notifyListeners(); + } + + // Set selected employee number + void setSelectedEmployeeNumber(String? employeeNumber) { + selectedEmployeeNumber = employeeNumber; + notifyListeners(); + } + + // Set attachment with base64 + void setAttachment(File file, String base64) { + attachment = file; + attachmentBase64 = base64; + notifyListeners(); + } + + // Remove attachment + void removeAttachment() { + attachment = null; + attachmentBase64 = null; + notifyListeners(); + } + + // Set description + void setDescription(String value) { + description = value; + notifyListeners(); + } + + // Clear create request form + void clearCreateRequestForm() { + selectedProject = null; + selectedEmployeeNumber = null; + attachment = null; + attachmentBase64 = null; + description = ''; + notifyListeners(); + } + + // Submit request + Future submitRequest(BuildContext context) async { + try { + Utils.showLoading(context); + int? isSuccess = await EtqanApiClient().createRequest(description, selectedProject?.projectId, attachmentBase64); + Utils.hideLoading(context); + if (isSuccess == 1) { + getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); + notifyListeners(); + Navigator.of(context).pop(); + return true; + } + return false; + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + return false; + } + } +} diff --git a/lib/ui/etqan_ovr/etqan_create_request.dart b/lib/ui/etqan_ovr/etqan_create_request.dart new file mode 100644 index 0000000..93925e0 --- /dev/null +++ b/lib/ui/etqan_ovr/etqan_create_request.dart @@ -0,0 +1,279 @@ +import 'dart:io'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/extensions/int_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/etqan_ovr/etqan_getprojects_model.dart'; +import 'package:mohem_flutter_app/provider/etqan_ovr_provider.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; +import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; +import 'package:mohem_flutter_app/widgets/button/default_button.dart'; +import 'package:mohem_flutter_app/widgets/image_picker.dart'; +import 'package:provider/provider.dart'; + +class EtqanOvrCreateRequest extends StatefulWidget { + const EtqanOvrCreateRequest({Key? key}) : super(key: key); + + @override + _EtqanOvrCreateRequestState createState() => _EtqanOvrCreateRequestState(); +} + +class _EtqanOvrCreateRequestState extends State { + final TextEditingController _descriptionController = TextEditingController(); + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + var provider = context.read(); + provider.initSpeech(); + provider.fetchEtqanProjects(context); + provider.clearCreateRequestForm(); + }); + } + + @override + void dispose() { + _descriptionController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget(context, title: LocaleKeys.createRequest.tr(), showHomeButton: false), + body: Consumer( + builder: (BuildContext context, EtqanOvrProviderModel provider, Widget? child) { + return Column( + children: [ + (ListView( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.all(21), + children: [ + _buildDropdownField( + label: LocaleKeys.project.tr(), + hint: LocaleKeys.selectProject.tr(), + value: _getProjectDisplayName(provider.selectedProject), + onTap: () => _showProjectSelectionSheet(context, provider), + ), + 16.height, + // _buildDropdownField( + // label: 'Employee Number', + // hint: 'Write a message', + // value: provider.selectedEmployeeNumber, + // onTap: () { + // // TODO: Show employee selection dialog + // }, + // ), + // 16.height, + _buildAttachmentSection(provider), + 16.height, + _buildDescriptionField(provider), + 24.height, + ], + )).expanded, + DefaultButton(LocaleKeys.submit.tr(), () async { + provider.setDescription(_descriptionController.text); + await provider.submitRequest(context); + }).insideContainer, + ], + ); + }, + ), + ); + } + + String? _getProjectDisplayName(EtqanGetProjectsResponse? project) { + if (project == null) return null; + return AppState().isArabic(context) ? project.projectAr : project.projectEn; + } + + void _showProjectSelectionSheet(BuildContext context, EtqanOvrProviderModel provider) { + if (provider.getEtqanProjectsList == null || provider.getEtqanProjectsList!.isEmpty) { + return; + } + showMyBottomSheet( + context, + callBackFunc: () {}, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: Text(LocaleKeys.selectProject.tr(), style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600, color: Color(0xff2B353E))), + ), + const Divider(), + ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + itemCount: provider.getEtqanProjectsList!.length, + separatorBuilder: (BuildContext context, int index) => const Divider(height: 1), + itemBuilder: (BuildContext context, int index) { + EtqanGetProjectsResponse project = provider.getEtqanProjectsList![index]; + bool isSelected = provider.selectedProject?.projectId == project.projectId; + return ListTile( + title: Text( + AppState().isArabic(context) ? (project.projectAr ?? '') : (project.projectEn ?? ''), + style: TextStyle(fontSize: 14, fontWeight: isSelected ? FontWeight.w600 : FontWeight.w400, color: isSelected ? MyColors.gradiantEndColor : const Color(0xff2B353E)), + ), + trailing: isSelected ? const Icon(Icons.check, color: MyColors.gradiantEndColor) : null, + onTap: () { + provider.setSelectedProject(project); + Navigator.pop(context); + }, + ); + }, + ), + 16.height, + ], + ), + ); + } + + Widget _buildDropdownField({required String label, required String hint, String? value, required VoidCallback onTap}) { + return InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(15), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all(color: const Color(0xffefefef), width: 1)), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.44)), + const SizedBox(height: 4), + Text(value ?? hint, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: value != null ? const Color(0xff2B353E) : const Color(0xff575757), letterSpacing: -0.56)), + ], + ), + ), + const Icon(Icons.keyboard_arrow_down, color: Color(0xff575757)), + ], + ), + ), + ); + } + + Widget _buildAttachmentSection(EtqanOvrProviderModel provider) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all(color: const Color(0xffefefef), width: 1)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(LocaleKeys.supportingDocument.tr(), style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Color(0xff2B353E))), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(LocaleKeys.attachments.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xff2B353E))), + if (provider.attachment == null) + InkWell( + onTap: () { + ImageOptions.showImageOptionsNew(context, true, (String base64, File file) { + provider.setAttachment(file, base64); + Navigator.of(context).pop(); + }); + }, + borderRadius: BorderRadius.circular(8), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), + decoration: BoxDecoration(color: MyColors.gradiantEndColor, borderRadius: BorderRadius.circular(8)), + child: Text(LocaleKeys.add.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.white)), + ), + ), + ], + ), + if (provider.attachment != null) ...[ + const SizedBox(height: 12), + Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Row( + children: [ + const Icon(Icons.attach_file, size: 18, color: Color(0xff575757)), + const SizedBox(width: 8), + Expanded(child: Text(provider.attachment!.path.split('/').last, style: const TextStyle(fontSize: 12, color: Color(0xff575757)), overflow: TextOverflow.ellipsis)), + InkWell( + onTap: () { + provider.removeAttachment(); + }, + child: const Icon(Icons.close, size: 18, color: Colors.red), + ), + ], + ), + ), + ], + ], + ), + ); + } + + Widget _buildDescriptionField(EtqanOvrProviderModel provider) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 15), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.white, border: Border.all(color: const Color(0xffefefef), width: 1)), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(LocaleKeys.description.tr(), style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.44)), + GestureDetector( + onTap: + provider.speechEnabled + ? () { + String localeId = AppState().isArabic(context) ? 'ar_SA' : 'en_US'; + provider.toggleListening(localeId, _descriptionController); + } + : null, + child: Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration(color: provider.isListening ? MyColors.gradiantEndColor.withOpacity(0.1) : Colors.transparent, borderRadius: BorderRadius.circular(20)), + child: Icon( + provider.isListening ? Icons.mic : Icons.mic_none, + color: provider.isListening ? MyColors.gradiantEndColor : (provider.speechEnabled ? MyColors.grey57Color : Colors.grey.shade300), + size: 24, + ), + ), + ), + ], + ), + if (provider.isListening) + Padding( + padding: const EdgeInsets.only(bottom: 8), + child: Row( + children: [ + Container(width: 8, height: 8, decoration: BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(4))), + const SizedBox(width: 8), + Text('Listening...', style: TextStyle(fontSize: 12, color: MyColors.gradiantEndColor, fontWeight: FontWeight.w500)), + ], + ), + ), + TextField( + controller: _descriptionController, + maxLines: 4, + onChanged: (String value) => provider.setDescription(value), + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: Color(0xff2B353E), letterSpacing: -0.44), + decoration: InputDecoration( + isDense: true, + hintText: LocaleKeys.writeAMessage.tr(), + hintStyle: const TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: Color(0xff575757), letterSpacing: -0.56), + contentPadding: EdgeInsets.zero, + border: InputBorder.none, + focusedBorder: InputBorder.none, + enabledBorder: InputBorder.none, + ), + ), + ], + ), + ); + } +} diff --git a/lib/ui/etqan_ovr/etqan_ovr_home.dart b/lib/ui/etqan_ovr/etqan_ovr_home.dart new file mode 100644 index 0000000..0f549f0 --- /dev/null +++ b/lib/ui/etqan_ovr/etqan_ovr_home.dart @@ -0,0 +1,105 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/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/config/routes.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_requests_model.dart'; +import 'package:mohem_flutter_app/provider/etqan_ovr_provider.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; +import 'package:mohem_flutter_app/widgets/button/default_button.dart'; +import 'package:provider/provider.dart'; + +class EtqanOvrHome extends StatefulWidget { + const EtqanOvrHome({Key? key}) : super(key: key); + + @override + _EtqanOvrHomeState createState() => _EtqanOvrHomeState(); +} + +class _EtqanOvrHomeState extends State { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + context.read().fetchEtqanEmployeeRequests(context); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget(context, title: LocaleKeys.etqanRequest.tr()), + body: Consumer( + builder: (BuildContext context, EtqanOvrProviderModel provider, Widget? child) { + return Column( + children: [ + (provider.getEtqanEmployeeRequestsList == null || provider.getEtqanEmployeeRequestsList!.isEmpty + ? Utils.getNoDataWidget(context) + : ListView.separated( + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.all(20), + itemBuilder: (BuildContext context, int index) { + EtqanGetEmployeeOvrRequestsResponse data = provider.getEtqanEmployeeRequestsList![index]; + return InkWell( + onTap: () { + context.read().setTicketId(data.ticketNumber ?? ''); + Navigator.pushNamed(context, AppRoutes.etqanGetIncidientRequest); + }, + child: Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(15), border: Border.all(color: const Color(0xffefefef), width: 1)), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + (data.ticketNumber ?? "").toText14(isBold: true, color: MyColors.darkTextColor), + (data.createdDate?.split(" ")[0] ?? "").toText12(color: MyColors.grey70Color), + ], + ), + 8.height, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + (data.description ?? "").toText12(color: MyColors.grey57Color), + RotatedBox( + quarterTurns: AppState().isArabic(context) ? 2 : 0, + child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.grey70Color, width: 16, height: 16), + ), + ], + ), + ], + ), + ), + ], + ), + ), + ); + }, + separatorBuilder: (BuildContext context, int index) => 12.height, + itemCount: provider.getEtqanEmployeeRequestsList!.length, + )) + .expanded, + DefaultButton(LocaleKeys.createRequest.tr(), () async { + await Navigator.pushNamed(context, AppRoutes.etqanCreateRequest); + // getOpenTickets(); + }).insideContainer, + ], + ); + }, + ), + ); + } +} diff --git a/lib/ui/etqan_ovr/etqan_request_detailed.dart b/lib/ui/etqan_ovr/etqan_request_detailed.dart new file mode 100644 index 0000000..b1ed12a --- /dev/null +++ b/lib/ui/etqan_ovr/etqan_request_detailed.dart @@ -0,0 +1,110 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/etqan_ovr/etqan_get_employee_incident_report.dart'; +import 'package:mohem_flutter_app/provider/etqan_ovr_provider.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; +import 'package:provider/provider.dart'; + +class EtqanOvrRequestDetailed extends StatefulWidget { + const EtqanOvrRequestDetailed({Key? key}) : super(key: key); + + @override + _EtqanOvrRequestDetailedState createState() => _EtqanOvrRequestDetailedState(); +} + +class _EtqanOvrRequestDetailedState extends State { + List get fieldOrder => ['Project', 'Created', 'Occurrence Time', 'OVR Reference No', 'Involved Employee Number', 'Description']; + + List get hiddenFields => ['OVR Id', 'Is Anonymous']; + + List _getOrderedList(List list) { + List orderedList = []; + + List filteredList = list.where((EtqanGetIncidentRequestResponse item) => !hiddenFields.any((f) => f.toLowerCase() == item.key?.toLowerCase())).toList(); + + for (String fieldName in fieldOrder) { + var item = filteredList.firstWhere((e) => e.key?.toLowerCase() == fieldName.toLowerCase(), orElse: () => EtqanGetIncidentRequestResponse()); + if (item.key != null) { + orderedList.add(item); + } + } + + for (var item in filteredList) { + if (!fieldOrder.any((f) => f.toLowerCase() == item.key?.toLowerCase())) { + orderedList.add(item); + } + } + + return orderedList; + } + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + var provider = context.read(); + provider.fetchEtqanEmployeeIncidentRequests(context, provider.getTicketId); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget(context, title: LocaleKeys.etqanRequest.tr()), + body: Consumer( + builder: (BuildContext context, EtqanOvrProviderModel provider, Widget? child) { + if (provider.getEtqanEmployeeIncidnetRequest == null || provider.getEtqanEmployeeIncidnetRequest!.isEmpty) { + return Utils.getNoDataWidget(context); + } + + List orderedList = _getOrderedList(provider.getEtqanEmployeeIncidnetRequest!); + + return SingleChildScrollView( + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.all(16), + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + border: Border.all(color: const Color(0xffefefef), width: 1), + boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2))], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: + orderedList.asMap().entries.map((MapEntry entry) { + int index = entry.key; + EtqanGetIncidentRequestResponse data = entry.value; + bool isLast = index == orderedList.length - 1; + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(flex: 2, child: (data.key ?? "").toText12(isBold: true, color: MyColors.grey57Color)), + 8.width, + Expanded(flex: 3, child: (data.value ?? "-").toText14(color: MyColors.darkTextColor)), + ], + ), + ), + if (!isLast) const Padding(padding: EdgeInsets.only(left: 14, right: 14), child: Divider(height: 1, thickness: 1, color: Color(0xffefefef))), + ], + ); + }).toList(), + ), + ), + ); + }, + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 6abe6fc..8852c2d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -102,6 +102,9 @@ dependencies: audio_waveforms: ^1.3.0 rxdart: ^0.27.7 + #Speech to Text + speech_to_text: ^7.0.0 + #Encryption flutter_des: ^2.1.0 From 74bcf3e1b7163baf1233afc7387c1f97a8d14000 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Thu, 8 Jan 2026 14:38:32 +0300 Subject: [PATCH 04/30] translation --- assets/langs/ar-SA.json | 4 ++-- assets/langs/en-US.json | 4 ++-- lib/generated/codegen_loader.g.dart | 8 ++++---- lib/ui/etqan_ovr/etqan_ovr_home.dart | 7 ++++--- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index ea64f78..84c9b46 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -643,8 +643,8 @@ "faRequest": "طلب التخلص من FA", "showMore": "عرض المزيد", "buyerDetails": "تفاصيل المشتري", - "etqanOVR": "إتقان (OVR)", - "etqanRequest": "طلب إلكتروني", + "etqanOVR": "الإبلاغ عن حادثة", + "etqanRequest": "طلبات الحوادث", "selectProject": "حدد المشروع", "project": "مشروع" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 630e0ad..8f95e9a 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -641,8 +641,8 @@ "faRequest":"FA Disposal Request", "showMore": "Show More", "buyerDetails": "Buyer Details", - "etqanOVR": "Etqan (OVR)", - "etqanRequest": "Etqan Request", + "etqanOVR": "Report An Incident", + "etqanRequest": "Incident Requests", "selectProject": "Select Project", "project": "Project" } \ No newline at end of file diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index dc0c1b9..4cb2359 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -656,8 +656,8 @@ class CodegenLoader extends AssetLoader{ "faRequest": "طلب التخلص من FA", "showMore": "عرض المزيد", "buyerDetails": "تفاصيل المشتري", - "etqanOVR": "Etqan (OVR)", - "etqanRequest": "طلب إلكتروني", + "etqanOVR": "الإبلاغ عن حادثة", + "etqanRequest": "طلبات الحوادث", "selectProject": "حدد المشروع", "project": "مشروع" }; @@ -1303,8 +1303,8 @@ static const Map _en_US = { "faRequest": "FA Disposal Request", "showMore": "Show More", "buyerDetails": "Buyer Details", - "etqanOVR": "Etqan (OVR)", - "etqanRequest": "Etqan Request", + "etqanOVR": "Report An Incident", + "etqanRequest": "Incident Requests", "selectProject": "Select Project", "project": "Project" }; diff --git a/lib/ui/etqan_ovr/etqan_ovr_home.dart b/lib/ui/etqan_ovr/etqan_ovr_home.dart index 0f549f0..7a48a51 100644 --- a/lib/ui/etqan_ovr/etqan_ovr_home.dart +++ b/lib/ui/etqan_ovr/etqan_ovr_home.dart @@ -1,10 +1,10 @@ -import 'package:easy_localization/easy_localization.dart'; +import 'package:easy_localization/easy_localization.dart' as lclize; import 'package:flutter/material.dart'; import 'package:flutter_svg/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/config/routes.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/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; @@ -14,6 +14,7 @@ import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:provider/provider.dart'; +import 'package:intl/src/intl/text_direction.dart' as intl; class EtqanOvrHome extends StatefulWidget { const EtqanOvrHome({Key? key}) : super(key: key); @@ -66,7 +67,7 @@ class _EtqanOvrHomeState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ (data.ticketNumber ?? "").toText14(isBold: true, color: MyColors.darkTextColor), - (data.createdDate?.split(" ")[0] ?? "").toText12(color: MyColors.grey70Color), + Directionality(textDirection: TextDirection.ltr, child: (data.createdDate ?? "").toText12(color: MyColors.grey70Color)), ], ), 8.height, From d021c95def20a4dc2754d2644d6d5ba167714e14 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Wed, 14 Jan 2026 16:20:01 +0300 Subject: [PATCH 05/30] Parking Qr Solution --- assets/images/drawer/car_parking_icon.svg | 4 + assets/langs/ar-SA.json | 3 +- assets/langs/en-US.json | 3 +- lib/api/dashboard_api_client.dart | 21 +++- lib/classes/consts.dart | 4 +- lib/config/routes.dart | 10 +- lib/generated/codegen_loader.g.dart | 6 +- lib/generated/locale_keys.g.dart | 1 + .../get_employee_parking_details_model.dart | 61 ++++++++++ lib/provider/dashboard_provider_model.dart | 14 +-- lib/ui/parking_qr/parking_qr.dart | 106 ++++++++++++++++++ 11 files changed, 212 insertions(+), 21 deletions(-) create mode 100644 assets/images/drawer/car_parking_icon.svg create mode 100644 lib/models/get_employee_parking_details_model.dart create mode 100644 lib/ui/parking_qr/parking_qr.dart diff --git a/assets/images/drawer/car_parking_icon.svg b/assets/images/drawer/car_parking_icon.svg new file mode 100644 index 0000000..f77afa0 --- /dev/null +++ b/assets/images/drawer/car_parking_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 916e8fb..a78c83f 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -642,5 +642,6 @@ "yearsUsed": "سنوات الاستخدام", "faRequest": "طلب التخلص من FA", "showMore": "عرض المزيد", - "buyerDetails": "تفاصيل المشتري" + "buyerDetails": "تفاصيل المشتري", + "parkingQr": "وقوف السيارات QR" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 5fcb35c..5b49d24 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -640,5 +640,6 @@ "yearsUsed": "Years Used", "faRequest":"FA Disposal Request", "showMore": "Show More", - "buyerDetails": "Buyer Details" + "buyerDetails": "Buyer Details", + "parkingQr": "Parking QR" } \ No newline at end of file diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index 2bddb42..6f7288c 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -16,6 +16,7 @@ import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_ import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; import 'package:mohem_flutter_app/models/dashboard/list_menu.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'; import 'package:mohem_flutter_app/models/itg/itg_response_model.dart'; import 'package:mohem_flutter_app/models/sso_auth_model.dart'; @@ -226,9 +227,9 @@ class DashboardApiClient { } Future getBookingSSOFinalRedirection({required String token}) async { - token = - "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhjZTE2OWM0YjIwYjQ2ZWM5YTQyOTU3Y2ZhODUzNzQ1IiwidHlwIjoiSldUIn0.eyJ0ZW5hbnRfaWQiOiJhOWY0ZDFhMDU5NmQ0YWVhOGY4MzA5OTJlYzRiZGFjMSIsImVpZCI6IjExNzkzMCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3NpZCI6Ijk2MDI0OGM1NzA3YzQ3MmFhYTEzM2I1N2ZhODE1ZmVhIiwibGFuZ3VhZ2UiOiJVUyIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6IjExNzkzMEBobWcuY29tIiwiZXhwIjoxNzgyNDc1NzY5LCJpc3MiOiJodHRwczovL3Nzby11YXQuaG1nLmNvbSIsImF1ZCI6ImE5ZjRkMWEwNTk2ZDRhZWE4ZjgzMDk5MmVjNGJkYWMxIn0.rJcLVsG8D0XECyLERCTD2uqGeWyvp-OBVGE9uL2qKrX4etFUHgdFt_5kYF6edFTtGy-0PIZadHDmv7e-IOhVWHm5HVMClaukiXoRXR8cDN8XA1wfme3Kd-U5PXN-IRh49AyRTzLO0rYNPvH81ScosWGlsFSkOvA-0hJNa2adHdtvgNvB8wJshSU5p7sAmF8mjdDY6aInG19etu2iEuUDwHHA4ZY_ts4hboHo8fE392hFaYGonExoD7bpW5RMx5xKWeRCmWpG_PK8Aw_z1jGzdB9PANus4pteRGuln1J-kmo2lQC9pVrSyZATAKp1HfgfyZ_vUhaHEfM69cMWaCslJQ"; - // var request = http.MultipartRequest('POST', Uri.parse('https://ek.techmaster.in/SSO/HMG')); + // token = + // "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhjZTE2OWM0YjIwYjQ2ZWM5YTQyOTU3Y2ZhODUzNzQ1IiwidHlwIjoiSldUIn0.eyJ0ZW5hbnRfaWQiOiJhOWY0ZDFhMDU5NmQ0YWVhOGY4MzA5OTJlYzRiZGFjMSIsImVpZCI6IjExNzkzMCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3NpZCI6Ijk2MDI0OGM1NzA3YzQ3MmFhYTEzM2I1N2ZhODE1ZmVhIiwibGFuZ3VhZ2UiOiJVUyIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6IjExNzkzMEBobWcuY29tIiwiZXhwIjoxNzgyNDc1NzY5LCJpc3MiOiJodHRwczovL3Nzby11YXQuaG1nLmNvbSIsImF1ZCI6ImE5ZjRkMWEwNTk2ZDRhZWE4ZjgzMDk5MmVjNGJkYWMxIn0.rJcLVsG8D0XECyLERCTD2uqGeWyvp-OBVGE9uL2qKrX4etFUHgdFt_5kYF6edFTtGy-0PIZadHDmv7e-IOhVWHm5HVMClaukiXoRXR8cDN8XA1wfme3Kd-U5PXN-IRh49AyRTzLO0rYNPvH81ScosWGlsFSkOvA-0hJNa2adHdtvgNvB8wJshSU5p7sAmF8mjdDY6aInG19etu2iEuUDwHHA4ZY_ts4hboHo8fE392hFaYGonExoD7bpW5RMx5xKWeRCmWpG_PK8Aw_z1jGzdB9PANus4pteRGuln1J-kmo2lQC9pVrSyZATAKp1HfgfyZ_vUhaHEfM69cMWaCslJQ"; + // // var request = http.MultipartRequest('POST', Uri.parse('https://ek.techmaster.in/SSO/HMG')); var request = http.MultipartRequest('POST', Uri.parse('https://Paxes-release.techmaster.in/SSO/HMG')); request.fields.addAll({'JWTToken': token}); @@ -414,4 +415,18 @@ class DashboardApiClient { // postParams, // ); // } + + Future getEmployeeParkingQrCode() async { + String url = "${ApiConsts.erpRest}GET_Employee_Parking"; + Map postParams = {}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject( + (json) { + GetEmployeeParking responseData = GetEmployeeParking.fromJson(json); + return responseData; + }, + url, + postParams, + ); + } } diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index dbec87d..a6b7173 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -4,12 +4,12 @@ class ApiConsts { // static String baseUrl = "http://10.200.204.11"; // Local server // static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server - // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver + static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver // static String baseUrl = "http://10.201.204.101:2024"; // static String baseUrl = "https://webservices.hmg.com"; // PreProd // static String baseUrl = "https://hmgwebservices.com"; // Live server - static String baseUrl = "https://mohemm.hmg.com"; // New Live server + // static String baseUrl = "https://mohemm.hmg.com"; // New Live server // // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver // static String baseUrl = "http://10.20.200.111:1010/"; diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 4653880..0afc22c 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -40,6 +40,7 @@ import 'package:mohem_flutter_app/ui/my_team/profile_details.dart'; import 'package:mohem_flutter_app/ui/my_team/subordinate_leave.dart'; import 'package:mohem_flutter_app/ui/my_team/team_members.dart'; import 'package:mohem_flutter_app/ui/my_team/view_attendance.dart'; +import 'package:mohem_flutter_app/ui/parking_qr/parking_qr.dart'; import 'package:mohem_flutter_app/ui/payslip/monthly_pay_slip_screen.dart'; import 'package:mohem_flutter_app/ui/performance/performance_appraisal_screen.dart'; import 'package:mohem_flutter_app/ui/profile/add_update_family_member.dart'; @@ -211,6 +212,9 @@ class AppRoutes { static const String activityScreen = "/activityScreen"; + //Parking QR + static const String parkingQr = "/parkingQrScreen"; + static final Map routes = { login: (BuildContext context) => LoginScreen(), verifyLogin: (BuildContext context) => VerifyLoginScreen(), @@ -328,6 +332,10 @@ class AppRoutes { unsafeDeviceScreen: (BuildContext context) => const UnsafeDeviceScreen(), appUpdateScreen: (BuildContext context) => const AppUpdateScreen(), childEducation: (BuildContext context) => ChildEducationAssistance(), - activityScreen:(BuildContext context) => const EventActivityScreen() + activityScreen:(BuildContext context) => const EventActivityScreen(), + + //Parking QR + parkingQr: (BuildContext context) => const ParkingQrScreen(), + }; } diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 6feb438..62bbce6 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -655,7 +655,8 @@ class CodegenLoader extends AssetLoader{ "yearsUsed": "سنوات الاستخدام", "faRequest": "طلب التخلص من FA", "showMore": "عرض المزيد", - "buyerDetails": "تفاصيل المشتري" + "buyerDetails": "تفاصيل المشتري", + "parkingQr": "وقوف السيارات QR" }; static const Map _en_US = { "mohemm": "Mohemm", @@ -1298,7 +1299,8 @@ static const Map _en_US = { "yearsUsed": "Years Used", "faRequest": "FA Disposal Request", "showMore": "Show More", - "buyerDetails": "Buyer Details" + "buyerDetails": "Buyer Details", + "parkingQr": "Parking QR" }; static const Map> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 1a1cf95..683c8c4 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -628,5 +628,6 @@ abstract class LocaleKeys { static const faRequest = 'faRequest'; static const showMore = 'showMore'; static const buyerDetails = 'buyerDetails'; + static const parkingQr = 'parkingQr'; } diff --git a/lib/models/get_employee_parking_details_model.dart b/lib/models/get_employee_parking_details_model.dart new file mode 100644 index 0000000..4a385c8 --- /dev/null +++ b/lib/models/get_employee_parking_details_model.dart @@ -0,0 +1,61 @@ +import 'dart:convert'; + +class GetEmployeeParking { + GetEmployeeParkingClass? getEmployeeParking; + + GetEmployeeParking({ + this.getEmployeeParking, + }); + + factory GetEmployeeParking.fromRawJson(String str) => GetEmployeeParking.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory GetEmployeeParking.fromJson(Map json) => GetEmployeeParking( + getEmployeeParking: json["GET_Employee_Parking"] == null ? null : GetEmployeeParkingClass.fromJson(json["GET_Employee_Parking"]), + ); + + Map toJson() => { + "GET_Employee_Parking": getEmployeeParking?.toJson(), + }; +} + +class GetEmployeeParkingClass { + DateTime? createdAt; + String? employeeCode; + String? isNewQrCode; + String? qrCode; + String? qrCodeImageBase64; + bool? success; + + GetEmployeeParkingClass({ + this.createdAt, + this.employeeCode, + this.isNewQrCode, + this.qrCode, + this.qrCodeImageBase64, + this.success, + }); + + factory GetEmployeeParkingClass.fromRawJson(String str) => GetEmployeeParkingClass.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory GetEmployeeParkingClass.fromJson(Map json) => GetEmployeeParkingClass( + createdAt: json["createdAt"] == null ? null : DateTime.parse(json["createdAt"]), + employeeCode: json["employeeCode"], + isNewQrCode: json["isNewQRCode"], + qrCode: json["qrCode"], + qrCodeImageBase64: json["qrCodeImageBase64"], + success: json["success"], + ); + + Map toJson() => { + "createdAt": createdAt?.toIso8601String(), + "employeeCode": employeeCode, + "isNewQRCode": isNewQrCode, + "qrCode": qrCode, + "qrCodeImageBase64": qrCodeImageBase64, + "success": success, + }; +} diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index 05c9ebb..dd1472b 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -120,6 +120,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { DrawerMenuItem("assets/images/drawer/my_profile.svg", LocaleKeys.myProfile.tr(), AppRoutes.profile), 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/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/drawer_marathon.svg", LocaleKeys.brainMarathon.tr(), AppRoutes.marathonIntroScreen), DrawerMenuItem("assets/images/drawer/change_password.svg", LocaleKeys.changePassword.tr(), AppRoutes.changePassword), @@ -326,8 +327,6 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { void fetchTicketBalance() async { try { GenericResponseModel? ticketResponse = await DashboardApiClient().getTicketBalance(); - logger.d("Ticket Balance Response: ${ticketResponse?.toJson()}"); - print(" ========= Ticket Balance Response ======= "); } catch (ex) { logger.wtf(ex); isEventLoadingLoading = false; @@ -340,8 +339,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { Future fetchSSOAuthRedirection({String? clientID}) async { try { SSOAuthModel? ssoResponse = await DashboardApiClient().getBookingSSOAuthRedirection(clientID: clientID!); - logger.d("SSO Response: ${ssoResponse!.toJson()}"); - if (ssoResponse.status == "Success") { + if (ssoResponse!.status == "Success") { return ssoResponse; } else { Utils.showToast(ssoResponse.message!.first ?? "Failed to fetch SSO Auth Token"); @@ -361,18 +359,12 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { dynamic res = await DashboardApiClient().getBookingSSOFinalRedirection(token: token); logger.d("SSO URL: $res"); return res; - // if (ssoResponse.status == "Success") { - // return ssoResponse; - // } else { - // Utils.showToast(ssoResponse.message!.first ?? "Failed to fetch SSO Auth Token"); - // return null; - // } } catch (ex) { logger.wtf(ex); isEventLoadingLoading = false; notifyListeners(); Utils.handleException(ex, null, null); - return null; // Ensure a return value in case of an exception + return null; } } diff --git a/lib/ui/parking_qr/parking_qr.dart b/lib/ui/parking_qr/parking_qr.dart new file mode 100644 index 0000000..641f84a --- /dev/null +++ b/lib/ui/parking_qr/parking_qr.dart @@ -0,0 +1,106 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/get_employee_parking_details_model.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; + +class ParkingQrScreen extends StatefulWidget { + const ParkingQrScreen({Key? key}) : super(key: key); + + @override + _RequestDetailsState createState() => _RequestDetailsState(); +} + +class _RequestDetailsState extends State { + GetEmployeeParking? qrData; + + @override + void initState() { + super.initState(); + getTicketDetails(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget(context, title: LocaleKeys.parkingQr.tr()), + body: + qrData?.getEmployeeParking == null + ? Center(child: Text(LocaleKeys.noDataAvailable.tr())) + : SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Center( + child: Container( + padding: const EdgeInsets.all(20.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [BoxShadow(color: Colors.grey.withValues(alpha: 0.2), spreadRadius: 5, blurRadius: 10, offset: const Offset(0, 3))], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // QR Code Image + if (qrData!.getEmployeeParking!.qrCodeImageBase64 != null && qrData!.getEmployeeParking!.qrCodeImageBase64!.isNotEmpty) + Image.memory(Utils.dataFromBase64String(qrData!.getEmployeeParking!.qrCodeImageBase64!), width: 300, height: 300, fit: BoxFit.contain) + else + Container( + width: 300, + height: 300, + color: MyColors.lightGreyEFColor, + child: Center(child: Text(LocaleKeys.noDataAvailable.tr(), style: const TextStyle(color: MyColors.grey67Color))), + ), + 10.height, + Container(height: 1, color: MyColors.lightGreyE3Color, margin: const EdgeInsets.symmetric(vertical: 10)), + 20.height, + SizedBox( + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Employee Code + if (qrData!.getEmployeeParking!.employeeCode != null && qrData!.getEmployeeParking!.employeeCode!.isNotEmpty) ...[ + Text(LocaleKeys.employeeNumber.tr(), style: const TextStyle(fontSize: 14, color: MyColors.grey67Color, fontWeight: FontWeight.w400)), + 8.height, + Text(qrData!.getEmployeeParking!.employeeCode!, style: const TextStyle(fontSize: 18, color: MyColors.darkTextColor, fontWeight: FontWeight.w600)), + 8.height, + ], + if (qrData!.getEmployeeParking!.createdAt != null) ...[ + Text(LocaleKeys.creationDate.tr(), style: const TextStyle(fontSize: 14, color: MyColors.grey67Color, fontWeight: FontWeight.w400)), + 8.height, + Text( + DateFormat('dd MMM yyyy, hh:mm a').format(qrData!.getEmployeeParking!.createdAt!), + style: const TextStyle(fontSize: 16, color: MyColors.darkTextColor, fontWeight: FontWeight.w500), + ), + ], + ], + ), + ), + ], + ), + ), + ), + ), + ), + ); + } + + void getTicketDetails() async { + try { + Utils.showLoading(context); + qrData = await DashboardApiClient().getEmployeeParkingQrCode(); + Utils.hideLoading(context); + setState(() {}); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } +} From e3fcc992df46734eafaf00b66a9aff77c80d7f41 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Sun, 25 Jan 2026 15:10:35 +0300 Subject: [PATCH 06/30] approval level commented Code for filter . --- lib/ui/work_list/itg_fragments/approval_level_fragment.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/ui/work_list/itg_fragments/approval_level_fragment.dart b/lib/ui/work_list/itg_fragments/approval_level_fragment.dart index d7d9c7e..61681eb 100644 --- a/lib/ui/work_list/itg_fragments/approval_level_fragment.dart +++ b/lib/ui/work_list/itg_fragments/approval_level_fragment.dart @@ -21,7 +21,11 @@ class ApprovalLevelfragment extends StatelessWidget { @override Widget build(BuildContext context) { - if (wFHistory.isNotEmpty) wFHistory = wFHistory.reversed.toList(); + if (wFHistory.isNotEmpty) { + //TODO: This COde is Written For Future. If Needed Enable to Filter The Empty Action Requests. + // wFHistory = wFHistory.where((item) => item.action != null && item.action!.isNotEmpty).toList(); + wFHistory = wFHistory.reversed.toList(); + } return Container( width: double.infinity, height: double.infinity, From 9112b269874f7a0c317dde02488ce86c5a2a5dfb Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 27 Jan 2026 18:06:36 +0300 Subject: [PATCH 07/30] updates --- assets/langs/ar-SA.json | 3 ++- assets/langs/en-US.json | 3 ++- lib/classes/consts.dart | 4 ++-- lib/generated/locale_keys.g.dart | 1 + lib/provider/dashboard_provider_model.dart | 3 ++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 84c9b46..acd9660 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -646,5 +646,6 @@ "etqanOVR": "الإبلاغ عن حادثة", "etqanRequest": "طلبات الحوادث", "selectProject": "حدد المشروع", - "project": "مشروع" + "project": "مشروع", + "keen": "حريص" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 8f95e9a..aa51e3e 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -644,5 +644,6 @@ "etqanOVR": "Report An Incident", "etqanRequest": "Incident Requests", "selectProject": "Select Project", - "project": "Project" + "project": "Project", + "keen": "Keen" } \ No newline at end of file diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 29d22d7..dbec87d 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -9,9 +9,9 @@ class ApiConsts { // static String baseUrl = "https://webservices.hmg.com"; // PreProd // static String baseUrl = "https://hmgwebservices.com"; // Live server - // static String baseUrl = "https://mohemm.hmg.com"; // New Live server + static String baseUrl = "https://mohemm.hmg.com"; // New Live server // - static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver + // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver // static String baseUrl = "http://10.20.200.111:1010/"; // static String baseUrl = "https://webservices.hmg.com"; // PreProd diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index c4efa13..b7e7295 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -632,5 +632,6 @@ abstract class LocaleKeys { static const etqanRequest = 'etqanRequest'; static const selectProject = 'selectProject'; static const project = 'project'; + static const keen = 'keen'; } diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index 55e16c3..2d1c50b 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -120,7 +120,8 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { DrawerMenuItem("assets/images/drawer/my_profile.svg", LocaleKeys.myProfile.tr(), AppRoutes.profile), 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.etqanOVR.tr(), AppRoutes.etqanOvr), + // DrawerMenuItem("assets/images/drawer/mowadhafi.svg", LocaleKeys.etqanOVR.tr(), AppRoutes.etqanOvr), + DrawerMenuItem("assets/images/drawer/mowadhafi.svg", LocaleKeys.keen.tr(), AppRoutes.etqanOvr), DrawerMenuItem("assets/images/drawer/pending_trasactions.svg", LocaleKeys.pendingTransactions.tr(), AppRoutes.pendingTransactions), // DrawerMenuItem("assets/images/drawer/drawer_marathon.svg", LocaleKeys.brainMarathon.tr(), AppRoutes.marathonIntroScreen), DrawerMenuItem("assets/images/drawer/change_password.svg", LocaleKeys.changePassword.tr(), AppRoutes.changePassword), From dfa7d9a713f023cc985da56bb421b4cfda573d35 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Wed, 28 Jan 2026 10:05:36 +0300 Subject: [PATCH 08/30] approval level commented Code for filter . --- assets/langs/ar-SA.json | 1 + assets/langs/en-US.json | 1 + lib/api/etqan_ovr_api_client.dart | 4 +-- lib/generated/codegen_loader.g.dart | 2 ++ lib/generated/locale_keys.g.dart | 1 + lib/provider/etqan_ovr_provider.dart | 10 +++++- lib/ui/etqan_ovr/etqan_create_request.dart | 39 ++++++++++++++++++++++ 7 files changed, 55 insertions(+), 3 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 84c9b46..da79f14 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -258,6 +258,7 @@ "fullName": "الأسم الكامل", "remove": "حذف", "submit": "ارسال", + "submitAsAnonymous": "إرسال كمجهول", "areYouSureYouWantToSubmit": "هل أنت متأكد أنك تريد أن تقدم؟", "comments": "تعليقات", "writeComment": "أكتب تعليقا", diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 8f95e9a..f1600be 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -256,6 +256,7 @@ "remove": "Remove", "Attendance": "Attendance", "submit": "Submit", + "submitAsAnonymous": "Submit as Anonymous", "areYouSureYouWantToSubmit": "Are you sure you want to submit?", "comments": "Comments", "writeComment": "Write a comment", diff --git a/lib/api/etqan_ovr_api_client.dart b/lib/api/etqan_ovr_api_client.dart index f9d97bd..3f595a4 100644 --- a/lib/api/etqan_ovr_api_client.dart +++ b/lib/api/etqan_ovr_api_client.dart @@ -93,7 +93,7 @@ class EtqanApiClient { ); } - Future createRequest(String description, int? projectID, String? attachmentBase64) async { + Future createRequest(String description, int? projectID, String? attachmentBase64, bool? isAnonymous) async { String url = "${ApiConsts.cocRest}ETQAN_CreateIncident"; Map postParams = { "P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER, @@ -102,7 +102,7 @@ class EtqanApiClient { "ETQAN_Description": description, "ETQAN_ProjectId": projectID, "ETQAN_FileInfoBase64": attachmentBase64, - "ETQAN_IsAnonymous": false, + "ETQAN_IsAnonymous": isAnonymous ?? false, }; postParams.addAll(AppState().postParamsJson); diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 4cb2359..3eef24e 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -274,6 +274,7 @@ class CodegenLoader extends AssetLoader{ "fullName": "الأسم الكامل", "remove": "حذف", "submit": "ارسال", + "submitAsAnonymous": "إرسال كمجهول", "areYouSureYouWantToSubmit": "هل أنت متأكد أنك تريد أن تقدم؟", "comments": "تعليقات", "writeComment": "أكتب تعليقا", @@ -919,6 +920,7 @@ static const Map _en_US = { "remove": "Remove", "Attendance": "Attendance", "submit": "Submit", + "submitAsAnonymous": "Submit as Anonymous", "areYouSureYouWantToSubmit": "Are you sure you want to submit?", "comments": "Comments", "writeComment": "Write a comment", diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index c4efa13..8402214 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -261,6 +261,7 @@ abstract class LocaleKeys { static const fullName = 'fullName'; static const remove = 'remove'; static const submit = 'submit'; + static const submitAsAnonymous = 'submitAsAnonymous'; static const areYouSureYouWantToSubmit = 'areYouSureYouWantToSubmit'; static const comments = 'comments'; static const writeComment = 'writeComment'; diff --git a/lib/provider/etqan_ovr_provider.dart b/lib/provider/etqan_ovr_provider.dart index a063463..83e4c9b 100644 --- a/lib/provider/etqan_ovr_provider.dart +++ b/lib/provider/etqan_ovr_provider.dart @@ -43,6 +43,7 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { File? attachment; String? attachmentBase64; String description = ''; + bool isAnonymous = false; /// Initialize speech recognition - call this once when screen loads Future initSpeech() async { @@ -149,6 +150,12 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { notifyListeners(); } + // Set isAnonymous + void setIsAnonymous(bool value) { + isAnonymous = value; + notifyListeners(); + } + // Clear create request form void clearCreateRequestForm() { selectedProject = null; @@ -156,6 +163,7 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { attachment = null; attachmentBase64 = null; description = ''; + isAnonymous = false; notifyListeners(); } @@ -163,7 +171,7 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { Future submitRequest(BuildContext context) async { try { Utils.showLoading(context); - int? isSuccess = await EtqanApiClient().createRequest(description, selectedProject?.projectId, attachmentBase64); + int? isSuccess = await EtqanApiClient().createRequest(description, selectedProject?.projectId, attachmentBase64, isAnonymous); Utils.hideLoading(context); if (isSuccess == 1) { getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); diff --git a/lib/ui/etqan_ovr/etqan_create_request.dart b/lib/ui/etqan_ovr/etqan_create_request.dart index 93925e0..8bc320e 100644 --- a/lib/ui/etqan_ovr/etqan_create_request.dart +++ b/lib/ui/etqan_ovr/etqan_create_request.dart @@ -74,6 +74,8 @@ class _EtqanOvrCreateRequestState extends State { _buildAttachmentSection(provider), 16.height, _buildDescriptionField(provider), + 16.height, + _buildAnonymousCheckbox(provider), 24.height, ], )).expanded, @@ -276,4 +278,41 @@ class _EtqanOvrCreateRequestState extends State { ), ); } + + Widget _buildAnonymousCheckbox(EtqanOvrProviderModel provider) { + return InkWell( + onTap: () { + provider.setIsAnonymous(!provider.isAnonymous); + }, + borderRadius: BorderRadius.circular(15), + child: Row( + children: [ + Checkbox( + value: provider.isAnonymous, + onChanged: (bool? value) { + if (value != null) { + provider.setIsAnonymous(value); + } + }, + activeColor: MyColors.gradiantEndColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + LocaleKeys.submitAsAnonymous.tr(), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Color(0xff2B353E), + letterSpacing: -0.44, + ), + ), + ), + ], + ), + ); + } } From eebaafcf7c6b6bdcd3443a0acf1f8c88ead3c1fe Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Wed, 28 Jan 2026 10:21:55 +0300 Subject: [PATCH 09/30] approval level commented Code for filter . --- lib/classes/consts.dart | 4 ++-- lib/ui/etqan_ovr/etqan_create_request.dart | 19 +++---------------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index dbec87d..a6b7173 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -4,12 +4,12 @@ class ApiConsts { // static String baseUrl = "http://10.200.204.11"; // Local server // static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server - // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver + static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver // static String baseUrl = "http://10.201.204.101:2024"; // static String baseUrl = "https://webservices.hmg.com"; // PreProd // static String baseUrl = "https://hmgwebservices.com"; // Live server - static String baseUrl = "https://mohemm.hmg.com"; // New Live server + // static String baseUrl = "https://mohemm.hmg.com"; // New Live server // // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver // static String baseUrl = "http://10.20.200.111:1010/"; diff --git a/lib/ui/etqan_ovr/etqan_create_request.dart b/lib/ui/etqan_ovr/etqan_create_request.dart index 8bc320e..2e546bb 100644 --- a/lib/ui/etqan_ovr/etqan_create_request.dart +++ b/lib/ui/etqan_ovr/etqan_create_request.dart @@ -74,7 +74,7 @@ class _EtqanOvrCreateRequestState extends State { _buildAttachmentSection(provider), 16.height, _buildDescriptionField(provider), - 16.height, + 8.height, _buildAnonymousCheckbox(provider), 24.height, ], @@ -284,7 +284,6 @@ class _EtqanOvrCreateRequestState extends State { onTap: () { provider.setIsAnonymous(!provider.isAnonymous); }, - borderRadius: BorderRadius.circular(15), child: Row( children: [ Checkbox( @@ -295,22 +294,10 @@ class _EtqanOvrCreateRequestState extends State { } }, activeColor: MyColors.gradiantEndColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(4), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), ), const SizedBox(width: 8), - Expanded( - child: Text( - LocaleKeys.submitAsAnonymous.tr(), - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Color(0xff2B353E), - letterSpacing: -0.44, - ), - ), - ), + Expanded(child: Text(LocaleKeys.submitAsAnonymous.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xff2B353E), letterSpacing: -0.44))), ], ), ); From 12d8fd95badd40da6702525359fb939150b2fa25 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Wed, 28 Jan 2026 10:27:08 +0300 Subject: [PATCH 10/30] approval level commented Code for filter . --- lib/ui/etqan_ovr/etqan_create_request.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/etqan_ovr/etqan_create_request.dart b/lib/ui/etqan_ovr/etqan_create_request.dart index 2e546bb..3a253d9 100644 --- a/lib/ui/etqan_ovr/etqan_create_request.dart +++ b/lib/ui/etqan_ovr/etqan_create_request.dart @@ -296,7 +296,7 @@ class _EtqanOvrCreateRequestState extends State { activeColor: MyColors.gradiantEndColor, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(4)), ), - const SizedBox(width: 8), + Expanded(child: Text(LocaleKeys.submitAsAnonymous.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xff2B353E), letterSpacing: -0.44))), ], ), From a426bc332efe453cdcaa3e9cb1aa6fce0c1520b3 Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Thu, 29 Jan 2026 11:04:59 +0300 Subject: [PATCH 11/30] E-learning Mandatory Courses - CR# 7150 --- assets/langs/ar-SA.json | 18 +- assets/langs/en-US.json | 15 +- lib/api/dashboard_api_client.dart | 26 ++ lib/config/routes.dart | 7 + lib/generated/codegen_loader.g.dart | 30 +- lib/generated/locale_keys.g.dart | 12 + .../dashboard/courses_response_model.dart | 91 +++++ lib/models/itg/advertisement.dart | 19 +- lib/provider/dashboard_provider_model.dart | 31 ++ .../itg/its_add_screen_video_image.dart | 43 ++- lib/ui/screens/courses/courses_screen.dart | 328 ++++++++++++++++++ .../ticket/ticket_detailed_screen.dart | 9 +- 12 files changed, 601 insertions(+), 28 deletions(-) create mode 100644 lib/models/dashboard/courses_response_model.dart create mode 100644 lib/ui/screens/courses/courses_screen.dart diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 916e8fb..fd27267 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -642,5 +642,19 @@ "yearsUsed": "سنوات الاستخدام", "faRequest": "طلب التخلص من FA", "showMore": "عرض المزيد", - "buyerDetails": "تفاصيل المشتري" -} \ No newline at end of file + "buyerDetails": "تفاصيل المشتري", + "ticketDetails": "تفاصيل التذكرة", + "currentTicketBalance": "رصيد التذكرة الحالي", + "ticketHistory": "تاريخ التذكرة", + "proceedBooking": "المتابعة للحجز", + "courses": "الدورات التدريبية", + "course": "دورة", + "status": "الحالة", + "deadline": "الموعد النهائي", + "overdue": "متأخر", + "open": "فتح", + "noCoursesAvailable": "لا توجد دورات متاحة", + "pullToRefresh": "اسحب للتحديث", + "courseUrlNotAvailable": "رابط الدورة غير متوفر" +} + diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 5fcb35c..9cb0be2 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -640,5 +640,18 @@ "yearsUsed": "Years Used", "faRequest":"FA Disposal Request", "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" } \ No newline at end of file diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index ae5fda8..20fdf93 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -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/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/itg/itg_main_response.dart'; import 'package:mohem_flutter_app/models/itg/itg_response_model.dart'; @@ -411,4 +412,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 4653880..ca4d30f 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -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/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'; @@ -166,6 +167,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"; @@ -291,6 +295,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 6feb438..ee1ed0e 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -563,7 +563,7 @@ class CodegenLoader extends AssetLoader{ "addAtLeastOneAttachment": "الرجاء إضافة مرفق واحد على الأقل.", "pleaseClickButtonToJoinMarathon": "الرجاء الضغط على الزر أدناه للانضمام إلى الماراثون", "youCannotJoinTheMarathon": "لا يمكنك الانضمام إلى الماراثون لأنك تجاوزت الحد الزمني", - "open": "يفتح", + "open": "فتح", "paymentRequest": "طلب الدفع", "paymentDetails": "تفاصيل الدفع", "requestNo": "رقم الطلب", @@ -655,7 +655,18 @@ class CodegenLoader extends AssetLoader{ "yearsUsed": "سنوات الاستخدام", "faRequest": "طلب التخلص من FA", "showMore": "عرض المزيد", - "buyerDetails": "تفاصيل المشتري" + "buyerDetails": "تفاصيل المشتري", + "ticketDetails": "تفاصيل التذكرة", + "currentTicketBalance": "رصيد التذكرة الحالي", + "ticketHistory": "تاريخ التذكرة", + "proceedBooking": "المتابعة للحجز", + "courses": "الدورات التدريبية", + "course": "دورة", + "status": "الحالة", + "deadline": "الموعد النهائي", + "noCoursesAvailable": "لا توجد دورات متاحة", + "pullToRefresh": "اسحب للتحديث", + "courseUrlNotAvailable": "رابط الدورة غير متوفر" }; static const Map _en_US = { "mohemm": "Mohemm", @@ -1220,7 +1231,7 @@ static const Map _en_US = { "userSearch": "User Search", "userName": "User Name", "userId": "UserID", - "open": "open", + "open": "Open", "addAtLeastOneAttachment": "Please add at least one attachment.", "pleaseClickButtonToJoinMarathon": "Press the button below to join the Marathon.", "youCannotJoinTheMarathon": "You cannot join the Marathon because you have exceeded the time limit.", @@ -1298,7 +1309,18 @@ static const Map _en_US = { "yearsUsed": "Years Used", "faRequest": "FA Disposal Request", "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> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 1a1cf95..b9a3cfc 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -628,5 +628,17 @@ abstract class LocaleKeys { static const faRequest = 'faRequest'; static const showMore = 'showMore'; 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'; } diff --git a/lib/models/dashboard/courses_response_model.dart b/lib/models/dashboard/courses_response_model.dart new file mode 100644 index 0000000..63a98fc --- /dev/null +++ b/lib/models/dashboard/courses_response_model.dart @@ -0,0 +1,91 @@ +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; + + CourseStatus({ + this.cId, + this.cFullname, + this.cNameAr, + this.cShortname, + this.cStatus, + this.cIsMohemmAffective, + this.cStartdate, + this.cEnddate, + this.cUrl, + }); + + 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']; + } + + 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; + 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 05c9ebb..00f339d 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 { @@ -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/mowadhafi.svg", LocaleKeys.mowadhafhi.tr(), AppRoutes.mowadhafhi), 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), ]; @@ -409,6 +416,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..ab0d39f 100644 --- a/lib/ui/landing/itg/its_add_screen_video_image.dart +++ b/lib/ui/landing/itg/its_add_screen_video_image.dart @@ -26,7 +26,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 +125,7 @@ class _ITGAdsScreenState extends State { @override void dispose() { - if (_controller != null) _controller.dispose(); + _controller?.dispose(); super.dispose(); } @@ -166,11 +166,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 +230,7 @@ class _ITGAdsScreenState extends State { alignment: Alignment.center, children: [ MyVideoProgressIndicator( - _controller, + _controller!, allowScrubbing: false, //padding: EdgeInsets.only(left: 60, right: 60), ), @@ -386,16 +386,29 @@ 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 { + 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..dc00ea1 --- /dev/null +++ b/lib/ui/screens/courses/courses_screen.dart @@ -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 { + 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 ?? ''), + 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(); + } +} 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))); } From 5705619ce4f93b766e26c0185b28c90646a245fc Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Thu, 29 Jan 2026 11:11:56 +0300 Subject: [PATCH 12/30] untracked icon added --- assets/images/drawer/courses.svg | 8 ++++++++ assets/images/ticket_bal.svg | 6 ++++++ assets/images/vacation_rule.svg | 16 ++++++++++++++++ assets/images/worklist/approve.svg | 1 + assets/images/worklist/reject.svg | 1 + 5 files changed, 32 insertions(+) create mode 100644 assets/images/drawer/courses.svg create mode 100644 assets/images/ticket_bal.svg create mode 100644 assets/images/vacation_rule.svg create mode 100644 assets/images/worklist/approve.svg create mode 100644 assets/images/worklist/reject.svg 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 From 19ccee6a208127fc4c9d40b338ef80de197c56bf Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Thu, 29 Jan 2026 11:23:29 +0300 Subject: [PATCH 13/30] updates --- assets/langs/ar-SA.json | 2 +- lib/generated/codegen_loader.g.dart | 10 ++++++---- lib/generated/locale_keys.g.dart | 2 +- lib/ui/screens/courses/courses_screen.dart | 6 +++--- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index aded425..5eb2c59 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -653,7 +653,7 @@ "overdue": "متأخر", "noCoursesAvailable": "لا توجد دورات متاحة", "pullToRefresh": "اسحب للتحديث", - "courseUrlNotAvailable": "رابط الدورة غير متوفر" + "courseUrlNotAvailable": "رابط الدورة غير متوفر", "buyerDetails": "تفاصيل المشتري", "parkingQr": "وقوف السيارات QR" } diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index e2de580..4a92eef 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -655,7 +655,6 @@ class CodegenLoader extends AssetLoader{ "yearsUsed": "سنوات الاستخدام", "faRequest": "طلب التخلص من FA", "showMore": "عرض المزيد", - "buyerDetails": "تفاصيل المشتري", "ticketDetails": "تفاصيل التذكرة", "currentTicketBalance": "رصيد التذكرة الحالي", "ticketHistory": "تاريخ التذكرة", @@ -664,9 +663,11 @@ class CodegenLoader extends AssetLoader{ "course": "دورة", "status": "الحالة", "deadline": "الموعد النهائي", + "overdue": "متأخر", "noCoursesAvailable": "لا توجد دورات متاحة", "pullToRefresh": "اسحب للتحديث", "courseUrlNotAvailable": "رابط الدورة غير متوفر", + "buyerDetails": "تفاصيل المشتري", "parkingQr": "وقوف السيارات QR" }; static const Map _en_US = { @@ -1310,8 +1311,6 @@ static const Map _en_US = { "yearsUsed": "Years Used", "faRequest": "FA Disposal Request", "showMore": "Show More", - "buyerDetails": "Buyer Details", - "parkingQr": "Parking QR", "ticketDetails": "Ticket Details", "currentTicketBalance": "Current Ticket Balance", "ticketHistory": "Ticket History", @@ -1320,9 +1319,12 @@ static const Map _en_US = { "course": "Course", "status": "Status", "deadline": "Deadline", + "overdue": "Overdue", "noCoursesAvailable": "No Courses Available", "pullToRefresh": "Pull to refresh", - "courseUrlNotAvailable": "Course URL not available" + "courseUrlNotAvailable": "Course URL not available", + "buyerDetails": "Buyer Details", + "parkingQr": "Parking QR" }; static const Map> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 37d0b60..57aad94 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -627,7 +627,6 @@ abstract class LocaleKeys { static const yearsUsed = 'yearsUsed'; static const faRequest = 'faRequest'; static const showMore = 'showMore'; - static const buyerDetails = 'buyerDetails'; static const ticketDetails = 'ticketDetails'; static const currentTicketBalance = 'currentTicketBalance'; static const ticketHistory = 'ticketHistory'; @@ -640,6 +639,7 @@ abstract class LocaleKeys { static const noCoursesAvailable = 'noCoursesAvailable'; static const pullToRefresh = 'pullToRefresh'; static const courseUrlNotAvailable = 'courseUrlNotAvailable'; + static const buyerDetails = 'buyerDetails'; static const parkingQr = 'parkingQr'; } diff --git a/lib/ui/screens/courses/courses_screen.dart b/lib/ui/screens/courses/courses_screen.dart index dc00ea1..7c995a2 100644 --- a/lib/ui/screens/courses/courses_screen.dart +++ b/lib/ui/screens/courses/courses_screen.dart @@ -201,7 +201,7 @@ class _CoursesScreenState extends State { ), 10.height, // Start Date - if (course.cStartdate != null && course.cStartdate!.isNotEmpty) + if (course.cEnddate != null && course.cEnddate!.isNotEmpty) Row( children: [ const Icon( @@ -210,10 +210,10 @@ class _CoursesScreenState extends State { color: MyColors.darkIconColor, ), 6.width, - LocaleKeys.startDateT.tr().toText11(color: MyColors.grey98Color), + LocaleKeys.endDateT.tr().toText11(color: MyColors.grey98Color), 8.width, Expanded( - child: _formatDate(course.cStartdate!).toText11( + child: _formatDate(course.cEnddate!).toText11( color: MyColors.darkTextColor, isBold: true, ), From eea35e4d77a4373f9582a93fed0b2de32e23557e Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Sun, 15 Feb 2026 15:09:38 +0300 Subject: [PATCH 14/30] approval level commented Code for filter . --- ios/Runner/Info.plist | 2 ++ lib/api/dashboard_api_client.dart | 2 +- lib/api/etqan_ovr_api_client.dart | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 01197de..595a98f 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -61,6 +61,8 @@ This app requires photo library access to select image as document & upload it. UIApplicationSupportsIndirectInputEvents +keen ovr NSSpeechRecognitionUsageDescription + This app requires speech recognition access to access voice command features. UIBackgroundModes fetch diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index 2bddb42..5256084 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -226,7 +226,7 @@ class DashboardApiClient { } Future getBookingSSOFinalRedirection({required String token}) async { - token = + // token = "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhjZTE2OWM0YjIwYjQ2ZWM5YTQyOTU3Y2ZhODUzNzQ1IiwidHlwIjoiSldUIn0.eyJ0ZW5hbnRfaWQiOiJhOWY0ZDFhMDU5NmQ0YWVhOGY4MzA5OTJlYzRiZGFjMSIsImVpZCI6IjExNzkzMCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3NpZCI6Ijk2MDI0OGM1NzA3YzQ3MmFhYTEzM2I1N2ZhODE1ZmVhIiwibGFuZ3VhZ2UiOiJVUyIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6IjExNzkzMEBobWcuY29tIiwiZXhwIjoxNzgyNDc1NzY5LCJpc3MiOiJodHRwczovL3Nzby11YXQuaG1nLmNvbSIsImF1ZCI6ImE5ZjRkMWEwNTk2ZDRhZWE4ZjgzMDk5MmVjNGJkYWMxIn0.rJcLVsG8D0XECyLERCTD2uqGeWyvp-OBVGE9uL2qKrX4etFUHgdFt_5kYF6edFTtGy-0PIZadHDmv7e-IOhVWHm5HVMClaukiXoRXR8cDN8XA1wfme3Kd-U5PXN-IRh49AyRTzLO0rYNPvH81ScosWGlsFSkOvA-0hJNa2adHdtvgNvB8wJshSU5p7sAmF8mjdDY6aInG19etu2iEuUDwHHA4ZY_ts4hboHo8fE392hFaYGonExoD7bpW5RMx5xKWeRCmWpG_PK8Aw_z1jGzdB9PANus4pteRGuln1J-kmo2lQC9pVrSyZATAKp1HfgfyZ_vUhaHEfM69cMWaCslJQ"; // var request = http.MultipartRequest('POST', Uri.parse('https://ek.techmaster.in/SSO/HMG')); var request = http.MultipartRequest('POST', Uri.parse('https://Paxes-release.techmaster.in/SSO/HMG')); diff --git a/lib/api/etqan_ovr_api_client.dart b/lib/api/etqan_ovr_api_client.dart index 3f595a4..e4b4e61 100644 --- a/lib/api/etqan_ovr_api_client.dart +++ b/lib/api/etqan_ovr_api_client.dart @@ -55,7 +55,7 @@ class EtqanApiClient { (json) { List responseData = []; if (json["ETQAN_GetEmployeeOVRRequests_Response"] != null && json["ETQAN_GetEmployeeOVRRequests_Response"].isNotEmpty) { - json["ETQAN_GetEmployeeOVRRequests_Response"].forEach((element) { + json["ETQAN_GetEmployeeOVRRequests_Response"].forEach((Map element) { responseData.add(EtqanGetEmployeeOvrRequestsResponse.fromJson(element)); }); } From 048c9b395f57fbafdce4fa0b085f09cc3ed53781 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Wed, 18 Feb 2026 13:13:23 +0300 Subject: [PATCH 15/30] approval level commented Code for filter . --- ios/Runner/Info.plist | 2 +- lib/api/etqan_ovr_api_client.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 595a98f..51a5801 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -61,7 +61,7 @@ This app requires photo library access to select image as document & upload it. UIApplicationSupportsIndirectInputEvents -keen ovr NSSpeechRecognitionUsageDescription + NSSpeechRecognitionUsageDescription This app requires speech recognition access to access voice command features. UIBackgroundModes diff --git a/lib/api/etqan_ovr_api_client.dart b/lib/api/etqan_ovr_api_client.dart index e4b4e61..3f595a4 100644 --- a/lib/api/etqan_ovr_api_client.dart +++ b/lib/api/etqan_ovr_api_client.dart @@ -55,7 +55,7 @@ class EtqanApiClient { (json) { List responseData = []; if (json["ETQAN_GetEmployeeOVRRequests_Response"] != null && json["ETQAN_GetEmployeeOVRRequests_Response"].isNotEmpty) { - json["ETQAN_GetEmployeeOVRRequests_Response"].forEach((Map element) { + json["ETQAN_GetEmployeeOVRRequests_Response"].forEach((element) { responseData.add(EtqanGetEmployeeOvrRequestsResponse.fromJson(element)); }); } From 82c9aa8a2f34e8d14c6f1f4cb3bd15e46dcf5d39 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 27 Jan 2026 17:33:38 +0300 Subject: [PATCH 16/30] updates --- lib/api/dashboard_api_client.dart | 3 ++- lib/classes/consts.dart | 4 ++-- lib/ui/work_list/itg_fragments/approval_level_fragment.dart | 4 ++-- pubspec.yaml | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index 6f7288c..0b8247f 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -230,7 +230,8 @@ class DashboardApiClient { // token = // "eyJhbGciOiJSUzI1NiIsImtpZCI6IjhjZTE2OWM0YjIwYjQ2ZWM5YTQyOTU3Y2ZhODUzNzQ1IiwidHlwIjoiSldUIn0.eyJ0ZW5hbnRfaWQiOiJhOWY0ZDFhMDU5NmQ0YWVhOGY4MzA5OTJlYzRiZGFjMSIsImVpZCI6IjExNzkzMCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3NpZCI6Ijk2MDI0OGM1NzA3YzQ3MmFhYTEzM2I1N2ZhODE1ZmVhIiwibGFuZ3VhZ2UiOiJVUyIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6IjExNzkzMEBobWcuY29tIiwiZXhwIjoxNzgyNDc1NzY5LCJpc3MiOiJodHRwczovL3Nzby11YXQuaG1nLmNvbSIsImF1ZCI6ImE5ZjRkMWEwNTk2ZDRhZWE4ZjgzMDk5MmVjNGJkYWMxIn0.rJcLVsG8D0XECyLERCTD2uqGeWyvp-OBVGE9uL2qKrX4etFUHgdFt_5kYF6edFTtGy-0PIZadHDmv7e-IOhVWHm5HVMClaukiXoRXR8cDN8XA1wfme3Kd-U5PXN-IRh49AyRTzLO0rYNPvH81ScosWGlsFSkOvA-0hJNa2adHdtvgNvB8wJshSU5p7sAmF8mjdDY6aInG19etu2iEuUDwHHA4ZY_ts4hboHo8fE392hFaYGonExoD7bpW5RMx5xKWeRCmWpG_PK8Aw_z1jGzdB9PANus4pteRGuln1J-kmo2lQC9pVrSyZATAKp1HfgfyZ_vUhaHEfM69cMWaCslJQ"; // // var request = http.MultipartRequest('POST', Uri.parse('https://ek.techmaster.in/SSO/HMG')); - var request = http.MultipartRequest('POST', Uri.parse('https://Paxes-release.techmaster.in/SSO/HMG')); + var request = http.MultipartRequest('POST', Uri.parse('https://Paxes-release.techmaster.in/SSO/HMG')); // UAT + // var request = http.MultipartRequest('POST', Uri.parse('https://paxes.com/sso/hmg')); // Prod request.fields.addAll({'JWTToken': token}); // request.headers.addAll(headers); diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index a6b7173..2f60326 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -11,7 +11,6 @@ class ApiConsts { // static String baseUrl = "https://mohemm.hmg.com"; // New Live server // - // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver // static String baseUrl = "http://10.20.200.111:1010/"; // static String baseUrl = "https://webservices.hmg.com"; // PreProd @@ -27,7 +26,8 @@ class ApiConsts { static String user = baseUrlServices + "api/User/"; static String cocRest = baseUrlServices + "COCWS.svc/REST/"; - static String ssoAuthRedirection = "https://sso-uat.hmg.com/api/auth/connect"; + // static String ssoAuthRedirection = "https://sso-uat.hmg.com/api/auth/connect"; // UAT + static String ssoAuthRedirection = "https://sso.hmg.com/api/auth/connect"; // Prod //Chat static String chatServerBaseUrl = "https://apiderichat.hmg.com/"; diff --git a/lib/ui/work_list/itg_fragments/approval_level_fragment.dart b/lib/ui/work_list/itg_fragments/approval_level_fragment.dart index 61681eb..9d7e94a 100644 --- a/lib/ui/work_list/itg_fragments/approval_level_fragment.dart +++ b/lib/ui/work_list/itg_fragments/approval_level_fragment.dart @@ -168,8 +168,8 @@ class ApprovalLevelfragment extends StatelessWidget { if (wFHistory[index].date!.isEmpty || wFHistory[index].action!.toLowerCase() == "NO ACTION" || wFHistory[index + 1].date! == "") { return ""; } else { - DateTime dateTimeTo = DateUtil.convertSimpleStringDateToDate(wFHistory[index].date!); - DateTime dateTimeFrom = DateUtil.convertSimpleStringDateToDate(wFHistory[index + 1].date!); + DateTime dateTimeTo = DateUtil.convertSimpleStringDateToDate(wFHistory[index].date!, isITG: true); + DateTime dateTimeFrom = DateUtil.convertSimpleStringDateToDate(wFHistory[index + 1].date!, isITG: true); Duration duration = dateTimeTo.difference(dateTimeFrom); return "Action duration: " + DateUtil.formatDuration(duration); } diff --git a/pubspec.yaml b/pubspec.yaml index 6abe6fc..fde2c08 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,8 +17,8 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -#version: 3.7.1+300081 -version: 3.9.3+1 +version: 3.7.1+300081 +#version: 3.9.3+1 environment: sdk: ^3.7.0 From 31d1804f7a6bc1ee3268e846043e42b65eff1830 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Thu, 19 Feb 2026 11:40:23 +0300 Subject: [PATCH 17/30] QIPS-1175,QIPS-1179,QIPS-1180 --- assets/langs/ar-SA.json | 6 ++- assets/langs/en-US.json | 6 ++- lib/api/etqan_ovr_api_client.dart | 4 +- lib/generated/codegen_loader.g.dart | 14 +++++- lib/generated/locale_keys.g.dart | 4 ++ lib/provider/etqan_ovr_provider.dart | 18 +++++--- lib/ui/etqan_ovr/etqan_create_request.dart | 52 ++++++++++++++++++++-- 7 files changed, 87 insertions(+), 17 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 49e2d82..64708d4 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -164,6 +164,7 @@ "quotationAnalysis": "تحليل الاقتباس", "subject": "موضوعات", "description": "وصف", + "pleaseEnterDescription": "الرجاء إدخال الوصف", "supplier": "المورد", "site": "موقع", "buyer": "مشتر", @@ -648,5 +649,8 @@ "etqanRequest": "طلبات الحوادث", "selectProject": "حدد المشروع", "project": "مشروع", - "keen": "حريص" + "keen": "حريص", + "ticketNumber": "رقم التذكرة", + "ticketId": "معرف التذكرة", + "pleaseSelectProject": "الرجاء اختيار مشروع" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 1e69102..b130204 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -158,6 +158,7 @@ "subject": "Subject", "quotationAnalysis": "Quotation Analysis", "description": "Description", + "pleaseEnterDescription": "Please enter description", "supplier": "Supplier", "site": "Site", "buyer": "Buyer", @@ -646,5 +647,8 @@ "etqanRequest": "Incident Requests", "selectProject": "Select Project", "project": "Project", - "keen": "Keen" + "keen": "Keen", + "ticketNumber": "Ticket Number", + "ticketId": "Ticket ID", + "pleaseSelectProject": "Please select a project" } \ No newline at end of file diff --git a/lib/api/etqan_ovr_api_client.dart b/lib/api/etqan_ovr_api_client.dart index 3f595a4..1018177 100644 --- a/lib/api/etqan_ovr_api_client.dart +++ b/lib/api/etqan_ovr_api_client.dart @@ -93,7 +93,7 @@ class EtqanApiClient { ); } - Future createRequest(String description, int? projectID, String? attachmentBase64, bool? isAnonymous) async { + Future?> createRequest(String description, int? projectID, String? attachmentBase64, bool? isAnonymous) async { String url = "${ApiConsts.cocRest}ETQAN_CreateIncident"; Map postParams = { "P_SELECTED_EMPLOYEE_NUMBER": AppState().memberInformationList?.eMPLOYEENUMBER, @@ -111,7 +111,7 @@ class EtqanApiClient { if (json["ETQAN_CreateIncident_Response"] != null) { var response = json["ETQAN_CreateIncident_Response"]; if (response["id"] != null && response["ticketNumber"] != null) { - return 1; + return {"id": response["id"], "ticketNumber": response["ticketNumber"]}; } return null; } diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 3eef24e..9f72052 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -180,6 +180,7 @@ class CodegenLoader extends AssetLoader{ "quotationAnalysis": "تحليل الاقتباس", "subject": "موضوعات", "description": "وصف", + "pleaseEnterDescription": "الرجاء إدخال الوصف", "supplier": "المورد", "site": "موقع", "buyer": "مشتر", @@ -660,7 +661,11 @@ class CodegenLoader extends AssetLoader{ "etqanOVR": "الإبلاغ عن حادثة", "etqanRequest": "طلبات الحوادث", "selectProject": "حدد المشروع", - "project": "مشروع" + "project": "مشروع", + "keen": "حريص", + "ticketNumber": "رقم التذكرة", + "ticketId": "معرف التذكرة", + "pleaseSelectProject": "الرجاء اختيار مشروع" }; static const Map _en_US = { "mohemm": "Mohemm", @@ -822,6 +827,7 @@ static const Map _en_US = { "subject": "Subject", "quotationAnalysis": "Quotation Analysis", "description": "Description", + "pleaseEnterDescription": "Please enter description", "supplier": "Supplier", "site": "Site", "buyer": "Buyer", @@ -1308,7 +1314,11 @@ static const Map _en_US = { "etqanOVR": "Report An Incident", "etqanRequest": "Incident Requests", "selectProject": "Select Project", - "project": "Project" + "project": "Project", + "keen": "Keen", + "ticketNumber": "Ticket Number", + "ticketId": "Ticket ID", + "pleaseSelectProject": "Please select a project" }; static const Map> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 7c74caa..6bcc80b 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -167,6 +167,7 @@ abstract class LocaleKeys { static const quotationAnalysis = 'quotationAnalysis'; static const subject = 'subject'; static const description = 'description'; + static const pleaseEnterDescription = 'pleaseEnterDescription'; static const supplier = 'supplier'; static const site = 'site'; static const buyer = 'buyer'; @@ -634,5 +635,8 @@ abstract class LocaleKeys { static const selectProject = 'selectProject'; static const project = 'project'; static const keen = 'keen'; + static const ticketNumber = 'ticketNumber'; + static const ticketId = 'ticketId'; + static const pleaseSelectProject = 'pleaseSelectProject'; } diff --git a/lib/provider/etqan_ovr_provider.dart b/lib/provider/etqan_ovr_provider.dart index 83e4c9b..b717b0f 100644 --- a/lib/provider/etqan_ovr_provider.dart +++ b/lib/provider/etqan_ovr_provider.dart @@ -150,6 +150,11 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { notifyListeners(); } + // Validate description + bool isDescriptionValid() { + return description.trim().isNotEmpty; + } + // Set isAnonymous void setIsAnonymous(bool value) { isAnonymous = value; @@ -168,22 +173,21 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } // Submit request - Future submitRequest(BuildContext context) async { + Future?> submitRequest(BuildContext context) async { try { Utils.showLoading(context); - int? isSuccess = await EtqanApiClient().createRequest(description, selectedProject?.projectId, attachmentBase64, isAnonymous); + Map? response = await EtqanApiClient().createRequest(description, selectedProject?.projectId, attachmentBase64, isAnonymous); Utils.hideLoading(context); - if (isSuccess == 1) { + if (response != null) { getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); notifyListeners(); - Navigator.of(context).pop(); - return true; + return response; } - return false; + return null; } catch (ex) { Utils.hideLoading(context); Utils.handleException(ex, context, null); - return false; + return null; } } } diff --git a/lib/ui/etqan_ovr/etqan_create_request.dart b/lib/ui/etqan_ovr/etqan_create_request.dart index 3a253d9..1618711 100644 --- a/lib/ui/etqan_ovr/etqan_create_request.dart +++ b/lib/ui/etqan_ovr/etqan_create_request.dart @@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; @@ -60,6 +61,7 @@ class _EtqanOvrCreateRequestState extends State { hint: LocaleKeys.selectProject.tr(), value: _getProjectDisplayName(provider.selectedProject), onTap: () => _showProjectSelectionSheet(context, provider), + isMandatory: true, ), 16.height, // _buildDropdownField( @@ -81,7 +83,39 @@ class _EtqanOvrCreateRequestState extends State { )).expanded, DefaultButton(LocaleKeys.submit.tr(), () async { provider.setDescription(_descriptionController.text); - await provider.submitRequest(context); + if (provider.selectedProject == null) { + Utils.showErrorDialog( + context: context, + message: LocaleKeys.pleaseSelectProject.tr(), + onOkTapped: () { + Navigator.of(context).pop(); + }, + ); + return; + } + if (!provider.isDescriptionValid()) { + Utils.showErrorDialog( + context: context, + message: LocaleKeys.pleaseEnterDescription.tr(), + onOkTapped: () { + Navigator.of(context).pop(); + }, + ); + return; + } + Map? ticketInfo = await provider.submitRequest(context); + if (ticketInfo != null) { + String ticketNumber = ticketInfo['ticketNumber'] ?? ''; + String ticketId = ticketInfo['id'] ?? ''; + String successMessage = '${LocaleKeys.requestCreatedSuccessfully.tr()}\n\n${LocaleKeys.ticketNumber.tr()}: $ticketNumber\n${LocaleKeys.ticketId.tr()}: $ticketId'; + Utils.showErrorDialog( + context: context, + message: successMessage, + onOkTapped: () { + Navigator.of(context).pop(); + }, + ); + } }).insideContainer, ], ); @@ -137,7 +171,7 @@ class _EtqanOvrCreateRequestState extends State { ); } - Widget _buildDropdownField({required String label, required String hint, String? value, required VoidCallback onTap}) { + Widget _buildDropdownField({required String label, required String hint, String? value, required VoidCallback onTap, bool isMandatory = false}) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(15), @@ -150,7 +184,12 @@ class _EtqanOvrCreateRequestState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(label, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.44)), + Row( + children: [ + Text(label, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.44)), + if (isMandatory) const Text(' *', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Colors.red, letterSpacing: -0.44)), + ], + ), const SizedBox(height: 4), Text(value ?? hint, style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400, color: value != null ? const Color(0xff2B353E) : const Color(0xff575757), letterSpacing: -0.56)), ], @@ -227,7 +266,12 @@ class _EtqanOvrCreateRequestState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text(LocaleKeys.description.tr(), style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.44)), + Row( + children: [ + Text(LocaleKeys.description.tr(), style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Color(0xff2B353E), letterSpacing: -0.44)), + const Text(' *', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: Colors.red, letterSpacing: -0.44)), + ], + ), GestureDetector( onTap: provider.speechEnabled From 2c7d3bf963546404b1766da9632e0a66174ea723 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Thu, 26 Feb 2026 14:51:56 +0300 Subject: [PATCH 18/30] QIPS-1175,QIPS-1179,QIPS-1180 --- assets/langs/ar-SA.json | 6 +- assets/langs/en-US.json | 6 +- lib/classes/utils.dart | 4 +- lib/generated/codegen_loader.g.dart | 12 ++- lib/generated/locale_keys.g.dart | 4 + .../etqan_get_employee_incident_report.dart | 12 +++ lib/provider/etqan_ovr_provider.dart | 13 ++- lib/ui/etqan_ovr/etqan_create_request.dart | 8 +- lib/ui/etqan_ovr/etqan_ovr_home.dart | 6 +- lib/ui/etqan_ovr/etqan_request_detailed.dart | 97 +++++++++++++++++-- 10 files changed, 150 insertions(+), 18 deletions(-) diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 64708d4..88a3891 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -650,7 +650,11 @@ "selectProject": "حدد المشروع", "project": "مشروع", "keen": "حريص", + "reportIncident": "الإبلاغ عن الحادث", + "myIncidents": "حوادثي", + "incidentDetails": "تفاصيل الحادث", "ticketNumber": "رقم التذكرة", "ticketId": "معرف التذكرة", - "pleaseSelectProject": "الرجاء اختيار مشروع" + "pleaseSelectProject": "الرجاء اختيار مشروع", + "viewAttachment" : "عرض المرفق" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index b130204..4e29cbc 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -648,7 +648,11 @@ "selectProject": "Select Project", "project": "Project", "keen": "Keen", + "reportIncident": "Report Incident", + "myIncidents": "My Incidents", + "incidentDetails": "Incident Details", "ticketNumber": "Ticket Number", "ticketId": "Ticket ID", - "pleaseSelectProject": "Please select a project" + "pleaseSelectProject": "Please select a project", + "viewAttachment" : "View Attachment" } \ No newline at end of file diff --git a/lib/classes/utils.dart b/lib/classes/utils.dart index 6a9b16b..2ae4fe3 100644 --- a/lib/classes/utils.dart +++ b/lib/classes/utils.dart @@ -135,8 +135,8 @@ class Utils { } } - static Future showErrorDialog({required BuildContext context, required VoidCallback onOkTapped, required String message}) async { - return showDialog(context: context, builder: (BuildContext context) => ConfirmDialog(message: message, onTap: onOkTapped)); + static Future showErrorDialog({required BuildContext context, required VoidCallback onOkTapped, required String message, VoidCallback? onCloseTap}) async { + return showDialog(context: context, builder: (BuildContext context) => ConfirmDialog(message: message, onTap: onOkTapped, onCloseTap: onCloseTap)); } static void confirmDialog(cxt, String message, {VoidCallback? onTap}) { diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 9f72052..1a3ec71 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -663,9 +663,13 @@ class CodegenLoader extends AssetLoader{ "selectProject": "حدد المشروع", "project": "مشروع", "keen": "حريص", + "reportIncident": "الإبلاغ عن الحادث", + "myIncidents": "حوادثي", + "incidentDetails": "تفاصيل الحادث", "ticketNumber": "رقم التذكرة", "ticketId": "معرف التذكرة", - "pleaseSelectProject": "الرجاء اختيار مشروع" + "pleaseSelectProject": "الرجاء اختيار مشروع", + "viewAttachment": "عرض المرفق" }; static const Map _en_US = { "mohemm": "Mohemm", @@ -1316,9 +1320,13 @@ static const Map _en_US = { "selectProject": "Select Project", "project": "Project", "keen": "Keen", + "reportIncident": "Report Incident", + "myIncidents": "My Incidents", + "incidentDetails": "Incident Details", "ticketNumber": "Ticket Number", "ticketId": "Ticket ID", - "pleaseSelectProject": "Please select a project" + "pleaseSelectProject": "Please select a project", + "viewAttachment": "View Attachment" }; static const Map> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 6bcc80b..d060682 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -635,8 +635,12 @@ abstract class LocaleKeys { static const selectProject = 'selectProject'; static const project = 'project'; static const keen = 'keen'; + static const reportIncident = 'reportIncident'; + static const myIncidents = 'myIncidents'; + static const incidentDetails = 'incidentDetails'; static const ticketNumber = 'ticketNumber'; static const ticketId = 'ticketId'; static const pleaseSelectProject = 'pleaseSelectProject'; + static const viewAttachment = 'viewAttachment'; } diff --git a/lib/models/etqan_ovr/etqan_get_employee_incident_report.dart b/lib/models/etqan_ovr/etqan_get_employee_incident_report.dart index 3743cf3..e4a57fd 100644 --- a/lib/models/etqan_ovr/etqan_get_employee_incident_report.dart +++ b/lib/models/etqan_ovr/etqan_get_employee_incident_report.dart @@ -22,11 +22,17 @@ class EtqanGetEmployeeIncidentRequestModel { class EtqanGetIncidentRequestResponse { String? key; + String? keyAr; String? value; + String? valueAr; + List? subValues; EtqanGetIncidentRequestResponse({ this.key, + this.keyAr, this.value, + this.valueAr, + this.subValues, }); factory EtqanGetIncidentRequestResponse.fromRawJson(String str) => EtqanGetIncidentRequestResponse.fromJson(json.decode(str)); @@ -35,11 +41,17 @@ class EtqanGetIncidentRequestResponse { factory EtqanGetIncidentRequestResponse.fromJson(Map json) => EtqanGetIncidentRequestResponse( key: json["key"], + keyAr: json["keyAr"], value: json["value"], + valueAr: json["valueAr"], + subValues: json["subValues"] == null ? [] : List.from(json["subValues"]!.map((x) => x)), ); Map toJson() => { "key": key, + "keyAr": keyAr, "value": value, + "valueAr": valueAr, + "subValues": subValues == null ? [] : List.from(subValues!.map((x) => x)), }; } diff --git a/lib/provider/etqan_ovr_provider.dart b/lib/provider/etqan_ovr_provider.dart index b717b0f..1f6ab80 100644 --- a/lib/provider/etqan_ovr_provider.dart +++ b/lib/provider/etqan_ovr_provider.dart @@ -47,7 +47,15 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { /// Initialize speech recognition - call this once when screen loads Future initSpeech() async { - _speechEnabled = await _speechToText.initialize(); + _speechEnabled = await _speechToText.initialize( + onStatus: (String status) { + // Update listening state when speech recognition status changes + if (status == 'done' || status == 'notListening') { + _isListening = false; + notifyListeners(); + } + }, + ); notifyListeners(); } @@ -61,6 +69,9 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { notifyListeners(); }, localeId: localeId, + listenFor: const Duration(seconds: 30), + pauseFor: const Duration(seconds: 3), + listenMode: ListenMode.confirmation, ); _isListening = true; notifyListeners(); diff --git a/lib/ui/etqan_ovr/etqan_create_request.dart b/lib/ui/etqan_ovr/etqan_create_request.dart index 1618711..ce636ff 100644 --- a/lib/ui/etqan_ovr/etqan_create_request.dart +++ b/lib/ui/etqan_ovr/etqan_create_request.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; @@ -46,7 +47,7 @@ class _EtqanOvrCreateRequestState extends State { Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, - appBar: AppBarWidget(context, title: LocaleKeys.createRequest.tr(), showHomeButton: false), + appBar: AppBarWidget(context, title: LocaleKeys.reportIncident.tr(), showHomeButton: false), body: Consumer( builder: (BuildContext context, EtqanOvrProviderModel provider, Widget? child) { return Column( @@ -112,7 +113,10 @@ class _EtqanOvrCreateRequestState extends State { context: context, message: successMessage, onOkTapped: () { - Navigator.of(context).pop(); + Navigator.popAndPushNamed(context, AppRoutes.etqanOvr); + }, + onCloseTap: () { + Navigator.popAndPushNamed(context, AppRoutes.etqanOvr); }, ); } diff --git a/lib/ui/etqan_ovr/etqan_ovr_home.dart b/lib/ui/etqan_ovr/etqan_ovr_home.dart index 7a48a51..c4174a1 100644 --- a/lib/ui/etqan_ovr/etqan_ovr_home.dart +++ b/lib/ui/etqan_ovr/etqan_ovr_home.dart @@ -4,7 +4,7 @@ import 'package:flutter_svg/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/config/routes.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/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; @@ -36,7 +36,7 @@ class _EtqanOvrHomeState extends State { Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, - appBar: AppBarWidget(context, title: LocaleKeys.etqanRequest.tr()), + appBar: AppBarWidget(context, title: LocaleKeys.myIncidents.tr()), body: Consumer( builder: (BuildContext context, EtqanOvrProviderModel provider, Widget? child) { return Column( @@ -93,7 +93,7 @@ class _EtqanOvrHomeState extends State { itemCount: provider.getEtqanEmployeeRequestsList!.length, )) .expanded, - DefaultButton(LocaleKeys.createRequest.tr(), () async { + DefaultButton(LocaleKeys.reportIncident.tr(), () async { await Navigator.pushNamed(context, AppRoutes.etqanCreateRequest); // getOpenTickets(); }).insideContainer, diff --git a/lib/ui/etqan_ovr/etqan_request_detailed.dart b/lib/ui/etqan_ovr/etqan_request_detailed.dart index b1ed12a..6f9aebc 100644 --- a/lib/ui/etqan_ovr/etqan_request_detailed.dart +++ b/lib/ui/etqan_ovr/etqan_request_detailed.dart @@ -1,3 +1,7 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; @@ -10,6 +14,8 @@ import 'package:mohem_flutter_app/provider/etqan_ovr_provider.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:provider/provider.dart'; +import 'package:open_filex/open_filex.dart'; +import 'package:path_provider/path_provider.dart'; class EtqanOvrRequestDetailed extends StatefulWidget { const EtqanOvrRequestDetailed({Key? key}) : super(key: key); @@ -23,6 +29,24 @@ class _EtqanOvrRequestDetailedState extends State { List get hiddenFields => ['OVR Id', 'Is Anonymous']; + Future _viewAttachment(BuildContext context, String base64Data) async { + try { + Uint8List bytes = base64.decode(base64Data); + String dir = (await getApplicationDocumentsDirectory()).path; + String ext = 'pdf'; + if (base64Data.startsWith('/9j/')) { + ext = 'jpg'; + } else if (base64Data.startsWith('iVBORw0KGgo')) { + ext = 'png'; + } + File file = File("$dir/${DateTime.now().millisecondsSinceEpoch}.$ext"); + await file.writeAsBytes(bytes); + OpenFilex.open(file.path); + } catch (e) { + Utils.showToast("Cannot open attachment."); + } + } + List _getOrderedList(List list) { List orderedList = []; @@ -48,7 +72,7 @@ class _EtqanOvrRequestDetailedState extends State { void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - var provider = context.read(); + EtqanOvrProviderModel provider = context.read(); provider.fetchEtqanEmployeeIncidentRequests(context, provider.getTicketId); }); } @@ -57,7 +81,7 @@ class _EtqanOvrRequestDetailedState extends State { Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, - appBar: AppBarWidget(context, title: LocaleKeys.etqanRequest.tr()), + appBar: AppBarWidget(context, title: LocaleKeys.incidentDetails.tr()), body: Consumer( builder: (BuildContext context, EtqanOvrProviderModel provider, Widget? child) { if (provider.getEtqanEmployeeIncidnetRequest == null || provider.getEtqanEmployeeIncidnetRequest!.isEmpty) { @@ -74,7 +98,7 @@ class _EtqanOvrRequestDetailedState extends State { color: Colors.white, borderRadius: BorderRadius.circular(15), border: Border.all(color: const Color(0xffefefef), width: 1), - boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2))], + boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.05), blurRadius: 10, offset: const Offset(0, 2))], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -83,6 +107,13 @@ class _EtqanOvrRequestDetailedState extends State { int index = entry.key; EtqanGetIncidentRequestResponse data = entry.value; bool isLast = index == orderedList.length - 1; + bool isAttachment = data.key?.toLowerCase() == 'attachment'; + + String displayKey = context.locale.languageCode == "ar" ? (data.keyAr ?? data.key ?? "") : (data.key ?? ""); + String displayValue = context.locale.languageCode == "ar" ? (data.valueAr ?? data.value ?? "") : (data.value ?? ""); + + bool hasSubValues = data.subValues != null && data.subValues!.isNotEmpty; + return Column( children: [ Padding( @@ -90,9 +121,63 @@ class _EtqanOvrRequestDetailedState extends State { child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded(flex: 2, child: (data.key ?? "").toText12(isBold: true, color: MyColors.grey57Color)), + Expanded(flex: 2, child: displayKey.toText12(isBold: true, color: MyColors.grey57Color)), 8.width, - Expanded(flex: 3, child: (data.value ?? "-").toText14(color: MyColors.darkTextColor)), + Expanded( + flex: 3, + child: + isAttachment && hasSubValues + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: + data.subValues!.asMap().entries.map((entry) { + int subIndex = entry.key; + String base64Data = entry.value; + return Padding( + padding: EdgeInsets.only(bottom: subIndex < data.subValues!.length - 1 ? 8.0 : 0), + child: GestureDetector( + onTap: () => _viewAttachment(context, base64Data), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: context.locale.languageCode == "ar" ? MainAxisAlignment.end : MainAxisAlignment.start, + children: [ + Text( + data.subValues!.length > 1 ? '${LocaleKeys.viewAttachment.tr()} ${subIndex + 1}' : LocaleKeys.viewAttachment.tr(), + // textAlign: context.locale.languageCode == "ar" ? TextAlign.start : TextAlign.start, + style: const TextStyle( + fontSize: 14, + color: MyColors.green9CColor, + decoration: TextDecoration.underline, + decorationColor: MyColors.green9CColor, + ), + ), + ], + ), + ), + ); + }).toList(), + ) + : hasSubValues + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (displayValue.isNotEmpty) displayValue.toText14(color: MyColors.darkTextColor), + if (displayValue.isNotEmpty) 8.height, + ...data.subValues! + .map( + (subValue) => Padding( + padding: const EdgeInsets.only(bottom: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [const Text("• ", style: TextStyle(fontSize: 14)), Expanded(child: subValue.toText14(color: MyColors.darkTextColor))], + ), + ), + ) + .toList(), + ], + ) + : (displayValue.isNotEmpty ? displayValue : "-").toText14(color: MyColors.darkTextColor), + ), ], ), ), @@ -104,7 +189,7 @@ class _EtqanOvrRequestDetailedState extends State { ), ); }, - ), + ).paddingOnly(bottom: 20), ); } } From a8f390d49f85e5422783bfb9242c7a67bfe0765c Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Sun, 1 Mar 2026 10:09:39 +0300 Subject: [PATCH 19/30] QIPS-1175,QIPS-1179,QIPS-1180 --- lib/provider/etqan_ovr_provider.dart | 22 ++++++++++++++++++++++ lib/ui/etqan_ovr/etqan_ovr_home.dart | 5 ++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/provider/etqan_ovr_provider.dart b/lib/provider/etqan_ovr_provider.dart index 1f6ab80..deba5ba 100644 --- a/lib/provider/etqan_ovr_provider.dart +++ b/lib/provider/etqan_ovr_provider.dart @@ -112,6 +112,17 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { Utils.showLoading(context); getEtqanEmployeeRequestsList?.clear(); getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); + + // Sort by createdDate in descending order (newest first) + if (getEtqanEmployeeRequestsList != null && getEtqanEmployeeRequestsList!.isNotEmpty) { + getEtqanEmployeeRequestsList!.sort((a, b) { + if (a.createdDate == null && b.createdDate == null) return 0; + if (a.createdDate == null) return 1; + if (b.createdDate == null) return -1; + return b.createdDate!.compareTo(a.createdDate!); + }); + } + Utils.hideLoading(context); isLoading = false; notifyListeners(); @@ -191,6 +202,17 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { Utils.hideLoading(context); if (response != null) { getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); + + // Sort by ticketNumber in descending order (newest first) + if (getEtqanEmployeeRequestsList != null && getEtqanEmployeeRequestsList!.isNotEmpty) { + getEtqanEmployeeRequestsList!.sort((a, b) { + if (a.ticketNumber == null && b.ticketNumber == null) return 0; + if (a.ticketNumber == null) return 1; + if (b.ticketNumber == null) return -1; + return b.ticketNumber!.compareTo(a.ticketNumber!); + }); + } + notifyListeners(); return response; } diff --git a/lib/ui/etqan_ovr/etqan_ovr_home.dart b/lib/ui/etqan_ovr/etqan_ovr_home.dart index c4174a1..978f894 100644 --- a/lib/ui/etqan_ovr/etqan_ovr_home.dart +++ b/lib/ui/etqan_ovr/etqan_ovr_home.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:easy_localization/easy_localization.dart' as lclize; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; @@ -74,7 +76,8 @@ class _EtqanOvrHomeState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - (data.description ?? "").toText12(color: MyColors.grey57Color), + (data.description ?? "").toText12(color: MyColors.grey57Color).expanded, + const SizedBox(width: 5,), RotatedBox( quarterTurns: AppState().isArabic(context) ? 2 : 0, child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.grey70Color, width: 16, height: 16), From ba8908d4eb187703b7bdfbe45fae01e01674c275 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Sun, 1 Mar 2026 12:01:46 +0300 Subject: [PATCH 20/30] Greeting Card --- lib/api/dashboard_api_client.dart | 15 + lib/models/greetings/greeting_card_model.dart | 105 ++ lib/provider/dashboard_provider_model.dart | 25 + lib/ui/landing/dashboard_screen.dart | 1280 +++++++++-------- 4 files changed, 851 insertions(+), 574 deletions(-) create mode 100644 lib/models/greetings/greeting_card_model.dart diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index 6f7288c..3265774 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -17,6 +17,7 @@ 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/generic_response_model.dart'; import 'package:mohem_flutter_app/models/get_employee_parking_details_model.dart'; +import 'package:mohem_flutter_app/models/greetings/greeting_card_model.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/sso_auth_model.dart'; @@ -429,4 +430,18 @@ class DashboardApiClient { postParams, ); } + + Future getGreetingCards() async { + String url = "${ApiConsts.erpRest}Get_GreetingCards"; + Map postParams = {}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject( + (json) { + GetGreetingCardsModelResponse responseData = GetGreetingCardsModelResponse.fromJson(json); + return responseData; + }, + url, + postParams, + ); + } } diff --git a/lib/models/greetings/greeting_card_model.dart b/lib/models/greetings/greeting_card_model.dart new file mode 100644 index 0000000..99b93ed --- /dev/null +++ b/lib/models/greetings/greeting_card_model.dart @@ -0,0 +1,105 @@ +import 'dart:convert'; + +class GetGreetingCardsModelResponse { + List? getGreetingCardsModelResponse; + + GetGreetingCardsModelResponse({ + this.getGreetingCardsModelResponse, + }); + + factory GetGreetingCardsModelResponse.fromRawJson(String str) => GetGreetingCardsModelResponse.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory GetGreetingCardsModelResponse.fromJson(Map json) => GetGreetingCardsModelResponse( + getGreetingCardsModelResponse: json["Get_GreetingCardsModel_Response"] == null ? [] : List.from(json["Get_GreetingCardsModel_Response"]!.map((x) => GetGreetingCardsModelResponseElement.fromJson(x))), + ); + + Map toJson() => { + "Get_GreetingCardsModel_Response": getGreetingCardsModelResponse == null ? [] : List.from(getGreetingCardsModelResponse!.map((x) => x.toJson())), + }; +} + +class GetGreetingCardsModelResponseElement { + int? id; + dynamic titleEn; + String? titleAr; + String? descriptionEn; + String? descriptionAr; + String? startDate; + String? endDate; + String? urlEn; + String? urlAr; + String? backgroundImageUrlEn; + String? backgroundImageUrlAr; + int? channel; + int? categoryId; + String? categoryNameAr; + String? categoryNameEn; + String? createdOn; + bool? isActive; + + GetGreetingCardsModelResponseElement({ + this.id, + this.titleEn, + this.titleAr, + this.descriptionEn, + this.descriptionAr, + this.startDate, + this.endDate, + this.urlEn, + this.urlAr, + this.backgroundImageUrlEn, + this.backgroundImageUrlAr, + this.channel, + this.categoryId, + this.categoryNameAr, + this.categoryNameEn, + this.createdOn, + this.isActive, + }); + + factory GetGreetingCardsModelResponseElement.fromRawJson(String str) => GetGreetingCardsModelResponseElement.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory GetGreetingCardsModelResponseElement.fromJson(Map json) => GetGreetingCardsModelResponseElement( + id: json["ID"], + titleEn: json["TitleEn"], + titleAr: json["TitleAr"], + descriptionEn: json["DescriptionEn"], + descriptionAr: json["DescriptionAr"], + startDate: json["StartDate"], + endDate: json["EndDate"], + urlEn: json["UrlEn"], + urlAr: json["UrlAr"], + backgroundImageUrlEn: json["BackgroundImageUrlEn"], + backgroundImageUrlAr: json["BackgroundImageUrlAr"], + channel: json["Channel"], + categoryId: json["CategoryID"], + categoryNameAr: json["CategoryNameAr"], + categoryNameEn: json["CategoryNameEn"], + createdOn: json["CreatedOn"], + isActive: json["IsActive"], + ); + + Map toJson() => { + "ID": id, + "TitleEn": titleEn, + "TitleAr": titleAr, + "DescriptionEn": descriptionEn, + "DescriptionAr": descriptionAr, + "StartDate": startDate, + "EndDate": endDate, + "UrlEn": urlEn, + "UrlAr": urlAr, + "BackgroundImageUrlEn": backgroundImageUrlEn, + "BackgroundImageUrlAr": backgroundImageUrlAr, + "Channel": channel, + "CategoryID": categoryId, + "CategoryNameAr": categoryNameAr, + "CategoryNameEn": categoryNameEn, + "CreatedOn": createdOn, + "IsActive": isActive, + }; +} diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index dd1472b..09c347e 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -21,6 +21,7 @@ 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/eit/get_eit_transaction_model.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart'; +import 'package:mohem_flutter_app/models/greetings/greeting_card_model.dart'; import 'package:mohem_flutter_app/models/itg/itg_response_model.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; import 'package:mohem_flutter_app/models/sso_auth_model.dart'; @@ -67,6 +68,9 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { bool isOffersLoading = true; List getOffersList = []; + bool isDisplayEidGreetings = false; + List? greetingCardsList; + //Attendance Tracking API's & Methods Future fetchAttendanceTracking(context) async { try { @@ -307,6 +311,27 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } } + void fetchGreetingCards() async { + try { + GetGreetingCardsModelResponse? response = await DashboardApiClient().getGreetingCards(); + greetingCardsList = response?.getGreetingCardsModelResponse ?? []; + + // Check if there are any active greeting cards + if (greetingCardsList != null && greetingCardsList!.isNotEmpty) { + isDisplayEidGreetings = greetingCardsList!.any((card) => card.isActive == true); + } else { + isDisplayEidGreetings = false; + } + + notifyListeners(); + } catch (ex) { + logger.wtf(ex); + isDisplayEidGreetings = false; + greetingCardsList = []; + notifyListeners(); + } + } + Future fetchTicketBooking() async { try { GenericResponseModel? genericResponseModel = await DashboardApiClient().getTicketBookingRedirection(); diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index 3122d17..aca7aba 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -155,6 +155,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb data.fetchLeaveTicketBalance(context, DateTime.now()); data.fetchMenuEntries(); data.fetchEventActivity(); + data.fetchGreetingCards(); data.getCategoryOffersListAPI(context); marathonProvider.getMarathonDetailsFromApi(); marathonProvider.getMarathonTutorial(); @@ -253,585 +254,716 @@ class _DashboardScreenState extends State with WidgetsBindingOb child: Scaffold( key: _scaffoldState, body: Column( - children: [ - Row( - children: [ - Builder( - builder: (BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Image.memory( - Utils.dataFromBase64String(AppState().memberInformationList!.eMPLOYEEIMAGE ?? ""), - errorBuilder: (BuildContext context, Object error, StackTrace? stackTrace) { - return SvgPicture.asset("assets/images/user.svg", height: 34, width: 34); - }, - width: 34, - height: 34, - fit: BoxFit.cover, - ).circle(50), - // CircularAvatar( - // width: 34, - // height: 34, - // url: "https://cdn4.iconfinder.com/data/icons/professions-2-2/151/89-512.png", - // ), - 8.width, - SvgPicture.asset("assets/images/side_nav.svg"), - ], - ).onPress(() { - _scaffoldState.currentState!.openDrawer(); - }); - }, - ), - Image.asset("assets/images/logos/main_mohemm_logo.png", width: 134, height: 28).expanded, - SvgPicture.asset("assets/images/announcements.svg", matchTextDirection: true).onPress(() async { - await Navigator.pushNamed(context, AppRoutes.announcements); - }), - ], - ).paddingOnly(left: 21, right: 21, top: 48, bottom: 7), - Expanded( - child: SmartRefresher( - enablePullDown: true, - enablePullUp: false, - header: const MaterialClassicHeader(color: MyColors.gradiantEndColor), - controller: _refreshController, - onRefresh: () { - _onRefresh(false); - }, - child: SingleChildScrollView( - child: Column( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.welcomeBack.tr().toText14(color: MyColors.grey77Color), - (AppState().memberInformationList!.eMPLOYEENAME ?? "").toText24(isBold: true), - 16.height, - Row( - children: [ - Expanded( - child: AspectRatio( - aspectRatio: 159 / 159, - child: Consumer( - builder: (BuildContext context, DashboardProviderModel model, Widget? child) { - return (model.isAttendanceTrackingLoading - ? GetAttendanceTrackingShimmer() - : Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), - gradient: const LinearGradient( - transform: GradientRotation(.46), - begin: Alignment.topRight, - end: Alignment.bottomLeft, - colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor], - ), - ), - child: Stack( - alignment: Alignment.center, - children: [ - if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true), - if (model.isTimeRemainingInSeconds == 0) DateTime.now().toString().split(" ")[0].toText12(color: Colors.white), - if (model.isTimeRemainingInSeconds != 0) - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 9.height, - Directionality( - textDirection: ui.TextDirection.ltr, - child: CountdownTimer( - endTime: model.endTime, - onEnd: null, - endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), - textStyle: const TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold), - ), - ), - LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), - 9.height, - ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(20)), - child: LinearProgressIndicator( - value: model.progress, - 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), - (model.attendanceTracking!.pSwipeIn == null ? "--:--" : model.attendanceTracking!.pSwipeIn).toString().toText14( - color: Colors.white, - isBold: true, - ), - 4.height, - ], - ).paddingOnly(left: 12, right: 12), - ), - Container( - margin: EdgeInsets.only(top: AppState().isArabic(context) ? 6 : 0), - width: 45, - height: 45, - padding: const EdgeInsets.only(left: 10, right: 10), - decoration: BoxDecoration( - color: const Color(0xff259EA4), - borderRadius: BorderRadius.only( - bottomRight: AppState().isArabic(context) ? const Radius.circular(0) : const Radius.circular(15), - bottomLeft: AppState().isArabic(context) ? const Radius.circular(15) : const Radius.circular(0), - ), - ), - child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/biometrics.svg" : "assets/images/biometrics.svg"), - ).onPress(() { - showMyBottomSheet(context, callBackFunc: () {}, child: MarkAttendanceWidget(model, isFromDashboard: true)); - }), - ], - ), - ], - ), - ], - ), - ).onPress(() { - Navigator.pushNamed(context, AppRoutes.todayAttendance); - })) - .animatedSwither(); - }, - ), - ), - ), - 9.width, - Expanded(child: MenusWidget()), - ], - ), - ], - ).paddingOnly(left: 21, right: 21, top: 7, bottom: 21), - eventActivityWidget(context), - - if (isDisplayMazaya) ...[ - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Directionality( - textDirection: AppState().isArabic(context) ? ui.TextDirection.rtl : ui.TextDirection.ltr, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - gradient: const LinearGradient(colors: [Color(0xFF91C481), Color(0xFF7CCED7)], begin: Alignment.centerLeft, end: Alignment.centerRight), - ), - child: Padding( - padding: const EdgeInsets.all(3.0), // This creates the border width - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(17), // Slightly less than outer radius - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 4, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - flex: 2, - child: RichText( - text: - AppState().isArabic(context) - ? TextSpan( - children: [ - TextSpan( - text: 'اطلع على مميزات', - style: TextStyle( - fontSize: 16, - letterSpacing: -0.2, - fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', - fontWeight: FontWeight.w700, - height: 24 / 16, - color: Color(0xFF5D5E5E), - ), - ), - TextSpan( - text: ' مزايا', - style: TextStyle( - fontSize: 16, - fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', - fontWeight: FontWeight.w700, - letterSpacing: -0.2, - height: 24 / 16, - color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed - ), - ), - ], - ) - : TextSpan( - children: [ - TextSpan( - text: LocaleKeys.explore.tr() + ' ', - style: const TextStyle( - fontSize: 16, - letterSpacing: -0.2, - fontFamily: 'Poppins', - fontWeight: FontWeight.w700, - height: 24 / 16, - color: Color(0xFF5D5E5E), - ), - ), - TextSpan( - text: LocaleKeys.mazaya.tr(), - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w700, - fontFamily: 'Poppins', - letterSpacing: -0.2, - height: 24 / 16, - color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed - ), - ), - TextSpan( - text: ' ' + LocaleKeys.benefits.tr(), - style: const TextStyle( - fontSize: 16, - letterSpacing: -0.2, - fontFamily: 'Poppins', - fontWeight: FontWeight.w700, - height: 24 / 16, - color: Color(0xFF5D5E5E), - ), - ), - ], - ), - ), - ), - const Expanded(flex: 1, child: SizedBox()), - ], - ), - const SizedBox(height: 8), - LocaleKeys.mazayaDesc.tr().toText11(color: const Color(0xFF5D5E5E)), - ], - ), - ), - Expanded( - flex: 2, - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - SvgPicture.asset("assets/icons/mazaya_brand.svg", width: 90, height: 47), - const SizedBox(height: 28), - LocaleKeys.viewallofferMazaya.tr().toText12(isUnderLine: true, color: const Color(0xFF3B3D4A)).onPress(() { - Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); - }), - ], - ), - ), - ], - ).paddingOnly(left: 21, right: 21, top: 14, bottom: 14), - ), - ), - ).paddingOnly(left: 21, right: 21, top: 0, bottom: 21), - ), - ], - ), - ], + children: [ + Row( + children: [ + Builder( + builder: (BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.memory( + Utils.dataFromBase64String(AppState().memberInformationList!.eMPLOYEEIMAGE ?? ""), + errorBuilder: (BuildContext context, Object error, StackTrace? stackTrace) { + return SvgPicture.asset("assets/images/user.svg", height: 34, width: 34); + }, + width: 34, + height: 34, + fit: BoxFit.cover, + ).circle(50), + // CircularAvatar( + // width: 34, + // height: 34, + // url: "https://cdn4.iconfinder.com/data/icons/professions-2-2/151/89-512.png", + // ), + 8.width, + SvgPicture.asset("assets/images/side_nav.svg"), + ], + ).onPress(() { + _scaffoldState.currentState!.openDrawer(); + }); + }, + ), + Image + .asset("assets/images/logos/main_mohemm_logo.png", width: 134, height: 28) + .expanded, + SvgPicture.asset("assets/images/announcements.svg", matchTextDirection: true).onPress(() async { + await Navigator.pushNamed(context, AppRoutes.announcements); + }), + ], + ).paddingOnly(left: 21, right: 21, top: 48, bottom: 7), + Expanded( + child: SmartRefresher( + enablePullDown: true, + enablePullUp: false, + header: const MaterialClassicHeader(color: MyColors.gradiantEndColor), + controller: _refreshController, + onRefresh: () { + _onRefresh(false); + }, + child: SingleChildScrollView( + child: Column( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.welcomeBack.tr().toText14(color: MyColors.grey77Color), + (AppState().memberInformationList!.eMPLOYEENAME ?? "").toText24(isBold: true), + 16.height, + Row( + children: [ + Expanded( + child: AspectRatio( + aspectRatio: 159 / 159, + child: Consumer( + builder: (BuildContext context, DashboardProviderModel model, Widget? child) { + return (model.isAttendanceTrackingLoading + ? GetAttendanceTrackingShimmer() + : Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + gradient: const LinearGradient( + transform: GradientRotation(.46), + begin: Alignment.topRight, + end: Alignment.bottomLeft, + colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor], + ), + ), + child: Stack( + alignment: Alignment.center, + children: [ + if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true), + if (model.isTimeRemainingInSeconds == 0) DateTime.now().toString().split(" ")[0].toText12(color: Colors.white), + if (model.isTimeRemainingInSeconds != 0) + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 9.height, + Directionality( + textDirection: ui.TextDirection.ltr, + child: CountdownTimer( + endTime: model.endTime, + onEnd: null, + endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), + textStyle: const TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold), + ), + ), + LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), + 9.height, + ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(20)), + child: LinearProgressIndicator( + value: model.progress, + 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), + (model.attendanceTracking!.pSwipeIn == null ? "--:--" : model.attendanceTracking!.pSwipeIn).toString().toText14( + color: Colors.white, + isBold: true, + ), + 4.height, + ], + ).paddingOnly(left: 12, right: 12), + ), + Container( + margin: EdgeInsets.only(top: AppState().isArabic(context) ? 6 : 0), + width: 45, + height: 45, + padding: const EdgeInsets.only(left: 10, right: 10), + decoration: BoxDecoration( + color: const Color(0xff259EA4), + borderRadius: BorderRadius.only( + bottomRight: AppState().isArabic(context) ? const Radius.circular(0) : const Radius.circular(15), + bottomLeft: AppState().isArabic(context) ? const Radius.circular(15) : const Radius.circular(0), + ), + ), + child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/biometrics.svg" : "assets/images/biometrics.svg"), + ).onPress(() { + showMyBottomSheet(context, callBackFunc: () {}, child: MarkAttendanceWidget(model, isFromDashboard: true)); + }), + ], + ), + ], + ), + ], + ), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.todayAttendance); + })) + .animatedSwither(); + }, + ), + ), + ), + 9.width, + Expanded(child: MenusWidget()), + ], + ), + ], + ).paddingOnly(left: 21, right: 21, top: 7, bottom: 21), + if (data.isDisplayEidGreetings) ...[ + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Directionality( + textDirection: AppState().isArabic(context) ? ui.TextDirection.rtl : ui.TextDirection.ltr, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + gradient: const LinearGradient(colors: [Color(0xFFE7E7E7), Color(0xFFE7E7E7)], begin: Alignment.centerLeft, end: Alignment.centerRight), + ), + child: Padding( + padding: const EdgeInsets.all(3.0), + child: Container( + height: 120, + decoration: BoxDecoration(color: const Color(0xFFFAF4E7),borderRadius: BorderRadius.circular(17), image: DecorationImage(image: NetworkImage(AppState().isArabic(context) ? data.greetingCardsList!.first.backgroundImageUrlAr ?? "" : data.greetingCardsList!.first.backgroundImageUrlEn ?? ""), fit: BoxFit.cover)), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Expanded( + // flex: 4, + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Row( + // children: [ + // Expanded( + // flex: 2, + // child: RichText( + // text: + // AppState().isArabic(context) + // ? TextSpan( + // children: [ + // TextSpan( + // text: 'اطلع على مميزات', + // style: TextStyle( + // fontSize: 16, + // letterSpacing: -0.2, + // fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', + // fontWeight: FontWeight.w700, + // height: 24 / 16, + // color: const Color(0xFF5D5E5E), + // ), + // ), + // TextSpan( + // text: ' مزايا', + // style: TextStyle( + // fontSize: 16, + // fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', + // fontWeight: FontWeight.w700, + // letterSpacing: -0.2, + // height: 24 / 16, + // color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed + // ), + // ), + // ], + // ) + // : TextSpan( + // children: [ + // TextSpan( + // text: LocaleKeys.explore.tr() + ' ', + // style: const TextStyle( + // fontSize: 16, + // letterSpacing: -0.2, + // fontFamily: 'Poppins', + // fontWeight: FontWeight.w700, + // height: 24 / 16, + // color: Color(0xFF5D5E5E), + // ), + // ), + // TextSpan( + // text: LocaleKeys.mazaya.tr(), + // style: const TextStyle( + // fontSize: 16, + // fontWeight: FontWeight.w700, + // fontFamily: 'Poppins', + // letterSpacing: -0.2, + // height: 24 / 16, + // color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed + // ), + // ), + // TextSpan( + // text: ' ' + LocaleKeys.benefits.tr(), + // style: const TextStyle( + // fontSize: 16, + // letterSpacing: -0.2, + // fontFamily: 'Poppins', + // fontWeight: FontWeight.w700, + // height: 24 / 16, + // color: Color(0xFF5D5E5E), + // ), + // ), + // ], + // ), + // ), + // ), + // const Expanded(flex: 1, child: SizedBox()), + // ], + // ), + // const SizedBox(height: 8), + // LocaleKeys.mazayaDesc.tr().toText11(color: const Color(0xFF5D5E5E)), + // ], + // ), + // ), + // Expanded( + // flex: 2, + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.end, + // mainAxisAlignment: MainAxisAlignment.end, + // children: [ + // SvgPicture.asset("assets/icons/mazaya_brand.svg", width: 90, height: 47), + // const SizedBox(height: 28), + // LocaleKeys.viewallofferMazaya.tr().toText12(isUnderLine: true, color: const Color(0xFF3B3D4A)).onPress(() { + // Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); + // }), + // ], + // ), + // ), + ], + ).paddingOnly(left: 21, right: 21, top: 14, bottom: 14), + ), + ), + ).paddingOnly(left: 21, right: 21, top: 0, bottom: 21), + ), + ], + ), + ], + eventActivityWidget(context), + if (isDisplayMazaya) ...[ + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Directionality( + textDirection: AppState().isArabic(context) ? ui.TextDirection.rtl : ui.TextDirection.ltr, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + gradient: const LinearGradient(colors: [Color(0xFF91C481), Color(0xFF7CCED7)], begin: Alignment.centerLeft, end: Alignment.centerRight), + ), + child: Padding( + padding: const EdgeInsets.all(3.0), // This creates the border width + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(17), // Slightly less than outer radius + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + flex: 2, + child: RichText( + text: + AppState().isArabic(context) + ? TextSpan( + children: [ + TextSpan( + text: 'اطلع على مميزات', + style: TextStyle( + fontSize: 16, + letterSpacing: -0.2, + fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', + fontWeight: FontWeight.w700, + height: 24 / 16, + color: const Color(0xFF5D5E5E), + ), + ), + TextSpan( + text: ' مزايا', + style: TextStyle( + fontSize: 16, + fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', + fontWeight: FontWeight.w700, + letterSpacing: -0.2, + height: 24 / 16, + color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed + ), + ), + ], + ) + : TextSpan( + children: [ + TextSpan( + text: LocaleKeys.explore.tr() + ' ', + style: const TextStyle( + fontSize: 16, + letterSpacing: -0.2, + fontFamily: 'Poppins', + fontWeight: FontWeight.w700, + height: 24 / 16, + color: Color(0xFF5D5E5E), + ), + ), + TextSpan( + text: LocaleKeys.mazaya.tr(), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + fontFamily: 'Poppins', + letterSpacing: -0.2, + height: 24 / 16, + color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed + ), + ), + TextSpan( + text: ' ' + LocaleKeys.benefits.tr(), + style: const TextStyle( + fontSize: 16, + letterSpacing: -0.2, + fontFamily: 'Poppins', + fontWeight: FontWeight.w700, + height: 24 / 16, + color: Color(0xFF5D5E5E), + ), + ), + ], + ), + ), + ), + const Expanded(flex: 1, child: SizedBox()), + ], + ), + const SizedBox(height: 8), + LocaleKeys.mazayaDesc.tr().toText11(color: const Color(0xFF5D5E5E)), + ], + ), + ), + Expanded( + flex: 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SvgPicture.asset("assets/icons/mazaya_brand.svg", width: 90, height: 47), + const SizedBox(height: 28), + LocaleKeys.viewallofferMazaya.tr().toText12(isUnderLine: true, color: const Color(0xFF3B3D4A)).onPress(() { + Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); + }), + ], + ), + ), + ], + ).paddingOnly(left: 21, right: 21, top: 14, bottom: 14), + ), + ), + ).paddingOnly(left: 21, right: 21, top: 0, bottom: 21), + ), + ], + ), + ], - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - LocaleKeys.offers.tr().toText12(), - Row( - children: [ - LocaleKeys.discounts.tr().toText24(isBold: true), - 6.width, - Container( - padding: const EdgeInsets.only(left: 8, right: 8), - decoration: BoxDecoration( - color: MyColors.yellowColor, - borderRadius: BorderRadius.circular(10), - ), - child: LocaleKeys.newString.tr().toText10(isBold: true)), - ], - ), - ], - ), - ), - LocaleKeys.viewAllOffers.tr().toText12(isUnderLine: true).onPress(() { - Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); - }) - ], - ).paddingOnly(left: 21, right: 21), - Consumer( - builder: (BuildContext context, DashboardProviderModel model, Widget? child) { - return SizedBox( - height: 103 + 33, - child: ListView.separated( - shrinkWrap: true, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.only(left: 21, right: 21, top: 13), - scrollDirection: Axis.horizontal, - itemBuilder: (BuildContext cxt, int index) { - return model.isOffersLoading - ? const OffersShimmerWidget() - : InkWell( - onTap: () { - navigateToDetails(data.getOffersList[index]); - }, - child: SizedBox( - width: 73, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - width: 73, - height: 73, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all( - Radius.circular(100), - ), - border: Border.all(color: MyColors.lightGreyE3Color, width: 1), - ), - child: ClipRRect( - borderRadius: const BorderRadius.all( - Radius.circular(50), - ), - child: Hero( - tag: "ItemImage" + data.getOffersList[index].offersDiscountId.toString()!, - transitionOnUserGestures: true, - child: Image.network( - data.getOffersList[index].logo ?? "", - fit: BoxFit.contain, - ), - ), - ), - ), - 4.height, - Expanded( - child: AppState().isArabic(context) - ? data.getOffersList[index].titleAr!.toText12(isCenter: true, maxLine: 1) - : data.getOffersList[index].titleEn!.toText12(isCenter: true, maxLine: 1), - ), - ], - ), - ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 8.width, - itemCount: 9), - ); - }, - ), - ], - ), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + LocaleKeys.offers.tr().toText12(), + Row( + children: [ + LocaleKeys.discounts.tr().toText24(isBold: true), + 6.width, + Container( + padding: const EdgeInsets.only(left: 8, right: 8), + decoration: BoxDecoration(color: MyColors.yellowColor, borderRadius: BorderRadius.circular(10)), + child: LocaleKeys.newString.tr().toText10(isBold: true), + ), + ], + ), + ], + ), + ), + LocaleKeys.viewAllOffers.tr().toText12(isUnderLine: true).onPress(() { + Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); + }), + ], + ).paddingOnly(left: 21, right: 21), + Consumer( + builder: (BuildContext context, DashboardProviderModel model, Widget? child) { + return SizedBox( + height: 103 + 33, + child: ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.only(left: 21, right: 21, top: 13), + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext cxt, int index) { + return model.isOffersLoading + ? const OffersShimmerWidget() + : InkWell( + onTap: () { + navigateToDetails(data.getOffersList[index]); + }, + child: SizedBox( + width: 73, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 73, + height: 73, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(100)), + border: Border.all(color: MyColors.lightGreyE3Color, width: 1), + ), + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(50)), + child: Hero( + tag: "ItemImage" + data.getOffersList[index].offersDiscountId.toString()!, + transitionOnUserGestures: true, + child: Image.network(data.getOffersList[index].logo ?? "", fit: BoxFit.contain), + ), + ), + ), + 4.height, + Expanded( + child: + AppState().isArabic(context) + ? data.getOffersList[index].titleAr!.toText12(isCenter: true, maxLine: 1) + : data.getOffersList[index].titleEn!.toText12(isCenter: true, maxLine: 1), + ), + ], + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => 8.width, + itemCount: 9, + ), + ); + }, + ), + ], + ), - Container( - width: double.infinity, - padding: const EdgeInsets.only(top: 31), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.only(topRight: Radius.circular(50), topLeft: Radius.circular(50)), - border: Border.all(color: MyColors.lightGreyEDColor, width: 1), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ServicesWidget(), - context.watch().isLoading ? const MarathonBannerShimmer().paddingAll(20) : const MarathonBanner().paddingOnly(left: 21, right: 21, bottom: 8, top: 8), - // context.watch().isTutorialLoading - // ? const MarathonBannerShimmer().paddingAll(20) - // : Container( - // padding: EdgeInsets.only(bottom: 12, top: 12), - // margin: EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 8), - // width: double.infinity, - // alignment: Alignment.center, - // decoration: BoxDecoration( - // color: MyColors.backgroundBlackColor, - // borderRadius: BorderRadius.circular(20), - // border: Border.all(color: MyColors.lightGreyEDColor, width: 1), - // ), - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisSize: MainAxisSize.min, - // children: [ - // Text( - // "Tutorial:", - // style: TextStyle( - // fontSize: 11, - // fontStyle: FontStyle.italic, - // fontWeight: FontWeight.w600, - // color: MyColors.white.withOpacity(0.83), - // letterSpacing: -0.4, - // ), - // ), - // Text( - // context.read().tutorial?.tutorialName ?? "", - // overflow: TextOverflow.ellipsis, - // style: TextStyle( - // fontStyle: FontStyle.italic, - // fontSize: 19, - // fontWeight: FontWeight.bold, - // color: MyColors.white, - // height: 32 / 22, - // ), - // ), - // ], - // ), - // ).onPress(() { - // checkERMChannel(); - // // Navigator.pushNamed(context, AppRoutes.marathonTutorialScreen); - // }), - ], - ), - ), - ], - ), - ), - ), - ), - ], - ), - drawer: AppDrawer(onLanguageChange: _onRefresh), - bottomNavigationBar: SizedBox( - height: Platform.isAndroid ? 70 : 100, - child: BottomNavigationBar( - items: [ - BottomNavigationBarItem( - icon: SvgPicture.asset("assets/icons/home.svg", color: currentIndex == 0 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), - label: LocaleKeys.home.tr(), - ), - BottomNavigationBarItem( - icon: SvgPicture.asset("assets/icons/create_req.svg", color: currentIndex == 1 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), - label: LocaleKeys.mowadhafhiRequest.tr(), - ), - BottomNavigationBarItem( - icon: Stack( - alignment: Alignment.centerLeft, - children: [ - SvgPicture.asset("assets/icons/work_list.svg", color: currentIndex == 2 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), - Consumer( - builder: (BuildContext cxt, DashboardProviderModel data, Widget? child) { - if (data.workListCounter == 0) { - return const SizedBox(); - } - return Positioned( - right: 0, - top: 0, - child: Container( - padding: const EdgeInsets.only(left: 4, right: 4), - alignment: Alignment.center, - decoration: BoxDecoration(color: MyColors.redColor, borderRadius: BorderRadius.circular(17)), - child: data.workListCounter.toString().toText10(color: Colors.white), - ), - ); - }, - ), - ], - ), - label: LocaleKeys.workList.tr(), - ), - BottomNavigationBarItem( - icon: SvgPicture.asset("assets/icons/item_for_sale.svg", color: currentIndex == 3 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), - label: LocaleKeys.itemsForSale.tr(), - ), - BottomNavigationBarItem( - icon: Stack( - alignment: Alignment.centerLeft, - children: [ - SvgPicture.asset( - "assets/icons/chat/chat.svg", - color: - !checkIfPrivilegedForChat() - ? MyColors.lightGreyE3Color - : currentIndex == 4 - ? MyColors.grey3AColor - : cProvider.disbaleChatForThisUser - ? MyColors.lightGreyE3Color - : MyColors.grey98Color, - ).paddingAll(4), - Consumer( - builder: (BuildContext cxt, ChatProviderModel data, Widget? child) { - return !checkIfPrivilegedForChat() - ? const SizedBox() - : Positioned( - right: 0, - top: 0, - child: Container( - padding: const EdgeInsets.only(left: 4, right: 4), - alignment: Alignment.center, - decoration: BoxDecoration(color: cProvider.disbaleChatForThisUser ? MyColors.pinkDarkColor : MyColors.redColor, borderRadius: BorderRadius.circular(17)), - child: data.chatUConvCounter.toString().toText10(color: Colors.white), - ), - ); - }, - ), - ], - ), - label: LocaleKeys.chat.tr(), - ), - ], - currentIndex: currentIndex, - selectedLabelStyle: const TextStyle(fontSize: 10, color: MyColors.grey3AColor, fontWeight: FontWeight.w600), - unselectedLabelStyle: const TextStyle(fontSize: 10, color: MyColors.grey98Color, fontWeight: FontWeight.w600), - type: BottomNavigationBarType.fixed, - selectedItemColor: MyColors.grey3AColor, - backgroundColor: MyColors.backgroundColor, - selectedIconTheme: const IconThemeData(color: MyColors.grey3AColor, size: 28), - unselectedIconTheme: const IconThemeData(color: MyColors.grey98Color, size: 28), - onTap: (int index) { - if (index == 1) { - Navigator.pushNamed(context, AppRoutes.mowadhafhi); - } else if (index == 2) { - Navigator.pushNamed(context, AppRoutes.workList); - } else if (index == 3) { - Navigator.pushNamed(context, AppRoutes.itemsForSale); - } else if (index == 4) { - if (!cProvider.disbaleChatForThisUser && checkIfPrivilegedForChat()) { - Navigator.pushNamed(context, AppRoutes.chat); - } - } - }, - ), - ), - ), + Container( + width: double.infinity, + padding: const EdgeInsets.only(top: 31), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.only(topRight: Radius.circular(50), topLeft: Radius.circular(50)), + border: Border.all(color: MyColors.lightGreyEDColor, width: 1), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ServicesWidget(), + context.watch().isLoading ? const MarathonBannerShimmer().paddingAll(20) : const MarathonBanner().paddingOnly(left: 21, right: 21, bottom: 8, top: 8), + // context.watch().isTutorialLoading + // ? const MarathonBannerShimmer().paddingAll(20) + // : Container( + // padding: EdgeInsets.only(bottom: 12, top: 12), + // margin: EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 8), + // width: double.infinity, + // alignment: Alignment.center, + // decoration: BoxDecoration( + // color: MyColors.backgroundBlackColor, + // borderRadius: BorderRadius.circular(20), + // border: Border.all(color: MyColors.lightGreyEDColor, width: 1), + // ), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text( + // "Tutorial:", + // style: TextStyle( + // fontSize: 11, + // fontStyle: FontStyle.italic, + // fontWeight: FontWeight.w600, + // color: MyColors.white.withOpacity(0.83), + // letterSpacing: -0.4, + // ), + // ), + // Text( + // context.read().tutorial?.tutorialName ?? "", + // overflow: TextOverflow.ellipsis, + // style: TextStyle( + // fontStyle: FontStyle.italic, + // fontSize: 19, + // fontWeight: FontWeight.bold, + // color: MyColors.white, + // height: 32 / 22, + // ), + // ), + // ], + // ), + // ).onPress(() { + // checkERMChannel(); + // // Navigator.pushNamed(context, AppRoutes.marathonTutorialScreen); + // }), + ], + ), + ), + ], + ), + ), + ), + ), + ], + ), + drawer: AppDrawer(onLanguageChange: _onRefresh), + bottomNavigationBar: SizedBox( + height: Platform.isAndroid ? 70 : 100, + child: BottomNavigationBar( + items: [ + BottomNavigationBarItem( + icon: SvgPicture.asset("assets/icons/home.svg", color: currentIndex == 0 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), + label: LocaleKeys.home.tr(), + ), + BottomNavigationBarItem( + icon: SvgPicture.asset("assets/icons/create_req.svg", color: currentIndex == 1 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), + label: LocaleKeys.mowadhafhiRequest.tr(), + ), + BottomNavigationBarItem( + icon: Stack( + alignment: Alignment.centerLeft, + children: [ + SvgPicture.asset("assets/icons/work_list.svg", color: currentIndex == 2 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), + Consumer( + builder: (BuildContext cxt, DashboardProviderModel data, Widget? child) { + if (data.workListCounter == 0) { + return const SizedBox(); + } + return Positioned( + right: 0, + top: 0, + child: Container( + padding: const EdgeInsets.only(left: 4, right: 4), + alignment: Alignment.center, + decoration: BoxDecoration(color: MyColors.redColor, borderRadius: BorderRadius.circular(17)), + child: data.workListCounter.toString().toText10(color: Colors.white), + ), ); - } + }, + ), + ], + ), + label: LocaleKeys.workList.tr(), + ), + BottomNavigationBarItem( + icon: SvgPicture.asset("assets/icons/item_for_sale.svg", color: currentIndex == 3 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), + label: LocaleKeys.itemsForSale.tr(), + ), + BottomNavigationBarItem( + icon: Stack( + alignment: Alignment.centerLeft, + children: [ + SvgPicture.asset( + "assets/icons/chat/chat.svg", + color: + !checkIfPrivilegedForChat() + ? MyColors.lightGreyE3Color + : currentIndex == 4 + ? MyColors.grey3AColor + : cProvider.disbaleChatForThisUser + ? MyColors.lightGreyE3Color + : MyColors.grey98Color, + ).paddingAll(4), + Consumer( + builder: (BuildContext cxt, ChatProviderModel data, Widget? child) { + return !checkIfPrivilegedForChat() + ? const SizedBox() + : Positioned( + right: 0, + top: 0, + child: Container( + padding: const EdgeInsets.only(left: 4, right: 4), + alignment: Alignment.center, + decoration: BoxDecoration(color: cProvider.disbaleChatForThisUser ? MyColors.pinkDarkColor : MyColors.redColor, borderRadius: BorderRadius.circular(17)), + child: data.chatUConvCounter.toString().toText10(color: Colors.white), + ), + ); + }, + ), + ], + ), + label: LocaleKeys.chat.tr(), + ), + ], + currentIndex: currentIndex, + selectedLabelStyle: const TextStyle(fontSize: 10, color: MyColors.grey3AColor, fontWeight: FontWeight.w600), + unselectedLabelStyle: const TextStyle(fontSize: 10, color: MyColors.grey98Color, fontWeight: FontWeight.w600), + type: BottomNavigationBarType.fixed, + selectedItemColor: MyColors.grey3AColor, + backgroundColor: MyColors.backgroundColor, + selectedIconTheme: const IconThemeData(color: MyColors.grey3AColor, size: 28), + unselectedIconTheme: const IconThemeData(color: MyColors.grey98Color, size: 28), + onTap: (int index) { + if (index == 1) { + Navigator.pushNamed(context, AppRoutes.mowadhafhi); + } else if (index == 2) { + Navigator.pushNamed(context, AppRoutes.workList); + } else if (index == 3) { + Navigator.pushNamed(context, AppRoutes.itemsForSale); + } else if (index == 4) { + if (!cProvider.disbaleChatForThisUser && checkIfPrivilegedForChat()) { + Navigator.pushNamed(context, AppRoutes.chat); + } + } + }, + ), + ), + ), + ); + } Widget eventActivityWidget(BuildContext context) { - return (context.watch().isEventLoadingLoading) + return (context + .watch() + .isEventLoadingLoading) ? const MarathonBannerShimmer().paddingOnly(left: 21, right: 21, bottom: 21, top: 0) - : (context.watch().eventActivity != null && context.watch().eventActivity!.isActive == true) + : (context + .watch() + .eventActivity != null && context + .watch() + .eventActivity! + .isActive == true) ? const EventActivityBanner().paddingOnly(left: 21, right: 21, bottom: 21, top: 0) : const SizedBox(); } @@ -864,4 +996,4 @@ class _DashboardScreenState extends State with WidgetsBindingOb } return false; } -} \ No newline at end of file +} From 9f663df3cce393a4941613b664201f0b81aa8849 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Sun, 1 Mar 2026 13:48:29 +0300 Subject: [PATCH 21/30] QIPS-1205 --- lib/api/etqan_ovr_api_client.dart | 20 ++++++++++++++++---- lib/provider/etqan_ovr_provider.dart | 21 --------------------- lib/ui/etqan_ovr/etqan_ovr_home.dart | 2 +- lib/widgets/image_picker.dart | 4 ++-- 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/lib/api/etqan_ovr_api_client.dart b/lib/api/etqan_ovr_api_client.dart index 1018177..4435ec7 100644 --- a/lib/api/etqan_ovr_api_client.dart +++ b/lib/api/etqan_ovr_api_client.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; +import 'package:intl/intl.dart'; import 'package:mohem_flutter_app/api/api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/consts.dart'; @@ -61,10 +62,21 @@ class EtqanApiClient { } if (responseData.isNotEmpty) { - for (EtqanGetEmployeeOvrRequestsResponse action in responseData) { - action.createdDate = action.createdDate?.split(" ")[0]; - } - responseData.sort((EtqanGetEmployeeOvrRequestsResponse a, EtqanGetEmployeeOvrRequestsResponse b) => (b.createdDate ?? '').compareTo(a.createdDate ?? '')); + responseData.sort((EtqanGetEmployeeOvrRequestsResponse a, EtqanGetEmployeeOvrRequestsResponse b) { + if (a.createdDate == null && b.createdDate == null) return 0; + if (a.createdDate == null) return 1; + if (b.createdDate == null) return -1; + + try { + // Parse dates in format "15-Feb-2026 05:24 PM" + DateFormat dateFormat = DateFormat("dd-MMM-yyyy hh:mm a"); + DateTime dateA = dateFormat.parse(a.createdDate!); + DateTime dateB = dateFormat.parse(b.createdDate!); + return dateB.compareTo(dateA); + } catch (e) { + return (b.createdDate ?? '').compareTo(a.createdDate ?? ''); + } + }); } return responseData; diff --git a/lib/provider/etqan_ovr_provider.dart b/lib/provider/etqan_ovr_provider.dart index deba5ba..6ac8cf7 100644 --- a/lib/provider/etqan_ovr_provider.dart +++ b/lib/provider/etqan_ovr_provider.dart @@ -112,17 +112,6 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { Utils.showLoading(context); getEtqanEmployeeRequestsList?.clear(); getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); - - // Sort by createdDate in descending order (newest first) - if (getEtqanEmployeeRequestsList != null && getEtqanEmployeeRequestsList!.isNotEmpty) { - getEtqanEmployeeRequestsList!.sort((a, b) { - if (a.createdDate == null && b.createdDate == null) return 0; - if (a.createdDate == null) return 1; - if (b.createdDate == null) return -1; - return b.createdDate!.compareTo(a.createdDate!); - }); - } - Utils.hideLoading(context); isLoading = false; notifyListeners(); @@ -203,16 +192,6 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { if (response != null) { getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); - // Sort by ticketNumber in descending order (newest first) - if (getEtqanEmployeeRequestsList != null && getEtqanEmployeeRequestsList!.isNotEmpty) { - getEtqanEmployeeRequestsList!.sort((a, b) { - if (a.ticketNumber == null && b.ticketNumber == null) return 0; - if (a.ticketNumber == null) return 1; - if (b.ticketNumber == null) return -1; - return b.ticketNumber!.compareTo(a.ticketNumber!); - }); - } - notifyListeners(); return response; } diff --git a/lib/ui/etqan_ovr/etqan_ovr_home.dart b/lib/ui/etqan_ovr/etqan_ovr_home.dart index 978f894..10c1306 100644 --- a/lib/ui/etqan_ovr/etqan_ovr_home.dart +++ b/lib/ui/etqan_ovr/etqan_ovr_home.dart @@ -69,7 +69,7 @@ class _EtqanOvrHomeState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ (data.ticketNumber ?? "").toText14(isBold: true, color: MyColors.darkTextColor), - Directionality(textDirection: TextDirection.ltr, child: (data.createdDate ?? "").toText12(color: MyColors.grey70Color)), + Directionality(textDirection: TextDirection.ltr, child: (data.createdDate!.split(" ").first ?? "").toText12(color: MyColors.grey70Color)), ], ), 8.height, diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart index a5d346f..984d6ad 100644 --- a/lib/widgets/image_picker.dart +++ b/lib/widgets/image_picker.dart @@ -141,7 +141,7 @@ void galleryImageAndroid(Function(String, File) image) async { } void cameraImageAndroid(Function(String, File) image) async { - File _image = File(( await picker.pickMedia())?.path ?? ""); + File _image = File((await picker.pickImage(source: ImageSource.camera, imageQuality: 20))?.path ?? ""); String fileName = _image.path; var bytes = File(fileName).readAsBytesSync(); String base64Encode = base64.encode(bytes); @@ -159,7 +159,7 @@ class _BottomSheet extends StatelessWidget { Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(vertical: 12.0), - decoration: BoxDecoration( borderRadius: const BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0))), + decoration: const BoxDecoration( borderRadius: BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0))), child: SafeArea( top: false, child: Column( From 71d03ba66467e4304cd4a141df18dfde79ffa74e Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Sun, 1 Mar 2026 15:09:32 +0300 Subject: [PATCH 22/30] QIPS-1205 --- lib/api/etqan_ovr_api_client.dart | 3 +-- lib/provider/etqan_ovr_provider.dart | 19 +++++++++++++++++-- lib/ui/etqan_ovr/etqan_create_request.dart | 2 +- lib/ui/etqan_ovr/etqan_ovr_home.dart | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/api/etqan_ovr_api_client.dart b/lib/api/etqan_ovr_api_client.dart index 4435ec7..bc68160 100644 --- a/lib/api/etqan_ovr_api_client.dart +++ b/lib/api/etqan_ovr_api_client.dart @@ -72,13 +72,12 @@ class EtqanApiClient { DateFormat dateFormat = DateFormat("dd-MMM-yyyy hh:mm a"); DateTime dateA = dateFormat.parse(a.createdDate!); DateTime dateB = dateFormat.parse(b.createdDate!); - return dateB.compareTo(dateA); + return dateA.compareTo(dateB); } catch (e) { return (b.createdDate ?? '').compareTo(a.createdDate ?? ''); } }); } - return responseData; }, url, diff --git a/lib/provider/etqan_ovr_provider.dart b/lib/provider/etqan_ovr_provider.dart index 6ac8cf7..a8128fe 100644 --- a/lib/provider/etqan_ovr_provider.dart +++ b/lib/provider/etqan_ovr_provider.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/etqan_ovr_api_client.dart'; @@ -71,6 +72,7 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { localeId: localeId, listenFor: const Duration(seconds: 30), pauseFor: const Duration(seconds: 3), + partialResults: true, listenMode: ListenMode.confirmation, ); _isListening = true; @@ -191,8 +193,21 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { Utils.hideLoading(context); if (response != null) { getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); - - notifyListeners(); + if (getEtqanEmployeeRequestsList != null) { + getEtqanEmployeeRequestsList!.sort((EtqanGetEmployeeOvrRequestsResponse a, EtqanGetEmployeeOvrRequestsResponse b) { + if (a.createdDate == null && b.createdDate == null) return 0; + if (a.createdDate == null) return 1; + if (b.createdDate == null) return -1; + try { + DateFormat dateFormat = DateFormat("dd-MMM-yyyy hh:mm a"); + DateTime dateA = dateFormat.parse(a.createdDate!); + DateTime dateB = dateFormat.parse(b.createdDate!); + return dateA.compareTo(dateB); + } catch (e) { + return (b.createdDate ?? '').compareTo(a.createdDate ?? ''); + } + }); + } return response; } return null; diff --git a/lib/ui/etqan_ovr/etqan_create_request.dart b/lib/ui/etqan_ovr/etqan_create_request.dart index ce636ff..8ade990 100644 --- a/lib/ui/etqan_ovr/etqan_create_request.dart +++ b/lib/ui/etqan_ovr/etqan_create_request.dart @@ -303,7 +303,7 @@ class _EtqanOvrCreateRequestState extends State { children: [ Container(width: 8, height: 8, decoration: BoxDecoration(color: Colors.red, borderRadius: BorderRadius.circular(4))), const SizedBox(width: 8), - Text('Listening...', style: TextStyle(fontSize: 12, color: MyColors.gradiantEndColor, fontWeight: FontWeight.w500)), + const Text('Listening...', style: TextStyle(fontSize: 12, color: MyColors.gradiantEndColor, fontWeight: FontWeight.w500)), ], ), ), diff --git a/lib/ui/etqan_ovr/etqan_ovr_home.dart b/lib/ui/etqan_ovr/etqan_ovr_home.dart index 10c1306..e2493e6 100644 --- a/lib/ui/etqan_ovr/etqan_ovr_home.dart +++ b/lib/ui/etqan_ovr/etqan_ovr_home.dart @@ -69,7 +69,7 @@ class _EtqanOvrHomeState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ (data.ticketNumber ?? "").toText14(isBold: true, color: MyColors.darkTextColor), - Directionality(textDirection: TextDirection.ltr, child: (data.createdDate!.split(" ").first ?? "").toText12(color: MyColors.grey70Color)), + Directionality(textDirection: TextDirection.ltr, child: (data.createdDate! ?? "").toText12(color: MyColors.grey70Color)), ], ), 8.height, From facb3bf4e60abc839f0bbad814e0f127deb01336 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Sun, 1 Mar 2026 15:20:54 +0300 Subject: [PATCH 23/30] QIPS-1205 --- lib/api/etqan_ovr_api_client.dart | 6 +- lib/provider/etqan_ovr_provider.dart | 68 +++++++++++++--------- lib/ui/etqan_ovr/etqan_create_request.dart | 2 + lib/ui/etqan_ovr/etqan_ovr_home.dart | 3 +- 4 files changed, 47 insertions(+), 32 deletions(-) diff --git a/lib/api/etqan_ovr_api_client.dart b/lib/api/etqan_ovr_api_client.dart index bc68160..f7512cc 100644 --- a/lib/api/etqan_ovr_api_client.dart +++ b/lib/api/etqan_ovr_api_client.dart @@ -68,11 +68,11 @@ class EtqanApiClient { if (b.createdDate == null) return -1; try { - // Parse dates in format "15-Feb-2026 05:24 PM" - DateFormat dateFormat = DateFormat("dd-MMM-yyyy hh:mm a"); + // Parse dates in format "26-Feb-2026 12:54 PM" or "01-Mar-2026 02:45 PM" + DateFormat dateFormat = DateFormat("dd-MMM-yyyy hh:mm a", "en_US"); DateTime dateA = dateFormat.parse(a.createdDate!); DateTime dateB = dateFormat.parse(b.createdDate!); - return dateA.compareTo(dateB); + return dateB.compareTo(dateA); // Descending order (newest first) } catch (e) { return (b.createdDate ?? '').compareTo(a.createdDate ?? ''); } diff --git a/lib/provider/etqan_ovr_provider.dart b/lib/provider/etqan_ovr_provider.dart index a8128fe..4f2fe0f 100644 --- a/lib/provider/etqan_ovr_provider.dart +++ b/lib/provider/etqan_ovr_provider.dart @@ -1,6 +1,5 @@ import 'dart:io'; -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:mohem_flutter_app/api/etqan_ovr_api_client.dart'; @@ -23,6 +22,8 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { final SpeechToText _speechToText = SpeechToText(); bool _speechEnabled = false; bool _isListening = false; + String _currentLocaleId = ''; + TextEditingController? _currentController; // Getters for speech state bool get speechEnabled => _speechEnabled; @@ -50,18 +51,25 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { Future initSpeech() async { _speechEnabled = await _speechToText.initialize( onStatus: (String status) { - // Update listening state when speech recognition status changes - if (status == 'done' || status == 'notListening') { - _isListening = false; - notifyListeners(); + print('Speech status: $status, isListening: $_isListening'); + // If user wants to keep listening but speech stopped, restart it + if ((status == 'done' || status == 'notListening') && _isListening && _currentController != null) { + // Automatically restart listening after a brief pause + Future.delayed(const Duration(milliseconds: 200), () { + if (_isListening && _currentController != null) { + _startListeningInternal(_currentLocaleId, _currentController!); + } + }); } }, ); notifyListeners(); } - /// Start listening for speech input - Future startListening(String localeId, TextEditingController controller) async { + /// Internal method to start listening + Future _startListeningInternal(String localeId, TextEditingController controller) async { + if (!_speechEnabled) return; + await _speechToText.listen( onResult: (SpeechRecognitionResult result) { description = result.recognizedWords; @@ -70,19 +78,31 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { notifyListeners(); }, localeId: localeId, - listenFor: const Duration(seconds: 30), - pauseFor: const Duration(seconds: 3), - partialResults: true, - listenMode: ListenMode.confirmation, + listenFor: const Duration(seconds: 60), + pauseFor: const Duration(seconds: 10), + listenOptions: SpeechListenOptions( + partialResults: true, + listenMode: ListenMode.confirmation, + cancelOnError: false, + ), ); + } + + /// Start listening for speech input + Future startListening(String localeId, TextEditingController controller) async { _isListening = true; + _currentLocaleId = localeId; + _currentController = controller; notifyListeners(); + await _startListeningInternal(localeId, controller); } /// Stop listening for speech input Future stopListening() async { - await _speechToText.stop(); _isListening = false; + _currentLocaleId = ''; + _currentController = null; + await _speechToText.stop(); notifyListeners(); } @@ -193,21 +213,7 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { Utils.hideLoading(context); if (response != null) { getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); - if (getEtqanEmployeeRequestsList != null) { - getEtqanEmployeeRequestsList!.sort((EtqanGetEmployeeOvrRequestsResponse a, EtqanGetEmployeeOvrRequestsResponse b) { - if (a.createdDate == null && b.createdDate == null) return 0; - if (a.createdDate == null) return 1; - if (b.createdDate == null) return -1; - try { - DateFormat dateFormat = DateFormat("dd-MMM-yyyy hh:mm a"); - DateTime dateA = dateFormat.parse(a.createdDate!); - DateTime dateB = dateFormat.parse(b.createdDate!); - return dateA.compareTo(dateB); - } catch (e) { - return (b.createdDate ?? '').compareTo(a.createdDate ?? ''); - } - }); - } + notifyListeners(); return response; } return null; @@ -217,4 +223,12 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { return null; } } + + @override + void dispose() { + if (_isListening) { + _speechToText.stop(); + } + super.dispose(); + } } diff --git a/lib/ui/etqan_ovr/etqan_create_request.dart b/lib/ui/etqan_ovr/etqan_create_request.dart index 8ade990..a1d64af 100644 --- a/lib/ui/etqan_ovr/etqan_create_request.dart +++ b/lib/ui/etqan_ovr/etqan_create_request.dart @@ -39,6 +39,8 @@ class _EtqanOvrCreateRequestState extends State { @override void dispose() { + // Stop listening if active + context.read().stopListening(); _descriptionController.dispose(); super.dispose(); } diff --git a/lib/ui/etqan_ovr/etqan_ovr_home.dart b/lib/ui/etqan_ovr/etqan_ovr_home.dart index e2493e6..80a27c6 100644 --- a/lib/ui/etqan_ovr/etqan_ovr_home.dart +++ b/lib/ui/etqan_ovr/etqan_ovr_home.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'package:easy_localization/easy_localization.dart' as lclize; import 'package:flutter/material.dart'; @@ -69,7 +68,7 @@ class _EtqanOvrHomeState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ (data.ticketNumber ?? "").toText14(isBold: true, color: MyColors.darkTextColor), - Directionality(textDirection: TextDirection.ltr, child: (data.createdDate! ?? "").toText12(color: MyColors.grey70Color)), + Directionality(textDirection: TextDirection.ltr, child: (data.createdDate!.split(" ").first ?? "").toText12(color: MyColors.grey70Color)), ], ), 8.height, From dae746587f5f539b130121db73d5e90fb9868a25 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Sun, 1 Mar 2026 15:26:09 +0300 Subject: [PATCH 24/30] QIPS-1205 --- lib/provider/etqan_ovr_provider.dart | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/lib/provider/etqan_ovr_provider.dart b/lib/provider/etqan_ovr_provider.dart index 4f2fe0f..a7cb842 100644 --- a/lib/provider/etqan_ovr_provider.dart +++ b/lib/provider/etqan_ovr_provider.dart @@ -24,6 +24,8 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { bool _isListening = false; String _currentLocaleId = ''; TextEditingController? _currentController; + String _baseText = ''; // Text that was there when we started listening + String _currentSessionText = ''; // Text captured in current listening session // Getters for speech state bool get speechEnabled => _speechEnabled; @@ -54,8 +56,14 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { print('Speech status: $status, isListening: $_isListening'); // If user wants to keep listening but speech stopped, restart it if ((status == 'done' || status == 'notListening') && _isListening && _currentController != null) { + // Save the finalized text before restarting + if (_currentSessionText.isNotEmpty) { + _baseText = _currentController!.text.trim(); + _currentSessionText = ''; + } + // Automatically restart listening after a brief pause - Future.delayed(const Duration(milliseconds: 200), () { + Future.delayed(const Duration(milliseconds: 300), () { if (_isListening && _currentController != null) { _startListeningInternal(_currentLocaleId, _currentController!); } @@ -72,8 +80,15 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { await _speechToText.listen( onResult: (SpeechRecognitionResult result) { - description = result.recognizedWords; - controller.text = result.recognizedWords; + _currentSessionText = result.recognizedWords; + + // Combine base text with current session text + String combinedText = _baseText.isEmpty + ? _currentSessionText + : _baseText + (_baseText.endsWith(' ') ? '' : ' ') + _currentSessionText; + + description = combinedText; + controller.text = combinedText; controller.selection = TextSelection.fromPosition(TextPosition(offset: controller.text.length)); notifyListeners(); }, @@ -93,6 +108,8 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { _isListening = true; _currentLocaleId = localeId; _currentController = controller; + _baseText = controller.text.trim(); // Save existing text + _currentSessionText = ''; notifyListeners(); await _startListeningInternal(localeId, controller); } @@ -102,6 +119,8 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { _isListening = false; _currentLocaleId = ''; _currentController = null; + _baseText = ''; + _currentSessionText = ''; await _speechToText.stop(); notifyListeners(); } From a958257ce93ef35dd4757bf311b5c536e1ba0f07 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Mon, 2 Mar 2026 14:20:11 +0300 Subject: [PATCH 25/30] Greeting --- assets/images/greeting.svg | 605 ++++++++ assets/langs/ar-SA.json | 43 +- assets/langs/en-US.json | 45 +- lib/generated/codegen_loader.g.dart | 6 +- lib/generated/locale_keys.g.dart | 1 + lib/provider/dashboard_provider_model.dart | 10 +- lib/ui/landing/dashboard_screen.dart | 1363 ++++++++--------- lib/ui/landing/widget/services_widget.dart | 5 +- .../shimmer/dashboard_shimmer_widget.dart | 78 + 9 files changed, 1400 insertions(+), 756 deletions(-) create mode 100644 assets/images/greeting.svg diff --git a/assets/images/greeting.svg b/assets/images/greeting.svg new file mode 100644 index 0000000..0682c60 --- /dev/null +++ b/assets/images/greeting.svg @@ -0,0 +1,605 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index a78c83f..c34661b 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -531,7 +531,7 @@ "noWinner": "حزين! لم يفز أحد اليوم.", "myTeam": "فريقي", "youCanPlayDemo": "لكن يمكنك لعب العرض", - "group" : "مجموعة", + "group": "مجموعة", "searchGroup": "مجموعة البحث", "connectHmgWifi": "قم بتوصيل HMG WIFI", "connectedHmgWifi": "اتصال HMG WIFI", @@ -575,20 +575,20 @@ "versionStatus": "حالة الإصدار", "supplierNo": "رقم المورد", "general": "عام", - "requesterOperatingUnit":"وحدة تشغيل مقدم الطلب", - "prepareEmpNum":"إعداد رقم الموظف", - "supplierInfo" : "معلومات المورد", + "requesterOperatingUnit": "وحدة تشغيل مقدم الطلب", + "prepareEmpNum": "إعداد رقم الموظف", + "supplierInfo": "معلومات المورد", "supplierAcNo": "رقم حساب المورد", "supplierAcName": "اسم حساب المورد", - "supplierIBAN" : "رقم IBAN للمورد", - "supplierCRNo" :"رقم السجل التجاري", - "suppliedAcNo" : "رقم الحساب المقدم", - "patientRefundInvoice" : "فاتورة استرداد الأموال للمريض", - "patientNumber" : "رقم المريض", - "patientName" : "اسم المريض", - "invoiceDate" : "تاريخ الفاتورة", - "refundInvoice" :"فاتورة الاسترجاع", - "hospitalClinic" : "عيادة المستشفى", + "supplierIBAN": "رقم IBAN للمورد", + "supplierCRNo": "رقم السجل التجاري", + "suppliedAcNo": "رقم الحساب المقدم", + "patientRefundInvoice": "فاتورة استرداد الأموال للمريض", + "patientNumber": "رقم المريض", + "patientName": "اسم المريض", + "invoiceDate": "تاريخ الفاتورة", + "refundInvoice": "فاتورة الاسترجاع", + "hospitalClinic": "عيادة المستشفى", "graphicalAnalysis": "التحليل الرسومي", "itemHistoryAnalysis": "تحليل تاريخ العنصر", "pOno": "امر شراء #", @@ -614,23 +614,23 @@ "members": "الأعضاء", "searchByUserName": "البحث بواسطة اسم المستخدم", "shareScreen": "مشاركة الشاشة", - "start":"يبدأ", - "about":"عن", + "start": "يبدأ", + "about": "عن", "explore": "يستكشف", "mazaya": "مازيا", "benefits": "فوائد", "mazayaDesc": "اكتشف الخصومات والعروض الخاصة المتاحة للموظفين", - "viewallofferMazaya" : "أعرض كل المزايا", + "viewallofferMazaya": "أعرض كل المزايا", "buyerName": "اسم المشتري", "buyerNumber": "رقم المشتري", "highestBidder": "أعلى مزايد", "remarks": "ملاحظات", "faHeader": "تفاصيل رأس الصفحة FA", - "bookTypeCode": "رمز نوع الكتاب", + "bookTypeCode": "رمز نوع الكتاب", "categoryCode": "رمز الفئة", "categoryGroup": "مجموعة الفئات", - "faLINES": "تفاصيل الأصول", - "assetNumber": "رقم الأصول", + "faLINES": "تفاصيل الأصول", + "assetNumber": "رقم الأصول", "assetDescription": "وصف الأصول", "barCodeNumber": "رقم الباركود", "datePlaceInService": "تاريخ وضعه في الخدمة", @@ -638,10 +638,11 @@ "disposedDate": "تاريخ التخلص", "netBookValue": "القيمة الدفترية الصافية", "purchasedPrice": "سعر الشراء", - "usefulLife": "العمر الإنتاجي", + "usefulLife": "العمر الإنتاجي", "yearsUsed": "سنوات الاستخدام", "faRequest": "طلب التخلص من FA", "showMore": "عرض المزيد", "buyerDetails": "تفاصيل المشتري", - "parkingQr": "وقوف السيارات QR" + "parkingQr": "وقوف السيارات QR", + "startNow": "ابدأ الآن" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 5b49d24..af3251e 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -565,7 +565,7 @@ "addAtLeastOneAttachment": "Please add at least one attachment.", "pleaseClickButtonToJoinMarathon": "Press the button below to join the Marathon.", "youCannotJoinTheMarathon": "You cannot join the Marathon because you have exceeded the time limit.", - "requesterOperatingUnit":"Requester Operating Unit", + "requesterOperatingUnit": "Requester Operating Unit", "generate": "Generate", "paymentRequest": "Pay Request", "paymentDetails": "Payment Details", @@ -591,19 +591,19 @@ "versionStatus": "Version Status", "supplierNo": "Supplier No", "general": "General", - "prepareEmpNum":"Prepare Employee Num", - "supplierInfo" : "Supplier Information", + "prepareEmpNum": "Prepare Employee Num", + "supplierInfo": "Supplier Information", "supplierAcNo": "Supplier Account No", - "supplierAcName":"Supplier Account Name", - "supplierIBAN" : "Supplier IBAN", - "supplierCRNo" : "CR Number", - "suppliedAcNo" : "Supplied Account No.", - "patientRefundInvoice" : "Patient Refund Invoice", - "patientNumber" : "Patient Number", - "patientName" : "Patient Name", - "invoiceDate" : "Invoice Date", - "refundInvoice" :"Refund Invoice", - "hospitalClinic" : "Hospital Clinic", + "supplierAcName": "Supplier Account Name", + "supplierIBAN": "Supplier IBAN", + "supplierCRNo": "CR Number", + "suppliedAcNo": "Supplied Account No.", + "patientRefundInvoice": "Patient Refund Invoice", + "patientNumber": "Patient Number", + "patientName": "Patient Name", + "invoiceDate": "Invoice Date", + "refundInvoice": "Refund Invoice", + "hospitalClinic": "Hospital Clinic", "graphicalAnalysis": "Graphical Analysis", "itemHistoryAnalysis": "Item History Analysis", "pOno": "P.O #", @@ -612,23 +612,23 @@ "qtyReceived": "Qty. Received", "bonusQty": "Bonus Qty.", "balQty": "Bal. Qty.", - "start":"Start", - "about":"About", + "start": "Start", + "about": "About", "explore": "Explore", "mazaya": "MAZAYA", "benefits": "Benefits", "mazayaDesc": "Discover special Discounts and offers available to Employees", - "viewallofferMazaya" : "View All Offers", + "viewallofferMazaya": "View All Offers", "buyerName": "Buyer Name", "buyerNumber": "Buyer Number", "highestBidder": "Highest Bidder", "remarks": "Remarks", "faHeader": "FA Header Details", - "bookTypeCode": "Book Type Code", + "bookTypeCode": "Book Type Code", "categoryCode": "Category Code", "categoryGroup": "Category Group", - "faLINES": "Asset Details", - "assetNumber": "Asset Number", + "faLINES": "Asset Details", + "assetNumber": "Asset Number", "assetDescription": "Asset Description", "barCodeNumber": "BarCode Number", "datePlaceInService": "Date place In Service", @@ -636,10 +636,11 @@ "disposedDate": "Disposed Date", "netBookValue": "Net Book Value", "purchasedPrice": "Purchased Price", - "usefulLife": "Useful Life", + "usefulLife": "Useful Life", "yearsUsed": "Years Used", - "faRequest":"FA Disposal Request", + "faRequest": "FA Disposal Request", "showMore": "Show More", "buyerDetails": "Buyer Details", - "parkingQr": "Parking QR" + "parkingQr": "Parking QR", + "startNow": "Start Now" } \ No newline at end of file diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index 62bbce6..3b24423 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -656,7 +656,8 @@ class CodegenLoader extends AssetLoader{ "faRequest": "طلب التخلص من FA", "showMore": "عرض المزيد", "buyerDetails": "تفاصيل المشتري", - "parkingQr": "وقوف السيارات QR" + "parkingQr": "وقوف السيارات QR", + "startNow": "ابدأ الآن" }; static const Map _en_US = { "mohemm": "Mohemm", @@ -1300,7 +1301,8 @@ static const Map _en_US = { "faRequest": "FA Disposal Request", "showMore": "Show More", "buyerDetails": "Buyer Details", - "parkingQr": "Parking QR" + "parkingQr": "Parking QR", + "startNow": "Start Now" }; static const Map> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 683c8c4..9cf563a 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -629,5 +629,6 @@ abstract class LocaleKeys { static const showMore = 'showMore'; static const buyerDetails = 'buyerDetails'; static const parkingQr = 'parkingQr'; + static const startNow = 'startNow'; } diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index 09c347e..169eb28 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -68,6 +68,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { bool isOffersLoading = true; List getOffersList = []; + bool isGreetingCardsLoading = true; bool isDisplayEidGreetings = false; List? greetingCardsList; @@ -119,6 +120,8 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { getMenuEntriesList = null; isOffersLoading = true; getOffersList = []; + isGreetingCardsLoading = true; + isDisplayEidGreetings = false; drawerMenuItemList = [ DrawerMenuItem("assets/images/drawer/my_profile.svg", LocaleKeys.myProfile.tr(), AppRoutes.profile), @@ -312,22 +315,23 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } void fetchGreetingCards() async { + isGreetingCardsLoading = true; + notifyListeners(); try { GetGreetingCardsModelResponse? response = await DashboardApiClient().getGreetingCards(); greetingCardsList = response?.getGreetingCardsModelResponse ?? []; - - // Check if there are any active greeting cards if (greetingCardsList != null && greetingCardsList!.isNotEmpty) { isDisplayEidGreetings = greetingCardsList!.any((card) => card.isActive == true); } else { isDisplayEidGreetings = false; } - + isGreetingCardsLoading = false; notifyListeners(); } catch (ex) { logger.wtf(ex); isDisplayEidGreetings = false; greetingCardsList = []; + isGreetingCardsLoading = false; notifyListeners(); } } diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index aca7aba..e067666 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui' as ui; - import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; @@ -254,716 +253,670 @@ class _DashboardScreenState extends State with WidgetsBindingOb child: Scaffold( key: _scaffoldState, body: Column( - children: [ - Row( - children: [ - Builder( - builder: (BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Image.memory( - Utils.dataFromBase64String(AppState().memberInformationList!.eMPLOYEEIMAGE ?? ""), - errorBuilder: (BuildContext context, Object error, StackTrace? stackTrace) { - return SvgPicture.asset("assets/images/user.svg", height: 34, width: 34); - }, - width: 34, - height: 34, - fit: BoxFit.cover, - ).circle(50), - // CircularAvatar( - // width: 34, - // height: 34, - // url: "https://cdn4.iconfinder.com/data/icons/professions-2-2/151/89-512.png", - // ), - 8.width, - SvgPicture.asset("assets/images/side_nav.svg"), - ], - ).onPress(() { - _scaffoldState.currentState!.openDrawer(); - }); - }, - ), - Image - .asset("assets/images/logos/main_mohemm_logo.png", width: 134, height: 28) - .expanded, - SvgPicture.asset("assets/images/announcements.svg", matchTextDirection: true).onPress(() async { - await Navigator.pushNamed(context, AppRoutes.announcements); - }), - ], - ).paddingOnly(left: 21, right: 21, top: 48, bottom: 7), - Expanded( - child: SmartRefresher( - enablePullDown: true, - enablePullUp: false, - header: const MaterialClassicHeader(color: MyColors.gradiantEndColor), - controller: _refreshController, - onRefresh: () { - _onRefresh(false); - }, - child: SingleChildScrollView( - child: Column( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.welcomeBack.tr().toText14(color: MyColors.grey77Color), - (AppState().memberInformationList!.eMPLOYEENAME ?? "").toText24(isBold: true), - 16.height, - Row( - children: [ - Expanded( - child: AspectRatio( - aspectRatio: 159 / 159, - child: Consumer( - builder: (BuildContext context, DashboardProviderModel model, Widget? child) { - return (model.isAttendanceTrackingLoading - ? GetAttendanceTrackingShimmer() - : Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(15), - gradient: const LinearGradient( - transform: GradientRotation(.46), - begin: Alignment.topRight, - end: Alignment.bottomLeft, - colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor], - ), - ), - child: Stack( - alignment: Alignment.center, - children: [ - if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true), - if (model.isTimeRemainingInSeconds == 0) DateTime.now().toString().split(" ")[0].toText12(color: Colors.white), - if (model.isTimeRemainingInSeconds != 0) - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - 9.height, - Directionality( - textDirection: ui.TextDirection.ltr, - child: CountdownTimer( - endTime: model.endTime, - onEnd: null, - endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), - textStyle: const TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold), - ), - ), - LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), - 9.height, - ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(20)), - child: LinearProgressIndicator( - value: model.progress, - 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), - (model.attendanceTracking!.pSwipeIn == null ? "--:--" : model.attendanceTracking!.pSwipeIn).toString().toText14( - color: Colors.white, - isBold: true, - ), - 4.height, - ], - ).paddingOnly(left: 12, right: 12), - ), - Container( - margin: EdgeInsets.only(top: AppState().isArabic(context) ? 6 : 0), - width: 45, - height: 45, - padding: const EdgeInsets.only(left: 10, right: 10), - decoration: BoxDecoration( - color: const Color(0xff259EA4), - borderRadius: BorderRadius.only( - bottomRight: AppState().isArabic(context) ? const Radius.circular(0) : const Radius.circular(15), - bottomLeft: AppState().isArabic(context) ? const Radius.circular(15) : const Radius.circular(0), - ), - ), - child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/biometrics.svg" : "assets/images/biometrics.svg"), - ).onPress(() { - showMyBottomSheet(context, callBackFunc: () {}, child: MarkAttendanceWidget(model, isFromDashboard: true)); - }), - ], - ), - ], - ), - ], - ), - ).onPress(() { - Navigator.pushNamed(context, AppRoutes.todayAttendance); - })) - .animatedSwither(); - }, - ), - ), - ), - 9.width, - Expanded(child: MenusWidget()), - ], - ), - ], - ).paddingOnly(left: 21, right: 21, top: 7, bottom: 21), - if (data.isDisplayEidGreetings) ...[ - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Directionality( - textDirection: AppState().isArabic(context) ? ui.TextDirection.rtl : ui.TextDirection.ltr, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - gradient: const LinearGradient(colors: [Color(0xFFE7E7E7), Color(0xFFE7E7E7)], begin: Alignment.centerLeft, end: Alignment.centerRight), - ), - child: Padding( - padding: const EdgeInsets.all(3.0), - child: Container( - height: 120, - decoration: BoxDecoration(color: const Color(0xFFFAF4E7),borderRadius: BorderRadius.circular(17), image: DecorationImage(image: NetworkImage(AppState().isArabic(context) ? data.greetingCardsList!.first.backgroundImageUrlAr ?? "" : data.greetingCardsList!.first.backgroundImageUrlEn ?? ""), fit: BoxFit.cover)), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Expanded( - // flex: 4, - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisAlignment: MainAxisAlignment.start, - // children: [ - // Row( - // children: [ - // Expanded( - // flex: 2, - // child: RichText( - // text: - // AppState().isArabic(context) - // ? TextSpan( - // children: [ - // TextSpan( - // text: 'اطلع على مميزات', - // style: TextStyle( - // fontSize: 16, - // letterSpacing: -0.2, - // fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', - // fontWeight: FontWeight.w700, - // height: 24 / 16, - // color: const Color(0xFF5D5E5E), - // ), - // ), - // TextSpan( - // text: ' مزايا', - // style: TextStyle( - // fontSize: 16, - // fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', - // fontWeight: FontWeight.w700, - // letterSpacing: -0.2, - // height: 24 / 16, - // color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed - // ), - // ), - // ], - // ) - // : TextSpan( - // children: [ - // TextSpan( - // text: LocaleKeys.explore.tr() + ' ', - // style: const TextStyle( - // fontSize: 16, - // letterSpacing: -0.2, - // fontFamily: 'Poppins', - // fontWeight: FontWeight.w700, - // height: 24 / 16, - // color: Color(0xFF5D5E5E), - // ), - // ), - // TextSpan( - // text: LocaleKeys.mazaya.tr(), - // style: const TextStyle( - // fontSize: 16, - // fontWeight: FontWeight.w700, - // fontFamily: 'Poppins', - // letterSpacing: -0.2, - // height: 24 / 16, - // color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed - // ), - // ), - // TextSpan( - // text: ' ' + LocaleKeys.benefits.tr(), - // style: const TextStyle( - // fontSize: 16, - // letterSpacing: -0.2, - // fontFamily: 'Poppins', - // fontWeight: FontWeight.w700, - // height: 24 / 16, - // color: Color(0xFF5D5E5E), - // ), - // ), - // ], - // ), - // ), - // ), - // const Expanded(flex: 1, child: SizedBox()), - // ], - // ), - // const SizedBox(height: 8), - // LocaleKeys.mazayaDesc.tr().toText11(color: const Color(0xFF5D5E5E)), - // ], - // ), - // ), - // Expanded( - // flex: 2, - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.end, - // mainAxisAlignment: MainAxisAlignment.end, - // children: [ - // SvgPicture.asset("assets/icons/mazaya_brand.svg", width: 90, height: 47), - // const SizedBox(height: 28), - // LocaleKeys.viewallofferMazaya.tr().toText12(isUnderLine: true, color: const Color(0xFF3B3D4A)).onPress(() { - // Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); - // }), - // ], - // ), - // ), - ], - ).paddingOnly(left: 21, right: 21, top: 14, bottom: 14), - ), - ), - ).paddingOnly(left: 21, right: 21, top: 0, bottom: 21), - ), - ], - ), - ], - eventActivityWidget(context), - if (isDisplayMazaya) ...[ - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Directionality( - textDirection: AppState().isArabic(context) ? ui.TextDirection.rtl : ui.TextDirection.ltr, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - gradient: const LinearGradient(colors: [Color(0xFF91C481), Color(0xFF7CCED7)], begin: Alignment.centerLeft, end: Alignment.centerRight), - ), - child: Padding( - padding: const EdgeInsets.all(3.0), // This creates the border width - child: Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(17), // Slightly less than outer radius - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - flex: 4, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - flex: 2, - child: RichText( - text: - AppState().isArabic(context) - ? TextSpan( - children: [ - TextSpan( - text: 'اطلع على مميزات', - style: TextStyle( - fontSize: 16, - letterSpacing: -0.2, - fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', - fontWeight: FontWeight.w700, - height: 24 / 16, - color: const Color(0xFF5D5E5E), - ), - ), - TextSpan( - text: ' مزايا', - style: TextStyle( - fontSize: 16, - fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', - fontWeight: FontWeight.w700, - letterSpacing: -0.2, - height: 24 / 16, - color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed - ), - ), - ], - ) - : TextSpan( - children: [ - TextSpan( - text: LocaleKeys.explore.tr() + ' ', - style: const TextStyle( - fontSize: 16, - letterSpacing: -0.2, - fontFamily: 'Poppins', - fontWeight: FontWeight.w700, - height: 24 / 16, - color: Color(0xFF5D5E5E), - ), - ), - TextSpan( - text: LocaleKeys.mazaya.tr(), - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w700, - fontFamily: 'Poppins', - letterSpacing: -0.2, - height: 24 / 16, - color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed - ), - ), - TextSpan( - text: ' ' + LocaleKeys.benefits.tr(), - style: const TextStyle( - fontSize: 16, - letterSpacing: -0.2, - fontFamily: 'Poppins', - fontWeight: FontWeight.w700, - height: 24 / 16, - color: Color(0xFF5D5E5E), - ), - ), - ], - ), - ), - ), - const Expanded(flex: 1, child: SizedBox()), - ], - ), - const SizedBox(height: 8), - LocaleKeys.mazayaDesc.tr().toText11(color: const Color(0xFF5D5E5E)), - ], - ), - ), - Expanded( - flex: 2, - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - SvgPicture.asset("assets/icons/mazaya_brand.svg", width: 90, height: 47), - const SizedBox(height: 28), - LocaleKeys.viewallofferMazaya.tr().toText12(isUnderLine: true, color: const Color(0xFF3B3D4A)).onPress(() { - Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); - }), - ], - ), - ), - ], - ).paddingOnly(left: 21, right: 21, top: 14, bottom: 14), - ), - ), - ).paddingOnly(left: 21, right: 21, top: 0, bottom: 21), - ), - ], - ), - ], + children: [ + Row( + children: [ + Builder( + builder: (BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Image.memory( + Utils.dataFromBase64String(AppState().memberInformationList!.eMPLOYEEIMAGE ?? ""), + errorBuilder: (BuildContext context, Object error, StackTrace? stackTrace) { + return SvgPicture.asset("assets/images/user.svg", height: 34, width: 34); + }, + width: 34, + height: 34, + fit: BoxFit.cover, + ).circle(50), + // CircularAvatar( + // width: 34, + // height: 34, + // url: "https://cdn4.iconfinder.com/data/icons/professions-2-2/151/89-512.png", + // ), + 8.width, + SvgPicture.asset("assets/images/side_nav.svg"), + ], + ).onPress(() { + _scaffoldState.currentState!.openDrawer(); + }); + }, + ), + Image.asset("assets/images/logos/main_mohemm_logo.png", width: 134, height: 28).expanded, + SvgPicture.asset("assets/images/announcements.svg", matchTextDirection: true).onPress(() async { + await Navigator.pushNamed(context, AppRoutes.announcements); + }), + ], + ).paddingOnly(left: 21, right: 21, top: 48, bottom: 7), + Expanded( + child: SmartRefresher( + enablePullDown: true, + enablePullUp: false, + header: const MaterialClassicHeader(color: MyColors.gradiantEndColor), + controller: _refreshController, + onRefresh: () { + _onRefresh(false); + }, + child: SingleChildScrollView( + child: Column( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.welcomeBack.tr().toText14(color: MyColors.grey77Color), + (AppState().memberInformationList!.eMPLOYEENAME ?? "").toText24(isBold: true), + 16.height, + Row( + children: [ + Expanded( + child: AspectRatio( + aspectRatio: 159 / 159, + child: Consumer( + builder: (BuildContext context, DashboardProviderModel model, Widget? child) { + return (model.isAttendanceTrackingLoading + ? GetAttendanceTrackingShimmer() + : Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + gradient: const LinearGradient( + transform: GradientRotation(.46), + begin: Alignment.topRight, + end: Alignment.bottomLeft, + colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor], + ), + ), + child: Stack( + alignment: Alignment.center, + children: [ + if (model.isTimeRemainingInSeconds == 0) SvgPicture.asset("assets/images/thumb.svg"), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + LocaleKeys.markAttendance.tr().toText14(color: Colors.white, isBold: true), + if (model.isTimeRemainingInSeconds == 0) DateTime.now().toString().split(" ")[0].toText12(color: Colors.white), + if (model.isTimeRemainingInSeconds != 0) + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 9.height, + Directionality( + textDirection: ui.TextDirection.ltr, + child: CountdownTimer( + endTime: model.endTime, + onEnd: null, + endWidget: "00:00:00".toText14(color: Colors.white, isBold: true), + textStyle: const TextStyle(color: Colors.white, fontSize: 14, letterSpacing: -0.48, fontWeight: FontWeight.bold), + ), + ), + LocaleKeys.timeLeftToday.tr().toText12(color: Colors.white), + 9.height, + ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(20)), + child: LinearProgressIndicator( + value: model.progress, + 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), + (model.attendanceTracking!.pSwipeIn == null ? "--:--" : model.attendanceTracking!.pSwipeIn).toString().toText14( + color: Colors.white, + isBold: true, + ), + 4.height, + ], + ).paddingOnly(left: 12, right: 12), + ), + Container( + margin: EdgeInsets.only(top: AppState().isArabic(context) ? 6 : 0), + width: 45, + height: 45, + padding: const EdgeInsets.only(left: 10, right: 10), + decoration: BoxDecoration( + color: const Color(0xff259EA4), + borderRadius: BorderRadius.only( + bottomRight: AppState().isArabic(context) ? const Radius.circular(0) : const Radius.circular(15), + bottomLeft: AppState().isArabic(context) ? const Radius.circular(15) : const Radius.circular(0), + ), + ), + child: SvgPicture.asset(model.isTimeRemainingInSeconds == 0 ? "assets/images/biometrics.svg" : "assets/images/biometrics.svg"), + ).onPress(() { + showMyBottomSheet(context, callBackFunc: () {}, child: MarkAttendanceWidget(model, isFromDashboard: true)); + }), + ], + ), + ], + ), + ], + ), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.todayAttendance); + })) + .animatedSwither(); + }, + ), + ), + ), + 9.width, + Expanded(child: MenusWidget()), + ], + ), + ], + ).paddingOnly(left: 21, right: 21, top: 7, bottom: 21), + Consumer( + builder: (BuildContext context, DashboardProviderModel model, Widget? child) { + if (model.isGreetingCardsLoading) { + return const GreetingCardShimmer(); + } else if (model.isDisplayEidGreetings) { + return Column( + children: [ + Directionality( + textDirection: AppState().isArabic(context) ? ui.TextDirection.rtl : ui.TextDirection.ltr, + child: Container( + height: 100, + decoration: BoxDecoration( + color: const Color(0xFFFAF4E7), + border: Border.all(width: 2, color: const Color(0xFFE7E7E7)), + borderRadius: BorderRadius.circular(20), + image: const DecorationImage( + image: NetworkImage( + "https://dummyimage.com/1200X600/faf4e7/faf4e7.jpg&text=0", + // AppState().isArabic(context) ? model.greetingCardsList!.first.backgroundImageUrlAr ?? "" : model.greetingCardsList!.first.backgroundImageUrlEn ?? "", + ), + fit: BoxFit.cover, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.asset("assets/images/greeting.svg", width: 65, height: 81), + const SizedBox(width: 10), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Text( + AppState().isArabic(context) ? model.greetingCardsList!.first.descriptionAr ?? '' : model.greetingCardsList!.first.descriptionEn ?? '', + style: TextStyle( + fontSize: AppState().isArabic(context) ? 18 : 16, + letterSpacing: -0.2, + fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', + fontWeight: FontWeight.w700, + height: 24 / 16, + color: const Color(0xFF3B3E4F), + ), + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + AppState().isArabic(context) ? model.greetingCardsList!.first.titleAr ?? '' : model.greetingCardsList!.first.titleEn ?? '', + style: TextStyle( + fontSize: AppState().isArabic(context) ? 18 : 16, + letterSpacing: -0.2, + fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', + fontWeight: FontWeight.w700, + height: 24 / 16, + color: const Color(0xFF3B3E4F), + ), + ), + ], + ), + ], + ), + ], + ).paddingOnly(top: 10, bottom: 10).expanded, + Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + LocaleKeys.startNow.tr().toText12(isUnderLine: true, color: const Color(0xFF3B3D4A)).onPress(() { + Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); + }), + ], + ), + ], + ).paddingOnly(left: 8, right: 8, top: 6, bottom: 12), + ).paddingOnly(left: 21, right: 21, top: 14, bottom: 14), + ), + ], + ); + } else { + return const SizedBox(); + } + }, + ), - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - LocaleKeys.offers.tr().toText12(), - Row( - children: [ - LocaleKeys.discounts.tr().toText24(isBold: true), - 6.width, - Container( - padding: const EdgeInsets.only(left: 8, right: 8), - decoration: BoxDecoration(color: MyColors.yellowColor, borderRadius: BorderRadius.circular(10)), - child: LocaleKeys.newString.tr().toText10(isBold: true), - ), - ], - ), - ], - ), - ), - LocaleKeys.viewAllOffers.tr().toText12(isUnderLine: true).onPress(() { - Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); - }), - ], - ).paddingOnly(left: 21, right: 21), - Consumer( - builder: (BuildContext context, DashboardProviderModel model, Widget? child) { - return SizedBox( - height: 103 + 33, - child: ListView.separated( - shrinkWrap: true, - physics: const BouncingScrollPhysics(), - padding: const EdgeInsets.only(left: 21, right: 21, top: 13), - scrollDirection: Axis.horizontal, - itemBuilder: (BuildContext cxt, int index) { - return model.isOffersLoading - ? const OffersShimmerWidget() - : InkWell( - onTap: () { - navigateToDetails(data.getOffersList[index]); - }, - child: SizedBox( - width: 73, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - width: 73, - height: 73, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.all(Radius.circular(100)), - border: Border.all(color: MyColors.lightGreyE3Color, width: 1), - ), - child: ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(50)), - child: Hero( - tag: "ItemImage" + data.getOffersList[index].offersDiscountId.toString()!, - transitionOnUserGestures: true, - child: Image.network(data.getOffersList[index].logo ?? "", fit: BoxFit.contain), - ), - ), - ), - 4.height, - Expanded( - child: - AppState().isArabic(context) - ? data.getOffersList[index].titleAr!.toText12(isCenter: true, maxLine: 1) - : data.getOffersList[index].titleEn!.toText12(isCenter: true, maxLine: 1), - ), - ], - ), - ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 8.width, - itemCount: 9, - ), - ); - }, - ), - ], - ), + eventActivityWidget(context), + if (isDisplayMazaya) ...[ + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Directionality( + textDirection: AppState().isArabic(context) ? ui.TextDirection.rtl : ui.TextDirection.ltr, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + gradient: const LinearGradient(colors: [Color(0xFF91C481), Color(0xFF7CCED7)], begin: Alignment.centerLeft, end: Alignment.centerRight), + ), + child: Padding( + padding: const EdgeInsets.all(3.0), // This creates the border width + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(17), // Slightly less than outer radius + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 4, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + flex: 2, + child: RichText( + text: + AppState().isArabic(context) + ? TextSpan( + children: [ + TextSpan( + text: 'اطلع على مميزات', + style: TextStyle( + fontSize: 16, + letterSpacing: -0.2, + fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', + fontWeight: FontWeight.w700, + height: 24 / 16, + color: const Color(0xFF5D5E5E), + ), + ), + TextSpan( + text: ' مزايا', + style: TextStyle( + fontSize: 16, + fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', + fontWeight: FontWeight.w700, + letterSpacing: -0.2, + height: 24 / 16, + color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed + ), + ), + ], + ) + : TextSpan( + children: [ + TextSpan( + text: LocaleKeys.explore.tr() + ' ', + style: const TextStyle( + fontSize: 16, + letterSpacing: -0.2, + fontFamily: 'Poppins', + fontWeight: FontWeight.w700, + height: 24 / 16, + color: Color(0xFF5D5E5E), + ), + ), + TextSpan( + text: LocaleKeys.mazaya.tr(), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + fontFamily: 'Poppins', + letterSpacing: -0.2, + height: 24 / 16, + color: MyColors.mazayaRedColor, // Use your MAZAYA red color here if defined, e.g. MyColors.mazayaRed + ), + ), + TextSpan( + text: ' ' + LocaleKeys.benefits.tr(), + style: const TextStyle( + fontSize: 16, + letterSpacing: -0.2, + fontFamily: 'Poppins', + fontWeight: FontWeight.w700, + height: 24 / 16, + color: Color(0xFF5D5E5E), + ), + ), + ], + ), + ), + ), + const Expanded(flex: 1, child: SizedBox()), + ], + ), + const SizedBox(height: 8), + LocaleKeys.mazayaDesc.tr().toText11(color: const Color(0xFF5D5E5E)), + ], + ), + ), + Expanded( + flex: 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SvgPicture.asset("assets/icons/mazaya_brand.svg", width: 90, height: 47), + const SizedBox(height: 28), + LocaleKeys.viewallofferMazaya.tr().toText12(isUnderLine: true, color: const Color(0xFF3B3D4A)).onPress(() { + Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); + }), + ], + ), + ), + ], + ).paddingOnly(left: 21, right: 21, top: 14, bottom: 14), + ), + ), + ).paddingOnly(left: 21, right: 21, top: 0, bottom: 21), + ), + ], + ), + ], - Container( - width: double.infinity, - padding: const EdgeInsets.only(top: 31), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: const BorderRadius.only(topRight: Radius.circular(50), topLeft: Radius.circular(50)), - border: Border.all(color: MyColors.lightGreyEDColor, width: 1), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ServicesWidget(), - context.watch().isLoading ? const MarathonBannerShimmer().paddingAll(20) : const MarathonBanner().paddingOnly(left: 21, right: 21, bottom: 8, top: 8), - // context.watch().isTutorialLoading - // ? const MarathonBannerShimmer().paddingAll(20) - // : Container( - // padding: EdgeInsets.only(bottom: 12, top: 12), - // margin: EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 8), - // width: double.infinity, - // alignment: Alignment.center, - // decoration: BoxDecoration( - // color: MyColors.backgroundBlackColor, - // borderRadius: BorderRadius.circular(20), - // border: Border.all(color: MyColors.lightGreyEDColor, width: 1), - // ), - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisSize: MainAxisSize.min, - // children: [ - // Text( - // "Tutorial:", - // style: TextStyle( - // fontSize: 11, - // fontStyle: FontStyle.italic, - // fontWeight: FontWeight.w600, - // color: MyColors.white.withOpacity(0.83), - // letterSpacing: -0.4, - // ), - // ), - // Text( - // context.read().tutorial?.tutorialName ?? "", - // overflow: TextOverflow.ellipsis, - // style: TextStyle( - // fontStyle: FontStyle.italic, - // fontSize: 19, - // fontWeight: FontWeight.bold, - // color: MyColors.white, - // height: 32 / 22, - // ), - // ), - // ], - // ), - // ).onPress(() { - // checkERMChannel(); - // // Navigator.pushNamed(context, AppRoutes.marathonTutorialScreen); - // }), - ], - ), - ), - ], - ), - ), - ), - ), - ], - ), - drawer: AppDrawer(onLanguageChange: _onRefresh), - bottomNavigationBar: SizedBox( - height: Platform.isAndroid ? 70 : 100, - child: BottomNavigationBar( - items: [ - BottomNavigationBarItem( - icon: SvgPicture.asset("assets/icons/home.svg", color: currentIndex == 0 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), - label: LocaleKeys.home.tr(), - ), - BottomNavigationBarItem( - icon: SvgPicture.asset("assets/icons/create_req.svg", color: currentIndex == 1 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), - label: LocaleKeys.mowadhafhiRequest.tr(), - ), - BottomNavigationBarItem( - icon: Stack( - alignment: Alignment.centerLeft, - children: [ - SvgPicture.asset("assets/icons/work_list.svg", color: currentIndex == 2 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), - Consumer( - builder: (BuildContext cxt, DashboardProviderModel data, Widget? child) { - if (data.workListCounter == 0) { - return const SizedBox(); - } - return Positioned( - right: 0, - top: 0, - child: Container( - padding: const EdgeInsets.only(left: 4, right: 4), - alignment: Alignment.center, - decoration: BoxDecoration(color: MyColors.redColor, borderRadius: BorderRadius.circular(17)), - child: data.workListCounter.toString().toText10(color: Colors.white), - ), - ); - }, - ), - ], - ), - label: LocaleKeys.workList.tr(), - ), - BottomNavigationBarItem( - icon: SvgPicture.asset("assets/icons/item_for_sale.svg", color: currentIndex == 3 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), - label: LocaleKeys.itemsForSale.tr(), - ), - BottomNavigationBarItem( - icon: Stack( - alignment: Alignment.centerLeft, - children: [ - SvgPicture.asset( - "assets/icons/chat/chat.svg", - color: - !checkIfPrivilegedForChat() - ? MyColors.lightGreyE3Color - : currentIndex == 4 - ? MyColors.grey3AColor - : cProvider.disbaleChatForThisUser - ? MyColors.lightGreyE3Color - : MyColors.grey98Color, - ).paddingAll(4), - Consumer( - builder: (BuildContext cxt, ChatProviderModel data, Widget? child) { - return !checkIfPrivilegedForChat() - ? const SizedBox() - : Positioned( - right: 0, - top: 0, - child: Container( - padding: const EdgeInsets.only(left: 4, right: 4), - alignment: Alignment.center, - decoration: BoxDecoration(color: cProvider.disbaleChatForThisUser ? MyColors.pinkDarkColor : MyColors.redColor, borderRadius: BorderRadius.circular(17)), - child: data.chatUConvCounter.toString().toText10(color: Colors.white), - ), - ); - }, - ), - ], - ), - label: LocaleKeys.chat.tr(), - ), - ], - currentIndex: currentIndex, - selectedLabelStyle: const TextStyle(fontSize: 10, color: MyColors.grey3AColor, fontWeight: FontWeight.w600), - unselectedLabelStyle: const TextStyle(fontSize: 10, color: MyColors.grey98Color, fontWeight: FontWeight.w600), - type: BottomNavigationBarType.fixed, - selectedItemColor: MyColors.grey3AColor, - backgroundColor: MyColors.backgroundColor, - selectedIconTheme: const IconThemeData(color: MyColors.grey3AColor, size: 28), - unselectedIconTheme: const IconThemeData(color: MyColors.grey98Color, size: 28), - onTap: (int index) { - if (index == 1) { - Navigator.pushNamed(context, AppRoutes.mowadhafhi); - } else if (index == 2) { - Navigator.pushNamed(context, AppRoutes.workList); - } else if (index == 3) { - Navigator.pushNamed(context, AppRoutes.itemsForSale); - } else if (index == 4) { - if (!cProvider.disbaleChatForThisUser && checkIfPrivilegedForChat()) { - Navigator.pushNamed(context, AppRoutes.chat); - } - } - }, - ), - ), - ), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + LocaleKeys.offers.tr().toText12(), + Row( + children: [ + LocaleKeys.discounts.tr().toText24(isBold: true), + 6.width, + Container( + padding: const EdgeInsets.only(left: 8, right: 8), + decoration: BoxDecoration(color: MyColors.yellowColor, borderRadius: BorderRadius.circular(10)), + child: LocaleKeys.newString.tr().toText10(isBold: true), + ), + ], + ), + ], + ), + ), + LocaleKeys.viewAllOffers.tr().toText12(isUnderLine: true).onPress(() { + Navigator.pushNamed(context, AppRoutes.offersAndDiscounts); + }), + ], + ).paddingOnly(left: 21, right: 21), + Consumer( + builder: (BuildContext context, DashboardProviderModel model, Widget? child) { + return SizedBox( + height: 103 + 33, + child: ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.only(left: 21, right: 21, top: 13), + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext cxt, int index) { + return model.isOffersLoading + ? const OffersShimmerWidget() + : InkWell( + onTap: () { + navigateToDetails(data.getOffersList[index]); + }, + child: SizedBox( + width: 73, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: 73, + height: 73, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(100)), + border: Border.all(color: MyColors.lightGreyE3Color, width: 1), + ), + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(50)), + child: Hero( + tag: "ItemImage" + data.getOffersList[index].offersDiscountId.toString()!, + transitionOnUserGestures: true, + child: Image.network(data.getOffersList[index].logo ?? "", fit: BoxFit.contain), + ), + ), + ), + 4.height, + Expanded( + child: + AppState().isArabic(context) + ? data.getOffersList[index].titleAr!.toText12(isCenter: true, maxLine: 1) + : data.getOffersList[index].titleEn!.toText12(isCenter: true, maxLine: 1), + ), + ], + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => 8.width, + itemCount: 9, + ), + ); + }, + ), + ], + ), + + Container( + width: double.infinity, + padding: const EdgeInsets.only(top: 31), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.only(topRight: Radius.circular(50), topLeft: Radius.circular(50)), + border: Border.all(color: MyColors.lightGreyEDColor, width: 1), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ServicesWidget(), + context.watch().isLoading ? const MarathonBannerShimmer().paddingAll(20) : const MarathonBanner().paddingOnly(left: 21, right: 21, bottom: 8, top: 8), + // context.watch().isTutorialLoading + // ? const MarathonBannerShimmer().paddingAll(20) + // : Container( + // padding: EdgeInsets.only(bottom: 12, top: 12), + // margin: EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 8), + // width: double.infinity, + // alignment: Alignment.center, + // decoration: BoxDecoration( + // color: MyColors.backgroundBlackColor, + // borderRadius: BorderRadius.circular(20), + // border: Border.all(color: MyColors.lightGreyEDColor, width: 1), + // ), + // child: Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text( + // "Tutorial:", + // style: TextStyle( + // fontSize: 11, + // fontStyle: FontStyle.italic, + // fontWeight: FontWeight.w600, + // color: MyColors.white.withOpacity(0.83), + // letterSpacing: -0.4, + // ), + // ), + // Text( + // context.read().tutorial?.tutorialName ?? "", + // overflow: TextOverflow.ellipsis, + // style: TextStyle( + // fontStyle: FontStyle.italic, + // fontSize: 19, + // fontWeight: FontWeight.bold, + // color: MyColors.white, + // height: 32 / 22, + // ), + // ), + // ], + // ), + // ).onPress(() { + // checkERMChannel(); + // // Navigator.pushNamed(context, AppRoutes.marathonTutorialScreen); + // }), + ], + ), + ), + ], + ), + ), + ), + ), + ], + ), + drawer: AppDrawer(onLanguageChange: _onRefresh), + bottomNavigationBar: SizedBox( + height: Platform.isAndroid ? 70 : 100, + child: BottomNavigationBar( + items: [ + BottomNavigationBarItem( + icon: SvgPicture.asset("assets/icons/home.svg", color: currentIndex == 0 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), + label: LocaleKeys.home.tr(), + ), + BottomNavigationBarItem( + icon: SvgPicture.asset("assets/icons/create_req.svg", color: currentIndex == 1 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), + label: LocaleKeys.mowadhafhiRequest.tr(), + ), + BottomNavigationBarItem( + icon: Stack( + alignment: Alignment.centerLeft, + children: [ + SvgPicture.asset("assets/icons/work_list.svg", color: currentIndex == 2 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), + Consumer( + builder: (BuildContext cxt, DashboardProviderModel data, Widget? child) { + if (data.workListCounter == 0) { + return const SizedBox(); + } + return Positioned( + right: 0, + top: 0, + child: Container( + padding: const EdgeInsets.only(left: 4, right: 4), + alignment: Alignment.center, + decoration: BoxDecoration(color: MyColors.redColor, borderRadius: BorderRadius.circular(17)), + child: data.workListCounter.toString().toText10(color: Colors.white), + ), + ); + }, + ), + ], + ), + label: LocaleKeys.workList.tr(), + ), + BottomNavigationBarItem( + icon: SvgPicture.asset("assets/icons/item_for_sale.svg", color: currentIndex == 3 ? MyColors.grey3AColor : MyColors.grey98Color).paddingAll(4), + label: LocaleKeys.itemsForSale.tr(), + ), + BottomNavigationBarItem( + icon: Stack( + alignment: Alignment.centerLeft, + children: [ + SvgPicture.asset( + "assets/icons/chat/chat.svg", + color: + !checkIfPrivilegedForChat() + ? MyColors.lightGreyE3Color + : currentIndex == 4 + ? MyColors.grey3AColor + : cProvider.disbaleChatForThisUser + ? MyColors.lightGreyE3Color + : MyColors.grey98Color, + ).paddingAll(4), + Consumer( + builder: (BuildContext cxt, ChatProviderModel data, Widget? child) { + return !checkIfPrivilegedForChat() + ? const SizedBox() + : Positioned( + right: 0, + top: 0, + child: Container( + padding: const EdgeInsets.only(left: 4, right: 4), + alignment: Alignment.center, + decoration: BoxDecoration(color: cProvider.disbaleChatForThisUser ? MyColors.pinkDarkColor : MyColors.redColor, borderRadius: BorderRadius.circular(17)), + child: data.chatUConvCounter.toString().toText10(color: Colors.white), + ), + ); + }, + ), + ], + ), + label: LocaleKeys.chat.tr(), + ), + ], + currentIndex: currentIndex, + selectedLabelStyle: const TextStyle(fontSize: 10, color: MyColors.grey3AColor, fontWeight: FontWeight.w600), + unselectedLabelStyle: const TextStyle(fontSize: 10, color: MyColors.grey98Color, fontWeight: FontWeight.w600), + type: BottomNavigationBarType.fixed, + selectedItemColor: MyColors.grey3AColor, + backgroundColor: MyColors.backgroundColor, + selectedIconTheme: const IconThemeData(color: MyColors.grey3AColor, size: 28), + unselectedIconTheme: const IconThemeData(color: MyColors.grey98Color, size: 28), + onTap: (int index) { + if (index == 1) { + Navigator.pushNamed(context, AppRoutes.mowadhafhi); + } else if (index == 2) { + Navigator.pushNamed(context, AppRoutes.workList); + } else if (index == 3) { + Navigator.pushNamed(context, AppRoutes.itemsForSale); + } else if (index == 4) { + if (!cProvider.disbaleChatForThisUser && checkIfPrivilegedForChat()) { + Navigator.pushNamed(context, AppRoutes.chat); + } + } + }, + ), + ), + ), ); - } + } Widget eventActivityWidget(BuildContext context) { - return (context - .watch() - .isEventLoadingLoading) + return (context.watch().isEventLoadingLoading) ? const MarathonBannerShimmer().paddingOnly(left: 21, right: 21, bottom: 21, top: 0) - : (context - .watch() - .eventActivity != null && context - .watch() - .eventActivity! - .isActive == true) + : (context.watch().eventActivity != null && context.watch().eventActivity!.isActive == true) ? const EventActivityBanner().paddingOnly(left: 21, right: 21, bottom: 21, top: 0) : const SizedBox(); } diff --git a/lib/ui/landing/widget/services_widget.dart b/lib/ui/landing/widget/services_widget.dart index 879173b..0a30857 100644 --- a/lib/ui/landing/widget/services_widget.dart +++ b/lib/ui/landing/widget/services_widget.dart @@ -92,7 +92,7 @@ class ServicesWidget extends StatelessWidget { aspectRatio: 105 / 105, child: data.isServicesMenusLoading - ? ServicesMenuShimmer() + ? const ServicesMenuShimmer() : Container( decoration: BoxDecoration( color: Colors.white, @@ -266,7 +266,6 @@ class ServicesWidget extends StatelessWidget { await pro.fetchTicketAccuralBalance(context, DateTime.now()); pro.ticketHistoryTransactionList = await EITApiClient().getEITTransactions("HMG_TICKET_ITENARY_HR_EIT_SS", isCompleteList: true); - Utils.hideLoading(context); // Here Need Work Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => TicketDetailedScreen(url: url, jwtToken: ssoToken.data!.accessToken))); @@ -311,7 +310,7 @@ class ServicesWidget extends StatelessWidget { padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), scrollDirection: Axis.horizontal, itemBuilder: (cxt, index) { - return AspectRatio(aspectRatio: 105 / 105, child: ServicesMenuShimmer()); + return AspectRatio(aspectRatio: 105 / 105, child: const ServicesMenuShimmer()); }, separatorBuilder: (cxt, index) => 9.width, itemCount: 4, diff --git a/lib/widgets/shimmer/dashboard_shimmer_widget.dart b/lib/widgets/shimmer/dashboard_shimmer_widget.dart index c11cf16..7d59899 100644 --- a/lib/widgets/shimmer/dashboard_shimmer_widget.dart +++ b/lib/widgets/shimmer/dashboard_shimmer_widget.dart @@ -8,6 +8,8 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:shimmer/shimmer.dart'; class GetAttendanceTrackingShimmer extends StatelessWidget { + const GetAttendanceTrackingShimmer({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Container( @@ -85,6 +87,8 @@ class GetAttendanceTrackingShimmer extends StatelessWidget { } class MenuShimmer extends StatelessWidget { + const MenuShimmer({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Container( @@ -122,6 +126,8 @@ class MenuShimmer extends StatelessWidget { } class ServicesHeaderShimmer extends StatelessWidget { + const ServicesHeaderShimmer({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Row( @@ -145,6 +151,8 @@ class ServicesHeaderShimmer extends StatelessWidget { } class ServicesMenuShimmer extends StatelessWidget { + const ServicesMenuShimmer({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Container( @@ -188,6 +196,75 @@ class ServicesMenuShimmer extends StatelessWidget { } } +class GreetingCardShimmer extends StatelessWidget { + const GreetingCardShimmer({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: 100, + decoration: BoxDecoration( + color: const Color(0xFFFAF4E7), + border: Border.all(width: 2, color: const Color(0xFFE7E7E7)), + borderRadius: BorderRadius.circular(20), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 65, + height: 81, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(10), + ), + ).toShimmer(), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 150, + height: 16, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + ), + ).toShimmer(), + const SizedBox(height: 8), + Container( + width: 120, + height: 16, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + ), + ).toShimmer(), + ], + ).expanded, + Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + width: 60, + height: 12, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + ), + ).toShimmer(), + ], + ), + ], + ).paddingOnly(left: 8, right: 8, top: 6, bottom: 12), + ).paddingOnly(left: 21, right: 21, top: 14, bottom: 14); + } +} + + class MarathonBannerShimmer extends StatelessWidget { const MarathonBannerShimmer({Key? key}) : super(key: key); @@ -305,3 +382,4 @@ class ChatHomeShimmer extends StatelessWidget { )); } } + From 257b803cce78c5791fac4443167fb29d3838e915 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Mon, 2 Mar 2026 15:01:29 +0300 Subject: [PATCH 26/30] QIPS-1205 --- .../etqan_ovr/etqan_get_requests_model.dart | 12 ++ lib/provider/etqan_ovr_provider.dart | 13 +- lib/ui/etqan_ovr/etqan_ovr_home.dart | 67 +++++++- lib/ui/etqan_ovr/etqan_request_detailed.dart | 156 +++++++++++------- 4 files changed, 183 insertions(+), 65 deletions(-) diff --git a/lib/models/etqan_ovr/etqan_get_requests_model.dart b/lib/models/etqan_ovr/etqan_get_requests_model.dart index c59e171..ac4911b 100644 --- a/lib/models/etqan_ovr/etqan_get_requests_model.dart +++ b/lib/models/etqan_ovr/etqan_get_requests_model.dart @@ -24,12 +24,18 @@ class EtqanGetEmployeeOvrRequestsResponse { String? createdDate; String? description; String? id; + bool? isAnonymous; + String? status; + String? statusAr; String? ticketNumber; EtqanGetEmployeeOvrRequestsResponse({ this.createdDate, this.description, this.id, + this.isAnonymous, + this.status, + this.statusAr, this.ticketNumber, }); @@ -41,6 +47,9 @@ class EtqanGetEmployeeOvrRequestsResponse { createdDate: json["createdDate"], description: json["description"], id: json["id"], + isAnonymous: json["isAnonymous"], + status: json["status"], + statusAr: json["statusAr"], ticketNumber: json["ticketNumber"], ); @@ -48,6 +57,9 @@ class EtqanGetEmployeeOvrRequestsResponse { "createdDate": createdDate, "description": description, "id": id, + "isAnonymous": isAnonymous, + "status": status, + "statusAr": statusAr, "ticketNumber": ticketNumber, }; } diff --git a/lib/provider/etqan_ovr_provider.dart b/lib/provider/etqan_ovr_provider.dart index a7cb842..b4f2eb1 100644 --- a/lib/provider/etqan_ovr_provider.dart +++ b/lib/provider/etqan_ovr_provider.dart @@ -17,6 +17,9 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { List? getEtqanEmployeeIncidnetRequest; bool isLoading = false; String? _ticketId; + String? _currentStatus; + String? _currentStatusAr; + bool? _currentIsAnonymous; // Speech to text state final SpeechToText _speechToText = SpeechToText(); @@ -34,10 +37,16 @@ class EtqanOvrProviderModel with ChangeNotifier, DiagnosticableTreeMixin { // Getter for ticketId String get getTicketId => _ticketId ?? ''; + String get getCurrentStatus => _currentStatus ?? ''; + String get getCurrentStatusAr => _currentStatusAr ?? ''; + bool get getCurrentIsAnonymous => _currentIsAnonymous ?? false; - // Setter for ticketId - void setTicketId(String ticketId) { + // Setter for ticketId with status and isAnonymous + void setTicketId(String ticketId, {String? status, String? statusAr, bool? isAnonymous}) { _ticketId = ticketId; + _currentStatus = status; + _currentStatusAr = statusAr; + _currentIsAnonymous = isAnonymous; notifyListeners(); } diff --git a/lib/ui/etqan_ovr/etqan_ovr_home.dart b/lib/ui/etqan_ovr/etqan_ovr_home.dart index 80a27c6..b55b4c0 100644 --- a/lib/ui/etqan_ovr/etqan_ovr_home.dart +++ b/lib/ui/etqan_ovr/etqan_ovr_home.dart @@ -1,4 +1,3 @@ - import 'package:easy_localization/easy_localization.dart' as lclize; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; @@ -16,6 +15,7 @@ import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:provider/provider.dart'; import 'package:intl/src/intl/text_direction.dart' as intl; +import 'package:sizer/sizer.dart'; class EtqanOvrHome extends StatefulWidget { const EtqanOvrHome({Key? key}) : super(key: key); @@ -33,6 +33,18 @@ class _EtqanOvrHomeState extends State { }); } + Color _getStatusColor(String status) { + String statusLower = status.toLowerCase(); + if (statusLower.contains('open') || statusLower.contains('مفتوح')) { + return const Color(0xff2196F3); // Blue for open + } else if (statusLower.contains('progress') || statusLower.contains('قيد التنفيذ')) { + return const Color(0xffFF9800); // Orange for in progress + } else if (statusLower.contains('closed') || statusLower.contains('مغلق')) { + return const Color(0xff4CAF50); // Green for closed + } + return MyColors.grey70Color; // Default gray + } + @override Widget build(BuildContext context) { return Scaffold( @@ -51,7 +63,7 @@ class _EtqanOvrHomeState extends State { EtqanGetEmployeeOvrRequestsResponse data = provider.getEtqanEmployeeRequestsList![index]; return InkWell( onTap: () { - context.read().setTicketId(data.ticketNumber ?? ''); + context.read().setTicketId(data.ticketNumber ?? '', status: data.status, statusAr: data.statusAr, isAnonymous: data.isAnonymous); Navigator.pushNamed(context, AppRoutes.etqanGetIncidientRequest); }, child: Container( @@ -64,6 +76,50 @@ class _EtqanOvrHomeState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ + Row( + children: [ + Expanded( + child: Wrap( + spacing: 8, + runSpacing: 8, + children: [ + if (data.status != null && data.status!.isNotEmpty) + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: _getStatusColor(data.status ?? "").withOpacity(0.1), + borderRadius: BorderRadius.circular(6), + border: Border.all(color: _getStatusColor(data.status ?? "").withOpacity(0.3), width: 1), + ), + child: (AppState().isArabic(context) ? (data.statusAr ?? "") : (data.status ?? "")).toText11( + color: _getStatusColor(data.status ?? ""), + isBold: true, + ), + ), + if (data.isAnonymous == true) + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: MyColors.grey70Color.withOpacity(0.15), + borderRadius: BorderRadius.circular(6), + border: Border.all(color: MyColors.grey70Color.withOpacity(0.3), width: 1), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.visibility_off, size: 12, color: MyColors.grey70Color), + const SizedBox(width: 4), + (AppState().isArabic(context) ? "مجهول" : "Anonymous").toText11(color: MyColors.grey70Color, isBold: true), + ], + ), + ), + ], + ), + ), + const SizedBox(width: 8), + ], + ), + 8.height, Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -72,11 +128,12 @@ class _EtqanOvrHomeState extends State { ], ), 8.height, + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [(data.description ?? "").toText12(color: MyColors.grey57Color).expanded]), + 3.height, Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.end, children: [ - (data.description ?? "").toText12(color: MyColors.grey57Color).expanded, - const SizedBox(width: 5,), RotatedBox( quarterTurns: AppState().isArabic(context) ? 2 : 0, child: SvgPicture.asset("assets/images/arrow_next.svg", color: MyColors.grey70Color, width: 16, height: 16), diff --git a/lib/ui/etqan_ovr/etqan_request_detailed.dart b/lib/ui/etqan_ovr/etqan_request_detailed.dart index 6f9aebc..41d11c6 100644 --- a/lib/ui/etqan_ovr/etqan_request_detailed.dart +++ b/lib/ui/etqan_ovr/etqan_request_detailed.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; @@ -25,9 +26,21 @@ class EtqanOvrRequestDetailed extends StatefulWidget { } class _EtqanOvrRequestDetailedState extends State { - List get fieldOrder => ['Project', 'Created', 'Occurrence Time', 'OVR Reference No', 'Involved Employee Number', 'Description']; - - List get hiddenFields => ['OVR Id', 'Is Anonymous']; + List get fieldOrder => ['Status', 'Project', 'Created', 'Is Anonymous', 'Occurrence Time', 'OVR Reference No', 'Involved Employee Number', 'Description']; + + List get hiddenFields => ['OVR Id']; + + Color _getStatusColor(String status) { + String statusLower = status.toLowerCase(); + if (statusLower.contains('open') || statusLower.contains('مفتوح')) { + return const Color(0xff2196F3); // Blue for open + } else if (statusLower.contains('progress') || statusLower.contains('قيد التنفيذ')) { + return const Color(0xffFF9800); // Orange for in progress + } else if (statusLower.contains('closed') || statusLower.contains('مغلق')) { + return const Color(0xff4CAF50); // Green for closed + } + return MyColors.grey70Color; // Default gray + } Future _viewAttachment(BuildContext context, String base64Data) async { try { @@ -47,15 +60,33 @@ class _EtqanOvrRequestDetailedState extends State { } } - List _getOrderedList(List list) { + List _getOrderedList(List list, EtqanOvrProviderModel provider) { List orderedList = []; List filteredList = list.where((EtqanGetIncidentRequestResponse item) => !hiddenFields.any((f) => f.toLowerCase() == item.key?.toLowerCase())).toList(); for (String fieldName in fieldOrder) { - var item = filteredList.firstWhere((e) => e.key?.toLowerCase() == fieldName.toLowerCase(), orElse: () => EtqanGetIncidentRequestResponse()); - if (item.key != null) { - orderedList.add(item); + // Check if this is Status or Is Anonymous - inject from provider + if (fieldName.toLowerCase() == 'status' && provider.getCurrentStatus.isNotEmpty) { + orderedList.add(EtqanGetIncidentRequestResponse( + key: 'Status', + keyAr: 'الحالة', + value: provider.getCurrentStatus, + valueAr: provider.getCurrentStatusAr.isNotEmpty ? provider.getCurrentStatusAr : provider.getCurrentStatus, + )); + } else if (fieldName.toLowerCase() == 'is anonymous') { + orderedList.add(EtqanGetIncidentRequestResponse( + key: 'Is Anonymous', + keyAr: 'مجهول', + value: provider.getCurrentIsAnonymous.toString(), + valueAr: provider.getCurrentIsAnonymous.toString(), + )); + } else { + // Regular field from API + var item = filteredList.firstWhere((e) => e.key?.toLowerCase() == fieldName.toLowerCase(), orElse: () => EtqanGetIncidentRequestResponse()); + if (item.key != null) { + orderedList.add(item); + } } } @@ -88,7 +119,7 @@ class _EtqanOvrRequestDetailedState extends State { return Utils.getNoDataWidget(context); } - List orderedList = _getOrderedList(provider.getEtqanEmployeeIncidnetRequest!); + List orderedList = _getOrderedList(provider.getEtqanEmployeeIncidnetRequest!, provider); return SingleChildScrollView( physics: const BouncingScrollPhysics(), @@ -108,6 +139,8 @@ class _EtqanOvrRequestDetailedState extends State { EtqanGetIncidentRequestResponse data = entry.value; bool isLast = index == orderedList.length - 1; bool isAttachment = data.key?.toLowerCase() == 'attachment'; + bool isStatus = data.key?.toLowerCase() == 'status'; + bool isAnonymous = data.key?.toLowerCase() == 'is anonymous'; String displayKey = context.locale.languageCode == "ar" ? (data.keyAr ?? data.key ?? "") : (data.key ?? ""); String displayValue = context.locale.languageCode == "ar" ? (data.valueAr ?? data.value ?? "") : (data.value ?? ""); @@ -125,58 +158,65 @@ class _EtqanOvrRequestDetailedState extends State { 8.width, Expanded( flex: 3, - child: - isAttachment && hasSubValues - ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: - data.subValues!.asMap().entries.map((entry) { - int subIndex = entry.key; - String base64Data = entry.value; - return Padding( - padding: EdgeInsets.only(bottom: subIndex < data.subValues!.length - 1 ? 8.0 : 0), - child: GestureDetector( - onTap: () => _viewAttachment(context, base64Data), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: context.locale.languageCode == "ar" ? MainAxisAlignment.end : MainAxisAlignment.start, - children: [ - Text( - data.subValues!.length > 1 ? '${LocaleKeys.viewAttachment.tr()} ${subIndex + 1}' : LocaleKeys.viewAttachment.tr(), - // textAlign: context.locale.languageCode == "ar" ? TextAlign.start : TextAlign.start, - style: const TextStyle( - fontSize: 14, - color: MyColors.green9CColor, - decoration: TextDecoration.underline, - decorationColor: MyColors.green9CColor, + child: isStatus + ? displayValue.toText14( + color: _getStatusColor(displayValue), + isBold: true, + ) + : isAnonymous + ? (displayValue.toLowerCase() == 'true' || displayValue.toLowerCase() == 'yes' || displayValue == '1' + ? (context.locale.languageCode == "ar" ? "نعم" : "Yes") + : (context.locale.languageCode == "ar" ? "لا" : "No") + ).toText14(color: MyColors.darkTextColor) + : isAttachment && hasSubValues + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: data.subValues!.asMap().entries.map((entry) { + int subIndex = entry.key; + String base64Data = entry.value; + return Padding( + padding: EdgeInsets.only(bottom: subIndex < data.subValues!.length - 1 ? 8.0 : 0), + child: GestureDetector( + onTap: () => _viewAttachment(context, base64Data), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: context.locale.languageCode == "ar" ? MainAxisAlignment.end : MainAxisAlignment.start, + children: [ + Text( + data.subValues!.length > 1 ? '${LocaleKeys.viewAttachment.tr()} ${subIndex + 1}' : LocaleKeys.viewAttachment.tr(), + style: const TextStyle( + fontSize: 14, + color: MyColors.green9CColor, + decoration: TextDecoration.underline, + decorationColor: MyColors.green9CColor, + ), ), - ), - ], - ), - ), - ); - }).toList(), - ) - : hasSubValues - ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (displayValue.isNotEmpty) displayValue.toText14(color: MyColors.darkTextColor), - if (displayValue.isNotEmpty) 8.height, - ...data.subValues! - .map( - (subValue) => Padding( - padding: const EdgeInsets.only(bottom: 4), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [const Text("• ", style: TextStyle(fontSize: 14)), Expanded(child: subValue.toText14(color: MyColors.darkTextColor))], + ], + ), ), - ), - ) - .toList(), - ], - ) - : (displayValue.isNotEmpty ? displayValue : "-").toText14(color: MyColors.darkTextColor), + ); + }).toList(), + ) + : hasSubValues + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (displayValue.isNotEmpty) displayValue.toText14(color: MyColors.darkTextColor), + if (displayValue.isNotEmpty) 8.height, + ...data.subValues! + .map( + (subValue) => Padding( + padding: const EdgeInsets.only(bottom: 4), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [const Text("• ", style: TextStyle(fontSize: 14)), Expanded(child: subValue.toText14(color: MyColors.darkTextColor))], + ), + ), + ) + .toList(), + ], + ) + : (displayValue.isNotEmpty ? displayValue : "-").toText14(color: MyColors.darkTextColor), ), ], ), From eb3eb1d10275762096c32cbb1c1b67aa6ce16fe5 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Sun, 15 Mar 2026 12:34:04 +0300 Subject: [PATCH 27/30] updates --- lib/classes/consts.dart | 9 +++++---- lib/ui/landing/dashboard_screen.dart | 3 ++- lib/ui/landing/widget/menus_widget.dart | 27 ++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 2f60326..88ebf73 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -4,13 +4,14 @@ class ApiConsts { // static String baseUrl = "http://10.200.204.11"; // Local server // static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server - static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver + // static String baseUrl = "https://uat.hmgwebservices.com"; // UAT ser343622ver // static String baseUrl = "http://10.201.204.101:2024"; // static String baseUrl = "https://webservices.hmg.com"; // PreProd // static String baseUrl = "https://hmgwebservices.com"; // Live server - // static String baseUrl = "https://mohemm.hmg.com"; // New Live server + static String baseUrl = "https://mohemm.hmg.com"; // New Live server // + // static String baseUrl = "http://10.20.200.111:1010/"; // static String baseUrl = "https://webservices.hmg.com"; // PreProd @@ -26,8 +27,8 @@ class ApiConsts { static String user = baseUrlServices + "api/User/"; static String cocRest = baseUrlServices + "COCWS.svc/REST/"; - // static String ssoAuthRedirection = "https://sso-uat.hmg.com/api/auth/connect"; // UAT - static String ssoAuthRedirection = "https://sso.hmg.com/api/auth/connect"; // Prod + static String ssoAuthRedirection = "https://sso-uat.hmg.com/api/auth/connect"; // UAT + // static String ssoAuthRedirection = "https://sso.hmg.com/api/auth/connect"; // Prod //Chat static String chatServerBaseUrl = "https://apiderichat.hmg.com/"; diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index 3122d17..deb3131 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -167,8 +167,9 @@ class _DashboardScreenState extends State with WidgetsBindingOb // await data.fetchTicketBooking(); // if (data.ticketBookingResponse != null && !data.ticketBookingResponse!.success) { - // // data.fetchTicketBalance(); + // data.fetchTicketBalance(); // } + //continue here } diff --git a/lib/ui/landing/widget/menus_widget.dart b/lib/ui/landing/widget/menus_widget.dart index bc5dceb..e764dca 100644 --- a/lib/ui/landing/widget/menus_widget.dart +++ b/lib/ui/landing/widget/menus_widget.dart @@ -1,13 +1,17 @@ import 'package:easy_localization/src/public_ext.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:mohem_flutter_app/api/eit_api_client.dart'; import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/sso_auth_model.dart'; import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart'; +import 'package:mohem_flutter_app/ui/screens/ticket/ticket_detailed_screen.dart'; import 'package:mohem_flutter_app/widgets/shimmer/dashboard_shimmer_widget.dart'; import 'package:provider/provider.dart'; @@ -117,8 +121,29 @@ class MenusWidget extends StatelessWidget { ), ], ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), - ).onPress(() { + ).onPress(() async { Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(LocaleKeys.ticketBalance.tr(), "HMG_TKT_NEW_EIT_SS")); + + // var pro = Provider.of(context, listen: false); + // + // // if (menuEntry.menuName == "HMG_TICKET_REQUESTS") { + // Utils.showLoading(context); + // //Ticket Work + // if (pro.ticketBookingResponse != null && pro.ticketBookingResponse!.success) { + // SSOAuthModel? ssoToken = await pro.fetchSSOAuthRedirection(clientID: pro.ticketBookingResponse!.clientId); + // if (ssoToken != null) { + // dynamic url = await pro.fetchURLRedirection(token: ssoToken.data!.accessToken!); + // await pro.fetchTicketAccuralBalance(context, DateTime.now()); + // pro.ticketHistoryTransactionList = await EITApiClient().getEITTransactions("HMG_TICKET_ITENARY_HR_EIT_SS", isCompleteList: true); + // + // Utils.hideLoading(context); + // // Here Need Work + // Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => TicketDetailedScreen(url: url, jwtToken: ssoToken.data!.accessToken))); + // } + // //} + // } else { + // Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(LocaleKeys.ticketBalance.tr(), "HMG_TKT_NEW_EIT_SS")); + // } }), ], ); From 3343e4fe109494b60fe436fa1135ae5bf9c5095d Mon Sep 17 00:00:00 2001 From: Sultan khan Date: Sun, 15 Mar 2026 12:46:43 +0300 Subject: [PATCH 28/30] sso and courses in app browser updated --- .../dashboard/courses_response_model.dart | 5 +- .../itg/its_add_screen_video_image.dart | 19 ++++++- lib/ui/screens/courses/courses_screen.dart | 55 ++++++++++--------- lib/widgets/sso_webview_widget.dart | 9 ++- 4 files changed, 57 insertions(+), 31 deletions(-) diff --git a/lib/models/dashboard/courses_response_model.dart b/lib/models/dashboard/courses_response_model.dart index 63a98fc..730d4a4 100644 --- a/lib/models/dashboard/courses_response_model.dart +++ b/lib/models/dashboard/courses_response_model.dart @@ -49,7 +49,7 @@ class CourseStatus { String? cStartdate; String? cEnddate; String? cUrl; - + String? token; CourseStatus({ this.cId, this.cFullname, @@ -60,6 +60,7 @@ class CourseStatus { this.cStartdate, this.cEnddate, this.cUrl, + this.token }); CourseStatus.fromJson(Map json) { @@ -72,6 +73,7 @@ class CourseStatus { cStartdate = json['c_startdate']; cEnddate = json['c_enddate']; cUrl = json['c_url']; + token =json['c_token']; } Map toJson() { @@ -85,6 +87,7 @@ class CourseStatus { data['c_startdate'] = cStartdate; data['c_enddate'] = cEnddate; data['c_url'] = cUrl; + data['c_token'] = token; return data; } } 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 ab0d39f..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'; @@ -396,8 +401,18 @@ class _ITGAdsScreenState extends State { 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 { + + 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) { diff --git a/lib/ui/screens/courses/courses_screen.dart b/lib/ui/screens/courses/courses_screen.dart index 7c995a2..598b6cf 100644 --- a/lib/ui/screens/courses/courses_screen.dart +++ b/lib/ui/screens/courses/courses_screen.dart @@ -14,6 +14,7 @@ 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'; @@ -148,7 +149,7 @@ class _CoursesScreenState extends State { Color statusColor = _getStatusColor(status); return InkWell( - onTap: () => _openCourse(course.cUrl ?? ''), + onTap: () => _openCourse(course.cUrl, course.token), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -291,33 +292,37 @@ class _CoursesScreenState extends State { return format.format(dateTime); } - void _openCourse(String url) { - if (url.isEmpty) { - Utils.showToast(LocaleKeys.courseUrlNotAvailable.tr()); - return; - } + void _openCourse(String? url, String? jwtToken) { - 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, - ), - ); + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: url, jwtToken: jwtToken))); + //Utils.showToast(LocaleKeys.courseUrlNotAvailable.tr()); - browser.openUrlRequest( - urlRequest: URLRequest(url: WebUri(url)), - settings: settings, - ); + + // 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 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))); } } From bdaccf133c2273324b5f5c398ebc2712658e9dfd Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Sun, 15 Mar 2026 16:04:06 +0300 Subject: [PATCH 29/30] updates --- assets/images/eid_banner.svg | 606 +++++++++++++++++++++ lib/provider/dashboard_provider_model.dart | 2 +- lib/ui/landing/dashboard_screen.dart | 6 +- 3 files changed, 610 insertions(+), 4 deletions(-) create mode 100644 assets/images/eid_banner.svg diff --git a/assets/images/eid_banner.svg b/assets/images/eid_banner.svg new file mode 100644 index 0000000..f8e4adb --- /dev/null +++ b/assets/images/eid_banner.svg @@ -0,0 +1,606 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index e9c6678..2b7a2bb 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -137,7 +137,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/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), ]; diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index 0ff22ae..eac7890 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -450,8 +450,8 @@ class _DashboardScreenState extends State with WidgetsBindingOb flipX: !AppState().isArabic(context), child: SvgPicture.network( AppState().isArabic(context) - ? (card.backgroundImageUrlAr ?? "http://meena-health-care-1.s3.eu-north-1.amazonaws.com/eid_banner.svg") - : (card.backgroundImageUrlEn ?? "http://meena-health-care-1.s3.eu-north-1.amazonaws.com/eid_banner.svg"), + ? (card.backgroundImageUrlAr ?? "https://www.hmg.com/Lists/CS_Greeting_Cards/Attachments/5/CS_Ramadan_26.jpg") + : (card.backgroundImageUrlEn ?? "https://www.hmg.com/Lists/CS_Greeting_Cards/Attachments/5/CS_Ramadan_26.jpg"), fit: BoxFit.contain, placeholderBuilder: (context) => ClipRRect(borderRadius: BorderRadius.circular(18), child: Container(height: 80, color: Colors.white).toShimmer()), ), @@ -460,7 +460,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SizedBox(width: AppState().isArabic(context) ? 10 : 40), + SizedBox(width: AppState().isArabic(context) ? 20 : 50), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, From 480d9d466fa269443769f96ebd0a289e5da9a466 Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Sun, 15 Mar 2026 16:34:08 +0300 Subject: [PATCH 30/30] updates --- lib/app_state/app_state.dart | 2 +- .../offers_and_discounts/offers_and_discounts_details.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/app_state/app_state.dart b/lib/app_state/app_state.dart index 799bcb5..703a8b0 100644 --- a/lib/app_state/app_state.dart +++ b/lib/app_state/app_state.dart @@ -90,7 +90,7 @@ class AppState { String get getHuaweiPushToken => _huaweiPushToken; - final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 9.5, mobileType: Platform.isAndroid ? "android" : "ios"); + final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 9.6, mobileType: Platform.isAndroid ? "android" : "ios"); void setPostParamsInitConfig() { isAuthenticated = false; diff --git a/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart b/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart index be0582c..846c52f 100644 --- a/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart +++ b/lib/ui/screens/offers_and_discounts/offers_and_discounts_details.dart @@ -70,7 +70,7 @@ class _OffersAndDiscountsDetailsState extends State { : getOffersList[0].titleEn!.toText22(isBold: true, color: const Color(0xff2B353E)).center, Html( data: AppState().isArabic(context) ? getOffersList[0].descriptionAr! : getOffersList[0].descriptionEn ?? "", - onLinkTap: (String? url, Map attributes, _) { + onLinkTap: (String? url, context, Map attributes, _) { launchUrl(Uri.parse(url!)); }, ),