Merge branch 'faiz_dev' into fatima_dev

# Conflicts:
#	pubspec.yaml
fatima_dev
Rand Alshammari 3 weeks ago
commit 8d4b375809

@ -23,6 +23,7 @@ import 'package:flutter/material.dart';
import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart'; import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart';
import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart'; import 'package:mc_common_app/models/provider_branches_models/branch_detail_model.dart';
import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/views/advertisement/favourite_ads_view.dart';
import 'package:mc_common_app/views/appointments/appointments_filter_view.dart'; import 'package:mc_common_app/views/appointments/appointments_filter_view.dart';
import 'package:mc_common_app/views/setting_options/provider_accepted_requests_view.dart'; import 'package:mc_common_app/views/setting_options/provider_accepted_requests_view.dart';
@ -69,5 +70,8 @@ class ProviderAppRoutes {
//Requests //Requests
AppRoutes.providerAcceptedRequestsView: (context) => const ProviderAcceptedRequestsView(), AppRoutes.providerAcceptedRequestsView: (context) => const ProviderAcceptedRequestsView(),
//Favourite Ads
AppRoutes.favouriteAdsView: (context) => const FavouriteAdsView(),
}; };
} }

@ -165,7 +165,9 @@ class BranchListPage extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
(branchModel.branchName ?? "").toText(fontSize: 16, height: 18 / 16), Expanded(
child: (branchModel.branchName ?? "").toText(fontSize: 16, height: 18 / 16),
)
], ],
).withArrow(isArrowEnabled: branchModel.branchStatus != BranchStatusEnum.blocked).margin(right: 10), ).withArrow(isArrowEnabled: branchModel.branchStatus != BranchStatusEnum.blocked).margin(right: 10),
(branchModel.branchDescription ?? "").toText( (branchModel.branchDescription ?? "").toText(

@ -8,6 +8,7 @@ import 'package:mc_common_app/classes/app_state.dart';
import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/int_extensions.dart';
import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart';
import 'package:mc_common_app/theme/colors.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/enums.dart';
import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/navigator.dart';
import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/utils/utils.dart';
@ -29,7 +30,10 @@ class AssignDealerUserSheet extends StatefulWidget {
} }
class _AssignDealerUserSheetState extends State<AssignDealerUserSheet> { class _AssignDealerUserSheetState extends State<AssignDealerUserSheet> {
late ServiceVM provider; ServiceVM? provider;
// Track initially assigned users to detect removals
Set<int> initiallyAssignedUserIds = {};
@override @override
void initState() { void initState() {
@ -40,11 +44,163 @@ class _AssignDealerUserSheetState extends State<AssignDealerUserSheet> {
}); });
} }
getBranchUsers() { getBranchUsers() async {
provider.getAllProviderDealers({ if (provider == null) return;
await provider!.getAllProviderDealers({
"ServiceProviderBranchID": widget.branchId, "ServiceProviderBranchID": widget.branchId,
"ServiceProviderID": AppState().getUser.data?.userInfo?.providerId.toString() ?? "", "ServiceProviderID": AppState().getUser.data?.userInfo?.providerId.toString() ?? "",
}); });
// Store initially assigned user IDs
initiallyAssignedUserIds.clear();
for (var user in provider!.allProviderDealersList) {
if (user.isBranchUser ?? false) {
initiallyAssignedUserIds.add(user.id);
}
}
setState(() {});
}
// Handle checkbox changes
Future<void> onUserSelectionChanged(int userId, bool? newValue) async {
if (provider == null) return;
final user = provider!.allProviderDealersList.firstWhere((u) => u.id == userId);
final wasInitiallyAssigned = initiallyAssignedUserIds.contains(userId);
if (newValue == true && !wasInitiallyAssigned) {
// User is checking a new dealer - show assign confirmation
showAssignConfirmation(userId);
} else if (newValue == false && wasInitiallyAssigned) {
// User is unchecking an assigned dealer - show remove confirmation
showRemoveConfirmation(userId);
} else {
// Just toggle for non-initially-assigned users
user.isBranchUser = newValue ?? false;
provider!.notifyListeners();
}
}
// Show confirmation bottom sheet for assigning a dealer
void showAssignConfirmation(int userId) {
actionConfirmationBottomSheet(
context: context,
title: LocaleKeys.assignDealerUser.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.confirmAssignDealerUser.tr(),
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.yes.tr(),
fontSize: 15,
onPressed: () async {
Navigator.pop(context);
await assignUser(userId);
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: LocaleKeys.no.tr(),
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
// Show confirmation bottom sheet for removing a dealer
void showRemoveConfirmation(int userId) {
actionConfirmationBottomSheet(
context: context,
title: LocaleKeys.removeDealerUser.tr().toText(fontSize: 28, isBold: true, letterSpacing: -1.44),
subtitle: LocaleKeys.confirmRemoveDealerUser.tr(),
actionButtonYes: Expanded(
child: ShowFillButton(
maxHeight: 55,
title: LocaleKeys.yes.tr(),
fontSize: 15,
backgroundColor: Colors.red,
onPressed: () async {
Navigator.pop(context);
await removeUser(userId);
},
),
),
actionButtonNo: Expanded(
child: ShowFillButton(
maxHeight: 55,
isFilled: false,
borderColor: MyColors.darkPrimaryColor,
title: LocaleKeys.no.tr(),
txtColor: MyColors.darkPrimaryColor,
fontSize: 15,
onPressed: () {
Navigator.pop(context);
},
),
),
);
}
// Assign a single user
Future<void> assignUser(int userId) async {
if (provider == null) return;
Utils.showLoading(context);
var map = {
"dealershipUserIDs": [userId],
"serviceProviderBranchID": widget.branchId,
};
GenericRespModel response = await provider!.assignDealerToBranch(map);
Utils.hideLoading(context);
if (response.messageStatus == 1) {
Utils.showToast(response.message.toString());
await getBranchUsers(); // Refresh the list
widget.callBackFunc();
} else {
Utils.showToast(response.message.toString());
// Revert checkbox on error
final user = provider!.allProviderDealersList.firstWhere((u) => u.id == userId);
user.isBranchUser = false;
provider!.notifyListeners();
}
}
// Remove a single user
Future<void> removeUser(int userId) async {
if (provider == null) return;
Utils.showLoading(context);
var map = {
"dealershipUserIDs": [userId],
"serviceProviderBranchID": widget.branchId,
};
GenericRespModel response = await provider!.removeDealerFromBranch(map);
Utils.hideLoading(context);
if (response.messageStatus == 1) {
Utils.showToast(response.message.toString());
await getBranchUsers(); // Refresh the list
widget.callBackFunc();
} else {
Utils.showToast(response.message.toString());
// Revert checkbox on error
final user = provider!.allProviderDealersList.firstWhere((u) => u.id == userId);
user.isBranchUser = true;
provider!.notifyListeners();
}
} }
@override @override
@ -82,55 +238,20 @@ class _AssignDealerUserSheetState extends State<AssignDealerUserSheet> {
height: 1, height: 1,
color: Colors.grey[100], color: Colors.grey[100],
), ),
Column( ShowFillButton(
children: [ title: ("+ ${LocaleKeys.createNewUser.tr()}"),
ShowFillButton( maxWidth: double.infinity,
title: LocaleKeys.assignDealerUser.tr(), maxHeight: 55,
maxWidth: double.infinity, fontSize: 15,
maxHeight: 55, backgroundColor: Colors.transparent,
fontSize: 15, borderColor: MyColors.darkPrimaryColor,
onPressed: () async { txtColor: MyColors.darkPrimaryColor,
if (validation()) { isFilled: false,
Utils.showLoading(context); onPressed: () {
List<int> userIds = []; context.read<DashboardVMProvider>().checkUserSubscription(SubscriptionActionTypeEnum.users, context, callback: () {
for (var element in provider.allProviderDealersList) { getBranchUsers();
if (element.isBranchUser ?? false) { });
userIds.add(element.id); },
}
}
var map = {
"dealershipUserIDs": userIds,
"serviceProviderBranchID": widget.branchId,
};
GenericRespModel response = await provider.assignDealerToBranch(map);
Utils.hideLoading(context);
if (response.messageStatus == 1) {
Utils.showToast(response.message.toString());
pop(context);
widget.callBackFunc();
} else {
Utils.showToast(response.message.toString());
}
}
},
),
12.height,
ShowFillButton(
title: ("+ ${LocaleKeys.createNewUser.tr()}"),
maxWidth: double.infinity,
maxHeight: 55,
fontSize: 15,
backgroundColor: Colors.transparent,
borderColor: MyColors.darkPrimaryColor,
txtColor: MyColors.darkPrimaryColor,
isFilled: false,
onPressed: () {
context.read<DashboardVMProvider>().checkUserSubscription(SubscriptionActionTypeEnum.users, context, callback: () {
getBranchUsers();
});
},
)
],
) )
], ],
), ),
@ -145,11 +266,7 @@ class _AssignDealerUserSheetState extends State<AssignDealerUserSheet> {
Checkbox( Checkbox(
value: model.allProviderDealersList[index].isBranchUser, value: model.allProviderDealersList[index].isBranchUser,
onChanged: (v) { onChanged: (v) {
for (var element in model.allProviderDealersList) { onUserSelectionChanged(model.allProviderDealersList[index].id, v);
element.isBranchUser = false;
}
model.allProviderDealersList[index].isBranchUser = v ?? false;
model.notifyListeners();
}, },
visualDensity: VisualDensity.comfortable, visualDensity: VisualDensity.comfortable,
), ),
@ -185,14 +302,5 @@ class _AssignDealerUserSheetState extends State<AssignDealerUserSheet> {
], ],
); );
} }
bool validation() {
bool isValid = false;
for (var element in provider.allProviderDealersList) {
if (element.isBranchUser ?? false) {
isValid = true;
}
}
return isValid;
}
} }

