Dashboard
This commit is contained in:
parent
52496d15ae
commit
01e291df56
|
|
@ -61,3 +61,36 @@ List<PaymentEntity> _recentPayments(
|
||||||
|
|
||||||
return payments;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,14 @@ abstract class QuoteState implements Built<QuoteState, QuoteStateBuilder> {
|
||||||
|
|
||||||
BuiltList<String> get list;
|
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) {
|
QuoteState loadQuotes(BuiltList<InvoiceEntity> quotes) {
|
||||||
final map = Map<String, InvoiceEntity>.fromIterable(
|
final map = Map<String, InvoiceEntity>.fromIterable(
|
||||||
quotes,
|
quotes,
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,10 @@ class DismissibleEntity extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
child: SelectedIndicator(
|
child: SelectedIndicator(
|
||||||
isSelected:
|
isSelected: isSelected &&
|
||||||
isSelected && !isMultiselect && !entity.entityType.isSetting,
|
showCheckbox &&
|
||||||
|
!isMultiselect &&
|
||||||
|
!entity.entityType.isSetting,
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: BoxConstraints(
|
constraints: BoxConstraints(
|
||||||
minHeight: 60,
|
minHeight: 60,
|
||||||
|
|
|
||||||
|
|
@ -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/app/lists/list_divider.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/invoice/invoice_list_item.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/payment/payment_list_item.dart';
|
||||||
|
import 'package:invoiceninja_flutter/ui/quote/quote_list_item.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||||
|
|
||||||
class SidebarScaffold extends StatelessWidget {
|
class SidebarScaffold extends StatelessWidget {
|
||||||
|
|
@ -185,37 +186,37 @@ class _QuoteSidebar extends StatelessWidget {
|
||||||
final localization = AppLocalization.of(context);
|
final localization = AppLocalization.of(context);
|
||||||
final store = StoreProvider.of<AppState>(context);
|
final store = StoreProvider.of<AppState>(context);
|
||||||
final state = store.state;
|
final state = store.state;
|
||||||
final upcomingInvoices = memoizedUpcomingInvoices(state.invoiceState.map);
|
final upcomingQuotes = memoizedUpcomingQuotes(state.quoteState.map);
|
||||||
final pastDueInvoices = memoizedPastDueInvoices(state.invoiceState.map);
|
final expriedQuotes = memoizedExpiredQuotes(state.quoteState.map);
|
||||||
final selectedIds = state.uiState.selectedEntities[EntityType.invoice];
|
final selectedIds = state.uiState.selectedEntities[EntityType.quote];
|
||||||
|
|
||||||
return _DashboardSidebar(
|
return _DashboardSidebar(
|
||||||
entityType: EntityType.invoice,
|
entityType: EntityType.quote,
|
||||||
label1: localization.upcomingInvoices +
|
label1: localization.upcomingQuotes +
|
||||||
(upcomingInvoices.isNotEmpty ? ' (${upcomingInvoices.length})' : ''),
|
(upcomingQuotes.isNotEmpty ? ' (${upcomingQuotes.length})' : ''),
|
||||||
list1: upcomingInvoices.isEmpty
|
list1: upcomingQuotes.isEmpty
|
||||||
? null
|
? null
|
||||||
: ListView.separated(
|
: ListView.separated(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: upcomingInvoices.length,
|
itemCount: upcomingQuotes.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return InvoiceListItem(
|
return QuoteListItem(
|
||||||
invoice: upcomingInvoices[index],
|
quote: upcomingQuotes[index],
|
||||||
showCheckbox: false,
|
showCheckbox: false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
separatorBuilder: (context, index) => ListDivider(),
|
separatorBuilder: (context, index) => ListDivider(),
|
||||||
),
|
),
|
||||||
label2: localization.pastDueInvoices +
|
label2: localization.pastDueInvoices +
|
||||||
(pastDueInvoices.isNotEmpty ? ' (${pastDueInvoices.length})' : ''),
|
(expriedQuotes.isNotEmpty ? ' (${expriedQuotes.length})' : ''),
|
||||||
list2: pastDueInvoices.isEmpty
|
list2: expriedQuotes.isEmpty
|
||||||
? null
|
? null
|
||||||
: ListView.separated(
|
: ListView.separated(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: pastDueInvoices.length,
|
itemCount: expriedQuotes.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return InvoiceListItem(
|
return QuoteListItem(
|
||||||
invoice: pastDueInvoices[index],
|
quote: expriedQuotes[index],
|
||||||
showCheckbox: false,
|
showCheckbox: false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -230,8 +231,8 @@ class _QuoteSidebar extends StatelessWidget {
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: selectedIds?.length,
|
itemCount: selectedIds?.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return InvoiceListItem(
|
return QuoteListItem(
|
||||||
invoice: state.invoiceState.get(selectedIds[index]),
|
quote: state.quoteState.get(selectedIds[index]),
|
||||||
showCheckbox: false,
|
showCheckbox: false,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,6 @@ class InvoiceListItem extends StatelessWidget {
|
||||||
|
|
||||||
return DismissibleEntity(
|
return DismissibleEntity(
|
||||||
isSelected: isDesktop(context) &&
|
isSelected: isDesktop(context) &&
|
||||||
showCheckbox &&
|
|
||||||
invoice.id ==
|
invoice.id ==
|
||||||
(uiState.isEditing
|
(uiState.isEditing
|
||||||
? invoiceUIState.editing.id
|
? invoiceUIState.editing.id
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,6 @@ class PaymentListItem extends StatelessWidget {
|
||||||
|
|
||||||
return DismissibleEntity(
|
return DismissibleEntity(
|
||||||
isSelected: isDesktop(context) &&
|
isSelected: isDesktop(context) &&
|
||||||
showCheckbox &&
|
|
||||||
payment.id ==
|
payment.id ==
|
||||||
(uiState.isEditing
|
(uiState.isEditing
|
||||||
? paymentUIState.editing.id
|
? paymentUIState.editing.id
|
||||||
|
|
|
||||||
|
|
@ -14,33 +14,24 @@ import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||||
|
|
||||||
class QuoteListItem extends StatelessWidget {
|
class QuoteListItem extends StatelessWidget {
|
||||||
const QuoteListItem({
|
const QuoteListItem({
|
||||||
@required this.user,
|
|
||||||
@required this.quote,
|
@required this.quote,
|
||||||
@required this.client,
|
this.filter,
|
||||||
@required this.filter,
|
this.showCheckbox = true,
|
||||||
this.onTap,
|
|
||||||
this.onLongPress,
|
|
||||||
this.onCheckboxChanged,
|
|
||||||
this.isChecked = false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final UserEntity user;
|
|
||||||
final GestureTapCallback onTap;
|
|
||||||
final GestureTapCallback onLongPress;
|
|
||||||
final InvoiceEntity quote;
|
final InvoiceEntity quote;
|
||||||
final ClientEntity client;
|
|
||||||
final String filter;
|
final String filter;
|
||||||
final Function(bool) onCheckboxChanged;
|
final bool showCheckbox;
|
||||||
final bool isChecked;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final state = StoreProvider.of<AppState>(context).state;
|
final state = StoreProvider.of<AppState>(context).state;
|
||||||
|
final client = state.clientState.get(quote.clientId);
|
||||||
final uiState = state.uiState;
|
final uiState = state.uiState;
|
||||||
final quoteUIState = uiState.quoteUIState;
|
final quoteUIState = uiState.quoteUIState;
|
||||||
final listUIState = state.getUIState(quote.entityType).listUIState;
|
final listUIState = state.getUIState(quote.entityType).listUIState;
|
||||||
final isInMultiselect = listUIState.isInMultiselect();
|
final isInMultiselect = showCheckbox && listUIState.isInMultiselect();
|
||||||
final showCheckbox = onCheckboxChanged != null || isInMultiselect;
|
final isChecked = isInMultiselect && listUIState.isSelected(quote.id);
|
||||||
final textStyle = TextStyle(fontSize: 16);
|
final textStyle = TextStyle(fontSize: 16);
|
||||||
final localization = AppLocalization.of(context);
|
final localization = AppLocalization.of(context);
|
||||||
final textColor = Theme.of(context).textTheme.bodyText1.color;
|
final textColor = Theme.of(context).textTheme.bodyText1.color;
|
||||||
|
|
@ -66,17 +57,14 @@ class QuoteListItem extends StatelessWidget {
|
||||||
? quoteUIState.editing.id
|
? quoteUIState.editing.id
|
||||||
: quoteUIState.selectedId),
|
: quoteUIState.selectedId),
|
||||||
userCompany: state.userCompany,
|
userCompany: state.userCompany,
|
||||||
|
showCheckbox: showCheckbox,
|
||||||
entity: quote,
|
entity: quote,
|
||||||
child: LayoutBuilder(
|
child: LayoutBuilder(
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
return constraints.maxWidth > kTableListWidthCutoff
|
return constraints.maxWidth > kTableListWidthCutoff
|
||||||
? InkWell(
|
? InkWell(
|
||||||
onTap: () => onTap != null
|
onTap: () => selectEntity(entity: quote, context: context),
|
||||||
? onTap()
|
onLongPress: () => selectEntity(
|
||||||
: selectEntity(entity: quote, context: context),
|
|
||||||
onLongPress: () => onLongPress != null
|
|
||||||
? onLongPress()
|
|
||||||
: selectEntity(
|
|
||||||
entity: quote, context: context, longPress: true),
|
entity: quote, context: context, longPress: true),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
|
|
@ -96,8 +84,7 @@ class QuoteListItem extends StatelessWidget {
|
||||||
value: isChecked,
|
value: isChecked,
|
||||||
materialTapTargetSize:
|
materialTapTargetSize:
|
||||||
MaterialTapTargetSize.shrinkWrap,
|
MaterialTapTargetSize.shrinkWrap,
|
||||||
onChanged: (value) =>
|
onChanged: (value) => null,
|
||||||
onCheckboxChanged(value),
|
|
||||||
activeColor:
|
activeColor:
|
||||||
Theme.of(context).accentColor,
|
Theme.of(context).accentColor,
|
||||||
),
|
),
|
||||||
|
|
@ -170,12 +157,8 @@ class QuoteListItem extends StatelessWidget {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: ListTile(
|
: ListTile(
|
||||||
onTap: () => onTap != null
|
onTap: () => selectEntity(entity: quote, context: context),
|
||||||
? onTap()
|
onLongPress: () => selectEntity(
|
||||||
: selectEntity(entity: quote, context: context),
|
|
||||||
onLongPress: () => onLongPress != null
|
|
||||||
? onLongPress()
|
|
||||||
: selectEntity(
|
|
||||||
entity: quote, context: context, longPress: true),
|
entity: quote, context: context, longPress: true),
|
||||||
leading: showCheckbox
|
leading: showCheckbox
|
||||||
? IgnorePointer(
|
? IgnorePointer(
|
||||||
|
|
@ -184,7 +167,7 @@ class QuoteListItem extends StatelessWidget {
|
||||||
value: isChecked,
|
value: isChecked,
|
||||||
materialTapTargetSize:
|
materialTapTargetSize:
|
||||||
MaterialTapTargetSize.shrinkWrap,
|
MaterialTapTargetSize.shrinkWrap,
|
||||||
onChanged: (value) => onCheckboxChanged(value),
|
onChanged: (value) => null,
|
||||||
activeColor: Theme.of(context).accentColor,
|
activeColor: Theme.of(context).accentColor,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,6 @@ class QuoteListBuilder extends StatelessWidget {
|
||||||
return StoreConnector<AppState, QuoteListVM>(
|
return StoreConnector<AppState, QuoteListVM>(
|
||||||
converter: QuoteListVM.fromStore,
|
converter: QuoteListVM.fromStore,
|
||||||
builder: (context, viewModel) {
|
builder: (context, viewModel) {
|
||||||
final state = viewModel.state;
|
|
||||||
return EntityList(
|
return EntityList(
|
||||||
entityType: EntityType.quote,
|
entityType: EntityType.quote,
|
||||||
presenter: QuotePresenter(),
|
presenter: QuotePresenter(),
|
||||||
|
|
@ -41,15 +40,10 @@ class QuoteListBuilder extends StatelessWidget {
|
||||||
itemBuilder: (BuildContext context, index) {
|
itemBuilder: (BuildContext context, index) {
|
||||||
final invoiceId = viewModel.invoiceList[index];
|
final invoiceId = viewModel.invoiceList[index];
|
||||||
final invoice = viewModel.invoiceMap[invoiceId];
|
final invoice = viewModel.invoiceMap[invoiceId];
|
||||||
final listState = state.getListState(EntityType.quote);
|
|
||||||
final isInMultiselect = listState.isInMultiselect();
|
|
||||||
|
|
||||||
return QuoteListItem(
|
return QuoteListItem(
|
||||||
user: state.user,
|
|
||||||
filter: viewModel.filter,
|
filter: viewModel.filter,
|
||||||
quote: invoice,
|
quote: invoice,
|
||||||
client: viewModel.clientMap[invoice.clientId] ?? ClientEntity(),
|
|
||||||
isChecked: isInMultiselect && listState.isSelected(invoice.id),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue