This commit is contained in:
Hillel Coren 2018-09-02 22:34:04 -07:00
parent a81cb5891f
commit d04386974b
7 changed files with 193 additions and 84 deletions

View File

@ -198,3 +198,10 @@ class FilterPaymentsByCustom2 implements PersistUI {
FilterPaymentsByCustom2(this.value);
}
class FilterPaymentsByClient implements PersistUI {
final int clientId;
FilterPaymentsByClient([this.clientId]);
}

View File

@ -68,17 +68,27 @@ class ClientOverview extends StatelessWidget {
EntityListTile(
icon: FontAwesomeIcons.filePdfO,
title: localization.invoices,
onTap: () => viewModel.onInvoicesPressed(context),
onTap: () => viewModel.onEntityPressed(context, EntityType.invoice),
subtitle: memoizedInvoiceStatsForClient(
client.id,
state.invoiceState.map,
localization.active,
localization.archived),
),
EntityListTile(
icon: FontAwesomeIcons.creditCard,
title: localization.payments,
onTap: () => viewModel.onEntityPressed(context, EntityType.payment),
subtitle: memoizedQuoteStatsForClient(
client.id,
state.quoteState.map,
localization.active,
localization.archived),
),
company.isModuleEnabled(EntityType.quote) ? EntityListTile(
icon: FontAwesomeIcons.fileAltO,
title: localization.quotes,
onTap: () => viewModel.onQuotesPressed(context),
onTap: () => viewModel.onEntityPressed(context, EntityType.quote),
subtitle: memoizedQuoteStatsForClient(
client.id,
state.quoteState.map,

View File

@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
import 'package:invoiceninja_flutter/redux/payment/payment_actions.dart';
import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart';
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
import 'package:invoiceninja_flutter/ui/client/client_screen.dart';
@ -42,8 +43,7 @@ class ClientViewVM {
final Function(BuildContext, EntityAction) onActionSelected;
final Function(BuildContext) onEditPressed;
final Function onBackPressed;
final Function(BuildContext) onInvoicesPressed;
final Function(BuildContext) onQuotesPressed;
final Function(BuildContext, EntityType) onEntityPressed;
final Function(BuildContext, bool) onRefreshed;
final bool isSaving;
final bool isLoading;
@ -53,9 +53,8 @@ class ClientViewVM {
@required this.client,
@required this.company,
@required this.onActionSelected,
@required this.onEntityPressed,
@required this.onEditPressed,
@required this.onInvoicesPressed,
@required this.onQuotesPressed,
@required this.onBackPressed,
@required this.isSaving,
@required this.isLoading,
@ -94,13 +93,21 @@ class ClientViewVM {
)));
});
},
onInvoicesPressed: (BuildContext context) {
onEntityPressed: (BuildContext context, EntityType entityType) {
switch (entityType) {
case EntityType.invoice:
store.dispatch(FilterInvoicesByClient(client.id));
store.dispatch(ViewInvoiceList(context));
},
onQuotesPressed: (BuildContext context) {
break;
case EntityType.quote:
store.dispatch(FilterQuotesByClient(client.id));
store.dispatch(ViewQuoteList(context));
break;
case EntityType.payment:
store.dispatch(FilterPaymentsByClient(client.id));
store.dispatch(ViewPaymentList(context));
break;
}
},
onRefreshed: (context, loadActivities) =>
_handleRefresh(context, loadActivities),

View File

@ -1,5 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/data/models/payment_model.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart';
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
@ -15,32 +16,21 @@ class PaymentList extends StatelessWidget {
@required this.viewModel,
}) : super(key: key);
@override
Widget build(BuildContext context) {
if (!viewModel.isLoaded) {
return LoadingIndicator();
} else if (viewModel.paymentList.isEmpty) {
return Opacity(
opacity: 0.5,
child: Center(
child: Text(
AppLocalization.of(context).noRecordsFound,
style: TextStyle(
fontSize: 18.0,
),
),
),
);
}
return _buildListView(context);
}
void _showMenu(BuildContext context, PaymentEntity payment) async {
void _showMenu(
BuildContext context, PaymentEntity payment, ClientEntity client) async {
final user = viewModel.user;
final message = await showDialog<String>(
context: context,
builder: (BuildContext context) => SimpleDialog(children: <Widget>[
user.canEditEntity(payment) && client.hasEmailAddress
? ListTile(
leading: Icon(Icons.send),
title: Text(AppLocalization.of(context).email),
onTap: () => viewModel.onEntityAction(
context, payment, EntityAction.email),
)
: Container(),
Divider(),
user.canEditEntity(payment) && !payment.isActive
? ListTile(
leading: Icon(Icons.restore),
@ -74,29 +64,96 @@ class PaymentList extends StatelessWidget {
}
}
Widget _buildListView(BuildContext context) {
return RefreshIndicator(
@override
Widget build(BuildContext context) {
final localization = AppLocalization.of(context);
final listState = viewModel.listState;
final filteredClientId = listState.filterClientId;
final filteredClient =
filteredClientId != null ? viewModel.clientMap[filteredClientId] : null;
return Column(
children: <Widget>[
filteredClient != null
? Material(
color: Colors.orangeAccent,
elevation: 6.0,
child: InkWell(
onTap: () => viewModel.onViewClientFilterPressed(context),
child: Row(
children: <Widget>[
SizedBox(width: 18.0),
Expanded(
child: Text(
localization.clientsPayments.replaceFirst(
':client', filteredClient.displayName),
style: TextStyle(
color: Colors.white,
fontSize: 16.0,
),
),
),
IconButton(
icon: Icon(
Icons.close,
color: Colors.white,
),
onPressed: () => viewModel.onClearClientFilterPressed(),
)
],
),
),
)
: Container(),
Expanded(
child: !viewModel.isLoaded
? LoadingIndicator()
: RefreshIndicator(
onRefresh: () => viewModel.onRefreshed(context),
child: ListView.builder(
child: viewModel.paymentList.isEmpty
? Opacity(
opacity: 0.5,
child: Center(
child: Text(
AppLocalization.of(context).noRecordsFound,
style: TextStyle(
fontSize: 18.0,
),
),
),
)
: ListView.builder(
shrinkWrap: true,
itemCount: viewModel.paymentList.length,
itemBuilder: (BuildContext context, index) {
final paymentId = viewModel.paymentList[index];
final payment = viewModel.paymentMap[paymentId];
return Column(children: <Widget>[
final client =
viewModel.clientMap[payment.clientId];
return Column(
children: <Widget>[
PaymentListItem(
user: viewModel.user,
filter: viewModel.filter,
payment: payment,
onDismissed: (DismissDirection direction) =>
viewModel.onDismissed(context, payment, direction),
onTap: () => viewModel.onPaymentTap(context, payment),
onLongPress: () => _showMenu(context, payment),
viewModel.onDismissed(
context, payment, direction),
onTap: () =>
viewModel.onPaymentTap(context, payment),
onLongPress: () =>
_showMenu(context, payment, client),
),
Divider(
height: 1.0,
),
]);
}),
],
);
},
),
),
),
],
);
}
}

View File

@ -1,4 +1,6 @@
import 'dart:async';
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
import 'package:redux/redux.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
@ -31,20 +33,25 @@ class PaymentListBuilder extends StatelessWidget {
class PaymentListVM {
final UserEntity user;
final ListUIState listState;
final List<int> paymentList;
final BuiltMap<int, PaymentEntity> paymentMap;
final BuiltMap<int, ClientEntity> clientMap;
final String filter;
final bool isLoading;
final bool isLoaded;
final Function(BuildContext, PaymentEntity) onPaymentTap;
final Function(BuildContext, PaymentEntity, DismissDirection) onDismissed;
final Function(BuildContext) onRefreshed;
final Function onClearClientFilterPressed;
final Function(BuildContext) onViewClientFilterPressed;
final Function(BuildContext, PaymentEntity, EntityAction) onEntityAction;
PaymentListVM({
@required this.user,
@required this.paymentList,
@required this.paymentMap,
@required this.clientMap,
@required this.filter,
@required this.isLoading,
@required this.isLoaded,
@ -52,6 +59,9 @@ class PaymentListVM {
@required this.onDismissed,
@required this.onRefreshed,
@required this.onEntityAction,
@required this.onClearClientFilterPressed,
@required this.onViewClientFilterPressed,
@required this.listState,
});
static PaymentListVM fromStore(Store<AppState> store) {
@ -72,9 +82,11 @@ class PaymentListVM {
paymentList: memoizedFilteredPaymentList(state.paymentState.map,
state.paymentState.list, state.paymentListState),
paymentMap: state.paymentState.map,
clientMap: state.clientState.map,
isLoading: state.isLoading,
isLoaded: state.paymentState.isLoaded,
filter: state.paymentUIState.listUIState.filter,
listState: state.paymentListState,
onPaymentTap: (context, payment) {
store.dispatch(ViewPayment(paymentId: payment.id, context: context));
},
@ -100,6 +112,12 @@ class PaymentListVM {
break;
}
},
onClearClientFilterPressed: () =>
store.dispatch(FilterPaymentsByClient()),
onViewClientFilterPressed: (BuildContext context) => store.dispatch(
ViewClient(
clientId: state.paymentListState.filterClientId,
context: context)),
onRefreshed: (context) => _handleRefresh(context),
onDismissed: (BuildContext context, PaymentEntity payment,
DismissDirection direction) {

View File

@ -153,6 +153,7 @@ class AppLocalization {
'please_enter_an_invoice_number': 'Please enter an invoice number',
'please_enter_a_quote_number': 'Please enter a quote number',
'clients_invoices': ':client\'s invoices',
'clients_payments': ':client\'s payments',
'past_due': 'Past Due',
'draft': 'Draft',
'sent': 'Sent',
@ -7418,6 +7419,9 @@ class AppLocalization {
String get clientsInvoices =>
_localizedValues[locale.languageCode]['clients_invoices'];
String get clientsPayments =>
_localizedValues[locale.languageCode]['clients_payments'];
String get pastDue => _localizedValues[locale.languageCode]['past_due'];
String get draft => _localizedValues[locale.languageCode]['draft'];

View File

@ -198,3 +198,9 @@ class FilterStubsByCustom2 implements PersistUI {
FilterStubsByCustom2(this.value);
}
class FilterStubsByClient implements PersistUI {
final int clientId;
FilterStubsByClient([this.clientId]);
}