Reports
This commit is contained in:
parent
c657e0d8a2
commit
22a474aae5
|
|
@ -86,6 +86,7 @@ class ReportsScreen extends StatelessWidget {
|
|||
Builder(builder: (BuildContext context) {
|
||||
return FlatButton(
|
||||
child: Text(localization.columns),
|
||||
textColor: Colors.white,
|
||||
onPressed: () {
|
||||
multiselectDialog(
|
||||
context: context,
|
||||
|
|
@ -100,6 +101,13 @@ class ReportsScreen extends StatelessWidget {
|
|||
},
|
||||
);
|
||||
}),
|
||||
FlatButton(
|
||||
child: Text(localization.export),
|
||||
textColor: Colors.white,
|
||||
onPressed: () {
|
||||
viewModel.onExportPressed(context);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
body: ListView(
|
||||
|
|
@ -580,12 +588,27 @@ class ReportResult {
|
|||
return true;
|
||||
}
|
||||
|
||||
List<String> sortedColumns(BuildContext context) {
|
||||
final store = StoreProvider.of<AppState>(context);
|
||||
final group = store.state.uiState.reportsUIState.group;
|
||||
final data = columns.toList();
|
||||
|
||||
/*
|
||||
if (group.isNotEmpty) {
|
||||
data.remove(group);
|
||||
data.insert(0, group);
|
||||
}
|
||||
*/
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
List<DataColumn> tableColumns(
|
||||
BuildContext context, Function(int, bool) onSortCallback) {
|
||||
final localization = AppLocalization.of(context);
|
||||
|
||||
return [
|
||||
for (String column in columns)
|
||||
for (String column in sortedColumns(context))
|
||||
DataColumn(
|
||||
tooltip: localization.lookup(column),
|
||||
label: Container(
|
||||
|
|
@ -607,7 +630,7 @@ class ReportResult {
|
|||
Function(String, String) onFilterChanged) {
|
||||
final localization = AppLocalization.of(context);
|
||||
return DataRow(cells: [
|
||||
for (String column in columns)
|
||||
for (String column in sortedColumns(context))
|
||||
if (getReportColumnType(column) == ReportColumnType.bool)
|
||||
DataCell(AppDropdownButton<bool>(
|
||||
labelText: null,
|
||||
|
|
@ -743,7 +766,7 @@ class ReportResult {
|
|||
} else {
|
||||
viewModel.reportTotals.forEach((group, values) {
|
||||
final cells = <DataCell>[];
|
||||
for (var column in columns) {
|
||||
for (var column in sortedColumns(context)) {
|
||||
String value = '';
|
||||
if (column == groupBy) {
|
||||
if (getReportColumnType(column) == ReportColumnType.dateTime) {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import 'dart:html';
|
||||
import 'dart:io' as file;
|
||||
import 'package:flutter_share/flutter_share.dart';
|
||||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -16,6 +19,7 @@ import 'package:invoiceninja_flutter/utils/dialogs.dart';
|
|||
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
import 'package:memoize/memoize.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
|
||||
import 'reports_screen.dart';
|
||||
|
|
@ -42,6 +46,7 @@ class ReportsScreenVM {
|
|||
@required this.onSettingsChanged,
|
||||
@required this.onReportColumnsChanged,
|
||||
@required this.onReportFiltersChanged,
|
||||
@required this.onExportPressed,
|
||||
@required this.onReportSorted,
|
||||
@required this.reportTotals,
|
||||
@required this.reportResult,
|
||||
|
|
@ -51,6 +56,7 @@ class ReportsScreenVM {
|
|||
final ReportResult reportResult;
|
||||
final Map<String, Map<String, double>> reportTotals;
|
||||
final Function(BuildContext, List<String>) onReportColumnsChanged;
|
||||
final Function(BuildContext) onExportPressed;
|
||||
final Function(BuildContext, BuiltMap<String, String>) onReportFiltersChanged;
|
||||
final Function(int, bool) onReportSorted;
|
||||
final Function({
|
||||
|
|
@ -79,91 +85,114 @@ class ReportsScreenVM {
|
|||
break;
|
||||
}
|
||||
|
||||
print('## TOTALS: ${memoizedReportTotals(reportResult, state.uiState.reportsUIState)}');
|
||||
print(
|
||||
'## TOTALS: ${memoizedReportTotals(reportResult, state.uiState.reportsUIState)}');
|
||||
|
||||
return ReportsScreenVM(
|
||||
state: state,
|
||||
reportResult: reportResult,
|
||||
reportTotals:
|
||||
memoizedReportTotals(reportResult, state.uiState.reportsUIState),
|
||||
onReportSorted: (index, ascending) {
|
||||
store.dispatch(UpdateReportSettings(
|
||||
report: state.uiState.reportsUIState.report,
|
||||
sortIndex: index,
|
||||
));
|
||||
},
|
||||
onReportFiltersChanged: (context, filterMap) {
|
||||
store.dispatch(UpdateReportSettings(
|
||||
report: report,
|
||||
filters: filterMap,
|
||||
));
|
||||
},
|
||||
onReportColumnsChanged: (context, columns) {
|
||||
final allReportSettings = state.userCompany.settings.reportSettings;
|
||||
final reportSettings =
|
||||
(allReportSettings != null && allReportSettings.containsKey(report)
|
||||
? allReportSettings[report]
|
||||
: ReportSettingsEntity())
|
||||
.rebuild((b) => b..columns.replace(BuiltList<String>(columns)));
|
||||
final user = state.user.rebuild((b) => b
|
||||
..userCompany
|
||||
.settings
|
||||
.reportSettings[state.uiState.reportsUIState.report] =
|
||||
reportSettings);
|
||||
final completer = snackBarCompleter<Null>(
|
||||
context, AppLocalization.of(context).savedSettings);
|
||||
if (state.authState.hasRecentlyEnteredPassword) {
|
||||
store.dispatch(
|
||||
SaveUserSettingsRequest(
|
||||
completer: completer,
|
||||
user: user,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
passwordCallback(
|
||||
context: context,
|
||||
callback: (password) {
|
||||
store.dispatch(
|
||||
SaveUserSettingsRequest(
|
||||
completer: completer,
|
||||
user: user,
|
||||
password: password,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
onSettingsChanged: ({
|
||||
String report,
|
||||
String group,
|
||||
String subgroup,
|
||||
String chart,
|
||||
String customStartDate,
|
||||
String customEndDate,
|
||||
}) {
|
||||
final reportState = state.uiState.reportsUIState;
|
||||
if (group != null && reportState.group != group) {
|
||||
state: state,
|
||||
reportResult: reportResult,
|
||||
reportTotals:
|
||||
memoizedReportTotals(reportResult, state.uiState.reportsUIState),
|
||||
onReportSorted: (index, ascending) {
|
||||
store.dispatch(UpdateReportSettings(
|
||||
report: report ?? reportState.report,
|
||||
group: group,
|
||||
chart: chart,
|
||||
subgroup: subgroup,
|
||||
customStartDate: '',
|
||||
customEndDate: '',
|
||||
filters: BuiltMap<String, String>(),
|
||||
report: state.uiState.reportsUIState.report,
|
||||
sortIndex: index,
|
||||
));
|
||||
} else {
|
||||
},
|
||||
onReportFiltersChanged: (context, filterMap) {
|
||||
store.dispatch(UpdateReportSettings(
|
||||
report: report ?? reportState.report,
|
||||
group: group,
|
||||
subgroup: subgroup,
|
||||
chart: chart,
|
||||
customStartDate: customStartDate,
|
||||
customEndDate: customEndDate,
|
||||
report: report,
|
||||
filters: filterMap,
|
||||
));
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
onReportColumnsChanged: (context, columns) {
|
||||
final allReportSettings = state.userCompany.settings.reportSettings;
|
||||
final reportSettings = (allReportSettings != null &&
|
||||
allReportSettings.containsKey(report)
|
||||
? allReportSettings[report]
|
||||
: ReportSettingsEntity())
|
||||
.rebuild((b) => b..columns.replace(BuiltList<String>(columns)));
|
||||
final user = state.user.rebuild((b) => b
|
||||
..userCompany
|
||||
.settings
|
||||
.reportSettings[state.uiState.reportsUIState.report] =
|
||||
reportSettings);
|
||||
final completer = snackBarCompleter<Null>(
|
||||
context, AppLocalization.of(context).savedSettings);
|
||||
if (state.authState.hasRecentlyEnteredPassword) {
|
||||
store.dispatch(
|
||||
SaveUserSettingsRequest(
|
||||
completer: completer,
|
||||
user: user,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
passwordCallback(
|
||||
context: context,
|
||||
callback: (password) {
|
||||
store.dispatch(
|
||||
SaveUserSettingsRequest(
|
||||
completer: completer,
|
||||
user: user,
|
||||
password: password,
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
onSettingsChanged: ({
|
||||
String report,
|
||||
String group,
|
||||
String subgroup,
|
||||
String chart,
|
||||
String customStartDate,
|
||||
String customEndDate,
|
||||
}) {
|
||||
final reportState = state.uiState.reportsUIState;
|
||||
if (group != null && reportState.group != group) {
|
||||
store.dispatch(UpdateReportSettings(
|
||||
report: report ?? reportState.report,
|
||||
group: group,
|
||||
chart: chart,
|
||||
subgroup: subgroup,
|
||||
customStartDate: '',
|
||||
customEndDate: '',
|
||||
filters: BuiltMap<String, String>(),
|
||||
));
|
||||
} else {
|
||||
store.dispatch(UpdateReportSettings(
|
||||
report: report ?? reportState.report,
|
||||
group: group,
|
||||
subgroup: subgroup,
|
||||
chart: chart,
|
||||
customStartDate: customStartDate,
|
||||
customEndDate: customEndDate,
|
||||
));
|
||||
}
|
||||
},
|
||||
onExportPressed: (context) async {
|
||||
print('## EXPORT ##');
|
||||
String data = 'test_data';
|
||||
const filename = 'export.csv';
|
||||
|
||||
if (kIsWeb) {
|
||||
final encodedFileContents = Uri.encodeComponent(data);
|
||||
AnchorElement(
|
||||
href: 'data:text/plain;charset=utf-8,$encodedFileContents')
|
||||
..setAttribute('download', filename)
|
||||
..click();
|
||||
} else {
|
||||
final directory = await getApplicationDocumentsDirectory();
|
||||
final filePath = '$directory/$filename';
|
||||
final csvFile = file.File(filePath);
|
||||
csvFile.writeAsString(data);
|
||||
|
||||
await FlutterShare.shareFile(
|
||||
title: 'Example share',
|
||||
text: 'Example share text',
|
||||
filePath: filePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ abstract class LocaleCodeAware {
|
|||
mixin LocalizationsProvider on LocaleCodeAware {
|
||||
static final Map<String, Map<String, String>> _localizedValues = {
|
||||
'en': {
|
||||
'export': 'Export',
|
||||
'chart': 'Chart',
|
||||
'count': 'Count',
|
||||
'totals': 'Totals',
|
||||
|
|
@ -16097,6 +16098,9 @@ mixin LocalizationsProvider on LocaleCodeAware {
|
|||
|
||||
String get chart => _localizedValues[localeCode]['chart'];
|
||||
|
||||
String get export => _localizedValues[localeCode]['export'];
|
||||
|
||||
|
||||
String lookup(String key) {
|
||||
final lookupKey = toSnakeCase(key);
|
||||
return _localizedValues[localeCode][lookupKey] ??
|
||||
|
|
|
|||
51
pubspec.lock
51
pubspec.lock
|
|
@ -56,7 +56,7 @@ packages:
|
|||
name: build_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.1+1"
|
||||
version: "0.4.2"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -77,14 +77,14 @@ packages:
|
|||
name: build_runner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.7.3"
|
||||
version: "1.7.4"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.3.0"
|
||||
version: "4.4.0"
|
||||
built_collection:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -168,7 +168,7 @@ packages:
|
|||
name: coverage
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.13.3+3"
|
||||
version: "0.13.6"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -196,7 +196,7 @@ packages:
|
|||
name: extended_image
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.8"
|
||||
version: "0.6.9"
|
||||
extended_image_library:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -217,7 +217,7 @@ packages:
|
|||
name: faker
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.2.1"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -231,7 +231,7 @@ packages:
|
|||
name: firebase
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "7.2.0"
|
||||
version: "7.2.1"
|
||||
firebase_auth:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -245,7 +245,7 @@ packages:
|
|||
name: firebase_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.3+2"
|
||||
version: "0.4.4"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -316,7 +316,7 @@ packages:
|
|||
name: flutter_plugin_android_lifecycle
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
version: "1.0.5"
|
||||
flutter_redux:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -324,6 +324,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.5.4"
|
||||
flutter_share:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_share
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2+1"
|
||||
flutter_slidable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -342,7 +349,7 @@ packages:
|
|||
name: flutter_typeahead
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.7.0"
|
||||
version: "1.8.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
|
@ -394,7 +401,7 @@ packages:
|
|||
name: google_sign_in_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.8.3"
|
||||
version: "0.8.3+1"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -429,7 +436,7 @@ packages:
|
|||
name: http_multi_server
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.2.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -618,7 +625,7 @@ packages:
|
|||
name: path_provider
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
version: "1.6.0"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -681,7 +688,7 @@ packages:
|
|||
name: quill_delta
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.0.1"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -730,14 +737,14 @@ packages:
|
|||
name: shared_preferences
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.5.6"
|
||||
version: "0.5.6+1"
|
||||
shared_preferences_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.1+3"
|
||||
version: "0.0.1+5"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -751,7 +758,7 @@ packages:
|
|||
name: shared_preferences_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.2+2"
|
||||
version: "0.1.2+3"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -805,7 +812,7 @@ packages:
|
|||
name: source_maps
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.10.8"
|
||||
version: "0.10.9"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -861,7 +868,7 @@ packages:
|
|||
name: synchronized
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.2.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -896,7 +903,7 @@ packages:
|
|||
name: timeago
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.24"
|
||||
version: "2.0.26"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -938,7 +945,7 @@ packages:
|
|||
name: url_launcher_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.0+2"
|
||||
version: "0.1.1"
|
||||
usage:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -1001,7 +1008,7 @@ packages:
|
|||
name: webview_flutter
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.19+5"
|
||||
version: "0.3.19+7"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ dependencies:
|
|||
sentry: ^2.2.0
|
||||
image_picker: ^0.6.1+4
|
||||
zefyr: ^0.8.0
|
||||
flutter_colorpicker: any
|
||||
flutter_colorpicker: ^0.2.0
|
||||
flutter_json_widget: ^1.0.2
|
||||
webview_flutter: ^0.3.15+1
|
||||
timeago: ^2.0.22
|
||||
|
|
@ -44,6 +44,7 @@ dependencies:
|
|||
native_pdf_view: any
|
||||
flutter_typeahead: ^1.7.0
|
||||
#quick_actions: ^0.2.1
|
||||
flutter_share: ^1.0.2+1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_driver:
|
||||
|
|
|
|||
Loading…
Reference in New Issue