replaced fab icon with icon on page
This commit is contained in:
parent
7f0c4d075d
commit
af5bfcaa59
@ -50,7 +50,6 @@ class _PaymentRequestDetailScreenState extends State<PaymentRequestDetailScreen>
|
||||
|
||||
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<PaymentRequestDetailScreen>
|
||||
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<PaymentRequestDetailScreen>
|
||||
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<PaymentRequestDetailScreen>
|
||||
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<PaymentRequestDetailScreen>
|
||||
);
|
||||
})),
|
||||
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<PaymentRequestDetailScreen>
|
||||
.hasAnyPermission(status.permissionIds ?? []);
|
||||
}).toList();
|
||||
|
||||
// Normal status buttons
|
||||
return SafeArea(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
@ -253,7 +224,6 @@ class _PaymentRequestDetailScreenState extends State<PaymentRequestDetailScreen>
|
||||
),
|
||||
),
|
||||
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<PaymentRequestDetailScreen>
|
||||
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<PaymentRequestDetailScreen>
|
||||
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<UpdateLog> 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,
|
||||
),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user