replaced fab icon with icon on page

This commit is contained in:
Vaibhav Surve 2025-11-11 16:12:13 +05:30
parent 7f0c4d075d
commit af5bfcaa59

View File

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