You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
car_provider_app/lib/views/subscriptions/sheet/downgrade_data_sheet.dart

369 lines
16 KiB
Dart

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<DowngradeDataSheet> createState() => _DowngradeDataSheetState();
}
class _DowngradeDataSheetState extends State<DowngradeDataSheet> {
@override
void initState() {
super.initState();
context.read<SubscriptionsVM>().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<BranchSelectionModel> 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<BranchSelectionModel> 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 = <String>[
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<SubscriptionsVM>(
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<int> listOfBranches = [];
List<int> 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<SubscriptionsVM>().getMySubscriptions(AppState().getUser.data?.userInfo?.providerId.toString() ?? "");
pop(context);
return false;
} else {
context.read<PaymentVM>().updateOrderProviderSubscriptionId(id: response.data);
// Utils.showToast(
// "Subscriptions is successfully updated");
return true;
}
} else {
Utils.showToast(response.message.toString());
context.read<SubscriptionsVM>().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);
}