refactor: enhance invoice display logic and add empty state in BillingView

This commit is contained in:
Achintha Isuru
2026-01-30 01:34:21 -05:00
parent d38cb07326
commit dd5b58b7bc
2 changed files with 150 additions and 110 deletions

View File

@@ -10,7 +10,6 @@ import '../blocs/billing_state.dart';
import '../widgets/invoice_history_section.dart'; import '../widgets/invoice_history_section.dart';
import '../widgets/payment_method_card.dart'; import '../widgets/payment_method_card.dart';
import '../widgets/pending_invoices_section.dart'; import '../widgets/pending_invoices_section.dart';
import '../widgets/savings_card.dart';
import '../widgets/spending_breakdown_card.dart'; import '../widgets/spending_breakdown_card.dart';
/// The entry point page for the client billing feature. /// The entry point page for the client billing feature.
@@ -199,7 +198,38 @@ class _BillingViewState extends State<BillingView> {
], ],
const PaymentMethodCard(), const PaymentMethodCard(),
const SpendingBreakdownCard(), const SpendingBreakdownCard(),
InvoiceHistorySection(invoices: state.invoiceHistory), if (state.invoiceHistory.isEmpty) _buildEmptyState(context)
else InvoiceHistorySection(invoices: state.invoiceHistory),
],
),
);
}
Widget _buildEmptyState(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const SizedBox(height: UiConstants.space12),
Container(
padding: const EdgeInsets.all(UiConstants.space6),
decoration: BoxDecoration(
color: UiColors.bgPopup,
shape: BoxShape.circle,
border: Border.all(color: UiColors.border),
),
child: const Icon(
UiIcons.file,
size: 48,
color: UiColors.textSecondary,
),
),
const SizedBox(height: UiConstants.space4),
Text(
'No Invoices for the selected period',
style: UiTypography.body1m.textSecondary,
textAlign: TextAlign.center,
),
], ],
), ),
); );

View File

@@ -24,11 +24,13 @@ class _PaymentMethodCardState extends State<PaymentMethodCard> {
return null; return null;
} }
final fdc.QueryResult<dc.GetAccountsByOwnerIdData, final fdc.QueryResult<
dc.GetAccountsByOwnerIdVariables> result = dc.GetAccountsByOwnerIdData,
await dc.ExampleConnector.instance dc.GetAccountsByOwnerIdVariables
.getAccountsByOwnerId(ownerId: businessId) >
.execute(); result = await dc.ExampleConnector.instance
.getAccountsByOwnerId(ownerId: businessId)
.execute();
return result.data; return result.data;
} }
@@ -36,115 +38,123 @@ class _PaymentMethodCardState extends State<PaymentMethodCard> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder<dc.GetAccountsByOwnerIdData?>( return FutureBuilder<dc.GetAccountsByOwnerIdData?>(
future: _accountsFuture, future: _accountsFuture,
builder: (BuildContext context, builder:
AsyncSnapshot<dc.GetAccountsByOwnerIdData?> snapshot) { (
final List<dc.GetAccountsByOwnerIdAccounts> accounts = BuildContext context,
snapshot.data?.accounts ?? AsyncSnapshot<dc.GetAccountsByOwnerIdData?> snapshot,
<dc.GetAccountsByOwnerIdAccounts>[]; ) {
final dc.GetAccountsByOwnerIdAccounts? account = final List<dc.GetAccountsByOwnerIdAccounts> accounts =
accounts.isNotEmpty ? accounts.first : null; snapshot.data?.accounts ?? <dc.GetAccountsByOwnerIdAccounts>[];
final String bankLabel = final dc.GetAccountsByOwnerIdAccounts? account = accounts.isNotEmpty
account?.bank.isNotEmpty == true ? account!.bank : '----'; ? accounts.first
final String last4 = : null;
account?.last4.isNotEmpty == true ? account!.last4 : '----';
final bool isPrimary = account?.isPrimary ?? false;
final String expiryLabel = _formatExpiry(account?.expiryTime);
return Container( if (account == null) {
padding: const EdgeInsets.all(UiConstants.space4), return const SizedBox.shrink();
decoration: BoxDecoration( }
color: UiColors.white,
borderRadius: UiConstants.radiusLg, final String bankLabel = account.bank.isNotEmpty == true
border: Border.all(color: UiColors.border), ? account.bank
boxShadow: <BoxShadow>[ : '----';
BoxShadow( final String last4 = account.last4.isNotEmpty == true
color: UiColors.black.withValues(alpha: 0.04), ? account.last4
blurRadius: 8, : '----';
offset: const Offset(0, 2), final bool isPrimary = account.isPrimary ?? false;
), final String expiryLabel = _formatExpiry(account.expiryTime);
],
), return Container(
child: Column( padding: const EdgeInsets.all(UiConstants.space4),
children: <Widget>[ decoration: BoxDecoration(
Row( color: UiColors.white,
mainAxisAlignment: MainAxisAlignment.spaceBetween, borderRadius: UiConstants.radiusLg,
children: <Widget>[ border: Border.all(color: UiColors.border),
Text( boxShadow: <BoxShadow>[
t.client_billing.payment_method, BoxShadow(
style: UiTypography.title2b.textPrimary, color: UiColors.black.withValues(alpha: 0.04),
), blurRadius: 8,
const SizedBox.shrink(), offset: const Offset(0, 2),
],
),
if (account != null) ...<Widget>[
const SizedBox(height: UiConstants.space3),
Container(
padding: const EdgeInsets.all(UiConstants.space3),
decoration: BoxDecoration(
color: UiColors.bgSecondary,
borderRadius: UiConstants.radiusMd,
), ),
child: Row( ],
),
child: Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Container( Text(
width: 40, t.client_billing.payment_method,
height: 28, style: UiTypography.title2b.textPrimary,
decoration: BoxDecoration(
color: UiColors.primary,
borderRadius: BorderRadius.circular(4),
),
child: Center(
child: Text(
bankLabel,
style: const TextStyle(
color: UiColors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
), ),
const SizedBox(width: UiConstants.space3), const SizedBox.shrink(),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'•••• $last4',
style: UiTypography.body2b.textPrimary,
),
Text(
t.client_billing.expires(date: expiryLabel),
style: UiTypography.footnote2r.textSecondary,
),
],
),
),
if (isPrimary)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: BoxDecoration(
color: UiColors.accent,
borderRadius: BorderRadius.circular(4),
),
child: Text(
t.client_billing.default_badge,
style: UiTypography.titleUppercase4b.textPrimary,
),
),
], ],
), ),
), const SizedBox(height: UiConstants.space3),
], Container(
], padding: const EdgeInsets.all(UiConstants.space3),
), decoration: BoxDecoration(
); color: UiColors.bgSecondary,
}, borderRadius: UiConstants.radiusMd,
),
child: Row(
children: <Widget>[
Container(
width: 40,
height: 28,
decoration: BoxDecoration(
color: UiColors.primary,
borderRadius: BorderRadius.circular(4),
),
child: Center(
child: Text(
bankLabel,
style: const TextStyle(
color: UiColors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
),
const SizedBox(width: UiConstants.space3),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'•••• $last4',
style: UiTypography.body2b.textPrimary,
),
Text(
t.client_billing.expires(date: expiryLabel),
style: UiTypography.footnote2r.textSecondary,
),
],
),
),
if (isPrimary)
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: BoxDecoration(
color: UiColors.accent,
borderRadius: BorderRadius.circular(4),
),
child: Text(
t.client_billing.default_badge,
style: UiTypography.titleUppercase4b.textPrimary,
),
),
],
),
),
],
),
);
},
); );
} }