2020-04-25 23:11:34 +00:00
< template >
2020-11-22 16:32:35 +00:00
< div >
2020-12-24 00:33:25 +00:00
< div class = "filter-container" v-if ="list.isSavedFilter && !list.isSavedFilter()" >
2020-12-22 11:49:34 +00:00
< div class = "items" >
< button @ click = "showFilters = !showFilters" class = "button" >
< span class = "icon is-small" >
< icon icon = "filter" / >
< / span >
Filters
< / button >
< / div >
< transition name = "fade" >
< filters
@ change = "() => {filtersChanged = true; loadBuckets()}"
v - if = "showFilters"
v - model = "params"
/ >
< / transition >
< / div >
2020-12-08 17:49:28 +00:00
< div : class = "{ 'is-loading': loading && !oneTaskUpdating}" class = "kanban loader-container" >
2020-11-22 16:32:35 +00:00
< div :key ="`bucket${bucket.id}`" class = "bucket" v-for ="bucket in buckets" >
< div class = "bucket-header" >
< h2
: ref = "`bucket${bucket.id}title`"
@ focusout = "() => saveBucketTitle(bucket.id)"
2021-01-15 22:46:43 +00:00
@ keydown . enter . prevent . stop = "() => saveBucketTitle(bucket.id)"
2020-11-22 16:32:35 +00:00
class = "title input"
contenteditable = "true"
spellcheck = "false" > { { bucket . title } } < / h2 >
< span
: class = "{'is-max': bucket.tasks.length >= bucket.limit}"
class = "limit"
v - if = "bucket.limit > 0" >
2020-12-28 22:42:09 +00:00
{ { bucket . tasks . length } } / { { bucket . limit } }
< / span >
2020-11-22 16:32:35 +00:00
< div
: class = "{ 'is-active': bucketOptionsDropDownActive[bucket.id] }"
class = "dropdown is-right options"
v - if = "canWrite"
>
< div @click.stop ="toggleBucketDropdown(bucket.id)" class = "dropdown-trigger" >
2020-04-25 23:11:34 +00:00
< span class = "icon" >
< icon icon = "ellipsis-v" / >
< / span >
2020-11-22 16:32:35 +00:00
< / div >
< div class = "dropdown-menu" role = "menu" >
< div class = "dropdown-content" >
< a
@ click . stop = "showSetLimitInput = true"
class = "dropdown-item"
>
< div class = "field has-addons" v-if ="showSetLimitInput" >
< div class = "control" >
< input
@ change = "() => updateBucket(bucket)"
@ keyup . enter = "() => updateBucket(bucket)"
class = "input"
type = "number"
v - focus . always
v - model = "bucket.limit"
/ >
< / div >
< div class = "control" >
< a class = "button is-primary has-no-shadow" >
2020-09-04 20:01:02 +00:00
< span class = "icon" >
< icon : icon = "['far', 'save']" / >
< / span >
2020-11-22 16:32:35 +00:00
< / a >
< / div >
2020-09-04 20:01:02 +00:00
< / div >
2020-11-22 16:32:35 +00:00
< template v-else >
Limit : { { bucket . limit > 0 ? bucket . limit : 'Not set' } }
< / template >
< / a >
< a
: class = "{'is-disabled': buckets.length <= 1}"
@ click = "() => deleteBucketModal(bucket.id)"
class = "dropdown-item has-text-danger"
v - tooltip = "buckets.length <= 1 ? 'You cannot remove the last bucket.' : ''"
>
< span class = "icon is-small" > < icon icon = "trash-alt" / > < / span >
Delete
< / a >
< / div >
2020-04-25 23:11:34 +00:00
< / div >
< / div >
< / div >
2020-11-22 16:32:35 +00:00
< div :ref ="`tasks-container${bucket.id}`" class = "tasks" >
2020-08-11 18:18:59 +00:00
<!-- Make the component either a div or a draggable component based on the user rights -- >
< component
2020-11-22 16:32:35 +00:00
: animation - duration = "150"
: drop - placeholder = "dropPlaceholderOptions"
: get - child - payload = "getTaskPayload(bucket.id)"
: is = "canWrite ? 'Container' : 'div'"
: should - accept - drop = "() => shouldAcceptDrop(bucket)"
@ drop = "e => onDrop(bucket.id, e)"
drag - class = "ghost-task"
drag - class - drop = "ghost-task-drop"
drag - handle - selector = ".task.draggable"
group - name = "buckets"
2020-08-11 18:18:59 +00:00
>
2020-11-22 16:32:35 +00:00
<!-- Make the component either a div or a draggable component based on the user rights -- >
< component
: is = "canWrite ? 'Draggable' : 'div'"
: key = "`bucket${bucket.id}-task${task.id}`"
v - for = "task in bucket.tasks"
>
< div
: class = " {
2021-01-09 14:24:06 +00:00
'is-loading' : ( taskService . loading || taskLoading ) && taskUpdating [ task . id ] ,
'draggable' : ! ( taskService . loading || taskLoading ) || ! taskUpdating [ task . id ] ,
2020-09-04 20:01:02 +00:00
'has-light-text' : ! colorIsDark ( task . hexColor ) && task . hexColor !== ` # ${ task . defaultColor } ` && task . hexColor !== task . defaultColor ,
2020-04-25 23:11:34 +00:00
} "
2020-11-22 16:32:35 +00:00
: style = "{'background-color': task.hexColor !== '#' && task.hexColor !== `#${task.defaultColor}` ? task.hexColor : false}"
@ click . ctrl = "() => markTaskAsDone(task)"
@ click . exact = "() => $router.push({ name: 'task.kanban.detail', params: { id: task.id } })"
@ click . meta = "() => markTaskAsDone(task)"
class = "task loader-container draggable"
>
2020-04-25 23:11:34 +00:00
< span class = "task-id" >
< span class = "is-done" v-if ="task.done" > Done < / span >
2020-06-11 21:35:50 +00:00
< template v-if ="task.identifier === ''" >
# { { task . index } }
< / template >
< template v-else >
2020-06-25 19:01:05 +00:00
{ { task . identifier } }
2020-06-11 21:35:50 +00:00
< / template >
2020-04-25 23:11:34 +00:00
< / span >
2020-11-22 16:32:35 +00:00
< span
: class = "{'overdue': task.dueDate <= new Date() && !task.done}"
class = "due-date"
v - if = "task.dueDate > 0"
v - tooltip = "formatDate(task.dueDate)" >
2021-01-10 17:27:18 +00:00
< span class = "icon" >
< icon : icon = "['far', 'calendar-alt']" / >
< / span >
< span >
{ { formatDateSince ( task . dueDate ) } }
< / span >
2020-04-25 23:11:34 +00:00
< / span >
2020-11-22 16:32:35 +00:00
< h3 > { { task . title } } < / h3 >
2020-12-31 15:16:07 +00:00
< progress
class = "progress is-small"
v - if = "task.percentDone > 0"
: value = "task.percentDone * 100" max = "100" >
{ { task . percentDone * 100 } } %
< / progress >
2020-11-22 16:32:35 +00:00
< div class = "footer" >
2021-01-10 17:27:18 +00:00
< span
: key = "label.id"
: style = "{'background': label.hexColor, 'color': label.textColor}"
class = "tag"
v - for = "label in task.labels" >
< span > { { label . title } } < / span >
< / span >
< priority -label :priority ="task.priority" / >
< div class = "assignees" v-if ="task.assignees.length > 0" >
< user
: avatar - size = "24"
: key = "task.id + 'assignee' + u.id"
: show - username = "false"
: user = "u"
v - for = "u in task.assignees"
/ >
2020-11-22 16:32:35 +00:00
< / div >
2021-01-10 17:27:18 +00:00
< span class = "icon" v-if ="task.attachments.length > 0" >
< icon icon = "paperclip" / >
< / span >
< span v-if ="task.description" class="icon" >
< icon icon = "align-left" / >
< / span >
2020-04-25 23:11:34 +00:00
< / div >
< / div >
2020-11-22 16:32:35 +00:00
< / component >
2020-08-11 18:18:59 +00:00
< / component >
2020-11-22 16:32:35 +00:00
< / div >
< div class = "bucket-footer" v-if ="canWrite" >
< div class = "field" v-if ="showNewTaskInput[bucket.id]" >
2020-12-08 17:49:28 +00:00
< div class = "control" : class = "{'is-loading': taskService.loading || loading}" >
2020-11-22 16:32:35 +00:00
< input
2020-11-28 14:44:48 +00:00
class = "input"
2020-12-08 17:49:28 +00:00
: disabled = "taskService.loading || loading"
2020-11-22 16:32:35 +00:00
@ focusout = "toggleShowNewTaskInput(bucket.id)"
@ keyup . enter = "addTaskToBucket(bucket.id)"
@ keyup . esc = "toggleShowNewTaskInput(bucket.id)"
placeholder = "Enter the new task text..."
type = "text"
v - focus . always
v - model = "newTaskText"
/ >
< / div >
< p class = "help is-danger" v-if ="newTaskError[bucket.id] && newTaskText === ''" >
Please specify a title .
< / p >
2020-04-25 23:11:34 +00:00
< / div >
2020-11-22 16:32:35 +00:00
< a
@ click = "toggleShowNewTaskInput(bucket.id)"
class = "button noshadow is-transparent is-fullwidth has-text-centered"
v - if = "!showNewTaskInput[bucket.id]" >
2020-04-25 23:11:34 +00:00
< span class = "icon is-small" >
< icon icon = "plus" / >
< / span >
2020-11-22 16:32:35 +00:00
< span v-if ="bucket.tasks.length === 0" >
2020-04-25 23:11:34 +00:00
Add a task
2020-11-28 14:44:48 +00:00
< / span >
2020-11-22 16:32:35 +00:00
< span v-else >
2020-04-25 23:11:34 +00:00
Add another task
< / span >
2020-11-22 16:32:35 +00:00
< / a >
< / div >
2020-04-25 23:11:34 +00:00
< / div >
2020-12-23 20:16:13 +00:00
< div class = "bucket new-bucket" v-if ="canWrite" >
2020-11-22 16:32:35 +00:00
< input
: class = "{'is-loading': loading}"
: disabled = "loading"
@ focusout = "() => showNewBucketInput = false"
@ keyup . enter = "createNewBucket"
@ keyup . esc = "() => showNewBucketInput = false"
class = "input"
placeholder = "Enter the new bucket title..."
type = "text"
v - focus . always
v - if = "showNewBucketInput"
v - model = "newBucketTitle"
/ >
< a
@ click = "() => showNewBucketInput = true"
class = "button noshadow is-transparent is-fullwidth has-text-centered" v - if = "!showNewBucketInput" >
2020-12-23 20:16:13 +00:00
< span class = "icon is-small" >
< icon icon = "plus" / >
< / span >
2020-11-22 16:32:35 +00:00
< span >
2020-12-23 20:16:13 +00:00
Create a new bucket
< / span >
2020-11-22 16:32:35 +00:00
< / a >
< / div >
2020-04-25 23:11:34 +00:00
< / div >
<!-- This router view is used to show the task popup while keeping the kanban board itself -- >
< transition name = "modal" >
< router -view / >
< / transition >
< modal
2020-09-05 20:35:52 +00:00
@ close = "showBucketDeleteModal = false"
@ submit = "deleteBucket()"
v - if = "showBucketDeleteModal" >
2020-04-25 23:11:34 +00:00
< span slot = "header" > Delete the bucket < / span >
< p slot = "text" >
Are you sure you want to delete this bucket ? < br / >
This will not delete any tasks but move them into the default bucket .
< / p >
< / modal >
< / div >
< / template >
< script >
2020-09-05 20:35:52 +00:00
import TaskService from '../../../services/task'
import TaskModel from '../../../models/task'
import BucketModel from '../../../models/bucket'
import { Container , Draggable } from 'vue-smooth-dnd'
import PriorityLabel from '../../../components/tasks/partials/priorityLabel'
import User from '../../../components/misc/user'
import Labels from '../../../components/tasks/partials/labels'
2020-12-22 11:49:34 +00:00
import Filters from '../../../components/list/partials/filters'
2020-09-05 20:35:52 +00:00
import { filterObject } from '@/helpers/filterObject'
import { applyDrag } from '@/helpers/applyDrag'
import { mapState } from 'vuex'
import { saveListView } from '@/helpers/saveListView'
import Rights from '../../../models/rights.json'
2021-01-09 14:24:06 +00:00
import { LOADING , LOADING _MODULE } from '../../../store/mutation-types'
2020-09-05 20:35:52 +00:00
export default {
name : 'Kanban' ,
components : {
Container ,
Draggable ,
Labels ,
User ,
PriorityLabel ,
2020-12-22 11:49:34 +00:00
Filters ,
2020-09-05 20:35:52 +00:00
} ,
data ( ) {
return {
taskService : TaskService ,
dropPlaceholderOptions : {
className : 'drop-preview' ,
animationDuration : 150 ,
showOnTop : true ,
} ,
sourceBucket : 0 ,
bucketOptionsDropDownActive : { } ,
showBucketDeleteModal : false ,
bucketToDelete : 0 ,
newTaskText : '' ,
showNewTaskInput : { } ,
newBucketTitle : '' ,
showNewBucketInput : false ,
newTaskError : { } ,
showSetLimitInput : false ,
// We're using this to show the loading animation only at the task when updating it
taskUpdating : { } ,
2020-12-08 17:49:28 +00:00
oneTaskUpdating : false ,
2020-12-22 11:49:34 +00:00
params : {
filter _by : [ ] ,
filter _value : [ ] ,
filter _comparator : [ ] ,
filter _concat : 'and' ,
} ,
showFilters : false ,
filtersChanged : false , // To trigger a reload of the board
2020-09-05 20:35:52 +00:00
}
} ,
created ( ) {
this . taskService = new TaskService ( )
this . loadBuckets ( )
this . $nextTick ( ( ) => document . addEventListener ( 'click' , this . closeBucketDropdowns ) )
// Save the current list view to local storage
// We use local storage and not vuex here to make it persistent across reloads.
saveListView ( this . $route . params . listId , this . $route . name )
} ,
watch : {
'$route.params.listId' : 'loadBuckets' ,
} ,
computed : mapState ( {
buckets : state => state . kanban . buckets ,
loadedListId : state => state . kanban . listId ,
2021-01-09 14:24:06 +00:00
loading : state => state [ LOADING ] && state [ LOADING _MODULE ] === 'kanban' ,
taskLoading : state => state [ LOADING ] && state [ LOADING _MODULE ] === 'tasks' ,
2020-09-05 20:35:52 +00:00
canWrite : state => state . currentList . maxRight > Rights . READ ,
2020-12-24 00:33:25 +00:00
list : state => state . currentList ,
2020-09-05 20:35:52 +00:00
} ) ,
methods : {
loadBuckets ( ) {
// Prevent trying to load buckets if the task popup view is active
if ( this . $route . name !== 'list.kanban' ) {
return
2020-04-25 23:11:34 +00:00
}
2020-05-21 09:35:09 +00:00
2020-09-05 20:35:52 +00:00
// Only load buckets if we don't already loaded them
if (
2020-12-22 11:49:34 +00:00
! this . filtersChanged && (
2020-09-05 20:35:52 +00:00
this . loadedListId === this . $route . params . listId ||
2020-12-22 11:49:34 +00:00
this . loadedListId === parseInt ( this . $route . params . listId ) )
2020-09-05 20:35:52 +00:00
) {
return
}
2020-05-21 09:35:09 +00:00
2020-09-05 20:35:52 +00:00
console . debug ( ` Loading buckets, loadedListId = ${ this . loadedListId } , $ route.params = ` , this . $route . params )
2020-12-22 11:49:34 +00:00
this . filtersChanged = false
2020-07-24 08:42:30 +00:00
2020-12-22 11:49:34 +00:00
this . $store . dispatch ( 'kanban/loadBucketsForList' , { listId : this . $route . params . listId , params : this . params } )
2020-09-05 20:35:52 +00:00
. catch ( e => {
this . error ( e , this )
} )
} ,
onDrop ( bucketId , dropResult ) {
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
// Note: A lot of this example comes from the excellent kanban example on https://github.com/kutlugsahin/vue-smooth-dnd/blob/master/demo/src/pages/cards.vue
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
const bucketIndex = filterObject ( this . buckets , b => b . id === bucketId )
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
if ( dropResult . removedIndex !== null || dropResult . addedIndex !== null ) {
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
// FIXME: This is probably not the best solution and more of a naive brute-force approach
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
// Duplicate the buckets to avoid stuff moving around without noticing
const buckets = Object . assign ( { } , this . buckets )
// Get the index of the bucket and the bucket itself
const bucket = buckets [ bucketIndex ]
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
// Rebuild the tasks from the bucket, removing/adding the moved task
bucket . tasks = applyDrag ( bucket . tasks , dropResult )
// Update the bucket in the list of all buckets
delete buckets [ bucketIndex ]
buckets [ bucketIndex ] = bucket
// Set the buckets, triggering a state update in vue
// FIXME: This seems to set some task attributes (like due date) wrong. Commented out, but seems to still work?
// Not sure what to do about this.
// this.$store.commit('kanban/setBuckets', buckets)
}
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
if ( dropResult . addedIndex !== null ) {
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
const taskIndex = dropResult . addedIndex
const taskBefore = typeof this . buckets [ bucketIndex ] . tasks [ taskIndex - 1 ] === 'undefined' ? null : this . buckets [ bucketIndex ] . tasks [ taskIndex - 1 ]
const taskAfter = typeof this . buckets [ bucketIndex ] . tasks [ taskIndex + 1 ] === 'undefined' ? null : this . buckets [ bucketIndex ] . tasks [ taskIndex + 1 ]
const task = this . buckets [ bucketIndex ] . tasks [ taskIndex ]
this . $set ( this . taskUpdating , task . id , true )
2020-12-08 17:49:28 +00:00
this . oneTaskUpdating = true
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
// If there is no task before, our task is the first task in which case we let it have half of the position of the task after it
if ( taskBefore === null && taskAfter !== null ) {
task . position = taskAfter . position / 2
}
// If there is no task after it, we just add 2^16 to the last position
if ( taskBefore !== null && taskAfter === null ) {
task . position = taskBefore . position + Math . pow ( 2 , 16 )
}
// If we have both a task before and after it, we acually calculate the position
if ( taskAfter !== null && taskBefore !== null ) {
task . position = taskBefore . position + ( taskAfter . position - taskBefore . position ) / 2
}
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
task . bucketId = bucketId
2020-04-25 23:11:34 +00:00
2020-07-05 19:29:14 +00:00
this . $store . dispatch ( 'tasks/update' , task )
. catch ( e => {
this . error ( e , this )
} )
. finally ( ( ) => {
this . $set ( this . taskUpdating , task . id , false )
2020-12-08 17:49:28 +00:00
this . oneTaskUpdating = false
2020-07-05 19:29:14 +00:00
} )
2020-09-05 20:35:52 +00:00
}
} ,
markTaskAsDone ( task ) {
2020-12-08 17:49:28 +00:00
this . oneTaskUpdating = true
this . $set ( this . taskUpdating , task . id , true )
2020-09-05 20:35:52 +00:00
task . done = ! task . done
this . $store . dispatch ( 'tasks/update' , task )
. catch ( e => {
this . error ( e , this )
} )
. finally ( ( ) => {
this . $set ( this . taskUpdating , task . id , false )
2020-12-08 17:49:28 +00:00
this . oneTaskUpdating = false
2020-09-05 20:35:52 +00:00
} )
} ,
getTaskPayload ( bucketId ) {
return index => {
const bucket = this . buckets [ filterObject ( this . buckets , b => b . id === bucketId ) ]
this . sourceBucket = bucket . id
return bucket . tasks [ index ]
}
} ,
toggleShowNewTaskInput ( bucket ) {
this . $set ( this . showNewTaskInput , bucket , ! this . showNewTaskInput [ bucket ] )
} ,
toggleBucketDropdown ( bucketId ) {
this . closeBucketDropdowns ( ) // Close all eventually open dropdowns
this . $set ( this . bucketOptionsDropDownActive , bucketId , ! this . bucketOptionsDropDownActive [ bucketId ] )
} ,
closeBucketDropdowns ( ) {
this . showSetLimitInput = false
for ( const bucketId in this . bucketOptionsDropDownActive ) {
this . bucketOptionsDropDownActive [ bucketId ] = false
}
} ,
addTaskToBucket ( bucketId ) {
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
if ( this . newTaskText === '' ) {
this . $set ( this . newTaskError , bucketId , true )
return
}
this . $set ( this . newTaskError , bucketId , false )
// We need the actual bucket index so we put that in a seperate function
const bucketIndex = ( ) => {
for ( const t in this . buckets ) {
if ( this . buckets [ t ] . id === bucketId ) {
return t
2020-04-25 23:11:34 +00:00
}
}
2020-09-05 20:35:52 +00:00
}
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
const bi = bucketIndex ( )
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
const task = new TaskModel ( {
title : this . newTaskText ,
bucketId : this . buckets [ bi ] . id ,
listId : this . $route . params . listId ,
} )
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
this . taskService . create ( task )
. then ( r => {
this . newTaskText = ''
this . $store . commit ( 'kanban/addTaskToBucket' , r )
} )
. catch ( e => {
this . error ( e , this )
2020-06-11 21:35:50 +00:00
} )
2020-09-05 20:35:52 +00:00
. finally ( ( ) => {
if ( ! this . $refs [ ` tasks-container ${ task . bucketId } ` ] [ 0 ] ) {
return
}
this . $refs [ ` tasks-container ${ task . bucketId } ` ] [ 0 ] . scrollTop = this . $refs [ ` tasks-container ${ task . bucketId } ` ] [ 0 ] . scrollHeight
} )
} ,
createNewBucket ( ) {
if ( this . newBucketTitle === '' ) {
return
}
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
const newBucket = new BucketModel ( {
title : this . newBucketTitle ,
listId : parseInt ( this . $route . params . listId ) ,
} )
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
this . $store . dispatch ( 'kanban/createBucket' , newBucket )
. then ( ( ) => {
this . newBucketTitle = ''
this . showNewBucketInput = false
2020-04-25 23:11:34 +00:00
} )
2020-09-05 20:35:52 +00:00
. catch ( e => {
this . error ( e , this )
2020-04-25 23:11:34 +00:00
} )
2020-09-05 20:35:52 +00:00
} ,
deleteBucketModal ( bucketId ) {
if ( this . buckets . length <= 1 ) {
return
}
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
this . bucketToDelete = bucketId
this . showBucketDeleteModal = true
} ,
deleteBucket ( ) {
const bucket = new BucketModel ( {
id : this . bucketToDelete ,
listId : this . $route . params . listId ,
} )
this . $store . dispatch ( 'kanban/deleteBucket' , bucket )
2021-01-15 22:46:43 +00:00
. then ( ( ) => {
this . success ( { message : 'The bucket has been deleted successfully.' } , this )
} )
2020-09-05 20:35:52 +00:00
. catch ( e => {
this . error ( e , this )
} )
. finally ( ( ) => {
this . showBucketDeleteModal = false
} )
} ,
saveBucketTitle ( bucketId ) {
2020-10-24 15:23:13 +00:00
const bucketTitleElement = this . $refs [ ` bucket ${ bucketId } title ` ] [ 0 ]
const bucketTitle = bucketTitleElement . textContent
2020-09-05 20:35:52 +00:00
const bucket = new BucketModel ( {
id : bucketId ,
title : bucketTitle ,
listId : Number ( this . $route . params . listId ) ,
} )
// Because the contenteditable does not have a change event,
// we're building it ourselves here and only updating the bucket
// if the title changed.
const realBucket = this . buckets [ filterObject ( this . buckets , b => b . id === bucketId ) ]
if ( realBucket . title === bucketTitle ) {
return
}
2020-04-25 23:11:34 +00:00
2020-09-05 20:35:52 +00:00
this . $store . dispatch ( 'kanban/updateBucket' , bucket )
. then ( r => {
realBucket . title = r . title
2020-10-24 15:23:13 +00:00
bucketTitleElement . blur ( )
2021-01-15 22:46:43 +00:00
this . success ( { message : 'The bucket title has been saved successfully.' } , this )
2020-09-05 20:35:52 +00:00
} )
. catch ( e => {
this . error ( e , this )
} )
} ,
updateBucket ( bucket ) {
bucket . limit = parseInt ( bucket . limit )
this . $store . dispatch ( 'kanban/updateBucket' , bucket )
2021-01-15 22:46:43 +00:00
. then ( ( ) => {
this . success ( { message : 'The bucket limit been saved successfully.' } , this )
} )
2020-09-05 20:35:52 +00:00
. catch ( e => {
this . error ( e , this )
} )
} ,
shouldAcceptDrop ( bucket ) {
return bucket . id === this . sourceBucket || // When dragging from a bucket who has its limit reached, dragging should still be possible
bucket . limit === 0 || // If there is no limit set, dragging & dropping should always work
bucket . tasks . length < bucket . limit // Disallow dropping to buckets which have their limit reached
2020-04-25 23:11:34 +00:00
} ,
2020-09-05 20:35:52 +00:00
} ,
}
2020-04-25 23:11:34 +00:00
< / script >