Task statuses

This commit is contained in:
Hillel Coren 2020-10-28 23:49:45 +02:00
parent 4f73e10874
commit 6fecfca836
7 changed files with 128 additions and 68 deletions

View File

@ -172,8 +172,8 @@ abstract class TaskEntity extends Object
createdAt: 0, createdAt: 0,
createdUserId: '', createdUserId: '',
vendorId: '', vendorId: '',
taskStatusId: '', statusId: '',
taskStatusSortOrder: 0, statusSortOrder: 0,
documents: BuiltList<DocumentEntity>(), documents: BuiltList<DocumentEntity>(),
); );
} }
@ -389,13 +389,11 @@ abstract class TaskEntity extends Object
@BuiltValueField(wireName: 'custom_value4') @BuiltValueField(wireName: 'custom_value4')
String get customValue4; String get customValue4;
@nullable @BuiltValueField(wireName: 'status_id')
@BuiltValueField(wireName: 'task_status_id') String get statusId;
String get taskStatusId;
@nullable @BuiltValueField(wireName: 'status_sort_order')
@BuiltValueField(wireName: 'task_status_sort_order') int get statusSortOrder;
int get taskStatusSortOrder;
@nullable @nullable
@BuiltValueField(wireName: 'vendor_id') @BuiltValueField(wireName: 'vendor_id')

View File

@ -180,6 +180,12 @@ class _$TaskEntitySerializer implements StructuredSerializer<TaskEntity> {
'custom_value2', 'custom_value2',
serializers.serialize(object.customValue2, serializers.serialize(object.customValue2,
specifiedType: const FullType(String)), specifiedType: const FullType(String)),
'status_id',
serializers.serialize(object.statusId,
specifiedType: const FullType(String)),
'status_sort_order',
serializers.serialize(object.statusSortOrder,
specifiedType: const FullType(int)),
'documents', 'documents',
serializers.serialize(object.documents, serializers.serialize(object.documents,
specifiedType: const FullType( specifiedType: const FullType(
@ -226,18 +232,6 @@ class _$TaskEntitySerializer implements StructuredSerializer<TaskEntity> {
..add(serializers.serialize(object.customValue4, ..add(serializers.serialize(object.customValue4,
specifiedType: const FullType(String))); specifiedType: const FullType(String)));
} }
if (object.taskStatusId != null) {
result
..add('task_status_id')
..add(serializers.serialize(object.taskStatusId,
specifiedType: const FullType(String)));
}
if (object.taskStatusSortOrder != null) {
result
..add('task_status_sort_order')
..add(serializers.serialize(object.taskStatusSortOrder,
specifiedType: const FullType(int)));
}
if (object.vendorId != null) { if (object.vendorId != null) {
result result
..add('vendor_id') ..add('vendor_id')
@ -330,12 +324,12 @@ class _$TaskEntitySerializer implements StructuredSerializer<TaskEntity> {
result.customValue4 = serializers.deserialize(value, result.customValue4 = serializers.deserialize(value,
specifiedType: const FullType(String)) as String; specifiedType: const FullType(String)) as String;
break; break;
case 'task_status_id': case 'status_id':
result.taskStatusId = serializers.deserialize(value, result.statusId = serializers.deserialize(value,
specifiedType: const FullType(String)) as String; specifiedType: const FullType(String)) as String;
break; break;
case 'task_status_sort_order': case 'status_sort_order':
result.taskStatusSortOrder = serializers.deserialize(value, result.statusSortOrder = serializers.deserialize(value,
specifiedType: const FullType(int)) as int; specifiedType: const FullType(int)) as int;
break; break;
case 'vendor_id': case 'vendor_id':
@ -690,9 +684,9 @@ class _$TaskEntity extends TaskEntity {
@override @override
final String customValue4; final String customValue4;
@override @override
final String taskStatusId; final String statusId;
@override @override
final int taskStatusSortOrder; final int statusSortOrder;
@override @override
final String vendorId; final String vendorId;
@override @override
@ -730,8 +724,8 @@ class _$TaskEntity extends TaskEntity {
this.customValue2, this.customValue2,
this.customValue3, this.customValue3,
this.customValue4, this.customValue4,
this.taskStatusId, this.statusId,
this.taskStatusSortOrder, this.statusSortOrder,
this.vendorId, this.vendorId,
this.documents, this.documents,
this.isChanged, this.isChanged,
@ -764,6 +758,12 @@ class _$TaskEntity extends TaskEntity {
if (customValue2 == null) { if (customValue2 == null) {
throw new BuiltValueNullFieldError('TaskEntity', 'customValue2'); throw new BuiltValueNullFieldError('TaskEntity', 'customValue2');
} }
if (statusId == null) {
throw new BuiltValueNullFieldError('TaskEntity', 'statusId');
}
if (statusSortOrder == null) {
throw new BuiltValueNullFieldError('TaskEntity', 'statusSortOrder');
}
if (documents == null) { if (documents == null) {
throw new BuiltValueNullFieldError('TaskEntity', 'documents'); throw new BuiltValueNullFieldError('TaskEntity', 'documents');
} }
@ -804,8 +804,8 @@ class _$TaskEntity extends TaskEntity {
customValue2 == other.customValue2 && customValue2 == other.customValue2 &&
customValue3 == other.customValue3 && customValue3 == other.customValue3 &&
customValue4 == other.customValue4 && customValue4 == other.customValue4 &&
taskStatusId == other.taskStatusId && statusId == other.statusId &&
taskStatusSortOrder == other.taskStatusSortOrder && statusSortOrder == other.statusSortOrder &&
vendorId == other.vendorId && vendorId == other.vendorId &&
documents == other.documents && documents == other.documents &&
isChanged == other.isChanged && isChanged == other.isChanged &&
@ -847,8 +847,8 @@ class _$TaskEntity extends TaskEntity {
customValue2.hashCode), customValue2.hashCode),
customValue3.hashCode), customValue3.hashCode),
customValue4.hashCode), customValue4.hashCode),
taskStatusId.hashCode), statusId.hashCode),
taskStatusSortOrder.hashCode), statusSortOrder.hashCode),
vendorId.hashCode), vendorId.hashCode),
documents.hashCode), documents.hashCode),
isChanged.hashCode), isChanged.hashCode),
@ -876,8 +876,8 @@ class _$TaskEntity extends TaskEntity {
..add('customValue2', customValue2) ..add('customValue2', customValue2)
..add('customValue3', customValue3) ..add('customValue3', customValue3)
..add('customValue4', customValue4) ..add('customValue4', customValue4)
..add('taskStatusId', taskStatusId) ..add('statusId', statusId)
..add('taskStatusSortOrder', taskStatusSortOrder) ..add('statusSortOrder', statusSortOrder)
..add('vendorId', vendorId) ..add('vendorId', vendorId)
..add('documents', documents) ..add('documents', documents)
..add('isChanged', isChanged) ..add('isChanged', isChanged)
@ -943,14 +943,14 @@ class TaskEntityBuilder implements Builder<TaskEntity, TaskEntityBuilder> {
String get customValue4 => _$this._customValue4; String get customValue4 => _$this._customValue4;
set customValue4(String customValue4) => _$this._customValue4 = customValue4; set customValue4(String customValue4) => _$this._customValue4 = customValue4;
String _taskStatusId; String _statusId;
String get taskStatusId => _$this._taskStatusId; String get statusId => _$this._statusId;
set taskStatusId(String taskStatusId) => _$this._taskStatusId = taskStatusId; set statusId(String statusId) => _$this._statusId = statusId;
int _taskStatusSortOrder; int _statusSortOrder;
int get taskStatusSortOrder => _$this._taskStatusSortOrder; int get statusSortOrder => _$this._statusSortOrder;
set taskStatusSortOrder(int taskStatusSortOrder) => set statusSortOrder(int statusSortOrder) =>
_$this._taskStatusSortOrder = taskStatusSortOrder; _$this._statusSortOrder = statusSortOrder;
String _vendorId; String _vendorId;
String get vendorId => _$this._vendorId; String get vendorId => _$this._vendorId;
@ -1012,8 +1012,8 @@ class TaskEntityBuilder implements Builder<TaskEntity, TaskEntityBuilder> {
_customValue2 = _$v.customValue2; _customValue2 = _$v.customValue2;
_customValue3 = _$v.customValue3; _customValue3 = _$v.customValue3;
_customValue4 = _$v.customValue4; _customValue4 = _$v.customValue4;
_taskStatusId = _$v.taskStatusId; _statusId = _$v.statusId;
_taskStatusSortOrder = _$v.taskStatusSortOrder; _statusSortOrder = _$v.statusSortOrder;
_vendorId = _$v.vendorId; _vendorId = _$v.vendorId;
_documents = _$v.documents?.toBuilder(); _documents = _$v.documents?.toBuilder();
_isChanged = _$v.isChanged; _isChanged = _$v.isChanged;
@ -1060,8 +1060,8 @@ class TaskEntityBuilder implements Builder<TaskEntity, TaskEntityBuilder> {
customValue2: customValue2, customValue2: customValue2,
customValue3: customValue3, customValue3: customValue3,
customValue4: customValue4, customValue4: customValue4,
taskStatusId: taskStatusId, statusId: statusId,
taskStatusSortOrder: taskStatusSortOrder, statusSortOrder: statusSortOrder,
vendorId: vendorId, vendorId: vendorId,
documents: documents.build(), documents: documents.build(),
isChanged: isChanged, isChanged: isChanged,

View File

@ -74,6 +74,48 @@ List<String> filteredTaskStatusesSelector(
return list; return list;
} }
var memoizedCalculateTaskStatusAmount = memo2((String taskStatusId,
BuiltMap<String, TaskEntity> taskMap) =>
calculateTaskStatusAmount(taskStatusId: taskStatusId, taskMap: taskMap));
int calculateTaskStatusAmount({
String taskStatusId,
BuiltMap<String, TaskEntity> taskMap,
}) {
int total = 0;
taskMap.forEach((taskId, task) {
if (task.statusId == taskStatusId) {
total += task.calculateDuration.inSeconds;
}
});
return total;
}
var memoizedTaskStatsForTaskStatus = memo2(
(String companyGatewayId, BuiltMap<String, TaskEntity> taskMap) =>
taskStatsForTaskStatus(companyGatewayId, taskMap));
EntityStats taskStatsForTaskStatus(
String statusId,
BuiltMap<String, TaskEntity> taskMap,
) {
int countActive = 0;
int countArchived = 0;
taskMap.forEach((taskId, task) {
if (task.statusId == statusId) {
if (task.isActive) {
countActive++;
} else if (task.isArchived) {
countArchived++;
}
}
});
return EntityStats(countActive: countActive, countArchived: countArchived);
}
bool hasTaskStatusChanges(TaskStatusEntity taskStatus, bool hasTaskStatusChanges(TaskStatusEntity taskStatus,
BuiltMap<String, TaskStatusEntity> taskStatusMap) => BuiltMap<String, TaskStatusEntity> taskStatusMap) =>
taskStatus.isNew taskStatus.isNew

View File

@ -87,7 +87,6 @@ class _TaskEditDetailsState extends State<TaskEditDetails> {
final viewModel = widget.viewModel; final viewModel = widget.viewModel;
final localization = AppLocalization.of(context); final localization = AppLocalization.of(context);
final task = viewModel.task; final task = viewModel.task;
final company = viewModel.company;
final state = viewModel.state; final state = viewModel.state;
return ListView( return ListView(
@ -137,22 +136,20 @@ class _TaskEditDetailsState extends State<TaskEditDetails> {
.onChanged(task.rebuild((b) => b..assignedUserId = userId)), .onChanged(task.rebuild((b) => b..assignedUserId = userId)),
), ),
// TODO Remove isNotEmpty check in v2 // TODO Remove isNotEmpty check in v2
company.taskStatusMap.isNotEmpty EntityDropdown(
? EntityDropdown( key: ValueKey('__task_status_${task.statusId}__'),
key: ValueKey('__task_status_${task.taskStatusId}__'),
allowClearing: false, allowClearing: false,
entityType: EntityType.taskStatus, entityType: EntityType.taskStatus,
labelText: localization.status, labelText: localization.status,
entityId: task.taskStatusId, entityId: task.statusId,
entityList: company.taskStatusMap.keys.toList(), entityList: state.taskStatusState.list.toList(),
onSelected: (selected) { onSelected: (selected) {
final taskStatus = selected as TaskStatusEntity; final taskStatus = selected as TaskStatusEntity;
viewModel.onChanged(task.rebuild((b) => b viewModel.onChanged(task.rebuild((b) => b
..taskStatusId = taskStatus?.id ..statusId = taskStatus?.id
..taskStatusSortOrder = 9999)); ..statusSortOrder = 9999));
}, },
) ),
: SizedBox(),
DecoratedFormField( DecoratedFormField(
maxLines: 4, maxLines: 4,
controller: _descriptionController, controller: _descriptionController,

View File

@ -57,11 +57,9 @@ class _TaskOverviewState extends State<TaskOverview> {
final Map<String, String> fields = {}; final Map<String, String> fields = {};
// TODO Remove isNotEmpty check in v2 if ((task.statusId ?? '').isNotEmpty) {
if (company.taskStatusMap.isNotEmpty &&
(task.taskStatusId ?? '').isNotEmpty) {
fields[localization.status] = fields[localization.status] =
company.taskStatusMap[task.taskStatusId]?.name ?? ''; company.taskStatusMap[task.statusId]?.name ?? '';
} }
if (task.customValue1.isNotEmpty) { if (task.customValue1.isNotEmpty) {

View File

@ -31,7 +31,6 @@ class TaskStatusScreen extends StatelessWidget {
final userCompany = state.userCompany; final userCompany = state.userCompany;
final localization = AppLocalization.of(context); final localization = AppLocalization.of(context);
final listUIState = state.uiState.taskStatusUIState.listUIState; final listUIState = state.uiState.taskStatusUIState.listUIState;
final isInMultiselect = listUIState.isInMultiselect();
return ListScaffold( return ListScaffold(
entityType: EntityType.taskStatus, entityType: EntityType.taskStatus,

View File

@ -1,7 +1,14 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/task_status/task_status_selectors.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_list_tile.dart';
import 'package:invoiceninja_flutter/ui/app/entity_header.dart';
import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart';
import 'package:invoiceninja_flutter/ui/task_status/view/task_status_view_vm.dart'; import 'package:invoiceninja_flutter/ui/task_status/view/task_status_view_vm.dart';
import 'package:invoiceninja_flutter/ui/app/view_scaffold.dart'; import 'package:invoiceninja_flutter/ui/app/view_scaffold.dart';
import 'package:invoiceninja_flutter/utils/formatting.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
class TaskStatusView extends StatefulWidget { class TaskStatusView extends StatefulWidget {
const TaskStatusView({ const TaskStatusView({
@ -21,14 +28,33 @@ class _TaskStatusViewState extends State<TaskStatusView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final viewModel = widget.viewModel; final viewModel = widget.viewModel;
final state = viewModel.state;
final taskStatus = viewModel.taskStatus; final taskStatus = viewModel.taskStatus;
final localization = AppLocalization.of(context);
final amount = memoizedCalculateTaskStatusAmount(
taskStatus.id, viewModel.state.taskState.map);
return ViewScaffold( return ViewScaffold(
isFilter: widget.isFilter, isFilter: widget.isFilter,
entity: taskStatus, entity: taskStatus,
onBackPressed: () => viewModel.onBackPressed(), onBackPressed: () => viewModel.onBackPressed(),
body: ListView( body: ListView(
children: <Widget>[], children: <Widget>[
EntityHeader(
entity: taskStatus,
label: localization.total,
value: formatDuration(Duration(seconds: amount))),
ListDivider(),
EntitiesListTile(
entity: taskStatus,
isFilter: widget.isFilter,
entityType: EntityType.task,
title: localization.tasks,
subtitle: memoizedTaskStatsForTaskStatus(
taskStatus.id, state.taskState.map)
.present(localization.active, localization.archived),
),
],
), ),
); );
} }