diff --git a/lib/data/models/task_model.dart b/lib/data/models/task_model.dart index 5cd879f5d..30ae107b9 100644 --- a/lib/data/models/task_model.dart +++ b/lib/data/models/task_model.dart @@ -85,13 +85,20 @@ class TaskFields { } abstract class TaskTime implements Built { - factory TaskTime({DateTime startDate, DateTime endDate}) { + factory TaskTime({ + DateTime startDate, + DateTime endDate, + String description, + bool isBillable, + }) { return _$TaskTime._( startDate: startDate ?? DateTime.fromMillisecondsSinceEpoch( (DateTime.now().millisecondsSinceEpoch / 1000).floor() * 1000, isUtc: true), endDate: endDate, + description: description ?? '', + isBillable: isBillable ?? true, ); } @@ -107,6 +114,10 @@ abstract class TaskTime implements Built { @nullable DateTime get endDate; + String get description; + + bool get isBillable; + Duration get duration => (endDate ?? DateTime.now()).difference(startDate); List get asList { diff --git a/lib/data/models/task_model.g.dart b/lib/data/models/task_model.g.dart index f80449d30..a31d41757 100644 --- a/lib/data/models/task_model.g.dart +++ b/lib/data/models/task_model.g.dart @@ -109,7 +109,14 @@ class _$TaskTimeSerializer implements StructuredSerializer { @override Iterable serialize(Serializers serializers, TaskTime object, {FullType specifiedType = FullType.unspecified}) { - final result = []; + final result = [ + 'description', + serializers.serialize(object.description, + specifiedType: const FullType(String)), + 'isBillable', + serializers.serialize(object.isBillable, + specifiedType: const FullType(bool)), + ]; Object value; value = object.startDate; if (value != null) { @@ -147,6 +154,14 @@ class _$TaskTimeSerializer implements StructuredSerializer { result.endDate = serializers.deserialize(value, specifiedType: const FullType(DateTime)) as DateTime; break; + case 'description': + result.description = serializers.deserialize(value, + specifiedType: const FullType(String)) as String; + break; + case 'isBillable': + result.isBillable = serializers.deserialize(value, + specifiedType: const FullType(bool)) as bool; + break; } } @@ -559,11 +574,22 @@ class _$TaskTime extends TaskTime { final DateTime startDate; @override final DateTime endDate; + @override + final String description; + @override + final bool isBillable; factory _$TaskTime([void Function(TaskTimeBuilder) updates]) => (new TaskTimeBuilder()..update(updates))._build(); - _$TaskTime._({this.startDate, this.endDate}) : super._(); + _$TaskTime._( + {this.startDate, this.endDate, this.description, this.isBillable}) + : super._() { + BuiltValueNullFieldError.checkNotNull( + description, r'TaskTime', 'description'); + BuiltValueNullFieldError.checkNotNull( + isBillable, r'TaskTime', 'isBillable'); + } @override TaskTime rebuild(void Function(TaskTimeBuilder) updates) => @@ -577,7 +603,9 @@ class _$TaskTime extends TaskTime { if (identical(other, this)) return true; return other is TaskTime && startDate == other.startDate && - endDate == other.endDate; + endDate == other.endDate && + description == other.description && + isBillable == other.isBillable; } int __hashCode; @@ -587,6 +615,8 @@ class _$TaskTime extends TaskTime { var _$hash = 0; _$hash = $jc(_$hash, startDate.hashCode); _$hash = $jc(_$hash, endDate.hashCode); + _$hash = $jc(_$hash, description.hashCode); + _$hash = $jc(_$hash, isBillable.hashCode); _$hash = $jf(_$hash); return __hashCode ??= _$hash; } @@ -595,7 +625,9 @@ class _$TaskTime extends TaskTime { String toString() { return (newBuiltValueToStringHelper(r'TaskTime') ..add('startDate', startDate) - ..add('endDate', endDate)) + ..add('endDate', endDate) + ..add('description', description) + ..add('isBillable', isBillable)) .toString(); } } @@ -611,6 +643,14 @@ class TaskTimeBuilder implements Builder { DateTime get endDate => _$this._endDate; set endDate(DateTime endDate) => _$this._endDate = endDate; + String _description; + String get description => _$this._description; + set description(String description) => _$this._description = description; + + bool _isBillable; + bool get isBillable => _$this._isBillable; + set isBillable(bool isBillable) => _$this._isBillable = isBillable; + TaskTimeBuilder(); TaskTimeBuilder get _$this { @@ -618,6 +658,8 @@ class TaskTimeBuilder implements Builder { if ($v != null) { _startDate = $v.startDate; _endDate = $v.endDate; + _description = $v.description; + _isBillable = $v.isBillable; _$v = null; } return this; @@ -638,8 +680,14 @@ class TaskTimeBuilder implements Builder { TaskTime build() => _build(); _$TaskTime _build() { - final _$result = - _$v ?? new _$TaskTime._(startDate: startDate, endDate: endDate); + final _$result = _$v ?? + new _$TaskTime._( + startDate: startDate, + endDate: endDate, + description: BuiltValueNullFieldError.checkNotNull( + description, r'TaskTime', 'description'), + isBillable: BuiltValueNullFieldError.checkNotNull( + isBillable, r'TaskTime', 'isBillable')); replace(_$result); return _$result; } diff --git a/lib/ui/task/edit/task_edit_desktop.dart b/lib/ui/task/edit/task_edit_desktop.dart index 09b319d5b..2468c685c 100644 --- a/lib/ui/task/edit/task_edit_desktop.dart +++ b/lib/ui/task/edit/task_edit_desktop.dart @@ -125,6 +125,7 @@ class _TaskEditDesktopState extends State { final state = viewModel.state; final company = state.company; + final settings = company.settings; final client = state.clientState.get(task.clientId); final showEndDate = company.showTaskEndDate; final taskTimes = task.getTaskTimes(sort: false); @@ -303,16 +304,17 @@ class _TaskEditDesktopState extends State { key: ValueKey('__table_${_updatedAt}__'), padding: const EdgeInsets.symmetric(horizontal: kMobileDialogPadding), children: [ - Row( - children: [ - Expanded(child: Text(localization.startDate)), - Expanded(child: Text(localization.startTime)), - if (showEndDate) Expanded(child: Text(localization.endDate)), - Expanded(child: Text(localization.endTime)), - Expanded(child: Text(localization.duration)), - SizedBox(width: 40), - ], - ), + if (!settings.showTaskItemDescription) + Row( + children: [ + Expanded(child: Text(localization.startDate)), + Expanded(child: Text(localization.startTime)), + if (showEndDate) Expanded(child: Text(localization.endDate)), + Expanded(child: Text(localization.endTime)), + Expanded(child: Text(localization.duration)), + SizedBox(width: 40), + ], + ), ...taskTimes.map((taskTime) { final index = taskTimes.indexOf(taskTime); return Row( @@ -331,6 +333,9 @@ class _TaskEditDesktopState extends State { child: DatePicker( key: ValueKey( '__${_startTimeUpdatedAt}_${_durationUpdateAt}_${index}__'), + labelText: settings.showTaskItemDescription + ? localization.startDate + : null, selectedDate: taskTimes[index].startDate == null ? null @@ -357,6 +362,9 @@ class _TaskEditDesktopState extends State { child: TimePicker( key: ValueKey( '__${_durationUpdateAt}_${index}__'), + labelText: settings.showTaskItemDescription + ? localization.startTime + : null, selectedDateTime: taskTimes[index].startDate, onSelected: (timeOfDay) { final taskTime = taskTimes[index] @@ -379,6 +387,9 @@ class _TaskEditDesktopState extends State { child: DatePicker( key: ValueKey( '__${_startDateUpdatedAt}_${_durationUpdateAt}_${_endTimeUpdatedAt}_${index}__'), + labelText: settings.showTaskItemDescription + ? localization.endDate + : null, selectedDate: taskTimes[index].endDate == null ? null @@ -404,6 +415,9 @@ class _TaskEditDesktopState extends State { child: TimePicker( key: ValueKey( '__${_endDateUpdatedAt}_${_durationUpdateAt}_${index}__'), + labelText: settings.showTaskItemDescription + ? localization.endTime + : null, selectedDateTime: taskTimes[index].endDate, isEndTime: true, onSelected: (timeOfDay) { @@ -426,6 +440,9 @@ class _TaskEditDesktopState extends State { child: DurationPicker( key: ValueKey( '__${_startTimeUpdatedAt}_${_endTimeUpdatedAt}_${_startDateUpdatedAt}_${_endDateUpdatedAt}_${index}__'), + labelText: settings.showTaskItemDescription + ? localization.duration + : null, onSelected: (Duration duration) { final taskTime = taskTimes[index] .copyWithDuration(duration); @@ -446,7 +463,7 @@ class _TaskEditDesktopState extends State { ), ], ), - if (company.settings.showTaskItemDescription) + if (settings.showTaskItemDescription) Padding( padding: const EdgeInsets.only(bottom: 16, right: 16), @@ -484,154 +501,6 @@ class _TaskEditDesktopState extends State { }).toList(), ], ), - /* - FormCard( - padding: const EdgeInsets.symmetric(horizontal: kMobileDialogPadding), - child: Table( - key: ValueKey('__table_old_${_updatedAt}__'), - columnWidths: { - showEndDate ? 5 : 4: FixedColumnWidth(kMinInteractiveDimension), - }, - children: [ - TableRow( - children: [ - TableHeader(localization.startDate, isFirst: true), - TableHeader(localization.startTime), - if (showEndDate) TableHeader(localization.endDate), - TableHeader(localization.endTime), - TableHeader(localization.duration), - TableHeader(''), - ], - decoration: tableHeaderColor.isNotEmpty - ? BoxDecoration( - color: convertHexStringToColor(tableHeaderColor), - ) - : null, - ), - for (var index = 0; index < taskTimes.length; index++) ...[ - TableRow(children: [ - Padding( - padding: const EdgeInsets.only(right: kTableColumnGap), - child: DatePicker( - key: ValueKey( - '__${_startTimeUpdatedAt}_${_durationUpdateAt}_${index}__'), - selectedDate: taskTimes[index].startDate == null - ? null - : convertDateTimeToSqlDate( - taskTimes[index].startDate.toLocal()), - onSelected: (date, _) { - final taskTime = taskTimes[index] - .copyWithStartDate(date, syncDates: !showEndDate); - viewModel.onUpdatedTaskTime(taskTime, index); - setState(() { - _startDateUpdatedAt = - DateTime.now().millisecondsSinceEpoch; - }); - }, - ), - ), - Padding( - padding: const EdgeInsets.only(right: kTableColumnGap), - child: TimePicker( - key: ValueKey('__${_durationUpdateAt}_${index}__'), - selectedDateTime: taskTimes[index].startDate, - onSelected: (timeOfDay) { - final taskTime = - taskTimes[index].copyWithStartTime(timeOfDay); - viewModel.onUpdatedTaskTime(taskTime, index); - setState(() { - _startTimeUpdatedAt = - DateTime.now().millisecondsSinceEpoch; - }); - }, - ), - ), - if (showEndDate) - Padding( - padding: const EdgeInsets.only(right: kTableColumnGap), - child: DatePicker( - key: ValueKey( - '__${_startDateUpdatedAt}_${_durationUpdateAt}_${_endTimeUpdatedAt}_${index}__'), - selectedDate: taskTimes[index].endDate == null - ? null - : convertDateTimeToSqlDate( - taskTimes[index].endDate.toLocal()), - onSelected: (date, _) { - final taskTime = - taskTimes[index].copyWithEndDate(date); - viewModel.onUpdatedTaskTime(taskTime, index); - setState(() { - _endDateUpdatedAt = - DateTime.now().millisecondsSinceEpoch; - }); - }, - ), - ), - Padding( - padding: const EdgeInsets.only(right: kTableColumnGap), - child: TimePicker( - key: ValueKey( - '__${_endDateUpdatedAt}_${_durationUpdateAt}_${index}__'), - selectedDateTime: taskTimes[index].endDate, - isEndTime: true, - onSelected: (timeOfDay) { - final taskTime = - taskTimes[index].copyWithEndTime(timeOfDay); - viewModel.onUpdatedTaskTime(taskTime, index); - setState(() { - _endTimeUpdatedAt = - DateTime.now().millisecondsSinceEpoch; - }); - }, - ), - ), - Padding( - padding: const EdgeInsets.only(right: kTableColumnGap), - child: DurationPicker( - key: ValueKey( - '__${_startTimeUpdatedAt}_${_endTimeUpdatedAt}_${_startDateUpdatedAt}_${_endDateUpdatedAt}_${index}__'), - onSelected: (Duration duration) { - final taskTime = - taskTimes[index].copyWithDuration(duration); - viewModel.onUpdatedTaskTime(taskTime, index); - setState(() { - _durationUpdateAt = - DateTime.now().millisecondsSinceEpoch; - }); - }, - selectedDuration: (taskTimes[index].startDate == null || - taskTimes[index].endDate == null) - ? null - : taskTimes[index].duration, - ), - ), - Padding( - padding: const EdgeInsets.only(top: 4), - child: IconButton( - icon: Icon( - Icons.clear, - color: overlapping.contains(index) ? Colors.red : null, - ), - tooltip: overlapping.contains(index) - ? localization.invalidTime - : localization.remove, - onPressed: taskTimes[index].isEmpty - ? null - : () { - viewModel.onRemoveTaskTime(index); - setState(() { - _updatedAt = - DateTime.now().millisecondsSinceEpoch; - }); - }, - ), - ), - ]), - ], - ], - ), - ), - */ SizedBox( height: kMobileDialogPadding, ),