mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Working on task kanban
This commit is contained in:
parent
dd7756d4ca
commit
ea6cdf23aa
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Task;
|
||||||
use App\Models\TaskStatus;
|
use App\Models\TaskStatus;
|
||||||
|
|
||||||
class TaskKanbanController extends BaseController
|
class TaskKanbanController extends BaseController
|
||||||
@ -11,9 +12,10 @@ class TaskKanbanController extends BaseController
|
|||||||
*/
|
*/
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
// check initial statuses exist
|
$tasks = Task::scope()->get();
|
||||||
$stauses = TaskStatus::scope()->get();
|
$stauses = TaskStatus::scope()->get();
|
||||||
|
|
||||||
|
// check initial statuses exist
|
||||||
if (! $stauses->count()) {
|
if (! $stauses->count()) {
|
||||||
$stauses = [];
|
$stauses = [];
|
||||||
$defaults = [
|
$defaults = [
|
||||||
@ -34,6 +36,7 @@ class TaskKanbanController extends BaseController
|
|||||||
$data = [
|
$data = [
|
||||||
'title' => trans('texts.kanban'),
|
'title' => trans('texts.kanban'),
|
||||||
'statuses' => $stauses,
|
'statuses' => $stauses,
|
||||||
|
'tasks' => $tasks,
|
||||||
];
|
];
|
||||||
|
|
||||||
return view('tasks.kanban', $data);
|
return view('tasks.kanban', $data);
|
||||||
|
@ -43,11 +43,16 @@
|
|||||||
padding-bottom: 3px;
|
padding-bottom: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.kanban-column-row {
|
||||||
|
margin-bottom: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
.kanban-column-row .view div {
|
.kanban-column-row .view div {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.kanban-column textarea {
|
.kanban-column textarea {
|
||||||
|
resize: vertical;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
@ -72,7 +77,10 @@
|
|||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
var statuses = {!! $statuses !!};
|
var statuses = {!! $statuses !!};
|
||||||
|
var tasks = {!! $tasks !!};
|
||||||
|
|
||||||
ko.bindingHandlers.enterkey = {
|
ko.bindingHandlers.enterkey = {
|
||||||
init: function (element, valueAccessor, allBindings, viewModel) {
|
init: function (element, valueAccessor, allBindings, viewModel) {
|
||||||
var callback = valueAccessor();
|
var callback = valueAccessor();
|
||||||
@ -99,10 +107,19 @@
|
|||||||
|
|
||||||
self.statuses = ko.observableArray();
|
self.statuses = ko.observableArray();
|
||||||
for (var i=0; i<statuses.length; i++) {
|
for (var i=0; i<statuses.length; i++) {
|
||||||
self.statuses.push(new StatusModel(statuses[i]));
|
var status = statuses[i];
|
||||||
|
var statusModel = new StatusModel(status);
|
||||||
|
self.statuses.push(statusModel);
|
||||||
}
|
}
|
||||||
self.statuses.push(new StatusModel());
|
self.statuses.push(new StatusModel());
|
||||||
|
|
||||||
|
for (var i=0; i<tasks.length; i++) {
|
||||||
|
var task = tasks[i];
|
||||||
|
var taskModel = new TaskModel(task);
|
||||||
|
var statusModel = self.statuses()[tasks.task_status_id || 0];
|
||||||
|
statusModel.tasks.push(taskModel);
|
||||||
|
}
|
||||||
|
|
||||||
self.onDragged = function() {
|
self.onDragged = function() {
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -113,9 +130,18 @@
|
|||||||
self.name = ko.observable();
|
self.name = ko.observable();
|
||||||
self.is_blank = ko.observable(false);
|
self.is_blank = ko.observable(false);
|
||||||
self.is_editing_status = ko.observable(false);
|
self.is_editing_status = ko.observable(false);
|
||||||
|
self.is_header_hovered = ko.observable(false);
|
||||||
self.tasks = ko.observableArray();
|
self.tasks = ko.observableArray();
|
||||||
self.new_task = new TaskModel();
|
self.new_task = new TaskModel();
|
||||||
|
|
||||||
|
self.onHeaderMouseOver = function() {
|
||||||
|
self.is_header_hovered(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onHeaderMouseOut = function() {
|
||||||
|
self.is_header_hovered(false);
|
||||||
|
}
|
||||||
|
|
||||||
self.inputValue = ko.computed({
|
self.inputValue = ko.computed({
|
||||||
read: function () {
|
read: function () {
|
||||||
return self.is_blank() ? '' : self.name();
|
return self.is_blank() ? '' : self.name();
|
||||||
@ -156,9 +182,10 @@
|
|||||||
self.new_task.endEdit();
|
self.new_task.endEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.saveNewTask = function(task) {
|
self.saveNewTask = function() {
|
||||||
|
console.log('description: ' + self.new_task.description());
|
||||||
var task = new TaskModel({
|
var task = new TaskModel({
|
||||||
description: task.description()
|
description: self.new_task.description()
|
||||||
})
|
})
|
||||||
self.tasks.push(task);
|
self.tasks.push(task);
|
||||||
self.new_task.reset();
|
self.new_task.reset();
|
||||||
@ -178,12 +205,12 @@
|
|||||||
function TaskModel(data) {
|
function TaskModel(data) {
|
||||||
var self = this;
|
var self = this;
|
||||||
self.description = ko.observable('');
|
self.description = ko.observable('');
|
||||||
|
self.description.orig = ko.observable('');
|
||||||
self.is_blank = ko.observable(false);
|
self.is_blank = ko.observable(false);
|
||||||
self.is_editing_task = ko.observable(false);
|
self.is_editing_task = ko.observable(false);
|
||||||
|
|
||||||
self.startEdit = function() {
|
self.startEdit = function() {
|
||||||
console.log('start edit');
|
self.description.orig(self.description());
|
||||||
|
|
||||||
self.is_editing_task(true);
|
self.is_editing_task(true);
|
||||||
$('.kanban-column-row.editing textarea').focus();
|
$('.kanban-column-row.editing textarea').focus();
|
||||||
}
|
}
|
||||||
@ -197,11 +224,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.cancelEditTask = function() {
|
self.cancelEditTask = function() {
|
||||||
/*
|
if (self.is_blank()) {
|
||||||
if (self.new_task.is_blank()) {
|
self.description('');
|
||||||
self.new_task.description('');
|
} else {
|
||||||
|
self.description(self.description.orig());
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
self.endEdit();
|
self.endEdit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,14 +269,13 @@
|
|||||||
<div data-bind="sortable: { data: statuses, as: 'status', afterMove: onDragged, allowDrop: true, connectClass: 'connect-column' }">
|
<div data-bind="sortable: { data: statuses, as: 'status', afterMove: onDragged, allowDrop: true, connectClass: 'connect-column' }">
|
||||||
<div class="well kanban-column">
|
<div class="well kanban-column">
|
||||||
|
|
||||||
<div class="kanban-column-header" data-bind="css: { editing: is_editing_status }">
|
<div class="kanban-column-header" data-bind="css: { editing: is_editing_status }, event: { mouseover: onHeaderMouseOver, mouseout: onHeaderMouseOut }">
|
||||||
<div class="pull-left" data-bind="event: { click: startEdit }">
|
<div class="pull-left" data-bind="event: { click: startEdit }">
|
||||||
<div class="view" data-bind="text: name"></div>
|
<div class="view" data-bind="text: name"></div>
|
||||||
<input class="edit" type="text"
|
<input class="edit" type="text" data-bind="value: inputValue, hasfocus: is_editing_status, selected: is_editing_status,
|
||||||
data-bind="value: inputValue, hasfocus: is_editing_status, selected: is_editing_status,
|
|
||||||
placeholder: placeholder, event: { blur: endEdit }, enterkey: endEdit"/>
|
placeholder: placeholder, event: { blur: endEdit }, enterkey: endEdit"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right" data-bind="click: archiveStatus, visible: ! is_blank()">
|
<div class="pull-right" data-bind="click: archiveStatus, visible: ! is_blank() && is_header_hovered">
|
||||||
<i class="fa fa-times" title="{{ trans('texts.archive') }}"></i>
|
<i class="fa fa-times" title="{{ trans('texts.archive') }}"></i>
|
||||||
</div><br/>
|
</div><br/>
|
||||||
</div>
|
</div>
|
||||||
@ -261,7 +288,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="edit">
|
<div class="edit">
|
||||||
<textarea data-bind="value: description"></textarea>
|
<textarea data-bind="value: description, valueUpdate: 'afterkeydown', enterkey: saveEditTask"></textarea>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type='button' class='btn btn-default btn-sm' data-bind="click: cancelEditTask">
|
<button type='button' class='btn btn-default btn-sm' data-bind="click: cancelEditTask">
|
||||||
{{ trans('texts.cancel') }}
|
{{ trans('texts.cancel') }}
|
||||||
@ -274,17 +301,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="kanban-column-row" data-bind="css: { editing: new_task.is_editing_task }, with: new_task">
|
<div class="kanban-column-footer" data-bind="css: { editing: new_task.is_editing_task }, with: new_task">
|
||||||
<div data-bind="event: { click: startEdit }">
|
<div data-bind="event: { click: startEdit }">
|
||||||
<div class="view panel" data-bind="visible: ! is_blank()">
|
<div class="view panel" data-bind="visible: ! is_blank()">
|
||||||
<div data-bind="text: description"></div>
|
<div data-bind="text: description"></div>
|
||||||
</div>
|
</div>
|
||||||
<a href="#" class="view text-muted" data-bind="visible: is_blank">
|
<a href="#" class="view text-muted" style="font-size:13px" data-bind="visible: is_blank">
|
||||||
{{ trans('texts.new_task') }}...
|
{{ trans('texts.new_task') }}...
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="edit">
|
<div class="edit">
|
||||||
<textarea data-bind="value: description"></textarea>
|
<textarea data-bind="value: description, valueUpdate: 'afterkeydown', enterkey: $parent.saveNewTask"></textarea>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
<button type='button' class='btn btn-default btn-sm' data-bind="click: $parent.cancelNewTask">
|
<button type='button' class='btn btn-default btn-sm' data-bind="click: $parent.cancelNewTask">
|
||||||
{{ trans('texts.cancel') }}
|
{{ trans('texts.cancel') }}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user