Dashboard

This commit is contained in:
Hillel Coren 2020-07-22 12:31:56 +03:00
parent 52496d15ae
commit 01e291df56
8 changed files with 76 additions and 57 deletions

View File

@ -61,3 +61,36 @@ List<PaymentEntity> _recentPayments(
return payments;
}
var memoizedUpcomingQuotes = memo1((BuiltMap<String, InvoiceEntity> quoteMap) =>
_upcomingQuotes(quoteMap: quoteMap));
List<InvoiceEntity> _upcomingQuotes(
{BuiltMap<String, InvoiceEntity> quoteMap}) {
final quotes = <InvoiceEntity>[];
quoteMap.forEach((index, quote) {
if (quote.isUpcoming) {
quotes.add(quote);
}
});
quotes.sort((quoteA, quoteB) => quoteA.dueDate.compareTo(quoteB.dueDate));
return quotes;
}
var memoizedExpiredQuotes = memo1((BuiltMap<String, InvoiceEntity> quoteMap) =>
_expiredQuotes(quoteMap: quoteMap));
List<InvoiceEntity> _expiredQuotes({BuiltMap<String, InvoiceEntity> quoteMap}) {
final quotes = <InvoiceEntity>[];
quoteMap.forEach((index, quote) {
if (quote.isPastDue) {
quotes.add(quote);
}
});
quotes.sort((quoteA, quoteB) => quoteA.dueDate.compareTo(quoteB.dueDate));
return quotes;
}

View File

