diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index dadaab0..eaa735c 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,6 +2,8 @@ + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleExecutable @@ -26,6 +28,10 @@ LaunchScreen UIMainStoryboardFile Main + NSCameraUsageDescription + This app requires camera access to capture & upload pictures. + NSPhotoLibraryUsageDescription + This app requires photo library access to select image as document & upload it. UISupportedInterfaceOrientations UIInterfaceOrientationPortrait @@ -39,9 +45,13 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + LSApplicationQueriesSchemes + + sms + tel + mailto + UIViewControllerBasedStatusBarAppearance - CADisableMinimumFrameDurationOnPhone - diff --git a/lib/api/api_client.dart b/lib/api/api_client.dart index 2f53f78..3385815 100644 --- a/lib/api/api_client.dart +++ b/lib/api/api_client.dart @@ -67,7 +67,7 @@ class ApiClient { factory ApiClient() => _instance; Future postJsonForObject(FactoryConstructor factoryConstructor, String url, T jsonObject, - {String? token, Map? queryParameters, Map? headers, int retryTimes = 0}) async { + {String? token, Map? queryParameters, Map? headers, int retryTimes = 0, bool isFormData = false}) async { var _headers = {'Accept': 'application/json'}; if (headers != null && headers.isNotEmpty) { _headers.addAll(headers); @@ -76,7 +76,7 @@ class ApiClient { print("Url:$url"); print("body:$jsonObject"); } - var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: _headers, retryTimes: retryTimes); + var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: _headers, retryTimes: retryTimes, isFormData: isFormData); // try { if (!kReleaseMode) { logger.i("res: " + response.body); @@ -101,8 +101,10 @@ class ApiClient { // } } - Future postJsonForResponse(String url, T jsonObject, {String? token, Map? queryParameters, Map? headers, int retryTimes = 0}) async { + Future postJsonForResponse(String url, T jsonObject, + {String? token, Map? queryParameters, Map? headers, int retryTimes = 0, bool isFormData = false}) async { String? requestBody; + late Map stringObj; if (jsonObject != null) { requestBody = jsonEncode(jsonObject); if (headers == null) { @@ -112,7 +114,12 @@ class ApiClient { } } - return await _postForResponse(url, requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes); + if (isFormData) { + headers = {'Content-Type': 'application/x-www-form-urlencoded'}; + stringObj = ((jsonObject ?? {}) as Map).map((key, value) => MapEntry(key, value?.toString() ?? "")); + } + + return await _postForResponse(url, isFormData ? stringObj : requestBody, token: token, queryParameters: queryParameters, headers: headers, retryTimes: retryTimes); } Future _postForResponse(String url, requestBody, {String? token, Map? queryParameters, Map? headers, int retryTimes = 0}) async { diff --git a/lib/api/items_for_sale/items_for_sale_api_client.dart b/lib/api/items_for_sale/items_for_sale_api_client.dart new file mode 100644 index 0000000..31acbc2 --- /dev/null +++ b/lib/api/items_for_sale/items_for_sale_api_client.dart @@ -0,0 +1,123 @@ +import 'dart:convert'; + +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/items_for_sale/get_employee_ads_list.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/get_items_for_sale_list.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/get_regions_list.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/get_sale_categories_list.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/item_review_model.dart'; + +class ItemsForSaleApiClient { + static final ItemsForSaleApiClient _instance = ItemsForSaleApiClient._internal(); + + ItemsForSaleApiClient._internal(); + + factory ItemsForSaleApiClient() => _instance; + + Future> getSaleCategories() async { + List getSaleCategoriesList = []; + + String url = "${ApiConsts.cocRest}Mohemm_ITG_GetItemSaleCategory"; + Map postParams = {"EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER, "ItgPageSize": 10, "ItgPageNo": 1}; + + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((response) { + final body = json.decode(response['Mohemm_ITG_ResponseItem']); + + GetSaleCategoriesList getSaleCategoriesListObj = new GetSaleCategoriesList(); + getSaleCategoriesListObj.categoryID = 0; + getSaleCategoriesListObj.title = "All"; + getSaleCategoriesListObj.titleAr = "الجميع"; + getSaleCategoriesListObj.isActive = true; + getSaleCategoriesListObj.content = + ''; + + getSaleCategoriesList.add(getSaleCategoriesListObj); + + body['result']['data'].forEach((v) { + getSaleCategoriesList.add(new GetSaleCategoriesList.fromJson(v)); + }); + return getSaleCategoriesList; + }, url, postParams); + } + + Future> getItemsForSale(int itgPageNo, int itgCategoryID) async { + List getItemsForSaleList = []; + + String url = "${ApiConsts.cocRest}Mohemm_ITG_GetItemForSale"; + Map postParams = { + "EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER, + "ItgPageSize": 10, + "ItgPageNo": itgPageNo, + "ItgStatus": "Approved", + "ItgCategoryID": itgCategoryID + }; + + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((response) { + final body = json.decode(response['Mohemm_ITG_ResponseItem']); + + body['result']['data'].forEach((v) { + getItemsForSaleList.add(new GetItemsForSaleList.fromJson(v)); + }); + return getItemsForSaleList; + }, url, postParams); + } + + Future> getEmployeePostedAds() async { + List employeePostedAdsList = []; + + String url = "${ApiConsts.cocRest}Mohemm_ITG_GetItemForSaleByEmployee"; + Map postParams = {"EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER, "ItgEmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER}; + + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((response) { + final body = json.decode(response['Mohemm_ITG_ResponseItem']); + + body['result']['data'].forEach((v) { + employeePostedAdsList.add(new EmployeePostedAds.fromJson(v)); + }); + return employeePostedAdsList; + }, url, postParams); + } + + Future> getRegions() async { + String url = "${ApiConsts.cocRest}Mohemm_ITG_GetRegion"; + List getRegionsList = []; + Map postParams = {"EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER, "ItgEmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER}; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((response) { + final body = json.decode(response['Mohemm_ITG_ResponseItem']); + + body['result']['data'].forEach((v) { + getRegionsList.add(new GetRegionsList.fromJson(v)); + }); + return getRegionsList; + }, url, postParams); + } + + Future addItemForSale(ItemReviewModel itemReviewModel, List> imagesList) async { + String url = "${ApiConsts.cocRest}Mohemm_ITG_AddItemForSaleMobile"; + Map postParams = { + "ItgImageCollList": imagesList, + "ItgTitle": itemReviewModel.itemTitle, + "ItgTitleAr": itemReviewModel.itemTitle, + "ItgCategoryID": itemReviewModel.selectedSaleCategory!.categoryID, + "ItgDescription": itemReviewModel.itemDescription, + "ItgDescriptionAr": itemReviewModel.itemDescription, + "ItgQuotePrice": itemReviewModel.itemPrice, + "RegionID": itemReviewModel.selectedRegion!.regionID, + "ItgIsActive": true, + "EmployeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER, + "employeeNumber": AppState().memberInformationList?.eMPLOYEENUMBER, + "ItgStatus": itemReviewModel.itemCondition + }; + postParams.addAll(AppState().postParamsJson); + return await ApiClient().postJsonForObject((response) { + final body = json.decode(response['Mohemm_ITG_ResponseItem']); + return body["message"]; + }, url, postParams); + } +} diff --git a/lib/classes/colors.dart b/lib/classes/colors.dart index 4b34186..794b37e 100644 --- a/lib/classes/colors.dart +++ b/lib/classes/colors.dart @@ -22,6 +22,7 @@ class MyColors { static const Color darkWhiteColor = Color(0xffE0E0E0); static const Color redColor = Color(0xffD02127); static const Color yellowColor = Color(0xffF4E31C); + static const Color orange = Color(0xFFCC9B14); static const Color backgroundBlackColor = Color(0xff202529); static const Color black = Color(0xff000000); static const Color white = Color(0xffffffff); diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index e806301..12f818e 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -22,4 +22,5 @@ class SharedPrefsConsts { static String doNotShowWelcomeVideo = "doNotShowWelcomeVideo"; static String mohemmWifiSSID = "mohemmWifiSSID"; static String mohemmWifiPassword = "mohemmWifiPassword"; + static String editItemForSale = "editItemForSale"; } diff --git a/lib/classes/utils.dart b/lib/classes/utils.dart index 292bc4c..e2ae38e 100644 --- a/lib/classes/utils.dart +++ b/lib/classes/utils.dart @@ -178,6 +178,31 @@ class Utils { ); } + static Decoration containerRadius(Color background, double radius) { + return BoxDecoration( + color: background, + border: Border.all( + width: 1, // + color: background // <--- border width here + ), + borderRadius: BorderRadius.circular(radius), + ); + } + + static Widget mHeight(double h) { + return Container( + height: h, + ); + } + + static Widget mDivider(Color color) { + return Divider( + // width: double.infinity, + height: 1, + color: color, + ); + } + static Widget tableColumnValue(String text, {bool isCapitable = true, bool alignCenter = false}) { return Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 72dc5ed..6928f09 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -28,6 +28,9 @@ import 'package:mohem_flutter_app/ui/screens/announcements/announcements.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/items_for_sale/add_new_item_for_sale.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/item_for_sale_detail.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/items_for_sale_home.dart'; 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'; @@ -109,6 +112,11 @@ class AppRoutes { static const String myRequests = "/myRequests"; static const String newRequest = "/newRequests"; + // Items For Sale + static const String itemsForSale = "/itemsForSale"; + static const String itemsForSaleDetail = "/itemsForSaleDetail"; + static const String addNewItemForSale = "/addNewItemForSale"; + //Pay slip static const String monthlyPaySlip = "/monthlyPaySlip"; @@ -175,7 +183,13 @@ class AppRoutes { myRequests: (context) => MyRequests(), newRequest: (context) => NewRequest(), + // Items for sale + itemsForSale: (context) => ItemsForSale(), + itemsForSaleDetail: (context) => ItemForSaleDetailPage(), + addNewItemForSale: (context) => AddNewItemForSale(), + + //pay slip monthlyPaySlip: (context) => MonthlyPaySlipScreen(), }; -} +} \ No newline at end of file diff --git a/lib/models/items_for_sale/add_item_for_sale_image_model.dart b/lib/models/items_for_sale/add_item_for_sale_image_model.dart new file mode 100644 index 0000000..b26929e --- /dev/null +++ b/lib/models/items_for_sale/add_item_for_sale_image_model.dart @@ -0,0 +1,25 @@ +class AddItemForSaleImageModel { + int? attachmentID; + String? base64Data; + String? fileName; + String? contentType; + + AddItemForSaleImageModel( + {this.attachmentID, this.base64Data, this.fileName, this.contentType}); + + AddItemForSaleImageModel.fromJson(Map json) { + attachmentID = json['AttachmentID']; + base64Data = json['Base64Data']; + fileName = json['FileName']; + contentType = json['ContentType']; + } + + Map toJson() { + final Map data = new Map(); + data['AttachmentID'] = this.attachmentID; + data['Base64Data'] = this.base64Data; + data['FileName'] = this.fileName; + data['ContentType'] = this.contentType; + return data; + } +} diff --git a/lib/models/items_for_sale/get_employee_ads_list.dart b/lib/models/items_for_sale/get_employee_ads_list.dart new file mode 100644 index 0000000..97e06b8 --- /dev/null +++ b/lib/models/items_for_sale/get_employee_ads_list.dart @@ -0,0 +1,181 @@ +class EmployeePostedAds { + int? itemSaleID; + String? title; + String? titleAr; + String? description; + String? descriptionAr; + int? categoryID; + String? categoryTitle; + int? regionID; + String? regionName; + String? countryName; + String? currencyCode; + String? startDate; + String? endDate; + int? quotePrice; + int? employeeNumber; + String? profilePicture; + String? fullName; + String? emailAddress; + String? mobileNumber; + bool? isApproved; + String? status; + List? itemAttachments; + String? created; + dynamic? comments; + dynamic? isActive; + int? pageSize; + int? pageNo; + dynamic? languageId; + + EmployeePostedAds( + {this.itemSaleID, + this.title, + this.titleAr, + this.description, + this.descriptionAr, + this.categoryID, + this.categoryTitle, + this.regionID, + this.regionName, + this.countryName, + this.currencyCode, + this.startDate, + this.endDate, + this.quotePrice, + this.employeeNumber, + this.profilePicture, + this.fullName, + this.emailAddress, + this.mobileNumber, + this.isApproved, + this.status, + this.itemAttachments, + this.created, + this.comments, + this.isActive, + this.pageSize, + this.pageNo, + this.languageId}); + + EmployeePostedAds.fromJson(Map json) { + itemSaleID = json['itemSaleID']; + title = json['title']; + titleAr = json['title_Ar']; + description = json['description']; + descriptionAr = json['description_Ar']; + categoryID = json['categoryID']; + categoryTitle = json['categoryTitle']; + regionID = json['regionID']; + regionName = json['regionName']; + countryName = json['countryName']; + currencyCode = json['currencyCode']; + startDate = json['startDate']; + endDate = json['endDate']; + quotePrice = json['quotePrice']; + employeeNumber = json['employeeNumber']; + profilePicture = json['profilePicture']; + fullName = json['fullName']; + emailAddress = json['emailAddress']; + mobileNumber = json['mobileNumber']; + isApproved = json['isApproved']; + status = json['status']; + if (json['itemAttachments'] != null) { + itemAttachments = []; + json['itemAttachments'].forEach((v) { + itemAttachments!.add(new ItemAttachments.fromJson(v)); + }); + } + created = json['created']; + comments = json['comments']; + isActive = json['isActive']; + pageSize = json['pageSize']; + pageNo = json['pageNo']; + languageId = json['languageId']; + } + + Map toJson() { + final Map data = new Map(); + data['itemSaleID'] = this.itemSaleID; + data['title'] = this.title; + data['title_Ar'] = this.titleAr; + data['description'] = this.description; + data['description_Ar'] = this.descriptionAr; + data['categoryID'] = this.categoryID; + data['categoryTitle'] = this.categoryTitle; + data['regionID'] = this.regionID; + data['regionName'] = this.regionName; + data['countryName'] = this.countryName; + data['currencyCode'] = this.currencyCode; + data['startDate'] = this.startDate; + data['endDate'] = this.endDate; + data['quotePrice'] = this.quotePrice; + data['employeeNumber'] = this.employeeNumber; + data['profilePicture'] = this.profilePicture; + data['fullName'] = this.fullName; + data['emailAddress'] = this.emailAddress; + data['mobileNumber'] = this.mobileNumber; + data['isApproved'] = this.isApproved; + data['status'] = this.status; + if (this.itemAttachments != null) { + data['itemAttachments'] = + this.itemAttachments!.map((v) => v.toJson()).toList(); + } + data['created'] = this.created; + data['comments'] = this.comments; + data['isActive'] = this.isActive; + data['pageSize'] = this.pageSize; + data['pageNo'] = this.pageNo; + data['languageId'] = this.languageId; + return data; + } +} + +class ItemAttachments { + int? attachmentId; + String? fileName; + String? contentType; + String? attachFileStream; + String? base64String; + dynamic? isActive; + int? referenceItemId; + String? content; + String? filePath; + + ItemAttachments( + {this.attachmentId, + this.fileName, + this.contentType, + this.attachFileStream, + this.base64String, + this.isActive, + this.referenceItemId, + this.content, + this.filePath}); + + ItemAttachments.fromJson(Map json) { + attachmentId = json['attachmentId']; + fileName = json['fileName']; + contentType = json['contentType']; + attachFileStream = json['attachFileStream']; + base64String = json['base64String']; + isActive = json['isActive']; + referenceItemId = json['referenceItemId']; + content = json['content']; + filePath = json['filePath']; + } + + Map toJson() { + final Map data = new Map(); + data['attachmentId'] = this.attachmentId; + data['fileName'] = this.fileName; + data['contentType'] = this.contentType; + data['attachFileStream'] = this.attachFileStream; + data['base64String'] = this.base64String; + data['isActive'] = this.isActive; + data['referenceItemId'] = this.referenceItemId; + data['content'] = this.content; + data['filePath'] = this.filePath; + return data; + } +} diff --git a/lib/models/items_for_sale/get_items_for_sale_list.dart b/lib/models/items_for_sale/get_items_for_sale_list.dart new file mode 100644 index 0000000..091b023 --- /dev/null +++ b/lib/models/items_for_sale/get_items_for_sale_list.dart @@ -0,0 +1,181 @@ +class GetItemsForSaleList { + int? itemSaleID; + String? title; + String? titleAr; + String? description; + String? descriptionAr; + int? categoryID; + String? categoryTitle; + int? regionID; + String? regionName; + String? countryName; + String? currencyCode; + String? startDate; + String? endDate; + int? quotePrice; + int? employeeNumber; + String? profilePicture; + String? fullName; + String? emailAddress; + String? mobileNumber; + bool? isApproved; + String? status; + List? itemAttachments; + String? created; + dynamic? comments; + dynamic? isActive; + dynamic? pageSize; + dynamic? pageNo; + dynamic? languageId; + + GetItemsForSaleList( + {this.itemSaleID, + this.title, + this.titleAr, + this.description, + this.descriptionAr, + this.categoryID, + this.categoryTitle, + this.regionID, + this.regionName, + this.countryName, + this.currencyCode, + this.startDate, + this.endDate, + this.quotePrice, + this.employeeNumber, + this.profilePicture, + this.fullName, + this.emailAddress, + this.mobileNumber, + this.isApproved, + this.status, + this.itemAttachments, + this.created, + this.comments, + this.isActive, + this.pageSize, + this.pageNo, + this.languageId}); + + GetItemsForSaleList.fromJson(Map json) { + itemSaleID = json['itemSaleID']; + title = json['title']; + titleAr = json['title_Ar']; + description = json['description']; + descriptionAr = json['description_Ar']; + categoryID = json['categoryID']; + categoryTitle = json['categoryTitle']; + regionID = json['regionID']; + regionName = json['regionName']; + countryName = json['countryName']; + currencyCode = json['currencyCode']; + startDate = json['startDate']; + endDate = json['endDate']; + quotePrice = json['quotePrice']; + employeeNumber = json['employeeNumber']; + profilePicture = json['profilePicture']; + fullName = json['fullName']; + emailAddress = json['emailAddress']; + mobileNumber = json['mobileNumber']; + isApproved = json['isApproved']; + status = json['status']; + if (json['itemAttachments'] != null) { + itemAttachments = []; + json['itemAttachments'].forEach((v) { + itemAttachments!.add(new ItemAttachments.fromJson(v)); + }); + } + created = json['created']; + comments = json['comments']; + isActive = json['isActive']; + pageSize = json['pageSize']; + pageNo = json['pageNo']; + languageId = json['languageId']; + } + + Map toJson() { + final Map data = new Map(); + data['itemSaleID'] = this.itemSaleID; + data['title'] = this.title; + data['title_Ar'] = this.titleAr; + data['description'] = this.description; + data['description_Ar'] = this.descriptionAr; + data['categoryID'] = this.categoryID; + data['categoryTitle'] = this.categoryTitle; + data['regionID'] = this.regionID; + data['regionName'] = this.regionName; + data['countryName'] = this.countryName; + data['currencyCode'] = this.currencyCode; + data['startDate'] = this.startDate; + data['endDate'] = this.endDate; + data['quotePrice'] = this.quotePrice; + data['employeeNumber'] = this.employeeNumber; + data['profilePicture'] = this.profilePicture; + data['fullName'] = this.fullName; + data['emailAddress'] = this.emailAddress; + data['mobileNumber'] = this.mobileNumber; + data['isApproved'] = this.isApproved; + data['status'] = this.status; + if (this.itemAttachments != null) { + data['itemAttachments'] = + this.itemAttachments!.map((v) => v.toJson()).toList(); + } + data['created'] = this.created; + data['comments'] = this.comments; + data['isActive'] = this.isActive; + data['pageSize'] = this.pageSize; + data['pageNo'] = this.pageNo; + data['languageId'] = this.languageId; + return data; + } +} + +class ItemAttachments { + int? attachmentId; + String? fileName; + String? contentType; + dynamic? attachFileStream; + dynamic? base64String; + dynamic? isActive; + int? referenceItemId; + String? content; + String? filePath; + + ItemAttachments( + {this.attachmentId, + this.fileName, + this.contentType, + this.attachFileStream, + this.base64String, + this.isActive, + this.referenceItemId, + this.content, + this.filePath}); + + ItemAttachments.fromJson(Map json) { + attachmentId = json['attachmentId']; + fileName = json['fileName']; + contentType = json['contentType']; + attachFileStream = json['attachFileStream']; + base64String = json['base64String']; + isActive = json['isActive']; + referenceItemId = json['referenceItemId']; + content = json['content']; + filePath = json['filePath']; + } + + Map toJson() { + final Map data = new Map(); + data['attachmentId'] = this.attachmentId; + data['fileName'] = this.fileName; + data['contentType'] = this.contentType; + data['attachFileStream'] = this.attachFileStream; + data['base64String'] = this.base64String; + data['isActive'] = this.isActive; + data['referenceItemId'] = this.referenceItemId; + data['content'] = this.content; + data['filePath'] = this.filePath; + return data; + } +} diff --git a/lib/models/items_for_sale/get_regions_list.dart b/lib/models/items_for_sale/get_regions_list.dart new file mode 100644 index 0000000..4b36f03 --- /dev/null +++ b/lib/models/items_for_sale/get_regions_list.dart @@ -0,0 +1,48 @@ +class GetRegionsList { + int? regionID; + String? regionName; + String? regionNameAr; + int? countryID; + String? countryName; + dynamic? isActive; + int? pageSize; + int? pageNo; + dynamic? languageId; + + GetRegionsList( + {this.regionID, + this.regionName, + this.regionNameAr, + this.countryID, + this.countryName, + this.isActive, + this.pageSize, + this.pageNo, + this.languageId}); + + GetRegionsList.fromJson(Map json) { + regionID = json['regionID']; + regionName = json['regionName']; + regionNameAr = json['regionName_Ar']; + countryID = json['countryID']; + countryName = json['countryName']; + isActive = json['isActive']; + pageSize = json['pageSize']; + pageNo = json['pageNo']; + languageId = json['languageId']; + } + + Map toJson() { + final Map data = new Map(); + data['regionID'] = this.regionID; + data['regionName'] = this.regionName; + data['regionName_Ar'] = this.regionNameAr; + data['countryID'] = this.countryID; + data['countryName'] = this.countryName; + data['isActive'] = this.isActive; + data['pageSize'] = this.pageSize; + data['pageNo'] = this.pageNo; + data['languageId'] = this.languageId; + return data; + } +} diff --git a/lib/models/items_for_sale/get_sale_categories_list.dart b/lib/models/items_for_sale/get_sale_categories_list.dart new file mode 100644 index 0000000..03f28e3 --- /dev/null +++ b/lib/models/items_for_sale/get_sale_categories_list.dart @@ -0,0 +1,36 @@ +class GetSaleCategoriesList { + int? categoryID; + String? title; + String? titleAr; + String? content; + bool? isActive; + dynamic? pageSize; + dynamic? pageNo; + dynamic? languageId; + + GetSaleCategoriesList({this.categoryID, this.title, this.titleAr, this.content, this.isActive, this.pageSize, this.pageNo, this.languageId}); + + GetSaleCategoriesList.fromJson(Map json) { + categoryID = json['categoryID']; + title = json['title']; + titleAr = json['title_Ar']; + content = json['content']; + isActive = json['isActive']; + pageSize = json['pageSize']; + pageNo = json['pageNo']; + languageId = json['languageId']; + } + + Map toJson() { + final Map data = new Map(); + data['categoryID'] = this.categoryID; + data['title'] = this.title; + data['title_Ar'] = this.titleAr; + data['content'] = this.content; + data['isActive'] = this.isActive; + data['pageSize'] = this.pageSize; + data['pageNo'] = this.pageNo; + data['languageId'] = this.languageId; + return data; + } +} diff --git a/lib/models/items_for_sale/item_review_model.dart b/lib/models/items_for_sale/item_review_model.dart new file mode 100644 index 0000000..4033746 --- /dev/null +++ b/lib/models/items_for_sale/item_review_model.dart @@ -0,0 +1,35 @@ +import 'package:mohem_flutter_app/models/items_for_sale/get_regions_list.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/get_sale_categories_list.dart'; + +class ItemReviewModel { + String? itemTitle; + String? itemDescription; + String? itemCondition; + GetRegionsList? selectedRegion; + num? itemPrice; + List? itemPhotos; + GetSaleCategoriesList? selectedSaleCategory; + + ItemReviewModel( + this.itemTitle, + this.itemDescription, + this.itemCondition, + this.selectedRegion, + this.itemPrice, + this.itemPhotos, + this.selectedSaleCategory, + ); + + Map toJson() { + final Map data = new Map(); + data['itemTitle'] = this.itemTitle; + data['itemDescription'] = this.itemDescription; + data['itemCondition'] = this.itemCondition; + data['selectedRegion'] = this.selectedRegion; + data['itemPrice'] = this.itemPrice; + data['itemPhotos'] = this.itemPhotos; + data['selectedSaleCategory'] = this.selectedSaleCategory; + return data; + } + +} diff --git a/lib/ui/landing/dashboard_screen.dart b/lib/ui/landing/dashboard_screen.dart index 4406a4d..d5acce6 100644 --- a/lib/ui/landing/dashboard_screen.dart +++ b/lib/ui/landing/dashboard_screen.dart @@ -389,8 +389,9 @@ class _DashboardScreenState extends State { selectedIconTheme: const IconThemeData(color: MyColors.grey3AColor, size: 28), unselectedIconTheme: const IconThemeData(color: MyColors.grey98Color, size: 28), onTap: (int index) { - currentIndex = index; - setState(() {}); + // currentIndex = index; + // setState(() {}); + Navigator.pushNamed(context, AppRoutes.itemsForSale); }, ), ), diff --git a/lib/ui/screens/items_for_sale/add_new_item_for_sale.dart b/lib/ui/screens/items_for_sale/add_new_item_for_sale.dart new file mode 100644 index 0000000..307a834 --- /dev/null +++ b/lib/ui/screens/items_for_sale/add_new_item_for_sale.dart @@ -0,0 +1,216 @@ +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/items_for_sale/items_for_sale_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/widget_extensions.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/get_sale_categories_list.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/item_review_model.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/fragments/add_details_fragment.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/fragments/item_review_fragment.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/fragments/select_category_fragment.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; + +class AddNewItemForSale extends StatefulWidget { + int? pageIndex = 0; + ItemReviewModel? itemReviewModel; + + AddNewItemForSale({Key? key, this.pageIndex, this.itemReviewModel}) : super(key: key); + + @override + State createState() => _AddNewItemForSaleState(); +} + +class _AddNewItemForSaleState extends State { + int _currentIndex = 0; + List getSaleCategoriesList = []; + late PageController _controller; + ItemReviewModel? itemReviewModel; + int pageIndex = 0; + + @override + void initState() { + _controller = PageController(); + getItemForSaleCategory(); + super.initState(); + } + + void changePageViewIndex(pageIndex) { + _controller.jumpToPage(pageIndex); + } + + @override + Widget build(BuildContext context) { + getRequestID(); + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget( + context, + // title: LocaleKeys.mowadhafhiRequest.tr(), + title: "Items for sale", + showHomeButton: true, + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AspectRatio( + aspectRatio: 335 / 118, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Row( + children: [ + Expanded( + child: showProgress( + title: "Select Category", + status: _currentIndex == 0 + ? "InProgress" + : _currentIndex > 0 + ? "Completed" + : "Locked", + color: _currentIndex == 0 ? MyColors.orange : MyColors.greenColor, + pageIndex: 0, + ), + ), + Expanded( + child: showProgress( + title: "Add Details", + status: _currentIndex == 1 + ? "InProgress" + : _currentIndex > 1 + ? "Completed" + : "Locked", + color: _currentIndex == 1 + ? MyColors.orange + : _currentIndex > 1 + ? MyColors.greenColor + : MyColors.lightGrayColor, + pageIndex: 1, + ), + ), + showProgress( + title: "Review & Sell", + status: _currentIndex == 2 ? "InProgress" : "Locked", + color: _currentIndex == 2 + ? MyColors.orange + : _currentIndex > 3 + ? MyColors.greenColor + : MyColors.lightGrayColor, + isNeedBorder: false, + pageIndex: 2, + ), + ], + ).paddingAll(21), + ).paddingOnly(left: 21, right: 21, top: 21), + ), + Expanded( + child: PageView( + physics: NeverScrollableScrollPhysics(), + controller: _controller, + onPageChanged: (index) { + setState(() { + _currentIndex = index; + }); + }, + scrollDirection: Axis.horizontal, + children: [ + getSaleCategoriesList.isNotEmpty ? SelectCategoryFragment(changePageViewIndex: changePageViewIndex, getSaleCategoriesList: getSaleCategoriesList) : Container(), + getSaleCategoriesList.isNotEmpty ? AddItemDetailsFragment(changePageViewIndex: changePageViewIndex, selectedSaleCategory: getSaleCategoriesList[0]) : Container(), + ItemReviewFragment(changePageViewIndex: changePageViewIndex), + ], + ), + ), + ], + ), + ); + } + + Widget showProgress({String? title, String? status, Color? color, bool isNeedBorder = true, int? pageIndex}) { + return InkWell( + onTap: () { + if (_currentIndex > pageIndex!) changePageViewIndex(pageIndex); + }, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 26, + height: 26, + decoration: Utils.containerRadius(color!, 200), + child: const Icon( + Icons.done, + color: Colors.white, + size: 16, + ), + ), + if (isNeedBorder) + Expanded( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Utils.mDivider(Colors.grey), + )), + ], + ), + Utils.mHeight(8), + Text( + title!, + style: const TextStyle( + fontSize: 11, + fontWeight: FontWeight.w600, + letterSpacing: -0.44, + ), + ), + Utils.mHeight(2), + Container( + padding: EdgeInsets.all(5), + decoration: Utils.containerRadius(color.withOpacity(0.2), 4), + child: Text( + status!, + style: TextStyle( + fontSize: 8, + fontWeight: FontWeight.w600, + letterSpacing: -0.32, + color: color, + ), + ), + ), + ], + ) + ], + ), + ); + } + + void getRequestID() async { + int args = (ModalRoute.of(context)?.settings.arguments ?? {}) as int; + pageIndex = args; + } + + void getItemForSaleCategory() async { + try { + Utils.showLoading(context); + getSaleCategoriesList = await ItemsForSaleApiClient().getSaleCategories(); + Utils.hideLoading(context); + setState(() {}); + if (pageIndex == 1) { + changePageViewIndex(1); + } + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } +} diff --git a/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart b/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart new file mode 100644 index 0000000..9d253f0 --- /dev/null +++ b/lib/ui/screens/items_for_sale/fragments/add_details_fragment.dart @@ -0,0 +1,293 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/items_for_sale/items_for_sale_api_client.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/consts.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/items_for_sale/get_regions_list.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/get_sale_categories_list.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/item_review_model.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/fragments/select_category_fragment.dart'; +import 'package:mohem_flutter_app/widgets/button/default_button.dart'; +import 'package:mohem_flutter_app/widgets/button/simple_button.dart'; +import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget.dart'; +import 'package:mohem_flutter_app/widgets/image_picker.dart'; +import 'package:mohem_flutter_app/widgets/radio/show_radio.dart'; + +class AddItemDetailsFragment extends StatefulWidget { + final Function changePageViewIndex; + final GetSaleCategoriesList selectedSaleCategory; + static late ItemReviewModel itemReviewModel; + + const AddItemDetailsFragment({Key? key, required this.changePageViewIndex, required this.selectedSaleCategory}) : super(key: key); + + @override + State createState() => _AddItemDetailsFragmentState(); +} + +class _AddItemDetailsFragmentState extends State { + String itemTitle = ""; + String itemDescription = ""; + num itemPrice = 0; + String selectedItemCondition = "new"; + + List getRegionsList = []; + GetRegionsList selectedRegion = GetRegionsList(); + + List images = []; + + @override + void initState() { + getRegions(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Add details".toText20(isBold: true).paddingOnly(top: 24, left: 21, right: 21), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DynamicTextFieldWidget( + "Title", + itemTitle.isEmpty ? "Item title" : itemTitle, + isEnable: true, + suffixIconData: Icons.search, + isPopup: false, + lines: 1, + isInputTypeNum: false, + isReadOnly: false, + onChange: (String value) { + itemTitle = value; + }, + ).paddingOnly(), + DynamicTextFieldWidget( + "Description", + itemDescription.isEmpty ? "Item description" : itemDescription, + isEnable: true, + suffixIconData: Icons.search, + isPopup: false, + lines: 4, + isInputTypeNum: false, + isReadOnly: false, + onChange: (String value) { + itemDescription = value; + }, + ).paddingOnly(top: 12), + "Item Condition".toText14(isBold: true).paddingOnly(top: 21), + Row( + children: [ + ShowRadio(title: "New", value: "new", groupValue: selectedItemCondition, selectedColor: MyColors.gradiantStartColor).onPress(() { + selectedItemCondition = "new"; + setState(() {}); + }), + 12.width, + ShowRadio(title: "Used", value: "used", groupValue: selectedItemCondition, selectedColor: MyColors.gradiantStartColor).onPress(() { + selectedItemCondition = "used"; + setState(() {}); + }), + ], + ).paddingOnly(top: 12), + PopupMenuButton( + child: DynamicTextFieldWidget( + "Region", + selectedRegion.regionName ?? "Select Region", + isEnable: false, + isPopup: true, + isInputTypeNum: true, + isReadOnly: false, + ), + itemBuilder: (_) => >[ + for (int i = 0; i < getRegionsList!.length; i++) PopupMenuItem(child: Text(getRegionsList[i].regionName!), value: i), + ], + onSelected: (int popupIndex) { + selectedRegion = getRegionsList![popupIndex]; + setState(() {}); + }, + ).paddingOnly(top: 21), + DynamicTextFieldWidget( + "Item Price", + itemPrice == 0 ? "Price" : itemPrice.toString(), + isEnable: true, + suffixIconData: Icons.search, + isPopup: false, + lines: 1, + isInputTypeNum: true, + isReadOnly: false, + onChange: (String value) { + itemPrice = num.parse(value); + }, + ).paddingOnly(top: 12), + "Item Photos".toText14(isBold: true).paddingOnly(top: 16), + attachmentView("Attachments").paddingOnly(top: 12), + Row( + children: [ + DefaultButton( + LocaleKeys.cancel.tr(), + () async { + Navigator.of(context).pop(); + }, + colors: const [Color(0xffD02127), Color(0xffD02127)], + ).expanded, + 12.width, + DefaultButton( + LocaleKeys.next.tr(), + isButtonDisabled() + ? null + : () async { + AddItemDetailsFragment.itemReviewModel = getItemReviewObject(); + widget.changePageViewIndex(2); + }, + disabledColor: MyColors.lightGrayColor, + ).expanded + ], + ).paddingOnly(top: 21), + ], + ).objectContainerView(title: "Item Info").paddingAll(21), + ], + ), + ); + } + + ItemReviewModel getItemReviewObject() { + ItemReviewModel itemReviewModel = ItemReviewModel(itemTitle, itemDescription, selectedItemCondition, selectedRegion, itemPrice, images, widget.selectedSaleCategory); + return itemReviewModel; + } + + bool isButtonDisabled() { + if (itemTitle.isNotEmpty && itemDescription.isNotEmpty && selectedRegion != null && itemPrice != 0 && images.isNotEmpty) { + return false; + } else { + return true; + } + } + + Widget attachmentView(String title) { + return Container( + padding: const EdgeInsets.only(top: 15, bottom: 15, left: 14, right: 14), + 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, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + title.toText16().expanded, + 6.width, + SimpleButton(LocaleKeys.add.tr(), () async { + ImageOptions.showImageOptions(context, (String image, File file) { + setState(() { + images.add(image); + }); + }); + }, fontSize: 14), + ], + ), + if (images.isNotEmpty) 12.height, + if (images.isNotEmpty) + ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (cxt, index) { + return Container( + margin: const EdgeInsets.all(10), + padding: const EdgeInsets.all(8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Icon(Icons.attach_file_sharp), + const SizedBox( + width: 8, + ), + 'image ${index + 1}.png'.toText16(), + ], + ), + InkWell( + onTap: () { + setState(() { + images.remove(images[index]); + }); + }, + child: Icon( + Icons.delete_sharp, + color: Colors.red[300], + )) + ], + ), + ); + }, + separatorBuilder: (cxt, index) => 6.height, + itemCount: images.length), + ], + ), + ); + } + + void getAdDetails() async { + String details = await Utils.getStringFromPrefs(SharedPrefsConsts.editItemForSale); + final body = json.decode(details); + + GetRegionsList selectedRegionAd = GetRegionsList(); + + GetSaleCategoriesList selectedSaleCategoryAd = GetSaleCategoriesList(); + + itemTitle = body["itemTitle"]; + itemDescription = body["itemDescription"]; + selectedItemCondition = body["itemCondition"].toString().toLowerCase(); + selectedRegionAd.regionID = body["selectedRegion"]["regionID"]; + selectedRegionAd.regionName = body["selectedRegion"]["regionName"]; + selectedRegion = selectedRegionAd; + itemPrice = body["itemPrice"]; + selectedSaleCategoryAd.categoryID = body["selectedSaleCategory"]["categoryID"]; + selectedSaleCategoryAd.title = body["selectedSaleCategory"]["title"]; + if (body["itemPhotos"].length != 0) { + images.add(body["itemPhotos"][0]); + } + ItemReviewModel itemReviewModel = + ItemReviewModel(body["itemTitle"], body["itemDescription"], body["itemCondition"].toString().toLowerCase(), selectedRegionAd, body["itemPrice"], images, selectedSaleCategoryAd); + + AddItemDetailsFragment.itemReviewModel = itemReviewModel; + SelectCategoryFragment.selectedSaleCategory = selectedSaleCategoryAd; + + setState(() {}); + } + + void getRegions() async { + try { + Utils.showLoading(context); + getRegionsList = await ItemsForSaleApiClient().getRegions(); + Utils.hideLoading(context); + setState(() {}); + getAdDetails(); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } +} diff --git a/lib/ui/screens/items_for_sale/fragments/item_review_fragment.dart b/lib/ui/screens/items_for_sale/fragments/item_review_fragment.dart new file mode 100644 index 0000000..0b02658 --- /dev/null +++ b/lib/ui/screens/items_for_sale/fragments/item_review_fragment.dart @@ -0,0 +1,154 @@ +import 'dart:convert'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/api/items_for_sale/items_for_sale_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/items_for_sale/add_item_for_sale_image_model.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/item_review_model.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/fragments/add_details_fragment.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/fragments/select_category_fragment.dart'; +import 'package:mohem_flutter_app/widgets/button/default_button.dart'; + +class ItemReviewFragment extends StatefulWidget { + final Function changePageViewIndex; + + ItemReviewFragment({Key? key, required this.changePageViewIndex}) : super(key: key); + + @override + State createState() => _ItemReviewFragmentState(); +} + +class _ItemReviewFragmentState extends State { + ItemReviewModel? itemReviewModel; + + @override + void initState() { + itemReviewModel = AddItemDetailsFragment.itemReviewModel; + itemReviewModel!.selectedSaleCategory = SelectCategoryFragment.selectedSaleCategory; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + itemReviewModel!.itemTitle.toString().toText17(isBold: true).paddingOnly(top: 12), + itemReviewModel!.itemDescription.toString().toText14().paddingOnly(top: 8), + itemReviewModel!.itemCondition.toString().toText14(color: MyColors.yellowColor).paddingOnly(top: 12), + SizedBox( + height: 105.0, + child: ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + scrollDirection: Axis.horizontal, + itemBuilder: (cxt, index) { + 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), + ), + ], + ), + width: 100, + height: 100, + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: Image.memory( + base64Decode(itemReviewModel!.itemPhotos![index]), + fit: BoxFit.contain, + ), + ), + ).paddingOnly(top: 12.0, bottom: 12.0); + }, + separatorBuilder: (cxt, index) => 8.width, + itemCount: itemReviewModel!.itemPhotos!.length), + ), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + "Selling for ".toText14(), + "${itemReviewModel!.itemPrice.toString()} SAR".toText20(isBold: true), + ], + ), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + const Icon( + Icons.warning_sharp, + size: 20, + color: MyColors.redColor, + ).paddingOnly(top: 21), + "This ad will be valid for 2 weeks after approval.".toText11(color: MyColors.redColor).paddingOnly(left: 10, right: 10), + ], + ), + const Spacer(), + Row( + children: [ + DefaultButton( + LocaleKeys.cancel.tr(), + () async { + Navigator.of(context).pop(); + }, + colors: const [Color(0xffD02127), Color(0xffD02127)], + ).expanded, + 12.width, + DefaultButton( + LocaleKeys.submit.tr(), + () async { + submitItemForSale(); + }, + disabledColor: MyColors.lightGrayColor, + ).expanded + ], + ).paddingOnly(top: 21), + ], + ), + ).paddingAll(21); + } + + void submitItemForSale() async { + List> imagesList = []; + int attachmentID = 1; + for (var element in itemReviewModel!.itemPhotos!) { + imagesList.add(AddItemForSaleImageModel(attachmentID: attachmentID, base64Data: element, fileName: "Image_$attachmentID", contentType: "image/png").toJson()); + attachmentID++; + } + + try { + Utils.showLoading(context); + String message = await ItemsForSaleApiClient().addItemForSale(itemReviewModel!, imagesList); + Utils.hideLoading(context); + Utils.showToast(message); + Navigator.of(context).pop(); + setState(() {}); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } +} diff --git a/lib/ui/screens/items_for_sale/fragments/items_for_sale.dart b/lib/ui/screens/items_for_sale/fragments/items_for_sale.dart new file mode 100644 index 0000000..ffb45e0 --- /dev/null +++ b/lib/ui/screens/items_for_sale/fragments/items_for_sale.dart @@ -0,0 +1,239 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:mohem_flutter_app/api/items_for_sale/items_for_sale_api_client.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/models/items_for_sale/get_items_for_sale_list.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/get_sale_categories_list.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/items_for_sale_home.dart'; +import 'package:mohem_flutter_app/widgets/dynamic_forms/dynamic_textfield_widget.dart'; + +class ItemsForSaleFragment extends StatefulWidget { + @override + State createState() => _ItemsForSaleFragmentState(); +} + +class _ItemsForSaleFragmentState extends State { + TextEditingController searchController = TextEditingController(); + + List getSaleCategoriesList = []; + List getItemsForSaleList = []; + + ScrollController gridScrollController = ScrollController(); + int currentPageNo = 1; + int currentCategoryID = 0; + + @override + void initState() { + getItemForSaleCategory(); + gridScrollController.addListener(() { + if (gridScrollController.position.atEdge) { + bool isTop = gridScrollController.position.pixels == 0; + if (!isTop) { + print('At the bottom'); + currentPageNo++; + getItemsForSale(currentPageNo, currentCategoryID); + } + } + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + controller: gridScrollController, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + DynamicTextFieldWidget( + "Search", + "Search Items", + isEnable: true, + suffixIconData: Icons.search, + isPopup: false, + lines: 1, + isInputTypeNum: false, + isReadOnly: false, + onChange: (String value) { + // _runFilter(value); + }, + ).paddingOnly(left: 21, right: 21, top: 21, bottom: 18), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Browse Categories".toText17(), + IconButton( + icon: const Icon(Icons.filter_alt_sharp, color: MyColors.darkIconColor, size: 28.0), + onPressed: () => Navigator.pop(context), + ), + ], + ).paddingOnly(left: 21, right: 21), + SizedBox( + height: 105.0, + child: getSaleCategoriesList.isNotEmpty + ? ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + padding: const EdgeInsets.only(left: 21, right: 21, top: 13, bottom: 13), + scrollDirection: Axis.horizontal, + itemBuilder: (cxt, index) { + return AspectRatio( + aspectRatio: 1 / 1, + child: InkWell( + onTap: () { + setState(() { + currentCategoryID = getSaleCategoriesList[index].categoryID!; + getItemsForSaleList.clear(); + currentPageNo = 1; + getItemsForSale(currentPageNo, currentCategoryID); + }); + }, + child: Container( + 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( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.string(getSaleCategoriesList[index].content!, fit: BoxFit.contain), + currentCategoryID == getSaleCategoriesList[index].categoryID ? const Icon(Icons.check_circle_rounded, color: MyColors.greenColor, size: 16.0) : Container(), + ], + ).expanded, + getSaleCategoriesList[index].title!.toText10() + ], + ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12), + ), + ), + ); + }, + separatorBuilder: (cxt, index) => 12.width, + itemCount: getSaleCategoriesList.length) + : Container(), + ), + getItemsForSaleList.isNotEmpty + ? GridView( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 162 / 266, crossAxisSpacing: 12, mainAxisSpacing: 12), + padding: const EdgeInsets.only(left: 21, right: 21, bottom: 21, top: 21), + shrinkWrap: true, + primary: false, + physics: const ScrollPhysics(), + children: getItemsForSaleWidgets(), + ) + : Utils.getNoDataWidget(context).paddingOnly(top: 50), + // 32.height, + ], + ), + ); + } + + List getItemsForSaleWidgets() { + List itemsList = []; + + getItemsForSaleList.forEach((element) { + itemsList.add(getItemCard(element)); + }); + + return itemsList; + } + + Widget getItemCard(GetItemsForSaleList getItemsForSaleList) { + return InkWell( + onTap: () { + Navigator.pushNamed(context, AppRoutes.itemsForSaleDetail, arguments: getItemsForSaleList); + }, + child: Container( + padding: const EdgeInsets.all(10.0), + 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: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Hero( + tag: "ItemImage" + getItemsForSaleList.itemSaleID.toString(), + transitionOnUserGestures: true, + child: AspectRatio( + aspectRatio: 148 / 127, + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: Image.memory( + base64Decode(getItemsForSaleList.itemAttachments![0].content!), + fit: BoxFit.cover, + ), + ), + ), + ), + 10.height, + getItemsForSaleList.title!.toText16(isBold: true, color: const Color(0xff2B353E), maxlines: 1), + 10.height, + getItemsForSaleList.description!.toText12(maxLine: 2, color: const Color(0xff535353)), + 16.height, + getItemsForSaleList.status!.toText14(isBold: true, color: getItemsForSaleList.status == 'Approved' ? MyColors.greenColor : MyColors.yellowColor), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: ["${getItemsForSaleList.quotePrice} ${getItemsForSaleList.currencyCode!}".toText14(isBold: true), SvgPicture.asset("assets/images/arrow_next.svg").paddingOnly(bottom: 4)], + ), + ], + ), + ), + ); + } + + void getItemForSaleCategory() async { + try { + Utils.showLoading(context); + getSaleCategoriesList = await ItemsForSaleApiClient().getSaleCategories(); + Utils.hideLoading(context); + setState(() {}); + getItemsForSale(currentPageNo, currentCategoryID); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } + + void getItemsForSale(int itgPageNo, int itgCategoryID) async { + List getItemsForSaleListLocal = []; + try { + Utils.showLoading(context); + getItemsForSaleListLocal.clear(); + getItemsForSaleListLocal = await ItemsForSaleApiClient().getItemsForSale(itgPageNo, itgCategoryID); + getItemsForSaleList.addAll(getItemsForSaleListLocal); + Utils.hideLoading(context); + setState(() {}); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } +} diff --git a/lib/ui/screens/items_for_sale/fragments/my_posted_ads_fragment.dart b/lib/ui/screens/items_for_sale/fragments/my_posted_ads_fragment.dart new file mode 100644 index 0000000..f477672 --- /dev/null +++ b/lib/ui/screens/items_for_sale/fragments/my_posted_ads_fragment.dart @@ -0,0 +1,213 @@ +import 'dart:convert'; + +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:mohem_flutter_app/api/items_for_sale/items_for_sale_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/consts.dart'; +import 'package:mohem_flutter_app/classes/date_uitl.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/items_for_sale/get_employee_ads_list.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/get_regions_list.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/get_sale_categories_list.dart'; +import 'package:mohem_flutter_app/models/items_for_sale/item_review_model.dart'; + +class MyPostedAdsFragment extends StatefulWidget { + const MyPostedAdsFragment({Key? key}) : super(key: key); + + @override + State createState() => _MyPostedAdsFragmentState(); +} + +class _MyPostedAdsFragmentState extends State { + List employeePostedAdsList = []; + + @override + void initState() { + getAdsByEmployee(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Container( + margin: const EdgeInsets.all(21), + child: employeePostedAdsList.isNotEmpty + ? ListView.separated( + shrinkWrap: true, + physics: const BouncingScrollPhysics(), + itemBuilder: (cxt, index) { + return Container( + 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( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.max, + children: [ + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: Colors.white, + boxShadow: [ + BoxShadow( + color: const Color(0xff000000).withOpacity(.05), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: employeePostedAdsList[index].itemAttachments!.isNotEmpty + ? Image.memory( + base64Decode(employeePostedAdsList[index].itemAttachments![0].content!), + fit: BoxFit.contain, + ) + : Container(), + ), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + employeePostedAdsList[index].title!.toText16(isBold: true, color: const Color(0xff2B353E)).paddingOnly(left: 12, right: 12), + "Posted on ${DateUtil.formatDateToDate(DateTime.parse(employeePostedAdsList[index].created!), false)}" + .toText12(color: Color(0xff969696)) + .paddingOnly(left: 7, right: 7), + ], + ), + employeePostedAdsList[index].description!.toText12(color: const Color(0xff2B353E), maxLine: 1).paddingOnly(left: 12, right: 12, bottom: 12), + employeePostedAdsList[index] + .status! + .toText12(isBold: true, color: employeePostedAdsList[index].status == 'Approved' ? MyColors.greenColor : MyColors.yellowColor) + .paddingOnly(left: 12, right: 12), + // Row( + // children: [ + // IconButton( + // padding: EdgeInsets.zero, + // icon: const Icon( + // Icons.delete_rounded, + // size: 25, + // color: MyColors.redColor, + // ), + // constraints: const BoxConstraints(), + // onPressed: () { + // updateItemForSale(employeePostedAdsList[index].itemSaleID!); + // }), + // 37.width, + // IconButton( + // padding: EdgeInsets.zero, + // icon: const Icon( + // Icons.edit_note_sharp, + // size: 25, + // color: Color(0xff2E303A), + // ), + // constraints: const BoxConstraints(), + // onPressed: () {}), + // ], + // ), + ], + ), + ), + ], + ).paddingOnly(left: 7, right: 7, bottom: 0, top: 12), + const Divider( + color: MyColors.lightGreyEFColor, + thickness: 1.0, + ), + Row( + children: [ + LocaleKeys.remove.tr().toText12(color: MyColors.redColor).center.onPress(() { + updateItemForSale(employeePostedAdsList[index].itemSaleID!); + }).expanded, + Container(width: 1, height: 30, color: MyColors.lightGreyEFColor), + LocaleKeys.edit.tr().toText12(color: MyColors.gradiantEndColor).center.onPress(() { + GetRegionsList selectedRegion = GetRegionsList(); + selectedRegion.regionID = employeePostedAdsList[index].regionID; + selectedRegion.regionName = employeePostedAdsList[index].regionName; + GetSaleCategoriesList selectedSaleCategory = GetSaleCategoriesList(); + selectedSaleCategory.categoryID = employeePostedAdsList[index].categoryID; + selectedSaleCategory.title = employeePostedAdsList[index].categoryTitle; + List itemPhotos = []; + itemPhotos.add(employeePostedAdsList[index].itemAttachments![0].content!.toString()); + ItemReviewModel itemReviewModel = ItemReviewModel(employeePostedAdsList[index].title, employeePostedAdsList[index].description, employeePostedAdsList[index].status, + selectedRegion, employeePostedAdsList[index].quotePrice, itemPhotos, selectedSaleCategory); + Utils.saveStringFromPrefs(SharedPrefsConsts.editItemForSale, jsonEncode(itemReviewModel.toJson())); + Navigator.pushNamed(context, AppRoutes.addNewItemForSale, arguments: 1); + }).expanded, + ], + ), + 8.height + ], + ), + ); + }, + separatorBuilder: (cxt, index) => 12.height, + itemCount: employeePostedAdsList.length) + : Utils.getNoDataWidget(context).paddingOnly(top: 200.0), + ), + ); + } + + void updateItemForSale(int itemSaleID) async { + Utils.showLoading(context); + + String? empNum = AppState().memberInformationList?.eMPLOYEENUMBER; + String? empMobNum = AppState().memberInformationList?.eMPLOYEEMOBILENUMBER; + String? loginTokenID = AppState().postParamsObject?.logInTokenID; + String? tokenID = AppState().postParamsObject?.tokenID; + + var request = http.MultipartRequest('POST', Uri.parse("${ApiConsts.cocRest}Mohemm_ITG_UpdateItemForSale")); + request.fields['itemSaleID'] = itemSaleID.toString(); + request.fields['Channel'] = "31"; + request.fields['isActive'] = "false"; + request.fields['LogInToken'] = loginTokenID!; + request.fields['Token'] = tokenID!; + request.fields['MobileNo'] = empMobNum!; + request.fields['EmployeeNumber'] = empNum!; + request.fields['employeeNumber'] = empNum; + var response = await request.send().catchError((e) { + Utils.hideLoading(context); + Utils.handleException(e, context, null); + }); + print(response.statusCode); + Utils.hideLoading(context); + getAdsByEmployee(); + } + + void getAdsByEmployee() async { + try { + employeePostedAdsList.clear(); + Utils.showLoading(context); + employeePostedAdsList = await ItemsForSaleApiClient().getEmployeePostedAds(); + Utils.hideLoading(context); + setState(() {}); + } catch (ex) { + Utils.hideLoading(context); + Utils.handleException(ex, context, null); + } + } +} diff --git a/lib/ui/screens/items_for_sale/fragments/select_category_fragment.dart b/lib/ui/screens/items_for_sale/fragments/select_category_fragment.dart new file mode 100644 index 0000000..6255302 --- /dev/null +++ b/lib/ui/screens/items_for_sale/fragments/select_category_fragment.dart @@ -0,0 +1,78 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.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/items_for_sale/get_sale_categories_list.dart'; + +class SelectCategoryFragment extends StatelessWidget { + final Function changePageViewIndex; + final List getSaleCategoriesList; + static late GetSaleCategoriesList selectedSaleCategory; + + SelectCategoryFragment({Key? key, required this.changePageViewIndex, required this.getSaleCategoriesList}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + "What are you offering".toText20(isBold: true).paddingOnly(top: 24, left: 21, right: 21), + getSaleCategoriesList.isNotEmpty + ? GridView( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 105 / 105, crossAxisSpacing: 12, mainAxisSpacing: 12), + padding: const EdgeInsets.only(top: 15, bottom: 15, left: 21, right: 21), + shrinkWrap: true, + primary: false, + physics: const ScrollPhysics(), + children: getItemsForSaleWidgets(), + ) + : Container(), + ], + ), + ); + } + + List getItemsForSaleWidgets() { + List itemsList = []; + + getSaleCategoriesList.forEach((element) { + itemsList.add(InkWell( + onTap: () { + selectedSaleCategory = element; + changePageViewIndex(1); + }, + child: Container( + 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( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.string(element.content!, fit: BoxFit.contain, width: 32, height: 32), + ], + ).expanded, + element.title!.toText12() + ], + ).paddingOnly(left: 10, right: 10, bottom: 10, top: 12), + ), + )); + }); + + return itemsList; + } +} diff --git a/lib/ui/screens/items_for_sale/item_for_sale_detail.dart b/lib/ui/screens/items_for_sale/item_for_sale_detail.dart new file mode 100644 index 0000000..8d3b09c --- /dev/null +++ b/lib/ui/screens/items_for_sale/item_for_sale_detail.dart @@ -0,0 +1,136 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/classes/date_uitl.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/items_for_sale/get_items_for_sale_list.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/circular_avatar.dart'; +import 'package:url_launcher/url_launcher.dart'; + +class ItemForSaleDetailPage extends StatefulWidget { + const ItemForSaleDetailPage({Key? key}) : super(key: key); + + @override + State createState() => _ItemForSaleDetailPageState(); +} + +class _ItemForSaleDetailPageState extends State { + late GetItemsForSaleList getItemsForSaleList; + + @override + Widget build(BuildContext context) { + getItemsForSaleList = ModalRoute.of(context)?.settings.arguments as GetItemsForSaleList; + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget(context, + // title: LocaleKeys.mowadhafhiRequest.tr(), + title: "Items for sale", + showHomeButton: true), + body: SingleChildScrollView( + child: AspectRatio( + aspectRatio: 336 / 554, + child: 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), + ), + ], + ), + // color: Colors.red, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Container( + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: const Color(0xffEBEBEB).withOpacity(1.0), + blurRadius: 26, + offset: const Offset(0, -3), + ), + ], + ), + child: Hero( + tag: "ItemImage" + getItemsForSaleList.itemSaleID.toString(), + transitionOnUserGestures: true, + child: AspectRatio( + aspectRatio: 148 / 127, + child: ClipRRect( + borderRadius: BorderRadius.circular(6), + child: Image.memory( + base64Decode(getItemsForSaleList.itemAttachments![0].content!), + fit: BoxFit.cover, + ), + ), + ).paddingAll(8), + ), + ), + getItemsForSaleList.title!.toText20(isBold: true, color: const Color(0xff2B353E)).paddingOnly(left: 21, right: 21), + getItemsForSaleList.description!.toText12(maxLine: 5, color: const Color(0xff535353)).paddingOnly(left: 21, right: 21, bottom: 21), + getItemsForSaleList.status!.toText16(isBold: true, color: getItemsForSaleList.status == 'Approved' ? MyColors.greenColor : MyColors.yellowColor).paddingOnly(left: 21, right: 21), + "${getItemsForSaleList.quotePrice} ${getItemsForSaleList.currencyCode!}".toText20(isBold: true).paddingOnly(left: 21, right: 21, bottom: 15), + const Divider().paddingOnly(left: 21, right: 21), + Row( + children: [ + CircularAvatar( + height: 40, + width: 40, + ).paddingOnly(left: 21, top: 21), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + getItemsForSaleList.fullName!.toText14(isBold: true).paddingOnly(left: 7, right: 7), + "Posted on: ${DateUtil.formatDateToDate(DateTime.parse(getItemsForSaleList.created!), false)}".toText12().paddingOnly(left: 7, right: 7), + ], + ).paddingOnly(top: 18), + ], + ), + ], + ), + ).paddingAll(21), + ), + ), + bottomSheet: Container( + decoration: const BoxDecoration( + color: MyColors.white, + boxShadow: [ + BoxShadow(color: MyColors.lightGreyEFColor, spreadRadius: 1), + ], + ), + child: Row( + children: [ + DefaultButton("Email", () async { + final Uri emailLaunchUri = Uri( + scheme: 'mailto', + path: getItemsForSaleList.emailAddress, + ); + launchUrl(emailLaunchUri); + }, iconData: Icons.email_sharp, isTextExpanded: false) + .insideContainer + .expanded, + DefaultButton("Call", () async { + final Uri callLaunchUri = Uri( + scheme: 'tel', + path: getItemsForSaleList.mobileNumber, + ); + launchUrl(callLaunchUri); + }, iconData: Icons.call_sharp, isTextExpanded: false) + .insideContainer + .expanded, + ], + ), + ), + ); + } +} diff --git a/lib/ui/screens/items_for_sale/items_for_sale_home.dart b/lib/ui/screens/items_for_sale/items_for_sale_home.dart new file mode 100644 index 0000000..360c0ae --- /dev/null +++ b/lib/ui/screens/items_for_sale/items_for_sale_home.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.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/models/items_for_sale/get_sale_categories_list.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/fragments/items_for_sale.dart'; +import 'package:mohem_flutter_app/ui/screens/items_for_sale/fragments/my_posted_ads_fragment.dart'; +import 'package:mohem_flutter_app/widgets/app_bar_widget.dart'; + +class ItemsForSale extends StatefulWidget { + const ItemsForSale({Key? key}) : super(key: key); + + @override + State createState() => _ItemsForSaleState(); +} + +class _ItemsForSaleState extends State { + int tabIndex = 0; + PageController controller = PageController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBarWidget(context, + // title: LocaleKeys.mowadhafhiRequest.tr(), + title: "Items for sale", + 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("Items for sale", 0), myTab("My posted ads", 1)], + ), + ), + PageView( + controller: controller, + physics: const NeverScrollableScrollPhysics(), + onPageChanged: (int pageIndex) { + setState(() { + tabIndex = pageIndex; + }); + }, + children: [ + ItemsForSaleFragment(), + MyPostedAdsFragment() + ], + ).expanded, + ], + ), + floatingActionButton: Container( + height: 50, + width: 50, + decoration: const BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient(transform: GradientRotation(.83), begin: Alignment.topRight, end: Alignment.bottomLeft, colors: [ + MyColors.gradiantEndColor, + MyColors.gradiantStartColor, + ]), + ), + child: const Icon(Icons.add, color: Colors.white, size: 30), + ).onPress(() { + Navigator.pushNamed(context, AppRoutes.addNewItemForSale); + }) + ); + } + + 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(() { + setState(() { + // showFabOptions = true; + }); + }) + ], + ).onPress(() { + controller.jumpToPage(index); + }).expanded; + } +} diff --git a/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart b/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart index c44be0d..5bf8875 100644 --- a/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart +++ b/lib/widgets/dynamic_forms/dynamic_textfield_widget.dart @@ -62,7 +62,8 @@ class DynamicTextFieldWidget extends StatelessWidget { TextField( enabled: isEnable, scrollPadding: EdgeInsets.zero, readOnly: isReadOnly, - keyboardType: isInputTypeNum ? TextInputType.number : TextInputType.text, + keyboardType: isInputTypeNum ? const TextInputType.numberWithOptions(signed: true) : TextInputType.text, + textInputAction: TextInputAction.done, //controller: controller, maxLines: lines, obscuringCharacter: "*", diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart new file mode 100644 index 0000000..6c1a06f --- /dev/null +++ b/lib/widgets/image_picker.dart @@ -0,0 +1,154 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:image_picker/image_picker.dart'; +import 'package:mohem_flutter_app/classes/colors.dart'; +import 'package:mohem_flutter_app/extensions/string_extensions.dart'; + +class ImageOptions { + static void showImageOptions( + BuildContext context, + Function(String, File) image, + ) { + showModalBottomSheet( + backgroundColor: Colors.transparent, + context: context, + builder: (BuildContext bc) { + return _BottomSheet( + children: [ + _BottomSheetItem( + title: "Select File Source", + onTap: () {}, + icon: Icons.file_present, + color: MyColors.black, + ), + _BottomSheetItem( + title: "Gallery", + icon: Icons.image, + onTap: () async { + if (Platform.isAndroid) { + galleryImageAndroid(image); + } else { + File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.gallery, imageQuality: 20))?.path ?? ""); + String fileName = _image.path; + final bytes = File(fileName).readAsBytesSync(); + String base64Encode = base64.encode(bytes); + if (base64Encode != null) { + image(base64Encode, _image); + } + } + }, + ), + _BottomSheetItem( + title: "Camera", + icon: Icons.camera_alt, + onTap: () async { + if (Platform.isAndroid) { + cameraImageAndroid(image); + } else { + File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.camera, imageQuality: 20))?.path ?? ""); + String fileName = _image.path; + final bytes = File(fileName).readAsBytesSync(); + String base64Encode = base64.encode(bytes); + if (base64Encode != null) { + image(base64Encode, _image); + } + } + }, + ), + _BottomSheetItem( + title: "Cancel", + onTap: () {}, + icon: Icons.cancel, + color: MyColors.redColor, + ) + ], + ); + }); + } +} + +void galleryImageAndroid(Function(String, File) image) async { + File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.gallery, imageQuality: 20))?.path ?? ""); + String fileName = _image.path; + final bytes = File(fileName).readAsBytesSync(); + String base64Encode = base64.encode(bytes); + if (base64Encode != null) { + image(base64Encode, _image); + } +} + +void cameraImageAndroid(Function(String, File) image) async { + File _image = File((await ImagePicker.platform.pickImage(source: ImageSource.camera, imageQuality: 20))?.path ?? ""); + String fileName = _image.path; + final bytes = File(fileName).readAsBytesSync(); + String base64Encode = base64.encode(bytes); + if (base64Encode != null) { + image(base64Encode, _image); + } +} + +class _BottomSheet extends StatelessWidget { + final List children; + + const _BottomSheet({Key? key, required this.children}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 12.0), + decoration: BoxDecoration(color: Theme.of(context).backgroundColor, borderRadius: const BorderRadius.only(topLeft: Radius.circular(16.0), topRight: Radius.circular(16.0))), + child: SafeArea( + top: false, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + decoration: BoxDecoration(color: Theme.of(context).dividerColor, borderRadius: BorderRadius.circular(3.0)), + width: 40.0, + height: 6.0, + ), + ...children + ], + ), + ), + ); + } +} + +class _BottomSheetItem extends StatelessWidget { + final Function onTap; + final IconData icon; + final String title; + final Color color; + + _BottomSheetItem({Key? key, required this.onTap, required this.title, required this.icon, this.color = MyColors.gradiantStartColor}) : super(key: key); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: () { + if (onTap != null) { + Navigator.pop(context); + onTap(); + } + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 18.0), + child: Row( + children: [ + if (icon != null) + Icon( + icon, + color: color, + size: 18.0, + ), + if (icon != null) const SizedBox(width: 24.0), + title.toText17(), + ], + ), + ), + ); + } +}