@ -8,12 +8,14 @@ import 'package:mc_common_app/models/appointments_models/appointment_list_model.
import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/enums.dart';
import 'package:mc_common_app/utils/navigator.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/ad_view_model.dart';
import 'package:mc_common_app/view_models/appointments_view_model.dart';
import 'package:mc_common_app/view_models/dashboard_view_model_provider.dart'; import 'package:mc_common_app/view_models/dashboard_view_model_provider.dart';
import 'package:mc_common_app/views/advertisement/components/ads_list_widget.dart'; import 'package:mc_common_app/views/advertisement/components/ads_list_widget.dart';
import 'package:mc_common_app/views/appointments/widgets/common_appointment_slider_widget.dart'; import 'package:mc_common_app/views/appointments/widgets/common_appointment_slider_widget.dart';
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart'; import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
import 'package:mc_common_app/widgets/common_widgets/view_all_widget.dart'; import 'package:mc_common_app/widgets/common_widgets/view_all_widget.dart';
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
import 'package:mc_common_app/widgets/shimmer/home_shimmer_widgets.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class HomeFragment extends StatelessWidget { class HomeFragment extends StatelessWidget {
@ -59,15 +61,25 @@ class HomeFragment extends StatelessWidget {
context.read<DashboardVMProvider>().onNavbarTapped(1); context.read<DashboardVMProvider>().onNavbarTapped(1);
}, },
).horPaddingMain(), ).horPaddingMain(),
CommonAppointmentSliderWidget( Consumer<AppointmentsVM>(
onAppointmentClick: (AppointmentListModel value) => navigateWithName( builder: (BuildContext context, AppointmentsVM appointmentVM, Widget? child) {
context, // Show shimmer when loading
AppRoutes.appointmentDetailList, if (appointmentVM.state == ViewState.busy) {
arguments: value, return const AppointmentSliderShimmer();
)).toWhiteContainer(width: double.infinity, backgroundColor: Colors.transparent).margin( }
// Show actual data when idle
return CommonAppointmentSliderWidget(
onAppointmentClick: (AppointmentListModel value) => navigateWithName(
context,
AppRoutes.appointmentDetailList,
arguments: value,
),
).toWhiteContainer(width: double.infinity, backgroundColor: Colors.transparent).margin(
left: 21, left: 21,
right: 21, right: 21,
), );
},
),
24.height, 24.height,
ViewAllWidget( ViewAllWidget(
title: LocaleKeys.myBranches.tr(), title: LocaleKeys.myBranches.tr(),
@ -83,65 +95,76 @@ class HomeFragment extends StatelessWidget {
), ),
Consumer( Consumer(
builder: (BuildContext context, AdVM adVM, Widget? child) { builder: (BuildContext context, AdVM adVM, Widget? child) {
// Show shimmer when loading
if (adVM.state == ViewState.busy) { if (adVM.state == ViewState.busy) {
return const Padding( return Padding(
padding: EdgeInsets.all(30), padding: const EdgeInsets.only(top: 15),
child: Center(child: CircularProgressIndicator()), child: Column(
); children: [
} else { ViewAllWidget(
return Column( title: LocaleKeys.my_active_Ads.tr().toUpperCase(),
children: [ subTitle: LocaleKeys.view_all.tr(),
if (adVM.myActiveAdsForHome.isNotEmpty) ...[ onSubtitleTapped: () {},
Column( ).horPaddingMain(),
children: [ 14.height,
15.height, const AdsListShimmer(itemCount: 2),
ViewAllWidget(
title: LocaleKeys.my_active_Ads.tr().toUpperCase(),
subTitle: LocaleKeys.view_all.tr(),
onSubtitleTapped: () {
context.read<DashboardVMProvider>().onNavbarTapped(3);
context.read<AdVM>().updateIsExploreAds(true);
context.read<AdVM>().applyFilterOnExploreAds(vehicleBrandId: 0); // allAds
},
).horPaddingMain(),
AdsListWidget(
isDraftAds: false,
shouldShowAdStatus: true,
isAdsFragment: false,
adsList: adVM.myActiveAdsForHome,
scrollPhysics: const NeverScrollableScrollPhysics(),
hasMoreData: adVM.isExploreAdsTapped ? adVM.hasMoreDataForExploreAds : adVM.hasMoreDataForMyAds,
),
],
)
], ],
if (adVM.exploreAds.isNotEmpty) ...[ ),
Column(
children: [
15.height,
ViewAllWidget(
title: LocaleKeys.myRecommendedAds.tr().toUpperCase(),
subTitle: LocaleKeys.view_all.tr(),
onSubtitleTapped: () {
context.read<DashboardVMProvider>().onNavbarTapped(3);
context.read<AdVM>().updateIsExploreAds(true);
context.read<AdVM>().applyFilterOnExploreAds(vehicleBrandId: 0); // allAds
},
).horPaddingMain(),
AdsListWidget(
isDraftAds: false,
shouldShowAdStatus: false,
adsList: adVM.exploreAds.length >= 3 ? adVM.exploreAds.take(3).toList() : adVM.exploreAds,
isAdsFragment: false,
scrollPhysics: const NeverScrollableScrollPhysics(),
hasMoreData: adVM.isExploreAdsTapped ? adVM.hasMoreDataForExploreAds : adVM.hasMoreDataForMyAds,
),
],
)
]
],
); );
} }
// Show actual data when idle
return Column(
children: [
if (adVM.myActiveAdsForHome.isNotEmpty) ...[
Column(
children: [
15.height,
ViewAllWidget(
title: LocaleKeys.my_active_Ads.tr().toUpperCase(),
subTitle: LocaleKeys.view_all.tr(),
onSubtitleTapped: () {
context.read<DashboardVMProvider>().onNavbarTapped(3);
context.read<AdVM>().updateIsExploreAds(true);
context.read<AdVM>().applyFilterOnExploreAds(vehicleBrandId: 0); // allAds
},
).horPaddingMain(),
AdsListWidget(
isDraftAds: false,
shouldShowAdStatus: true,
isAdsFragment: false,
adsList: adVM.myActiveAdsForHome,
scrollPhysics: const NeverScrollableScrollPhysics(),
hasMoreData: adVM.isExploreAdsTapped ? adVM.hasMoreDataForExploreAds : adVM.hasMoreDataForMyAds,
),
],
)
],
if (adVM.exploreAds.isNotEmpty) ...[
Column(
children: [
15.height,
ViewAllWidget(
title: LocaleKeys.myRecommendedAds.tr().toUpperCase(),
subTitle: LocaleKeys.view_all.tr(),
onSubtitleTapped: () {
context.read<DashboardVMProvider>().onNavbarTapped(3);
context.read<AdVM>().updateIsExploreAds(true);
context.read<AdVM>().applyFilterOnExploreAds(vehicleBrandId: 0); // allAds
},
).horPaddingMain(),
AdsListWidget(
isDraftAds: false,
shouldShowAdStatus: false,
adsList: adVM.exploreAds.length >= 3 ? adVM.exploreAds.take(3).toList() : adVM.exploreAds,
isAdsFragment: false,
scrollPhysics: const NeverScrollableScrollPhysics(),
hasMoreData: adVM.isExploreAdsTapped ? adVM.hasMoreDataForExploreAds : adVM.hasMoreDataForMyAds,
),
],
)
]
],
);
}, },
).toContainer(backgroundColor: const Color(0xFFF8F8F8), padding: EdgeInsets.zero, margin: EdgeInsets.zero), ).toContainer(backgroundColor: const Color(0xFFF8F8F8), padding: EdgeInsets.zero, margin: EdgeInsets.zero),
21.height, 21.height,

@ -68,7 +68,7 @@ class _SubscriptionsPageState extends State<SubscriptionsPage> {
}, },
), ),
21.height, 21.height,
ListView.separated( ListView.separated(
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return AllSubscriptionsCard( return AllSubscriptionsCard(
subcriptionsVM.tempSubscriptions[index], subcriptionsVM.tempSubscriptions[index],

@ -18,7 +18,8 @@ class MySubscriptionsCard extends StatelessWidget {
Function onUpgradeSubscriptionClick; Function onUpgradeSubscriptionClick;
bool isMainPage; bool isMainPage;
MySubscriptionsCard(this.subscriptionVM, {super.key, this.isSubscribed = false, this.backgroundColor, required this.onRenewSubscriptionClick, required this.onUpgradeSubscriptionClick, this.isMainPage = true}); MySubscriptionsCard(this.subscriptionVM,
{super.key, this.isSubscribed = false, this.backgroundColor, required this.onRenewSubscriptionClick, required this.onUpgradeSubscriptionClick, this.isMainPage = true});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -101,13 +102,15 @@ class MySubscriptionsCard extends StatelessWidget {
), ),
Row( Row(
children: [ children: [
"${subscriptionVM.isExpired! ? LocaleKeys.expired.tr() : LocaleKeys.expiresOn.tr()} ${DateFormat('d MMMM yyyy', context.locale.toString()).format(DateHelper.parseStringToDate(DateHelper.formatDateT(subscriptionVM.dateEnd ?? "")))}".toText(fontSize: 14, letterSpacing: -0.56, color: textColor, fontWeight: MyFonts.Medium), "${subscriptionVM.isExpired! ? LocaleKeys.expired.tr() : LocaleKeys.expiresOn.tr()} ${DateFormat('d MMMM yyyy', context.locale.toString()).format(DateHelper.parseStringToDate(DateHelper.formatDateT(subscriptionVM.dateEnd ?? "")))}"
.toText(fontSize: 14, letterSpacing: -0.56, color: textColor, fontWeight: MyFonts.Medium),
// "${subscriptionVM.isExpired! ? LocaleKeys.expired.tr() : LocaleKeys.expiresOn.tr()} ${DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(subscriptionVM.dateEnd ?? "")))}".toText(fontSize: 14, letterSpacing: -0.56, color: textColor, fontWeight: MyFonts.Medium), // "${subscriptionVM.isExpired! ? LocaleKeys.expired.tr() : LocaleKeys.expiresOn.tr()} ${DateHelper.formatAsDayMonthYear(DateHelper.parseStringToDate(DateHelper.formatDateT(subscriptionVM.dateEnd ?? "")))}".toText(fontSize: 14, letterSpacing: -0.56, color: textColor, fontWeight: MyFonts.Medium),
if (subscriptionVM.isRenewable!) if (subscriptionVM.isRenewable!)
Row( Row(
children: [ children: [
6.width, 6.width,
LocaleKeys.renew.tr().toText(color: MyColors.primaryColor, fontSize: 14, letterSpacing: -0.56, textDecoration: TextDecoration.underline, decorationColor: MyColors.primaryColor, fontWeight: MyFonts.Medium), LocaleKeys.renew.tr().toText(
color: MyColors.primaryColor, fontSize: 14, letterSpacing: -0.56, textDecoration: TextDecoration.underline, decorationColor: MyColors.primaryColor, fontWeight: MyFonts.Medium),
], ],
).onPress(() { ).onPress(() {
onRenewSubscriptionClick(); onRenewSubscriptionClick();

@ -76,6 +76,8 @@ flutter:
# - packages/mc_common_app/assets/images/ # - packages/mc_common_app/assets/images/
# fonts: # fonts:
# - family: Poppins # - family: Poppins
# fonts: # fonts:

Loading…
Cancel
Save