feat: add advance payment indicator in payment request screens and improve code formatting

This commit is contained in:
Vaibhav Surve 2025-11-21 16:57:37 +05:30
parent 2c98ac359c
commit 603e7ee7e5
2 changed files with 94 additions and 32 deletions

View File

@ -120,7 +120,8 @@ class _PaymentRequestDetailScreenState extends State<PaymentRequestDetailScreen>
final request = controller.paymentRequest.value; final request = controller.paymentRequest.value;
if ((controller.errorMessage.value).isNotEmpty) { if ((controller.errorMessage.value).isNotEmpty) {
return Center(child: MyText.bodyMedium(controller.errorMessage.value)); return Center(
child: MyText.bodyMedium(controller.errorMessage.value));
} }
if (request == null) { if (request == null) {
@ -152,7 +153,8 @@ class _PaymentRequestDetailScreenState extends State<PaymentRequestDetailScreen>
request: request, request: request,
colorParser: _parseColor, colorParser: _parseColor,
employeeInfo: employeeInfo, employeeInfo: employeeInfo,
onEdit: () => _openEditPaymentRequestBottomSheet(request), onEdit: () =>
_openEditPaymentRequestBottomSheet(request),
), ),
const Divider(height: 30, thickness: 1.2), const Divider(height: 30, thickness: 1.2),
_Logs( _Logs(
@ -272,8 +274,7 @@ class _PaymentRequestDetailScreenState extends State<PaymentRequestDetailScreen>
message: success message: success
? 'Status updated successfully' ? 'Status updated successfully'
: 'Failed to update status', : 'Failed to update status',
type: type: success ? SnackbarType.success : SnackbarType.error,
success ? SnackbarType.success : SnackbarType.error,
); );
if (success) await controller.fetchPaymentRequestDetail(); if (success) await controller.fetchPaymentRequestDetail();
@ -323,8 +324,8 @@ class _PaymentRequestDetailScreenState extends State<PaymentRequestDetailScreen>
), ),
MySpacing.height(2), MySpacing.height(2),
GetBuilder<ProjectController>(builder: (_) { GetBuilder<ProjectController>(builder: (_) {
final name = final name = projectController.selectedProject?.name ??
projectController.selectedProject?.name ?? 'Select Project'; 'Select Project';
return Row( return Row(
children: [ children: [
const Icon(Icons.work_outline, const Icon(Icons.work_outline,
@ -409,12 +410,37 @@ class _HeaderState extends State<_Header> with UIMixin {
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [ children: [
MyText.bodyMedium( MyText.bodyMedium(
'ID: ${widget.request.paymentRequestUID ?? '-'}', 'ID: ${widget.request.paymentRequestUID ?? '-'}',
fontWeight: 700, fontWeight: 700,
fontSize: 14, fontSize: 14,
), ),
// 🔥 ADVANCE CHIP show only if true
if (widget.request.isAdvancePayment == true) ...[
const SizedBox(width: 8),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.orange.shade700,
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'ADVANCE',
style: TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
),
],
],
),
if (canEdit) if (canEdit)
IconButton( IconButton(
onPressed: widget.onEdit, onPressed: widget.onEdit,
@ -513,17 +539,17 @@ class _Logs extends StatelessWidget {
final last = updatedBy?.lastName ?? ''; final last = updatedBy?.lastName ?? '';
final initials = final initials =
'${first.isNotEmpty ? first[0] : ''}${last.isNotEmpty ? last[0] : ''}'; '${first.isNotEmpty ? first[0] : ''}${last.isNotEmpty ? last[0] : ''}';
final name = ((first + ' ' + last).trim().isNotEmpty) final name =
? '$first $last' ((first + ' ' + last).trim().isNotEmpty) ? '$first $last' : '-';
: '-';
final updatedAt = log.updatedAt; final updatedAt = log.updatedAt;
final timeAgo = (updatedAt != null) final timeAgo = (updatedAt != null)
? timeago.format(updatedAt.toUtc().add(const Duration(hours: 5, minutes: 30))) ? timeago.format(updatedAt
.toUtc()
.add(const Duration(hours: 5, minutes: 30)))
: '-'; : '-';
final nextStatusColor = final nextStatusColor = colorParser(log.nextStatus?.color ?? '');
colorParser(log.nextStatus?.color ?? '');
return TimelineTile( return TimelineTile(
alignment: TimelineAlign.start, alignment: TimelineAlign.start,
@ -667,14 +693,18 @@ class _DetailsTable extends StatelessWidget {
_labelValueRow("Transaction ID:", request.paidTransactionId ?? ''), _labelValueRow("Transaction ID:", request.paidTransactionId ?? ''),
_labelValueRow("Payee:", request.payee ?? '-'), _labelValueRow("Payee:", request.payee ?? '-'),
_labelValueRow("Project:", request.project?.name ?? '-'), _labelValueRow("Project:", request.project?.name ?? '-'),
_labelValueRow("Expense Category:", request.expenseCategory?.name ?? '-'), _labelValueRow(
"Expense Category:", request.expenseCategory?.name ?? '-'),
// Amounts // Amounts
_labelValueRow("Amount:", _formatCurrencyAmount(currencySymbol, request.amount)), _labelValueRow(
"Amount:", _formatCurrencyAmount(currencySymbol, request.amount)),
if (request.baseAmount != null) if (request.baseAmount != null)
_labelValueRow("Base Amount:", _formatCurrencyAmount(currencySymbol, request.baseAmount)), _labelValueRow("Base Amount:",
_formatCurrencyAmount(currencySymbol, request.baseAmount)),
if (request.taxAmount != null) if (request.taxAmount != null)
_labelValueRow("Tax Amount:", _formatCurrencyAmount(currencySymbol, request.taxAmount)), _labelValueRow("Tax Amount:",
_formatCurrencyAmount(currencySymbol, request.taxAmount)),
if (request.expenseCategory?.noOfPersonsRequired == true) if (request.expenseCategory?.noOfPersonsRequired == true)
_labelValueRow("Additional Persons Required:", "Yes"), _labelValueRow("Additional Persons Required:", "Yes"),
if (request.expenseCategory?.isAttachmentRequried == true) if (request.expenseCategory?.isAttachmentRequried == true)
@ -706,27 +736,36 @@ class _DetailsTable extends StatelessWidget {
format: 'dd MMM yyyy'), format: 'dd MMM yyyy'),
), ),
if (request.paidBy != null) if (request.paidBy != null)
_labelValueRow("Paid By:", "${request.paidBy?.firstName ?? ''} ${request.paidBy?.lastName ?? ''}".trim()), _labelValueRow(
"Paid By:",
"${request.paidBy?.firstName ?? ''} ${request.paidBy?.lastName ?? ''}"
.trim()),
// Flags // Flags
_labelValueRow( _labelValueRow("Advance Payment:",
"Advance Payment:", (request.isAdvancePayment ?? false) ? "Yes" : "No"), (request.isAdvancePayment ?? false) ? "Yes" : "No"),
_labelValueRow( _labelValueRow("Expense Created:",
"Expense Created:", (request.isExpenseCreated ?? false) ? "Yes" : "No"), (request.isExpenseCreated ?? false) ? "Yes" : "No"),
_labelValueRow("Active:", (request.isActive ?? false) ? "Yes" : "No"), _labelValueRow("Active:", (request.isActive ?? false) ? "Yes" : "No"),
// Recurring Payment Info // Recurring Payment Info
if (request.recurringPayment != null) ...[ if (request.recurringPayment != null) ...[
const SizedBox(height: 6), const SizedBox(height: 6),
MyText.bodySmall("Recurring Payment Info:", fontWeight: 600), MyText.bodySmall("Recurring Payment Info:", fontWeight: 600),
_labelValueRow("Recurring ID:", request.recurringPayment?.recurringPaymentUID ?? '-'), _labelValueRow("Recurring ID:",
_labelValueRow("Amount:", _formatCurrencyAmount(currencySymbol, request.recurringPayment?.amount)), request.recurringPayment?.recurringPaymentUID ?? '-'),
_labelValueRow("Variable Amount:", (request.recurringPayment?.isVariable ?? false) ? "Yes" : "No"), _labelValueRow(
"Amount:",
_formatCurrencyAmount(
currencySymbol, request.recurringPayment?.amount)),
_labelValueRow("Variable Amount:",
(request.recurringPayment?.isVariable ?? false) ? "Yes" : "No"),
], ],
// Description & Attachments // Description & Attachments
_labelValueRow("Description:", request.description ?? '-'), _labelValueRow("Description:", request.description ?? '-'),
_labelValueRow("Attachment:", (request.attachments ?? []).isNotEmpty ? "Yes" : "No"), _labelValueRow("Attachment:",
(request.attachments ?? []).isNotEmpty ? "Yes" : "No"),
], ],
); );
} }

View File

@ -355,7 +355,6 @@ class _PaymentRequestMainScreenState extends State<PaymentRequestMainScreen>
child: InkWell( child: InkWell(
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
onTap: () { onTap: () {
// Navigate to detail screen, passing the payment request ID
Get.to(() => PaymentRequestDetailScreen(paymentRequestId: item.id)); Get.to(() => PaymentRequestDetailScreen(paymentRequestId: item.id));
}, },
child: Padding( child: Padding(
@ -366,6 +365,30 @@ class _PaymentRequestMainScreenState extends State<PaymentRequestMainScreen>
Row( Row(
children: [ children: [
MyText.bodyMedium(item.expenseCategory.name, fontWeight: 600), MyText.bodyMedium(item.expenseCategory.name, fontWeight: 600),
// -------------------------------
// ADV CHIP (only if advance)
// -------------------------------
if (item.isAdvancePayment == true) ...[
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: Colors.orange.withOpacity(0.2),
borderRadius: BorderRadius.circular(4),
border: Border.all(color: Colors.orange),
),
child: const Text(
"ADV",
style: TextStyle(
fontSize: 10,
color: Colors.orange,
fontWeight: FontWeight.bold,
),
),
),
],
], ],
), ),
const SizedBox(height: 6), const SizedBox(height: 6),