diff --git a/lib/config/customer_dependencies.dart b/lib/config/customer_dependencies.dart index 804a53f..274c9e2 100644 --- a/lib/config/customer_dependencies.dart +++ b/lib/config/customer_dependencies.dart @@ -1,4 +1,5 @@ import 'package:car_customer_app/repositories/provider_repo.dart'; +import 'package:car_customer_app/repositories/request_repo.dart'; import 'package:car_customer_app/repositories/schedule_repo.dart'; import 'package:mc_common_app/config/dependencies.dart'; @@ -8,5 +9,6 @@ class CustomerDependencies { injector.registerSingleton(() => ProviderRepoImp()); injector.registerSingleton(() => ScheduleRepoImp()); + } } diff --git a/lib/config/customer_routes.dart b/lib/config/customer_routes.dart index 6cca179..29cc908 100644 --- a/lib/config/customer_routes.dart +++ b/lib/config/customer_routes.dart @@ -6,6 +6,7 @@ import 'package:car_customer_app/views/appointments/review_appointment_view.dart import 'package:car_customer_app/views/dashboard/dashboard_page.dart'; import 'package:car_customer_app/views/provider/branch_detail_page.dart'; import 'package:car_customer_app/views/provider/provider_profile_page.dart'; +import 'package:car_customer_app/views/requests/create_request_page.dart'; import 'package:flutter/cupertino.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart'; @@ -18,7 +19,9 @@ import 'package:mc_common_app/views/advertisement/select_ad_type_view.dart'; import 'package:mc_common_app/views/payments/payment_methods_view.dart'; import 'package:mc_common_app/views/advertisement/ads_search_filter_view.dart'; - class CustomerAppRoutes { +import '../views/requests/offer_list_page.dart'; + +class CustomerAppRoutes { static final Map routes = { AppRoutes.dashboard: (context) => DashboardPage(), AppRoutes.bookProviderAppView: (context) => BookProviderAppView(), @@ -33,5 +36,7 @@ import 'package:mc_common_app/views/advertisement/ads_search_filter_view.dart'; AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(paymentType: ModalRoute.of(context)!.settings.arguments as PaymentTypesEnum), AppRoutes.branchDetailPage: (context) => BranchDetailPage(branchDetailModel: ModalRoute.of(context)!.settings.arguments as BranchDetailModel), AppRoutes.providerProfilePage: (context) => ProviderProfilePage(providerId: ModalRoute.of(context)!.settings.arguments as int), + AppRoutes.offersListPage: (context) => OfferListPage(), + AppRoutes.createRequestPage: (context) => CreateRequestPage(), }; } diff --git a/lib/main.dart b/lib/main.dart index 966f089..1276a0a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,11 @@ import 'package:car_customer_app/config/customer_dependencies.dart'; import 'package:car_customer_app/config/customer_routes.dart'; import 'package:car_customer_app/repositories/provider_repo.dart'; +import 'package:car_customer_app/repositories/request_repo.dart'; import 'package:car_customer_app/repositories/schedule_repo.dart'; import 'package:car_customer_app/view_models/appointments_view_model.dart'; import 'package:car_customer_app/view_models/dashboard_view_model.dart'; +import 'package:car_customer_app/view_models/requests_view_model.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; @@ -65,6 +67,13 @@ Future main() async { ChangeNotifierProvider( create: (_) => PaymentVM(paymentService: injector.get(), paymentRepo: injector.get()), ), + ChangeNotifierProvider( + create: (_) => RequestsVM( + commonServices: injector.get(), + commonRepo: injector.get(), + requestRepo: injector.get(), + ), + ), ], child: const MyApp(), ).setupLocale(), diff --git a/lib/model/request.dart b/lib/model/request.dart new file mode 100644 index 0000000..e0bc006 --- /dev/null +++ b/lib/model/request.dart @@ -0,0 +1,138 @@ +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/utils/enums.dart'; + +class Request { + int requestType; + String requestTypeName; + String requestStatusName; + RequestStatus requestStatus; + String cityName; + String vehicleTypeName; + String countryName; + String customerName; + dynamic serviceProviders; + int offerCount; + int id; + int customerId; + dynamic customer; + String brand; + String model; + int year; + bool isNew; + String description; + List requestImages; + int cityId; + dynamic city; + double price; + int paymentStatus; + int vehicleTypeId; + int countryId; + List requestProviderItem; + bool isActive; + int createdBy; + DateTime createdOn; + dynamic modifiedBy; + dynamic modifiedOn; + + Request({ + required this.requestType, + required this.requestTypeName, + required this.requestStatusName, + required this.requestStatus, + required this.cityName, + required this.vehicleTypeName, + required this.countryName, + required this.customerName, + required this.serviceProviders, + required this.offerCount, + required this.id, + required this.customerId, + required this.customer, + required this.brand, + required this.model, + required this.year, + required this.isNew, + required this.description, + required this.requestImages, + required this.cityId, + required this.city, + required this.price, + required this.paymentStatus, + required this.vehicleTypeId, + required this.countryId, + required this.requestProviderItem, + required this.isActive, + required this.createdBy, + required this.createdOn, + required this.modifiedBy, + required this.modifiedOn, + }); + + factory Request.fromJson(Map json) => Request( + requestType: json["requestType"], + requestTypeName: json["requestTypeName"], + requestStatusName: json["requestStatusName"], + requestStatus: (json['requestStatus'] as int).toRequestStatusEnum(), + cityName: json["cityName"], + vehicleTypeName: json["vehicleTypeName"], + countryName: json["countryName"], + customerName: json["customerName"], + serviceProviders: json["serviceProviders"], + offerCount: json["offerCount"], + id: json["id"], + customerId: json["customerID"], + customer: json["customer"], + brand: json["brand"], + model: json["model"], + year: json["year"], + isNew: json["isNew"], + description: json["description"], + requestImages: List.from(json["requestImages"].map((x) => x)), + cityId: json["cityID"], + city: json["city"], + price: json["price"], + paymentStatus: json["paymentStatus"], + vehicleTypeId: json["vehicleTypeID"], + countryId: json["countryID"], + requestProviderItem: List.from(json["requestProviderItem"].map((x) => x)), + isActive: json["isActive"], + createdBy: json["createdBy"], + createdOn: DateTime.parse(json["createdOn"]), + modifiedBy: json["modifiedBy"], + modifiedOn: json["modifiedOn"], + ); + + Map toJson() => { + "requestType": requestType, + "requestTypeName": requestTypeName, + "requestStatusName": requestStatusName, + "requestStatus": requestStatus, + "cityName": cityName, + "vehicleTypeName": vehicleTypeName, + "countryName": countryName, + "customerName": customerName, + "serviceProviders": serviceProviders, + "offerCount": offerCount, + "id": id, + "customerID": customerId, + "customer": customer, + "brand": brand, + "model": model, + "year": year, + "isNew": isNew, + "description": description, + "requestImages": List.from(requestImages.map((x) => x)), + "cityID": cityId, + "city": city, + "price": price, + "paymentStatus": paymentStatus, + "vehicleTypeID": vehicleTypeId, + "countryID": countryId, + "requestProviderItem": List.from(requestProviderItem.map((x) => x)), + "isActive": isActive, + "createdBy": createdBy, + "createdOn": createdOn.toIso8601String(), + "modifiedBy": modifiedBy, + "modifiedOn": modifiedOn, + }; +} diff --git a/lib/repositories/request_repo.dart b/lib/repositories/request_repo.dart new file mode 100644 index 0000000..f507c0e --- /dev/null +++ b/lib/repositories/request_repo.dart @@ -0,0 +1,45 @@ +import 'package:car_customer_app/model/request.dart'; +import 'package:mc_common_app/api/api_client.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/config/dependencies.dart'; +import 'package:mc_common_app/models/generic_resp_model.dart'; + +abstract class RequestRepo { + Future createRequest(Map map); + + Future> getRequests(Map postParams); +} + +class RequestRepoImp implements RequestRepo { + ApiClient apiClient = injector.get(); + AppState appState = injector.get(); + + @override + Future createRequest(Map postParams) async { + GenericRespModel enumGenericModel = await apiClient.postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.createRequest, + postParams, + token: appState.getUser.data!.accessToken, + ); + return enumGenericModel; + } + + @override + Future> getRequests(Map postParams) async { + GenericRespModel enumGenericModel = await apiClient.postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.getRequest, + postParams, + token: appState.getUser.data!.accessToken, + ); + List requests = List.generate( + enumGenericModel.data.length, + (index) => Request.fromJson( + enumGenericModel.data[index], + ), + ); + return requests; + } +} diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart new file mode 100644 index 0000000..0e67638 --- /dev/null +++ b/lib/view_models/requests_view_model.dart @@ -0,0 +1,272 @@ +import 'dart:io'; + +import 'package:car_customer_app/repositories/request_repo.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/models/advertisment_models/vehicle_details_models.dart'; +import 'package:mc_common_app/models/enums.dart'; +import 'package:mc_common_app/models/generic_resp_model.dart'; +import 'package:mc_common_app/models/widgets_models.dart'; +import 'package:mc_common_app/repositories/common_repo.dart'; +import 'package:mc_common_app/services/common_services.dart'; +import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/base_view_model.dart'; + +import '../model/request.dart'; + +class RequestsVM extends BaseVM { + final CommonAppServices commonServices; + final CommonRepo commonRepo; + final RequestRepo requestRepo; + + RequestsVM({required this.commonServices, required this.commonRepo, required this.requestRepo}); + + List requestsFilterOptions = []; + + populateRequestsFilterList() { + requestsFilterOptions.clear(); + requestsFilterOptions = [ + FilterListModel(title: "Cars", isSelected: true, id: 1), + FilterListModel(title: "Spare Parts", isSelected: false, id: 2), + ]; + notifyListeners(); + } + + applyFilterOnRequestsVM({required int index}) { + if (requestsFilterOptions.isEmpty) return; + for (var value in requestsFilterOptions) { + value.isSelected = false; + } + requestsFilterOptions[index].isSelected = true; + notifyListeners(); + } + + List pickedVehicleImages = []; + String vehicleImageError = ""; + + void removeImageFromList(String filePath) { + int index = pickedVehicleImages.indexWhere((element) => element.path == filePath); + if (index == -1) { + return; + } + pickedVehicleImages.removeAt(index); + notifyListeners(); + } + + void pickMultipleImages() async { + List Images = await commonServices.pickMultipleImages(); + pickedVehicleImages.addAll(Images); + if (pickedVehicleImages.isNotEmpty) vehicleImageError = ""; + notifyListeners(); + } + + bool isFetchingRequestType = false; + bool isFetchingVehicleType = true; + bool isFetchingVehicleDetail = false; + List requestTypes = []; + List vehicleTypes = []; + VehicleDetailsModel? vehicleDetails; + List vehicleBrands = []; + List vehicleModels = []; + List vehicleModelYears = []; + List vehicleCountries = []; + List vehicleCities = []; + + SelectionModel requestTypeId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + + getRequestTypes() async { + requestTypeId.selectedId = -1; + isFetchingRequestType = true; + requestTypes = await commonRepo.getEnumTypeValues(enumTypeID: 16); //TODO: 16 is to get Request types + isFetchingRequestType = false; + notifyListeners(); + } + + void updateSelectionRequestTypeId(SelectionModel id) async { + requestTypeId = id; + getVehicleTypes(); + notifyListeners(); + } + + SelectionModel vehicleTypeId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + + Future getVehicleTypes() async { + reset(); + isFetchingVehicleType = true; + vehicleTypes = await commonRepo.getVehicleTypes(); + isFetchingVehicleType = false; + notifyListeners(); + } + + reset() { + vehicleTypeId.selectedId = -1; + vehicleBrandId.selectedId = -1; + vehicleModelId.selectedId = -1; + vehicleModelYearId.selectedId = -1; + vehicleCountryId.selectedId = -1; + vehicleCityId.selectedId = -1; + } + + void updateSelectionVehicleTypeId(SelectionModel id) async { + vehicleTypeId = id; + getVehicleBrandsByVehicleTypeId(); + notifyListeners(); + } + + Future getVehicleBrandsByVehicleTypeId() async { + // if (vehicleBrandId.selectedId == -1) { + // return; + // } + isFetchingVehicleDetail = true; + notifyListeners(); + vehicleDetails = await commonRepo.getVehicleDetails(vehicleTypeId: vehicleTypeId.selectedId); + + if (vehicleDetails != null) { + vehicleBrands = vehicleDetails!.vehicleBrands!; + vehicleModels = vehicleDetails!.vehicleModels!; + vehicleModelYears = vehicleDetails!.vehicleModelYears!; + vehicleCountries = vehicleDetails!.vehicleCountries!; + } + isFetchingVehicleDetail = false; + notifyListeners(); + } + + SelectionModel vehicleBrandId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + + void updateSelectionVehicleBrandId(SelectionModel id) { + vehicleBrandId = id; + vehicleModelId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + vehicleModelYearId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + notifyListeners(); + } + + SelectionModel vehicleModelId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + + void updateSelectionVehicleModelId(SelectionModel id) { + vehicleModelId = id; + vehicleModelYearId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + notifyListeners(); + } + + SelectionModel vehicleModelYearId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + + void updateSelectionVehicleModelYearId(SelectionModel id) { + vehicleModelYearId = id; + notifyListeners(); + } + + bool isShippingDeliveryEnabled = false; + + void updateShippingDeliverEnabled(bool v) { + isShippingDeliveryEnabled = v; + notifyListeners(); + } + + bool isCountryFetching = false; + SelectionModel vehicleCountryId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + + void updateSelectionVehicleCountryId(SelectionModel id) async { + vehicleCountryId = id; + isCountryFetching = true; + notifyListeners(); + vehicleCities = await commonRepo.getVehicleCities(countryId: vehicleCountryId.selectedId); + isCountryFetching = false; + notifyListeners(); + } + + SelectionModel vehicleCityId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + + void updateSelectionVehicleCityId(SelectionModel id) { + vehicleCityId = id; + notifyListeners(); + } + + //Request Management + String price = "", description = ""; + + updatePrice(String v) { + price = v; + } + + updateDescription(String v) { + description = v; + } + + Future createRequest() async { + if (validate()) { + Map m = { + "customerID": AppState().getUser.data!.userInfo!.customerId ?? 0, + "requestType": requestTypeId.selectedId, + "vehicleTypeID": vehicleTypeId.selectedId, + "brand": vehicleBrandId.selectedOption, + "model": vehicleModelId.selectedOption, + "year": vehicleModelYearId.selectedOption, + "isNew": true, + "countryID": vehicleCountryId.selectedId, + "cityID": vehicleCityId.selectedId, + "price": price, + "description": description, + "isSpecialServiceNeeded": false, + "requestImages": [] + }; + GenericRespModel respModel = await requestRepo.createRequest(m); + return respModel; + } else { + return null; + } + } + + bool validate() { + bool isValid = true; + if (requestTypeId.selectedId == -1) { + Utils.showToast("Please select valid Request Type"); + isValid = false; + } else if (vehicleTypeId.selectedId == -1) { + Utils.showToast("Please select valid Vehicle Type"); + isValid = false; + } else if (vehicleBrandId.selectedId == -1) { + Utils.showToast("Please select valid Brand"); + isValid = false; + } else if (vehicleModelId.selectedId == -1) { + Utils.showToast("Please select valid Model"); + isValid = false; + } else if (vehicleModelYearId.selectedId == -1) { + Utils.showToast("Please select valid Year"); + isValid = false; + } else if (vehicleCountryId.selectedId == -1) { + Utils.showToast("Please select valid Country"); + isValid = false; + } else if (vehicleCityId.selectedId == -1) { + Utils.showToast("Please select valid City"); + isValid = false; + } else if (price.isEmpty) { + Utils.showToast("Please add valid Price"); + isValid = false; + } else if (description.isEmpty) { + Utils.showToast("Please add valid Description"); + isValid = false; + } + return isValid; + } + + bool isRequestLoading = true; + List requests = []; + + getRequests() async { + isRequestLoading = true; + notifyListeners(); + + int selectedRequestType; + // Find the FilterListModel with isSelected equal to true + + requests = await requestRepo.getRequests( + { + "customerID": AppState().getUser.data!.userInfo!.customerId, + "pageSize": 100, + "pageIndex": 0, + "requestType": requestsFilterOptions.firstWhere((element) => element.isSelected).id, + }, + ); + isRequestLoading = false; + notifyListeners(); + } +} diff --git a/lib/views/dashboard/dashboard_page.dart b/lib/views/dashboard/dashboard_page.dart index 7145d88..a2e359b 100644 --- a/lib/views/dashboard/dashboard_page.dart +++ b/lib/views/dashboard/dashboard_page.dart @@ -1,7 +1,9 @@ import 'dart:async'; +import 'package:car_customer_app/config/customer_routes.dart'; import 'package:car_customer_app/view_models/appointments_view_model.dart'; import 'package:car_customer_app/view_models/dashboard_view_model.dart'; +import 'package:car_customer_app/view_models/requests_view_model.dart'; import 'package:car_customer_app/views/dashboard/fragments/ads_fragment.dart'; import 'package:car_customer_app/views/dashboard/fragments/appointments_fragment.dart'; import 'package:car_customer_app/views/dashboard/fragments/home_fragment.dart'; @@ -19,6 +21,7 @@ import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; import 'fragments/branches_fragment.dart'; +import 'fragments/requests_fragment.dart'; class DashboardPage extends StatefulWidget { const DashboardPage({Key? key}) : super(key: key); @@ -38,6 +41,7 @@ class _DashboardPageState extends State { scheduleMicrotask(() { context.read().populateAppointmentsFilterList(); context.read().populateProvidersFilterList(); + context.read().populateRequestsFilterList(); context.read().populateAdsFilterList(); context.read().getAllNearBranches(); _onRefresh(); @@ -81,7 +85,7 @@ class _DashboardPageState extends State { const AppointmentsFragment(), const HomeFragment(), AdsFragment(), - const SettingsFragment(), + RequestsFragment(), ]; String getPageTitle(int index) { @@ -98,7 +102,7 @@ class _DashboardPageState extends State { return "Ads"; } if (index == 4) { - return ""; + return "My Requests"; } return ""; } @@ -128,6 +132,19 @@ class _DashboardPageState extends State { drawer: CustomDrawer(dashboardVM: context.watch()), bottomNavigationBar: CustomBottomNavbar(), body: fragments[context.watch().selectedNavbarBarIndex], + floatingActionButton: context.watch().selectedNavbarBarIndex == 4 + ? FloatingActionButton( + onPressed: () { + if (context.read().selectedNavbarBarIndex == 4) { + navigateWithName(context, AppRoutes.createRequestPage); + } + }, + child: Icon( + Icons.add, + color: Colors.white, + ), + ) + : null, ); } } diff --git a/lib/views/dashboard/fragments/requests_fragment.dart b/lib/views/dashboard/fragments/requests_fragment.dart new file mode 100644 index 0000000..d19d4eb --- /dev/null +++ b/lib/views/dashboard/fragments/requests_fragment.dart @@ -0,0 +1,63 @@ +import 'package:car_customer_app/view_models/requests_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/common_widgets/categories_list.dart'; +import 'package:provider/provider.dart'; + +import '../../requests/widget/request_item.dart'; + +class RequestsFragment extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + color: MyColors.backgroundColor, + width: double.infinity, + height: double.infinity, + child: Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) { + return Column( + children: [ + ShowFillButton( + title: "title", + onPressed: () { + context.read().getRequests(); + }), + 16.height, + FiltersList( + filterList: requestsVM.requestsFilterOptions, + onFilterTapped: (index, selectedFilterId) => requestsVM.applyFilterOnRequestsVM(index: index), + ), + 8.height, + Expanded( + child: requestsVM.isRequestLoading + ? Center(child: CircularProgressIndicator()) + : requestsVM.requests.length == 0 + ? Center( + child: "No Request Available".toText( + isBold: true, + ), + ) + : ListView.separated( + itemBuilder: (context, index) { + return RequestItem(requestsVM.requests[index]); + }, + separatorBuilder: (context, index) { + return 16.height; + }, + itemCount: requestsVM.requests.length, + padding: EdgeInsets.only( + left: 16, + right: 16, + bottom: 16, + top: 8, + ), + ), + ) + ], + ); + }), + ); + } +} diff --git a/lib/views/dashboard/widgets/bottom_nav_bar.dart b/lib/views/dashboard/widgets/bottom_nav_bar.dart index c7b9159..7cdcb4c 100644 --- a/lib/views/dashboard/widgets/bottom_nav_bar.dart +++ b/lib/views/dashboard/widgets/bottom_nav_bar.dart @@ -42,7 +42,7 @@ class CustomBottomNavbar extends StatelessWidget { BottomNavigationBarItem( icon: SvgPicture.asset(MyAssets.settingsIcon).paddingAll(5), activeIcon: SvgPicture.asset(MyAssets.settingsIcon, color: MyColors.darkIconColor).paddingAll(5), - label: LocaleKeys.settings.tr(), + label: LocaleKeys.requests.tr(), ), ], currentIndex: dashboardVM.selectedNavbarBarIndex, diff --git a/lib/views/requests/create_request_page.dart b/lib/views/requests/create_request_page.dart new file mode 100644 index 0000000..6947e6a --- /dev/null +++ b/lib/views/requests/create_request_page.dart @@ -0,0 +1,249 @@ +import 'package:car_customer_app/view_models/requests_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/classes/consts.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/models/generic_resp_model.dart'; +import 'package:mc_common_app/models/widgets_models.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/views/advertisement/ad_creation_steps/ad_creation_steps_containers.dart'; +import 'package:mc_common_app/views/advertisement/picked_images_container.dart'; +import 'package:mc_common_app/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/common_widgets/dotted_rect.dart'; +import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:mc_common_app/widgets/txt_field.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; + +class CreateRequestPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + // context.read().getVehicleTypes(); + context.read().getRequestTypes(); + return Scaffold( + appBar: CustomAppBar( + title: "Create Request", + ), + body: Consumer(builder: (context, model, widget) { + return Column( + children: [ + Expanded( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Vehicle Detail".toText(fontSize: 18, isBold: true), + 8.height, + if (model.isFetchingRequestType) ...[ + Center( + child: CircularProgressIndicator(), + ), + ] else ...[ + Builder(builder: (context) { + List requestTypeDrop = []; + for (var element in model.requestTypes) { + requestTypeDrop.add(DropValue(element.enumValue.toInt() ?? 0, element.enumValueStr ?? "", "")); + } + return DropdownField( + (DropValue value) => model.updateSelectionRequestTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)), + list: requestTypeDrop, + dropdownValue: model.requestTypeId.selectedId != -1 ? DropValue(model.requestTypeId.selectedId, model.requestTypeId.selectedOption, "") : null, + hint: "Request Type", + errorValue: model.requestTypeId.errorValue, + ); + }), + ], + 8.height, + if (model.requestTypeId.selectedId != -1) + if (model.isFetchingVehicleType) ...[ + Center( + child: CircularProgressIndicator(), + ), + ] else ...[ + Builder(builder: (context) { + List vehicleTypeDrop = []; + for (var element in model.vehicleTypes) { + vehicleTypeDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleTypeName ?? "", "")); + } + return DropdownField( + (DropValue value) => model.updateSelectionVehicleTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)), + list: vehicleTypeDrop, + dropdownValue: model.vehicleTypeId.selectedId != -1 ? DropValue(model.vehicleTypeId.selectedId, model.vehicleTypeId.selectedOption, "") : null, + hint: "Vehicle Type", + errorValue: model.vehicleTypeId.errorValue, + ); + }), + ], + 8.height, + if (model.vehicleTypeId.selectedId != -1) + if (model.isFetchingVehicleDetail) ...[ + Center( + child: CircularProgressIndicator(), + ), + ] else ...[ + Column( + children: [ + Builder(builder: (context) { + List vehicleBrandsDrop = []; + for (var element in model.vehicleBrands) { + vehicleBrandsDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleBrandDescription ?? "", "")); + } + return DropdownField( + (DropValue value) => model.updateSelectionVehicleBrandId(SelectionModel(selectedId: value.id, selectedOption: value.value)), + list: vehicleBrandsDrop, + dropdownValue: model.vehicleBrandId.selectedId != -1 ? DropValue(model.vehicleBrandId.selectedId, model.vehicleBrandId.selectedOption, "") : null, + hint: "Brand", + errorValue: model.vehicleBrandId.errorValue, + ); + }), + 8.height, + Builder(builder: (context) { + List vehicleModelsDrop = []; + for (var element in model.vehicleModels) { + if (model.vehicleBrandId.selectedId == element.vehicleBrandID) vehicleModelsDrop.add(DropValue(element.id?.toInt() ?? 0, element.model ?? "", "")); + } + return DropdownField( + (DropValue value) => model.updateSelectionVehicleModelId(SelectionModel(selectedId: value.id, selectedOption: value.value)), + list: vehicleModelsDrop, + dropdownValue: model.vehicleModelId.selectedId != -1 ? DropValue(model.vehicleModelId.selectedId, model.vehicleModelId.selectedOption, "") : null, + hint: "Model", + errorValue: model.vehicleModelId.errorValue, + ); + }), + 8.height, + Builder(builder: (context) { + List vehicleYearModelsDrop = []; + for (var element in model.vehicleModelYears) { + vehicleYearModelsDrop.add(DropValue(element.id?.toInt() ?? 0, element.modelYear ?? "", "")); + } + + return DropdownField( + (DropValue value) => model.updateSelectionVehicleModelYearId(SelectionModel(selectedId: value.id, selectedOption: value.value)), + list: vehicleYearModelsDrop, + dropdownValue: model.vehicleModelYearId.selectedId != -1 ? DropValue(model.vehicleModelYearId.selectedId, model.vehicleModelYearId.selectedOption, "") : null, + hint: "Year", + errorValue: model.vehicleModelYearId.errorValue, + ); + }), + 8.height, + Builder(builder: (context) { + List vehicleCountriesDrop = []; + for (var element in model.vehicleCountries) { + vehicleCountriesDrop.add(DropValue(element.id?.toInt() ?? 0, element.countryName ?? "", "")); + } + return DropdownField( + (DropValue value) => model.updateSelectionVehicleCountryId(SelectionModel(selectedOption: value.value, selectedId: value.id)), + list: vehicleCountriesDrop, + dropdownValue: model.vehicleCountryId.selectedId != -1 ? DropValue(model.vehicleCountryId.selectedId, model.vehicleCountryId.selectedOption, "") : null, + hint: "Country", + errorValue: model.vehicleCountryId.errorValue, + ); + }), + if (model.vehicleCountryId.selectedId != -1) ...[ + if (model.isCountryFetching) ...[ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [const CircularProgressIndicator().paddingAll(10)], + ), + ] else ...[ + 8.height, + Builder(builder: (context) { + List vehicleCitiesDrop = []; + for (var element in model.vehicleCities) { + vehicleCitiesDrop.add(DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", "")); + } + return DropdownField( + (DropValue value) => model.updateSelectionVehicleCityId(SelectionModel(selectedId: value.id, selectedOption: value.value)), + list: vehicleCitiesDrop, + dropdownValue: model.vehicleCityId.selectedId != -1 ? DropValue(model.vehicleCityId.selectedId, model.vehicleCityId.selectedOption, "") : null, + hint: "City", + errorValue: model.vehicleCityId.errorValue, + ); + }), + ], + ], + 8.height, + TxtField( + hint: "Price", + value: model.price, + onChanged: (e) => model.updatePrice(e), + ), + 8.height, + TxtField( + hint: "Description", + maxLines: 5, + value: model.description, + onChanged: (e) => model.updateDescription(e), + ), + 8.height, + DottedRectContainer( + onTap: () => context.read().pickMultipleImages(), + text: "Attach Image", + icon: MyAssets.attachmentIcon.buildSvg(), + ), + if (model.vehicleImageError != "") ...[ + 10.height, + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + model.vehicleImageError.toText(fontSize: 14, color: Colors.red), + ], + ).paddingOnly(right: 10) + ], + if (model.pickedVehicleImages.isNotEmpty) ...[ + 16.height, + PickedImagesContainer( + pickedImages: model.pickedVehicleImages, + onCrossPressedPrimary: model.removeImageFromList, + onAddImagePressed: () { + context.read().pickMultipleImages(); + }, + ), + ], + 16.height, + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Checkbox( + value: model.isShippingDeliveryEnabled, + onChanged: null, + ), + "Shipping/Delivery".toText(color: MyColors.darkPrimaryColor, isBold: true, fontSize: 18) + ], + ).onPress(() { + model.updateShippingDeliverEnabled(!model.isShippingDeliveryEnabled); + }), + ], + ), + ] + ], + ).toContainer(isShadowEnabled: true, marginAll: 16, paddingAll: 12), + ), + ), + ShowFillButton( + title: "Create Request", + maxWidth: double.infinity, + margin: EdgeInsets.all(16), + onPressed: () async { + Utils.showLoading(context); + GenericRespModel? response = await model.createRequest(); + Utils.hideLoading(context); + if (response != null) { + if (response.messageStatus == 1) { + Utils.showToast("Request Successfully Created"); + } else { + Utils.showToast(response.message.toString()); + } + } + }, + ) + ], + ); + }), + ); + } +} diff --git a/lib/views/requests/offer_list_page.dart b/lib/views/requests/offer_list_page.dart new file mode 100644 index 0000000..d5c2824 --- /dev/null +++ b/lib/views/requests/offer_list_page.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; + +class OfferListPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + title: "Offers", + ), + body: ListView.separated( + itemBuilder: (context, index) { + return showOfferItem(); + }, + separatorBuilder: (context, index) { + return 16.height; + }, + itemCount: 2, + padding: EdgeInsets.all(16), + ), + ); + } + + Widget showOfferItem() { + return Stack( + alignment: Alignment.bottomRight, + children: [ + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Toyota Corolla".toText(fontSize: 16, isBold: true), + Center( + child: "2".toText( + color: Colors.white, + isBold: true, + fontSize: 10, + ), + ).toContainer( + backgroundColor: MyColors.redColor, + borderRadius: 100, + paddingAll: 1, + width: 20, + height: 20, + ), + ], + ), + 8.height, + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: + "Dear, how you doing. I have great offer for You for this car. Dear, how you doing. I have great offer for You for this car. Dear, how you doing. I have great offer for You for this car. Dear, how you doing. I have great offer for You for this car. Dear, how you doing. I have great offer for You for this car." + .toText( + color: MyColors.lightTextColor, + fontSize: 12, + ), + ), + 12.width, + "9 Hours Ago".toText( + color: MyColors.lightTextColor, + ) + ], + ), + ], + ), + Icon( + Icons.arrow_forward, + color: MyColors.darkIconColor, + size: 18, + ), + ], + ).toContainer( + isShadowEnabled: true, + ); + } +} diff --git a/lib/views/requests/widget/request_item.dart b/lib/views/requests/widget/request_item.dart new file mode 100644 index 0000000..c2076b7 --- /dev/null +++ b/lib/views/requests/widget/request_item.dart @@ -0,0 +1,119 @@ +import 'package:car_customer_app/model/request.dart'; +import 'package:car_customer_app/views/requests/widget/request_status_chip.dart'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/config/routes.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; + +class RequestItem extends StatelessWidget { + Request request; + + RequestItem(this.request); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RequestStatusChip(request), + 6.height, + "${request.brand} ${request.model}".toText(fontSize: 16, isBold: true), + showItem("Model:", "${request.year}"), + showItem("Customer Name:", "${request.customerName}"), + ], + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Center( + child: "${request.offerCount}".toText( + color: Colors.white, + isBold: true, + fontSize: 10, + ), + ).toContainer( + backgroundColor: MyColors.redColor, + borderRadius: 100, + paddingAll: 1, + width: 20, + height: 20, + ), + 2.height, + request.cityName.toText( + color: MyColors.lightTextColor, + ), + "9 Hours Ago".toText( + color: MyColors.lightTextColor, + ) + ], + ) + ], + ), + showItem("Description: ", request.description), + showItem("Price Range: ", ""), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + request.price.toString().toText( + fontSize: 20, + color: Colors.black, + isBold: true, + ), + 2.width, + "SAR" + .toText( + color: MyColors.lightTextColor, + fontSize: 10, + ) + .paddingOnly( + bottom: 3, + ), + ], + ), + Icon( + Icons.arrow_forward, + color: MyColors.darkIconColor, + size: 18, + ), + ], + ), + ], + ) + .toContainer( + isShadowEnabled: true, + ) + .onPress(() { + navigateWithName(context, AppRoutes.offersListPage); + }); + } + + Widget showItem(String title, String value) { + return Row( + children: [ + title.toText( + color: MyColors.lightTextColor, + ), + 2.width, + value.toText( + isBold: true, + ), + ], + ); + } +} diff --git a/lib/views/requests/widget/request_status_chip.dart b/lib/views/requests/widget/request_status_chip.dart new file mode 100644 index 0000000..d9f4e5e --- /dev/null +++ b/lib/views/requests/widget/request_status_chip.dart @@ -0,0 +1,30 @@ +import 'package:car_customer_app/model/request.dart'; +import 'package:flutter/material.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; + +class RequestStatusChip extends StatelessWidget { + Request request; + + RequestStatusChip(this.request); + + @override + Widget build(BuildContext context) { + return request.requestStatusName + .toText( + fontSize: 8, + color: Colors.white, + ) + .toContainer( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), + backgroundColor: request.requestStatus == RequestStatus.pending + ? MyColors.adPendingStatusColor + : request.requestStatus == RequestStatus.cancelled + ? MyColors.redColor + : MyColors.greenColor, + borderRadius: 100, + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 3c4aa16..ea556a8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,7 +36,7 @@ dependencies: cupertino_icons: ^1.0.2 mc_common_app: - path: /Volumes/Data/Projects/Flutter/car_common_app + path: C:/Users/mirza.shafique/AndroidStudioProjects/mc_common_app dev_dependencies: flutter_test: