Task item details

This commit is contained in:
Hillel Coren 2023-03-22 17:17:13 +02:00
parent cd826cd709
commit b0886f5ccd
3 changed files with 94 additions and 166 deletions

View File

@ -85,13 +85,20 @@ class TaskFields {
}
abstract class TaskTime implements Built<TaskTime, TaskTimeBuilder> {
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<TaskTime, TaskTimeBuilder> {
@nullable
DateTime get endDate;
String get description;
bool get isBillable;
Duration get duration => (endDate ?? DateTime.now()).difference(startDate);
List<dynamic> get asList {

View File

@ -109,7 +109,14 @@ class _$TaskTimeSerializer implements StructuredSerializer<TaskTime> {
@override
Iterable<Object> serialize(Serializers serializers, TaskTime object,
{FullType specifiedType = FullType.unspecified}) {
final result = <Object>[];
final result = <Object>[
'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<TaskTime> {
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<TaskTime, TaskTimeBuilder> {
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<TaskTime, TaskTimeBuilder> {
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, TaskTimeBuilder> {
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;
}

View File

@ -125,6 +125,7 @@ class _TaskEditDesktopState extends State<TaskEditDesktop> {
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,6 +304,7 @@ class _TaskEditDesktopState extends State<TaskEditDesktop> {
key: ValueKey('__table_${_updatedAt}__'),
padding: const EdgeInsets.symmetric(horizontal: kMobileDialogPadding),
children: [
if (!settings.showTaskItemDescription)
Row(
children: [
Expanded(child: Text(localization.startDate)),
@ -331,6 +333,9 @@ class _TaskEditDesktopState extends State<TaskEditDesktop> {
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<TaskEditDesktop> {
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<TaskEditDesktop> {
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<TaskEditDesktop> {
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<TaskEditDesktop> {
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<TaskEditDesktop> {
),
],
),
if (company.settings.showTaskItemDescription)
if (settings.showTaskItemDescription)
Padding(
padding:
const EdgeInsets.only(bottom: 16, right: 16),
@ -484,154 +501,6 @@ class _TaskEditDesktopState extends State<TaskEditDesktop> {
}).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,
),