import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:marco/controller/finance/payment_request_detail_controller.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/widgets/my_text_style.dart'; import 'package:marco/helpers/utils/base_bottom_sheet.dart'; import 'package:marco/helpers/widgets/my_snackbar.dart'; import 'package:marco/model/expense/employee_selector_bottom_sheet.dart'; class UpdatePaymentRequestWithReimbursement extends StatefulWidget { final String expenseId; final String statusId; final void Function() onClose; const UpdatePaymentRequestWithReimbursement({ super.key, required this.expenseId, required this.onClose, required this.statusId, }); @override State createState() => _UpdatePaymentRequestWithReimbursement(); } class _UpdatePaymentRequestWithReimbursement extends State { final PaymentRequestDetailController controller = Get.find(); final TextEditingController commentCtrl = TextEditingController(); final TextEditingController txnCtrl = TextEditingController(); final TextEditingController tdsCtrl = TextEditingController(text: '0'); final TextEditingController baseAmountCtrl = TextEditingController(); final TextEditingController taxAmountCtrl = TextEditingController(); final RxString dateStr = ''.obs; @override void dispose() { commentCtrl.dispose(); txnCtrl.dispose(); tdsCtrl.dispose(); baseAmountCtrl.dispose(); taxAmountCtrl.dispose(); super.dispose(); } /// Employee selection bottom sheet void _showEmployeeList() async { await showModalBottomSheet( context: context, isScrollControlled: true, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), backgroundColor: Colors.transparent, builder: (_) => ReusableEmployeeSelectorBottomSheet( searchController: controller.employeeSearchController, searchResults: controller.employeeSearchResults, isSearching: controller.isSearchingEmployees, onSearch: controller.searchEmployees, onSelect: (emp) => controller.selectedReimbursedBy.value = emp, ), ); // Optional cleanup controller.employeeSearchController.clear(); controller.employeeSearchResults.clear(); } InputDecoration _inputDecoration(String hint) { return InputDecoration( hintText: hint, hintStyle: MyTextStyle.bodySmall(xMuted: true), filled: true, fillColor: Colors.grey.shade100, border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade300), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey.shade300), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: Colors.blueAccent, width: 1.5), ), contentPadding: MySpacing.all(16), ); } @override Widget build(BuildContext context) { return Obx(() { return BaseBottomSheet( title: "Proceed Payment", isSubmitting: controller.isLoading.value, onCancel: () { widget.onClose(); Navigator.pop(context); }, onSubmit: () async { // Mandatory fields validation if (commentCtrl.text.trim().isEmpty || txnCtrl.text.trim().isEmpty || dateStr.value.isEmpty || baseAmountCtrl.text.trim().isEmpty || taxAmountCtrl.text.trim().isEmpty) { showAppSnackbar( title: "Incomplete", message: "Please fill all mandatory fields", type: SnackbarType.warning, ); return; } try { // Parse inputs final parsedDate = DateFormat('dd-MM-yyyy').parse(dateStr.value, true); final baseAmount = double.tryParse(baseAmountCtrl.text.trim()) ?? 0; final taxAmount = double.tryParse(taxAmountCtrl.text.trim()) ?? 0; final tdsPercentage = tdsCtrl.text.trim().isEmpty ? null : tdsCtrl.text.trim(); // Call API final success = await controller.updatePaymentRequestStatus( statusId: widget.statusId, comment: commentCtrl.text.trim(), paidTransactionId: txnCtrl.text.trim(), paidById: controller.selectedReimbursedBy.value?.id, paidAt: parsedDate, baseAmount: baseAmount, taxAmount: taxAmount, tdsPercentage: tdsPercentage, ); // Show snackbar showAppSnackbar( title: success ? 'Success' : 'Error', message: success ? 'Payment updated successfully' : 'Failed to update payment', type: success ? SnackbarType.success : SnackbarType.error, ); if (success) { // Ensure bottom sheet closes and callback is called widget.onClose(); // optional callback for parent refresh if (Navigator.canPop(context)) { Navigator.pop(context); } else { Get.close(1); // fallback if Navigator can't pop } } } catch (e, st) { print("Error updating payment: $e\n$st"); showAppSnackbar( title: 'Error', message: 'Something went wrong. Please try again.', type: SnackbarType.error, ); } }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.labelMedium("Transaction ID*"), MySpacing.height(8), TextField( controller: txnCtrl, decoration: _inputDecoration("Enter transaction ID"), ), MySpacing.height(16), MyText.labelMedium("Transaction Date*"), MySpacing.height(8), GestureDetector( onTap: () async { final today = DateTime.now(); final firstDate = DateTime(2020); final lastDate = today; final picked = await showDatePicker( context: context, initialDate: today, firstDate: firstDate, lastDate: lastDate, ); if (picked != null) { dateStr.value = DateFormat('dd-MM-yyyy').format(picked); } }, child: AbsorbPointer( child: TextField( controller: TextEditingController(text: dateStr.value), decoration: _inputDecoration("Select Date").copyWith( suffixIcon: const Icon(Icons.calendar_today), ), ), ), ), MySpacing.height(16), MyText.labelMedium("Paid By (Optional)"), MySpacing.height(8), GestureDetector( onTap: _showEmployeeList, child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade300), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( controller.selectedReimbursedBy.value == null ? "Select Paid By" : '${controller.selectedReimbursedBy.value?.firstName ?? ''} ${controller.selectedReimbursedBy.value?.lastName ?? ''}', style: const TextStyle(fontSize: 14), ), const Icon(Icons.arrow_drop_down, size: 22), ], ), ), ), MySpacing.height(16), MyText.labelMedium("TDS Percentage (Optional)"), MySpacing.height(8), TextField( controller: tdsCtrl, keyboardType: TextInputType.number, decoration: _inputDecoration("Enter TDS Percentage"), ), MySpacing.height(16), MyText.labelMedium("Base Amount*"), MySpacing.height(8), TextField( controller: baseAmountCtrl, keyboardType: TextInputType.number, decoration: _inputDecoration("Enter Base Amount"), ), MySpacing.height(16), MyText.labelMedium("Tax Amount*"), MySpacing.height(8), TextField( controller: taxAmountCtrl, keyboardType: TextInputType.number, decoration: _inputDecoration("Enter Tax Amount"), ), MySpacing.height(16), MyText.labelMedium("Comment*"), MySpacing.height(8), TextField( controller: commentCtrl, decoration: _inputDecoration("Enter comment"), ), ], ), ); }); } }