From 99414bf787122b2610936699cc7ac110723fc505 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Wed, 7 Jan 2026 14:57:25 +0300 Subject: [PATCH 01/18] 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 02/18] 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 9112b269874f7a0c317dde02488ce86c5a2a5dfb Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Tue, 27 Jan 2026 18:06:36 +0300 Subject: [PATCH 03/18] 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 04/18] 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 05/18] 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 06/18] 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 eea35e4d77a4373f9582a93fed0b2de32e23557e Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Sun, 15 Feb 2026 15:09:38 +0300 Subject: [PATCH 07/18] 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 08/18] 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 09/18] 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 10/18] 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 11/18] 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 12/18] 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 9f663df3cce393a4941613b664201f0b81aa8849 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Sun, 1 Mar 2026 13:48:29 +0300 Subject: [PATCH 13/18] 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 14/18] 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 15/18] 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 16/18] 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 257b803cce78c5791fac4443167fb29d3838e915 Mon Sep 17 00:00:00 2001 From: aamir-csol Date: Mon, 2 Mar 2026 15:01:29 +0300 Subject: [PATCH 17/18] 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 18/18] 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")); + // } }), ], );