diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 132ad69..6d2f83f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,13 +8,13 @@ - + - + diff --git a/android/gradle.properties b/android/gradle.properties index fbdc0de..8df3fe9 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,5 @@ -org.gradle.jvmargs=-Xmx2048M +org.gradle.jvmargs=-Xmx4096m +kotlin.daemon.jvmargs=-Xmx4096m android.useAndroidX=true android.enableJetifier=true apmsInstrumentationEnabled=false diff --git a/assets/images/drawer/car_parking_icon.svg b/assets/images/drawer/car_parking_icon.svg new file mode 100644 index 0000000..f77afa0 --- /dev/null +++ b/assets/images/drawer/car_parking_icon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/drawer/courses.svg b/assets/images/drawer/courses.svg new file mode 100644 index 0000000..d15e742 --- /dev/null +++ b/assets/images/drawer/courses.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/assets/images/greeting.svg b/assets/images/greeting.svg new file mode 100644 index 0000000..0682c60 --- /dev/null +++ b/assets/images/greeting.svg @@ -0,0 +1,605 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/ticket_bal.svg b/assets/images/ticket_bal.svg new file mode 100644 index 0000000..8fadde8 --- /dev/null +++ b/assets/images/ticket_bal.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/vacation_rule.svg b/assets/images/vacation_rule.svg new file mode 100644 index 0000000..0a536f7 --- /dev/null +++ b/assets/images/vacation_rule.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/assets/images/worklist/approve.svg b/assets/images/worklist/approve.svg new file mode 100644 index 0000000..8d62ae9 --- /dev/null +++ b/assets/images/worklist/approve.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/worklist/reject.svg b/assets/images/worklist/reject.svg new file mode 100644 index 0000000..cb5da34 --- /dev/null +++ b/assets/images/worklist/reject.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 08b00da..f3552d5 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -164,6 +164,7 @@ "quotationAnalysis": "تحليل الاقتباس", "subject": "موضوعات", "description": "وصف", + "pleaseEnterDescription": "الرجاء إدخال الوصف", "supplier": "المورد", "site": "موقع", "buyer": "مشتر", @@ -258,6 +259,7 @@ "fullName": "الأسم الكامل", "remove": "حذف", "submit": "ارسال", + "submitAsAnonymous": "إرسال كمجهول", "areYouSureYouWantToSubmit": "هل أنت متأكد أنك تريد أن تقدم؟", "comments": "تعليقات", "writeComment": "أكتب تعليقا", @@ -531,7 +533,7 @@ "noWinner": "حزين! لم يفز أحد اليوم.", "myTeam": "فريقي", "youCanPlayDemo": "لكن يمكنك لعب العرض", - "group" : "مجموعة", + "group": "مجموعة", "searchGroup": "مجموعة البحث", "connectHmgWifi": "قم بتوصيل HMG WIFI", "connectedHmgWifi": "اتصال HMG WIFI", @@ -575,20 +577,20 @@ "versionStatus": "حالة الإصدار", "supplierNo": "رقم المورد", "general": "عام", - "requesterOperatingUnit":"وحدة تشغيل مقدم الطلب", - "prepareEmpNum":"إعداد رقم الموظف", - "supplierInfo" : "معلومات المورد", + "requesterOperatingUnit": "وحدة تشغيل مقدم الطلب", + "prepareEmpNum": "إعداد رقم الموظف", + "supplierInfo": "معلومات المورد", "supplierAcNo": "رقم حساب المورد", "supplierAcName": "اسم حساب المورد", - "supplierIBAN" : "رقم IBAN للمورد", - "supplierCRNo" :"رقم السجل التجاري", - "suppliedAcNo" : "رقم الحساب المقدم", - "patientRefundInvoice" : "فاتورة استرداد الأموال للمريض", - "patientNumber" : "رقم المريض", - "patientName" : "اسم المريض", - "invoiceDate" : "تاريخ الفاتورة", - "refundInvoice" :"فاتورة الاسترجاع", - "hospitalClinic" : "عيادة المستشفى", + "supplierIBAN": "رقم IBAN للمورد", + "supplierCRNo": "رقم السجل التجاري", + "suppliedAcNo": "رقم الحساب المقدم", + "patientRefundInvoice": "فاتورة استرداد الأموال للمريض", + "patientNumber": "رقم المريض", + "patientName": "اسم المريض", + "invoiceDate": "تاريخ الفاتورة", + "refundInvoice": "فاتورة الاسترجاع", + "hospitalClinic": "عيادة المستشفى", "graphicalAnalysis": "التحليل الرسومي", "itemHistoryAnalysis": "تحليل تاريخ العنصر", "pOno": "امر شراء #", @@ -614,11 +616,60 @@ "members": "الأعضاء", "searchByUserName": "البحث بواسطة اسم المستخدم", "shareScreen": "مشاركة الشاشة", - "start":"يبدأ", - "about":"عن", + "start": "يبدأ", + "about": "عن", "explore": "يستكشف", "mazaya": "مازيا", "benefits": "فوائد", "mazayaDesc": "اكتشف الخصومات والعروض الخاصة المتاحة للموظفين", - "viewallofferMazaya" : "أعرض كل المزايا" -} \ No newline at end of file + "viewallofferMazaya": "أعرض كل المزايا", + "buyerName": "اسم المشتري", + "buyerNumber": "رقم المشتري", + "highestBidder": "أعلى مزايد", + "remarks": "ملاحظات", + "faHeader": "تفاصيل رأس الصفحة FA", + "bookTypeCode": "رمز نوع الكتاب", + "categoryCode": "رمز الفئة", + "categoryGroup": "مجموعة الفئات", + "faLINES": "تفاصيل الأصول", + "assetNumber": "رقم الأصول", + "assetDescription": "وصف الأصول", + "barCodeNumber": "رقم الباركود", + "datePlaceInService": "تاريخ وضعه في الخدمة", + "serialNumber": "رقم سري", + "disposedDate": "تاريخ التخلص", + "netBookValue": "القيمة الدفترية الصافية", + "purchasedPrice": "سعر الشراء", + "usefulLife": "العمر الإنتاجي", + "yearsUsed": "سنوات الاستخدام", + "faRequest": "طلب التخلص من FA", + "showMore": "عرض المزيد", + "ticketDetails": "تفاصيل التذكرة", + "currentTicketBalance": "رصيد التذكرة الحالي", + "ticketHistory": "تاريخ التذكرة", + "proceedBooking": "المتابعة للحجز", + "courses": "الدورات التدريبية", + "course": "دورة", + "status": "الحالة", + "deadline": "الموعد النهائي", + "overdue": "متأخر", + "noCoursesAvailable": "لا توجد دورات متاحة", + "pullToRefresh": "اسحب للتحديث", + "courseUrlNotAvailable": "رابط الدورة غير متوفر", + "buyerDetails": "تفاصيل المشتري", + "etqanOVR": "الإبلاغ عن حادثة", + "etqanRequest": "طلبات الحوادث", + "selectProject": "حدد المشروع", + "project": "مشروع", + "keen": "حريص", + "reportIncident": "الإبلاغ عن الحادث", + "myIncidents": "حوادثي", + "incidentDetails": "تفاصيل الحادث", + "ticketNumber": "رقم التذكرة", + "ticketId": "معرف التذكرة", + "pleaseSelectProject": "الرجاء اختيار مشروع", + "viewAttachment" : "عرض المرفق", + "parkingQr": "وقوف السيارات QR", + "startNow": "ابدأ الآن" +} + diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index ff30994..a227a73 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", @@ -256,6 +257,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", @@ -565,7 +567,7 @@ "addAtLeastOneAttachment": "Please add at least one attachment.", "pleaseClickButtonToJoinMarathon": "Press the button below to join the Marathon.", "youCannotJoinTheMarathon": "You cannot join the Marathon because you have exceeded the time limit.", - "requesterOperatingUnit":"Requester Operating Unit", + "requesterOperatingUnit": "Requester Operating Unit", "generate": "Generate", "paymentRequest": "Pay Request", "paymentDetails": "Payment Details", @@ -591,19 +593,19 @@ "versionStatus": "Version Status", "supplierNo": "Supplier No", "general": "General", - "prepareEmpNum":"Prepare Employee Num", - "supplierInfo" : "Supplier Information", + "prepareEmpNum": "Prepare Employee Num", + "supplierInfo": "Supplier Information", "supplierAcNo": "Supplier Account No", - "supplierAcName":"Supplier Account Name", - "supplierIBAN" : "Supplier IBAN", - "supplierCRNo" : "CR Number", - "suppliedAcNo" : "Supplied Account No.", - "patientRefundInvoice" : "Patient Refund Invoice", - "patientNumber" : "Patient Number", - "patientName" : "Patient Name", - "invoiceDate" : "Invoice Date", - "refundInvoice" :"Refund Invoice", - "hospitalClinic" : "Hospital Clinic", + "supplierAcName": "Supplier Account Name", + "supplierIBAN": "Supplier IBAN", + "supplierCRNo": "CR Number", + "suppliedAcNo": "Supplied Account No.", + "patientRefundInvoice": "Patient Refund Invoice", + "patientNumber": "Patient Number", + "patientName": "Patient Name", + "invoiceDate": "Invoice Date", + "refundInvoice": "Refund Invoice", + "hospitalClinic": "Hospital Clinic", "graphicalAnalysis": "Graphical Analysis", "itemHistoryAnalysis": "Item History Analysis", "pOno": "P.O #", @@ -612,12 +614,59 @@ "qtyReceived": "Qty. Received", "bonusQty": "Bonus Qty.", "balQty": "Bal. Qty.", - "start":"Start", - "about":"About", + "start": "Start", + "about": "About", "explore": "Explore", "mazaya": "MAZAYA", "benefits": "Benefits", "mazayaDesc": "Discover special Discounts and offers available to Employees", - "viewallofferMazaya" : "View All Offers" - + "viewallofferMazaya": "View All Offers", + "buyerName": "Buyer Name", + "buyerNumber": "Buyer Number", + "highestBidder": "Highest Bidder", + "remarks": "Remarks", + "faHeader": "FA Header Details", + "bookTypeCode": "Book Type Code", + "categoryCode": "Category Code", + "categoryGroup": "Category Group", + "faLINES": "Asset Details", + "assetNumber": "Asset Number", + "assetDescription": "Asset Description", + "barCodeNumber": "BarCode Number", + "datePlaceInService": "Date place In Service", + "serialNumber": "Serial Number", + "disposedDate": "Disposed Date", + "netBookValue": "Net Book Value", + "purchasedPrice": "Purchased Price", + "usefulLife": "Useful Life", + "yearsUsed": "Years Used", + "faRequest": "FA Disposal Request", + "showMore": "Show More", + "ticketDetails": "Ticket Details", + "currentTicketBalance": "Current Ticket Balance", + "ticketHistory": "Ticket History", + "proceedBooking": "Proceed For Booking", + "courses": "Courses", + "course": "Course", + "status": "Status", + "deadline": "Deadline", + "overdue": "Overdue", + "noCoursesAvailable": "No Courses Available", + "pullToRefresh": "Pull to refresh", + "courseUrlNotAvailable": "Course URL not available", + "buyerDetails": "Buyer Details", + "etqanOVR": "Report An Incident", + "etqanRequest": "Incident Requests", + "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", + "viewAttachment" : "View Attachment", + "parkingQr": "Parking QR", + "startNow": "Start Now" } \ No newline at end of file diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 01197de..51a5801 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 + NSSpeechRecognitionUsageDescription + This app requires speech recognition access to access voice command features. UIBackgroundModes fetch diff --git a/lib/api/api_client.dart b/lib/api/api_client.dart index 74a12c8..7e346c8 100644 --- a/lib/api/api_client.dart +++ b/lib/api/api_client.dart @@ -112,7 +112,7 @@ class ApiClient { } else if (jsonData["MessageStatus"] == 2 && jsonData["IsOTPMaxLimitExceed"] == true) { await Utils.performLogout(AppRoutes.navigatorKey.currentContext, null); throw const APIException(APIException.UNAUTHORIZED, error: null); - } else { + } else { APIError? apiError; apiError = APIError(jsonData['ErrorCode'], jsonData['ErrorEndUserMessage'], jsonData['ErrorType'] ?? 0, jsonData['ErrorStatusCode']); throw APIException(APIException.BAD_REQUEST, error: apiError); diff --git a/lib/api/dashboard_api_client.dart b/lib/api/dashboard_api_client.dart index de71e7f..acdf413 100644 --- a/lib/api/dashboard_api_client.dart +++ b/lib/api/dashboard_api_client.dart @@ -15,13 +15,15 @@ import 'package:mohem_flutter_app/models/dashboard/get_attendance_tracking_list_ import 'package:mohem_flutter_app/models/dashboard/itg_forms_model.dart'; import 'package:mohem_flutter_app/models/dashboard/list_menu.dart'; import 'package:mohem_flutter_app/models/eit_attachment_ticket_model.dart'; +import 'package:mohem_flutter_app/models/dashboard/courses_response_model.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart'; +import 'package:mohem_flutter_app/models/get_employee_parking_details_model.dart'; +import 'package:mohem_flutter_app/models/greetings/greeting_card_model.dart'; import 'package:mohem_flutter_app/models/itg/itg_main_response.dart'; import 'package:mohem_flutter_app/models/itg/itg_response_model.dart'; import 'package:mohem_flutter_app/models/sso_auth_model.dart'; // import 'package:platform_device_id/platform_device_id.dart'; -// import 'package:platform_device_id/platform_device_id.dart'; import 'package:uuid/uuid.dart'; class DashboardApiClient { @@ -272,13 +274,17 @@ class DashboardApiClient { String QRValue = "", String payrollCode = "", }) async { + var _mobileDeviceIdentifierPlugin = MobileDeviceIdentifier(); + Codec stringToBase64 = utf8.fuse(base64); String url = "${ApiConsts.swpRest}AuthenticateAndSwipeUserSupportNFC"; Uuid uuid = Uuid(); String? deviceId = ""; // Generate a v4 (random) id if (Platform.isAndroid) { AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo; - deviceId = androidInfo.id; + deviceId = await _mobileDeviceIdentifierPlugin.getDeviceId() ?? androidInfo.id; + deviceId = stringToBase64.encode(deviceId); + deviceId = deviceId.length >= 50 ? deviceId.substring(0, 48) : deviceId; } else if (Platform.isIOS) { IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo; deviceId = iosInfo.identifierForVendor; @@ -428,4 +434,57 @@ class DashboardApiClient { // postParams, // ); // } + + Future getEmployeeParkingQrCode() async { + String url = "${ApiConsts.erpRest}GET_Employee_Parking"; + Map postParams = {}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject( + (json) { + GetEmployeeParking responseData = GetEmployeeParking.fromJson(json); + return responseData; + }, + url, + postParams, + ); + } + + //Fetch All Courses Status + Future fetchAllCoursesStatus() async { + String url = ApiConsts.eLearningBaseUrl +"Fetch_All_Courses_Status.php"; + + Map headers = { + 'x-api-key': 'bLi@mbXJeXTHd/)h&LFh%25+%25(Nbnaq6hBg%7d%5dyQbthY%7bv6ew6-5UT\$NASwucn%7d,_PSJpuwNCCen2%7djj%7b00HR2T-%5b,k7W%7d-0yepK?%258', + 'Content-Type': 'application/x-www-form-urlencoded', + }; + + Map postParams = { + "username": AppState().memberInformationList?.eMPLOYEENUMBER ?? "", + }; + + return await ApiClient().postJsonForObject( + (json) { + CoursesResponseModel responseData = CoursesResponseModel.fromJson(json); + return responseData; + }, + url, + postParams, + headers: headers, + isFormData: false, + ); + } + + Future getGreetingCards() async { + String url = "${ApiConsts.erpRest}Get_GreetingCards"; + Map postParams = {}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject( + (json) { + GetGreetingCardsModelResponse responseData = GetGreetingCardsModelResponse.fromJson(json); + return responseData; + }, + url, + postParams, + ); + } } diff --git a/lib/api/etqan_ovr_api_client.dart b/lib/api/etqan_ovr_api_client.dart new file mode 100644 index 0000000..f7512cc --- /dev/null +++ b/lib/api/etqan_ovr_api_client.dart @@ -0,0 +1,140 @@ +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'; +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) { + 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 "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 dateB.compareTo(dateA); // Descending order (newest first) + } catch (e) { + return (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, bool? isAnonymous) 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": 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 {"id": response["id"], "ticketNumber": response["ticketNumber"]}; + } + return null; + } + return null; + + // "ETQAN_CreateIncident_Response": { + // "id": "00adabe4-4a32-4b35-1f00-08de4d152e7c", + // "ticketNumber": "OVR/OLY/2026/224" + // }, + }, + url, + postParams, + ); + } +} diff --git a/lib/api/worklist/worklist_api_client.dart b/lib/api/worklist/worklist_api_client.dart index 51a47bb..10d9472 100644 --- a/lib/api/worklist/worklist_api_client.dart +++ b/lib/api/worklist/worklist_api_client.dart @@ -28,6 +28,7 @@ import 'package:mohem_flutter_app/models/notification_get_respond_attributes_lis import 'package:mohem_flutter_app/models/termination/termination_notification_body.dart'; import 'package:mohem_flutter_app/models/update_user_item_type_list.dart'; import 'package:mohem_flutter_app/models/worklist/GetRFCEmployeeList.dart'; +import 'package:mohem_flutter_app/models/worklist/get_fad_notifications.dart'; import 'package:mohem_flutter_app/models/worklist/get_favorite_replacements_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/eit_otification_body_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_address_notification_body_list.dart'; @@ -216,9 +217,24 @@ class WorkListApiClient { ); } - Future getUserInformation(int pSelectedResopID, String selectedEmployeeNumber) async { - String url = "${ApiConsts.erpRest}Get_UserInformation"; - Map postParams = {"P_SELECTED_RESP_ID": pSelectedResopID, "P_PAGE_LIMIT": 100, "P_PAGE_NUM": 1}; + // Future getUserInformation(int pSelectedResopID, String selectedEmployeeNumber) async { + // String url = "${ApiConsts.erpRest}Get_UserInformation"; + // Map postParams = {"P_SELECTED_RESP_ID": pSelectedResopID, "P_PAGE_LIMIT": 100, "P_PAGE_NUM": 1}; + // postParams.addAll(AppState().postParamsJson); + // if (selectedEmployeeNumber != null) postParams["P_SELECTED_EMPLOYEE_NUMBER"] = selectedEmployeeNumber; + // return await ApiClient().postJsonForObject( + // (json) { + // GenericResponseModel responseData = GenericResponseModel.fromJson(json); + // return responseData.memberInformationList![0]; + // }, + // url, + // postParams, + // ); + // } + + Future getNotificationUserInformation(int pSelectedResopID, String selectedEmployeeNumber, int pNotificationID) async { + String url = "${ApiConsts.erpRest}Get_Notification_UserInformation"; + Map postParams = {"P_SELECTED_RESP_ID": pSelectedResopID, "P_PAGE_LIMIT": 100, "P_PAGE_NUM": 1, "P_NOTIFICATION_ID": pNotificationID}; postParams.addAll(AppState().postParamsJson); if (selectedEmployeeNumber != null) postParams["P_SELECTED_EMPLOYEE_NUMBER"] = selectedEmployeeNumber; return await ApiClient().postJsonForObject( @@ -831,4 +847,19 @@ class WorkListApiClient { postParams, ); } + + + Future getFADNotificationBody(int? notificationId) async { + String url = "${ApiConsts.erpRest}GetFADisposalNtfDetails"; + Map postParams = {"P_NOTIFICATION_ID": notificationId, "P_PAGE_LIMIT": 100, "P_PAGE_NUM": 1}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject( + (json) { + GenericResponseModel responseData = GenericResponseModel.fromJson(json); + return responseData.getFADisposalNtfDetails; + }, + url, + postParams, + ); + } } diff --git a/lib/app_state/app_state.dart b/lib/app_state/app_state.dart index e68e169..799bcb5 100644 --- a/lib/app_state/app_state.dart +++ b/lib/app_state/app_state.dart @@ -90,7 +90,7 @@ class AppState { String get getHuaweiPushToken => _huaweiPushToken; - final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 9.3, mobileType: Platform.isAndroid ? "android" : "ios"); + final PostParamsModel _postParamsInitConfig = PostParamsModel(channel: 31, versionID: 9.5, mobileType: Platform.isAndroid ? "android" : "ios"); void setPostParamsInitConfig() { isAuthenticated = false; diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 5e30acc..c20cdbc 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -5,18 +5,23 @@ class ApiConsts { // static String baseUrl = "https://erptstapp.srca.org.sa"; // SRCA server // 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 = "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 ssoBaseUrl = "https://sso.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://mohemm.hmg.com"; // New Live server + //static String eLearningBaseUrl = "https://elearning.hmg.com/moodle_dev/api/"; // Elearning server UAT + static String eLearningBaseUrl = "https://elearning.hmg.com/moodle/api/"; // Elearning server live - // static String baseUrl = "https://webservices.hmg.com"; // PreProd - // static String baseUrl = "https://mohemm.hmg.com"; + // static String baseUrl = "http://10.20.200.111:1010/"; + + // static String baseUrl = "https://webservices.hmg.com"; // PreProd + // static String baseUrl = "https://mohemm.hmg.com"; // static String baseUrl = "https://hmgwebservices.com"; // Live server static String baseUrlServices = baseUrl + "/Services/"; // server @@ -28,9 +33,9 @@ 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 = ssoBaseUrl + "/api/auth/connect"; + //Chat static String chatServerBaseUrl = "https://apiderichat.hmg.com/"; static String chatServerBaseApiUrl = chatServerBaseUrl + "api/"; 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/config/routes.dart b/lib/config/routes.dart index 4653880..9bae8df 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'; @@ -40,6 +43,7 @@ import 'package:mohem_flutter_app/ui/my_team/profile_details.dart'; import 'package:mohem_flutter_app/ui/my_team/subordinate_leave.dart'; import 'package:mohem_flutter_app/ui/my_team/team_members.dart'; import 'package:mohem_flutter_app/ui/my_team/view_attendance.dart'; +import 'package:mohem_flutter_app/ui/parking_qr/parking_qr.dart'; import 'package:mohem_flutter_app/ui/payslip/monthly_pay_slip_screen.dart'; import 'package:mohem_flutter_app/ui/performance/performance_appraisal_screen.dart'; import 'package:mohem_flutter_app/ui/profile/add_update_family_member.dart'; @@ -54,6 +58,7 @@ import 'package:mohem_flutter_app/ui/profile/profile_screen.dart'; import 'package:mohem_flutter_app/ui/screens/announcements/announcement_details.dart'; import 'package:mohem_flutter_app/ui/screens/announcements/announcements.dart'; import 'package:mohem_flutter_app/ui/screens/child_education/child_education_assistance.dart'; +import 'package:mohem_flutter_app/ui/screens/courses/courses_screen.dart'; // import 'package:mohem_flutter_app/ui/my_attendance/work_from_home_screen.dart'; import 'package:mohem_flutter_app/ui/screens/eit/add_eit.dart'; import 'package:mohem_flutter_app/ui/screens/event_activity/event_activity.dart'; @@ -166,6 +171,9 @@ class AppRoutes { static const String offersAndDiscounts = "/offersAndDiscounts"; static const String offersAndDiscountsDetails = "/offersAndDiscountsDetails"; + // Courses + static const String courses = "/courses"; + //Pay slip static const String monthlyPaySlip = "/monthlyPaySlip"; @@ -211,6 +219,13 @@ class AppRoutes { static const String activityScreen = "/activityScreen"; + static const String etqanOvr = "/etqanOvr"; + static const String etqanCreateRequest = "/etqanCreateRequest"; + static const String etqanGetIncidientRequest = "/etqanGetIncidientRequest"; + + //Parking QR + static const String parkingQr = "/parkingQrScreen"; + static final Map routes = { login: (BuildContext context) => LoginScreen(), verifyLogin: (BuildContext context) => VerifyLoginScreen(), @@ -291,6 +306,9 @@ class AppRoutes { offersAndDiscounts: (BuildContext context) => OffersAndDiscountsHome(), offersAndDiscountsDetails: (BuildContext context) => OffersAndDiscountsDetails(), + // Courses + courses: (BuildContext context) => const CoursesScreen(), + //pay slip monthlyPaySlip: (BuildContext context) => MonthlyPaySlipScreen(), performanceEvaluation: (BuildContext context) => PerformanceAppraisal(), @@ -328,6 +346,16 @@ 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(), + activityScreen:(BuildContext context) => const EventActivityScreen(), + + //Parking QR + parkingQr: (BuildContext context) => const ParkingQrScreen(), + }; } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 91ded82..93a6cf8 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -81,28 +81,30 @@ extension EmailValidator on String { ), ); - Widget toText12({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0}) => Text( + Widget toText12({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int? maxLine, TextOverflow? overflow}) => Text( this, textAlign: isCenter ? TextAlign.center : null, - maxLines: (maxLine > 0) ? maxLine : null, + maxLines: maxLine == 0 ? null : maxLine, style: TextStyle( fontSize: 12, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, letterSpacing: -0.72, decoration: isUnderLine ? TextDecoration.underline : null, + overflow: TextOverflow.visible ), ); - Widget toText12Auto({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int maxLine = 0}) => AutoSizeText( + Widget toText12Auto({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, int? maxLine,TextOverflow? overflow}) => AutoSizeText( this, textAlign: isCenter ? TextAlign.center : null, - maxLines: (maxLine > 0) ? maxLine : null, + maxLines: maxLine == 0 ? null : maxLine, minFontSize: 8, style: TextStyle( fontSize: 12, fontWeight: isBold ? FontWeight.bold : FontWeight.w600, color: color ?? MyColors.darkTextColor, + overflow: overflow, letterSpacing: -0.72, decoration: isUnderLine ? TextDecoration.underline : null, ), diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index f3fda6a..f186a22 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": "مشتر", @@ -274,6 +275,7 @@ class CodegenLoader extends AssetLoader{ "fullName": "الأسم الكامل", "remove": "حذف", "submit": "ارسال", + "submitAsAnonymous": "إرسال كمجهول", "areYouSureYouWantToSubmit": "هل أنت متأكد أنك تريد أن تقدم؟", "comments": "تعليقات", "writeComment": "أكتب تعليقا", @@ -633,7 +635,55 @@ class CodegenLoader extends AssetLoader{ "mazaya": "مازيا", "benefits": "فوائد", "mazayaDesc": "اكتشف الخصومات والعروض الخاصة المتاحة للموظفين", - "viewallofferMazaya": "أعرض كل المزايا" + "viewallofferMazaya": "أعرض كل المزايا", + "buyerName": "اسم المشتري", + "buyerNumber": "رقم المشتري", + "highestBidder": "أعلى مزايد", + "remarks": "ملاحظات", + "faHeader": "تفاصيل رأس الصفحة FA", + "bookTypeCode": "رمز نوع الكتاب", + "categoryCode": "رمز الفئة", + "categoryGroup": "مجموعة الفئات", + "faLINES": "تفاصيل الأصول", + "assetNumber": "رقم الأصول", + "assetDescription": "وصف الأصول", + "barCodeNumber": "رقم الباركود", + "datePlaceInService": "تاريخ وضعه في الخدمة", + "serialNumber": "رقم سري", + "disposedDate": "تاريخ التخلص", + "netBookValue": "القيمة الدفترية الصافية", + "purchasedPrice": "سعر الشراء", + "usefulLife": "العمر الإنتاجي", + "yearsUsed": "سنوات الاستخدام", + "faRequest": "طلب التخلص من FA", + "showMore": "عرض المزيد", + "ticketDetails": "تفاصيل التذكرة", + "currentTicketBalance": "رصيد التذكرة الحالي", + "ticketHistory": "تاريخ التذكرة", + "proceedBooking": "المتابعة للحجز", + "courses": "الدورات التدريبية", + "course": "دورة", + "status": "الحالة", + "deadline": "الموعد النهائي", + "overdue": "متأخر", + "noCoursesAvailable": "لا توجد دورات متاحة", + "pullToRefresh": "اسحب للتحديث", + "courseUrlNotAvailable": "رابط الدورة غير متوفر", + "buyerDetails": "تفاصيل المشتري", + "etqanOVR": "الإبلاغ عن حادثة", + "etqanRequest": "طلبات الحوادث", + "selectProject": "حدد المشروع", + "project": "مشروع", + "keen": "حريص", + "reportIncident": "الإبلاغ عن الحادث", + "myIncidents": "حوادثي", + "incidentDetails": "تفاصيل الحادث", + "ticketNumber": "رقم التذكرة", + "ticketId": "معرف التذكرة", + "pleaseSelectProject": "الرجاء اختيار مشروع", + "viewAttachment": "عرض المرفق", + "parkingQr": "وقوف السيارات QR", + "startNow": "ابدأ الآن" }; static const Map _en_US = { "mohemm": "Mohemm", @@ -795,6 +845,7 @@ static const Map _en_US = { "subject": "Subject", "quotationAnalysis": "Quotation Analysis", "description": "Description", + "pleaseEnterDescription": "Please enter description", "supplier": "Supplier", "site": "Site", "buyer": "Buyer", @@ -893,6 +944,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", @@ -1254,7 +1306,55 @@ static const Map _en_US = { "mazaya": "MAZAYA", "benefits": "Benefits", "mazayaDesc": "Discover special Discounts and offers available to Employees", - "viewallofferMazaya": "View All Offers" + "viewallofferMazaya": "View All Offers", + "buyerName": "Buyer Name", + "buyerNumber": "Buyer Number", + "highestBidder": "Highest Bidder", + "remarks": "Remarks", + "faHeader": "FA Header Details", + "bookTypeCode": "Book Type Code", + "categoryCode": "Category Code", + "categoryGroup": "Category Group", + "faLINES": "Asset Details", + "assetNumber": "Asset Number", + "assetDescription": "Asset Description", + "barCodeNumber": "BarCode Number", + "datePlaceInService": "Date place In Service", + "serialNumber": "Serial Number", + "disposedDate": "Disposed Date", + "netBookValue": "Net Book Value", + "purchasedPrice": "Purchased Price", + "usefulLife": "Useful Life", + "yearsUsed": "Years Used", + "faRequest": "FA Disposal Request", + "showMore": "Show More", + "ticketDetails": "Ticket Details", + "currentTicketBalance": "Current Ticket Balance", + "ticketHistory": "Ticket History", + "proceedBooking": "Proceed For Booking", + "courses": "Courses", + "course": "Course", + "status": "Status", + "deadline": "Deadline", + "overdue": "Overdue", + "noCoursesAvailable": "No Courses Available", + "pullToRefresh": "Pull to refresh", + "courseUrlNotAvailable": "Course URL not available", + "buyerDetails": "Buyer Details", + "etqanOVR": "Report An Incident", + "etqanRequest": "Incident Requests", + "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", + "viewAttachment": "View Attachment", + "parkingQr": "Parking QR", + "startNow": "Start Now" }; static const Map> mapLocales = {"ar_SA": _ar_SA, "en_US": _en_US}; } diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index b748380..972583a 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'; @@ -261,6 +262,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'; @@ -606,5 +608,53 @@ abstract class LocaleKeys { static const benefits = 'benefits'; static const mazayaDesc = 'mazayaDesc'; static const viewallofferMazaya = 'viewallofferMazaya'; + static const buyerName = 'buyerName'; + static const buyerNumber = 'buyerNumber'; + static const highestBidder = 'highestBidder'; + static const remarks = 'remarks'; + static const faHeader = 'faHeader'; + static const bookTypeCode = 'bookTypeCode'; + static const categoryCode = 'categoryCode'; + static const categoryGroup = 'categoryGroup'; + static const faLINES = 'faLINES'; + static const assetNumber = 'assetNumber'; + static const assetDescription = 'assetDescription'; + static const barCodeNumber = 'barCodeNumber'; + static const datePlaceInService = 'datePlaceInService'; + static const serialNumber = 'serialNumber'; + static const disposedDate = 'disposedDate'; + static const netBookValue = 'netBookValue'; + static const purchasedPrice = 'purchasedPrice'; + static const usefulLife = 'usefulLife'; + static const yearsUsed = 'yearsUsed'; + static const faRequest = 'faRequest'; + static const showMore = 'showMore'; + static const ticketDetails = 'ticketDetails'; + static const currentTicketBalance = 'currentTicketBalance'; + static const ticketHistory = 'ticketHistory'; + static const proceedBooking = 'proceedBooking'; + static const courses = 'courses'; + static const course = 'course'; + static const status = 'status'; + static const deadline = 'deadline'; + static const overdue = 'overdue'; + static const noCoursesAvailable = 'noCoursesAvailable'; + static const pullToRefresh = 'pullToRefresh'; + static const courseUrlNotAvailable = 'courseUrlNotAvailable'; + static const buyerDetails = 'buyerDetails'; + static const etqanOVR = 'etqanOVR'; + static const etqanRequest = 'etqanRequest'; + 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'; + static const parkingQr = 'parkingQr'; + static const startNow = 'startNow'; } diff --git a/lib/main.dart b/lib/main.dart index 39f19e5..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(), // ), @@ -82,18 +84,22 @@ class MyApp extends StatelessWidget { List> delegates = context.localizationDelegates; // delegates.add(GlobalMaterialLocalizations.delegate); delegates.add(MonthYearPickerLocalizations.delegate); - return MaterialApp( - navigatorKey: AppRoutes.navigatorKey, - builder: (BuildContext context, Widget? child) { - return MediaQuery(data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), child: child!); - }, - theme: AppTheme.getTheme(EasyLocalization.of(context)?.locale.languageCode == "ar"), - debugShowCheckedModeBanner: false, - localizationsDelegates: delegates, - supportedLocales: context.supportedLocales, - locale: context.locale, - initialRoute: AppRoutes.initialRoute, - routes: AppRoutes.routes, + return SafeArea( + top: false, // Set to true if you want to avoid the notch area as well + bottom: Platform.isIOS ? false : true, + child: MaterialApp( + navigatorKey: AppRoutes.navigatorKey, + builder: (BuildContext context, Widget? child) { + return MediaQuery(data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), child: child!); + }, + theme: AppTheme.getTheme(EasyLocalization.of(context)?.locale.languageCode == "ar"), + debugShowCheckedModeBanner: false, + localizationsDelegates: delegates, + supportedLocales: context.supportedLocales, + locale: context.locale, + initialRoute: AppRoutes.initialRoute, + routes: AppRoutes.routes, + ), ); }, ); diff --git a/lib/models/dashboard/courses_response_model.dart b/lib/models/dashboard/courses_response_model.dart new file mode 100644 index 0000000..730d4a4 --- /dev/null +++ b/lib/models/dashboard/courses_response_model.dart @@ -0,0 +1,94 @@ +class CoursesResponseModel { + bool? success; + int? status; + String? username; + List? coursesStatus; + int? totalCourses; + + CoursesResponseModel({ + this.success, + this.status, + this.username, + this.coursesStatus, + this.totalCourses, + }); + + CoursesResponseModel.fromJson(Map json) { + success = json['success']; + status = json['status']; + username = json['username']; + if (json['courses_status'] != null) { + coursesStatus = []; + json['courses_status'].forEach((v) { + coursesStatus!.add(CourseStatus.fromJson(v)); + }); + } + totalCourses = json['total_courses']; + } + + Map toJson() { + Map data = {}; + data['success'] = success; + data['status'] = status; + data['username'] = username; + if (coursesStatus != null) { + data['courses_status'] = coursesStatus!.map((v) => v.toJson()).toList(); + } + data['total_courses'] = totalCourses; + return data; + } +} + +class CourseStatus { + String? cId; + String? cFullname; + String? cNameAr; + String? cShortname; + String? cStatus; + String? cIsMohemmAffective; + String? cStartdate; + String? cEnddate; + String? cUrl; + String? token; + CourseStatus({ + this.cId, + this.cFullname, + this.cNameAr, + this.cShortname, + this.cStatus, + this.cIsMohemmAffective, + this.cStartdate, + this.cEnddate, + this.cUrl, + this.token + }); + + CourseStatus.fromJson(Map json) { + cId = json['c_id']; + cFullname = json['c_fullname']; + cNameAr = json['c_name_ar']; + cShortname = json['c_shortname']; + cStatus = json['c_status']; + cIsMohemmAffective = json['c_is_mohemm_affective']; + cStartdate = json['c_startdate']; + cEnddate = json['c_enddate']; + cUrl = json['c_url']; + token =json['c_token']; + } + + Map toJson() { + Map data = {}; + data['c_id'] = cId; + data['c_fullname'] = cFullname; + data['c_name_ar'] = cNameAr; + data['c_shortname'] = cShortname; + data['c_status'] = cStatus; + data['c_is_mohemm_affective'] = cIsMohemmAffective; + data['c_startdate'] = cStartdate; + data['c_enddate'] = cEnddate; + data['c_url'] = cUrl; + data['c_token'] = token; + return data; + } +} + diff --git a/lib/models/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..e4a57fd --- /dev/null +++ b/lib/models/etqan_ovr/etqan_get_employee_incident_report.dart @@ -0,0 +1,57 @@ +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? 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)); + + String toRawJson() => json.encode(toJson()); + + 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/models/etqan_ovr/etqan_get_requests_model.dart b/lib/models/etqan_ovr/etqan_get_requests_model.dart new file mode 100644 index 0000000..ac4911b --- /dev/null +++ b/lib/models/etqan_ovr/etqan_get_requests_model.dart @@ -0,0 +1,65 @@ +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; + bool? isAnonymous; + String? status; + String? statusAr; + String? ticketNumber; + + EtqanGetEmployeeOvrRequestsResponse({ + this.createdDate, + this.description, + this.id, + this.isAnonymous, + this.status, + this.statusAr, + 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"], + isAnonymous: json["isAnonymous"], + status: json["status"], + statusAr: json["statusAr"], + ticketNumber: json["ticketNumber"], + ); + + Map toJson() => { + "createdDate": createdDate, + "description": description, + "id": id, + "isAnonymous": isAnonymous, + "status": status, + "statusAr": statusAr, + "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/models/generic_response_model.dart b/lib/models/generic_response_model.dart index b7e786c..df94c8b 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -108,6 +108,7 @@ import 'package:mohem_flutter_app/models/vacation_rule/vr_item_types_list_model. import 'package:mohem_flutter_app/models/vacation_rule/wf_look_up_list_model.dart'; import 'package:mohem_flutter_app/models/validate_eit_transaction_list_model.dart'; import 'package:mohem_flutter_app/models/worklist/GetRFCEmployeeList.dart'; +import 'package:mohem_flutter_app/models/worklist/get_fad_notifications.dart'; import 'package:mohem_flutter_app/models/worklist/get_favorite_replacements_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/eit_otification_body_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_address_notification_body_list.dart'; @@ -388,6 +389,7 @@ class GenericResponseModel { String? validatePhonesTransactionList; List? vrItemTypesList; List? wFLookUpList; + GetFaDisposalNtfDetails? getFADisposalNtfDetails; String? eLearningGETEMPLOYEEPROFILEList; String? eLearningLOGINList; String? eLearningValidateLoginList; @@ -667,6 +669,7 @@ class GenericResponseModel { this.ePharmacyGetItemOnHandList, this.isActiveCode, this.isSMSSent, + this.getFADisposalNtfDetails }); GenericResponseModel.fromJson(Map json) { @@ -938,6 +941,9 @@ class GenericResponseModel { }); } + getFADisposalNtfDetails = json['GetFADisposalNtfDetails'] != null ? GetFaDisposalNtfDetails.fromJson(json['GetFADisposalNtfDetails']) : null; + + if (json['GetEarningsList'] != null) { getEarningsList = []; json['GetEarningsList'].forEach((v) { @@ -1555,6 +1561,7 @@ class GenericResponseModel { if (this.getActionHistoryList != null) { data['GetActionHistoryList'] = this.getActionHistoryList!.map((v) => v.toJson()).toList(); } + data['GetFADisposalNtfDetails'] =this.getFADisposalNtfDetails; data['GetAddressDffStructureList'] = this.getAddressDffStructureList; data['GetAddressNotificationBodyList'] = this.getAddressNotificationBodyList; diff --git a/lib/models/get_day_hours_type_details_list_model.dart b/lib/models/get_day_hours_type_details_list_model.dart index e4eef25..0a0c8b0 100644 --- a/lib/models/get_day_hours_type_details_list_model.dart +++ b/lib/models/get_day_hours_type_details_list_model.dart @@ -22,6 +22,8 @@ class GetDayHoursTypeDetailsList { dynamic? fROMROWNUM; String? lATEINFLAG; String? lATEINHRS; + String? lEAVEDESCRIPTION; + String? lEAVETYPECODE; String? mISSINGSWIPEFLAG; String? nONSCHEDULEDFLAG; dynamic? nOOFROWS; @@ -66,6 +68,8 @@ class GetDayHoursTypeDetailsList { this.fROMROWNUM, this.lATEINFLAG, this.lATEINHRS, + this.lEAVEDESCRIPTION, + this.lEAVETYPECODE, this.mISSINGSWIPEFLAG, this.nONSCHEDULEDFLAG, this.nOOFROWS, @@ -110,6 +114,8 @@ class GetDayHoursTypeDetailsList { fROMROWNUM = json['FROM_ROW_NUM']; lATEINFLAG = json['LATE_IN_FLAG']; lATEINHRS = json['LATE_IN_HRS']; + lEAVEDESCRIPTION = json['LEAVE_DESCRIPTION']; + lEAVETYPECODE = json['LEAVE_TYPE_CODE']; mISSINGSWIPEFLAG = json['MISSING_SWIPE_FLAG']; nONSCHEDULEDFLAG = json['NON_SCHEDULED_FLAG']; nOOFROWS = json['NO_OF_ROWS']; @@ -156,6 +162,8 @@ class GetDayHoursTypeDetailsList { data['FROM_ROW_NUM'] = this.fROMROWNUM; data['LATE_IN_FLAG'] = this.lATEINFLAG; data['LATE_IN_HRS'] = this.lATEINHRS; + data['LEAVE_DESCRIPTION'] = this.lEAVEDESCRIPTION; + data['LEAVE_TYPE_CODE'] = this.lEAVETYPECODE; data['MISSING_SWIPE_FLAG'] = this.mISSINGSWIPEFLAG; data['NON_SCHEDULED_FLAG'] = this.nONSCHEDULEDFLAG; data['NO_OF_ROWS'] = this.nOOFROWS; diff --git a/lib/models/get_employee_parking_details_model.dart b/lib/models/get_employee_parking_details_model.dart new file mode 100644 index 0000000..4a385c8 --- /dev/null +++ b/lib/models/get_employee_parking_details_model.dart @@ -0,0 +1,61 @@ +import 'dart:convert'; + +class GetEmployeeParking { + GetEmployeeParkingClass? getEmployeeParking; + + GetEmployeeParking({ + this.getEmployeeParking, + }); + + factory GetEmployeeParking.fromRawJson(String str) => GetEmployeeParking.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory GetEmployeeParking.fromJson(Map json) => GetEmployeeParking( + getEmployeeParking: json["GET_Employee_Parking"] == null ? null : GetEmployeeParkingClass.fromJson(json["GET_Employee_Parking"]), + ); + + Map toJson() => { + "GET_Employee_Parking": getEmployeeParking?.toJson(), + }; +} + +class GetEmployeeParkingClass { + DateTime? createdAt; + String? employeeCode; + String? isNewQrCode; + String? qrCode; + String? qrCodeImageBase64; + bool? success; + + GetEmployeeParkingClass({ + this.createdAt, + this.employeeCode, + this.isNewQrCode, + this.qrCode, + this.qrCodeImageBase64, + this.success, + }); + + factory GetEmployeeParkingClass.fromRawJson(String str) => GetEmployeeParkingClass.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory GetEmployeeParkingClass.fromJson(Map json) => GetEmployeeParkingClass( + createdAt: json["createdAt"] == null ? null : DateTime.parse(json["createdAt"]), + employeeCode: json["employeeCode"], + isNewQrCode: json["isNewQRCode"], + qrCode: json["qrCode"], + qrCodeImageBase64: json["qrCodeImageBase64"], + success: json["success"], + ); + + Map toJson() => { + "createdAt": createdAt?.toIso8601String(), + "employeeCode": employeeCode, + "isNewQRCode": isNewQrCode, + "qrCode": qrCode, + "qrCodeImageBase64": qrCodeImageBase64, + "success": success, + }; +} diff --git a/lib/models/greetings/greeting_card_model.dart b/lib/models/greetings/greeting_card_model.dart new file mode 100644 index 0000000..486aa8f --- /dev/null +++ b/lib/models/greetings/greeting_card_model.dart @@ -0,0 +1,142 @@ +import 'dart:convert'; + +class GetGreetingCardsModelResponse { + List? getGreetingCardsModelResponse; + + GetGreetingCardsModelResponse({ + this.getGreetingCardsModelResponse, + }); + + factory GetGreetingCardsModelResponse.fromRawJson(String str) => GetGreetingCardsModelResponse.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory GetGreetingCardsModelResponse.fromJson(Map json) => GetGreetingCardsModelResponse( + getGreetingCardsModelResponse: json["Get_GreetingCardsModel_Response"] == null ? [] : List.from(json["Get_GreetingCardsModel_Response"]!.map((x) => GetGreetingCardsModelResponseElement.fromJson(x))), + ); + + Map toJson() => { + "Get_GreetingCardsModel_Response": getGreetingCardsModelResponse == null ? [] : List.from(getGreetingCardsModelResponse!.map((x) => x.toJson())), + }; +} + +class GetGreetingCardsModelResponseElement { + String? backgroundImageUrlAr; + String? backgroundImageUrlEn; + String? buttonBgColor; + String? buttonText; + String? buttonTextN; + int? cardType; + int? categoryId; + String? categoryNameAr; + String? categoryNameEn; + int? channel; + String? createdOn; + String? descriptionAr; + String? descriptionEn; + String? endDate; + bool? hasButton; + int? id; + bool? isActive; + int? moduleId; + String? moduleName; + String? moduleNameN; + String? startDate; + String? textColor; + String? titleAr; + String? titleEn; + String? urlAr; + String? urlEn; + + GetGreetingCardsModelResponseElement({ + this.backgroundImageUrlAr, + this.backgroundImageUrlEn, + this.buttonBgColor, + this.buttonText, + this.buttonTextN, + this.cardType, + this.categoryId, + this.categoryNameAr, + this.categoryNameEn, + this.channel, + this.createdOn, + this.descriptionAr, + this.descriptionEn, + this.endDate, + this.hasButton, + this.id, + this.isActive, + this.moduleId, + this.moduleName, + this.moduleNameN, + this.startDate, + this.textColor, + this.titleAr, + this.titleEn, + this.urlAr, + this.urlEn, + }); + + factory GetGreetingCardsModelResponseElement.fromRawJson(String str) => GetGreetingCardsModelResponseElement.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory GetGreetingCardsModelResponseElement.fromJson(Map json) => GetGreetingCardsModelResponseElement( + backgroundImageUrlAr: json["BackgroundImageUrlAr"], + backgroundImageUrlEn: json["BackgroundImageUrlEn"], + buttonBgColor: json["ButtonBgColor"], + buttonText: json["ButtonText"], + buttonTextN: json["ButtonTextN"], + cardType: json["CardType"], + categoryId: json["CategoryID"], + categoryNameAr: json["CategoryNameAr"], + categoryNameEn: json["CategoryNameEn"], + channel: json["Channel"], + createdOn: json["CreatedOn"], + descriptionAr: json["DescriptionAr"], + descriptionEn: json["DescriptionEn"], + endDate: json["EndDate"], + hasButton: json["HasButton"], + id: json["ID"], + isActive: json["IsActive"], + moduleId: json["ModuleID"], + moduleName: json["ModuleName"], + moduleNameN: json["ModuleNameN"], + startDate: json["StartDate"], + textColor: json["TextColor"], + titleAr: json["TitleAr"], + titleEn: json["TitleEn"], + urlAr: json["UrlAr"], + urlEn: json["UrlEn"], + ); + + Map toJson() => { + "BackgroundImageUrlAr": backgroundImageUrlAr, + "BackgroundImageUrlEn": backgroundImageUrlEn, + "ButtonBgColor": buttonBgColor, + "ButtonText": buttonText, + "ButtonTextN": buttonTextN, + "CardType": cardType, + "CategoryID": categoryId, + "CategoryNameAr": categoryNameAr, + "CategoryNameEn": categoryNameEn, + "Channel": channel, + "CreatedOn": createdOn, + "DescriptionAr": descriptionAr, + "DescriptionEn": descriptionEn, + "EndDate": endDate, + "HasButton": hasButton, + "ID": id, + "IsActive": isActive, + "ModuleID": moduleId, + "ModuleName": moduleName, + "ModuleNameN": moduleNameN, + "StartDate": startDate, + "TextColor": textColor, + "TitleAr": titleAr, + "TitleEn": titleEn, + "UrlAr": urlAr, + "UrlEn": urlEn, + }; +} + diff --git a/lib/models/itg/advertisement.dart b/lib/models/itg/advertisement.dart index 002f73d..2379d37 100644 --- a/lib/models/itg/advertisement.dart +++ b/lib/models/itg/advertisement.dart @@ -136,10 +136,21 @@ class ActionButtonsColl { late String btnTextEn; late String btnTextAr; late String actionValue; - late dynamic iconOrImage; + dynamic iconOrImage; + String? actionType; late int orderNo; + String? url; - ActionButtonsColl({required this.actionButtonId, required this.btnTextEn, required this.btnTextAr, required this.actionValue, required this.iconOrImage, required this.orderNo}); + ActionButtonsColl({ + required this.actionButtonId, + required this.btnTextEn, + required this.btnTextAr, + required this.actionValue, + this.iconOrImage, + this.actionType, + required this.orderNo, + this.url, + }); ActionButtonsColl.fromJson(Map json) { actionButtonId = json['actionButtonId']; @@ -147,7 +158,9 @@ class ActionButtonsColl { btnTextAr = json['btnTextAr']; actionValue = json['actionValue']; iconOrImage = json['iconOrImage']; + actionType = json['actionType']; orderNo = json['orderNo']; + url = json['url']; } Map toJson() { @@ -157,7 +170,9 @@ class ActionButtonsColl { data['btnTextAr'] = this.btnTextAr; data['actionValue'] = this.actionValue; data['iconOrImage'] = this.iconOrImage; + data['actionType'] = this.actionType; data['orderNo'] = this.orderNo; + data['url'] = this.url; return data; } } diff --git a/lib/models/worklist/get_fad_notifications.dart b/lib/models/worklist/get_fad_notifications.dart new file mode 100644 index 0000000..3a7ed2d --- /dev/null +++ b/lib/models/worklist/get_fad_notifications.dart @@ -0,0 +1,233 @@ +import 'dart:convert'; + +class GetFaDisposalNtfDetails { + List? pFaBuyers; + List? pFaHeader; + List? pFaLines; + String? pInformation; + dynamic pQuestion; + + GetFaDisposalNtfDetails({ + this.pFaBuyers, + this.pFaHeader, + this.pFaLines, + this.pInformation, + this.pQuestion, + }); + + factory GetFaDisposalNtfDetails.fromRawJson(String str) => GetFaDisposalNtfDetails.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory GetFaDisposalNtfDetails.fromJson(Map json) => GetFaDisposalNtfDetails( + pFaBuyers: json["P_FA_BUYERS"] == null ? [] : List.from(json["P_FA_BUYERS"]!.map((x) => PFaBuyer.fromJson(x))), + pFaHeader: json["P_FA_HEADER"] == null ? [] : List.from(json["P_FA_HEADER"]!.map((x) => PFaHeader.fromJson(x))), + pFaLines: json["P_FA_LINES"] == null ? [] : List.from(json["P_FA_LINES"]!.map((x) => PFaLine.fromJson(x))), + pInformation: json["P_INFORMATION"], + pQuestion: json["P_QUESTION"], + ); + + Map toJson() => { + "P_FA_BUYERS": pFaBuyers == null ? [] : List.from(pFaBuyers!.map((x) => x.toJson())), + "P_FA_HEADER": pFaHeader == null ? [] : List.from(pFaHeader!.map((x) => x.toJson())), + "P_FA_LINES": pFaLines == null ? [] : List.from(pFaLines!.map((x) => x.toJson())), + "P_INFORMATION": pInformation, + "P_QUESTION": pQuestion, + }; +} + +class PFaBuyer { + int? amount; + String? buyerName; + String? buyerNumber; + int? fromRowNum; + String? highestBidder; + int? noOfRows; + String? receiptNumber; + String? remarks; + int? requestNo; + int? rowNum; + int? toRowNum; + + PFaBuyer({ + this.amount, + this.buyerName, + this.buyerNumber, + this.fromRowNum, + this.highestBidder, + this.noOfRows, + this.receiptNumber, + this.remarks, + this.requestNo, + this.rowNum, + this.toRowNum, + }); + + factory PFaBuyer.fromRawJson(String str) => PFaBuyer.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory PFaBuyer.fromJson(Map json) => PFaBuyer( + amount: json["AMOUNT"], + buyerName: json["BUYER_NAME"], + buyerNumber: json["BUYER_NUMBER"], + fromRowNum: json["FROM_ROW_NUM"], + highestBidder: json["HIGHEST_BIDDER"], + noOfRows: json["NO_OF_ROWS"], + receiptNumber: json["RECEIPT_NUMBER"], + remarks: json["REMARKS"], + requestNo: json["REQUEST_NO"], + rowNum: json["ROW_NUM"], + toRowNum: json["TO_ROW_NUM"], + ); + + Map toJson() => { + "AMOUNT": amount, + "BUYER_NAME": buyerName, + "BUYER_NUMBER": buyerNumber, + "FROM_ROW_NUM": fromRowNum, + "HIGHEST_BIDDER": highestBidder, + "NO_OF_ROWS": noOfRows, + "RECEIPT_NUMBER": receiptNumber, + "REMARKS": remarks, + "REQUEST_NO": requestNo, + "ROW_NUM": rowNum, + "TO_ROW_NUM": toRowNum, + }; +} + +class PFaHeader { + String? bookTypeCode; + String? categoryCode; + String? categoryGroup; + String? comments; + int? requestNo; + String? status; + + PFaHeader({ + this.bookTypeCode, + this.categoryCode, + this.categoryGroup, + this.comments, + this.requestNo, + this.status, + }); + + factory PFaHeader.fromRawJson(String str) => PFaHeader.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory PFaHeader.fromJson(Map json) => PFaHeader( + bookTypeCode: json["BOOK_TYPE_CODE"], + categoryCode: json["CATEGORY_CODE"], + categoryGroup: json["CATEGORY_GROUP"], + comments: json["COMMENTS"], + requestNo: json["REQUEST_NO"], + status: json["STATUS"], + ); + + Map toJson() => { + "BOOK_TYPE_CODE": bookTypeCode, + "CATEGORY_CODE": categoryCode, + "CATEGORY_GROUP": categoryGroup, + "COMMENTS": comments, + "REQUEST_NO": requestNo, + "STATUS": status, + }; +} + +class PFaLine { + String? action; + String? assetDescription; + int? assetNumber; + String? barcodeNumber; + String? bme; + String? datePlacedInService; + String? department; + String? disposedDate; + int? fromRowNum; + double? netBookValue; + int? noOfRows; + String? poNumber; + double? purchasePrice; + int? quantity; + int? requestNo; + int? rowNum; + String? serialNumber; + int? toRowNum; + int? usefulLife; + String? yearsUsed; + + PFaLine({ + this.action, + this.assetDescription, + this.assetNumber, + this.barcodeNumber, + this.bme, + this.datePlacedInService, + this.department, + this.disposedDate, + this.fromRowNum, + this.netBookValue, + this.noOfRows, + this.poNumber, + this.purchasePrice, + this.quantity, + this.requestNo, + this.rowNum, + this.serialNumber, + this.toRowNum, + this.usefulLife, + this.yearsUsed, + }); + + factory PFaLine.fromRawJson(String str) => PFaLine.fromJson(json.decode(str)); + + String toRawJson() => json.encode(toJson()); + + factory PFaLine.fromJson(Map json) => PFaLine( + action: json["ACTION"], + assetDescription: json["ASSET_DESCRIPTION"], + assetNumber: json["ASSET_NUMBER"], + barcodeNumber: json["BARCODE_NUMBER"], + bme: json["BME"], + datePlacedInService: json["DATE_PLACED_IN_SERVICE"], + department: json["DEPARTMENT"], + disposedDate: json["DISPOSED_DATE"], + fromRowNum: json["FROM_ROW_NUM"], + netBookValue: json["NET_BOOK_VALUE"]?.toDouble(), + noOfRows: json["NO_OF_ROWS"], + poNumber: json["PO_NUMBER"], + purchasePrice: json["PURCHASE_PRICE"]?.toDouble(), + quantity: json["QUANTITY"], + requestNo: json["REQUEST_NO"], + rowNum: json["ROW_NUM"], + serialNumber: json["SERIAL_NUMBER"], + toRowNum: json["TO_ROW_NUM"], + usefulLife: json["USEFUL_LIFE"], + yearsUsed: json["YEARS_USED"], + ); + + Map toJson() => { + "ACTION": action, + "ASSET_DESCRIPTION": assetDescription, + "ASSET_NUMBER": assetNumber, + "BARCODE_NUMBER": barcodeNumber, + "BME": bme, + "DATE_PLACED_IN_SERVICE": datePlacedInService, + "DEPARTMENT": department, + "DISPOSED_DATE": disposedDate, + "FROM_ROW_NUM": fromRowNum, + "NET_BOOK_VALUE": netBookValue, + "NO_OF_ROWS": noOfRows, + "PO_NUMBER": poNumber, + "PURCHASE_PRICE": purchasePrice, + "QUANTITY": quantity, + "REQUEST_NO": requestNo, + "ROW_NUM": rowNum, + "SERIAL_NUMBER": serialNumber, + "TO_ROW_NUM": toRowNum, + "USEFUL_LIFE": usefulLife, + "YEARS_USED": yearsUsed, + }; +} diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index 4a8ec61..965fe52 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -19,9 +19,11 @@ import 'package:mohem_flutter_app/models/dashboard/list_menu.dart'; import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; import 'package:mohem_flutter_app/models/dashboard/menus.dart'; import 'package:mohem_flutter_app/models/dashboard/mohemm_itg_pending_task_responseitem.dart'; +import 'package:mohem_flutter_app/models/dashboard/courses_response_model.dart'; import 'package:mohem_flutter_app/models/eit/get_eit_transaction_model.dart'; import 'package:mohem_flutter_app/models/eit_attachment_ticket_model.dart'; import 'package:mohem_flutter_app/models/generic_response_model.dart'; +import 'package:mohem_flutter_app/models/greetings/greeting_card_model.dart'; import 'package:mohem_flutter_app/models/itg/itg_response_model.dart'; import 'package:mohem_flutter_app/models/offers_and_discounts/get_offers_list.dart'; import 'package:mohem_flutter_app/models/sso_auth_model.dart'; @@ -68,6 +70,15 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { bool isOffersLoading = true; List getOffersList = []; + //Courses + bool isCoursesLoading = true; + CoursesResponseModel? coursesResponse; + List coursesList = []; + + bool isGreetingCardsLoading = true; + bool isDisplayEidGreetings = false; + List? greetingCardsList; + //Attendance Tracking API's & Methods Future fetchAttendanceTracking(context) async { try { @@ -116,12 +127,18 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { getMenuEntriesList = null; isOffersLoading = true; getOffersList = []; + isGreetingCardsLoading = true; + isDisplayEidGreetings = false; drawerMenuItemList = [ DrawerMenuItem("assets/images/drawer/my_profile.svg", LocaleKeys.myProfile.tr(), AppRoutes.profile), 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.keen.tr(), AppRoutes.etqanOvr), + // DrawerMenuItem("assets/images/drawer/car_parking_icon.svg", LocaleKeys.parkingQr.tr(), AppRoutes.parkingQr), DrawerMenuItem("assets/images/drawer/pending_trasactions.svg", LocaleKeys.pendingTransactions.tr(), AppRoutes.pendingTransactions), + DrawerMenuItem("assets/images/drawer/courses.svg", "courses".tr(), AppRoutes.courses), // DrawerMenuItem("assets/images/drawer/drawer_marathon.svg", LocaleKeys.brainMarathon.tr(), AppRoutes.marathonIntroScreen), DrawerMenuItem("assets/images/drawer/change_password.svg", LocaleKeys.changePassword.tr(), AppRoutes.changePassword), ]; @@ -307,6 +324,28 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { } } + void fetchGreetingCards() async { + isGreetingCardsLoading = true; + notifyListeners(); + try { + GetGreetingCardsModelResponse? response = await DashboardApiClient().getGreetingCards(); + greetingCardsList = response?.getGreetingCardsModelResponse ?? []; + if (greetingCardsList != null && greetingCardsList!.isNotEmpty) { + isDisplayEidGreetings = greetingCardsList!.any((card) => card.isActive == true); + } else { + isDisplayEidGreetings = false; + } + isGreetingCardsLoading = false; + notifyListeners(); + } catch (ex) { + logger.wtf(ex); + isDisplayEidGreetings = false; + greetingCardsList = []; + isGreetingCardsLoading = false; + notifyListeners(); + } + } + Future fetchTicketBooking() async { try { GenericResponseModel? genericResponseModel = await DashboardApiClient().getTicketBookingRedirection(); @@ -328,8 +367,6 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { void fetchTicketBalance() async { try { GenericResponseModel? ticketResponse = await DashboardApiClient().getTicketBalance(); - logger.d("Ticket Balance Response: ${ticketResponse?.toJson()}"); - print(" ========= Ticket Balance Response ======= "); } catch (ex) { logger.wtf(ex); isEventLoadingLoading = false; @@ -342,8 +379,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { Future fetchSSOAuthRedirection({String? clientID}) async { try { SSOAuthModel? ssoResponse = await DashboardApiClient().getBookingSSOAuthRedirection(clientID: clientID!); - logger.d("SSO Response: ${ssoResponse!.toJson()}"); - if (ssoResponse.status == "Success") { + if (ssoResponse!.status == "Success") { return ssoResponse; } else { Utils.showToast(ssoResponse.message!.first ?? "Failed to fetch SSO Auth Token"); @@ -363,18 +399,12 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { dynamic res = await DashboardApiClient().getBookingSSOFinalRedirection(token: token); logger.d("SSO URL: $res"); return res; - // if (ssoResponse.status == "Success") { - // return ssoResponse; - // } else { - // Utils.showToast(ssoResponse.message!.first ?? "Failed to fetch SSO Auth Token"); - // return null; - // } } catch (ex) { logger.wtf(ex); isEventLoadingLoading = false; notifyListeners(); Utils.handleException(ex, null, null); - return null; // Ensure a return value in case of an exception + return null; } } @@ -428,6 +458,30 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { return res; } + //Courses API's & Methods + Future fetchCourses(BuildContext context) async { + try { + isCoursesLoading = true; + notifyListeners(); + + coursesResponse = await DashboardApiClient().fetchAllCoursesStatus(); + + if (coursesResponse != null && coursesResponse!.success == true) { + coursesList = coursesResponse!.coursesStatus ?? []; + } else { + coursesList = []; + } + + isCoursesLoading = false; + notifyListeners(); + } catch (ex) { + isCoursesLoading = false; + coursesList = []; + notifyListeners(); + Utils.handleException(ex, context, null); + } + } + void notify() { notifyListeners(); } diff --git a/lib/provider/etqan_ovr_provider.dart b/lib/provider/etqan_ovr_provider.dart new file mode 100644 index 0000000..b4f2eb1 --- /dev/null +++ b/lib/provider/etqan_ovr_provider.dart @@ -0,0 +1,262 @@ +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; + String? _currentStatus; + String? _currentStatusAr; + bool? _currentIsAnonymous; + + // Speech to text state + final SpeechToText _speechToText = SpeechToText(); + bool _speechEnabled = false; + 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; + + bool get isListening => _isListening; + + // Getter for ticketId + String get getTicketId => _ticketId ?? ''; + String get getCurrentStatus => _currentStatus ?? ''; + String get getCurrentStatusAr => _currentStatusAr ?? ''; + bool get getCurrentIsAnonymous => _currentIsAnonymous ?? false; + + // 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(); + } + + // Create request screen state + EtqanGetProjectsResponse? selectedProject; + String? selectedEmployeeNumber; + File? attachment; + String? attachmentBase64; + String description = ''; + bool isAnonymous = false; + + /// Initialize speech recognition - call this once when screen loads + Future initSpeech() async { + _speechEnabled = await _speechToText.initialize( + onStatus: (String status) { + 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: 300), () { + if (_isListening && _currentController != null) { + _startListeningInternal(_currentLocaleId, _currentController!); + } + }); + } + }, + ); + notifyListeners(); + } + + /// Internal method to start listening + Future _startListeningInternal(String localeId, TextEditingController controller) async { + if (!_speechEnabled) return; + + await _speechToText.listen( + onResult: (SpeechRecognitionResult result) { + _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(); + }, + localeId: localeId, + 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; + _baseText = controller.text.trim(); // Save existing text + _currentSessionText = ''; + notifyListeners(); + await _startListeningInternal(localeId, controller); + } + + /// Stop listening for speech input + Future stopListening() async { + _isListening = false; + _currentLocaleId = ''; + _currentController = null; + _baseText = ''; + _currentSessionText = ''; + await _speechToText.stop(); + 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(); + } + + // Validate description + bool isDescriptionValid() { + return description.trim().isNotEmpty; + } + + // Set isAnonymous + void setIsAnonymous(bool value) { + isAnonymous = value; + notifyListeners(); + } + + // Clear create request form + void clearCreateRequestForm() { + selectedProject = null; + selectedEmployeeNumber = null; + attachment = null; + attachmentBase64 = null; + description = ''; + isAnonymous = false; + notifyListeners(); + } + + // Submit request + Future?> submitRequest(BuildContext context) async { + try { + Utils.showLoading(context); + Map? response = await EtqanApiClient().createRequest(description, selectedProject?.projectId, attachmentBase64, isAnonymous); + Utils.hideLoading(context); + if (response != null) { + getEtqanEmployeeRequestsList = await EtqanApiClient().getEmployeeEtqanRequests(); + notifyListeners(); + return response; + } + return null; + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + return null; + } + } + + @override + void dispose() { + if (_isListening) { + _speechToText.stop(); + } + super.dispose(); + } +} diff --git a/lib/ui/attendance/monthly_attendance_screen.dart b/lib/ui/attendance/monthly_attendance_screen.dart index cce9d3b..5fbd585 100644 --- a/lib/ui/attendance/monthly_attendance_screen.dart +++ b/lib/ui/attendance/monthly_attendance_screen.dart @@ -315,6 +315,26 @@ class _MonthlyAttendanceScreenState extends State { bool isDayIsPresent = getDayHoursTypeDetailsList[index].aTTENDEDFLAG == 'Y'; bool isDayIsAbsent = getDayHoursTypeDetailsList[index].aTTENDEDFLAG == 'N' && getDayHoursTypeDetailsList[index].aBSENTFLAG == 'Y'; + // Get leave type code and corresponding color + String? leaveTypeCode = getDayHoursTypeDetailsList[index].lEAVETYPECODE; + Color? leaveTypeColor; + bool useGradient = false; + + // Determine color based on leave type code + if (leaveTypeCode != null && leaveTypeCode.isNotEmpty) { + leaveTypeColor = leaveTypeColors[leaveTypeCode]; + } + + // If no leave type color found, use default colors based on attendance status + if (leaveTypeColor == null) { + if (isDayIsPresent) { + leaveTypeColor = leaveTypeColors['Present']; + useGradient = true; // Use gradient for present days without leave type + } else if (isDayIsAbsent) { + leaveTypeColor = leaveTypeColors['Absent']; + } + } + if (isDayIsOff) { return Container( margin: const EdgeInsets.all(4), @@ -331,12 +351,13 @@ class _MonthlyAttendanceScreenState extends State { return Container( margin: const EdgeInsets.all(4), decoration: BoxDecoration( - gradient: const LinearGradient( + gradient: useGradient && leaveTypeColor != null ? LinearGradient( transform: GradientRotation(.46), begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [MyColors.gradiantEndColor, MyColors.gradiantStartColor], - ), + ) : null, + color: !useGradient && leaveTypeColor != null ? leaveTypeColor : null, shape: BoxShape.circle, boxShadow: [ BoxShadow( @@ -358,7 +379,7 @@ class _MonthlyAttendanceScreenState extends State { return Container( margin: const EdgeInsets.all(4), decoration: BoxDecoration( - color: MyColors.backgroundBlackColor, + color: leaveTypeColor ?? MyColors.backgroundBlackColor, shape: BoxShape.circle, boxShadow: [ BoxShadow( @@ -751,3 +772,24 @@ class Meeting { Color background; bool isAllDay; } +const Map leaveTypeColors = { + 'Present': Color(0xFF00ad62), + 'Absent': Color(0xFFcb3232), + 'HOLIDAY': Color(0xFFF26B0F), + 'BUS_TRIP': Color(0xFF001A6E), + 'WFH': Color(0xFF5DB996), + 'ANNUAL': Color(0xFFf39c12), + 'EMERGENCY': Color(0xFFFF2929), + 'EXAM': Color(0xFFCB6040), + 'HAJJ': Color(0xFF798645), + 'HALF': Color(0xFFE8B86D), + 'LWOP': Color(0xFFCD5C08), + 'LWOP_MATUNPAID': Color(0xFFE73879), + 'LWOP_UNAUUNPAID': Color(0xFFA02334), + 'MATPAID': Color(0xFFE73879), + 'MRGE': Color(0xFF0D7C66), + 'PAT': Color(0xFFC96868), + 'PROF': Color(0xFF6256CA), + 'SICK': Color(0xFFA5B68D), +}; + 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..a1d64af --- /dev/null +++ b/lib/ui/etqan_ovr/etqan_create_request.dart @@ -0,0 +1,355 @@ +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/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'; +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() { + // Stop listening if active + context.read().stopListening(); + _descriptionController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget(context, title: LocaleKeys.reportIncident.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), + isMandatory: true, + ), + 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), + 8.height, + _buildAnonymousCheckbox(provider), + 24.height, + ], + )).expanded, + DefaultButton(LocaleKeys.submit.tr(), () async { + provider.setDescription(_descriptionController.text); + 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.popAndPushNamed(context, AppRoutes.etqanOvr); + }, + onCloseTap: () { + Navigator.popAndPushNamed(context, AppRoutes.etqanOvr); + }, + ); + } + }).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, bool isMandatory = false}) { + 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: [ + 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)), + ], + ), + ), + 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: [ + 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 + ? () { + 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), + const 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, + ), + ), + ], + ), + ); + } + + Widget _buildAnonymousCheckbox(EtqanOvrProviderModel provider) { + return InkWell( + onTap: () { + provider.setIsAnonymous(!provider.isAnonymous); + }, + 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)), + ), + + Expanded(child: Text(LocaleKeys.submitAsAnonymous.tr(), style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500, color: Color(0xff2B353E), letterSpacing: -0.44))), + ], + ), + ); + } +} 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..b55b4c0 --- /dev/null +++ b/lib/ui/etqan_ovr/etqan_ovr_home.dart @@ -0,0 +1,165 @@ +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/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'; +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); + + @override + _EtqanOvrHomeState createState() => _EtqanOvrHomeState(); +} + +class _EtqanOvrHomeState extends State { + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + context.read().fetchEtqanEmployeeRequests(context); + }); + } + + 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( + backgroundColor: Colors.white, + appBar: AppBarWidget(context, title: LocaleKeys.myIncidents.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 ?? '', status: data.status, statusAr: data.statusAr, isAnonymous: data.isAnonymous); + 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( + 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: [ + (data.ticketNumber ?? "").toText14(isBold: true, color: MyColors.darkTextColor), + Directionality(textDirection: TextDirection.ltr, child: (data.createdDate!.split(" ").first ?? "").toText12(color: MyColors.grey70Color)), + ], + ), + 8.height, + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [(data.description ?? "").toText12(color: MyColors.grey57Color).expanded]), + 3.height, + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + 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.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 new file mode 100644 index 0000000..41d11c6 --- /dev/null +++ b/lib/ui/etqan_ovr/etqan_request_detailed.dart @@ -0,0 +1,235 @@ +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/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'; +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'; +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); + + @override + _EtqanOvrRequestDetailedState createState() => _EtqanOvrRequestDetailedState(); +} + +class _EtqanOvrRequestDetailedState extends State { + 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 { + 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, EtqanOvrProviderModel provider) { + List orderedList = []; + + List filteredList = list.where((EtqanGetIncidentRequestResponse item) => !hiddenFields.any((f) => f.toLowerCase() == item.key?.toLowerCase())).toList(); + + for (String fieldName in fieldOrder) { + // 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); + } + } + } + + 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((_) { + EtqanOvrProviderModel provider = context.read(); + provider.fetchEtqanEmployeeIncidentRequests(context, provider.getTicketId); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget(context, title: LocaleKeys.incidentDetails.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!, provider); + + 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.withValues(alpha: 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; + 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 ?? ""); + + bool hasSubValues = data.subValues != null && data.subValues!.isNotEmpty; + + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(flex: 2, child: displayKey.toText12(isBold: true, color: MyColors.grey57Color)), + 8.width, + Expanded( + flex: 3, + 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), + ), + ], + ), + ), + if (!isLast) const Padding(padding: EdgeInsets.only(left: 14, right: 14), child: Divider(height: 1, thickness: 1, color: Color(0xffefefef))), + ], + ); + }).toList(), + ), + ), + ); + }, + ).paddingOnly(bottom: 20), + ); + } +} diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index a973aae..e053b8c 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui' as ui; - import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; @@ -34,6 +33,7 @@ import 'package:mohem_flutter_app/widgets/shimmer/offers_shimmer_widget.dart'; import 'package:provider/provider.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:signalr_netcore/signalr_client.dart'; +import 'package:url_launcher/url_launcher.dart'; late HubConnection chatHubConnection; @@ -156,6 +156,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb data.fetchMenuEntries(); data.fetchEventActivity(); data.getCategoryOffersListAPI(context); + data.fetchGreetingCards(); marathonProvider.getMarathonDetailsFromApi(); marathonProvider.getMarathonTutorial(); if (isFromInit) { @@ -164,10 +165,12 @@ class _DashboardScreenState extends State with WidgetsBindingOb if (!cProvider.disbaleChatForThisUser && !isFromInit) checkHubCon(); _refreshController.refreshCompleted(); - await data.fetchTicketBooking(); + // await data.fetchTicketBooking(); + // if (data.ticketBookingResponse != null && !data.ticketBookingResponse!.success) { - // // data.fetchTicketBalance(); + // data.fetchTicketBalance(); // } + //continue here } @@ -422,8 +425,102 @@ class _DashboardScreenState extends State with WidgetsBindingOb ), ], ).paddingOnly(left: 21, right: 21, top: 7, bottom: 21), - eventActivityWidget(context), + Consumer( + builder: (BuildContext context, DashboardProviderModel model, Widget? child) { + if (model.isGreetingCardsLoading) { + return const GreetingCardShimmer(); + } else if (model.isDisplayEidGreetings && model.greetingCardsList != null && model.greetingCardsList!.isNotEmpty) { + return SizedBox( + height: 120, + child: ListView.separated( + shrinkWrap: true, + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.symmetric(horizontal: 21), + itemCount: model.greetingCardsList!.length, + separatorBuilder: (context, index) => const SizedBox(width: 12), + itemBuilder: (context, index) { + var card = model.greetingCardsList![index]; + return SizedBox( + width: MediaQuery.of(context).size.width - 42, + child: Directionality( + textDirection: AppState().isArabic(context) ? ui.TextDirection.rtl : ui.TextDirection.ltr, + child: Stack( + children: [ + Transform.flip( + flipX: !AppState().isArabic(context), + child: SvgPicture.network( + AppState().isArabic(context) + ? (card.backgroundImageUrlAr ?? "http://meena-health-care-1.s3.eu-north-1.amazonaws.com/eid_banner.svg") + : (card.backgroundImageUrlEn ?? "http://meena-health-care-1.s3.eu-north-1.amazonaws.com/eid_banner.svg"), + fit: BoxFit.contain, + placeholderBuilder: (context) => ClipRRect(borderRadius: BorderRadius.circular(18), child: Container(height: 80, color: Colors.white).toShimmer()), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(width: AppState().isArabic(context) ? 10 : 40), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + AppState().isArabic(context) ? card.titleAr ?? '' : card.titleEn ?? '', + style: TextStyle( + fontSize: AppState().isArabic(context) ? 20 : 18, + letterSpacing: -0.2, + fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', + fontWeight: FontWeight.w700, + height: 24 / 16, + color: const Color(0xFF3B3E4F), + ), + ), + const SizedBox(height: 4), + Text( + AppState().isArabic(context) ? card.descriptionAr ?? '' : card.descriptionEn ?? '', + style: TextStyle( + fontSize: AppState().isArabic(context) ? 16 : 14, + letterSpacing: -0.2, + fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', + fontWeight: FontWeight.w500, + color: const Color(0xFF3B3E4F), + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + (AppState().isArabic(context) + ? card.buttonTextN!.toText12(isUnderLine: true, color: const Color(0xFF3B3D4A)) + : card.buttonText!.toText12(isUnderLine: true, color: const Color(0xFF3B3D4A))) + .onPress(() { + launchUrl(Uri.parse(AppState().isArabic(context) ? card.urlAr! : card.urlEn!)); + }), + ], + ).paddingOnly(bottom: 10, right: 15, left: 15), + ], + ).paddingOnly(left: AppState().isArabic(context) ? 10 : 36, right: AppState().isArabic(context) ? 66 : 10, top: 6, bottom: 40), + ], + ), + ), + ); + }, + ), + ).paddingOnly(bottom: 0); + } else { + return const SizedBox(); + } + }, + ), + // eventActivityWidget(context), if (isDisplayMazaya) ...[ Column( mainAxisSize: MainAxisSize.min, @@ -470,7 +567,7 @@ class _DashboardScreenState extends State with WidgetsBindingOb fontFamily: AppState().isArabic(context) ? 'Cairo' : 'Poppins', fontWeight: FontWeight.w700, height: 24 / 16, - color: Color(0xFF5D5E5E), + color: const Color(0xFF5D5E5E), ), ), TextSpan( diff --git a/lib/ui/landing/itg/its_add_screen_video_image.dart b/lib/ui/landing/itg/its_add_screen_video_image.dart index 2076d7c..23bfc66 100644 --- a/lib/ui/landing/itg/its_add_screen_video_image.dart +++ b/lib/ui/landing/itg/its_add_screen_video_image.dart @@ -6,14 +6,19 @@ import 'package:mohem_flutter_app/app_state/app_state.dart'; import 'package:mohem_flutter_app/classes/colors.dart'; import 'package:mohem_flutter_app/classes/lottie_consts.dart'; import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/config/routes.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; import 'package:mohem_flutter_app/main.dart'; +import 'package:mohem_flutter_app/models/dashboard/menu_entries.dart'; import 'package:mohem_flutter_app/models/itg/advertisement.dart' as ads; +import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; +import 'package:mohem_flutter_app/ui/my_attendance/services_menu_list_screen.dart'; import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; import 'package:mohem_flutter_app/widgets/button/default_button.dart'; import 'package:mohem_flutter_app/widgets/my_video_progress_indicator.dart'; +import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:video_player/video_player.dart'; @@ -26,7 +31,7 @@ class ITGAdsScreen extends StatefulWidget { class _ITGAdsScreenState extends State { late Future _futureController; - late VideoPlayerController _controller; + VideoPlayerController? _controller; bool skip = false; bool isVideo = false; bool isImage = false; @@ -125,7 +130,7 @@ class _ITGAdsScreenState extends State { @override void dispose() { - if (_controller != null) _controller.dispose(); + _controller?.dispose(); super.dispose(); } @@ -166,11 +171,11 @@ class _ITGAdsScreenState extends State { child: isAudio ? Lottie.asset(MyLottieConsts.audioPlaybackLottie) : AspectRatio( - aspectRatio: _controller.value.aspectRatio, + aspectRatio: _controller!.value.aspectRatio, child: Stack( alignment: Alignment.bottomCenter, children: [ - VideoPlayer(_controller), + VideoPlayer(_controller!), if (advertisementData?.isOptional ?? false) Positioned( top: 4, @@ -230,7 +235,7 @@ class _ITGAdsScreenState extends State { alignment: Alignment.center, children: [ MyVideoProgressIndicator( - _controller, + _controller!, allowScrubbing: false, //padding: EdgeInsets.only(left: 60, right: 60), ), @@ -386,16 +391,39 @@ class _ITGAdsScreenState extends State { if (advertisementData!.actionButtonsColl![index].actionValue.toLowerCase() == "skip") { Navigator.pop(context); } else { - Uri uri = Uri.parse(advertisementData!.actionButtonsColl![index].iconOrImage); - // if (await canLaunchUrl(uri)) { - await launchUrl(uri, mode: LaunchMode.externalApplication).catchError((err) { - // print(err); - Utils.showToast('Could not launch'); - }); - Navigator.pop(context); - // } else { - // Utils.showToast('Could not launch'); - // } + if (advertisementData!.actionButtonsColl![index].actionType!.toLowerCase() == 'redirect' && advertisementData!.actionButtonsColl![index].actionValue!.toLowerCase() == + 'external') { + Uri uri = Uri.parse(advertisementData!.actionButtonsColl![index].url!); + await launchUrl(uri, mode: LaunchMode.externalApplication).catchError((err) { + // print(err); + Utils.showToast('Could not launch'); + }); + Navigator.pop(context); + } else if (advertisementData!.actionButtonsColl![index].actionType!.toLowerCase() == 'redirect' && advertisementData!.actionButtonsColl![index].actionValue!.toLowerCase() == + 'internal') { + + await Navigator.pushNamed(context, advertisementData!.actionButtonsColl![index].url!); + + } else if(advertisementData!.actionButtonsColl![index].actionType!.toLowerCase() == 'eit' && advertisementData!.actionButtonsColl![index].actionValue!.toLowerCase() == + 'internal'){ + var pro = Provider.of(context, listen: false); + List _menuList = + pro.getMenuEntriesList?.where((element) => element.parentMenuName == advertisementData!.actionButtonsColl![index].url && (element.menuEntryType == "FUNCTION" || element.menuEntryType == "MENU")).toList() ?? []; + Navigator.pushNamed(context, AppRoutes.servicesMenuListScreen, arguments: ServicesMenuListScreenParams(advertisementData!.advertisementTitle!, _menuList)); + + } + else{ + Uri uri = Uri.parse(advertisementData!.actionButtonsColl![index].iconOrImage); + // if (await canLaunchUrl(uri)) { + await launchUrl(uri, mode: LaunchMode.externalApplication).catchError((err) { + // print(err); + Utils.showToast('Could not launch'); + }); + Navigator.pop(context); + // } else { + // Utils.showToast('Could not launch'); + // } + } } }, ), diff --git a/lib/ui/landing/widget/app_drawer.dart b/lib/ui/landing/widget/app_drawer.dart index 0eb0e52..44843be 100644 --- a/lib/ui/landing/widget/app_drawer.dart +++ b/lib/ui/landing/widget/app_drawer.dart @@ -166,4 +166,14 @@ class _AppDrawerState extends State { widget.onLanguageChange(); setState(() {}); } + + void performLogout() async { + AppState().isAuthenticated = false; + AppState().isLogged = false; + AppState().setPostParamsInitConfig(); + chatData.disposeData(); + // SharedPreferences prefs = await SharedPreferences.getInstance(); + // await prefs.clear(); + Navigator.pushNamedAndRemoveUntil(context, AppRoutes.login, (Route route) => false, arguments: null); + } } diff --git a/lib/ui/landing/widget/menus_widget.dart b/lib/ui/landing/widget/menus_widget.dart index 72df91c..6abfb35 100644 --- a/lib/ui/landing/widget/menus_widget.dart +++ b/lib/ui/landing/widget/menus_widget.dart @@ -123,30 +123,28 @@ class MenusWidget extends StatelessWidget { ], ).paddingOnly(left: 10, right: 10, bottom: 6, top: 6), ).onPress(() async { + Navigator.pushNamed(context, AppRoutes.dynamicScreen, arguments: DynamicListViewParams(LocaleKeys.ticketBalance.tr(), "HMG_TKT_NEW_EIT_SS")); + + // var pro = Provider.of(context, listen: false); // - // // Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => TicketDetailedScreen(url: url, jwtToken: ssoToken.data!.accessToken))); + // // 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); // - // 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")); - } + // 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")); + // } }), ], ); diff --git a/lib/ui/landing/widget/services_widget.dart b/lib/ui/landing/widget/services_widget.dart index deea7e9..0a30857 100644 --- a/lib/ui/landing/widget/services_widget.dart +++ b/lib/ui/landing/widget/services_widget.dart @@ -92,7 +92,7 @@ class ServicesWidget extends StatelessWidget { aspectRatio: 105 / 105, child: data.isServicesMenusLoading - ? ServicesMenuShimmer() + ? const ServicesMenuShimmer() : Container( decoration: BoxDecoration( color: Colors.white, @@ -257,16 +257,15 @@ class ServicesWidget extends StatelessWidget { } } else { if (menuEntry.menuName == "HMG_TICKET_REQUESTS") { - Utils.showLoading(context); //Ticket Work if (pro.ticketBookingResponse != null && pro.ticketBookingResponse!.success) { + Utils.showLoading(context); 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))); @@ -311,7 +310,7 @@ class ServicesWidget extends StatelessWidget { padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), scrollDirection: Axis.horizontal, itemBuilder: (cxt, index) { - return AspectRatio(aspectRatio: 105 / 105, child: ServicesMenuShimmer()); + return AspectRatio(aspectRatio: 105 / 105, child: const ServicesMenuShimmer()); }, separatorBuilder: (cxt, index) => 9.width, itemCount: 4, diff --git a/lib/ui/parking_qr/parking_qr.dart b/lib/ui/parking_qr/parking_qr.dart new file mode 100644 index 0000000..641f84a --- /dev/null +++ b/lib/ui/parking_qr/parking_qr.dart @@ -0,0 +1,106 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/dashboard_api_client.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/get_employee_parking_details_model.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; + +class ParkingQrScreen extends StatefulWidget { + const ParkingQrScreen({Key? key}) : super(key: key); + + @override + _RequestDetailsState createState() => _RequestDetailsState(); +} + +class _RequestDetailsState extends State { + GetEmployeeParking? qrData; + + @override + void initState() { + super.initState(); + getTicketDetails(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget(context, title: LocaleKeys.parkingQr.tr()), + body: + qrData?.getEmployeeParking == null + ? Center(child: Text(LocaleKeys.noDataAvailable.tr())) + : SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Center( + child: Container( + padding: const EdgeInsets.all(20.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16), + boxShadow: [BoxShadow(color: Colors.grey.withValues(alpha: 0.2), spreadRadius: 5, blurRadius: 10, offset: const Offset(0, 3))], + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // QR Code Image + if (qrData!.getEmployeeParking!.qrCodeImageBase64 != null && qrData!.getEmployeeParking!.qrCodeImageBase64!.isNotEmpty) + Image.memory(Utils.dataFromBase64String(qrData!.getEmployeeParking!.qrCodeImageBase64!), width: 300, height: 300, fit: BoxFit.contain) + else + Container( + width: 300, + height: 300, + color: MyColors.lightGreyEFColor, + child: Center(child: Text(LocaleKeys.noDataAvailable.tr(), style: const TextStyle(color: MyColors.grey67Color))), + ), + 10.height, + Container(height: 1, color: MyColors.lightGreyE3Color, margin: const EdgeInsets.symmetric(vertical: 10)), + 20.height, + SizedBox( + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Employee Code + if (qrData!.getEmployeeParking!.employeeCode != null && qrData!.getEmployeeParking!.employeeCode!.isNotEmpty) ...[ + Text(LocaleKeys.employeeNumber.tr(), style: const TextStyle(fontSize: 14, color: MyColors.grey67Color, fontWeight: FontWeight.w400)), + 8.height, + Text(qrData!.getEmployeeParking!.employeeCode!, style: const TextStyle(fontSize: 18, color: MyColors.darkTextColor, fontWeight: FontWeight.w600)), + 8.height, + ], + if (qrData!.getEmployeeParking!.createdAt != null) ...[ + Text(LocaleKeys.creationDate.tr(), style: const TextStyle(fontSize: 14, color: MyColors.grey67Color, fontWeight: FontWeight.w400)), + 8.height, + Text( + DateFormat('dd MMM yyyy, hh:mm a').format(qrData!.getEmployeeParking!.createdAt!), + style: const TextStyle(fontSize: 16, color: MyColors.darkTextColor, fontWeight: FontWeight.w500), + ), + ], + ], + ), + ), + ], + ), + ), + ), + ), + ), + ); + } + + void getTicketDetails() async { + try { + Utils.showLoading(context); + qrData = await DashboardApiClient().getEmployeeParkingQrCode(); + Utils.hideLoading(context); + setState(() {}); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } +} diff --git a/lib/ui/screens/courses/courses_screen.dart b/lib/ui/screens/courses/courses_screen.dart new file mode 100644 index 0000000..598b6cf --- /dev/null +++ b/lib/ui/screens/courses/courses_screen.dart @@ -0,0 +1,333 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/inAppWebView.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; +import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; +import 'package:mohem_flutter_app/models/dashboard/courses_response_model.dart'; +import 'package:mohem_flutter_app/provider/dashboard_provider_model.dart'; +import 'package:mohem_flutter_app/theme/colors.dart' as theme_colors; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; +import 'package:mohem_flutter_app/widgets/sso_webview_widget.dart'; +import 'package:provider/provider.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; + +class CoursesScreen extends StatefulWidget { + const CoursesScreen({Key? key}) : super(key: key); + + @override + _CoursesScreenState createState() => _CoursesScreenState(); +} + +class _CoursesScreenState extends State { + late DashboardProviderModel data; + final RefreshController _refreshController = RefreshController(initialRefresh: false); + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + data = Provider.of(context, listen: false); + _onRefresh(); + }); + } + + void _onRefresh() async { + await data.fetchCourses(context); + _refreshController.refreshCompleted(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBarWidget( + context, + title: LocaleKeys.courses.tr(), + ), + body: Consumer( + builder: (context, provider, child) { + return SmartRefresher( + enablePullDown: true, + enablePullUp: false, + controller: _refreshController, + onRefresh: _onRefresh, + child: provider.isCoursesLoading + ? _buildShimmerLoading() + : provider.coursesList.isEmpty + ? _buildEmptyState() + : _buildCoursesList(provider.coursesList), + ); + }, + ), + ); + } + + Widget _buildShimmerLoading() { + return ListView.builder( + padding: const EdgeInsets.all(21), + itemCount: 5, + itemBuilder: (context, index) { + return Container( + margin: const EdgeInsets.only(bottom: 12), + height: 150, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + height: 20, + width: 200, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(4), + ), + ).toShimmer(), + 12.height, + Container( + height: 14, + width: 100, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(4), + ), + ).toShimmer(), + const Spacer(), + Container( + height: 40, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(6), + ), + ).toShimmer(), + ], + ).paddingAll(14), + ); + }, + ); + } + + Widget _buildEmptyState() { + return Utils.getNoDataWidget(context); + } + + Widget _buildCoursesList(List courses) { + return ListView.separated( + physics: const BouncingScrollPhysics(), + shrinkWrap: true, + padding: const EdgeInsets.all(21), + itemBuilder: (BuildContext context, int index) { + return _buildCourseCard(courses[index]); + }, + separatorBuilder: (BuildContext context, int index) => 12.height, + itemCount: courses.length, + ); + } + + Widget _buildCourseCard(CourseStatus course) { + bool isArabic = AppState().isArabic(context); + String courseName = isArabic && course.cNameAr != null && course.cNameAr!.isNotEmpty + ? course.cNameAr! + : course.cFullname ?? ''; + + String status = course.cStatus ?? 'Unknown'; + Color statusColor = _getStatusColor(status); + + return InkWell( + onTap: () => _openCourse(course.cUrl, course.token), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Course Title + Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: MyColors.lightGreyEFColor, + borderRadius: BorderRadius.circular(8), + ), + child: const Icon( + Icons.school_rounded, + color: MyColors.gradiantStartColor, + size: 20, + ), + ), + 12.width, + Expanded( + child: courseName.toText14(isBold: true, color: MyColors.darkTextColor), + ), + ], + ), + 12.height, + // Status Badge + Row( + children: [ + Icon( + _getStatusIcon(status), + size: 16, + color: statusColor, + ), + 6.width, + LocaleKeys.status.tr().toText11(color: MyColors.grey98Color), + 8.width, + Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + decoration: BoxDecoration( + color: statusColor.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: statusColor.withOpacity(0.3), + width: 1, + ), + ), + child: status.toText10(color: statusColor, isBold: true), + ), + ], + ), + 10.height, + // Start Date + if (course.cEnddate != null && course.cEnddate!.isNotEmpty) + Row( + children: [ + const Icon( + Icons.calendar_month_rounded, + size: 16, + color: MyColors.darkIconColor, + ), + 6.width, + LocaleKeys.endDateT.tr().toText11(color: MyColors.grey98Color), + 8.width, + Expanded( + child: _formatDate(course.cEnddate!).toText11( + color: MyColors.darkTextColor, + isBold: true, + ), + ), + ], + ), + if (course.cStartdate != null && course.cStartdate!.isNotEmpty) 14.height, + // Open Button + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + LocaleKeys.open.tr().toText12(color: MyColors.gradiantStartColor, isUnderLine: true), + 6.width, + RotatedBox( + quarterTurns: AppState().isArabic(context) ? 2 : 4, + child: SvgPicture.asset( + "assets/images/arrow_next.svg", + color: MyColors.gradiantStartColor, + width: 16, + height: 16, + ), + ), + ], + ), + ], + ).objectContainerView(), + ); + } + + + IconData _getStatusIcon(String status) { + if (status.toLowerCase().contains('passed')) { + return Icons.check_circle_rounded; + } else if (status.toLowerCase().contains('not passed') || status.toLowerCase().contains('failed')) { + return Icons.cancel_rounded; + } else if (status.toLowerCase().contains('in progress')) { + return Icons.pending_rounded; + } else { + return Icons.info_rounded; + } + } + + Color _getStatusColor(String status) { + if (status.toLowerCase().contains('passed') && !status.toLowerCase().contains('not passed')) { + return Colors.green; + } else if (status.toLowerCase().contains('not passed') || status.toLowerCase().contains('failed')) { + return Colors.red; + } else if (status.toLowerCase().contains('in progress')) { + return Colors.yellow[700]!; + } else { + return MyColors.grey98Color; + } + } + + DateTime? _parseDate(String? date) { + if (date == null || date.isEmpty) return null; + try { + return DateTime.parse(date.replaceAll(' ', 'T')); + } catch (e) { + try { + DateFormat format = DateFormat('yyyy-MM-dd HH:mm:ss'); + return format.parse(date); + } catch (e) { + return null; + } + } + } + + String _formatDate(String date) { + DateTime? dateTime = _parseDate(date); + if (dateTime == null) return date; + + bool isArabic = AppState().isArabic(context); + DateFormat format = DateFormat('dd MMM yyyy, hh:mm a', isArabic ? 'ar' : 'en'); + return format.format(dateTime); + } + + void _openCourse(String? url, String? jwtToken) { + + + Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: url, jwtToken: jwtToken))); + //Utils.showToast(LocaleKeys.courseUrlNotAvailable.tr()); + + + // return; + // } + // + // MyInAppBrowser browser = MyInAppBrowser( + // onExitCallback: () {}, + // onLoadStartCallback: (String url) {}, + // ); + // + // var settings = InAppBrowserClassSettings( + // webViewSettings: InAppWebViewSettings( + // useShouldOverrideUrlLoading: false, + // transparentBackground: false, + // ), + // browserSettings: InAppBrowserSettings( + // hideUrlBar: true, + // hideTitleBar: false, + // toolbarTopBackgroundColor: theme_colors.primaryColor, + // ), + // ); + // + // browser.openUrlRequest( + // urlRequest: URLRequest(url: WebUri(url)), + // settings: settings, + // ); + } + + @override + void dispose() { + _refreshController.dispose(); + super.dispose(); + } +} diff --git a/lib/ui/screens/mowadhafhi/mowadhafhi_hr_request.dart b/lib/ui/screens/mowadhafhi/mowadhafhi_hr_request.dart index 44b3833..e7d2fde 100644 --- a/lib/ui/screens/mowadhafhi/mowadhafhi_hr_request.dart +++ b/lib/ui/screens/mowadhafhi/mowadhafhi_hr_request.dart @@ -150,7 +150,9 @@ class _MowadhafhiHRRequestState extends State { isReadOnly: false, onChange: (String value) { debugPrint(value); - description = value; + setState(() { + description = value; + }); }, ), ], @@ -171,7 +173,7 @@ class _MowadhafhiHRRequestState extends State { } bool checkValidation() { - if (selectedServiceType == null || selectedDepartment == null || selectedSection == null || selectedTopic == null) { + if (selectedServiceType == null || selectedDepartment == null || selectedSection == null || selectedTopic == null || description == "") { return false; } else { return true; diff --git a/lib/ui/screens/ticket/ticket_detailed_screen.dart b/lib/ui/screens/ticket/ticket_detailed_screen.dart index f5b3583..aa2a301 100644 --- a/lib/ui/screens/ticket/ticket_detailed_screen.dart +++ b/lib/ui/screens/ticket/ticket_detailed_screen.dart @@ -10,6 +10,7 @@ import 'package:mohem_flutter_app/classes/utils.dart'; import 'package:mohem_flutter_app/extensions/int_extensions.dart'; import 'package:mohem_flutter_app/extensions/string_extensions.dart'; import 'package:mohem_flutter_app/extensions/widget_extensions.dart'; +import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:mohem_flutter_app/models/dashboard/get_accural_ticket_balance_model.dart'; import 'package:mohem_flutter_app/models/eit/get_eit_transaction_model.dart'; import 'package:mohem_flutter_app/models/eit_attachment_ticket_model.dart'; @@ -58,7 +59,7 @@ class _TicketDetailedScreenState extends State { Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, - appBar: AppBarWidget(context, title: "Ticket Details"), + appBar: AppBarWidget(context, title:LocaleKeys.ticketDetails.tr()), body: Column( children: [ ListView( @@ -77,7 +78,7 @@ class _TicketDetailedScreenState extends State { children: [ Row( children: [ - "Current Ticket Balance".toText20().expanded, + LocaleKeys.currentTicketBalance.tr().toText20().expanded, Row( children: [ const Icon(Icons.calendar_month_rounded, color: MyColors.darkIconColor, size: 16), @@ -120,7 +121,7 @@ class _TicketDetailedScreenState extends State { shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), children: [ - "Tickets History".toText20(), + LocaleKeys.ticketHistory.tr().toText20().expanded, 12.height, ListView.separated( physics: const NeverScrollableScrollPhysics(), @@ -223,7 +224,7 @@ class _TicketDetailedScreenState extends State { ], ], ).expanded, - DefaultButton("Proceed For Booking", () { + DefaultButton(LocaleKeys.proceedBooking.tr(), () { if (widget.url != null && widget.jwtToken != null) { Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => SsoLoginWebView(url: widget.url, jwtToken: widget.jwtToken))); } diff --git a/lib/ui/work_list/itg_fragments/approval_level_fragment.dart b/lib/ui/work_list/itg_fragments/approval_level_fragment.dart index d7d9c7e..9d7e94a 100644 --- a/lib/ui/work_list/itg_fragments/approval_level_fragment.dart +++ b/lib/ui/work_list/itg_fragments/approval_level_fragment.dart @@ -21,7 +21,11 @@ class ApprovalLevelfragment extends StatelessWidget { @override Widget build(BuildContext context) { - if (wFHistory.isNotEmpty) wFHistory = wFHistory.reversed.toList(); + if (wFHistory.isNotEmpty) { + //TODO: This COde is Written For Future. If Needed Enable to Filter The Empty Action Requests. + // wFHistory = wFHistory.where((item) => item.action != null && item.action!.isNotEmpty).toList(); + wFHistory = wFHistory.reversed.toList(); + } return Container( width: double.infinity, height: double.infinity, @@ -164,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/lib/ui/work_list/sheets/delegate_sheet.dart b/lib/ui/work_list/sheets/delegate_sheet.dart index ef38ce0..d72b041 100644 --- a/lib/ui/work_list/sheets/delegate_sheet.dart +++ b/lib/ui/work_list/sheets/delegate_sheet.dart @@ -1,5 +1,4 @@ import 'dart:collection'; -import 'dart:convert'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/cupertino.dart'; @@ -33,16 +32,8 @@ class DelegateSheet extends StatefulWidget { VoidCallback callBackFunc; List getNotificationRespondAttributes; - DelegateSheet({ - super.key, - required this.title, - required this.apiMode, - this.notificationID, - this.actionHistoryList, - this.wFHistory, - required this.callBackFunc, - this.getNotificationRespondAttributes = const [], - }); + DelegateSheet( + {required this.title, required this.apiMode, this.notificationID, this.actionHistoryList, this.wFHistory, required this.callBackFunc, this.getNotificationRespondAttributes = const []}); @override State createState() => _DelegateSheetState(); @@ -55,8 +46,8 @@ class _DelegateSheetState extends State { String? selectedFavLetter; String selectedType = "Workflow"; String inputRes = ""; + List? favLetters; List? favUsersList; - List? filteredFavUsersList = []; List? replacementList; bool isImageLoaded = false; @@ -66,14 +57,14 @@ class _DelegateSheetState extends State { super.initState(); if (widget.wFHistory != null) { widget.wFHistory = widget.wFHistory!.reversed.toList(); - Set ids = widget.wFHistory!.map((WFHistory e) => e.employeeID).toSet(); - widget.wFHistory!.retainWhere((WFHistory x) => ids.remove(x.employeeID)); + var ids = widget.wFHistory!.map((e) => e.employeeID).toSet(); + widget.wFHistory!.retainWhere((x) => ids.remove(x.employeeID)); } if (widget.actionHistoryList != null) { widget.actionHistoryList = widget.actionHistoryList!.reversed.toList(); - Set ids = widget.actionHistoryList!.map((GetActionHistoryList e) => e.uSERNAME).toSet(); - widget.actionHistoryList!.retainWhere((GetActionHistoryList x) => ids.remove(x.uSERNAME)); + var ids = widget.actionHistoryList!.map((e) => e.uSERNAME).toSet(); + widget.actionHistoryList!.retainWhere((x) => ids.remove(x.uSERNAME)); } } @@ -82,96 +73,36 @@ class _DelegateSheetState extends State { super.dispose(); } - Future fetchFavUsers({bool isNeedLoading = true}) async { - favUsersList = []; - filteredFavUsersList = []; - + Future fetchFavLetters({bool isNeedLoading = true}) async { + favLetters = []; if (isNeedLoading) Utils.showLoading(context); - - try { - List? favList = await WorkListApiClient().getFavoriteReplacementWithoutImage(); - favUsersList = favList; - - if (favUsersList != null && favUsersList!.isNotEmpty) { - favUsersList!.sort((GetFavoriteReplacements a, GetFavoriteReplacements b) => (a.employeeDisplayName ?? '').toLowerCase().compareTo((b.employeeDisplayName ?? '').toLowerCase())); - filteredFavUsersList = favUsersList; - - if (isNeedLoading) Utils.hideLoading(context); - setState(() {}); - _fetchAndAttachUserImages(); - } else { - if (isNeedLoading) Utils.hideLoading(context); - setState(() {}); - } - } catch (e) { - if (isNeedLoading) Utils.hideLoading(context); - setState(() {}); - } + List? favList = await WorkListApiClient().getFavoriteReplacementWithoutImage(); + List result = []; + favList!.forEach((element) { + result.add(element.employeeDisplayName![0]); + }); + favLetters = LinkedHashSet.from(result).toList(); + if (isNeedLoading) Utils.hideLoading(context); + setState(() { + favLetters!.sort((a, b) { + return a.toLowerCase().compareTo(b.toLowerCase()); + }); + }); + return null; } - Future _fetchAndAttachUserImages() async { - try { - List? tempList = await WorkListApiClient().getFavoriteReplacementWithImageNew(""); - - if (tempList != null && tempList.isNotEmpty && favUsersList != null) { - for (GetFavoriteReplacements element in favUsersList!) { - for (GetFavoriteReplacements element2 in tempList) { - if (element.userName == element2.userName && element2.employeeImage != null && element2.employeeImage!.isNotEmpty) { - element.employeeImage = element2.employeeImage; - } - } - } - filteredFavUsersList = favUsersList; - setState(() {}); - } - } catch (e) {} + Future fetchFavUsersList({bool isNeedLoading = true}) async { + if (isNeedLoading) Utils.showLoading(context); + favUsersList = await WorkListApiClient().getFavoriteReplacementWithImage(selectedFavLetter ?? ""); + if (isNeedLoading) Utils.hideLoading(context); + setState(() {}); + fetchFavUsersListNew(isNeedLoading: false); + return null; } - // Future fetchFavUsers({bool isNeedLoading = true}) async { - // favUsersList = []; - // if (isNeedLoading) Utils.showLoading(context); - // - // try { - // List? favList = await WorkListApiClient().getFavoriteReplacementWithoutImage(); - // favUsersList = favList; - // - // if (favUsersList != null && favUsersList!.isNotEmpty) { - // setState(() { - // favUsersList!.sort((GetFavoriteReplacements a, GetFavoriteReplacements b) => (a.employeeDisplayName ?? '').toLowerCase().compareTo((b.employeeDisplayName ?? '').toLowerCase())); - // }); - // if (isNeedLoading) Utils.hideLoading(context); - // - // List? tempList = await WorkListApiClient().getFavoriteReplacementWithImageNew(""); - // if (tempList != null && tempList.isNotEmpty) { - // for (GetFavoriteReplacements element in favUsersList!) { - // for (GetFavoriteReplacements element2 in tempList) { - // if (element.userName == element2.userName) { - // if (element2.employeeImage != null && element2.employeeImage!.isNotEmpty) { - // element.employeeImage = element2.employeeImage; - // } - // } - // } - // } - // setState(() {}); - // } - // } - // } catch (e) { - // // if (isNeedLoading) Utils.hideLoading(context); - // } - // } - - // Future fetchFavUsersList({bool isNeedLoading = true}) async { - // if (isNeedLoading) Utils.showLoading(context); - // favUsersList = await WorkListApiClient().getFavoriteReplacementWithImage(selectedFavLetter ?? ""); - // if (isNeedLoading) Utils.hideLoading(context); - // setState(() {}); - // fetchFavUsersListNew(isNeedLoading: false); - // return null; - // } - Future fetchFavUsersListNew({bool isNeedLoading = true}) async { if (isNeedLoading) Utils.showLoading(context); - filteredFavUsersList = await WorkListApiClient().getFavoriteReplacementWithImageNew(selectedFavLetter ?? ""); + favUsersList = await WorkListApiClient().getFavoriteReplacementWithImageNew(selectedFavLetter ?? ""); if (isNeedLoading) Utils.hideLoading(context); setState(() { isImageLoaded = true; @@ -181,10 +112,18 @@ class _DelegateSheetState extends State { void fetchChangeFav({required String email, required String employeName, required String image, required String userName, bool isFav = false, bool isNeedToRefresh = false}) async { Utils.showLoading(context); - GenericResponseModel model = await WorkListApiClient().changeFavoriteReplacements(email: email, employeName: employeName, image: image, userName: userName, isFav: isFav); + favLetters = null; + selectedFavLetter = null; + GenericResponseModel model = await WorkListApiClient().changeFavoriteReplacements( + email: email, + employeName: employeName, + image: image, + userName: userName, + isFav: isFav, + ); if (isNeedToRefresh) { - await fetchFavUsers(isNeedLoading: false); - // if (favUsersList != null) await fetchFavUsersList(isNeedLoading: false); + await fetchFavLetters(isNeedLoading: false); + if (favLetters != null) await fetchFavUsersList(isNeedLoading: false); } Utils.hideLoading(context); @@ -198,58 +137,44 @@ class _DelegateSheetState extends State { userName: selectedType == "Employee Name" ? inputRes : "", email: selectedType == "Employee Email" ? inputRes : "", ); - if (isNeedLoading) Utils.hideLoading(context); setState(() {}); return null; } - Future searchFavUser() async { - if (favUsersList != null && favUsersList!.isNotEmpty) { - // if (inputRes.length > 0) { - filteredFavUsersList = - favUsersList!.where((GetFavoriteReplacements element) { - String query = inputRes.toLowerCase(); - return (element.employeeDisplayName != null && element.employeeDisplayName!.toLowerCase().startsWith(query)) || - (element.userName != null && element.userName!.toLowerCase().contains(query)) || - (element.emailAddress != null && element.emailAddress!.toLowerCase().contains(query)); - }).toList(); - // } else { - // filteredFavUsersList = favUsersList; - // } - } else { - filteredFavUsersList = []; - } - - setState(() {}); - } - @override Widget build(BuildContext context) { - return SizedBox( + return Container( width: double.infinity, height: MediaQuery.of(context).size.height - 80, child: Column( - children: [ + children: [ Expanded( child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(21), child: Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ + children: [ widget.title.toText24(isBold: true), 21.height, LocaleKeys.search.tr().toText16(), 11.height, Column( crossAxisAlignment: CrossAxisAlignment.end, - children: [ + children: [ Container( - padding: const EdgeInsets.only(left: 8, right: 4, top: 6, bottom: 6), - decoration: BoxDecoration(borderRadius: BorderRadius.circular(15), color: Colors.transparent, border: Border.all(color: const Color(0xffefefef), width: 1)), + padding: EdgeInsets.only(left: 8, right: 4, top: 6, bottom: 6), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(15), + color: Colors.transparent, + border: Border.all( + color: Color(0xffefefef), + width: 1, + ), + ), child: Row( - children: [ + children: [ Expanded( child: InputWidget( "Search By " + selectedType, @@ -260,18 +185,29 @@ class _DelegateSheetState extends State { verticalPadding: 3, isInputTypeNum: selectedType == "Employee ID" ? true : false, isEnable: isNeedEnableTextField, - onChange: (String v) { + onChange: (v) { inputRes = v; - if (selectedType == "Favorites") { - searchFavUser(); - } }, ), ), - Container(height: 36, width: 1, color: const Color(0xffE5E5E5)), Container( - padding: const EdgeInsets.all(8), - child: Row(children: [selectedType.toText12(), 4.width, const Icon(Icons.keyboard_arrow_down, color: Colors.black, size: 16)]), + height: 36, + width: 1, + color: Color(0xffE5E5E5), + ), + Container( + padding: EdgeInsets.all(8), + child: Row( + children: [ + selectedType.toText12(), + 4.width, + const Icon( + Icons.keyboard_arrow_down, + color: Colors.black, + size: 16, + ), + ], + ), ).onPress(() { showMyBottomSheet( context, @@ -283,17 +219,19 @@ class _DelegateSheetState extends State { if (value == "Workflow") { setState(() { isNeedEnableTextField = false; - // selectedFavLetter = null; - filteredFavUsersList = null; + selectedFavLetter = null; + favLetters = null; + favUsersList = null; }); } else if (value == "Favorites") { - isNeedEnableTextField = true; - fetchFavUsers(); + isNeedEnableTextField = false; + fetchFavLetters(); } else { setState(() { isNeedEnableTextField = true; - // selectedFavLetter = null; - filteredFavUsersList = null; + selectedFavLetter = null; + favLetters = null; + favUsersList = null; }); } }, @@ -303,105 +241,143 @@ class _DelegateSheetState extends State { ], ), ), - if (isNeedEnableTextField && selectedType != "Favorites") + if (isNeedEnableTextField) TextButton( onPressed: () { fetchUserByInput(); }, - child: const Text("Search", style: TextStyle(color: Colors.blue, decoration: TextDecoration.underline)), - ), + child: const Text( + "Search", + style: TextStyle( + color: Colors.blue, + decoration: TextDecoration.underline, + ), + ), + ) ], ), if (!isNeedEnableTextField) 12.height, Row( - children: [ + children: [ Expanded( - child: SizedBox( + child: Container( width: double.infinity, - child: - // selectedFavLetter == null && favLetters != null - // ? Container(width: double.infinity, alignment: Alignment.center, child: "Please select letter to see Fav results".toText12()) - // : - filteredFavUsersList != null && selectedType == "Favorites" - ? ListView.separated( - itemBuilder: (BuildContext context, int index) { - return showFavUserItem(filteredFavUsersList![index]); - }, - separatorBuilder: (BuildContext context, int index) { - return Container(color: MyColors.borderColor, width: double.infinity, height: 1, margin: const EdgeInsets.only(top: 8, bottom: 8)).onPress(() {}); - }, - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: filteredFavUsersList!.length, - padding: const EdgeInsets.only(top: 8, bottom: 8), - ) - : isNeedEnableTextField && replacementList == null - ? Container(width: double.infinity, alignment: Alignment.center, child: "Search User".toText12()) - : isNeedEnableTextField && replacementList!.isEmpty - ? Container(width: double.infinity, alignment: Alignment.center, child: "No Data Found".toText12()) - : isNeedEnableTextField - ? ListView.separated( - itemBuilder: (BuildContext context, int index) { - return showInputUserItem(replacementList![index]); - }, - separatorBuilder: (BuildContext context, int index) { - return Container(color: MyColors.borderE3Color, width: double.infinity, height: 1, margin: const EdgeInsets.only(top: 8, bottom: 8)); - }, - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: replacementList!.length, - padding: const EdgeInsets.only(top: 8, bottom: 8), - ) - : (widget.wFHistory != null - ? ListView.separated( - itemBuilder: (BuildContext context, int index) { - return showItgItem(widget.wFHistory![index]); - }, - separatorBuilder: (BuildContext context, int index) { - return Container(color: MyColors.borderE3Color, width: double.infinity, height: 1, margin: const EdgeInsets.only(top: 8, bottom: 8)); - }, - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: widget.wFHistory!.length, - padding: const EdgeInsets.only(top: 8, bottom: 8), - ) - : ListView.separated( - itemBuilder: (BuildContext context, int index) { - return showItem(widget.actionHistoryList![index]); - }, - separatorBuilder: (BuildContext context, int index) { - return Container(color: MyColors.borderE3Color, width: double.infinity, height: 1, margin: const EdgeInsets.only(top: 8, bottom: 8)); - }, - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: widget.actionHistoryList!.length, - padding: const EdgeInsets.only(top: 8, bottom: 8), - )), + child: selectedFavLetter == null && favLetters != null + ? Container( + width: double.infinity, + alignment: Alignment.center, + child: "Please select letter to see Fav results".toText12(), + ) + : favUsersList != null + ? ListView.separated( + itemBuilder: (context, index) { + return showFavUserItem(favUsersList![index]); + }, + separatorBuilder: (context, index) { + return Container( + color: MyColors.borderColor, + width: double.infinity, + height: 1, + margin: EdgeInsets.only(top: 8, bottom: 8), + ).onPress(() {}); + }, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: favUsersList!.length, + padding: EdgeInsets.only(top: 8, bottom: 8), + ) + : isNeedEnableTextField && replacementList == null + ? Container( + width: double.infinity, + alignment: Alignment.center, + child: "Search User".toText12(), + ) + : isNeedEnableTextField && replacementList!.length == 0 + ? Container( + width: double.infinity, + alignment: Alignment.center, + child: "No Data Found".toText12(), + ) + : isNeedEnableTextField + ? ListView.separated( + itemBuilder: (context, index) { + return showInputUserItem(replacementList![index]); + }, + separatorBuilder: (context, index) { + return Container( + color: MyColors.borderE3Color, + width: double.infinity, + height: 1, + margin: EdgeInsets.only(top: 8, bottom: 8), + ); + }, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: replacementList!.length, + padding: EdgeInsets.only(top: 8, bottom: 8), + ) + : (widget.wFHistory != null + ? ListView.separated( + itemBuilder: (context, index) { + return showItgItem(widget.wFHistory![index]); + }, + separatorBuilder: (context, index) { + return Container( + color: MyColors.borderE3Color, + width: double.infinity, + height: 1, + margin: EdgeInsets.only(top: 8, bottom: 8), + ); + }, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: widget.wFHistory!.length, + padding: EdgeInsets.only(top: 8, bottom: 8), + ) + : ListView.separated( + itemBuilder: (context, index) { + return showItem(widget.actionHistoryList![index]); + }, + separatorBuilder: (context, index) { + return Container( + color: MyColors.borderE3Color, + width: double.infinity, + height: 1, + margin: EdgeInsets.only(top: 8, bottom: 8), + ); + }, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: widget.actionHistoryList!.length, + padding: EdgeInsets.only(top: 8, bottom: 8), + )), ), ), - // if (favLetters != null) - // SizedBox( - // width: 30, - // // color: Colors.red, - // child: ListView.separated( - // itemBuilder: (BuildContext context, int index) { - // return Container( - // padding: const EdgeInsets.all(8), - // alignment: Alignment.center, - // child: favLetters![index].toText14(color: selectedFavLetter == favLetters![index] ? MyColors.gradiantStartColor : Colors.black), - // ).onPress(() { - // selectedFavLetter = favLetters![index].toUpperCase(); - // fetchFavUsersList(); - // }); - // }, - // separatorBuilder: (BuildContext context, int index) { - // return const SizedBox(height: 0); - // }, - // physics: const NeverScrollableScrollPhysics(), - // shrinkWrap: true, - // itemCount: favLetters!.length, - // ), - // ), + if (favLetters != null) + Container( + width: 30, + // color: Colors.red, + child: ListView.separated( + itemBuilder: (context, index) { + return Container( + padding: EdgeInsets.all(8), + alignment: Alignment.center, + child: favLetters![index].toText14(color: selectedFavLetter == favLetters![index] ? MyColors.gradiantStartColor : Colors.black), + ).onPress(() { + selectedFavLetter = favLetters![index].toUpperCase(); + fetchFavUsersList(); + }); + }, + separatorBuilder: (context, index) { + return SizedBox( + height: 0, + ); + }, + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: favLetters!.length, + ), + ), ], ), ], @@ -409,15 +385,22 @@ class _DelegateSheetState extends State { ), ), ), - Container(width: double.infinity, height: 1, color: MyColors.borderColor), + Container( + width: double.infinity, + height: 1, + color: MyColors.borderColor, + ), DefaultButton( "Cancel", - () { + () { Navigator.pop(context); }, textColor: Colors.black, - colors: const [Color(0xffE6E6E6), Color(0xffE6E6E6)], - ).insideContainer, + colors: [ + Color(0xffE6E6E6), + Color(0xffE6E6E6), + ], + ).insideContainer ], ), ); @@ -427,22 +410,25 @@ class _DelegateSheetState extends State { return InkWell( onTap: () { Navigator.pop(context); - showMyBottomSheet( - context, - callBackFunc: widget.callBackFunc, - child: SelectedItemSheet( - "Comment", - apiMode: widget.apiMode, - actionHistoryList: actionHistory, - notificationID: widget.notificationID, - isITGRequest: widget.wFHistory != null, - getNotificationRespondAttributes: widget.getNotificationRespondAttributes, - ), - ); + showMyBottomSheet(context, + callBackFunc: widget.callBackFunc, + child: SelectedItemSheet( + "Comment", + apiMode: widget.apiMode, + actionHistoryList: actionHistory, + notificationID: widget.notificationID, + isITGRequest: widget.wFHistory != null, + getNotificationRespondAttributes: widget.getNotificationRespondAttributes, + )); }, child: Row( - children: [ - CircularAvatar(url: actionHistory.eMPLOYEEIMAGE ?? "", height: 30, width: 30, isImageBase64: true), + children: [ + CircularAvatar( + url: actionHistory.eMPLOYEEIMAGE ?? "", + height: 30, + width: 30, + isImageBase64: true, + ), 9.width, (actionHistory.nAME ?? "").toText12().expanded, IconButton( @@ -458,8 +444,12 @@ class _DelegateSheetState extends State { isFav: true, ); }, - icon: Icon(Icons.star, size: 16, color: (actionHistory.isFavorite ?? false) ? MyColors.yellowColor : MyColors.borderColor), - ), + icon: Icon( + Icons.star, + size: 16, + color: (actionHistory.isFavorite ?? false) ? MyColors.yellowColor : MyColors.borderColor, + ), + ) ], ), ); @@ -469,18 +459,25 @@ class _DelegateSheetState extends State { return InkWell( onTap: () { Navigator.pop(context); - showMyBottomSheet( - context, - callBackFunc: widget.callBackFunc, - child: SelectedItgItemSheet( - "Comment", - apiMode: widget.apiMode, - wfHistory: wfHistory, - // notificationID: widget.notificationID, - ), - ); + showMyBottomSheet(context, + callBackFunc: widget.callBackFunc, + child: SelectedItgItemSheet( + "Comment", + apiMode: widget.apiMode, + wfHistory: wfHistory, + // notificationID: widget.notificationID, + )); }, - child: Row(children: [CircularAvatar(height: 30, width: 30), 9.width, (wfHistory.name ?? "").toText12().expanded]), + child: Row( + children: [ + CircularAvatar( + height: 30, + width: 30, + ), + 9.width, + (wfHistory.name ?? "").toText12().expanded, + ], + ), ); } @@ -488,31 +485,47 @@ class _DelegateSheetState extends State { return InkWell( onTap: () { Navigator.pop(context); - showMyBottomSheet( - context, - callBackFunc: widget.callBackFunc, - child: SelectedItemSheet( - "Comment", - apiMode: widget.apiMode, - favoriteReplacements: actionHistory, - notificationID: widget.notificationID, - isITGRequest: widget.wFHistory != null, - getNotificationRespondAttributes: widget.getNotificationRespondAttributes, - ), - ); + showMyBottomSheet(context, + callBackFunc: widget.callBackFunc, + child: SelectedItemSheet( + "Comment", + apiMode: widget.apiMode, + favoriteReplacements: actionHistory, + notificationID: widget.notificationID, + isITGRequest: widget.wFHistory != null, + getNotificationRespondAttributes: widget.getNotificationRespondAttributes, + )); }, child: Row( - children: [ - actionHistory.employeeImage != "" && actionHistory.employeeImage != null - ? CircularAvatar(url: actionHistory.employeeImage, height: 40, width: 40, isImageBase64: (actionHistory.employeeImage != null || actionHistory.employeeImage!.isNotEmpty) ? true : false) + children: [ + actionHistory.employeeImage != "" + ? CircularAvatar( + url: actionHistory.employeeImage, + height: 40, + width: 40, + isImageBase64: (actionHistory.employeeImage != null || actionHistory.employeeImage!.isNotEmpty) ? true : false, + ) : isImageLoaded - ? CircularAvatar(height: 40, width: 40, isImageBase64: false) + ? CircularAvatar( + height: 40, + width: 40, + isImageBase64: false, + ) : ClipRRect( - borderRadius: const BorderRadius.all(Radius.circular(50)), - child: Image.network("https://play-lh.googleusercontent.com/NPo88ojmhah4HDiposucJmfQIop4z4xc8kqJK9ITO9o-yCab2zxIp7PPB_XPj2iUojo", fit: BoxFit.cover, height: 40, width: 40).toShimmer(), - ), + borderRadius: const BorderRadius.all( + Radius.circular(50), + ), + child: Image.network( + "https://play-lh.googleusercontent.com/NPo88ojmhah4HDiposucJmfQIop4z4xc8kqJK9ITO9o-yCab2zxIp7PPB_XPj2iUojo", + fit: BoxFit.cover, + height: 40, + width: 40, + ).toShimmer(), + ), 16.width, - Expanded(child: (actionHistory.employeeDisplayName ?? "").toText12()), + Expanded( + child: (actionHistory.employeeDisplayName ?? "").toText12(), + ), IconButton( onPressed: () { fetchChangeFav( @@ -524,8 +537,12 @@ class _DelegateSheetState extends State { isNeedToRefresh: true, ); }, - icon: const Icon(Icons.star, size: 16, color: MyColors.yellowColor), - ), + icon: Icon( + Icons.star, + size: 16, + color: MyColors.yellowColor, + ), + ) ], ), ); @@ -535,24 +552,29 @@ class _DelegateSheetState extends State { return InkWell( onTap: () { Navigator.pop(context); - showMyBottomSheet( - context, - callBackFunc: widget.callBackFunc, - child: SelectedItemSheet( - LocaleKeys.comments.tr(), - apiMode: widget.apiMode, - replacementList: actionHistory, - notificationID: widget.notificationID, - isITGRequest: widget.wFHistory != null, - getNotificationRespondAttributes: widget.getNotificationRespondAttributes, - ), - ); + showMyBottomSheet(context, + callBackFunc: widget.callBackFunc, + child: SelectedItemSheet( + LocaleKeys.comments.tr(), + apiMode: widget.apiMode, + replacementList: actionHistory, + notificationID: widget.notificationID, + isITGRequest: widget.wFHistory != null, + getNotificationRespondAttributes: widget.getNotificationRespondAttributes, + )); }, child: Row( - children: [ - CircularAvatar(url: actionHistory.employeeImage, height: 30, width: 30, isImageBase64: actionHistory.employeeImage != null ? true : false), + children: [ + CircularAvatar( + url: actionHistory.employeeImage, + height: 30, + width: 30, + isImageBase64: actionHistory.employeeImage != null ? true : false, + ), 16.width, - Expanded(child: (actionHistory.employeeDisplayName ?? "").toText12()), + Expanded( + child: (actionHistory.employeeDisplayName ?? "").toText12(), + ), IconButton( onPressed: () { actionHistory.isFavorite = !(actionHistory.isFavorite ?? false); @@ -565,8 +587,12 @@ class _DelegateSheetState extends State { isNeedToRefresh: false, ); }, - icon: Icon(Icons.star, size: 16, color: (actionHistory.isFavorite ?? false) ? MyColors.yellowColor : MyColors.borderColor), - ), + icon: Icon( + Icons.star, + size: 16, + color: (actionHistory.isFavorite ?? false) ? MyColors.yellowColor : MyColors.borderColor, + ), + ) ], ), ); diff --git a/lib/ui/work_list/sheets/selected_item_sheet.dart b/lib/ui/work_list/sheets/selected_item_sheet.dart index 2efe88b..9b98c8b 100644 --- a/lib/ui/work_list/sheets/selected_item_sheet.dart +++ b/lib/ui/work_list/sheets/selected_item_sheet.dart @@ -175,7 +175,7 @@ class SelectedItemSheet extends StatelessWidget { if (favoriteReplacements != null) empID = favoriteReplacements!.userName; if (replacementList != null) empID = replacementList!.userName; try { - memberInformationListModel = await WorkListApiClient().getUserInformation(-999, empID!); + memberInformationListModel = await WorkListApiClient().getNotificationUserInformation(-999, empID!, notificationID!); if (actionHistoryList != null) empID = actionHistoryList!.eMPLOYEEIMAGE = memberInformationListModel!.eMPLOYEEIMAGE ?? AppState().getBase64ImageEmp; if (favoriteReplacements != null) empID = favoriteReplacements!.employeeImage = memberInformationListModel!.eMPLOYEEIMAGE ?? AppState().getBase64ImageEmp; if (replacementList != null) empID = replacementList!.employeeImage = memberInformationListModel!.eMPLOYEEIMAGE ?? AppState().getBase64ImageEmp; diff --git a/lib/ui/work_list/work_list_screen.dart b/lib/ui/work_list/work_list_screen.dart index 7637cd1..d659ea6 100644 --- a/lib/ui/work_list/work_list_screen.dart +++ b/lib/ui/work_list/work_list_screen.dart @@ -125,6 +125,17 @@ class _WorkListScreenState extends State { key: 'PAY_REQ', disable: false, ), + WorkListItemTypeModelData( + value: 0, + name: 'FA Disposal Request ', + fullName: LocaleKeys.faRequest.tr(), + active: false, + color: [Color(0xff42d7bf), Color(0xff42d7bf)], + icon: "assets/images/miss_swipe.svg", + key: 'HMG_FA_D', + disable: false, + ), + ]; int? workListItemIndex; diff --git a/lib/ui/work_list/worklist_detail_screen.dart b/lib/ui/work_list/worklist_detail_screen.dart index f1a7a3e..6bf8ea9 100644 --- a/lib/ui/work_list/worklist_detail_screen.dart +++ b/lib/ui/work_list/worklist_detail_screen.dart @@ -25,6 +25,7 @@ import 'package:mohem_flutter_app/models/member_information_list_model.dart'; import 'package:mohem_flutter_app/models/notification_get_respond_attributes_list_model.dart'; import 'package:mohem_flutter_app/models/termination/termination_notification_body.dart'; import 'package:mohem_flutter_app/models/worklist/GetRFCEmployeeList.dart'; +import 'package:mohem_flutter_app/models/worklist/get_fad_notifications.dart'; import 'package:mohem_flutter_app/models/worklist/hr/eit_otification_body_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_address_notification_body_list.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_basic_det_ntf_body_list_model.dart'; @@ -92,7 +93,7 @@ class _WorkListDetailScreenState extends State { GetPrNotificationBodyList? getPrNotificationBody; GetItemCreationNtfBodyList? getItemCreationNtfBody; GetPaymentNotificationBodyList? getPaymentNotificationBodyList; - + GetFaDisposalNtfDetails? getFADNotificationBody; bool isCloseAvailable = false; bool isApproveAvailable = false; bool isRejectAvailable = false; @@ -177,6 +178,9 @@ class _WorkListDetailScreenState extends State { if (workListData!.iTEMTYPE == "PAY_REQ") { getPaymentNotification(); } + if (workListData!.iTEMTYPE == "HMG_FA_D") { + getFADNotification(); + } if (controller.hasClients) { controller.jumpToPage(0); @@ -272,6 +276,7 @@ class _WorkListDetailScreenState extends State { getPrNotificationBodyList: getPrNotificationBody, getTerminationNotificationBodyList: getTerminationNotificationBodyList, getPaymentNotificationBodyList: getPaymentNotificationBodyList, + getFADNotificationBodyList: getFADNotificationBody ), (workListData!.iTEMTYPE == "HRSSA" || workListData!.iTEMTYPE == "STAMP" || workListData!.iTEMTYPE == "PAY_REQ") ? DetailFragment(workListData, memberInformationListModel) @@ -813,7 +818,7 @@ class _WorkListDetailScreenState extends State { if (apiCallCount == 0) Utils.showLoading(context); apiCallCount++; memberInformationListModel = null; - memberInformationListModel = await WorkListApiClient().getUserInformation(-999, workListData!.sELECTEDEMPLOYEENUMBER!); + memberInformationListModel = await WorkListApiClient().getNotificationUserInformation(-999, workListData!.sELECTEDEMPLOYEENUMBER!, workListData!.nOTIFICATIONID!); apiCallCount--; if (apiCallCount == 0) { Utils.hideLoading(context); @@ -1036,6 +1041,23 @@ class _WorkListDetailScreenState extends State { } } + Future getFADNotification() async { + try { + if (apiCallCount == 0) Utils.showLoading(context); + apiCallCount++; + getFADNotificationBody = await WorkListApiClient().getFADNotificationBody(workListData!.nOTIFICATIONID); + apiCallCount--; + if (apiCallCount == 0) { + Utils.hideLoading(context); + setState(() {}); + } + } catch (ex) { + apiCallCount--; + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + void notificationGetRespondAttributes() async { try { if (apiCallCount == 0) Utils.showLoading(context); diff --git a/lib/ui/work_list/worklist_fragments/info_fragments.dart b/lib/ui/work_list/worklist_fragments/info_fragments.dart index 73a87f7..541d347 100644 --- a/lib/ui/work_list/worklist_fragments/info_fragments.dart +++ b/lib/ui/work_list/worklist_fragments/info_fragments.dart @@ -12,6 +12,7 @@ import 'package:mohem_flutter_app/models/get_po_notification_body_list_model.dar import 'package:mohem_flutter_app/models/get_pr_notification_body_list_model.dart'; import 'package:mohem_flutter_app/models/get_stamp_ms_notification_body_list_model.dart'; import 'package:mohem_flutter_app/models/get_stamp_ns_notification_body_list_model.dart'; +import 'package:mohem_flutter_app/models/worklist/get_fad_notifications.dart'; import 'package:mohem_flutter_app/models/worklist/hr/eit_otification_body_model.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_address_notification_body_list.dart'; import 'package:mohem_flutter_app/models/worklist/hr/get_basic_det_ntf_body_list_model.dart'; @@ -39,7 +40,7 @@ class InfoFragment extends StatelessWidget { List? getAddressNotificationBodyList = []; List? getTerminationNotificationBodyList = []; GetPaymentNotificationBodyList? getPaymentNotificationBodyList; - + GetFaDisposalNtfDetails? getFADNotificationBodyList; InfoFragment( {this.workListData, this.poHeaderList = const [], @@ -54,7 +55,9 @@ class InfoFragment extends StatelessWidget { this.getPrNotificationBodyList, this.getAddressNotificationBodyList, this.getTerminationNotificationBodyList, - this.getPaymentNotificationBodyList}); + this.getPaymentNotificationBodyList, + this.getFADNotificationBodyList + }); double itemHeight = 0; double itemWidth = 0; @@ -129,7 +132,10 @@ class InfoFragment extends StatelessWidget { getTerminationNotificationBodyList!), if (getPaymentNotificationBodyList != null) getPaymentNotificationBodyListWidget(getPaymentNotificationBodyList ?? - GetPaymentNotificationBodyList()) + GetPaymentNotificationBodyList()), + if (getFADNotificationBodyList != null) + getFANotificationBodyListWidget(getFADNotificationBodyList ?? + GetFaDisposalNtfDetails()) .objectContainerView(), ]; return Container( @@ -922,5 +928,125 @@ class InfoFragment extends StatelessWidget { ], ); + } + + Widget getFANotificationBodyListWidget( + GetFaDisposalNtfDetails? data) { + return Column( + children: [ + 12.height, + + if (data!.pFaHeader!.isNotEmpty) + Column( + children: [ + LocaleKeys.faHeader.tr().toText14(color: MyColors.textMixColor), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.requestNo.tr(), + data.pFaHeader![0].requestNo.toString() ?? ""), + ItemDetailViewCol( + LocaleKeys.stats.tr(), data.pFaHeader![0].status ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol( + LocaleKeys.bookTypeCode.tr(), data.pFaHeader![0].bookTypeCode ?? ""), + ItemDetailViewCol(LocaleKeys.categoryCode.tr(), + data.pFaHeader![0].categoryCode ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.categoryGroup.tr(), + data.pFaHeader![0].categoryGroup ?? ""), + ItemDetailViewCol( + LocaleKeys.comments.tr(), data.pFaHeader![0].comments ?? ""), + ), + + + ], + ).objectContainerView(), + if (data!.pFaLines!.isNotEmpty) + Column( + children: [ + LocaleKeys.faLINES.tr().toText14(color: MyColors.textMixColor), + ItemDetailGrid( + ItemDetailViewCol( LocaleKeys.requestNo.tr(), + data.pFaLines![0].requestNo.toString() ?? ""), + ItemDetailViewCol( + LocaleKeys.quantity.tr(), data.pFaLines![0].quantity.toString() ?? "" ), + + ), + ItemDetailGrid( + ItemDetailViewCol( + LocaleKeys.assetNumber.tr(), data.pFaLines![0].assetNumber.toString() ?? ""), + ItemDetailViewCol(LocaleKeys.assetDescription.tr(), + data.pFaLines![0].assetDescription ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.barCodeNumber.tr(), + data.pFaLines![0] .barcodeNumber.toString() ?? ""), + ItemDetailViewCol( + LocaleKeys.datePlaceInService.tr(), DateUtil.formatDateToDate( + DateUtil.convertStringToDate( data.pFaLines![0].datePlacedInService ?? ""), false)), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.departmentName.tr(), + data.pFaLines![0].department ?? ""), + ItemDetailViewCol(LocaleKeys.serialNumber.tr(), + data.pFaLines![0].serialNumber ?? "") , + ), + ItemDetailGrid( + ItemDetailViewCol( + LocaleKeys.disposedDate.tr(), DateUtil.formatDateToDate( + DateUtil.convertStringToDate( data.pFaLines![0].disposedDate ?? ""), false)), + ItemDetailViewCol(LocaleKeys.netBookValue.tr(), + data.pFaLines![0].netBookValue.toString() ?? ""), + + ), + ItemDetailGrid( + ItemDetailViewCol( + LocaleKeys.poNumber.tr(), data.pFaLines![0].poNumber.toString() ?? "" ), + ItemDetailViewCol(LocaleKeys.purchasedPrice.tr(), + data.pFaLines![0].purchasePrice.toString() ?? ""), + + ), + ItemDetailGrid( + ItemDetailViewCol( + LocaleKeys.usefulLife.tr(), data.pFaLines![0].usefulLife.toString() ?? "" ), + ItemDetailViewCol(LocaleKeys.yearsUsed.tr(), + data.pFaLines![0].yearsUsed.toString() ?? ""), + + ), + ], + ).objectContainerView(), + + if (data!.pFaBuyers!.isNotEmpty) + Column( + children: [ + LocaleKeys.buyer.tr().toText14(color: MyColors.textMixColor), + ItemDetailGrid( + ItemDetailViewCol( + LocaleKeys.requestNo.tr(), data.pFaBuyers![0].requestNo.toString() ?? ""), + ItemDetailViewCol( LocaleKeys.amount.tr(), + data.pFaBuyers![0].amount.toString() ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol(LocaleKeys.buyerNumber.tr(), + data.pFaBuyers![0].buyerName!), + ItemDetailViewCol( + LocaleKeys.buyerNumber.tr(), data.pFaBuyers![0].buyerNumber ?? ""), + ), + ItemDetailGrid( + ItemDetailViewCol( LocaleKeys.highestBidder.tr(), + data.pFaBuyers![0].highestBidder ?? ""), + ItemDetailViewCol( + LocaleKeys.remarks.tr(), data.pFaBuyers![0].remarks ?? ""), + ), + + ], + ).objectContainerView(), + + ]); + + + + } } diff --git a/lib/widgets/bottom_sheet.dart b/lib/widgets/bottom_sheet.dart index 4101746..d5cb271 100644 --- a/lib/widgets/bottom_sheet.dart +++ b/lib/widgets/bottom_sheet.dart @@ -1,5 +1,9 @@ +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/generated/locale_keys.g.dart'; void showMyBottomSheet(BuildContext context, {required Widget child, required VoidCallback callBackFunc, String? type}) { showModalBottomSheet( @@ -49,6 +53,67 @@ void showMyBottomSheet(BuildContext context, {required Widget child, required Vo }); } + + Widget openWorkListBottomSheet(BuildContext context, String? val, String? title){ + return SafeArea( + child: Container( + padding: const EdgeInsets.all(16), + height: MediaQuery.of(context).size.height * 0.3 , // half-screen height + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // ---- Top Row with Title & Close Button ---- + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + (title ?? "Details").toText14(isBold: true, color: Color(0xff2BB8A6)), + IconButton( + icon: const Icon(Icons.close, color: Colors.black54, size: 20), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ), + const Divider(height: 1, color: Colors.grey), + const SizedBox(height: 12), + // ---- Scrollable Content ---- + Expanded( + child: SingleChildScrollView( + child: ((val?.isEmpty ?? true) + ? "--" + : val.toString()).toText14(), + ), + ), + SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xff2BB8A6), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + padding: const EdgeInsets.symmetric(vertical: 12), + ), + onPressed: () { + Navigator.pop(context); + }, + child: Text( + LocaleKeys.close.tr(), + style: const TextStyle( + color: Colors.white, + fontSize: 14, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + ); +} + class BottomSheetItem extends StatelessWidget { final Function onTap; final IconData icon; 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( diff --git a/lib/widgets/item_detail_view_widget.dart b/lib/widgets/item_detail_view_widget.dart index 64bbd01..de82afe 100644 --- a/lib/widgets/item_detail_view_widget.dart +++ b/lib/widgets/item_detail_view_widget.dart @@ -1,11 +1,14 @@ import 'dart:convert'; +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/itg_forms_models/itg_worklist_table_model.dart'; +import 'package:mohem_flutter_app/widgets/bottom_sheet.dart'; class ItemDetailView extends StatelessWidget { final String title; @@ -40,7 +43,7 @@ class ItemDetailViewCol extends StatelessWidget { children: [ "$title:".toText12(isBold: true, color: const Color(0xff2BB8A6), maxLine: 2), 4.width, - (value.isEmpty ? "--" : value).toText12(color: MyColors.normalTextColor, maxLine: 5), + Flexible(child: (value.isEmpty ? "--" : value).toText12(color: MyColors.normalTextColor, maxLine: null)), ], ); } @@ -84,18 +87,51 @@ class ItemDetailViewGridItem extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ - title != null ? Flexible(child: "$title:".toText12Auto(isBold: true, color: const Color(0xff2BB8A6))) : Container(), + title != null ? Flexible(child: "$title:".toText12(isBold: true, color: const Color(0xff2BB8A6))) : Container(), 4.width, - type != null - ? type!.toLowerCase() == "table" - ? getStringFromJSON(value!) - // : Flexible(child: (value!.isEmpty ? "--" : value).toString().toText12Auto(color: MyColors.normalTextColor, maxLine: 5)) - : (value!.isEmpty ? "--" : value).toString().toText12Auto(color: MyColors.normalTextColor, maxLine: maxLine) - : Container(), + type != null + ? type!.toLowerCase() == "table" + ? getStringFromJSON(value!) + // : Flexible(child: (value!.isEmpty ? "--" : value).toString().toText12Auto(color: MyColors.normalTextColor, maxLine: 5)) + : (value!.isEmpty ? "--" : value).toString().toText12(color: MyColors.normalTextColor, maxLine: 2) + : Container(), + if (value!.length > 100) + GestureDetector( + onTap: () { + showModalBottomSheet( + context: context, + enableDrag: true, + isDismissible: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)), + ), + backgroundColor: Colors.white, + builder: (context) { + return openWorkListBottomSheet(context, value, title); + }, + ); + + }, + child: Padding( + padding: const EdgeInsets.only(top: 4), + child: Text( + LocaleKeys.showMore.tr(), + textAlign: TextAlign.right, + style: const TextStyle( + color: Color(0xff2BB8A6), + fontSize: 12, + fontWeight: FontWeight.w600, + + ), + ), + ), + ), ], - ).paddingOnly(top: showSpaceAfterLine ? 16 : 0), + + ).paddingOnly(top: showSpaceAfterLine ? 16 : 0, left: 2, right: 2), ); } + } class ItemDetailGrid extends StatelessWidget { @@ -153,3 +189,4 @@ Widget getStringFromJSON(String jsonString) { return Flexible(child: ("-").toString().toText12Auto(color: MyColors.normalTextColor)); } } + diff --git a/lib/widgets/shimmer/dashboard_shimmer_widget.dart b/lib/widgets/shimmer/dashboard_shimmer_widget.dart index c11cf16..7d59899 100644 --- a/lib/widgets/shimmer/dashboard_shimmer_widget.dart +++ b/lib/widgets/shimmer/dashboard_shimmer_widget.dart @@ -8,6 +8,8 @@ import 'package:mohem_flutter_app/generated/locale_keys.g.dart'; import 'package:shimmer/shimmer.dart'; class GetAttendanceTrackingShimmer extends StatelessWidget { + const GetAttendanceTrackingShimmer({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Container( @@ -85,6 +87,8 @@ class GetAttendanceTrackingShimmer extends StatelessWidget { } class MenuShimmer extends StatelessWidget { + const MenuShimmer({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Container( @@ -122,6 +126,8 @@ class MenuShimmer extends StatelessWidget { } class ServicesHeaderShimmer extends StatelessWidget { + const ServicesHeaderShimmer({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Row( @@ -145,6 +151,8 @@ class ServicesHeaderShimmer extends StatelessWidget { } class ServicesMenuShimmer extends StatelessWidget { + const ServicesMenuShimmer({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Container( @@ -188,6 +196,75 @@ class ServicesMenuShimmer extends StatelessWidget { } } +class GreetingCardShimmer extends StatelessWidget { + const GreetingCardShimmer({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: 100, + decoration: BoxDecoration( + color: const Color(0xFFFAF4E7), + border: Border.all(width: 2, color: const Color(0xFFE7E7E7)), + borderRadius: BorderRadius.circular(20), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 65, + height: 81, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(10), + ), + ).toShimmer(), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: 150, + height: 16, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + ), + ).toShimmer(), + const SizedBox(height: 8), + Container( + width: 120, + height: 16, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + ), + ).toShimmer(), + ], + ).expanded, + Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + width: 60, + height: 12, + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(4), + ), + ).toShimmer(), + ], + ), + ], + ).paddingOnly(left: 8, right: 8, top: 6, bottom: 12), + ).paddingOnly(left: 21, right: 21, top: 14, bottom: 14); + } +} + + class MarathonBannerShimmer extends StatelessWidget { const MarathonBannerShimmer({Key? key}) : super(key: key); @@ -305,3 +382,4 @@ class ChatHomeShimmer extends StatelessWidget { )); } } + diff --git a/lib/widgets/sso_webview_widget.dart b/lib/widgets/sso_webview_widget.dart index 02ed5eb..f584c2c 100644 --- a/lib/widgets/sso_webview_widget.dart +++ b/lib/widgets/sso_webview_widget.dart @@ -15,10 +15,13 @@ class SsoLoginWebView extends StatefulWidget { class _SsoLoginWebViewState extends State { late final WebViewController _controller; - + String? url; @override void initState() { // TODO: implement initState + + url = widget.url ?? "https://Paxes-release.techmaster.in/SSO/HMG"; + super.initState(); _controller = WebViewController() @@ -57,6 +60,6 @@ class _SsoLoginWebViewState extends State { ////
@override Widget build(BuildContext context) { - return Scaffold(appBar: AppBar(title: Text('Logging in...')), body: WebViewWidget(controller: _controller)); + return Scaffold(appBar: null, body:SafeArea(child: WebViewWidget(controller: _controller))); } } diff --git a/pubspec.yaml b/pubspec.yaml index 7e97774..49dec60 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.6.8+300078 -#version: 3.9.1+1 +version: 3.7.1+300081 +#version: 3.9.3+1 environment: sdk: ^3.7.0 @@ -56,7 +56,8 @@ dependencies: logger: ^1.1.0 flutter_countdown_timer: ^4.1.0 - platform_device_id_plus: ^1.0.7 +# platform_device_id_plus: ^1.0.7 + mobile_device_identifier: ^0.0.3 device_info_plus: ^11.5.0 image_picker: ^1.1.2 file_picker: ^8.3.1 @@ -101,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 @@ -139,12 +143,13 @@ dependencies: #todo its for temporary purpose, later will remove this. dotted_border: ^2.0.0+3 webview_flutter: ^4.13.0 + nfc_manager: ^3.2.0 # saf: ^1.0.3+4 dependency_overrides: - nfc_manager: ^3.2.0 +# nfc_manager: 3.2.0 # firebase_core_platform_interface: 4.5.1