@ -29,6 +29,14 @@ abstract class QuoteState implements Built<QuoteState, QuoteStateBuilder> {
BuiltList<String> get list;
InvoiceEntity get(String quoteId) {
if (map.containsKey(quoteId)) {
return map[quoteId];
} else {
return InvoiceEntity(id: quoteId, entityType: EntityType.quote);
}
}
QuoteState loadQuotes(BuiltList<InvoiceEntity> quotes) {
final map = Map<String, InvoiceEntity>.fromIterable(
quotes,

View File

@ -92,8 +92,10 @@ class DismissibleEntity extends StatelessWidget {
),
],
child: SelectedIndicator(
isSelected:
isSelected && !isMultiselect && !entity.entityType.isSetting,
isSelected: isSelected &&
showCheckbox &&
!isMultiselect &&
!entity.entityType.isSetting,
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: 60,

View File

@ -9,6 +9,7 @@ import 'package:invoiceninja_flutter/ui/app/help_text.dart';
import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart';
import 'package:invoiceninja_flutter/ui/invoice/invoice_list_item.dart';
import 'package:invoiceninja_flutter/ui/payment/payment_list_item.dart';
import 'package:invoiceninja_flutter/ui/quote/quote_list_item.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
class SidebarScaffold extends StatelessWidget {
@ -185,37 +186,37 @@ class _QuoteSidebar extends StatelessWidget {
final localization = AppLocalization.of(context);
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final upcomingInvoices = memoizedUpcomingInvoices(state.invoiceState.map);
final pastDueInvoices = memoizedPastDueInvoices(state.invoiceState.map);
final selectedIds = state.uiState.selectedEntities[EntityType.invoice];
final upcomingQuotes = memoizedUpcomingQuotes(state.quoteState.map);
final expriedQuotes = memoizedExpiredQuotes(state.quoteState.map);
final selectedIds = state.uiState.selectedEntities[EntityType.quote];
return _DashboardSidebar(
entityType: EntityType.invoice,
label1: localization.upcomingInvoices +
(upcomingInvoices.isNotEmpty ? ' (${upcomingInvoices.length})' : ''),
list1: upcomingInvoices.isEmpty
entityType: EntityType.quote,
label1: localization.upcomingQuotes +
(upcomingQuotes.isNotEmpty ? ' (${upcomingQuotes.length})' : ''),
list1: upcomingQuotes.isEmpty
? null
: ListView.separated(
shrinkWrap: true,
itemCount: upcomingInvoices.length,
itemCount: upcomingQuotes.length,
itemBuilder: (BuildContext context, int index) {
return InvoiceListItem(
invoice: upcomingInvoices[index],
return QuoteListItem(
quote: upcomingQuotes[index],
showCheckbox: false,
);
},
separatorBuilder: (context, index) => ListDivider(),
),
label2: localization.pastDueInvoices +
(pastDueInvoices.isNotEmpty ? ' (${pastDueInvoices.length})' : ''),
list2: pastDueInvoices.isEmpty
(expriedQuotes.isNotEmpty ? ' (${expriedQuotes.length})' : ''),
list2: expriedQuotes.isEmpty
? null
: ListView.separated(
shrinkWrap: true,
itemCount: pastDueInvoices.length,
itemCount: expriedQuotes.length,
itemBuilder: (BuildContext context, int index) {
return InvoiceListItem(
invoice: pastDueInvoices[index],
return QuoteListItem(
quote: expriedQuotes[index],
showCheckbox: false,
);
},
@ -230,8 +231,8 @@ class _QuoteSidebar extends StatelessWidget {
shrinkWrap: true,
itemCount: selectedIds?.length,
itemBuilder: (BuildContext context, int index) {
return InvoiceListItem(
invoice: state.invoiceState.get(selectedIds[index]),
return QuoteListItem(
quote: state.quoteState.get(selectedIds[index]),
showCheckbox: false,
);
},

View File

@ -59,7 +59,6 @@ class InvoiceListItem extends StatelessWidget {
return DismissibleEntity(
isSelected: isDesktop(context) &&
showCheckbox &&
invoice.id ==
(uiState.isEditing
? invoiceUIState.editing.id

View File

@ -55,7 +55,6 @@ class PaymentListItem extends StatelessWidget {
return DismissibleEntity(
isSelected: isDesktop(context) &&
showCheckbox &&
payment.id ==
(uiState.isEditing
? paymentUIState.editing.id

View File

@ -14,33 +14,24 @@ import 'package:invoiceninja_flutter/utils/localization.dart';
class QuoteListItem extends StatelessWidget {
const QuoteListItem({
@required this.user,
@required this.quote,
@required this.client,
@required this.filter,
this.onTap,
this.onLongPress,
this.onCheckboxChanged,
this.isChecked = false,
this.filter,
this.showCheckbox = true,
});
final UserEntity user;
final GestureTapCallback onTap;
final GestureTapCallback onLongPress;
final InvoiceEntity quote;
final ClientEntity client;
final String filter;
final Function(bool) onCheckboxChanged;
final bool isChecked;
final bool showCheckbox;
@override
Widget build(BuildContext context) {
final state = StoreProvider.of<AppState>(context).state;
final client = state.clientState.get(quote.clientId);
final uiState = state.uiState;
final quoteUIState = uiState.quoteUIState;
final listUIState = state.getUIState(quote.entityType).listUIState;
final isInMultiselect = listUIState.isInMultiselect();
final showCheckbox = onCheckboxChanged != null || isInMultiselect;
final isInMultiselect = showCheckbox && listUIState.isInMultiselect();
final isChecked = isInMultiselect && listUIState.isSelected(quote.id);
final textStyle = TextStyle(fontSize: 16);
final localization = AppLocalization.of(context);
final textColor = Theme.of(context).textTheme.bodyText1.color;
@ -66,17 +57,14 @@ class QuoteListItem extends StatelessWidget {
? quoteUIState.editing.id
: quoteUIState.selectedId),
userCompany: state.userCompany,
showCheckbox: showCheckbox,
entity: quote,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return constraints.maxWidth > kTableListWidthCutoff
? InkWell(
onTap: () => onTap != null
? onTap()
: selectEntity(entity: quote, context: context),
onLongPress: () => onLongPress != null
? onLongPress()
: selectEntity(
onTap: () => selectEntity(entity: quote, context: context),
onLongPress: () => selectEntity(
entity: quote, context: context, longPress: true),
child: Padding(
padding: const EdgeInsets.only(
@ -96,8 +84,7 @@ class QuoteListItem extends StatelessWidget {
value: isChecked,
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
onChanged: (value) =>
onCheckboxChanged(value),
onChanged: (value) => null,
activeColor:
Theme.of(context).accentColor,
),
@ -170,12 +157,8 @@ class QuoteListItem extends StatelessWidget {
),
)
: ListTile(
onTap: () => onTap != null
? onTap()
: selectEntity(entity: quote, context: context),
onLongPress: () => onLongPress != null
? onLongPress()
: selectEntity(
onTap: () => selectEntity(entity: quote, context: context),
onLongPress: () => selectEntity(
entity: quote, context: context, longPress: true),
leading: showCheckbox
? IgnorePointer(
@ -184,7 +167,7 @@ class QuoteListItem extends StatelessWidget {
value: isChecked,
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
onChanged: (value) => onCheckboxChanged(value),
onChanged: (value) => null,
activeColor: Theme.of(context).accentColor,
),
)

View File

@ -27,7 +27,6 @@ class QuoteListBuilder extends StatelessWidget {
return StoreConnector<AppState, QuoteListVM>(
converter: QuoteListVM.fromStore,
builder: (context, viewModel) {
final state = viewModel.state;
return EntityList(
entityType: EntityType.quote,
presenter: QuotePresenter(),
@ -41,15 +40,10 @@ class QuoteListBuilder extends StatelessWidget {
itemBuilder: (BuildContext context, index) {
final invoiceId = viewModel.invoiceList[index];
final invoice = viewModel.invoiceMap[invoiceId];
final listState = state.getListState(EntityType.quote);
final isInMultiselect = listState.isInMultiselect();
return QuoteListItem(
user: state.user,
filter: viewModel.filter,
quote: invoice,
client: viewModel.clientMap[invoice.clientId] ?? ClientEntity(),
isChecked: isInMultiselect && listState.isSelected(invoice.id),
);
});
},