From b437bf4c918239773d2ffd8a0ab5ad92aa99e874 Mon Sep 17 00:00:00 2001 From: "Mirza.Shafique@cloudsolutions.com.sa" <5093@Shf> Date: Sun, 31 Dec 2023 09:31:06 +0300 Subject: [PATCH] Appointment Implementation --- lib/config/provider_routes.dart | 17 +- lib/main.dart | 27 +- lib/repositories/branch_repo.dart | 169 ++++++-- lib/view_models/items_view_model.dart | 20 +- lib/view_models/service_view_model.dart | 71 +++- .../appoinment_detail_list_page.dart | 69 +++- .../appoinments/update_appointment_page.dart | 326 +++++++++++++-- lib/views/dashboard/dashboard_page.dart | 26 +- .../fragments/appoinment_fragment.dart | 232 +++++++++-- .../dashboard/fragments/home_fragment.dart | 34 +- .../widget/appointment_slider_widget.dart | 391 +++++++++++++++--- pubspec.lock | 64 +-- 12 files changed, 1213 insertions(+), 233 deletions(-) diff --git a/lib/config/provider_routes.dart b/lib/config/provider_routes.dart index 596ec56..ab27297 100644 --- a/lib/config/provider_routes.dart +++ b/lib/config/provider_routes.dart @@ -1,4 +1,5 @@ import 'package:car_provider_app/views/appoinments/appoinment_detail_list_page.dart'; +import 'package:car_provider_app/views/appoinments/merge_appointment_page.dart'; import 'package:car_provider_app/views/appoinments/update_appointment_page.dart'; import 'package:car_provider_app/views/requests/request_detail_page.dart'; import 'package:car_provider_app/views/requests/send_offer_page.dart'; @@ -24,11 +25,14 @@ import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/models/advertisment_models/ad_details_model.dart'; import 'package:flutter/material.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/views/advertisement/ads_detail_view.dart'; import 'package:mc_common_app/views/advertisement/create_ad_view.dart'; +import '../views/appoinments/add_new_service_appointment_page.dart'; +import '../views/appoinments/appointment_page.dart'; import '../views/dashboard/dashboard_page.dart'; class ProviderAppRoutes { @@ -40,8 +44,11 @@ class ProviderAppRoutes { static const String defineBranch = "/defineBranch"; //Appointments + static const String appointment = "/appointment"; static const String appointmentDetailList = "/appointmentDetailList"; static const String updateAppointmentPage = "/updateAppointmentPage"; + static const String addServiceInAppointment = "/addServiceInAppointment"; + static const String mergeAppointments = "/mergeAppointments"; //Requests static const String requestsDetailPage = "/requestsDetailPage"; @@ -84,8 +91,14 @@ class ProviderAppRoutes { ModalRoute.of(context)!.settings.arguments as BranchDetailModel), //Appointments - appointmentDetailList: (context) => const AppointmentDetailListPage(), - updateAppointmentPage: (context) => const UpdateAppointmentPage(), + appointment: (context) => AppointmentPage( + branch: + ModalRoute.of(context)!.settings.arguments as BranchDetailModel), + appointmentDetailList: (context) => AppointmentDetailListPage(), + updateAppointmentPage: (context) => UpdateAppointmentPage(), + addServiceInAppointment: (context) => AddNewServiceAppointmentPage( + ModalRoute.of(context)!.settings.arguments as AppointmentListModel), + mergeAppointments: (context) => MergeAppointmentListPage(), //Requests requestsDetailPage: (context) => const RequestDetailPage(), diff --git a/lib/main.dart b/lib/main.dart index 2e3e759..60d85d8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,13 +17,16 @@ import 'package:mc_common_app/config/dependencies.dart'; import 'package:mc_common_app/config/routes.dart'; import 'package:mc_common_app/models/general_models/post_params_model.dart'; import 'package:mc_common_app/repositories/ads_repo.dart'; +import 'package:mc_common_app/repositories/appointment_repo.dart'; import 'package:mc_common_app/repositories/common_repo.dart'; +import 'package:mc_common_app/repositories/provider_repo.dart'; import 'package:mc_common_app/repositories/request_repo.dart'; import 'package:mc_common_app/repositories/user_repo.dart'; import 'package:mc_common_app/services/common_services.dart'; import 'package:mc_common_app/theme/app_theme.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; +import 'package:mc_common_app/view_models/appointments_view_model.dart'; import 'package:mc_common_app/view_models/base_view_model.dart'; import 'package:mc_common_app/view_models/requests_view_model.dart'; import 'package:mc_common_app/view_models/user_view_model.dart'; @@ -68,7 +71,8 @@ Future main() async { ), ), ChangeNotifierProvider( - create: (_) => SubscriptionsVM(subscriptionRepo: injector.get()), + create: (_) => + SubscriptionsVM(subscriptionRepo: injector.get()), ), ChangeNotifierProvider( create: (_) => ItemsVM( @@ -95,6 +99,14 @@ Future main() async { // requestRepo: injector.get(), // ), // ), + ChangeNotifierProvider( + create: (_) => AppointmentsVM( + scheduleRepo: injector.get(), + providerRepo: injector.get(), + commonServices: injector.get(), + commonRepo: injector.get(), + ), + ), ], child: const MyApp(), ).setupLocale()); @@ -116,12 +128,19 @@ class MyApp extends StatelessWidget { injector.get().setAppType(AppType.provider); AppState().setPostParamsModel( PostParamsModel( - languageID: EasyLocalization.of(context)?.locale.languageCode == "ar" ? 1 : 2, + languageID: + EasyLocalization.of(context)?.locale.languageCode == "ar" + ? 1 + : 2, ), ); - ThemeData data = AppTheme.getTheme(isArabic: EasyLocalization.of(context)?.locale.languageCode == "ar"); + ThemeData data = AppTheme.getTheme( + isArabic: + EasyLocalization.of(context)?.locale.languageCode == "ar"); return MaterialApp( - theme: AppTheme.getTheme(isArabic: EasyLocalization.of(context)?.locale.languageCode == "ar"), + theme: AppTheme.getTheme( + isArabic: + EasyLocalization.of(context)?.locale.languageCode == "ar"), debugShowCheckedModeBanner: false, localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, diff --git a/lib/repositories/branch_repo.dart b/lib/repositories/branch_repo.dart index 8c247b1..8b98de2 100644 --- a/lib/repositories/branch_repo.dart +++ b/lib/repositories/branch_repo.dart @@ -10,8 +10,6 @@ import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/config/dependencies.dart'; import 'package:mc_common_app/models/general_models/m_response.dart'; - - import 'package:flutter/cupertino.dart'; import 'package:mc_common_app/models/provider_branches_models/profile/branch.dart'; import 'package:mc_common_app/models/provider_branches_models/profile/categroy.dart'; @@ -20,9 +18,18 @@ import 'package:mc_common_app/models/provider_branches_models/profile/services.d import 'package:mc_common_app/models/provider_branches_models/provider_model.dart'; abstract class BranchRepo { - Future createBranch(String branchName, String branchDescription, String cityId, String address, String latitude, String longitude); - - Future updateBranch(int id, String branchName, String branchDescription, String cityId, String address, String latitude, String longitude, {bool isNeedToDelete = true}); + Future createBranch(String branchName, String branchDescription, + String cityId, String address, String latitude, String longitude); + + Future updateBranch( + int id, + String branchName, + String branchDescription, + String cityId, + String address, + String latitude, + String longitude, + {bool isNeedToDelete = true}); Future fetchAllBranches(); @@ -30,11 +37,14 @@ abstract class BranchRepo { Future fetchServicesByCategoryId(String serviceCategoryId); + Future fetchProviderServices(String branchID, String serviceCategoryId); + Future createNewService(List> map); Future getServiceProviderDocument(dynamic userId); - Future serviceProviderDocumentsUpdate(List? documents); + Future serviceProviderDocumentsUpdate( + List? documents); Future getBranchAndServices(); @@ -42,7 +52,8 @@ abstract class BranchRepo { Future updateService(List> map); - Future getMatchedServices(int oldBranchId, int newBranchId, int categoryId); + Future getMatchedServices( + int oldBranchId, int newBranchId, int categoryId); Future duplicateItems(Map map); @@ -53,11 +64,14 @@ abstract class BranchRepo { Future assignDealerToBranch(Map map); Future removeDealerFromBranch(Map map); + + Future addNewServicesInAppointment(Map map); } class BranchRepoImp implements BranchRepo { @override - Future createBranch(String branchName, String branchDescription, String cityId, String address, String latitude, String longitude) async { + Future createBranch(String branchName, String branchDescription, + String cityId, String address, String latitude, String longitude) async { var postParams = { "serviceProviderID": AppState().getUser.data?.userInfo?.providerId ?? "", "branchName": branchName, @@ -70,23 +84,37 @@ class BranchRepoImp implements BranchRepo { }; String t = AppState().getUser.data!.accessToken ?? ""; debugPrint("token " + t); - return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.createProviderBranch, postParams, token: t); + return await injector.get().postJsonForObject( + (json) => MResponse.fromJson(json), + ApiConsts.createProviderBranch, + postParams, + token: t); } @override Future fetchAllBranches() async { - var postParams = {"ServiceProviderID": AppState().getUser.data?.userInfo?.providerId.toString() ?? ""}; + var postParams = { + "ServiceProviderID": + AppState().getUser.data?.userInfo?.providerId.toString() ?? "" + }; String t = AppState().getUser.data!.accessToken ?? ""; debugPrint("token " + t); - return await injector.get().getJsonForObject((json) => Branch.fromJson(json), ApiConsts.ServiceProviderBranchGet, queryParameters: postParams, token: t); + return await injector.get().getJsonForObject( + (json) => Branch.fromJson(json), ApiConsts.ServiceProviderBranchGet, + queryParameters: postParams, token: t); } @override Future fetchBranchCategory() async { - var postParams = {"ServiceProviderID": AppState().getUser.data?.userInfo?.providerId.toString() ?? ""}; + var postParams = { + "ServiceProviderID": + AppState().getUser.data?.userInfo?.providerId.toString() ?? "" + }; String t = AppState().getUser.data!.accessToken ?? ""; debugPrint("token " + t); - return await injector.get().getJsonForObject((json) => Category.fromJson(json), ApiConsts.ServiceCategory_Get, queryParameters: postParams, token: t); + return await injector.get().getJsonForObject( + (json) => Category.fromJson(json), ApiConsts.ServiceCategory_Get, + queryParameters: postParams, token: t); } @override @@ -94,13 +122,19 @@ class BranchRepoImp implements BranchRepo { var postParams = {"ServiceCategoryID": serviceCategoryId}; String t = AppState().getUser.data!.accessToken ?? ""; debugPrint("token " + t); - return await injector.get().getJsonForObject((json) => Services.fromJson(json), ApiConsts.Services_Get, queryParameters: postParams, token: t); + return await injector.get().getJsonForObject( + (json) => Services.fromJson(json), ApiConsts.Services_Get, + queryParameters: postParams, token: t); } @override Future createNewService(List> map) async { String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.ServiceProviderService_Create, map, token: t); + return await injector.get().postJsonForObject( + (json) => MResponse.fromJson(json), + ApiConsts.ServiceProviderService_Create, + map, + token: t); } @override @@ -110,13 +144,15 @@ class BranchRepoImp implements BranchRepo { }; String? token = AppState().getUser.data?.accessToken; debugPrint(token); - return await injector - .get() - .getJsonForObject((json) => Document.fromJson(json), ApiConsts.GetProviderDocument, queryParameters: queryParameters, token: AppState().getUser.data!.accessToken ?? ""); + return await injector.get().getJsonForObject( + (json) => Document.fromJson(json), ApiConsts.GetProviderDocument, + queryParameters: queryParameters, + token: AppState().getUser.data!.accessToken ?? ""); } @override - Future serviceProviderDocumentsUpdate(List? documents) async { + Future serviceProviderDocumentsUpdate( + List? documents) async { List> map = []; for (int i = 0; i < documents!.length; i++) { if (documents[i].document != null) { @@ -133,19 +169,36 @@ class BranchRepoImp implements BranchRepo { } String t = AppState().getUser.data!.accessToken ?? ""; debugPrint("token " + t); - return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.ServiceProviderDocument_Update, map, token: t); + return await injector.get().postJsonForObject( + (json) => MResponse.fromJson(json), + ApiConsts.ServiceProviderDocument_Update, + map, + token: t); } @override Future getBranchAndServices() async { - var postParams = {"serviceProviderID": AppState().getUser.data?.userInfo?.providerId.toString() ?? ""}; + var postParams = { + "serviceProviderID": + AppState().getUser.data?.userInfo?.providerId.toString() ?? "" + }; String t = AppState().getUser.data!.accessToken ?? ""; print("tokeen121 " + t); - return await injector.get().getJsonForObject((json) => ProviderModel.fromJson(json), ApiConsts.BranchesAndServices, queryParameters: postParams, token: t); + return await injector.get().getJsonForObject( + (json) => ProviderModel.fromJson(json), ApiConsts.BranchesAndServices, + queryParameters: postParams, token: t); } @override - Future updateBranch(int id, String branchName, String branchDescription, String cityId, String address, String latitude, String longitude, {bool isNeedToDelete = true}) async { + Future updateBranch( + int id, + String branchName, + String branchDescription, + String cityId, + String address, + String latitude, + String longitude, + {bool isNeedToDelete = true}) async { String lat = "0", long = "0"; try { lat = latitude.substring(0, 9); @@ -163,59 +216,109 @@ class BranchRepoImp implements BranchRepo { "isActive": isNeedToDelete }; String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.updateProviderBranch, postParams, token: t); + return await injector.get().postJsonForObject( + (json) => MResponse.fromJson(json), + ApiConsts.updateProviderBranch, + postParams, + token: t); } @override Future createService(List> map) async { String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.ServiceProviderService_Create, map, token: t); + return await injector.get().postJsonForObject( + (json) => MResponse.fromJson(json), + ApiConsts.ServiceProviderService_Create, + map, + token: t); } @override Future updateService(List> map) async { String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.ServiceProviderService_Update, map, token: t); + return await injector.get().postJsonForObject( + (json) => MResponse.fromJson(json), + ApiConsts.ServiceProviderService_Update, + map, + token: t); } @override - Future getMatchedServices(int oldBranchId, int newBranchId, int categoryId) async { + Future getMatchedServices( + int oldBranchId, int newBranchId, int categoryId) async { var postParams = { "ProviderBranchIDExisted": oldBranchId.toString(), "ProviderBranchIDNew": newBranchId.toString(), "CategoryID": categoryId.toString(), }; String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().getJsonForObject((json) => MResponse.fromJson(json), ApiConsts.getMatchedServices, queryParameters: postParams, token: t); + return await injector.get().getJsonForObject( + (json) => MResponse.fromJson(json), ApiConsts.getMatchedServices, + queryParameters: postParams, token: t); } @override Future duplicateItems(Map map) async { String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.duplicateItems, map, token: t); + return await injector.get().postJsonForObject( + (json) => MResponse.fromJson(json), ApiConsts.duplicateItems, map, + token: t); } @override Future getAllProviderDealers(Map map) async { String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().getJsonForObject((json) => MResponse.fromJson(json), ApiConsts.getAllProviderDealers, queryParameters: map, token: t); + return await injector.get().getJsonForObject( + (json) => MResponse.fromJson(json), ApiConsts.getAllProviderDealers, + queryParameters: map, token: t); } @override Future getBranchUsers(Map map) async { String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().getJsonForObject((json) => MResponse.fromJson(json), ApiConsts.getBranchUser, queryParameters: map, token: t); + return await injector.get().getJsonForObject( + (json) => MResponse.fromJson(json), ApiConsts.getBranchUser, + queryParameters: map, token: t); } @override Future assignDealerToBranch(Map map) async { String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.assignDealerToBranch, map, token: t); + return await injector.get().postJsonForObject( + (json) => MResponse.fromJson(json), ApiConsts.assignDealerToBranch, map, + token: t); } @override Future removeDealerFromBranch(Map map) async { String t = AppState().getUser.data!.accessToken ?? ""; - return await injector.get().postJsonForObject((json) => MResponse.fromJson(json), ApiConsts.removeDealerFromBranch, map, token: t); + return await injector.get().postJsonForObject( + (json) => MResponse.fromJson(json), + ApiConsts.removeDealerFromBranch, + map, + token: t); + } + + @override + Future fetchProviderServices(String branchID, String serviceCategoryId) async { + var postParams = { + "ServiceCategoryID": serviceCategoryId, + "ProviderBranchID": branchID, + }; + String t = AppState().getUser.data!.accessToken ?? ""; + debugPrint("token " + t); + return await injector.get().getJsonForObject( + (json) => Services.fromJson(json), ApiConsts.GetProviderServices, + queryParameters: postParams, token: t); + } + + @override + Future addNewServicesInAppointment(Map map) async { + String t = AppState().getUser.data!.accessToken ?? ""; + return await injector.get().postJsonForObject( + (json) => MResponse.fromJson(json), + ApiConsts.AddNewServicesInAppointment, + map, + token: t); } } diff --git a/lib/view_models/items_view_model.dart b/lib/view_models/items_view_model.dart index 0adfac7..036a075 100644 --- a/lib/view_models/items_view_model.dart +++ b/lib/view_models/items_view_model.dart @@ -9,6 +9,8 @@ import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/base_view_model.dart'; import 'package:file_picker/file_picker.dart'; +import '../views/settings/schedule/widgets/chips_picker_item.dart'; + class ItemsVM extends BaseVM { final ItemsRepo itemsRepo; final CommonAppServices commonServices; @@ -40,9 +42,25 @@ class ItemsVM extends BaseVM { return response; } - Future getServiceItems(int serviceId) async { + Future getServiceItems(int serviceId, + {List? list}) async { + serviceItems = null; setOnlyState(ViewState.busy); serviceItems = await itemsRepo.getServiceItems(serviceId); + if (list != null) { + for (var element in list) { + for (var innerElement in serviceItems!.data!) { + if (element.id == innerElement.id) { + innerElement.isUpdateOrSelected = true; + } + } + // serviceItems!.data!.where( + // (innerElement) => element.id == innerElement.id + // ? innerElement.isUpdateOrSelected = true + // : innerElement.isUpdateOrSelected = true, + // ); + } + } setState(ViewState.idle); return serviceItems; } diff --git a/lib/view_models/service_view_model.dart b/lib/view_models/service_view_model.dart index fd1fcaa..d395326 100644 --- a/lib/view_models/service_view_model.dart +++ b/lib/view_models/service_view_model.dart @@ -260,7 +260,6 @@ import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/base_view_model.dart'; import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; - class ServiceVM extends BaseVM { final BranchRepo branchRepo; final CommonAppServices commonServices; @@ -347,7 +346,8 @@ class ServiceVM extends BaseVM { setState(ViewState.idle); } - Future getAllCountriesList(BranchDetailModel? branchData, String countryCode) async { + Future getAllCountriesList( + BranchDetailModel? branchData, String countryCode) async { setState(ViewState.busy); resetValues(); country = await commonRepo.getAllCountries(); @@ -356,7 +356,9 @@ class ServiceVM extends BaseVM { if (element.id == branchData.countryID) { countryValue = DropValue( element.id ?? 0, - countryCode == "SA" ? (element.countryNameN ?? "") : (element.countryName ?? ""), + countryCode == "SA" + ? (element.countryNameN ?? "") + : (element.countryName ?? ""), element.countryCode ?? "", ); } @@ -364,7 +366,9 @@ class ServiceVM extends BaseVM { countryDropList.add( DropValue( element.id ?? 0, - countryCode == "SA" ? (element.countryNameN ?? "") : (element.countryName ?? ""), + countryCode == "SA" + ? (element.countryNameN ?? "") + : (element.countryName ?? ""), element.countryCode ?? "", ), ); @@ -375,7 +379,8 @@ class ServiceVM extends BaseVM { setState(ViewState.idle); } - Future getAllCities(BranchDetailModel? branchData, String countryCode) async { + Future getAllCities( + BranchDetailModel? branchData, String countryCode) async { setState(ViewState.busy); citiesDropList.clear(); cities = null; @@ -393,7 +398,9 @@ class ServiceVM extends BaseVM { cityId = branchData.cityId!; cityValue = DropValue( element.id ?? 0, - countryCode == "SA" ? (element.cityNameN ?? "") : (element.cityName ?? ""), + countryCode == "SA" + ? (element.cityNameN ?? "") + : (element.cityName ?? ""), element.id.toString() ?? "", ); } @@ -401,7 +408,9 @@ class ServiceVM extends BaseVM { citiesDropList.add( DropValue( element.id ?? 0, - countryCode == "SA" ? (element.cityNameN ?? "") : (element.cityName ?? ""), + countryCode == "SA" + ? (element.cityNameN ?? "") + : (element.cityName ?? ""), element.id.toString() ?? "", ), ); @@ -409,8 +418,10 @@ class ServiceVM extends BaseVM { setState(ViewState.idle); } - Future createBranch(String branchName, String branchDescription, String cityId, String address, String latitude, String longitude) async { - return await branchRepo.createBranch(branchName, branchDescription, cityId.toString(), address, latitude.toString(), longitude.toString()); + Future createBranch(String branchName, String branchDescription, + String cityId, String address, String latitude, String longitude) async { + return await branchRepo.createBranch(branchName, branchDescription, + cityId.toString(), address, latitude.toString(), longitude.toString()); } Future updateBranch( @@ -498,6 +509,24 @@ class ServiceVM extends BaseVM { setState(ViewState.idle); } + Future fetchProviderServices(String branchID, String categoryId) async { + servicesDropList = []; + services=null; + setState(ViewState.busy); + services = await branchRepo.fetchProviderServices(branchID, categoryId); + + for (var element in services!.data!) { + servicesDropList.add( + DropValue( + element.id ?? 0, + element.description ?? "N/A", + "", + ), + ); + } + setState(ViewState.idle); + } + Future createService(List> map) async { return await branchRepo.createService(map); } @@ -514,13 +543,16 @@ class ServiceVM extends BaseVM { List? matchedServices; bool isAllSelected = false; - Future getAllMatchedServices(int oldBranchId, int newBranchId, int categoryId) async { + Future getAllMatchedServices( + int oldBranchId, int newBranchId, int categoryId) async { matchedServices = null; - final MResponse response = await branchRepo.getMatchedServices(oldBranchId, newBranchId, categoryId); + final MResponse response = await branchRepo.getMatchedServices( + oldBranchId, newBranchId, categoryId); matchedServices = []; if (response.messageStatus == 1) { - matchedServices = List.from(response.data.map((x) => ServiceModel.fromJson(x))); + matchedServices = List.from( + response.data.map((x) => ServiceModel.fromJson(x))); } notifyListeners(); } @@ -560,8 +592,9 @@ class ServiceVM extends BaseVM { setState(ViewState.busy); MResponse response = await branchRepo.getAllProviderDealers(map); if (response.messageStatus == 1) { - allProviderDealersList=[]; - allProviderDealersList = List.from(response.data.map((x) => BranchUser.fromJson(x))); + allProviderDealersList = []; + allProviderDealersList = List.from( + response.data.map((x) => BranchUser.fromJson(x))); } setState(ViewState.idle); } @@ -570,8 +603,9 @@ class ServiceVM extends BaseVM { setState(ViewState.busy); MResponse response = await branchRepo.getBranchUsers(map); if (response.messageStatus == 1) { - branchUserList=[]; - branchUserList = List.from(response.data.map((x) => BranchUser.fromJson(x))); + branchUserList = []; + branchUserList = List.from( + response.data.map((x) => BranchUser.fromJson(x))); } setState(ViewState.idle); } @@ -585,4 +619,9 @@ class ServiceVM extends BaseVM { MResponse response = await branchRepo.removeDealerFromBranch(map); return response; } + + Future addNewServiceInAppointment(Map map) async { + MResponse response = await branchRepo.addNewServicesInAppointment(map); + return response; + } } diff --git a/lib/views/appoinments/appoinment_detail_list_page.dart b/lib/views/appoinments/appoinment_detail_list_page.dart index 5ea135f..faaf44e 100644 --- a/lib/views/appoinments/appoinment_detail_list_page.dart +++ b/lib/views/appoinments/appoinment_detail_list_page.dart @@ -1,11 +1,18 @@ -import 'package:car_provider_app/views/appoinments/widget/appointment_detail_list_widget.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/view_models/appointments_view_model.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; +import '../../config/provider_routes.dart'; +import '../dashboard/widget/appointment_slider_widget.dart'; +import 'package:provider/provider.dart'; + class AppointmentDetailListPage extends StatelessWidget { - const AppointmentDetailListPage({Key? key}) : super(key: key); + AppointmentDetailListPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -19,20 +26,60 @@ class AppointmentDetailListPage extends StatelessWidget { child: Column( children: [ Expanded( - child: ListView( - padding: const EdgeInsets.all(21), - children: [ - AppointmentDetailListWidget(), - 21.height, - AppointmentDetailListWidget(), - ], - ), + child: Consumer(builder: (BuildContext context, + AppointmentsVM appointmentsVM, Widget? child) { + if (appointmentsVM.state == ViewState.busy) { + return const Center(child: CircularProgressIndicator()); + } else { + return ListView.separated( + itemBuilder: (context, index) { + return AppointmentSliderWidget( + appointmentListModel: appointmentsVM + .myFilteredAppointments2[ + appointmentsVM.selectedAppointmentIndex] + .customerAppointmentList![index], + isNeedTotalPayment: true, + isNeedToShowAppointmentStatus: true, + isNeedToShowMergeStatus: true, + onTap: () { + appointmentsVM.selectedAppointmentId = + appointmentsVM + .myFilteredAppointments2[appointmentsVM + .selectedAppointmentIndex] + .customerAppointmentList![index] + .id ?? + 0; + appointmentsVM.selectedAppointmentSubIndex = index; + navigateWithName(context, + ProviderAppRoutes.updateAppointmentPage, + arguments: appointmentsVM + .myFilteredAppointments2[ + appointmentsVM.selectedAppointmentIndex] + .customerAppointmentList![index]); + }, + ); + }, + separatorBuilder: (context, snapchat) { + return 21.height; + }, + itemCount: appointmentsVM + .myFilteredAppointments2[ + appointmentsVM.selectedAppointmentIndex] + .customerAppointmentList! + .length, + padding: const EdgeInsets.all(21), + ); + } + }), ), ShowFillButton( title: "Merge Appointments", maxWidth: double.infinity, margin: const EdgeInsets.all(21), - onPressed: () {}, + onPressed: () { + navigateWithName( + context, ProviderAppRoutes.mergeAppointments); + }, ), ], )), diff --git a/lib/views/appoinments/update_appointment_page.dart b/lib/views/appoinments/update_appointment_page.dart index 888adbd..c847c8b 100644 --- a/lib/views/appoinments/update_appointment_page.dart +++ b/lib/views/appoinments/update_appointment_page.dart @@ -1,11 +1,27 @@ +import 'package:car_provider_app/config/provider_routes.dart'; import 'package:car_provider_app/views/appoinments/widget/appointment_detail_list_widget.dart'; +import 'package:car_provider_app/views/appoinments/widget/sheets.dart'; import 'package:flutter/material.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/config/dependencies.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/theme/colors.dart'; +import 'package:mc_common_app/utils/enums.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/appointments_view_model.dart'; +import 'package:mc_common_app/widgets/bottom_sheet.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:provider/provider.dart'; +import '../dashboard/widget/appointment_slider_widget.dart'; class UpdateAppointmentPage extends StatelessWidget { - const UpdateAppointmentPage({Key? key}) : super(key: key); + late AppointmentListModel appointmentListModel; + + UpdateAppointmentPage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -16,42 +32,284 @@ class UpdateAppointmentPage extends StatelessWidget { body: SizedBox( width: double.infinity, height: double.infinity, - child: Column( - children: [ - Expanded( - child: ListView( - padding: const EdgeInsets.all(21), - children: [ - AppointmentDetailListWidget( - isNeedClick: false, + child: Consumer(builder: (BuildContext context, + AppointmentsVM appointmentsVM, Widget? child) { + appointmentListModel = appointmentsVM + .myFilteredAppointments2[ + appointmentsVM.selectedAppointmentIndex] + .customerAppointmentList![ + appointmentsVM.selectedAppointmentSubIndex]; + if (appointmentsVM.state == ViewState.busy) { + return const Center(child: CircularProgressIndicator()); + } else { + return Column( + children: [ + Expanded( + child: ListView( + padding: const EdgeInsets.all(21), + children: [ + AppointmentSliderWidget( + appointmentListModel: appointmentListModel, + isNeedTotalPayment: true, + isNeedToShowItems: true, + isNeedToShowToMoreText: false, + onTap: () {}, + ), + 21.height, + ShowFillButton( + title: "+ Add New Service", + txtColor: MyColors.darkPrimaryColor, + isFilled: false, + onPressed: () { + navigateWithName( + context, + ProviderAppRoutes.addServiceInAppointment, + arguments: appointmentListModel, + ); + }, + ), + ], ), - ], - ), - ), - Padding( - padding: const EdgeInsets.all(21.0), - child: Row( - children: [ - Expanded( - child: ShowFillButton( - title: "Cancel", - maxWidth: double.infinity, - onPressed: () {}, - ), + ), + if (appointmentListModel.appointmentStatusEnum == + AppointmentStatusEnum.cancelled) + "Appointment is Canceled".toText(), + if (appointmentListModel.appointmentStatusEnum == + AppointmentStatusEnum.booked) + Column( + children: [ + showPayNowButton(context, appointmentsVM), + showCancelButton(context, appointmentsVM), + ], ), - 21.width, - Expanded( - child: ShowFillButton( - title: "Merge Appointments", - maxWidth: double.infinity, - onPressed: () {}, - ), + + + if (appointmentListModel.appointmentStatusEnum == + AppointmentStatusEnum.confirmed && + appointmentListModel.appointmentPaymentStatusEnum == + AppointmentPaymentStatusEnum.defaultStatus) + Column( + children: [ + showArrivedutton(context, appointmentsVM), + ], + ), + + if (appointmentListModel.appointmentStatusEnum == + AppointmentStatusEnum.arrived && + appointmentListModel.appointmentPaymentStatusEnum == + AppointmentPaymentStatusEnum.defaultStatus) + Column( + children: [ + showPayNowButton(context, appointmentsVM), + showPayLaterButton(context, appointmentsVM), + ], ), - ], - ), - ), - ], - )), + + if ((appointmentListModel.appointmentStatusEnum == + AppointmentStatusEnum.arrived || + appointmentListModel.appointmentStatusEnum == + AppointmentStatusEnum.workStarted) && + appointmentListModel.appointmentPaymentStatusEnum == + AppointmentPaymentStatusEnum.payNow) + "Waiting for the payment from the customer".toText(), + + if (appointmentListModel.appointmentStatusEnum == + AppointmentStatusEnum.arrived && + (appointmentListModel.appointmentPaymentStatusEnum == + AppointmentPaymentStatusEnum.paid || + appointmentListModel.appointmentPaymentStatusEnum == + AppointmentPaymentStatusEnum.payLater)) + Column( + children: [ + showWorkStartButton(context, appointmentsVM), + ], + ), + + if (appointmentListModel.appointmentStatusEnum == + AppointmentStatusEnum.workStarted && + appointmentListModel.appointmentPaymentStatusEnum == + AppointmentPaymentStatusEnum.payPartial) + Column( + children: [ + showPayLaterButton(context, appointmentsVM), + showPayNowButton(context, appointmentsVM), + ], + ), + // "Show Pay Now".toText(), + + if (appointmentListModel.appointmentStatusEnum == + AppointmentStatusEnum.workStarted && + appointmentListModel.appointmentPaymentStatusEnum == + AppointmentPaymentStatusEnum.paid) + Column( + children: [ + showCompleteButton(context, appointmentsVM), + ], + ), + // "Show Complete Button".toText(), + if(appointmentListModel.appointmentStatusEnum==AppointmentStatusEnum.workStarted&&appointmentListModel.appointmentPaymentStatusEnum==AppointmentPaymentStatusEnum.payLater) + showPayNowButton(context, appointmentsVM), + + if (appointmentListModel.appointmentStatusEnum == + AppointmentStatusEnum.visitCompleted) + "Appointment is completed".toText(), + + // Padding( + // padding: const EdgeInsets.all(21.0), + // child: Column( + // children: [ + // ShowFillButton( + // title: "Confirm Arrive", + // maxWidth: double.infinity, + // onPressed: () { + // showMyBottomSheet( + // context, + // child: ShowCollectPaymentSheet( + // onClickYes: () {}, + // onClickNo: () {}, + // ), + // ); + // }, + // ), + // 12.height, + // ShowFillButton( + // title: "Cancel Appointment", + // maxWidth: double.infinity, + // isFilled: false, + // txtColor: MyColors.redColor, + // borderColor: MyColors.redColor, + // onPressed: () { + // showMyBottomSheet(context, + // child: CancelAppointmentReasonSheet( + // onCancelClick: (String reason) {}, + // )); + // }, + // ), + // ], + // ), + // ), + ], + ); + } + })), + ); + } + + Widget showWorkStartButton( + BuildContext context, AppointmentsVM appointmentsVM) { + return ShowFillButton( + title: "Work Start", + maxWidth: double.infinity, + onPressed: () async { + Utils.showLoading(context); + await appointmentsVM.updateAppointmentStatus({ + "appointmentID": appointmentListModel.id.toString(), + "appointmentStatusID": 7 + }); + Utils.hideLoading(context); + // showMyBottomSheet( + // context, + // child: ShowCollectPaymentSheet( + // onClickYes: () {}, + // onClickNo: () {}, + // ), + // ); + }, ); } + + Widget showPayNowButton(BuildContext context, AppointmentsVM appointmentsVM) { + return ShowFillButton( + title: "Pay Now", + maxWidth: double.infinity, + onPressed: () async { + Utils.showLoading(context); + await appointmentsVM.updateAppointmentPaymentStatus({ + "appointmentID": appointmentListModel.id.toString(), + "appointmentServicePaymentStatusID": 2 + }); + Utils.hideLoading(context); + }, + ); + } + + Widget showArrivedutton(BuildContext context, AppointmentsVM appointmentsVM) { + return ShowFillButton( + title: "Arrived", + maxWidth: double.infinity, + onPressed: () async { + Utils.showLoading(context); + await appointmentsVM.updateAppointmentStatus({ + "appointmentID": appointmentListModel.id.toString(), + "appointmentStatusID": 3 + }); + Utils.hideLoading(context); + }, + ); + } + + Widget showPayLaterButton( + BuildContext context, AppointmentsVM appointmentsVM) { + return ShowFillButton( + title: "Pay Later", + maxWidth: double.infinity, + onPressed: () async { + Utils.showLoading(context); + await appointmentsVM.updateAppointmentPaymentStatus({ + "appointmentID": appointmentListModel.id.toString(), + "appointmentServicePaymentStatusID": 4 + }); + Utils.hideLoading(context); + }, + ); + } + + Widget showCompleteButton( + BuildContext context, AppointmentsVM appointmentsVM) { + return ShowFillButton( + title: "Complete", + maxWidth: double.infinity, + onPressed: () async { + Utils.showLoading(context); + await appointmentsVM.updateAppointmentStatus({ + "appointmentID": appointmentListModel.id.toString(), + "appointmentStatusID": 8 + }); + Utils.hideLoading(context); + }, + ); + } + + Widget showCancelButton(BuildContext context, AppointmentsVM appointmentsVM) { + return ShowFillButton( + title: "Cancel", + maxWidth: double.infinity, + isFilled: false, + txtColor: MyColors.redColor, + borderColor: MyColors.redColor, + onPressed: () { + showMyBottomSheet( + context, + child: ShowCollectPaymentSheet( + onClickYes: () {}, + onClickNo: () {}, + ), + ); + }, + ); + } + + Future _updateAppointment(BuildContext context, int branchId) async { + await context.read().getProviderMyAppointments({ + "ServiceProviderID": injector + .get() + .getUser + .data + ?.userInfo + ?.providerId + .toString() ?? + "0", + "ProviderBranchID": branchId.toString(), + }, isNeedToRebuild: true); + } } diff --git a/lib/views/dashboard/dashboard_page.dart b/lib/views/dashboard/dashboard_page.dart index ba71453..bd749a7 100644 --- a/lib/views/dashboard/dashboard_page.dart +++ b/lib/views/dashboard/dashboard_page.dart @@ -11,14 +11,18 @@ import 'package:car_provider_app/views/dashboard/widget/drawer_widget.dart'; import 'package:car_provider_app/views/settings/branch/branch_list_page.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/config/dependencies.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/view_models/ad_view_model.dart'; +import 'package:mc_common_app/view_models/appointments_view_model.dart'; import 'package:mc_common_app/view_models/requests_view_model.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/common_widgets/bottom_nav_bar.dart'; import 'package:provider/provider.dart'; +import 'fragments/branch_appointment_fragment.dart'; import 'fragments/home_fragment.dart'; import 'fragments/request_list_fragment.dart'; @@ -44,15 +48,23 @@ class _DashboardPageState extends State { } Future _onRefresh() async { - context.read().populateAdsFilterList(); + // context.read().populateAdsFilterList(); context.read().getBranchAndServices(); AdVM adVm = Provider.of(context, listen: false); - if (adVm.myAds.isEmpty) { - await adVm.getMyAds(); - } - if (adVm.exploreAds.isEmpty) { - await adVm.getExploreAds(); + AppointmentsVM appointmentsVM = + Provider.of(context, listen: false); + if (appointmentsVM.myAppointments.isEmpty) { + await appointmentsVM.getProviderMyAppointments({ + "ServiceProviderID": + injector.get().getUser.data?.userInfo?.providerId.toString() ?? "0" + }); } + // if (adVm.myAds.isEmpty) { + // await adVm.getMyAds(); + // } + // if (adVm.exploreAds.isEmpty) { + // await adVm.getExploreAds(); + // } } @override @@ -63,7 +75,7 @@ class _DashboardPageState extends State { dashboardVM.updateIndex(2); }, ), - AppointmentFragment( + BranchAppointmentFragment( onBackButtonTapped: () { dashboardVM.updateIndex(2); }, diff --git a/lib/views/dashboard/fragments/appoinment_fragment.dart b/lib/views/dashboard/fragments/appoinment_fragment.dart index 5367af5..a5c73fe 100644 --- a/lib/views/dashboard/fragments/appoinment_fragment.dart +++ b/lib/views/dashboard/fragments/appoinment_fragment.dart @@ -1,18 +1,52 @@ +import 'package:car_provider_app/config/provider_routes.dart'; import 'package:car_provider_app/views/dashboard/widget/appointment_slider_widget.dart'; +import 'package:mc_common_app/classes/app_state.dart'; +import 'package:mc_common_app/config/dependencies.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/theme/colors.dart'; +import 'package:mc_common_app/utils/date_helper.dart'; +import 'package:mc_common_app/utils/navigator.dart'; +import 'package:mc_common_app/utils/utils.dart'; +import 'package:mc_common_app/view_models/appointments_view_model.dart'; +import 'package:mc_common_app/views/appointments/widgets/customer_appointment_slider_widget.dart'; +import 'package:mc_common_app/widgets/common_widgets/categories_list.dart'; +import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:mc_common_app/widgets/tab/menu_tabs.dart'; 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/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart'; +import 'package:percent_indicator/percent_indicator.dart'; +import 'package:provider/provider.dart'; class AppointmentFragment extends StatelessWidget { + String date = ""; VoidCallback onBackButtonTapped; - AppointmentFragment({required this.onBackButtonTapped}); + AppointmentFragment({Key? key, required this.onBackButtonTapped}) + : super(key: key); + + GlobalKey refreshIndicatorKey = + GlobalKey(); + + Future _pullRefresh(BuildContext context) async { + await context.read().getProviderMyAppointments({ + "ServiceProviderID": injector + .get() + .getUser + .data + ?.userInfo + ?.providerId + .toString() ?? + "0" + }); + } @override Widget build(BuildContext context) { + context.read().setupProviderAppointmentFilter(); + date = DateHelper.formatAsDayMonthYear(DateTime.now()); return Scaffold( appBar: CustomAppBar( profileImageUrl: MyAssets.carBanner, @@ -29,38 +63,180 @@ class AppointmentFragment extends StatelessWidget { body: SizedBox( width: double.infinity, height: double.infinity, - child: SingleChildScrollView( - child: Column( - children: [ - 12.height, - SizedBox( - height: 35, - width: double.infinity, - child: MenuTabs( - 0, - [ - DropValue(0, "All Appointments", ""), - DropValue(1, "Booked", ""), - DropValue(2, "Confirmed", ""), - DropValue(3, "Arrived", ""), - ], - onSelect: (DropValue value) {}, + child: Consumer(builder: (BuildContext context, + AppointmentsVM appointmentsVM, Widget? child) { + return RefreshIndicator( + onRefresh: () => _pullRefresh(context), + key: refreshIndicatorKey, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: Column( + children: [ + progressWidget(context), + FiltersList( + filterList: appointmentsVM.appointmentsFilterOptions, + padding: const EdgeInsets.symmetric(horizontal: 18), + onFilterTapped: (index, selectedFilterId) { + appointmentsVM.applyFilterOnAppointmentsVM( + appointmentStatusEnum: + selectedFilterId.toAppointmentStatusEnum(), + ); + }, + ), + ListView.separated( + itemBuilder: (context, index) { + return AppointmentSliderWidget( + appointmentListModel: + appointmentsVM.myFilteredAppointments2[index], + isNeedTotalPayment: false, + onTap: () { + + navigateWithName( + context, + ProviderAppRoutes.appointmentDetailList, + arguments: appointmentsVM + .myFilteredAppointments2[index] + .customerAppointmentList, + ); + }, + ); + }, + separatorBuilder: (context, snapchat) { + return 21.height; + }, + itemCount: appointmentsVM.myFilteredAppointments2.length, + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + padding: const EdgeInsets.all(21), + ), + ], + ), + ), + ); + }), + ), + ); + } + + Widget progressWidget(BuildContext context) { + return Column( + children: [ + Row( + children: [ + Expanded( + child: "Slots Overview".toText( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Row( + children: [ + date.toText( + fontWeight: FontWeight.bold, + ), + const Icon( + Icons.keyboard_arrow_down_outlined, + size: 16, ), + ], + ) + .toContainer( + backgroundColor: MyColors.lightGreyEAColor, + borderRadius: 100, + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, ), - ListView( - padding: const EdgeInsets.all(21), - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, + ) + .onPress(() async { + date = await Utils.pickDateFromDatePicker( + context, + firstDate: null, + ); + context.read().notifyListeners(); + }), + ], + ), + 24.height, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Container( + width: 14, + height: 14, + color: MyColors.lightGreyEAColor, + ), + 4.width, + "Empty: ".toText( + fontSize: 8, + color: Colors.white, + ), + "8".toText( + fontSize: 9, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ], + ).toContainer( + backgroundColor: MyColors.darkIconColor, + ), + 8.height, + Row( + children: [ + Container( + width: 14, + height: 14, + color: MyColors.darkPrimaryColor, + ), + 4.width, + "Occupied: ".toText( + fontSize: 8, + color: Colors.white, + ), + "54".toText( + fontSize: 9, + fontWeight: FontWeight.bold, + color: Colors.white, + ), + ], + ).toContainer( + backgroundColor: MyColors.darkIconColor, + ), + ], + ), + CircularPercentIndicator( + radius: 60.0, + lineWidth: 12.0, + percent: 0.7, + circularStrokeCap: CircularStrokeCap.round, + center: Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - const AppointmentSliderWidget(), - 21.height, - const AppointmentSliderWidget(), + "Total Slots".toText( + fontSize: 13, + fontWeight: FontWeight.bold, + ), + "24".toText( + fontSize: 24, + fontWeight: FontWeight.bold, + ), ], ), - ], - ), - ), - ), + backgroundColor: MyColors.lightGreyEAColor, + progressColor: MyColors.darkPrimaryColor, + ), + ], + ) + ], + ).toWhiteContainer( + width: double.infinity, + pading: const EdgeInsets.all(12), + margin: const EdgeInsets.all(21), ); } } diff --git a/lib/views/dashboard/fragments/home_fragment.dart b/lib/views/dashboard/fragments/home_fragment.dart index 19e8f12..7d24f08 100644 --- a/lib/views/dashboard/fragments/home_fragment.dart +++ b/lib/views/dashboard/fragments/home_fragment.dart @@ -5,8 +5,11 @@ import 'package:mc_common_app/classes/consts.dart'; import 'package:mc_common_app/extensions/int_extensions.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/view_models/ad_view_model.dart'; +import 'package:mc_common_app/view_models/appointments_view_model.dart'; import 'package:mc_common_app/views/advertisement/ads_list.dart'; +import 'package:mc_common_app/views/appointments/widgets/customer_appointment_slider_widget.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/common_widgets/view_all_widget.dart'; @@ -17,10 +20,15 @@ import 'package:provider/provider.dart'; import '../widget/appointment_slider_widget.dart'; class HomeFragment extends StatelessWidget { - VoidCallback onTap,onNotificaitonClick; + VoidCallback onTap, onNotificaitonClick; RefreshCallback onRefresh; - HomeFragment({required this.onTap, required this.onRefresh,required this.onNotificaitonClick, Key? key}) : super(key: key); + HomeFragment( + {required this.onTap, + required this.onRefresh, + required this.onNotificaitonClick, + Key? key}) + : super(key: key); @override Widget build(BuildContext context) { @@ -64,7 +72,17 @@ class HomeFragment extends StatelessWidget { subTitle: 'View All', onSubtitleTapped: () {}, ).horPaddingMain(), - const AppointmentSliderWidget().horPaddingMain(), + // const AppointmentSliderWidget().horPaddingMain(), + CustomerAppointmentSliderWidget( + myUpComingAppointments: + context.read().myUpComingAppointments, + isNeedToShowEmptyMessage: true, + onAppointmentClick: (){ + // navigateWithName( + // context, ProviderAppRoutes.appointment, + // arguments: branches[index]); + }, + ), 21.height, ViewAllWidget( title: 'My Branches', @@ -95,7 +113,8 @@ class HomeFragment extends StatelessWidget { shouldShowAdStatus: true, isAdsFragment: false, adsList: adVM.myActiveAdsForHome, - scrollPhysics: const NeverScrollableScrollPhysics(), + scrollPhysics: + const NeverScrollableScrollPhysics(), ), ], ) @@ -115,9 +134,12 @@ class HomeFragment extends StatelessWidget { ).horPaddingMain(), BuildAdsList( shouldShowAdStatus: false, - adsList: adVM.exploreAds.length >= 3 ? adVM.exploreAds.take(3).toList() : adVM.exploreAds, + adsList: adVM.exploreAds.length >= 3 + ? adVM.exploreAds.take(3).toList() + : adVM.exploreAds, isAdsFragment: false, - scrollPhysics: const NeverScrollableScrollPhysics(), + scrollPhysics: + const NeverScrollableScrollPhysics(), ), ], ) diff --git a/lib/views/dashboard/widget/appointment_slider_widget.dart b/lib/views/dashboard/widget/appointment_slider_widget.dart index 4418158..71fb6fa 100644 --- a/lib/views/dashboard/widget/appointment_slider_widget.dart +++ b/lib/views/dashboard/widget/appointment_slider_widget.dart @@ -1,96 +1,353 @@ import 'package:car_provider_app/config/provider_routes.dart'; 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/models/appointments_models/appointment_list_model.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'; -import 'package:sizer/sizer.dart'; +import 'package:flutter_svg/flutter_svg.dart'; class AppointmentSliderWidget extends StatelessWidget { - const AppointmentSliderWidget({Key? key}) : super(key: key); + AppointmentListModel appointmentListModel; + bool isNeedTotalPayment; + bool isNeedToShowItems; + bool isNeedToShowToMoreText; + bool isSelectable; + bool isNeedToShowAppointmentStatus; + bool isNeedToShowMergeStatus; + Function()? onTap; + + AppointmentSliderWidget( + {required this.appointmentListModel, + required this.onTap, + this.isNeedTotalPayment = false, + this.isNeedToShowItems = false, + this.isNeedToShowToMoreText = true, + this.isSelectable = false, + this.isNeedToShowAppointmentStatus = false, + this.isNeedToShowMergeStatus = false, + Key? key}) + : super(key: key); + + List buildServicesFromAppointment( + {required AppointmentListModel appointmentListModel}) { + if (appointmentListModel.appointmentServicesList == null || + appointmentListModel.appointmentServicesList!.isEmpty) { + return [SizedBox()]; + } + + if (appointmentListModel.appointmentServicesList!.length == 1) { + List itemsList = []; + if (isNeedToShowItems) { + itemsList = List.generate( + appointmentListModel + .appointmentServicesList?.first.serviceItems?.length ?? + 0, + (index) => (appointmentListModel.appointmentServicesList?.first + .serviceItems?[index].name ?? + "") + .toString() + .toText( + color: MyColors.lightTextColor, + isBold: true, + ), + ); + } + return [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + showServices( + appointmentListModel + .appointmentServicesList![0].providerServiceDescription, + MyAssets.modificationsIcon, + ), + if (isNeedToShowItems) ...itemsList, + ], + ) + ]; + } + + List servicesList = List.generate( + isNeedToShowToMoreText + ? 2 + : appointmentListModel.appointmentServicesList?.length ?? 0, + (index) { + List itemsList = []; + if (isNeedToShowItems) { + itemsList = List.generate( + appointmentListModel + .appointmentServicesList?[index].serviceItems?.length ?? + 0, + (mIndex) => + (" - ${appointmentListModel.appointmentServicesList?[index].serviceItems?[mIndex].name ?? ""}") + .toString() + .toText( + color: MyColors.lightTextColor, + isBold: true, + ), + ); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + showServices( + appointmentListModel + .appointmentServicesList![index].providerServiceDescription, + MyAssets.modificationsIcon), + if (isNeedToShowItems) ...itemsList, + ], + ).paddingOnly(bottom: 4); + }, + ); + + if (isNeedToShowToMoreText && + appointmentListModel.appointmentServicesList!.length > 1) { + servicesList.add( + showServices( + "+ ${appointmentListModel.appointmentServicesList!.length - 1} More", + "", + isMoreText: true, + ), + ); + } + return servicesList; + } @override Widget build(BuildContext context) { - return Column( + return Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + if (isSelectable) + SizedBox( + child: const SizedBox( + width: 14, + height: 14, + ).toContainer( + borderRadius: 24, + marginAll: 4, + paddingAll: 0, + backgroundColor: (appointmentListModel.isSelected ?? false) + ? MyColors.primaryColor + : MyColors.greyButtonColor, + ), + ).toContainer( + borderRadius: 24, + isEnabledBorder: true, + paddingAll: 0, + borderWidget: 3, + borderColor: MyColors.primaryColor, + ), + if (isSelectable) 12.width, + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (isNeedToShowMergeStatus) + "Appointment Merged".toText(color: MyColors.white).toContainer( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 3), + borderRadius: 12, + backgroundColor: MyColors.greenColor, + ), + Row( mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - "Olaya Brach".toText( - fontSize: 12, - color: MyColors.darkTextColor, - isBold: true, - ), - "Abdullah Alhbas".toText( - fontSize: 14, - isBold: true, - ), - Row( - children: [ - "Appt. On:".toText( - color: MyColors.lightTextColor, - ), - 2.width, - "19-Mar-2023 11:48 AM".toText(), - ], + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + (appointmentListModel.branchName ?? "").toText( + fontSize: 12, + color: MyColors.darkTextColor, + isBold: true, + ), + (appointmentListModel.customerName ?? "").toText( + color: MyColors.black, isBold: true, fontSize: 16), + Row( + children: [ + "Phone:".toText( + color: MyColors.lightTextColor, + ), + 2.width, + appointmentListModel.customerMobileNum + .toString() + .toText( + fontSize: 12, + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Appt. On:".toText( + color: MyColors.lightTextColor, + ), + 2.width, + Expanded( + child: + "${appointmentListModel.duration ?? ""} ${appointmentListModel.appointmentDate!.toFormattedDateWithoutTime()}" + .toText( + fontSize: 12, + ), + ), + ], + ), + Row( + children: [ + "Location: ".toText( + color: MyColors.lightTextColor, + ), + 2.width, + appointmentListModel.appointmentType + .toString() + .toText( + fontSize: 12, + ), + ], + ), + ], + ), ), + if (!isNeedTotalPayment) + if (appointmentListModel.customerAppointmentList!.length > + 1) + "${appointmentListModel.customerAppointmentList!.length - 1}+ Appointments" + .toText( + fontSize: 8, + ) + .toContainer( + borderRadius: 15, + backgroundColor: MyColors.lightGreyEAColor, + padding: const EdgeInsets.symmetric( + vertical: 6, + horizontal: 12, + ), + ), + if (isNeedToShowAppointmentStatus) + (appointmentListModel.appointmentStatusEnum! + .getAppointmentNameFromEnum()) + .toText(color: MyColors.white) + .toContainer( + padding: const EdgeInsets.symmetric( + horizontal: 6, vertical: 3), + borderRadius: 12, + backgroundColor: MyColors.primaryColor, + ), ], ), - ), - "1+ Requests".toText(fontSize: 10).toContainer( - borderRadius: 15, - backgroundColor: MyColors.lightGreyEAColor, - padding: const EdgeInsets.symmetric( - vertical: 6, - horizontal: 12, - ), - ), - ], - ), - 8.height, - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Expanded( - child: Column( + 8.height, + Row( + crossAxisAlignment: CrossAxisAlignment.end, children: [ - showServices("Maintenance"), - 2.height, - showServices("Accessories and Modification"), + Expanded( + child: Column( + children: [ + ...buildServicesFromAppointment( + appointmentListModel: appointmentListModel), + if (isNeedTotalPayment) + Column( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 24.height, + "Total Amount".toText( + fontSize: 12, + color: MyColors.lightTextColor, + isBold: true, + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + appointmentListModel.totalAmount + .toString() + .toText(fontSize: 16, isBold: true), + 2.width, + "SAR:".toText( + color: MyColors.lightTextColor, + ), + ], + ), + ], + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 2.height, + "Remaining Amount".toText( + fontSize: 12, + color: MyColors.lightTextColor, + isBold: true, + ), + Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + appointmentListModel.remainingAmount + .toString() + .toText(fontSize: 16, isBold: true), + 2.width, + "SAR:".toText( + color: MyColors.lightTextColor, + ), + ], + ), + ], + ), + ], + ) + ], + ), + ), + if (!isNeedToShowItems) + const Icon( + Icons.arrow_forward, + ), ], ), - ), - const Icon( - Icons.arrow_forward, - ), - ], - ), + ], + ), + ) ], - ).toWhiteContainer( - width: double.infinity, - allPading: 12, - onTap: () { - navigateWithName(context, ProviderAppRoutes.appointmentDetailList); - }); + ).toWhiteContainer(width: double.infinity, allPading: 12, onTap: onTap); } - Widget showServices(String title) { + double calculateTotalPrice() { + double totalServiceListPrice = 0; + appointmentListModel.appointmentServicesList!.forEach((servicesElement) { + servicesElement.serviceItems!.forEach((itemsElement) { + totalServiceListPrice += double.parse(itemsElement.price ?? "0"); + }); + }); + + return totalServiceListPrice; + } + + Widget showServices(String title, String icon, {bool isMoreText = false}) { return Row( children: [ - const Icon( - Icons.ac_unit, - color: MyColors.primaryColor, - size: 18, - ), - 8.width, - title.toText( - fontSize: 14, - isBold: true, + if (icon != "") ...[ + SvgPicture.asset(icon), + 8.width, + ], + Flexible( + child: title.toText( + fontSize: 12, + isBold: true, + color: isMoreText ? MyColors.primaryColor : MyColors.black, + ), ), ], ); diff --git a/pubspec.lock b/pubspec.lock index e856ea9..48c1517 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.18.0" country_code_picker: dependency: transitive description: @@ -226,10 +226,10 @@ packages: dependency: "direct dev" description: name: flutter_lints - sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493 + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "2.0.3" flutter_localizations: dependency: transitive description: flutter @@ -425,10 +425,10 @@ packages: dependency: transitive description: name: intl - sha256: a3715e3bc90294e971cb7dc063fbf3cd9ee0ebf8604ffeafabd9e6f16abbdbe6 + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.18.0" + version: "0.18.1" js: dependency: transitive description: @@ -441,10 +441,10 @@ packages: dependency: transitive description: name: lints - sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.1.1" local_auth: dependency: transitive description: @@ -505,22 +505,22 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" mc_common_app: dependency: "direct main" description: - path: "C:/Users/mirza.shafique/AndroidStudioProjects/mc_common_app" + path: "/Users/mirzashafiq/AndroidStudioProjects/car_common_app" relative: false source: path version: "1.0.0+1" @@ -528,10 +528,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" nested: dependency: transitive description: @@ -628,6 +628,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.11.1" + percent_indicator: + dependency: transitive + description: + name: percent_indicator + sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c + url: "https://pub.dev" + source: hosted + version: "4.2.3" permission_handler: dependency: transitive description: @@ -813,10 +821,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" sqflite: dependency: transitive description: @@ -845,18 +853,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -901,10 +909,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.1" tuple: dependency: transitive description: @@ -1009,6 +1017,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + web: + dependency: transitive + description: + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + url: "https://pub.dev" + source: hosted + version: "0.3.0" web_socket_channel: dependency: transitive description: @@ -1042,5 +1058,5 @@ packages: source: hosted version: "6.1.0" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.10.0"