import 'package:boardview/board_item.dart'; import 'package:boardview/board_list.dart'; import 'package:boardview/boardview.dart'; import 'package:boardview/boardview_controller.dart'; import 'package:flutter/material.dart'; import 'package:invoiceninja_flutter/data/models/entities.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart'; import 'package:invoiceninja_flutter/ui/app/history_drawer_vm.dart'; import 'package:invoiceninja_flutter/ui/app/list_filter.dart'; import 'package:invoiceninja_flutter/ui/app/menu_drawer_vm.dart'; import 'package:invoiceninja_flutter/ui/task/kanban_view_vm.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart'; import 'package:timeago/timeago.dart' as timeago; class KanbanView extends StatefulWidget { const KanbanView({ Key key, @required this.viewModel, }) : super(key: key); final KanbanVM viewModel; @override _KanbanViewState createState() => _KanbanViewState(); } class _KanbanViewState extends State { final _boardViewController = new BoardViewController(); List _statuses = []; Map> _tasks = {}; @override void initState() { super.initState(); print('## initState: ${_statuses.length}'); final state = widget.viewModel.state; _statuses = state.taskStatusState.list .map((statusId) => state.taskStatusState.get(statusId)) .where((status) => status.isActive) .toList(); _statuses.sort((statusA, statusB) { if (statusA.statusOrder == statusB.statusOrder) { return statusB.updatedAt.compareTo(statusA.updatedAt); } else { return (statusA.statusOrder ?? 99999) .compareTo(statusB.statusOrder ?? 99999); } }); state.taskState.list.forEach((taskId) { final task = state.taskState.map[taskId]; if (task.isActive && task.statusId.isNotEmpty) { final status = state.taskStatusState.get(task.statusId); if (!_tasks.containsKey(status.id)) { _tasks[status.id] = []; } _tasks[status.id].add(task); } }); _tasks.forEach((key, value) { _tasks[key].sort((taskA, taskB) { if (taskA.statusOrder == taskB.statusOrder) { return taskB.updatedAt.compareTo(taskA.updatedAt); } else { return (taskA.statusOrder ?? 99999) .compareTo(taskB.statusOrder ?? 99999); } }); }); } @override Widget build(BuildContext context) { print('## BUILD: ${_statuses.length}'); final state = widget.viewModel.state; final boardList = _statuses.map((status) { return BoardList( backgroundColor: Theme.of(context).cardColor, headerBackgroundColor: Theme.of(context).cardColor, onDropList: (endIndex, startIndex) { if (endIndex == startIndex) { return; } setState(() { final status = _statuses[startIndex]; _statuses.removeAt(startIndex); _statuses = [ ..._statuses.sublist(0, endIndex), status, ..._statuses.sublist(endIndex), ]; }); widget.viewModel.onStatusOrderChanged(context, status.id, endIndex); }, header: [ Expanded( child: Padding( padding: EdgeInsets.all(8), child: Text( '${status.statusOrder} - ${status.name} - ${timeago.format(DateTime.fromMillisecondsSinceEpoch(status.updatedAt * 1000))}'), ), ), ], footer: Align( alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.only(left: 8), child: TextButton( child: Text(AppLocalization.of(context).newTask), onPressed: () { // }, ), ), ), items: (_tasks[status.id] ?? []) .map( (task) => BoardItem( item: _TaskCard(task: task), onDropItem: ( int listIndex, int itemIndex, int oldListIndex, int oldItemIndex, BoardItemState state, ) { if (listIndex == oldListIndex && itemIndex == oldItemIndex) { return; } final oldStatus = _statuses[oldListIndex]; final newStatus = _statuses[listIndex]; final task = _tasks[status.id][oldItemIndex]; setState(() { if (_tasks.containsKey(oldStatus.id) && _tasks[oldStatus.id].contains(task)) { _tasks[oldStatus.id].remove(task); } if (!_tasks.containsKey(newStatus.id)) { _tasks[newStatus.id] = []; } _tasks[newStatus.id] = [ ..._tasks[newStatus.id].sublist(0, itemIndex), task, ..._tasks[newStatus.id].sublist(itemIndex), ]; }); widget.viewModel.onTaskOrderChanged( context, task.id, newStatus.id, itemIndex); }, ), ) .toList(), ); }).toList(); return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: BoardView( boardViewController: _boardViewController, lists: boardList, dragDelay: 1, ), ); } } class _TaskCard extends StatefulWidget { const _TaskCard({this.task}); final TaskEntity task; @override __TaskCardState createState() => __TaskCardState(); } class __TaskCardState extends State<_TaskCard> { bool _isEditing = false; @override Widget build(BuildContext context) { final localization = AppLocalization.of(context); if (_isEditing) { return Card( color: Theme.of(context).backgroundColor, child: Column( children: [ DecoratedFormField( autofocus: true, initialValue: widget.task.description, minLines: 4, maxLines: 4, ), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ ElevatedButton( onPressed: () => setState(() => _isEditing = false), child: Text(localization.cancel), ), ElevatedButton( onPressed: () { // }, child: Text(localization.view), ), ElevatedButton( onPressed: () { // }, child: Text(localization.save), ), ], ) ], ), ); } return InkWell( child: Card( color: Theme.of(context).backgroundColor, child: Padding( padding: const EdgeInsets.all(8), child: Text( '${widget.task.statusOrder} - ${widget.task.id} - ${timeago.format(DateTime.fromMillisecondsSinceEpoch(widget.task.updatedAt * 1000))}'), ), ), onTap: () { setState(() { _isEditing = true; }); }, ); } }