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.
442 lines
20 KiB
Dart
442 lines
20 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:easy_localization/easy_localization.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:hmg_patient_app_new/core/app_assets.dart';
|
|
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
|
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
|
|
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/covid_get_test_proceedure_resp.dart';
|
|
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/covid_questionnare_model.dart';
|
|
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
|
|
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
|
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
|
|
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
|
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
|
|
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
|
|
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:hmg_patient_app_new/presentation/covid19test/covid_payment_screen.dart';
|
|
|
|
class CovidReviewScreen extends StatefulWidget {
|
|
|
|
final HospitalsModel selectedHospital;
|
|
final List<CovidQuestionnaireModel> qaList;
|
|
|
|
const CovidReviewScreen({super.key, required this.selectedHospital, required this.qaList});
|
|
|
|
@override
|
|
State<CovidReviewScreen> createState() => _CovidReviewScreenState();
|
|
}
|
|
|
|
class _CovidReviewScreenState extends State<CovidReviewScreen> {
|
|
|
|
late HmgServicesViewModel hmgServicesViewModel;
|
|
bool _acceptedTerms =false;
|
|
Covid19GetTestProceduresResp? _selectedProcedure;
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
hmgServicesViewModel = Provider.of<HmgServicesViewModel>(context, listen: false);
|
|
scheduleMicrotask(() {
|
|
|
|
});
|
|
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return CollapsingListView(
|
|
title: "COVID-19",
|
|
bottomChild: Consumer<HmgServicesViewModel>(builder: (context, vm, _) {
|
|
final info = vm.covidPaymentInfo;
|
|
return Container(
|
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
|
color: AppColors.whiteColor,
|
|
borderRadius: 24.r,
|
|
hasShadow: true,
|
|
),
|
|
child: SizedBox(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
if (info == null) ...[
|
|
// show a placeholder/loading while payment info is null
|
|
SizedBox(height: 24.h),
|
|
Center(child: CircularProgressIndicator()),
|
|
SizedBox(height: 24.h),
|
|
] else ...[
|
|
Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
LocaleKeys.amountBeforeTax.tr(context: context).toText18(isBold: true),
|
|
Utils.getPaymentAmountWithSymbol(
|
|
(info.patientShareField ?? 0).toString().toText16(isBold: true),
|
|
AppColors.blackColor,
|
|
13,
|
|
isSaudiCurrency: true),
|
|
],
|
|
),
|
|
SizedBox(height: 4.h),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
LocaleKeys.vat15.tr(context: context).toText14(isBold: true),
|
|
Utils.getPaymentAmountWithSymbol(
|
|
(info.patientTaxAmountField ?? 0).toString().toText16(isBold: true),
|
|
AppColors.blackColor,
|
|
13,
|
|
isSaudiCurrency: true)
|
|
],
|
|
),
|
|
SizedBox(height: 18.h),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
SizedBox(
|
|
width: 150.h,
|
|
child: Utils.getPaymentMethods(),
|
|
),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Utils.getPaymentAmountWithSymbol(
|
|
(info.patientShareWithTaxField ?? 0).toString().toText24(isBold: true),
|
|
AppColors.blackColor,
|
|
17,
|
|
isSaudiCurrency: true),
|
|
],
|
|
),
|
|
],
|
|
)
|
|
],
|
|
).paddingOnly(left: 16.h, top: 24.h, right: 16.h, bottom: 0.h),
|
|
|
|
GestureDetector(
|
|
onTap: () {
|
|
setState(() {
|
|
_acceptedTerms = !_acceptedTerms;
|
|
});
|
|
},
|
|
child: Container(
|
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
|
color: AppColors.whiteColor,
|
|
borderRadius: 12.r,
|
|
hasShadow: false,
|
|
),
|
|
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 12.h),
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
width: 20.w,
|
|
height: 20.h,
|
|
decoration: BoxDecoration(
|
|
color: _acceptedTerms
|
|
? AppColors.primaryRedColor
|
|
: AppColors.whiteColor,
|
|
border: Border.all(
|
|
color: _acceptedTerms
|
|
? AppColors.primaryRedColor
|
|
: AppColors.greyTextColor.withValues(alpha: 0.3),
|
|
width: 2,
|
|
),
|
|
borderRadius: BorderRadius.circular(4.r),
|
|
),
|
|
child: _acceptedTerms
|
|
? Icon(
|
|
Icons.check,
|
|
size: 14.h,
|
|
color: AppColors.whiteColor,
|
|
)
|
|
: null,
|
|
),
|
|
SizedBox(width: 12.w),
|
|
Expanded(
|
|
child: RichText(
|
|
text: TextSpan(
|
|
style: TextStyle(
|
|
fontSize: 14.f,
|
|
color: AppColors.textColor,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
children: [
|
|
const TextSpan(text: "I agree to the "),
|
|
TextSpan(
|
|
text: "terms and conditions",
|
|
style: TextStyle(
|
|
color: AppColors.primaryRedColor,
|
|
fontWeight: FontWeight.w600,
|
|
decoration: TextDecoration.underline,
|
|
decorationColor: AppColors.primaryRedColor,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
// Two-button layout: Cancel (left) and Next (right)
|
|
Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 24.h),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: CustomButton(
|
|
height: 56.h,
|
|
text: LocaleKeys.cancel.tr(),
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
backgroundColor: AppColors.secondaryLightRedColor,
|
|
borderColor: AppColors.secondaryLightRedColor,
|
|
textColor: AppColors.primaryRedColor,
|
|
// icon: AppAssets.cancel,
|
|
// iconColor: AppColors.primaryRedColor,
|
|
borderRadius: 12.r,
|
|
),
|
|
),
|
|
SizedBox(width: 8.h),
|
|
Expanded(
|
|
child: CustomButton(
|
|
height: 56.h,
|
|
text: LocaleKeys.next.tr(context: context),
|
|
onPressed: () async {
|
|
// Validate selection and payment info
|
|
if (_selectedProcedure == null) {
|
|
showCommonBottomSheetWithoutHeight(
|
|
context,
|
|
child: Utils.getErrorWidget(loadingText: "Please select a procedure"),
|
|
callBackFunc: () {},
|
|
isFullScreen: false,
|
|
isCloseButtonVisible: true,
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (info == null) {
|
|
// If payment info missing, attempt to fetch
|
|
LoaderBottomSheet.showLoader();
|
|
try {
|
|
await hmgServicesViewModel.getPaymentInfo(procedureID: _selectedProcedure!.procedureId!);
|
|
} catch (e) {
|
|
debugPrint('getPaymentInfo error: $e');
|
|
} finally {
|
|
LoaderBottomSheet.hideLoader();
|
|
}
|
|
|
|
if (hmgServicesViewModel.covidPaymentInfo == null) {
|
|
showCommonBottomSheetWithoutHeight(
|
|
context,
|
|
child: Utils.getErrorWidget(loadingText: "Payment information not available"),
|
|
callBackFunc: () {},
|
|
isFullScreen: false,
|
|
isCloseButtonVisible: true,
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Compute amount and project/clinic IDs defensively
|
|
final paymentInfo = hmgServicesViewModel.covidPaymentInfo ?? info!;
|
|
final double amount = paymentInfo.patientShareWithTaxField ?? (paymentInfo.patientShareField?.toDouble() ?? 0.0);
|
|
|
|
// projectID may be int or string; parse defensively
|
|
int projectID = 0;
|
|
try {
|
|
final p = widget.selectedHospital.mainProjectID;
|
|
if (p is int) projectID = p;
|
|
else if (p is String) projectID = int.tryParse(p) ?? 0;
|
|
} catch (_) {}
|
|
|
|
int clinicID = 0;
|
|
try {
|
|
clinicID = int.tryParse(widget.selectedHospital.setupID ?? '') ?? int.tryParse(widget.selectedHospital.iD?.toString() ?? '') ?? 0;
|
|
} catch (_) {}
|
|
|
|
// Navigate to payment screen
|
|
Navigator.of(context).push(
|
|
CustomPageRoute(
|
|
page: CovidPaymentScreen(
|
|
amount: amount,
|
|
projectID: projectID,
|
|
clinicID: clinicID,
|
|
procedureId: _selectedProcedure!.procedureId ?? '',
|
|
taxAmount: paymentInfo.patientTaxAmountField?.toDouble() ?? 0.0,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
backgroundColor: AppColors.primaryRedColor,
|
|
borderColor: AppColors.primaryRedColor,
|
|
textColor: AppColors.whiteColor,
|
|
fontSize: 16.f,
|
|
fontWeight: FontWeight.w500,
|
|
borderRadius: 12.r,
|
|
padding: EdgeInsets.symmetric(horizontal: 10.w),
|
|
// icon: AppAssets.add_icon,
|
|
// iconColor: AppColors.whiteColor,
|
|
iconSize: 18.h,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
]
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
child: SingleChildScrollView(
|
|
child: Padding(
|
|
padding: EdgeInsets.all(24.h),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
|
|
"Please select the procedure:".toText14(
|
|
color: AppColors.textColor,
|
|
weight: FontWeight.w500,
|
|
),
|
|
SizedBox(height: 16.h),
|
|
|
|
Consumer<HmgServicesViewModel>(
|
|
builder: (context, vm, _) {
|
|
final procedures = vm.covidTestProcedureList;
|
|
|
|
// ensure default selection is first item (once available)
|
|
if (_selectedProcedure == null && procedures.isNotEmpty) {
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
if (mounted) {
|
|
setState(() {
|
|
_selectedProcedure = procedures[0];
|
|
});
|
|
|
|
// also fetch payment info for default selection and show loader while loading
|
|
if (procedures[0].procedureId != null) {
|
|
LoaderBottomSheet.showLoader();
|
|
hmgServicesViewModel
|
|
.getPaymentInfo(procedureID: procedures[0].procedureId!)
|
|
.whenComplete(() {
|
|
LoaderBottomSheet.hideLoader();
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Use a shrink-wrapped ListView.separated and match prescription styling
|
|
return Container(
|
|
|
|
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
|
|
color: AppColors.whiteColor,
|
|
borderRadius: 20.h,
|
|
hasShadow: false,
|
|
),
|
|
child: Padding(
|
|
padding: EdgeInsets.symmetric(horizontal: 16.h),
|
|
child: ListView.separated(
|
|
shrinkWrap: true,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
itemCount: procedures.length,
|
|
separatorBuilder: (context, index) => Divider(
|
|
height: 1.h,
|
|
thickness: 1.h,
|
|
color: AppColors.borderOnlyColor.withValues(alpha: 0.05),
|
|
).paddingOnly(top:8,bottom:16.h),
|
|
itemBuilder: (context, index) {
|
|
final item = procedures[index];
|
|
// Let the radio option widget manage its own padding to avoid doubled spacing
|
|
return _buildRadioOption(value: item, title: item.procedureName ?? '');
|
|
},
|
|
).paddingOnly(bottom:16.h)
|
|
),
|
|
);
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
}
|
|
|
|
Widget _buildRadioOption({
|
|
required Covid19GetTestProceduresResp value,
|
|
required String title,
|
|
}) {
|
|
final bool isSelected = _selectedProcedure?.procedureId == value.procedureId;
|
|
|
|
return GestureDetector(
|
|
onTap: () async {
|
|
setState(() {
|
|
_selectedProcedure = value;
|
|
});
|
|
|
|
// show bottomsheet loader while fetching payment info
|
|
if (value.procedureId != null) {
|
|
LoaderBottomSheet.showLoader();
|
|
try {
|
|
await hmgServicesViewModel.getPaymentInfo(procedureID: value.procedureId!, projectID: widget.selectedHospital.mainProjectID);
|
|
} catch (e) {
|
|
debugPrint('getPaymentInfo error: $e');
|
|
} finally {
|
|
LoaderBottomSheet.hideLoader();
|
|
}
|
|
}
|
|
},
|
|
|
|
|
|
child: Row(
|
|
children: [
|
|
Container(
|
|
width: 20.h,
|
|
height: 20.h,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
border: Border.all(
|
|
color: isSelected
|
|
? AppColors.primaryRedColor
|
|
: AppColors.greyTextColor.withValues(alpha: 0.3),
|
|
width: 2,
|
|
),
|
|
),
|
|
child: isSelected
|
|
? Center(
|
|
child: Container(
|
|
width: 10.h,
|
|
height: 10.h,
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
color: AppColors.primaryRedColor,
|
|
),
|
|
),
|
|
)
|
|
: null,
|
|
),
|
|
SizedBox(width: 12.h),
|
|
Expanded(
|
|
child: title.toText14(
|
|
color: AppColors.textColor,
|
|
weight: FontWeight.w400,
|
|
),
|
|
),
|
|
],
|
|
)
|
|
// Keep only bottom padding here and rely on the surrounding container's left/right inset
|
|
.paddingOnly(left: 0.h, right: 0.h, top: 0.h, bottom: 12.h),
|
|
);
|
|
|
|
}
|
|
}
|