diff --git a/lib/chat/chat_view.dart b/lib/chat/chat_view.dart new file mode 100644 index 0000000..70609aa --- /dev/null +++ b/lib/chat/chat_view.dart @@ -0,0 +1,195 @@ +import 'package:flutter/material.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/theme/colors.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/extensions/extensions_widget.dart'; +import 'package:mc_common_app/widgets/txt_field.dart'; + +class ChatView extends StatelessWidget { + const ChatView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar(title: "Chat"), + body: Column( + children: [ + Expanded( + child: ListView.separated( + itemCount: 15, + separatorBuilder: (BuildContext context, int index) => 20.height, + itemBuilder: (BuildContext context, int index) { + return ChatMessageCustomWidget( + isSent: index.isOdd, + profileUrl: MyAssets.bnCar, + messageText: "Hi, How Are you? I can help you out with the desired request.", + messageTypeEnum: index == 10 + ? (MessageTypeEnum.newOfferRequired) + : index == 12 + ? (MessageTypeEnum.offerProvided) + : (MessageTypeEnum.text), + senderName: "Al Abdullah Cars", + ); + }).horPaddingMain(), + ), + 10.width, + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + flex: 7, + child: TxtField( + // value: adVM.vehicleDemandAmount, + // errorValue: adVM.demandAmountError, + hint: "Type your message here..", + keyboardType: TextInputType.text, + isNeedBorder: false, + onChanged: (v) => null, + ), + ), + Expanded( + flex: 1, + child: Icon( + Icons.send_rounded, + color: MyColors.darkPrimaryColor, + size: 30, + ).onPress(() {})) + ], + ).toContainer(isShadowEnabled: true), + ], + ), + // body: + ); + } +} + +class ChatMessageCustomWidget extends StatelessWidget { + final String profileUrl; + final String senderName; + final String messageText; + final MessageTypeEnum messageTypeEnum; + final bool isSent; + + const ChatMessageCustomWidget({ + super.key, + required this.profileUrl, + required this.senderName, + required this.messageText, + required this.messageTypeEnum, + required this.isSent, + }); + + @override + Widget build(BuildContext context) { + return Directionality( + textDirection: isSent ? TextDirection.rtl : TextDirection.ltr, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 1, + child: Image.asset( + profileUrl, + width: 34, + height: 34, + fit: BoxFit.fill, + ).toCircle(borderRadius: 100), + ), + 10.width, + Expanded( + flex: 10, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "${isSent ? "You" : senderName}".toText(fontSize: 16, isBold: true), + ], + ), + 5.height, + Column( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: "${messageText}".toText( + color: isSent ? MyColors.white : MyColors.lightTextColor, + fontSize: 12, + // isBold: true, + ), + ), + ], + ), + if (messageTypeEnum == MessageTypeEnum.offerProvided || messageTypeEnum == MessageTypeEnum.newOfferRequired) ...[ + 5.height, + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + "40000".toText(fontSize: 19, isBold: true), + 2.width, + "SAR".toText(color: MyColors.lightTextColor, height: 2.2, fontSize: 10, isBold: true), + ], + ), + 10.height, + if (messageTypeEnum == MessageTypeEnum.newOfferRequired) ...[ + Center( + child: "You asked for the new offer.".toText( + color: MyColors.adPendingStatusColor, + fontSize: 12, + isItalic: true, + ), + ).toContainer(borderRadius: 40, width: double.infinity, backgroundColor: MyColors.adPendingStatusColor.withOpacity(0.16)), + ] else ...[ + Row( + children: [ + Expanded( + child: ShowFillButton( + maxHeight: 27, + title: "Accept", + fontSize: 9, + borderColor: MyColors.greenColor, + isFilled: false, + onPressed: () {}, + backgroundColor: MyColors.white, + txtColor: MyColors.greenColor, + ), + ), + 20.width, + Expanded( + child: ShowFillButton( + maxHeight: 27, + title: "Reject", + borderColor: MyColors.redColor, + isFilled: false, + onPressed: () {}, + backgroundColor: MyColors.white, + txtColor: MyColors.redColor, + fontSize: 9, + ), + ) + ], + ), + ], + ], + ], + ).toContainer( + isShadowEnabled: !isSent, + backgroundColor: isSent ? MyColors.darkIconColor : MyColors.white, + borderRadius: 0, + margin: EdgeInsets.fromLTRB(isSent ? 25 : 0, 0, !isSent ? 25 : 0, 0), + ), + ], + ), + ) + ], + ), + ); + } +} + +enum MessageTypeEnum { text, picture, offerProvided, recording, video, newOfferRequired } diff --git a/lib/config/customer_dependencies.dart b/lib/config/customer_dependencies.dart index 03dd236..6fd51e7 100644 --- a/lib/config/customer_dependencies.dart +++ b/lib/config/customer_dependencies.dart @@ -1,7 +1,6 @@ import 'package:car_customer_app/repositories/provider_repo.dart'; import 'package:car_customer_app/repositories/appointment_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'; class CustomerDependencies { @@ -9,7 +8,6 @@ class CustomerDependencies { AppDependencies.addDependencies(); injector.registerSingleton(() => ProviderRepoImp()); injector.registerSingleton(() => ScheduleRepoImp()); - - + injector.registerSingleton(() => RequestRepoImp()); } } diff --git a/lib/config/customer_routes.dart b/lib/config/customer_routes.dart index b7b0a1f..8f85512 100644 --- a/lib/config/customer_routes.dart +++ b/lib/config/customer_routes.dart @@ -1,3 +1,4 @@ +import 'package:car_customer_app/chat/chat_view.dart'; import 'package:car_customer_app/views/appointments/appointment_detail_view.dart'; import 'package:car_customer_app/views/appointments/book_appointment_schedules_view.dart'; import 'package:car_customer_app/views/appointments/book_appointment_services_view.dart'; @@ -13,6 +14,7 @@ import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart'; +import 'package:mc_common_app/models/requests/offers_model.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/views/advertisement/ads_detail_view.dart'; import 'package:mc_common_app/views/advertisement/create_ad_view.dart'; @@ -40,7 +42,8 @@ class CustomerAppRoutes { AppRoutes.paymentMethodsView: (context) => PaymentMethodsView(paymentType: ModalRoute.of(context)!.settings.arguments as PaymentTypes), 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.offersListPage: (context) => OfferListPage(offersList: ModalRoute.of(context)!.settings.arguments as List), AppRoutes.createRequestPage: (context) => CreateRequestPage(), + AppRoutes.chatView: (context) => ChatView(), }; } diff --git a/lib/main.dart b/lib/main.dart index 308c167..79209f2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,7 +3,6 @@ import 'package:car_customer_app/config/customer_routes.dart'; import 'package:car_customer_app/repositories/provider_repo.dart'; import 'package:car_customer_app/repositories/appointment_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'; @@ -68,8 +67,13 @@ Future main() async { ChangeNotifierProvider( create: (_) => PaymentVM(paymentService: injector.get(), paymentRepo: injector.get()), ), - scheduleRepo: injector.get(), - + ChangeNotifierProvider( + create: (_) => RequestsVM( + requestRepo: injector.get(), + commonServices: injector.get(), + commonRepo: injector.get(), + ), + ), ], child: MyApp(), ).setupLocale(), diff --git a/lib/model/request.dart b/lib/model/request.dart deleted file mode 100644 index e0bc006..0000000 --- a/lib/model/request.dart +++ /dev/null @@ -1,138 +0,0 @@ -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 index f507c0e..2b8de66 100644 --- a/lib/repositories/request_repo.dart +++ b/lib/repositories/request_repo.dart @@ -1,14 +1,17 @@ -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'; +import 'package:mc_common_app/models/requests/offers_model.dart'; +import 'package:mc_common_app/models/requests/request_model.dart'; abstract class RequestRepo { Future createRequest(Map map); - Future> getRequests(Map postParams); + Future> getOffersByRequest({required int requestId, int serviceProviderId = 0}); + + Future> getRequests(Map postParams); } class RequestRepoImp implements RequestRepo { @@ -27,19 +30,40 @@ class RequestRepoImp implements RequestRepo { } @override - Future> getRequests(Map postParams) async { + 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( + List requests = List.generate( enumGenericModel.data.length, - (index) => Request.fromJson( + (index) => RequestModel.fromJson( enumGenericModel.data[index], ), ); return requests; } + + @override + Future> getOffersByRequest({required int requestId, int serviceProviderId = 0}) async { + var queryParameters = { + "RequestID": requestId.toString(), + "ServiceProviderID": serviceProviderId.toString(), + }; + GenericRespModel genericRespModel = await apiClient.getJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.getRequestOffers, + queryParameters: queryParameters, + token: appState.getUser.data!.accessToken, + ); + List offersList = List.generate( + genericRespModel.data.length, + (index) => OffersModel.fromJson( + genericRespModel.data[index], + ), + ); + return offersList; + } } diff --git a/lib/view_models/appointments_view_model.dart b/lib/view_models/appointments_view_model.dart index ce6a26b..613ba4d 100644 --- a/lib/view_models/appointments_view_model.dart +++ b/lib/view_models/appointments_view_model.dart @@ -9,6 +9,7 @@ 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/models/appointments_models/appointment_list_model.dart'; +import 'package:mc_common_app/models/enums_model.dart'; import 'package:mc_common_app/models/generic_resp_model.dart'; import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart'; import 'package:mc_common_app/models/provider_branches_models/provider_profile_model.dart'; @@ -218,15 +219,18 @@ class AppointmentsVM extends BaseVM { serviceAppointmentScheduleList.clear(); } - populateAppointmentsFilterList() { + List myAppointmentsEnum = []; + + populateAppointmentsFilterList() async { appointmentsFilterOptions.clear(); - appointmentsFilterOptions = [ - FilterListModel(title: "All Appointments", isSelected: true, id: -1), - FilterListModel(title: "Booked", isSelected: false, id: 1), - FilterListModel(title: "Confirmed", isSelected: false, id: 2), - FilterListModel(title: "Arrived", isSelected: false, id: 3), - FilterListModel(title: "Cancelled", isSelected: false, id: 4), - ]; + + myAppointmentsEnum = await commonRepo.getEnumTypeValues(enumTypeID: 13); //TODO: 13 is to get Appointments Filter Enums + + for (int i = 0; i < myAppointmentsEnum.length; i++) { + appointmentsFilterOptions.add(FilterListModel(title: myAppointmentsEnum[i].enumValueStr, isSelected: false, id: myAppointmentsEnum[i].enumValue)); + } + appointmentsFilterOptions.insert(0, FilterListModel(title: "All Appointments", isSelected: true, id: 0)); + notifyListeners(); } @@ -235,9 +239,9 @@ class AppointmentsVM extends BaseVM { for (var value in appointmentsFilterOptions) { value.isSelected = false; } - appointmentsFilterOptions[appointmentStatusEnum.getIdFromAppointmentStatusEnum() == -1 ? 0 : appointmentStatusEnum.getIdFromAppointmentStatusEnum()].isSelected = true; + appointmentsFilterOptions[appointmentStatusEnum.getIdFromAppointmentStatusEnum()].isSelected = true; - if (appointmentStatusEnum.getIdFromAppointmentStatusEnum() == -1) { + if (appointmentStatusEnum.getIdFromAppointmentStatusEnum() == 0) { myFilteredAppointments = myAppointments; notifyListeners(); return; @@ -247,13 +251,14 @@ class AppointmentsVM extends BaseVM { notifyListeners(); } - Future getMyAppointments() async { - isFetchingLists = true; + Future getMyAppointments({bool isNeedToRebuild = false}) async { + if (isNeedToRebuild) setState(ViewState.busy); + myAppointments = await commonRepo.getMyAppointments(); myFilteredAppointments = myAppointments; myUpComingAppointments = myAppointments.where((element) => element.appointmentStatusEnum == AppointmentStatusEnum.booked).toList(); + setState(ViewState.idle); // applyFilterOnAppointmentsVM(appointmentStatusEnum: AppointmentStatusEnum.allAppointments); - isFetchingLists = false; notifyListeners(); } @@ -331,12 +336,12 @@ class AppointmentsVM extends BaseVM { populateBranchesFilterList() { providersFilterOptions.clear(); providersFilterOptions = [ - FilterListModel(title: "All Providers", isSelected: true, id: -1), - FilterListModel(title: "Maintenance", isSelected: false, id: 0), - FilterListModel(title: "Oil Service", isSelected: false, id: 1), - FilterListModel(title: "Accessories", isSelected: false, id: 2), - FilterListModel(title: "Tire Service", isSelected: false, id: 3), - FilterListModel(title: "Dent and Paint", isSelected: false, id: 4), + FilterListModel(title: "All Providers", isSelected: true, id: 0), + FilterListModel(title: "Maintenance", isSelected: false, id: 1), + FilterListModel(title: "Oil Service", isSelected: false, id: 2), + FilterListModel(title: "Accessories", isSelected: false, id: 3), + FilterListModel(title: "Tire Service", isSelected: false, id: 4), + FilterListModel(title: "Dent and Paint", isSelected: false, id: 5), ]; notifyListeners(); } @@ -405,7 +410,6 @@ class AppointmentsVM extends BaseVM { pickHomeLocationError = ""; if (categoryId != -1) { isFetchingServices = true; - // notifyListeners(); branchServices = getFilteredBranchServices(categoryId: categoryId); isFetchingServices = false; notifyListeners(); diff --git a/lib/view_models/requests_view_model.dart b/lib/view_models/requests_view_model.dart index 0e67638..e825048 100644 --- a/lib/view_models/requests_view_model.dart +++ b/lib/view_models/requests_view_model.dart @@ -1,18 +1,22 @@ +import 'dart:convert'; import 'dart:io'; import 'package:car_customer_app/repositories/request_repo.dart'; +import 'package:flutter/cupertino.dart'; import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/extensions/string_extensions.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/enums_model.dart'; import 'package:mc_common_app/models/generic_resp_model.dart'; +import 'package:mc_common_app/models/requests/offers_model.dart'; +import 'package:mc_common_app/models/requests/request_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/enums.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; @@ -20,23 +24,49 @@ class RequestsVM extends BaseVM { RequestsVM({required this.commonServices, required this.commonRepo, required this.requestRepo}); - List requestsFilterOptions = []; + List myRequests = []; + List myFilteredRequests = []; + + List requestsTypeFilterOptions = []; + + List myRequestsTypeEnum = []; + + populateRequestsFilterList() async { + requestsTypeFilterOptions.clear(); + + myRequestsTypeEnum = await commonRepo.getEnumTypeValues(enumTypeID: 16); //TODO: 13 is to get Requests Filter Enums + + for (int i = 0; i < myRequestsTypeEnum.length; i++) { + requestsTypeFilterOptions.add(FilterListModel(title: myRequestsTypeEnum[i].enumValueStr, isSelected: false, id: myRequestsTypeEnum[i].enumValue)); + } + + notifyListeners(); + } + + Future getMyRequests({bool isNeedToRebuild = false}) async { + if (isNeedToRebuild) setState(ViewState.busy); - populateRequestsFilterList() { - requestsFilterOptions.clear(); - requestsFilterOptions = [ - FilterListModel(title: "Cars", isSelected: true, id: 1), - FilterListModel(title: "Spare Parts", isSelected: false, id: 2), - ]; + myRequests = await requestRepo.getRequests( + { + "customerID": AppState().getUser.data!.userInfo!.customerId, + "pageSize": 100, + "pageIndex": 0, + "requestType": 0, + }, + ); + applyFilterOnRequestsVM(requestsTypeEnum: RequestsTypeEnum.specialCarRequest); + setState(ViewState.idle); notifyListeners(); } - applyFilterOnRequestsVM({required int index}) { - if (requestsFilterOptions.isEmpty) return; - for (var value in requestsFilterOptions) { + applyFilterOnRequestsVM({required RequestsTypeEnum requestsTypeEnum}) { + if (requestsTypeFilterOptions.isEmpty) return; + for (var value in requestsTypeFilterOptions) { value.isSelected = false; } - requestsFilterOptions[index].isSelected = true; + requestsTypeFilterOptions[requestsTypeEnum.getIdFromRequestTypeStatusEnum() - 1].isSelected = true; // -1 to match with the index + myFilteredRequests = myRequests.where((element) => element.requestType == requestsTypeEnum.getIdFromRequestTypeStatusEnum()).toList(); + notifyListeners(); } @@ -62,7 +92,6 @@ class RequestsVM extends BaseVM { bool isFetchingRequestType = false; bool isFetchingVehicleType = true; bool isFetchingVehicleDetail = false; - List requestTypes = []; List vehicleTypes = []; VehicleDetailsModel? vehicleDetails; List vehicleBrands = []; @@ -73,14 +102,6 @@ class RequestsVM extends BaseVM { 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(); @@ -90,14 +111,14 @@ class RequestsVM extends BaseVM { SelectionModel vehicleTypeId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); Future getVehicleTypes() async { - reset(); + resetRequestCreationForm(); isFetchingVehicleType = true; vehicleTypes = await commonRepo.getVehicleTypes(); isFetchingVehicleType = false; notifyListeners(); } - reset() { + resetRequestCreationForm() { vehicleTypeId.selectedId = -1; vehicleBrandId.selectedId = -1; vehicleModelId.selectedId = -1; @@ -191,9 +212,27 @@ class RequestsVM extends BaseVM { description = v; } - Future createRequest() async { - if (validate()) { - Map m = { + Future convertFileToRequestPostingImages({required File file}) async { + List imageBytes = await file.readAsBytes(); + String image = base64Encode(imageBytes); + String fileName = file.path.split('/').last; + VehiclePostingImages vehiclePostingImages = VehiclePostingImages( + imageName: fileName, + imageStr: image, + imageUrl: file.path, + ); + return vehiclePostingImages; + } + + Future onCreateRequestTapped(BuildContext context) async { + if (validateCreateRequestForm()) { + Utils.showLoading(context); + List vehicleImages = []; + pickedVehicleImages.forEach((element) async { + vehicleImages.add(await convertFileToRequestPostingImages(file: element)); + }); + + Map body = { "customerID": AppState().getUser.data!.userInfo!.customerId ?? 0, "requestType": requestTypeId.selectedId, "vehicleTypeID": vehicleTypeId.selectedId, @@ -206,16 +245,26 @@ class RequestsVM extends BaseVM { "price": price, "description": description, "isSpecialServiceNeeded": false, - "requestImages": [] + "requestImages": vehicleImages, }; - GenericRespModel respModel = await requestRepo.createRequest(m); - return respModel; - } else { - return null; + try { + GenericRespModel respModel = await requestRepo.createRequest(body); + Utils.hideLoading(context); + if (respModel.messageStatus == 1) { + Utils.showToast("Request Successfully Created"); + Navigator.pop(context); + await getMyRequests(); + } else { + Utils.showToast(respModel.message.toString()); + } + } catch (e, s) { + Utils.hideLoading(context); + print(s); + } } } - bool validate() { + bool validateCreateRequestForm() { bool isValid = true; if (requestTypeId.selectedId == -1) { Utils.showToast("Please select valid Request Type"); @@ -248,25 +297,16 @@ class RequestsVM extends BaseVM { 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(); + Future> getOffersByRequest({required int requestId, required BuildContext context}) async { + try { + Utils.showLoading(context); + List respModel = await requestRepo.getOffersByRequest(requestId: requestId); + Utils.hideLoading(context); + return respModel; + } catch (e) { + Utils.showToast(e.toString()); + Utils.hideLoading(context); + return []; + } } } diff --git a/lib/views/appointments/appointment_detail_view.dart b/lib/views/appointments/appointment_detail_view.dart index ac79906..bf02f3a 100644 --- a/lib/views/appointments/appointment_detail_view.dart +++ b/lib/views/appointments/appointment_detail_view.dart @@ -79,6 +79,22 @@ class AppointmentDetailView extends StatelessWidget { ); case AppointmentStatusEnum.allAppointments: return SizedBox(); + case AppointmentStatusEnum.rescheduled: + return Align( + alignment: Alignment.bottomCenter, + child: Row( + children: [ + getBaseActionButtonWidget(color: MyColors.redColor, onPressed: () => appointmentCancelConfirmationSheet(context), text: "Cancel"), + 12.width, + getBaseActionButtonWidget( + color: MyColors.greenColor, + onPressed: () { + context.read().onConfirmAppointmentPressed(context: context, appointmentId: appointmentListModel.id); + }, + text: "Confirm"), + ], + ), + ); } } diff --git a/lib/views/dashboard/dashboard_page.dart b/lib/views/dashboard/dashboard_page.dart index 0e3373e..d3e87b9 100644 --- a/lib/views/dashboard/dashboard_page.dart +++ b/lib/views/dashboard/dashboard_page.dart @@ -40,7 +40,6 @@ class _DashboardPageState extends State { scheduleMicrotask(() { context.read().populateAppointmentsFilterList(); context.read().populateBranchesFilterList(); - context.read().populateProvidersFilterList(); context.read().populateRequestsFilterList(); context.read().populateAdsFilterList(); _onRefresh(); @@ -50,6 +49,7 @@ class _DashboardPageState extends State { Future _onRefresh() async { AdVM adVM = Provider.of(context, listen: false); AppointmentsVM appointmentsVM = Provider.of(context, listen: false); + RequestsVM requestsVM = Provider.of(context, listen: false); if (appointmentsVM.myAppointments.isEmpty) { await appointmentsVM.getMyAppointments(); } @@ -62,6 +62,9 @@ class _DashboardPageState extends State { if (adVM.exploreAds.isEmpty) { await adVM.getExploreAds(); } + if (requestsVM.myRequests.isEmpty) { + await requestsVM.getMyRequests(); + } if (adVM.vehicleTypes.isEmpty) { await adVM.getVehicleTypes(); @@ -135,19 +138,6 @@ 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/ads_fragment.dart b/lib/views/dashboard/fragments/ads_fragment.dart index 0bfc170..b41116e 100644 --- a/lib/views/dashboard/fragments/ads_fragment.dart +++ b/lib/views/dashboard/fragments/ads_fragment.dart @@ -5,7 +5,6 @@ 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/models/advertisment_models/ad_details_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/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; diff --git a/lib/views/dashboard/fragments/appointments_fragment.dart b/lib/views/dashboard/fragments/appointments_fragment.dart index 382d923..9fb81e8 100644 --- a/lib/views/dashboard/fragments/appointments_fragment.dart +++ b/lib/views/dashboard/fragments/appointments_fragment.dart @@ -8,6 +8,7 @@ import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/widgets/common_widgets/categories_list.dart'; import 'package:provider/provider.dart'; @@ -27,38 +28,46 @@ class AppointmentsFragment extends StatelessWidget { children: [ 16.height, FiltersList( - filterList: appointmentsVM.appointmentsFilterOptions, - onFilterTapped: (index, selectedFilterId) => appointmentsVM.applyFilterOnAppointmentsVM(appointmentStatusEnum: selectedFilterId.toAppointmentStatusEnum()), - ), + filterList: appointmentsVM.appointmentsFilterOptions, + onFilterTapped: (index, selectedFilterId) { + appointmentsVM.applyFilterOnAppointmentsVM(appointmentStatusEnum: selectedFilterId.toAppointmentStatusEnum()); + }), 16.height, Expanded( - child: appointmentsVM.myFilteredAppointments.isEmpty - ? Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - "No Appointments to show.".toText(fontSize: 16, color: MyColors.lightTextColor), - ], - ) - : ListView.builder( - shrinkWrap: true, - itemCount: appointmentsVM.myFilteredAppointments.length, - itemBuilder: (BuildContext context, int index) { - return BuildAppointmentContainerForCustomer( - onTapped: () { - AppointmentListModel appointmentModel = appointmentsVM.myFilteredAppointments[index]; - appointmentModel.appointmentServicesList!.forEach((service) { - double totalServicePrice = 0.0; - service.serviceItems!.forEach((item) { - totalServicePrice = totalServicePrice + (double.parse("${item.price ?? 0.0}")); - }); - service.currentTotalServicePrice = totalServicePrice; - }); - navigateWithName(context, AppRoutes.appointmentDetailView, arguments: appointmentModel); - }, - appointmentListModel: appointmentsVM.myFilteredAppointments[index], - ); - }, - ), + child: RefreshIndicator( + onRefresh: () async { + appointmentsVM.getMyAppointments(isNeedToRebuild: true); + }, + child: appointmentsVM.state == ViewState.busy + ? const Center(child: CircularProgressIndicator()) + : appointmentsVM.myFilteredAppointments.isEmpty + ? Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + "No Appointments to show.".toText(fontSize: 16, color: MyColors.lightTextColor), + ], + ) + : ListView.builder( + shrinkWrap: true, + itemCount: appointmentsVM.myFilteredAppointments.length, + itemBuilder: (BuildContext context, int index) { + return BuildAppointmentContainerForCustomer( + onTapped: () { + AppointmentListModel appointmentModel = appointmentsVM.myFilteredAppointments[index]; + appointmentModel.appointmentServicesList!.forEach((service) { + double totalServicePrice = 0.0; + service.serviceItems!.forEach((item) { + totalServicePrice = totalServicePrice + (double.parse("${item.price ?? 0.0}")); + }); + service.currentTotalServicePrice = totalServicePrice; + }); + navigateWithName(context, AppRoutes.appointmentDetailView, arguments: appointmentModel); + }, + appointmentListModel: appointmentsVM.myFilteredAppointments[index], + ); + }, + ), + ), ), ], ); diff --git a/lib/views/dashboard/fragments/branches_fragment.dart b/lib/views/dashboard/fragments/branches_fragment.dart index 792202f..c2b2dfc 100644 --- a/lib/views/dashboard/fragments/branches_fragment.dart +++ b/lib/views/dashboard/fragments/branches_fragment.dart @@ -38,44 +38,35 @@ class BranchesFragment extends StatelessWidget { onRefresh: () async { context.read().getAllNearBranches(isNeedToRebuild: true); }, - child: SingleChildScrollView( - child: Container( + child: Container( width: double.infinity, height: MediaQuery.of(context).size.height / 1.37, - child: Consumer( - builder: (context, model, _) { - if (model.state == ViewState.busy) { - return const Center(child: CircularProgressIndicator()); - } else { - return model.nearbyBranches.isEmpty - ? Center(child: LocaleKeys.no_branch.tr().toText(fontSize: 16, color: MyColors.lightTextColor)) - : ListView.separated( - itemCount: model.nearbyBranches.length, - itemBuilder: (context, index) { - BranchDetailModel branchDetailModel = model.nearbyBranches[index]; + child: appointmentsVM.state == ViewState.busy + ? const Center(child: CircularProgressIndicator()) + : appointmentsVM.nearbyBranches.isEmpty + ? Center(child: LocaleKeys.no_branch.tr().toText(fontSize: 16, color: MyColors.lightTextColor)) + : ListView.separated( + itemCount: appointmentsVM.nearbyBranches.length, + itemBuilder: (context, index) { + BranchDetailModel branchDetailModel = appointmentsVM.nearbyBranches[index]; - return ProviderDetailsCard( - onCardTapped: () { - navigateWithName(context, AppRoutes.branchDetailPage, arguments: branchDetailModel); - }, - providerImageUrl: MyAssets.bnCar, - title: branchDetailModel.branchName ?? "", - providerLocation: branchDetailModel.distanceKm.toString() + " KM", - providerName: branchDetailModel.serviceProviderName ?? "", - providerRatings: "4.9", - services: branchDetailModel.branchServices, - ); - }, - separatorBuilder: (context, index) { - return 12.height; - }, - padding: const EdgeInsets.all(12), - ); - } - }, - ), - ), - ), + return ProviderDetailsCard( + onCardTapped: () { + navigateWithName(context, AppRoutes.branchDetailPage, arguments: branchDetailModel); + }, + providerImageUrl: MyAssets.bnCar, + title: branchDetailModel.branchName ?? "", + providerLocation: branchDetailModel.distanceKm.toString() + " KM", + providerName: branchDetailModel.serviceProviderName ?? "", + providerRatings: "4.9", + services: branchDetailModel.branchServices, + ); + }, + separatorBuilder: (context, index) { + return 12.height; + }, + padding: const EdgeInsets.all(12), + )), ), ), ], diff --git a/lib/views/dashboard/fragments/requests_fragment.dart b/lib/views/dashboard/fragments/requests_fragment.dart index d19d4eb..dfad7fb 100644 --- a/lib/views/dashboard/fragments/requests_fragment.dart +++ b/lib/views/dashboard/fragments/requests_fragment.dart @@ -1,63 +1,69 @@ import 'package:car_customer_app/view_models/requests_view_model.dart'; +import 'package:car_customer_app/views/requests/widget/request_item.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/widgets/button/show_fill_button.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/navigator.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, + return Scaffold( + body: Container( + color: MyColors.backgroundColor, + width: double.infinity, + height: double.infinity, + child: Consumer(builder: (BuildContext context, RequestsVM requestsVM, Widget? child) { + return Column( + children: [ + 16.height, + FiltersList( + filterList: requestsVM.requestsTypeFilterOptions, + onFilterTapped: (index, selectedFilterId) { + requestsVM.applyFilterOnRequestsVM(requestsTypeEnum: selectedFilterId.toRequestTypeStatusEnum()); + }, + ), + 8.height, + Expanded( + child: RefreshIndicator( + onRefresh: () async => await requestsVM.getMyRequests(isNeedToRebuild: true), + child: requestsVM.state == ViewState.busy + ? const Center(child: CircularProgressIndicator()) + : requestsVM.myFilteredRequests.isEmpty + ? Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + "No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor), + ], + ) + : ListView.separated( + itemBuilder: (context, index) { + return RequestItem(request: requestsVM.myRequests[index]); + }, + separatorBuilder: (context, index) { + return 16.height; + }, + itemCount: requestsVM.myRequests.length, + padding: EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 8), ), - ), - ) - ], - ); - }), + )) + ], + ); + }), + ), + floatingActionButton: FloatingActionButton( + onPressed: () => navigateWithName(context, AppRoutes.createRequestPage), + backgroundColor: MyColors.darkPrimaryColor, + child: Icon( + Icons.add, + color: MyColors.white, + ), + ), ); } } diff --git a/lib/views/requests/create_request_page.dart b/lib/views/requests/create_request_page.dart index 6947e6a..75445a5 100644 --- a/lib/views/requests/create_request_page.dart +++ b/lib/views/requests/create_request_page.dart @@ -1,34 +1,27 @@ 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) { + body: Consumer(builder: (context, requestsVM, widget) { return Column( children: [ Expanded( @@ -38,49 +31,49 @@ class CreateRequestPage extends StatelessWidget { children: [ "Vehicle Detail".toText(fontSize: 18, isBold: true), 8.height, - if (model.isFetchingRequestType) ...[ + if (requestsVM.isFetchingRequestType) ...[ Center( child: CircularProgressIndicator(), ), ] else ...[ Builder(builder: (context) { List requestTypeDrop = []; - for (var element in model.requestTypes) { + for (var element in requestsVM.myRequestsTypeEnum) { requestTypeDrop.add(DropValue(element.enumValue.toInt() ?? 0, element.enumValueStr ?? "", "")); } return DropdownField( - (DropValue value) => model.updateSelectionRequestTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)), + (DropValue value) => requestsVM.updateSelectionRequestTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)), list: requestTypeDrop, - dropdownValue: model.requestTypeId.selectedId != -1 ? DropValue(model.requestTypeId.selectedId, model.requestTypeId.selectedOption, "") : null, + dropdownValue: requestsVM.requestTypeId.selectedId != -1 ? DropValue(requestsVM.requestTypeId.selectedId, requestsVM.requestTypeId.selectedOption, "") : null, hint: "Request Type", - errorValue: model.requestTypeId.errorValue, + errorValue: requestsVM.requestTypeId.errorValue, ); }), ], 8.height, - if (model.requestTypeId.selectedId != -1) - if (model.isFetchingVehicleType) ...[ + if (requestsVM.requestTypeId.selectedId != -1) + if (requestsVM.isFetchingVehicleType) ...[ Center( child: CircularProgressIndicator(), ), ] else ...[ Builder(builder: (context) { List vehicleTypeDrop = []; - for (var element in model.vehicleTypes) { + for (var element in requestsVM.vehicleTypes) { vehicleTypeDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleTypeName ?? "", "")); } return DropdownField( - (DropValue value) => model.updateSelectionVehicleTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)), + (DropValue value) => requestsVM.updateSelectionVehicleTypeId(SelectionModel(selectedId: value.id, selectedOption: value.value)), list: vehicleTypeDrop, - dropdownValue: model.vehicleTypeId.selectedId != -1 ? DropValue(model.vehicleTypeId.selectedId, model.vehicleTypeId.selectedOption, "") : null, + dropdownValue: requestsVM.vehicleTypeId.selectedId != -1 ? DropValue(requestsVM.vehicleTypeId.selectedId, requestsVM.vehicleTypeId.selectedOption, "") : null, hint: "Vehicle Type", - errorValue: model.vehicleTypeId.errorValue, + errorValue: requestsVM.vehicleTypeId.errorValue, ); }), ], 8.height, - if (model.vehicleTypeId.selectedId != -1) - if (model.isFetchingVehicleDetail) ...[ + if (requestsVM.vehicleTypeId.selectedId != -1) + if (requestsVM.isFetchingVehicleDetail) ...[ Center( child: CircularProgressIndicator(), ), @@ -89,62 +82,62 @@ class CreateRequestPage extends StatelessWidget { children: [ Builder(builder: (context) { List vehicleBrandsDrop = []; - for (var element in model.vehicleBrands) { + for (var element in requestsVM.vehicleBrands) { vehicleBrandsDrop.add(DropValue(element.id?.toInt() ?? 0, element.vehicleBrandDescription ?? "", "")); } return DropdownField( - (DropValue value) => model.updateSelectionVehicleBrandId(SelectionModel(selectedId: value.id, selectedOption: value.value)), + (DropValue value) => requestsVM.updateSelectionVehicleBrandId(SelectionModel(selectedId: value.id, selectedOption: value.value)), list: vehicleBrandsDrop, - dropdownValue: model.vehicleBrandId.selectedId != -1 ? DropValue(model.vehicleBrandId.selectedId, model.vehicleBrandId.selectedOption, "") : null, + dropdownValue: requestsVM.vehicleBrandId.selectedId != -1 ? DropValue(requestsVM.vehicleBrandId.selectedId, requestsVM.vehicleBrandId.selectedOption, "") : null, hint: "Brand", - errorValue: model.vehicleBrandId.errorValue, + errorValue: requestsVM.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 ?? "", "")); + for (var element in requestsVM.vehicleModels) { + if (requestsVM.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)), + (DropValue value) => requestsVM.updateSelectionVehicleModelId(SelectionModel(selectedId: value.id, selectedOption: value.value)), list: vehicleModelsDrop, - dropdownValue: model.vehicleModelId.selectedId != -1 ? DropValue(model.vehicleModelId.selectedId, model.vehicleModelId.selectedOption, "") : null, + dropdownValue: requestsVM.vehicleModelId.selectedId != -1 ? DropValue(requestsVM.vehicleModelId.selectedId, requestsVM.vehicleModelId.selectedOption, "") : null, hint: "Model", - errorValue: model.vehicleModelId.errorValue, + errorValue: requestsVM.vehicleModelId.errorValue, ); }), 8.height, Builder(builder: (context) { List vehicleYearModelsDrop = []; - for (var element in model.vehicleModelYears) { + for (var element in requestsVM.vehicleModelYears) { vehicleYearModelsDrop.add(DropValue(element.id?.toInt() ?? 0, element.modelYear ?? "", "")); } return DropdownField( - (DropValue value) => model.updateSelectionVehicleModelYearId(SelectionModel(selectedId: value.id, selectedOption: value.value)), + (DropValue value) => requestsVM.updateSelectionVehicleModelYearId(SelectionModel(selectedId: value.id, selectedOption: value.value)), list: vehicleYearModelsDrop, - dropdownValue: model.vehicleModelYearId.selectedId != -1 ? DropValue(model.vehicleModelYearId.selectedId, model.vehicleModelYearId.selectedOption, "") : null, + dropdownValue: requestsVM.vehicleModelYearId.selectedId != -1 ? DropValue(requestsVM.vehicleModelYearId.selectedId, requestsVM.vehicleModelYearId.selectedOption, "") : null, hint: "Year", - errorValue: model.vehicleModelYearId.errorValue, + errorValue: requestsVM.vehicleModelYearId.errorValue, ); }), 8.height, Builder(builder: (context) { List vehicleCountriesDrop = []; - for (var element in model.vehicleCountries) { + for (var element in requestsVM.vehicleCountries) { vehicleCountriesDrop.add(DropValue(element.id?.toInt() ?? 0, element.countryName ?? "", "")); } return DropdownField( - (DropValue value) => model.updateSelectionVehicleCountryId(SelectionModel(selectedOption: value.value, selectedId: value.id)), + (DropValue value) => requestsVM.updateSelectionVehicleCountryId(SelectionModel(selectedOption: value.value, selectedId: value.id)), list: vehicleCountriesDrop, - dropdownValue: model.vehicleCountryId.selectedId != -1 ? DropValue(model.vehicleCountryId.selectedId, model.vehicleCountryId.selectedOption, "") : null, + dropdownValue: requestsVM.vehicleCountryId.selectedId != -1 ? DropValue(requestsVM.vehicleCountryId.selectedId, requestsVM.vehicleCountryId.selectedOption, "") : null, hint: "Country", - errorValue: model.vehicleCountryId.errorValue, + errorValue: requestsVM.vehicleCountryId.errorValue, ); }), - if (model.vehicleCountryId.selectedId != -1) ...[ - if (model.isCountryFetching) ...[ + if (requestsVM.vehicleCountryId.selectedId != -1) ...[ + if (requestsVM.isCountryFetching) ...[ Row( mainAxisAlignment: MainAxisAlignment.center, children: [const CircularProgressIndicator().paddingAll(10)], @@ -153,15 +146,15 @@ class CreateRequestPage extends StatelessWidget { 8.height, Builder(builder: (context) { List vehicleCitiesDrop = []; - for (var element in model.vehicleCities) { + for (var element in requestsVM.vehicleCities) { vehicleCitiesDrop.add(DropValue(element.id?.toInt() ?? 0, element.cityName ?? "", "")); } return DropdownField( - (DropValue value) => model.updateSelectionVehicleCityId(SelectionModel(selectedId: value.id, selectedOption: value.value)), + (DropValue value) => requestsVM.updateSelectionVehicleCityId(SelectionModel(selectedId: value.id, selectedOption: value.value)), list: vehicleCitiesDrop, - dropdownValue: model.vehicleCityId.selectedId != -1 ? DropValue(model.vehicleCityId.selectedId, model.vehicleCityId.selectedOption, "") : null, + dropdownValue: requestsVM.vehicleCityId.selectedId != -1 ? DropValue(requestsVM.vehicleCityId.selectedId, requestsVM.vehicleCityId.selectedOption, "") : null, hint: "City", - errorValue: model.vehicleCityId.errorValue, + errorValue: requestsVM.vehicleCityId.errorValue, ); }), ], @@ -169,15 +162,15 @@ class CreateRequestPage extends StatelessWidget { 8.height, TxtField( hint: "Price", - value: model.price, - onChanged: (e) => model.updatePrice(e), + value: requestsVM.price, + onChanged: (e) => requestsVM.updatePrice(e), ), 8.height, TxtField( hint: "Description", maxLines: 5, - value: model.description, - onChanged: (e) => model.updateDescription(e), + value: requestsVM.description, + onChanged: (e) => requestsVM.updateDescription(e), ), 8.height, DottedRectContainer( @@ -185,20 +178,20 @@ class CreateRequestPage extends StatelessWidget { text: "Attach Image", icon: MyAssets.attachmentIcon.buildSvg(), ), - if (model.vehicleImageError != "") ...[ + if (requestsVM.vehicleImageError != "") ...[ 10.height, Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - model.vehicleImageError.toText(fontSize: 14, color: Colors.red), + requestsVM.vehicleImageError.toText(fontSize: 14, color: Colors.red), ], ).paddingOnly(right: 10) ], - if (model.pickedVehicleImages.isNotEmpty) ...[ + if (requestsVM.pickedVehicleImages.isNotEmpty) ...[ 16.height, PickedImagesContainer( - pickedImages: model.pickedVehicleImages, - onCrossPressedPrimary: model.removeImageFromList, + pickedImages: requestsVM.pickedVehicleImages, + onCrossPressedPrimary: requestsVM.removeImageFromList, onAddImagePressed: () { context.read().pickMultipleImages(); }, @@ -209,13 +202,14 @@ class CreateRequestPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, children: [ Checkbox( - value: model.isShippingDeliveryEnabled, + value: requestsVM.isShippingDeliveryEnabled, onChanged: null, + ), "Shipping/Delivery".toText(color: MyColors.darkPrimaryColor, isBold: true, fontSize: 18) ], ).onPress(() { - model.updateShippingDeliverEnabled(!model.isShippingDeliveryEnabled); + requestsVM.updateShippingDeliverEnabled(!requestsVM.isShippingDeliveryEnabled); }), ], ), @@ -229,16 +223,7 @@ class CreateRequestPage extends StatelessWidget { 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()); - } - } + await context.read().onCreateRequestTapped(context); }, ) ], diff --git a/lib/views/requests/offer_list_page.dart b/lib/views/requests/offer_list_page.dart index d5c2824..d97689f 100644 --- a/lib/views/requests/offer_list_page.dart +++ b/lib/views/requests/offer_list_page.dart @@ -1,84 +1,87 @@ 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/models/requests/offers_model.dart'; import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/navigator.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 { + final List offersList; + + OfferListPage({required this.offersList}); + @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), - ), - ); - } + appBar: CustomAppBar(title: "Offers"), + body: offersList.isEmpty + ? Center(child: "No Requests to show.".toText(fontSize: 16, color: MyColors.lightTextColor)) + : ListView.separated( + itemCount: offersList.length, + padding: EdgeInsets.all(16), + itemBuilder: (context, index) { + OffersModel offersModel = offersList[index]; + return Stack( + alignment: Alignment.bottomRight, + children: [ + Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "${offersModel.serviceProvider!.companyName ?? ""}".toText(fontSize: 16, isBold: true), - 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, - ) - ], + //Un read offers count + + // 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: "${offersModel.comment} and This is dummy text edition.".toText( + color: MyColors.lightTextColor, + fontSize: 12, + ), + ), + 12.width, + "9 Hours Ago".toText( + color: MyColors.lightTextColor, + ) + ], + ), + ], + ), + Icon( + Icons.arrow_forward, + color: MyColors.darkIconColor, + size: 18, + ), + ], + ).onPress(() { + navigateWithName(context, AppRoutes.chatView); + }).toContainer(isShadowEnabled: true); + }, + separatorBuilder: (context, index) => 16.height, ), - ], - ), - 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 index c2076b7..08cbd78 100644 --- a/lib/views/requests/widget/request_item.dart +++ b/lib/views/requests/widget/request_item.dart @@ -1,17 +1,21 @@ -import 'package:car_customer_app/model/request.dart'; +import 'package:car_customer_app/view_models/requests_view_model.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/models/requests/offers_model.dart'; +import 'package:mc_common_app/models/requests/request_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; +import 'package:provider/provider.dart'; class RequestItem extends StatelessWidget { - Request request; + final RequestModel request; - RequestItem(this.request); + RequestItem({required this.request}); @override Widget build(BuildContext context) { @@ -25,7 +29,7 @@ class RequestItem extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - RequestStatusChip(request), + Utils.statusContainerChip(text: request.requestStatusName, chipColor: Utils.getChipColorByRequestStatus(request.requestStatus)), 6.height, "${request.brand} ${request.model}".toText(fontSize: 16, isBold: true), showItem("Model:", "${request.year}"), @@ -37,19 +41,21 @@ class RequestItem extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: [ - Center( - child: "${request.offerCount}".toText( - color: Colors.white, - isBold: true, - fontSize: 10, + if (request.offerCount > 0) ...[ + Center( + child: "${request.offerCount}".toText( + color: Colors.white, + isBold: true, + fontSize: 10, + ), + ).toContainer( + backgroundColor: MyColors.redColor, + borderRadius: 100, + paddingAll: 1, + width: 20, + height: 20, ), - ).toContainer( - backgroundColor: MyColors.redColor, - borderRadius: 100, - paddingAll: 1, - width: 20, - height: 20, - ), + ], 2.height, request.cityName.toText( color: MyColors.lightTextColor, @@ -94,12 +100,9 @@ class RequestItem extends StatelessWidget { ], ), ], - ) - .toContainer( - isShadowEnabled: true, - ) - .onPress(() { - navigateWithName(context, AppRoutes.offersListPage); + ).toContainer(isShadowEnabled: true).onPress(() async { + List offers = await context.read().getOffersByRequest(requestId: request.id, context: context); + navigateWithName(context, AppRoutes.offersListPage, arguments: offers); }); } diff --git a/lib/views/requests/widget/request_status_chip.dart b/lib/views/requests/widget/request_status_chip.dart index d9f4e5e..21db680 100644 --- a/lib/views/requests/widget/request_status_chip.dart +++ b/lib/views/requests/widget/request_status_chip.dart @@ -1,12 +1,12 @@ -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/models/requests/request_model.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; + RequestModel request; RequestStatusChip(this.request); diff --git a/pubspec.yaml b/pubspec.yaml index 987fcff..6d94c35 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,7 +37,7 @@ dependencies: table_calendar: ^3.0.9 mc_common_app: - path: C:/Users/mirza.shafique/AndroidStudioProjects/mc_common_app + path: /Volumes/Data/Projects/Flutter/car_common_app dev_dependencies: flutter_test: