Add task and expense tabs to dashboard
This commit is contained in:
parent
790c032693
commit
8da124d1ca
|
|
@ -8,6 +8,7 @@ import 'package:invoiceninja_flutter/redux/dashboard/dashboard_actions.dart';
|
|||
import 'package:invoiceninja_flutter/redux/dashboard/dashboard_sidebar_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/help_text.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart';
|
||||
import 'package:invoiceninja_flutter/ui/expense/expense_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/quote/quote_list_item.dart';
|
||||
|
|
@ -369,29 +370,31 @@ class ExpenseSidbar extends StatelessWidget {
|
|||
final localization = AppLocalization.of(context);
|
||||
final store = StoreProvider.of<AppState>(context);
|
||||
final state = store.state;
|
||||
final runningTasks = memoizedRunningTasks(
|
||||
state.taskState.map,
|
||||
|
||||
/*
|
||||
final recentExpenses = memoizedRecentExpenses(
|
||||
state.expenseState.map,
|
||||
state.clientState.map,
|
||||
);
|
||||
final recentTasks = memoizedRecentTasks(
|
||||
state.taskState.map,
|
||||
final upcomingExpenses = memoizedUpcomingExpenses(
|
||||
state.expenseState.map,
|
||||
state.clientState.map,
|
||||
);
|
||||
final selectedIds =
|
||||
state.dashboardUIState.selectedEntities[EntityType.task];
|
||||
state.dashboardUIState.selectedEntities[EntityType.expense];
|
||||
|
||||
return _DashboardSidebar(
|
||||
entityType: EntityType.quote,
|
||||
label1: localization.runningTasks +
|
||||
(runningTasks.isNotEmpty ? ' (${runningTasks.length})' : ''),
|
||||
list1: runningTasks.isEmpty
|
||||
label1: localization.upcomingExpenses +
|
||||
(upcomingExpenses.isNotEmpty ? ' (${upcomingExpenses.length})' : ''),
|
||||
list1: upcomingExpenses.isEmpty
|
||||
? null
|
||||
: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
itemCount: runningTasks.length,
|
||||
itemCount: upcomingExpenses.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return TaskListItem(
|
||||
task: runningTasks[index],
|
||||
return ExpenseListItem(
|
||||
expense: upcomingExpenses[index],
|
||||
showCheckbox: false,
|
||||
);
|
||||
},
|
||||
|
|
@ -432,6 +435,7 @@ class ExpenseSidbar extends StatelessWidget {
|
|||
separatorBuilder: (context, index) => ListDivider(),
|
||||
),
|
||||
);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,24 +14,22 @@ import 'package:invoiceninja_flutter/utils/platforms.dart';
|
|||
|
||||
class ExpenseListItem extends StatelessWidget {
|
||||
const ExpenseListItem({
|
||||
@required this.user,
|
||||
@required this.expense,
|
||||
@required this.filter,
|
||||
this.filter,
|
||||
this.onTap,
|
||||
this.onLongPress,
|
||||
this.onCheckboxChanged,
|
||||
this.isChecked = false,
|
||||
this.showCheckbox = false,
|
||||
this.isDismissible = true,
|
||||
this.isChecked = false,
|
||||
});
|
||||
|
||||
final UserEntity user;
|
||||
final Function(bool) onCheckboxChanged;
|
||||
final GestureTapCallback onTap;
|
||||
final GestureTapCallback onLongPress;
|
||||
final ExpenseEntity expense;
|
||||
final String filter;
|
||||
final Function(bool) onCheckboxChanged;
|
||||
final bool isChecked;
|
||||
final bool showCheckbox;
|
||||
final bool isDismissible;
|
||||
final bool isChecked;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -49,6 +47,9 @@ class ExpenseListItem extends StatelessWidget {
|
|||
final listUIState = expenseUIState.listUIState;
|
||||
final isInMultiselect = listUIState.isInMultiselect();
|
||||
final showCheckbox = onCheckboxChanged != null || isInMultiselect;
|
||||
final isChecked = isDismissible
|
||||
? (isInMultiselect && listUIState.isSelected(expense.id))
|
||||
: this.isChecked;
|
||||
final textStyle = TextStyle(fontSize: 16);
|
||||
final textColor = Theme.of(context).textTheme.bodyText1.color;
|
||||
|
||||
|
|
@ -90,11 +91,12 @@ class ExpenseListItem extends StatelessWidget {
|
|||
? InkWell(
|
||||
onTap: () => onTap != null
|
||||
? onTap()
|
||||
: selectEntity(entity: expense, context: context),
|
||||
onLongPress: () => onLongPress != null
|
||||
? onLongPress()
|
||||
: selectEntity(
|
||||
entity: expense, context: context, longPress: true),
|
||||
entity: expense,
|
||||
context: context,
|
||||
forceView: !showCheckbox),
|
||||
onLongPress: () => selectEntity(
|
||||
entity: expense, context: context, longPress: true),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 10,
|
||||
|
|
@ -185,11 +187,12 @@ class ExpenseListItem extends StatelessWidget {
|
|||
: ListTile(
|
||||
onTap: () => onTap != null
|
||||
? onTap()
|
||||
: selectEntity(entity: expense, context: context),
|
||||
onLongPress: () => onLongPress != null
|
||||
? onLongPress()
|
||||
: selectEntity(
|
||||
entity: expense, context: context, longPress: true),
|
||||
entity: expense,
|
||||
context: context,
|
||||
forceView: !showCheckbox),
|
||||
onLongPress: () => selectEntity(
|
||||
entity: expense, context: context, longPress: true),
|
||||
leading: showCheckbox
|
||||
? IgnorePointer(
|
||||
ignoring: listUIState.isInMultiselect(),
|
||||
|
|
|
|||
|
|
@ -37,16 +37,10 @@ class ExpenseListBuilder extends StatelessWidget {
|
|||
itemBuilder: (BuildContext context, index) {
|
||||
final expenseId = viewModel.expenseList[index];
|
||||
final expense = viewModel.expenseMap[expenseId];
|
||||
final state = viewModel.state;
|
||||
final listUIState = state.getListState(EntityType.expense);
|
||||
final isInMultiselect = listUIState.isInMultiselect();
|
||||
|
||||
return ExpenseListItem(
|
||||
user: state.user,
|
||||
filter: viewModel.filter,
|
||||
expense: expense,
|
||||
isChecked:
|
||||
isInMultiselect && listUIState.isSelected(expense.id),
|
||||
);
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -205,10 +205,9 @@ class _InvoiceItemSelectorState extends State<InvoiceItemSelector>
|
|||
return ProductListItem(
|
||||
isDismissible: false,
|
||||
onCheckboxChanged: (checked) => _toggleEntity(product),
|
||||
isChecked: _selected.contains(product),
|
||||
product: product,
|
||||
userCompany: state.userCompany,
|
||||
filter: _filter,
|
||||
isChecked: _selected.contains(product),
|
||||
onTap: () {
|
||||
if (_selected.isNotEmpty) {
|
||||
_toggleEntity(product);
|
||||
|
|
@ -247,6 +246,15 @@ class _InvoiceItemSelectorState extends State<InvoiceItemSelector>
|
|||
isDismissible: false,
|
||||
task: task,
|
||||
filter: _filter,
|
||||
isChecked: _selected.contains(task),
|
||||
onTap: () {
|
||||
if (_selected.isNotEmpty) {
|
||||
_toggleEntity(task);
|
||||
} else {
|
||||
_selected.add(task);
|
||||
_onItemsSelected(context);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
@ -271,7 +279,6 @@ class _InvoiceItemSelectorState extends State<InvoiceItemSelector>
|
|||
final expense = state.expenseState.map[entityId] ?? ExpenseEntity();
|
||||
return ExpenseListItem(
|
||||
isDismissible: false,
|
||||
user: state.user,
|
||||
onCheckboxChanged: (checked) => _toggleEntity(expense),
|
||||
isChecked: _selected.contains(expense),
|
||||
expense: expense,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import 'package:invoiceninja_flutter/utils/platforms.dart';
|
|||
|
||||
class ProductListItem extends StatelessWidget {
|
||||
const ProductListItem({
|
||||
@required this.userCompany,
|
||||
@required this.product,
|
||||
@required this.filter,
|
||||
this.onTap,
|
||||
|
|
@ -23,14 +22,11 @@ class ProductListItem extends StatelessWidget {
|
|||
this.isDismissible = true,
|
||||
});
|
||||
|
||||
final UserCompanyEntity userCompany;
|
||||
final GestureTapCallback onTap;
|
||||
final GestureTapCallback onLongPress;
|
||||
final Function(bool) onCheckboxChanged;
|
||||
final bool isChecked;
|
||||
final bool isDismissible;
|
||||
|
||||
//final ValueChanged<bool> onCheckboxChanged;
|
||||
final ProductEntity product;
|
||||
final String filter;
|
||||
|
||||
|
|
@ -56,7 +52,7 @@ class ProductListItem extends StatelessWidget {
|
|||
(uiState.isEditing
|
||||
? productUIState.editing.id
|
||||
: productUIState.selectedId),
|
||||
userCompany: userCompany,
|
||||
userCompany: state.userCompany,
|
||||
entity: product,
|
||||
child: LayoutBuilder(
|
||||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ class ProductListBuilder extends StatelessWidget {
|
|||
final isInMultiselect = listState.isInMultiselect();
|
||||
|
||||
return ProductListItem(
|
||||
userCompany: viewModel.state.userCompany,
|
||||
filter: viewModel.filter,
|
||||
product: product,
|
||||
isChecked: isInMultiselect && listState.isSelected(product.id),
|
||||
|
|
|
|||
|
|
@ -19,14 +19,20 @@ class TaskListItem extends StatelessWidget {
|
|||
const TaskListItem({
|
||||
@required this.task,
|
||||
this.filter,
|
||||
this.onTap,
|
||||
this.onCheckboxChanged,
|
||||
this.isDismissible = true,
|
||||
this.showCheckbox = true,
|
||||
this.showCheckbox = false,
|
||||
this.isChecked = false,
|
||||
});
|
||||
|
||||
final Function(bool) onCheckboxChanged;
|
||||
final GestureTapCallback onTap;
|
||||
final TaskEntity task;
|
||||
final String filter;
|
||||
final bool showCheckbox;
|
||||
final bool isDismissible;
|
||||
final bool isChecked;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
@ -39,8 +45,11 @@ class TaskListItem extends StatelessWidget {
|
|||
? (task.matchesFilterValue(filter) ?? client.matchesFilterValue(filter))
|
||||
: null;
|
||||
final listUIState = taskUIState.listUIState;
|
||||
final isInMultiselect = showCheckbox && listUIState.isInMultiselect();
|
||||
final isChecked = isInMultiselect && listUIState.isSelected(task.id);
|
||||
final isInMultiselect = listUIState.isInMultiselect();
|
||||
final showCheckbox = onCheckboxChanged != null || isInMultiselect;
|
||||
final isChecked = isDismissible
|
||||
? (isInMultiselect && listUIState.isSelected(task.id))
|
||||
: this.isChecked;
|
||||
final textStyle = TextStyle(fontSize: 16);
|
||||
final subtitle = client.displayName;
|
||||
final textColor = Theme.of(context).textTheme.bodyText1.color;
|
||||
|
|
@ -83,11 +92,13 @@ class TaskListItem extends StatelessWidget {
|
|||
builder: (BuildContext context, BoxConstraints constraints) {
|
||||
return constraints.maxWidth > kTableListWidthCutoff
|
||||
? InkWell(
|
||||
onTap: () => selectEntity(
|
||||
entity: task,
|
||||
context: context,
|
||||
forceView: !showCheckbox,
|
||||
),
|
||||
onTap: () => onTap != null
|
||||
? onTap()
|
||||
: selectEntity(
|
||||
entity: task,
|
||||
context: context,
|
||||
forceView: !showCheckbox,
|
||||
),
|
||||
onLongPress: () => selectEntity(
|
||||
entity: task,
|
||||
context: context,
|
||||
|
|
@ -113,7 +124,8 @@ class TaskListItem extends StatelessWidget {
|
|||
value: isChecked,
|
||||
materialTapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
onChanged: (value) => null,
|
||||
onChanged: (value) =>
|
||||
onCheckboxChanged(value),
|
||||
activeColor: Theme.of(context).accentColor,
|
||||
),
|
||||
),
|
||||
|
|
@ -178,11 +190,13 @@ class TaskListItem extends StatelessWidget {
|
|||
),
|
||||
)
|
||||
: ListTile(
|
||||
onTap: () => selectEntity(
|
||||
entity: task,
|
||||
context: context,
|
||||
forceView: !showCheckbox,
|
||||
),
|
||||
onTap: () => onTap != null
|
||||
? onTap()
|
||||
: selectEntity(
|
||||
entity: task,
|
||||
context: context,
|
||||
forceView: !showCheckbox,
|
||||
),
|
||||
onLongPress: () => selectEntity(
|
||||
entity: task, context: context, longPress: true),
|
||||
leading: showCheckbox
|
||||
|
|
@ -192,7 +206,7 @@ class TaskListItem extends StatelessWidget {
|
|||
value: isChecked,
|
||||
materialTapTargetSize:
|
||||
MaterialTapTargetSize.shrinkWrap,
|
||||
onChanged: (value) => null,
|
||||
onChanged: (value) => onCheckboxChanged(value),
|
||||
activeColor: Theme.of(context).accentColor,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ mixin LocalizationsProvider on LocaleCodeAware {
|
|||
'running_tasks': 'Running Tasks',
|
||||
'recent_tasks': 'Recent Tasks',
|
||||
'recent_expenses': 'Recent Expenses',
|
||||
'upcoming_expenses': 'Upcoming Expenses',
|
||||
'update_app': 'Update App',
|
||||
'started_import': 'Successfully started import',
|
||||
'duplicate_column_mapping': 'Duplicate column mapping',
|
||||
|
|
@ -5332,6 +5333,9 @@ mixin LocalizationsProvider on LocaleCodeAware {
|
|||
String get recentExpenses =>
|
||||
_localizedValues[localeCode]['recent_expenses'] ?? '';
|
||||
|
||||
String get upcomingExpenses =>
|
||||
_localizedValues[localeCode]['upcoming_expenses'] ?? '';
|
||||
|
||||
String lookup(String key) {
|
||||
final lookupKey = toSnakeCase(key);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue