Show overlapping times

This commit is contained in:
Hillel Coren 2022-03-01 16:45:14 +02:00
parent 80f942fd1e
commit d98fa17c91
6 changed files with 62 additions and 12 deletions

View File

@ -352,6 +352,35 @@ abstract class TaskEntity extends Object
return isValid && countRunning <= 1; return isValid && countRunning <= 1;
} }
List get getInvalidTimeIndices {
final times = getTaskTimes();
DateTime lastDateTime = DateTime(2000);
final indices = <int>[];
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 { bool get isRunning {
final taskTimes = getTaskTimes(); final taskTimes = getTaskTimes();

View File

@ -123,10 +123,12 @@ class _TaskEditDesktopState extends State<TaskEditDesktop> {
final client = state.clientState.get(task.clientId); final client = state.clientState.get(task.clientId);
final showEndDate = company.showTaskEndDate; final showEndDate = company.showTaskEndDate;
final taskTimes = task.getTaskTimes(sort: false); final taskTimes = task.getTaskTimes(sort: false);
if (!taskTimes.any((taskTime) => taskTime.isEmpty)) { if (!taskTimes.any((taskTime) => taskTime.isEmpty)) {
taskTimes.add(TaskTime().rebuild((b) => b..startDate = null)); taskTimes.add(TaskTime().rebuild((b) => b..startDate = null));
} }
final overlapping = task.getInvalidTimeIndices;
final rateLabel = localization.rate + final rateLabel = localization.rate +
'' + '' +
formatNumber( formatNumber(
@ -413,8 +415,13 @@ class _TaskEditDesktopState extends State<TaskEditDesktop> {
Padding( Padding(
padding: const EdgeInsets.only(top: 4), padding: const EdgeInsets.only(top: 4),
child: IconButton( child: IconButton(
icon: Icon(Icons.clear), icon: Icon(
tooltip: localization.remove, Icons.clear,
color: overlapping.contains(index) ? Colors.red : null,
),
tooltip: overlapping.contains(index)
? localization.invalidTime
: localization.remove,
onPressed: taskTimes[index].isEmpty onPressed: taskTimes[index].isEmpty
? null ? null
: () { : () {

View File

@ -52,6 +52,7 @@ class _TaskEditTimesState extends State<TaskEditTimes> {
final viewModel = widget.viewModel; final viewModel = widget.viewModel;
final task = viewModel.task; final task = viewModel.task;
final taskTimes = task.getTaskTimes(); final taskTimes = task.getTaskTimes();
final invalidTimes = task.getInvalidTimeIndices;
final taskTime = viewModel.taskTimeIndex != null && final taskTime = viewModel.taskTimeIndex != null &&
taskTimes.length > viewModel.taskTimeIndex taskTimes.length > viewModel.taskTimeIndex
? taskTimes[viewModel.taskTimeIndex] ? taskTimes[viewModel.taskTimeIndex]
@ -68,15 +69,18 @@ class _TaskEditTimesState extends State<TaskEditTimes> {
return HelpText(localization.clickPlusToAddTime); return HelpText(localization.clickPlusToAddTime);
} }
final taskTimeWidgets = task final sortedTaskTimes = task.getTaskTimes().toList().reversed.toList();
.getTaskTimes() final taskTimeWidgets = <Widget>[];
.toList()
.reversed for (var i = 0; i < sortedTaskTimes.length; i++) {
.map<Widget>((taskTime) => TaskTimeListTile( final taskTime = sortedTaskTimes[i];
task: task, taskTimeWidgets.add(TaskTimeListTile(
taskTime: taskTime, task: task,
onTap: (context) => _showTaskTimeEditor(taskTime, context), taskTime: taskTime,
)); onTap: (context) => _showTaskTimeEditor(taskTime, context),
isValid: !invalidTimes.contains(sortedTaskTimes.length - i - 1),
));
}
return ScrollableListView( return ScrollableListView(
children: taskTimeWidgets.toList(), children: taskTimeWidgets.toList(),

View File

@ -18,11 +18,13 @@ class TaskTimeListTile extends StatelessWidget {
@required this.task, @required this.task,
@required this.taskTime, @required this.taskTime,
@required this.onTap, @required this.onTap,
@required this.isValid,
}); });
final Function(BuildContext context) onTap; final Function(BuildContext context) onTap;
final TaskEntity task; final TaskEntity task;
final TaskTime taskTime; final TaskTime taskTime;
final bool isValid;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -51,7 +53,9 @@ class TaskTimeListTile extends StatelessWidget {
], ],
), ),
subtitle: Text(subtitle), subtitle: Text(subtitle),
trailing: onTap != null ? Icon(Icons.navigate_next) : null, trailing: onTap != null
? Icon(isValid ? Icons.navigate_next : Icons.error)
: null,
), ),
Divider( Divider(
height: 1.0, height: 1.0,

View File

@ -200,6 +200,7 @@ class _TaskOverviewState extends State<TaskOverview> {
TaskTimeListTile( TaskTimeListTile(
task: task, task: task,
taskTime: taskTime, taskTime: taskTime,
isValid: true,
onTap: (BuildContext context) => onTap: (BuildContext context) =>
viewModel.state.userCompany.canEditEntity(task) viewModel.state.userCompany.canEditEntity(task)
? viewModel.onEditPressed(context, taskTime) ? viewModel.onEditPressed(context, taskTime)

View File

@ -16,6 +16,7 @@ mixin LocalizationsProvider on LocaleCodeAware {
static final Map<String, Map<String, String>> _localizedValues = { static final Map<String, Map<String, String>> _localizedValues = {
'en': { 'en': {
// STARTER: lang key - do not remove comment // STARTER: lang key - do not remove comment
'invalid_time': 'Invalid Time',
'client_shipping_state': 'Client Shipping State', 'client_shipping_state': 'Client Shipping State',
'client_shipping_city': 'Client Shipping City', 'client_shipping_city': 'Client Shipping City',
'client_shipping_postal_code': 'Client Shipping Postal Code', 'client_shipping_postal_code': 'Client Shipping Postal Code',
@ -74125,6 +74126,10 @@ mixin LocalizationsProvider on LocaleCodeAware {
_localizedValues[localeCode]['client_shipping_country'] ?? _localizedValues[localeCode]['client_shipping_country'] ??
_localizedValues['en']['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 // STARTER: lang field - do not remove comment
String lookup(String key) { String lookup(String key) {