From d98fa17c91d460d7f42c936b201b2183aa9a4af7 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Tue, 1 Mar 2022 16:45:14 +0200 Subject: [PATCH] Show overlapping times --- lib/data/models/task_model.dart | 29 ++++++++++++++++++++++++ lib/ui/task/edit/task_edit_desktop.dart | 11 +++++++-- lib/ui/task/edit/task_edit_times.dart | 22 ++++++++++-------- lib/ui/task/task_time_view.dart | 6 ++++- lib/ui/task/view/task_view_overview.dart | 1 + lib/utils/i18n.dart | 5 ++++ 6 files changed, 62 insertions(+), 12 deletions(-) diff --git a/lib/data/models/task_model.dart b/lib/data/models/task_model.dart index 0fc699531..9248757ae 100644 --- a/lib/data/models/task_model.dart +++ b/lib/data/models/task_model.dart @@ -352,6 +352,35 @@ abstract class TaskEntity extends Object return isValid && countRunning <= 1; } + List get getInvalidTimeIndices { + final times = getTaskTimes(); + DateTime lastDateTime = DateTime(2000); + + final indices = []; + int counter = 0; + + times.forEach((time) { + final startDate = time.startDate; + final endDate = time.endDate; + + if (time.isRunning) { + // + } else { + if (startDate.isBefore(lastDateTime) || startDate.isAfter(endDate)) { + indices.add(counter); + } + if (endDate.isBefore(startDate) || endDate.isBefore(lastDateTime)) { + indices.add(counter); + } + lastDateTime = lastDateTime.isAfter(endDate) ? lastDateTime : endDate; + } + + counter++; + }); + + return indices; + } + bool get isRunning { final taskTimes = getTaskTimes(); diff --git a/lib/ui/task/edit/task_edit_desktop.dart b/lib/ui/task/edit/task_edit_desktop.dart index 657ffd610..f546aaea9 100644 --- a/lib/ui/task/edit/task_edit_desktop.dart +++ b/lib/ui/task/edit/task_edit_desktop.dart @@ -123,10 +123,12 @@ class _TaskEditDesktopState extends State { final client = state.clientState.get(task.clientId); final showEndDate = company.showTaskEndDate; final taskTimes = task.getTaskTimes(sort: false); + if (!taskTimes.any((taskTime) => taskTime.isEmpty)) { taskTimes.add(TaskTime().rebuild((b) => b..startDate = null)); } + final overlapping = task.getInvalidTimeIndices; final rateLabel = localization.rate + ' • ' + formatNumber( @@ -413,8 +415,13 @@ class _TaskEditDesktopState extends State { Padding( padding: const EdgeInsets.only(top: 4), child: IconButton( - icon: Icon(Icons.clear), - tooltip: localization.remove, + icon: Icon( + Icons.clear, + color: overlapping.contains(index) ? Colors.red : null, + ), + tooltip: overlapping.contains(index) + ? localization.invalidTime + : localization.remove, onPressed: taskTimes[index].isEmpty ? null : () { diff --git a/lib/ui/task/edit/task_edit_times.dart b/lib/ui/task/edit/task_edit_times.dart index bff232d68..281f7c95a 100644 --- a/lib/ui/task/edit/task_edit_times.dart +++ b/lib/ui/task/edit/task_edit_times.dart @@ -52,6 +52,7 @@ class _TaskEditTimesState extends State { final viewModel = widget.viewModel; final task = viewModel.task; final taskTimes = task.getTaskTimes(); + final invalidTimes = task.getInvalidTimeIndices; final taskTime = viewModel.taskTimeIndex != null && taskTimes.length > viewModel.taskTimeIndex ? taskTimes[viewModel.taskTimeIndex] @@ -68,15 +69,18 @@ class _TaskEditTimesState extends State { return HelpText(localization.clickPlusToAddTime); } - final taskTimeWidgets = task - .getTaskTimes() - .toList() - .reversed - .map((taskTime) => TaskTimeListTile( - task: task, - taskTime: taskTime, - onTap: (context) => _showTaskTimeEditor(taskTime, context), - )); + final sortedTaskTimes = task.getTaskTimes().toList().reversed.toList(); + final taskTimeWidgets = []; + + for (var i = 0; i < sortedTaskTimes.length; i++) { + final taskTime = sortedTaskTimes[i]; + taskTimeWidgets.add(TaskTimeListTile( + task: task, + taskTime: taskTime, + onTap: (context) => _showTaskTimeEditor(taskTime, context), + isValid: !invalidTimes.contains(sortedTaskTimes.length - i - 1), + )); + } return ScrollableListView( children: taskTimeWidgets.toList(), diff --git a/lib/ui/task/task_time_view.dart b/lib/ui/task/task_time_view.dart index 24c4b0a57..84668e2e2 100644 --- a/lib/ui/task/task_time_view.dart +++ b/lib/ui/task/task_time_view.dart @@ -18,11 +18,13 @@ class TaskTimeListTile extends StatelessWidget { @required this.task, @required this.taskTime, @required this.onTap, + @required this.isValid, }); final Function(BuildContext context) onTap; final TaskEntity task; final TaskTime taskTime; + final bool isValid; @override Widget build(BuildContext context) { @@ -51,7 +53,9 @@ class TaskTimeListTile extends StatelessWidget { ], ), subtitle: Text(subtitle), - trailing: onTap != null ? Icon(Icons.navigate_next) : null, + trailing: onTap != null + ? Icon(isValid ? Icons.navigate_next : Icons.error) + : null, ), Divider( height: 1.0, diff --git a/lib/ui/task/view/task_view_overview.dart b/lib/ui/task/view/task_view_overview.dart index a974d2300..9ab75787c 100644 --- a/lib/ui/task/view/task_view_overview.dart +++ b/lib/ui/task/view/task_view_overview.dart @@ -200,6 +200,7 @@ class _TaskOverviewState extends State { TaskTimeListTile( task: task, taskTime: taskTime, + isValid: true, onTap: (BuildContext context) => viewModel.state.userCompany.canEditEntity(task) ? viewModel.onEditPressed(context, taskTime) diff --git a/lib/utils/i18n.dart b/lib/utils/i18n.dart index ea91a8fa3..b83557383 100644 --- a/lib/utils/i18n.dart +++ b/lib/utils/i18n.dart @@ -16,6 +16,7 @@ mixin LocalizationsProvider on LocaleCodeAware { static final Map> _localizedValues = { 'en': { // STARTER: lang key - do not remove comment + 'invalid_time': 'Invalid Time', 'client_shipping_state': 'Client Shipping State', 'client_shipping_city': 'Client Shipping City', 'client_shipping_postal_code': 'Client Shipping Postal Code', @@ -74125,6 +74126,10 @@ mixin LocalizationsProvider on LocaleCodeAware { _localizedValues[localeCode]['client_shipping_country'] ?? _localizedValues['en']['client_shipping_country']; + String get invalidTime => + _localizedValues[localeCode]['invalid_time'] ?? + _localizedValues['en']['invalid_time']; + // STARTER: lang field - do not remove comment String lookup(String key) {