diff --git a/lib/view/finance/payment_request_detail_screen.dart b/lib/view/finance/payment_request_detail_screen.dart index acd46e5..0c25112 100644 --- a/lib/view/finance/payment_request_detail_screen.dart +++ b/lib/view/finance/payment_request_detail_screen.dart @@ -50,7 +50,6 @@ class _PaymentRequestDetailScreenState extends State void _checkPermissionToSubmit(PaymentRequestData request) { const draftStatusId = '6537018f-f4e9-4cb3-a210-6c3b2da999d7'; - final isCreatedByCurrentUser = employeeInfo?.id == request.createdBy.id; final hasDraftNextStatus = request.nextStatus.any((s) => s.id == draftStatusId); @@ -109,7 +108,6 @@ class _PaymentRequestDetailScreenState extends State body: SafeArea(child: Obx(() { if (controller.isLoading.value && controller.paymentRequest.value == null) { - // Show skeleton only if data not yet loaded return SkeletonLoaders.paymentRequestDetailSkeletonLoader(); } @@ -124,7 +122,6 @@ class _PaymentRequestDetailScreenState extends State return Center(child: MyText.bodyMedium("No data to display.")); } - // ✅ Actual content return MyRefreshIndicator( onRefresh: controller.fetchPaymentRequestDetail, child: SingleChildScrollView( @@ -140,14 +137,19 @@ class _PaymentRequestDetailScreenState extends State child: Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(5)), - elevation: 3, child: Padding( padding: const EdgeInsets.symmetric( - vertical: 14, horizontal: 14), + vertical: 12, horizontal: 14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _Header(request: request, colorParser: _parseColor), + _Header( + request: request, + colorParser: _parseColor, + employeeInfo: employeeInfo, + onEdit: () => + _openEditPaymentRequestBottomSheet(request), + ), const Divider(height: 30, thickness: 1.2), _Logs( logs: request.updateLogs, colorParser: _parseColor), @@ -168,36 +170,6 @@ class _PaymentRequestDetailScreenState extends State ); })), bottomNavigationBar: _buildBottomActionBar(), - - // ✅ Added Floating Action Button for Edit - floatingActionButton: Obx(() { - final request = controller.paymentRequest.value; - - // Don't show FAB if loading or data not loaded - if (controller.isLoading.value || - request == null || - employeeInfo == null) { - return const SizedBox.shrink(); - } - - final canEdit = PaymentRequestPermissionHelper.canEditPaymentRequest( - employeeInfo, - request, - ); - - if (!canEdit) return const SizedBox.shrink(); - - return FloatingActionButton.extended( - onPressed: () => _openEditPaymentRequestBottomSheet(request), - backgroundColor: contentTheme.primary, - icon: const Icon(Icons.edit), - label: MyText.bodyMedium( - "Edit Payment Request", - fontWeight: 600, - color: Colors.white, - ), - ); - }), ); } @@ -228,7 +200,6 @@ class _PaymentRequestDetailScreenState extends State .hasAnyPermission(status.permissionIds ?? []); }).toList(); - // Normal status buttons return SafeArea( child: Container( decoration: BoxDecoration( @@ -253,7 +224,6 @@ class _PaymentRequestDetailScreenState extends State ), ), onPressed: () async { - // If status is reimbursement, show reimbursement bottom sheet if (status.id == reimbursementStatusId) { showModalBottomSheet( context: context, @@ -268,15 +238,11 @@ class _PaymentRequestDetailScreenState extends State onClose: () {}, ), ); - - // If status is b8586f67-dc19-49c3-b4af-224149efe1d3, open create expense } else if (status.id == 'b8586f67-dc19-49c3-b4af-224149efe1d3') { showCreateExpenseBottomSheet( statusId: status.id, ); - - // Normal status flow } else { final comment = await showCommentBottomSheet( context, status.displayName); @@ -298,8 +264,11 @@ class _PaymentRequestDetailScreenState extends State if (success) await controller.fetchPaymentRequestDetail(); } }, - child: Text(status.displayName, - style: const TextStyle(color: Colors.white)), + child: MyText.bodySmall( + status.displayName, + color: Colors.white, + fontWeight: 600, + ), ); }).toList(), ), @@ -391,62 +360,102 @@ class PaymentRequestPermissionHelper { } } -class _Header extends StatelessWidget { +// ------------------ Sub-widgets ------------------ + +class _Header extends StatelessWidget with UIMixin { final PaymentRequestData request; final Color Function(String) colorParser; - const _Header({required this.request, required this.colorParser}); + final VoidCallback? onEdit; + final EmployeeInfo? employeeInfo; + + _Header({ + required this.request, + required this.colorParser, + this.onEdit, + this.employeeInfo, + }); @override Widget build(BuildContext context) { final statusColor = colorParser(request.expenseStatus.color); - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + + final canEdit = employeeInfo != null && + PaymentRequestPermissionHelper.canEditPaymentRequest( + employeeInfo, request); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: Row( - children: [ - const Icon(Icons.calendar_month, size: 18, color: Colors.grey), - MySpacing.width(6), - MyText.bodySmall('Created At:', fontWeight: 600), - MySpacing.width(6), - Expanded( - child: MyText.bodySmall( - DateTimeUtils.convertUtcToLocal( - request.createdAt.toIso8601String(), - format: 'dd MMM yyyy'), - fontWeight: 600, - overflow: TextOverflow.ellipsis, - ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + MyText.bodyMedium( + 'ID: ${request.paymentRequestUID}', + fontWeight: 700, + fontSize: 14, + ), + if (canEdit) + IconButton( + onPressed: onEdit, + icon: Icon(Icons.edit, color: contentTheme.primary), + tooltip: "Edit Payment Request", ), - ], - ), + ], ), - Container( - decoration: BoxDecoration( - color: statusColor.withOpacity(0.15), - borderRadius: BorderRadius.circular(5)), - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), - child: Row( - children: [ - Icon(Icons.flag, size: 16, color: statusColor), - MySpacing.width(4), - SizedBox( - width: 100, - child: MyText.labelSmall( - request.expenseStatus.displayName, - color: statusColor, - fontWeight: 600, - overflow: TextOverflow.ellipsis, - ), + const SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Row( + children: [ + const Icon(Icons.calendar_month, + size: 18, color: Colors.grey), + MySpacing.width(6), + MyText.bodySmall('Created At:', fontWeight: 600), + MySpacing.width(6), + Expanded( + child: MyText.bodySmall( + DateTimeUtils.convertUtcToLocal( + request.createdAt.toIso8601String(), + format: 'dd MMM yyyy'), + fontWeight: 600, + overflow: TextOverflow.ellipsis, + ), + ), + ], ), - ], - ), + ), + Container( + decoration: BoxDecoration( + color: statusColor.withOpacity(0.15), + borderRadius: BorderRadius.circular(5)), + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Row( + children: [ + Icon(Icons.flag, size: 16, color: statusColor), + MySpacing.width(4), + SizedBox( + width: 100, + child: MyText.labelSmall( + request.expenseStatus.displayName, + color: statusColor, + fontWeight: 600, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), + ], ), ], ); } } +// ------------------ Logs, Parties, Details, Documents ------------------ + class _Logs extends StatelessWidget { final List logs; final Color Function(String) colorParser; @@ -578,11 +587,32 @@ class _Parties extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _labelValueRow('Project', request.project.name), - _labelValueRow('Payee', request.payee), - _labelValueRow('Created By', - '${request.createdBy.firstName} ${request.createdBy.lastName}'), - _labelValueRow('Pre-Approved', request.isAdvancePayment ? 'Yes' : 'No'), + MyText.bodySmall("Parties:", fontWeight: 600), + MySpacing.height(8), + Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.labelMedium("Payee", fontWeight: 600), + MySpacing.height(2), + MyText.bodyMedium(request.payee), + ], + ), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MyText.labelMedium("Project", fontWeight: 600), + MySpacing.height(2), + MyText.bodyMedium(request.project.name), + ], + ), + ), + ], + ), ], ); } @@ -626,7 +656,7 @@ class _Documents extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.bodySmall("Documents:", fontWeight: 600), - const SizedBox(height: 12), + MySpacing.height(12), ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -679,7 +709,7 @@ class _Documents extends StatelessWidget { size: 20, color: Colors.grey[600]), const SizedBox(width: 7), Expanded( - child: MyText.labelSmall( + child: MyText.bodySmall( doc.fileName, overflow: TextOverflow.ellipsis, ),