Quotes
This commit is contained in:
parent
150f1f68b0
commit
52bb232b85
|
|
@ -40,6 +40,20 @@ class QuoteFields {
|
|||
static const String quoteDate = 'quoteDate';
|
||||
static const String validUntil = 'validUntil';
|
||||
static const String quoteStatusId = 'quoteStatusId';
|
||||
|
||||
static String convertField(String field) {
|
||||
if (field == InvoiceFields.invoiceStatusId) {
|
||||
return QuoteFields.quoteStatusId;
|
||||
} else if (field == InvoiceFields.invoiceNumber) {
|
||||
return QuoteFields.quoteNumber;
|
||||
} else if (field == InvoiceFields.invoiceDate) {
|
||||
return QuoteFields.quoteDate;
|
||||
} else if (field == InvoiceFields.dueDate) {
|
||||
return QuoteFields.validUntil;
|
||||
} else {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InvoiceFields {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import 'package:invoiceninja_flutter/ui/invoice/invoice_list_vm.dart';
|
|||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class InvoiceList extends StatelessWidget {
|
||||
final InvoiceListVM viewModel;
|
||||
final EntityListVM viewModel;
|
||||
|
||||
const InvoiceList({
|
||||
Key key,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ class InvoiceListBuilder extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StoreConnector<AppState, InvoiceListVM>(
|
||||
//rebuildOnChange: true,
|
||||
converter: InvoiceListVM.fromStore,
|
||||
builder: (context, vm) {
|
||||
return InvoiceList(
|
||||
|
|
@ -35,7 +34,7 @@ class InvoiceListBuilder extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class InvoiceListVM {
|
||||
class EntityListVM {
|
||||
final UserEntity user;
|
||||
final ListUIState listState;
|
||||
final List<int> invoiceList;
|
||||
|
|
@ -51,7 +50,7 @@ class InvoiceListVM {
|
|||
final Function(BuildContext) onViewClientFilterPressed;
|
||||
final Function(BuildContext, InvoiceEntity, EntityAction) onEntityAction;
|
||||
|
||||
InvoiceListVM({
|
||||
EntityListVM({
|
||||
@required this.user,
|
||||
@required this.listState,
|
||||
@required this.invoiceList,
|
||||
|
|
@ -67,6 +66,40 @@ class InvoiceListVM {
|
|||
@required this.onViewClientFilterPressed,
|
||||
@required this.onEntityAction,
|
||||
});
|
||||
}
|
||||
|
||||
class InvoiceListVM extends EntityListVM {
|
||||
InvoiceListVM({
|
||||
UserEntity user,
|
||||
ListUIState listState,
|
||||
List<int> invoiceList,
|
||||
BuiltMap<int, InvoiceEntity> invoiceMap,
|
||||
BuiltMap<int, ClientEntity> clientMap,
|
||||
String filter,
|
||||
bool isLoading,
|
||||
bool isLoaded,
|
||||
Function(BuildContext, InvoiceEntity) onInvoiceTap,
|
||||
Function(BuildContext, InvoiceEntity, DismissDirection) onDismissed,
|
||||
Function(BuildContext) onRefreshed,
|
||||
Function onClearClientFilterPressed,
|
||||
Function(BuildContext) onViewClientFilterPressed,
|
||||
Function(BuildContext, InvoiceEntity, EntityAction) onEntityAction,
|
||||
}) : super(
|
||||
user: user,
|
||||
listState: listState,
|
||||
invoiceList: invoiceList,
|
||||
invoiceMap: invoiceMap,
|
||||
clientMap: clientMap,
|
||||
filter: filter,
|
||||
isLoading: isLoading,
|
||||
isLoaded: isLoaded,
|
||||
onInvoiceTap: onInvoiceTap,
|
||||
onDismissed: onDismissed,
|
||||
onRefreshed: onRefreshed,
|
||||
onClearClientFilterPressed: onClearClientFilterPressed,
|
||||
onViewClientFilterPressed: onViewClientFilterPressed,
|
||||
onEntityAction: onEntityAction,
|
||||
);
|
||||
|
||||
static InvoiceListVM fromStore(Store<AppState> store) {
|
||||
Future<Null> _handleRefresh(BuildContext context) {
|
||||
|
|
@ -113,14 +146,12 @@ class InvoiceListVM {
|
|||
break;
|
||||
case EntityAction.markSent:
|
||||
store.dispatch(MarkSentInvoiceRequest(
|
||||
popCompleter(
|
||||
context, localization.markedInvoiceAsSent),
|
||||
popCompleter(context, localization.markedInvoiceAsSent),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.email:
|
||||
store.dispatch(ShowEmailInvoice(
|
||||
completer: popCompleter(
|
||||
context, localization.emailedInvoice),
|
||||
completer: popCompleter(context, localization.emailedInvoice),
|
||||
invoice: invoice,
|
||||
context: context));
|
||||
break;
|
||||
|
|
@ -131,20 +162,17 @@ class InvoiceListVM {
|
|||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreInvoiceRequest(
|
||||
popCompleter(
|
||||
context, localization.restoredInvoice),
|
||||
popCompleter(context, localization.restoredInvoice),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveInvoiceRequest(
|
||||
popCompleter(
|
||||
context, localization.archivedInvoice),
|
||||
popCompleter(context, localization.archivedInvoice),
|
||||
invoice.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteInvoiceRequest(
|
||||
popCompleter(
|
||||
context, localization.deletedInvoice),
|
||||
popCompleter(context, localization.deletedInvoice),
|
||||
invoice.id));
|
||||
break;
|
||||
}
|
||||
|
|
@ -155,25 +183,21 @@ class InvoiceListVM {
|
|||
if (direction == DismissDirection.endToStart) {
|
||||
if (invoice.isDeleted || invoice.isArchived) {
|
||||
store.dispatch(RestoreInvoiceRequest(
|
||||
snackBarCompleter(
|
||||
context, localization.restoredInvoice),
|
||||
snackBarCompleter(context, localization.restoredInvoice),
|
||||
invoice.id));
|
||||
} else {
|
||||
store.dispatch(ArchiveInvoiceRequest(
|
||||
snackBarCompleter(
|
||||
context, localization.archivedInvoice),
|
||||
snackBarCompleter(context, localization.archivedInvoice),
|
||||
invoice.id));
|
||||
}
|
||||
} else if (direction == DismissDirection.startToEnd) {
|
||||
if (invoice.isDeleted) {
|
||||
store.dispatch(RestoreInvoiceRequest(
|
||||
snackBarCompleter(
|
||||
context, localization.restoredInvoice),
|
||||
snackBarCompleter(context, localization.restoredInvoice),
|
||||
invoice.id));
|
||||
} else {
|
||||
store.dispatch(DeleteInvoiceRequest(
|
||||
snackBarCompleter(
|
||||
context, localization.deletedInvoice),
|
||||
snackBarCompleter(context, localization.deletedInvoice),
|
||||
invoice.id));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:invoiceninja_flutter/constants.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/buttons/edit_icon_button.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/one_value_header.dart';
|
||||
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -13,7 +14,7 @@ import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view_vm.dart';
|
|||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class InvoiceView extends StatefulWidget {
|
||||
final InvoiceViewVM viewModel;
|
||||
final EntityViewVM viewModel;
|
||||
|
||||
const InvoiceView({
|
||||
Key key,
|
||||
|
|
@ -35,18 +36,26 @@ class _InvoiceViewState extends State<InvoiceView> {
|
|||
List<Widget> _buildView() {
|
||||
final invoice = widget.viewModel.invoice;
|
||||
final user = widget.viewModel.company.user;
|
||||
final color = invoice.isPastDue
|
||||
? Colors.red
|
||||
: InvoiceStatusColors.colors[invoice.invoiceStatusId];
|
||||
final widgets = <Widget>[
|
||||
TwoValueHeader(
|
||||
backgroundColor: invoice.isPastDue
|
||||
? Colors.red
|
||||
: InvoiceStatusColors.colors[invoice.invoiceStatusId],
|
||||
label1: localization.totalAmount,
|
||||
value1:
|
||||
formatNumber(invoice.amount, context, clientId: invoice.clientId),
|
||||
label2: localization.balanceDue,
|
||||
value2: formatNumber(invoice.balance, context,
|
||||
clientId: invoice.clientId),
|
||||
),
|
||||
invoice.isQuote
|
||||
? OneValueHeader(
|
||||
backgroundColor: color,
|
||||
label: localization.totalAmount,
|
||||
value: formatNumber(invoice.amount, context,
|
||||
clientId: invoice.clientId),
|
||||
)
|
||||
: TwoValueHeader(
|
||||
backgroundColor: color,
|
||||
label1: localization.totalAmount,
|
||||
value1: formatNumber(invoice.amount, context,
|
||||
clientId: invoice.clientId),
|
||||
label2: localization.balanceDue,
|
||||
value2: formatNumber(invoice.balance, context,
|
||||
clientId: invoice.clientId),
|
||||
),
|
||||
];
|
||||
|
||||
final Map<String, String> fields = {
|
||||
|
|
@ -79,6 +88,9 @@ class _InvoiceViewState extends State<InvoiceView> {
|
|||
|
||||
final List<Widget> fieldWidgets = [];
|
||||
fields.forEach((field, value) {
|
||||
if (invoice.isQuote) {
|
||||
field = QuoteFields.convertField(field);
|
||||
}
|
||||
if (value != null && value.isNotEmpty) {
|
||||
fieldWidgets.add(Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
|
@ -254,7 +266,7 @@ class _CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
@required this.viewModel,
|
||||
});
|
||||
|
||||
final InvoiceViewVM viewModel;
|
||||
final EntityViewVM viewModel;
|
||||
|
||||
@override
|
||||
final Size preferredSize = const Size(double.infinity, 54.0);
|
||||
|
|
@ -271,10 +283,12 @@ class _CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
actions: invoice.isNew
|
||||
? []
|
||||
: [
|
||||
user.canEditEntity(invoice) ? EditIconButton(
|
||||
isVisible: !invoice.isDeleted,
|
||||
onPressed: () => viewModel.onEditPressed(context),
|
||||
) : Container(),
|
||||
user.canEditEntity(invoice)
|
||||
? EditIconButton(
|
||||
isVisible: !invoice.isDeleted,
|
||||
onPressed: () => viewModel.onEditPressed(context),
|
||||
)
|
||||
: Container(),
|
||||
ActionMenuButton(
|
||||
user: user,
|
||||
customActions: [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
|
|
@ -8,7 +9,6 @@ import 'package:invoiceninja_flutter/ui/invoice/invoice_screen.dart';
|
|||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/pdf.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view.dart';
|
||||
|
|
@ -36,7 +36,7 @@ class InvoiceViewScreen extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class InvoiceViewVM {
|
||||
class EntityViewVM {
|
||||
final CompanyEntity company;
|
||||
final InvoiceEntity invoice;
|
||||
final ClientEntity client;
|
||||
|
|
@ -48,7 +48,7 @@ class InvoiceViewVM {
|
|||
final Function(BuildContext) onRefreshed;
|
||||
final Function onBackPressed;
|
||||
|
||||
InvoiceViewVM({
|
||||
EntityViewVM({
|
||||
@required this.company,
|
||||
@required this.invoice,
|
||||
@required this.client,
|
||||
|
|
@ -61,6 +61,48 @@ class InvoiceViewVM {
|
|||
@required this.onRefreshed,
|
||||
});
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) =>
|
||||
client == other.client &&
|
||||
company == other.company &&
|
||||
invoice == other.quote &&
|
||||
isSaving == other.isSaving &&
|
||||
isDirty == other.isDirty;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
client.hashCode ^
|
||||
company.hashCode ^
|
||||
invoice.hashCode ^
|
||||
isSaving.hashCode ^
|
||||
isDirty.hashCode;
|
||||
}
|
||||
|
||||
class InvoiceViewVM extends EntityViewVM {
|
||||
InvoiceViewVM({
|
||||
CompanyEntity company,
|
||||
InvoiceEntity invoice,
|
||||
ClientEntity client,
|
||||
bool isSaving,
|
||||
bool isDirty,
|
||||
Function(BuildContext, EntityAction) onActionSelected,
|
||||
Function(BuildContext, [InvoiceItemEntity]) onEditPressed,
|
||||
Function(BuildContext) onClientPressed,
|
||||
Function(BuildContext) onRefreshed,
|
||||
Function onBackPressed,
|
||||
}) : super(
|
||||
company: company,
|
||||
invoice: invoice,
|
||||
client: client,
|
||||
isSaving: isSaving,
|
||||
isDirty: isDirty,
|
||||
onActionSelected: onActionSelected,
|
||||
onEditPressed: onEditPressed,
|
||||
onClientPressed: onClientPressed,
|
||||
onRefreshed: onRefreshed,
|
||||
onBackPressed: onBackPressed,
|
||||
);
|
||||
|
||||
factory InvoiceViewVM.fromStore(Store<AppState> store) {
|
||||
final state = store.state;
|
||||
final invoice = state.invoiceState.map[state.invoiceUIState.selectedId];
|
||||
|
|
@ -141,20 +183,4 @@ class InvoiceViewVM {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) =>
|
||||
client == other.client &&
|
||||
company == other.company &&
|
||||
invoice == other.quote &&
|
||||
isSaving == other.isSaving &&
|
||||
isDirty == other.isDirty;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
client.hashCode ^
|
||||
company.hashCode ^
|
||||
invoice.hashCode ^
|
||||
isSaving.hashCode ^
|
||||
isDirty.hashCode;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,182 +0,0 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/invoice_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';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/invoice_list_item.dart';
|
||||
import 'package:invoiceninja_flutter/ui/quote/quote_list_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class QuoteList extends StatelessWidget {
|
||||
final QuoteListVM viewModel;
|
||||
|
||||
const QuoteList({
|
||||
Key key,
|
||||
@required this.viewModel,
|
||||
}) : super(key: key);
|
||||
|
||||
void _showMenu(
|
||||
BuildContext context, InvoiceEntity quote, ClientEntity client) async {
|
||||
final user = viewModel.user;
|
||||
final message = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => SimpleDialog(children: <Widget>[
|
||||
user.canCreate(EntityType.quote)
|
||||
? ListTile(
|
||||
leading: Icon(Icons.control_point_duplicate),
|
||||
title: Text(AppLocalization.of(context).clone),
|
||||
onTap: () => viewModel.onEntityAction(
|
||||
context, quote, EntityAction.clone),
|
||||
)
|
||||
: Container(),
|
||||
user.canEditEntity(quote) && !quote.isPublic
|
||||
? ListTile(
|
||||
leading: Icon(Icons.publish),
|
||||
title: Text(AppLocalization.of(context).markSent),
|
||||
onTap: () => viewModel.onEntityAction(
|
||||
context, quote, EntityAction.markSent),
|
||||
)
|
||||
: Container(),
|
||||
user.canEditEntity(quote) && client.hasEmailAddress
|
||||
? ListTile(
|
||||
leading: Icon(Icons.send),
|
||||
title: Text(AppLocalization.of(context).email),
|
||||
onTap: () => viewModel.onEntityAction(
|
||||
context, quote, EntityAction.email),
|
||||
)
|
||||
: Container(),
|
||||
ListTile(
|
||||
leading: Icon(Icons.picture_as_pdf),
|
||||
title: Text(AppLocalization.of(context).pdf),
|
||||
onTap: () => viewModel.onEntityAction(
|
||||
context, quote, EntityAction.pdf),
|
||||
),
|
||||
Divider(),
|
||||
user.canEditEntity(quote) && !quote.isActive
|
||||
? ListTile(
|
||||
leading: Icon(Icons.restore),
|
||||
title: Text(AppLocalization.of(context).restore),
|
||||
onTap: () => viewModel.onEntityAction(
|
||||
context, quote, EntityAction.restore),
|
||||
)
|
||||
: Container(),
|
||||
user.canEditEntity(quote) && quote.isActive
|
||||
? ListTile(
|
||||
leading: Icon(Icons.archive),
|
||||
title: Text(AppLocalization.of(context).archive),
|
||||
onTap: () => viewModel.onEntityAction(
|
||||
context, quote, EntityAction.archive),
|
||||
)
|
||||
: Container(),
|
||||
user.canEditEntity(quote) && !quote.isDeleted
|
||||
? ListTile(
|
||||
leading: Icon(Icons.delete),
|
||||
title: Text(AppLocalization.of(context).delete),
|
||||
onTap: () => viewModel.onEntityAction(
|
||||
context, quote, EntityAction.delete),
|
||||
)
|
||||
: Container(),
|
||||
]));
|
||||
if (message != null) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: message,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
@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.clientsInvoices.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: viewModel.quoteList.isEmpty
|
||||
? Opacity(
|
||||
opacity: 0.5,
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppLocalization.of(context).noRecordsFound,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.quoteList.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
final quoteId = viewModel.quoteList[index];
|
||||
final quote = viewModel.quoteMap[quoteId];
|
||||
final client =
|
||||
viewModel.clientMap[quote.clientId];
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
InvoiceListItem(
|
||||
user: viewModel.user,
|
||||
filter: viewModel.filter,
|
||||
invoice: quote,
|
||||
client: viewModel.clientMap[quote.clientId],
|
||||
onDismissed: (DismissDirection direction) =>
|
||||
viewModel.onDismissed(
|
||||
context, quote, direction),
|
||||
onTap: () =>
|
||||
viewModel.onQuoteTap(context, quote),
|
||||
onLongPress: () =>
|
||||
_showMenu(context, quote, client),
|
||||
),
|
||||
Divider(
|
||||
height: 1.0,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
import 'package:invoiceninja_flutter/constants.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/entity_state_label.dart';
|
||||
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/dismissible_entity.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class QuoteListItem extends StatelessWidget {
|
||||
final UserEntity user;
|
||||
final DismissDirectionCallback onDismissed;
|
||||
final GestureTapCallback onTap;
|
||||
final GestureTapCallback onLongPress;
|
||||
final InvoiceEntity invoice;
|
||||
final ClientEntity client;
|
||||
final String filter;
|
||||
|
||||
const QuoteListItem({
|
||||
@required this.user,
|
||||
@required this.onDismissed,
|
||||
@required this.onTap,
|
||||
@required this.onLongPress,
|
||||
@required this.invoice,
|
||||
@required this.client,
|
||||
@required this.filter,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
final filterMatch = filter != null && filter.isNotEmpty
|
||||
? (invoice.matchesFilterValue(filter) ??
|
||||
client.matchesFilterValue(filter))
|
||||
: null;
|
||||
|
||||
return DismissibleEntity(
|
||||
user: user,
|
||||
entity: invoice,
|
||||
onDismissed: onDismissed,
|
||||
child: ListTile(
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
title: Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
client.displayName,
|
||||
style: Theme.of(context).textTheme.title,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
formatNumber(invoice.amount, context,
|
||||
clientId: invoice.clientId),
|
||||
style: Theme.of(context).textTheme.title),
|
||||
],
|
||||
),
|
||||
),
|
||||
subtitle: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: filterMatch == null
|
||||
? Text(invoice.invoiceNumber)
|
||||
: Text(
|
||||
filterMatch,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
invoice.isPastDue
|
||||
? localization.pastDue
|
||||
: localization.lookup(
|
||||
'invoice_status_${invoice.invoiceStatusId}'),
|
||||
style: TextStyle(
|
||||
color: invoice.isPastDue
|
||||
? Colors.red
|
||||
: InvoiceStatusColors.colors[invoice.invoiceStatusId],
|
||||
)),
|
||||
],
|
||||
),
|
||||
EntityStateLabel(invoice),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,8 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
|
||||
import 'package:invoiceninja_flutter/ui/quote/quote_list.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/invoice_list.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/invoice_list_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:invoiceninja_flutter/utils/pdf.dart';
|
||||
|
|
@ -24,10 +25,9 @@ class QuoteListBuilder extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StoreConnector<AppState, QuoteListVM>(
|
||||
//rebuildOnChange: true,
|
||||
converter: QuoteListVM.fromStore,
|
||||
builder: (context, vm) {
|
||||
return QuoteList(
|
||||
return InvoiceList(
|
||||
viewModel: vm,
|
||||
);
|
||||
},
|
||||
|
|
@ -35,38 +35,39 @@ class QuoteListBuilder extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class QuoteListVM {
|
||||
final UserEntity user;
|
||||
final ListUIState listState;
|
||||
final List<int> quoteList;
|
||||
final BuiltMap<int, InvoiceEntity> quoteMap;
|
||||
final BuiltMap<int, ClientEntity> clientMap;
|
||||
final String filter;
|
||||
final bool isLoading;
|
||||
final bool isLoaded;
|
||||
final Function(BuildContext, InvoiceEntity) onQuoteTap;
|
||||
final Function(BuildContext, InvoiceEntity, DismissDirection) onDismissed;
|
||||
final Function(BuildContext) onRefreshed;
|
||||
final Function onClearClientFilterPressed;
|
||||
final Function(BuildContext) onViewClientFilterPressed;
|
||||
final Function(BuildContext, InvoiceEntity, EntityAction) onEntityAction;
|
||||
class QuoteListVM extends EntityListVM {
|
||||
|
||||
QuoteListVM({
|
||||
@required this.user,
|
||||
@required this.listState,
|
||||
@required this.quoteList,
|
||||
@required this.quoteMap,
|
||||
@required this.clientMap,
|
||||
@required this.isLoading,
|
||||
@required this.isLoaded,
|
||||
@required this.filter,
|
||||
@required this.onQuoteTap,
|
||||
@required this.onDismissed,
|
||||
@required this.onRefreshed,
|
||||
@required this.onClearClientFilterPressed,
|
||||
@required this.onViewClientFilterPressed,
|
||||
@required this.onEntityAction,
|
||||
});
|
||||
UserEntity user,
|
||||
ListUIState listState,
|
||||
List<int> invoiceList,
|
||||
BuiltMap<int, InvoiceEntity> invoiceMap,
|
||||
BuiltMap<int, ClientEntity> clientMap,
|
||||
String filter,
|
||||
bool isLoading,
|
||||
bool isLoaded,
|
||||
Function(BuildContext, InvoiceEntity) onInvoiceTap,
|
||||
Function(BuildContext, InvoiceEntity, DismissDirection) onDismissed,
|
||||
Function(BuildContext) onRefreshed,
|
||||
Function onClearClientFilterPressed,
|
||||
Function(BuildContext) onViewClientFilterPressed,
|
||||
Function(BuildContext, InvoiceEntity, EntityAction) onEntityAction,
|
||||
}) : super(
|
||||
user: user,
|
||||
listState: listState,
|
||||
invoiceList: invoiceList,
|
||||
invoiceMap: invoiceMap,
|
||||
clientMap: clientMap,
|
||||
filter: filter,
|
||||
isLoading: isLoading,
|
||||
isLoaded: isLoaded,
|
||||
onInvoiceTap: onInvoiceTap,
|
||||
onDismissed: onDismissed,
|
||||
onRefreshed: onRefreshed,
|
||||
onClearClientFilterPressed: onClearClientFilterPressed,
|
||||
onViewClientFilterPressed: onViewClientFilterPressed,
|
||||
onEntityAction: onEntityAction,
|
||||
);
|
||||
|
||||
static QuoteListVM fromStore(Store<AppState> store) {
|
||||
Future<Null> _handleRefresh(BuildContext context) {
|
||||
|
|
@ -84,17 +85,17 @@ class QuoteListVM {
|
|||
return QuoteListVM(
|
||||
user: state.user,
|
||||
listState: state.quoteListState,
|
||||
quoteList: memoizedFilteredQuoteList(
|
||||
invoiceList: memoizedFilteredQuoteList(
|
||||
state.quoteState.map,
|
||||
state.quoteState.list,
|
||||
state.clientState.map,
|
||||
state.quoteListState),
|
||||
quoteMap: state.quoteState.map,
|
||||
invoiceMap: state.quoteState.map,
|
||||
clientMap: state.clientState.map,
|
||||
isLoading: state.isLoading,
|
||||
isLoaded: state.quoteState.isLoaded && state.clientState.isLoaded,
|
||||
filter: state.quoteListState.filter,
|
||||
onQuoteTap: (context, quote) {
|
||||
onInvoiceTap: (context, quote) {
|
||||
store.dispatch(ViewQuote(quoteId: quote.id, context: context));
|
||||
},
|
||||
onRefreshed: (context) => _handleRefresh(context),
|
||||
|
|
|
|||
|
|
@ -1,312 +0,0 @@
|
|||
import 'package:invoiceninja_flutter/constants.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/buttons/edit_icon_button.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/one_value_header.dart';
|
||||
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/actions_menu_button.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/icon_message.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/invoice/invoice_item_view.dart';
|
||||
import 'package:invoiceninja_flutter/ui/quote/view/quote_view_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class QuoteView extends StatefulWidget {
|
||||
final QuoteViewVM viewModel;
|
||||
|
||||
const QuoteView({
|
||||
Key key,
|
||||
@required this.viewModel,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_QuoteViewState createState() => new _QuoteViewState();
|
||||
}
|
||||
|
||||
class _QuoteViewState extends State<QuoteView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
final viewModel = widget.viewModel;
|
||||
final client = viewModel.client;
|
||||
final company = viewModel.company;
|
||||
|
||||
List<Widget> _buildView() {
|
||||
final quote = widget.viewModel.quote;
|
||||
final user = widget.viewModel.company.user;
|
||||
final widgets = <Widget>[
|
||||
OneValueHeader(
|
||||
backgroundColor: quote.isPastDue
|
||||
? Colors.red
|
||||
: InvoiceStatusColors.colors[quote.invoiceStatusId],
|
||||
label: localization.totalAmount,
|
||||
value: formatNumber(quote.amount, context, clientId: quote.clientId),
|
||||
),
|
||||
];
|
||||
|
||||
final Map<String, String> fields = {
|
||||
QuoteFields.quoteStatusId: quote.isPastDue
|
||||
? localization.pastDue
|
||||
: localization.lookup('invoice_status_${quote.invoiceStatusId}'),
|
||||
QuoteFields.quoteDate: formatDate(quote.invoiceDate, context),
|
||||
QuoteFields.validUntil: formatDate(quote.dueDate, context),
|
||||
InvoiceFields.partial: formatNumber(quote.partial, context,
|
||||
clientId: quote.clientId, zeroIsNull: true),
|
||||
InvoiceFields.partialDueDate: formatDate(quote.partialDueDate, context),
|
||||
InvoiceFields.poNumber: quote.poNumber,
|
||||
InvoiceFields.discount: formatNumber(quote.discount, context,
|
||||
clientId: quote.clientId,
|
||||
zeroIsNull: true,
|
||||
formatNumberType: quote.isAmountDiscount
|
||||
? FormatNumberType.money
|
||||
: FormatNumberType.percent),
|
||||
};
|
||||
|
||||
if (quote.customTextValue1.isNotEmpty) {
|
||||
final label1 = company.getCustomFieldLabel(CustomFieldType.invoice1);
|
||||
fields[label1] = quote.customTextValue1;
|
||||
}
|
||||
if (quote.customTextValue2.isNotEmpty) {
|
||||
final label2 = company.getCustomFieldLabel(CustomFieldType.invoice2);
|
||||
fields[label2] = quote.customTextValue2;
|
||||
}
|
||||
|
||||
final List<Widget> fieldWidgets = [];
|
||||
fields.forEach((field, value) {
|
||||
if (value != null && value.isNotEmpty) {
|
||||
fieldWidgets.add(Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Text(
|
||||
localization.lookup(field),
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w300,
|
||||
),
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
)),
|
||||
],
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
widgets.addAll([
|
||||
Material(
|
||||
color: Theme.of(context).canvasColor,
|
||||
child: ListTile(
|
||||
title: Text(client?.displayName ?? ''),
|
||||
leading: Icon(FontAwesomeIcons.users, size: 18.0),
|
||||
trailing: Icon(Icons.navigate_next),
|
||||
onTap: () => viewModel.onClientPressed(context),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
height: 12.0,
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context).canvasColor,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 16.0, top: 10.0, right: 16.0),
|
||||
child: GridView.count(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
primary: true,
|
||||
crossAxisCount: 2,
|
||||
children: fieldWidgets,
|
||||
childAspectRatio: 3.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
height: 12.0,
|
||||
),
|
||||
]);
|
||||
|
||||
if (quote.privateNotes != null && quote.privateNotes.isNotEmpty) {
|
||||
widgets.addAll([
|
||||
IconMessage(quote.privateNotes),
|
||||
Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
height: 12.0,
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
quote.invoiceItems.forEach((quoteItem) {
|
||||
widgets.addAll([
|
||||
InvoiceItemListTile(
|
||||
invoice: quote,
|
||||
invoiceItem: quoteItem,
|
||||
onTap: () => user.canEditEntity(quote)
|
||||
? viewModel.onEditPressed(context, quoteItem)
|
||||
: null,
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
widgets.addAll([
|
||||
Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
height: 12.0,
|
||||
),
|
||||
]);
|
||||
|
||||
Widget surchargeRow(String label, double amount) {
|
||||
return Container(
|
||||
color: Theme.of(context).canvasColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16.0, top: 12.0, right: 16.0, bottom: 12.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
Text(label),
|
||||
SizedBox(
|
||||
width: 80.0,
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Text(formatNumber(amount, context,
|
||||
clientId: quote.clientId))),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (quote.customValue1 != 0 && company.enableCustomInvoiceTaxes1) {
|
||||
widgets.add(surchargeRow(
|
||||
company.getCustomFieldLabel(CustomFieldType.surcharge1),
|
||||
quote.customValue1));
|
||||
}
|
||||
|
||||
if (quote.customValue2 != 0 && company.enableCustomInvoiceTaxes2) {
|
||||
widgets.add(surchargeRow(
|
||||
company.getCustomFieldLabel(CustomFieldType.surcharge2),
|
||||
quote.customValue2));
|
||||
}
|
||||
|
||||
quote
|
||||
.calculateTaxes(company.enableInclusiveTaxes)
|
||||
.forEach((taxName, taxAmount) {
|
||||
widgets.add(surchargeRow(taxName, taxAmount));
|
||||
});
|
||||
|
||||
if (quote.customValue1 != 0 && !company.enableCustomInvoiceTaxes1) {
|
||||
widgets.add(surchargeRow(
|
||||
company.getCustomFieldLabel(CustomFieldType.surcharge1),
|
||||
quote.customValue1));
|
||||
}
|
||||
|
||||
if (quote.customValue2 != 0 && !company.enableCustomInvoiceTaxes2) {
|
||||
widgets.add(surchargeRow(
|
||||
company.getCustomFieldLabel(CustomFieldType.surcharge2),
|
||||
quote.customValue2));
|
||||
}
|
||||
|
||||
return widgets;
|
||||
}
|
||||
|
||||
return WillPopScope(
|
||||
onWillPop: () async {
|
||||
viewModel.onBackPressed();
|
||||
return true;
|
||||
},
|
||||
child: Scaffold(
|
||||
appBar: _CustomAppBar(
|
||||
viewModel: viewModel,
|
||||
),
|
||||
body: Builder(
|
||||
builder: (BuildContext context) {
|
||||
return RefreshIndicator(
|
||||
onRefresh: () => viewModel.onRefreshed(context),
|
||||
child: Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: ListView(
|
||||
children: _buildView(),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
const _CustomAppBar({
|
||||
@required this.viewModel,
|
||||
});
|
||||
|
||||
final QuoteViewVM viewModel;
|
||||
|
||||
@override
|
||||
final Size preferredSize = const Size(double.infinity, 54.0);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
final quote = viewModel.quote;
|
||||
final client = viewModel.client;
|
||||
final user = viewModel.company.user;
|
||||
|
||||
return AppBar(
|
||||
title: Text((localization.quote + ' ' + quote.invoiceNumber) ?? ''),
|
||||
actions: quote.isNew
|
||||
? []
|
||||
: [
|
||||
user.canEditEntity(quote)
|
||||
? EditIconButton(
|
||||
isVisible: !quote.isDeleted,
|
||||
onPressed: () => viewModel.onEditPressed(context),
|
||||
)
|
||||
: Container(),
|
||||
ActionMenuButton(
|
||||
user: user,
|
||||
customActions: [
|
||||
user.canCreate(EntityType.quote)
|
||||
? ActionMenuChoice(
|
||||
action: EntityAction.clone,
|
||||
icon: Icons.control_point_duplicate,
|
||||
label: AppLocalization.of(context).clone,
|
||||
)
|
||||
: null,
|
||||
user.canEditEntity(quote) && !quote.isPublic
|
||||
? ActionMenuChoice(
|
||||
action: EntityAction.markSent,
|
||||
icon: Icons.publish,
|
||||
label: AppLocalization.of(context).markSent,
|
||||
)
|
||||
: null,
|
||||
user.canEditEntity(quote) && client.hasEmailAddress
|
||||
? ActionMenuChoice(
|
||||
action: EntityAction.email,
|
||||
icon: Icons.send,
|
||||
label: AppLocalization.of(context).email,
|
||||
)
|
||||
: null,
|
||||
ActionMenuChoice(
|
||||
action: EntityAction.pdf,
|
||||
icon: Icons.picture_as_pdf,
|
||||
label: AppLocalization.of(context).pdf,
|
||||
),
|
||||
],
|
||||
isSaving: viewModel.isSaving,
|
||||
entity: quote,
|
||||
onSelected: viewModel.onActionSelected,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/quote/quote_screen.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
|
@ -11,7 +13,6 @@ import 'package:invoiceninja_flutter/utils/pdf.dart';
|
|||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/quote/view/quote_view.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
|
||||
|
||||
|
|
@ -28,7 +29,7 @@ class QuoteViewScreen extends StatelessWidget {
|
|||
return QuoteViewVM.fromStore(store);
|
||||
},
|
||||
builder: (context, viewModel) {
|
||||
return QuoteView(
|
||||
return InvoiceView(
|
||||
viewModel: viewModel,
|
||||
);
|
||||
},
|
||||
|
|
@ -36,30 +37,31 @@ class QuoteViewScreen extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class QuoteViewVM {
|
||||
final CompanyEntity company;
|
||||
final InvoiceEntity quote;
|
||||
final ClientEntity client;
|
||||
final bool isSaving;
|
||||
final bool isDirty;
|
||||
final Function(BuildContext, EntityAction) onActionSelected;
|
||||
final Function(BuildContext, [InvoiceItemEntity]) onEditPressed;
|
||||
final Function(BuildContext) onClientPressed;
|
||||
final Function(BuildContext) onRefreshed;
|
||||
final Function onBackPressed;
|
||||
class QuoteViewVM extends EntityViewVM {
|
||||
|
||||
QuoteViewVM({
|
||||
@required this.company,
|
||||
@required this.quote,
|
||||
@required this.client,
|
||||
@required this.isSaving,
|
||||
@required this.isDirty,
|
||||
@required this.onActionSelected,
|
||||
@required this.onEditPressed,
|
||||
@required this.onBackPressed,
|
||||
@required this.onClientPressed,
|
||||
@required this.onRefreshed,
|
||||
});
|
||||
CompanyEntity company,
|
||||
InvoiceEntity invoice,
|
||||
ClientEntity client,
|
||||
bool isSaving,
|
||||
bool isDirty,
|
||||
Function(BuildContext, EntityAction) onActionSelected,
|
||||
Function(BuildContext, [InvoiceItemEntity]) onEditPressed,
|
||||
Function(BuildContext) onClientPressed,
|
||||
Function(BuildContext) onRefreshed,
|
||||
Function onBackPressed,
|
||||
}) : super(
|
||||
company: company,
|
||||
invoice: invoice,
|
||||
client: client,
|
||||
isSaving: isSaving,
|
||||
isDirty: isDirty,
|
||||
onActionSelected: onActionSelected,
|
||||
onEditPressed: onEditPressed,
|
||||
onClientPressed: onClientPressed,
|
||||
onRefreshed: onRefreshed,
|
||||
onBackPressed: onBackPressed,
|
||||
);
|
||||
|
||||
factory QuoteViewVM.fromStore(Store<AppState> store) {
|
||||
final state = store.state;
|
||||
|
|
@ -77,11 +79,11 @@ class QuoteViewVM {
|
|||
company: state.selectedCompany,
|
||||
isSaving: state.isSaving,
|
||||
isDirty: quote.isNew,
|
||||
quote: quote,
|
||||
invoice: quote,
|
||||
client: client,
|
||||
onEditPressed: (BuildContext context, [InvoiceItemEntity invoiceItem]) {
|
||||
final Completer<InvoiceEntity> completer =
|
||||
new Completer<InvoiceEntity>();
|
||||
new Completer<InvoiceEntity>();
|
||||
store.dispatch(EditQuote(
|
||||
quote: quote,
|
||||
context: context,
|
||||
|
|
@ -90,8 +92,8 @@ class QuoteViewVM {
|
|||
completer.future.then((invoice) {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: SnackBarRow(
|
||||
message: AppLocalization.of(context).updatedQuote,
|
||||
)));
|
||||
message: AppLocalization.of(context).updatedQuote,
|
||||
)));
|
||||
});
|
||||
},
|
||||
onRefreshed: (context) => _handleRefresh(context),
|
||||
|
|
@ -114,19 +116,17 @@ class QuoteViewVM {
|
|||
case EntityAction.email:
|
||||
store.dispatch(ShowEmailQuote(
|
||||
completer:
|
||||
snackBarCompleter(context, localization.emailedQuote),
|
||||
snackBarCompleter(context, localization.emailedQuote),
|
||||
quote: quote,
|
||||
context: context));
|
||||
break;
|
||||
case EntityAction.archive:
|
||||
store.dispatch(ArchiveQuoteRequest(
|
||||
popCompleter(context, localization.archivedQuote),
|
||||
quote.id));
|
||||
popCompleter(context, localization.archivedQuote), quote.id));
|
||||
break;
|
||||
case EntityAction.delete:
|
||||
store.dispatch(DeleteQuoteRequest(
|
||||
popCompleter(context, localization.deletedQuote),
|
||||
quote.id));
|
||||
popCompleter(context, localization.deletedQuote), quote.id));
|
||||
break;
|
||||
case EntityAction.restore:
|
||||
store.dispatch(RestoreQuoteRequest(
|
||||
|
|
@ -135,26 +135,9 @@ class QuoteViewVM {
|
|||
break;
|
||||
case EntityAction.clone:
|
||||
Navigator.of(context).pop();
|
||||
store.dispatch(
|
||||
EditQuote(context: context, quote: quote.clone));
|
||||
store.dispatch(EditQuote(context: context, quote: quote.clone));
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) =>
|
||||
client == other.client &&
|
||||
company == other.company &&
|
||||
quote == other.quote &&
|
||||
isSaving == other.isSaving &&
|
||||
isDirty == other.isDirty;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
client.hashCode ^
|
||||
company.hashCode ^
|
||||
quote.hashCode ^
|
||||
isSaving.hashCode ^
|
||||
isDirty.hashCode;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue