feat: enhance job detail screen to display project branch information and improve payment request handling
This commit is contained in:
parent
d0b40a9822
commit
87a7d19672
@ -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<PaymentRequestData?> paymentRequest = Rx<PaymentRequestData?>(null);
|
||||
@ -26,6 +27,8 @@ class PaymentRequestDetailController extends GetxController {
|
||||
final RxList<EmployeeModel> employeeSearchResults = <EmployeeModel>[].obs;
|
||||
final TextEditingController employeeSearchController =
|
||||
TextEditingController();
|
||||
PaymentRequestController get paymentRequestController =>
|
||||
Get.find<PaymentRequestController>();
|
||||
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',
|
||||
|
||||
@ -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!();
|
||||
|
||||
@ -47,6 +47,7 @@ class JobData {
|
||||
final User? createdBy;
|
||||
final List<Tag>? tags;
|
||||
final List<UpdateLog>? 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<String, dynamic>? 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<dynamic>?)
|
||||
?.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<dynamic>?)
|
||||
?.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<String, dynamic> 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<PaymentRequestMainScreen>
|
||||
|
||||
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<PaymentRequestMainScreen>
|
||||
}
|
||||
});
|
||||
|
||||
return RefreshIndicator(
|
||||
return MyRefreshIndicator(
|
||||
onRefresh: _refreshPaymentRequests,
|
||||
child: list.isEmpty
|
||||
? ListView(
|
||||
@ -315,14 +315,13 @@ class _PaymentRequestMainScreenState extends State<PaymentRequestMainScreen>
|
||||
],
|
||||
)
|
||||
: 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<PaymentRequestMainScreen>
|
||||
)
|
||||
: const SizedBox.shrink());
|
||||
}
|
||||
|
||||
final item = list[index];
|
||||
return _buildPaymentRequestTile(item);
|
||||
},
|
||||
|
||||
@ -496,7 +496,7 @@ class _JobDetailsScreenState extends State<JobDetailsScreen> 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<JobDetailsScreen> 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<JobDetailsScreen> 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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user