diff --git a/lib/api/api_client.dart b/lib/api/api_client.dart index e31ce3a..b87e842 100644 --- a/lib/api/api_client.dart +++ b/lib/api/api_client.dart @@ -81,8 +81,7 @@ class ApiClientImp implements ApiClient { var response = await postJsonForResponse(url, jsonObject, token: token, queryParameters: queryParameters, headers: headers0, retryTimes: retryTimes); try { if (!kReleaseMode) { - log("res121:${response.body}"); - log("res121:${response.statusCode}"); + log("statusCode:${response.statusCode}"); } var jsonData = jsonDecode(response.body); return factoryConstructor(jsonData); diff --git a/lib/classes/consts.dart b/lib/classes/consts.dart index 51f3faa..3c94efb 100644 --- a/lib/classes/consts.dart +++ b/lib/classes/consts.dart @@ -51,6 +51,7 @@ class ApiConsts { static String BranchesAndServices = "${baseUrlServices}api/ServiceProviders/ServiceProviderDetail_Get"; static String GetAllNearBranches = "${baseUrlServices}api/ServiceProviders/ServiceProviderBranchDetail_Get"; static String GetServiceItemAppointmentScheduleSlots = "${baseUrlServices}api/ServiceProviders/ServiceItemAppointmentScheduleSlots_Get"; + static String ServiceProvidersAppointmentCreate = "${baseUrlServices}api/ServiceProviders/ServiceProvidersAppointment_Create"; //Appointment APIs static String serviceProvidersAppointmentGet = "${baseUrlServices}api/ServiceProviders/ServiceProvidersAppointment_Get"; @@ -94,6 +95,9 @@ class ApiConsts { static String vehicleAdsSingleStepCreate = "${baseUrlServices}api/Advertisement/AdsSingleStep_Create"; static String vehicleAdsGet = "${baseUrlServices}api/Advertisement/Ads_Get"; static String myAdsReserveGet = "${baseUrlServices}api/Advertisement/AdsReserve_Get"; + static String reserveAdsBankDetailsGet = "${baseUrlServices}api/Advertisement/MCBankAccountAd_Get"; + static String adsUpdateStatus = "${baseUrlServices}api/Advertisement/Ads_UpdateStatus"; + static String deleteAd = "${baseUrlServices}api/Advertisement/Ads_Delete"; static String adsCarCheckupSPBranchScheduleSlotGet = "${baseUrlServices}api/Advertisement/AdsCarCheckupSPBranchScheduleSlot_Get"; static String adsPhotoOfficeAppointmentScheduleSlotGet = "${baseUrlServices}api/Advertisement/PhotoOfficeAppointmentScheduleSlot_Get"; diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 2f07f17..f3f1471 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -176,6 +176,8 @@ extension AdPostEnum on int { return AdPostStatus.buyingService; } else if (this == 11) { return AdPostStatus.reserveCancel; + } else if (this == -1) { + return AdPostStatus.allAds; } else { return AdPostStatus.pendingForPost; } @@ -338,17 +340,31 @@ extension DateTimeConversions on DateTime { } extension VehicleAdTypeEnum on int { - VehicleTypeEnum getVehicleTypeEnum() { + VehicleType toVehicleTypeEnum() { if (this == 1) { - return VehicleTypeEnum.car; + return VehicleType.car; } else if (this == 2) { - return VehicleTypeEnum.motorCycle; + return VehicleType.motorCycle; } else if (this == 3) { - return VehicleTypeEnum.golfCart; + return VehicleType.golfCart; } else if (this == 4) { - return VehicleTypeEnum.buggy; + return VehicleType.buggy; } else { - return VehicleTypeEnum.car; + return VehicleType.car; + } + } + + String toVehicleTypeString() { + if (this == 1) { + return "Car"; + } else if (this == 2) { + return "Motorcycle"; + } else if (this == 3) { + return "Golf Cart"; + } else if (this == 4) { + return "Buggy"; + } else { + return "Car"; } } } diff --git a/lib/models/advertisment_models/ad_details_model.dart b/lib/models/advertisment_models/ad_details_model.dart index 4c7f85f..f47b698 100644 --- a/lib/models/advertisment_models/ad_details_model.dart +++ b/lib/models/advertisment_models/ad_details_model.dart @@ -40,6 +40,7 @@ class AdDetailsModel { AdPostStatus? adPostStatus; AdReserveStatus? adReserveStatus; bool? isMyAd; + bool? isReservedByMe; String? phoneNo; String? whatsAppNo; CreatedByRoleEnum? createdByRoleEnum; @@ -78,6 +79,7 @@ class AdDetailsModel { this.adPostStatus, this.adReserveStatus, this.isMyAd, + this.isReservedByMe, this.phoneNo, this.whatsAppNo, this.createdByRoleEnum, @@ -127,12 +129,14 @@ class AdDetailsModel { modifiedOn = json['modifiedOn']; whatsAppNo = json['phoneNo']; modifiedOn = json['whatsAppNo']; + // adPostStatus = AdPostStatus.expired; adPostStatus = (json['statusID'] as int).toAdPostEnum(); //TODO: THIS ID SHOULD BE UPDATED! adReserveStatus = AdReserveStatus.defaultStatus; - createdByRoleEnum = CreatedByRoleEnum.admin; - // createdByRoleEnum = (json['createdByRole'] as int).toCreatedByRoleEnum(); + // createdByRoleEnum = CreatedByRoleEnum.admin; + createdByRoleEnum = (json['createdByRole'] as int).toCreatedByRoleEnum(); isMyAd = isMyAds; + isReservedByMe = false; } Map toJson() { diff --git a/lib/models/advertisment_models/vehicle_details_models.dart b/lib/models/advertisment_models/vehicle_details_models.dart index 905e43b..10779c7 100644 --- a/lib/models/advertisment_models/vehicle_details_models.dart +++ b/lib/models/advertisment_models/vehicle_details_models.dart @@ -59,14 +59,14 @@ class VehicleTypeModel { String? vehicleTypeName; String? vehicleTypeNameN; bool? isActive; - VehicleTypeEnum? vehicleTypeEnum; + VehicleType? vehicleTypeEnum; VehicleTypeModel({ this.id, this.vehicleTypeName, this.vehicleTypeNameN, this.isActive, - this.vehicleTypeEnum = VehicleTypeEnum.car, + this.vehicleTypeEnum = VehicleType.car, }); VehicleTypeModel.fromJson(Map json) { @@ -74,7 +74,7 @@ class VehicleTypeModel { vehicleTypeName = json['vehicleTypeName']; vehicleTypeNameN = json['vehicleTypeNameN']; isActive = json['isActive']; - vehicleTypeEnum = (json['id'] as int).getVehicleTypeEnum(); + vehicleTypeEnum = (json['id'] as int).toVehicleTypeEnum(); } Map toJson() { diff --git a/lib/models/generic_resp_model.dart b/lib/models/generic_resp_model.dart index 89d935d..3b87348 100644 --- a/lib/models/generic_resp_model.dart +++ b/lib/models/generic_resp_model.dart @@ -3,22 +3,26 @@ class GenericRespModel { this.data, this.messageStatus, this.totalItemsCount, + this.message, }); dynamic data; int? messageStatus; int? totalItemsCount; + String? message; factory GenericRespModel.fromJson(Map json) => GenericRespModel( data: json["data"], messageStatus: json["messageStatus"], totalItemsCount: json["totalItemsCount"], + message: json["message"], ); Map toJson() => { "data": data, "messageStatus": messageStatus, "totalItemsCount": totalItemsCount, + "message": message, }; } diff --git a/lib/models/service_schedule_model.dart b/lib/models/service_schedule_model.dart index b8081f5..dea952e 100644 --- a/lib/models/service_schedule_model.dart +++ b/lib/models/service_schedule_model.dart @@ -2,13 +2,23 @@ import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/models/services/item_model.dart'; import 'package:mc_common_app/models/widgets_models.dart'; +class CustomTimeDateSlotModel { + TimeSlotModel? date; + List? availableSlots; + + CustomTimeDateSlotModel({this.date, this.availableSlots}); +} + class ServiceAppointmentScheduleModel { List? serviceSlotList; List? serviceItemList; - String? selectedDate; - String? selectedTimeSlot; - List? availableDates; - List? availableTimeSlots; + int? selectedDateIndex; + + // String? selectedTimeSlot; + // List? availableDates; + // List? availableTimeSlots; + List? customTimeDateSlotList; + CustomTimeDateSlotModel? selectedCustomTimeDateSlotModel; double? amountToPay; double? amountTotal; double? amountRem; @@ -16,15 +26,41 @@ class ServiceAppointmentScheduleModel { ServiceAppointmentScheduleModel({ this.serviceSlotList, this.serviceItemList, - this.selectedDate, - this.selectedTimeSlot, - this.availableDates, - this.availableTimeSlots, + this.selectedDateIndex, + // this.selectedTimeSlot, + // this.availableDates, + // this.availableTimeSlots, + this.customTimeDateSlotList, + this.selectedCustomTimeDateSlotModel, this.amountToPay, this.amountTotal, this.amountRem, }); + List getFormattedDateTimeSlotPackage() { + List customTimeDateSlotList = []; + for (var element in serviceSlotList!) { + if (!isAlreadyThere(element.slotDate!, customTimeDateSlotList)) { + customTimeDateSlotList.add(CustomTimeDateSlotModel( + date: TimeSlotModel(slotId: element.id!, isSelected: false, date: element.slotDate!, allowAppointment: true), + availableSlots: getAvailableSlotsByDate(element.slotDate!), + )); + } + } + + return customTimeDateSlotList; + } + + List? getAvailableSlotsByDate(String date) { + List timeslots = []; + for (var element in serviceSlotList!) { + if (element.slotDate == date) { + timeslots.add(TimeSlotModel(slotId: element.id!, slot: element.startTime!, date: element.slotDate!, isSelected: false, allowAppointment: element.bookAppointment! < element.allowAppointment!)); + } + } + return timeslots; + } + List getFormattedSlotTimes() { var seenSlots = {}; @@ -33,31 +69,30 @@ class ServiceAppointmentScheduleModel { slot: slot.startTime!, slotId: slot.id!, isSelected: false, + date: slot.slotDate!.toFormattedDateWithoutTime(), ))) .toList(); List slotTime = []; for (var element in slotTimeData) { - slotTime.add(TimeSlotModel(isSelected: false, slotId: element.id!, slot: element.slotDate ?? "")); + slotTime.add(TimeSlotModel(isSelected: false, slotId: element.id!, slot: element.startTime ?? "", date: element.slotDate!.toFormattedDateWithoutTime() ?? "")); } return slotTime; } - List getFormattedSlotDates() { - var seenDates = {}; - - var slotDatesData = serviceSlotList! - .where((slot) => seenDates.add(TimeSlotModel( - slot: slot.slotDate!, - slotId: slot.id!, - isSelected: false, - ))) - .toList(); - List slotDates = []; - for (var element in slotDatesData) { - slotDates.add(TimeSlotModel(isSelected: false, slotId: element.id!, slot: element.slotDate!.toFormattedDateWithoutTime() ?? "")); + bool isAlreadyThere(String dateToFind, List slots) { + int index = slots.indexWhere((element) { + return element.date!.date == dateToFind; + }); + if (index == -1) { + return false; + } else { + return true; } + } + List getFormattedSlotDates() { + List slotDates = []; return slotDates; } @@ -77,12 +112,9 @@ class ServiceAppointmentScheduleModel { serviceItemList!.add(ItemData.fromJson(v)); }); } - + customTimeDateSlotList = getFormattedDateTimeSlotPackage(); amountToPay = json['amountToPay']; - selectedDate = ''; - selectedTimeSlot = ''; - availableDates = getFormattedSlotDates(); - availableTimeSlots = getFormattedSlotTimes(); + selectedDateIndex = null; amountTotal = json['amountTotal']; amountRem = json['amountRem']; } @@ -143,7 +175,7 @@ class ServiceSlotList { branchAppointmentScheduleID = json['branchAppointmentScheduleID']; branchAppointmentSchedule = json['branchAppointmentSchedule']; serviceProviderID = json['serviceProviderID']; - slotDate = json['slotDate']; + slotDate = (json['slotDate'] as String).toFormattedDateWithoutTime(); startTime = json['startTime']; endTime = json['endTime']; bookAppointment = json['bookAppointment']; @@ -177,4 +209,9 @@ class ServiceSlotList { data['modifiedOn'] = modifiedOn; return data; } + + @override + String toString() { + return 'ServiceSlotList{id: $id, branchAppointmentScheduleID: $branchAppointmentScheduleID, branchAppointmentSchedule: $branchAppointmentSchedule, serviceProviderID: $serviceProviderID, slotDate: $slotDate, startTime: $startTime, endTime: $endTime, bookAppointment: $bookAppointment, allowAppointment: $allowAppointment, slotDurationMinute: $slotDurationMinute, appointmentType: $appointmentType, isActive: $isActive, createdBy: $createdBy, createdOn: $createdOn, modifiedBy: $modifiedBy, modifiedOn: $modifiedOn}'; + } } diff --git a/lib/models/services/item_model.dart b/lib/models/services/item_model.dart index 2ea572c..8292282 100644 --- a/lib/models/services/item_model.dart +++ b/lib/models/services/item_model.dart @@ -45,6 +45,7 @@ class ItemData { final dynamic pictureUrl; final int? companyId; final int? serviceProviderServiceId; + final String? serviceProviderServiceDescription; final bool? isActive; final bool? isAllowAppointment; final bool? isAppointmentCompanyLoc; @@ -61,6 +62,7 @@ class ItemData { this.pictureUrl, this.companyId, this.serviceProviderServiceId, + this.serviceProviderServiceDescription, this.isActive, this.isAllowAppointment, this.isAppointmentCompanyLoc, @@ -78,6 +80,7 @@ class ItemData { pictureUrl: json["pictureUrl"], companyId: json["companyID"], serviceProviderServiceId: json["serviceProviderServiceID"], + serviceProviderServiceDescription: json["serviceProviderServiceDescription"], isActive: json["isActive"], isAllowAppointment: json["isAllowAppointment"], isAppointmentCompanyLoc: json["isAppointmentCompanyLoc"], diff --git a/lib/models/widgets_models.dart b/lib/models/widgets_models.dart index 4bf55eb..52ef1fb 100644 --- a/lib/models/widgets_models.dart +++ b/lib/models/widgets_models.dart @@ -6,7 +6,6 @@ class FilterListModel { FilterListModel({required this.id, required this.isSelected, required this.title}); } - class SelectionModel { String selectedOption; int selectedId; @@ -24,11 +23,20 @@ class SelectionModel { class TimeSlotModel { int slotId; bool isSelected; + bool allowAppointment; String slot; + String date; TimeSlotModel({ this.slot = "", this.slotId = 0, + this.date = "", this.isSelected = false, + this.allowAppointment = false, }); -} \ No newline at end of file + + @override + String toString() { + return 'TimeSlotModel{slotId: $slotId, isSelected: $isSelected, slot: $slot, date: $date}'; + } +} diff --git a/lib/packages/cutom_country_code_picker.dart b/lib/packages/cutom_country_code_picker.dart deleted file mode 100644 index 3e1aa11..0000000 --- a/lib/packages/cutom_country_code_picker.dart +++ /dev/null @@ -1,302 +0,0 @@ -import 'package:collection/collection.dart' show IterableExtension; -import 'package:country_code_picker/country_code_picker.dart'; -import 'package:flutter/material.dart'; - -class CustomCountryCodePicker extends StatefulWidget { - final bool isTextNeeded; - final ValueChanged? onChanged; - final ValueChanged? onInit; - final String? initialSelection; - final List favorite; - final TextStyle? textStyle; - final EdgeInsetsGeometry padding; - final bool showCountryOnly; - final InputDecoration searchDecoration; - final TextStyle? searchStyle; - final TextStyle? dialogTextStyle; - final WidgetBuilder? emptySearchBuilder; - final Function(CountryCode?)? builder; - final bool enabled; - final TextOverflow textOverflow; - final Icon closeIcon; - - /// Barrier color of ModalBottomSheet - final Color? barrierColor; - - /// Background color of ModalBottomSheet - final Color? backgroundColor; - - /// BoxDecoration for dialog - final BoxDecoration? boxDecoration; - - /// the size of the selection dialog - final Size? dialogSize; - - /// Background color of selection dialog - final Color? dialogBackgroundColor; - - /// used to customize the country list - final List? countryFilter; - - /// shows the name of the country instead of the dialcode - final bool showOnlyCountryWhenClosed; - - /// aligns the flag and the Text left - /// - /// additionally this option also fills the available space of the widget. - /// this is especially useful in combination with [showOnlyCountryWhenClosed], - /// because longer country names are displayed in one line - final bool alignLeft; - - /// shows the flag - final bool showFlag; - - final bool hideMainText; - - final bool? showFlagMain; - - final bool? showFlagDialog; - - /// Width of the flag images - final double flagWidth; - - /// Use this property to change the order of the options - final Comparator? comparator; - - /// Set to true if you want to hide the search part - final bool hideSearch; - - /// Set to true if you want to show drop down button - final bool showDropDownButton; - - /// [BoxDecoration] for the flag image - final Decoration? flagDecoration; - - /// An optional argument for injecting a list of countries - /// with customized codes. - final List> countryList; - - const CustomCountryCodePicker({ - this.isTextNeeded = true, - this.onChanged, - this.onInit, - this.initialSelection, - this.favorite = const [], - this.textStyle, - this.padding = const EdgeInsets.all(8.0), - this.showCountryOnly = false, - this.searchDecoration = const InputDecoration(), - this.searchStyle, - this.dialogTextStyle, - this.emptySearchBuilder, - this.showOnlyCountryWhenClosed = false, - this.alignLeft = false, - this.showFlag = true, - this.showFlagDialog, - this.hideMainText = false, - this.showFlagMain, - this.flagDecoration, - this.builder, - this.flagWidth = 32.0, - this.enabled = true, - this.textOverflow = TextOverflow.ellipsis, - this.barrierColor, - this.backgroundColor, - this.boxDecoration, - this.comparator, - this.countryFilter, - this.hideSearch = false, - this.showDropDownButton = false, - this.dialogSize, - this.dialogBackgroundColor, - this.closeIcon = const Icon(Icons.close), - this.countryList = codes, - Key? key, - }) : super(key: key); - - @override - // ignore: no_logic_in_create_state - State createState() { - List> jsonList = countryList; - - List elements = jsonList.map((json) => CountryCode.fromJson(json)).toList(); - - if (comparator != null) { - elements.sort(comparator); - } - - if (countryFilter != null && countryFilter!.isNotEmpty) { - final uppercaseCustomList = countryFilter!.map((criteria) => criteria.toUpperCase()).toList(); - elements = elements.where((criteria) => uppercaseCustomList.contains(criteria.code) || uppercaseCustomList.contains(criteria.name) || uppercaseCustomList.contains(criteria.dialCode)).toList(); - } - - return CustomCountryCodePickerState(elements); - } -} - -class CustomCountryCodePickerState extends State { - CountryCode? selectedItem; - List elements = []; - List favoriteElements = []; - - CustomCountryCodePickerState(this.elements); - - @override - Widget build(BuildContext context) { - Widget internalWidget; - if (widget.builder != null) { - internalWidget = InkWell( - onTap: showCountryCodePickerDialog, - child: widget.builder!(selectedItem), - ); - } else { - internalWidget = TextButton( - onPressed: widget.enabled ? showCountryCodePickerDialog : null, - child: Padding( - padding: widget.padding, - child: Flex( - direction: Axis.horizontal, - mainAxisSize: MainAxisSize.min, - children: [ - if (widget.showFlagMain != null ? widget.showFlagMain! : widget.showFlag) - Flexible( - flex: widget.alignLeft ? 0 : 1, - fit: widget.alignLeft ? FlexFit.tight : FlexFit.loose, - child: Container( - clipBehavior: widget.flagDecoration == null ? Clip.none : Clip.hardEdge, - decoration: widget.flagDecoration, - margin: widget.alignLeft ? const EdgeInsets.only(right: 16.0, left: 8.0) : const EdgeInsets.only(right: 16.0), - child: Image.asset( - selectedItem!.flagUri!, - package: 'country_code_picker', - width: widget.flagWidth, - ), - ), - ), - if (!widget.hideMainText) - Flexible( - fit: widget.alignLeft ? FlexFit.tight : FlexFit.loose, - child: Text( - widget.showOnlyCountryWhenClosed ? selectedItem!.toCountryStringOnly() : selectedItem.toString(), - style: widget.textStyle ?? Theme.of(context).textTheme.labelLarge, - overflow: widget.textOverflow, - ), - ), - if (widget.showDropDownButton) - Flexible( - flex: widget.alignLeft ? 0 : 1, - fit: widget.alignLeft ? FlexFit.tight : FlexFit.loose, - child: Padding( - padding: widget.alignLeft ? const EdgeInsets.only(right: 16.0, left: 8.0) : const EdgeInsets.only(right: 16.0), - child: Icon( - Icons.arrow_drop_down, - color: Colors.grey, - size: widget.flagWidth, - )), - ), - ], - ), - ), - ); - } - return internalWidget; - } - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - - elements = elements.map((element) => element.localize(context)).toList(); - _onInit(selectedItem); - } - - @override - void didUpdateWidget(CustomCountryCodePicker oldWidget) { - super.didUpdateWidget(oldWidget); - - if (oldWidget.initialSelection != widget.initialSelection) { - if (widget.initialSelection != null) { - selectedItem = elements.firstWhere( - (criteria) => - (criteria.code!.toUpperCase() == widget.initialSelection!.toUpperCase()) || - (criteria.dialCode == widget.initialSelection) || - (criteria.name!.toUpperCase() == widget.initialSelection!.toUpperCase()), - orElse: () => elements[0]); - } else { - selectedItem = elements[0]; - } - _onInit(selectedItem); - } - } - - @override - void initState() { - super.initState(); - - if (widget.initialSelection != null) { - selectedItem = elements.firstWhere( - (item) => - (item.code!.toUpperCase() == widget.initialSelection!.toUpperCase()) || - (item.dialCode == widget.initialSelection) || - (item.name!.toUpperCase() == widget.initialSelection!.toUpperCase()), - orElse: () => elements[0]); - } else { - selectedItem = elements[0]; - } - - favoriteElements = elements - .where((item) => - widget.favorite.firstWhereOrNull((criteria) => item.code!.toUpperCase() == criteria.toUpperCase() || item.dialCode == criteria || item.name!.toUpperCase() == criteria.toUpperCase()) != - null) - .toList(); - } - - void showCountryCodePickerDialog() async { - final item = await showDialog( - barrierColor: widget.barrierColor ?? Colors.grey.withOpacity(0.5), - context: context, - builder: (context) => Center( - child: Dialog( - child: SelectionDialog( - elements, - favoriteElements, - showCountryOnly: widget.showCountryOnly, - emptySearchBuilder: widget.emptySearchBuilder, - searchDecoration: widget.searchDecoration, - searchStyle: widget.searchStyle, - textStyle: widget.dialogTextStyle, - boxDecoration: widget.boxDecoration, - showFlag: widget.showFlagDialog ?? widget.showFlag, - flagWidth: widget.flagWidth, - size: widget.dialogSize, - backgroundColor: widget.dialogBackgroundColor, - barrierColor: widget.barrierColor, - hideSearch: widget.hideSearch, - closeIcon: widget.closeIcon, - flagDecoration: widget.flagDecoration, - ), - ), - ), - ); - - if (item != null) { - setState(() { - selectedItem = item; - }); - - _publishSelection(item); - } - } - - void _publishSelection(CountryCode countryCode) { - if (widget.onChanged != null) { - widget.onChanged!(countryCode); - } - } - - void _onInit(CountryCode? countryCode) { - if (widget.onInit != null) { - widget.onInit!(countryCode); - } - } -} diff --git a/lib/repositories/ads_repo.dart b/lib/repositories/ads_repo.dart index fb54c80..2dbd80c 100644 --- a/lib/repositories/ads_repo.dart +++ b/lib/repositories/ads_repo.dart @@ -53,6 +53,10 @@ abstract class AdsRepo { Future> getMyReservedAds(); Future> getMyAds(); + + Future updateAdStatus({required int adId, required AdPostStatus adStatusToUpdate}); + + Future deleteAd({required int adId}); } class AdsRepoImp implements AdsRepo { @@ -374,4 +378,40 @@ class AdsRepoImp implements AdsRepo { List vehicleAdsDetails = List.generate(adsGenericModel.data.length, (index) => AdDetailsModel.fromJson(adsGenericModel.data[index], true)); return vehicleAdsDetails; } + + @override + Future updateAdStatus({required int adId, required AdPostStatus adStatusToUpdate}) async { + var postParams = { + "id": adId, + "status": adStatusToUpdate.getIdFromAdPostStatusEnum().toString(), + "comment": "", + }; + + String token = appState.getUser.data!.accessToken ?? ""; + GenericRespModel adsGenericModel = await apiClient.postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.adsUpdateStatus, + postParams, + token: token, + ); + + return Future.value(adsGenericModel); + } + + @override + Future deleteAd({required int adId}) async { + var postParams = { + "adID": adId, + }; + + String token = appState.getUser.data!.accessToken ?? ""; + GenericRespModel adsGenericModel = await apiClient.postJsonForObject( + (json) => GenericRespModel.fromJson(json), + ApiConsts.deleteAd, + postParams, + token: token, + ); + + return Future.value(adsGenericModel); + } } diff --git a/lib/utils/AppPermissionHandler.dart b/lib/utils/AppPermissionHandler.dart index 287e645..56d9082 100644 --- a/lib/utils/AppPermissionHandler.dart +++ b/lib/utils/AppPermissionHandler.dart @@ -1,7 +1,7 @@ import 'package:permission_handler/permission_handler.dart'; -import 'dialogs.dart'; +import 'dialogs_and_bottomsheets.dart'; enum ConfirmAction { CANCEL, ACCEPT } diff --git a/lib/utils/dialogs.dart b/lib/utils/dialogs.dart deleted file mode 100644 index 46409c5..0000000 --- a/lib/utils/dialogs.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:mc_common_app/utils/AppPermissionHandler.dart'; - - - -Future showConfirmDialogs( - context, msg, positiveText, negativeText) async { - return showDialog( - context: context, - barrierDismissible: false, - builder: (context) { - return AlertDialog( - backgroundColor: Colors.white, - title: Text(msg, style: TextStyle(fontSize: 16)), - actions: [ - TextButton( - child: Text( - negativeText, - ), - onPressed: () { - Navigator.of(context).pop(ConfirmAction.CANCEL); - }, - ), - TextButton( - child: Text( - positiveText, - ), - onPressed: () { - Navigator.of(context).pop(ConfirmAction.ACCEPT); - }, - ) - ], - ); - }, - ); -} diff --git a/lib/utils/dialogs_and_bottomsheets.dart b/lib/utils/dialogs_and_bottomsheets.dart new file mode 100644 index 0000000..7d1f06f --- /dev/null +++ b/lib/utils/dialogs_and_bottomsheets.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; +import 'package:mc_common_app/extensions/int_extensions.dart'; +import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/utils/AppPermissionHandler.dart'; +import 'package:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; + +Future showConfirmDialogs(context, msg, positiveText, negativeText) async { + return showDialog( + context: context, + barrierDismissible: false, + builder: (context) { + return AlertDialog( + backgroundColor: Colors.white, + title: Text(msg, style: TextStyle(fontSize: 16)), + actions: [ + TextButton( + child: Text( + negativeText, + ), + onPressed: () { + Navigator.of(context).pop(ConfirmAction.CANCEL); + }, + ), + TextButton( + child: Text( + positiveText, + ), + onPressed: () { + Navigator.of(context).pop(ConfirmAction.ACCEPT); + }, + ) + ], + ); + }, + ); +} + +void actionConfirmationBottomSheet({required BuildContext context, required Widget title, required subtitle, required Widget actionButtonYes, required Widget actionButtonNo}) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: true, + builder: (BuildContext context) { + return InfoBottomSheet( + title: title, + description: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "$subtitle".toText(fontSize: 16), + 25.height, + Row( + children: [ + actionButtonYes, + 20.width, + actionButtonNo, + ], + ), + 19.height, + ], + )); + }, + ); +} diff --git a/lib/utils/enums.dart b/lib/utils/enums.dart index 2e45e3c..0889c93 100644 --- a/lib/utils/enums.dart +++ b/lib/utils/enums.dart @@ -5,7 +5,7 @@ // unverified, // } -enum VehicleTypeEnum { +enum VehicleType { car, motorCycle, golfCart, @@ -39,9 +39,10 @@ enum AdPostStatus { reserved, buyingService, reserveCancel, + allAds, } -enum PaymentMethodsEnum { +enum PaymentMethods { mada, visa, applePay, @@ -49,15 +50,16 @@ enum PaymentMethodsEnum { tamara, } -enum PaymentTypesEnum { +enum PaymentTypes { subscription, appointment, ads, adReserve, request, + extendAds, } -enum AdCreationStepsEnum { +enum AdCreationSteps { vehicleDetails, damageParts, adDuration, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 3ba8337..b116951 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -2,7 +2,6 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:mc_common_app/utils/date_helper.dart'; import 'package:path/path.dart' as p; @@ -14,7 +13,8 @@ import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/widgets/loading_dialog.dart'; -import 'package:path/path.dart' as p; +import 'package:url_launcher/url_launcher.dart'; +import 'package:url_launcher/url_launcher_string.dart'; class Utils { static bool _isLoadingVisible = false; @@ -26,6 +26,24 @@ class Utils { msg: message, toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM, timeInSecForIosWeb: 2, backgroundColor: Colors.black54, textColor: Colors.white, fontSize: 16.0); } + static Future openNumberViaCaller({required String phoneNumber}) async { + Uri phoneLaunchUrl = Uri.parse('tel:$phoneNumber'); + + if (await canLaunchUrl(phoneLaunchUrl)) { + try { + await launchUrl(phoneLaunchUrl); + } catch (e) { + await launchUrl(phoneLaunchUrl); + } + } + } + + static Future openNumberViaWhatsApp({required String phoneNumber}) async { + final url = 'https://wa.me/$phoneNumber?text='; + + await launchUrlString(url, mode: LaunchMode.externalApplication); + } + static Future pickDateFromDatePicker(BuildContext context, {DateTime? firstDate, DateTime? lastDate}) async { DateTime? pickedDate = await showDatePicker( context: context, @@ -110,7 +128,7 @@ class Utils { hexColor = hexColor.toUpperCase().replaceAll('#', ''); if (hexColor.length == 6) { - hexColor = 'FF' + hexColor; + hexColor = 'FF$hexColor'; } return Color(int.parse(hexColor, radix: 16)); @@ -200,6 +218,7 @@ class Utils { case AdPostStatus.buyingService: case AdPostStatus.reserveCancel: + case AdPostStatus.allAds: return MyColors.greenColor; } } diff --git a/lib/view_models/ad_view_model.dart b/lib/view_models/ad_view_model.dart index 31076db..4564a77 100644 --- a/lib/view_models/ad_view_model.dart +++ b/lib/view_models/ad_view_model.dart @@ -33,7 +33,7 @@ class AdVM extends BaseVM { AdVM({required this.commonServices, required this.commonRepo, required this.adsRepo}); - AdCreationStepsEnum currentProgressStep = AdCreationStepsEnum.vehicleDetails; + AdCreationSteps currentProgressStep = AdCreationSteps.vehicleDetails; VehicleDetailsModel? vehicleDetails; @@ -142,7 +142,7 @@ class AdVM extends BaseVM { isExploreAdsTapped = value; //To show the Active Ads - applyFilterOnMyAds(index: 0, adPostStatusEnum: AdPostStatus.active); + applyFilterOnMyAds(index: 0, adPostStatusEnum: AdPostStatus.allAds); applyFilterOnExploreAds(index: 0, createdByRoleFilter: CreatedByRoleEnum.allAds); // if (value) { // await getExploreAds(); @@ -163,11 +163,14 @@ class AdVM extends BaseVM { ]; myAdsFilterOptions = [ - // FilterListModel(title: "All Ads", isSelected: true, id: -1), + FilterListModel(title: "All Ads", isSelected: true, id: -1), FilterListModel(title: "Active", isSelected: false, id: 6), FilterListModel(title: "Pending For Review", isSelected: false, id: 1), FilterListModel(title: "Pending For Payment", isSelected: false, id: 2), + FilterListModel(title: "Sold", isSelected: false, id: 8), + FilterListModel(title: "Deactivated", isSelected: false, id: 4), FilterListModel(title: "Reserved", isSelected: false, id: 9), + FilterListModel(title: "Expired", isSelected: false, id: 7), FilterListModel(title: "Rejected", isSelected: false, id: 3), FilterListModel(title: "Pending For Post", isSelected: false, id: 5), ]; @@ -210,6 +213,9 @@ class AdVM extends BaseVM { if (index == 3 && adPostStatusEnum.getIdFromAdPostStatusEnum() == 9) { selectedIds = myReservedAdsRespModel.map((component) => component.adsID).toList(); myAdsFilteredList = myAds.where((element) => selectedIds.contains(element.id)).toList(); + for (var ad in myAdsFilteredList) { + ad.isReservedByMe = true; + } notifyListeners(); return; } @@ -259,6 +265,54 @@ class AdVM extends BaseVM { notifyListeners(); } + Future markAdAsSold(BuildContext context, {required int adId}) async { + Utils.showLoading(context); + GenericRespModel respModel = await adsRepo.updateAdStatus(adId: adId, adStatusToUpdate: AdPostStatus.sold); + + if (respModel.messageStatus != 1) { + Utils.hideLoading(context); + Utils.showToast(respModel.message ?? "Something went wrong!"); + return; + } + Utils.hideLoading(context); + Utils.showToast("A has been marked as sold successfully!"); + updateIsExploreAds(false); + applyFilterOnMyAds(index: 1, adPostStatusEnum: AdPostStatus.sold); //pending for review + navigateReplaceWithName(context, AppRoutes.dashboard); + } + + Future deleteMyAd(BuildContext context, {required int adId}) async { + Utils.showLoading(context); + GenericRespModel respModel = await adsRepo.deleteAd(adId: adId); + + if (respModel.messageStatus != 1) { + Utils.hideLoading(context); + Utils.showToast(respModel.message ?? "Something went wrong!"); + return; + } + Utils.hideLoading(context); + Utils.showToast("A has been deleted successfully!"); + updateIsExploreAds(false); + applyFilterOnMyAds(index: 1, adPostStatusEnum: AdPostStatus.active); //pending for review + navigateReplaceWithName(context, AppRoutes.dashboard); + } + + Future deactivateTheAd(BuildContext context, {required int adId}) async { + Utils.showLoading(context); + GenericRespModel respModel = await adsRepo.updateAdStatus(adId: adId, adStatusToUpdate: AdPostStatus.cancelled); + + if (respModel.messageStatus != 1) { + Utils.hideLoading(context); + Utils.showToast(respModel.message ?? "Something went wrong!"); + return; + } + Utils.hideLoading(context); + Utils.showToast("A has been deactivated successfully!"); + updateIsExploreAds(false); + applyFilterOnMyAds(index: 1, adPostStatusEnum: AdPostStatus.cancelled); //pending for review + navigateReplaceWithName(context, AppRoutes.dashboard); + } + bool isFetchingLists = false; bool isCountryFetching = false; @@ -281,7 +335,7 @@ class AdVM extends BaseVM { // } getVehicleBrandsByVehicleTypeId() async { - if (vehicleTypeId.selectedId == -1) { + if (vehicleTypeId.selectedId == -1 || vehicleBrands.isNotEmpty) { return; } vehicleBrands = await adsRepo.getVehicleBrands(vehicleTypeId: vehicleTypeId.selectedId); @@ -491,6 +545,13 @@ class AdVM extends BaseVM { notifyListeners(); } + SelectionModel vehicleExtendAdDurationId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); + + void updateVehicleExtendAdDurationId(SelectionModel id) { + vehicleAdDurationId = id; + notifyListeners(); + } + SelectionModel vehicleAdReservableId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); void updateVehicleAdReservableId(SelectionModel id) { @@ -812,20 +873,20 @@ class AdVM extends BaseVM { void onBackButtonPressed(BuildContext context) { switch (currentProgressStep) { - case AdCreationStepsEnum.vehicleDetails: + case AdCreationSteps.vehicleDetails: resetValues(); pop(context); break; - case AdCreationStepsEnum.damageParts: - currentProgressStep = AdCreationStepsEnum.vehicleDetails; + case AdCreationSteps.damageParts: + currentProgressStep = AdCreationSteps.vehicleDetails; notifyListeners(); break; - case AdCreationStepsEnum.adDuration: - currentProgressStep = AdCreationStepsEnum.damageParts; + case AdCreationSteps.adDuration: + currentProgressStep = AdCreationSteps.damageParts; notifyListeners(); break; - case AdCreationStepsEnum.reviewAd: - currentProgressStep = AdCreationStepsEnum.adDuration; + case AdCreationSteps.reviewAd: + currentProgressStep = AdCreationSteps.adDuration; notifyListeners(); break; } @@ -833,28 +894,28 @@ class AdVM extends BaseVM { void updateCurrentStep(BuildContext context) async { switch (currentProgressStep) { - case AdCreationStepsEnum.vehicleDetails: + case AdCreationSteps.vehicleDetails: if (isVehicleDetailsValidated()) { - currentProgressStep = AdCreationStepsEnum.damageParts; + currentProgressStep = AdCreationSteps.damageParts; getVehicleDamagePartsList(); notifyListeners(); } break; - case AdCreationStepsEnum.damageParts: + case AdCreationSteps.damageParts: if (isDamagePartsValidated()) { - currentProgressStep = AdCreationStepsEnum.adDuration; + currentProgressStep = AdCreationSteps.adDuration; getVehicleAdsDuration(); getVehicleAdsSpecialServices(); notifyListeners(); } break; - case AdCreationStepsEnum.adDuration: + case AdCreationSteps.adDuration: if (isAdDurationValidated()) { - currentProgressStep = AdCreationStepsEnum.reviewAd; + currentProgressStep = AdCreationSteps.reviewAd; notifyListeners(); } break; - case AdCreationStepsEnum.reviewAd: + case AdCreationSteps.reviewAd: Utils.showLoading(context); try { int status = await createNewAd(); @@ -865,7 +926,7 @@ class AdVM extends BaseVM { } Utils.hideLoading(context); Utils.showToast("A new ads has been created."); - currentProgressStep = AdCreationStepsEnum.vehicleDetails; + currentProgressStep = AdCreationSteps.vehicleDetails; resetValues(); updateIsExploreAds(false); applyFilterOnMyAds(index: 1, adPostStatusEnum: AdPostStatus.pendingForReview); //pending for review @@ -982,7 +1043,7 @@ class AdVM extends BaseVM { pickedVehicleImages.clear(); vehicleDamageCards.clear(); specialServiceCards.clear(); - currentProgressStep = AdCreationStepsEnum.vehicleDetails; + currentProgressStep = AdCreationSteps.vehicleDetails; vehicleTypeId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); vehicleTypeId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); vehicleBrandId = SelectionModel(selectedOption: "", selectedId: -1, errorValue: ""); diff --git a/lib/view_models/payment_view_model.dart b/lib/view_models/payment_view_model.dart index 2fc559d..b6886d7 100644 --- a/lib/view_models/payment_view_model.dart +++ b/lib/view_models/payment_view_model.dart @@ -13,8 +13,8 @@ class PaymentVM extends ChangeNotifier { PaymentVM({required this.paymentService, required this.paymentRepo}); - PaymentMethodsEnum selectedPaymentMethod = PaymentMethodsEnum.mada; - PaymentTypesEnum currentPaymentType = PaymentTypesEnum.ads; + PaymentMethods selectedPaymentMethod = PaymentMethods.mada; + PaymentTypes currentPaymentType = PaymentTypes.ads; int currentAdId = -1; @@ -22,26 +22,26 @@ class PaymentVM extends ChangeNotifier { currentAdId = id; } - updateSelectedPaymentMethod(PaymentMethodsEnum selectedMethod) { + updateSelectedPaymentMethod(PaymentMethods selectedMethod) { selectedPaymentMethod = selectedMethod; notifyListeners(); } - Future onContinuePressed(BuildContext context, {required PaymentTypesEnum paymentType}) async { + Future onContinuePressed(BuildContext context, {required PaymentTypes paymentType}) async { switch (selectedPaymentMethod) { - case PaymentMethodsEnum.mada: + case PaymentMethods.mada: // TODO: Handle this case. break; - case PaymentMethodsEnum.visa: + case PaymentMethods.visa: await onVisaCardSelected(context, paymentType); break; - case PaymentMethodsEnum.applePay: + case PaymentMethods.applePay: // TODO: Handle this case. break; - case PaymentMethodsEnum.masterCard: + case PaymentMethods.masterCard: // TODO: Handle this case. break; - case PaymentMethodsEnum.tamara: + case PaymentMethods.tamara: // TODO: Handle this case. break; } @@ -49,74 +49,64 @@ class PaymentVM extends ChangeNotifier { return; } - Future onVisaCardSelected(BuildContext context, PaymentTypesEnum paymentType) async { + Future placeThePayment({required int adId, required int paymentTypeId, required BuildContext context}) async { + await paymentService.placeAdPayment( + id: currentAdId, + paymentType: paymentTypeId, + onFailure: () { + Utils.showToast("Payment Failed!"); + }, + onSuccess: () async { + Utils.showLoading(context); + PayOrderDetailRespModel payOrderDetailRespModel = await paymentRepo.getPayOrderDetails(paymentId: paymentTypeId, adId: currentAdId); + await Future.delayed(const Duration(seconds: 2)); + Utils.hideLoading(context); + + print("payOrderDetailRespModel: ${payOrderDetailRespModel.toString()}"); + + if (payOrderDetailRespModel.isPaid == null || !payOrderDetailRespModel.isPaid!) { + Utils.showToast("Payment Failed!"); + return; + } + + if (payOrderDetailRespModel.isPaid != null && payOrderDetailRespModel.isPaid!) { + Utils.showToast("Payment Successful"); + navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); + } + }, + ); + } + + Future onVisaCardSelected(BuildContext context, PaymentTypes paymentType) async { currentPaymentType = paymentType; switch (currentPaymentType) { - case PaymentTypesEnum.subscription: - // TODO: Handle this case. - break; - case PaymentTypesEnum.appointment: - // TODO: Handle this case. - break; - case PaymentTypesEnum.ads: + + case PaymentTypes.ads: if (currentAdId == -1) return; int paymentType = 3; - await paymentService.placeAdPayment( - id: currentAdId, - paymentType: paymentType, - onFailure: () { - Utils.showToast("Payment Failed!"); - }, - onSuccess: () async { - Utils.showLoading(context); - PayOrderDetailRespModel payOrderDetailRespModel = await paymentRepo.getPayOrderDetails(paymentId: paymentType, adId: currentAdId); - await Future.delayed(const Duration(seconds: 2)); - Utils.hideLoading(context); - - print("payOrderDetailRespModel: ${payOrderDetailRespModel.toString()}"); - - if (payOrderDetailRespModel.isPaid == null || !payOrderDetailRespModel.isPaid!) { - Utils.showToast("Payment Failed!"); - return; - } - - if (payOrderDetailRespModel.isPaid != null && payOrderDetailRespModel.isPaid!) { - Utils.showToast("Payment Successfully Completed!"); - navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); - } - }, - ); + await placeThePayment(adId: currentAdId, context: context, paymentTypeId: paymentType); break; - case PaymentTypesEnum.adReserve: + + case PaymentTypes.adReserve: if (currentAdId == -1) return; int paymentType = 4; - await paymentService.placeAdPayment( - id: currentAdId, - paymentType: paymentType, - onFailure: () { - Utils.showToast("Payment Failed!"); - }, - onSuccess: () async { - Utils.showLoading(context); - PayOrderDetailRespModel payOrderDetailRespModel = await paymentRepo.getPayOrderDetails(paymentId: paymentType, adId: currentAdId); - await Future.delayed(const Duration(seconds: 2)); - Utils.hideLoading(context); - - print("payOrderDetailRespModel: ${payOrderDetailRespModel.toString()}"); - - if (payOrderDetailRespModel.isPaid == null || !payOrderDetailRespModel.isPaid!) { - Utils.showToast("Payment Failed!"); - return; - } - - if (payOrderDetailRespModel.isPaid != null && payOrderDetailRespModel.isPaid!) { - Utils.showToast("Payment Successfully Completed!"); - navigateReplaceWithNameUntilRoute(context, AppRoutes.dashboard); - } - }, - ); + await placeThePayment(adId: currentAdId, context: context, paymentTypeId: paymentType); + break; + + case PaymentTypes.extendAds: + if (currentAdId == -1) return; + int paymentType = 6; + await placeThePayment(adId: currentAdId, context: context, paymentTypeId: paymentType); + break; + + + case PaymentTypes.request: + // TODO: Handle this case. + break; + case PaymentTypes.subscription: + // TODO: Handle this case. break; - case PaymentTypesEnum.request: + case PaymentTypes.appointment: // TODO: Handle this case. break; } diff --git a/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart b/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart index 5d42ada..7cc8db1 100644 --- a/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart +++ b/lib/views/advertisement/ad_creation_steps/ad_duration_container.dart @@ -36,7 +36,7 @@ class AdDuration extends StatelessWidget { enableDrag: true, builder: (BuildContext context) { return InfoBottomSheet( - title: "Reserve Ad Price Info", + title: "Reserve Ad Price Info".toText(fontSize: 24, isBold: true), description: Flexible( child: GlobalConsts.reserveAdPriceInfo.toText( textAlign: TextAlign.justify, diff --git a/lib/views/advertisement/ad_duration_selection_sheet_content.dart b/lib/views/advertisement/ad_duration_selection_sheet_content.dart index 6efc03e..300da1f 100644 --- a/lib/views/advertisement/ad_duration_selection_sheet_content.dart +++ b/lib/views/advertisement/ad_duration_selection_sheet_content.dart @@ -5,6 +5,7 @@ import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/models/advertisment_models/ads_duration_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'; import 'package:mc_common_app/view_models/ad_view_model.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; @@ -12,13 +13,14 @@ import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; class AdDurationSelectionSheetContent extends StatelessWidget { - const AdDurationSelectionSheetContent({super.key}); + final bool isFromExtendAd; + final bool isUpdateAdSelected; + + const AdDurationSelectionSheetContent({super.key, required this.isFromExtendAd, required this.isUpdateAdSelected}); @override Widget build(BuildContext context) { AdVM adVM = context.watch(); - - print("hello : ${adVM.vehicleAdDurationId.selectedId}"); return SizedBox( height: MediaQuery.of(context).size.height / 1.2, child: Column( @@ -70,6 +72,16 @@ class AdDurationSelectionSheetContent extends StatelessWidget { ], ).toWhiteContainer(width: double.infinity, allPading: 12, isBorderRequired: adDuration.id == adVM.vehicleAdDurationId.selectedId ? true : false), ).onPress(() { + if (isFromExtendAd) { + adVM.updateVehicleExtendAdDurationId( + SelectionModel( + selectedId: adDuration.id ?? 0, + selectedOption: "${adDuration.days} Days", + itemPrice: adDuration.price!.toInt().toString(), + ), + ); + return; + } adVM.updateVehicleAdDurationId( SelectionModel( selectedId: adDuration.id ?? 0, @@ -90,7 +102,10 @@ class AdDurationSelectionSheetContent extends StatelessWidget { maxWidth: double.infinity, margin: const EdgeInsets.all(21), onPressed: () { - + if (isFromExtendAd && !isUpdateAdSelected) { + navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.extendAds); + return; + } navigateReplaceWithName(context, AppRoutes.createAdView); }, ).toContainer(paddingAll: 0, backgroundColor: Colors.white), diff --git a/lib/views/advertisement/ads_detail_view.dart b/lib/views/advertisement/ads_detail_view.dart index 30465cf..5521122 100644 --- a/lib/views/advertisement/ads_detail_view.dart +++ b/lib/views/advertisement/ads_detail_view.dart @@ -1,14 +1,22 @@ 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/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/dialogs_and_bottomsheets.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/ad_view_model.dart'; import 'package:mc_common_app/view_models/payment_view_model.dart'; +import 'package:mc_common_app/views/advertisement/ad_duration_selection_sheet_content.dart'; import 'package:mc_common_app/views/advertisement/ads_images_slider.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:mc_common_app/widgets/common_widgets/info_bottom_sheet.dart'; @@ -20,17 +28,72 @@ class AdsDetailView extends StatelessWidget { const AdsDetailView({Key? key, required this.adDetails}) : super(key: key); + void deleteAdBottomSheet(BuildContext context) { + AdVM adVM = context.read(); + return actionConfirmationBottomSheet( + context: context, + title: "Do you want to delete the ad?".toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + subtitle: "Your ad will be permanently deleted and you cannot undo this action.", + actionButtonYes: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: "Yes", + fontSize: 15, + onPressed: () { + Navigator.pop(context); + adVM.deleteMyAd(context, adId: adDetails.id!); + }, + ), + ), + actionButtonNo: Expanded( + child: ShowFillButton( + maxHeight: 55, + isFilled: false, + borderColor: MyColors.darkPrimaryColor, + title: "No", + txtColor: MyColors.darkPrimaryColor, + fontSize: 15, + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ); + } + @override Widget build(BuildContext context) { print("adId: ${adDetails.id}"); - print("statusID: ${adDetails.adPostStatus}"); + print("statusID: ${adDetails.statusID}"); return Scaffold( appBar: CustomAppBar( title: "Ads", profileImageUrl: MyAssets.bnCar, isRemoveBackButton: false, isDrawerEnabled: false, - actions: [const Icon(Icons.chat_outlined).paddingOnly(right: 21)], + actions: [ + (adDetails.isMyAd ?? false + ? IconButton( + icon: const Icon(Icons.delete_outline, color: MyColors.redColor), + onPressed: () { + return deleteAdBottomSheet(context); + }, + ) + : IconButton( + icon: const Icon(Icons.chat_outlined, color: Colors.black), + onPressed: () {}, + )) + .toContainer( + margin: const EdgeInsets.fromLTRB(0, 8, 21, 8), + paddingAll: 0, + padding: const EdgeInsets.only(right: 21), + borderRadius: 100, + borderColor: MyColors.lightGreyEFColor, + isEnabledBorder: true, + height: 40, + width: 42, + ) + ], onTap: () {}, ), body: Container( @@ -97,6 +160,27 @@ class AdsDetailView extends StatelessWidget { fontSize: 14, isBold: true, ), + if (adDetails.isMyAd ?? false) ...[ + 8.height, + "Demand: ".toText(fontSize: 14, isBold: true, color: MyColors.lightTextColor), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, height: 1.2, isBold: true), + " SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), + ], + ), + if (adDetails.adPostStatus == AdPostStatus.expired) ...[ + 8.height, + const Divider(thickness: 1, height: 1), + 8.height, + "Your Ad Duration time is over.".toText( + color: MyColors.redColor, + fontSize: 12, + isItalic: true, + ), + ], + ] ], ).toWhiteContainer(width: double.infinity, allPading: 12), ], @@ -105,16 +189,18 @@ class AdsDetailView extends StatelessWidget { SizedBox( child: Column( children: [ - const Divider(thickness: 1, height: 1), - 18.height, - Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, isBold: true), - " SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), - ], - ), - 14.height, + if (!(adDetails.isMyAd ?? false)) ...[ + const Divider(thickness: 1, height: 1), + 18.height, + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + adDetails.vehicle!.demandAmount!.toInt().toString().toText(fontSize: 30, isBold: true), + " SAR".toText(fontSize: 15, isBold: true, color: MyColors.lightTextColor).paddingOnly(bottom: 5), + ], + ), + 14.height, + ], adDetails.isMyAd ?? false ? BuildAdDetailsActionButtonForMyAds(adDetailsModel: adDetails) : BuildAdDetailsActionButtonForExploreAds(adDetailsModel: adDetails), ], ), @@ -138,7 +224,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { enableDrag: true, builder: (BuildContext context) { return InfoBottomSheet( - title: "Reserve Ad", + title: "Reserve Ad".toText(fontSize: 24, isBold: true), description: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -270,7 +356,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { title: "Complete Reservation", onPressed: () { Navigator.pop(context); - navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypesEnum.adReserve); + navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.adReserve); }, ), ), @@ -322,7 +408,9 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)), //TODO: It Will be replaced by a WhatsApp Icon child: const Icon(Icons.message, color: MyColors.black), - ).onPress(() {}), + ).onPress(() { + Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? ""); + }), ], if (adDetailsModel.phoneNo != null) ...[ 8.width, @@ -332,14 +420,16 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { alignment: Alignment.center, decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)), child: const Icon(Icons.phone, color: MyColors.black), - ).onPress(() {}), + ).onPress(() { + Utils.openNumberViaCaller(phoneNumber: adDetailsModel.phoneNo ?? ""); + }), ] ], ); } Widget defaultActionForProviderAndCustomer(BuildContext context, AdDetailsModel adDetailsModel) { - return (adDetailsModel.phoneNo != null) + return (adDetailsModel.phoneNo == null) ? Row( children: [ Expanded( @@ -352,10 +442,12 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { padding: EdgeInsets.only(right: 10), child: Icon(Icons.phone, color: MyColors.white, size: 24), ), - onPressed: () {}, + onPressed: () { + Utils.openNumberViaCaller(phoneNumber: adDetailsModel.phoneNo ?? ""); + }, ), ), - if (adDetailsModel.whatsAppNo != null) ...[ + if (adDetailsModel.whatsAppNo == null) ...[ 8.width, Container( height: 55, @@ -364,7 +456,9 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { decoration: BoxDecoration(border: Border.all(color: MyColors.black, width: 2)), //TODO: It Will be replaced by a WhatsApp Icon child: const Icon(Icons.message, color: MyColors.black), - ).onPress(() {}), + ).onPress(() { + Utils.openNumberViaWhatsApp(phoneNumber: adDetailsModel.whatsAppNo ?? ""); + }), ], ], ) @@ -401,7 +495,7 @@ class BuildAdDetailsActionButtonForExploreAds extends StatelessWidget { case CreatedByRoleEnum.admin: return reserveAdAction(context, adDetailsModel); case CreatedByRoleEnum.allAds: - return SizedBox.shrink(); + return const SizedBox.shrink(); } } } @@ -418,7 +512,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { enableDrag: true, builder: (BuildContext context) { return InfoBottomSheet( - title: "Reserve Ad", + title: "Reserve Ad".toText(fontSize: 24, isBold: true), description: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -555,7 +649,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { }); } - Widget pendingForReviewAction(BuildContext context, {required int adID}) { + Widget pendingForReviewAction(BuildContext context, {required bool isPendingPost}) { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -566,7 +660,7 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { backgroundColor: MyColors.grey98Color.withOpacity(0.3), txtColor: MyColors.lightTextColor, maxHeight: 55, - title: "Waiting for Admins Approval", + title: isPendingPost ? "Waiting for admin to post" : "Waiting for Admins Approval", isBold: false, onPressed: () {}, ), @@ -588,29 +682,18 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { maxHeight: 55, title: "Pay Now", onPressed: () { - navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypesEnum.ads); + navigateWithName(context, AppRoutes.paymentMethodsView, arguments: PaymentTypes.ads); }, ), ), ], ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - InkWell( - onTap: () {}, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: "Delete Ad".toText(fontSize: 15, isBold: true, color: MyColors.redColor), - ), - ), - ], - ) ], ); } Widget markAsSoldAction(BuildContext context) { + AdVM adVM = context.read(); return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ @@ -620,20 +703,55 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { child: ShowFillButton( maxHeight: 55, title: "Mark As Sold", - onPressed: () {}, + isBold: false, + onPressed: () { + adVM.markAdAsSold(context, adId: adDetailsModel.id!); + }, ), ), ], ), + 8.height, Row( children: [ Expanded( child: ShowFillButton( - isFilled: true, - borderColor: MyColors.darkPrimaryColor, + isFilled: false, + borderColor: MyColors.redColor, maxHeight: 55, title: "Deactivate Ad", - onPressed: () {}, + txtColor: MyColors.redColor, + onPressed: () { + return actionConfirmationBottomSheet( + context: context, + title: "Do you want to the Deactivate this Ad?".toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + subtitle: "We will stop showing this ad to the buyers.", + actionButtonYes: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: "Yes", + fontSize: 15, + onPressed: () { + Navigator.pop(context); + adVM.deactivateTheAd(context, adId: adDetailsModel.id!); + }, + ), + ), + actionButtonNo: Expanded( + child: ShowFillButton( + maxHeight: 55, + isFilled: false, + borderColor: MyColors.darkPrimaryColor, + title: "No", + txtColor: MyColors.darkPrimaryColor, + fontSize: 15, + onPressed: () { + Navigator.pop(context); + }, + ), + ), + ); + }, ), ), ], @@ -660,6 +778,60 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { ); } + Widget expiredAdAction(BuildContext context) { + return Row( + children: [ + Expanded( + child: ShowFillButton( + fontSize: 16, + maxHeight: 55, + title: "Extend Ad", + onPressed: () { + final AdVM adVM = context.read(); + return actionConfirmationBottomSheet( + context: context, + title: "Do you want to update the Ad Details?".toText(fontSize: 28, isBold: true, letterSpacing: -1.44), + subtitle: "You can change the ad duration and details before extending the ad.", + actionButtonYes: Expanded( + child: ShowFillButton( + maxHeight: 55, + title: "Yes", + fontSize: 15, + onPressed: () { + Navigator.pop(context); + adVM.updateSelectionVehicleTypeId( + SelectionModel(selectedId: adDetailsModel.vehicle!.vehicleType!, selectedOption: (adDetailsModel.vehicle!.vehicleType!).toVehicleTypeString(), errorValue: ""), + ); + + showMyBottomSheet(context, child: const AdDurationSelectionSheetContent(isFromExtendAd: true, isUpdateAdSelected: true)); + }, + ), + ), + actionButtonNo: Expanded( + child: ShowFillButton( + maxHeight: 55, + isFilled: false, + borderColor: MyColors.darkPrimaryColor, + title: "No", + txtColor: MyColors.darkPrimaryColor, + fontSize: 15, + onPressed: () { + adVM.updateSelectionVehicleTypeId( + SelectionModel(selectedId: adDetailsModel.vehicle!.vehicleType!, selectedOption: (adDetailsModel.vehicle!.vehicleType!).toVehicleTypeString(), errorValue: ""), + ); + + showMyBottomSheet(context, child: const AdDurationSelectionSheetContent(isFromExtendAd: true, isUpdateAdSelected: false)); + }, + ), + ), + ); + }, + ), + ), + ], + ); + } + Widget defaultAction(BuildContext context) { return Row( children: [ @@ -709,12 +881,15 @@ class BuildAdDetailsActionButtonForMyAds extends StatelessWidget { case AdPostStatus.rejected: case AdPostStatus.cancelled: case AdPostStatus.pendingForPost: + return pendingForReviewAction(context, isPendingPost: true); + case AdPostStatus.pendingForReview: - return pendingForReviewAction(context, adID: adDetailsModel.id!); + return pendingForReviewAction(context, isPendingPost: false); - // return pendingForPaymentAction(context, adID: adId); case AdPostStatus.sold: case AdPostStatus.expired: + return expiredAdAction(context); + case AdPostStatus.allAds: break; } return const SizedBox.shrink(); diff --git a/lib/views/advertisement/create_ad_progress_steps.dart b/lib/views/advertisement/create_ad_progress_steps.dart index 6033ed2..391c034 100644 --- a/lib/views/advertisement/create_ad_progress_steps.dart +++ b/lib/views/advertisement/create_ad_progress_steps.dart @@ -39,19 +39,19 @@ class CreateAdProgressSteps extends StatelessWidget { ); } - bool isStepCompleted({required AdCreationStepsEnum currentStep}) { + bool isStepCompleted({required AdCreationSteps currentStep}) { return true; } - int getProgressStepNumber({required AdCreationStepsEnum currentStep}) { + int getProgressStepNumber({required AdCreationSteps currentStep}) { switch (currentStep) { - case AdCreationStepsEnum.vehicleDetails: + case AdCreationSteps.vehicleDetails: return 1; - case AdCreationStepsEnum.damageParts: + case AdCreationSteps.damageParts: return 2; - case AdCreationStepsEnum.adDuration: + case AdCreationSteps.adDuration: return 3; - case AdCreationStepsEnum.reviewAd: + case AdCreationSteps.reviewAd: return 4; } } diff --git a/lib/views/advertisement/create_ad_view.dart b/lib/views/advertisement/create_ad_view.dart index 783f0da..195d438 100644 --- a/lib/views/advertisement/create_ad_view.dart +++ b/lib/views/advertisement/create_ad_view.dart @@ -73,7 +73,7 @@ class BuildFooterButton extends StatelessWidget { return Consumer( builder: (BuildContext context, AdVM adVm, Widget? child) { switch (adVm.currentProgressStep) { - case AdCreationStepsEnum.vehicleDetails: + case AdCreationSteps.vehicleDetails: return SizedBox( width: double.infinity, child: ShowFillButton( @@ -83,7 +83,7 @@ class BuildFooterButton extends StatelessWidget { }, ), ); - case AdCreationStepsEnum.damageParts: + case AdCreationSteps.damageParts: return Row( children: [ Expanded( @@ -111,7 +111,7 @@ class BuildFooterButton extends StatelessWidget { ), ], ); - case AdCreationStepsEnum.adDuration: + case AdCreationSteps.adDuration: return Row( children: [ Expanded( @@ -139,7 +139,7 @@ class BuildFooterButton extends StatelessWidget { ), ], ); - case AdCreationStepsEnum.reviewAd: + case AdCreationSteps.reviewAd: return Row( children: [ Expanded( @@ -182,7 +182,7 @@ class BuildAdStepContainer extends StatelessWidget { final WidgetBuilder onDamageParts; final WidgetBuilder onAdDuration; final WidgetBuilder onReviewAd; - final AdCreationStepsEnum adCreationStepsEnum; + final AdCreationSteps adCreationStepsEnum; const BuildAdStepContainer({ Key? key, @@ -196,16 +196,16 @@ class BuildAdStepContainer extends StatelessWidget { @override Widget build(BuildContext context) { switch (adCreationStepsEnum) { - case AdCreationStepsEnum.vehicleDetails: + case AdCreationSteps.vehicleDetails: return onVehicleDetails(context); - case AdCreationStepsEnum.damageParts: + case AdCreationSteps.damageParts: return onDamageParts(context); - case AdCreationStepsEnum.adDuration: + case AdCreationSteps.adDuration: return onAdDuration(context); - case AdCreationStepsEnum.reviewAd: + case AdCreationSteps.reviewAd: return onReviewAd(context); } } diff --git a/lib/views/advertisement/select_ad_type_view.dart b/lib/views/advertisement/select_ad_type_view.dart index fa2654b..812f6d8 100644 --- a/lib/views/advertisement/select_ad_type_view.dart +++ b/lib/views/advertisement/select_ad_type_view.dart @@ -22,18 +22,18 @@ class SelectAdTypeView extends StatelessWidget { this.isProvider = true, }) : super(key: key); - Widget getVehicleAdTypeIcon(VehicleTypeEnum vehicleTypeEnum) { + Widget getVehicleAdTypeIcon(VehicleType vehicleTypeEnum) { switch (vehicleTypeEnum) { - case VehicleTypeEnum.car: + case VehicleType.car: return MyAssets.vehicleTypeCar.buildSvg(height: 22); - case VehicleTypeEnum.motorCycle: + case VehicleType.motorCycle: return MyAssets.vehicleTypeMotorcycle.buildSvg(height: 26); - case VehicleTypeEnum.golfCart: + case VehicleType.golfCart: return MyAssets.vehicleTypeGolfCart.buildSvg(height: 26); - case VehicleTypeEnum.buggy: + case VehicleType.buggy: return MyAssets.vehicleTypeBuggy.buildSvg(height: 26); } } @@ -61,7 +61,7 @@ class SelectAdTypeView extends StatelessWidget { SelectionModel(selectedId: vehicleTypeModel.id!, selectedOption: vehicleTypeModel.vehicleTypeName ?? "", errorValue: ""), ); - showMyBottomSheet(context, child: const AdDurationSelectionSheetContent()); + showMyBottomSheet(context, child: const AdDurationSelectionSheetContent(isUpdateAdSelected: false, isFromExtendAd: false)); }, child: SizedBox( width: double.infinity, diff --git a/lib/views/payments/payment_methods_view.dart b/lib/views/payments/payment_methods_view.dart index 01d6071..f16e003 100644 --- a/lib/views/payments/payment_methods_view.dart +++ b/lib/views/payments/payment_methods_view.dart @@ -11,7 +11,7 @@ import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:provider/provider.dart'; class PaymentMethodsView extends StatelessWidget { - final PaymentTypesEnum paymentType; + final PaymentTypes paymentType; const PaymentMethodsView({Key? key, required this.paymentType}) : super(key: key); @@ -40,24 +40,24 @@ class PaymentMethodsView extends StatelessWidget { shrinkWrap: true, children: [ PaymentMethodContainer( - isSelected: paymentVm.selectedPaymentMethod == PaymentMethodsEnum.mada, - onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethodsEnum.mada), + isSelected: paymentVm.selectedPaymentMethod == PaymentMethods.mada, + onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethods.mada), cardAsset: MyAssets.madaPng), PaymentMethodContainer( - isSelected: paymentVm.selectedPaymentMethod == PaymentMethodsEnum.visa, - onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethodsEnum.visa), + isSelected: paymentVm.selectedPaymentMethod == PaymentMethods.visa, + onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethods.visa), cardAsset: MyAssets.visaPng), PaymentMethodContainer( - isSelected: paymentVm.selectedPaymentMethod == PaymentMethodsEnum.applePay, - onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethodsEnum.applePay), + isSelected: paymentVm.selectedPaymentMethod == PaymentMethods.applePay, + onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethods.applePay), cardAsset: MyAssets.applePayPng), PaymentMethodContainer( - isSelected: paymentVm.selectedPaymentMethod == PaymentMethodsEnum.masterCard, - onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethodsEnum.masterCard), + isSelected: paymentVm.selectedPaymentMethod == PaymentMethods.masterCard, + onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethods.masterCard), cardAsset: MyAssets.mastercardPng), PaymentMethodContainer( - isSelected: paymentVm.selectedPaymentMethod == PaymentMethodsEnum.tamara, - onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethodsEnum.tamara), + isSelected: paymentVm.selectedPaymentMethod == PaymentMethods.tamara, + onTap: () => paymentVm.updateSelectedPaymentMethod(PaymentMethods.tamara), cardAsset: MyAssets.tamaraEngPng), ], ), diff --git a/lib/views/user/login_with_password_page.dart b/lib/views/user/login_with_password_page.dart index 0d74b32..c3d4005 100644 --- a/lib/views/user/login_with_password_page.dart +++ b/lib/views/user/login_with_password_page.dart @@ -29,7 +29,7 @@ class _LoginWithPasswordState extends State { ClassType type = ClassType.EMAIL; //TODO: ONLY FOR DEVELOPMENT PURPOSE - String phoneNum = "966504278213", password = "Fa@1234"; + String phoneNum = "966504278212", password = "Fa@123"; String email = ""; String countryCode = ""; Country? _country; diff --git a/lib/widgets/button/show_fill_button.dart b/lib/widgets/button/show_fill_button.dart index e55f813..6be9970 100644 --- a/lib/widgets/button/show_fill_button.dart +++ b/lib/widgets/button/show_fill_button.dart @@ -29,7 +29,7 @@ class ShowFillButton extends StatelessWidget { this.fontSize = 16, this.horizontalPadding = 16, this.isFlatButton = false, - this.isBold = true, + this.isBold = false, this.horizontalMargin = 0, this.verticalMargin = 0, this.margin, diff --git a/lib/widgets/common_widgets/app_bar.dart b/lib/widgets/common_widgets/app_bar.dart index 1ee93b4..8c1c8ac 100644 --- a/lib/widgets/common_widgets/app_bar.dart +++ b/lib/widgets/common_widgets/app_bar.dart @@ -104,45 +104,45 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { centerTitle: isTitleCenter ?? true, leading: isDrawerEnabled ? InkWell( - onTap: onTap, - child: Row( - children: [ - Image.asset( - profileImageUrl, - width: 34, - height: 34, - fit: BoxFit.fill, - ).toCircle(borderRadius: 100), - 10.width, - SvgPicture.asset(MyAssets.dashboardDrawerIcon), - ], - ).paddingOnly(left: 21), - ) + onTap: onTap, + child: Row( + children: [ + Image.asset( + profileImageUrl, + width: 34, + height: 34, + fit: BoxFit.fill, + ).toCircle(borderRadius: 100), + 10.width, + SvgPicture.asset(MyAssets.dashboardDrawerIcon), + ], + ).paddingOnly(left: 21), + ) : isRemoveBackButton - ? null - : Row( - children: [ - 21.width, - IconButton( - icon: const Icon( - Icons.arrow_back_ios, - color: Colors.black, - size: 16, - ), - onPressed: onBackButtonTapped ?? () { - Navigator.pop(context); - }, - ).toContainer( - - paddingAll: 0, - borderRadius: 100, - borderColor: MyColors.lightGreyEFColor, - isEnabledBorder: true, - height: 40, - width: 40, - ), - ], - ), + ? null + : Row( + children: [ + 21.width, + IconButton( + icon: const Icon( + Icons.arrow_back_ios, + color: Colors.black, + size: 16, + ), + onPressed: onBackButtonTapped ?? + () { + Navigator.pop(context); + }, + ).toContainer( + paddingAll: 0, + borderRadius: 100, + borderColor: MyColors.lightGreyEFColor, + isEnabledBorder: true, + height: 40, + width: 40, + ), + ], + ), iconTheme: IconThemeData( color: backIconColor ?? Colors.black, //change your color here ), diff --git a/lib/widgets/common_widgets/info_bottom_sheet.dart b/lib/widgets/common_widgets/info_bottom_sheet.dart index 5c0d6c4..429e54c 100644 --- a/lib/widgets/common_widgets/info_bottom_sheet.dart +++ b/lib/widgets/common_widgets/info_bottom_sheet.dart @@ -5,7 +5,7 @@ import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; class InfoBottomSheet extends StatelessWidget { - final String title; + final Widget title; final Widget description; const InfoBottomSheet({Key? key, required this.title, required this.description}) : super(key: key); @@ -13,8 +13,11 @@ class InfoBottomSheet extends StatelessWidget { @override Widget build(BuildContext context) { return SingleChildScrollView( - // height: MediaQuery.of(context).size.height * 0.35, child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, children: [ Container( margin: const EdgeInsets.all(8), @@ -22,16 +25,13 @@ class InfoBottomSheet extends StatelessWidget { width: 60, decoration: const BoxDecoration(color: MyColors.lightTextColor, borderRadius: BorderRadius.all(Radius.circular(20))), ), - 12.height, - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - title.toText(fontSize: 24, isBold: true), - ], - ), - 8.height, - description, ], - ).horPaddingMain()); + ), + 12.height, + title, + 8.height, + description, + ], + ).horPaddingMain()); } } diff --git a/lib/widgets/common_widgets/time_slots.dart b/lib/widgets/common_widgets/time_slots.dart index 9e56974..32607a9 100644 --- a/lib/widgets/common_widgets/time_slots.dart +++ b/lib/widgets/common_widgets/time_slots.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; +import 'package:mc_common_app/models/service_schedule_model.dart'; import 'package:mc_common_app/models/widgets_models.dart'; import 'package:mc_common_app/theme/colors.dart'; @@ -45,3 +46,46 @@ class BuildTimeSlots extends StatelessWidget { ); } } + +class BuildDateSlotsForAppointment extends StatelessWidget { + final List customDateSlots; + final Function(int) onPressed; + + const BuildDateSlotsForAppointment({Key? key, required this.customDateSlots, required this.onPressed}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 37, + width: double.infinity, + child: ListView.builder( + physics: const BouncingScrollPhysics(), + itemCount: customDateSlots.length, + scrollDirection: Axis.horizontal, + shrinkWrap: true, + itemBuilder: (BuildContext context, int index) { + return InkWell( + onTap: () { + onPressed(index); + }, + child: Container( + alignment: Alignment.center, + margin: const EdgeInsets.only(right: 8), + // width: 50, + padding: const EdgeInsets.symmetric(horizontal: 9), + decoration: BoxDecoration( + color: customDateSlots[index].date!.isSelected ? MyColors.darkIconColor : null, + border: Border.all( + color: customDateSlots[index].date!.isSelected ? MyColors.darkIconColor : MyColors.primaryColor, + ), + ), + child: customDateSlots[index].date!.date.toText( + fontSize: 12, + color: customDateSlots[index].date!.isSelected ? MyColors.white : null, + ), + ), + ); + }), + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index ef7b31d..1cd95e8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,7 +31,7 @@ dependencies: local_auth: any hexcolor: ^3.0.1 cached_network_image: any - url_launcher: ^6.1.7 + url_launcher: ^6.1.14 badges: ^3.0.2 carousel_slider: ^4.2.1 dropdown_button2: ^2.0.0