import 'package:flutter/material.dart'; import 'package:mc_common_app/classes/app_state.dart'; import 'package:mc_common_app/extensions/int_extensions.dart'; import 'package:mc_common_app/extensions/string_extensions.dart'; import 'package:mc_common_app/generated/locale_keys.g.dart'; import 'package:mc_common_app/models/general_models/generic_resp_model.dart'; import 'package:mc_common_app/models/subscriptions_models/branch_user_selection_model.dart'; import 'package:mc_common_app/models/subscriptions_models/subscription_model.dart'; import 'package:mc_common_app/theme/colors.dart'; import 'package:mc_common_app/utils/enums.dart'; import 'package:mc_common_app/utils/navigator.dart'; import 'package:mc_common_app/utils/utils.dart'; import 'package:mc_common_app/view_models/payment_view_model.dart'; import 'package:mc_common_app/view_models/subscriptions_view_model.dart'; import 'package:mc_common_app/views/payments/payment_methods_view.dart'; import 'package:mc_common_app/widgets/button/show_fill_button.dart'; import 'package:mc_common_app/widgets/extensions/extensions_widget.dart'; import 'package:provider/provider.dart'; import 'package:easy_localization/easy_localization.dart'; class DowngradeDataSheet extends StatefulWidget { final Subscription subscription; const DowngradeDataSheet(this.subscription, {super.key}); @override State createState() => _DowngradeDataSheetState(); } class _DowngradeDataSheetState extends State { @override void initState() { super.initState(); context.read().getSPBranchUserGet(); } // The provider's current active plan (not the plan being downgraded to) holds how many // branches/sub-users they currently have -- confirmed with the user this is where // "current totals" come from, as opposed to widget.subscription which is the target plan. Subscription? get _currentSubscription { final list = AppState().getproviderSubscription; return list.isEmpty ? null : list.first; } // subscriptionBranches/subscriptionSubUsers are each plan's own allowed quota. // totalBranches/totalSubUsers looked plausible but turned out to be unrelated (they // read identically on both the current and target plan records in testing). int get _currentBranches => _currentSubscription?.subscriptionBranches ?? 0; int get _currentUsers => _currentSubscription?.subscriptionSubUsers ?? 0; int get _targetBranches => widget.subscription.subscriptionBranches ?? 0; int get _targetUsers => widget.subscription.subscriptionSubUsers ?? 0; int get _branchesToRemove => (_currentBranches - _targetBranches).clamp(0, _currentBranches); int get _usersToRemove => (_currentUsers - _targetUsers).clamp(0, _currentUsers); // Selecting a whole branch removes every user in it too, so that counts towards the // user quota as well, not just the branch quota. _SelectedCounts _selectedCounts(List branchSelectionList) { int selectedBranches = 0; int selectedUsers = 0; for (var branch in branchSelectionList) { if (branch.isSelected) { selectedBranches++; selectedUsers += branch.usersList.length; } else { selectedUsers += branch.usersList.where((u) => u.isSelected).length; } } return _SelectedCounts(selectedBranches, selectedUsers); } Widget _buildRequirementCounter(List branchSelectionList) { final counts = _selectedCounts(branchSelectionList); final remainingBranches = (_branchesToRemove - counts.branches).clamp(0, _branchesToRemove); final remainingUsers = (_usersToRemove - counts.users).clamp(0, _usersToRemove); if (remainingBranches == 0 && remainingUsers == 0) { return "You have selected enough branches/users to downgrade.".toText(fontSize: 13, color: MyColors.primaryColor, isBold: true); } final parts = [ if (remainingBranches > 0) "$remainingBranches more branch${remainingBranches == 1 ? '' : 'es'}", if (remainingUsers > 0) "$remainingUsers more user${remainingUsers == 1 ? '' : 's'}", ]; return "Select ${parts.join(' and ')} to downgrade.".toText(fontSize: 13, color: MyColors.textColor, isBold: true); } @override Widget build(BuildContext context) { final targetBranchesText = "$_targetBranches branch${_targetBranches == 1 ? '' : 'es'}"; final targetUsersText = "$_targetUsers sub-user${_targetUsers == 1 ? '' : 's'}"; return SizedBox( width: double.infinity, height: MediaQuery.of(context).size.height / 1.2, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.all(12), child: Column( children: [ "Deactivate the branches & users".toText( fontSize: 19, isBold: true, ), 8.height, LocaleKeys.downgradeDeactivateInstruction.tr(namedArgs: {'branches': targetBranchesText, 'users': targetUsersText}).toText( fontSize: 14, color: MyColors.textColor, ), 12.height, // isStartNow is passed as false for a downgrade, so the new package isn't // applied immediately -- it takes effect once the current one expires. Make // that explicit so it isn't mistaken for an immediate switch. Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Icon(Icons.info_outline_rounded, size: 18, color: MyColors.textColor), 8.width, Expanded( child: LocaleKeys.downgradeAppliedAfterExpiry.tr().toText(fontSize: 13, color: MyColors.textColor), ), ], ).toContainer( backgroundColor: MyColors.greyButtonColor, paddingAll: 10, borderRadius: 6, ), ], ), ), Expanded( child: Consumer( builder: (BuildContext context, model, Widget? child) { final counts = model.branchSelectionList == null ? _SelectedCounts(0, 0) : _selectedCounts(model.branchSelectionList!); final canProceed = model.branchSelectionList != null && counts.branches >= _branchesToRemove && counts.users >= _usersToRemove; return Column( children: [ if (model.branchSelectionList != null) Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: _buildRequirementCounter(model.branchSelectionList!), ), if (model.branchSelectionList != null) 8.height, Expanded( child: model.branchSelectionList == null ? const Center(child: CircularProgressIndicator()) : ListView.separated( itemBuilder: (context, index) { return showBranchItem(model, model.branchSelectionList![index]); }, separatorBuilder: (context, index) { return 12.height; }, padding: const EdgeInsets.all(12), itemCount: model.branchSelectionList?.length ?? 0, ), ), ShowFillButton( title: LocaleKeys.next.tr(), maxWidth: double.infinity, margin: const EdgeInsets.all(12), isDisabled: !canProceed, onPressed: () async { List listOfBranches = []; List listOfUsers = []; for (var branch in model.branchSelectionList!) { if (branch.isSelected) { listOfBranches.add(branch.branchId); for (var user in branch.usersList) { listOfUsers.add(user.userId); } } else { for (var user in branch.usersList) { if (user.isSelected) { listOfUsers.add(user.userId); } } } } if (listOfBranches.isNotEmpty || listOfUsers.isNotEmpty) { navigateTo( context, PaymentMethodsView( paymentType: PaymentTypes.subscription, onTempContinue: () async { Utils.showLoading(context); GenericRespModel response = await model.payForSubscription( subscriptionId: widget.subscription.id!, isStartNow: false, isRenew: false, amount: widget.subscription.price.toString(), isDegrade: true, listOfBranches: listOfBranches, listOfUsers: listOfUsers, ); Utils.hideLoading(context); if (response.messageStatus == 1) { if (response.message!.contains("Your current sub-users are not")) { Utils.showToast(response.message.toString()); context.read().getMySubscriptions(AppState().getUser.data?.userInfo?.providerId.toString() ?? ""); pop(context); return false; } else { context.read().updateOrderProviderSubscriptionId(id: response.data); // Utils.showToast( // "Subscriptions is successfully updated"); return true; } } else { Utils.showToast(response.message.toString()); context.read().getMySubscriptions(AppState().getUser.data?.userInfo?.providerId.toString() ?? ""); return false; } }, ), ); } }, ), ], ); }, ), ), ], ), ); } Widget showBranchItem(SubscriptionsVM viewModel, BranchSelectionModel model) { return Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ // Padding around the checkbox widens its tap target (18x18 alone is too small to // hit reliably) without growing the checkbox's own visual size or changing the // row's separate tap-to-expand behavior. Padding( padding: const EdgeInsets.all(10), child: customCheckBox(model.isSelected), ).onPress(() { model.isSelected = !model.isSelected; if (model.isSelected) { for (var user in model.usersList) { user.isSelected = true; } } else { for (var user in model.usersList) { user.isSelected = false; } } model.isOpend = true; viewModel.setState(ViewState.idle); }), 12.width, Expanded( child: Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: double.infinity, child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ model.branchName.toText( fontSize: 18, isBold: true, ), ("${model.usersList.length} Users").toText( fontSize: 10, color: MyColors.lightTextColor, ) ], ), ), Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Icon( model.isOpend ? Icons.keyboard_arrow_up_rounded : Icons.keyboard_arrow_down_rounded, ), ], ), ], ), ), if (model.isOpend) 12.height, if (model.isOpend) ListView.separated( itemBuilder: (context, index) { return Row( children: [ customCheckBox(model.usersList[index].isSelected), 8.width, model.usersList[index].userName.toText( fontSize: 14, ), ], ).onPress(() { // for (int i = 0; i < model.usersList.length; i++) { // if (i != index) { // model.usersList[index].isSelected = false; // } // } model.usersList[index].isSelected = !model.usersList[index].isSelected; viewModel.setState(ViewState.idle); }); }, separatorBuilder: (context, index) { return 12.height; }, physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: model.usersList.length, ), ], ).onPress(() { model.isOpend = !model.isOpend; viewModel.setState(ViewState.idle); }), ) ], ).toWhiteContainer( width: double.infinity, pading: const EdgeInsets.all(12), ); } Widget customCheckBox(bool isChecked) { return SizedBox( height: 18, width: 18, child: isChecked ? const Icon( Icons.done, color: Colors.white, size: 14, ) : const SizedBox(), ).toContainer( isEnabledBorder: true, paddingAll: 0, borderRadius: 0, borderWidget: 1, borderColor: MyColors.primaryColor, backgroundColor: isChecked ? MyColors.primaryColor : Colors.transparent, ); } } class _SelectedCounts { final int branches; final int users; _SelectedCounts(this.branches, this.users); }