diff --git a/assets/images/cancel_button.svg b/assets/images/cancel_button.svg new file mode 100644 index 0000000..2aba62a --- /dev/null +++ b/assets/images/cancel_button.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/images/document.svg b/assets/images/document.svg new file mode 100644 index 0000000..ca0e83f --- /dev/null +++ b/assets/images/document.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 4b7acc9..48f443e 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -530,5 +530,12 @@ "connectHmgWifi": "قم بتوصيل HMG WIFI", "connectedHmgWifi": "اتصال HMG WIFI", "itgForms": "نماذج (ITG)", - "resetAdPassword": "إعادة تعيين كلمة مرور AD" + "resetAdPassword": "إعادة تعيين كلمة مرور AD", + "myDocuments": "مستنداتي", + "requiredDocuments": "المستندات المطلوبة", + "optionalDocuments": "المستندات الاختيارية", + "allDocuments": "كافة المستندات", + "expiredDocuments": "المستندات منتهية الصلاحية", + "missingDocuments": "مستندات مفقودة", + "uploadedDocuments": "المستندات التي تم تحميلها" } \ No newline at end of file diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 3f460a1..b005710 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -530,5 +530,12 @@ "connectHmgWifi": "Connect HMG WIFI", "connectedHmgWifi": "Connected HMG WIFI", "itgForms": "ITG Forms", - "resetAdPassword": "Reset AD Password" + "resetAdPassword": "Reset AD Password", + "myDocuments": "My Documents", + "requiredDocuments": "Required Documents", + "optionalDocuments": "Optional Documents", + "allDocuments": "All\nDocuments", + "expiredDocuments": "Expired\nDocuments", + "missingDocuments": "Missing\nDocuments", + "uploadedDocuments": "Uploaded\nDocuments" } \ No newline at end of file diff --git a/lib/api/profile_api_client.dart b/lib/api/profile_api_client.dart index 6a8cfb0..030a949 100644 --- a/lib/api/profile_api_client.dart +++ b/lib/api/profile_api_client.dart @@ -12,6 +12,7 @@ import 'package:mohem_flutter_app/models/get_employee_address_model.dart'; import 'package:mohem_flutter_app/models/get_employee_basic_details.model.dart'; import 'package:mohem_flutter_app/models/get_employee_contacts.model.dart'; import 'package:mohem_flutter_app/models/get_employee_phones_model.dart'; +import 'package:mohem_flutter_app/models/my_documents/employee_documents_list_model.dart'; import 'package:mohem_flutter_app/models/performance.dart'; import 'package:mohem_flutter_app/models/profile/basic_details_dff_structure.dart'; import 'package:mohem_flutter_app/models/profile/get_contact_clos_structure_list.dart'; @@ -84,6 +85,19 @@ class ProfileApiClient { }, url, postParams); } + Future> getEmployeeDocuments() async { + String url = "${ApiConsts.erpRest}GET_EMPLOYEE_DOCUMENTS"; + Map postParams = { + "P_MENU_TYPE": "E", + "P_SELECTED_RESP_ID": -999, + }; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((json) { + GenericResponseModel? responseData = GenericResponseModel.fromJson(json); + return responseData.employeeDocumentsList ?? []; + }, url, postParams); + } + Future updateEmpImage(img) async { String url = "${ApiConsts.erpRest}UPDATE_EMPLOYEE_IMAGE"; Map postParams = {"P_MENU_TYPE": "E", "P_SELECTED_RESP_ID": -999, "P_IMAGE": img}; diff --git a/lib/classes/colors.dart b/lib/classes/colors.dart index 5c9b30e..915f35d 100644 --- a/lib/classes/colors.dart +++ b/lib/classes/colors.dart @@ -35,6 +35,7 @@ class MyColors { static const Color orange = Color(0xFFCC9B14); static const Color yellowFavColor = Color(0xffEAC321); static const Color yellowColorII = Color(0xffEAA118); + static const Color yellowColor00 = Color(0xffE6AA00); static const Color backgroundBlackColor = Color(0xff202529); static const Color black = Color(0xff000000); static const Color white = Color(0xffffffff); @@ -64,4 +65,5 @@ class MyColors { static const Color grey71Color = Color(0xff717171); static const Color darkGrey3BColor = Color(0xff3B3B3B); static const Color lightGreyIconColor = Color(0xff919191); + static const Color selectedBorderColor = Color(0xff37A4BE); } diff --git a/lib/classes/utils.dart b/lib/classes/utils.dart index 1838cb8..7885fad 100644 --- a/lib/classes/utils.dart +++ b/lib/classes/utils.dart @@ -349,7 +349,7 @@ class Utils { } static Future selectDate(BuildContext context, DateTime selectedDate) async { - if (!Platform.isIOS) { + if (Platform.isIOS) { await showCupertinoModalPopup( context: context, builder: (BuildContext cxt) => Container( diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 7ccb6ce..705e40d 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/models/my_documents/employee_documents_list_model.dart'; import 'package:mohem_flutter_app/ui/app_update_screen.dart'; import 'package:mohem_flutter_app/ui/attendance/add_vacation_rule_screen.dart'; import 'package:mohem_flutter_app/ui/attendance/monthly_attendance_screen.dart'; @@ -59,6 +60,8 @@ import 'package:mohem_flutter_app/ui/screens/items_for_sale/items_for_sale_home. import 'package:mohem_flutter_app/ui/screens/mowadhafhi/mowadhafhi_home.dart'; import 'package:mohem_flutter_app/ui/screens/mowadhafhi/mowadhafhi_hr_request.dart'; import 'package:mohem_flutter_app/ui/screens/mowadhafhi/request_details.dart'; +import 'package:mohem_flutter_app/ui/screens/my_documents/my_document_detail_screen.dart'; +import 'package:mohem_flutter_app/ui/screens/my_documents/my_documents_screen.dart'; import 'package:mohem_flutter_app/ui/screens/my_requests/my_requests.dart'; import 'package:mohem_flutter_app/ui/screens/my_requests/new_request.dart'; import 'package:mohem_flutter_app/ui/screens/offers_and_discounts/offers_and_discounts_details.dart'; @@ -149,6 +152,8 @@ class AppRoutes { // My Requests static const String myRequests = "/myRequests"; static const String newRequest = "/newRequests"; + static const String myDocuments = "/myDocuments"; + static const String myDocumentDetail = "/myDocumentDetail"; // Items For Sale static const String itemsForSale = "/itemsForSale"; @@ -265,7 +270,7 @@ class AppRoutes { //My Requests myRequests: (BuildContext context) => MyRequests(), newRequest: (BuildContext context) => NewRequest(), - + myDocuments: (BuildContext context) => MyDocumentsScreen(), // Items for sale itemsForSale: (BuildContext context) => ItemsForSale(), itemsForSaleDetail: (BuildContext context) => ItemForSaleDetailPage(), diff --git a/lib/generated/codegen_loader.g.dart b/lib/generated/codegen_loader.g.dart index ba21c29..c8bfdfa 100644 --- a/lib/generated/codegen_loader.g.dart +++ b/lib/generated/codegen_loader.g.dart @@ -546,7 +546,14 @@ class CodegenLoader extends AssetLoader{ "connectHmgWifi": "قم بتوصيل HMG WIFI", "connectedHmgWifi": "اتصال HMG WIFI", "itgForms": "نماذج (ITG)", - "resetAdPassword": "إعادة تعيين كلمة مرور AD" + "resetAdPassword": "إعادة تعيين كلمة مرور AD", + "myDocuments": "مستنداتي", + "requiredDocuments": "المستندات المطلوبة", + "optionalDocuments": "المستندات الاختيارية", + "allDocuments": "كافة المستندات", + "expiredDocuments": "المستندات منتهية الصلاحية", + "missingDocuments": "مستندات مفقودة", + "uploadedDocuments": "المستندات التي تم تحميلها" }; static const Map en_US = { "mohemm": "Mohemm", @@ -1080,7 +1087,14 @@ static const Map en_US = { "connectHmgWifi": "Connect HMG WIFI", "connectedHmgWifi": "Connected HMG WIFI", "itgForms": "ITG Forms", - "resetAdPassword": "Reset AD Password" + "resetAdPassword": "Reset AD Password", + "myDocuments": "My Documents", + "requiredDocuments": "Required Documents", + "optionalDocuments": "Optional Documents", + "allDocuments": "All\nDocuments", + "expiredDocuments": "Expired\nDocuments", + "missingDocuments": "Missing\nDocuments", + "uploadedDocuments": "Uploaded\nDocuments" }; 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 343bf38..3a4921f 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -517,5 +517,12 @@ abstract class LocaleKeys { static const connectedHmgWifi = 'connectedHmgWifi'; static const itgForms = 'itgForms'; static const resetAdPassword = 'resetAdPassword'; + static const myDocuments = 'myDocuments'; + static const requiredDocuments = 'requiredDocuments'; + static const optionalDocuments = 'optionalDocuments'; + static const allDocuments = 'allDocuments'; + static const expiredDocuments = 'expiredDocuments'; + static const missingDocuments = 'missingDocuments'; + static const uploadedDocuments = 'uploadedDocuments'; } diff --git a/lib/models/generic_response_model.dart b/lib/models/generic_response_model.dart index a9006eb..5188b2c 100644 --- a/lib/models/generic_response_model.dart +++ b/lib/models/generic_response_model.dart @@ -60,6 +60,7 @@ 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/my_documents/employee_documents_list_model.dart'; import 'package:mohem_flutter_app/models/my_requests/get_ccp_dff_structure_model.dart'; import 'package:mohem_flutter_app/models/my_requests/get_ccp_output_model.dart'; import 'package:mohem_flutter_app/models/my_requests/get_ccp_transactions_model.dart'; @@ -160,6 +161,7 @@ class GenericResponseModel { String? deleteVacationRuleList; String? disableSessionList; String? employeeQR; + List? employeeDocumentsList; String? forgetPasswordTokenID; List? getCcpTransactionsListNew; List? getConcurrentProgramsList; @@ -431,6 +433,7 @@ class GenericResponseModel { this.deleteVacationRuleList, this.disableSessionList, this.employeeQR, + this.employeeDocumentsList, this.forgetPasswordTokenID, this.getAbsenceAttachmentsList, this.getAbsenceAttendanceTypesList, @@ -709,6 +712,14 @@ class GenericResponseModel { deleteVacationRuleList = json['DeleteVacationRuleList']; disableSessionList = json['DisableSessionList']; employeeQR = json['EmployeeQR']; + + if (json['Employee_Documents_List'] != null) { + employeeDocumentsList = []; + json['Employee_Documents_List'].forEach((v) { + employeeDocumentsList!.add(EmployeeDocumentsList.fromJson(v)); + }); + } + forgetPasswordTokenID = json['ForgetPasswordTokenID']; getAbsenceAttachmentsList = json['GetAbsenceAttachmentsList']; @@ -1315,7 +1326,7 @@ class GenericResponseModel { if (json['RespondRolesList'] != null) { respondRolesList = []; json['RespondRolesList'].forEach((v) { - // respondRolesList!.add(v); + // respondRolesList!.add(v); }); } resubmitAbsenceTransactionList = json['ResubmitAbsenceTransactionList']; @@ -1341,10 +1352,7 @@ class GenericResponseModel { submitAddressTransactionList = json['SubmitAddressTransactionList'] != null ? SubmitAddressTransaction.fromJson(json['SubmitAddressTransactionList']) : null; submitBasicDetTransactionList = json['SubmitBasicDetTransactionList'] != null ? SubmitBasicDetailsTransactionList.fromJson(json['SubmitBasicDetTransactionList']) : null; submitCEITransactionList = json['SubmitCEITransactionList']; - submitCcpTransactionList = json['SubmitCcpTransactionList'] != null - ? new SubmitCcpTransactionList.fromJson( - json['SubmitCcpTransactionList']) - : null; + submitCcpTransactionList = json['SubmitCcpTransactionList'] != null ? new SubmitCcpTransactionList.fromJson(json['SubmitCcpTransactionList']) : null; submitContactTransactionList = json['SubmitContactTransactionList'] != null ? SubmitContactTransactionList.fromJson(json['SubmitContactTransactionList']) : null; submitEITTransactionList = json['SubmitEITTransactionList'] != null ? SubmitEITTransactionList.fromJson(json['SubmitEITTransactionList']) : null; @@ -1471,6 +1479,11 @@ class GenericResponseModel { data['DeleteVacationRuleList'] = this.deleteVacationRuleList; data['DisableSessionList'] = this.disableSessionList; data['EmployeeQR'] = this.employeeQR; + + if (this.employeeDocumentsList != null) { + data['Employee_Documents_List'] = this.employeeDocumentsList!.map((v) => v.toJson()).toList(); + } + data['ForgetPasswordTokenID'] = this.forgetPasswordTokenID; data['GetAbsenceAttachmentsList'] = this.getAbsenceAttachmentsList; @@ -1804,8 +1817,7 @@ class GenericResponseModel { data['SubmitCEITransactionList'] = this.submitCEITransactionList; if (this.submitCcpTransactionList != null) { - data['SubmitCcpTransactionList'] = - this.submitCcpTransactionList!.toJson(); + data['SubmitCcpTransactionList'] = this.submitCcpTransactionList!.toJson(); } data['SubmitContactTransactionList'] = this.submitContactTransactionList; diff --git a/lib/models/my_documents/employee_documents_list_model.dart b/lib/models/my_documents/employee_documents_list_model.dart new file mode 100644 index 0000000..97ef785 --- /dev/null +++ b/lib/models/my_documents/employee_documents_list_model.dart @@ -0,0 +1,29 @@ +class EmployeeDocumentsList { + String? dOCUMENTREQUIREDSTATUS; + String? dOCUMENTSTATUS; + String? dOCUMENTTYPE; + String? fUNCTIONNAME; + + EmployeeDocumentsList({ + this.dOCUMENTREQUIREDSTATUS, + this.dOCUMENTSTATUS, + this.dOCUMENTTYPE, + this.fUNCTIONNAME, + }); + + EmployeeDocumentsList.fromJson(Map json) { + dOCUMENTREQUIREDSTATUS = json['DOCUMENT_REQUIRED_STATUS']; + dOCUMENTSTATUS = json['DOCUMENT_STATUS']; + dOCUMENTTYPE = json['DOCUMENT_TYPE']; + fUNCTIONNAME = json['FUNCTION_NAME']; + } + + Map toJson() { + Map data = Map(); + data['DOCUMENT_REQUIRED_STATUS'] = dOCUMENTREQUIREDSTATUS; + data['DOCUMENT_STATUS'] = dOCUMENTSTATUS; + data['DOCUMENT_TYPE'] = dOCUMENTTYPE; + data['FUNCTION_NAME'] = fUNCTIONNAME; + return data; + } +} diff --git a/lib/provider/dashboard_provider_model.dart b/lib/provider/dashboard_provider_model.dart index 0e18544..5eac532 100644 --- a/lib/provider/dashboard_provider_model.dart +++ b/lib/provider/dashboard_provider_model.dart @@ -278,6 +278,7 @@ class DashboardProviderModel with ChangeNotifier, DiagnosticableTreeMixin { getMenuEntriesList.add(activeDirectoryEntry); list.add(GetMenuEntriesList(requestType: "ITG_FORMS", prompt: LocaleKeys.itgForms.tr(), menuName: 'ITG_FORMS')); + list.add(GetMenuEntriesList(requestType: "MY_DOCUMENTS", prompt: LocaleKeys.myDocuments.tr(), menuName: 'MY_DOCUMENTS')); } menus.add(Menus(getMenuEntriesList[i], list)); diff --git a/lib/ui/landing/widget/services_widget.dart b/lib/ui/landing/widget/services_widget.dart index 2333c18..7dabb0c 100644 --- a/lib/ui/landing/widget/services_widget.dart +++ b/lib/ui/landing/widget/services_widget.dart @@ -69,7 +69,9 @@ class ServicesWidget extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ - SvgPicture.asset(AppState().isArabic(context) ? getMenuIconAr(data.homeMenus![parentIndex].menuEntiesList[index].prompt!) : getMenuIconEn(data.homeMenus![parentIndex].menuEntiesList[index].prompt!)), + SvgPicture.asset(AppState().isArabic(context) + ? getMenuIconAr(data.homeMenus![parentIndex].menuEntiesList[index].prompt!) + : getMenuIconEn(data.homeMenus![parentIndex].menuEntiesList[index].prompt!)), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ @@ -138,6 +140,9 @@ class ServicesWidget extends StatelessWidget { case "قاعدة الاجازات": returnImage = "assets/images/services_icons/vacation_rule.svg"; break; + case "مستنداتي": + returnImage = "assets/images/document.svg"; + break; default: returnImage = "assets/images/monthly_attendance.svg"; break; @@ -181,6 +186,9 @@ class ServicesWidget extends StatelessWidget { case "Vacation Rule": returnImage = "assets/images/services_icons/vacation_rule.svg"; break; + case "My Documents": + returnImage = "assets/images/document.svg"; + break; default: returnImage = "assets/images/monthly_attendance.svg"; break; @@ -199,6 +207,9 @@ class ServicesWidget extends StatelessWidget { } else if (menuEntry.menuName == "MBL_PERINFO_SS") { Navigator.of(context).pushNamed(AppRoutes.profile); return; + } else if (menuEntry.menuName == "MY_DOCUMENTS") { + Navigator.pushNamed(context, AppRoutes.myDocuments); + return; } List menuList = pro.getMenuEntriesList?.where((element) => element.parentMenuName == menuEntry.menuName && (element.menuEntryType == "FUNCTION")).toList() ?? []; menuEntry.icon = ""; diff --git a/lib/ui/misc/request_submit_screen.dart b/lib/ui/misc/request_submit_screen.dart index 3611241..6fa0ef8 100644 --- a/lib/ui/misc/request_submit_screen.dart +++ b/lib/ui/misc/request_submit_screen.dart @@ -31,8 +31,9 @@ class RequestSubmitScreenParams { String pItemId; String approvalFlag; String? selectedEmployeeID; + String? popNavigateToSpecificRoute; - RequestSubmitScreenParams(this.title, this.transactionId, this.pItemId, this.approvalFlag, {this.selectedEmployeeID}); + RequestSubmitScreenParams(this.title, this.transactionId, this.pItemId, this.approvalFlag, {this.selectedEmployeeID, this.popNavigateToSpecificRoute}); } class RequestSubmitScreen extends StatefulWidget { @@ -145,8 +146,13 @@ class _RequestSubmitScreenState extends State { Utils.hideLoading(context); Utils.showToast(LocaleKeys.yourRequestHasBeenSubmittedForApprovals.tr(), longDuration: true); AppState().cancelRequestTrancsection = false; - Navigator.of(context).popUntil((route) => route.settings.name == AppRoutes.dashboard); - Navigator.pushNamed(context, AppRoutes.workList); + + if (params?.popNavigateToSpecificRoute == null) { + Navigator.of(context).popUntil((route) => route.settings.name == AppRoutes.dashboard); + Navigator.pushNamed(context, AppRoutes.workList); + } else { + Navigator.of(context).popUntil((route) => route.settings.name == params!.popNavigateToSpecificRoute); + } } catch (ex) { Utils.hideLoading(context); Utils.handleException(ex, context, null); diff --git a/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart b/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart index 3e75813..8d595bf 100644 --- a/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart +++ b/lib/ui/my_attendance/dynamic_screens/dynamic_input_screen.dart @@ -94,8 +94,17 @@ class _DynamicInputScreenState extends State { genericResponseModel = await MyAttendanceApiClient().validateEitTransaction(dESCFLEXCONTEXTCODE, dynamicParams!.dynamicId, values, empID: dynamicParams!.selectedEmp); SubmitEITTransactionList submitEITTransactionList = await MyAttendanceApiClient().submitEitTransaction(dESCFLEXCONTEXTCODE, dynamicParams!.dynamicId, values, empID: dynamicParams!.selectedEmp); Utils.hideLoading(context); - await Navigator.pushNamed(context, AppRoutes.requestSubmitScreen, - arguments: RequestSubmitScreenParams(LocaleKeys.submit.tr(), submitEITTransactionList.pTRANSACTIONID!, submitEITTransactionList.pITEMKEY!, 'eit')); + await Navigator.pushNamed( + context, + AppRoutes.requestSubmitScreen, + arguments: RequestSubmitScreenParams( + LocaleKeys.submit.tr(), + submitEITTransactionList.pTRANSACTIONID!, + submitEITTransactionList.pITEMKEY!, + 'eit', + popNavigateToSpecificRoute: dynamicParams!.popUntilRoute, + ), + ); if (!AppState().cancelRequestTrancsection) { return; } diff --git a/lib/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart b/lib/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart index a9c9694..9294159 100644 --- a/lib/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart +++ b/lib/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart @@ -19,12 +19,13 @@ class DynamicListViewParams { String requestID; String colsURL; bool isUpdate; + String? popUntilRoute; List? collectionNotificationList; final String? selectedEmp; DynamicListViewParams(this.title, this.dynamicId, - {this.selectedEmp, this.uRL = 'GET_EIT_DFF_STRUCTURE', this.requestID = '', this.colsURL = '', this.isUpdate = false, this.collectionNotificationList}); + {this.selectedEmp, this.uRL = 'GET_EIT_DFF_STRUCTURE', this.requestID = '', this.colsURL = '', this.isUpdate = false, this.collectionNotificationList, this.popUntilRoute}); } class DynamicListViewScreen extends StatefulWidget { diff --git a/lib/ui/screens/my_documents/my_document_detail_screen.dart b/lib/ui/screens/my_documents/my_document_detail_screen.dart new file mode 100644 index 0000000..ec97586 --- /dev/null +++ b/lib/ui/screens/my_documents/my_document_detail_screen.dart @@ -0,0 +1,174 @@ +import 'dart:io'; + +import 'package:dotted_border/dotted_border.dart'; +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:mohem_flutter_app/app_state/app_state.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/utils.dart'; +import 'package:mohem_flutter_app/extensions/int_extensions.dart'; +import 'package:mohem_flutter_app/extensions/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/my_documents/employee_documents_list_model.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/dynamic_forms/dynamic_textfield_widget.dart'; +import 'package:mohem_flutter_app/widgets/my_document_item.dart'; +import 'package:sizer/sizer.dart'; + +class MyDocumentDetailScreen extends StatefulWidget { + EmployeeDocumentsList document; + final Color color; + + MyDocumentDetailScreen(this.document, this.color, {Key? key}) : super(key: key); + + @override + _MyDocumentDetailScreenState createState() { + return _MyDocumentDetailScreenState(); + } +} + +class _MyDocumentDetailScreenState extends State { + DateTime? expiryDate; + List imagesList = []; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget(context, title: widget.document.dOCUMENTTYPE!, showHomeButton: true), + body: Column( + children: [ + ListView( + padding: const EdgeInsets.all(21), + children: [ + MyDocumentItem(widget.document, widget.color,isNotInDetailView: false), + 20.height, + DynamicTextFieldWidget( + LocaleKeys.employeeNumber.tr(), + AppState().getUserName!, + isInputTypeNum: true, + isReadOnly: true, + ), + 12.height, + PopupMenuButton( + child: DynamicTextFieldWidget( + "Document Type*", + "National ID", + isEnable: false, + isPopup: true, + isInputTypeNum: true, + // isReadOnly: true, + ), + itemBuilder: (_) => >[], + onSelected: (int popipIndex) async {}), + 12.height, + DynamicTextFieldWidget( + "Expiry Date", + expiryDate == null ? LocaleKeys.dateRequired.tr() : Utils.getMonthNamedFormat(expiryDate!).replaceAll("-", " "), + suffixIconData: Icons.calendar_today, + isEnable: false, + onTap: () async { + DateTime date = await Utils.selectDate(context, expiryDate ?? DateTime.now()); + String dateString = date.toString().split(' ').first; + if (date != expiryDate) { + expiryDate = date; + setState(() {}); + } + }, + ), + 12.height, + DottedBorder( + borderType: BorderType.RRect, + radius: const Radius.circular(10), + padding: const EdgeInsets.all(12), + dashPattern: const [2, 1], + color: MyColors.selectedBorderColor, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Transform.rotate( + angle: 45, + child: const Icon(Icons.attach_file_rounded, size: 16, color: MyColors.selectedBorderColor), + ), + 4.width, + "Attach Image".toText14(color: MyColors.selectedBorderColor), + ], + ).center, + ).onPress(() async { + ImagePicker picker = ImagePicker(); + List list = await picker.pickMultiImage(); + if (list.isNotEmpty) { + imagesList.addAll(list); + var seen = {}; + imagesList = imagesList.where((image) => seen.add(image.name)).toList(); + setState(() {}); + } + }), + 12.height, + GridView.builder( + padding: EdgeInsets.zero, + itemCount: imagesList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, index) { + return Stack( + alignment: Alignment.topRight, + children: [ + AspectRatio( + aspectRatio: 1, + child: Image.file( + File(imagesList[index].path), + fit: BoxFit.cover, + errorBuilder: (BuildContext context, Object error, StackTrace? stackTrace) { + return const Center(child: Text('This image type is not supported')); + }, + ), + ).paddingOnly(top: 6, right: 6), + Container( + height: 16, + width: 16, + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Color(0xffd85323), + ), + child: const Icon(Icons.clear, color: Colors.white, size: 12), + ).onPress(() { + imagesList.removeAt(index); + setState(() {}); + }), + ], + ); + }, + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 4, + mainAxisSpacing: 8, + crossAxisSpacing: 8, + childAspectRatio: 1, + ), + ) + ], + ).expanded, + const Divider(height: 1, color: MyColors.lightGreyEFColor), + DefaultButton( + LocaleKeys.submit.tr(), + widget.document.dOCUMENTSTATUS == "Exist" ? null : () {}, + ).paddingOnly(left: 21, right: 21, bottom: 21, top: 14), + ], + ), + ); + } +} diff --git a/lib/ui/screens/my_documents/my_documents_fragment.dart b/lib/ui/screens/my_documents/my_documents_fragment.dart new file mode 100644 index 0000000..89ad69d --- /dev/null +++ b/lib/ui/screens/my_documents/my_documents_fragment.dart @@ -0,0 +1,157 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/profile_api_client.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/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/my_documents/employee_documents_list_model.dart'; +import 'package:mohem_flutter_app/ui/my_attendance/dynamic_screens/dynamic_listview_screen.dart'; +import 'package:mohem_flutter_app/ui/screens/my_documents/my_document_detail_screen.dart'; +import 'package:mohem_flutter_app/widgets/my_document_item.dart'; + +class MyDocumentsFragment extends StatefulWidget { + List? list; + + MyDocumentsFragment(this.list, {Key? key}) : super(key: key); + + @override + _MyDocumentsFragmentState createState() { + return _MyDocumentsFragmentState(); + } +} + +class _MyDocumentsFragmentState extends State { + int selectedIndex = 0; + List? documentsList; + + @override + void initState() { + super.initState(); + documentsList = widget.list; + } + + @override + void dispose() { + super.dispose(); + } + + @override + void didUpdateWidget(covariant MyDocumentsFragment oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.list != widget.list) { + documentsList = widget.list; + } + } + + @override + Widget build(BuildContext context) { + List documentfilteredList = getTagBySelectedTab(selectedIndex); + return Column( + children: [ + GridView.count( + crossAxisSpacing: 6, + crossAxisCount: 4, + childAspectRatio: 79 / 79, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: const EdgeInsets.only(left: 21, right: 21, bottom: 11, top: 21), + children: [ + gridViewItem(LocaleKeys.allDocuments.tr(), getTagBySelectedTab(0).length.toString(), 0, MyColors.darkTextColor), + gridViewItem(LocaleKeys.expiredDocuments.tr(), getTagBySelectedTab(1).length.toString(), 1, MyColors.redA3Color), + gridViewItem(LocaleKeys.missingDocuments.tr(), getTagBySelectedTab(2).length.toString(), 2, MyColors.yellowColor00), + gridViewItem(LocaleKeys.uploadedDocuments.tr(), getTagBySelectedTab(3).length.toString(), 3, MyColors.greenColor), + ], + ), + documentsList == null + ? const SizedBox() + : (documentfilteredList.isNotEmpty + ? ListView.separated( + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 11), + itemBuilder: (cxt, index) { + return MyDocumentItem(documentfilteredList[index], getColorByDocumentStatus(documentfilteredList[index].dOCUMENTSTATUS!)).onPress(() { + Navigator.pushNamed(context, AppRoutes.addDynamicInput, + arguments: DynamicListViewParams(documentfilteredList[index].dOCUMENTTYPE!, documentfilteredList[index].fUNCTIONNAME!, selectedEmp: AppState().getUserName, popUntilRoute: AppRoutes.myDocuments)); + }); + }, + separatorBuilder: (cxt, index) => 12.height, + itemCount: documentfilteredList.length) + : Utils.getNoDataWidget(context).center) + .expanded, + ], + ); + } + + Widget gridViewItem(String title, String value, int index, Color color) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(15), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, 0), + ), + ], + border: Border.all(color: selectedIndex == index ? MyColors.selectedBorderColor : Colors.white, width: 2), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + title.toText10(), + value.toText20(isBold: true, color: color), + ], + ), + ).onPress(() { + setState(() { + selectedIndex = index; + }); + }); + } + + Color getColorByDocumentStatus(String status) { + Color _color; + switch (status) { + case "Exist": + _color = MyColors.greenColor; + break; + case "Missing": + _color = MyColors.yellowColor00; + break; + case "Not Exist": + _color = MyColors.redA3Color; + break; + default: + _color = MyColors.darkTextColor; + break; + } + return _color; + } + + List getTagBySelectedTab(int index) { + List list = []; + switch (index) { + case 1: + list = documentsList?.where((element) => element.dOCUMENTSTATUS == "Not Exist").toList() ?? []; + break; + case 2: + list = documentsList?.where((element) => element.dOCUMENTSTATUS == "Missing").toList() ?? []; + break; + case 3: + list = documentsList?.where((element) => element.dOCUMENTSTATUS == "Exist").toList() ?? []; + break; + default: + list = documentsList ?? []; + break; + } + return list; + } +} diff --git a/lib/ui/screens/my_documents/my_documents_screen.dart b/lib/ui/screens/my_documents/my_documents_screen.dart new file mode 100644 index 0000000..ffddd36 --- /dev/null +++ b/lib/ui/screens/my_documents/my_documents_screen.dart @@ -0,0 +1,132 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/profile_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/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/my_documents/employee_documents_list_model.dart'; +import 'package:mohem_flutter_app/ui/screens/my_documents/my_documents_fragment.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; + +class MyDocumentsScreen extends StatefulWidget { + MyDocumentsScreen({Key? key}) : super(key: key); + + @override + _MyDocumentsScreenState createState() { + return _MyDocumentsScreenState(); + } +} + +class _MyDocumentsScreenState extends State { + int tabIndex = 0; + PageController controller = PageController(); + + List? documentsList; + + @override + void initState() { + super.initState(); + getDocuments(); + } + + @override + void dispose() { + super.dispose(); + } + + void getDocuments() async { + try { + documentsList?.clear(); + Utils.showLoading(context); + documentsList = await ProfileApiClient().getEmployeeDocuments(); + Utils.hideLoading(context); + setState(() {}); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + + @override + Widget build(BuildContext context) { + var requiredDocumentsList; + var optionalDocumentsList; + + if (documentsList?.isNotEmpty ?? false) { + requiredDocumentsList = []; + optionalDocumentsList = []; + documentsList!.forEach((element) { + if (element.dOCUMENTREQUIREDSTATUS == "Required") { + requiredDocumentsList.add(element); + } else { + optionalDocumentsList.add(element); + } + }); + } + + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget(context, title: LocaleKeys.myDocuments.tr(), showHomeButton: true), + body: Column( + children: [ + Container( + padding: const EdgeInsets.only(left: 21, right: 21, top: 16, bottom: 16), + decoration: const BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + ), + gradient: LinearGradient( + transform: GradientRotation(.83), + begin: Alignment.topRight, + end: Alignment.bottomLeft, + colors: [ + MyColors.gradiantEndColor, + MyColors.gradiantStartColor, + ], + ), + ), + child: Row( + children: [myTab(LocaleKeys.requiredDocuments.tr(), 0), myTab(LocaleKeys.optionalDocuments.tr(), 1)], + ), + ), + PageView( + controller: controller, + physics: const NeverScrollableScrollPhysics(), + onPageChanged: (int pageIndex) { + setState(() { + tabIndex = pageIndex; + }); + }, + children: [MyDocumentsFragment(requiredDocumentsList), MyDocumentsFragment(optionalDocumentsList)], + ).expanded, + ], + ), + ); + } + + Widget myTab(String title, int index) { + bool isSelected = (index == tabIndex); + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + title.toText12(color: isSelected ? Colors.white : Colors.white.withOpacity(.74), isCenter: true), + 4.height, + Container( + height: 8, + width: 8, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: isSelected ? Colors.white : Colors.transparent, + ), + ), + ], + ).onPress(() { + controller.jumpToPage(index); + }).expanded; + } +} diff --git a/lib/widgets/my_document_item.dart b/lib/widgets/my_document_item.dart new file mode 100644 index 0000000..b0d4b51 --- /dev/null +++ b/lib/widgets/my_document_item.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.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/models/my_documents/employee_documents_list_model.dart'; +import 'package:sizer/sizer.dart'; + +class MyDocumentItem extends StatelessWidget { + EmployeeDocumentsList document; + final Color color; + final bool isNotInDetailView; + + MyDocumentItem(this.document, this.color, {Key? key,this.isNotInDetailView = true}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(10.0), + child: Stack( + children: [ + Positioned( + top: -35, + child: Container( + width: 45, + height: 45, + transform: Matrix4.rotationZ(0.8), + color: color, + ), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + SvgPicture.asset('assets/images/document.svg').paddingOnly(top: 6), + 12.width, + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + document.dOCUMENTTYPE!.toText16(), + document.dOCUMENTSTATUS!.toText10(color: color), + ], + ).expanded, + ], + ).expanded, + if(isNotInDetailView) + const Icon( + Icons.arrow_forward, + size: 20, + color: Color(0xff2E303A), + ) + ], + ).paddingOnly(top: 14, bottom: 18, right: 14, left: 24), + ], + ), + ), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index bc70be9..aae7196 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -117,6 +117,9 @@ dependencies: # store_checker: ^1.1.0 google_api_availability: ^3.0.1 + #todo its for temporary purpose, later will remove this. + dotted_border: ^2.0.0+3 + dependency_overrides: firebase_core_platform_interface: 4.5.1