diff --git a/lib/controller/finance/payment_request_detail_controller.dart b/lib/controller/finance/payment_request_detail_controller.dart index b0d17fb..048495b 100644 --- a/lib/controller/finance/payment_request_detail_controller.dart +++ b/lib/controller/finance/payment_request_detail_controller.dart @@ -13,6 +13,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:geocoding/geocoding.dart'; import 'package:geolocator/geolocator.dart'; import 'package:mime/mime.dart'; +import 'package:marco/controller/finance/payment_request_controller.dart'; class PaymentRequestDetailController extends GetxController { final Rx paymentRequest = Rx(null); @@ -26,6 +27,8 @@ class PaymentRequestDetailController extends GetxController { final RxList employeeSearchResults = [].obs; final TextEditingController employeeSearchController = TextEditingController(); + PaymentRequestController get paymentRequestController => + Get.find(); final RxBool isSearchingEmployees = false.obs; // Attachments @@ -297,6 +300,7 @@ class PaymentRequestDetailController extends GetxController { message: 'Payment submitted successfully', type: SnackbarType.success); await fetchPaymentRequestDetail(); + paymentRequestController.fetchPaymentRequests(); } else { showAppSnackbar( title: 'Error', diff --git a/lib/helpers/widgets/expense/expense_main_components.dart b/lib/helpers/widgets/expense/expense_main_components.dart index ae04ff6..3cc4b77 100644 --- a/lib/helpers/widgets/expense/expense_main_components.dart +++ b/lib/helpers/widgets/expense/expense_main_components.dart @@ -305,7 +305,6 @@ class ExpenseList extends StatelessWidget { onTap: () async { final result = await Get.to( () => ExpenseDetailScreen(expenseId: expense.id), - arguments: {'expense': expense}, ); if (result == true && onViewDetail != null) { await onViewDetail!(); diff --git a/lib/model/service_project/service_project_job_detail_model.dart b/lib/model/service_project/service_project_job_detail_model.dart index 97a77fa..e7b48fc 100644 --- a/lib/model/service_project/service_project_job_detail_model.dart +++ b/lib/model/service_project/service_project_job_detail_model.dart @@ -47,6 +47,7 @@ class JobData { final User? createdBy; final List? tags; final List? updateLogs; + final ProjectBranch? projectBranch; JobData({ this.id, @@ -66,6 +67,7 @@ class JobData { this.createdBy, this.tags, this.updateLogs, + this.projectBranch, }); factory JobData.fromJson(Map? json) { @@ -76,7 +78,8 @@ class JobData { title: json['title'] as String?, description: json['description'] as String?, jobTicketUId: json['jobTicketUId'] as String?, - project: json['project'] != null ? Project.fromJson(json['project']) : null, + project: + json['project'] != null ? Project.fromJson(json['project']) : null, assignees: (json['assignees'] as List?) ?.map((e) => Assignee.fromJson(e)) .toList() ?? @@ -89,7 +92,8 @@ class JobData { attendanceId: json['attendanceId'] as String?, nextTaggingAction: json['nextTaggingAction'] as int?, createdAt: json['createdAt'] as String?, - createdBy: json['createdBy'] != null ? User.fromJson(json['createdBy']) : null, + createdBy: + json['createdBy'] != null ? User.fromJson(json['createdBy']) : null, tags: (json['tags'] as List?) ?.map((e) => Tag.fromJson(e)) .toList() ?? @@ -98,6 +102,9 @@ class JobData { ?.map((e) => UpdateLog.fromJson(e)) .toList() ?? [], + projectBranch: json['projectBranch'] != null + ? ProjectBranch.fromJson(json['projectBranch']) + : null, ); } } @@ -170,6 +177,26 @@ class Assignee { } } +class ProjectBranch { + final String? id; + final String? branchName; + final String? branchType; + + ProjectBranch({ + this.id, + this.branchName, + this.branchType, + }); + + factory ProjectBranch.fromJson(Map json) { + return ProjectBranch( + id: json['id'], + branchName: json['branchName'], + branchType: json['branchType'], + ); + } +} + class Status { final String? id; final String? name; @@ -266,9 +293,12 @@ class UpdateLog { return UpdateLog( id: json['id'] as String?, status: json['status'] != null ? Status.fromJson(json['status']) : null, - nextStatus: json['nextStatus'] != null ? Status.fromJson(json['nextStatus']) : null, + nextStatus: json['nextStatus'] != null + ? Status.fromJson(json['nextStatus']) + : null, comment: json['comment'] as String?, - updatedBy: json['updatedBy'] != null ? User.fromJson(json['updatedBy']) : null, + updatedBy: + json['updatedBy'] != null ? User.fromJson(json['updatedBy']) : null, ); } } diff --git a/lib/view/finance/payment_request_detail_screen.dart b/lib/view/finance/payment_request_detail_screen.dart index 3d1e647..dfc3b87 100644 --- a/lib/view/finance/payment_request_detail_screen.dart +++ b/lib/view/finance/payment_request_detail_screen.dart @@ -26,7 +26,7 @@ class PaymentRequestDetailScreen extends StatefulWidget { final String paymentRequestId; const PaymentRequestDetailScreen({super.key, required this.paymentRequestId}); - @override + @override State createState() => _PaymentRequestDetailScreenState(); } diff --git a/lib/view/finance/payment_request_screen.dart b/lib/view/finance/payment_request_screen.dart index 417bfd4..7374376 100644 --- a/lib/view/finance/payment_request_screen.dart +++ b/lib/view/finance/payment_request_screen.dart @@ -12,6 +12,8 @@ import 'package:marco/view/finance/payment_request_detail_screen.dart'; import 'package:marco/helpers/widgets/my_custom_skeleton.dart'; import 'package:marco/controller/permission_controller.dart'; import 'package:marco/helpers/utils/permission_constants.dart'; +import 'package:marco/helpers/widgets/my_refresh_indicator.dart'; + class PaymentRequestMainScreen extends StatefulWidget { const PaymentRequestMainScreen({super.key}); @@ -283,10 +285,8 @@ class _PaymentRequestMainScreenState extends State final list = filteredList(isHistory: isHistory); - // Single ScrollController for this list + // ScrollController for infinite scroll final scrollController = ScrollController(); - - // Load more when reaching near bottom scrollController.addListener(() { if (scrollController.position.pixels >= scrollController.position.maxScrollExtent - 100 && @@ -295,7 +295,7 @@ class _PaymentRequestMainScreenState extends State } }); - return RefreshIndicator( + return MyRefreshIndicator( onRefresh: _refreshPaymentRequests, child: list.isEmpty ? ListView( @@ -315,14 +315,13 @@ class _PaymentRequestMainScreenState extends State ], ) : ListView.separated( - controller: scrollController, // attach controller + controller: scrollController, padding: const EdgeInsets.fromLTRB(12, 12, 12, 80), - itemCount: list.length + 1, // extra item for loading + itemCount: list.length + 1, separatorBuilder: (_, __) => Divider(color: Colors.grey.shade300, height: 20), itemBuilder: (context, index) { if (index == list.length) { - // Show loading indicator at bottom return Obx(() => paymentController.isLoading.value ? const Padding( padding: EdgeInsets.symmetric(vertical: 16), @@ -330,7 +329,6 @@ class _PaymentRequestMainScreenState extends State ) : const SizedBox.shrink()); } - final item = list[index]; return _buildPaymentRequestTile(item); }, diff --git a/lib/view/service_project/service_project_job_detail_screen.dart b/lib/view/service_project/service_project_job_detail_screen.dart index a323829..34ac74a 100644 --- a/lib/view/service_project/service_project_job_detail_screen.dart +++ b/lib/view/service_project/service_project_job_detail_screen.dart @@ -496,7 +496,7 @@ class _JobDetailsScreenState extends State with UIMixin { onPressed: () async { isAttendanceExpanded.value = !isAttendanceExpanded.value; - if (isAttendanceExpanded.value ) { + if (isAttendanceExpanded.value) { await controller .fetchJobAttendanceLog(job.attendanceId ?? ''); } @@ -716,6 +716,40 @@ class _JobDetailsScreenState extends State with UIMixin { }); } + Widget _rowItem(String label, String value) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 3, + child: MyText.bodySmall(label, + fontWeight: 600, color: Colors.grey.shade700), + ), + Expanded( + flex: 5, + child: MyText.bodyMedium(value, fontWeight: 500), + ), + ], + ); + } + + Widget _branchDisplay() { + final job = controller.jobDetail.value?.data; + final branch = job?.projectBranch; + if (branch == null) { + return MyText.labelMedium("No branch assigned"); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _rowItem("Branch Name", branch.branchName ?? "-"), + MySpacing.height(8), + _rowItem("Branch Type", branch.branchType ?? "-"), + ], + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -765,6 +799,12 @@ class _JobDetailsScreenState extends State with UIMixin { ], ), MySpacing.height(12), + _buildSectionCard( + title: "Project Branch", + titleIcon: Icons.account_tree_outlined, + children: [_branchDisplay()], + ), + MySpacing.height(16), _buildSectionCard( title: "Assignees", titleIcon: Icons.person_outline,