Bug Fixes
parent
67cf63f732
commit
c56aa5d32e
@ -1,433 +0,0 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:car_provider_app/views/dashboard/widget/general_appointment_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:mc_common_app/classes/consts.dart';
|
||||
import 'package:mc_common_app/extensions/int_extensions.dart';
|
||||
import 'package:mc_common_app/extensions/string_extensions.dart';
|
||||
import 'package:mc_common_app/generated/locale_keys.g.dart';
|
||||
import 'package:mc_common_app/models/appointments_models/appointment_list_model.dart';
|
||||
import 'package:mc_common_app/models/general_models/generic_resp_model.dart';
|
||||
import 'package:mc_common_app/theme/colors.dart';
|
||||
import 'package:mc_common_app/utils/navigator.dart';
|
||||
import 'package:mc_common_app/utils/enums.dart';
|
||||
import 'package:mc_common_app/utils/utils.dart';
|
||||
import 'package:mc_common_app/view_models/appointments_view_model.dart';
|
||||
import 'package:mc_common_app/view_models/service_view_model.dart';
|
||||
import 'package:mc_common_app/widgets/button/show_fill_button.dart';
|
||||
import 'package:mc_common_app/widgets/common_widgets/app_bar.dart';
|
||||
import 'package:mc_common_app/widgets/dropdown/dropdow_field.dart';
|
||||
import 'package:mc_common_app/widgets/extensions/extensions_widget.dart';
|
||||
import 'package:mc_common_app/widgets/txt_field.dart';
|
||||
import 'package:mc_common_app/models/appointments_models/selected_appointment_service_model.dart';
|
||||
import 'package:car_provider_app/views/appoinments/widget/add_appointment_service_bottom_sheet.dart';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class AddNewServiceAppointmentPage extends StatefulWidget {
|
||||
final AppointmentListModel appointmentListModel;
|
||||
|
||||
const AddNewServiceAppointmentPage(this.appointmentListModel, {Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<AddNewServiceAppointmentPage> createState() =>
|
||||
_AddNewServiceAppointmentPageState();
|
||||
}
|
||||
|
||||
class _AddNewServiceAppointmentPageState
|
||||
extends State<AddNewServiceAppointmentPage> {
|
||||
final List<SelectedAppointmentServiceModel> appointmentSelectedServices = [];
|
||||
ServiceVM? serviceVM;
|
||||
bool isSubmitting = false;
|
||||
|
||||
DropValue? selectedCurrency;
|
||||
final List<DropValue> currencies = [
|
||||
DropValue(1, 'SAR', 'SAR'),
|
||||
DropValue(2, 'AED', 'AED'),
|
||||
DropValue(3, 'USD', 'USD'),
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
scheduleMicrotask(() {
|
||||
if (!mounted) return;
|
||||
serviceVM = context.read<ServiceVM>();
|
||||
serviceVM!.fetchBranchCategory(
|
||||
EasyLocalization.of(context)?.currentLocale?.countryCode ?? 'SA',
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _openAddServiceSheet({int? editIndex}) async {
|
||||
if (isSubmitting) return;
|
||||
final existing = editIndex == null
|
||||
? null
|
||||
: appointmentSelectedServices[editIndex];
|
||||
|
||||
final result = await showModalBottomSheet<SelectedAppointmentServiceModel>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
enableDrag: true,
|
||||
builder: (_) => AddAppointmentServiceBottomSheet(
|
||||
branchId: widget.appointmentListModel.branchId ?? 0,
|
||||
initialService: existing,
|
||||
),
|
||||
);
|
||||
|
||||
if (!mounted || result == null) return;
|
||||
setState(() {
|
||||
if (editIndex == null) {
|
||||
appointmentSelectedServices.add(result);
|
||||
} else {
|
||||
appointmentSelectedServices[editIndex] = result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
double get _totalItemsPrice => appointmentSelectedServices.fold<double>(
|
||||
0,
|
||||
(sum, selected) => sum + selected.totalPrice,
|
||||
);
|
||||
|
||||
List<int> get _allSelectedItemIds => appointmentSelectedServices
|
||||
.expand((selected) => selected.itemIds)
|
||||
.where((id) => id > 0)
|
||||
.toSet()
|
||||
.toList();
|
||||
|
||||
Future<void> _submit() async {
|
||||
if (isSubmitting) return;
|
||||
final vm = serviceVM ?? context.read<ServiceVM>();
|
||||
Map<String, dynamic> postParams;
|
||||
|
||||
if (vm.customServiceEnabledStatus) {
|
||||
if (!vm.isCustomServiceDetailValidated()) return;
|
||||
postParams = {
|
||||
'providerBranchID': widget.appointmentListModel.branchId,
|
||||
'appointmentID': widget.appointmentListModel.id,
|
||||
'serviceItemsFreeText': [
|
||||
{
|
||||
'name': vm.customServiceTitle,
|
||||
'price': vm.customServicePrice,
|
||||
'tax': vm.customerServiceTax,
|
||||
}
|
||||
],
|
||||
};
|
||||
} else {
|
||||
final ids = _allSelectedItemIds;
|
||||
if (ids.isEmpty) {
|
||||
Utils.showToast(LocaleKeys.pleaseSelectItems.tr());
|
||||
return;
|
||||
}
|
||||
postParams = {
|
||||
'providerBranchID': widget.appointmentListModel.branchId,
|
||||
'appointmentID': widget.appointmentListModel.id,
|
||||
'serviceItemID': ids,
|
||||
};
|
||||
}
|
||||
|
||||
setState(() => isSubmitting = true);
|
||||
bool loadingVisible = false;
|
||||
try {
|
||||
Utils.showLoading(context);
|
||||
loadingVisible = true;
|
||||
final GenericRespModel res =
|
||||
await vm.addNewServiceInAppointment(postParams);
|
||||
if (!mounted) return;
|
||||
Utils.hideLoading(context);
|
||||
loadingVisible = false;
|
||||
|
||||
if (res.messageStatus == 1) {
|
||||
vm.resetCustomServiceForm();
|
||||
await _updateAppointment(context, widget.appointmentListModel.branchId ?? 0);
|
||||
if (!mounted) return;
|
||||
Utils.showToast(LocaleKeys.itemsAddedSuccessfully.tr());
|
||||
pop(context);
|
||||
} else {
|
||||
Utils.showToast(res.message.toString());
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) Utils.showToast(e.toString());
|
||||
} finally {
|
||||
if (mounted && loadingVisible) Utils.hideLoading(context);
|
||||
if (mounted) setState(() => isSubmitting = false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: MyColors.white,
|
||||
appBar: CustomAppBar(title: LocaleKeys.addService.tr()),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(21.0),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
GeneralAppointmentWidget(
|
||||
appointmentListModel: widget.appointmentListModel,
|
||||
isNeedTotalPayment: true,
|
||||
isNeedToShowItems: true,
|
||||
isNeedToShowToMoreText: false,
|
||||
onTap: () {},
|
||||
),
|
||||
12.height,
|
||||
Consumer<ServiceVM>(
|
||||
builder: (context, ServiceVM serviceVm, _) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
LocaleKeys.customService.tr().toText(fontSize: 18),
|
||||
8.width,
|
||||
Center(
|
||||
child: Checkbox(
|
||||
value: serviceVm.customServiceEnabledStatus,
|
||||
onChanged: (value) {
|
||||
serviceVm.updateCustomServiceEnabledStatus(
|
||||
value ?? false,
|
||||
);
|
||||
},
|
||||
activeColor: MyColors.darkPrimaryColor,
|
||||
checkColor: MyColors.white,
|
||||
fillColor: serviceVm.customServiceEnabledStatus
|
||||
? WidgetStateProperty.all(MyColors.darkPrimaryColor)
|
||||
: WidgetStateProperty.all(MyColors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
5.height,
|
||||
if (serviceVm.customServiceEnabledStatus) ...[
|
||||
LocaleKeys.addServiceDetails.tr().toText(fontSize: 16),
|
||||
12.height,
|
||||
Column(
|
||||
children: [
|
||||
8.height,
|
||||
TxtField(
|
||||
value: serviceVm.customServiceTitle,
|
||||
errorValue: serviceVm.customServiceTitleError,
|
||||
hint: LocaleKeys.serviceName.tr(),
|
||||
onChanged: (v) => serviceVm.updateCustomServiceTitle(v),
|
||||
),
|
||||
8.height,
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: TxtField(
|
||||
value: serviceVm.customServicePrice,
|
||||
errorValue: serviceVm.customServicePriceError,
|
||||
keyboardType: TextInputType.number,
|
||||
numbersOnly: true,
|
||||
hint: LocaleKeys.price.tr(),
|
||||
onChanged: (v) => serviceVm.updateCustomServicePrice(v),
|
||||
),
|
||||
),
|
||||
8.width,
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: DropdownField(
|
||||
(DropValue value) {
|
||||
setState(() => selectedCurrency = value);
|
||||
},
|
||||
dropdownValue: selectedCurrency,
|
||||
list: currencies,
|
||||
hint: 'Currency',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
8.height,
|
||||
TxtField(
|
||||
postfixWidget: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
'%'.toText(
|
||||
fontWeight: MyFonts.Medium,
|
||||
fontSize: 15,
|
||||
color: borderColor,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
isNeedClickAll: true,
|
||||
isBackgroundEnabled: true,
|
||||
hint: LocaleKeys.tax.tr(),
|
||||
onTap: () {},
|
||||
value: serviceVm.customerServiceTax,
|
||||
errorValue: '',
|
||||
keyboardType: TextInputType.number,
|
||||
numbersOnly: true,
|
||||
onChanged: (v) => serviceVm.updateCustomerServiceTax(v),
|
||||
),
|
||||
],
|
||||
),
|
||||
] else ...[
|
||||
// READY-MADE SERVICES: select via Bottom Sheet, display cards.
|
||||
LocaleKeys.selectServiceDetails.tr().toText(fontSize: 16),
|
||||
12.height,
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: OutlinedButton.icon(
|
||||
onPressed: isSubmitting ? null : () => _openAddServiceSheet(),
|
||||
icon: const Icon(Icons.add_circle_outline),
|
||||
label: Text(LocaleKeys.addServices.tr()),
|
||||
style: OutlinedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(52),
|
||||
foregroundColor: MyColors.darkPrimaryColor,
|
||||
side: const BorderSide(color: MyColors.darkPrimaryColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
12.height,
|
||||
...appointmentSelectedServices.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final selected = entry.value;
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
selected.service.value,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Edit
|
||||
MyAssets.icEdit.buildSvg(
|
||||
height: 17,
|
||||
width: 17,
|
||||
).onPress(() {
|
||||
_openAddServiceSheet(editIndex: index);
|
||||
}),
|
||||
const SizedBox(width: 10),
|
||||
// Delete
|
||||
MyAssets.closeWithOrangeBg.buildSvg(
|
||||
height: 30,
|
||||
width: 30,
|
||||
).onPress(() {
|
||||
setState(() {
|
||||
appointmentSelectedServices.removeAt(index);
|
||||
});
|
||||
}),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
selected.category.value,
|
||||
style: const TextStyle(color: Colors.grey),
|
||||
),
|
||||
8.height,
|
||||
...selected.items.map(
|
||||
(item) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: Text(item.name ?? '')),
|
||||
Text('${item.price ?? '0'} SAR'),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
Text(
|
||||
'${selected.totalPrice.toStringAsFixed(2)} SAR',
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
if (appointmentSelectedServices.isNotEmpty) ...[
|
||||
12.height,
|
||||
LocaleKeys.totalAdditionalAmount.tr().toText(
|
||||
fontSize: 14,
|
||||
isBold: true,
|
||||
color: MyColors.lightTextColor,
|
||||
),
|
||||
Text(
|
||||
'${_totalItemsPrice.toStringAsFixed(2)} SAR',
|
||||
style: const TextStyle(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
).toWhiteContainer(width: double.infinity, allPading: 12);
|
||||
},
|
||||
),
|
||||
85.height,
|
||||
],
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ShowFillButton(
|
||||
title: LocaleKeys.cancel.tr(),
|
||||
maxWidth: double.infinity,
|
||||
isFilled: false,
|
||||
txtColor: MyColors.redColor,
|
||||
borderColor: MyColors.redColor,
|
||||
onPressed: () {
|
||||
(serviceVM ?? context.read<ServiceVM>())
|
||||
.resetCustomServiceForm();
|
||||
pop(context);
|
||||
},
|
||||
),
|
||||
),
|
||||
12.width,
|
||||
Expanded(
|
||||
child: ShowFillButton(
|
||||
title: LocaleKeys.add.tr(),
|
||||
maxWidth: double.infinity,
|
||||
onPressed: _submit,
|
||||
),
|
||||
),
|
||||
],
|
||||
).toWhiteContainer(width: double.infinity),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _updateAppointment(BuildContext context, int branchId) async {
|
||||
await context.read<AppointmentsVM>().applyFilterOnAppointmentsVMForProviders(
|
||||
branchID: branchId,
|
||||
appointmentStatusEnum: AppointmentStatusEnum.allAppointments,
|
||||
isNeedCustomerFilter: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue